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