directory-handle: print `errno` reason on `unlinkat()` error
[lttng-tools.git] / src / common / compat / directory-handle.c
CommitLineData
a42089bd
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>
fcde531a 24#include <common/dynamic-array.h>
a42089bd
JG
25
26#include <assert.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29#include <fcntl.h>
30#include <unistd.h>
fcde531a 31#include <dirent.h>
a42089bd 32
a4ee1b7e
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 */
a42089bd
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);
a0c5463e 51static
a4ee1b7e
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
fcde531a
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,
58ec7253 88 uid_t uid, gid_t gid, int flags);
fcde531a 89static
a0c5463e 90void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle);
a42089bd
JG
91
92#ifdef COMPAT_DIRFD
93
94LTTNG_HIDDEN
d44a713b 95int lttng_directory_handle_init(struct lttng_directory_handle *new_handle,
a42089bd 96 const char *path)
d44a713b
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)
a42089bd
JG
112{
113 int ret;
114
115 if (!path) {
d44a713b 116 ret = lttng_directory_handle_copy(handle, new_handle);
a42089bd
JG
117 goto end;
118 }
d44a713b
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);
a42089bd
JG
125 if (ret == -1) {
126 PERROR("Failed to initialize directory handle to \"%s\"", path);
127 goto end;
128 }
d44a713b 129 new_handle->dirfd = ret;
a42089bd
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
a0c5463e
JG
148 if (handle->dirfd == AT_FDCWD || handle->dirfd == -1) {
149 goto end;
a42089bd
JG
150 }
151 ret = close(handle->dirfd);
152 if (ret == -1) {
153 PERROR("Failed to close directory file descriptor of directory handle");
84a93c08 154 abort();
a42089bd 155 }
a0c5463e
JG
156end:
157 lttng_directory_handle_invalidate(handle);
a42089bd
JG
158}
159
1197e651
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
a0c5463e
JG
178static
179void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle)
180{
181 handle->dirfd = -1;
182}
183
a42089bd
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
a4ee1b7e
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)
a42089bd
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
fcde531a
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{
c7389bae
FD
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;
fcde531a
JG
301}
302
303static
304int _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
310static
311int _run_as_rmdir_recursive(
312 const struct lttng_directory_handle *handle, const char *name,
58ec7253 313 uid_t uid, gid_t gid, int flags)
fcde531a 314{
58ec7253 315 return run_as_rmdirat_recursive(handle->dirfd, name, uid, gid, flags);
fcde531a
JG
316}
317
a42089bd
JG
318#else /* COMPAT_DIRFD */
319
d44a713b
JG
320static
321int get_full_path(const struct lttng_directory_handle *handle,
322 const char *subdirectory, char *fullpath, size_t size)
323{
324 int ret;
fcde531a
JG
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 ? "/" : "");
d44a713b
JG
341 if (ret == -1 || ret >= size) {
342 ERR("Failed to format subdirectory from directory handle");
343 ret = -1;
fcde531a 344 goto end;
d44a713b
JG
345 }
346 ret = 0;
fcde531a 347end:
d44a713b
JG
348 return ret;
349}
350
a42089bd
JG
351LTTNG_HIDDEN
352int lttng_directory_handle_init(struct lttng_directory_handle *handle,
353 const char *path)
354{
355 int ret;
fcde531a 356 const char *cwd = "";
d44a713b
JG
357 size_t cwd_len, path_len;
358 char cwd_buf[LTTNG_PATH_MAX] = {};
359 char handle_buf[LTTNG_PATH_MAX] = {};
fcde531a 360 bool add_cwd_slash = false, add_trailing_slash = false;
d44a713b
JG
361 const struct lttng_directory_handle cwd_handle = {
362 .base_path = handle_buf,
363 };
364
d44a713b 365 path_len = path ? strlen(path) : 0;
d44a713b 366 add_trailing_slash = path && path[path_len - 1] != '/';
fcde531a
JG
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 }
d44a713b
JG
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;
a42089bd 391 }
fcde531a 392
d44a713b
JG
393 ret = lttng_directory_handle_init_from_handle(handle, path,
394 &cwd_handle);
395end:
396 return ret;
397}
a42089bd 398
d44a713b
JG
399LTTNG_HIDDEN
400int 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;
a42089bd 408
d44a713b
JG
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';
a42089bd 423 }
d44a713b
JG
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;
a42089bd
JG
445 }
446
d44a713b
JG
447 add_trailing_slash = path[path_len - 1] != '/';
448
449 handle_path_len = strlen(handle->base_path) + path_len +
450 !!add_trailing_slash;
a42089bd
JG
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 }
d44a713b
JG
457 new_handle->base_path = zmalloc(handle_path_len);
458 if (!new_handle->base_path) {
a42089bd
JG
459 PERROR("Failed to initialize directory handle");
460 ret = -1;
461 goto end;
462 }
463
d44a713b
JG
464 ret = sprintf(new_handle->base_path, "%s%s%s",
465 handle->base_path,
466 path,
467 add_trailing_slash ? "/" : "");
a42089bd
JG
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 }
473end:
474 return ret;
475}
476
477LTTNG_HIDDEN
478int 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
485LTTNG_HIDDEN
486void lttng_directory_handle_fini(struct lttng_directory_handle *handle)
487{
488 free(handle->base_path);
a0c5463e 489 lttng_directory_handle_invalidate(handle);
a42089bd
JG
490}
491
1197e651
JG
492LTTNG_HIDDEN
493int 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
a0c5463e
JG
500static
501void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle)
502{
503 handle->base_path = NULL;
504}
505
a42089bd
JG
506static
507int 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);
520end:
521 return ret;
522}
523
524static
525int 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);
538end:
539 return ret;
540}
541
a4ee1b7e
JG
542static
543int 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);
556end:
557 return ret;
558}
559
560static
561int 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);
575end:
576 return ret;
577}
578
a42089bd
JG
579static
580int _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);
593end:
594 return ret;
595}
596
a4ee1b7e
JG
597static
598int _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);
612end:
613 return ret;
614}
615
616static
617int _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);
630end:
631 return ret;
632}
633
a42089bd
JG
634static
635int _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);
648end:
649 return ret;
650}
651
fcde531a
JG
652static
653int _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);
677end:
678 return ret;
679}
680
681static
682int _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);
705end:
706 return ret;
707}
708
709static
710DIR *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);
724end:
725 return dir_stream;
726}
727
728static
729int 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);
742end:
743 return ret;
744}
745
746static
747int _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);
760end:
761 return ret;
762}
763
764static
765int _run_as_rmdir_recursive(
766 const struct lttng_directory_handle *handle, const char *name,
58ec7253 767 uid_t uid, gid_t gid, int flags)
fcde531a
JG
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
58ec7253 778 ret = run_as_rmdir_recursive(fullpath, uid, gid, flags);
fcde531a
JG
779end:
780 return ret;
781}
782
a42089bd
JG
783#endif /* COMPAT_DIRFD */
784
fcde531a
JG
785/* Common implementation. */
786
a42089bd
JG
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 */
796static
797int 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 }
fcde531a
JG
814 } else if (errno != ENOENT) {
815 goto end;
a42089bd
JG
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);
823end:
824 return ret;
825}
826
a0c5463e
JG
827LTTNG_HIDDEN
828struct lttng_directory_handle
829lttng_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
a42089bd
JG
837static
838int 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 }
888error:
889 return ret;
890}
891
892LTTNG_HIDDEN
893int lttng_directory_handle_create_subdirectory_as_user(
894 const struct lttng_directory_handle *handle,
895 const char *subdirectory,
6992f9f1 896 mode_t mode, const struct lttng_credentials *creds)
a42089bd
JG
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
912LTTNG_HIDDEN
913int lttng_directory_handle_create_subdirectory_recursive_as_user(
914 const struct lttng_directory_handle *handle,
915 const char *subdirectory_path,
6992f9f1 916 mode_t mode, const struct lttng_credentials *creds)
a42089bd
JG
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
932LTTNG_HIDDEN
933int 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
942LTTNG_HIDDEN
943int 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}
a4ee1b7e
JG
951
952LTTNG_HIDDEN
953int 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
972LTTNG_HIDDEN
973int 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
982LTTNG_HIDDEN
983int 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
999LTTNG_HIDDEN
1000int 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}
fcde531a
JG
1007
1008LTTNG_HIDDEN
1009int 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
1019LTTNG_HIDDEN
1020int 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
1040LTTNG_HIDDEN
1041int 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
1049LTTNG_HIDDEN
1050int 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
1066struct rmdir_frame {
58ec7253 1067 ssize_t parent_frame_idx;
fcde531a 1068 DIR *dir;
58ec7253 1069 bool empty;
fcde531a
JG
1070 /* Size including '\0'. */
1071 size_t path_size;
1072};
1073
1074static
1075void rmdir_frame_fini(void *data)
1076{
e0d70799 1077 int ret;
fcde531a
JG
1078 struct rmdir_frame *frame = data;
1079
e0d70799
FD
1080 ret = closedir(frame->dir);
1081 if (ret == -1) {
1082 PERROR("Failed to close directory stream");
1083 }
fcde531a
JG
1084}
1085
1086static
1087int remove_directory_recursive(const struct lttng_directory_handle *handle,
58ec7253 1088 const char *path, int flags)
fcde531a
JG
1089{
1090 int ret;
1091 struct lttng_dynamic_array frames;
1092 size_t current_frame_idx = 0;
1093 struct rmdir_frame initial_frame = {
58ec7253 1094 .parent_frame_idx = -1,
fcde531a 1095 .dir = lttng_directory_handle_opendir(handle, path),
58ec7253 1096 .empty = true,
fcde531a
JG
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),
58ec7253
MD
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 }
fcde531a
JG
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
58ec7253
MD
1133 ret = lttng_dynamic_buffer_append(
1134 &current_path, path, initial_frame.path_size);
1135 if (ret) {
fcde531a
JG
1136 ERR("Failed to set initial path during recursive directory removal");
1137 ret = -1;
1138 goto end;
58ec7253 1139 }
fcde531a 1140
58ec7253 1141 while (lttng_dynamic_array_get_count(&frames) > 0) {
fcde531a
JG
1142 struct dirent *entry;
1143 struct rmdir_frame *current_frame =
58ec7253
MD
1144 lttng_dynamic_array_get_element(
1145 &frames, current_frame_idx);
fcde531a 1146
58ec7253
MD
1147 assert(current_frame->dir);
1148 ret = lttng_dynamic_buffer_set_size(
1149 &current_path, current_frame->path_size);
fcde531a
JG
1150 assert(!ret);
1151 current_path.data[current_path.size - 1] = '\0';
1152
1153 while ((entry = readdir(current_frame->dir))) {
1154 struct stat st;
fcde531a 1155
58ec7253
MD
1156 if (!strcmp(entry->d_name, ".") ||
1157 !strcmp(entry->d_name, "..")) {
fcde531a
JG
1158 continue;
1159 }
1160
1161 /* Set current_path to the entry's path. */
58ec7253
MD
1162 ret = lttng_dynamic_buffer_set_size(
1163 &current_path, current_path.size - 1);
fcde531a
JG
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
58ec7253
MD
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 }
fcde531a
JG
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)) {
58ec7253
MD
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;
fcde531a 1231 }
58ec7253
MD
1232 }
1233 if (entry) {
1234 continue;
1235 }
fcde531a 1236
58ec7253
MD
1237 /* Pop rmdir frame. */
1238 if (current_frame->empty) {
1239 ret = lttng_directory_handle_rmdir(
1240 handle, current_path.data);
fcde531a 1241 if (ret) {
58ec7253
MD
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");
fcde531a 1249 }
58ec7253
MD
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--;
fcde531a
JG
1265 }
1266end:
1267 lttng_dynamic_array_reset(&frames);
1268 lttng_dynamic_buffer_reset(&current_path);
1269 return ret;
1270}
1271
1272LTTNG_HIDDEN
1273int lttng_directory_handle_remove_subdirectory_recursive(
1274 const struct lttng_directory_handle *handle,
58ec7253
MD
1275 const char *name,
1276 int flags)
fcde531a
JG
1277{
1278 return lttng_directory_handle_remove_subdirectory_recursive_as_user(
58ec7253 1279 handle, name, NULL, flags);
fcde531a
JG
1280}
1281
1282LTTNG_HIDDEN
1283int lttng_directory_handle_remove_subdirectory_recursive_as_user(
1284 const struct lttng_directory_handle *handle,
1285 const char *name,
58ec7253
MD
1286 const struct lttng_credentials *creds,
1287 int flags)
fcde531a
JG
1288{
1289 int ret;
1290
1291 if (!creds) {
1292 /* Run as current user. */
58ec7253 1293 ret = remove_directory_recursive(handle, name, flags);
fcde531a
JG
1294 } else {
1295 ret = _run_as_rmdir_recursive(handle, name, creds->uid,
58ec7253 1296 creds->gid, flags);
fcde531a
JG
1297 }
1298 return ret;
1299}
This page took 0.078786 seconds and 4 git commands to generate.