mirror of
https://github.com/systemd/systemd.git
synced 2024-10-31 16:21:26 +03:00
path-util: use path_find_first_component() in path_make_relative()
This also makes the function checks the result is a valid path or not.
This commit is contained in:
parent
63f11e354a
commit
fe69c41ee8
@ -107,93 +107,96 @@ int path_make_absolute_cwd(const char *p, char **ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int path_make_relative(const char *from_dir, const char *to_path, char **_r) {
|
||||
char *f, *t, *r, *p;
|
||||
unsigned n_parents = 0;
|
||||
int path_make_relative(const char *from, const char *to, char **ret) {
|
||||
_cleanup_free_ char *result = NULL;
|
||||
unsigned n_parents;
|
||||
const char *f, *t;
|
||||
int r, k;
|
||||
char *p;
|
||||
|
||||
assert(from_dir);
|
||||
assert(to_path);
|
||||
assert(_r);
|
||||
assert(from);
|
||||
assert(to);
|
||||
assert(ret);
|
||||
|
||||
/* Strips the common part, and adds ".." elements as necessary. */
|
||||
|
||||
if (!path_is_absolute(from_dir) || !path_is_absolute(to_path))
|
||||
if (!path_is_absolute(from) || !path_is_absolute(to))
|
||||
return -EINVAL;
|
||||
|
||||
f = strdupa(from_dir);
|
||||
t = strdupa(to_path);
|
||||
|
||||
path_simplify(f, true);
|
||||
path_simplify(t, true);
|
||||
|
||||
/* Skip the common part. */
|
||||
for (;;) {
|
||||
size_t a, b;
|
||||
r = path_find_first_component(&from, true, &f);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
f += *f == '/';
|
||||
t += *t == '/';
|
||||
k = path_find_first_component(&to, true, &t);
|
||||
if (k < 0)
|
||||
return k;
|
||||
|
||||
if (!*f) {
|
||||
if (!*t)
|
||||
/* from_dir equals to_path. */
|
||||
r = strdup(".");
|
||||
else
|
||||
/* from_dir is a parent directory of to_path. */
|
||||
r = strdup(t);
|
||||
if (!r)
|
||||
return -ENOMEM;
|
||||
if (r == 0) {
|
||||
/* end of 'from' */
|
||||
if (k == 0) {
|
||||
/* from and to are equivalent. */
|
||||
result = strdup(".");
|
||||
if (!result)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
/* 'to' is inside of 'from'. */
|
||||
result = strdup(t);
|
||||
if (!result)
|
||||
return -ENOMEM;
|
||||
|
||||
*_r = r;
|
||||
path_simplify(result, true);
|
||||
|
||||
if (!path_is_valid(result))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!*t)
|
||||
if (r != k || !strneq(f, t, r))
|
||||
break;
|
||||
|
||||
a = strcspn(f, "/");
|
||||
b = strcspn(t, "/");
|
||||
|
||||
if (a != b || memcmp(f, t, a) != 0)
|
||||
break;
|
||||
|
||||
f += a;
|
||||
t += b;
|
||||
}
|
||||
|
||||
/* If we're here, then "from_dir" has one or more elements that need to
|
||||
* be replaced with "..". */
|
||||
|
||||
/* Count the number of necessary ".." elements. */
|
||||
for (; *f;) {
|
||||
size_t w;
|
||||
|
||||
w = strcspn(f, "/");
|
||||
|
||||
/* If this includes ".." we can't do a simple series of "..", refuse */
|
||||
if (w == 2 && f[0] == '.' && f[1] == '.')
|
||||
return -EINVAL;
|
||||
|
||||
/* Count number of elements */
|
||||
n_parents++;
|
||||
|
||||
f += w;
|
||||
f += *f == '/';
|
||||
for (n_parents = 1;; n_parents++) {
|
||||
/* If this includes ".." we can't do a simple series of "..". */
|
||||
r = path_find_first_component(&from, false, &f);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
r = new(char, n_parents * 3 + strlen(t) + 1);
|
||||
if (!r)
|
||||
if (isempty(t) && n_parents * 3 > PATH_MAX)
|
||||
/* PATH_MAX is counted *with* the trailing NUL byte */
|
||||
return -EINVAL;
|
||||
|
||||
result = new(char, n_parents * 3 + !isempty(t) + strlen_ptr(t));
|
||||
if (!result)
|
||||
return -ENOMEM;
|
||||
|
||||
for (p = r; n_parents > 0; n_parents--)
|
||||
for (p = result; n_parents > 0; n_parents--)
|
||||
p = mempcpy(p, "../", 3);
|
||||
|
||||
if (*t)
|
||||
strcpy(p, t);
|
||||
else
|
||||
/* Remove trailing slash */
|
||||
*(--p) = 0;
|
||||
if (isempty(t)) {
|
||||
/* Remove trailing slash and terminate string. */
|
||||
*(--p) = '\0';
|
||||
*ret = TAKE_PTR(result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*_r = r;
|
||||
strcpy(p, t);
|
||||
|
||||
path_simplify(result, true);
|
||||
|
||||
if (!path_is_valid(result))
|
||||
return -EINVAL;
|
||||
|
||||
*ret = TAKE_PTR(result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ int path_split_and_make_absolute(const char *p, char ***ret);
|
||||
char* path_make_absolute(const char *p, const char *prefix);
|
||||
int safe_getcwd(char **ret);
|
||||
int path_make_absolute_cwd(const char *p, char **ret);
|
||||
int path_make_relative(const char *from_dir, const char *to_path, char **_r);
|
||||
int path_make_relative(const char *from, const char *to, char **ret);
|
||||
char *path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) _pure_;
|
||||
static inline char* path_startswith(const char *path, const char *prefix) {
|
||||
return path_startswith_full(path, prefix, true);
|
||||
|
@ -420,29 +420,32 @@ static void test_fsck_exists(void) {
|
||||
assert_se(fsck_exists("/../bin/") == 0);
|
||||
}
|
||||
|
||||
static void test_make_relative(void) {
|
||||
char *result;
|
||||
static void test_path_make_relative_one(const char *from, const char *to, const char *expected) {
|
||||
_cleanup_free_ char *z = NULL;
|
||||
int r;
|
||||
|
||||
log_info("/* %s(%s, %s) */", __func__, from, to);
|
||||
|
||||
r = path_make_relative(from, to, &z);
|
||||
assert_se((r >= 0) == !!expected);
|
||||
assert_se(streq_ptr(z, expected));
|
||||
}
|
||||
|
||||
static void test_make_relative(void) {
|
||||
log_info("/* %s */", __func__);
|
||||
|
||||
assert_se(path_make_relative("some/relative/path", "/some/path", &result) < 0);
|
||||
assert_se(path_make_relative("/some/path", "some/relative/path", &result) < 0);
|
||||
assert_se(path_make_relative("/some/dotdot/../path", "/some/path", &result) < 0);
|
||||
test_path_make_relative_one("some/relative/path", "/some/path", NULL);
|
||||
test_path_make_relative_one("/some/path", "some/relative/path", NULL);
|
||||
test_path_make_relative_one("/some/dotdot/../path", "/some/path", NULL);
|
||||
|
||||
#define test(from_dir, to_path, expected) { \
|
||||
_cleanup_free_ char *z = NULL; \
|
||||
path_make_relative(from_dir, to_path, &z); \
|
||||
assert_se(streq(z, expected)); \
|
||||
}
|
||||
|
||||
test("/", "/", ".");
|
||||
test("/", "/some/path", "some/path");
|
||||
test("/some/path", "/some/path", ".");
|
||||
test("/some/path", "/some/path/in/subdir", "in/subdir");
|
||||
test("/some/path", "/", "../..");
|
||||
test("/some/path", "/some/other/path", "../other/path");
|
||||
test("/some/path/./dot", "/some/further/path", "../../further/path");
|
||||
test("//extra.//.//./.slashes//./won't////fo.ol///anybody//", "/././/extra././/.slashes////ar.e/.just/././.fine///", "../../../ar.e/.just/.fine");
|
||||
test_path_make_relative_one("/", "/", ".");
|
||||
test_path_make_relative_one("/", "/some/path", "some/path");
|
||||
test_path_make_relative_one("/some/path", "/some/path", ".");
|
||||
test_path_make_relative_one("/some/path", "/some/path/in/subdir", "in/subdir");
|
||||
test_path_make_relative_one("/some/path", "/", "../..");
|
||||
test_path_make_relative_one("/some/path", "/some/other/path", "../other/path");
|
||||
test_path_make_relative_one("/some/path/./dot", "/some/further/path", "../../further/path");
|
||||
test_path_make_relative_one("//extra.//.//./.slashes//./won't////fo.ol///anybody//", "/././/extra././/.slashes////ar.e/.just/././.fine///", "../../../ar.e/.just/.fine");
|
||||
}
|
||||
|
||||
static void test_strv_resolve(void) {
|
||||
|
Loading…
Reference in New Issue
Block a user