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 int lttng_directory_handle_stat(const struct lttng_directory_handle
*handle
,
33 const char *path
, struct stat
*st
);
35 int lttng_directory_handle_mkdir(
36 const struct lttng_directory_handle
*handle
,
37 const char *path
, mode_t mode
);
39 int _run_as_mkdir(const struct lttng_directory_handle
*handle
, const char *path
,
40 mode_t mode
, uid_t uid
, gid_t gid
);
42 int _run_as_mkdir_recursive(const struct lttng_directory_handle
*handle
,
43 const char *path
, mode_t mode
, uid_t uid
, gid_t gid
);
45 void lttng_directory_handle_invalidate(struct lttng_directory_handle
*handle
);
50 int lttng_directory_handle_init(struct lttng_directory_handle
*handle
,
56 handle
->dirfd
= AT_FDCWD
;
60 ret
= open(path
, O_RDONLY
| O_DIRECTORY
| O_CLOEXEC
);
62 PERROR("Failed to initialize directory handle to \"%s\"", path
);
72 int lttng_directory_handle_init_from_dirfd(
73 struct lttng_directory_handle
*handle
, int dirfd
)
75 handle
->dirfd
= dirfd
;
80 void lttng_directory_handle_fini(struct lttng_directory_handle
*handle
)
84 if (handle
->dirfd
== AT_FDCWD
|| handle
->dirfd
== -1) {
87 ret
= close(handle
->dirfd
);
89 PERROR("Failed to close directory file descriptor of directory handle");
92 lttng_directory_handle_invalidate(handle
);
96 int lttng_directory_handle_copy(const struct lttng_directory_handle
*handle
,
97 struct lttng_directory_handle
*new_copy
)
101 if (handle
->dirfd
== AT_FDCWD
) {
102 new_copy
->dirfd
= handle
->dirfd
;
104 new_copy
->dirfd
= dup(handle
->dirfd
);
105 if (new_copy
->dirfd
== -1) {
106 PERROR("Failed to duplicate directory fd of directory handle");
114 void lttng_directory_handle_invalidate(struct lttng_directory_handle
*handle
)
120 int lttng_directory_handle_stat(const struct lttng_directory_handle
*handle
,
121 const char *path
, struct stat
*st
)
123 return fstatat(handle
->dirfd
, path
, st
, 0);
127 int lttng_directory_handle_mkdir(
128 const struct lttng_directory_handle
*handle
,
129 const char *path
, mode_t mode
)
131 return mkdirat(handle
->dirfd
, path
, mode
);
135 int _run_as_mkdir(const struct lttng_directory_handle
*handle
, const char *path
,
136 mode_t mode
, uid_t uid
, gid_t gid
)
138 return run_as_mkdirat(handle
->dirfd
, path
, mode
, uid
, gid
);
142 int _run_as_mkdir_recursive(const struct lttng_directory_handle
*handle
,
143 const char *path
, mode_t mode
, uid_t uid
, gid_t gid
)
145 return run_as_mkdirat_recursive(handle
->dirfd
, path
, mode
, uid
, gid
);
148 #else /* COMPAT_DIRFD */
151 int lttng_directory_handle_init(struct lttng_directory_handle
*handle
,
155 size_t cwd_len
, path_len
, handle_path_len
;
156 char cwd_buf
[LTTNG_PATH_MAX
];
158 bool add_slash
= false;
159 struct stat stat_buf
;
161 cwd
= getcwd(cwd_buf
, sizeof(cwd_buf
));
163 PERROR("Failed to initialize directory handle, can't get current working directory");
167 cwd_len
= strlen(cwd
);
169 ERR("Failed to initialize directory handle to \"%s\": getcwd() returned an empty string",
174 if (cwd
[cwd_len
- 1] != '/') {
179 path_len
= strlen(path
);
181 ERR("Failed to initialize directory handle: provided path is an empty string");
187 * Ensure that 'path' is a directory. There is a race
188 * (TOCTOU) since the directory could be removed/replaced/renamed,
189 * but this is inevitable on platforms that don't provide dirfd support.
191 ret
= stat(path
, &stat_buf
);
193 PERROR("Failed to initialize directory handle to \"%s\", stat() failed",
197 if (!S_ISDIR(stat_buf
.st_mode
)) {
198 ERR("Failed to initialize directory handle to \"%s\": not a directory",
204 handle
->base_path
= strdup(path
);
205 if (!handle
->base_path
) {
217 handle_path_len
= cwd_len
+ path_len
+ !!add_slash
+ 2;
218 if (handle_path_len
>= LTTNG_PATH_MAX
) {
219 ERR("Failed to initialize directory handle as the resulting path's length (%zu bytes) exceeds the maximal allowed length (%d bytes)",
220 handle_path_len
, LTTNG_PATH_MAX
);
224 handle
->base_path
= zmalloc(handle_path_len
);
225 if (!handle
->base_path
) {
226 PERROR("Failed to initialize directory handle");
231 ret
= sprintf(handle
->base_path
, "%s%s%s/", cwd
,
232 add_slash
? "/" : "", path
);
233 if (ret
== -1 || ret
>= handle_path_len
) {
234 ERR("Failed to initialize directory handle: path formatting failed");
243 int lttng_directory_handle_init_from_dirfd(
244 struct lttng_directory_handle
*handle
, int dirfd
)
246 assert(dirfd
== AT_FDCWD
);
247 return lttng_directory_handle_init(handle
, NULL
);
251 void lttng_directory_handle_fini(struct lttng_directory_handle
*handle
)
253 free(handle
->base_path
);
254 lttng_directory_handle_invalidate(handle
);
258 int lttng_directory_handle_copy(const struct lttng_directory_handle
*handle
,
259 struct lttng_directory_handle
*new_copy
)
261 new_copy
->base_path
= strdup(handle
->base_path
);
262 return new_copy
->base_path
? 0 : -1;
266 void lttng_directory_handle_invalidate(struct lttng_directory_handle
*handle
)
268 handle
->base_path
= NULL
;
272 int get_full_path(const struct lttng_directory_handle
*handle
,
273 const char *subdirectory
, char *fullpath
, size_t size
)
278 * Don't include the base path if subdirectory is absolute.
279 * This is the same behaviour than mkdirat.
281 ret
= snprintf(fullpath
, size
, "%s%s",
282 *subdirectory
!= '/' ? handle
->base_path
: "",
284 if (ret
== -1 || ret
>= size
) {
285 ERR("Failed to format subdirectory from directory handle");
293 int lttng_directory_handle_stat(const struct lttng_directory_handle
*handle
,
294 const char *subdirectory
, struct stat
*st
)
297 char fullpath
[LTTNG_PATH_MAX
];
299 ret
= get_full_path(handle
, subdirectory
, fullpath
, sizeof(fullpath
));
305 ret
= stat(fullpath
, st
);
311 int lttng_directory_handle_mkdir(const struct lttng_directory_handle
*handle
,
312 const char *subdirectory
, mode_t mode
)
315 char fullpath
[LTTNG_PATH_MAX
];
317 ret
= get_full_path(handle
, subdirectory
, fullpath
, sizeof(fullpath
));
323 ret
= mkdir(fullpath
, mode
);
329 int _run_as_mkdir(const struct lttng_directory_handle
*handle
, const char *path
,
330 mode_t mode
, uid_t uid
, gid_t gid
)
333 char fullpath
[LTTNG_PATH_MAX
];
335 ret
= get_full_path(handle
, path
, fullpath
, sizeof(fullpath
));
341 ret
= run_as_mkdir(fullpath
, mode
, uid
, gid
);
347 int _run_as_mkdir_recursive(const struct lttng_directory_handle
*handle
,
348 const char *path
, mode_t mode
, uid_t uid
, gid_t gid
)
351 char fullpath
[LTTNG_PATH_MAX
];
353 ret
= get_full_path(handle
, path
, fullpath
, sizeof(fullpath
));
359 ret
= run_as_mkdir_recursive(fullpath
, mode
, uid
, gid
);
364 #endif /* COMPAT_DIRFD */
367 * On some filesystems (e.g. nfs), mkdir will validate access rights before
368 * checking for the existence of the path element. This means that on a setup
369 * where "/home/" is a mounted NFS share, and running as an unpriviledged user,
370 * recursively creating a path of the form "/home/my_user/trace/" will fail with
371 * EACCES on mkdir("/home", ...).
373 * Checking the path for existence allows us to work around this behaviour.
376 int create_directory_check_exists(const struct lttng_directory_handle
*handle
,
377 const char *path
, mode_t mode
)
382 ret
= lttng_directory_handle_stat(handle
, path
, &st
);
384 if (S_ISDIR(st
.st_mode
)) {
385 /* Directory exists, skip. */
388 /* Exists, but is not a directory. */
396 * Let mkdir handle other errors as the caller expects mkdir
399 ret
= lttng_directory_handle_mkdir(handle
, path
, mode
);
404 /* Common implementation. */
406 struct lttng_directory_handle
407 lttng_directory_handle_move(struct lttng_directory_handle
*original
)
409 const struct lttng_directory_handle tmp
= *original
;
411 lttng_directory_handle_invalidate(original
);
416 int create_directory_recursive(const struct lttng_directory_handle
*handle
,
417 const char *path
, mode_t mode
)
419 char *p
, tmp
[LTTNG_PATH_MAX
];
425 ret
= lttng_strncpy(tmp
, path
, sizeof(tmp
));
427 ERR("Failed to create directory: provided path's length (%zu bytes) exceeds the maximal allowed length (%zu bytes)",
428 strlen(path
) + 1, sizeof(tmp
));
433 if (tmp
[len
- 1] == '/') {
437 for (p
= tmp
+ 1; *p
; p
++) {
440 if (tmp
[strlen(tmp
) - 1] == '.' &&
441 tmp
[strlen(tmp
) - 2] == '.' &&
442 tmp
[strlen(tmp
) - 3] == '/') {
443 ERR("Using '/../' is not permitted in the trace path (%s)",
448 ret
= create_directory_check_exists(handle
, tmp
, mode
);
450 if (errno
!= EACCES
) {
451 PERROR("Failed to create directory \"%s\"",
461 ret
= create_directory_check_exists(handle
, tmp
, mode
);
463 PERROR("mkdirat recursive last element");
471 int lttng_directory_handle_create_subdirectory_as_user(
472 const struct lttng_directory_handle
*handle
,
473 const char *subdirectory
,
474 mode_t mode
, const struct lttng_credentials
*creds
)
479 /* Run as current user. */
480 ret
= create_directory_check_exists(handle
,
483 ret
= _run_as_mkdir(handle
, subdirectory
,
484 mode
, creds
->uid
, creds
->gid
);
491 int lttng_directory_handle_create_subdirectory_recursive_as_user(
492 const struct lttng_directory_handle
*handle
,
493 const char *subdirectory_path
,
494 mode_t mode
, const struct lttng_credentials
*creds
)
499 /* Run as current user. */
500 ret
= create_directory_recursive(handle
,
501 subdirectory_path
, mode
);
503 ret
= _run_as_mkdir_recursive(handle
, subdirectory_path
,
504 mode
, creds
->uid
, creds
->gid
);
511 int lttng_directory_handle_create_subdirectory(
512 const struct lttng_directory_handle
*handle
,
513 const char *subdirectory
,
516 return lttng_directory_handle_create_subdirectory_as_user(
517 handle
, subdirectory
, mode
, NULL
);
521 int lttng_directory_handle_create_subdirectory_recursive(
522 const struct lttng_directory_handle
*handle
,
523 const char *subdirectory_path
,
526 return lttng_directory_handle_create_subdirectory_recursive_as_user(
527 handle
, subdirectory_path
, mode
, NULL
);