]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
mount: handle bind mount of file with non-existing target
authorDavid Tardon <dtardon@redhat.com>
Fri, 13 Jan 2023 14:58:39 +0000 (15:58 +0100)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 16 Jan 2023 13:16:49 +0000 (22:16 +0900)
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
src/core/mount.c

index 773ca04cd6db16a781d41973bacd82c7dee47211..da6ade86c8628d6bc9cbc1935ddce656c6912336 100644 (file)
         <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>
 
index 4ea609a4fde9c80f40fe6f140a5022f39758d8a6..f47c511da6033de7463112cf93e81b3cf5335fcf 100644 (file)
@@ -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