From: Lennart Poettering Date: Mon, 3 Aug 2020 10:05:37 +0000 (+0200) Subject: mkdir: add new mkdir_p_root() helper X-Git-Tag: v247-rc1~369^2~18 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=0de3c4f4b7b47481fb78e68f6110a167b27b43c9;p=thirdparty%2Fsystemd.git mkdir: add new mkdir_p_root() helper --- diff --git a/src/basic/mkdir.c b/src/basic/mkdir.c index d591e65e410..eea8f1c9aa7 100644 --- a/src/basic/mkdir.c +++ b/src/basic/mkdir.c @@ -5,6 +5,7 @@ #include #include "alloc-util.h" +#include "fd-util.h" #include "format-util.h" #include "fs-util.h" #include "macro.h" @@ -187,3 +188,54 @@ int mkdir_p(const char *path, mode_t mode) { int mkdir_p_safe(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags) { return mkdir_p_internal(prefix, path, mode, uid, gid, flags, mkdir_errno_wrapper); } + +int mkdir_p_root(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m) { + _cleanup_free_ char *pp = NULL; + _cleanup_close_ int dfd = -1; + const char *bn; + int r; + + pp = dirname_malloc(p); + if (!pp) + return -ENOMEM; + + /* Not top-level? */ + if (!(path_equal(pp, "/") || isempty(pp) || path_equal(pp, "."))) { + + /* Recurse up */ + r = mkdir_p_root(root, pp, uid, gid, m); + if (r < 0) + return r; + } + + bn = basename(p); + if (path_equal(bn, "/") || isempty(bn) || path_equal(bn, ".")) + return 0; + + if (!filename_is_valid(bn)) + return -EINVAL; + + dfd = chase_symlinks_and_open(pp, root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_DIRECTORY, NULL); + if (dfd < 0) + return dfd; + + if (mkdirat(dfd, bn, m) < 0) { + if (errno == EEXIST) + return 0; + + return -errno; + } + + if (uid_is_valid(uid) || gid_is_valid(gid)) { + _cleanup_close_ int nfd = -1; + + nfd = openat(dfd, bn, O_RDONLY|O_CLOEXEC|O_DIRECTORY); + if (nfd < 0) + return -errno; + + if (fchown(nfd, uid, gid) < 0) + return -errno; + } + + return 1; +} diff --git a/src/basic/mkdir.h b/src/basic/mkdir.h index 8bfaaf405bc..fc66a7d3651 100644 --- a/src/basic/mkdir.h +++ b/src/basic/mkdir.h @@ -26,3 +26,5 @@ typedef int (*mkdir_func_t)(const char *pathname, mode_t mode); int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdir_func_t _mkdir); int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdir_func_t _mkdir); int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdir_func_t _mkdir); + +int mkdir_p_root(const char *root, const char *p, uid_t uid, gid_t gid, mode_t m);