directory-handle: make lttng_directory_handle_stat public
[lttng-tools.git] / src / common / compat / directory-handle.c
CommitLineData
18710679
JG
1/*
2 * Copyright (C) 2019 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2 only,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 */
17
18#include <common/compat/directory-handle.h>
19#include <common/error.h>
20#include <common/macros.h>
21#include <common/runas.h>
22#include <common/credentials.h>
23#include <lttng/constant.h>
93bed9fe 24#include <common/dynamic-array.h>
18710679
JG
25
26#include <assert.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29#include <fcntl.h>
30#include <unistd.h>
93bed9fe 31#include <dirent.h>
18710679 32
2912cead
JG
33/*
34 * This compatibility layer shares a common "base" that is implemented
35 * in terms of an internal API. This file contains two implementations
36 * of the internal API below.
37 */
18710679 38static
18710679
JG
39int lttng_directory_handle_mkdir(
40 const struct lttng_directory_handle *handle,
41 const char *path, mode_t mode);
42static
43int _run_as_mkdir(const struct lttng_directory_handle *handle, const char *path,
44 mode_t mode, uid_t uid, gid_t gid);
45static
46int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle,
47 const char *path, mode_t mode, uid_t uid, gid_t gid);
46307ffe 48static
2912cead
JG
49int lttng_directory_handle_open(const struct lttng_directory_handle *handle,
50 const char *filename, int flags, mode_t mode);
51static
52int _run_as_open(const struct lttng_directory_handle *handle,
53 const char *filename,
54 int flags, mode_t mode, uid_t uid, gid_t gid);
55static
56int lttng_directory_handle_unlink(
57 const struct lttng_directory_handle *handle,
58 const char *filename);
59static
60int _run_as_unlink(const struct lttng_directory_handle *handle,
61 const char *filename, uid_t uid, gid_t gid);
62static
93bed9fe
JG
63int _lttng_directory_handle_rename(
64 const struct lttng_directory_handle *old_handle,
65 const char *old_name,
66 const struct lttng_directory_handle *new_handle,
67 const char *new_name);
68static
69int _run_as_rename(const struct lttng_directory_handle *old_handle,
70 const char *old_name,
71 const struct lttng_directory_handle *new_handle,
72 const char *new_name, uid_t uid, gid_t gid);
73static
74DIR *lttng_directory_handle_opendir(const struct lttng_directory_handle *handle,
75 const char *path);
76static
77int lttng_directory_handle_rmdir(
78 const struct lttng_directory_handle *handle, const char *name);
79static
80int _run_as_rmdir(const struct lttng_directory_handle *handle,
81 const char *name, uid_t uid, gid_t gid);
82static
83int _run_as_rmdir_recursive(
84 const struct lttng_directory_handle *handle, const char *name,
f75c5439 85 uid_t uid, gid_t gid, int flags);
93bed9fe 86static
46307ffe 87void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle);
cbf53d23
JG
88static
89void lttng_directory_handle_release(struct urcu_ref *ref);
18710679
JG
90
91#ifdef COMPAT_DIRFD
92
0e985513
JG
93/*
94 * Special inode number reserved to represent the "current working directory".
95 * ino_t is spec'ed as being an unsigned integral type.
96 */
97#define RESERVED_AT_FDCWD_INO \
98 ({ \
99 uint64_t reserved_val; \
100 switch (sizeof(ino_t)) { \
101 case 4: \
102 reserved_val = UINT32_MAX; \
103 break; \
104 case 8: \
105 reserved_val = UINT64_MAX; \
106 break; \
107 default: \
108 abort(); \
109 } \
110 (ino_t) reserved_val; \
111 })
112
18710679 113LTTNG_HIDDEN
cbf53d23 114struct lttng_directory_handle *lttng_directory_handle_create(const char *path)
fd774fc6
JG
115{
116 const struct lttng_directory_handle cwd_handle = {
117 .dirfd = AT_FDCWD,
118 };
119
120 /* Open a handle to the CWD if NULL is passed. */
cbf53d23 121 return lttng_directory_handle_create_from_handle(path, &cwd_handle);
fd774fc6
JG
122}
123
124LTTNG_HIDDEN
cbf53d23
JG
125struct lttng_directory_handle *lttng_directory_handle_create_from_handle(
126 const char *path,
127 const struct lttng_directory_handle *ref_handle)
18710679 128{
cbf53d23
JG
129 int dirfd;
130 struct lttng_directory_handle *handle = NULL;
18710679
JG
131
132 if (!path) {
cbf53d23 133 handle = lttng_directory_handle_copy(ref_handle);
18710679
JG
134 goto end;
135 }
fd774fc6
JG
136 if (!*path) {
137 ERR("Failed to initialize directory handle: provided path is an empty string");
fd774fc6
JG
138 goto end;
139 }
cbf53d23
JG
140
141 dirfd = openat(ref_handle->dirfd, path, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
142 if (dirfd == -1) {
18710679
JG
143 PERROR("Failed to initialize directory handle to \"%s\"", path);
144 goto end;
145 }
cbf53d23
JG
146
147 handle = lttng_directory_handle_create_from_dirfd(dirfd);
148 if (!handle) {
149 goto error_close;
150 }
18710679 151end:
cbf53d23
JG
152 return handle;
153error_close:
154 if (close(dirfd)) {
155 PERROR("Failed to close directory file descriptor");
156 }
157 return NULL;
18710679
JG
158}
159
160LTTNG_HIDDEN
cbf53d23
JG
161struct lttng_directory_handle *lttng_directory_handle_create_from_dirfd(
162 int dirfd)
18710679 163{
0e985513 164 int ret;
cbf53d23 165 struct lttng_directory_handle *handle = zmalloc(sizeof(*handle));
0e985513 166 struct stat stat_buf;
cbf53d23
JG
167
168 if (!handle) {
169 goto end;
170 }
0e985513
JG
171
172 if (dirfd != AT_FDCWD) {
173 ret = fstat(dirfd, &stat_buf);
174 if (ret) {
175 PERROR("Failed to fstat directory file descriptor %i", dirfd);
176 lttng_directory_handle_release(&handle->ref);
177 }
178 } else {
179 handle->directory_inode = RESERVED_AT_FDCWD_INO;
180 }
18710679 181 handle->dirfd = dirfd;
cbf53d23
JG
182 urcu_ref_init(&handle->ref);
183end:
184 return handle;
18710679
JG
185}
186
cbf53d23
JG
187static
188void lttng_directory_handle_release(struct urcu_ref *ref)
18710679
JG
189{
190 int ret;
cbf53d23
JG
191 struct lttng_directory_handle *handle =
192 container_of(ref, struct lttng_directory_handle, ref);
18710679 193
46307ffe
JG
194 if (handle->dirfd == AT_FDCWD || handle->dirfd == -1) {
195 goto end;
18710679
JG
196 }
197 ret = close(handle->dirfd);
198 if (ret == -1) {
199 PERROR("Failed to close directory file descriptor of directory handle");
200 }
46307ffe
JG
201end:
202 lttng_directory_handle_invalidate(handle);
cbf53d23 203 free(handle);
18710679
JG
204}
205
578e21bd 206LTTNG_HIDDEN
cbf53d23
JG
207struct lttng_directory_handle *lttng_directory_handle_copy(
208 const struct lttng_directory_handle *handle)
578e21bd 209{
cbf53d23 210 struct lttng_directory_handle *new_handle = NULL;
578e21bd
JG
211
212 if (handle->dirfd == AT_FDCWD) {
cbf53d23 213 new_handle = lttng_directory_handle_create_from_dirfd(AT_FDCWD);
578e21bd 214 } else {
cbf53d23
JG
215 const int new_dirfd = dup(handle->dirfd);
216
217 if (new_dirfd == -1) {
218 PERROR("Failed to duplicate directory file descriptor of directory handle");
219 goto end;
220 }
221 new_handle = lttng_directory_handle_create_from_dirfd(
222 new_dirfd);
223 if (!new_handle && close(new_dirfd)) {
224 PERROR("Failed to close directory file descriptor of directory handle");
578e21bd
JG
225 }
226 }
cbf53d23
JG
227end:
228 return new_handle;
578e21bd
JG
229}
230
0e985513
JG
231LTTNG_HIDDEN
232bool lttng_directory_handle_equals(const struct lttng_directory_handle *lhs,
233 const struct lttng_directory_handle *rhs)
234{
235 return lhs->directory_inode == rhs->directory_inode;
236}
237
46307ffe
JG
238static
239void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle)
240{
241 handle->dirfd = -1;
242}
243
57b14318 244LTTNG_HIDDEN
18710679
JG
245int lttng_directory_handle_stat(const struct lttng_directory_handle *handle,
246 const char *path, struct stat *st)
247{
248 return fstatat(handle->dirfd, path, st, 0);
249}
250
251static
252int lttng_directory_handle_mkdir(
253 const struct lttng_directory_handle *handle,
254 const char *path, mode_t mode)
255{
256 return mkdirat(handle->dirfd, path, mode);
257}
258
259static
2912cead
JG
260int lttng_directory_handle_open(const struct lttng_directory_handle *handle,
261 const char *filename, int flags, mode_t mode)
262{
263 return openat(handle->dirfd, filename, flags, mode);
264}
265
266static
267int _run_as_open(const struct lttng_directory_handle *handle,
268 const char *filename,
269 int flags, mode_t mode, uid_t uid, gid_t gid)
270{
271 return run_as_openat(handle->dirfd, filename, flags, mode, uid, gid);
272}
273
274static
275int _run_as_unlink(const struct lttng_directory_handle *handle,
276 const char *filename, uid_t uid, gid_t gid)
277{
278 return run_as_unlinkat(handle->dirfd, filename, uid, gid);
279}
280
281static
282int lttng_directory_handle_unlink(
283 const struct lttng_directory_handle *handle,
284 const char *filename)
285{
286 return unlinkat(handle->dirfd, filename, 0);
287}
288
289static
290int _run_as_mkdir(const struct lttng_directory_handle *handle,
291 const char *path, mode_t mode, uid_t uid, gid_t gid)
18710679
JG
292{
293 return run_as_mkdirat(handle->dirfd, path, mode, uid, gid);
294}
295
296static
297int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle,
298 const char *path, mode_t mode, uid_t uid, gid_t gid)
299{
300 return run_as_mkdirat_recursive(handle->dirfd, path, mode, uid, gid);
301}
302
93bed9fe
JG
303static
304int _lttng_directory_handle_rename(
305 const struct lttng_directory_handle *old_handle,
306 const char *old_name,
307 const struct lttng_directory_handle *new_handle,
308 const char *new_name)
309{
310 return renameat(old_handle->dirfd, old_name,
311 new_handle->dirfd, new_name);
312}
313
314static
315int _run_as_rename(const struct lttng_directory_handle *old_handle,
316 const char *old_name,
317 const struct lttng_directory_handle *new_handle,
318 const char *new_name, uid_t uid, gid_t gid)
319{
320 return run_as_renameat(old_handle->dirfd, old_name, new_handle->dirfd,
321 new_name, uid, gid);
322}
323
324static
325DIR *lttng_directory_handle_opendir(const struct lttng_directory_handle *handle,
326 const char *path)
327{
328 DIR *dir_stream = NULL;
329 int fd = openat(handle->dirfd, path, O_RDONLY);
330
331 if (fd < 0) {
332 goto end;
333 }
334
335 dir_stream = fdopendir(fd);
336 if (!dir_stream) {
337 int ret;
338
339 PERROR("Failed to open directory stream");
340 ret = close(fd);
341 if (ret) {
342 PERROR("Failed to close file descriptor to %s", path);
343 }
344 goto end;
345 }
346
347end:
348 return dir_stream;
349}
350
351static
352int lttng_directory_handle_rmdir(
353 const struct lttng_directory_handle *handle, const char *name)
354{
355 return unlinkat(handle->dirfd, name, AT_REMOVEDIR);
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
18710679
JG
373#else /* COMPAT_DIRFD */
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
18710679 420LTTNG_HIDDEN
cbf53d23 421struct lttng_directory_handle *lttng_directory_handle_create(
18710679
JG
422 const char *path)
423{
424 int ret;
93bed9fe 425 const char *cwd = "";
fd774fc6
JG
426 size_t cwd_len, path_len;
427 char cwd_buf[LTTNG_PATH_MAX] = {};
428 char handle_buf[LTTNG_PATH_MAX] = {};
cbf53d23 429 struct lttng_directory_handle *new_handle = NULL;
93bed9fe 430 bool add_cwd_slash = false, add_trailing_slash = false;
fd774fc6
JG
431 const struct lttng_directory_handle cwd_handle = {
432 .base_path = handle_buf,
433 };
434
fd774fc6 435 path_len = path ? strlen(path) : 0;
fd774fc6 436 add_trailing_slash = path && path[path_len - 1] != '/';
93bed9fe
JG
437 if (!path || (path && *path != '/')) {
438 cwd = getcwd(cwd_buf, sizeof(cwd_buf));
439 if (!cwd) {
440 PERROR("Failed to initialize directory handle, can't get current working directory");
441 ret = -1;
442 goto end;
443 }
444 cwd_len = strlen(cwd);
445 if (cwd_len == 0) {
446 ERR("Failed to initialize directory handle, current working directory path has a length of 0");
447 ret = -1;
448 goto end;
449 }
450 add_cwd_slash = cwd[cwd_len - 1] != '/';
451 }
fd774fc6
JG
452
453 ret = snprintf(handle_buf, sizeof(handle_buf), "%s%s%s%s",
454 cwd,
455 add_cwd_slash ? "/" : "",
456 path ? : "",
457 add_trailing_slash ? "/" : "");
458 if (ret == -1 || ret >= LTTNG_PATH_MAX) {
459 ERR("Failed to initialize directory handle, failed to format directory path");
460 goto end;
18710679 461 }
93bed9fe 462
cbf53d23 463 new_handle = lttng_directory_handle_create_from_handle(path, &cwd_handle);
fd774fc6 464end:
cbf53d23 465 return new_handle;
fd774fc6 466}
18710679 467
fd774fc6 468LTTNG_HIDDEN
cbf53d23
JG
469struct lttng_directory_handle *lttng_directory_handle_create_from_handle(
470 const char *path,
471 const struct lttng_directory_handle *ref_handle)
fd774fc6
JG
472{
473 int ret;
474 size_t path_len, handle_path_len;
475 bool add_trailing_slash;
476 struct stat stat_buf;
cbf53d23
JG
477 struct lttng_directory_handle *new_handle = NULL;
478 char *new_path = NULL;
18710679 479
cbf53d23 480 assert(ref_handle && ref_handle->base_path);
fd774fc6 481
cbf53d23 482 ret = lttng_directory_handle_stat(ref_handle, path, &stat_buf);
fd774fc6
JG
483 if (ret == -1) {
484 PERROR("Failed to create directory handle");
485 goto end;
486 } else if (!S_ISDIR(stat_buf.st_mode)) {
487 char full_path[LTTNG_PATH_MAX];
488
489 /* Best effort for logging purposes. */
cbf53d23 490 ret = get_full_path(ref_handle, path, full_path,
fd774fc6
JG
491 sizeof(full_path));
492 if (ret) {
493 full_path[0] = '\0';
18710679 494 }
fd774fc6
JG
495
496 ERR("Failed to initialize directory handle to \"%s\": not a directory",
497 full_path);
fd774fc6
JG
498 goto end;
499 }
500 if (!path) {
cbf53d23 501 new_handle = lttng_directory_handle_copy(ref_handle);
fd774fc6
JG
502 goto end;
503 }
504
505 path_len = strlen(path);
506 if (path_len == 0) {
507 ERR("Failed to initialize directory handle: provided path is an empty string");
508 ret = -1;
509 goto end;
510 }
511 if (*path == '/') {
cbf53d23
JG
512 new_path = strdup(path);
513 if (!new_path) {
514 goto end;
515 }
516 /* Takes ownership of new_path. */
517 new_handle = _lttng_directory_handle_create(new_path);
518 new_path = NULL;
fd774fc6 519 goto end;
18710679
JG
520 }
521
fd774fc6
JG
522 add_trailing_slash = path[path_len - 1] != '/';
523
cbf53d23 524 handle_path_len = strlen(ref_handle->base_path) + path_len +
fd774fc6 525 !!add_trailing_slash;
18710679
JG
526 if (handle_path_len >= LTTNG_PATH_MAX) {
527 ERR("Failed to initialize directory handle as the resulting path's length (%zu bytes) exceeds the maximal allowed length (%d bytes)",
528 handle_path_len, LTTNG_PATH_MAX);
18710679
JG
529 goto end;
530 }
cbf53d23
JG
531 new_path = zmalloc(handle_path_len);
532 if (!new_path) {
18710679 533 PERROR("Failed to initialize directory handle");
18710679
JG
534 goto end;
535 }
536
fd774fc6 537 ret = sprintf(new_handle->base_path, "%s%s%s",
cbf53d23 538 ref_handle->base_path,
fd774fc6
JG
539 path,
540 add_trailing_slash ? "/" : "");
18710679
JG
541 if (ret == -1 || ret >= handle_path_len) {
542 ERR("Failed to initialize directory handle: path formatting failed");
18710679
JG
543 goto end;
544 }
cbf53d23
JG
545 new_handle = _lttng_directory_handle_create(new_path);
546 new_path = NULL;
18710679 547end:
cbf53d23
JG
548 free(new_path);
549 return new_handle;
18710679
JG
550}
551
552LTTNG_HIDDEN
cbf53d23
JG
553struct lttng_directory_handle *lttng_directory_handle_create_from_dirfd(
554 int dirfd)
18710679
JG
555{
556 assert(dirfd == AT_FDCWD);
cbf53d23 557 return lttng_directory_handle_create(NULL);
18710679
JG
558}
559
cbf53d23
JG
560static
561void lttng_directory_handle_release(struct urcu_ref *ref)
18710679 562{
cbf53d23
JG
563 struct lttng_directory_handle *handle =
564 container_of(ref, struct lttng_directory_handle, ref);
565
18710679 566 free(handle->base_path);
46307ffe 567 lttng_directory_handle_invalidate(handle);
cbf53d23 568 free(handle);
18710679
JG
569}
570
578e21bd 571LTTNG_HIDDEN
cbf53d23
JG
572struct lttng_directory_handle *lttng_directory_handle_copy(
573 const struct lttng_directory_handle *handle)
578e21bd 574{
cbf53d23
JG
575 struct lttng_directory_handle *new_handle = NULL;
576 char *new_path = NULL;
577
578 if (handle->base_path) {
579 new_path = strdup(handle->base_path);
580 if (!new_path) {
581 goto end;
582 }
583 }
584 new_handle = _lttng_directory_handle_create(new_path);
585end:
586 return new_handle;
578e21bd
JG
587}
588
0e985513
JG
589LTTNG_HIDDEN
590bool lttng_directory_handle_equals(const struct lttng_directory_handle *lhs,
591 const struct lttng_directory_handle *rhs)
592{
593 return strcmp(lhs->path, rhs->path) == 0;
594}
595
46307ffe
JG
596static
597void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle)
598{
599 handle->base_path = NULL;
600}
601
57b14318 602LTTNG_HIDDEN
18710679
JG
603int lttng_directory_handle_stat(const struct lttng_directory_handle *handle,
604 const char *subdirectory, struct stat *st)
605{
606 int ret;
607 char fullpath[LTTNG_PATH_MAX];
608
609 ret = get_full_path(handle, subdirectory, fullpath, sizeof(fullpath));
610 if (ret) {
611 errno = ENOMEM;
612 goto end;
613 }
614
615 ret = stat(fullpath, st);
616end:
617 return ret;
618}
619
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
18710679
JG
879#endif /* COMPAT_DIRFD */
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
931 assert(path);
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
978LTTNG_HIDDEN
979bool lttng_directory_handle_get(struct lttng_directory_handle *handle)
980{
981 return urcu_ref_get_unless_zero(&handle->ref);
982}
983
984LTTNG_HIDDEN
985void lttng_directory_handle_put(struct lttng_directory_handle *handle)
986{
987 if (!handle) {
988 return;
989 }
990 assert(handle->ref.refcount);
991 urcu_ref_put(&handle->ref, lttng_directory_handle_release);
992}
993
18710679
JG
994LTTNG_HIDDEN
995int lttng_directory_handle_create_subdirectory_as_user(
996 const struct lttng_directory_handle *handle,
997 const char *subdirectory,
69e3a560 998 mode_t mode, const struct lttng_credentials *creds)
18710679
JG
999{
1000 int ret;
1001
1002 if (!creds) {
1003 /* Run as current user. */
1004 ret = create_directory_check_exists(handle,
1005 subdirectory, mode);
1006 } else {
1007 ret = _run_as_mkdir(handle, subdirectory,
1008 mode, creds->uid, creds->gid);
1009 }
1010
1011 return ret;
1012}
1013
1014LTTNG_HIDDEN
1015int lttng_directory_handle_create_subdirectory_recursive_as_user(
1016 const struct lttng_directory_handle *handle,
1017 const char *subdirectory_path,
69e3a560 1018 mode_t mode, const struct lttng_credentials *creds)
18710679
JG
1019{
1020 int ret;
1021
1022 if (!creds) {
1023 /* Run as current user. */
1024 ret = create_directory_recursive(handle,
1025 subdirectory_path, mode);
1026 } else {
1027 ret = _run_as_mkdir_recursive(handle, subdirectory_path,
1028 mode, creds->uid, creds->gid);
1029 }
1030
1031 return ret;
1032}
1033
1034LTTNG_HIDDEN
1035int lttng_directory_handle_create_subdirectory(
1036 const struct lttng_directory_handle *handle,
1037 const char *subdirectory,
1038 mode_t mode)
1039{
1040 return lttng_directory_handle_create_subdirectory_as_user(
1041 handle, subdirectory, mode, NULL);
1042}
1043
1044LTTNG_HIDDEN
1045int lttng_directory_handle_create_subdirectory_recursive(
1046 const struct lttng_directory_handle *handle,
1047 const char *subdirectory_path,
1048 mode_t mode)
1049{
1050 return lttng_directory_handle_create_subdirectory_recursive_as_user(
1051 handle, subdirectory_path, mode, NULL);
1052}
2912cead
JG
1053
1054LTTNG_HIDDEN
1055int lttng_directory_handle_open_file_as_user(
1056 const struct lttng_directory_handle *handle,
1057 const char *filename,
1058 int flags, mode_t mode,
1059 const struct lttng_credentials *creds)
1060{
1061 int ret;
1062
1063 if (!creds) {
1064 /* Run as current user. */
1065 ret = lttng_directory_handle_open(handle, filename, flags,
1066 mode);
1067 } else {
1068 ret = _run_as_open(handle, filename, flags, mode,
1069 creds->uid, creds->gid);
1070 }
1071 return ret;
1072}
1073
1074LTTNG_HIDDEN
1075int lttng_directory_handle_open_file(
1076 const struct lttng_directory_handle *handle,
1077 const char *filename,
1078 int flags, mode_t mode)
1079{
1080 return lttng_directory_handle_open_file_as_user(handle, filename, flags,
1081 mode, NULL);
1082}
1083
1084LTTNG_HIDDEN
1085int lttng_directory_handle_unlink_file_as_user(
1086 const struct lttng_directory_handle *handle,
1087 const char *filename,
1088 const struct lttng_credentials *creds)
1089{
1090 int ret;
1091
1092 if (!creds) {
1093 /* Run as current user. */
1094 ret = lttng_directory_handle_unlink(handle, filename);
1095 } else {
1096 ret = _run_as_unlink(handle, filename, creds->uid, creds->gid);
1097 }
1098 return ret;
1099}
1100
1101LTTNG_HIDDEN
1102int lttng_directory_handle_unlink_file(
1103 const struct lttng_directory_handle *handle,
1104 const char *filename)
1105{
1106 return lttng_directory_handle_unlink_file_as_user(handle,
1107 filename, NULL);
1108}
93bed9fe
JG
1109
1110LTTNG_HIDDEN
1111int lttng_directory_handle_rename(
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{
1117 return lttng_directory_handle_rename_as_user(old_handle, old_name,
1118 new_handle, new_name, NULL);
1119}
1120
1121LTTNG_HIDDEN
1122int lttng_directory_handle_rename_as_user(
1123 const struct lttng_directory_handle *old_handle,
1124 const char *old_name,
1125 const struct lttng_directory_handle *new_handle,
1126 const char *new_name,
1127 const struct lttng_credentials *creds)
1128{
1129 int ret;
1130
1131 if (!creds) {
1132 /* Run as current user. */
1133 ret = _lttng_directory_handle_rename(old_handle,
1134 old_name, new_handle, new_name);
1135 } else {
1136 ret = _run_as_rename(old_handle, old_name, new_handle,
1137 new_name, creds->uid, creds->gid);
1138 }
1139 return ret;
1140}
1141
1142LTTNG_HIDDEN
1143int lttng_directory_handle_remove_subdirectory(
1144 const struct lttng_directory_handle *handle,
1145 const char *name)
1146{
1147 return lttng_directory_handle_remove_subdirectory_as_user(handle, name,
1148 NULL);
1149}
1150
1151LTTNG_HIDDEN
1152int lttng_directory_handle_remove_subdirectory_as_user(
1153 const struct lttng_directory_handle *handle,
1154 const char *name,
1155 const struct lttng_credentials *creds)
1156{
1157 int ret;
1158
1159 if (!creds) {
1160 /* Run as current user. */
1161 ret = lttng_directory_handle_rmdir(handle, name);
1162 } else {
1163 ret = _run_as_rmdir(handle, name, creds->uid, creds->gid);
1164 }
1165 return ret;
1166}
1167
1168struct rmdir_frame {
f75c5439 1169 ssize_t parent_frame_idx;
93bed9fe 1170 DIR *dir;
f75c5439 1171 bool empty;
93bed9fe
JG
1172 /* Size including '\0'. */
1173 size_t path_size;
1174};
1175
1176static
1177void rmdir_frame_fini(void *data)
1178{
41066401 1179 int ret;
93bed9fe
JG
1180 struct rmdir_frame *frame = data;
1181
41066401
FD
1182 ret = closedir(frame->dir);
1183 if (ret == -1) {
1184 PERROR("Failed to close directory stream");
1185 }
93bed9fe
JG
1186}
1187
1188static
1189int remove_directory_recursive(const struct lttng_directory_handle *handle,
f75c5439 1190 const char *path, int flags)
93bed9fe
JG
1191{
1192 int ret;
1193 struct lttng_dynamic_array frames;
1194 size_t current_frame_idx = 0;
1195 struct rmdir_frame initial_frame = {
f75c5439 1196 .parent_frame_idx = -1,
93bed9fe 1197 .dir = lttng_directory_handle_opendir(handle, path),
f75c5439 1198 .empty = true,
93bed9fe
JG
1199 .path_size = strlen(path) + 1,
1200 };
1201 struct lttng_dynamic_buffer current_path;
1202 const char separator = '/';
1203
1204 lttng_dynamic_buffer_init(&current_path);
1205 lttng_dynamic_array_init(&frames, sizeof(struct rmdir_frame),
f75c5439
MD
1206 rmdir_frame_fini);
1207
1208 if (flags & ~(LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG |
1209 LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG)) {
1210 ERR("Unknown flags %d", flags);
1211 ret = -1;
1212 goto end;
1213 }
1214
1215 if (!initial_frame.dir) {
1216 if (flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG &&
1217 errno == ENOENT) {
1218 DBG("Cannot rmdir \"%s\": root does not exist", path);
1219 ret = 0;
1220 goto end;
1221 } else {
1222 PERROR("Failed to rmdir \"%s\"", path);
1223 ret = -1;
1224 goto end;
1225 }
1226 }
93bed9fe
JG
1227
1228 ret = lttng_dynamic_array_add_element(&frames, &initial_frame);
1229 if (ret) {
1230 ERR("Failed to push context frame during recursive directory removal");
1231 rmdir_frame_fini(&initial_frame);
1232 goto end;
1233 }
1234
f75c5439
MD
1235 ret = lttng_dynamic_buffer_append(
1236 &current_path, path, initial_frame.path_size);
1237 if (ret) {
93bed9fe
JG
1238 ERR("Failed to set initial path during recursive directory removal");
1239 ret = -1;
1240 goto end;
f75c5439 1241 }
93bed9fe 1242
f75c5439 1243 while (lttng_dynamic_array_get_count(&frames) > 0) {
93bed9fe
JG
1244 struct dirent *entry;
1245 struct rmdir_frame *current_frame =
f75c5439
MD
1246 lttng_dynamic_array_get_element(
1247 &frames, current_frame_idx);
93bed9fe 1248
f75c5439
MD
1249 assert(current_frame->dir);
1250 ret = lttng_dynamic_buffer_set_size(
1251 &current_path, current_frame->path_size);
93bed9fe
JG
1252 assert(!ret);
1253 current_path.data[current_path.size - 1] = '\0';
1254
1255 while ((entry = readdir(current_frame->dir))) {
1256 struct stat st;
93bed9fe 1257
f75c5439
MD
1258 if (!strcmp(entry->d_name, ".") ||
1259 !strcmp(entry->d_name, "..")) {
93bed9fe
JG
1260 continue;
1261 }
1262
1263 /* Set current_path to the entry's path. */
f75c5439
MD
1264 ret = lttng_dynamic_buffer_set_size(
1265 &current_path, current_path.size - 1);
93bed9fe
JG
1266 assert(!ret);
1267 ret = lttng_dynamic_buffer_append(&current_path,
1268 &separator, sizeof(separator));
1269 if (ret) {
1270 goto end;
1271 }
1272 ret = lttng_dynamic_buffer_append(&current_path,
1273 entry->d_name,
1274 strlen(entry->d_name) + 1);
1275 if (ret) {
1276 goto end;
1277 }
1278
f75c5439
MD
1279 if (lttng_directory_handle_stat(
1280 handle, current_path.data, &st)) {
1281 if ((flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG) &&
1282 errno == ENOENT) {
1283 break;
1284 }
93bed9fe
JG
1285 PERROR("Failed to stat \"%s\"",
1286 current_path.data);
1287 ret = -1;
1288 goto end;
1289 }
1290
1291 if (!S_ISDIR(st.st_mode)) {
f75c5439
MD
1292 if (flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG) {
1293 current_frame->empty = false;
1294 break;
1295 } else {
1296 /* Not empty, abort. */
1297 DBG("Directory \"%s\" is not empty; refusing to remove directory",
1298 current_path.data);
1299 ret = -1;
1300 goto end;
1301 }
1302 } else {
1303 struct rmdir_frame new_frame = {
1304 .path_size = current_path.size,
1305 .dir = lttng_directory_handle_opendir(
1306 handle,
1307 current_path.data),
1308 .empty = true,
1309 .parent_frame_idx = current_frame_idx,
1310 };
1311
1312 if (!new_frame.dir) {
1313 if (flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG &&
1314 errno == ENOENT) {
1315 DBG("Non-existing directory stream during recursive directory removal");
1316 break;
1317 } else {
1318 PERROR("Failed to open directory stream during recursive directory removal");
1319 ret = -1;
1320 goto end;
1321 }
1322 }
1323 ret = lttng_dynamic_array_add_element(
1324 &frames, &new_frame);
1325 if (ret) {
1326 ERR("Failed to push context frame during recursive directory removal");
1327 rmdir_frame_fini(&new_frame);
1328 goto end;
1329 }
1330 current_frame_idx++;
1331 /* We break iteration on readdir. */
1332 break;
93bed9fe 1333 }
f75c5439
MD
1334 }
1335 if (entry) {
1336 continue;
1337 }
93bed9fe 1338
f75c5439
MD
1339 /* Pop rmdir frame. */
1340 if (current_frame->empty) {
1341 ret = lttng_directory_handle_rmdir(
1342 handle, current_path.data);
93bed9fe 1343 if (ret) {
f75c5439
MD
1344 if ((flags & LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG) ||
1345 errno != ENOENT) {
1346 PERROR("Failed to remove \"%s\" during recursive directory removal",
1347 current_path.data);
1348 goto end;
1349 }
1350 DBG("Non-existing directory stream during recursive directory removal");
93bed9fe 1351 }
f75c5439
MD
1352 } else if (current_frame->parent_frame_idx >= 0) {
1353 struct rmdir_frame *parent_frame;
1354
1355 parent_frame = lttng_dynamic_array_get_element(&frames,
1356 current_frame->parent_frame_idx);
1357 assert(parent_frame);
1358 parent_frame->empty = false;
1359 }
1360 ret = lttng_dynamic_array_remove_element(
1361 &frames, current_frame_idx);
1362 if (ret) {
1363 ERR("Failed to pop context frame during recursive directory removal");
1364 goto end;
1365 }
1366 current_frame_idx--;
93bed9fe
JG
1367 }
1368end:
1369 lttng_dynamic_array_reset(&frames);
1370 lttng_dynamic_buffer_reset(&current_path);
1371 return ret;
1372}
1373
1374LTTNG_HIDDEN
1375int lttng_directory_handle_remove_subdirectory_recursive(
1376 const struct lttng_directory_handle *handle,
f75c5439
MD
1377 const char *name,
1378 int flags)
93bed9fe
JG
1379{
1380 return lttng_directory_handle_remove_subdirectory_recursive_as_user(
f75c5439 1381 handle, name, NULL, flags);
93bed9fe
JG
1382}
1383
1384LTTNG_HIDDEN
1385int lttng_directory_handle_remove_subdirectory_recursive_as_user(
1386 const struct lttng_directory_handle *handle,
1387 const char *name,
f75c5439
MD
1388 const struct lttng_credentials *creds,
1389 int flags)
93bed9fe
JG
1390{
1391 int ret;
1392
1393 if (!creds) {
1394 /* Run as current user. */
f75c5439 1395 ret = remove_directory_recursive(handle, name, flags);
93bed9fe
JG
1396 } else {
1397 ret = _run_as_rmdir_recursive(handle, name, creds->uid,
f75c5439 1398 creds->gid, flags);
93bed9fe
JG
1399 }
1400 return ret;
1401}
This page took 0.084274 seconds and 4 git commands to generate.