X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fcommon%2Fcompat%2Fdirectory-handle.c;h=5bfdd699ddd175658dc731f1d07d9760601d5ded;hp=7077378e0a3e8aa40911eee665c6b972c4ef9168;hb=658f12fa973b58f324b6f018672cc03222cfc7b4;hpb=18710679a8ac57fda5dbd26cf16bb180dce9e286 diff --git a/src/common/compat/directory-handle.c b/src/common/compat/directory-handle.c index 7077378e0..5bfdd699d 100644 --- a/src/common/compat/directory-handle.c +++ b/src/common/compat/directory-handle.c @@ -41,26 +41,47 @@ int _run_as_mkdir(const struct lttng_directory_handle *handle, const char *path, static int _run_as_mkdir_recursive(const struct lttng_directory_handle *handle, const char *path, mode_t mode, uid_t uid, gid_t gid); +static +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; } - ret = open(path, O_RDONLY | O_DIRECTORY | O_CLOEXEC); + if (!*path) { + ERR("Failed to initialize directory handle: provided path is an empty string"); + ret = -1; + goto end; + } + 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; @@ -79,13 +100,39 @@ void lttng_directory_handle_fini(struct lttng_directory_handle *handle) { int ret; - if (handle->dirfd == AT_FDCWD) { - return; + if (handle->dirfd == AT_FDCWD || handle->dirfd == -1) { + goto end; } ret = close(handle->dirfd); if (ret == -1) { PERROR("Failed to close directory file descriptor of directory handle"); } +end: + lttng_directory_handle_invalidate(handle); +} + +LTTNG_HIDDEN +int lttng_directory_handle_copy(const struct lttng_directory_handle *handle, + struct lttng_directory_handle *new_copy) +{ + int ret = 0; + + if (handle->dirfd == AT_FDCWD) { + new_copy->dirfd = handle->dirfd; + } else { + new_copy->dirfd = dup(handle->dirfd); + if (new_copy->dirfd == -1) { + PERROR("Failed to duplicate directory fd of directory handle"); + ret = -1; + } + } + return ret; +} + +static +void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle) +{ + handle->dirfd = -1; } static @@ -119,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) { @@ -138,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; @@ -223,27 +333,21 @@ LTTNG_HIDDEN void lttng_directory_handle_fini(struct lttng_directory_handle *handle) { free(handle->base_path); + lttng_directory_handle_invalidate(handle); } -static -int get_full_path(const struct lttng_directory_handle *handle, - const char *subdirectory, char *fullpath, size_t size) +LTTNG_HIDDEN +int lttng_directory_handle_copy(const struct lttng_directory_handle *handle, + struct lttng_directory_handle *new_copy) { - int ret; + new_copy->base_path = strdup(handle->base_path); + return new_copy->base_path ? 0 : -1; +} - /* - * 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 +void lttng_directory_handle_invalidate(struct lttng_directory_handle *handle) +{ + handle->base_path = NULL; } static @@ -359,6 +463,16 @@ end: } /* Common implementation. */ +LTTNG_HIDDEN +struct lttng_directory_handle +lttng_directory_handle_move(struct lttng_directory_handle *original) +{ + const struct lttng_directory_handle tmp = *original; + + lttng_directory_handle_invalidate(original); + return tmp; +} + static int create_directory_recursive(const struct lttng_directory_handle *handle, const char *path, mode_t mode) @@ -418,7 +532,7 @@ LTTNG_HIDDEN int lttng_directory_handle_create_subdirectory_as_user( const struct lttng_directory_handle *handle, const char *subdirectory, - mode_t mode, struct lttng_credentials *creds) + mode_t mode, const struct lttng_credentials *creds) { int ret; @@ -438,7 +552,7 @@ LTTNG_HIDDEN int lttng_directory_handle_create_subdirectory_recursive_as_user( const struct lttng_directory_handle *handle, const char *subdirectory_path, - mode_t mode, struct lttng_credentials *creds) + mode_t mode, const struct lttng_credentials *creds) { int ret;