diff options
author | Yu Watanabe <watanabe.yu+github@gmail.com> | 2021-05-04 14:40:56 +0900 |
---|---|---|
committer | Yu Watanabe <watanabe.yu+github@gmail.com> | 2021-05-28 13:44:38 +0900 |
commit | 0195046449fc1cf946fe3087b7304a600453dbdd (patch) | |
tree | 940a826836968b4c469e122aab16d15263e307b6 /src/basic/path-util.c | |
parent | 484cd43cae536d952cd84abfda3260235dc893cc (diff) | |
download | systemd-0195046449fc1cf946fe3087b7304a600453dbdd.tar.gz |
path-util: make path_extract_filename/directory() handle "." gracefully
This makes the functions handle "xx/" and "xx/." as equivalent.
Moreover, now path_extract_directory() returns normalized path, that is
no redundant "/" or "/./" are contained.
Diffstat (limited to 'src/basic/path-util.c')
-rw-r--r-- | src/basic/path-util.c | 79 |
1 files changed, 39 insertions, 40 deletions
diff --git a/src/basic/path-util.c b/src/basic/path-util.c index ab89623c5a..0be5af8da1 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -1043,82 +1043,81 @@ const char *last_path_component(const char *path) { return path + k; } -int path_extract_filename(const char *p, char **ret) { +int path_extract_filename(const char *path, char **ret) { _cleanup_free_ char *a = NULL; - const char *c; - size_t n; + 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: * - * -EINVAL → if the passed in path is not a valid path - * -EADDRNOTAVAIL → if only a directory was specified, but no filename, i.e. the root dir itself is specified + * -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 * -ENOMEM → no memory * - * Returns >= 0 on success. If the input path has a trailing slash, returns O_DIRECTORY, to indicate - * the referenced file must be a directory. + * Returns >= 0 on success. If the input path has a trailing slash, returns O_DIRECTORY, to + * indicate the referenced file must be a directory. * * 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(p)) + if (!path_is_valid(path)) return -EINVAL; - /* Special case the root dir, because in that case we simply have no filename, but - * last_path_component() won't complain */ - if (path_equal(p, "/")) + r = path_find_last_component(path, false, &next, &c); + if (r < 0) + return r; + if (r == 0) /* root directory */ return -EADDRNOTAVAIL; - c = last_path_component(p); - n = strcspn(c, "/"); - - a = strndup(c, n); + a = strndup(c, r); if (!a) return -ENOMEM; - if (!filename_is_valid(a)) - return -EINVAL; - *ret = TAKE_PTR(a); - return c[n] == '/' ? O_DIRECTORY : 0; + return strlen(c) > (size_t)r ? O_DIRECTORY : 0; } -int path_extract_directory(const char *p, char **ret) { +int path_extract_directory(const char *path, char **ret) { _cleanup_free_ char *a = NULL; - const char *c; + const char *c, *next = NULL; + int r; /* The inverse of path_extract_filename(), i.e. returns the directory path prefix. Returns: * - * -EINVAL → if the passed in path is not a valid path + * -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 was specified + * -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(). */ - if (!path_is_valid(p)) - return -EINVAL; - - /* Special case the root dir, because otherwise for an input of "///" last_path_component() returns - * the pointer to the last slash only, which might be seen as a valid path below. */ - if (path_equal(p, "/")) - return -EADDRNOTAVAIL; - - c = last_path_component(p); - - /* Delete trailing slashes, but keep one */ - while (c > p+1 && c[-1] == '/') - c--; - - if (p == c) /* No path whatsoever? Then return a recognizable error */ - return -EDESTADDRREQ; + 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; + + a = strdup("/"); + if (!a) + return -ENOMEM; + *ret = TAKE_PTR(a); + return 0; + } - a = strndup(p, c - p); + a = strndup(path, next - path); if (!a) return -ENOMEM; + path_simplify(a, true); + if (!path_is_valid(a)) return -EINVAL; |