diff --git a/src/basic/dirent-util.c b/src/basic/dirent-util.c index 2eea228c20..17df6a24c9 100644 --- a/src/basic/dirent-util.c +++ b/src/basic/dirent-util.c @@ -8,11 +8,11 @@ #include "stat-util.h" #include "string-util.h" -static int dirent_ensure_type(DIR *d, struct dirent *de) { +int dirent_ensure_type(int dir_fd, struct dirent *de) { STRUCT_STATX_DEFINE(sx); int r; - assert(d); + assert(dir_fd >= 0); assert(de); if (de->d_type != DT_UNKNOWN) @@ -24,7 +24,7 @@ static int dirent_ensure_type(DIR *d, struct dirent *de) { } /* Let's ask only for the type, nothing else. */ - r = statx_fallback(dirfd(d), de->d_name, AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT, STATX_TYPE, &sx); + r = statx_fallback(dir_fd, de->d_name, AT_SYMLINK_NOFOLLOW|AT_NO_AUTOMOUNT, STATX_TYPE, &sx); if (r < 0) return r; @@ -80,7 +80,7 @@ struct dirent *readdir_ensure_type(DIR *d) { if (!de) return NULL; - r = dirent_ensure_type(d, de); + r = dirent_ensure_type(dirfd(d), de); if (r >= 0) return de; if (r != -ENOENT) { diff --git a/src/basic/dirent-util.h b/src/basic/dirent-util.h index 5fde9043a3..0f1fb23119 100644 --- a/src/basic/dirent-util.h +++ b/src/basic/dirent-util.h @@ -10,6 +10,7 @@ bool dirent_is_file(const struct dirent *de) _pure_; bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) _pure_; +int dirent_ensure_type(int dir_fd, struct dirent *de); struct dirent *readdir_ensure_type(DIR *d); struct dirent *readdir_no_dot(DIR *dirp); diff --git a/src/basic/recurse-dir.c b/src/basic/recurse-dir.c index 908e833501..fe18b98d5b 100644 --- a/src/basic/recurse-dir.c +++ b/src/basic/recurse-dir.c @@ -33,6 +33,7 @@ int readdir_all(int dir_fd, struct dirent *entry; DirectoryEntries *nde; size_t add, sz, j; + int r; assert(dir_fd >= 0); @@ -84,6 +85,15 @@ int readdir_all(int dir_fd, if (ignore_dirent(entry, flags)) continue; + if (FLAGS_SET(flags, RECURSE_DIR_ENSURE_TYPE)) { + r = dirent_ensure_type(dir_fd, entry); + if (r == -ENOENT) + /* dentry gone by now? no problem, let's just suppress it */ + continue; + if (r < 0) + return r; + } + de->n_entries++; } @@ -104,8 +114,14 @@ int readdir_all(int dir_fd, if (ignore_dirent(entry, flags)) continue; + /* If d_type == DT_UNKNOWN that means we failed to ensure the type in the earlier loop and + * didn't include the dentry in de->n_entries and as such should skip it here as well. */ + if (FLAGS_SET(flags, RECURSE_DIR_ENSURE_TYPE) && entry->d_type == DT_UNKNOWN) + continue; + de->entries[j++] = entry; } + assert(j == de->n_entries); if (FLAGS_SET(flags, RECURSE_DIR_SORT)) typesafe_qsort(de->entries, de->n_entries, sort_func); @@ -160,7 +176,8 @@ int recurse_dir( return r; } - r = readdir_all(dir_fd, flags, &de); + /* Mask out RECURSE_DIR_ENSURE_TYPE so we can do it ourselves and avoid an extra statx() call. */ + r = readdir_all(dir_fd, flags & ~RECURSE_DIR_ENSURE_TYPE, &de); if (r < 0) return r; diff --git a/src/shared/mkfs-util.c b/src/shared/mkfs-util.c index 2a995fed52..e89b1b8845 100644 --- a/src/shared/mkfs-util.c +++ b/src/shared/mkfs-util.c @@ -130,7 +130,8 @@ static int setup_userns(uid_t uid, gid_t gid) { static int do_mcopy(const char *node, const char *root) { _cleanup_free_ char *mcopy = NULL; _cleanup_strv_free_ char **argv = NULL; - _cleanup_closedir_ DIR *rootdir = NULL; + _cleanup_close_ int rfd = -1; + _cleanup_free_ DirectoryEntries *de = NULL; struct stat st; int r; @@ -154,15 +155,24 @@ static int do_mcopy(const char *node, const char *root) { /* mcopy copies the top level directory instead of everything in it so we have to pass all * the subdirectories to mcopy instead to end up with the correct directory structure. */ - rootdir = opendir(root); - if (!rootdir) - return log_error_errno(errno, "Failed to open directory '%s'", root); + rfd = open(root, O_RDONLY|O_DIRECTORY|O_CLOEXEC); + if (rfd < 0) + return log_error_errno(errno, "Failed to open directory '%s': %m", root); - FOREACH_DIRENT(de, rootdir, return -errno) { - char *p = path_join(root, de->d_name); + r = readdir_all(rfd, RECURSE_DIR_SORT|RECURSE_DIR_ENSURE_TYPE, &de); + if (r < 0) + return log_error_errno(r, "Failed to read '%s' contents: %m", root); + + for (size_t i = 0; i < de->n_entries; i++) { + char *p = path_join(root, de->entries[i]->d_name); if (!p) return log_oom(); + if (!IN_SET(de->entries[i]->d_type, DT_REG, DT_DIR)) { + log_debug("%s is not a file/directory which are the only file types supported by vfat, ignoring", p); + continue; + } + r = strv_consume(&argv, TAKE_PTR(p)); if (r < 0) return log_oom(); @@ -172,7 +182,7 @@ static int do_mcopy(const char *node, const char *root) { if (r < 0) return log_oom(); - if (fstat(dirfd(rootdir), &st) < 0) + if (fstat(rfd, &st) < 0) return log_error_errno(errno, "Failed to stat '%s': %m", root); r = safe_fork("(mcopy)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_STDOUT_TO_STDERR|FORK_NEW_USERNS|FORK_CLOSE_ALL_FDS, NULL); @@ -269,7 +279,7 @@ static int make_protofile(const char *root, char **ret) { "0 0\n" "d--755 0 0\n", f); - r = recurse_dir_at(AT_FDCWD, root, STATX_TYPE|STATX_MODE, UINT_MAX, 0, protofile_print_item, f); + r = recurse_dir_at(AT_FDCWD, root, STATX_TYPE|STATX_MODE, UINT_MAX, RECURSE_DIR_SORT, protofile_print_item, f); if (r < 0) return log_error_errno(r, "Failed to recurse through %s: %m", root);