mirror of
https://github.com/systemd/systemd.git
synced 2024-11-24 10:43:35 +08:00
Merge pull request #26541 from DaanDeMeyer/xfs-whitespace-fix
mkfs-util: Escape spaces with slashes in protofile format
This commit is contained in:
commit
686b983040
@ -435,9 +435,9 @@
|
||||
|
||||
<para>Note that when populating XFS filesystems with <command>systemd-repart</command> and loop
|
||||
devices are not available, populating XFS filesystems with files containing spaces, tabs or newlines
|
||||
will fail due to limitations of <citerefentry
|
||||
project='man-pages'><refentrytitle>mkfs.xfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
protofile format.</para>
|
||||
might fail on old versions of
|
||||
<citerefentry project='man-pages'><refentrytitle>mkfs.xfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
due to limitations of its protofile format.</para>
|
||||
|
||||
<para>This option cannot be combined with <varname>CopyBlocks=</varname>.</para>
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
#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"
|
||||
@ -170,6 +171,12 @@ 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,
|
||||
@ -179,11 +186,12 @@ static int protofile_print_item(
|
||||
const struct statx *sx,
|
||||
void *userdata) {
|
||||
|
||||
FILE *f = ASSERT_PTR(userdata);
|
||||
ProtofileData *data = ASSERT_PTR(userdata);
|
||||
_cleanup_free_ char *copy = NULL;
|
||||
int r;
|
||||
|
||||
if (event == RECURSE_DIR_LEAVE) {
|
||||
fputs("$\n", f);
|
||||
fputs("$\n", data->file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -199,38 +207,78 @@ static int protofile_print_item(
|
||||
if (type == 0)
|
||||
return RECURSE_DIR_CONTINUE;
|
||||
|
||||
fprintf(f, "%s %c%c%c%03o 0 0 ",
|
||||
de->d_name,
|
||||
/* 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 0 0 ",
|
||||
copy ?: de->d_name,
|
||||
type,
|
||||
sx->stx_mode & S_ISUID ? 'u' : '-',
|
||||
sx->stx_mode & S_ISGID ? 'g' : '-',
|
||||
(unsigned) (sx->stx_mode & 0777));
|
||||
|
||||
if (S_ISREG(sx->stx_mode))
|
||||
fputs(path, f);
|
||||
else if (S_ISLNK(sx->stx_mode)) {
|
||||
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);
|
||||
|
||||
fputs(p, f);
|
||||
} else if (S_ISBLK(sx->stx_mode) || S_ISCHR(sx->stx_mode))
|
||||
fprintf(f, "%" PRIu32 " %" PRIu32, sx->stx_rdev_major, sx->stx_rdev_minor);
|
||||
/* 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. */
|
||||
|
||||
fputc('\n', f);
|
||||
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) {
|
||||
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);
|
||||
assert(ret_path);
|
||||
assert(ret_has_filename_with_spaces);
|
||||
assert(ret_tmpdir);
|
||||
|
||||
r = var_tmp_dir(&vt);
|
||||
if (r < 0)
|
||||
@ -240,11 +288,19 @@ static int make_protofile(const char *root, char **ret) {
|
||||
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, UINT_MAX, RECURSE_DIR_SORT, protofile_print_item, f);
|
||||
r = recurse_dir_at(AT_FDCWD, root, STATX_TYPE|STATX_MODE, UINT_MAX, RECURSE_DIR_SORT, protofile_print_item, &data);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to recurse through %s: %m", root);
|
||||
|
||||
@ -254,7 +310,9 @@ static int make_protofile(const char *root, char **ret) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to flush %s: %m", p);
|
||||
|
||||
*ret = TAKE_PTR(p);
|
||||
*ret_path = TAKE_PTR(p);
|
||||
*ret_has_filename_with_spaces = data.has_filename_with_spaces;
|
||||
*ret_tmpdir = TAKE_PTR(tmpdir);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -272,6 +330,7 @@ int make_filesystem(
|
||||
|
||||
_cleanup_free_ char *mkfs = NULL, *mangled_label = NULL;
|
||||
_cleanup_strv_free_ char **argv = 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};
|
||||
@ -424,11 +483,23 @@ int make_filesystem(
|
||||
return log_oom();
|
||||
|
||||
if (root) {
|
||||
r = make_protofile(root, &protofile);
|
||||
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;
|
||||
|
||||
if (strv_extend_strv(&argv, STRV_MAKE("-p", protofile), false) < 0)
|
||||
/* 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_strv(&argv, STRV_MAKE("-p", protofile_with_opt), false) < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
int resize_fs(int fd, uint64_t sz, uint64_t *ret_size);
|
||||
|
||||
#define BTRFS_MINIMAL_SIZE (256U*1024U*1024U)
|
||||
#define XFS_MINIMAL_SIZE (16U*1024U*1024U)
|
||||
#define XFS_MINIMAL_SIZE (300U*1024U*1024U)
|
||||
#define EXT4_MINIMAL_SIZE (1024U*1024U)
|
||||
|
||||
uint64_t minimal_size_by_fs_magic(statfs_f_type_t magic);
|
||||
|
Loading…
Reference in New Issue
Block a user