diff options
-rw-r--r-- | man/kernel-command-line.xml | 3 | ||||
-rw-r--r-- | man/systemd-udevd.service.xml | 20 | ||||
-rw-r--r-- | src/udev/udevd.c | 86 |
3 files changed, 100 insertions, 9 deletions
diff --git a/man/kernel-command-line.xml b/man/kernel-command-line.xml index 7e59a1aaed..52939deec0 100644 --- a/man/kernel-command-line.xml +++ b/man/kernel-command-line.xml @@ -273,7 +273,8 @@ <term><varname>rd.udev.event_timeout=</varname></term> <term><varname>udev.timeout_signal=</varname></term> <term><varname>rd.udev.timeout_signal=</varname></term> - + <term><varname>udev.blockdev_read_only</varname></term> + <term><varname>rd.udev.blockdev_read_only</varname></term> <term><varname>net.ifnames=</varname></term> <term><varname>net.naming-scheme=</varname></term> diff --git a/man/systemd-udevd.service.xml b/man/systemd-udevd.service.xml index 55edc17353..302f44aa87 100644 --- a/man/systemd-udevd.service.xml +++ b/man/systemd-udevd.service.xml @@ -140,8 +140,8 @@ <refsect1><title>Kernel command line</title> <variablelist class='kernel-commandline-options'> - <para>Parameters starting with "rd." will be read when - <command>systemd-udevd</command> is used in an initrd.</para> + <para>Parameters prefixed with "rd." will be read when <command>systemd-udevd</command> is used in an + initrd, those without will be processed both in the initrd and on the host.</para> <varlistentry> <term><varname>udev.log_priority=</varname></term> <term><varname>rd.udev.log_priority=</varname></term> @@ -185,6 +185,22 @@ </listitem> </varlistentry> <varlistentry> + <term><varname>udev.blockdev_read_only</varname></term> + <term><varname>rd.udev.blockdev_read_only</varname></term> + <listitem> + <para>If specified, mark all physical block devices read-only as they appear. Synthetic block + devices (such as loopback block devices or device mapper devices) are left as they are. This is + useful to guarantee that the contents of physical block devices remains unmodified during runtime, + for example to implement fully stateless systems, for testing or for recovery situations where + corrupted file systems shall not be corrupted further through accidental modification.</para> + + <para>A block device may be marked writable again by issuing the <command>blockdev + --setrw</command> command, see <citerefentry + project='man-pages'><refentrytitle>blockdev</refentrytitle><manvolnum>8</manvolnum></citerefentry> + for details.</para> + </listitem> + </varlistentry> + <varlistentry> <term><varname>net.ifnames=</varname></term> <listitem> <para>Network interfaces are renamed to give them predictable names diff --git a/src/udev/udevd.c b/src/udev/udevd.c index a2b8c6162c..6e0ce72553 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -76,6 +76,7 @@ static unsigned arg_children_max = 0; static usec_t arg_exec_delay_usec = 0; static usec_t arg_event_timeout_usec = 180 * USEC_PER_SEC; static int arg_timeout_signal = SIGKILL; +static bool arg_blockdev_read_only = false; typedef struct Manager { sd_event *event; @@ -383,6 +384,56 @@ static int worker_lock_block_device(sd_device *dev, int *ret_fd) { return 1; } +static int worker_mark_block_device_read_only(sd_device *dev) { + _cleanup_close_ int fd = -1; + const char *val; + int state = 1, r; + + assert(dev); + + if (!arg_blockdev_read_only) + return 0; + + /* Do this only once, when the block device is new. If the device is later retriggered let's not + * toggle the bit again, so that people can boot up with full read-only mode and then unset the bit + * for specific devices only. */ + if (!device_for_action(dev, DEVICE_ACTION_ADD)) + return 0; + + r = sd_device_get_subsystem(dev, &val); + if (r < 0) + return log_device_debug_errno(dev, r, "Failed to get subsystem: %m"); + + if (!streq(val, "block")) + return 0; + + r = sd_device_get_sysname(dev, &val); + if (r < 0) + return log_device_debug_errno(dev, r, "Failed to get sysname: %m"); + + /* Exclude synthetic devices for now, this is supposed to be a safety feature to avoid modification + * of physical devices, and what sits on top of those doesn't really matter if we don't allow the + * underlying block devices to recieve changes. */ + if (STARTSWITH_SET(val, "dm-", "md", "drbd", "loop", "nbd", "zram")) + return 0; + + r = sd_device_get_devname(dev, &val); + if (r == -ENOENT) + return 0; + if (r < 0) + return log_device_debug_errno(dev, r, "Failed to get devname: %m"); + + fd = open(val, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK); + if (fd < 0) + return log_device_debug_errno(dev, errno, "Failed to open '%s', ignoring: %m", val); + + if (ioctl(fd, BLKROSET, &state) < 0) + return log_device_warning_errno(dev, errno, "Failed to mark block device '%s' read-only: %m", val); + + log_device_info(dev, "Successfully marked block device '%s' read-only.", val); + return 0; +} + static int worker_process_device(Manager *manager, sd_device *dev) { _cleanup_(udev_event_freep) UdevEvent *udev_event = NULL; _cleanup_close_ int fd_lock = -1; @@ -412,6 +463,8 @@ static int worker_process_device(Manager *manager, sd_device *dev) { if (r < 0) return r; + (void) worker_mark_block_device_read_only(dev); + /* apply rules, create node, symlinks */ r = udev_event_execute_rules(udev_event, arg_event_timeout_usec, arg_timeout_signal, manager->properties, manager->rules); if (r < 0) @@ -1417,15 +1470,13 @@ static int listen_fds(int *ret_ctrl, int *ret_netlink) { * udev.children_max=<number of workers> events are fully serialized if set to 1 * udev.exec_delay=<number of seconds> delay execution of every executed program * udev.event_timeout=<number of seconds> seconds to wait before terminating an event + * udev.blockdev_read_only<=bool> mark all block devices read-only when they appear */ static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { - int r = 0; + int r; assert(key); - if (!value) - return 0; - if (proc_cmdline_key_streq(key, "udev.log_priority")) { if (proc_cmdline_value_missing(key, value)) @@ -1457,14 +1508,37 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat r = parse_sec(value, &arg_exec_delay_usec); } else if (proc_cmdline_key_streq(key, "udev.timeout_signal")) { + if (proc_cmdline_value_missing(key, value)) return 0; r = signal_from_string(value); if (r > 0) arg_timeout_signal = r; - } else if (startswith(key, "udev.")) - log_warning("Unknown udev kernel command line option \"%s\", ignoring", key); + + } else if (proc_cmdline_key_streq(key, "udev.blockdev_read_only")) { + + if (!value) + arg_blockdev_read_only = true; + else { + r = parse_boolean(value); + if (r < 0) + log_warning_errno(r, "Failed to parse udev.blockdev-read-only argument, ignoring: %s", value); + else + arg_blockdev_read_only = r; + } + + if (arg_blockdev_read_only) + log_notice("All physical block devices will be marked read-only."); + + return 0; + + } else { + if (startswith(key, "udev.")) + log_warning("Unknown udev kernel command line option \"%s\", ignoring.", key); + + return 0; + } if (r < 0) log_warning_errno(r, "Failed to parse \"%s=%s\", ignoring: %m", key, value); |