diff options
author | Yu Watanabe <watanabe.yu+github@gmail.com> | 2021-05-02 05:53:14 +0900 |
---|---|---|
committer | Yu Watanabe <watanabe.yu+github@gmail.com> | 2021-05-28 13:41:23 +0900 |
commit | fe69c41ee80920714b13c958cb180cce03f4098e (patch) | |
tree | d234fc6073e90dde4d282f2efbb2653e2790f8eb /src/basic/path-util.c | |
parent | 63f11e354a3e5d64ee4cabb7bb584307d237fee4 (diff) | |
download | systemd-fe69c41ee80920714b13c958cb180cce03f4098e.tar.gz |
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.
Diffstat (limited to 'src/basic/path-util.c')
-rw-r--r-- | src/basic/path-util.c | 123 |
1 files changed, 63 insertions, 60 deletions
diff --git a/src/basic/path-util.c b/src/basic/path-util.c index aa65e2950f..6cfad9efe6 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -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; - - assert(from_dir); - assert(to_path); - assert(_r); +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); + 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; + + k = path_find_first_component(&to, true, &t); + if (k < 0) + return k; + + 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; - f += *f == '/'; - t += *t == '/'; + path_simplify(result, true); - 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 (!path_is_valid(result)) + return -EINVAL; + } - *_r = r; + *ret = TAKE_PTR(result); return 0; } - if (!*t) - break; - - a = strcspn(f, "/"); - b = strcspn(t, "/"); - - if (a != b || memcmp(f, t, a) != 0) + if (r != k || !strneq(f, t, r)) 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; + 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; + } - w = strcspn(f, "/"); + if (isempty(t) && n_parents * 3 > PATH_MAX) + /* PATH_MAX is counted *with* the trailing NUL byte */ + return -EINVAL; - /* If this includes ".." we can't do a simple series of "..", refuse */ - if (w == 2 && f[0] == '.' && f[1] == '.') - return -EINVAL; + result = new(char, n_parents * 3 + !isempty(t) + strlen_ptr(t)); + if (!result) + return -ENOMEM; - /* Count number of elements */ - n_parents++; + for (p = result; n_parents > 0; n_parents--) + p = mempcpy(p, "../", 3); - f += w; - f += *f == '/'; + if (isempty(t)) { + /* Remove trailing slash and terminate string. */ + *(--p) = '\0'; + *ret = TAKE_PTR(result); + return 0; } - r = new(char, n_parents * 3 + strlen(t) + 1); - if (!r) - return -ENOMEM; + strcpy(p, t); - for (p = r; n_parents > 0; n_parents--) - p = mempcpy(p, "../", 3); + path_simplify(result, true); - if (*t) - strcpy(p, t); - else - /* Remove trailing slash */ - *(--p) = 0; + if (!path_is_valid(result)) + return -EINVAL; - *_r = r; + *ret = TAKE_PTR(result); return 0; } |