diff --git a/src/basic/path-util.c b/src/basic/path-util.c index fe8321edce..50ba44492e 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -819,11 +819,21 @@ const char *last_path_component(const char *path) { int path_extract_filename(const char *p, char **ret) { _cleanup_free_ char *a = NULL; const char *c; + size_t n; /* 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 - * -EADDRNOTAVAIL if specified parameter includes no filename (i.e. is "/" or so). Returns -EINVAL if - * not a valid path in the first place. */ + * 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 + * -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. + * + * 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)) return -EINVAL; @@ -834,8 +844,9 @@ int path_extract_filename(const char *p, char **ret) { return -EADDRNOTAVAIL; c = last_path_component(p); + n = strcspn(c, "/"); - a = strndup(c, strcspn(c, "/")); + a = strndup(c, n); if (!a) return -ENOMEM; @@ -843,7 +854,7 @@ int path_extract_filename(const char *p, char **ret) { return -EINVAL; *ret = TAKE_PTR(a); - return 0; + return c[n] == '/' ? O_DIRECTORY : 0; } int path_extract_directory(const char *p, char **ret) { diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c index db6c1a9efa..b92a77b9f4 100644 --- a/src/test/test-path-util.c +++ b/src/test/test-path-util.c @@ -570,7 +570,10 @@ static void test_path_extract_filename_one(const char *input, const char *output int r; r = path_extract_filename(input, &k); - log_info("%s → %s/%s [expected: %s/%s]", strnull(input), strnull(k), strerror_safe(r), strnull(output), strerror_safe(ret)); + log_info_errno(r, "%s → %s/%m [expected: %s/%s]", + strnull(input), + strnull(k), /* strerror(r) is printed via %m, to avoid that the two strerror()'s overwrite each other's buffers */ + strnull(output), ret < 0 ? strerror_safe(ret) : "-"); assert_se(streq_ptr(k, output)); assert_se(r == ret); } @@ -580,7 +583,7 @@ static void test_path_extract_filename(void) { test_path_extract_filename_one(NULL, NULL, -EINVAL); test_path_extract_filename_one("a/b/c", "c", 0); - test_path_extract_filename_one("a/b/c/", "c", 0); + test_path_extract_filename_one("a/b/c/", "c", O_DIRECTORY); test_path_extract_filename_one("/", NULL, -EADDRNOTAVAIL); test_path_extract_filename_one("//", NULL, -EADDRNOTAVAIL); test_path_extract_filename_one("///", NULL, -EADDRNOTAVAIL); @@ -589,13 +592,13 @@ static void test_path_extract_filename(void) { test_path_extract_filename_one("././", NULL, -EINVAL); test_path_extract_filename_one("././/", NULL, -EINVAL); test_path_extract_filename_one("/foo/a", "a", 0); - test_path_extract_filename_one("/foo/a/", "a", 0); + test_path_extract_filename_one("/foo/a/", "a", O_DIRECTORY); test_path_extract_filename_one("", NULL, -EINVAL); test_path_extract_filename_one("a", "a", 0); - test_path_extract_filename_one("a/", "a", 0); + test_path_extract_filename_one("a/", "a", O_DIRECTORY); test_path_extract_filename_one("/a", "a", 0); - test_path_extract_filename_one("/a/", "a", 0); - test_path_extract_filename_one("/////////////a/////////////", "a", 0); + test_path_extract_filename_one("/a/", "a", O_DIRECTORY); + test_path_extract_filename_one("/////////////a/////////////", "a", O_DIRECTORY); test_path_extract_filename_one("xx/.", NULL, -EINVAL); test_path_extract_filename_one("xx/..", NULL, -EINVAL); test_path_extract_filename_one("..", NULL, -EINVAL);