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