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