Print the location of trace chunk produced at session destruction
[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 /*
32 * This compatibility layer shares a common "base" that is implemented
33 * in terms of an internal API. This file contains two implementations
34 * of the internal API below.
35 */
36 static
37 int lttng_directory_handle_stat(const struct lttng_directory_handle *handle,
38 const char *path, struct stat *st);
39 static
40 int lttng_directory_handle_mkdir(
41 const struct lttng_directory_handle *handle,
42 const char *path, mode_t mode);
43 static
44 int _run_as_mkdir(const struct lttng_directory_handle *handle, const char *path,
45 mode_t mode, uid_t uid, gid_t gid);
46 static
47 int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle,
48 const char *path, mode_t mode, uid_t uid, gid_t gid);
49 static
50 int lttng_directory_handle_open(const struct lttng_directory_handle *handle,
51 const char *filename, int flags, mode_t mode);
52 static
53 int _run_as_open(const struct lttng_directory_handle *handle,
54 const char *filename,
55 int flags, mode_t mode, uid_t uid, gid_t gid);
56 static
57 int lttng_directory_handle_unlink(
58 const struct lttng_directory_handle *handle,
59 const char *filename);
60 static
61 int _run_as_unlink(const struct lttng_directory_handle *handle,
62 const char *filename, uid_t uid, gid_t gid);
63 static
64 void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle);
65
66 #ifdef COMPAT_DIRFD
67
68 LTTNG_HIDDEN
69 int lttng_directory_handle_init(struct lttng_directory_handle *new_handle,
70 const char *path)
71 {
72 const struct lttng_directory_handle cwd_handle = {
73 .dirfd = AT_FDCWD,
74 };
75
76 /* Open a handle to the CWD if NULL is passed. */
77 return lttng_directory_handle_init_from_handle(new_handle,
78 path,
79 &cwd_handle);
80 }
81
82 LTTNG_HIDDEN
83 int lttng_directory_handle_init_from_handle(
84 struct lttng_directory_handle *new_handle, const char *path,
85 const struct lttng_directory_handle *handle)
86 {
87 int ret;
88
89 if (!path) {
90 ret = lttng_directory_handle_copy(handle, new_handle);
91 goto end;
92 }
93 if (!*path) {
94 ERR("Failed to initialize directory handle: provided path is an empty string");
95 ret = -1;
96 goto end;
97 }
98 ret = openat(handle->dirfd, path, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
99 if (ret == -1) {
100 PERROR("Failed to initialize directory handle to \"%s\"", path);
101 goto end;
102 }
103 new_handle->dirfd = ret;
104 ret = 0;
105 end:
106 return ret;
107 }
108
109 LTTNG_HIDDEN
110 int lttng_directory_handle_init_from_dirfd(
111 struct lttng_directory_handle *handle, int dirfd)
112 {
113 handle->dirfd = dirfd;
114 return 0;
115 }
116
117 LTTNG_HIDDEN
118 void lttng_directory_handle_fini(struct lttng_directory_handle *handle)
119 {
120 int ret;
121
122 if (handle->dirfd == AT_FDCWD || handle->dirfd == -1) {
123 goto end;
124 }
125 ret = close(handle->dirfd);
126 if (ret == -1) {
127 PERROR("Failed to close directory file descriptor of directory handle");
128 }
129 end:
130 lttng_directory_handle_invalidate(handle);
131 }
132
133 LTTNG_HIDDEN
134 int lttng_directory_handle_copy(const struct lttng_directory_handle *handle,
135 struct lttng_directory_handle *new_copy)
136 {
137 int ret = 0;
138
139 if (handle->dirfd == AT_FDCWD) {
140 new_copy->dirfd = handle->dirfd;
141 } else {
142 new_copy->dirfd = dup(handle->dirfd);
143 if (new_copy->dirfd == -1) {
144 PERROR("Failed to duplicate directory fd of directory handle");
145 ret = -1;
146 }
147 }
148 return ret;
149 }
150
151 static
152 void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle)
153 {
154 handle->dirfd = -1;
155 }
156
157 static
158 int lttng_directory_handle_stat(const struct lttng_directory_handle *handle,
159 const char *path, struct stat *st)
160 {
161 return fstatat(handle->dirfd, path, st, 0);
162 }
163
164 static
165 int lttng_directory_handle_mkdir(
166 const struct lttng_directory_handle *handle,
167 const char *path, mode_t mode)
168 {
169 return mkdirat(handle->dirfd, path, mode);
170 }
171
172 static
173 int lttng_directory_handle_open(const struct lttng_directory_handle *handle,
174 const char *filename, int flags, mode_t mode)
175 {
176 return openat(handle->dirfd, filename, flags, mode);
177 }
178
179 static
180 int _run_as_open(const struct lttng_directory_handle *handle,
181 const char *filename,
182 int flags, mode_t mode, uid_t uid, gid_t gid)
183 {
184 return run_as_openat(handle->dirfd, filename, flags, mode, uid, gid);
185 }
186
187 static
188 int _run_as_unlink(const struct lttng_directory_handle *handle,
189 const char *filename, uid_t uid, gid_t gid)
190 {
191 return run_as_unlinkat(handle->dirfd, filename, uid, gid);
192 }
193
194 static
195 int lttng_directory_handle_unlink(
196 const struct lttng_directory_handle *handle,
197 const char *filename)
198 {
199 return unlinkat(handle->dirfd, filename, 0);
200 }
201
202 static
203 int _run_as_mkdir(const struct lttng_directory_handle *handle,
204 const char *path, mode_t mode, uid_t uid, gid_t gid)
205 {
206 return run_as_mkdirat(handle->dirfd, path, mode, uid, gid);
207 }
208
209 static
210 int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle,
211 const char *path, mode_t mode, uid_t uid, gid_t gid)
212 {
213 return run_as_mkdirat_recursive(handle->dirfd, path, mode, uid, gid);
214 }
215
216 #else /* COMPAT_DIRFD */
217
218 static
219 int get_full_path(const struct lttng_directory_handle *handle,
220 const char *subdirectory, char *fullpath, size_t size)
221 {
222 int ret;
223
224 subdirectory = subdirectory ? : "";
225 /*
226 * Don't include the base path if subdirectory is absolute.
227 * This is the same behaviour than mkdirat.
228 */
229 ret = snprintf(fullpath, size, "%s%s",
230 *subdirectory != '/' ? handle->base_path : "",
231 subdirectory);
232 if (ret == -1 || ret >= size) {
233 ERR("Failed to format subdirectory from directory handle");
234 ret = -1;
235 }
236 ret = 0;
237 return ret;
238 }
239
240 LTTNG_HIDDEN
241 int lttng_directory_handle_init(struct lttng_directory_handle *handle,
242 const char *path)
243 {
244 int ret;
245 const char *cwd;
246 size_t cwd_len, path_len;
247 char cwd_buf[LTTNG_PATH_MAX] = {};
248 char handle_buf[LTTNG_PATH_MAX] = {};
249 bool add_cwd_slash, add_trailing_slash;
250 const struct lttng_directory_handle cwd_handle = {
251 .base_path = handle_buf,
252 };
253
254 if (path && *path == '/') {
255 /*
256 * Creation of an handle to an absolute path; no need to sample
257 * the cwd.
258 */
259 goto create;
260 }
261 path_len = path ? strlen(path) : 0;
262
263 cwd = getcwd(cwd_buf, sizeof(cwd_buf));
264 if (!cwd) {
265 PERROR("Failed to initialize directory handle, can't get current working directory");
266 ret = -1;
267 goto end;
268 }
269 cwd_len = strlen(cwd);
270 if (cwd_len == 0) {
271 ERR("Failed to initialize directory handle, current working directory path has a length of 0");
272 ret = -1;
273 goto end;
274 }
275
276 add_cwd_slash = cwd[cwd_len - 1] != '/';
277 add_trailing_slash = path && path[path_len - 1] != '/';
278
279 ret = snprintf(handle_buf, sizeof(handle_buf), "%s%s%s%s",
280 cwd,
281 add_cwd_slash ? "/" : "",
282 path ? : "",
283 add_trailing_slash ? "/" : "");
284 if (ret == -1 || ret >= LTTNG_PATH_MAX) {
285 ERR("Failed to initialize directory handle, failed to format directory path");
286 goto end;
287 }
288 create:
289 ret = lttng_directory_handle_init_from_handle(handle, path,
290 &cwd_handle);
291 end:
292 return ret;
293 }
294
295 LTTNG_HIDDEN
296 int lttng_directory_handle_init_from_handle(
297 struct lttng_directory_handle *new_handle, const char *path,
298 const struct lttng_directory_handle *handle)
299 {
300 int ret;
301 size_t path_len, handle_path_len;
302 bool add_trailing_slash;
303 struct stat stat_buf;
304
305 assert(handle && handle->base_path);
306
307 ret = lttng_directory_handle_stat(handle, path, &stat_buf);
308 if (ret == -1) {
309 PERROR("Failed to create directory handle");
310 goto end;
311 } else if (!S_ISDIR(stat_buf.st_mode)) {
312 char full_path[LTTNG_PATH_MAX];
313
314 /* Best effort for logging purposes. */
315 ret = get_full_path(handle, path, full_path,
316 sizeof(full_path));
317 if (ret) {
318 full_path[0] = '\0';
319 }
320
321 ERR("Failed to initialize directory handle to \"%s\": not a directory",
322 full_path);
323 ret = -1;
324 goto end;
325 }
326 if (!path) {
327 ret = lttng_directory_handle_copy(handle, new_handle);
328 goto end;
329 }
330
331 path_len = strlen(path);
332 if (path_len == 0) {
333 ERR("Failed to initialize directory handle: provided path is an empty string");
334 ret = -1;
335 goto end;
336 }
337 if (*path == '/') {
338 new_handle->base_path = strdup(path);
339 ret = new_handle->base_path ? 0 : -1;
340 goto end;
341 }
342
343 add_trailing_slash = path[path_len - 1] != '/';
344
345 handle_path_len = strlen(handle->base_path) + path_len +
346 !!add_trailing_slash;
347 if (handle_path_len >= LTTNG_PATH_MAX) {
348 ERR("Failed to initialize directory handle as the resulting path's length (%zu bytes) exceeds the maximal allowed length (%d bytes)",
349 handle_path_len, LTTNG_PATH_MAX);
350 ret = -1;
351 goto end;
352 }
353 new_handle->base_path = zmalloc(handle_path_len);
354 if (!new_handle->base_path) {
355 PERROR("Failed to initialize directory handle");
356 ret = -1;
357 goto end;
358 }
359
360 ret = sprintf(new_handle->base_path, "%s%s%s",
361 handle->base_path,
362 path,
363 add_trailing_slash ? "/" : "");
364 if (ret == -1 || ret >= handle_path_len) {
365 ERR("Failed to initialize directory handle: path formatting failed");
366 ret = -1;
367 goto end;
368 }
369 end:
370 return ret;
371 }
372
373 LTTNG_HIDDEN
374 int lttng_directory_handle_init_from_dirfd(
375 struct lttng_directory_handle *handle, int dirfd)
376 {
377 assert(dirfd == AT_FDCWD);
378 return lttng_directory_handle_init(handle, NULL);
379 }
380
381 LTTNG_HIDDEN
382 void lttng_directory_handle_fini(struct lttng_directory_handle *handle)
383 {
384 free(handle->base_path);
385 lttng_directory_handle_invalidate(handle);
386 }
387
388 LTTNG_HIDDEN
389 int lttng_directory_handle_copy(const struct lttng_directory_handle *handle,
390 struct lttng_directory_handle *new_copy)
391 {
392 new_copy->base_path = strdup(handle->base_path);
393 return new_copy->base_path ? 0 : -1;
394 }
395
396 static
397 void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle)
398 {
399 handle->base_path = NULL;
400 }
401
402 static
403 int lttng_directory_handle_stat(const struct lttng_directory_handle *handle,
404 const char *subdirectory, struct stat *st)
405 {
406 int ret;
407 char fullpath[LTTNG_PATH_MAX];
408
409 ret = get_full_path(handle, subdirectory, fullpath, sizeof(fullpath));
410 if (ret) {
411 errno = ENOMEM;
412 goto end;
413 }
414
415 ret = stat(fullpath, st);
416 end:
417 return ret;
418 }
419
420 static
421 int lttng_directory_handle_mkdir(const struct lttng_directory_handle *handle,
422 const char *subdirectory, mode_t mode)
423 {
424 int ret;
425 char fullpath[LTTNG_PATH_MAX];
426
427 ret = get_full_path(handle, subdirectory, fullpath, sizeof(fullpath));
428 if (ret) {
429 errno = ENOMEM;
430 goto end;
431 }
432
433 ret = mkdir(fullpath, mode);
434 end:
435 return ret;
436 }
437
438 static
439 int lttng_directory_handle_open(const struct lttng_directory_handle *handle,
440 const char *filename, int flags, mode_t mode)
441 {
442 int ret;
443 char fullpath[LTTNG_PATH_MAX];
444
445 ret = get_full_path(handle, filename, fullpath, sizeof(fullpath));
446 if (ret) {
447 errno = ENOMEM;
448 goto end;
449 }
450
451 ret = open(fullpath, flags, mode);
452 end:
453 return ret;
454 }
455
456 static
457 int lttng_directory_handle_unlink(
458 const struct lttng_directory_handle *handle,
459 const char *filename)
460 {
461 int ret;
462 char fullpath[LTTNG_PATH_MAX];
463
464 ret = get_full_path(handle, filename, fullpath, sizeof(fullpath));
465 if (ret) {
466 errno = ENOMEM;
467 goto end;
468 }
469
470 ret = unlink(fullpath);
471 end:
472 return ret;
473 }
474
475 static
476 int _run_as_mkdir(const struct lttng_directory_handle *handle, const char *path,
477 mode_t mode, uid_t uid, gid_t gid)
478 {
479 int ret;
480 char fullpath[LTTNG_PATH_MAX];
481
482 ret = get_full_path(handle, path, fullpath, sizeof(fullpath));
483 if (ret) {
484 errno = ENOMEM;
485 goto end;
486 }
487
488 ret = run_as_mkdir(fullpath, mode, uid, gid);
489 end:
490 return ret;
491 }
492
493 static
494 int _run_as_open(const struct lttng_directory_handle *handle,
495 const char *filename,
496 int flags, mode_t mode, uid_t uid, gid_t gid)
497 {
498 int ret;
499 char fullpath[LTTNG_PATH_MAX];
500
501 ret = get_full_path(handle, filename, fullpath, sizeof(fullpath));
502 if (ret) {
503 errno = ENOMEM;
504 goto end;
505 }
506
507 ret = run_as_open(fullpath, flags, mode, uid, gid);
508 end:
509 return ret;
510 }
511
512 static
513 int _run_as_unlink(const struct lttng_directory_handle *handle,
514 const char *filename, uid_t uid, gid_t gid)
515 {
516 int ret;
517 char fullpath[LTTNG_PATH_MAX];
518
519 ret = get_full_path(handle, filename, fullpath, sizeof(fullpath));
520 if (ret) {
521 errno = ENOMEM;
522 goto end;
523 }
524
525 ret = run_as_unlink(fullpath, uid, gid);
526 end:
527 return ret;
528 }
529
530 static
531 int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle,
532 const char *path, mode_t mode, uid_t uid, gid_t gid)
533 {
534 int ret;
535 char fullpath[LTTNG_PATH_MAX];
536
537 ret = get_full_path(handle, path, fullpath, sizeof(fullpath));
538 if (ret) {
539 errno = ENOMEM;
540 goto end;
541 }
542
543 ret = run_as_mkdir_recursive(fullpath, mode, uid, gid);
544 end:
545 return ret;
546 }
547
548 #endif /* COMPAT_DIRFD */
549
550 /*
551 * On some filesystems (e.g. nfs), mkdir will validate access rights before
552 * checking for the existence of the path element. This means that on a setup
553 * where "/home/" is a mounted NFS share, and running as an unpriviledged user,
554 * recursively creating a path of the form "/home/my_user/trace/" will fail with
555 * EACCES on mkdir("/home", ...).
556 *
557 * Checking the path for existence allows us to work around this behaviour.
558 */
559 static
560 int create_directory_check_exists(const struct lttng_directory_handle *handle,
561 const char *path, mode_t mode)
562 {
563 int ret = 0;
564 struct stat st;
565
566 ret = lttng_directory_handle_stat(handle, path, &st);
567 if (ret == 0) {
568 if (S_ISDIR(st.st_mode)) {
569 /* Directory exists, skip. */
570 goto end;
571 } else {
572 /* Exists, but is not a directory. */
573 errno = ENOTDIR;
574 ret = -1;
575 goto end;
576 }
577 }
578
579 /*
580 * Let mkdir handle other errors as the caller expects mkdir
581 * semantics.
582 */
583 ret = lttng_directory_handle_mkdir(handle, path, mode);
584 end:
585 return ret;
586 }
587
588 /* Common implementation. */
589 LTTNG_HIDDEN
590 struct lttng_directory_handle
591 lttng_directory_handle_move(struct lttng_directory_handle *original)
592 {
593 const struct lttng_directory_handle tmp = *original;
594
595 lttng_directory_handle_invalidate(original);
596 return tmp;
597 }
598
599 static
600 int create_directory_recursive(const struct lttng_directory_handle *handle,
601 const char *path, mode_t mode)
602 {
603 char *p, tmp[LTTNG_PATH_MAX];
604 size_t len;
605 int ret;
606
607 assert(path);
608
609 ret = lttng_strncpy(tmp, path, sizeof(tmp));
610 if (ret) {
611 ERR("Failed to create directory: provided path's length (%zu bytes) exceeds the maximal allowed length (%zu bytes)",
612 strlen(path) + 1, sizeof(tmp));
613 goto error;
614 }
615
616 len = strlen(path);
617 if (tmp[len - 1] == '/') {
618 tmp[len - 1] = 0;
619 }
620
621 for (p = tmp + 1; *p; p++) {
622 if (*p == '/') {
623 *p = 0;
624 if (tmp[strlen(tmp) - 1] == '.' &&
625 tmp[strlen(tmp) - 2] == '.' &&
626 tmp[strlen(tmp) - 3] == '/') {
627 ERR("Using '/../' is not permitted in the trace path (%s)",
628 tmp);
629 ret = -1;
630 goto error;
631 }
632 ret = create_directory_check_exists(handle, tmp, mode);
633 if (ret < 0) {
634 if (errno != EACCES) {
635 PERROR("Failed to create directory \"%s\"",
636 path);
637 ret = -errno;
638 goto error;
639 }
640 }
641 *p = '/';
642 }
643 }
644
645 ret = create_directory_check_exists(handle, tmp, mode);
646 if (ret < 0) {
647 PERROR("mkdirat recursive last element");
648 ret = -errno;
649 }
650 error:
651 return ret;
652 }
653
654 LTTNG_HIDDEN
655 int lttng_directory_handle_create_subdirectory_as_user(
656 const struct lttng_directory_handle *handle,
657 const char *subdirectory,
658 mode_t mode, const struct lttng_credentials *creds)
659 {
660 int ret;
661
662 if (!creds) {
663 /* Run as current user. */
664 ret = create_directory_check_exists(handle,
665 subdirectory, mode);
666 } else {
667 ret = _run_as_mkdir(handle, subdirectory,
668 mode, creds->uid, creds->gid);
669 }
670
671 return ret;
672 }
673
674 LTTNG_HIDDEN
675 int lttng_directory_handle_create_subdirectory_recursive_as_user(
676 const struct lttng_directory_handle *handle,
677 const char *subdirectory_path,
678 mode_t mode, const struct lttng_credentials *creds)
679 {
680 int ret;
681
682 if (!creds) {
683 /* Run as current user. */
684 ret = create_directory_recursive(handle,
685 subdirectory_path, mode);
686 } else {
687 ret = _run_as_mkdir_recursive(handle, subdirectory_path,
688 mode, creds->uid, creds->gid);
689 }
690
691 return ret;
692 }
693
694 LTTNG_HIDDEN
695 int lttng_directory_handle_create_subdirectory(
696 const struct lttng_directory_handle *handle,
697 const char *subdirectory,
698 mode_t mode)
699 {
700 return lttng_directory_handle_create_subdirectory_as_user(
701 handle, subdirectory, mode, NULL);
702 }
703
704 LTTNG_HIDDEN
705 int lttng_directory_handle_create_subdirectory_recursive(
706 const struct lttng_directory_handle *handle,
707 const char *subdirectory_path,
708 mode_t mode)
709 {
710 return lttng_directory_handle_create_subdirectory_recursive_as_user(
711 handle, subdirectory_path, mode, NULL);
712 }
713
714 LTTNG_HIDDEN
715 int lttng_directory_handle_open_file_as_user(
716 const struct lttng_directory_handle *handle,
717 const char *filename,
718 int flags, mode_t mode,
719 const struct lttng_credentials *creds)
720 {
721 int ret;
722
723 if (!creds) {
724 /* Run as current user. */
725 ret = lttng_directory_handle_open(handle, filename, flags,
726 mode);
727 } else {
728 ret = _run_as_open(handle, filename, flags, mode,
729 creds->uid, creds->gid);
730 }
731 return ret;
732 }
733
734 LTTNG_HIDDEN
735 int lttng_directory_handle_open_file(
736 const struct lttng_directory_handle *handle,
737 const char *filename,
738 int flags, mode_t mode)
739 {
740 return lttng_directory_handle_open_file_as_user(handle, filename, flags,
741 mode, NULL);
742 }
743
744 LTTNG_HIDDEN
745 int lttng_directory_handle_unlink_file_as_user(
746 const struct lttng_directory_handle *handle,
747 const char *filename,
748 const struct lttng_credentials *creds)
749 {
750 int ret;
751
752 if (!creds) {
753 /* Run as current user. */
754 ret = lttng_directory_handle_unlink(handle, filename);
755 } else {
756 ret = _run_as_unlink(handle, filename, creds->uid, creds->gid);
757 }
758 return ret;
759 }
760
761 LTTNG_HIDDEN
762 int lttng_directory_handle_unlink_file(
763 const struct lttng_directory_handle *handle,
764 const char *filename)
765 {
766 return lttng_directory_handle_unlink_file_as_user(handle,
767 filename, NULL);
768 }
This page took 0.044172 seconds and 5 git commands to generate.