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_copy(const struct lttng_directory_handle
*handle
,
93 struct lttng_directory_handle
*new_copy
)
97 if (handle
->dirfd
== AT_FDCWD
) {
98 new_copy
->dirfd
= handle
->dirfd
;
100 new_copy
->dirfd
= dup(handle
->dirfd
);
101 if (new_copy
->dirfd
== -1) {
102 PERROR("Failed to duplicate directory fd of directory handle");
110 int lttng_directory_handle_stat(const struct lttng_directory_handle
*handle
,
111 const char *path
, struct stat
*st
)
113 return fstatat(handle
->dirfd
, path
, st
, 0);
117 int lttng_directory_handle_mkdir(
118 const struct lttng_directory_handle
*handle
,
119 const char *path
, mode_t mode
)
121 return mkdirat(handle
->dirfd
, path
, mode
);
125 int _run_as_mkdir(const struct lttng_directory_handle
*handle
, const char *path
,
126 mode_t mode
, uid_t uid
, gid_t gid
)
128 return run_as_mkdirat(handle
->dirfd
, path
, mode
, uid
, gid
);
132 int _run_as_mkdir_recursive(const struct lttng_directory_handle
*handle
,
133 const char *path
, mode_t mode
, uid_t uid
, gid_t gid
)
135 return run_as_mkdirat_recursive(handle
->dirfd
, path
, mode
, uid
, gid
);
138 #else /* COMPAT_DIRFD */
141 int lttng_directory_handle_init(struct lttng_directory_handle
*handle
,
145 size_t cwd_len
, path_len
, handle_path_len
;
146 char cwd_buf
[LTTNG_PATH_MAX
];
148 bool add_slash
= false;
149 struct stat stat_buf
;
151 cwd
= getcwd(cwd_buf
, sizeof(cwd_buf
));
153 PERROR("Failed to initialize directory handle, can't get current working directory");
157 cwd_len
= strlen(cwd
);
159 ERR("Failed to initialize directory handle to \"%s\": getcwd() returned an empty string",
164 if (cwd
[cwd_len
- 1] != '/') {
169 path_len
= strlen(path
);
171 ERR("Failed to initialize directory handle: provided path is an empty string");
177 * Ensure that 'path' is a directory. There is a race
178 * (TOCTOU) since the directory could be removed/replaced/renamed,
179 * but this is inevitable on platforms that don't provide dirfd support.
181 ret
= stat(path
, &stat_buf
);
183 PERROR("Failed to initialize directory handle to \"%s\", stat() failed",
187 if (!S_ISDIR(stat_buf
.st_mode
)) {
188 ERR("Failed to initialize directory handle to \"%s\": not a directory",
194 handle
->base_path
= strdup(path
);
195 if (!handle
->base_path
) {
207 handle_path_len
= cwd_len
+ path_len
+ !!add_slash
+ 2;
208 if (handle_path_len
>= LTTNG_PATH_MAX
) {
209 ERR("Failed to initialize directory handle as the resulting path's length (%zu bytes) exceeds the maximal allowed length (%d bytes)",
210 handle_path_len
, LTTNG_PATH_MAX
);
214 handle
->base_path
= zmalloc(handle_path_len
);
215 if (!handle
->base_path
) {
216 PERROR("Failed to initialize directory handle");
221 ret
= sprintf(handle
->base_path
, "%s%s%s/", cwd
,
222 add_slash
? "/" : "", path
);
223 if (ret
== -1 || ret
>= handle_path_len
) {
224 ERR("Failed to initialize directory handle: path formatting failed");
233 int lttng_directory_handle_init_from_dirfd(
234 struct lttng_directory_handle
*handle
, int dirfd
)
236 assert(dirfd
== AT_FDCWD
);
237 return lttng_directory_handle_init(handle
, NULL
);
241 void lttng_directory_handle_fini(struct lttng_directory_handle
*handle
)
243 free(handle
->base_path
);
247 int lttng_directory_handle_copy(const struct lttng_directory_handle
*handle
,
248 struct lttng_directory_handle
*new_copy
)
250 new_copy
->base_path
= strdup(handle
->base_path
);
251 return new_copy
->base_path
? 0 : -1;
255 int get_full_path(const struct lttng_directory_handle
*handle
,
256 const char *subdirectory
, char *fullpath
, size_t size
)
261 * Don't include the base path if subdirectory is absolute.
262 * This is the same behaviour than mkdirat.
264 ret
= snprintf(fullpath
, size
, "%s%s",
265 *subdirectory
!= '/' ? handle
->base_path
: "",
267 if (ret
== -1 || ret
>= size
) {
268 ERR("Failed to format subdirectory from directory handle");
276 int lttng_directory_handle_stat(const struct lttng_directory_handle
*handle
,
277 const char *subdirectory
, struct stat
*st
)
280 char fullpath
[LTTNG_PATH_MAX
];
282 ret
= get_full_path(handle
, subdirectory
, fullpath
, sizeof(fullpath
));
288 ret
= stat(fullpath
, st
);
294 int lttng_directory_handle_mkdir(const struct lttng_directory_handle
*handle
,
295 const char *subdirectory
, mode_t mode
)
298 char fullpath
[LTTNG_PATH_MAX
];
300 ret
= get_full_path(handle
, subdirectory
, fullpath
, sizeof(fullpath
));
306 ret
= mkdir(fullpath
, mode
);
312 int _run_as_mkdir(const struct lttng_directory_handle
*handle
, const char *path
,
313 mode_t mode
, uid_t uid
, gid_t gid
)
316 char fullpath
[LTTNG_PATH_MAX
];
318 ret
= get_full_path(handle
, path
, fullpath
, sizeof(fullpath
));
324 ret
= run_as_mkdir(fullpath
, mode
, uid
, gid
);
330 int _run_as_mkdir_recursive(const struct lttng_directory_handle
*handle
,
331 const char *path
, mode_t mode
, uid_t uid
, gid_t gid
)
334 char fullpath
[LTTNG_PATH_MAX
];
336 ret
= get_full_path(handle
, path
, fullpath
, sizeof(fullpath
));
342 ret
= run_as_mkdir_recursive(fullpath
, mode
, uid
, gid
);
347 #endif /* COMPAT_DIRFD */
350 * On some filesystems (e.g. nfs), mkdir will validate access rights before
351 * checking for the existence of the path element. This means that on a setup
352 * where "/home/" is a mounted NFS share, and running as an unpriviledged user,
353 * recursively creating a path of the form "/home/my_user/trace/" will fail with
354 * EACCES on mkdir("/home", ...).
356 * Checking the path for existence allows us to work around this behaviour.
359 int create_directory_check_exists(const struct lttng_directory_handle
*handle
,
360 const char *path
, mode_t mode
)
365 ret
= lttng_directory_handle_stat(handle
, path
, &st
);
367 if (S_ISDIR(st
.st_mode
)) {
368 /* Directory exists, skip. */
371 /* Exists, but is not a directory. */
379 * Let mkdir handle other errors as the caller expects mkdir
382 ret
= lttng_directory_handle_mkdir(handle
, path
, mode
);
387 /* Common implementation. */
389 int create_directory_recursive(const struct lttng_directory_handle
*handle
,
390 const char *path
, mode_t mode
)
392 char *p
, tmp
[LTTNG_PATH_MAX
];
398 ret
= lttng_strncpy(tmp
, path
, sizeof(tmp
));
400 ERR("Failed to create directory: provided path's length (%zu bytes) exceeds the maximal allowed length (%zu bytes)",
401 strlen(path
) + 1, sizeof(tmp
));
406 if (tmp
[len
- 1] == '/') {
410 for (p
= tmp
+ 1; *p
; p
++) {
413 if (tmp
[strlen(tmp
) - 1] == '.' &&
414 tmp
[strlen(tmp
) - 2] == '.' &&
415 tmp
[strlen(tmp
) - 3] == '/') {
416 ERR("Using '/../' is not permitted in the trace path (%s)",
421 ret
= create_directory_check_exists(handle
, tmp
, mode
);
423 if (errno
!= EACCES
) {
424 PERROR("Failed to create directory \"%s\"",
434 ret
= create_directory_check_exists(handle
, tmp
, mode
);
436 PERROR("mkdirat recursive last element");
444 int lttng_directory_handle_create_subdirectory_as_user(
445 const struct lttng_directory_handle
*handle
,
446 const char *subdirectory
,
447 mode_t mode
, const struct lttng_credentials
*creds
)
452 /* Run as current user. */
453 ret
= create_directory_check_exists(handle
,
456 ret
= _run_as_mkdir(handle
, subdirectory
,
457 mode
, creds
->uid
, creds
->gid
);
464 int lttng_directory_handle_create_subdirectory_recursive_as_user(
465 const struct lttng_directory_handle
*handle
,
466 const char *subdirectory_path
,
467 mode_t mode
, const struct lttng_credentials
*creds
)
472 /* Run as current user. */
473 ret
= create_directory_recursive(handle
,
474 subdirectory_path
, mode
);
476 ret
= _run_as_mkdir_recursive(handle
, subdirectory_path
,
477 mode
, creds
->uid
, creds
->gid
);
484 int lttng_directory_handle_create_subdirectory(
485 const struct lttng_directory_handle
*handle
,
486 const char *subdirectory
,
489 return lttng_directory_handle_create_subdirectory_as_user(
490 handle
, subdirectory
, mode
, NULL
);
494 int lttng_directory_handle_create_subdirectory_recursive(
495 const struct lttng_directory_handle
*handle
,
496 const char *subdirectory_path
,
499 return lttng_directory_handle_create_subdirectory_recursive_as_user(
500 handle
, subdirectory_path
, mode
, NULL
);
This page took 0.089677 seconds and 4 git commands to generate.