From bf3598befff0137592834465ef728fdaabf1e778 Mon Sep 17 00:00:00 2001 From: Daan De Meyer Date: Tue, 11 Oct 2022 10:50:58 +0200 Subject: mkfs-util: Add support to populate vfat without mounting using mcopy mkfs.vfat doesn't support specifying a root directory to bootstrap the filesystem from (see https://github.com/dosfstools/dosfstools/issues/183). Instead, we can use the mcopy tool from the mtools package to copy files into the vfat filesystem after creating it without needing to mount the vfat filesystem. --- mkosi.conf.d/10-systemd.conf | 2 ++ src/shared/mkfs-util.c | 64 +++++++++++++++++++++++++++++++++++++++++++- test/TEST-58-REPART/test.sh | 1 + test/test-functions | 5 ++++ 4 files changed, 71 insertions(+), 1 deletion(-) diff --git a/mkosi.conf.d/10-systemd.conf b/mkosi.conf.d/10-systemd.conf index 0915e71357..f9e4d08616 100644 --- a/mkosi.conf.d/10-systemd.conf +++ b/mkosi.conf.d/10-systemd.conf @@ -21,6 +21,7 @@ Packages= coreutils diffutils dnsmasq + dosfstools e2fsprogs findutils gcc # For sanitizer libraries @@ -30,6 +31,7 @@ Packages= kexec-tools kmod less + mtools nano nftables openssl diff --git a/src/shared/mkfs-util.c b/src/shared/mkfs-util.c index f7f4a35212..2adb2a25c7 100644 --- a/src/shared/mkfs-util.c +++ b/src/shared/mkfs-util.c @@ -2,11 +2,14 @@ #include +#include "dirent-util.h" +#include "fd-util.h" #include "id128-util.h" #include "mkfs-util.h" #include "mountpoint-util.h" #include "path-util.h" #include "process-util.h" +#include "stat-util.h" #include "stdio-util.h" #include "string-util.h" #include "utf8.h" @@ -34,7 +37,7 @@ int mkfs_exists(const char *fstype) { } int mkfs_supports_root_option(const char *fstype) { - return fstype_is_ro(fstype) || STR_IN_SET(fstype, "ext2", "ext3", "ext4", "btrfs"); + return fstype_is_ro(fstype) || STR_IN_SET(fstype, "ext2", "ext3", "ext4", "btrfs", "vfat"); } static int mangle_linux_fs_label(const char *s, size_t max_len, char **ret) { @@ -91,6 +94,59 @@ static int mangle_fat_label(const char *s, char **ret) { return 0; } +static int do_mcopy(const char *node, const char *root) { + _cleanup_strv_free_ char **argv = NULL; + _cleanup_closedir_ DIR *rootdir = NULL; + int r; + + assert(node); + assert(root); + + /* Return early if there's nothing to copy. */ + if (dir_is_empty(root, /*ignore_hidden_or_backup=*/ false)) + return 0; + + argv = strv_new("mcopy", "-b", "-s", "-p", "-Q", "-n", "-m", "-i", node); + if (!argv) + return log_oom(); + + /* 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); + + FOREACH_DIRENT(de, rootdir, return -errno) { + char *p = path_join(root, de->d_name); + if (!p) + return log_oom(); + + r = strv_consume(&argv, TAKE_PTR(p)); + if (r < 0) + return log_oom(); + } + + r = strv_extend(&argv, "::"); + if (r < 0) + return log_oom(); + + r = safe_fork("(mcopy)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_STDOUT_TO_STDERR|FORK_NEW_USERNS, NULL); + if (r < 0) + return r; + if (r == 0) { + /* Avoid failures caused by mismatch in expectations between mkfs.vfat and mcopy by disabling + * the stricter mcopy checks using MTOOLS_SKIP_CHECK. */ + execvpe("mcopy", argv, STRV_MAKE("MTOOLS_SKIP_CHECK=1")); + + log_error_errno(errno, "Failed to execute mcopy: %m"); + + _exit(EXIT_FAILURE); + } + + return 0; +} + int make_filesystem( const char *node, const char *fstype, @@ -304,6 +360,12 @@ int make_filesystem( _exit(EXIT_FAILURE); } + if (root && streq(fstype, "vfat")) { + r = do_mcopy(node, root); + if (r < 0) + return r; + } + if (STR_IN_SET(fstype, "ext2", "ext3", "ext4", "btrfs", "f2fs", "xfs", "vfat", "swap")) log_info("%s successfully formatted as %s (label \"%s\", uuid %s)", node, fstype, label, vol_id); diff --git a/test/TEST-58-REPART/test.sh b/test/TEST-58-REPART/test.sh index 1638cb2b16..fb4a05fc77 100755 --- a/test/TEST-58-REPART/test.sh +++ b/test/TEST-58-REPART/test.sh @@ -15,6 +15,7 @@ test_append_files() { if command -v openssl >/dev/null 2>&1; then inst_binary openssl fi + inst_binary mcopy instmods dm_verity =md generate_module_dependencies image_install -o /sbin/mksquashfs diff --git a/test/test-functions b/test/test-functions index 18c76a69ab..e59a20f091 100644 --- a/test/test-functions +++ b/test/test-functions @@ -1310,6 +1310,11 @@ install_missing_libraries() { inst_simple "${path}/engines-3/capi.so" || true inst_simple "${path}/engines-3/loader_attic.so" || true inst_simple "${path}/engines-3/padlock.so" || true + + # Binaries from mtools depend on the gconv modules to translate between codepages. Because there's no + # pkg-config file for these, we copy every gconv/ directory we can find in /usr/lib and /usr/lib64. + # shellcheck disable=SC2046 + inst_recursive $(find /usr/lib* -name gconv 2>/dev/null) } cleanup_loopdev() { -- cgit v1.2.1