Add a method to create a directory handle relative to another one
authorJérémie Galarneau <jeremie.galarneau@efficios.com>
Wed, 1 May 2019 21:34:34 +0000 (17:34 -0400)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Thu, 18 Jul 2019 19:58:24 +0000 (15:58 -0400)
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 <jeremie.galarneau@efficios.com>
src/common/compat/directory-handle.c
src/common/compat/directory-handle.h
src/common/utils.c

index 6e04c6a3c250bf20530656ab8bb9d336ecaa4036..5bfdd699ddd175658dc731f1d07d9760601d5ded 100644 (file)
@@ -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)
index 12191809471f9f4e183b1dece7196ac9774793b0..fbdc19179e594aa358ec12564b46cdc7aae999fc 100644 (file)
@@ -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.
  *
index f058e2003a440f6e1dffbf2076752abf0908c786..a91ede65c84af5d5c1d01797dc5766ee7e16057b 100644 (file)
@@ -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;
 }
 
This page took 0.029704 seconds and 4 git commands to generate.