summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am1
-rw-r--r--src/basic/fs-util.c17
-rw-r--r--src/basic/fs-util.h1
-rw-r--r--src/test/test-fs-util.c62
4 files changed, 80 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am
index 1018afea57..870b22cefc 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1837,6 +1837,7 @@ test_util_SOURCES = \
src/test/test-util.c
test_util_LDADD = \
+ libsystemd-internal.la \
libsystemd-shared.la
test_hexdecoct_SOURCES = \
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index b239cf15cb..1e7c828e7b 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -561,6 +561,10 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
assert(path);
+ /* Either the file may be missing, or we return an fd to the final object, but both make no sense */
+ if ((flags & (CHASE_NONEXISTENT|CHASE_OPEN)) == (CHASE_NONEXISTENT|CHASE_OPEN))
+ return -EINVAL;
+
/* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following
* symlinks relative to a root directory, instead of the root of the host.
*
@@ -816,6 +820,19 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
done = NULL;
}
+ if (flags & CHASE_OPEN) {
+ int q;
+
+ /* Return the O_PATH fd we currently are looking to the caller. It can translate it to a proper fd by
+ * opening /proc/self/fd/xyz. */
+
+ assert(fd >= 0);
+ q = fd;
+ fd = -1;
+
+ return q;
+ }
+
return exists;
}
diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h
index f42dc06804..8d6703e8fd 100644
--- a/src/basic/fs-util.h
+++ b/src/basic/fs-util.h
@@ -82,6 +82,7 @@ enum {
CHASE_NONEXISTENT = 1U << 1, /* If set, it's OK if the path doesn't actually exist. */
CHASE_NO_AUTOFS = 1U << 2, /* If set, return -EREMOTE if autofs mount point found */
CHASE_SAFE = 1U << 3, /* If set, return EPERM if we ever traverse from unprivileged to privileged files or directories */
+ CHASE_OPEN = 1U << 4, /* If set, return an O_PATH object to the final component */
};
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 d3e7de35ee..3ccb4ed714 100644
--- a/src/test/test-fs-util.c
+++ b/src/test/test-fs-util.c
@@ -23,10 +23,13 @@
#include "fileio.h"
#include "fd-util.h"
#include "fs-util.h"
+#include "io-util.h"
+#include "hexdecoct.h"
#include "macro.h"
#include "mkdir.h"
#include "path-util.h"
#include "rm-rf.h"
+#include "stdio-util.h"
#include "string-util.h"
#include "strv.h"
#include "util.h"
@@ -128,11 +131,45 @@ static void test_var_tmp(void) {
}
}
+static int id128_read_fd(int fd, sd_id128_t *ret) {
+ char buf[33];
+ ssize_t k;
+ unsigned j;
+ sd_id128_t t;
+
+ assert_return(fd >= 0, -EINVAL);
+
+ k = loop_read(fd, buf, 33, false);
+ if (k < 0)
+ return (int) k;
+
+ if (k != 33)
+ return -EIO;
+
+ if (buf[32] !='\n')
+ return -EIO;
+
+ for (j = 0; j < 16; j++) {
+ int a, b;
+
+ a = unhexchar(buf[j*2]);
+ b = unhexchar(buf[j*2+1]);
+
+ if (a < 0 || b < 0)
+ return -EIO;
+
+ t.bytes[j] = a << 4 | b;
+ }
+
+ *ret = t;
+ return 0;
+}
+
static void test_chase_symlinks(void) {
_cleanup_free_ char *result = NULL;
char temp[] = "/tmp/test-chase.XXXXXX";
const char *top, *p, *pslash, *q, *qslash;
- int r;
+ int r, pfd;
assert_se(mkdtemp(temp));
@@ -357,6 +394,29 @@ static void test_chase_symlinks(void) {
assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0);
}
+ p = strjoina(temp, "/machine-id-test");
+ assert_se(symlink("/usr/../etc/./machine-id", p) >= 0);
+
+ pfd = chase_symlinks(p, NULL, CHASE_OPEN, NULL);
+ if (pfd != -ENOENT) {
+ char procfs[sizeof("/proc/self/fd/") - 1 + DECIMAL_STR_MAX(pfd) + 1];
+ _cleanup_close_ int fd = -1;
+ sd_id128_t a, b;
+
+ assert_se(pfd >= 0);
+
+ xsprintf(procfs, "/proc/self/fd/%i", pfd);
+
+ fd = open(procfs, O_RDONLY|O_CLOEXEC);
+ assert_se(fd >= 0);
+
+ safe_close(pfd);
+
+ assert_se(id128_read_fd(fd, &a) >= 0);
+ assert_se(sd_id128_get_machine(&b) >= 0);
+ assert_se(sd_id128_equal(a, b));
+ }
+
assert_se(rm_rf(temp, REMOVE_ROOT) >= 0);
}