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