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