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