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