directory-handle: add an equals method
[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
JG
38static
39int lttng_directory_handle_stat(const struct lttng_directory_handle *handle,
40 const char *path, struct stat *st);
41static
42int lttng_directory_handle_mkdir(
43 const struct lttng_directory_handle *handle,
44 const char *path, mode_t mode);
45static
46int _run_as_mkdir(const struct lttng_directory_handle *handle, const char *path,
47 mode_t mode, uid_t uid, gid_t gid);
48static
49int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle,
50 const char *path, mode_t mode, uid_t uid, gid_t gid);
46307ffe 51static
2912cead
JG
52int lttng_directory_handle_open(const struct lttng_directory_handle *handle,
53 const char *filename, int flags, mode_t mode);
54static
55int _run_as_open(const struct lttng_directory_handle *handle,
56 const char *filename,
57 int flags, mode_t mode, uid_t uid, gid_t gid);
58static
59int lttng_directory_handle_unlink(
60 const struct lttng_directory_handle *handle,
61 const char *filename);
62static
63int _run_as_unlink(const struct lttng_directory_handle *handle,
64 const char *filename, uid_t uid, gid_t gid);
65static
93bed9fe
JG
66int _lttng_directory_handle_rename(
67 const struct lttng_directory_handle *old_handle,
68 const char *old_name,
69 const struct lttng_directory_handle *new_handle,
70 const char *new_name);
71static
72int _run_as_rename(const struct lttng_directory_handle *old_handle,
73 const char *old_name,
74 const struct lttng_directory_handle *new_handle,
75 const char *new_name, uid_t uid, gid_t gid);
76static
77DIR *lttng_directory_handle_opendir(const struct lttng_directory_handle *handle,
78 const char *path);
79static
80int lttng_directory_handle_rmdir(
81 const struct lttng_directory_handle *handle, const char *name);
82static
83int _run_as_rmdir(const struct lttng_directory_handle *handle,
84 const char *name, uid_t uid, gid_t gid);
85static
86int _run_as_rmdir_recursive(
87 const struct lttng_directory_handle *handle, const char *name,
f75c5439 88 uid_t uid, gid_t gid, int flags);
93bed9fe 89static
46307ffe 90void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle);
cbf53d23
JG
91static
92void lttng_directory_handle_release(struct urcu_ref *ref);
18710679
JG
93
94#ifdef COMPAT_DIRFD
95
0e985513
JG
96/*
97 * Special inode number reserved to represent the "current working directory".
98 * ino_t is spec'ed as being an unsigned integral type.
99 */
100#define RESERVED_AT_FDCWD_INO \
101 ({ \
102 uint64_t reserved_val; \
103 switch (sizeof(ino_t)) { \
104 case 4: \
105 reserved_val = UINT32_MAX; \
106 break; \
107 case 8: \
108 reserved_val = UINT64_MAX; \
109 break; \
110 default: \
111 abort(); \
112 } \
113 (ino_t) reserved_val; \
114 })
115
18710679 116LTTNG_HIDDEN
cbf53d23 117struct lttng_directory_handle *lttng_directory_handle_create(const char *path)
fd774fc6
JG
118{
119 const struct lttng_directory_handle cwd_handle = {
120 .dirfd = AT_FDCWD,
121 };
122
123 /* Open a handle to the CWD if NULL is passed. */
cbf53d23 124 return lttng_directory_handle_create_from_handle(path, &cwd_handle);
fd774fc6
JG
125}
126
127LTTNG_HIDDEN
cbf53d23
JG
128struct lttng_directory_handle *lttng_directory_handle_create_from_handle(
129 const char *path,
130 const struct lttng_directory_handle *ref_handle)
18710679 131{
cbf53d23
JG
132 int dirfd;
133 struct lttng_directory_handle *handle = NULL;
18710679
JG
134
135 if (!path) {
cbf53d23 136 handle = lttng_directory_handle_copy(ref_handle);
18710679
JG
137 goto end;
138 }
fd774fc6
JG
139 if (!*path) {
140 ERR("Failed to initialize directory handle: provided path is an empty string");
fd774fc6
JG
141 goto end;
142 }
cbf53d23
JG
143
144 dirfd = openat(ref_handle->dirfd, path, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
145 if (dirfd == -1) {
18710679
JG
146 PERROR("Failed to initialize directory handle to \"%s\"", path);
147 goto end;
148 }
cbf53d23
JG
149
150 handle = lttng_directory_handle_create_from_dirfd(dirfd);
151 if (!handle) {
152 goto error_close;
153 }
18710679 154end:
cbf53d23
JG
155 return handle;
156error_close:
157 if (close(dirfd)) {
158 PERROR("Failed to close directory file descriptor");
159 }
160 return NULL;
18710679
JG
161}
162
163LTTNG_HIDDEN
cbf53d23
JG
164struct lttng_directory_handle *lttng_directory_handle_create_from_dirfd(
165 int dirfd)
18710679 166{
0e985513 167 int ret;
cbf53d23 168 struct lttng_directory_handle *handle = zmalloc(sizeof(*handle));
0e985513 169 struct stat stat_buf;
cbf53d23
JG
170
171 if (!handle) {
172 goto end;
173 }
0e985513
JG
174
175 if (dirfd != AT_FDCWD) {
176 ret = fstat(dirfd, &stat_buf);
177 if (ret) {
178 PERROR("Failed to fstat directory file descriptor %i", dirfd);
179 lttng_directory_handle_release(&handle->ref);
180 }
181 } else {
182 handle->directory_inode = RESERVED_AT_FDCWD_INO;
183 }
18710679 184 handle->dirfd = dirfd;
cbf53d23
JG
185 urcu_ref_init(&handle->ref);
186end:
187 return handle;
18710679
JG
188}
189
cbf53d23
JG
190static
191void lttng_directory_handle_release(struct urcu_ref *ref)
18710679
JG
192{
193 int ret;
cbf53d23
JG
194 struct lttng_directory_handle *handle =
195 container_of(ref, struct lttng_directory_handle, ref);
18710679 196
46307ffe
JG
197 if (handle->dirfd == AT_FDCWD || handle->dirfd == -1) {
198 goto end;
18710679
JG
199 }
200 ret = close(handle->dirfd);
201 if (ret == -1) {
202 PERROR("Failed to close directory file descriptor of directory handle");
203 }
46307ffe
JG
204end:
205 lttng_directory_handle_invalidate(handle);
cbf53d23 206 free(handle);
18710679
JG
207}
208
578e21bd 209LTTNG_HIDDEN
cbf53d23
JG
210struct lttng_directory_handle *lttng_directory_handle_copy(
211 const struct lttng_directory_handle *handle)
578e21bd 212{
cbf53d23 213 struct lttng_directory_handle *new_handle = NULL;
578e21bd
JG
214
215 if (handle->dirfd == AT_FDCWD) {
cbf53d23 216 new_handle = lttng_directory_handle_create_from_dirfd(AT_FDCWD);
578e21bd 217 } else {
cbf53d23
JG
218 const int new_dirfd = dup(handle->dirfd);
219
220 if (new_dirfd == -1) {
221 PERROR("Failed to duplicate directory file descriptor of directory handle");
222 goto end;
223 }
224 new_handle = lttng_directory_handle_create_from_dirfd(
225 new_dirfd);
226 if (!new_handle && close(new_dirfd)) {
227 PERROR("Failed to close directory file descriptor of directory handle");
578e21bd
JG
228 }
229 }
cbf53d23
JG
230end:
231 return new_handle;
578e21bd
JG
232}
233
0e985513
JG
234LTTNG_HIDDEN
235bool lttng_directory_handle_equals(const struct lttng_directory_handle *lhs,
236 const struct lttng_directory_handle *rhs)
237{
238 return lhs->directory_inode == rhs->directory_inode;
239}
240
46307ffe
JG
241static
242void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle)
243{
244 handle->dirfd = -1;
245}
246
18710679
JG
247static
248int lttng_directory_handle_stat(const struct lttng_directory_handle *handle,
249 const char *path, struct stat *st)
250{
251 return fstatat(handle->dirfd, path, st, 0);
252}
253
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{
596 return strcmp(lhs->path, rhs->path) == 0;
597}
598
46307ffe
JG
599static
600void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle)
601{
602 handle->base_path = NULL;
603}
604
18710679
JG
605static
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
623static
624int lttng_directory_handle_mkdir(const struct lttng_directory_handle *handle,
625 const char *subdirectory, mode_t mode)
626{
627 int ret;
628 char fullpath[LTTNG_PATH_MAX];
629
630 ret = get_full_path(handle, subdirectory, fullpath, sizeof(fullpath));
631 if (ret) {
632 errno = ENOMEM;
633 goto end;
634 }
635
636 ret = mkdir(fullpath, mode);
637end:
638 return ret;
639}
640
2912cead
JG
641static
642int lttng_directory_handle_open(const struct lttng_directory_handle *handle,
643 const char *filename, int flags, mode_t mode)
644{
645 int ret;
646 char fullpath[LTTNG_PATH_MAX];
647
648 ret = get_full_path(handle, filename, fullpath, sizeof(fullpath));
649 if (ret) {
650 errno = ENOMEM;
651 goto end;
652 }
653
654 ret = open(fullpath, flags, mode);
655end:
656 return ret;
657}
658
659static
660int lttng_directory_handle_unlink(
661 const struct lttng_directory_handle *handle,
662 const char *filename)
663{
664 int ret;
665 char fullpath[LTTNG_PATH_MAX];
666
667 ret = get_full_path(handle, filename, fullpath, sizeof(fullpath));
668 if (ret) {
669 errno = ENOMEM;
670 goto end;
671 }
672
673 ret = unlink(fullpath);
674end:
675 return ret;
676}
677
18710679
JG
678static
679int _run_as_mkdir(const struct lttng_directory_handle *handle, const char *path,
680 mode_t mode, uid_t uid, gid_t gid)
681{
682 int ret;
683 char fullpath[LTTNG_PATH_MAX];
684
685 ret = get_full_path(handle, path, fullpath, sizeof(fullpath));
686 if (ret) {
687 errno = ENOMEM;
688 goto end;
689 }
690
691 ret = run_as_mkdir(fullpath, mode, uid, gid);
692end:
693 return ret;
694}
695
2912cead
JG
696static
697int _run_as_open(const struct lttng_directory_handle *handle,
698 const char *filename,
699 int flags, mode_t mode, uid_t uid, gid_t gid)
700{
701 int ret;
702 char fullpath[LTTNG_PATH_MAX];
703
704 ret = get_full_path(handle, filename, fullpath, sizeof(fullpath));
705 if (ret) {
706 errno = ENOMEM;
707 goto end;
708 }
709
710 ret = run_as_open(fullpath, flags, mode, uid, gid);
711end:
712 return ret;
713}
714
715static
716int _run_as_unlink(const struct lttng_directory_handle *handle,
717 const char *filename, uid_t uid, gid_t gid)
718{
719 int ret;
720 char fullpath[LTTNG_PATH_MAX];
721
722 ret = get_full_path(handle, filename, fullpath, sizeof(fullpath));
723 if (ret) {
724 errno = ENOMEM;
725 goto end;
726 }
727
728 ret = run_as_unlink(fullpath, uid, gid);
729end:
730 return ret;
731}
732
18710679
JG
733static
734int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle,
735 const char *path, mode_t mode, uid_t uid, gid_t gid)
736{
737 int ret;
738 char fullpath[LTTNG_PATH_MAX];
739
740 ret = get_full_path(handle, path, fullpath, sizeof(fullpath));
741 if (ret) {
742 errno = ENOMEM;
743 goto end;
744 }
745
746 ret = run_as_mkdir_recursive(fullpath, mode, uid, gid);
747end:
748 return ret;
749}
750
93bed9fe
JG
751static
752int _lttng_directory_handle_rename(
753 const struct lttng_directory_handle *old_handle,
754 const char *old_name,
755 const struct lttng_directory_handle *new_handle,
756 const char *new_name)
757{
758 int ret;
759 char old_fullpath[LTTNG_PATH_MAX];
760 char new_fullpath[LTTNG_PATH_MAX];
761
762 ret = get_full_path(old_handle, old_name, old_fullpath,
763 sizeof(old_fullpath));
764 if (ret) {
765 errno = ENOMEM;
766 goto end;
767 }
768 ret = get_full_path(new_handle, new_name, new_fullpath,
769 sizeof(new_fullpath));
770 if (ret) {
771 errno = ENOMEM;
772 goto end;
773 }
774
775 ret = rename(old_fullpath, new_fullpath);
776end:
777 return ret;
778}
779
780static
781int _run_as_rename(const struct lttng_directory_handle *old_handle,
782 const char *old_name,
783 const struct lttng_directory_handle *new_handle,
784 const char *new_name, uid_t uid, gid_t gid)
785{
786 int ret;
787 char old_fullpath[LTTNG_PATH_MAX];
788 char new_fullpath[LTTNG_PATH_MAX];
789
790 ret = get_full_path(old_handle, old_name, old_fullpath,
791 sizeof(old_fullpath));
792 if (ret) {
793 errno = ENOMEM;
794 goto end;
795 }
796 ret = get_full_path(new_handle, new_name, new_fullpath,
797 sizeof(new_fullpath));
798 if (ret) {
799 errno = ENOMEM;
800 goto end;
801 }
802
803 ret = run_as_rename(old_fullpath, new_fullpath, uid, gid);
804end:
805 return ret;
806}
807
808static
809DIR *lttng_directory_handle_opendir(const struct lttng_directory_handle *handle,
810 const char *path)
811{
812 int ret;
813 DIR *dir_stream = NULL;
814 char fullpath[LTTNG_PATH_MAX];
815
816 ret = get_full_path(handle, path, fullpath, sizeof(fullpath));
817 if (ret) {
818 errno = ENOMEM;
819 goto end;
820 }
821
822 dir_stream = opendir(fullpath);
823end:
824 return dir_stream;
825}
826
827static
828int lttng_directory_handle_rmdir(
829 const struct lttng_directory_handle *handle, const char *name)
830{
831 int ret;
832 char fullpath[LTTNG_PATH_MAX];
833
834 ret = get_full_path(handle, name, fullpath, sizeof(fullpath));
835 if (ret) {
836 errno = ENOMEM;
837 goto end;
838 }
839
840 ret = rmdir(fullpath);
841end:
842 return ret;
843}
844
845static
846int _run_as_rmdir(const struct lttng_directory_handle *handle,
847 const char *name, uid_t uid, gid_t gid)
848{
849 int ret;
850 char fullpath[LTTNG_PATH_MAX];
851
852 ret = get_full_path(handle, name, fullpath, sizeof(fullpath));
853 if (ret) {
854 errno = ENOMEM;
855 goto end;
856 }
857
858 ret = run_as_rmdir(fullpath, uid, gid);
859end:
860 return ret;
861}
862
863static
864int _run_as_rmdir_recursive(
865 const struct lttng_directory_handle *handle, const char *name,
f75c5439 866 uid_t uid, gid_t gid, int flags)
93bed9fe
JG
867{
868 int ret;
869 char fullpath[LTTNG_PATH_MAX];
870
871 ret = get_full_path(handle, name, fullpath, sizeof(fullpath));
872 if (ret) {
873 errno = ENOMEM;
874 goto end;
875 }
876
f75c5439 877 ret = run_as_rmdir_recursive(fullpath, uid, gid, flags);
93bed9fe
JG
878end:
879 return ret;
880}
881
18710679
JG
882#endif /* COMPAT_DIRFD */
883
93bed9fe
JG
884/* Common implementation. */
885
18710679
JG
886/*
887 * On some filesystems (e.g. nfs), mkdir will validate access rights before
888 * checking for the existence of the path element. This means that on a setup
889 * where "/home/" is a mounted NFS share, and running as an unpriviledged user,
890 * recursively creating a path of the form "/home/my_user/trace/" will fail with
891 * EACCES on mkdir("/home", ...).
892 *
893 * Checking the path for existence allows us to work around this behaviour.
894 */
895static
896int create_directory_check_exists(const struct lttng_directory_handle *handle,
897 const char *path, mode_t mode)
898{
899 int ret = 0;
900 struct stat st;
901
902 ret = lttng_directory_handle_stat(handle, path, &st);
903 if (ret == 0) {
904 if (S_ISDIR(st.st_mode)) {
905 /* Directory exists, skip. */
906 goto end;
907 } else {
908 /* Exists, but is not a directory. */
909 errno = ENOTDIR;
910 ret = -1;
911 goto end;
912 }
93bed9fe
JG
913 } else if (errno != ENOENT) {
914 goto end;
18710679
JG
915 }
916
917 /*
918 * Let mkdir handle other errors as the caller expects mkdir
919 * semantics.
920 */
921 ret = lttng_directory_handle_mkdir(handle, path, mode);
922end:
923 return ret;
924}
925
18710679
JG
926static
927int create_directory_recursive(const struct lttng_directory_handle *handle,
928 const char *path, mode_t mode)
929{
930 char *p, tmp[LTTNG_PATH_MAX];
931 size_t len;
932 int ret;
933
934 assert(path);
935
936 ret = lttng_strncpy(tmp, path, sizeof(tmp));
937 if (ret) {
938 ERR("Failed to create directory: provided path's length (%zu bytes) exceeds the maximal allowed length (%zu bytes)",
939 strlen(path) + 1, sizeof(tmp));
940 goto error;
941 }
942
943 len = strlen(path);
944 if (tmp[len - 1] == '/') {
945 tmp[len - 1] = 0;
946 }
947
948 for (p = tmp + 1; *p; p++) {
949 if (*p == '/') {
950 *p = 0;
951 if (tmp[strlen(tmp) - 1] == '.' &&
952 tmp[strlen(tmp) - 2] == '.' &&
953 tmp[strlen(tmp) - 3] == '/') {
954 ERR("Using '/../' is not permitted in the trace path (%s)",
955 tmp);
956 ret = -1;
957 goto error;
958 }
959 ret = create_directory_check_exists(handle, tmp, mode);
960 if (ret < 0) {
961 if (errno != EACCES) {
962 PERROR("Failed to create directory \"%s\"",
963 path);
964 ret = -errno;
965 goto error;
966 }
967 }
968 *p = '/';
969 }
970 }
971
972 ret = create_directory_check_exists(handle, tmp, mode);
973 if (ret < 0) {
974 PERROR("mkdirat recursive last element");
975 ret = -errno;
976 }
977error:
978 return ret;
979}
980
cbf53d23
JG
981LTTNG_HIDDEN
982bool lttng_directory_handle_get(struct lttng_directory_handle *handle)
983{
984 return urcu_ref_get_unless_zero(&handle->ref);
985}
986
987LTTNG_HIDDEN
988void lttng_directory_handle_put(struct lttng_directory_handle *handle)
989{
990 if (!handle) {
991 return;
992 }
993 assert(handle->ref.refcount);
994 urcu_ref_put(&handle->ref, lttng_directory_handle_release);
995}
996
18710679
JG
997LTTNG_HIDDEN
998int lttng_directory_handle_create_subdirectory_as_user(
999 const struct lttng_directory_handle *handle,
1000 const char *subdirectory,
69e3a560 1001 mode_t mode, const struct lttng_credentials *creds)
18710679
JG
1002{
1003 int ret;
1004
1005 if (!creds) {
1006 /* Run as current user. */
1007 ret = create_directory_check_exists(handle,
1008 subdirectory, mode);
1009 } else {
1010 ret = _run_as_mkdir(handle, subdirectory,
1011 mode, creds->uid, creds->gid);
1012 }
1013
1014 return ret;
1015}
1016
1017LTTNG_HIDDEN
1018int lttng_directory_handle_create_subdirectory_recursive_as_user(
1019 const struct lttng_directory_handle *handle,
1020 const char *subdirectory_path,
69e3a560 1021 mode_t mode, const struct lttng_credentials *creds)
18710679
JG
1022{
1023 int ret;
1024
1025 if (!creds) {
1026 /* Run as current user. */
1027 ret = create_directory_recursive(handle,
1028 subdirectory_path, mode);
1029 } else {
1030 ret = _run_as_mkdir_recursive(handle, subdirectory_path,
1031 mode, creds->uid, creds->gid);
1032 }
1033
1034 return ret;
1035}
1036
1037LTTNG_HIDDEN
1038int lttng_directory_handle_create_subdirectory(
1039 const struct lttng_directory_handle *handle,
1040 const char *subdirectory,
1041 mode_t mode)
1042{
1043 return lttng_directory_handle_create_subdirectory_as_user(
1044 handle, subdirectory, mode, NULL);
1045}
1046
1047LTTNG_HIDDEN
1048int lttng_directory_handle_create_subdirectory_recursive(
1049 const struct lttng_directory_handle *handle,
1050 const char *subdirectory_path,
1051 mode_t mode)
1052{
1053 return lttng_directory_handle_create_subdirectory_recursive_as_user(
1054 handle, subdirectory_path, mode, NULL);
1055}
2912cead
JG
1056
1057LTTNG_HIDDEN
1058int lttng_directory_handle_open_file_as_user(
1059 const struct lttng_directory_handle *handle,
1060 const char *filename,
1061 int flags, mode_t mode,
1062 const struct lttng_credentials *creds)
1063{
1064 int ret;
1065
1066 if (!creds) {
1067 /* Run as current user. */
1068 ret = lttng_directory_handle_open(handle, filename, flags,
1069 mode);
1070 } else {
1071 ret = _run_as_open(handle, filename, flags, mode,
1072 creds->uid, creds->gid);
1073 }
1074 return ret;
1075}
1076
1077LTTNG_HIDDEN
1078int lttng_directory_handle_open_file(
1079 const struct lttng_directory_handle *handle,
1080 const char *filename,
1081 int flags, mode_t mode)
1082{
1083 return lttng_directory_handle_open_file_as_user(handle, filename, flags,
1084 mode, NULL);
1085}
1086
1087LTTNG_HIDDEN
1088int lttng_directory_handle_unlink_file_as_user(
1089 const struct lttng_directory_handle *handle,
1090 const char *filename,
1091 const struct lttng_credentials *creds)
1092{
1093 int ret;
1094
1095 if (!creds) {
1096 /* Run as current user. */
1097 ret = lttng_directory_handle_unlink(handle, filename);
1098 } else {
1099 ret = _run_as_unlink(handle, filename, creds->uid, creds->gid);
1100 }
1101 return ret;
1102}
1103
1104LTTNG_HIDDEN
1105int lttng_directory_handle_unlink_file(
1106 const struct lttng_directory_handle *handle,
1107 const char *filename)
1108{
1109 return lttng_directory_handle_unlink_file_as_user(handle,
1110 filename, NULL);
1111}
93bed9fe
JG
1112
1113LTTNG_HIDDEN
1114int lttng_directory_handle_rename(
1115 const struct lttng_directory_handle *old_handle,
1116 const char *old_name,
1117 const struct lttng_directory_handle *new_handle,
1118 const char *new_name)
1119{
1120 return lttng_directory_handle_rename_as_user(old_handle, old_name,
1121 new_handle, new_name, NULL);
1122}
1123
1124LTTNG_HIDDEN
1125int lttng_directory_handle_rename_as_user(
1126 const struct lttng_directory_handle *old_handle,
1127 const char *old_name,
1128 const struct lttng_directory_handle *new_handle,
1129 const char *new_name,
1130 const struct lttng_credentials *creds)
1131{
1132 int ret;
1133
1134 if (!creds) {
1135 /* Run as current user. */
1136 ret = _lttng_directory_handle_rename(old_handle,
1137 old_name, new_handle, new_name);
1138 } else {
1139 ret = _run_as_rename(old_handle, old_name, new_handle,
1140 new_name, creds->uid, creds->gid);
1141 }
1142 return ret;
1143}
1144
1145LTTNG_HIDDEN
1146int lttng_directory_handle_remove_subdirectory(
1147 const struct lttng_directory_handle *handle,
1148 const char *name)
1149{
1150 return lttng_directory_handle_remove_subdirectory_as_user(handle, name,
1151 NULL);
1152}
1153
1154LTTNG_HIDDEN
1155int lttng_directory_handle_remove_subdirectory_as_user(
1156 const struct lttng_directory_handle *handle,
1157 const char *name,
1158 const struct lttng_credentials *creds)
1159{
1160 int ret;
1161
1162 if (!creds) {
1163 /* Run as current user. */
1164 ret = lttng_directory_handle_rmdir(handle, name);
1165 } else {
1166 ret = _run_as_rmdir(handle, name, creds->uid, creds->gid);
1167 }
1168 return ret;
1169}
1170
1171struct rmdir_frame {
f75c5439 1172 ssize_t parent_frame_idx;
93bed9fe 1173 DIR *dir;
f75c5439 1174 bool empty;
93bed9fe
JG
1175 /* Size including '\0'. */
1176 size_t path_size;
1177};
1178
1179static
1180void rmdir_frame_fini(void *data)
1181{
41066401 1182 int ret;
93bed9fe
JG
1183 struct rmdir_frame *frame = data;
1184
41066401
FD
1185 ret = closedir(frame->dir);
1186 if (ret == -1) {
1187 PERROR("Failed to close directory stream");
1188 }
93bed9fe
JG
1189}
1190
1191static
1192int remove_directory_recursive(const struct lttng_directory_handle *handle,
f75c5439 1193 const char *path, int flags)
93bed9fe
JG
1194{
1195 int ret;
1196 struct lttng_dynamic_array frames;
1197 size_t current_frame_idx = 0;
1198 struct rmdir_frame initial_frame = {
f75c5439 1199 .parent_frame_idx = -1,
93bed9fe 1200 .dir = lttng_directory_handle_opendir(handle, path),
f75c5439 1201 .empty = true,
93bed9fe
JG
1202 .path_size = strlen(path) + 1,
1203 };
1204 struct lttng_dynamic_buffer current_path;
1205 const char separator = '/';
1206
1207 lttng_dynamic_buffer_init(&current_path);
1208 lttng_dynamic_array_init(&frames, sizeof(struct rmdir_frame),
f75c5439
MD
1209 rmdir_frame_fini);
1210
1211 if (flags & ~(LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG |
1212 LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG)) {
1213 ERR("Unknown flags %d", flags);
1214 ret = -1;
1215 goto end;
1216 }
1217
1218 if (!initial_frame.dir) {
1219 if (flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG &&
1220 errno == ENOENT) {
1221 DBG("Cannot rmdir \"%s\": root does not exist", path);
1222 ret = 0;
1223 goto end;
1224 } else {
1225 PERROR("Failed to rmdir \"%s\"", path);
1226 ret = -1;
1227 goto end;
1228 }
1229 }
93bed9fe
JG
1230
1231 ret = lttng_dynamic_array_add_element(&frames, &initial_frame);
1232 if (ret) {
1233 ERR("Failed to push context frame during recursive directory removal");
1234 rmdir_frame_fini(&initial_frame);
1235 goto end;
1236 }
1237
f75c5439
MD
1238 ret = lttng_dynamic_buffer_append(
1239 &current_path, path, initial_frame.path_size);
1240 if (ret) {
93bed9fe
JG
1241 ERR("Failed to set initial path during recursive directory removal");
1242 ret = -1;
1243 goto end;
f75c5439 1244 }
93bed9fe 1245
f75c5439 1246 while (lttng_dynamic_array_get_count(&frames) > 0) {
93bed9fe
JG
1247 struct dirent *entry;
1248 struct rmdir_frame *current_frame =
f75c5439
MD
1249 lttng_dynamic_array_get_element(
1250 &frames, current_frame_idx);
93bed9fe 1251
f75c5439
MD
1252 assert(current_frame->dir);
1253 ret = lttng_dynamic_buffer_set_size(
1254 &current_path, current_frame->path_size);
93bed9fe
JG
1255 assert(!ret);
1256 current_path.data[current_path.size - 1] = '\0';
1257
1258 while ((entry = readdir(current_frame->dir))) {
1259 struct stat st;
93bed9fe 1260
f75c5439
MD
1261 if (!strcmp(entry->d_name, ".") ||
1262 !strcmp(entry->d_name, "..")) {
93bed9fe
JG
1263 continue;
1264 }
1265
1266 /* Set current_path to the entry's path. */
f75c5439
MD
1267 ret = lttng_dynamic_buffer_set_size(
1268 &current_path, current_path.size - 1);
93bed9fe
JG
1269 assert(!ret);
1270 ret = lttng_dynamic_buffer_append(&current_path,
1271 &separator, sizeof(separator));
1272 if (ret) {
1273 goto end;
1274 }
1275 ret = lttng_dynamic_buffer_append(&current_path,
1276 entry->d_name,
1277 strlen(entry->d_name) + 1);
1278 if (ret) {
1279 goto end;
1280 }
1281
f75c5439
MD
1282 if (lttng_directory_handle_stat(
1283 handle, current_path.data, &st)) {
1284 if ((flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG) &&
1285 errno == ENOENT) {
1286 break;
1287 }
93bed9fe
JG
1288 PERROR("Failed to stat \"%s\"",
1289 current_path.data);
1290 ret = -1;
1291 goto end;
1292 }
1293
1294 if (!S_ISDIR(st.st_mode)) {
f75c5439
MD
1295 if (flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG) {
1296 current_frame->empty = false;
1297 break;
1298 } else {
1299 /* Not empty, abort. */
1300 DBG("Directory \"%s\" is not empty; refusing to remove directory",
1301 current_path.data);
1302 ret = -1;
1303 goto end;
1304 }
1305 } else {
1306 struct rmdir_frame new_frame = {
1307 .path_size = current_path.size,
1308 .dir = lttng_directory_handle_opendir(
1309 handle,
1310 current_path.data),
1311 .empty = true,
1312 .parent_frame_idx = current_frame_idx,
1313 };
1314
1315 if (!new_frame.dir) {
1316 if (flags & LTTNG_DIRECTORY_HANDLE_SKIP_NON_EMPTY_FLAG &&
1317 errno == ENOENT) {
1318 DBG("Non-existing directory stream during recursive directory removal");
1319 break;
1320 } else {
1321 PERROR("Failed to open directory stream during recursive directory removal");
1322 ret = -1;
1323 goto end;
1324 }
1325 }
1326 ret = lttng_dynamic_array_add_element(
1327 &frames, &new_frame);
1328 if (ret) {
1329 ERR("Failed to push context frame during recursive directory removal");
1330 rmdir_frame_fini(&new_frame);
1331 goto end;
1332 }
1333 current_frame_idx++;
1334 /* We break iteration on readdir. */
1335 break;
93bed9fe 1336 }
f75c5439
MD
1337 }
1338 if (entry) {
1339 continue;
1340 }
93bed9fe 1341
f75c5439
MD
1342 /* Pop rmdir frame. */
1343 if (current_frame->empty) {
1344 ret = lttng_directory_handle_rmdir(
1345 handle, current_path.data);
93bed9fe 1346 if (ret) {
f75c5439
MD
1347 if ((flags & LTTNG_DIRECTORY_HANDLE_FAIL_NON_EMPTY_FLAG) ||
1348 errno != ENOENT) {
1349 PERROR("Failed to remove \"%s\" during recursive directory removal",
1350 current_path.data);
1351 goto end;
1352 }
1353 DBG("Non-existing directory stream during recursive directory removal");
93bed9fe 1354 }
f75c5439
MD
1355 } else if (current_frame->parent_frame_idx >= 0) {
1356 struct rmdir_frame *parent_frame;
1357
1358 parent_frame = lttng_dynamic_array_get_element(&frames,
1359 current_frame->parent_frame_idx);
1360 assert(parent_frame);
1361 parent_frame->empty = false;
1362 }
1363 ret = lttng_dynamic_array_remove_element(
1364 &frames, current_frame_idx);
1365 if (ret) {
1366 ERR("Failed to pop context frame during recursive directory removal");
1367 goto end;
1368 }
1369 current_frame_idx--;
93bed9fe
JG
1370 }
1371end:
1372 lttng_dynamic_array_reset(&frames);
1373 lttng_dynamic_buffer_reset(&current_path);
1374 return ret;
1375}
1376
1377LTTNG_HIDDEN
1378int lttng_directory_handle_remove_subdirectory_recursive(
1379 const struct lttng_directory_handle *handle,
f75c5439
MD
1380 const char *name,
1381 int flags)
93bed9fe
JG
1382{
1383 return lttng_directory_handle_remove_subdirectory_recursive_as_user(
f75c5439 1384 handle, name, NULL, flags);
93bed9fe
JG
1385}
1386
1387LTTNG_HIDDEN
1388int lttng_directory_handle_remove_subdirectory_recursive_as_user(
1389 const struct lttng_directory_handle *handle,
1390 const char *name,
f75c5439
MD
1391 const struct lttng_credentials *creds,
1392 int flags)
93bed9fe
JG
1393{
1394 int ret;
1395
1396 if (!creds) {
1397 /* Run as current user. */
f75c5439 1398 ret = remove_directory_recursive(handle, name, flags);
93bed9fe
JG
1399 } else {
1400 ret = _run_as_rmdir_recursive(handle, name, creds->uid,
f75c5439 1401 creds->gid, flags);
93bed9fe
JG
1402 }
1403 return ret;
1404}
This page took 0.088678 seconds and 5 git commands to generate.