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.
This commit is contained in:
Daan De Meyer 2022-10-11 10:50:58 +02:00
parent 68665704dc
commit bf3598beff
4 changed files with 71 additions and 1 deletions

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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() {