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