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