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
);
48 int lttng_directory_handle_init(struct lttng_directory_handle
*handle
,
54 handle
->dirfd
= AT_FDCWD
;
58 ret
= open(path
, O_RDONLY
| O_DIRECTORY
| O_CLOEXEC
);
60 PERROR("Failed to initialize directory handle to \"%s\"", path
);
70 int lttng_directory_handle_init_from_dirfd(
71 struct lttng_directory_handle
*handle
, int dirfd
)
73 handle
->dirfd
= dirfd
;
78 void lttng_directory_handle_fini(struct lttng_directory_handle
*handle
)
82 if (handle
->dirfd
== AT_FDCWD
) {
85 ret
= close(handle
->dirfd
);
87 PERROR("Failed to close directory file descriptor of directory handle");
92 int lttng_directory_handle_stat(const struct lttng_directory_handle
*handle
,
93 const char *path
, struct stat
*st
)
95 return fstatat(handle
->dirfd
, path
, st
, 0);
99 int lttng_directory_handle_mkdir(
100 const struct lttng_directory_handle
*handle
,
101 const char *path
, mode_t mode
)
103 return mkdirat(handle
->dirfd
, path
, mode
);
107 int _run_as_mkdir(const struct lttng_directory_handle
*handle
, const char *path
,
108 mode_t mode
, uid_t uid
, gid_t gid
)
110 return run_as_mkdirat(handle
->dirfd
, path
, mode
, uid
, gid
);
114 int _run_as_mkdir_recursive(const struct lttng_directory_handle
*handle
,
115 const char *path
, mode_t mode
, uid_t uid
, gid_t gid
)
117 return run_as_mkdirat_recursive(handle
->dirfd
, path
, mode
, uid
, gid
);
120 #else /* COMPAT_DIRFD */
123 int lttng_directory_handle_init(struct lttng_directory_handle
*handle
,
127 size_t cwd_len
, path_len
, handle_path_len
;
128 char cwd_buf
[LTTNG_PATH_MAX
];
130 bool add_slash
= false;
131 struct stat stat_buf
;
133 cwd
= getcwd(cwd_buf
, sizeof(cwd_buf
));
135 PERROR("Failed to initialize directory handle, can't get current working directory");
139 cwd_len
= strlen(cwd
);
141 ERR("Failed to initialize directory handle to \"%s\": getcwd() returned an empty string",
146 if (cwd
[cwd_len
- 1] != '/') {
151 path_len
= strlen(path
);
153 ERR("Failed to initialize directory handle: provided path is an empty string");
159 * Ensure that 'path' is a directory. There is a race
160 * (TOCTOU) since the directory could be removed/replaced/renamed,
161 * but this is inevitable on platforms that don't provide dirfd support.
163 ret
= stat(path
, &stat_buf
);
165 PERROR("Failed to initialize directory handle to \"%s\", stat() failed",
169 if (!S_ISDIR(stat_buf
.st_mode
)) {
170 ERR("Failed to initialize directory handle to \"%s\": not a directory",
176 handle
->base_path
= strdup(path
);
177 if (!handle
->base_path
) {
189 handle_path_len
= cwd_len
+ path_len
+ !!add_slash
+ 2;
190 if (handle_path_len
>= LTTNG_PATH_MAX
) {
191 ERR("Failed to initialize directory handle as the resulting path's length (%zu bytes) exceeds the maximal allowed length (%d bytes)",
192 handle_path_len
, LTTNG_PATH_MAX
);
196 handle
->base_path
= zmalloc(handle_path_len
);
197 if (!handle
->base_path
) {
198 PERROR("Failed to initialize directory handle");
203 ret
= sprintf(handle
->base_path
, "%s%s%s/", cwd
,
204 add_slash
? "/" : "", path
);
205 if (ret
== -1 || ret
>= handle_path_len
) {
206 ERR("Failed to initialize directory handle: path formatting failed");
215 int lttng_directory_handle_init_from_dirfd(
216 struct lttng_directory_handle
*handle
, int dirfd
)
218 assert(dirfd
== AT_FDCWD
);
219 return lttng_directory_handle_init(handle
, NULL
);
223 void lttng_directory_handle_fini(struct lttng_directory_handle
*handle
)
225 free(handle
->base_path
);
229 int get_full_path(const struct lttng_directory_handle
*handle
,
230 const char *subdirectory
, char *fullpath
, size_t size
)
235 * Don't include the base path if subdirectory is absolute.
236 * This is the same behaviour than mkdirat.
238 ret
= snprintf(fullpath
, size
, "%s%s",
239 *subdirectory
!= '/' ? handle
->base_path
: "",
241 if (ret
== -1 || ret
>= size
) {
242 ERR("Failed to format subdirectory from directory handle");
250 int lttng_directory_handle_stat(const struct lttng_directory_handle
*handle
,
251 const char *subdirectory
, struct stat
*st
)
254 char fullpath
[LTTNG_PATH_MAX
];
256 ret
= get_full_path(handle
, subdirectory
, fullpath
, sizeof(fullpath
));
262 ret
= stat(fullpath
, st
);
268 int lttng_directory_handle_mkdir(const struct lttng_directory_handle
*handle
,
269 const char *subdirectory
, mode_t mode
)
272 char fullpath
[LTTNG_PATH_MAX
];
274 ret
= get_full_path(handle
, subdirectory
, fullpath
, sizeof(fullpath
));
280 ret
= mkdir(fullpath
, mode
);
286 int _run_as_mkdir(const struct lttng_directory_handle
*handle
, const char *path
,
287 mode_t mode
, uid_t uid
, gid_t gid
)
290 char fullpath
[LTTNG_PATH_MAX
];
292 ret
= get_full_path(handle
, path
, fullpath
, sizeof(fullpath
));
298 ret
= run_as_mkdir(fullpath
, mode
, uid
, gid
);
304 int _run_as_mkdir_recursive(const struct lttng_directory_handle
*handle
,
305 const char *path
, mode_t mode
, uid_t uid
, gid_t gid
)
308 char fullpath
[LTTNG_PATH_MAX
];
310 ret
= get_full_path(handle
, path
, fullpath
, sizeof(fullpath
));
316 ret
= run_as_mkdir_recursive(fullpath
, mode
, uid
, gid
);
321 #endif /* COMPAT_DIRFD */
324 * On some filesystems (e.g. nfs), mkdir will validate access rights before
325 * checking for the existence of the path element. This means that on a setup
326 * where "/home/" is a mounted NFS share, and running as an unpriviledged user,
327 * recursively creating a path of the form "/home/my_user/trace/" will fail with
328 * EACCES on mkdir("/home", ...).
330 * Checking the path for existence allows us to work around this behaviour.
333 int create_directory_check_exists(const struct lttng_directory_handle
*handle
,
334 const char *path
, mode_t mode
)
339 ret
= lttng_directory_handle_stat(handle
, path
, &st
);
341 if (S_ISDIR(st
.st_mode
)) {
342 /* Directory exists, skip. */
345 /* Exists, but is not a directory. */
353 * Let mkdir handle other errors as the caller expects mkdir
356 ret
= lttng_directory_handle_mkdir(handle
, path
, mode
);
361 /* Common implementation. */
363 int create_directory_recursive(const struct lttng_directory_handle
*handle
,
364 const char *path
, mode_t mode
)
366 char *p
, tmp
[LTTNG_PATH_MAX
];
372 ret
= lttng_strncpy(tmp
, path
, sizeof(tmp
));
374 ERR("Failed to create directory: provided path's length (%zu bytes) exceeds the maximal allowed length (%zu bytes)",
375 strlen(path
) + 1, sizeof(tmp
));
380 if (tmp
[len
- 1] == '/') {
384 for (p
= tmp
+ 1; *p
; p
++) {
387 if (tmp
[strlen(tmp
) - 1] == '.' &&
388 tmp
[strlen(tmp
) - 2] == '.' &&
389 tmp
[strlen(tmp
) - 3] == '/') {
390 ERR("Using '/../' is not permitted in the trace path (%s)",
395 ret
= create_directory_check_exists(handle
, tmp
, mode
);
397 if (errno
!= EACCES
) {
398 PERROR("Failed to create directory \"%s\"",
408 ret
= create_directory_check_exists(handle
, tmp
, mode
);
410 PERROR("mkdirat recursive last element");
418 int lttng_directory_handle_create_subdirectory_as_user(
419 const struct lttng_directory_handle
*handle
,
420 const char *subdirectory
,
421 mode_t mode
, const struct lttng_credentials
*creds
)
426 /* Run as current user. */
427 ret
= create_directory_check_exists(handle
,
430 ret
= _run_as_mkdir(handle
, subdirectory
,
431 mode
, creds
->uid
, creds
->gid
);
438 int lttng_directory_handle_create_subdirectory_recursive_as_user(
439 const struct lttng_directory_handle
*handle
,
440 const char *subdirectory_path
,
441 mode_t mode
, const struct lttng_credentials
*creds
)
446 /* Run as current user. */
447 ret
= create_directory_recursive(handle
,
448 subdirectory_path
, mode
);
450 ret
= _run_as_mkdir_recursive(handle
, subdirectory_path
,
451 mode
, creds
->uid
, creds
->gid
);
458 int lttng_directory_handle_create_subdirectory(
459 const struct lttng_directory_handle
*handle
,
460 const char *subdirectory
,
463 return lttng_directory_handle_create_subdirectory_as_user(
464 handle
, subdirectory
, mode
, NULL
);
468 int lttng_directory_handle_create_subdirectory_recursive(
469 const struct lttng_directory_handle
*handle
,
470 const char *subdirectory_path
,
473 return lttng_directory_handle_create_subdirectory_recursive_as_user(
474 handle
, subdirectory_path
, mode
, NULL
);
This page took 0.039744 seconds and 4 git commands to generate.