2 * Copyright (C) 2019 Jérémie Galarneau <jeremie.galarneau@efficios.com>
4 * SPDX-License-Identifier: GPL-2.0-only
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>
17 #include <sys/types.h>
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.
29 int lttng_directory_handle_mkdir(
30 const struct lttng_directory_handle
*handle
,
31 const char *path
, mode_t mode
);
33 int _run_as_mkdir(const struct lttng_directory_handle
*handle
, const char *path
,
34 mode_t mode
, uid_t uid
, gid_t gid
);
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
);
39 int lttng_directory_handle_open(const struct lttng_directory_handle
*handle
,
40 const char *filename
, int flags
, mode_t mode
);
42 int _run_as_open(const struct lttng_directory_handle
*handle
,
44 int flags
, mode_t mode
, uid_t uid
, gid_t gid
);
46 int lttng_directory_handle_unlink(
47 const struct lttng_directory_handle
*handle
,
48 const char *filename
);
50 int _run_as_unlink(const struct lttng_directory_handle
*handle
,
51 const char *filename
, uid_t uid
, gid_t gid
);
53 int _lttng_directory_handle_rename(
54 const struct lttng_directory_handle
*old_handle
,
56 const struct lttng_directory_handle
*new_handle
,
57 const char *new_name
);
59 int _run_as_rename(const struct lttng_directory_handle
*old_handle
,
61 const struct lttng_directory_handle
*new_handle
,
62 const char *new_name
, uid_t uid
, gid_t gid
);
64 DIR *lttng_directory_handle_opendir(const struct lttng_directory_handle
*handle
,
67 int lttng_directory_handle_rmdir(
68 const struct lttng_directory_handle
*handle
, const char *name
);
70 int _run_as_rmdir(const struct lttng_directory_handle
*handle
,
71 const char *name
, uid_t uid
, gid_t gid
);
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
);
77 void lttng_directory_handle_invalidate(struct lttng_directory_handle
*handle
);
79 void lttng_directory_handle_release(struct urcu_ref
*ref
);
84 * Special inode number reserved to represent the "current working directory".
85 * ino_t is spec'ed as being an unsigned integral type.
87 #define RESERVED_AT_FDCWD_INO \
89 uint64_t reserved_val; \
90 switch (sizeof(ino_t)) { \
92 reserved_val = UINT32_MAX; \
95 reserved_val = UINT64_MAX; \
100 (ino_t) reserved_val; \
104 struct lttng_directory_handle
*lttng_directory_handle_create(const char *path
)
106 const struct lttng_directory_handle cwd_handle
= {
110 /* Open a handle to the CWD if NULL is passed. */
111 return lttng_directory_handle_create_from_handle(path
, &cwd_handle
);
115 struct lttng_directory_handle
*lttng_directory_handle_create_from_handle(
117 const struct lttng_directory_handle
*ref_handle
)
120 struct lttng_directory_handle
*handle
= NULL
;
123 handle
= lttng_directory_handle_copy(ref_handle
);
127 ERR("Failed to initialize directory handle: provided path is an empty string");
131 dirfd
= openat(ref_handle
->dirfd
, path
, O_RDONLY
| O_DIRECTORY
| O_CLOEXEC
);
133 PERROR("Failed to initialize directory handle to \"%s\"", path
);
137 handle
= lttng_directory_handle_create_from_dirfd(dirfd
);
145 PERROR("Failed to close directory file descriptor");
151 struct lttng_directory_handle
*lttng_directory_handle_create_from_dirfd(
155 struct lttng_directory_handle
*handle
= zmalloc(sizeof(*handle
));
156 struct stat stat_buf
;
162 if (dirfd
!= AT_FDCWD
) {
163 ret
= fstat(dirfd
, &stat_buf
);
165 PERROR("Failed to fstat directory file descriptor %i", dirfd
);
166 lttng_directory_handle_release(&handle
->ref
);
169 handle
->directory_inode
= RESERVED_AT_FDCWD_INO
;
171 handle
->dirfd
= dirfd
;
172 urcu_ref_init(&handle
->ref
);
178 void lttng_directory_handle_release(struct urcu_ref
*ref
)
181 struct lttng_directory_handle
*handle
=
182 container_of(ref
, struct lttng_directory_handle
, ref
);
184 if (handle
->dirfd
== AT_FDCWD
|| handle
->dirfd
== -1) {
187 ret
= close(handle
->dirfd
);
189 PERROR("Failed to close directory file descriptor of directory handle");
192 lttng_directory_handle_invalidate(handle
);
197 struct lttng_directory_handle
*lttng_directory_handle_copy(
198 const struct lttng_directory_handle
*handle
)
200 struct lttng_directory_handle
*new_handle
= NULL
;
202 if (handle
->dirfd
== AT_FDCWD
) {
203 new_handle
= lttng_directory_handle_create_from_dirfd(AT_FDCWD
);
205 const int new_dirfd
= dup(handle
->dirfd
);
207 if (new_dirfd
== -1) {
208 PERROR("Failed to duplicate directory file descriptor of directory handle");
211 new_handle
= lttng_directory_handle_create_from_dirfd(
213 if (!new_handle
&& close(new_dirfd
)) {
214 PERROR("Failed to close directory file descriptor of directory handle");
222 bool lttng_directory_handle_equals(const struct lttng_directory_handle
*lhs
,
223 const struct lttng_directory_handle
*rhs
)
225 return lhs
->directory_inode
== rhs
->directory_inode
;
229 void lttng_directory_handle_invalidate(struct lttng_directory_handle
*handle
)
235 int lttng_directory_handle_stat(const struct lttng_directory_handle
*handle
,
236 const char *path
, struct stat
*st
)
238 return fstatat(handle
->dirfd
, path
, st
, 0);
242 bool lttng_directory_handle_uses_fd(
243 const struct lttng_directory_handle
*handle
)
245 return handle
->dirfd
!= AT_FDCWD
;
249 int lttng_directory_handle_mkdir(
250 const struct lttng_directory_handle
*handle
,
251 const char *path
, mode_t mode
)
253 return mkdirat(handle
->dirfd
, path
, mode
);
257 int lttng_directory_handle_open(const struct lttng_directory_handle
*handle
,
258 const char *filename
, int flags
, mode_t mode
)
260 return openat(handle
->dirfd
, filename
, flags
, mode
);
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
)
268 return run_as_openat(handle
->dirfd
, filename
, flags
, mode
, uid
, gid
);
272 int _run_as_unlink(const struct lttng_directory_handle
*handle
,
273 const char *filename
, uid_t uid
, gid_t gid
)
275 return run_as_unlinkat(handle
->dirfd
, filename
, uid
, gid
);
279 int lttng_directory_handle_unlink(
280 const struct lttng_directory_handle
*handle
,
281 const char *filename
)
283 return unlinkat(handle
->dirfd
, filename
, 0);
287 int _run_as_mkdir(const struct lttng_directory_handle
*handle
,
288 const char *path
, mode_t mode
, uid_t uid
, gid_t gid
)
290 return run_as_mkdirat(handle
->dirfd
, path
, mode
, uid
, gid
);
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
)
297 return run_as_mkdirat_recursive(handle
->dirfd
, path
, mode
, uid
, gid
);
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
)
307 return renameat(old_handle
->dirfd
, old_name
,
308 new_handle
->dirfd
, new_name
);
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
)
317 return run_as_renameat(old_handle
->dirfd
, old_name
, new_handle
->dirfd
,
322 DIR *lttng_directory_handle_opendir(const struct lttng_directory_handle
*handle
,
325 DIR *dir_stream
= NULL
;
326 int fd
= openat(handle
->dirfd
, path
, O_RDONLY
);
332 dir_stream
= fdopendir(fd
);
336 PERROR("Failed to open directory stream");
339 PERROR("Failed to close file descriptor to %s", path
);
349 int lttng_directory_handle_rmdir(
350 const struct lttng_directory_handle
*handle
, const char *name
)
352 return unlinkat(handle
->dirfd
, name
, AT_REMOVEDIR
);
356 int _run_as_rmdir(const struct lttng_directory_handle
*handle
,
357 const char *name
, uid_t uid
, gid_t gid
)
359 return run_as_rmdirat(handle
->dirfd
, name
, uid
, gid
);
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
)
367 return run_as_rmdirat_recursive(handle
->dirfd
, name
, uid
, gid
, flags
);
370 #else /* COMPAT_DIRFD */
373 int get_full_path(const struct lttng_directory_handle
*handle
,
374 const char *subdirectory
, char *fullpath
, size_t size
)
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
?
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] != '/';
388 ret
= snprintf(fullpath
, size
, "%s%s%s%s",
390 add_separator_slash
? "/" : "",
392 add_trailing_slash
? "/" : "");
393 if (ret
== -1 || ret
>= size
) {
394 ERR("Failed to format subdirectory from directory handle");
404 struct lttng_directory_handle
*_lttng_directory_handle_create(char *path
)
406 struct lttng_directory_handle
*handle
= zmalloc(sizeof(*handle
));
411 urcu_ref_init(&handle
->ref
);
412 handle
->base_path
= path
;
418 struct lttng_directory_handle
*lttng_directory_handle_create(
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
,
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
));
437 PERROR("Failed to initialize directory handle, can't get current working directory");
441 cwd_len
= strlen(cwd
);
443 ERR("Failed to initialize directory handle, current working directory path has a length of 0");
447 add_cwd_slash
= cwd
[cwd_len
- 1] != '/';
450 ret
= snprintf(handle_buf
, sizeof(handle_buf
), "%s%s%s%s",
452 add_cwd_slash
? "/" : "",
454 add_trailing_slash
? "/" : "");
455 if (ret
== -1 || ret
>= LTTNG_PATH_MAX
) {
456 ERR("Failed to initialize directory handle, failed to format directory path");
460 new_handle
= lttng_directory_handle_create_from_handle(path
, &cwd_handle
);
466 struct lttng_directory_handle
*lttng_directory_handle_create_from_handle(
468 const struct lttng_directory_handle
*ref_handle
)
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
;
477 assert(ref_handle
&& ref_handle
->base_path
);
479 ret
= lttng_directory_handle_stat(ref_handle
, path
, &stat_buf
);
481 PERROR("Failed to create directory handle");
483 } else if (!S_ISDIR(stat_buf
.st_mode
)) {
484 char full_path
[LTTNG_PATH_MAX
];
486 /* Best effort for logging purposes. */
487 ret
= get_full_path(ref_handle
, path
, full_path
,
493 ERR("Failed to initialize directory handle to \"%s\": not a directory",
498 new_handle
= lttng_directory_handle_copy(ref_handle
);
502 path_len
= strlen(path
);
504 ERR("Failed to initialize directory handle: provided path is an empty string");
509 new_path
= strdup(path
);
513 /* Takes ownership of new_path. */
514 new_handle
= _lttng_directory_handle_create(new_path
);
519 add_trailing_slash
= path
[path_len
- 1] != '/';
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
);
528 new_path
= zmalloc(handle_path_len
);
530 PERROR("Failed to initialize directory handle");
534 ret
= sprintf(new_handle
->base_path
, "%s%s%s",
535 ref_handle
->base_path
,
537 add_trailing_slash
? "/" : "");
538 if (ret
== -1 || ret
>= handle_path_len
) {
539 ERR("Failed to initialize directory handle: path formatting failed");
542 new_handle
= _lttng_directory_handle_create(new_path
);
550 struct lttng_directory_handle
*lttng_directory_handle_create_from_dirfd(
553 assert(dirfd
== AT_FDCWD
);
554 return lttng_directory_handle_create(NULL
);
558 void lttng_directory_handle_release(struct urcu_ref
*ref
)
560 struct lttng_directory_handle
*handle
=
561 container_of(ref
, struct lttng_directory_handle
, ref
);
563 free(handle
->base_path
);
564 lttng_directory_handle_invalidate(handle
);
569 struct lttng_directory_handle
*lttng_directory_handle_copy(
570 const struct lttng_directory_handle
*handle
)
572 struct lttng_directory_handle
*new_handle
= NULL
;
573 char *new_path
= NULL
;
575 if (handle
->base_path
) {
576 new_path
= strdup(handle
->base_path
);
581 new_handle
= _lttng_directory_handle_create(new_path
);
587 bool lttng_directory_handle_equals(const struct lttng_directory_handle
*lhs
,
588 const struct lttng_directory_handle
*rhs
)
590 return strcmp(lhs
->path
, rhs
->path
) == 0;
594 void lttng_directory_handle_invalidate(struct lttng_directory_handle
*handle
)
596 handle
->base_path
= NULL
;
600 int lttng_directory_handle_stat(const struct lttng_directory_handle
*handle
,
601 const char *subdirectory
, struct stat
*st
)
604 char fullpath
[LTTNG_PATH_MAX
];
606 ret
= get_full_path(handle
, subdirectory
, fullpath
, sizeof(fullpath
));
612 ret
= stat(fullpath
, st
);
618 bool lttng_directory_handle_uses_fd(
619 const struct lttng_directory_handle
*handle
)
625 int lttng_directory_handle_mkdir(const struct lttng_directory_handle
*handle
,
626 const char *subdirectory
, mode_t mode
)
629 char fullpath
[LTTNG_PATH_MAX
];
631 ret
= get_full_path(handle
, subdirectory
, fullpath
, sizeof(fullpath
));
637 ret
= mkdir(fullpath
, mode
);
643 int lttng_directory_handle_open(const struct lttng_directory_handle
*handle
,
644 const char *filename
, int flags
, mode_t mode
)
647 char fullpath
[LTTNG_PATH_MAX
];
649 ret
= get_full_path(handle
, filename
, fullpath
, sizeof(fullpath
));
655 ret
= open(fullpath
, flags
, mode
);
661 int lttng_directory_handle_unlink(
662 const struct lttng_directory_handle
*handle
,
663 const char *filename
)
666 char fullpath
[LTTNG_PATH_MAX
];
668 ret
= get_full_path(handle
, filename
, fullpath
, sizeof(fullpath
));
674 ret
= unlink(fullpath
);
680 int _run_as_mkdir(const struct lttng_directory_handle
*handle
, const char *path
,
681 mode_t mode
, uid_t uid
, gid_t gid
)
684 char fullpath
[LTTNG_PATH_MAX
];
686 ret
= get_full_path(handle
, path
, fullpath
, sizeof(fullpath
));
692 ret
= run_as_mkdir(fullpath
, mode
, uid
, gid
);
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
)
703 char fullpath
[LTTNG_PATH_MAX
];
705 ret
= get_full_path(handle
, filename
, fullpath
, sizeof(fullpath
));
711 ret
= run_as_open(fullpath
, flags
, mode
, uid
, gid
);
717 int _run_as_unlink(const struct lttng_directory_handle
*handle
,
718 const char *filename
, uid_t uid
, gid_t gid
)
721 char fullpath
[LTTNG_PATH_MAX
];
723 ret
= get_full_path(handle
, filename
, fullpath
, sizeof(fullpath
));
729 ret
= run_as_unlink(fullpath
, uid
, gid
);
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
)
739 char fullpath
[LTTNG_PATH_MAX
];
741 ret
= get_full_path(handle
, path
, fullpath
, sizeof(fullpath
));
747 ret
= run_as_mkdir_recursive(fullpath
, mode
, uid
, gid
);
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
)
760 char old_fullpath
[LTTNG_PATH_MAX
];
761 char new_fullpath
[LTTNG_PATH_MAX
];
763 ret
= get_full_path(old_handle
, old_name
, old_fullpath
,
764 sizeof(old_fullpath
));
769 ret
= get_full_path(new_handle
, new_name
, new_fullpath
,
770 sizeof(new_fullpath
));
776 ret
= rename(old_fullpath
, new_fullpath
);
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
)
788 char old_fullpath
[LTTNG_PATH_MAX
];
789 char new_fullpath
[LTTNG_PATH_MAX
];
791 ret
= get_full_path(old_handle
, old_name
, old_fullpath
,
792 sizeof(old_fullpath
));
797 ret
= get_full_path(new_handle
, new_name
, new_fullpath
,
798 sizeof(new_fullpath
));
804 ret
= run_as_rename(old_fullpath
, new_fullpath
, uid
, gid
);
810 DIR *lttng_directory_handle_opendir(const struct lttng_directory_handle
*handle
,
814 DIR *dir_stream
= NULL
;
815 char fullpath
[LTTNG_PATH_MAX
];
817 ret
= get_full_path(handle
, path
, fullpath
, sizeof(fullpath
));
823 dir_stream
= opendir(fullpath
);
829 int lttng_directory_handle_rmdir(
830 const struct lttng_directory_handle
*handle
, const char *name
)
833 char fullpath
[LTTNG_PATH_MAX
];
835 ret
= get_full_path(handle
, name
, fullpath
, sizeof(fullpath
));
841 ret
= rmdir(fullpath
);
847 int _run_as_rmdir(const struct lttng_directory_handle
*handle
,
848 const char *name
, uid_t uid
, gid_t gid
)
851 char fullpath
[LTTNG_PATH_MAX
];
853 ret
= get_full_path(handle
, name
, fullpath
, sizeof(fullpath
));
859 ret
= run_as_rmdir(fullpath
, uid
, gid
);
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
)
870 char fullpath
[LTTNG_PATH_MAX
];
872 ret
= get_full_path(handle
, name
, fullpath
, sizeof(fullpath
));
878 ret
= run_as_rmdir_recursive(fullpath
, uid
, gid
, flags
);
883 #endif /* COMPAT_DIRFD */
885 /* Common implementation. */
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", ...).
894 * Checking the path for existence allows us to work around this behaviour.
897 int create_directory_check_exists(const struct lttng_directory_handle
*handle
,
898 const char *path
, mode_t mode
)
903 ret
= lttng_directory_handle_stat(handle
, path
, &st
);
905 if (S_ISDIR(st
.st_mode
)) {
906 /* Directory exists, skip. */
909 /* Exists, but is not a directory. */
914 } else if (errno
!= ENOENT
) {
919 * Let mkdir handle other errors as the caller expects mkdir
922 ret
= lttng_directory_handle_mkdir(handle
, path
, mode
);
928 int create_directory_recursive(const struct lttng_directory_handle
*handle
,
929 const char *path
, mode_t mode
)
931 char *p
, tmp
[LTTNG_PATH_MAX
];
937 ret
= lttng_strncpy(tmp
, path
, sizeof(tmp
));
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
));
945 if (tmp
[len
- 1] == '/') {
949 for (p
= tmp
+ 1; *p
; p
++) {
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)",
960 ret
= create_directory_check_exists(handle
, tmp
, mode
);
962 if (errno
!= EACCES
) {
963 PERROR("Failed to create directory \"%s\"",
973 ret
= create_directory_check_exists(handle
, tmp
, mode
);
975 PERROR("mkdirat recursive last element");
983 bool lttng_directory_handle_get(struct lttng_directory_handle
*handle
)
985 return urcu_ref_get_unless_zero(&handle
->ref
);
989 void lttng_directory_handle_put(struct lttng_directory_handle
*handle
)
994 assert(handle
->ref
.refcount
);
995 urcu_ref_put(&handle
->ref
, lttng_directory_handle_release
);
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
)
1007 /* Run as current user. */
1008 ret
= create_directory_check_exists(handle
,
1009 subdirectory
, mode
);
1011 ret
= _run_as_mkdir(handle
, subdirectory
,
1012 mode
, creds
->uid
, creds
->gid
);
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
)
1027 /* Run as current user. */
1028 ret
= create_directory_recursive(handle
,
1029 subdirectory_path
, mode
);
1031 ret
= _run_as_mkdir_recursive(handle
, subdirectory_path
,
1032 mode
, creds
->uid
, creds
->gid
);
1039 int lttng_directory_handle_create_subdirectory(
1040 const struct lttng_directory_handle
*handle
,
1041 const char *subdirectory
,
1044 return lttng_directory_handle_create_subdirectory_as_user(
1045 handle
, subdirectory
, mode
, NULL
);
1049 int lttng_directory_handle_create_subdirectory_recursive(
1050 const struct lttng_directory_handle
*handle
,
1051 const char *subdirectory_path
,
1054 return lttng_directory_handle_create_subdirectory_recursive_as_user(
1055 handle
, subdirectory_path
, mode
, NULL
);
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
)
1068 /* Run as current user. */
1069 ret
= lttng_directory_handle_open(handle
, filename
, flags
,
1072 ret
= _run_as_open(handle
, filename
, flags
, mode
,
1073 creds
->uid
, creds
->gid
);
1079 int lttng_directory_handle_open_file(
1080 const struct lttng_directory_handle
*handle
,
1081 const char *filename
,
1082 int flags
, mode_t mode
)
1084 return lttng_directory_handle_open_file_as_user(handle
, filename
, flags
,
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
)
1097 /* Run as current user. */
1098 ret
= lttng_directory_handle_unlink(handle
, filename
);
1100 ret
= _run_as_unlink(handle
, filename
, creds
->uid
, creds
->gid
);
1106 int lttng_directory_handle_unlink_file(
1107 const struct lttng_directory_handle
*handle
,
1108 const char *filename
)
1110 return lttng_directory_handle_unlink_file_as_user(handle
,
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
)
1121 return lttng_directory_handle_rename_as_user(old_handle
, old_name
,
1122 new_handle
, new_name
, NULL
);
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
)
1136 /* Run as current user. */
1137 ret
= _lttng_directory_handle_rename(old_handle
,
1138 old_name
, new_handle
, new_name
);
1140 ret
= _run_as_rename(old_handle
, old_name
, new_handle
,
1141 new_name
, creds
->uid
, creds
->gid
);
1147 int lttng_directory_handle_remove_subdirectory(
1148 const struct lttng_directory_handle
*handle
,
1151 return lttng_directory_handle_remove_subdirectory_as_user(handle
, name
,
1156 int lttng_directory_handle_remove_subdirectory_as_user(
1157 const struct lttng_directory_handle
*handle
,
1159 const struct lttng_credentials
*creds
)
1164 /* Run as current user. */
1165 ret
= lttng_directory_handle_rmdir(handle
, name
);
1167 ret
= _run_as_rmdir(handle
, name
, creds
->uid
, creds
->gid
);
1172 struct rmdir_frame
{
1173 ssize_t parent_frame_idx
;
1176 /* Size including '\0'. */
1181 void rmdir_frame_fini(void *data
)
1184 struct rmdir_frame
*frame
= data
;
1186 ret
= closedir(frame
->dir
);
1188 PERROR("Failed to close directory stream");
1193 int remove_directory_recursive(const struct lttng_directory_handle
*handle
,
1194 const char *path
, int flags
)
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
),
1203 .path_size
= strlen(path
) + 1,
1205 struct lttng_dynamic_buffer current_path
;
1206 const char separator
= '/';
1208 lttng_dynamic_buffer_init(¤t_path
);
1209 lttng_dynamic_array_init(&frames
, sizeof(struct rmdir_frame
),
1212 if (flags
& ~(LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
|
1213 LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG
)) {
1214 ERR("Unknown flags %d", flags
);
1219 if (!initial_frame
.dir
) {
1220 if (flags
& LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
&&
1222 DBG("Cannot rmdir \"%s\": root does not exist", path
);
1226 PERROR("Failed to rmdir \"%s\"", path
);
1232 ret
= lttng_dynamic_array_add_element(&frames
, &initial_frame
);
1234 ERR("Failed to push context frame during recursive directory removal");
1235 rmdir_frame_fini(&initial_frame
);
1239 ret
= lttng_dynamic_buffer_append(
1240 ¤t_path
, path
, initial_frame
.path_size
);
1242 ERR("Failed to set initial path during recursive directory removal");
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
);
1253 assert(current_frame
->dir
);
1254 ret
= lttng_dynamic_buffer_set_size(
1255 ¤t_path
, current_frame
->path_size
);
1257 current_path
.data
[current_path
.size
- 1] = '\0';
1259 while ((entry
= readdir(current_frame
->dir
))) {
1262 if (!strcmp(entry
->d_name
, ".") ||
1263 !strcmp(entry
->d_name
, "..")) {
1267 /* Set current_path to the entry's path. */
1268 ret
= lttng_dynamic_buffer_set_size(
1269 ¤t_path
, current_path
.size
- 1);
1271 ret
= lttng_dynamic_buffer_append(¤t_path
,
1272 &separator
, sizeof(separator
));
1276 ret
= lttng_dynamic_buffer_append(¤t_path
,
1278 strlen(entry
->d_name
) + 1);
1283 if (lttng_directory_handle_stat(
1284 handle
, current_path
.data
, &st
)) {
1285 if ((flags
& LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
) &&
1289 PERROR("Failed to stat \"%s\"",
1295 if (!S_ISDIR(st
.st_mode
)) {
1296 if (flags
& LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
) {
1297 current_frame
->empty
= false;
1300 /* Not empty, abort. */
1301 DBG("Directory \"%s\" is not empty; refusing to remove directory",
1307 struct rmdir_frame new_frame
= {
1308 .path_size
= current_path
.size
,
1309 .dir
= lttng_directory_handle_opendir(
1313 .parent_frame_idx
= current_frame_idx
,
1316 if (!new_frame
.dir
) {
1317 if (flags
& LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
&&
1319 DBG("Non-existing directory stream during recursive directory removal");
1322 PERROR("Failed to open directory stream during recursive directory removal");
1327 ret
= lttng_dynamic_array_add_element(
1328 &frames
, &new_frame
);
1330 ERR("Failed to push context frame during recursive directory removal");
1331 rmdir_frame_fini(&new_frame
);
1334 current_frame_idx
++;
1335 /* We break iteration on readdir. */
1343 /* Pop rmdir frame. */
1344 if (current_frame
->empty
) {
1345 ret
= lttng_directory_handle_rmdir(
1346 handle
, current_path
.data
);
1348 if ((flags
& LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG
) ||
1350 PERROR("Failed to remove \"%s\" during recursive directory removal",
1354 DBG("Non-existing directory stream during recursive directory removal");
1356 } else if (current_frame
->parent_frame_idx
>= 0) {
1357 struct rmdir_frame
*parent_frame
;
1359 parent_frame
= lttng_dynamic_array_get_element(&frames
,
1360 current_frame
->parent_frame_idx
);
1361 assert(parent_frame
);
1362 parent_frame
->empty
= false;
1364 ret
= lttng_dynamic_array_remove_element(
1365 &frames
, current_frame_idx
);
1367 ERR("Failed to pop context frame during recursive directory removal");
1370 current_frame_idx
--;
1373 lttng_dynamic_array_reset(&frames
);
1374 lttng_dynamic_buffer_reset(¤t_path
);
1379 int lttng_directory_handle_remove_subdirectory_recursive(
1380 const struct lttng_directory_handle
*handle
,
1384 return lttng_directory_handle_remove_subdirectory_recursive_as_user(
1385 handle
, name
, NULL
, flags
);
1389 int lttng_directory_handle_remove_subdirectory_recursive_as_user(
1390 const struct lttng_directory_handle
*handle
,
1392 const struct lttng_credentials
*creds
,
1398 /* Run as current user. */
1399 ret
= remove_directory_recursive(handle
, name
, flags
);
1401 ret
= _run_as_rmdir_recursive(handle
, name
, creds
->uid
,