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