summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2016-11-29 18:02:45 +0100
committerLennart Poettering <lennart@poettering.net>2016-12-01 00:25:51 +0100
commita9fb08670f392e044c345b623e70287f6c6a02c1 (patch)
treeb473f23d7d27e75e5b5052dbdb0a40029979422d
parentc4f4fce79e157800212f3cba1b21870097030e81 (diff)
fs-util: add new CHASE_NON_EXISTING flag to chase_symlinks()
This new flag controls whether to consider a problem if the referenced path doesn't actually exist. If specified it's OK if the final file doesn't exist. Note that this permits one or more final components of the path not to exist, but these must not contain "../" for safety reasons (or, to be extra safe, neither "./" and a couple of others, i.e. what path_is_safe() permits). This new flag is useful when resolving paths before issuing an mkdir() or open(O_CREAT) on a path, as it permits that the file or directory is created later. The return code of chase_symlinks() is changed to return 1 if the file exists, and 0 if it doesn't. The latter is only returned in case CHASE_NON_EXISTING is set.
-rw-r--r--src/basic/fs-util.c22
-rw-r--r--src/basic/fs-util.h1
-rw-r--r--src/test/test-fs-util.c56
3 files changed, 66 insertions, 13 deletions
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index 0a3e983631..0ca4656fdd 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -602,6 +602,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
_cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL;
_cleanup_close_ int fd = -1;
unsigned max_follow = 32; /* how many symlinks to follow before giving up and returning ELOOP */
+ bool exists = true;
char *todo;
int r;
@@ -707,8 +708,25 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
/* Otherwise let's see what this is. */
child = openat(fd, first + n, O_CLOEXEC|O_NOFOLLOW|O_PATH);
- if (child < 0)
+ if (child < 0) {
+
+ if (errno == ENOENT &&
+ (flags & CHASE_NON_EXISTING) &&
+ (isempty(todo) || path_is_safe(todo))) {
+
+ /* If CHASE_NON_EXISTING is set, and the path does not exist, then that's OK, return
+ * what we got so far. But don't allow this if the remaining path contains "../ or "./"
+ * or something else weird. */
+
+ if (!strextend(&done, first, todo, NULL))
+ return -ENOMEM;
+
+ exists = false;
+ break;
+ }
+
return -errno;
+ }
if (fstat(child, &st) < 0)
return -errno;
@@ -793,5 +811,5 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
*ret = done;
done = NULL;
- return 0;
+ return exists;
}
diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h
index ee3d6bf7af..3931534a42 100644
--- a/src/basic/fs-util.h
+++ b/src/basic/fs-util.h
@@ -80,6 +80,7 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask);
enum {
CHASE_PREFIX_ROOT = 1, /* If set, the specified path will be prefixed by the specified root before beginning the iteration */
+ CHASE_NON_EXISTING = 2, /* If set, it's OK if the path doesn't actually exist. */
};
int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret);
diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c
index fac3a1d089..2570bc5859 100644
--- a/src/test/test-fs-util.c
+++ b/src/test/test-fs-util.c
@@ -63,7 +63,7 @@ static void test_chase_symlinks(void) {
/* Paths that use symlinks underneath the "root" */
r = chase_symlinks(p, NULL, 0, &result);
- assert_se(r >= 0);
+ assert_se(r > 0);
assert_se(path_equal(result, "/usr"));
result = mfree(result);
@@ -71,10 +71,15 @@ static void test_chase_symlinks(void) {
assert_se(r == -ENOENT);
q = strjoina(temp, "/usr");
+
+ r = chase_symlinks(p, temp, CHASE_NON_EXISTING, &result);
+ assert_se(r == 0);
+ assert_se(path_equal(result, q));
+
assert_se(mkdir(q, 0700) >= 0);
r = chase_symlinks(p, temp, 0, &result);
- assert_se(r >= 0);
+ assert_se(r > 0);
assert_se(path_equal(result, q));
p = strjoina(temp, "/slash");
@@ -82,12 +87,12 @@ static void test_chase_symlinks(void) {
result = mfree(result);
r = chase_symlinks(p, NULL, 0, &result);
- assert_se(r >= 0);
+ assert_se(r > 0);
assert_se(path_equal(result, "/"));
result = mfree(result);
r = chase_symlinks(p, temp, 0, &result);
- assert_se(r >= 0);
+ assert_se(r > 0);
assert_se(path_equal(result, temp));
/* Paths that would "escape" outside of the "root" */
@@ -97,21 +102,21 @@ static void test_chase_symlinks(void) {
result = mfree(result);
r = chase_symlinks(p, temp, 0, &result);
- assert_se(r == 0 && path_equal(result, temp));
+ assert_se(r > 0 && path_equal(result, temp));
p = strjoina(temp, "/6dotsusr");
assert_se(symlink("../../../usr", p) >= 0);
result = mfree(result);
r = chase_symlinks(p, temp, 0, &result);
- assert_se(r == 0 && path_equal(result, q));
+ assert_se(r > 0 && path_equal(result, q));
p = strjoina(temp, "/top/8dotsusr");
assert_se(symlink("../../../../usr", p) >= 0);
result = mfree(result);
r = chase_symlinks(p, temp, 0, &result);
- assert_se(r == 0 && path_equal(result, q));
+ assert_se(r > 0 && path_equal(result, q));
/* Paths that contain repeated slashes */
@@ -120,24 +125,24 @@ static void test_chase_symlinks(void) {
result = mfree(result);
r = chase_symlinks(p, NULL, 0, &result);
- assert_se(r >= 0);
+ assert_se(r > 0);
assert_se(path_equal(result, "/usr"));
result = mfree(result);
r = chase_symlinks(p, temp, 0, &result);
- assert_se(r >= 0);
+ assert_se(r > 0);
assert_se(path_equal(result, q));
/* Paths using . */
result = mfree(result);
r = chase_symlinks("/etc/./.././", NULL, 0, &result);
- assert_se(r >= 0);
+ assert_se(r > 0);
assert_se(path_equal(result, "/"));
result = mfree(result);
r = chase_symlinks("/etc/./.././", "/etc", 0, &result);
- assert_se(r == 0 && path_equal(result, "/etc"));
+ assert_se(r > 0 && path_equal(result, "/etc"));
result = mfree(result);
r = chase_symlinks("/etc/machine-id/foo", NULL, 0, &result);
@@ -151,6 +156,35 @@ static void test_chase_symlinks(void) {
r = chase_symlinks(p, NULL, 0, &result);
assert_se(r == -ELOOP);
+ /* Path which doesn't exist */
+
+ p = strjoina(temp, "/idontexist");
+ r = chase_symlinks(p, NULL, 0, &result);
+ assert_se(r == -ENOENT);
+
+ r = chase_symlinks(p, NULL, CHASE_NON_EXISTING, &result);
+ assert_se(r == 0);
+ assert_se(path_equal(result, p));
+ result = mfree(result);
+
+ p = strjoina(temp, "/idontexist/meneither");
+ r = chase_symlinks(p, NULL, 0, &result);
+ assert_se(r == -ENOENT);
+
+ r = chase_symlinks(p, NULL, CHASE_NON_EXISTING, &result);
+ assert_se(r == 0);
+ assert_se(path_equal(result, p));
+ result = mfree(result);
+
+ /* Path which doesn't exist, but contains weird stuff */
+
+ p = strjoina(temp, "/idontexist/..");
+ r = chase_symlinks(p, NULL, 0, &result);
+ assert_se(r == -ENOENT);
+
+ r = chase_symlinks(p, NULL, CHASE_NON_EXISTING, &result);
+ assert_se(r == -ENOENT);
+
assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}