summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Tardon <dtardon@redhat.com>2023-01-13 15:58:39 +0100
committerYu Watanabe <watanabe.yu+github@gmail.com>2023-01-16 22:16:49 +0900
commit218cfe23354397ded28ac898f82b52724f48dae7 (patch)
tree73c8ad7c084f753dc44610b4d91e778266f32a1a
parent87d1221174f252aec6fe3f3031d0b9b548aa6097 (diff)
downloadsystemd-218cfe23354397ded28ac898f82b52724f48dae7.tar.gz
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
-rw-r--r--man/systemd.mount.xml4
-rw-r--r--src/core/mount.c21
2 files changed, 21 insertions, 4 deletions
diff --git a/man/systemd.mount.xml b/man/systemd.mount.xml
index 773ca04cd6..da6ade86c8 100644
--- a/man/systemd.mount.xml
+++ b/man/systemd.mount.xml
@@ -476,7 +476,9 @@
<term><varname>Where=</varname></term>
<listitem><para>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 (<varname>What=</varname>) is not a directory.
+ This string must be reflected in the unit filename. (See above.) This option
is mandatory.</para></listitem>
</varlistentry>
diff --git a/src/core/mount.c b/src/core/mount.c
index 4ea609a4fd..f47c511da6 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