diff --git a/NEWS b/NEWS index 08a1ae4d2b..a47f0f37a1 100644 --- a/NEWS +++ b/NEWS @@ -55,7 +55,14 @@ CHANGES WITH 260 in spe: Device nodes should not be owned by a non-system user/group. It is recommended to check udev rules files with 'udevadm verify' and/or - 'udevadm test' commands . + 'udevadm test' commands. + + * systemd-repart will now make use of mkfs.xfs's support for + populating XFS filesystems from a directory. This support was + added in xfsprogs 6.17.0 released 20 October 2025. As there is no + proper way to detect whether mkfs.xfs supports populating from a + directory or not, we make use of it unconditionally and have dropped + support for the old way using protofiles. New system interfaces and components: diff --git a/man/repart.d.xml b/man/repart.d.xml index 66faa8c057..7d3dc4e04b 100644 --- a/man/repart.d.xml +++ b/man/repart.d.xml @@ -497,17 +497,6 @@ in a user namespace with the current user mapped to the root user to make sure the files and directories in the image are owned by the root user. - Note that when populating XFS filesystems with systemd-repart and loop - devices are not available, populating XFS filesystems with files containing spaces, tabs or newlines - might fail on old versions of - mkfs.xfs8 - due to limitations of its protofile format. - - Note that when populating XFS filesystems with systemd-repart and loop - devices are not available, extended attributes will not be copied into generated XFS filesystems - due to limitations mkfs.xfs8's - protofile format. - This option cannot be combined with CopyBlocks=. When @@ -517,6 +506,9 @@ or switches are used, the source paths are taken relative to the specified root directory or disk image root. + Note that when CopyFiles= is used with Format=xfs, + xfsprogs 6.17 or newer is required. + diff --git a/src/shared/mkfs-util.c b/src/shared/mkfs-util.c index 67bfb45193..b3e575efd3 100644 --- a/src/shared/mkfs-util.c +++ b/src/shared/mkfs-util.c @@ -4,10 +4,6 @@ #include #include -#include "fd-util.h" -#include "fileio.h" -#include "format-util.h" -#include "fs-util.h" #include "log.h" #include "mkfs-util.h" #include "mount-util.h" @@ -15,12 +11,10 @@ #include "path-util.h" #include "process-util.h" #include "recurse-dir.h" -#include "rm-rf.h" #include "stat-util.h" #include "stdio-util.h" #include "string-util.h" #include "strv.h" -#include "tmpfile-util.h" #include "utf8.h" int mkfs_exists(const char *fstype) { @@ -171,154 +165,6 @@ static int do_mcopy(const char *node, const char *root) { return 0; } -typedef struct ProtofileData { - FILE *file; - bool has_filename_with_spaces; - const char *tmpdir; -} ProtofileData; - -static int protofile_print_item( - RecurseDirEvent event, - const char *path, - int dir_fd, - int inode_fd, - const struct dirent *de, - const struct statx *sx, - void *userdata) { - - ProtofileData *data = ASSERT_PTR(userdata); - _cleanup_free_ char *copy = NULL; - int r; - - if (event == RECURSE_DIR_LEAVE) { - fputs("$\n", data->file); - return 0; - } - - if (!IN_SET(event, RECURSE_DIR_ENTER, RECURSE_DIR_ENTRY)) - return RECURSE_DIR_CONTINUE; - - char type = S_ISDIR(sx->stx_mode) ? 'd' : - S_ISREG(sx->stx_mode) ? '-' : - S_ISLNK(sx->stx_mode) ? 'l' : - S_ISFIFO(sx->stx_mode) ? 'p' : - S_ISBLK(sx->stx_mode) ? 'b' : - S_ISCHR(sx->stx_mode) ? 'c' : 0; - if (type == 0) - return RECURSE_DIR_CONTINUE; - - /* The protofile format does not support spaces in filenames as whitespace is used as a token - * delimiter. To work around this limitation, mkfs.xfs allows escaping whitespace by using the / - * character (which isn't allowed in filenames and as such can be used to escape whitespace). See - * https://lore.kernel.org/linux-xfs/20230222090303.h6tujm7y32gjhgal@andromeda/T/#m8066b3e7d62a080ee7434faac4861d944e64493b - * for more information. */ - - if (strchr(de->d_name, ' ')) { - copy = strdup(de->d_name); - if (!copy) - return log_oom(); - - string_replace_char(copy, ' ', '/'); - data->has_filename_with_spaces = true; - } - - fprintf(data->file, "%s %c%c%c%03o "UID_FMT" "GID_FMT" ", - copy ?: de->d_name, - type, - sx->stx_mode & S_ISUID ? 'u' : '-', - sx->stx_mode & S_ISGID ? 'g' : '-', - (unsigned) (sx->stx_mode & 0777), - sx->stx_uid, sx->stx_gid); - - if (S_ISREG(sx->stx_mode)) { - _cleanup_free_ char *p = NULL; - - /* While we can escape whitespace in the filename, we cannot escape whitespace in the source - * path, so hack around that by creating a symlink to the path in a temporary directory and - * using the symlink as the source path instead. */ - - if (strchr(path, ' ')) { - r = tempfn_random_child(data->tmpdir, "mkfs-xfs", &p); - if (r < 0) - return log_error_errno(r, "Failed to generate random child name in %s: %m", data->tmpdir); - - if (symlink(path, p) < 0) - return log_error_errno(errno, "Failed to symlink %s to %s: %m", p, path); - } - - fputs(p ?: path, data->file); - } else if (S_ISLNK(sx->stx_mode)) { - _cleanup_free_ char *p = NULL; - - r = readlinkat_malloc(dir_fd, de->d_name, &p); - if (r < 0) - return log_error_errno(r, "Failed to read symlink %s: %m", path); - - /* If we have a symlink to a path with whitespace in it, we're out of luck, as there's no way - * to encode that in the mkfs.xfs protofile format. */ - - if (strchr(p, ' ')) - return log_error_errno(r, "Symlinks to paths containing whitespace are not supported by mkfs.xfs: %m"); - - fputs(p, data->file); - } else if (S_ISBLK(sx->stx_mode) || S_ISCHR(sx->stx_mode)) - fprintf(data->file, "%" PRIu32 " %" PRIu32, sx->stx_rdev_major, sx->stx_rdev_minor); - - fputc('\n', data->file); - - return RECURSE_DIR_CONTINUE; -} - -static int make_protofile(const char *root, char **ret_path, bool *ret_has_filename_with_spaces, char **ret_tmpdir) { - _cleanup_(rm_rf_physical_and_freep) char *tmpdir = NULL; - _cleanup_fclose_ FILE *f = NULL; - _cleanup_(unlink_and_freep) char *p = NULL; - struct ProtofileData data = {}; - const char *vt; - int r; - - assert(ret_path); - assert(ret_has_filename_with_spaces); - assert(ret_tmpdir); - - r = var_tmp_dir(&vt); - if (r < 0) - return log_error_errno(r, "Failed to get persistent temporary directory: %m"); - - r = fopen_temporary_child(vt, &f, &p); - if (r < 0) - return log_error_errno(r, "Failed to open temporary file: %m"); - - /* Explicitly use /tmp here because this directory cannot have spaces its path. */ - r = mkdtemp_malloc("/tmp/systemd-mkfs-XXXXXX", &tmpdir); - if (r < 0) - return log_error_errno(r, "Failed to create temporary directory: %m"); - - data.file = f; - data.tmpdir = tmpdir; - - fputs("/\n" - "0 0\n" - "d--755 0 0\n", f); - - r = recurse_dir_at(AT_FDCWD, root, STATX_TYPE|STATX_MODE|STATX_UID|STATX_GID, UINT_MAX, - RECURSE_DIR_SORT, protofile_print_item, &data); - if (r < 0) - return log_error_errno(r, "Failed to recurse through %s: %m", root); - - fputs("$\n", f); - - r = fflush_and_check(f); - if (r < 0) - return log_error_errno(r, "Failed to flush %s: %m", p); - - *ret_path = TAKE_PTR(p); - *ret_has_filename_with_spaces = data.has_filename_with_spaces; - *ret_tmpdir = TAKE_PTR(tmpdir); - - return 0; -} - int make_filesystem( const char *node, const char *fstype, @@ -333,8 +179,6 @@ int make_filesystem( _cleanup_free_ char *mkfs = NULL, *mangled_label = NULL; _cleanup_strv_free_ char **argv = NULL, **env = NULL; - _cleanup_(rm_rf_physical_and_freep) char *protofile_tmpdir = NULL; - _cleanup_(unlink_and_freep) char *protofile = NULL; char vol_id[CONST_MAX(SD_ID128_UUID_STRING_MAX, 8U + 1U)] = {}; int stdio_fds[3] = { -EBADF, STDERR_FILENO, STDERR_FILENO}; ForkFlags fork_flags = FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_WAIT| @@ -560,26 +404,8 @@ int make_filesystem( if (!FLAGS_SET(flags, MKFS_DISCARD) && strv_extend(&argv, "-K") < 0) return log_oom(); - if (root) { - bool has_filename_with_spaces = false; - _cleanup_free_ char *protofile_with_opt = NULL; - - r = make_protofile(root, &protofile, &has_filename_with_spaces, &protofile_tmpdir); - if (r < 0) - return r; - - /* Gross hack to make mkfs.xfs interpret slashes as spaces so we can encode filenames - * with spaces in the protofile format. */ - if (has_filename_with_spaces) - protofile_with_opt = strjoin("slashes_are_spaces=1,", protofile); - else - protofile_with_opt = strdup(protofile); - if (!protofile_with_opt) - return -ENOMEM; - - if (strv_extend_many(&argv, "-p", protofile_with_opt) < 0) - return log_oom(); - } + if (root && strv_extend_many(&argv, "-p", root) < 0) + return log_oom(); if (sector_size > 0) { if (strv_extend(&argv, "-s") < 0)