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
= NULL
;
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
= NULL
;
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
= NULL
;
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(handle
, subdirectory
, mode
, NULL
);
1031 int lttng_directory_handle_create_subdirectory_recursive(
1032 const struct lttng_directory_handle
*handle
, const char *subdirectory_path
, mode_t mode
)
1034 return lttng_directory_handle_create_subdirectory_recursive_as_user(
1035 handle
, subdirectory_path
, mode
, NULL
);
1038 int lttng_directory_handle_open_file_as_user(const struct lttng_directory_handle
*handle
,
1039 const char *filename
,
1042 const struct lttng_credentials
*creds
)
1047 /* Run as current user. */
1048 ret
= lttng_directory_handle_open(handle
, filename
, flags
, mode
);
1050 ret
= _run_as_open(handle
,
1054 lttng_credentials_get_uid(creds
),
1055 lttng_credentials_get_gid(creds
));
1060 int lttng_directory_handle_open_file(const struct lttng_directory_handle
*handle
,
1061 const char *filename
,
1065 return lttng_directory_handle_open_file_as_user(handle
, filename
, flags
, mode
, NULL
);
1068 int lttng_directory_handle_unlink_file_as_user(const struct lttng_directory_handle
*handle
,
1069 const char *filename
,
1070 const struct lttng_credentials
*creds
)
1075 /* Run as current user. */
1076 ret
= lttng_directory_handle_unlink(handle
, filename
);
1078 ret
= _run_as_unlink(handle
,
1080 lttng_credentials_get_uid(creds
),
1081 lttng_credentials_get_gid(creds
));
1086 int lttng_directory_handle_unlink_file(const struct lttng_directory_handle
*handle
,
1087 const char *filename
)
1089 return lttng_directory_handle_unlink_file_as_user(handle
, filename
, NULL
);
1092 int lttng_directory_handle_rename(const struct lttng_directory_handle
*old_handle
,
1093 const char *old_name
,
1094 const struct lttng_directory_handle
*new_handle
,
1095 const char *new_name
)
1097 return lttng_directory_handle_rename_as_user(
1098 old_handle
, old_name
, new_handle
, new_name
, NULL
);
1101 int lttng_directory_handle_rename_as_user(const struct lttng_directory_handle
*old_handle
,
1102 const char *old_name
,
1103 const struct lttng_directory_handle
*new_handle
,
1104 const char *new_name
,
1105 const struct lttng_credentials
*creds
)
1110 /* Run as current user. */
1111 ret
= _lttng_directory_handle_rename(old_handle
, old_name
, new_handle
, new_name
);
1113 ret
= _run_as_rename(old_handle
,
1117 lttng_credentials_get_uid(creds
),
1118 lttng_credentials_get_gid(creds
));
1123 int lttng_directory_handle_remove_subdirectory(const struct lttng_directory_handle
*handle
,
1126 return lttng_directory_handle_remove_subdirectory_as_user(handle
, name
, NULL
);
1129 int lttng_directory_handle_remove_subdirectory_as_user(const struct lttng_directory_handle
*handle
,
1131 const struct lttng_credentials
*creds
)
1136 /* Run as current user. */
1137 ret
= lttng_directory_handle_rmdir(handle
, name
);
1139 ret
= _run_as_rmdir(handle
,
1141 lttng_credentials_get_uid(creds
),
1142 lttng_credentials_get_gid(creds
));
1148 struct rmdir_frame
{
1149 ssize_t parent_frame_idx
;
1152 /* Size including '\0'. */
1157 static void rmdir_frame_fini(void *data
)
1160 struct rmdir_frame
*frame
= (rmdir_frame
*) data
;
1162 ret
= closedir(frame
->dir
);
1164 PERROR("Failed to close directory stream");
1169 remove_directory_recursive(const struct lttng_directory_handle
*handle
, const char *path
, int flags
)
1172 struct lttng_dynamic_array frames
;
1173 size_t current_frame_idx
= 0;
1174 struct rmdir_frame initial_frame
= {
1175 .parent_frame_idx
= -1,
1176 .dir
= lttng_directory_handle_opendir(handle
, path
),
1178 .path_size
= strlen(path
) + 1,
1180 struct lttng_dynamic_buffer current_path
;
1181 const char separator
= '/';
1183 lttng_dynamic_buffer_init(¤t_path
);
1184 lttng_dynamic_array_init(&frames
, sizeof(struct rmdir_frame
), rmdir_frame_fini
);
1187 ~(LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
|
1188 LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG
)) {
1189 ERR("Unknown flags %d", flags
);
1194 if (!initial_frame
.dir
) {
1195 if (flags
& LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
&& errno
== ENOENT
) {
1196 DBG("Cannot rmdir \"%s\": root does not exist", path
);
1200 PERROR("Failed to rmdir \"%s\"", path
);
1206 ret
= lttng_dynamic_array_add_element(&frames
, &initial_frame
);
1208 ERR("Failed to push context frame during recursive directory removal");
1209 rmdir_frame_fini(&initial_frame
);
1213 ret
= lttng_dynamic_buffer_append(¤t_path
, path
, initial_frame
.path_size
);
1215 ERR("Failed to set initial path during recursive directory removal");
1220 while (lttng_dynamic_array_get_count(&frames
) > 0) {
1221 struct dirent
*entry
;
1222 struct rmdir_frame
*current_frame
=
1223 (rmdir_frame
*) lttng_dynamic_array_get_element(&frames
, current_frame_idx
);
1225 LTTNG_ASSERT(current_frame
->dir
);
1226 ret
= lttng_dynamic_buffer_set_size(¤t_path
, current_frame
->path_size
);
1228 current_path
.data
[current_path
.size
- 1] = '\0';
1230 while ((entry
= readdir(current_frame
->dir
))) {
1233 if (!strcmp(entry
->d_name
, ".") || !strcmp(entry
->d_name
, "..")) {
1237 /* Set current_path to the entry's path. */
1238 ret
= lttng_dynamic_buffer_set_size(¤t_path
, current_path
.size
- 1);
1240 ret
= lttng_dynamic_buffer_append(
1241 ¤t_path
, &separator
, sizeof(separator
));
1245 ret
= lttng_dynamic_buffer_append(
1246 ¤t_path
, entry
->d_name
, strlen(entry
->d_name
) + 1);
1251 if (lttng_directory_handle_stat(handle
, current_path
.data
, &st
)) {
1252 if ((flags
& LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
) &&
1256 PERROR("Failed to stat \"%s\"", current_path
.data
);
1261 if (!S_ISDIR(st
.st_mode
)) {
1262 if (flags
& LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
) {
1263 current_frame
->empty
= false;
1266 /* Not empty, abort. */
1267 DBG("Directory \"%s\" is not empty; refusing to remove directory",
1273 struct rmdir_frame new_frame
= {
1274 .parent_frame_idx
= (ssize_t
) current_frame_idx
,
1275 .dir
= lttng_directory_handle_opendir(handle
,
1278 .path_size
= current_path
.size
,
1281 if (!new_frame
.dir
) {
1282 if (flags
& LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG
&&
1284 DBG("Non-existing directory stream during recursive directory removal");
1287 PERROR("Failed to open directory stream during recursive directory removal");
1292 ret
= lttng_dynamic_array_add_element(&frames
, &new_frame
);
1294 ERR("Failed to push context frame during recursive directory removal");
1295 rmdir_frame_fini(&new_frame
);
1298 current_frame_idx
++;
1299 /* We break iteration on readdir. */
1307 /* Pop rmdir frame. */
1308 if (current_frame
->empty
) {
1309 ret
= lttng_directory_handle_rmdir(handle
, current_path
.data
);
1311 if ((flags
& LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG
) ||
1313 PERROR("Failed to remove \"%s\" during recursive directory removal",
1317 DBG("Non-existing directory stream during recursive directory removal");
1319 } else if (current_frame
->parent_frame_idx
>= 0) {
1320 struct rmdir_frame
*parent_frame
;
1322 parent_frame
= (rmdir_frame
*) lttng_dynamic_array_get_element(
1323 &frames
, current_frame
->parent_frame_idx
);
1324 LTTNG_ASSERT(parent_frame
);
1325 parent_frame
->empty
= false;
1327 ret
= lttng_dynamic_array_remove_element(&frames
, current_frame_idx
);
1329 ERR("Failed to pop context frame during recursive directory removal");
1332 current_frame_idx
--;
1335 lttng_dynamic_array_reset(&frames
);
1336 lttng_dynamic_buffer_reset(¤t_path
);
1340 int lttng_directory_handle_remove_subdirectory_recursive(
1341 const struct lttng_directory_handle
*handle
, const char *name
, int flags
)
1343 return lttng_directory_handle_remove_subdirectory_recursive_as_user(
1344 handle
, name
, NULL
, flags
);
1347 int lttng_directory_handle_remove_subdirectory_recursive_as_user(
1348 const struct lttng_directory_handle
*handle
,
1350 const struct lttng_credentials
*creds
,
1356 /* Run as current user. */
1357 ret
= remove_directory_recursive(handle
, name
, flags
);
1359 ret
= _run_as_rmdir_recursive(handle
,
1361 lttng_credentials_get_uid(creds
),
1362 lttng_credentials_get_gid(creds
),