mirror of
https://github.com/systemd/systemd.git
synced 2025-01-26 14:04:03 +03:00
Merge pull request #23261 from poettering/dir-is-empty
dir_is_empty() fixes
This commit is contained in:
commit
8dd11be77f
@ -71,13 +71,10 @@ int is_device_node(const char *path) {
|
||||
return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode));
|
||||
}
|
||||
|
||||
int dir_is_empty_at(int dir_fd, const char *path) {
|
||||
int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup) {
|
||||
_cleanup_close_ int fd = -1;
|
||||
/* Allocate space for at least 3 full dirents, since every dir has at least two entries ("." +
|
||||
* ".."), and only once we have seen if there's a third we know whether the dir is empty or not. */
|
||||
DEFINE_DIRENT_BUFFER(buffer, 3);
|
||||
struct dirent *de;
|
||||
ssize_t n;
|
||||
struct dirent *buf;
|
||||
size_t m;
|
||||
|
||||
if (path) {
|
||||
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
|
||||
@ -99,15 +96,30 @@ int dir_is_empty_at(int dir_fd, const char *path) {
|
||||
return fd;
|
||||
}
|
||||
|
||||
n = getdents64(fd, &buffer, sizeof(buffer));
|
||||
if (n < 0)
|
||||
return -errno;
|
||||
/* Allocate space for at least 3 full dirents, since every dir has at least two entries ("." +
|
||||
* ".."), and only once we have seen if there's a third we know whether the dir is empty or not. If
|
||||
* 'ignore_hidden_or_backup' is true we'll allocate a bit more, since we might skip over a bunch of
|
||||
* entries that we end up ignoring. */
|
||||
m = (ignore_hidden_or_backup ? 16 : 3) * DIRENT_SIZE_MAX;
|
||||
buf = alloca(m);
|
||||
|
||||
msan_unpoison(&buffer, n);
|
||||
for (;;) {
|
||||
struct dirent *de;
|
||||
ssize_t n;
|
||||
|
||||
FOREACH_DIRENT_IN_BUFFER(de, &buffer.de, n)
|
||||
if (!hidden_or_backup_file(de->d_name))
|
||||
return 0;
|
||||
n = getdents64(fd, buf, m);
|
||||
if (n < 0)
|
||||
return -errno;
|
||||
if (n == 0)
|
||||
break;
|
||||
|
||||
assert((size_t) n <= m);
|
||||
msan_unpoison(buf, n);
|
||||
|
||||
FOREACH_DIRENT_IN_BUFFER(de, buf, n)
|
||||
if (!(ignore_hidden_or_backup ? hidden_or_backup_file(de->d_name) : dot_or_dot_dot(de->d_name)))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -17,17 +17,9 @@ int is_dir(const char *path, bool follow);
|
||||
int is_dir_fd(int fd);
|
||||
int is_device_node(const char *path);
|
||||
|
||||
int dir_is_empty_at(int dir_fd, const char *path);
|
||||
static inline int dir_is_empty(const char *path) {
|
||||
return dir_is_empty_at(AT_FDCWD, path);
|
||||
}
|
||||
|
||||
static inline int dir_is_populated(const char *path) {
|
||||
int r;
|
||||
r = dir_is_empty(path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
return !r;
|
||||
int dir_is_empty_at(int dir_fd, const char *path, bool ignore_hidden_or_backup);
|
||||
static inline int dir_is_empty(const char *path, bool ignore_hidden_or_backup) {
|
||||
return dir_is_empty_at(AT_FDCWD, path, ignore_hidden_or_backup);
|
||||
}
|
||||
|
||||
bool null_or_empty(struct stat *st) _pure_;
|
||||
|
@ -1639,7 +1639,7 @@ static int are_we_installed(const char *esp_path) {
|
||||
return log_oom();
|
||||
|
||||
log_debug("Checking whether %s contains any files…", p);
|
||||
r = dir_is_empty(p);
|
||||
r = dir_is_empty(p, /* ignore_hidden_or_backup= */ false);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to check whether %s contains any files: %m", p);
|
||||
|
||||
|
@ -1290,7 +1290,7 @@ static void test_usr(void) {
|
||||
|
||||
/* Check that /usr is either on the same file system as / or mounted already. */
|
||||
|
||||
if (dir_is_empty("/usr") <= 0)
|
||||
if (dir_is_empty("/usr", /* ignore_hidden_or_backup= */ false) <= 0)
|
||||
return;
|
||||
|
||||
log_warning("/usr appears to be on its own filesystem and is not already mounted. This is not a supported setup. "
|
||||
|
@ -976,7 +976,7 @@ int manager_new(LookupScope scope, ManagerTestRunFlags test_run_flags, Manager *
|
||||
|
||||
m->taint_usr =
|
||||
!in_initrd() &&
|
||||
dir_is_empty("/usr") > 0;
|
||||
dir_is_empty("/usr", /* ignore_hidden_or_backup= */ false) > 0;
|
||||
|
||||
/* Note that we do not set up the notify fd here. We do that after deserialization,
|
||||
* since they might have gotten serialized across the reexec. */
|
||||
|
@ -213,7 +213,7 @@ static bool path_spec_check_good(PathSpec *s, bool initial, bool from_trigger_no
|
||||
case PATH_DIRECTORY_NOT_EMPTY: {
|
||||
int k;
|
||||
|
||||
k = dir_is_empty(s->path);
|
||||
k = dir_is_empty(s->path, /* ignore_hidden_or_backup= */ true);
|
||||
good = !(IN_SET(k, -ENOENT, -ENOTDIR) || k > 0);
|
||||
break;
|
||||
}
|
||||
|
@ -4776,7 +4776,7 @@ void unit_warn_if_dir_nonempty(Unit *u, const char* where) {
|
||||
if (!unit_log_level_test(u, LOG_NOTICE))
|
||||
return;
|
||||
|
||||
r = dir_is_empty(where);
|
||||
r = dir_is_empty(where, /* ignore_hidden_or_backup= */ false);
|
||||
if (r > 0 || r == -ENOTDIR)
|
||||
return;
|
||||
if (r < 0) {
|
||||
|
@ -305,7 +305,7 @@ static int path_is_busy(const char *where) {
|
||||
return log_warning_errno(r, "Cannot check if \"%s\" is a mount point: %m", where);
|
||||
|
||||
/* not a mountpoint but it contains files */
|
||||
r = dir_is_empty(where);
|
||||
r = dir_is_empty(where, /* ignore_hidden_or_backup= */ false);
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Cannot check if \"%s\" is empty: %m", where);
|
||||
if (r > 0)
|
||||
|
@ -207,7 +207,7 @@ int home_create_cifs(UserRecord *h, HomeSetup *setup, UserRecord **ret_home) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dir_is_empty_at(setup->root_fd, NULL);
|
||||
r = dir_is_empty_at(setup->root_fd, NULL, /* ignore_hidden_or_backup= */ false);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to detect if CIFS directory is empty: %m");
|
||||
if (r == 0)
|
||||
|
@ -445,7 +445,7 @@ int user_record_test_home_directory(UserRecord *h) {
|
||||
}
|
||||
|
||||
/* Otherwise it's not OK */
|
||||
r = dir_is_empty(hd);
|
||||
r = dir_is_empty(hd, /* ignore_hidden_or_backup= */ false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
|
@ -449,7 +449,7 @@ int remove_bridge(const char *bridge_name) {
|
||||
|
||||
path = strjoina("/sys/class/net/", bridge_name, "/brif");
|
||||
|
||||
r = dir_is_empty(path);
|
||||
r = dir_is_empty(path, /* ignore_hidden_or_backup= */ false);
|
||||
if (r == -ENOENT) /* Already gone? */
|
||||
return 0;
|
||||
if (r < 0)
|
||||
|
@ -2680,7 +2680,7 @@ static int setup_journal(const char *directory) {
|
||||
} else if (access(p, F_OK) < 0)
|
||||
return 0;
|
||||
|
||||
if (dir_is_empty(q) == 0)
|
||||
if (dir_is_empty(q, /* ignore_hidden_or_backup= */ false) == 0)
|
||||
log_warning("%s is not empty, proceeding anyway.", q);
|
||||
|
||||
r = userns_mkdir(directory, p, 0755, 0, 0);
|
||||
|
@ -922,7 +922,7 @@ static int condition_test_directory_not_empty(Condition *c, char **env) {
|
||||
assert(c->parameter);
|
||||
assert(c->type == CONDITION_DIRECTORY_NOT_EMPTY);
|
||||
|
||||
r = dir_is_empty(c->parameter);
|
||||
r = dir_is_empty(c->parameter, /* ignore_hidden_or_backup= */ true);
|
||||
return r <= 0 && !IN_SET(r, -ENOENT, -ENOTDIR);
|
||||
}
|
||||
|
||||
|
@ -905,7 +905,7 @@ static int fd_copy_directory(
|
||||
|
||||
exists = false;
|
||||
if (copy_flags & COPY_MERGE_EMPTY) {
|
||||
r = dir_is_empty_at(dt, to);
|
||||
r = dir_is_empty_at(dt, to, /* ignore_hidden_or_backup= */ false);
|
||||
if (r < 0 && r != -ENOENT)
|
||||
return r;
|
||||
else if (r == 1)
|
||||
|
@ -1552,7 +1552,7 @@ int dissected_image_mount(
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
return r;
|
||||
} else if (dir_is_empty(p) > 0) {
|
||||
} else if (dir_is_empty(p, /* ignore_hidden_or_backup= */ false) > 0) {
|
||||
/* It exists and is an empty directory. Let's mount the ESP there. */
|
||||
r = mount_partition(m->partitions + PARTITION_ESP, where, "/boot", uid_shift, uid_range, flags);
|
||||
if (r < 0)
|
||||
|
@ -1466,7 +1466,7 @@ Tpm2Support tpm2_support(void) {
|
||||
* got the host sysfs mounted. Since devices are generally not virtualized for containers,
|
||||
* let's assume containers never have a TPM, at least for now. */
|
||||
|
||||
r = dir_is_empty("/sys/class/tpmrm");
|
||||
r = dir_is_empty("/sys/class/tpmrm", /* ignore_hidden_or_backup= */ false);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
log_debug_errno(r, "Unable to test whether /sys/class/tpmrm/ exists and is populated, assuming it is not: %m");
|
||||
|
@ -296,7 +296,7 @@ static int merge_hierarchy(
|
||||
else if (r < 0)
|
||||
return log_error_errno(r, "Failed to resolve host hierarchy '%s': %m", hierarchy);
|
||||
else {
|
||||
r = dir_is_empty(resolved_hierarchy);
|
||||
r = dir_is_empty(resolved_hierarchy, /* ignore_hidden_or_backup= */ false);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to check if host hierarchy '%s' is empty: %m", resolved_hierarchy);
|
||||
if (r > 0) {
|
||||
@ -337,7 +337,7 @@ static int merge_hierarchy(
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to resolve hierarchy '%s' in extension '%s': %m", hierarchy, *p);
|
||||
|
||||
r = dir_is_empty(resolved);
|
||||
r = dir_is_empty(resolved, /* ignore_hidden_or_backup= */ false);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to check if hierarchy '%s' in extension '%s' is empty: %m", resolved, *p);
|
||||
if (r > 0) {
|
||||
|
@ -153,17 +153,17 @@ TEST(dir_is_empty) {
|
||||
_cleanup_(rm_rf_physical_and_freep) char *empty_dir = NULL;
|
||||
_cleanup_free_ char *j = NULL, *jj = NULL, *jjj = NULL;
|
||||
|
||||
assert_se(dir_is_empty_at(AT_FDCWD, "/proc") == 0);
|
||||
assert_se(dir_is_empty_at(AT_FDCWD, "/icertainlydontexistdoi") == -ENOENT);
|
||||
assert_se(dir_is_empty_at(AT_FDCWD, "/proc", /* ignore_hidden_or_backup= */ true) == 0);
|
||||
assert_se(dir_is_empty_at(AT_FDCWD, "/icertainlydontexistdoi", /* ignore_hidden_or_backup= */ true) == -ENOENT);
|
||||
|
||||
assert_se(mkdtemp_malloc("/tmp/emptyXXXXXX", &empty_dir) >= 0);
|
||||
assert_se(dir_is_empty_at(AT_FDCWD, empty_dir) > 0);
|
||||
assert_se(dir_is_empty_at(AT_FDCWD, empty_dir, /* ignore_hidden_or_backup= */ true) > 0);
|
||||
|
||||
j = path_join(empty_dir, "zzz");
|
||||
assert_se(j);
|
||||
assert_se(touch(j) >= 0);
|
||||
|
||||
assert_se(dir_is_empty_at(AT_FDCWD, empty_dir) == 0);
|
||||
assert_se(dir_is_empty_at(AT_FDCWD, empty_dir, /* ignore_hidden_or_backup= */ true) == 0);
|
||||
|
||||
jj = path_join(empty_dir, "ppp");
|
||||
assert_se(jj);
|
||||
@ -173,13 +173,17 @@ TEST(dir_is_empty) {
|
||||
assert_se(jjj);
|
||||
assert_se(touch(jjj) >= 0);
|
||||
|
||||
assert_se(dir_is_empty_at(AT_FDCWD, empty_dir) == 0);
|
||||
assert_se(dir_is_empty_at(AT_FDCWD, empty_dir, /* ignore_hidden_or_backup= */ true) == 0);
|
||||
assert_se(dir_is_empty_at(AT_FDCWD, empty_dir, /* ignore_hidden_or_backup= */ false) == 0);
|
||||
assert_se(unlink(j) >= 0);
|
||||
assert_se(dir_is_empty_at(AT_FDCWD, empty_dir) == 0);
|
||||
assert_se(dir_is_empty_at(AT_FDCWD, empty_dir, /* ignore_hidden_or_backup= */ true) == 0);
|
||||
assert_se(dir_is_empty_at(AT_FDCWD, empty_dir, /* ignore_hidden_or_backup= */ false) == 0);
|
||||
assert_se(unlink(jj) >= 0);
|
||||
assert_se(dir_is_empty_at(AT_FDCWD, empty_dir) > 0);
|
||||
assert_se(dir_is_empty_at(AT_FDCWD, empty_dir, /* ignore_hidden_or_backup= */ true) > 0);
|
||||
assert_se(dir_is_empty_at(AT_FDCWD, empty_dir, /* ignore_hidden_or_backup= */ false) == 0);
|
||||
assert_se(unlink(jjj) >= 0);
|
||||
assert_se(dir_is_empty_at(AT_FDCWD, empty_dir) > 0);
|
||||
assert_se(dir_is_empty_at(AT_FDCWD, empty_dir, /* ignore_hidden_or_backup= */ true) > 0);
|
||||
assert_se(dir_is_empty_at(AT_FDCWD, empty_dir, /* ignore_hidden_or_backup= */ false) > 0);
|
||||
}
|
||||
|
||||
static int intro(void) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user