summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaan De Meyer <daan.j.demeyer@gmail.com>2022-10-11 10:50:58 +0200
committerDaan De Meyer <daan.j.demeyer@gmail.com>2022-11-15 20:07:54 +0100
commitbf3598befff0137592834465ef728fdaabf1e778 (patch)
treeab0bb5ac836eeaf651f02fc2c9fe17c3a585fc76
parent68665704dc9bb706d2b993808839d59dbabd7a9d (diff)
downloadsystemd-bf3598befff0137592834465ef728fdaabf1e778.tar.gz
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.
-rw-r--r--mkosi.conf.d/10-systemd.conf2
-rw-r--r--src/shared/mkfs-util.c64
-rwxr-xr-xtest/TEST-58-REPART/test.sh1
-rw-r--r--test/test-functions5
4 files changed, 71 insertions, 1 deletions
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 <unistd.h>
+#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() {