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