From fd774fc6fa1c368a400976ad362e4f60f46e9861 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9mie=20Galarneau?= Date: Wed, 1 May 2019 17:34:34 -0400 Subject: [PATCH] Add a method to create a directory handle relative to another one MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Adds both implementations (dirfd present or not) of the lttng_directory_handle_init_from_handle method of the lttng_directory_handle. Signed-off-by: Jérémie Galarneau --- src/common/compat/directory-handle.c | 209 +++++++++++++++++---------- src/common/compat/directory-handle.h | 23 ++- src/common/utils.c | 12 +- 3 files changed, 165 insertions(+), 79 deletions(-) diff --git a/src/common/compat/directory-handle.c b/src/common/compat/directory-handle.c index 6e04c6a3c..5bfdd699d 100644 --- a/src/common/compat/directory-handle.c +++ b/src/common/compat/directory-handle.c @@ -47,22 +47,41 @@ void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle); #ifdef COMPAT_DIRFD LTTNG_HIDDEN -int lttng_directory_handle_init(struct lttng_directory_handle *handle, +int lttng_directory_handle_init(struct lttng_directory_handle *new_handle, const char *path) +{ + const struct lttng_directory_handle cwd_handle = { + .dirfd = AT_FDCWD, + }; + + /* Open a handle to the CWD if NULL is passed. */ + return lttng_directory_handle_init_from_handle(new_handle, + path, + &cwd_handle); +} + +LTTNG_HIDDEN +int lttng_directory_handle_init_from_handle( + struct lttng_directory_handle *new_handle, const char *path, + const struct lttng_directory_handle *handle) { int ret; if (!path) { - handle->dirfd = AT_FDCWD; - ret = 0; + ret = lttng_directory_handle_copy(handle, new_handle); + goto end; + } + if (!*path) { + ERR("Failed to initialize directory handle: provided path is an empty string"); + ret = -1; goto end; } - ret = open(path, O_RDONLY | O_DIRECTORY | O_CLOEXEC); + ret = openat(handle->dirfd, path, O_RDONLY | O_DIRECTORY | O_CLOEXEC); if (ret == -1) { PERROR("Failed to initialize directory handle to \"%s\"", path); goto end; } - handle->dirfd = ret; + new_handle->dirfd = ret; ret = 0; end: return ret; @@ -147,16 +166,50 @@ int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle, #else /* COMPAT_DIRFD */ +static +int get_full_path(const struct lttng_directory_handle *handle, + const char *subdirectory, char *fullpath, size_t size) +{ + int ret; + + subdirectory = subdirectory ? : ""; + /* + * Don't include the base path if subdirectory is absolute. + * This is the same behaviour than mkdirat. + */ + ret = snprintf(fullpath, size, "%s%s", + *subdirectory != '/' ? handle->base_path : "", + subdirectory); + if (ret == -1 || ret >= size) { + ERR("Failed to format subdirectory from directory handle"); + ret = -1; + } + ret = 0; + return ret; +} + LTTNG_HIDDEN int lttng_directory_handle_init(struct lttng_directory_handle *handle, const char *path) { int ret; - size_t cwd_len, path_len, handle_path_len; - char cwd_buf[LTTNG_PATH_MAX]; const char *cwd; - bool add_slash = false; - struct stat stat_buf; + size_t cwd_len, path_len; + char cwd_buf[LTTNG_PATH_MAX] = {}; + char handle_buf[LTTNG_PATH_MAX] = {}; + bool add_cwd_slash, add_trailing_slash; + const struct lttng_directory_handle cwd_handle = { + .base_path = handle_buf, + }; + + if (path && *path == '/') { + /* + * Creation of an handle to an absolute path; no need to sample + * the cwd. + */ + goto create; + } + path_len = path ? strlen(path) : 0; cwd = getcwd(cwd_buf, sizeof(cwd_buf)); if (!cwd) { @@ -166,70 +219,99 @@ int lttng_directory_handle_init(struct lttng_directory_handle *handle, } cwd_len = strlen(cwd); if (cwd_len == 0) { - ERR("Failed to initialize directory handle to \"%s\": getcwd() returned an empty string", - path); + ERR("Failed to initialize directory handle, current working directory path has a length of 0"); ret = -1; goto end; } - if (cwd[cwd_len - 1] != '/') { - add_slash = true; + + add_cwd_slash = cwd[cwd_len - 1] != '/'; + add_trailing_slash = path && path[path_len - 1] != '/'; + + ret = snprintf(handle_buf, sizeof(handle_buf), "%s%s%s%s", + cwd, + add_cwd_slash ? "/" : "", + path ? : "", + add_trailing_slash ? "/" : ""); + if (ret == -1 || ret >= LTTNG_PATH_MAX) { + ERR("Failed to initialize directory handle, failed to format directory path"); + goto end; } +create: + ret = lttng_directory_handle_init_from_handle(handle, path, + &cwd_handle); +end: + return ret; +} - if (path) { - path_len = strlen(path); - if (path_len == 0) { - ERR("Failed to initialize directory handle: provided path is an empty string"); - ret = -1; - goto end; - } +LTTNG_HIDDEN +int lttng_directory_handle_init_from_handle( + struct lttng_directory_handle *new_handle, const char *path, + const struct lttng_directory_handle *handle) +{ + int ret; + size_t path_len, handle_path_len; + bool add_trailing_slash; + struct stat stat_buf; - /* - * Ensure that 'path' is a directory. There is a race - * (TOCTOU) since the directory could be removed/replaced/renamed, - * but this is inevitable on platforms that don't provide dirfd support. - */ - ret = stat(path, &stat_buf); - if (ret == -1) { - PERROR("Failed to initialize directory handle to \"%s\", stat() failed", - path); - goto end; - } - if (!S_ISDIR(stat_buf.st_mode)) { - ERR("Failed to initialize directory handle to \"%s\": not a directory", - path); - ret = -1; - goto end; - } - if (*path == '/') { - handle->base_path = strdup(path); - if (!handle->base_path) { - ret = -1; - } - /* Not an error. */ - goto end; + assert(handle && handle->base_path); + + ret = lttng_directory_handle_stat(handle, path, &stat_buf); + if (ret == -1) { + PERROR("Failed to create directory handle"); + goto end; + } else if (!S_ISDIR(stat_buf.st_mode)) { + char full_path[LTTNG_PATH_MAX]; + + /* Best effort for logging purposes. */ + ret = get_full_path(handle, path, full_path, + sizeof(full_path)); + if (ret) { + full_path[0] = '\0'; } - } else { - path = ""; - path_len = 0; - add_slash = false; + + ERR("Failed to initialize directory handle to \"%s\": not a directory", + full_path); + ret = -1; + goto end; + } + if (!path) { + ret = lttng_directory_handle_copy(handle, new_handle); + goto end; } - handle_path_len = cwd_len + path_len + !!add_slash + 2; + path_len = strlen(path); + if (path_len == 0) { + ERR("Failed to initialize directory handle: provided path is an empty string"); + ret = -1; + goto end; + } + if (*path == '/') { + new_handle->base_path = strdup(path); + ret = new_handle->base_path ? 0 : -1; + goto end; + } + + add_trailing_slash = path[path_len - 1] != '/'; + + handle_path_len = strlen(handle->base_path) + path_len + + !!add_trailing_slash; if (handle_path_len >= LTTNG_PATH_MAX) { ERR("Failed to initialize directory handle as the resulting path's length (%zu bytes) exceeds the maximal allowed length (%d bytes)", handle_path_len, LTTNG_PATH_MAX); ret = -1; goto end; } - handle->base_path = zmalloc(handle_path_len); - if (!handle->base_path) { + new_handle->base_path = zmalloc(handle_path_len); + if (!new_handle->base_path) { PERROR("Failed to initialize directory handle"); ret = -1; goto end; } - ret = sprintf(handle->base_path, "%s%s%s/", cwd, - add_slash ? "/" : "", path); + ret = sprintf(new_handle->base_path, "%s%s%s", + handle->base_path, + path, + add_trailing_slash ? "/" : ""); if (ret == -1 || ret >= handle_path_len) { ERR("Failed to initialize directory handle: path formatting failed"); ret = -1; @@ -268,27 +350,6 @@ void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle) handle->base_path = NULL; } -static -int get_full_path(const struct lttng_directory_handle *handle, - const char *subdirectory, char *fullpath, size_t size) -{ - int ret; - - /* - * Don't include the base path if subdirectory is absolute. - * This is the same behaviour than mkdirat. - */ - ret = snprintf(fullpath, size, "%s%s", - *subdirectory != '/' ? handle->base_path : "", - subdirectory); - if (ret == -1 || ret >= size) { - ERR("Failed to format subdirectory from directory handle"); - ret = -1; - } - ret = 0; - return ret; -} - static int lttng_directory_handle_stat(const struct lttng_directory_handle *handle, const char *subdirectory, struct stat *st) diff --git a/src/common/compat/directory-handle.h b/src/common/compat/directory-handle.h index 121918094..fbdc19179 100644 --- a/src/common/compat/directory-handle.h +++ b/src/common/compat/directory-handle.h @@ -40,9 +40,7 @@ struct lttng_directory_handle { /* * Initialize a directory handle to the provided path. Passing a NULL path - * returns a handle to the current working directory. The working directory - * is not sampled; it will be accessed at the time of use of the functions - * of this API. + * returns a handle to the current working directory. * * An initialized directory handle must be finalized using * lttng_directory_handle_fini(). @@ -51,6 +49,25 @@ LTTNG_HIDDEN int lttng_directory_handle_init(struct lttng_directory_handle *handle, const char *path); +/* + * Initialize a new directory handle to a path relative to an existing handle. + * + * The provided path must already exist. Note that the creation of a + * subdirectory and the creation of a handle are kept as separate operations + * to highlight the fact that there is an inherent race between the creation of + * a directory and the creation of a handle to it. + * + * Passing a NULL path effectively copies the original handle. + * + * An initialized directory handle must be finalized using + * lttng_directory_handle_fini(). + */ +LTTNG_HIDDEN +int lttng_directory_handle_init_from_handle( + struct lttng_directory_handle *new_handle, + const char *path, + const struct lttng_directory_handle *handle); + /* * Initialize a new directory handle from an existing directory fd. * diff --git a/src/common/utils.c b/src/common/utils.c index f058e2003..a91ede65c 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -684,11 +684,15 @@ int utils_mkdir(const char *path, mode_t mode, int uid, int gid) .gid = (gid_t) gid, }; - (void) lttng_directory_handle_init(&handle, NULL); + ret = lttng_directory_handle_init(&handle, NULL); + if (ret) { + goto end; + } ret = lttng_directory_handle_create_subdirectory_as_user( &handle, path, mode, (uid >= 0 || gid >= 0) ? &creds : NULL); lttng_directory_handle_fini(&handle); +end: return ret; } @@ -708,11 +712,15 @@ int utils_mkdir_recursive(const char *path, mode_t mode, int uid, int gid) .gid = (gid_t) gid, }; - (void) lttng_directory_handle_init(&handle, NULL); + ret = lttng_directory_handle_init(&handle, NULL); + if (ret) { + goto end; + } ret = lttng_directory_handle_create_subdirectory_recursive_as_user( &handle, path, mode, (uid >= 0 || gid >= 0) ? &creds : NULL); lttng_directory_handle_fini(&handle); +end: return ret; } -- 2.34.1