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