2 * Copyright (C) 2019 Jérémie Galarneau <jeremie.galarneau@efficios.com>
4 * SPDX-License-Identifier: LGPL-2.1-only
8 #include <common/compat/directory-handle.hpp>
9 #include <common/credentials.hpp>
10 #include <common/dynamic-array.hpp>
11 #include <common/error.hpp>
12 #include <common/macros.hpp>
13 #include <common/runas.hpp>
15 #include <lttng/constant.h>
20 #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.
28 static int lttng_directory_handle_mkdir(const struct lttng_directory_handle
*handle
,
31 static int _run_as_mkdir(const struct lttng_directory_handle
*handle
,
36 static int _run_as_mkdir_recursive(const struct lttng_directory_handle
*handle
,
41 static int lttng_directory_handle_open(const struct lttng_directory_handle
*handle
,
45 static int _run_as_open(const struct lttng_directory_handle
*handle
,
51 static int lttng_directory_handle_unlink(const struct lttng_directory_handle
*handle
,
52 const char *filename
);
53 static int _run_as_unlink(const struct lttng_directory_handle
*handle
,
57 static int _lttng_directory_handle_rename(const struct lttng_directory_handle
*old_handle
,
59 const struct lttng_directory_handle
*new_handle
,
60 const char *new_name
);
61 static int _run_as_rename(const struct lttng_directory_handle
*old_handle
,
63 const struct lttng_directory_handle
*new_handle
,
67 static DIR *lttng_directory_handle_opendir(const struct lttng_directory_handle
*handle
,
69 static int lttng_directory_handle_rmdir(const struct lttng_directory_handle
*handle
,
72 _run_as_rmdir(const struct lttng_directory_handle
*handle
, const char *name
, uid_t uid
, gid_t gid
);
73 static int _run_as_rmdir_recursive(const struct lttng_directory_handle
*handle
,
78 static void lttng_directory_handle_invalidate(struct lttng_directory_handle
*handle
);
79 static 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; \
103 struct lttng_directory_handle
*lttng_directory_handle_create(const char *path
)
105 lttng_directory_handle cwd_handle
{};
106 cwd_handle
.dirfd
= AT_FDCWD
;
108 /* Open a handle to the CWD if NULL is passed. */
109 return lttng_directory_handle_create_from_handle(path
, &cwd_handle
);
112 struct lttng_directory_handle
*
113 lttng_directory_handle_create_from_handle(const char *path
,
114 const struct lttng_directory_handle
*ref_handle
)
117 struct lttng_directory_handle
*handle
= nullptr;
120 handle
= lttng_directory_handle_copy(ref_handle
);
124 ERR("Failed to initialize directory handle: provided path is an empty string");
128 dirfd
= openat(ref_handle
->dirfd
, path
, O_RDONLY
| O_DIRECTORY
| O_CLOEXEC
);
130 PERROR("Failed to initialize directory handle to \"%s\"", path
);
134 handle
= lttng_directory_handle_create_from_dirfd(dirfd
);
142 PERROR("Failed to close directory file descriptor");
147 struct lttng_directory_handle
*lttng_directory_handle_create_from_dirfd(int dirfd
)
150 struct lttng_directory_handle
*handle
= zmalloc
<lttng_directory_handle
>();
151 struct stat stat_buf
;
157 if (dirfd
!= AT_FDCWD
) {
158 ret
= fstat(dirfd
, &stat_buf
);
160 PERROR("Failed to fstat directory file descriptor %i", dirfd
);
161 lttng_directory_handle_release(&handle
->ref
);
166 handle
->directory_inode
= RESERVED_AT_FDCWD_INO
;
168 handle
->dirfd
= dirfd
;
169 urcu_ref_init(&handle
->ref
);
174 static void lttng_directory_handle_release(struct urcu_ref
*ref
)
177 struct lttng_directory_handle
*handle
=
178 lttng::utils::container_of(ref
, <tng_directory_handle::ref
);
180 if (handle
->destroy_cb
) {
181 handle
->destroy_cb(handle
, handle
->destroy_cb_data
);
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
);
196 struct lttng_directory_handle
*
197 lttng_directory_handle_copy(const struct lttng_directory_handle
*handle
)
199 struct lttng_directory_handle
*new_handle
= nullptr;
201 if (handle
->dirfd
== AT_FDCWD
) {
202 new_handle
= lttng_directory_handle_create_from_dirfd(AT_FDCWD
);
204 const int new_dirfd
= dup(handle
->dirfd
);
206 if (new_dirfd
== -1) {
207 PERROR("Failed to duplicate directory file descriptor of directory handle");
210 new_handle
= lttng_directory_handle_create_from_dirfd(new_dirfd
);
211 if (!new_handle
&& close(new_dirfd
)) {
212 PERROR("Failed to close directory file descriptor of directory handle");
219 bool lttng_directory_handle_equals(const struct lttng_directory_handle
*lhs
,
220 const struct lttng_directory_handle
*rhs
)
222 return lhs
->directory_inode
== rhs
->directory_inode
;
225 static void lttng_directory_handle_invalidate(struct lttng_directory_handle
*handle
)
230 int lttng_directory_handle_stat(const struct lttng_directory_handle
*handle
,
234 return fstatat(handle
->dirfd
, path
, st
, 0);
237 bool lttng_directory_handle_uses_fd(const struct lttng_directory_handle
*handle
)
239 return handle
->dirfd
!= AT_FDCWD
;
242 static int lttng_directory_handle_mkdir(const struct lttng_directory_handle
*handle
,
246 return mkdirat(handle
->dirfd
, path
, mode
);
249 static int lttng_directory_handle_open(const struct lttng_directory_handle
*handle
,
250 const char *filename
,
254 return openat(handle
->dirfd
, filename
, flags
, mode
);
257 static int _run_as_open(const struct lttng_directory_handle
*handle
,
258 const char *filename
,
264 return run_as_openat(handle
->dirfd
, filename
, flags
, mode
, uid
, gid
);
267 static int _run_as_unlink(const struct lttng_directory_handle
*handle
,
268 const char *filename
,
272 return run_as_unlinkat(handle
->dirfd
, filename
, uid
, gid
);
275 static int lttng_directory_handle_unlink(const struct lttng_directory_handle
*handle
,
276 const char *filename
)
278 return unlinkat(handle
->dirfd
, filename
, 0);
281 static int _run_as_mkdir(const struct lttng_directory_handle
*handle
,
287 return run_as_mkdirat(handle
->dirfd
, path
, mode
, uid
, gid
);
290 static int _run_as_mkdir_recursive(const struct lttng_directory_handle
*handle
,
296 return run_as_mkdirat_recursive(handle
->dirfd
, path
, mode
, uid
, gid
);
299 static int _lttng_directory_handle_rename(const struct lttng_directory_handle
*old_handle
,
300 const char *old_name
,
301 const struct lttng_directory_handle
*new_handle
,
302 const char *new_name
)
304 return renameat(old_handle
->dirfd
, old_name
, new_handle
->dirfd
, new_name
);
307 static int _run_as_rename(const struct lttng_directory_handle
*old_handle
,
308 const char *old_name
,
309 const struct lttng_directory_handle
*new_handle
,
310 const char *new_name
,
314 return run_as_renameat(old_handle
->dirfd
, old_name
, new_handle
->dirfd
, new_name
, uid
, gid
);
317 static DIR *lttng_directory_handle_opendir(const struct lttng_directory_handle
*handle
,
320 DIR *dir_stream
= nullptr;
321 int fd
= openat(handle
->dirfd
, path
, O_RDONLY
);
327 dir_stream
= fdopendir(fd
);
331 PERROR("Failed to open directory stream");
334 PERROR("Failed to close file descriptor to %s", path
);
343 static int lttng_directory_handle_rmdir(const struct lttng_directory_handle
*handle
,
346 int ret
= unlinkat(handle
->dirfd
, name
, AT_REMOVEDIR
);
348 PERROR("Failed to remove directory `%s`", name
);
355 _run_as_rmdir(const struct lttng_directory_handle
*handle
, const char *name
, uid_t uid
, gid_t gid
)
357 return run_as_rmdirat(handle
->dirfd
, name
, uid
, gid
);
360 static int _run_as_rmdir_recursive(const struct lttng_directory_handle
*handle
,
366 return run_as_rmdirat_recursive(handle
->dirfd
, name
, uid
, gid
, flags
);
369 #else /* HAVE_DIRFD */
371 static int get_full_path(const struct lttng_directory_handle
*handle
,
372 const char *subdirectory
,
377 const bool subdirectory_is_absolute
= subdirectory
&& *subdirectory
== '/';
378 const char *const base
= subdirectory_is_absolute
? subdirectory
: handle
->base_path
;
379 const char *const end
= subdirectory
&& !subdirectory_is_absolute
? subdirectory
: NULL
;
380 const size_t base_len
= strlen(base
);
381 const size_t end_len
= end
? strlen(end
) : 0;
382 const bool add_separator_slash
= end
&& base
[base_len
- 1] != '/';
383 const bool add_trailing_slash
= end
&& end
[end_len
- 1] != '/';
385 ret
= snprintf(fullpath
,
389 add_separator_slash
? "/" : "",
391 add_trailing_slash
? "/" : "");
392 if (ret
== -1 || ret
>= size
) {
393 ERR("Failed to format subdirectory from directory handle");
402 static struct lttng_directory_handle
*_lttng_directory_handle_create(char *path
)
404 struct lttng_directory_handle
*handle
= zmalloc
<lttng_directory_handle
>();
409 urcu_ref_init(&handle
->ref
);
410 handle
->base_path
= path
;
415 struct lttng_directory_handle
*lttng_directory_handle_create(const char *path
)
418 const char *cwd
= "";
419 size_t cwd_len
, path_len
;
420 char cwd_buf
[LTTNG_PATH_MAX
] = {};
421 char handle_buf
[LTTNG_PATH_MAX
] = {};
422 struct lttng_directory_handle
*new_handle
= NULL
;
423 bool add_cwd_slash
= false, add_trailing_slash
= false;
424 const struct lttng_directory_handle cwd_handle
= {
425 .base_path
= handle_buf
,
428 path_len
= path
? strlen(path
) : 0;
429 add_trailing_slash
= path
&& path
[path_len
- 1] != '/';
430 if (!path
|| (path
&& *path
!= '/')) {
431 cwd
= getcwd(cwd_buf
, sizeof(cwd_buf
));
433 PERROR("Failed to initialize directory handle, can't get current working directory");
437 cwd_len
= strlen(cwd
);
439 ERR("Failed to initialize directory handle, current working directory path has a length of 0");
443 add_cwd_slash
= cwd
[cwd_len
- 1] != '/';
446 ret
= snprintf(handle_buf
,
450 add_cwd_slash
? "/" : "",
452 add_trailing_slash
? "/" : "");
453 if (ret
== -1 || ret
>= LTTNG_PATH_MAX
) {
454 ERR("Failed to initialize directory handle, failed to format directory path");
458 new_handle
= lttng_directory_handle_create_from_handle(path
, &cwd_handle
);
463 struct lttng_directory_handle
*
464 lttng_directory_handle_create_from_handle(const char *path
,
465 const struct lttng_directory_handle
*ref_handle
)
468 size_t path_len
, handle_path_len
;
469 bool add_trailing_slash
;
470 struct stat stat_buf
;
471 struct lttng_directory_handle
*new_handle
= NULL
;
472 char *new_path
= NULL
;
474 LTTNG_ASSERT(ref_handle
&& ref_handle
->base_path
);
476 ret
= lttng_directory_handle_stat(ref_handle
, path
, &stat_buf
);
478 PERROR("Failed to create directory handle");
480 } else if (!S_ISDIR(stat_buf
.st_mode
)) {
481 char full_path
[LTTNG_PATH_MAX
];
483 /* Best effort for logging purposes. */
484 ret
= get_full_path(ref_handle
, path
, full_path
, sizeof(full_path
));
489 ERR("Failed to initialize directory handle to \"%s\": not a directory", full_path
);
493 new_handle
= lttng_directory_handle_copy(ref_handle
);
497 path_len
= strlen(path
);
499 ERR("Failed to initialize directory handle: provided path is an empty string");
504 new_path
= strdup(path
);
508 /* Takes ownership of new_path. */
509 new_handle
= _lttng_directory_handle_create(new_path
);
514 add_trailing_slash
= path
[path_len
- 1] != '/';
516 handle_path_len
= strlen(ref_handle
->base_path
) + path_len
+ !!add_trailing_slash
;
517 if (handle_path_len
>= LTTNG_PATH_MAX
) {
518 ERR("Failed to initialize directory handle as the resulting path's length (%zu bytes) exceeds the maximal allowed length (%d bytes)",
523 new_path
= zmalloc
<char>(handle_path_len
);
525 PERROR("Failed to initialize directory handle");
529 ret
= sprintf(new_handle
->base_path
,
531 ref_handle
->base_path
,
533 add_trailing_slash
? "/" : "");
534 if (ret
== -1 || ret
>= handle_path_len
) {
535 ERR("Failed to initialize directory handle: path formatting failed");
538 new_handle
= _lttng_directory_handle_create(new_path
);
545 struct lttng_directory_handle
*lttng_directory_handle_create_from_dirfd(int dirfd
)
547 LTTNG_ASSERT(dirfd
== AT_FDCWD
);
548 return lttng_directory_handle_create(NULL
);
551 static void lttng_directory_handle_release(struct urcu_ref
*ref
)
553 struct lttng_directory_handle
*handle
=
554 lttng::utils::container_of(ref
, <tng_directory_handle::ref
);
556 free(handle
->base_path
);
557 lttng_directory_handle_invalidate(handle
);
561 struct lttng_directory_handle
*
562 lttng_directory_handle_copy(const struct lttng_directory_handle
*handle
)
564 struct lttng_directory_handle
*new_handle
= NULL
;
565 char *new_path
= NULL
;
567 if (handle
->base_path
) {
568 new_path
= strdup(handle
->base_path
);
573 new_handle
= _lttng_directory_handle_create(new_path
);
578 bool lttng_directory_handle_equals(const struct lttng_directory_handle
*lhs
,
579 const struct lttng_directory_handle
*rhs
)
581 return strcmp(lhs
->base_path
, rhs
->base_path
) == 0;
584 static void lttng_directory_handle_invalidate(struct lttng_directory_handle
*handle
)
586 handle
->base_path
= NULL
;
589 int lttng_directory_handle_stat(const struct lttng_directory_handle
*handle
,
590 const char *subdirectory
,
594 char fullpath
[LTTNG_PATH_MAX
];
596 ret
= get_full_path(handle
, subdirectory
, fullpath
, sizeof(fullpath
));
602 ret
= stat(fullpath
, st
);
607 bool lttng_directory_handle_uses_fd(const struct lttng_directory_handle
*handle
)
612 static int lttng_directory_handle_mkdir(const struct lttng_directory_handle
*handle
,
613 const char *subdirectory
,
617 char fullpath
[LTTNG_PATH_MAX
];
619 ret
= get_full_path(handle
, subdirectory
, fullpath
, sizeof(fullpath
));
625 ret
= mkdir(fullpath
, mode
);
630 static int lttng_directory_handle_open(const struct lttng_directory_handle
*handle
,
631 const char *filename
,
636 char fullpath
[LTTNG_PATH_MAX
];
638 ret
= get_full_path(handle
, filename
, fullpath
, sizeof(fullpath
));
644 ret
= open(fullpath
, flags
, mode
);
649 static int lttng_directory_handle_unlink(const struct lttng_directory_handle
*handle
,
650 const char *filename
)
653 char fullpath
[LTTNG_PATH_MAX
];
655 ret
= get_full_path(handle
, filename
, fullpath
, sizeof(fullpath
));
661 ret
= unlink(fullpath
);
666 static int _run_as_mkdir(const struct lttng_directory_handle
*handle
,
673 char fullpath
[LTTNG_PATH_MAX
];
675 ret
= get_full_path(handle
, path
, fullpath
, sizeof(fullpath
));
681 ret
= run_as_mkdir(fullpath
, mode
, uid
, gid
);
686 static int _run_as_open(const struct lttng_directory_handle
*handle
,
687 const char *filename
,
694 char fullpath
[LTTNG_PATH_MAX
];
696 ret
= get_full_path(handle
, filename
, fullpath
, sizeof(fullpath
));
702 ret
= run_as_open(fullpath
, flags
, mode
, uid
, gid
);
707 static int _run_as_unlink(const struct lttng_directory_handle
*handle
,
708 const char *filename
,
713 char fullpath
[LTTNG_PATH_MAX
];
715 ret
= get_full_path(handle
, filename
, fullpath
, sizeof(fullpath
));
721 ret
= run_as_unlink(fullpath
, uid
, gid
);
726 static int _run_as_mkdir_recursive(const struct lttng_directory_handle
*handle
,
733 char fullpath
[LTTNG_PATH_MAX
];
735 ret
= get_full_path(handle
, path
, fullpath
, sizeof(fullpath
));
741 ret
= run_as_mkdir_recursive(fullpath
, mode
, uid
, gid
);
746 static int _lttng_directory_handle_rename(const struct lttng_directory_handle
*old_handle
,
747 const char *old_name
,
748 const struct lttng_directory_handle
*new_handle
,
749 const char *new_name
)
752 char old_fullpath
[LTTNG_PATH_MAX
];
753 char new_fullpath
[LTTNG_PATH_MAX
];
755 ret
= get_full_path(old_handle
, old_name
, old_fullpath
, sizeof(old_fullpath
));
760 ret
= get_full_path(new_handle
, new_name
, new_fullpath
, sizeof(new_fullpath
));
766 ret
= rename(old_fullpath
, new_fullpath
);
771 static int _run_as_rename(const struct lttng_directory_handle
*old_handle
,
772 const char *old_name
,
773 const struct lttng_directory_handle
*new_handle
,
774 const char *new_name
,
779 char old_fullpath
[LTTNG_PATH_MAX
];
780 char new_fullpath
[LTTNG_PATH_MAX
];
782 ret
= get_full_path(old_handle
, old_name
, old_fullpath
, sizeof(old_fullpath
));
787 ret
= get_full_path(new_handle
, new_name
, new_fullpath
, sizeof(new_fullpath
));
793 ret
= run_as_rename(old_fullpath
, new_fullpath
, uid
, gid
);
798 static DIR *lttng_directory_handle_opendir(const struct lttng_directory_handle
*handle
,
802 DIR *dir_stream
= NULL
;
803 char fullpath
[LTTNG_PATH_MAX
];
805 ret
= get_full_path(handle
, path
, fullpath
, sizeof(fullpath
));
811 dir_stream
= opendir(fullpath
);
816 static int lttng_directory_handle_rmdir(const struct lttng_directory_handle
*handle
,
820 char fullpath
[LTTNG_PATH_MAX
];
822 ret
= get_full_path(handle
, name
, fullpath
, sizeof(fullpath
));
828 ret
= rmdir(fullpath
);
834 _run_as_rmdir(const struct lttng_directory_handle
*handle
, const char *name
, uid_t uid
, gid_t gid
)
837 char fullpath
[LTTNG_PATH_MAX
];
839 ret
= get_full_path(handle
, name
, fullpath
, sizeof(fullpath
));
845 ret
= run_as_rmdir(fullpath
, uid
, gid
);
850 static int _run_as_rmdir_recursive(const struct lttng_directory_handle
*handle
,
857 char fullpath
[LTTNG_PATH_MAX
];
859 ret
= get_full_path(handle
, name
, fullpath
, sizeof(fullpath
));
865 ret
= run_as_rmdir_recursive(fullpath
, uid
, gid
, flags
);
870 #endif /* HAVE_DIRFD */
872 /* Common implementation. */
875 * On some filesystems (e.g. nfs), mkdir will validate access rights before
876 * checking for the existence of the path element. This means that on a setup
877 * where "/home/" is a mounted NFS share, and running as an unpriviledged user,
878 * recursively creating a path of the form "/home/my_user/trace/" will fail with
879 * EACCES on mkdir("/home", ...).
881 * Checking the path for existence allows us to work around this behaviour.
883 static int create_directory_check_exists(const struct lttng_directory_handle
*handle
,
890 ret
= lttng_directory_handle_stat(handle
, path
, &st
);
892 if (S_ISDIR(st
.st_mode
)) {
893 /* Directory exists, skip. */
896 /* Exists, but is not a directory. */
901 } else if (errno
!= ENOENT
) {
906 * Let mkdir handle other errors as the caller expects mkdir
909 ret
= lttng_directory_handle_mkdir(handle
, path
, mode
);
914 static int create_directory_recursive(const struct lttng_directory_handle
*handle
,
918 char *p
, tmp
[LTTNG_PATH_MAX
];
924 ret
= lttng_strncpy(tmp
, path
, sizeof(tmp
));
926 ERR("Failed to create directory: provided path's length (%zu bytes) exceeds the maximal allowed length (%zu bytes)",
933 if (tmp
[len
- 1] == '/') {
937 for (p
= tmp
+ 1; *p
; p
++) {
940 if (tmp
[strlen(tmp
) - 1] == '.' && tmp
[strlen(tmp
) - 2] == '.' &&
941 tmp
[strlen(tmp
) - 3] == '/') {
942 ERR("Using '/../' is not permitted in the trace path (%s)", tmp
);
946 ret
= create_directory_check_exists(handle
, tmp
, mode
);
948 if (errno
!= EACCES
) {
949 PERROR("Failed to create directory \"%s\"", path
);
958 ret
= create_directory_check_exists(handle
, tmp
, mode
);
960 PERROR("mkdirat recursive last element");
967 bool lttng_directory_handle_get(struct lttng_directory_handle
*handle
)
969 return urcu_ref_get_unless_zero(&handle
->ref
);
972 void lttng_directory_handle_put(struct lttng_directory_handle
*handle
)
977 LTTNG_ASSERT(handle
->ref
.refcount
);
978 urcu_ref_put(&handle
->ref
, lttng_directory_handle_release
);
981 int lttng_directory_handle_create_subdirectory_as_user(const struct lttng_directory_handle
*handle
,
982 const char *subdirectory
,
984 const struct lttng_credentials
*creds
)
989 /* Run as current user. */
990 ret
= create_directory_check_exists(handle
, subdirectory
, mode
);
992 ret
= _run_as_mkdir(handle
,
995 lttng_credentials_get_uid(creds
),
996 lttng_credentials_get_gid(creds
));
1002 int lttng_directory_handle_create_subdirectory_recursive_as_user(
1003 const struct lttng_directory_handle
*handle
,
1004 const char *subdirectory_path
,
1006 const struct lttng_credentials
*creds
)
1011 /* Run as current user. */
1012 ret
= create_directory_recursive(handle
, subdirectory_path
, mode
);
1014 ret
= _run_as_mkdir_recursive(handle
,
1017 lttng_credentials_get_uid(creds
),
1018 lttng_credentials_get_gid(creds
));
1024 int lttng_directory_handle_create_subdirectory(const struct lttng_directory_handle
*handle
,
1025 const char *subdirectory
,
1028 return lttng_directory_handle_create_subdirectory_as_user(
1029 handle
, subdirectory
, mode
, nullptr);
1032 int lttng_directory_handle_create_subdirectory_recursive(
1033 const struct lttng_directory_handle
*handle
, const char *subdirectory_path
, mode_t mode
)
1035 return lttng_directory_handle_create_subdirectory_recursive_as_user(
1036 handle
, subdirectory_path
, mode
, nullptr);
1039 int lttng_directory_handle_open_file_as_user(const struct lttng_directory_handle
*handle
,
1040 const char *filename
,
1043 const struct lttng_credentials
*creds
)
1048 /* Run as current user. */
1049 ret
= lttng_directory_handle_open(handle
, filename
, flags
, mode
);
1051 ret
= _run_as_open(handle
,
1055 lttng_credentials_get_uid(creds
),
1056 lttng_credentials_get_gid(creds
));
1061 int lttng_directory_handle_open_file(const struct lttng_directory_handle
*handle
,
1062 const char *filename
,
1066 return lttng_directory_handle_open_file_as_user(handle
, filename
, flags
, mode
, nullptr);
1069 int lttng_directory_handle_unlink_file_as_user(const struct lttng_directory_handle
*handle
,
1070 const char *filename
,
1071 const struct lttng_credentials
*creds
)
1076 /* Run as current user. */
1077 ret
= lttng_directory_handle_unlink(handle
, filename
);
1079 ret
= _run_as_unlink(handle
,
1081 lttng_credentials_get_uid(creds
),
1082 lttng_credentials_get_gid(creds
));
1087 int lttng_directory_handle_unlink_file(const struct lttng_directory_handle
*handle
,
1088 const char *filename
)
1090 return lttng_directory_handle_unlink_file_as_user(handle
, filename
, nullptr);
1093 int lttng_directory_handle_rename(const struct lttng_directory_handle
*old_handle
,
1094 const char *old_name
,
1095 const struct lttng_directory_handle
*new_handle
,
1096 const char *new_name
)
1098 return lttng_directory_handle_rename_as_user(
1099 old_handle
, old_name
, new_handle
, new_name
, nullptr);
1102 int lttng_directory_handle_rename_as_user(const struct lttng_directory_handle
*old_handle
,
1103 const char *old_name
,
1104 const struct lttng_directory_handle
*new_handle
,
1105 const char *new_name
,
1106 const struct lttng_credentials
*creds
)
1111 /* Run as current user. */
1112 ret
= _lttng_directory_handle_rename(old_handle
, old_name
, new_handle
, new_name
);
1114 ret
= _run_as_rename(old_handle
,
1118 lttng_credentials_get_uid(creds
),
1119 lttng_credentials_get_gid(creds
));
1124 int lttng_directory_handle_remove_subdirectory(const struct lttng_directory_handle
*handle
,
1127 return lttng_directory_handle_remove_subdirectory_as_user(handle
, name
, nullptr);
1130 int lttng_directory_handle_remove_subdirectory_as_user(const struct lttng_directory_handle
*handle
,
1132 const struct lttng_credentials
*creds
)
1137 /* Run as current user. */
1138 ret
= lttng_directory_handle_rmdir(handle
, name
);
1140 ret
= _run_as_rmdir(handle
,
1142 lttng_credentials_get_uid(creds
),
1143 lttng_credentials_get_gid(creds
));
1149 struct rmdir_frame
{
1150 ssize_t parent_frame_idx
;
1153 /* Size including '\0'. */
1158 static void rmdir_frame_fini(void *data
)
1161 struct rmdir_frame
*frame
= (rmdir_frame
*) data
;
1163 ret
= closedir(frame
->dir
);
1165 PERROR("Failed to close directory stream");
1170 remove_directory_recursive(const struct lttng_directory_handle
*handle
, const char *path
, int flags
)
1173 struct lttng_dynamic_array frames
;
1174 size_t current_frame_idx
= 0;
1175 struct rmdir_frame initial_frame
= {
1176 .parent_frame_idx
= -1,
1177 .dir
= lttng_directory_handle_opendir(handle
, path
),
1179 .path_size
= strlen(path
) + 1,
1181 struct lttng_dynamic_buffer current_path
;
1182 const char separator
= '/';
1184 lttng_dynamic_buffer_init(¤t_path
);
1185 lttng_dynamic_array_init(&frames
, sizeof(struct rmdir_frame
), rmdir_frame_fini
);
1188 ~(LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
|
1189 LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG
)) {
1190 ERR("Unknown flags %d", flags
);
1195 if (!initial_frame
.dir
) {
1196 if (flags
& LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
&& errno
== ENOENT
) {
1197 DBG("Cannot rmdir \"%s\": root does not exist", path
);
1201 PERROR("Failed to rmdir \"%s\"", path
);
1207 ret
= lttng_dynamic_array_add_element(&frames
, &initial_frame
);
1209 ERR("Failed to push context frame during recursive directory removal");
1210 rmdir_frame_fini(&initial_frame
);
1214 ret
= lttng_dynamic_buffer_append(¤t_path
, path
, initial_frame
.path_size
);
1216 ERR("Failed to set initial path during recursive directory removal");
1221 while (lttng_dynamic_array_get_count(&frames
) > 0) {
1222 struct dirent
*entry
;
1223 struct rmdir_frame
*current_frame
=
1224 (rmdir_frame
*) lttng_dynamic_array_get_element(&frames
, current_frame_idx
);
1226 LTTNG_ASSERT(current_frame
->dir
);
1227 ret
= lttng_dynamic_buffer_set_size(¤t_path
, current_frame
->path_size
);
1229 current_path
.data
[current_path
.size
- 1] = '\0';
1231 while ((entry
= readdir(current_frame
->dir
))) {
1234 if (!strcmp(entry
->d_name
, ".") || !strcmp(entry
->d_name
, "..")) {
1238 /* Set current_path to the entry's path. */
1239 ret
= lttng_dynamic_buffer_set_size(¤t_path
, current_path
.size
- 1);
1241 ret
= lttng_dynamic_buffer_append(
1242 ¤t_path
, &separator
, sizeof(separator
));
1246 ret
= lttng_dynamic_buffer_append(
1247 ¤t_path
, entry
->d_name
, strlen(entry
->d_name
) + 1);
1252 if (lttng_directory_handle_stat(handle
, current_path
.data
, &st
)) {
1253 if ((flags
& LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
) &&
1257 PERROR("Failed to stat \"%s\"", current_path
.data
);
1262 if (!S_ISDIR(st
.st_mode
)) {
1263 if (flags
& LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
) {
1264 current_frame
->empty
= false;
1267 /* Not empty, abort. */
1268 DBG("Directory \"%s\" is not empty; refusing to remove directory",
1274 struct rmdir_frame new_frame
= {
1275 .parent_frame_idx
= (ssize_t
) current_frame_idx
,
1276 .dir
= lttng_directory_handle_opendir(handle
,
1279 .path_size
= current_path
.size
,
1282 if (!new_frame
.dir
) {
1283 if (flags
& LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
&&
1285 DBG("Non-existing directory stream during recursive directory removal");
1288 PERROR("Failed to open directory stream during recursive directory removal");
1293 ret
= lttng_dynamic_array_add_element(&frames
, &new_frame
);
1295 ERR("Failed to push context frame during recursive directory removal");
1296 rmdir_frame_fini(&new_frame
);
1299 current_frame_idx
++;
1300 /* We break iteration on readdir. */
1308 /* Pop rmdir frame. */
1309 if (current_frame
->empty
) {
1310 ret
= lttng_directory_handle_rmdir(handle
, current_path
.data
);
1312 if ((flags
& LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG
) ||
1314 PERROR("Failed to remove \"%s\" during recursive directory removal",
1318 DBG("Non-existing directory stream during recursive directory removal");
1320 } else if (current_frame
->parent_frame_idx
>= 0) {
1321 struct rmdir_frame
*parent_frame
;
1323 parent_frame
= (rmdir_frame
*) lttng_dynamic_array_get_element(
1324 &frames
, current_frame
->parent_frame_idx
);
1325 LTTNG_ASSERT(parent_frame
);
1326 parent_frame
->empty
= false;
1328 ret
= lttng_dynamic_array_remove_element(&frames
, current_frame_idx
);
1330 ERR("Failed to pop context frame during recursive directory removal");
1333 current_frame_idx
--;
1336 lttng_dynamic_array_reset(&frames
);
1337 lttng_dynamic_buffer_reset(¤t_path
);
1341 int lttng_directory_handle_remove_subdirectory_recursive(
1342 const struct lttng_directory_handle
*handle
, const char *name
, int flags
)
1344 return lttng_directory_handle_remove_subdirectory_recursive_as_user(
1345 handle
, name
, nullptr, flags
);
1348 int lttng_directory_handle_remove_subdirectory_recursive_as_user(
1349 const struct lttng_directory_handle
*handle
,
1351 const struct lttng_credentials
*creds
,
1357 /* Run as current user. */
1358 ret
= remove_directory_recursive(handle
, name
, flags
);
1360 ret
= _run_as_rmdir_recursive(handle
,
1362 lttng_credentials_get_uid(creds
),
1363 lttng_credentials_get_gid(creds
),