]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
path-util: unify path_extract_filename/directory into path_split_prefix_filename()
authorMike Yuan <me@yhndnzj.com>
Mon, 9 Feb 2026 00:25:39 +0000 (01:25 +0100)
committerMike Yuan <me@yhndnzj.com>
Mon, 9 Feb 2026 08:05:45 +0000 (09:05 +0100)
src/basic/path-util.c
src/basic/path-util.h

index 6902246c97288ed3bda18cd66714c2caa1b0ed03..0394d26420c587010bdcf3188d6e74934e96e04e 100644 (file)
@@ -1101,18 +1101,17 @@ const char* last_path_component(const char *path) {
         return path + k;
 }
 
-int path_extract_filename(const char *path, char **ret) {
-        _cleanup_free_ char *a = NULL;
+int path_split_prefix_filename(const char *path, char **ret_dir, char **ret_filename) {
+        _cleanup_free_ char *d = NULL;
         const char *c, *next = NULL;
         int r;
 
-        /* Extracts the filename part (i.e. right-most component) from a path, i.e. string that passes
-         * filename_is_valid(). A wrapper around last_path_component(), but eats up trailing
-         * slashes. Returns:
+        /* Split the path into dir prefix/filename pair. Returns:
          *
          * -EINVAL        → if the path is not valid
-         * -EADDRNOTAVAIL → if only a directory was specified, but no filename, i.e. the root dir
-         *                  itself or "." is specified
+         * -EADDRNOTAVAIL → if the path refers to the uppermost directory in hierarchy (i.e. has neither
+         *                  dir prefix nor filename - the root dir itself or ".")
+         * -EDESTADDRREQ  → if only a filename was passed, and caller only specifies ret_dir
          * -ENOMEM        → no memory
          *
          * Returns >= 0 on success. If the input path has a trailing slash, returns O_DIRECTORY, to
@@ -1121,63 +1120,52 @@ int path_extract_filename(const char *path, char **ret) {
          * This function guarantees to return a fully valid filename, i.e. one that passes
          * filename_is_valid() – this means "." and ".." are not accepted. */
 
-        if (!path_is_valid(path))
+        if (isempty(path))
                 return -EINVAL;
 
-        r = path_find_last_component(path, false, &next, &c);
+        r = path_find_last_component(path, /* accept_dot_dot = */ false, &next, &c);
         if (r < 0)
                 return r;
-        if (r == 0) /* root directory */
+        if (r == 0) /* root directory or "." */
                 return -EADDRNOTAVAIL;
 
-        a = strndup(c, r);
-        if (!a)
-                return -ENOMEM;
+        if (ret_dir) {
+                if (next == path) {
+                        if (*path != '/') { /* filename only */
+                                if (!ret_filename)
+                                        return -EDESTADDRREQ;
+                        } else {
+                                d = strdup("/");
+                                if (!d)
+                                        return -ENOMEM;
+                        }
+                } else {
+                        d = strndup(path, next - path);
+                        if (!d)
+                                return -ENOMEM;
 
-        *ret = TAKE_PTR(a);
-        return strlen(c) > (size_t) r ? O_DIRECTORY : 0;
-}
+                        path_simplify(d);
 
-int path_extract_directory(const char *path, char **ret) {
-        const char *c, *next = NULL;
-        int r;
+                        if (!path_is_valid(d))
+                                return -EINVAL;
+                }
 
-        /* The inverse of path_extract_filename(), i.e. returns the directory path prefix. Returns:
-         *
-         * -EINVAL        → if the path is not valid
-         * -EDESTADDRREQ  → if no directory was specified in the passed in path, i.e. only a filename was passed
-         * -EADDRNOTAVAIL → if the passed in parameter had no filename but did have a directory, i.e.
-         *                   the root dir itself or "." was specified
-         * -ENOMEM        → no memory (surprise!)
-         *
-         * This function guarantees to return a fully valid path, i.e. one that passes path_is_valid().
-         */
+        } else if (!path_is_valid(path))
+                /* We didn't validate the dir prefix, hence check if the whole path is valid now */
+                return -EINVAL;
 
-        r = path_find_last_component(path, false, &next, &c);
-        if (r < 0)
-                return r;
-        if (r == 0) /* empty or root */
-                return isempty(path) ? -EINVAL : -EADDRNOTAVAIL;
-        if (next == path) {
-                if (*path != '/') /* filename only */
-                        return -EDESTADDRREQ;
+        if (ret_filename) {
+                char *fn = strndup(c, r);
+                if (!fn)
+                        return -ENOMEM;
 
-                return strdup_to(ret, "/");
+                *ret_filename = fn;
         }
 
-        _cleanup_free_ char *a = strndup(path, next - path);
-        if (!a)
-                return -ENOMEM;
-
-        path_simplify(a);
+        if (ret_dir)
+                *ret_dir = TAKE_PTR(d);
 
-        if (!path_is_valid(a))
-                return -EINVAL;
-
-        if (ret)
-                *ret = TAKE_PTR(a);
-
-        return 0;
+        return strlen(c) > (size_t) r ? O_DIRECTORY : 0;
 }
 
 bool filename_part_is_valid(const char *p) {
index 5b2baef4e13257c65b0b68856a39cf483913527c..d70fc3b1bc6866f3bff07865af7d5abbe8ef7c07 100644 (file)
@@ -135,8 +135,15 @@ int fsck_exists_for_fstype(const char *fstype);
 int path_find_first_component(const char **p, bool accept_dot_dot, const char **ret);
 int path_find_last_component(const char *path, bool accept_dot_dot, const char **next, const char **ret);
 const char* last_path_component(const char *path);
-int path_extract_filename(const char *path, char **ret);
-int path_extract_directory(const char *path, char **ret);
+
+int path_split_prefix_filename(const char *path, char **ret_dir, char **ret_filename);
+static inline int path_extract_filename(const char *path, char **ret) {
+        return path_split_prefix_filename(path, NULL, ret);
+}
+static inline int path_extract_directory(const char *path, char **ret) {
+        int r = path_split_prefix_filename(path, ret, NULL);
+        return r < 0 ? r : 0; /* suppress O_DIRECTORY */
+}
 
 bool filename_part_is_valid(const char *p) _pure_;
 bool filename_is_valid(const char *p) _pure_;