Fix: futex wait: handle spurious futex wakeups
[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/error.hpp>
10 #include <common/macros.hpp>
11 #include <common/runas.hpp>
12 #include <common/credentials.hpp>
13 #include <lttng/constant.h>
14 #include <common/dynamic-array.hpp>
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 = 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 = 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 lttng::utils::container_of(ref, &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<lttng_directory_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<char>(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 lttng::utils::container_of(ref, &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 namespace {
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 } /* namespace */
1163
1164 static
1165 void rmdir_frame_fini(void *data)
1166 {
1167 int ret;
1168 struct rmdir_frame *frame = (rmdir_frame *) data;
1169
1170 ret = closedir(frame->dir);
1171 if (ret == -1) {
1172 PERROR("Failed to close directory stream");
1173 }
1174 }
1175
1176 static
1177 int remove_directory_recursive(const struct lttng_directory_handle *handle,
1178 const char *path, int flags)
1179 {
1180 int ret;
1181 struct lttng_dynamic_array frames;
1182 size_t current_frame_idx = 0;
1183 struct rmdir_frame initial_frame = {
1184 .parent_frame_idx = -1,
1185 .dir = lttng_directory_handle_opendir(handle, path),
1186 .empty = true,
1187 .path_size = strlen(path) + 1,
1188 };
1189 struct lttng_dynamic_buffer current_path;
1190 const char separator = '/';
1191
1192 lttng_dynamic_buffer_init(&current_path);
1193 lttng_dynamic_array_init(&frames, sizeof(struct rmdir_frame),
1194 rmdir_frame_fini);
1195
1196 if (flags & ~(LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG |
1197 LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG)) {
1198 ERR("Unknown flags %d", flags);
1199 ret = -1;
1200 goto end;
1201 }
1202
1203 if (!initial_frame.dir) {
1204 if (flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG &&
1205 errno == ENOENT) {
1206 DBG("Cannot rmdir \"%s\": root does not exist", path);
1207 ret = 0;
1208 goto end;
1209 } else {
1210 PERROR("Failed to rmdir \"%s\"", path);
1211 ret = -1;
1212 goto end;
1213 }
1214 }
1215
1216 ret = lttng_dynamic_array_add_element(&frames, &initial_frame);
1217 if (ret) {
1218 ERR("Failed to push context frame during recursive directory removal");
1219 rmdir_frame_fini(&initial_frame);
1220 goto end;
1221 }
1222
1223 ret = lttng_dynamic_buffer_append(
1224 &current_path, path, initial_frame.path_size);
1225 if (ret) {
1226 ERR("Failed to set initial path during recursive directory removal");
1227 ret = -1;
1228 goto end;
1229 }
1230
1231 while (lttng_dynamic_array_get_count(&frames) > 0) {
1232 struct dirent *entry;
1233 struct rmdir_frame *current_frame =
1234 (rmdir_frame *) lttng_dynamic_array_get_element(
1235 &frames, current_frame_idx);
1236
1237 LTTNG_ASSERT(current_frame->dir);
1238 ret = lttng_dynamic_buffer_set_size(
1239 &current_path, current_frame->path_size);
1240 LTTNG_ASSERT(!ret);
1241 current_path.data[current_path.size - 1] = '\0';
1242
1243 while ((entry = readdir(current_frame->dir))) {
1244 struct stat st;
1245
1246 if (!strcmp(entry->d_name, ".") ||
1247 !strcmp(entry->d_name, "..")) {
1248 continue;
1249 }
1250
1251 /* Set current_path to the entry's path. */
1252 ret = lttng_dynamic_buffer_set_size(
1253 &current_path, current_path.size - 1);
1254 LTTNG_ASSERT(!ret);
1255 ret = lttng_dynamic_buffer_append(&current_path,
1256 &separator, sizeof(separator));
1257 if (ret) {
1258 goto end;
1259 }
1260 ret = lttng_dynamic_buffer_append(&current_path,
1261 entry->d_name,
1262 strlen(entry->d_name) + 1);
1263 if (ret) {
1264 goto end;
1265 }
1266
1267 if (lttng_directory_handle_stat(
1268 handle, current_path.data, &st)) {
1269 if ((flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG) &&
1270 errno == ENOENT) {
1271 break;
1272 }
1273 PERROR("Failed to stat \"%s\"",
1274 current_path.data);
1275 ret = -1;
1276 goto end;
1277 }
1278
1279 if (!S_ISDIR(st.st_mode)) {
1280 if (flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG) {
1281 current_frame->empty = false;
1282 break;
1283 } else {
1284 /* Not empty, abort. */
1285 DBG("Directory \"%s\" is not empty; refusing to remove directory",
1286 current_path.data);
1287 ret = -1;
1288 goto end;
1289 }
1290 } else {
1291 struct rmdir_frame new_frame = {
1292 .parent_frame_idx = (ssize_t) current_frame_idx,
1293 .dir = lttng_directory_handle_opendir(
1294 handle,
1295 current_path.data),
1296 .empty = true,
1297 .path_size = current_path.size,
1298 };
1299
1300 if (!new_frame.dir) {
1301 if (flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG &&
1302 errno == ENOENT) {
1303 DBG("Non-existing directory stream during recursive directory removal");
1304 break;
1305 } else {
1306 PERROR("Failed to open directory stream during recursive directory removal");
1307 ret = -1;
1308 goto end;
1309 }
1310 }
1311 ret = lttng_dynamic_array_add_element(
1312 &frames, &new_frame);
1313 if (ret) {
1314 ERR("Failed to push context frame during recursive directory removal");
1315 rmdir_frame_fini(&new_frame);
1316 goto end;
1317 }
1318 current_frame_idx++;
1319 /* We break iteration on readdir. */
1320 break;
1321 }
1322 }
1323 if (entry) {
1324 continue;
1325 }
1326
1327 /* Pop rmdir frame. */
1328 if (current_frame->empty) {
1329 ret = lttng_directory_handle_rmdir(
1330 handle, current_path.data);
1331 if (ret) {
1332 if ((flags & LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG) ||
1333 errno != ENOENT) {
1334 PERROR("Failed to remove \"%s\" during recursive directory removal",
1335 current_path.data);
1336 goto end;
1337 }
1338 DBG("Non-existing directory stream during recursive directory removal");
1339 }
1340 } else if (current_frame->parent_frame_idx >= 0) {
1341 struct rmdir_frame *parent_frame;
1342
1343 parent_frame = (rmdir_frame *) lttng_dynamic_array_get_element(&frames,
1344 current_frame->parent_frame_idx);
1345 LTTNG_ASSERT(parent_frame);
1346 parent_frame->empty = false;
1347 }
1348 ret = lttng_dynamic_array_remove_element(
1349 &frames, current_frame_idx);
1350 if (ret) {
1351 ERR("Failed to pop context frame during recursive directory removal");
1352 goto end;
1353 }
1354 current_frame_idx--;
1355 }
1356 end:
1357 lttng_dynamic_array_reset(&frames);
1358 lttng_dynamic_buffer_reset(&current_path);
1359 return ret;
1360 }
1361
1362 int lttng_directory_handle_remove_subdirectory_recursive(
1363 const struct lttng_directory_handle *handle,
1364 const char *name,
1365 int flags)
1366 {
1367 return lttng_directory_handle_remove_subdirectory_recursive_as_user(
1368 handle, name, NULL, flags);
1369 }
1370
1371 int lttng_directory_handle_remove_subdirectory_recursive_as_user(
1372 const struct lttng_directory_handle *handle,
1373 const char *name,
1374 const struct lttng_credentials *creds,
1375 int flags)
1376 {
1377 int ret;
1378
1379 if (!creds) {
1380 /* Run as current user. */
1381 ret = remove_directory_recursive(handle, name, flags);
1382 } else {
1383 ret = _run_as_rmdir_recursive(handle, name, lttng_credentials_get_uid(creds),
1384 lttng_credentials_get_gid(creds), flags);
1385 }
1386 return ret;
1387 }
This page took 0.089074 seconds and 4 git commands to generate.