From 06da125ea19284a5200e206e704a37c17dbbab5e Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Thu, 24 Nov 2022 13:13:05 +0100 Subject: [PATCH 1/3] dirent-util: Expose dirent_ensure_type() --- src/basic/dirent-util.c | 8 ++++---- src/basic/dirent-util.h | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) 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); From a0a4c57818ac9137e7fe2903b6f2a032fca9292e Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Thu, 24 Nov 2022 13:13:21 +0100 Subject: [PATCH 2/3] recurse-dir: Handle RECURSE_DIR_ENSURE_TYPE in readdir_all() --- src/basic/recurse-dir.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) 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; From c75cf0164cbd69104f13cbe7be42ab639953bd7d Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Wed, 23 Nov 2022 14:12:38 +0100 Subject: [PATCH 3/3] mkfs-util: Skip non files/directories when calling mcopy Only files and directories are supported by vfat. When we pass a symlink to mcopy, it will try to dereference them and copy what the symlink points at into the vfat partition instead. Let's avoid this by skipping all unsupported file types when establishing the list of top level targets that mcopy should copy. We also use RECURSE_DIR_SORT everywhere when iterating directories to make things more reproducible. --- src/shared/mkfs-util.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/shared/mkfs-util.c b/src/shared/mkfs-util.c index 5a56c5e473..cd15e88ee0 100644 --- a/src/shared/mkfs-util.c +++ b/src/shared/mkfs-util.c @@ -129,7 +129,8 @@ static int setup_userns(uid_t uid, gid_t gid) { static int do_mcopy(const char *node, const char *root) { _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; @@ -147,15 +148,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(); @@ -165,7 +175,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); @@ -262,7 +272,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);