1
0
mirror of https://github.com/systemd/systemd synced 2026-03-27 09:14:51 +01:00

Compare commits

...

4 Commits

Author SHA1 Message Date
Lennart Poettering
dd4c15296c
Merge pull request #20618 from yuwata/path-find-component
use path_find_{first,last}_component() at more several places
2021-09-07 10:02:44 +02:00
Yu Watanabe
af7c37a486 test: do not try to remove /dev 2021-09-07 14:08:21 +09:00
Yu Watanabe
4e046c5c2f fs-util: rewrite rmdir_parents() with path_find_last_component() 2021-09-07 14:08:21 +09:00
Yu Watanabe
3008a6f21c mkdir: rewrite mkdir_parents() with path_find_{first,last}_component() 2021-09-07 14:08:18 +09:00
4 changed files with 112 additions and 60 deletions

View File

@ -45,50 +45,39 @@ int unlink_noerrno(const char *path) {
} }
int rmdir_parents(const char *path, const char *stop) { int rmdir_parents(const char *path, const char *stop) {
size_t l; char *p;
int r = 0; int r;
assert(path); assert(path);
assert(stop); assert(stop);
l = strlen(path); if (!path_is_safe(path))
return -EINVAL;
/* Skip trailing slashes */ if (!path_is_safe(stop))
while (l > 0 && path[l-1] == '/') return -EINVAL;
l--;
while (l > 0) { p = strdupa(path);
char *t;
/* Skip last component */ for (;;) {
while (l > 0 && path[l-1] != '/') char *slash = NULL;
l--;
/* Skip trailing slashes */ /* skip the last component. */
while (l > 0 && path[l-1] == '/') r = path_find_last_component(p, /* accept_dot_dot= */ false, (const char **) &slash, NULL);
l--; if (r <= 0)
return r;
if (l <= 0) if (slash == p)
break;
t = strndup(path, l);
if (!t)
return -ENOMEM;
if (path_startswith(stop, t)) {
free(t);
return 0; return 0;
}
r = rmdir(t); assert(*slash == '/');
free(t); *slash = '\0';
if (r < 0) if (path_startswith_full(stop, p, /* accept_dot_dot= */ false))
if (errno != ENOENT) return 0;
if (rmdir(p) < 0 && errno != ENOENT)
return -errno; return -errno;
} }
return 0;
} }
int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) { int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {

View File

@ -95,57 +95,65 @@ int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags f
} }
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_parents_internal(const char *prefix, const char *path, mode_t mode, uid_t uid, gid_t gid, MkdirFlags flags, mkdir_func_t _mkdir) {
const char *p, *e; const char *p, *e = NULL;
int r; int r;
assert(path); assert(path);
assert(_mkdir != mkdir); assert(_mkdir != mkdir);
if (prefix && !path_startswith(path, prefix)) if (prefix) {
p = path_startswith_full(path, prefix, /* accept_dot_dot= */ false);
if (!p)
return -ENOTDIR;
} else
p = path;
if (isempty(p))
return 0;
if (!path_is_safe(p))
return -ENOTDIR; return -ENOTDIR;
/* return immediately if directory exists */ /* return immediately if directory exists */
e = strrchr(path, '/'); r = path_find_last_component(p, /* accept_dot_dot= */ false, &e, NULL);
if (!e) if (r <= 0) /* r == 0 means path is equivalent to prefix. */
return r;
if (e == p)
return 0; return 0;
if (e == path) assert(e > p);
return 0; assert(*e == '/');
p = strndupa(path, e - path); /* drop the last component */
r = is_dir(p, true); path = strndupa(path, e - path);
r = is_dir(path, true);
if (r > 0) if (r > 0)
return 0; return 0;
if (r == 0) if (r == 0)
return -ENOTDIR; return -ENOTDIR;
/* create every parent directory in the path, except the last component */ /* create every parent directory in the path, except the last component */
p = path + strspn(path, "/"); for (p = path;;) {
for (;;) { char *s;
char t[strlen(path) + 1]; int n;
e = p + strcspn(p, "/"); n = path_find_first_component(&p, /* accept_dot_dot= */ false, (const char **) &s);
p = e + strspn(e, "/"); if (n <= 0)
return n;
/* Is this the last component? If so, then we're done */ assert(p);
if (*p == 0) assert(s >= path);
return 0; assert(IN_SET(s[n], '/', '\0'));
memcpy(t, path, e - path); s[n] = '\0';
t[e-path] = 0;
if (prefix && path_startswith(prefix, t)) if (!prefix || !path_startswith_full(prefix, path, /* accept_dot_dot= */ false)) {
continue; r = mkdir_safe_internal(path, mode, uid, gid, flags, _mkdir);
if (!uid_is_valid(uid) && !gid_is_valid(gid) && flags == 0) {
r = _mkdir(t, mode);
if (r < 0 && r != -EEXIST)
return r;
} else {
r = mkdir_safe_internal(t, mode, uid, gid, flags, _mkdir);
if (r < 0 && r != -EEXIST) if (r < 0 && r != -EEXIST)
return r; return r;
} }
s[n] = *p == '\0' ? '\0' : '/';
} }
} }

View File

@ -865,6 +865,60 @@ static void test_conservative_rename(void) {
assert_se(access(q, F_OK) < 0 && errno == ENOENT); assert_se(access(q, F_OK) < 0 && errno == ENOENT);
} }
static void test_rmdir_parents_one(
const char *prefix,
const char *path,
const char *stop,
int expected,
const char *test_exist,
const char *test_nonexist_subdir) {
const char *p, *s;
log_debug("/* %s(%s, %s) */", __func__, path, stop);
p = strjoina(prefix, path);
s = strjoina(prefix, stop);
if (expected >= 0)
assert_se(mkdir_parents(p, 0700) >= 0);
assert_se(rmdir_parents(p, s) == expected);
if (expected >= 0) {
const char *e, *f;
e = strjoina(prefix, test_exist);
f = strjoina(e, test_nonexist_subdir);
assert_se(access(e, F_OK) >= 0);
assert_se(access(f, F_OK) < 0);
}
}
static void test_rmdir_parents(void) {
char *temp;
log_info("/* %s */", __func__);
temp = strjoina(arg_test_dir ?: "/tmp", "/test-rmdir.XXXXXX");
assert_se(mkdtemp(temp));
test_rmdir_parents_one(temp, "/aaa/../hoge/foo", "/hoge/foo", -EINVAL, NULL, NULL);
test_rmdir_parents_one(temp, "/aaa/bbb/ccc", "/hoge/../aaa", -EINVAL, NULL, NULL);
test_rmdir_parents_one(temp, "/aaa/bbb/ccc/ddd/eee", "/aaa/bbb/ccc/ddd", 0, "/aaa/bbb/ccc/ddd", "/eee");
test_rmdir_parents_one(temp, "/aaa/bbb/ccc/ddd/eee", "/aaa/bbb/ccc", 0, "/aaa/bbb/ccc", "/ddd");
test_rmdir_parents_one(temp, "/aaa/bbb/ccc/ddd/eee", "/aaa/bbb", 0, "/aaa/bbb", "/ccc");
test_rmdir_parents_one(temp, "/aaa/bbb/ccc/ddd/eee", "/aaa", 0, "/aaa", "/bbb");
test_rmdir_parents_one(temp, "/aaa/bbb/ccc/ddd/eee", "/", 0, "/", "/aaa");
test_rmdir_parents_one(temp, "/aaa/bbb/ccc/ddd/eee", "/aaa/hoge/foo", 0, "/aaa", "/bbb");
test_rmdir_parents_one(temp, "/aaa////bbb/.//ccc//ddd/eee///./.", "///././aaa/.", 0, "/aaa", "/bbb");
assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
test_setup_logging(LOG_INFO); test_setup_logging(LOG_INFO);
@ -883,6 +937,7 @@ int main(int argc, char *argv[]) {
test_rename_noreplace(); test_rename_noreplace();
test_chmod_and_chown(); test_chmod_and_chown();
test_conservative_rename(); test_conservative_rename();
test_rmdir_parents();
return 0; return 0;
} }

View File

@ -126,7 +126,7 @@ static int run(int argc, char *argv[]) {
} else { } else {
if (unlink(devname) < 0) if (unlink(devname) < 0)
return log_error_errno(errno, "unlink('%s') failed: %m", devname); return log_error_errno(errno, "unlink('%s') failed: %m", devname);
(void) rmdir_parents(devname, "/"); (void) rmdir_parents(devname, "/dev");
} }
} }