2 * Copyright (C) 2019 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
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.
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
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.
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>
26 #include <sys/types.h>
32 * This compatibility layer shares a common "base" that is implemented
33 * in terms of an internal API. This file contains two implementations
34 * of the internal API below.
37 int lttng_directory_handle_stat(const struct lttng_directory_handle
*handle
,
38 const char *path
, struct stat
*st
);
40 int lttng_directory_handle_mkdir(
41 const struct lttng_directory_handle
*handle
,
42 const char *path
, mode_t mode
);
44 int _run_as_mkdir(const struct lttng_directory_handle
*handle
, const char *path
,
45 mode_t mode
, uid_t uid
, gid_t gid
);
47 int _run_as_mkdir_recursive(const struct lttng_directory_handle
*handle
,
48 const char *path
, mode_t mode
, uid_t uid
, gid_t gid
);
50 int lttng_directory_handle_open(const struct lttng_directory_handle
*handle
,
51 const char *filename
, int flags
, mode_t mode
);
53 int _run_as_open(const struct lttng_directory_handle
*handle
,
55 int flags
, mode_t mode
, uid_t uid
, gid_t gid
);
57 int lttng_directory_handle_unlink(
58 const struct lttng_directory_handle
*handle
,
59 const char *filename
);
61 int _run_as_unlink(const struct lttng_directory_handle
*handle
,
62 const char *filename
, uid_t uid
, gid_t gid
);
64 void lttng_directory_handle_invalidate(struct lttng_directory_handle
*handle
);
69 int lttng_directory_handle_init(struct lttng_directory_handle
*new_handle
,
72 const struct lttng_directory_handle cwd_handle
= {
76 /* Open a handle to the CWD if NULL is passed. */
77 return lttng_directory_handle_init_from_handle(new_handle
,
83 int lttng_directory_handle_init_from_handle(
84 struct lttng_directory_handle
*new_handle
, const char *path
,
85 const struct lttng_directory_handle
*handle
)
90 ret
= lttng_directory_handle_copy(handle
, new_handle
);
94 ERR("Failed to initialize directory handle: provided path is an empty string");
98 ret
= openat(handle
->dirfd
, path
, O_RDONLY
| O_DIRECTORY
| O_CLOEXEC
);
100 PERROR("Failed to initialize directory handle to \"%s\"", path
);
103 new_handle
->dirfd
= ret
;
110 int lttng_directory_handle_init_from_dirfd(
111 struct lttng_directory_handle
*handle
, int dirfd
)
113 handle
->dirfd
= dirfd
;
118 void lttng_directory_handle_fini(struct lttng_directory_handle
*handle
)
122 if (handle
->dirfd
== AT_FDCWD
|| handle
->dirfd
== -1) {
125 ret
= close(handle
->dirfd
);
127 PERROR("Failed to close directory file descriptor of directory handle");
130 lttng_directory_handle_invalidate(handle
);
134 int lttng_directory_handle_copy(const struct lttng_directory_handle
*handle
,
135 struct lttng_directory_handle
*new_copy
)
139 if (handle
->dirfd
== AT_FDCWD
) {
140 new_copy
->dirfd
= handle
->dirfd
;
142 new_copy
->dirfd
= dup(handle
->dirfd
);
143 if (new_copy
->dirfd
== -1) {
144 PERROR("Failed to duplicate directory fd of directory handle");
152 void lttng_directory_handle_invalidate(struct lttng_directory_handle
*handle
)
158 int lttng_directory_handle_stat(const struct lttng_directory_handle
*handle
,
159 const char *path
, struct stat
*st
)
161 return fstatat(handle
->dirfd
, path
, st
, 0);
165 int lttng_directory_handle_mkdir(
166 const struct lttng_directory_handle
*handle
,
167 const char *path
, mode_t mode
)
169 return mkdirat(handle
->dirfd
, path
, mode
);
173 int lttng_directory_handle_open(const struct lttng_directory_handle
*handle
,
174 const char *filename
, int flags
, mode_t mode
)
176 return openat(handle
->dirfd
, filename
, flags
, mode
);
180 int _run_as_open(const struct lttng_directory_handle
*handle
,
181 const char *filename
,
182 int flags
, mode_t mode
, uid_t uid
, gid_t gid
)
184 return run_as_openat(handle
->dirfd
, filename
, flags
, mode
, uid
, gid
);
188 int _run_as_unlink(const struct lttng_directory_handle
*handle
,
189 const char *filename
, uid_t uid
, gid_t gid
)
191 return run_as_unlinkat(handle
->dirfd
, filename
, uid
, gid
);
195 int lttng_directory_handle_unlink(
196 const struct lttng_directory_handle
*handle
,
197 const char *filename
)
199 return unlinkat(handle
->dirfd
, filename
, 0);
203 int _run_as_mkdir(const struct lttng_directory_handle
*handle
,
204 const char *path
, mode_t mode
, uid_t uid
, gid_t gid
)
206 return run_as_mkdirat(handle
->dirfd
, path
, mode
, uid
, gid
);
210 int _run_as_mkdir_recursive(const struct lttng_directory_handle
*handle
,
211 const char *path
, mode_t mode
, uid_t uid
, gid_t gid
)
213 return run_as_mkdirat_recursive(handle
->dirfd
, path
, mode
, uid
, gid
);
216 #else /* COMPAT_DIRFD */
219 int get_full_path(const struct lttng_directory_handle
*handle
,
220 const char *subdirectory
, char *fullpath
, size_t size
)
224 subdirectory
= subdirectory
? : "";
226 * Don't include the base path if subdirectory is absolute.
227 * This is the same behaviour than mkdirat.
229 ret
= snprintf(fullpath
, size
, "%s%s",
230 *subdirectory
!= '/' ? handle
->base_path
: "",
232 if (ret
== -1 || ret
>= size
) {
233 ERR("Failed to format subdirectory from directory handle");
241 int lttng_directory_handle_init(struct lttng_directory_handle
*handle
,
246 size_t cwd_len
, path_len
;
247 char cwd_buf
[LTTNG_PATH_MAX
] = {};
248 char handle_buf
[LTTNG_PATH_MAX
] = {};
249 bool add_cwd_slash
, add_trailing_slash
;
250 const struct lttng_directory_handle cwd_handle
= {
251 .base_path
= handle_buf
,
254 if (path
&& *path
== '/') {
256 * Creation of an handle to an absolute path; no need to sample
261 path_len
= path
? strlen(path
) : 0;
263 cwd
= getcwd(cwd_buf
, sizeof(cwd_buf
));
265 PERROR("Failed to initialize directory handle, can't get current working directory");
269 cwd_len
= strlen(cwd
);
271 ERR("Failed to initialize directory handle, current working directory path has a length of 0");
276 add_cwd_slash
= cwd
[cwd_len
- 1] != '/';
277 add_trailing_slash
= path
&& path
[path_len
- 1] != '/';
279 ret
= snprintf(handle_buf
, sizeof(handle_buf
), "%s%s%s%s",
281 add_cwd_slash
? "/" : "",
283 add_trailing_slash
? "/" : "");
284 if (ret
== -1 || ret
>= LTTNG_PATH_MAX
) {
285 ERR("Failed to initialize directory handle, failed to format directory path");
289 ret
= lttng_directory_handle_init_from_handle(handle
, path
,
296 int lttng_directory_handle_init_from_handle(
297 struct lttng_directory_handle
*new_handle
, const char *path
,
298 const struct lttng_directory_handle
*handle
)
301 size_t path_len
, handle_path_len
;
302 bool add_trailing_slash
;
303 struct stat stat_buf
;
305 assert(handle
&& handle
->base_path
);
307 ret
= lttng_directory_handle_stat(handle
, path
, &stat_buf
);
309 PERROR("Failed to create directory handle");
311 } else if (!S_ISDIR(stat_buf
.st_mode
)) {
312 char full_path
[LTTNG_PATH_MAX
];
314 /* Best effort for logging purposes. */
315 ret
= get_full_path(handle
, path
, full_path
,
321 ERR("Failed to initialize directory handle to \"%s\": not a directory",
327 ret
= lttng_directory_handle_copy(handle
, new_handle
);
331 path_len
= strlen(path
);
333 ERR("Failed to initialize directory handle: provided path is an empty string");
338 new_handle
->base_path
= strdup(path
);
339 ret
= new_handle
->base_path
? 0 : -1;
343 add_trailing_slash
= path
[path_len
- 1] != '/';
345 handle_path_len
= strlen(handle
->base_path
) + path_len
+
346 !!add_trailing_slash
;
347 if (handle_path_len
>= LTTNG_PATH_MAX
) {
348 ERR("Failed to initialize directory handle as the resulting path's length (%zu bytes) exceeds the maximal allowed length (%d bytes)",
349 handle_path_len
, LTTNG_PATH_MAX
);
353 new_handle
->base_path
= zmalloc(handle_path_len
);
354 if (!new_handle
->base_path
) {
355 PERROR("Failed to initialize directory handle");
360 ret
= sprintf(new_handle
->base_path
, "%s%s%s",
363 add_trailing_slash
? "/" : "");
364 if (ret
== -1 || ret
>= handle_path_len
) {
365 ERR("Failed to initialize directory handle: path formatting failed");
374 int lttng_directory_handle_init_from_dirfd(
375 struct lttng_directory_handle
*handle
, int dirfd
)
377 assert(dirfd
== AT_FDCWD
);
378 return lttng_directory_handle_init(handle
, NULL
);
382 void lttng_directory_handle_fini(struct lttng_directory_handle
*handle
)
384 free(handle
->base_path
);
385 lttng_directory_handle_invalidate(handle
);
389 int lttng_directory_handle_copy(const struct lttng_directory_handle
*handle
,
390 struct lttng_directory_handle
*new_copy
)
392 new_copy
->base_path
= strdup(handle
->base_path
);
393 return new_copy
->base_path
? 0 : -1;
397 void lttng_directory_handle_invalidate(struct lttng_directory_handle
*handle
)
399 handle
->base_path
= NULL
;
403 int lttng_directory_handle_stat(const struct lttng_directory_handle
*handle
,
404 const char *subdirectory
, struct stat
*st
)
407 char fullpath
[LTTNG_PATH_MAX
];
409 ret
= get_full_path(handle
, subdirectory
, fullpath
, sizeof(fullpath
));
415 ret
= stat(fullpath
, st
);
421 int lttng_directory_handle_mkdir(const struct lttng_directory_handle
*handle
,
422 const char *subdirectory
, mode_t mode
)
425 char fullpath
[LTTNG_PATH_MAX
];
427 ret
= get_full_path(handle
, subdirectory
, fullpath
, sizeof(fullpath
));
433 ret
= mkdir(fullpath
, mode
);
439 int lttng_directory_handle_open(const struct lttng_directory_handle
*handle
,
440 const char *filename
, int flags
, mode_t mode
)
443 char fullpath
[LTTNG_PATH_MAX
];
445 ret
= get_full_path(handle
, filename
, fullpath
, sizeof(fullpath
));
451 ret
= open(fullpath
, flags
, mode
);
457 int lttng_directory_handle_unlink(
458 const struct lttng_directory_handle
*handle
,
459 const char *filename
)
462 char fullpath
[LTTNG_PATH_MAX
];
464 ret
= get_full_path(handle
, filename
, fullpath
, sizeof(fullpath
));
470 ret
= unlink(fullpath
);
476 int _run_as_mkdir(const struct lttng_directory_handle
*handle
, const char *path
,
477 mode_t mode
, uid_t uid
, gid_t gid
)
480 char fullpath
[LTTNG_PATH_MAX
];
482 ret
= get_full_path(handle
, path
, fullpath
, sizeof(fullpath
));
488 ret
= run_as_mkdir(fullpath
, mode
, uid
, gid
);
494 int _run_as_open(const struct lttng_directory_handle
*handle
,
495 const char *filename
,
496 int flags
, mode_t mode
, uid_t uid
, gid_t gid
)
499 char fullpath
[LTTNG_PATH_MAX
];
501 ret
= get_full_path(handle
, filename
, fullpath
, sizeof(fullpath
));
507 ret
= run_as_open(fullpath
, flags
, mode
, uid
, gid
);
513 int _run_as_unlink(const struct lttng_directory_handle
*handle
,
514 const char *filename
, uid_t uid
, gid_t gid
)
517 char fullpath
[LTTNG_PATH_MAX
];
519 ret
= get_full_path(handle
, filename
, fullpath
, sizeof(fullpath
));
525 ret
= run_as_unlink(fullpath
, uid
, gid
);
531 int _run_as_mkdir_recursive(const struct lttng_directory_handle
*handle
,
532 const char *path
, mode_t mode
, uid_t uid
, gid_t gid
)
535 char fullpath
[LTTNG_PATH_MAX
];
537 ret
= get_full_path(handle
, path
, fullpath
, sizeof(fullpath
));
543 ret
= run_as_mkdir_recursive(fullpath
, mode
, uid
, gid
);
548 #endif /* COMPAT_DIRFD */
551 * On some filesystems (e.g. nfs), mkdir will validate access rights before
552 * checking for the existence of the path element. This means that on a setup
553 * where "/home/" is a mounted NFS share, and running as an unpriviledged user,
554 * recursively creating a path of the form "/home/my_user/trace/" will fail with
555 * EACCES on mkdir("/home", ...).
557 * Checking the path for existence allows us to work around this behaviour.
560 int create_directory_check_exists(const struct lttng_directory_handle
*handle
,
561 const char *path
, mode_t mode
)
566 ret
= lttng_directory_handle_stat(handle
, path
, &st
);
568 if (S_ISDIR(st
.st_mode
)) {
569 /* Directory exists, skip. */
572 /* Exists, but is not a directory. */
580 * Let mkdir handle other errors as the caller expects mkdir
583 ret
= lttng_directory_handle_mkdir(handle
, path
, mode
);
588 /* Common implementation. */
590 struct lttng_directory_handle
591 lttng_directory_handle_move(struct lttng_directory_handle
*original
)
593 const struct lttng_directory_handle tmp
= *original
;
595 lttng_directory_handle_invalidate(original
);
600 int create_directory_recursive(const struct lttng_directory_handle
*handle
,
601 const char *path
, mode_t mode
)
603 char *p
, tmp
[LTTNG_PATH_MAX
];
609 ret
= lttng_strncpy(tmp
, path
, sizeof(tmp
));
611 ERR("Failed to create directory: provided path's length (%zu bytes) exceeds the maximal allowed length (%zu bytes)",
612 strlen(path
) + 1, sizeof(tmp
));
617 if (tmp
[len
- 1] == '/') {
621 for (p
= tmp
+ 1; *p
; p
++) {
624 if (tmp
[strlen(tmp
) - 1] == '.' &&
625 tmp
[strlen(tmp
) - 2] == '.' &&
626 tmp
[strlen(tmp
) - 3] == '/') {
627 ERR("Using '/../' is not permitted in the trace path (%s)",
632 ret
= create_directory_check_exists(handle
, tmp
, mode
);
634 if (errno
!= EACCES
) {
635 PERROR("Failed to create directory \"%s\"",
645 ret
= create_directory_check_exists(handle
, tmp
, mode
);
647 PERROR("mkdirat recursive last element");
655 int lttng_directory_handle_create_subdirectory_as_user(
656 const struct lttng_directory_handle
*handle
,
657 const char *subdirectory
,
658 mode_t mode
, const struct lttng_credentials
*creds
)
663 /* Run as current user. */
664 ret
= create_directory_check_exists(handle
,
667 ret
= _run_as_mkdir(handle
, subdirectory
,
668 mode
, creds
->uid
, creds
->gid
);
675 int lttng_directory_handle_create_subdirectory_recursive_as_user(
676 const struct lttng_directory_handle
*handle
,
677 const char *subdirectory_path
,
678 mode_t mode
, const struct lttng_credentials
*creds
)
683 /* Run as current user. */
684 ret
= create_directory_recursive(handle
,
685 subdirectory_path
, mode
);
687 ret
= _run_as_mkdir_recursive(handle
, subdirectory_path
,
688 mode
, creds
->uid
, creds
->gid
);
695 int lttng_directory_handle_create_subdirectory(
696 const struct lttng_directory_handle
*handle
,
697 const char *subdirectory
,
700 return lttng_directory_handle_create_subdirectory_as_user(
701 handle
, subdirectory
, mode
, NULL
);
705 int lttng_directory_handle_create_subdirectory_recursive(
706 const struct lttng_directory_handle
*handle
,
707 const char *subdirectory_path
,
710 return lttng_directory_handle_create_subdirectory_recursive_as_user(
711 handle
, subdirectory_path
, mode
, NULL
);
715 int lttng_directory_handle_open_file_as_user(
716 const struct lttng_directory_handle
*handle
,
717 const char *filename
,
718 int flags
, mode_t mode
,
719 const struct lttng_credentials
*creds
)
724 /* Run as current user. */
725 ret
= lttng_directory_handle_open(handle
, filename
, flags
,
728 ret
= _run_as_open(handle
, filename
, flags
, mode
,
729 creds
->uid
, creds
->gid
);
735 int lttng_directory_handle_open_file(
736 const struct lttng_directory_handle
*handle
,
737 const char *filename
,
738 int flags
, mode_t mode
)
740 return lttng_directory_handle_open_file_as_user(handle
, filename
, flags
,
745 int lttng_directory_handle_unlink_file_as_user(
746 const struct lttng_directory_handle
*handle
,
747 const char *filename
,
748 const struct lttng_credentials
*creds
)
753 /* Run as current user. */
754 ret
= lttng_directory_handle_unlink(handle
, filename
);
756 ret
= _run_as_unlink(handle
, filename
, creds
->uid
, creds
->gid
);
762 int lttng_directory_handle_unlink_file(
763 const struct lttng_directory_handle
*handle
,
764 const char *filename
)
766 return lttng_directory_handle_unlink_file_as_user(handle
,