Generate a UUID on lttng-sessiond launch
[lttng-tools.git] / src / common / compat / directory-handle.c
1 /*
2 * Copyright (C) 2019 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2 only,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 */
17
18 #include <common/compat/directory-handle.h>
19 #include <common/error.h>
20 #include <common/macros.h>
21 #include <common/runas.h>
22 #include <common/credentials.h>
23 #include <lttng/constant.h>
24
25 #include <assert.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30
31 static
32 int lttng_directory_handle_stat(const struct lttng_directory_handle *handle,
33 const char *path, struct stat *st);
34 static
35 int lttng_directory_handle_mkdir(
36 const struct lttng_directory_handle *handle,
37 const char *path, mode_t mode);
38 static
39 int _run_as_mkdir(const struct lttng_directory_handle *handle, const char *path,
40 mode_t mode, uid_t uid, gid_t gid);
41 static
42 int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle,
43 const char *path, mode_t mode, uid_t uid, gid_t gid);
44 static
45 void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle);
46
47 #ifdef COMPAT_DIRFD
48
49 LTTNG_HIDDEN
50 int lttng_directory_handle_init(struct lttng_directory_handle *new_handle,
51 const char *path)
52 {
53 const struct lttng_directory_handle cwd_handle = {
54 .dirfd = AT_FDCWD,
55 };
56
57 /* Open a handle to the CWD if NULL is passed. */
58 return lttng_directory_handle_init_from_handle(new_handle,
59 path,
60 &cwd_handle);
61 }
62
63 LTTNG_HIDDEN
64 int lttng_directory_handle_init_from_handle(
65 struct lttng_directory_handle *new_handle, const char *path,
66 const struct lttng_directory_handle *handle)
67 {
68 int ret;
69
70 if (!path) {
71 ret = lttng_directory_handle_copy(handle, new_handle);
72 goto end;
73 }
74 if (!*path) {
75 ERR("Failed to initialize directory handle: provided path is an empty string");
76 ret = -1;
77 goto end;
78 }
79 ret = openat(handle->dirfd, path, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
80 if (ret == -1) {
81 PERROR("Failed to initialize directory handle to \"%s\"", path);
82 goto end;
83 }
84 new_handle->dirfd = ret;
85 ret = 0;
86 end:
87 return ret;
88 }
89
90 LTTNG_HIDDEN
91 int lttng_directory_handle_init_from_dirfd(
92 struct lttng_directory_handle *handle, int dirfd)
93 {
94 handle->dirfd = dirfd;
95 return 0;
96 }
97
98 LTTNG_HIDDEN
99 void lttng_directory_handle_fini(struct lttng_directory_handle *handle)
100 {
101 int ret;
102
103 if (handle->dirfd == AT_FDCWD || handle->dirfd == -1) {
104 goto end;
105 }
106 ret = close(handle->dirfd);
107 if (ret == -1) {
108 PERROR("Failed to close directory file descriptor of directory handle");
109 }
110 end:
111 lttng_directory_handle_invalidate(handle);
112 }
113
114 LTTNG_HIDDEN
115 int lttng_directory_handle_copy(const struct lttng_directory_handle *handle,
116 struct lttng_directory_handle *new_copy)
117 {
118 int ret = 0;
119
120 if (handle->dirfd == AT_FDCWD) {
121 new_copy->dirfd = handle->dirfd;
122 } else {
123 new_copy->dirfd = dup(handle->dirfd);
124 if (new_copy->dirfd == -1) {
125 PERROR("Failed to duplicate directory fd of directory handle");
126 ret = -1;
127 }
128 }
129 return ret;
130 }
131
132 static
133 void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle)
134 {
135 handle->dirfd = -1;
136 }
137
138 static
139 int lttng_directory_handle_stat(const struct lttng_directory_handle *handle,
140 const char *path, struct stat *st)
141 {
142 return fstatat(handle->dirfd, path, st, 0);
143 }
144
145 static
146 int lttng_directory_handle_mkdir(
147 const struct lttng_directory_handle *handle,
148 const char *path, mode_t mode)
149 {
150 return mkdirat(handle->dirfd, path, mode);
151 }
152
153 static
154 int _run_as_mkdir(const struct lttng_directory_handle *handle, const char *path,
155 mode_t mode, uid_t uid, gid_t gid)
156 {
157 return run_as_mkdirat(handle->dirfd, path, mode, uid, gid);
158 }
159
160 static
161 int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle,
162 const char *path, mode_t mode, uid_t uid, gid_t gid)
163 {
164 return run_as_mkdirat_recursive(handle->dirfd, path, mode, uid, gid);
165 }
166
167 #else /* COMPAT_DIRFD */
168
169 static
170 int get_full_path(const struct lttng_directory_handle *handle,
171 const char *subdirectory, char *fullpath, size_t size)
172 {
173 int ret;
174
175 subdirectory = subdirectory ? : "";
176 /*
177 * Don't include the base path if subdirectory is absolute.
178 * This is the same behaviour than mkdirat.
179 */
180 ret = snprintf(fullpath, size, "%s%s",
181 *subdirectory != '/' ? handle->base_path : "",
182 subdirectory);
183 if (ret == -1 || ret >= size) {
184 ERR("Failed to format subdirectory from directory handle");
185 ret = -1;
186 }
187 ret = 0;
188 return ret;
189 }
190
191 LTTNG_HIDDEN
192 int lttng_directory_handle_init(struct lttng_directory_handle *handle,
193 const char *path)
194 {
195 int ret;
196 const char *cwd;
197 size_t cwd_len, path_len;
198 char cwd_buf[LTTNG_PATH_MAX] = {};
199 char handle_buf[LTTNG_PATH_MAX] = {};
200 bool add_cwd_slash, add_trailing_slash;
201 const struct lttng_directory_handle cwd_handle = {
202 .base_path = handle_buf,
203 };
204
205 if (path && *path == '/') {
206 /*
207 * Creation of an handle to an absolute path; no need to sample
208 * the cwd.
209 */
210 goto create;
211 }
212 path_len = path ? strlen(path) : 0;
213
214 cwd = getcwd(cwd_buf, sizeof(cwd_buf));
215 if (!cwd) {
216 PERROR("Failed to initialize directory handle, can't get current working directory");
217 ret = -1;
218 goto end;
219 }
220 cwd_len = strlen(cwd);
221 if (cwd_len == 0) {
222 ERR("Failed to initialize directory handle, current working directory path has a length of 0");
223 ret = -1;
224 goto end;
225 }
226
227 add_cwd_slash = cwd[cwd_len - 1] != '/';
228 add_trailing_slash = path && path[path_len - 1] != '/';
229
230 ret = snprintf(handle_buf, sizeof(handle_buf), "%s%s%s%s",
231 cwd,
232 add_cwd_slash ? "/" : "",
233 path ? : "",
234 add_trailing_slash ? "/" : "");
235 if (ret == -1 || ret >= LTTNG_PATH_MAX) {
236 ERR("Failed to initialize directory handle, failed to format directory path");
237 goto end;
238 }
239 create:
240 ret = lttng_directory_handle_init_from_handle(handle, path,
241 &cwd_handle);
242 end:
243 return ret;
244 }
245
246 LTTNG_HIDDEN
247 int lttng_directory_handle_init_from_handle(
248 struct lttng_directory_handle *new_handle, const char *path,
249 const struct lttng_directory_handle *handle)
250 {
251 int ret;
252 size_t path_len, handle_path_len;
253 bool add_trailing_slash;
254 struct stat stat_buf;
255
256 assert(handle && handle->base_path);
257
258 ret = lttng_directory_handle_stat(handle, path, &stat_buf);
259 if (ret == -1) {
260 PERROR("Failed to create directory handle");
261 goto end;
262 } else if (!S_ISDIR(stat_buf.st_mode)) {
263 char full_path[LTTNG_PATH_MAX];
264
265 /* Best effort for logging purposes. */
266 ret = get_full_path(handle, path, full_path,
267 sizeof(full_path));
268 if (ret) {
269 full_path[0] = '\0';
270 }
271
272 ERR("Failed to initialize directory handle to \"%s\": not a directory",
273 full_path);
274 ret = -1;
275 goto end;
276 }
277 if (!path) {
278 ret = lttng_directory_handle_copy(handle, new_handle);
279 goto end;
280 }
281
282 path_len = strlen(path);
283 if (path_len == 0) {
284 ERR("Failed to initialize directory handle: provided path is an empty string");
285 ret = -1;
286 goto end;
287 }
288 if (*path == '/') {
289 new_handle->base_path = strdup(path);
290 ret = new_handle->base_path ? 0 : -1;
291 goto end;
292 }
293
294 add_trailing_slash = path[path_len - 1] != '/';
295
296 handle_path_len = strlen(handle->base_path) + path_len +
297 !!add_trailing_slash;
298 if (handle_path_len >= LTTNG_PATH_MAX) {
299 ERR("Failed to initialize directory handle as the resulting path's length (%zu bytes) exceeds the maximal allowed length (%d bytes)",
300 handle_path_len, LTTNG_PATH_MAX);
301 ret = -1;
302 goto end;
303 }
304 new_handle->base_path = zmalloc(handle_path_len);
305 if (!new_handle->base_path) {
306 PERROR("Failed to initialize directory handle");
307 ret = -1;
308 goto end;
309 }
310
311 ret = sprintf(new_handle->base_path, "%s%s%s",
312 handle->base_path,
313 path,
314 add_trailing_slash ? "/" : "");
315 if (ret == -1 || ret >= handle_path_len) {
316 ERR("Failed to initialize directory handle: path formatting failed");
317 ret = -1;
318 goto end;
319 }
320 end:
321 return ret;
322 }
323
324 LTTNG_HIDDEN
325 int lttng_directory_handle_init_from_dirfd(
326 struct lttng_directory_handle *handle, int dirfd)
327 {
328 assert(dirfd == AT_FDCWD);
329 return lttng_directory_handle_init(handle, NULL);
330 }
331
332 LTTNG_HIDDEN
333 void lttng_directory_handle_fini(struct lttng_directory_handle *handle)
334 {
335 free(handle->base_path);
336 lttng_directory_handle_invalidate(handle);
337 }
338
339 LTTNG_HIDDEN
340 int lttng_directory_handle_copy(const struct lttng_directory_handle *handle,
341 struct lttng_directory_handle *new_copy)
342 {
343 new_copy->base_path = strdup(handle->base_path);
344 return new_copy->base_path ? 0 : -1;
345 }
346
347 static
348 void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle)
349 {
350 handle->base_path = NULL;
351 }
352
353 static
354 int lttng_directory_handle_stat(const struct lttng_directory_handle *handle,
355 const char *subdirectory, struct stat *st)
356 {
357 int ret;
358 char fullpath[LTTNG_PATH_MAX];
359
360 ret = get_full_path(handle, subdirectory, fullpath, sizeof(fullpath));
361 if (ret) {
362 errno = ENOMEM;
363 goto end;
364 }
365
366 ret = stat(fullpath, st);
367 end:
368 return ret;
369 }
370
371 static
372 int lttng_directory_handle_mkdir(const struct lttng_directory_handle *handle,
373 const char *subdirectory, mode_t mode)
374 {
375 int ret;
376 char fullpath[LTTNG_PATH_MAX];
377
378 ret = get_full_path(handle, subdirectory, fullpath, sizeof(fullpath));
379 if (ret) {
380 errno = ENOMEM;
381 goto end;
382 }
383
384 ret = mkdir(fullpath, mode);
385 end:
386 return ret;
387 }
388
389 static
390 int _run_as_mkdir(const struct lttng_directory_handle *handle, const char *path,
391 mode_t mode, uid_t uid, gid_t gid)
392 {
393 int ret;
394 char fullpath[LTTNG_PATH_MAX];
395
396 ret = get_full_path(handle, path, fullpath, sizeof(fullpath));
397 if (ret) {
398 errno = ENOMEM;
399 goto end;
400 }
401
402 ret = run_as_mkdir(fullpath, mode, uid, gid);
403 end:
404 return ret;
405 }
406
407 static
408 int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle,
409 const char *path, mode_t mode, uid_t uid, gid_t gid)
410 {
411 int ret;
412 char fullpath[LTTNG_PATH_MAX];
413
414 ret = get_full_path(handle, path, fullpath, sizeof(fullpath));
415 if (ret) {
416 errno = ENOMEM;
417 goto end;
418 }
419
420 ret = run_as_mkdir_recursive(fullpath, mode, uid, gid);
421 end:
422 return ret;
423 }
424
425 #endif /* COMPAT_DIRFD */
426
427 /*
428 * On some filesystems (e.g. nfs), mkdir will validate access rights before
429 * checking for the existence of the path element. This means that on a setup
430 * where "/home/" is a mounted NFS share, and running as an unpriviledged user,
431 * recursively creating a path of the form "/home/my_user/trace/" will fail with
432 * EACCES on mkdir("/home", ...).
433 *
434 * Checking the path for existence allows us to work around this behaviour.
435 */
436 static
437 int create_directory_check_exists(const struct lttng_directory_handle *handle,
438 const char *path, mode_t mode)
439 {
440 int ret = 0;
441 struct stat st;
442
443 ret = lttng_directory_handle_stat(handle, path, &st);
444 if (ret == 0) {
445 if (S_ISDIR(st.st_mode)) {
446 /* Directory exists, skip. */
447 goto end;
448 } else {
449 /* Exists, but is not a directory. */
450 errno = ENOTDIR;
451 ret = -1;
452 goto end;
453 }
454 }
455
456 /*
457 * Let mkdir handle other errors as the caller expects mkdir
458 * semantics.
459 */
460 ret = lttng_directory_handle_mkdir(handle, path, mode);
461 end:
462 return ret;
463 }
464
465 /* Common implementation. */
466 LTTNG_HIDDEN
467 struct lttng_directory_handle
468 lttng_directory_handle_move(struct lttng_directory_handle *original)
469 {
470 const struct lttng_directory_handle tmp = *original;
471
472 lttng_directory_handle_invalidate(original);
473 return tmp;
474 }
475
476 static
477 int create_directory_recursive(const struct lttng_directory_handle *handle,
478 const char *path, mode_t mode)
479 {
480 char *p, tmp[LTTNG_PATH_MAX];
481 size_t len;
482 int ret;
483
484 assert(path);
485
486 ret = lttng_strncpy(tmp, path, sizeof(tmp));
487 if (ret) {
488 ERR("Failed to create directory: provided path's length (%zu bytes) exceeds the maximal allowed length (%zu bytes)",
489 strlen(path) + 1, sizeof(tmp));
490 goto error;
491 }
492
493 len = strlen(path);
494 if (tmp[len - 1] == '/') {
495 tmp[len - 1] = 0;
496 }
497
498 for (p = tmp + 1; *p; p++) {
499 if (*p == '/') {
500 *p = 0;
501 if (tmp[strlen(tmp) - 1] == '.' &&
502 tmp[strlen(tmp) - 2] == '.' &&
503 tmp[strlen(tmp) - 3] == '/') {
504 ERR("Using '/../' is not permitted in the trace path (%s)",
505 tmp);
506 ret = -1;
507 goto error;
508 }
509 ret = create_directory_check_exists(handle, tmp, mode);
510 if (ret < 0) {
511 if (errno != EACCES) {
512 PERROR("Failed to create directory \"%s\"",
513 path);
514 ret = -errno;
515 goto error;
516 }
517 }
518 *p = '/';
519 }
520 }
521
522 ret = create_directory_check_exists(handle, tmp, mode);
523 if (ret < 0) {
524 PERROR("mkdirat recursive last element");
525 ret = -errno;
526 }
527 error:
528 return ret;
529 }
530
531 LTTNG_HIDDEN
532 int lttng_directory_handle_create_subdirectory_as_user(
533 const struct lttng_directory_handle *handle,
534 const char *subdirectory,
535 mode_t mode, const struct lttng_credentials *creds)
536 {
537 int ret;
538
539 if (!creds) {
540 /* Run as current user. */
541 ret = create_directory_check_exists(handle,
542 subdirectory, mode);
543 } else {
544 ret = _run_as_mkdir(handle, subdirectory,
545 mode, creds->uid, creds->gid);
546 }
547
548 return ret;
549 }
550
551 LTTNG_HIDDEN
552 int lttng_directory_handle_create_subdirectory_recursive_as_user(
553 const struct lttng_directory_handle *handle,
554 const char *subdirectory_path,
555 mode_t mode, const struct lttng_credentials *creds)
556 {
557 int ret;
558
559 if (!creds) {
560 /* Run as current user. */
561 ret = create_directory_recursive(handle,
562 subdirectory_path, mode);
563 } else {
564 ret = _run_as_mkdir_recursive(handle, subdirectory_path,
565 mode, creds->uid, creds->gid);
566 }
567
568 return ret;
569 }
570
571 LTTNG_HIDDEN
572 int lttng_directory_handle_create_subdirectory(
573 const struct lttng_directory_handle *handle,
574 const char *subdirectory,
575 mode_t mode)
576 {
577 return lttng_directory_handle_create_subdirectory_as_user(
578 handle, subdirectory, mode, NULL);
579 }
580
581 LTTNG_HIDDEN
582 int lttng_directory_handle_create_subdirectory_recursive(
583 const struct lttng_directory_handle *handle,
584 const char *subdirectory_path,
585 mode_t mode)
586 {
587 return lttng_directory_handle_create_subdirectory_recursive_as_user(
588 handle, subdirectory_path, mode, NULL);
589 }
This page took 0.041173 seconds and 4 git commands to generate.