From 218cfe23354397ded28ac898f82b52724f48dae7 Mon Sep 17 00:00:00 2001 From: David Tardon Date: Fri, 13 Jan 2023 15:58:39 +0100 Subject: [PATCH] mount: handle bind mount of file with non-existing target When the target (Where=) of a mount does not exist, systemd tries to create it. But previously, it'd always been created as a directory. That doesn't work if one wants to bind-mount a file to a target that doesn't exist. Fixes: #17184 --- man/systemd.mount.xml | 4 +++- src/core/mount.c | 21 ++++++++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/man/systemd.mount.xml b/man/systemd.mount.xml index 773ca04cd6d..da6ade86c86 100644 --- a/man/systemd.mount.xml +++ b/man/systemd.mount.xml @@ -476,7 +476,9 @@ Where= Takes an absolute path of a file or directory for the mount point; in particular, the destination cannot be a symbolic link. If the mount point does not exist at the time of mounting, it - is created as directory. This string must be reflected in the unit filename. (See above.) This option + is created as either a directory or a file. The former is the usual case; the latter is done only if this mount + is a bind mount and the source (What=) is not a directory. + This string must be reflected in the unit filename. (See above.) This option is mandatory. diff --git a/src/core/mount.c b/src/core/mount.c index 4ea609a4fde..f47c511da60 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -13,6 +13,7 @@ #include "device.h" #include "exit-status.h" #include "format-util.h" +#include "fs-util.h" #include "fstab-util.h" #include "initrd-util.h" #include "libmount-util.h" @@ -27,6 +28,7 @@ #include "process-util.h" #include "serialize.h" #include "special.h" +#include "stat-util.h" #include "string-table.h" #include "string-util.h" #include "strv.h" @@ -1074,6 +1076,7 @@ fail: static void mount_enter_mounting(Mount *m) { int r; MountParameters *p; + bool source_is_dir = true; assert(m); @@ -1081,16 +1084,28 @@ static void mount_enter_mounting(Mount *m) { if (r < 0) goto fail; - (void) mkdir_p_label(m->where, m->directory_mode); + p = get_mount_parameters_fragment(m); + if (p && mount_is_bind(p)) { + r = is_dir(p->what, /* follow = */ true); + if (r < 0 && r != -ENOENT) + log_unit_info_errno(UNIT(m), r, "Failed to determine type of bind mount source '%s', ignoring: %m", p->what); + else if (r == 0) + source_is_dir = false; + } - unit_warn_if_dir_nonempty(UNIT(m), m->where); + if (source_is_dir) + (void) mkdir_p_label(m->where, m->directory_mode); + else + (void) touch_file(m->where, /* parents = */ true, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID); + + if (source_is_dir) + unit_warn_if_dir_nonempty(UNIT(m), m->where); unit_warn_leftover_processes(UNIT(m), unit_log_leftover_process_start); m->control_command_id = MOUNT_EXEC_MOUNT; m->control_command = m->exec_command + MOUNT_EXEC_MOUNT; /* Create the source directory for bind-mounts if needed */ - p = get_mount_parameters_fragment(m); if (p && mount_is_bind(p)) { r = mkdir_p_label(p->what, m->directory_mode); /* mkdir_p_label() can return -EEXIST if the target path exists and is not a directory - which is