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);
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);
+
+ for (;;) {
+ struct dirent *de;
+ ssize_t n;
+
+ n = getdents64(fd, buf, m);
+ if (n < 0)
+ return -errno;
+ if (n == 0)
+ break;
- msan_unpoison(&buffer, n);
+ assert((size_t) n <= m);
+ msan_unpoison(buf, n);
- FOREACH_DIRENT_IN_BUFFER(de, &buffer.de, n)
- if (!hidden_or_backup_file(de->d_name))
- return 0;
+ 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;
}
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);
+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);
}
-static inline int dir_is_populated(const char *path) {
+static inline int dir_is_populated(const char *path, bool ignore_hidden_or_backup) {
int r;
- r = dir_is_empty(path);
+ r = dir_is_empty(path, ignore_hidden_or_backup);
if (r < 0)
return r;
return !r;
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);
/* 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. "
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. */
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;
}
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) {
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)
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)
}
/* 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)
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)
} 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);
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);
}
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)
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)
* 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");
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) {
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) {
_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);
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) {