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