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