-/* SPDX-License-Identifier: LGPL-2.1+ */
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <errno.h>
#include <signal.h>
MOUNT_CLEANING);
}
+static bool mount_is_automount(const MountParameters *p) {
+ assert(p);
+
+ return fstab_test_option(p->options,
+ "comment=systemd.automount\0"
+ "x-systemd.automount\0");
+}
+
static bool mount_is_network(const MountParameters *p) {
assert(p);
return false;
}
+static bool mount_is_nofail(const Mount *m) {
+ assert(m);
+
+ if (!m->from_fragment)
+ return false;
+
+ return fstab_test_yes_no_option(m->parameters_fragment.options, "nofail\0" "fail\0");
+}
+
static bool mount_is_loop(const MountParameters *p) {
assert(p);
m->timer_event_source = sd_event_source_unref(m->timer_event_source);
}
-_pure_ static MountParameters* get_mount_parameters_fragment(Mount *m) {
+static MountParameters* get_mount_parameters_fragment(Mount *m) {
assert(m);
if (m->from_fragment)
return NULL;
}
-_pure_ static MountParameters* get_mount_parameters(Mount *m) {
+static MountParameters* get_mount_parameters(Mount *m) {
assert(m);
if (m->from_proc_self_mountinfo)
static int mount_add_mount_dependencies(Mount *m) {
MountParameters *pm;
Unit *other;
- Iterator i;
Set *s;
int r;
/* Adds in dependencies to other units that use this path or paths further down in the hierarchy */
s = manager_get_units_requiring_mounts_for(UNIT(m)->manager, m->where);
- SET_FOREACH(other, s, i) {
+ SET_FOREACH(other, s) {
if (other->load_state != UNIT_LOADED)
continue;
if (!is_device_path(p->what))
return 0;
- /* /dev/root is a really weird thing, it's not a real device,
- * but just a path the kernel exports for the root file system
- * specified on the kernel command line. Ignore it here. */
- if (path_equal(p->what, "/dev/root"))
+ /* /dev/root is a really weird thing, it's not a real device, but just a path the kernel exports for
+ * the root file system specified on the kernel command line. Ignore it here. */
+ if (PATH_IN_SET(p->what, "/dev/root", "/dev/nfs"))
return 0;
if (path_equal(m->where, "/"))
return 0;
- /* Mount units from /proc/self/mountinfo are not bound to devices
- * by default since they're subject to races when devices are
- * unplugged. But the user can still force this dep with an
- * appropriate option (or udev property) so the mount units are
- * automatically stopped when the device disappears suddenly. */
+ /* Mount units from /proc/self/mountinfo are not bound to devices by default since they're subject to
+ * races when devices are unplugged. But the user can still force this dep with an appropriate option
+ * (or udev property) so the mount units are automatically stopped when the device disappears
+ * suddenly. */
dep = mount_is_bound_to_device(m) ? UNIT_BINDS_TO : UNIT_REQUIRES;
/* We always use 'what' from /proc/self/mountinfo if mounted */
if (r < 0)
return r;
- return 0;
+ return unit_add_blockdev_dependency(UNIT(m), p->what, mask);
}
static int mount_add_quota_dependencies(Mount *m) {
MountParameters *p;
assert(m);
- /* Returns true for all units that are "magic" and should be excluded from the usual start-up and shutdown
- * dependencies. We call them "extrinsic" here, as they are generally mounted outside of the systemd dependency
- * logic. We shouldn't attempt to manage them ourselves but it's fine if the user operates on them with us. */
+ /* Returns true for all units that are "magic" and should be excluded from the usual
+ * start-up and shutdown dependencies. We call them "extrinsic" here, as they are generally
+ * mounted outside of the systemd dependency logic. We shouldn't attempt to manage them
+ * ourselves but it's fine if the user operates on them with us. */
- if (!MANAGER_IS_SYSTEM(UNIT(m)->manager)) /* We only automatically manage mounts if we are in system mode */
+ /* We only automatically manage mounts if we are in system mode */
+ if (!MANAGER_IS_SYSTEM(UNIT(m)->manager))
return true;
if (UNIT(m)->perpetual) /* All perpetual units never change state */
return true;
- if (PATH_IN_SET(m->where, /* Don't bother with the OS data itself */
- "/", /* (strictly speaking redundant: should already be covered by the perpetual flag check above) */
- "/usr",
- "/etc"))
- return true;
-
- if (PATH_STARTSWITH_SET(m->where,
- "/run/initramfs", /* This should stay around from before we boot until after we shutdown */
- "/proc", /* All of this is API VFS */
- "/sys", /* … dito … */
- "/dev")) /* … dito … */
- return true;
-
- /* If this is an initrd mount, and we are not in the initrd, then leave this around forever, too. */
p = get_mount_parameters(m);
- if (p && fstab_test_option(p->options, "x-initrd.mount\0") && !in_initrd())
+ if (p && fstab_is_extrinsic(m->where, p->options))
return true;
return false;
}
+static int mount_add_default_ordering_dependencies(
+ Mount *m,
+ MountParameters *p,
+ UnitDependencyMask mask) {
+
+ const char *after, *before, *e;
+ int r;
+
+ assert(m);
+
+ e = path_startswith(m->where, "/sysroot");
+ if (e && in_initrd()) {
+ /* All mounts under /sysroot need to happen later, at initrd-fs.target time. IOW,
+ * it's not technically part of the basic initrd filesystem itself, and so
+ * shouldn't inherit the default Before=local-fs.target dependency. */
+
+ after = NULL;
+ before = isempty(e) ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_INITRD_FS_TARGET;
+
+ } else if (mount_is_network(p)) {
+ after = SPECIAL_REMOTE_FS_PRE_TARGET;
+ before = SPECIAL_REMOTE_FS_TARGET;
+
+ } else {
+ after = SPECIAL_LOCAL_FS_PRE_TARGET;
+ before = SPECIAL_LOCAL_FS_TARGET;
+ }
+
+ if (!mount_is_nofail(m) && !mount_is_automount(p)) {
+ r = unit_add_dependency_by_name(UNIT(m), UNIT_BEFORE, before, true, mask);
+ if (r < 0)
+ return r;
+ }
+
+ if (after) {
+ r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, true, mask);
+ if (r < 0)
+ return r;
+ }
+
+ return unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_CONFLICTS,
+ SPECIAL_UMOUNT_TARGET, true, mask);
+}
+
static int mount_add_default_dependencies(Mount *m) {
- const char *after, *before;
UnitDependencyMask mask;
MountParameters *p;
- bool nofail;
int r;
assert(m);
if (!UNIT(m)->default_dependencies)
return 0;
- /* We do not add any default dependencies to /, /usr or /run/initramfs/, since they are guaranteed to stay
- * mounted the whole time, since our system is on it. Also, don't bother with anything mounted below virtual
- * file systems, it's also going to be virtual, and hence not worth the effort. */
+ /* We do not add any default dependencies to /, /usr or /run/initramfs/, since they are
+ * guaranteed to stay mounted the whole time, since our system is on it. Also, don't
+ * bother with anything mounted below virtual file systems, it's also going to be virtual,
+ * and hence not worth the effort. */
if (mount_is_extrinsic(m))
return 0;
return 0;
mask = m->from_fragment ? UNIT_DEPENDENCY_FILE : UNIT_DEPENDENCY_MOUNTINFO_DEFAULT;
- nofail = m->from_fragment ? fstab_test_yes_no_option(m->parameters_fragment.options, "nofail\0" "fail\0") : false;
+
+ r = mount_add_default_ordering_dependencies(m, p, mask);
+ if (r < 0)
+ return r;
if (mount_is_network(p)) {
- /* We order ourselves after network.target. This is
- * primarily useful at shutdown: services that take
- * down the network should order themselves before
- * network.target, so that they are shut down only
- * after this mount unit is stopped. */
+ /* We order ourselves after network.target. This is primarily useful at shutdown:
+ * services that take down the network should order themselves before
+ * network.target, so that they are shut down only after this mount unit is
+ * stopped. */
r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_NETWORK_TARGET, true, mask);
if (r < 0)
return r;
- /* We pull in network-online.target, and order
- * ourselves after it. This is useful at start-up to
- * actively pull in tools that want to be started
- * before we start mounting network file systems, and
- * whose purpose it is to delay this until the network
- * is "up". */
+ /* We pull in network-online.target, and order ourselves after it. This is useful
+ * at start-up to actively pull in tools that want to be started before we start
+ * mounting network file systems, and whose purpose it is to delay this until the
+ * network is "up". */
r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_WANTS, UNIT_AFTER, SPECIAL_NETWORK_ONLINE_TARGET, true, mask);
if (r < 0)
return r;
-
- after = SPECIAL_REMOTE_FS_PRE_TARGET;
- before = SPECIAL_REMOTE_FS_TARGET;
- } else {
- after = SPECIAL_LOCAL_FS_PRE_TARGET;
- before = SPECIAL_LOCAL_FS_TARGET;
- }
-
- if (!nofail) {
- r = unit_add_dependency_by_name(UNIT(m), UNIT_BEFORE, before, true, mask);
- if (r < 0)
- return r;
}
- r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, after, true, mask);
- if (r < 0)
- return r;
-
- r = unit_add_two_dependencies_by_name(UNIT(m), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, true, mask);
- if (r < 0)
- return r;
-
/* If this is a tmpfs mount then we have to unmount it before we try to deactivate swaps */
if (streq_ptr(p->fstype, "tmpfs")) {
r = unit_add_dependency_by_name(UNIT(m), UNIT_AFTER, SPECIAL_SWAP_TARGET, true, mask);
}
p = get_mount_parameters_fragment(m);
- if (p && !p->what) {
- log_unit_error(UNIT(m), "What= setting is missing. Refusing.");
- return -ENOEXEC;
- }
+ if (p && !p->what && !UNIT(m)->perpetual)
+ return log_unit_error_errno(UNIT(m), SYNTHETIC_ERRNO(ENOEXEC),
+ "What= setting is missing. Refusing.");
if (m->exec_context.pam_name && m->kill_context.kill_mode != KILL_CONTROL_GROUP) {
log_unit_error(UNIT(m), "Unit has PAM enabled. Kill mode must be set to control-group'. Refusing.");
"%sSloppyOptions: %s\n"
"%sLazyUnmount: %s\n"
"%sForceUnmount: %s\n"
+ "%sReadWriteOnly: %s\n"
"%sTimeoutSec: %s\n",
prefix, mount_state_to_string(m->state),
prefix, mount_result_to_string(m->result),
prefix, yes_no(m->sloppy_options),
prefix, yes_no(m->lazy_unmount),
prefix, yes_no(m->force_unmount),
+ prefix, yes_no(m->read_write_only),
prefix, format_timespan(buf, sizeof(buf), m->timeout_usec, USEC_PER_SEC));
if (m->control_pid > 0)
m->result = f;
unit_log_result(UNIT(m), m->result == MOUNT_SUCCESS, mount_result_to_string(m->result));
+ unit_warn_leftover_processes(UNIT(m), unit_log_leftover_process_stop);
+
mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD);
m->exec_runtime = exec_runtime_unref(m->exec_runtime, true);
- unit_destroy_runtime_directory(UNIT(m), &m->exec_context);
+ unit_destroy_runtime_data(UNIT(m), &m->exec_context);
unit_unref_uid_gid(UNIT(m), true);
(void) mkdir_p_label(m->where, m->directory_mode);
unit_warn_if_dir_nonempty(UNIT(m), m->where);
- unit_warn_leftover_processes(UNIT(m));
+ 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))
- (void) mkdir_p_label(p->what, m->directory_mode);
+ if (p && mount_is_bind(p)) {
+ r = mkdir_p_label(p->what, m->directory_mode);
+ if (r < 0)
+ log_unit_error_errno(UNIT(m), r, "Failed to make bind mount source '%s': %m", p->what);
+ }
if (p) {
_cleanup_free_ char *opts = NULL;
r = exec_command_set(m->control_command, MOUNT_PATH, p->what, m->where, NULL);
if (r >= 0 && m->sloppy_options)
r = exec_command_append(m->control_command, "-s", NULL);
+ if (r >= 0 && m->read_write_only)
+ r = exec_command_append(m->control_command, "-w", NULL);
if (r >= 0 && p->fstype)
r = exec_command_append(m->control_command, "-t", p->fstype, NULL);
if (r >= 0 && !isempty(opts))
"-o", o, NULL);
if (r >= 0 && m->sloppy_options)
r = exec_command_append(m->control_command, "-s", NULL);
+ if (r >= 0 && m->read_write_only)
+ r = exec_command_append(m->control_command, "-w", NULL);
if (r >= 0 && p->fstype)
r = exec_command_append(m->control_command, "-t", p->fstype, NULL);
} else
if (!is_path(where))
return 0;
+ /* Mount unit names have to be (like all other unit names) short enough to fit into file names. This
+ * means there's a good chance that overly long mount point paths after mangling them to look like a
+ * unit name would result in unit names we don't actually consider valid. This should be OK however
+ * as such long mount point paths should not happen on regular systems — and if they appear
+ * nonetheless they are generally synthesized by software, and thus managed by that other
+ * software. Having such long names just means you cannot use systemd to manage those specific mount
+ * points, which should be an OK restriction to make. After all we don't have to be able to manage
+ * all mount points in the world — as long as we don't choke on them when we encounter them. */
r = unit_name_from_path(where, ".mount", &e);
- if (r < 0)
- return log_error_errno(r, "Failed to generate unit name from path '%s': %m", where);
+ if (r < 0) {
+ static RateLimit rate_limit = { /* Let's log about this at warning level at most once every
+ * 5s. Given that we generate this whenever we read the file
+ * otherwise we probably shouldn't flood the logs with
+ * this */
+ .interval = 5 * USEC_PER_SEC,
+ .burst = 1,
+ };
+
+ return log_struct_errno(
+ ratelimit_below(&rate_limit) ? LOG_WARNING : LOG_DEBUG, r,
+ "MESSAGE_ID=" SD_MESSAGE_MOUNT_POINT_PATH_NOT_SUITABLE_STR,
+ "MOUNT_POINT=%s", where,
+ LOG_MESSAGE("Failed to generate valid unit name from path '%s', ignoring mount point: %m", where));
+ }
u = manager_get_unit(m, e);
if (u)
* by the sysadmin having called mount(8) directly. */
r = mount_setup_new_unit(m, e, what, where, options, fstype, &flags, &u);
if (r < 0)
- return log_warning_errno(r, "Failed to set up mount unit: %m");
+ return log_warning_errno(r, "Failed to set up mount unit for '%s': %m", where);
/* If the mount changed properties or state, let's notify our clients */
if (flags & (MOUNT_PROC_JUST_CHANGED|MOUNT_PROC_JUST_MOUNTED))
static int mount_process_proc_self_mountinfo(Manager *m) {
_cleanup_set_free_free_ Set *around = NULL, *gone = NULL;
const char *what;
- Iterator i;
Unit *u;
int r;
/* Remember that this device might just have disappeared */
if (set_ensure_allocated(&gone, &path_hash_ops) < 0 ||
- set_put_strdup(gone, mount->parameters_proc_self_mountinfo.what) < 0)
+ set_put_strdup(&gone, mount->parameters_proc_self_mountinfo.what) < 0)
log_oom(); /* we don't care too much about OOM here... */
}
/* Track devices currently used */
if (set_ensure_allocated(&around, &path_hash_ops) < 0 ||
- set_put_strdup(around, mount->parameters_proc_self_mountinfo.what) < 0)
+ set_put_strdup(&around, mount->parameters_proc_self_mountinfo.what) < 0)
log_oom();
}
mount->proc_flags = 0;
}
- SET_FOREACH(what, gone, i) {
+ SET_FOREACH(what, gone) {
if (set_contains(around, what))
continue;
.control_pid = mount_control_pid,
- .bus_vtable = bus_mount_vtable,
.bus_set_property = bus_mount_set_property,
.bus_commit_properties = bus_mount_commit_properties,