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