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
This commit is contained in:
David Tardon 2023-01-13 15:58:39 +01:00 committed by Yu Watanabe
parent 87d1221174
commit 218cfe2335
2 changed files with 21 additions and 4 deletions

View file

@ -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>

View 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