Move to kernel style SPDX license identifiers
[lttng-tools.git] / src / common / compat / directory-handle.c
1 /*
2 * Copyright (C) 2019 Jérémie Galarneau <jeremie.galarneau@efficios.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 */
7
8 #include <common/compat/directory-handle.h>
9 #include <common/error.h>
10 #include <common/macros.h>
11 #include <common/runas.h>
12 #include <common/credentials.h>
13 #include <lttng/constant.h>
14 #include <common/dynamic-array.h>
15
16 #include <assert.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <fcntl.h>
20 #include <unistd.h>
21 #include <dirent.h>
22
23 /*
24 * This compatibility layer shares a common "base" that is implemented
25 * in terms of an internal API. This file contains two implementations
26 * of the internal API below.
27 */
28 static
29 int lttng_directory_handle_mkdir(
30 const struct lttng_directory_handle *handle,
31 const char *path, mode_t mode);
32 static
33 int _run_as_mkdir(const struct lttng_directory_handle *handle, const char *path,
34 mode_t mode, uid_t uid, gid_t gid);
35 static
36 int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle,
37 const char *path, mode_t mode, uid_t uid, gid_t gid);
38 static
39 int lttng_directory_handle_open(const struct lttng_directory_handle *handle,
40 const char *filename, int flags, mode_t mode);
41 static
42 int _run_as_open(const struct lttng_directory_handle *handle,
43 const char *filename,
44 int flags, mode_t mode, uid_t uid, gid_t gid);
45 static
46 int lttng_directory_handle_unlink(
47 const struct lttng_directory_handle *handle,
48 const char *filename);
49 static
50 int _run_as_unlink(const struct lttng_directory_handle *handle,
51 const char *filename, uid_t uid, gid_t gid);
52 static
53 int _lttng_directory_handle_rename(
54 const struct lttng_directory_handle *old_handle,
55 const char *old_name,
56 const struct lttng_directory_handle *new_handle,
57 const char *new_name);
58 static
59 int _run_as_rename(const struct lttng_directory_handle *old_handle,
60 const char *old_name,
61 const struct lttng_directory_handle *new_handle,
62 const char *new_name, uid_t uid, gid_t gid);
63 static
64 DIR *lttng_directory_handle_opendir(const struct lttng_directory_handle *handle,
65 const char *path);
66 static
67 int lttng_directory_handle_rmdir(
68 const struct lttng_directory_handle *handle, const char *name);
69 static
70 int _run_as_rmdir(const struct lttng_directory_handle *handle,
71 const char *name, uid_t uid, gid_t gid);
72 static
73 int _run_as_rmdir_recursive(
74 const struct lttng_directory_handle *handle, const char *name,
75 uid_t uid, gid_t gid, int flags);
76 static
77 void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle);
78 static
79 void lttng_directory_handle_release(struct urcu_ref *ref);
80
81 #ifdef COMPAT_DIRFD
82
83 /*
84 * Special inode number reserved to represent the "current working directory".
85 * ino_t is spec'ed as being an unsigned integral type.
86 */
87 #define RESERVED_AT_FDCWD_INO \
88 ({ \
89 uint64_t reserved_val; \
90 switch (sizeof(ino_t)) { \
91 case 4: \
92 reserved_val = UINT32_MAX; \
93 break; \
94 case 8: \
95 reserved_val = UINT64_MAX; \
96 break; \
97 default: \
98 abort(); \
99 } \
100 (ino_t) reserved_val; \
101 })
102
103 LTTNG_HIDDEN
104 struct lttng_directory_handle *lttng_directory_handle_create(const char *path)
105 {
106 const struct lttng_directory_handle cwd_handle = {
107 .dirfd = AT_FDCWD,
108 };
109
110 /* Open a handle to the CWD if NULL is passed. */
111 return lttng_directory_handle_create_from_handle(path, &cwd_handle);
112 }
113
114 LTTNG_HIDDEN
115 struct lttng_directory_handle *lttng_directory_handle_create_from_handle(
116 const char *path,
117 const struct lttng_directory_handle *ref_handle)
118 {
119 int dirfd;
120 struct lttng_directory_handle *handle = NULL;
121
122 if (!path) {
123 handle = lttng_directory_handle_copy(ref_handle);
124 goto end;
125 }
126 if (!*path) {
127 ERR("Failed to initialize directory handle: provided path is an empty string");
128 goto end;
129 }
130
131 dirfd = openat(ref_handle->dirfd, path, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
132 if (dirfd == -1) {
133 PERROR("Failed to initialize directory handle to \"%s\"", path);
134 goto end;
135 }
136
137 handle = lttng_directory_handle_create_from_dirfd(dirfd);
138 if (!handle) {
139 goto error_close;
140 }
141 end:
142 return handle;
143 error_close:
144 if (close(dirfd)) {
145 PERROR("Failed to close directory file descriptor");
146 }
147 return NULL;
148 }
149
150 LTTNG_HIDDEN
151 struct lttng_directory_handle *lttng_directory_handle_create_from_dirfd(
152 int dirfd)
153 {
154 int ret;
155 struct lttng_directory_handle *handle = zmalloc(sizeof(*handle));
156 struct stat stat_buf;
157
158 if (!handle) {
159 goto end;
160 }
161
162 if (dirfd != AT_FDCWD) {
163 ret = fstat(dirfd, &stat_buf);
164 if (ret) {
165 PERROR("Failed to fstat directory file descriptor %i", dirfd);
166 lttng_directory_handle_release(&handle->ref);
167 }
168 } else {
169 handle->directory_inode = RESERVED_AT_FDCWD_INO;
170 }
171 handle->dirfd = dirfd;
172 urcu_ref_init(&handle->ref);
173 end:
174 return handle;
175 }
176
177 static
178 void lttng_directory_handle_release(struct urcu_ref *ref)
179 {
180 int ret;
181 struct lttng_directory_handle *handle =
182 container_of(ref, struct lttng_directory_handle, ref);
183
184 if (handle->dirfd == AT_FDCWD || handle->dirfd == -1) {
185 goto end;
186 }
187 ret = close(handle->dirfd);
188 if (ret == -1) {
189 PERROR("Failed to close directory file descriptor of directory handle");
190 }
191 end:
192 lttng_directory_handle_invalidate(handle);
193 free(handle);
194 }
195
196 LTTNG_HIDDEN
197 struct lttng_directory_handle *lttng_directory_handle_copy(
198 const struct lttng_directory_handle *handle)
199 {
200 struct lttng_directory_handle *new_handle = NULL;
201
202 if (handle->dirfd == AT_FDCWD) {
203 new_handle = lttng_directory_handle_create_from_dirfd(AT_FDCWD);
204 } else {
205 const int new_dirfd = dup(handle->dirfd);
206
207 if (new_dirfd == -1) {
208 PERROR("Failed to duplicate directory file descriptor of directory handle");
209 goto end;
210 }
211 new_handle = lttng_directory_handle_create_from_dirfd(
212 new_dirfd);
213 if (!new_handle && close(new_dirfd)) {
214 PERROR("Failed to close directory file descriptor of directory handle");
215 }
216 }
217 end:
218 return new_handle;
219 }
220
221 LTTNG_HIDDEN
222 bool lttng_directory_handle_equals(const struct lttng_directory_handle *lhs,
223 const struct lttng_directory_handle *rhs)
224 {
225 return lhs->directory_inode == rhs->directory_inode;
226 }
227
228 static
229 void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle)
230 {
231 handle->dirfd = -1;
232 }
233
234 LTTNG_HIDDEN
235 int lttng_directory_handle_stat(const struct lttng_directory_handle *handle,
236 const char *path, struct stat *st)
237 {
238 return fstatat(handle->dirfd, path, st, 0);
239 }
240
241 LTTNG_HIDDEN
242 bool lttng_directory_handle_uses_fd(
243 const struct lttng_directory_handle *handle)
244 {
245 return handle->dirfd != AT_FDCWD;
246 }
247
248 static
249 int lttng_directory_handle_mkdir(
250 const struct lttng_directory_handle *handle,
251 const char *path, mode_t mode)
252 {
253 return mkdirat(handle->dirfd, path, mode);
254 }
255
256 static
257 int lttng_directory_handle_open(const struct lttng_directory_handle *handle,
258 const char *filename, int flags, mode_t mode)
259 {
260 return openat(handle->dirfd, filename, flags, mode);
261 }
262
263 static
264 int _run_as_open(const struct lttng_directory_handle *handle,
265 const char *filename,
266 int flags, mode_t mode, uid_t uid, gid_t gid)
267 {
268 return run_as_openat(handle->dirfd, filename, flags, mode, uid, gid);
269 }
270
271 static
272 int _run_as_unlink(const struct lttng_directory_handle *handle,
273 const char *filename, uid_t uid, gid_t gid)
274 {
275 return run_as_unlinkat(handle->dirfd, filename, uid, gid);
276 }
277
278 static
279 int lttng_directory_handle_unlink(
280 const struct lttng_directory_handle *handle,
281 const char *filename)
282 {
283 return unlinkat(handle->dirfd, filename, 0);
284 }
285
286 static
287 int _run_as_mkdir(const struct lttng_directory_handle *handle,
288 const char *path, mode_t mode, uid_t uid, gid_t gid)
289 {
290 return run_as_mkdirat(handle->dirfd, path, mode, uid, gid);
291 }
292
293 static
294 int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle,
295 const char *path, mode_t mode, uid_t uid, gid_t gid)
296 {
297 return run_as_mkdirat_recursive(handle->dirfd, path, mode, uid, gid);
298 }
299
300 static
301 int _lttng_directory_handle_rename(
302 const struct lttng_directory_handle *old_handle,
303 const char *old_name,
304 const struct lttng_directory_handle *new_handle,
305 const char *new_name)
306 {
307 return renameat(old_handle->dirfd, old_name,
308 new_handle->dirfd, new_name);
309 }
310
311 static
312 int _run_as_rename(const struct lttng_directory_handle *old_handle,
313 const char *old_name,
314 const struct lttng_directory_handle *new_handle,
315 const char *new_name, uid_t uid, gid_t gid)
316 {
317 return run_as_renameat(old_handle->dirfd, old_name, new_handle->dirfd,
318 new_name, uid, gid);
319 }
320
321 static
322 DIR *lttng_directory_handle_opendir(const struct lttng_directory_handle *handle,
323 const char *path)
324 {
325 DIR *dir_stream = NULL;
326 int fd = openat(handle->dirfd, path, O_RDONLY);
327
328 if (fd < 0) {
329 goto end;
330 }
331
332 dir_stream = fdopendir(fd);
333 if (!dir_stream) {
334 int ret;
335
336 PERROR("Failed to open directory stream");
337 ret = close(fd);
338 if (ret) {
339 PERROR("Failed to close file descriptor to %s", path);
340 }
341 goto end;
342 }
343
344 end:
345 return dir_stream;
346 }
347
348 static
349 int lttng_directory_handle_rmdir(
350 const struct lttng_directory_handle *handle, const char *name)
351 {
352 return unlinkat(handle->dirfd, name, AT_REMOVEDIR);
353 }
354
355 static
356 int _run_as_rmdir(const struct lttng_directory_handle *handle,
357 const char *name, uid_t uid, gid_t gid)
358 {
359 return run_as_rmdirat(handle->dirfd, name, uid, gid);
360 }
361
362 static
363 int _run_as_rmdir_recursive(
364 const struct lttng_directory_handle *handle, const char *name,
365 uid_t uid, gid_t gid, int flags)
366 {
367 return run_as_rmdirat_recursive(handle->dirfd, name, uid, gid, flags);
368 }
369
370 #else /* COMPAT_DIRFD */
371
372 static
373 int get_full_path(const struct lttng_directory_handle *handle,
374 const char *subdirectory, char *fullpath, size_t size)
375 {
376 int ret;
377 const bool subdirectory_is_absolute =
378 subdirectory && *subdirectory == '/';
379 const char * const base = subdirectory_is_absolute ?
380 subdirectory : handle->base_path;
381 const char * const end = subdirectory && !subdirectory_is_absolute ?
382 subdirectory : NULL;
383 const size_t base_len = strlen(base);
384 const size_t end_len = end ? strlen(end) : 0;
385 const bool add_separator_slash = end && base[base_len - 1] != '/';
386 const bool add_trailing_slash = end && end[end_len - 1] != '/';
387
388 ret = snprintf(fullpath, size, "%s%s%s%s",
389 base,
390 add_separator_slash ? "/" : "",
391 end ? end : "",
392 add_trailing_slash ? "/" : "");
393 if (ret == -1 || ret >= size) {
394 ERR("Failed to format subdirectory from directory handle");
395 ret = -1;
396 goto end;
397 }
398 ret = 0;
399 end:
400 return ret;
401 }
402
403 static
404 struct lttng_directory_handle *_lttng_directory_handle_create(char *path)
405 {
406 struct lttng_directory_handle *handle = zmalloc(sizeof(*handle));
407
408 if (!handle) {
409 goto end;
410 }
411 urcu_ref_init(&handle->ref);
412 handle->base_path = path;
413 end:
414 return handle;
415 }
416
417 LTTNG_HIDDEN
418 struct lttng_directory_handle *lttng_directory_handle_create(
419 const char *path)
420 {
421 int ret;
422 const char *cwd = "";
423 size_t cwd_len, path_len;
424 char cwd_buf[LTTNG_PATH_MAX] = {};
425 char handle_buf[LTTNG_PATH_MAX] = {};
426 struct lttng_directory_handle *new_handle = NULL;
427 bool add_cwd_slash = false, add_trailing_slash = false;
428 const struct lttng_directory_handle cwd_handle = {
429 .base_path = handle_buf,
430 };
431
432 path_len = path ? strlen(path) : 0;
433 add_trailing_slash = path && path[path_len - 1] != '/';
434 if (!path || (path && *path != '/')) {
435 cwd = getcwd(cwd_buf, sizeof(cwd_buf));
436 if (!cwd) {
437 PERROR("Failed to initialize directory handle, can't get current working directory");
438 ret = -1;
439 goto end;
440 }
441 cwd_len = strlen(cwd);
442 if (cwd_len == 0) {
443 ERR("Failed to initialize directory handle, current working directory path has a length of 0");
444 ret = -1;
445 goto end;
446 }
447 add_cwd_slash = cwd[cwd_len - 1] != '/';
448 }
449
450 ret = snprintf(handle_buf, sizeof(handle_buf), "%s%s%s%s",
451 cwd,
452 add_cwd_slash ? "/" : "",
453 path ? : "",
454 add_trailing_slash ? "/" : "");
455 if (ret == -1 || ret >= LTTNG_PATH_MAX) {
456 ERR("Failed to initialize directory handle, failed to format directory path");
457 goto end;
458 }
459
460 new_handle = lttng_directory_handle_create_from_handle(path, &cwd_handle);
461 end:
462 return new_handle;
463 }
464
465 LTTNG_HIDDEN
466 struct lttng_directory_handle *lttng_directory_handle_create_from_handle(
467 const char *path,
468 const struct lttng_directory_handle *ref_handle)
469 {
470 int ret;
471 size_t path_len, handle_path_len;
472 bool add_trailing_slash;
473 struct stat stat_buf;
474 struct lttng_directory_handle *new_handle = NULL;
475 char *new_path = NULL;
476
477 assert(ref_handle && ref_handle->base_path);
478
479 ret = lttng_directory_handle_stat(ref_handle, path, &stat_buf);
480 if (ret == -1) {
481 PERROR("Failed to create directory handle");
482 goto end;
483 } else if (!S_ISDIR(stat_buf.st_mode)) {
484 char full_path[LTTNG_PATH_MAX];
485
486 /* Best effort for logging purposes. */
487 ret = get_full_path(ref_handle, path, full_path,
488 sizeof(full_path));
489 if (ret) {
490 full_path[0] = '\0';
491 }
492
493 ERR("Failed to initialize directory handle to \"%s\": not a directory",
494 full_path);
495 goto end;
496 }
497 if (!path) {
498 new_handle = lttng_directory_handle_copy(ref_handle);
499 goto end;
500 }
501
502 path_len = strlen(path);
503 if (path_len == 0) {
504 ERR("Failed to initialize directory handle: provided path is an empty string");
505 ret = -1;
506 goto end;
507 }
508 if (*path == '/') {
509 new_path = strdup(path);
510 if (!new_path) {
511 goto end;
512 }
513 /* Takes ownership of new_path. */
514 new_handle = _lttng_directory_handle_create(new_path);
515 new_path = NULL;
516 goto end;
517 }
518
519 add_trailing_slash = path[path_len - 1] != '/';
520
521 handle_path_len = strlen(ref_handle->base_path) + path_len +
522 !!add_trailing_slash;
523 if (handle_path_len >= LTTNG_PATH_MAX) {
524 ERR("Failed to initialize directory handle as the resulting path's length (%zu bytes) exceeds the maximal allowed length (%d bytes)",
525 handle_path_len, LTTNG_PATH_MAX);
526 goto end;
527 }
528 new_path = zmalloc(handle_path_len);
529 if (!new_path) {
530 PERROR("Failed to initialize directory handle");
531 goto end;
532 }
533
534 ret = sprintf(new_handle->base_path, "%s%s%s",
535 ref_handle->base_path,
536 path,
537 add_trailing_slash ? "/" : "");
538 if (ret == -1 || ret >= handle_path_len) {
539 ERR("Failed to initialize directory handle: path formatting failed");
540 goto end;
541 }
542 new_handle = _lttng_directory_handle_create(new_path);
543 new_path = NULL;
544 end:
545 free(new_path);
546 return new_handle;
547 }
548
549 LTTNG_HIDDEN
550 struct lttng_directory_handle *lttng_directory_handle_create_from_dirfd(
551 int dirfd)
552 {
553 assert(dirfd == AT_FDCWD);
554 return lttng_directory_handle_create(NULL);
555 }
556
557 static
558 void lttng_directory_handle_release(struct urcu_ref *ref)
559 {
560 struct lttng_directory_handle *handle =
561 container_of(ref, struct lttng_directory_handle, ref);
562
563 free(handle->base_path);
564 lttng_directory_handle_invalidate(handle);
565 free(handle);
566 }
567
568 LTTNG_HIDDEN
569 struct lttng_directory_handle *lttng_directory_handle_copy(
570 const struct lttng_directory_handle *handle)
571 {
572 struct lttng_directory_handle *new_handle = NULL;
573 char *new_path = NULL;
574
575 if (handle->base_path) {
576 new_path = strdup(handle->base_path);
577 if (!new_path) {
578 goto end;
579 }
580 }
581 new_handle = _lttng_directory_handle_create(new_path);
582 end:
583 return new_handle;
584 }
585
586 LTTNG_HIDDEN
587 bool lttng_directory_handle_equals(const struct lttng_directory_handle *lhs,
588 const struct lttng_directory_handle *rhs)
589 {
590 return strcmp(lhs->path, rhs->path) == 0;
591 }
592
593 static
594 void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle)
595 {
596 handle->base_path = NULL;
597 }
598
599 LTTNG_HIDDEN
600 int lttng_directory_handle_stat(const struct lttng_directory_handle *handle,
601 const char *subdirectory, struct stat *st)
602 {
603 int ret;
604 char fullpath[LTTNG_PATH_MAX];
605
606 ret = get_full_path(handle, subdirectory, fullpath, sizeof(fullpath));
607 if (ret) {
608 errno = ENOMEM;
609 goto end;
610 }
611
612 ret = stat(fullpath, st);
613 end:
614 return ret;
615 }
616
617 LTTNG_HIDDEN
618 bool lttng_directory_handle_uses_fd(
619 const struct lttng_directory_handle *handle)
620 {
621 return false;
622 }
623
624 static
625 int lttng_directory_handle_mkdir(const struct lttng_directory_handle *handle,
626 const char *subdirectory, mode_t mode)
627 {
628 int ret;
629 char fullpath[LTTNG_PATH_MAX];
630
631 ret = get_full_path(handle, subdirectory, fullpath, sizeof(fullpath));
632 if (ret) {
633 errno = ENOMEM;
634 goto end;
635 }
636
637 ret = mkdir(fullpath, mode);
638 end:
639 return ret;
640 }
641
642 static
643 int lttng_directory_handle_open(const struct lttng_directory_handle *handle,
644 const char *filename, int flags, mode_t mode)
645 {
646 int ret;
647 char fullpath[LTTNG_PATH_MAX];
648
649 ret = get_full_path(handle, filename, fullpath, sizeof(fullpath));
650 if (ret) {
651 errno = ENOMEM;
652 goto end;
653 }
654
655 ret = open(fullpath, flags, mode);
656 end:
657 return ret;
658 }
659
660 static
661 int lttng_directory_handle_unlink(
662 const struct lttng_directory_handle *handle,
663 const char *filename)
664 {
665 int ret;
666 char fullpath[LTTNG_PATH_MAX];
667
668 ret = get_full_path(handle, filename, fullpath, sizeof(fullpath));
669 if (ret) {
670 errno = ENOMEM;
671 goto end;
672 }
673
674 ret = unlink(fullpath);
675 end:
676 return ret;
677 }
678
679 static
680 int _run_as_mkdir(const struct lttng_directory_handle *handle, const char *path,
681 mode_t mode, uid_t uid, gid_t gid)
682 {
683 int ret;
684 char fullpath[LTTNG_PATH_MAX];
685
686 ret = get_full_path(handle, path, fullpath, sizeof(fullpath));
687 if (ret) {
688 errno = ENOMEM;
689 goto end;
690 }
691
692 ret = run_as_mkdir(fullpath, mode, uid, gid);
693 end:
694 return ret;
695 }
696
697 static
698 int _run_as_open(const struct lttng_directory_handle *handle,
699 const char *filename,
700 int flags, mode_t mode, uid_t uid, gid_t gid)
701 {
702 int ret;
703 char fullpath[LTTNG_PATH_MAX];
704
705 ret = get_full_path(handle, filename, fullpath, sizeof(fullpath));
706 if (ret) {
707 errno = ENOMEM;
708 goto end;
709 }
710
711 ret = run_as_open(fullpath, flags, mode, uid, gid);
712 end:
713 return ret;
714 }
715
716 static
717 int _run_as_unlink(const struct lttng_directory_handle *handle,
718 const char *filename, uid_t uid, gid_t gid)
719 {
720 int ret;
721 char fullpath[LTTNG_PATH_MAX];
722
723 ret = get_full_path(handle, filename, fullpath, sizeof(fullpath));
724 if (ret) {
725 errno = ENOMEM;
726 goto end;
727 }
728
729 ret = run_as_unlink(fullpath, uid, gid);
730 end:
731 return ret;
732 }
733
734 static
735 int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle,
736 const char *path, mode_t mode, uid_t uid, gid_t gid)
737 {
738 int ret;
739 char fullpath[LTTNG_PATH_MAX];
740
741 ret = get_full_path(handle, path, fullpath, sizeof(fullpath));
742 if (ret) {
743 errno = ENOMEM;
744 goto end;
745 }
746
747 ret = run_as_mkdir_recursive(fullpath, mode, uid, gid);
748 end:
749 return ret;
750 }
751
752 static
753 int _lttng_directory_handle_rename(
754 const struct lttng_directory_handle *old_handle,
755 const char *old_name,
756 const struct lttng_directory_handle *new_handle,
757 const char *new_name)
758 {
759 int ret;
760 char old_fullpath[LTTNG_PATH_MAX];
761 char new_fullpath[LTTNG_PATH_MAX];
762
763 ret = get_full_path(old_handle, old_name, old_fullpath,
764 sizeof(old_fullpath));
765 if (ret) {
766 errno = ENOMEM;
767 goto end;
768 }
769 ret = get_full_path(new_handle, new_name, new_fullpath,
770 sizeof(new_fullpath));
771 if (ret) {
772 errno = ENOMEM;
773 goto end;
774 }
775
776 ret = rename(old_fullpath, new_fullpath);
777 end:
778 return ret;
779 }
780
781 static
782 int _run_as_rename(const struct lttng_directory_handle *old_handle,
783 const char *old_name,
784 const struct lttng_directory_handle *new_handle,
785 const char *new_name, uid_t uid, gid_t gid)
786 {
787 int ret;
788 char old_fullpath[LTTNG_PATH_MAX];
789 char new_fullpath[LTTNG_PATH_MAX];
790
791 ret = get_full_path(old_handle, old_name, old_fullpath,
792 sizeof(old_fullpath));
793 if (ret) {
794 errno = ENOMEM;
795 goto end;
796 }
797 ret = get_full_path(new_handle, new_name, new_fullpath,
798 sizeof(new_fullpath));
799 if (ret) {
800 errno = ENOMEM;
801 goto end;
802 }
803
804 ret = run_as_rename(old_fullpath, new_fullpath, uid, gid);
805 end:
806 return ret;
807 }
808
809 static
810 DIR *lttng_directory_handle_opendir(const struct lttng_directory_handle *handle,
811 const char *path)
812 {
813 int ret;
814 DIR *dir_stream = NULL;
815 char fullpath[LTTNG_PATH_MAX];
816
817 ret = get_full_path(handle, path, fullpath, sizeof(fullpath));
818 if (ret) {
819 errno = ENOMEM;
820 goto end;
821 }
822
823 dir_stream = opendir(fullpath);
824 end:
825 return dir_stream;
826 }
827
828 static
829 int lttng_directory_handle_rmdir(
830 const struct lttng_directory_handle *handle, const char *name)
831 {
832 int ret;
833 char fullpath[LTTNG_PATH_MAX];
834
835 ret = get_full_path(handle, name, fullpath, sizeof(fullpath));
836 if (ret) {
837 errno = ENOMEM;
838 goto end;
839 }
840
841 ret = rmdir(fullpath);
842 end:
843 return ret;
844 }
845
846 static
847 int _run_as_rmdir(const struct lttng_directory_handle *handle,
848 const char *name, uid_t uid, gid_t gid)
849 {
850 int ret;
851 char fullpath[LTTNG_PATH_MAX];
852
853 ret = get_full_path(handle, name, fullpath, sizeof(fullpath));
854 if (ret) {
855 errno = ENOMEM;
856 goto end;
857 }
858
859 ret = run_as_rmdir(fullpath, uid, gid);
860 end:
861 return ret;
862 }
863
864 static
865 int _run_as_rmdir_recursive(
866 const struct lttng_directory_handle *handle, const char *name,
867 uid_t uid, gid_t gid, int flags)
868 {
869 int ret;
870 char fullpath[LTTNG_PATH_MAX];
871
872 ret = get_full_path(handle, name, fullpath, sizeof(fullpath));
873 if (ret) {
874 errno = ENOMEM;
875 goto end;
876 }
877
878 ret = run_as_rmdir_recursive(fullpath, uid, gid, flags);
879 end:
880 return ret;
881 }
882
883 #endif /* COMPAT_DIRFD */
884
885 /* Common implementation. */
886
887 /*
888 * On some filesystems (e.g. nfs), mkdir will validate access rights before
889 * checking for the existence of the path element. This means that on a setup
890 * where "/home/" is a mounted NFS share, and running as an unpriviledged user,
891 * recursively creating a path of the form "/home/my_user/trace/" will fail with
892 * EACCES on mkdir("/home", ...).
893 *
894 * Checking the path for existence allows us to work around this behaviour.
895 */
896 static
897 int create_directory_check_exists(const struct lttng_directory_handle *handle,
898 const char *path, mode_t mode)
899 {
900 int ret = 0;
901 struct stat st;
902
903 ret = lttng_directory_handle_stat(handle, path, &st);
904 if (ret == 0) {
905 if (S_ISDIR(st.st_mode)) {
906 /* Directory exists, skip. */
907 goto end;
908 } else {
909 /* Exists, but is not a directory. */
910 errno = ENOTDIR;
911 ret = -1;
912 goto end;
913 }
914 } else if (errno != ENOENT) {
915 goto end;
916 }
917
918 /*
919 * Let mkdir handle other errors as the caller expects mkdir
920 * semantics.
921 */
922 ret = lttng_directory_handle_mkdir(handle, path, mode);
923 end:
924 return ret;
925 }
926
927 static
928 int create_directory_recursive(const struct lttng_directory_handle *handle,
929 const char *path, mode_t mode)
930 {
931 char *p, tmp[LTTNG_PATH_MAX];
932 size_t len;
933 int ret;
934
935 assert(path);
936
937 ret = lttng_strncpy(tmp, path, sizeof(tmp));
938 if (ret) {
939 ERR("Failed to create directory: provided path's length (%zu bytes) exceeds the maximal allowed length (%zu bytes)",
940 strlen(path) + 1, sizeof(tmp));
941 goto error;
942 }
943
944 len = strlen(path);
945 if (tmp[len - 1] == '/') {
946 tmp[len - 1] = 0;
947 }
948
949 for (p = tmp + 1; *p; p++) {
950 if (*p == '/') {
951 *p = 0;
952 if (tmp[strlen(tmp) - 1] == '.' &&
953 tmp[strlen(tmp) - 2] == '.' &&
954 tmp[strlen(tmp) - 3] == '/') {
955 ERR("Using '/../' is not permitted in the trace path (%s)",
956 tmp);
957 ret = -1;
958 goto error;
959 }
960 ret = create_directory_check_exists(handle, tmp, mode);
961 if (ret < 0) {
962 if (errno != EACCES) {
963 PERROR("Failed to create directory \"%s\"",
964 path);
965 ret = -errno;
966 goto error;
967 }
968 }
969 *p = '/';
970 }
971 }
972
973 ret = create_directory_check_exists(handle, tmp, mode);
974 if (ret < 0) {
975 PERROR("mkdirat recursive last element");
976 ret = -errno;
977 }
978 error:
979 return ret;
980 }
981
982 LTTNG_HIDDEN
983 bool lttng_directory_handle_get(struct lttng_directory_handle *handle)
984 {
985 return urcu_ref_get_unless_zero(&handle->ref);
986 }
987
988 LTTNG_HIDDEN
989 void lttng_directory_handle_put(struct lttng_directory_handle *handle)
990 {
991 if (!handle) {
992 return;
993 }
994 assert(handle->ref.refcount);
995 urcu_ref_put(&handle->ref, lttng_directory_handle_release);
996 }
997
998 LTTNG_HIDDEN
999 int lttng_directory_handle_create_subdirectory_as_user(
1000 const struct lttng_directory_handle *handle,
1001 const char *subdirectory,
1002 mode_t mode, const struct lttng_credentials *creds)
1003 {
1004 int ret;
1005
1006 if (!creds) {
1007 /* Run as current user. */
1008 ret = create_directory_check_exists(handle,
1009 subdirectory, mode);
1010 } else {
1011 ret = _run_as_mkdir(handle, subdirectory,
1012 mode, creds->uid, creds->gid);
1013 }
1014
1015 return ret;
1016 }
1017
1018 LTTNG_HIDDEN
1019 int lttng_directory_handle_create_subdirectory_recursive_as_user(
1020 const struct lttng_directory_handle *handle,
1021 const char *subdirectory_path,
1022 mode_t mode, const struct lttng_credentials *creds)
1023 {
1024 int ret;
1025
1026 if (!creds) {
1027 /* Run as current user. */
1028 ret = create_directory_recursive(handle,
1029 subdirectory_path, mode);
1030 } else {
1031 ret = _run_as_mkdir_recursive(handle, subdirectory_path,
1032 mode, creds->uid, creds->gid);
1033 }
1034
1035 return ret;
1036 }
1037
1038 LTTNG_HIDDEN
1039 int lttng_directory_handle_create_subdirectory(
1040 const struct lttng_directory_handle *handle,
1041 const char *subdirectory,
1042 mode_t mode)
1043 {
1044 return lttng_directory_handle_create_subdirectory_as_user(
1045 handle, subdirectory, mode, NULL);
1046 }
1047
1048 LTTNG_HIDDEN
1049 int lttng_directory_handle_create_subdirectory_recursive(
1050 const struct lttng_directory_handle *handle,
1051 const char *subdirectory_path,
1052 mode_t mode)
1053 {
1054 return lttng_directory_handle_create_subdirectory_recursive_as_user(
1055 handle, subdirectory_path, mode, NULL);
1056 }
1057
1058 LTTNG_HIDDEN
1059 int lttng_directory_handle_open_file_as_user(
1060 const struct lttng_directory_handle *handle,
1061 const char *filename,
1062 int flags, mode_t mode,
1063 const struct lttng_credentials *creds)
1064 {
1065 int ret;
1066
1067 if (!creds) {
1068 /* Run as current user. */
1069 ret = lttng_directory_handle_open(handle, filename, flags,
1070 mode);
1071 } else {
1072 ret = _run_as_open(handle, filename, flags, mode,
1073 creds->uid, creds->gid);
1074 }
1075 return ret;
1076 }
1077
1078 LTTNG_HIDDEN
1079 int lttng_directory_handle_open_file(
1080 const struct lttng_directory_handle *handle,
1081 const char *filename,
1082 int flags, mode_t mode)
1083 {
1084 return lttng_directory_handle_open_file_as_user(handle, filename, flags,
1085 mode, NULL);
1086 }
1087
1088 LTTNG_HIDDEN
1089 int lttng_directory_handle_unlink_file_as_user(
1090 const struct lttng_directory_handle *handle,
1091 const char *filename,
1092 const struct lttng_credentials *creds)
1093 {
1094 int ret;
1095
1096 if (!creds) {
1097 /* Run as current user. */
1098 ret = lttng_directory_handle_unlink(handle, filename);
1099 } else {
1100 ret = _run_as_unlink(handle, filename, creds->uid, creds->gid);
1101 }
1102 return ret;
1103 }
1104
1105 LTTNG_HIDDEN
1106 int lttng_directory_handle_unlink_file(
1107 const struct lttng_directory_handle *handle,
1108 const char *filename)
1109 {
1110 return lttng_directory_handle_unlink_file_as_user(handle,
1111 filename, NULL);
1112 }
1113
1114 LTTNG_HIDDEN
1115 int lttng_directory_handle_rename(
1116 const struct lttng_directory_handle *old_handle,
1117 const char *old_name,
1118 const struct lttng_directory_handle *new_handle,
1119 const char *new_name)
1120 {
1121 return lttng_directory_handle_rename_as_user(old_handle, old_name,
1122 new_handle, new_name, NULL);
1123 }
1124
1125 LTTNG_HIDDEN
1126 int lttng_directory_handle_rename_as_user(
1127 const struct lttng_directory_handle *old_handle,
1128 const char *old_name,
1129 const struct lttng_directory_handle *new_handle,
1130 const char *new_name,
1131 const struct lttng_credentials *creds)
1132 {
1133 int ret;
1134
1135 if (!creds) {
1136 /* Run as current user. */
1137 ret = _lttng_directory_handle_rename(old_handle,
1138 old_name, new_handle, new_name);
1139 } else {
1140 ret = _run_as_rename(old_handle, old_name, new_handle,
1141 new_name, creds->uid, creds->gid);
1142 }
1143 return ret;
1144 }
1145
1146 LTTNG_HIDDEN
1147 int lttng_directory_handle_remove_subdirectory(
1148 const struct lttng_directory_handle *handle,
1149 const char *name)
1150 {
1151 return lttng_directory_handle_remove_subdirectory_as_user(handle, name,
1152 NULL);
1153 }
1154
1155 LTTNG_HIDDEN
1156 int lttng_directory_handle_remove_subdirectory_as_user(
1157 const struct lttng_directory_handle *handle,
1158 const char *name,
1159 const struct lttng_credentials *creds)
1160 {
1161 int ret;
1162
1163 if (!creds) {
1164 /* Run as current user. */
1165 ret = lttng_directory_handle_rmdir(handle, name);
1166 } else {
1167 ret = _run_as_rmdir(handle, name, creds->uid, creds->gid);
1168 }
1169 return ret;
1170 }
1171
1172 struct rmdir_frame {
1173 ssize_t parent_frame_idx;
1174 DIR *dir;
1175 bool empty;
1176 /* Size including '\0'. */
1177 size_t path_size;
1178 };
1179
1180 static
1181 void rmdir_frame_fini(void *data)
1182 {
1183 int ret;
1184 struct rmdir_frame *frame = data;
1185
1186 ret = closedir(frame->dir);
1187 if (ret == -1) {
1188 PERROR("Failed to close directory stream");
1189 }
1190 }
1191
1192 static
1193 int remove_directory_recursive(const struct lttng_directory_handle *handle,
1194 const char *path, int flags)
1195 {
1196 int ret;
1197 struct lttng_dynamic_array frames;
1198 size_t current_frame_idx = 0;
1199 struct rmdir_frame initial_frame = {
1200 .parent_frame_idx = -1,
1201 .dir = lttng_directory_handle_opendir(handle, path),
1202 .empty = true,
1203 .path_size = strlen(path) + 1,
1204 };
1205 struct lttng_dynamic_buffer current_path;
1206 const char separator = '/';
1207
1208 lttng_dynamic_buffer_init(&current_path);
1209 lttng_dynamic_array_init(&frames, sizeof(struct rmdir_frame),
1210 rmdir_frame_fini);
1211
1212 if (flags & ~(LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG |
1213 LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG)) {
1214 ERR("Unknown flags %d", flags);
1215 ret = -1;
1216 goto end;
1217 }
1218
1219 if (!initial_frame.dir) {
1220 if (flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG &&
1221 errno == ENOENT) {
1222 DBG("Cannot rmdir \"%s\": root does not exist", path);
1223 ret = 0;
1224 goto end;
1225 } else {
1226 PERROR("Failed to rmdir \"%s\"", path);
1227 ret = -1;
1228 goto end;
1229 }
1230 }
1231
1232 ret = lttng_dynamic_array_add_element(&frames, &initial_frame);
1233 if (ret) {
1234 ERR("Failed to push context frame during recursive directory removal");
1235 rmdir_frame_fini(&initial_frame);
1236 goto end;
1237 }
1238
1239 ret = lttng_dynamic_buffer_append(
1240 &current_path, path, initial_frame.path_size);
1241 if (ret) {
1242 ERR("Failed to set initial path during recursive directory removal");
1243 ret = -1;
1244 goto end;
1245 }
1246
1247 while (lttng_dynamic_array_get_count(&frames) > 0) {
1248 struct dirent *entry;
1249 struct rmdir_frame *current_frame =
1250 lttng_dynamic_array_get_element(
1251 &frames, current_frame_idx);
1252
1253 assert(current_frame->dir);
1254 ret = lttng_dynamic_buffer_set_size(
1255 &current_path, current_frame->path_size);
1256 assert(!ret);
1257 current_path.data[current_path.size - 1] = '\0';
1258
1259 while ((entry = readdir(current_frame->dir))) {
1260 struct stat st;
1261
1262 if (!strcmp(entry->d_name, ".") ||
1263 !strcmp(entry->d_name, "..")) {
1264 continue;
1265 }
1266
1267 /* Set current_path to the entry's path. */
1268 ret = lttng_dynamic_buffer_set_size(
1269 &current_path, current_path.size - 1);
1270 assert(!ret);
1271 ret = lttng_dynamic_buffer_append(&current_path,
1272 &separator, sizeof(separator));
1273 if (ret) {
1274 goto end;
1275 }
1276 ret = lttng_dynamic_buffer_append(&current_path,
1277 entry->d_name,
1278 strlen(entry->d_name) + 1);
1279 if (ret) {
1280 goto end;
1281 }
1282
1283 if (lttng_directory_handle_stat(
1284 handle, current_path.data, &st)) {
1285 if ((flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG) &&
1286 errno == ENOENT) {
1287 break;
1288 }
1289 PERROR("Failed to stat \"%s\"",
1290 current_path.data);
1291 ret = -1;
1292 goto end;
1293 }
1294
1295 if (!S_ISDIR(st.st_mode)) {
1296 if (flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG) {
1297 current_frame->empty = false;
1298 break;
1299 } else {
1300 /* Not empty, abort. */
1301 DBG("Directory \"%s\" is not empty; refusing to remove directory",
1302 current_path.data);
1303 ret = -1;
1304 goto end;
1305 }
1306 } else {
1307 struct rmdir_frame new_frame = {
1308 .path_size = current_path.size,
1309 .dir = lttng_directory_handle_opendir(
1310 handle,
1311 current_path.data),
1312 .empty = true,
1313 .parent_frame_idx = current_frame_idx,
1314 };
1315
1316 if (!new_frame.dir) {
1317 if (flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG &&
1318 errno == ENOENT) {
1319 DBG("Non-existing directory stream during recursive directory removal");
1320 break;
1321 } else {
1322 PERROR("Failed to open directory stream during recursive directory removal");
1323 ret = -1;
1324 goto end;
1325 }
1326 }
1327 ret = lttng_dynamic_array_add_element(
1328 &frames, &new_frame);
1329 if (ret) {
1330 ERR("Failed to push context frame during recursive directory removal");
1331 rmdir_frame_fini(&new_frame);
1332 goto end;
1333 }
1334 current_frame_idx++;
1335 /* We break iteration on readdir. */
1336 break;
1337 }
1338 }
1339 if (entry) {
1340 continue;
1341 }
1342
1343 /* Pop rmdir frame. */
1344 if (current_frame->empty) {
1345 ret = lttng_directory_handle_rmdir(
1346 handle, current_path.data);
1347 if (ret) {
1348 if ((flags & LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG) ||
1349 errno != ENOENT) {
1350 PERROR("Failed to remove \"%s\" during recursive directory removal",
1351 current_path.data);
1352 goto end;
1353 }
1354 DBG("Non-existing directory stream during recursive directory removal");
1355 }
1356 } else if (current_frame->parent_frame_idx >= 0) {
1357 struct rmdir_frame *parent_frame;
1358
1359 parent_frame = lttng_dynamic_array_get_element(&frames,
1360 current_frame->parent_frame_idx);
1361 assert(parent_frame);
1362 parent_frame->empty = false;
1363 }
1364 ret = lttng_dynamic_array_remove_element(
1365 &frames, current_frame_idx);
1366 if (ret) {
1367 ERR("Failed to pop context frame during recursive directory removal");
1368 goto end;
1369 }
1370 current_frame_idx--;
1371 }
1372 end:
1373 lttng_dynamic_array_reset(&frames);
1374 lttng_dynamic_buffer_reset(&current_path);
1375 return ret;
1376 }
1377
1378 LTTNG_HIDDEN
1379 int lttng_directory_handle_remove_subdirectory_recursive(
1380 const struct lttng_directory_handle *handle,
1381 const char *name,
1382 int flags)
1383 {
1384 return lttng_directory_handle_remove_subdirectory_recursive_as_user(
1385 handle, name, NULL, flags);
1386 }
1387
1388 LTTNG_HIDDEN
1389 int lttng_directory_handle_remove_subdirectory_recursive_as_user(
1390 const struct lttng_directory_handle *handle,
1391 const char *name,
1392 const struct lttng_credentials *creds,
1393 int flags)
1394 {
1395 int ret;
1396
1397 if (!creds) {
1398 /* Run as current user. */
1399 ret = remove_directory_recursive(handle, name, flags);
1400 } else {
1401 ret = _run_as_rmdir_recursive(handle, name, creds->uid,
1402 creds->gid, flags);
1403 }
1404 return ret;
1405 }
This page took 0.099937 seconds and 5 git commands to generate.