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