diff options
author | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2017-01-14 19:48:04 -0500 |
---|---|---|
committer | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2017-01-14 21:29:27 -0500 |
commit | 6b3d378331fe714c7bf2263eaa9a8b33fc878e7c (patch) | |
tree | 5fca867e0b2cda62c2dc4f1a9a4bd78ab345b158 /src | |
parent | ab8864ebc3ac01288729b44f0d5f18fff37defb5 (diff) | |
parent | bafbac4e85a5eefd4b57a5cd0eb61885fb60edc9 (diff) |
Merge pull request #4879 from poettering/systemd
Diffstat (limited to 'src')
59 files changed, 2232 insertions, 1061 deletions
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index dc13025115..d2d18f13f0 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -2361,6 +2361,7 @@ int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) { bool cg_is_unified_wanted(void) { static thread_local int wanted = -1; int r, unified; + bool b; /* If the hierarchy is already mounted, then follow whatever * was chosen for it. */ @@ -2374,20 +2375,11 @@ bool cg_is_unified_wanted(void) { if (wanted >= 0) return wanted; - r = get_proc_cmdline_key("systemd.unified_cgroup_hierarchy", NULL); - if (r > 0) - return (wanted = true); - else { - _cleanup_free_ char *value = NULL; - - r = get_proc_cmdline_key("systemd.unified_cgroup_hierarchy=", &value); - if (r < 0) - return false; - if (r == 0) - return (wanted = false); + r = proc_cmdline_get_bool("systemd.unified_cgroup_hierarchy", &b); + if (r < 0) + return false; - return (wanted = parse_boolean(value) > 0); - } + return (wanted = r > 0 ? b : false); } bool cg_is_legacy_wanted(void) { @@ -2397,6 +2389,7 @@ bool cg_is_legacy_wanted(void) { bool cg_is_unified_systemd_controller_wanted(void) { static thread_local int wanted = -1; int r, unified; + bool b; /* If the unified hierarchy is requested in full, no need to * bother with this. */ @@ -2415,23 +2408,11 @@ bool cg_is_unified_systemd_controller_wanted(void) { if (wanted >= 0) return wanted; - r = get_proc_cmdline_key("systemd.legacy_systemd_cgroup_controller", NULL); - if (r > 0) - wanted = false; - else { - _cleanup_free_ char *value = NULL; - - r = get_proc_cmdline_key("systemd.legacy_systemd_cgroup_controller=", &value); - if (r < 0) - return false; - - if (r == 0) - wanted = false; - else - wanted = parse_boolean(value) <= 0; - } + r = proc_cmdline_get_bool("systemd.legacy_systemd_cgroup_controller", &b); + if (r < 0) + return false; - return wanted; + return (wanted = r > 0 ? b : false); } bool cg_is_legacy_systemd_controller_wanted(void) { diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index 5b23269109..e31fa2711a 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -799,8 +799,10 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, return -ENOMEM; } - *ret = done; - done = NULL; + if (ret) { + *ret = done; + done = NULL; + } return exists; } diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c index c5bda6c4d6..6843aedd0a 100644 --- a/src/basic/hexdecoct.c +++ b/src/basic/hexdecoct.c @@ -97,6 +97,9 @@ int unhexmem(const char *p, size_t l, void **mem, size_t *len) { assert(len); assert(p); + if (l % 2 != 0) + return -EINVAL; + z = r = malloc((l + 1) / 2 + 1); if (!r) return -ENOMEM; @@ -107,12 +110,10 @@ int unhexmem(const char *p, size_t l, void **mem, size_t *len) { a = unhexchar(x[0]); if (a < 0) return a; - else if (x+1 < p + l) { - b = unhexchar(x[1]); - if (b < 0) - return b; - } else - b = 0; + + b = unhexchar(x[1]); + if (b < 0) + return b; *(z++) = (uint8_t) a << 4 | (uint8_t) b; } diff --git a/src/basic/log.c b/src/basic/log.c index 557212c022..1362b1c086 100644 --- a/src/basic/log.c +++ b/src/basic/log.c @@ -981,24 +981,30 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat if (streq(key, "debug") && !value) log_set_max_level(LOG_DEBUG); - else if (streq(key, "systemd.log_target") && value) { + else if (proc_cmdline_key_streq(key, "systemd.log_target")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; if (log_set_target_from_string(value) < 0) log_warning("Failed to parse log target '%s'. Ignoring.", value); - } else if (streq(key, "systemd.log_level") && value) { + } else if (proc_cmdline_key_streq(key, "systemd.log_level")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; if (log_set_max_level_from_string(value) < 0) log_warning("Failed to parse log level '%s'. Ignoring.", value); - } else if (streq(key, "systemd.log_color") && value) { + } else if (proc_cmdline_key_streq(key, "systemd.log_color")) { - if (log_show_color_from_string(value) < 0) + if (log_show_color_from_string(value ?: "1") < 0) log_warning("Failed to parse log color setting '%s'. Ignoring.", value); - } else if (streq(key, "systemd.log_location") && value) { + } else if (proc_cmdline_key_streq(key, "systemd.log_location")) { - if (log_show_location_from_string(value) < 0) + if (log_show_location_from_string(value ?: "1") < 0) log_warning("Failed to parse log location setting '%s'. Ignoring.", value); } @@ -1009,10 +1015,9 @@ void log_parse_environment(void) { const char *e; if (get_ctty_devnr(0, NULL) < 0) - /* Only try to read the command line in daemons. - We assume that anything that has a controlling - tty is user stuff. */ - (void) parse_proc_cmdline(parse_proc_cmdline_item, NULL, true); + /* Only try to read the command line in daemons. We assume that anything that has a controlling tty is + user stuff. */ + (void) proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX); e = secure_getenv("SYSTEMD_LOG_TARGET"); if (e && log_set_target_from_string(e) < 0) diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c index 840e94a553..f0bc9cac18 100644 --- a/src/basic/mount-util.c +++ b/src/basic/mount-util.c @@ -673,6 +673,9 @@ int mount_verbose( else if ((flags & MS_BIND) && !type) log_debug("Bind-mounting %s on %s (%s \"%s\")...", what, where, strnull(fl), strempty(options)); + else if (flags & MS_MOVE) + log_debug("Moving mount %s → %s (%s \"%s\")...", + what, where, strnull(fl), strempty(options)); else log_debug("Mounting %s on %s (%s \"%s\")...", strna(type), where, strnull(fl), strempty(options)); diff --git a/src/basic/proc-cmdline.c b/src/basic/proc-cmdline.c index 8297a222b7..6ecb6c3f0d 100644 --- a/src/basic/proc-cmdline.c +++ b/src/basic/proc-cmdline.c @@ -34,17 +34,30 @@ #include "virt.h" int proc_cmdline(char **ret) { + const char *e; assert(ret); + /* For testing purposes it is sometimes useful to be able to override what we consider /proc/cmdline to be */ + e = secure_getenv("SYSTEMD_PROC_CMDLINE"); + if (e) { + char *m; + + m = strdup(e); + if (!m) + return -ENOMEM; + + *ret = m; + return 0; + } + if (detect_container() > 0) return get_process_cmdline(1, 0, false, ret); else return read_one_line_file("/proc/cmdline", ret); } -int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value, void *data), - void *data, - bool strip_prefix) { +int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, unsigned flags) { + _cleanup_free_ char *line = NULL; const char *p; int r; @@ -58,7 +71,7 @@ int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value, voi p = line; for (;;) { _cleanup_free_ char *word = NULL; - char *value = NULL, *unprefixed; + char *value, *key, *q; r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX); if (r < 0) @@ -66,17 +79,23 @@ int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value, voi if (r == 0) break; - /* Filter out arguments that are intended only for the - * initrd */ - unprefixed = startswith(word, "rd."); - if (unprefixed && !in_initrd()) - continue; + key = word; + + /* Filter out arguments that are intended only for the initrd */ + q = startswith(word, "rd."); + if (q) { + if (!in_initrd()) + continue; + + if (flags & PROC_CMDLINE_STRIP_RD_PREFIX) + key = q; + } - value = strchr(word, '='); + value = strchr(key, '='); if (value) *(value++) = 0; - r = parse_item(strip_prefix && unprefixed ? unprefixed : word, value, data); + r = parse_item(key, value, data); if (r < 0) return r; } @@ -84,13 +103,64 @@ int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value, voi return 0; } -int get_proc_cmdline_key(const char *key, char **value) { +static bool relaxed_equal_char(char a, char b) { + + return a == b || + (a == '_' && b == '-') || + (a == '-' && b == '_'); +} + +char *proc_cmdline_key_startswith(const char *s, const char *prefix) { + + assert(s); + assert(prefix); + + /* Much like startswith(), but considers "-" and "_" the same */ + + for (; *prefix != 0; s++, prefix++) + if (!relaxed_equal_char(*s, *prefix)) + return NULL; + + return (char*) s; +} + +bool proc_cmdline_key_streq(const char *x, const char *y) { + assert(x); + assert(y); + + /* Much like streq(), but considers "-" and "_" the same */ + + for (; *x != 0 || *y != 0; x++, y++) + if (!relaxed_equal_char(*x, *y)) + return false; + + return true; +} + +int proc_cmdline_get_key(const char *key, unsigned flags, char **value) { _cleanup_free_ char *line = NULL, *ret = NULL; bool found = false; const char *p; int r; - assert(key); + /* Looks for a specific key on the kernel command line. Supports two modes: + * + * a) The "value" parameter is used. In this case a parameter beginning with the "key" string followed by "=" + * is searched, and the value following this is returned in "value". + * + * b) as above, but the PROC_CMDLINE_VALUE_OPTIONAL flag is set. In this case if the the key is found as a + * separate word (i.e. not followed by "=" but instead by whitespace or the end of the command line), then + * this is also accepted, and "value" is returned as NULL. + * + * c) The "value" parameter is NULL. In this case a search for the exact "key" parameter is performed. + * + * In all three cases, > 0 is returned if the key is found, 0 if not.*/ + + if (isempty(key)) + return -EINVAL; + + if ((flags & PROC_CMDLINE_VALUE_OPTIONAL) && !value) + return -EINVAL; r = proc_cmdline(&line); if (r < 0) @@ -107,21 +177,26 @@ int get_proc_cmdline_key(const char *key, char **value) { if (r == 0) break; - /* Filter out arguments that are intended only for the - * initrd */ + /* Automatically filter out arguments that are intended only for the initrd, if we are not in the + * initrd. */ if (!in_initrd() && startswith(word, "rd.")) continue; if (value) { - e = startswith(word, key); + e = proc_cmdline_key_startswith(word, key); if (!e) continue; - r = free_and_strdup(&ret, e); - if (r < 0) - return r; + if (*e == '=') { + r = free_and_strdup(&ret, e+1); + if (r < 0) + return r; + + found = true; + + } else if (*e == 0 && (flags & PROC_CMDLINE_VALUE_OPTIONAL)) + found = true; - found = true; } else { if (streq(word, key)) found = true; @@ -134,20 +209,42 @@ int get_proc_cmdline_key(const char *key, char **value) { } return found; +} + +int proc_cmdline_get_bool(const char *key, bool *ret) { + _cleanup_free_ char *v = NULL; + int r; + + assert(ret); + + r = proc_cmdline_get_key(key, PROC_CMDLINE_VALUE_OPTIONAL, &v); + if (r < 0) + return r; + if (r == 0) { + *ret = false; + return 0; + } + + if (v) { /* parameter passed */ + r = parse_boolean(v); + if (r < 0) + return r; + *ret = r; + } else /* no parameter passed */ + *ret = true; + return 1; } int shall_restore_state(void) { - _cleanup_free_ char *value = NULL; + bool ret; int r; - r = get_proc_cmdline_key("systemd.restore_state=", &value); + r = proc_cmdline_get_bool("systemd.restore_state", &ret); if (r < 0) return r; - if (r == 0) - return true; - return parse_boolean(value); + return r > 0 ? ret : true; } static const char * const rlmap[] = { diff --git a/src/basic/proc-cmdline.h b/src/basic/proc-cmdline.h index 6d6ee95c11..ebfed355e9 100644 --- a/src/basic/proc-cmdline.h +++ b/src/basic/proc-cmdline.h @@ -19,11 +19,36 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <stdbool.h> + +#include "log.h" + +enum { + PROC_CMDLINE_STRIP_RD_PREFIX = 1, + PROC_CMDLINE_VALUE_OPTIONAL = 2, +}; + +typedef int (*proc_cmdline_parse_t)(const char *key, const char *value, void *data); + int proc_cmdline(char **ret); -int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value, void *data), - void *data, - bool strip_prefix); -int get_proc_cmdline_key(const char *parameter, char **value); + +int proc_cmdline_parse(const proc_cmdline_parse_t parse, void *userdata, unsigned flags); + +int proc_cmdline_get_key(const char *parameter, unsigned flags, char **value); +int proc_cmdline_get_bool(const char *key, bool *ret); + +char *proc_cmdline_key_startswith(const char *s, const char *prefix); +bool proc_cmdline_key_streq(const char *x, const char *y); int shall_restore_state(void); const char* runlevel_to_target(const char *rl); + +/* A little helper call, to be used in proc_cmdline_parse_t callbacks */ +static inline bool proc_cmdline_value_missing(const char *key, const char *value) { + if (!value) { + log_warning("Missing argument for %s= kernel command line switch, ignoring.", key); + return true; + } + + return false; +} diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index 309e84b93d..7e1914aa14 100644 --- a/src/basic/stat-util.c +++ b/src/basic/stat-util.c @@ -28,6 +28,7 @@ #include "dirent-util.h" #include "fd-util.h" +#include "fs-util.h" #include "macro.h" #include "missing.h" #include "stat-util.h" @@ -143,22 +144,29 @@ int path_is_read_only_fs(const char *path) { } int path_is_os_tree(const char *path) { - char *p; int r; assert(path); - /* We use /usr/lib/os-release as flag file if something is an OS */ - p = strjoina(path, "/usr/lib/os-release"); - r = access(p, F_OK); - if (r >= 0) - return 1; + /* Does the path exist at all? If not, generate an error immediately. This is useful so that a missing root dir + * always results in -ENOENT, and we can properly distuingish the case where the whole root doesn't exist from + * the case where just the os-release file is missing. */ + if (laccess(path, F_OK) < 0) + return -errno; - /* Also check for the old location in /etc, just in case. */ - p = strjoina(path, "/etc/os-release"); - r = access(p, F_OK); + /* We use /usr/lib/os-release as flag file if something is an OS */ + r = chase_symlinks("/usr/lib/os-release", path, CHASE_PREFIX_ROOT, NULL); + if (r == -ENOENT) { + + /* Also check for the old location in /etc, just in case. */ + r = chase_symlinks("/etc/os-release", path, CHASE_PREFIX_ROOT, NULL); + if (r == -ENOENT) + return 0; /* We got nothing */ + } + if (r < 0) + return r; - return r >= 0; + return 1; } int files_same(const char *filea, const char *fileb) { @@ -196,7 +204,7 @@ int fd_check_fstype(int fd, statfs_f_type_t magic_value) { int path_check_fstype(const char *path, statfs_f_type_t magic_value) { _cleanup_close_ int fd = -1; - fd = open(path, O_RDONLY); + fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH); if (fd < 0) return -errno; @@ -216,3 +224,13 @@ int fd_is_temporary_fs(int fd) { return is_temporary_fs(&s); } + +int path_is_temporary_fs(const char *path) { + _cleanup_close_ int fd = -1; + + fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH); + if (fd < 0) + return -errno; + + return fd_is_temporary_fs(fd); +} diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h index 56d28f791e..5d571efe18 100644 --- a/src/basic/stat-util.h +++ b/src/basic/stat-util.h @@ -61,6 +61,7 @@ int path_check_fstype(const char *path, statfs_f_type_t magic_value); bool is_temporary_fs(const struct statfs *s) _pure_; int fd_is_temporary_fs(int fd); +int path_is_temporary_fs(const char *path); /* Because statfs.t_type can be int on some architectures, we have to cast * the const magic to the type, otherwise the compiler warns about diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 9af49dd1bc..9876251438 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -35,6 +35,7 @@ #include "fd-util.h" #include "fileio.h" #include "format-util.h" +#include "fs-util.h" #include "install.h" #include "log.h" #include "path-util.h" @@ -1484,25 +1485,36 @@ static int method_switch_root(sd_bus_message *message, void *userdata, sd_bus_er if (r < 0) return r; - if (path_equal(root, "/") || !path_is_absolute(root)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid switch root path %s", root); + if (isempty(root)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New root directory may not be the empty string."); + if (!path_is_absolute(root)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New root path '%s' is not absolute.", root); + if (path_equal(root, "/")) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New root directory cannot be the old root directory."); /* Safety check */ if (isempty(init)) { - if (!path_is_os_tree(root)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified switch root path %s does not seem to be an OS tree. os-release file is missing.", root); + r = path_is_os_tree(root); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to determine whether root path '%s' contains an OS tree: %m", root); + if (r == 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified switch root path '%s' does not seem to be an OS tree. os-release file is missing.", root); } else { - _cleanup_free_ char *p = NULL; + _cleanup_free_ char *chased = NULL; if (!path_is_absolute(init)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid init path %s", init); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path to init binary '%s' not absolute.", init); - p = strappend(root, init); - if (!p) - return -ENOMEM; + r = chase_symlinks(init, root, CHASE_PREFIX_ROOT, &chased); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Could not resolve init executable %s: %m", init); + + if (laccess(chased, X_OK) < 0) { + if (errno == EACCES) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Init binary %s is not executable.", init); - if (access(p, X_OK) < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified init binary %s does not exist.", p); + return sd_bus_error_set_errnof(error, r, "Could not check whether init binary %s is executable: %m", init); + } } rt = strdup(root); diff --git a/src/core/job.c b/src/core/job.c index 2ba4c78096..f7c4c59c32 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -645,7 +645,7 @@ _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobR [JOB_DEPENDENCY] = "Dependency failed for %s.", [JOB_ASSERT] = "Assertion failed for %s.", [JOB_UNSUPPORTED] = "Starting of %s not supported.", - [JOB_COLLECTED] = "Unecessary job for %s was removed.", + [JOB_COLLECTED] = "Unnecessary job for %s was removed.", }; static const char *const generic_finished_stop_job[_JOB_RESULT_MAX] = { [JOB_DONE] = "Stopped %s.", diff --git a/src/core/main.c b/src/core/main.c index 02992c7324..56a81ab94a 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -49,6 +49,7 @@ #include "cpu-set-util.h" #include "dbus-manager.h" #include "def.h" +#include "emergency-action.h" #include "env-util.h" #include "fd-util.h" #include "fdset.h" @@ -90,7 +91,6 @@ #include "user-util.h" #include "virt.h" #include "watchdog.h" -#include "emergency-action.h" static enum { ACTION_RUN, @@ -337,60 +337,73 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat assert(key); - if (streq(key, "systemd.unit") && value) { - - if (!in_initrd()) - return free_and_strdup(&arg_default_unit, value); + if (STR_IN_SET(key, "systemd.unit", "rd.systemd.unit")) { - } else if (streq(key, "rd.systemd.unit") && value) { + if (proc_cmdline_value_missing(key, value)) + return 0; - if (in_initrd()) - return free_and_strdup(&arg_default_unit, value); + if (!unit_name_is_valid(value, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) + log_warning("Unit name specified on %s= is not valid, ignoring: %s", key, value); + else if (in_initrd() == !!startswith(key, "rd.")) { + if (free_and_strdup(&arg_default_unit, value) < 0) + return log_oom(); + } - } else if (streq(key, "systemd.dump_core") && value) { + } else if (proc_cmdline_key_streq(key, "systemd.dump_core")) { - r = parse_boolean(value); + r = value ? parse_boolean(value) : true; if (r < 0) log_warning("Failed to parse dump core switch %s. Ignoring.", value); else arg_dump_core = r; - } else if (streq(key, "systemd.crash_chvt") && value) { + } else if (proc_cmdline_key_streq(key, "systemd.crash_chvt")) { - if (parse_crash_chvt(value) < 0) + if (!value) + arg_crash_chvt = 0; /* turn on */ + else if (parse_crash_chvt(value) < 0) log_warning("Failed to parse crash chvt switch %s. Ignoring.", value); - } else if (streq(key, "systemd.crash_shell") && value) { + } else if (proc_cmdline_key_streq(key, "systemd.crash_shell")) { - r = parse_boolean(value); + r = value ? parse_boolean(value) : true; if (r < 0) log_warning("Failed to parse crash shell switch %s. Ignoring.", value); else arg_crash_shell = r; - } else if (streq(key, "systemd.crash_reboot") && value) { + } else if (proc_cmdline_key_streq(key, "systemd.crash_reboot")) { - r = parse_boolean(value); + r = value ? parse_boolean(value) : true; if (r < 0) log_warning("Failed to parse crash reboot switch %s. Ignoring.", value); else arg_crash_reboot = r; - } else if (streq(key, "systemd.confirm_spawn") && value) { - - arg_confirm_spawn = mfree(arg_confirm_spawn); + } else if (proc_cmdline_key_streq(key, "systemd.confirm_spawn")) { + char *s; - r = parse_confirm_spawn(value, &arg_confirm_spawn); + r = parse_confirm_spawn(value, &s); if (r < 0) log_warning_errno(r, "Failed to parse confirm_spawn switch %s. Ignoring.", value); + else { + free(arg_confirm_spawn); + arg_confirm_spawn = s; + } - } else if (streq(key, "systemd.show_status") && value) { + } else if (proc_cmdline_key_streq(key, "systemd.show_status")) { - r = parse_show_status(value, &arg_show_status); - if (r < 0) - log_warning("Failed to parse show status switch %s. Ignoring.", value); + if (value) { + r = parse_show_status(value, &arg_show_status); + if (r < 0) + log_warning("Failed to parse show status switch %s. Ignoring.", value); + } else + arg_show_status = SHOW_STATUS_YES; + + } else if (proc_cmdline_key_streq(key, "systemd.default_standard_output")) { - } else if (streq(key, "systemd.default_standard_output") && value) { + if (proc_cmdline_value_missing(key, value)) + return 0; r = exec_output_from_string(value); if (r < 0) @@ -398,7 +411,10 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat else arg_default_std_output = r; - } else if (streq(key, "systemd.default_standard_error") && value) { + } else if (proc_cmdline_key_streq(key, "systemd.default_standard_error")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; r = exec_output_from_string(value); if (r < 0) @@ -406,24 +422,42 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat else arg_default_std_error = r; - } else if (streq(key, "systemd.setenv") && value) { + } else if (streq(key, "systemd.setenv")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; if (env_assignment_is_valid(value)) { char **env; env = strv_env_set(arg_default_environment, value); - if (env) - arg_default_environment = env; - else - log_warning_errno(ENOMEM, "Setting environment variable '%s' failed, ignoring: %m", value); + if (!env) + return log_oom(); + + arg_default_environment = env; } else log_warning("Environment variable name '%s' is not valid. Ignoring.", value); - } else if (streq(key, "systemd.machine_id") && value) { + } else if (proc_cmdline_key_streq(key, "systemd.machine_id")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + + r = set_machine_id(value); + if (r < 0) + log_warning("MachineID '%s' is not valid. Ignoring.", value); + + } else if (proc_cmdline_key_streq(key, "systemd.default_timeout_start_sec")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + + r = parse_sec(value, &arg_default_timeout_start_usec); + if (r < 0) + log_warning_errno(r, "Failed to parse default start timeout: %s, ignoring.", value); - r = set_machine_id(value); - if (r < 0) - log_warning("MachineID '%s' is not valid. Ignoring.", value); + if (arg_default_timeout_start_usec <= 0) + arg_default_timeout_start_usec = USEC_INFINITY; } else if (streq(key, "quiet") && !value) { @@ -445,15 +479,6 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat target = runlevel_to_target(key); if (target) return free_and_strdup(&arg_default_unit, target); - - } else if (streq(key, "systemd.default_timeout_start_sec") && value) { - - r = parse_sec(value, &arg_default_timeout_start_usec); - if (r < 0) - log_warning_errno(r, "Failed to parse default start timeout: %s, ignoring.", value); - - if (arg_default_timeout_start_usec <= 0) - arg_default_timeout_start_usec = USEC_INFINITY; } return 0; @@ -1341,10 +1366,9 @@ static int fixup_environment(void) { * However if TERM was configured through the kernel * command line then leave it alone. */ - r = get_proc_cmdline_key("TERM=", &term); + r = proc_cmdline_get_key("TERM", 0, &term); if (r < 0) return r; - if (r == 0) { term = strdup(default_term_for_tty("/dev/console")); if (!term) @@ -1411,7 +1435,7 @@ int main(int argc, char *argv[]) { called 'systemd'. That is confusing, hence let's call us systemd right-away. */ program_invocation_short_name = systemd; - prctl(PR_SET_NAME, systemd); + (void) prctl(PR_SET_NAME, systemd); saved_argv = argv; saved_argc = argc; @@ -1435,9 +1459,10 @@ int main(int argc, char *argv[]) { if (!skip_setup) { r = mount_setup_early(); if (r < 0) { - error_message = "Failed to early mount API filesystems"; + error_message = "Failed to mount early API filesystems"; goto finish; } + dual_timestamp_get(&security_start_timestamp); if (mac_selinux_setup(&loaded_policy) < 0) { error_message = "Failed to load SELinux policy"; @@ -1513,7 +1538,7 @@ int main(int argc, char *argv[]) { log_close_console(); /* force reopen of /dev/console */ log_open(); - /* For the later on, see above... */ + /* For later on, see above... */ log_set_target(LOG_TARGET_JOURNAL); /* clear the kernel timestamp, @@ -1590,7 +1615,7 @@ int main(int argc, char *argv[]) { } if (arg_system) { - r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, false); + r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0); if (r < 0) log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); } diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c index 6338067d7e..9c2bf3a0ef 100644 --- a/src/core/mount-setup.c +++ b/src/core/mount-setup.c @@ -360,7 +360,6 @@ int mount_setup(bool loaded_policy) { int r = 0; r = mount_points_setup(ELEMENTSOF(mount_table), loaded_policy); - if (r < 0) return r; @@ -391,25 +390,24 @@ int mount_setup(bool loaded_policy) { * udevd. */ dev_setup(NULL, UID_INVALID, GID_INVALID); - /* Mark the root directory as shared in regards to mount - * propagation. The kernel defaults to "private", but we think - * it makes more sense to have a default of "shared" so that - * nspawn and the container tools work out of the box. If - * specific setups need other settings they can reset the - * propagation mode to private if needed. */ + /* Mark the root directory as shared in regards to mount propagation. The kernel defaults to "private", but we + * think it makes more sense to have a default of "shared" so that nspawn and the container tools work out of + * the box. If specific setups need other settings they can reset the propagation mode to private if + * needed. Note that we set this only when we are invoked directly by the kernel. If we are invoked by a + * container manager we assume the container manager knows what it is doing (for example, because it set up + * some directories with different propagation modes). */ if (detect_container() <= 0) if (mount(NULL, "/", NULL, MS_REC|MS_SHARED, NULL) < 0) log_warning_errno(errno, "Failed to set up the root directory for shared mount propagation: %m"); - /* Create a few directories we always want around, Note that - * sd_booted() checks for /run/systemd/system, so this mkdir - * really needs to stay for good, otherwise software that - * copied sd-daemon.c into their sources will misdetect - * systemd. */ + /* Create a few directories we always want around, Note that sd_booted() checks for /run/systemd/system, so + * this mkdir really needs to stay for good, otherwise software that copied sd-daemon.c into their sources will + * misdetect systemd. */ (void) mkdir_label("/run/systemd", 0755); (void) mkdir_label("/run/systemd/system", 0755); - (void) mkdir_label("/run/systemd/inaccessible", 0000); + /* Set up inaccessible items */ + (void) mkdir_label("/run/systemd/inaccessible", 0000); (void) mknod("/run/systemd/inaccessible/reg", S_IFREG | 0000, 0); (void) mkdir_label("/run/systemd/inaccessible/dir", 0000); (void) mknod("/run/systemd/inaccessible/chr", S_IFCHR | 0000, makedev(0, 0)); diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c index 68029865a0..23bf014929 100644 --- a/src/cryptsetup/cryptsetup-generator.c +++ b/src/cryptsetup/cryptsetup-generator.c @@ -102,18 +102,17 @@ static int create_disk( if (!f) return log_error_errno(errno, "Failed to create unit file %s: %m", p); - fputs( - "# Automatically generated by systemd-cryptsetup-generator\n\n" - "[Unit]\n" - "Description=Cryptography Setup for %I\n" - "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n" - "SourcePath=/etc/crypttab\n" - "DefaultDependencies=no\n" - "Conflicts=umount.target\n" - "BindsTo=dev-mapper-%i.device\n" - "IgnoreOnIsolate=true\n" - "After=cryptsetup-pre.target\n", - f); + fputs("# Automatically generated by systemd-cryptsetup-generator\n\n" + "[Unit]\n" + "Description=Cryptography Setup for %I\n" + "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n" + "SourcePath=/etc/crypttab\n" + "DefaultDependencies=no\n" + "Conflicts=umount.target\n" + "BindsTo=dev-mapper-%i.device\n" + "IgnoreOnIsolate=true\n" + "After=cryptsetup-pre.target\n", + f); if (!nofail) fprintf(f, @@ -278,27 +277,30 @@ static crypto_device *get_crypto_device(const char *uuid) { } static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { - int r; - crypto_device *d; _cleanup_free_ char *uuid = NULL, *uuid_value = NULL; + crypto_device *d; + int r; - if (streq(key, "luks") && value) { + if (streq(key, "luks")) { - r = parse_boolean(value); + r = value ? parse_boolean(value) : 1; if (r < 0) - log_warning("Failed to parse luks switch %s. Ignoring.", value); + log_warning("Failed to parse luks= kernel command line switch %s. Ignoring.", value); else arg_enabled = r; - } else if (streq(key, "luks.crypttab") && value) { + } else if (streq(key, "luks.crypttab")) { - r = parse_boolean(value); + r = value ? parse_boolean(value) : 1; if (r < 0) - log_warning("Failed to parse luks crypttab switch %s. Ignoring.", value); + log_warning("Failed to parse luks.crypttab= kernel command line switch %s. Ignoring.", value); else arg_read_crypttab = r; - } else if (streq(key, "luks.uuid") && value) { + } else if (streq(key, "luks.uuid")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; d = get_crypto_device(startswith(value, "luks-") ? value+5 : value); if (!d) @@ -306,7 +308,10 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat d->create = arg_whitelist = true; - } else if (streq(key, "luks.options") && value) { + } else if (streq(key, "luks.options")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value); if (r == 2) { @@ -314,13 +319,14 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat if (!d) return log_oom(); - free(d->options); - d->options = uuid_value; - uuid_value = NULL; + free_and_replace(d->options, uuid_value); } else if (free_and_strdup(&arg_default_options, value) < 0) return log_oom(); - } else if (streq(key, "luks.key") && value) { + } else if (streq(key, "luks.key")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value); if (r == 2) { @@ -328,13 +334,14 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat if (!d) return log_oom(); - free(d->keyfile); - d->keyfile = uuid_value; - uuid_value = NULL; + free_and_replace(d->keyfile, uuid_value); } else if (free_and_strdup(&arg_default_keyfile, value) < 0) return log_oom(); - } else if (streq(key, "luks.name") && value) { + } else if (streq(key, "luks.name")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value); if (r == 2) { @@ -349,7 +356,6 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat uuid_value = NULL; } else log_warning("Failed to parse luks name switch %s. Ignoring.", value); - } return 0; @@ -458,7 +464,7 @@ static int add_proc_cmdline_devices(void) { } int main(int argc, char *argv[]) { - int r = EXIT_FAILURE; + int r; if (argc > 1 && argc != 4) { log_error("This program takes three or no arguments."); @@ -475,32 +481,36 @@ int main(int argc, char *argv[]) { umask(0022); arg_disks = hashmap_new(&string_hash_ops); - if (!arg_disks) - goto cleanup; + if (!arg_disks) { + r = log_oom(); + goto finish; + } - r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, true); + r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX); if (r < 0) { - log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); - r = EXIT_FAILURE; + log_warning_errno(r, "Failed to parse kernel command line: %m"); + goto finish; } if (!arg_enabled) { - r = EXIT_SUCCESS; - goto cleanup; + r = 0; + goto finish; } - if (add_crypttab_devices() < 0) - goto cleanup; + r = add_crypttab_devices(); + if (r < 0) + goto finish; - if (add_proc_cmdline_devices() < 0) - goto cleanup; + r = add_proc_cmdline_devices(); + if (r < 0) + goto finish; - r = EXIT_SUCCESS; + r = 0; -cleanup: +finish: free_arg_disks(); free(arg_default_options); free(arg_default_keyfile); - return r; + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index c7fec609df..bff5664f0f 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -310,7 +310,7 @@ static char *disk_mount_point(const char *label) { if (asprintf(&device, "/dev/mapper/%s", label) < 0) return NULL; - f = setmntent("/etc/fstab", "r"); + f = setmntent("/etc/fstab", "re"); if (!f) return NULL; @@ -593,17 +593,18 @@ static int help(void) { } int main(int argc, char *argv[]) { - int r = EXIT_FAILURE; struct crypt_device *cd = NULL; + int r; if (argc <= 1) { - help(); - return EXIT_SUCCESS; + r = help(); + goto finish; } if (argc < 3) { log_error("This program requires at least two arguments."); - return EXIT_FAILURE; + r = -EINVAL; + goto finish; } log_set_target(LOG_TARGET_AUTO); @@ -614,7 +615,6 @@ int main(int argc, char *argv[]) { if (streq(argv[1], "attach")) { uint32_t flags = 0; - int k; unsigned tries; usec_t until; crypt_status_info status; @@ -648,11 +648,11 @@ int main(int argc, char *argv[]) { if (arg_header) { log_debug("LUKS header: %s", arg_header); - k = crypt_init(&cd, arg_header); + r = crypt_init(&cd, arg_header); } else - k = crypt_init(&cd, argv[3]); - if (k != 0) { - log_error_errno(k, "crypt_init() failed: %m"); + r = crypt_init(&cd, argv[3]); + if (r < 0) { + log_error_errno(r, "crypt_init() failed: %m"); goto finish; } @@ -661,7 +661,7 @@ int main(int argc, char *argv[]) { status = crypt_status(cd, argv[2]); if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) { log_info("Volume %s already active.", argv[2]); - r = EXIT_SUCCESS; + r = 0; goto finish; } @@ -691,29 +691,30 @@ int main(int argc, char *argv[]) { _cleanup_strv_free_erase_ char **passwords = NULL; if (!key_file) { - k = get_password(argv[2], argv[3], until, tries == 0 && !arg_verify, &passwords); - if (k == -EAGAIN) + r = get_password(argv[2], argv[3], until, tries == 0 && !arg_verify, &passwords); + if (r == -EAGAIN) continue; - else if (k < 0) + if (r < 0) goto finish; } if (streq_ptr(arg_type, CRYPT_TCRYPT)) - k = attach_tcrypt(cd, argv[2], key_file, passwords, flags); + r = attach_tcrypt(cd, argv[2], key_file, passwords, flags); else - k = attach_luks_or_plain(cd, + r = attach_luks_or_plain(cd, argv[2], key_file, arg_header ? argv[3] : NULL, passwords, flags); - if (k >= 0) + if (r >= 0) break; - else if (k == -EAGAIN) { + if (r == -EAGAIN) { key_file = NULL; continue; - } else if (k != -EPERM) { - log_error_errno(k, "Failed to activate: %m"); + } + if (r != -EPERM) { + log_error_errno(r, "Failed to activate: %m"); goto finish; } @@ -722,28 +723,28 @@ int main(int argc, char *argv[]) { if (arg_tries != 0 && tries >= arg_tries) { log_error("Too many attempts; giving up."); - r = EXIT_FAILURE; + r = -EPERM; goto finish; } } else if (streq(argv[1], "detach")) { - int k; - k = crypt_init_by_name(&cd, argv[2]); - if (k == -ENODEV) { + r = crypt_init_by_name(&cd, argv[2]); + if (r == -ENODEV) { log_info("Volume %s already inactive.", argv[2]); - r = EXIT_SUCCESS; + r = 0; goto finish; - } else if (k) { - log_error_errno(k, "crypt_init_by_name() failed: %m"); + } + if (r < 0) { + log_error_errno(r, "crypt_init_by_name() failed: %m"); goto finish; } crypt_set_log_callback(cd, log_glue, NULL); - k = crypt_deactivate(cd, argv[2]); - if (k < 0) { - log_error_errno(k, "Failed to deactivate: %m"); + r = crypt_deactivate(cd, argv[2]); + if (r < 0) { + log_error_errno(r, "Failed to deactivate: %m"); goto finish; } @@ -752,10 +753,9 @@ int main(int argc, char *argv[]) { goto finish; } - r = EXIT_SUCCESS; + r = 0; finish: - if (cd) crypt_free(cd); @@ -764,5 +764,5 @@ finish: free(arg_header); strv_free(arg_tcrypt_keyfiles); - return r; + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/debug-generator/debug-generator.c b/src/debug-generator/debug-generator.c index 28ebe36b38..1d8bc71e57 100644 --- a/src/debug-generator/debug-generator.c +++ b/src/debug-generator/debug-generator.c @@ -39,56 +39,53 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat assert(key); if (streq(key, "systemd.mask")) { + char *n; - if (!value) - log_error("Missing argument for systemd.mask= kernel command line parameter."); - else { - char *n; + if (proc_cmdline_value_missing(key, value)) + return 0; - r = unit_name_mangle(value, UNIT_NAME_NOGLOB, &n); - if (r < 0) - return log_error_errno(r, "Failed to glob unit name: %m"); + r = unit_name_mangle(value, UNIT_NAME_NOGLOB, &n); + if (r < 0) + return log_error_errno(r, "Failed to glob unit name: %m"); - r = strv_consume(&arg_mask, n); - if (r < 0) - return log_oom(); - } + r = strv_consume(&arg_mask, n); + if (r < 0) + return log_oom(); } else if (streq(key, "systemd.wants")) { + char *n; - if (!value) - log_error("Missing argument for systemd.want= kernel command line parameter."); - else { - char *n; + if (proc_cmdline_value_missing(key, value)) + return 0; - r = unit_name_mangle(value, UNIT_NAME_NOGLOB, &n); - if (r < 0) - return log_error_errno(r, "Failed to glob unit name: %m"); + r = unit_name_mangle(value, UNIT_NAME_NOGLOB, &n); + if (r < 0) + return log_error_errno(r, "Failed to glob unit name: %m"); - r = strv_consume(&arg_wants, n); - if (r < 0) - return log_oom(); - } + r = strv_consume(&arg_wants, n); + if (r < 0) + return log_oom(); - } else if (streq(key, "systemd.debug-shell")) { + } else if (proc_cmdline_key_streq(key, "systemd.debug_shell")) { if (value) { r = parse_boolean(value); if (r < 0) - log_error("Failed to parse systemd.debug-shell= argument '%s', ignoring.", value); + log_error("Failed to parse systemd.debug_shell= argument '%s', ignoring.", value); else arg_debug_shell = r; } else arg_debug_shell = true; + } else if (streq(key, "systemd.unit")) { - if (!value) - log_error("Missing argument for systemd.unit= kernel command line parameter."); - else { - r = free_and_strdup(&arg_default_unit, value); - if (r < 0) - return log_error_errno(r, "Failed to set default unit %s: %m", value); - } + if (proc_cmdline_value_missing(key, value)) + return 0; + + r = free_and_strdup(&arg_default_unit, value); + if (r < 0) + return log_error_errno(r, "Failed to set default unit %s: %m", value); + } else if (!value) { const char *target; @@ -173,7 +170,7 @@ int main(int argc, char *argv[]) { umask(0022); - r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, false); + r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0); if (r < 0) log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index f2f1e135ec..fd9db5ba87 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -35,7 +35,7 @@ static enum { } arg_action = ACTION_DISSECT; static const char *arg_image = NULL; static const char *arg_path = NULL; -static DissectImageFlags arg_flags = DISSECT_IMAGE_DISCARD_ON_LOOP; +static DissectImageFlags arg_flags = DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP; static void *arg_root_hash = NULL; static size_t arg_root_hash_size = 0; @@ -191,7 +191,7 @@ int main(int argc, char *argv[]) { goto finish; } - r = dissect_image(d->fd, arg_root_hash, arg_root_hash_size, &m); + r = dissect_image(d->fd, arg_root_hash, arg_root_hash_size, arg_flags, &m); if (r == -ENOPKG) { log_error_errno(r, "Couldn't identify a suitable partition table or file system in %s.", arg_image); goto finish; @@ -221,6 +221,9 @@ int main(int argc, char *argv[]) { p->rw ? "writable" : "read-only", partition_designator_to_string(i)); + if (!sd_id128_is_null(p->uuid)) + printf(" (UUID " SD_ID128_FORMAT_STR ")", SD_ID128_FORMAT_VAL(p->uuid)); + if (p->fstype) printf(" of type %s", p->fstype); diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index c9e8e54ee3..fd7051f21e 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -33,6 +33,7 @@ #include "mkdir.h" #include "parse-util.h" #include "path-util.h" +#include "proc-cmdline.h" #include "random-util.h" #include "string-util.h" #include "strv.h" @@ -825,6 +826,7 @@ static int parse_argv(int argc, char *argv[]) { } int main(int argc, char *argv[]) { + bool enabled; int r; r = parse_argv(argc, argv); @@ -837,6 +839,16 @@ int main(int argc, char *argv[]) { umask(0022); + r = proc_cmdline_get_bool("systemd.firstboot", &enabled); + if (r < 0) { + log_error_errno(r, "Failed to parse systemd.firstboot= kernel command line argument, ignoring."); + goto finish; + } + if (r > 0 && !enabled) { + r = 0; /* disabled */ + goto finish; + } + r = process_locale(); if (r < 0) goto finish; diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c index be25c6a2b2..2100681e17 100644 --- a/src/fsck/fsck.c +++ b/src/fsck/fsck.c @@ -99,7 +99,10 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat assert(key); - if (streq(key, "fsck.mode") && value) { + if (streq(key, "fsck.mode")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; if (streq(value, "auto")) arg_force = arg_skip = false; @@ -110,7 +113,10 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat else log_warning("Invalid fsck.mode= parameter '%s'. Ignoring.", value); - } else if (streq(key, "fsck.repair") && value) { + } else if (streq(key, "fsck.repair")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; if (streq(value, "preen")) arg_repair = "-a"; @@ -293,7 +299,7 @@ int main(int argc, char *argv[]) { umask(0022); - r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, true); + r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX); if (r < 0) log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index f6a912ae06..3c601a63e2 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -42,16 +42,20 @@ #include "unit-name.h" #include "util.h" #include "virt.h" +#include "volatile-util.h" static const char *arg_dest = "/tmp"; +static const char *arg_dest_late = "/tmp"; static bool arg_fstab_enabled = true; static char *arg_root_what = NULL; static char *arg_root_fstype = NULL; static char *arg_root_options = NULL; +static char *arg_root_hash = NULL; static int arg_root_rw = -1; static char *arg_usr_what = NULL; static char *arg_usr_fstype = NULL; static char *arg_usr_options = NULL; +static VolatileMode arg_volatile_mode = _VOLATILE_MODE_INVALID; static int add_swap( const char *what, @@ -235,6 +239,7 @@ static int write_requires_mounts_for(FILE *f, const char *opts) { } static int add_mount( + const char *dest, const char *what, const char *where, const char *fstype, @@ -286,7 +291,7 @@ static int add_mount( if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); - unit = strjoin(arg_dest, "/", name); + unit = strjoin(dest, "/", name); if (!unit) return log_oom(); @@ -318,7 +323,7 @@ static int add_mount( } if (passno != 0) { - r = generator_write_fsck_deps(f, arg_dest, what, where, fstype); + r = generator_write_fsck_deps(f, dest, what, where, fstype); if (r < 0) return r; } @@ -334,7 +339,7 @@ static int add_mount( if (!isempty(fstype) && !streq(fstype, "auto")) fprintf(f, "Type=%s\n", fstype); - r = generator_write_timeouts(arg_dest, what, where, opts, &filtered); + r = generator_write_timeouts(dest, what, where, opts, &filtered); if (r < 0) return r; @@ -350,7 +355,7 @@ static int add_mount( return log_error_errno(r, "Failed to write unit file %s: %m", unit); if (!noauto && !automount) { - lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", name); + lnk = strjoin(dest, "/", post, nofail ? ".wants/" : ".requires/", name); if (!lnk) return log_oom(); @@ -364,7 +369,7 @@ static int add_mount( if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); - automount_unit = strjoin(arg_dest, "/", automount_name); + automount_unit = strjoin(dest, "/", automount_name); if (!automount_unit) return log_oom(); @@ -406,7 +411,7 @@ static int add_mount( return log_error_errno(r, "Failed to write unit file %s: %m", automount_unit); free(lnk); - lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name); + lnk = strjoin(dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name); if (!lnk) return log_oom(); @@ -479,7 +484,8 @@ static int parse_fstab(bool initrd) { else post = SPECIAL_LOCAL_FS_TARGET; - k = add_mount(what, + k = add_mount(arg_dest, + what, where, me->mnt_type, me->mnt_opts, @@ -540,7 +546,8 @@ static int add_sysroot_mount(void) { return r; } - return add_mount(what, + return add_mount(arg_dest, + what, "/sysroot", arg_root_fstype, opts, @@ -593,7 +600,8 @@ static int add_sysroot_usr_mount(void) { opts = arg_usr_options; log_debug("Found entry what=%s where=/sysroot/usr type=%s", what, strna(arg_usr_fstype)); - return add_mount(what, + return add_mount(arg_dest, + what, "/sysroot/usr", arg_usr_fstype, opts, @@ -605,6 +613,46 @@ static int add_sysroot_usr_mount(void) { "/proc/cmdline"); } +static int add_volatile_root(void) { + const char *from, *to; + + if (arg_volatile_mode != VOLATILE_YES) + return 0; + + /* Let's add in systemd-remount-volatile.service which will remount the root device to tmpfs if this is + * requested, leaving only /usr from the root mount inside. */ + + from = strjoina(SYSTEM_DATA_UNIT_PATH "/systemd-volatile-root.service"); + to = strjoina(arg_dest, "/" SPECIAL_INITRD_ROOT_FS_TARGET, ".requires/systemd-volatile-root.service"); + + (void) mkdir_parents(to, 0755); + + if (symlink(from, to) < 0) + return log_error_errno(errno, "Failed to hook in volatile remount service: %m"); + + return 0; +} + +static int add_volatile_var(void) { + + if (arg_volatile_mode != VOLATILE_STATE) + return 0; + + /* If requested, mount /var as tmpfs, but do so only if there's nothing else defined for this. */ + + return add_mount(arg_dest_late, + "tmpfs", + "/var", + "tmpfs", + "mode=0755", + 0, + false, + false, + false, + SPECIAL_LOCAL_FS_TARGET, + "/proc/cmdline"); +} + static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { int r; @@ -612,27 +660,36 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat * instance should take precedence. In the case of multiple rootflags= * or usrflags= the arguments should be concatenated */ - if (STR_IN_SET(key, "fstab", "rd.fstab") && value) { + if (STR_IN_SET(key, "fstab", "rd.fstab")) { - r = parse_boolean(value); + r = value ? parse_boolean(value) : 1; if (r < 0) log_warning("Failed to parse fstab switch %s. Ignoring.", value); else arg_fstab_enabled = r; - } else if (streq(key, "root") && value) { + } else if (streq(key, "root")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; if (free_and_strdup(&arg_root_what, value) < 0) return log_oom(); - } else if (streq(key, "rootfstype") && value) { + } else if (streq(key, "rootfstype")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; if (free_and_strdup(&arg_root_fstype, value) < 0) return log_oom(); - } else if (streq(key, "rootflags") && value) { + } else if (streq(key, "rootflags")) { char *o; + if (proc_cmdline_value_missing(key, value)) + return 0; + o = arg_root_options ? strjoin(arg_root_options, ",", value) : strdup(value); @@ -641,20 +698,36 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat free(arg_root_options); arg_root_options = o; + } else if (streq(key, "roothash")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; - } else if (streq(key, "mount.usr") && value) { + if (free_and_strdup(&arg_root_hash, value) < 0) + return log_oom(); + + } else if (streq(key, "mount.usr")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; if (free_and_strdup(&arg_usr_what, value) < 0) return log_oom(); - } else if (streq(key, "mount.usrfstype") && value) { + } else if (streq(key, "mount.usrfstype")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; if (free_and_strdup(&arg_usr_fstype, value) < 0) return log_oom(); - } else if (streq(key, "mount.usrflags") && value) { + } else if (streq(key, "mount.usrflags")) { char *o; + if (proc_cmdline_value_missing(key, value)) + return 0; + o = arg_usr_options ? strjoin(arg_usr_options, ",", value) : strdup(value); @@ -668,10 +741,40 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat arg_root_rw = true; else if (streq(key, "ro") && !value) arg_root_rw = false; + else if (streq(key, "systemd.volatile")) { + VolatileMode m; + + if (value) { + m = volatile_mode_from_string(value); + if (m < 0) + log_warning("Failed to parse systemd.volatile= argument: %s", value); + else + arg_volatile_mode = m; + } else + arg_volatile_mode = VOLATILE_YES; + } return 0; } +static int determine_root(void) { + /* If we have a root hash but no root device then Verity is used, and we use the "root" DM device as root. */ + + if (arg_root_what) + return 0; + + if (!arg_root_hash) + return 0; + + arg_root_what = strdup("/dev/mapper/root"); + if (!arg_root_what) + return log_oom(); + + log_info("Using verity root device %s.", arg_root_what); + + return 1; +} + int main(int argc, char *argv[]) { int r = 0; @@ -682,6 +785,8 @@ int main(int argc, char *argv[]) { if (argc > 1) arg_dest = argv[1]; + if (argc > 3) + arg_dest_late = argv[3]; log_set_target(LOG_TARGET_SAFE); log_parse_environment(); @@ -689,10 +794,12 @@ int main(int argc, char *argv[]) { umask(0022); - r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, false); + r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0); if (r < 0) log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); + (void) determine_root(); + /* Always honour root= and usr= in the kernel command line if we are in an initrd */ if (in_initrd()) { int k; @@ -702,8 +809,12 @@ int main(int argc, char *argv[]) { k = add_sysroot_usr_mount(); if (k < 0) r = k; + + k = add_volatile_root(); + if (k < 0) + r = k; } else - r = 0; + r = add_volatile_var(); /* Honour /etc/fstab only when that's enabled */ if (arg_fstab_enabled) { @@ -729,6 +840,7 @@ int main(int argc, char *argv[]) { free(arg_root_what); free(arg_root_fstype); free(arg_root_options); + free(arg_root_hash); free(arg_usr_what); free(arg_usr_fstype); diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index 0f95f0d813..e61ef8f249 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -29,6 +29,7 @@ #include "blkid-util.h" #include "btrfs-util.h" #include "dirent-util.h" +#include "dissect-image.h" #include "efivars.h" #include "fd-util.h" #include "fileio.h" @@ -54,7 +55,7 @@ static bool arg_enabled = true; static bool arg_root_enabled = true; static bool arg_root_rw = false; -static int add_cryptsetup(const char *id, const char *what, bool rw, char **device) { +static int add_cryptsetup(const char *id, const char *what, bool rw, bool require, char **device) { _cleanup_free_ char *e = NULL, *n = NULL, *p = NULL, *d = NULL, *to = NULL; _cleanup_fclose_ FILE *f = NULL; char *from, *ret; @@ -62,7 +63,6 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, char **devi assert(id); assert(what); - assert(device); r = unit_name_from_path(what, ".device", &d); if (r < 0) @@ -119,23 +119,26 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, char **devi if (symlink(from, to) < 0) return log_error_errno(errno, "Failed to create symlink %s: %m", to); - free(to); - to = strjoin(arg_dest, "/cryptsetup.target.requires/", n); - if (!to) - return log_oom(); + if (require) { + free(to); - mkdir_parents_label(to, 0755); - if (symlink(from, to) < 0) - return log_error_errno(errno, "Failed to create symlink %s: %m", to); + to = strjoin(arg_dest, "/cryptsetup.target.requires/", n); + if (!to) + return log_oom(); - free(to); - to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n); - if (!to) - return log_oom(); + mkdir_parents_label(to, 0755); + if (symlink(from, to) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", to); - mkdir_parents_label(to, 0755); - if (symlink(from, to) < 0) - return log_error_errno(errno, "Failed to create symlink %s: %m", to); + free(to); + to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n); + if (!to) + return log_oom(); + + mkdir_parents_label(to, 0755); + if (symlink(from, to) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", to); + } free(p); p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf"); @@ -155,7 +158,8 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, char **devi if (!ret) return log_oom(); - *device = ret; + if (device) + *device = ret; return 0; } @@ -182,7 +186,7 @@ static int add_mount( if (streq_ptr(fstype, "crypto_LUKS")) { - r = add_cryptsetup(id, what, rw, &crypto_what); + r = add_cryptsetup(id, what, rw, true, &crypto_what); if (r < 0) return r; @@ -270,61 +274,28 @@ static bool path_is_busy(const char *where) { return false; } -static int probe_and_add_mount( +static int add_partition_mount( + DissectedPartition *p, const char *id, - const char *what, const char *where, - bool rw, - const char *description, - const char *post) { + const char *description) { - _cleanup_blkid_free_probe_ blkid_probe b = NULL; - const char *fstype = NULL; - int r; - - assert(id); - assert(what); - assert(where); - assert(description); + assert(p); if (path_is_busy(where)) { log_debug("%s already populated, ignoring.", where); return 0; } - /* Let's check the partition type here, so that we know - * whether to do LUKS magic. */ - - errno = 0; - b = blkid_new_probe_from_filename(what); - if (!b) { - if (errno == 0) - return log_oom(); - return log_error_errno(errno, "Failed to allocate prober: %m"); - } - - blkid_probe_enable_superblocks(b, 1); - blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE); - - errno = 0; - r = blkid_do_safeprobe(b); - if (r == -2 || r == 1) /* no result or uncertain */ - return 0; - else if (r != 0) - return log_error_errno(errno ?: EIO, "Failed to probe %s: %m", what); - - /* add_mount is OK with fstype being NULL. */ - (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL); - return add_mount( id, - what, + p->node, where, - fstype, - rw, + p->fstype, + p->rw, NULL, description, - post); + SPECIAL_LOCAL_FS_TARGET); } static int add_swap(const char *path) { @@ -449,22 +420,17 @@ static int add_automount( return 0; } -static int add_boot(const char *what) { +static int add_esp(DissectedPartition *p) { const char *esp; int r; - assert(what); + assert(p); if (in_initrd()) { log_debug("In initrd, ignoring the ESP."); return 0; } - if (detect_container() > 0) { - log_debug("In a container, ignoring the ESP."); - return 0; - } - /* If /efi exists we'll use that. Otherwise we'll use /boot, as that's usually the better choice */ esp = access("/efi/", F_OK) >= 0 ? "/efi" : "/boot"; @@ -480,9 +446,7 @@ static int add_boot(const char *what) { } if (is_efi_boot()) { - _cleanup_blkid_free_probe_ blkid_probe b = NULL; - const char *fstype = NULL, *uuid_string = NULL; - sd_id128_t loader_uuid, part_uuid; + sd_id128_t loader_uuid; /* If this is an EFI boot, be extra careful, and only mount the ESP if it was the ESP used for booting. */ @@ -494,43 +458,7 @@ static int add_boot(const char *what) { if (r < 0) return log_error_errno(r, "Failed to read ESP partition UUID: %m"); - errno = 0; - b = blkid_new_probe_from_filename(what); - if (!b) { - if (errno == 0) - return log_oom(); - return log_error_errno(errno, "Failed to allocate prober: %m"); - } - - blkid_probe_enable_partitions(b, 1); - blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); - - errno = 0; - r = blkid_do_safeprobe(b); - if (r == -2 || r == 1) /* no result or uncertain */ - return 0; - else if (r != 0) - return log_error_errno(errno ?: EIO, "Failed to probe %s: %m", what); - - (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL); - if (!streq_ptr(fstype, "vfat")) { - log_debug("Partition for %s is not a FAT filesystem, ignoring.", esp); - return 0; - } - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &uuid_string, NULL); - if (r != 0) { - log_debug_errno(errno, "Partition for %s does not have a UUID, ignoring.", esp); - return 0; - } - - if (sd_id128_from_string(uuid_string, &part_uuid) < 0) { - log_debug("Partition for %s does not have a valid UUID, ignoring.", esp); - return 0; - } - - if (!sd_id128_equal(part_uuid, loader_uuid)) { + if (!sd_id128_equal(p->uuid, loader_uuid)) { log_debug("Partition for %s does not appear to be the partition we are booted from.", esp); return 0; } @@ -538,35 +466,29 @@ static int add_boot(const char *what) { log_debug("Not an EFI boot, skipping ESP check."); return add_automount("boot", - what, - esp, - "vfat", - true, - "umask=0077", - "EFI System Partition Automount", - 120 * USEC_PER_SEC); + p->node, + esp, + p->fstype, + true, + "umask=0077", + "EFI System Partition Automount", + 120 * USEC_PER_SEC); } #else -static int add_boot(const char *what) { +static int add_esp(const char *what) { return 0; } #endif -static int enumerate_partitions(dev_t devnum) { - - _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; +static int open_parent(dev_t devnum, int *ret) { _cleanup_udev_device_unref_ struct udev_device *d = NULL; - _cleanup_blkid_free_probe_ blkid_probe b = NULL; _cleanup_udev_unref_ struct udev *udev = NULL; - _cleanup_free_ char *boot = NULL, *home = NULL, *srv = NULL; - struct udev_list_entry *first, *item; - struct udev_device *parent = NULL; - const char *name, *node, *pttype, *devtype; - int boot_nr = -1, home_nr = -1, srv_nr = -1; - bool home_rw = true, srv_rw = true; - blkid_partlist pl; - int r, k; + const char *name, *devtype, *node; + struct udev_device *parent; dev_t pn; + int fd; + + assert(ret); udev = udev_new(); if (!udev) @@ -580,228 +502,94 @@ static int enumerate_partitions(dev_t devnum) { if (!name) name = udev_device_get_syspath(d); if (!name) { - log_debug("Device %u:%u does not have a name, ignoring.", - major(devnum), minor(devnum)); - return 0; + log_debug("Device %u:%u does not have a name, ignoring.", major(devnum), minor(devnum)); + goto not_found; } parent = udev_device_get_parent(d); if (!parent) { log_debug("%s: not a partitioned device, ignoring.", name); - return 0; + goto not_found; } /* Does it have a devtype? */ devtype = udev_device_get_devtype(parent); if (!devtype) { log_debug("%s: parent doesn't have a device type, ignoring.", name); - return 0; + goto not_found; } /* Is this a disk or a partition? We only care for disks... */ if (!streq(devtype, "disk")) { log_debug("%s: parent isn't a raw disk, ignoring.", name); - return 0; + goto not_found; } /* Does it have a device node? */ node = udev_device_get_devnode(parent); if (!node) { log_debug("%s: parent device does not have device node, ignoring.", name); - return 0; + goto not_found; } log_debug("%s: root device %s.", name, node); pn = udev_device_get_devnum(parent); - if (major(pn) == 0) - return 0; - - errno = 0; - b = blkid_new_probe_from_filename(node); - if (!b) { - if (errno == 0) - return log_oom(); - - return log_error_errno(errno, "%s: failed to allocate prober: %m", node); + if (major(pn) == 0) { + log_debug("%s: parent device is not a proper block device, ignoring.", name); + goto not_found; } - blkid_probe_enable_partitions(b, 1); - blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); + fd = open(node, O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + return log_error_errno(errno, "Failed to open %s: %m", node); - errno = 0; - r = blkid_do_safeprobe(b); - if (r == 1) - return 0; /* no results */ - else if (r == -2) { - log_warning("%s: probe gave ambiguous results, ignoring.", node); - return 0; - } else if (r != 0) - return log_error_errno(errno ?: EIO, "%s: failed to probe: %m", node); + *ret = fd; + return 1; - errno = 0; - r = blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL); - if (r != 0) { - if (errno == 0) - return 0; /* No partition table found. */ +not_found: + *ret = -1; + return 0; +} - return log_error_errno(errno, "%s: failed to determine partition table type: %m", node); - } +static int enumerate_partitions(dev_t devnum) { - /* We only do this all for GPT... */ - if (!streq_ptr(pttype, "gpt")) { - log_debug("%s: not a GPT partition table, ignoring.", node); - return 0; - } + _cleanup_close_ int fd = -1; + _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL; + int r, k; - errno = 0; - pl = blkid_probe_get_partitions(b); - if (!pl) { - if (errno == 0) - return log_oom(); + r = open_parent(devnum, &fd); + if (r <= 0) + return r; - return log_error_errno(errno, "%s: failed to list partitions: %m", node); + r = dissect_image(fd, NULL, 0, DISSECT_IMAGE_GPT_ONLY, &m); + if (r == -ENOPKG) { + log_debug_errno(r, "No suitable partition table found, ignoring."); + return 0; } - - e = udev_enumerate_new(udev); - if (!e) - return log_oom(); - - r = udev_enumerate_add_match_parent(e, parent); - if (r < 0) - return log_oom(); - - r = udev_enumerate_add_match_subsystem(e, "block"); - if (r < 0) - return log_oom(); - - r = udev_enumerate_scan_devices(e); if (r < 0) - return log_error_errno(r, "%s: failed to enumerate partitions: %m", node); - - first = udev_enumerate_get_list_entry(e); - udev_list_entry_foreach(item, first) { - _cleanup_udev_device_unref_ struct udev_device *q; - unsigned long long flags; - const char *stype, *subnode; - sd_id128_t type_id; - blkid_partition pp; - dev_t qn; - int nr; - - q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)); - if (!q) - continue; - - qn = udev_device_get_devnum(q); - if (major(qn) == 0) - continue; - - if (qn == devnum) - continue; - - if (qn == pn) - continue; - - subnode = udev_device_get_devnode(q); - if (!subnode) - continue; - - pp = blkid_partlist_devno_to_partition(pl, qn); - if (!pp) - continue; - - nr = blkid_partition_get_partno(pp); - if (nr < 0) - continue; - - stype = blkid_partition_get_type_string(pp); - if (!stype) - continue; - - if (sd_id128_from_string(stype, &type_id) < 0) - continue; - - flags = blkid_partition_get_flags(pp); - - if (sd_id128_equal(type_id, GPT_SWAP)) { - - if (flags & GPT_FLAG_NO_AUTO) - continue; - - if (flags & GPT_FLAG_READ_ONLY) { - log_debug("%s marked as read-only swap partition, which is bogus. Ignoring.", subnode); - continue; - } - - k = add_swap(subnode); - if (k < 0) - r = k; - - } else if (sd_id128_equal(type_id, GPT_ESP)) { - - /* We only care for the first /boot partition */ - if (boot && nr >= boot_nr) - continue; - - /* Note that we do not honour the "no-auto" - * flag for the ESP, as it is often unset, to - * hide it from Windows. */ - - boot_nr = nr; - - r = free_and_strdup(&boot, subnode); - if (r < 0) - return log_oom(); - - } else if (sd_id128_equal(type_id, GPT_HOME)) { + return log_error_errno(r, "Failed to dissect: %m"); - if (flags & GPT_FLAG_NO_AUTO) - continue; - - /* We only care for the first /home partition */ - if (home && nr >= home_nr) - continue; - - home_nr = nr; - home_rw = !(flags & GPT_FLAG_READ_ONLY), - - r = free_and_strdup(&home, subnode); - if (r < 0) - return log_oom(); - - } else if (sd_id128_equal(type_id, GPT_SRV)) { - - if (flags & GPT_FLAG_NO_AUTO) - continue; - - /* We only care for the first /srv partition */ - if (srv && nr >= srv_nr) - continue; - - srv_nr = nr; - srv_rw = !(flags & GPT_FLAG_READ_ONLY), - - r = free_and_strdup(&srv, subnode); - if (r < 0) - return log_oom(); - } + if (m->partitions[PARTITION_SWAP].found) { + k = add_swap(m->partitions[PARTITION_SWAP].node); + if (k < 0) + r = k; } - if (boot) { - k = add_boot(boot); + if (m->partitions[PARTITION_ESP].found) { + k = add_esp(m->partitions + PARTITION_ESP); if (k < 0) r = k; } - if (home) { - k = probe_and_add_mount("home", home, "/home", home_rw, "Home Partition", SPECIAL_LOCAL_FS_TARGET); + if (m->partitions[PARTITION_HOME].found) { + k = add_partition_mount(m->partitions + PARTITION_HOME, "home", "/home", "Home Partition"); if (k < 0) r = k; } - if (srv) { - k = probe_and_add_mount("srv", srv, "/srv", srv_rw, "Server Data Partition", SPECIAL_LOCAL_FS_TARGET); + if (m->partitions[PARTITION_SRV].found) { + k = add_partition_mount(m->partitions + PARTITION_SRV, "srv", "/srv", "Server Data Partition"); if (k < 0) r = k; } @@ -876,8 +664,40 @@ static int get_block_device_harder(const char *path, dev_t *dev) { if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN)) continue; - if (found) /* Don't try to support multiple backing block devices */ - goto fallback; + if (found) { + _cleanup_free_ char *u = NULL, *v = NULL, *a = NULL, *b = NULL; + + /* We found a device backed by multiple other devices. We don't really support automatic + * discovery on such setups, with the exception of dm-verity partitions. In this case there are + * two backing devices: the data partition and the hash partition. We are fine with such + * setups, however, only if both partitions are on the same physical device. Hence, let's + * verify this. */ + + u = strjoin(p, "/", de->d_name, "/../dev"); + if (!u) + return -ENOMEM; + + v = strjoin(p, "/", found->d_name, "/../dev"); + if (!v) + return -ENOMEM; + + r = read_one_line_file(u, &a); + if (r < 0) { + log_debug_errno(r, "Failed to read %s: %m", u); + goto fallback; + } + + r = read_one_line_file(v, &b); + if (r < 0) { + log_debug_errno(r, "Failed to read %s: %m", v); + goto fallback; + } + + /* Check if the parent device is the same. If not, then the two backing devices are on + * different physical devices, and we don't support that. */ + if (!streq(a, b)) + goto fallback; + } found = de; } @@ -912,21 +732,33 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat assert(key); - if (STR_IN_SET(key, "systemd.gpt_auto", "rd.systemd.gpt_auto") && value) { + if (STR_IN_SET(key, "systemd.gpt_auto", "rd.systemd.gpt_auto")) { - r = parse_boolean(value); + r = value ? parse_boolean(value) : 1; if (r < 0) log_warning("Failed to parse gpt-auto switch \"%s\". Ignoring.", value); else arg_enabled = r; - } else if (streq(key, "root") && value) { + } else if (streq(key, "root")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; /* Disable root disk logic if there's a root= value * specified (unless it happens to be "gpt-auto") */ arg_root_enabled = streq(value, "gpt-auto"); + } else if (streq(key, "roothash")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + + /* Disable root disk logic if there's roothash= defined (i.e. verity enabled) */ + + arg_root_enabled = false; + } else if (streq(key, "rw") && !value) arg_root_rw = true; else if (streq(key, "ro") && !value) @@ -935,6 +767,16 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat return 0; } +#ifdef ENABLE_EFI +static int add_root_cryptsetup(void) { + + /* If a device /dev/gpt-auto-root-luks appears, then make it pull in systemd-cryptsetup-root.service, which + * sets it up, and causes /dev/gpt-auto-root to appear which is all we are looking for. */ + + return add_cryptsetup("root", "/dev/gpt-auto-root-luks", true, false, NULL); +} +#endif + static int add_root_mount(void) { #ifdef ENABLE_EFI @@ -960,6 +802,10 @@ static int add_root_mount(void) { r = generator_write_initrd_root_device_deps(arg_dest, "/dev/gpt-auto-root"); if (r < 0) return 0; + + r = add_root_cryptsetup(); + if (r < 0) + return r; } return add_mount( @@ -983,11 +829,11 @@ static int add_mounts(void) { r = get_block_device_harder("/", &devno); if (r < 0) return log_error_errno(r, "Failed to determine block device of root file system: %m"); - else if (r == 0) { + if (r == 0) { r = get_block_device_harder("/usr", &devno); if (r < 0) return log_error_errno(r, "Failed to determine block device of /usr file system: %m"); - else if (r == 0) { + if (r == 0) { log_debug("Neither root nor /usr file system are on a (single) block device."); return 0; } @@ -997,7 +843,7 @@ static int add_mounts(void) { } int main(int argc, char *argv[]) { - int r = 0; + int r = 0, k; if (argc > 1 && argc != 4) { log_error("This program takes three or no arguments."); @@ -1018,7 +864,7 @@ int main(int argc, char *argv[]) { return EXIT_SUCCESS; } - r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, false); + r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0); if (r < 0) log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); @@ -1031,8 +877,6 @@ int main(int argc, char *argv[]) { r = add_root_mount(); if (!in_initrd()) { - int k; - k = add_mounts(); if (k < 0) r = k; diff --git a/src/hibernate-resume/hibernate-resume-generator.c b/src/hibernate-resume/hibernate-resume-generator.c index 9f59c04b26..a97fe668d5 100644 --- a/src/hibernate-resume/hibernate-resume-generator.c +++ b/src/hibernate-resume/hibernate-resume-generator.c @@ -31,15 +31,22 @@ #include "util.h" static const char *arg_dest = "/tmp"; -static char *arg_resume_dev = NULL; +static char *arg_resume_device = NULL; static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { - if (streq(key, "resume") && value) { - free(arg_resume_dev); - arg_resume_dev = fstab_node_to_udev_node(value); - if (!arg_resume_dev) + if (streq(key, "resume")) { + char *s; + + if (proc_cmdline_value_missing(key, value)) + return 0; + + s = fstab_node_to_udev_node(value); + if (!s) return log_oom(); + + free(arg_resume_device); + arg_resume_device = s; } return 0; @@ -49,10 +56,10 @@ static int process_resume(void) { _cleanup_free_ char *name = NULL, *lnk = NULL; int r; - if (!arg_resume_dev) + if (!arg_resume_device) return 0; - r = unit_name_from_path_instance("systemd-hibernate-resume", arg_resume_dev, ".service", &name); + r = unit_name_from_path_instance("systemd-hibernate-resume", arg_resume_device, ".service", &name); if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); @@ -88,12 +95,12 @@ int main(int argc, char *argv[]) { if (!in_initrd()) return EXIT_SUCCESS; - r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, false); + r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0); if (r < 0) log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); r = process_resume(); - free(arg_resume_dev); + free(arg_resume_device); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/import/pull-common.c b/src/import/pull-common.c index 5ddc0c56f4..62a9195cc4 100644 --- a/src/import/pull-common.c +++ b/src/import/pull-common.c @@ -218,37 +218,40 @@ int pull_make_path(const char *url, const char *etag, const char *image_root, co return 0; } -int pull_make_settings_job( +int pull_make_auxiliary_job( PullJob **ret, const char *url, + int (*strip_suffixes)(const char *name, char **ret), + const char *suffix, CurlGlue *glue, PullJobFinished on_finished, void *userdata) { - _cleanup_free_ char *last_component = NULL, *ll = NULL, *settings_url = NULL; + _cleanup_free_ char *last_component = NULL, *ll = NULL, *auxiliary_url = NULL; _cleanup_(pull_job_unrefp) PullJob *job = NULL; const char *q; int r; assert(ret); assert(url); + assert(strip_suffixes); assert(glue); r = import_url_last_component(url, &last_component); if (r < 0) return r; - r = tar_strip_suffixes(last_component, &ll); + r = strip_suffixes(last_component, &ll); if (r < 0) return r; - q = strjoina(ll, ".nspawn"); + q = strjoina(ll, suffix); - r = import_url_change_last_component(url, q, &settings_url); + r = import_url_change_last_component(url, q, &auxiliary_url); if (r < 0) return r; - r = pull_job_new(&job, settings_url, glue, userdata); + r = pull_job_new(&job, auxiliary_url, glue, userdata); if (r < 0) return r; @@ -320,45 +323,39 @@ int pull_make_verification_jobs( return 0; } -int pull_verify(PullJob *main_job, - PullJob *settings_job, - PullJob *checksum_job, - PullJob *signature_job) { - - _cleanup_close_pair_ int gpg_pipe[2] = { -1, -1 }; +static int verify_one(PullJob *checksum_job, PullJob *job) { _cleanup_free_ char *fn = NULL; - _cleanup_close_ int sig_file = -1; - const char *p, *line; - char sig_file_path[] = "/tmp/sigXXXXXX", gpg_home[] = "/tmp/gpghomeXXXXXX"; - _cleanup_(sigkill_waitp) pid_t pid = 0; - bool gpg_home_created = false; + const char *line, *p; int r; - assert(main_job); - assert(main_job->state == PULL_JOB_DONE); + assert(checksum_job); - if (!checksum_job) + if (!job) return 0; - assert(main_job->calc_checksum); - assert(main_job->checksum); - assert(checksum_job->state == PULL_JOB_DONE); + assert(IN_SET(job->state, PULL_JOB_DONE, PULL_JOB_FAILED)); - if (!checksum_job->payload || checksum_job->payload_size <= 0) { - log_error("Checksum is empty, cannot verify."); - return -EBADMSG; - } + /* Don't verify the checksum if we didn't actually successfully download something new */ + if (job->state != PULL_JOB_DONE) + return 0; + if (job->error != 0) + return 0; + if (job->etag_exists) + return 0; - r = import_url_last_component(main_job->url, &fn); + assert(job->calc_checksum); + assert(job->checksum); + + r = import_url_last_component(job->url, &fn); if (r < 0) return log_oom(); if (!filename_is_valid(fn)) { - log_error("Cannot verify checksum, could not determine valid server-side file name."); + log_error("Cannot verify checksum, could not determine server-side file name."); return -EBADMSG; } - line = strjoina(main_job->checksum, " *", fn, "\n"); + line = strjoina(job->checksum, " *", fn, "\n"); p = memmem(checksum_job->payload, checksum_job->payload_size, @@ -366,47 +363,55 @@ int pull_verify(PullJob *main_job, strlen(line)); if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) { - log_error("DOWNLOAD INVALID: Checksum did not check out, payload has been tampered with."); + log_error("DOWNLOAD INVALID: Checksum of %s file did not checkout, file has been tampered with.", fn); return -EBADMSG; } - log_info("SHA256 checksum of %s is valid.", main_job->url); + log_info("SHA256 checksum of %s is valid.", job->url); + return 1; +} - assert(!settings_job || IN_SET(settings_job->state, PULL_JOB_DONE, PULL_JOB_FAILED)); +int pull_verify(PullJob *main_job, + PullJob *roothash_job, + PullJob *settings_job, + PullJob *checksum_job, + PullJob *signature_job) { - if (settings_job && - settings_job->state == PULL_JOB_DONE && - settings_job->error == 0 && - !settings_job->etag_exists) { + _cleanup_close_pair_ int gpg_pipe[2] = { -1, -1 }; + _cleanup_free_ char *fn = NULL; + _cleanup_close_ int sig_file = -1; + char sig_file_path[] = "/tmp/sigXXXXXX", gpg_home[] = "/tmp/gpghomeXXXXXX"; + _cleanup_(sigkill_waitp) pid_t pid = 0; + bool gpg_home_created = false; + int r; - _cleanup_free_ char *settings_fn = NULL; + assert(main_job); + assert(main_job->state == PULL_JOB_DONE); - assert(settings_job->calc_checksum); - assert(settings_job->checksum); + if (!checksum_job) + return 0; - r = import_url_last_component(settings_job->url, &settings_fn); - if (r < 0) - return log_oom(); + assert(main_job->calc_checksum); + assert(main_job->checksum); - if (!filename_is_valid(settings_fn)) { - log_error("Cannot verify checksum, could not determine server-side settings file name."); - return -EBADMSG; - } + assert(checksum_job->state == PULL_JOB_DONE); - line = strjoina(settings_job->checksum, " *", settings_fn, "\n"); + if (!checksum_job->payload || checksum_job->payload_size <= 0) { + log_error("Checksum is empty, cannot verify."); + return -EBADMSG; + } - p = memmem(checksum_job->payload, - checksum_job->payload_size, - line, - strlen(line)); + r = verify_one(checksum_job, main_job); + if (r < 0) + return r; - if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) { - log_error("DOWNLOAD INVALID: Checksum of settings file did not checkout, settings file has been tampered with."); - return -EBADMSG; - } + r = verify_one(checksum_job, roothash_job); + if (r < 0) + return r; - log_info("SHA256 checksum of %s is valid.", settings_job->url); - } + r = verify_one(checksum_job, settings_job); + if (r < 0) + return r; if (!signature_job) return 0; diff --git a/src/import/pull-common.h b/src/import/pull-common.h index 929a131c88..f1f1a17fa9 100644 --- a/src/import/pull-common.h +++ b/src/import/pull-common.h @@ -30,7 +30,7 @@ int pull_find_old_etags(const char *url, const char *root, int dt, const char *p int pull_make_path(const char *url, const char *etag, const char *image_root, const char *prefix, const char *suffix, char **ret); -int pull_make_settings_job(PullJob **ret, const char *url, CurlGlue *glue, PullJobFinished on_finished, void *userdata); +int pull_make_auxiliary_job(PullJob **ret, const char *url, int (*strip_suffixes)(const char *name, char **ret), const char *suffix, CurlGlue *glue, PullJobFinished on_finished, void *userdata); int pull_make_verification_jobs(PullJob **ret_checksum_job, PullJob **ret_signature_job, ImportVerify verify, const char *url, CurlGlue *glue, PullJobFinished on_finished, void *userdata); -int pull_verify(PullJob *main_job, PullJob *settings_job, PullJob *checksum_job, PullJob *signature_job); +int pull_verify(PullJob *main_job, PullJob *roothash_job, PullJob *settings_job, PullJob *checksum_job, PullJob *signature_job); diff --git a/src/import/pull-raw.c b/src/import/pull-raw.c index 0cf410a5d9..ef7fb6ac42 100644 --- a/src/import/pull-raw.c +++ b/src/import/pull-raw.c @@ -63,6 +63,7 @@ struct RawPull { char *image_root; PullJob *raw_job; + PullJob *roothash_job; PullJob *settings_job; PullJob *checksum_job; PullJob *signature_job; @@ -74,6 +75,7 @@ struct RawPull { bool force_local; bool grow_machine_directory; bool settings; + bool roothash; char *final_path; char *temp_path; @@ -81,6 +83,9 @@ struct RawPull { char *settings_path; char *settings_temp_path; + char *roothash_path; + char *roothash_temp_path; + ImportVerify verify; }; @@ -90,6 +95,7 @@ RawPull* raw_pull_unref(RawPull *i) { pull_job_unref(i->raw_job); pull_job_unref(i->settings_job); + pull_job_unref(i->roothash_job); pull_job_unref(i->checksum_job); pull_job_unref(i->signature_job); @@ -101,12 +107,18 @@ RawPull* raw_pull_unref(RawPull *i) { free(i->temp_path); } + if (i->roothash_temp_path) { + (void) unlink(i->roothash_temp_path); + free(i->roothash_temp_path); + } + if (i->settings_temp_path) { (void) unlink(i->settings_temp_path); free(i->settings_temp_path); } free(i->final_path); + free(i->roothash_path); free(i->settings_path); free(i->image_root); free(i->local); @@ -176,6 +188,11 @@ static void raw_pull_report_progress(RawPull *i, RawProgress p) { remain -= 5; } + if (i->roothash_job) { + percent += i->roothash_job->progress_percent * 5 / 100; + remain -= 5; + } + if (i->checksum_job) { percent += i->checksum_job->progress_percent * 5 / 100; remain -= 5; @@ -262,6 +279,55 @@ static int raw_pull_maybe_convert_qcow2(RawPull *i) { return 1; } +static int raw_pull_determine_path(RawPull *i, const char *suffix, char **field) { + int r; + + assert(i); + assert(field); + + if (*field) + return 0; + + assert(i->raw_job); + + r = pull_make_path(i->raw_job->url, i->raw_job->etag, i->image_root, ".raw-", suffix, field); + if (r < 0) + return log_oom(); + + return 1; +} + +static int raw_pull_copy_auxiliary_file( + RawPull *i, + const char *suffix, + char **path) { + + const char *local; + int r; + + assert(i); + assert(suffix); + assert(path); + + r = raw_pull_determine_path(i, suffix, path); + if (r < 0) + return r; + + local = strjoina(i->image_root, "/", i->local, suffix); + + r = copy_file_atomic(*path, local, 0644, i->force_local, 0); + if (r == -EEXIST) + log_warning_errno(r, "File %s already exists, not replacing.", local); + else if (r == -ENOENT) + log_debug_errno(r, "Skipping creation of auxiliary file, since none was found."); + else if (r < 0) + log_warning_errno(r, "Failed to copy file %s, ignoring: %m", local); + else + log_info("Created new file %s.", local); + + return 0; +} + static int raw_pull_make_local_copy(RawPull *i) { _cleanup_free_ char *tp = NULL; _cleanup_close_ int dfd = -1; @@ -274,12 +340,6 @@ static int raw_pull_make_local_copy(RawPull *i) { if (!i->local) return 0; - if (!i->final_path) { - r = pull_make_path(i->raw_job->url, i->raw_job->etag, i->image_root, ".raw-", ".raw", &i->final_path); - if (r < 0) - return log_oom(); - } - if (i->raw_job->etag_exists) { /* We have downloaded this one previously, reopen it */ @@ -338,27 +398,16 @@ static int raw_pull_make_local_copy(RawPull *i) { log_info("Created new local image '%s'.", i->local); - if (i->settings) { - const char *local_settings; - assert(i->settings_job); - - if (!i->settings_path) { - r = pull_make_path(i->settings_job->url, i->settings_job->etag, i->image_root, ".settings-", NULL, &i->settings_path); - if (r < 0) - return log_oom(); - } - - local_settings = strjoina(i->image_root, "/", i->local, ".nspawn"); + if (i->roothash) { + r = raw_pull_copy_auxiliary_file(i, ".roothash", &i->roothash_path); + if (r < 0) + return r; + } - r = copy_file_atomic(i->settings_path, local_settings, 0644, i->force_local, 0); - if (r == -EEXIST) - log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings); - else if (r == -ENOENT) - log_debug_errno(r, "Skipping creation of settings file, since none was found."); - else if (r < 0) - log_warning_errno(r, "Failed to copy settings files %s, ignoring: %m", local_settings); - else - log_info("Created new settings file %s.", local_settings); + if (i->settings) { + r = raw_pull_copy_auxiliary_file(i, ".nspawn", &i->settings_path); + if (r < 0) + return r; } return 0; @@ -370,6 +419,8 @@ static bool raw_pull_is_done(RawPull *i) { if (!PULL_JOB_IS_COMPLETE(i->raw_job)) return false; + if (i->roothash_job && !PULL_JOB_IS_COMPLETE(i->roothash_job)) + return false; if (i->settings_job && !PULL_JOB_IS_COMPLETE(i->settings_job)) return false; if (i->checksum_job && !PULL_JOB_IS_COMPLETE(i->checksum_job)) @@ -380,6 +431,39 @@ static bool raw_pull_is_done(RawPull *i) { return true; } +static int raw_pull_rename_auxiliary_file( + RawPull *i, + const char *suffix, + char **temp_path, + char **path) { + + int r; + + assert(i); + assert(temp_path); + assert(suffix); + assert(path); + + /* Regenerate final name for this auxiliary file, we might know the etag of the raw file now, and we shoud + * incorporate it in the file name if we can */ + *path = mfree(*path); + r = raw_pull_determine_path(i, suffix, path); + if (r < 0) + return r; + + r = import_make_read_only(*temp_path); + if (r < 0) + return r; + + r = rename_noreplace(AT_FDCWD, *temp_path, AT_FDCWD, *path); + if (r < 0) + return log_error_errno(r, "Failed to rename file %s to %s: %m", *temp_path, *path); + + *temp_path = mfree(*temp_path); + + return 1; +} + static void raw_pull_job_on_finished(PullJob *j) { RawPull *i; int r; @@ -388,7 +472,10 @@ static void raw_pull_job_on_finished(PullJob *j) { assert(j->userdata); i = j->userdata; - if (j == i->settings_job) { + if (j == i->roothash_job) { + if (j->error != 0) + log_info_errno(j->error, "Root hash file could not be retrieved, proceeding without."); + } else if (j == i->settings_job) { if (j->error != 0) log_info_errno(j->error, "Settings file could not be retrieved, proceeding without."); } else if (j->error != 0) { @@ -413,16 +500,22 @@ static void raw_pull_job_on_finished(PullJob *j) { if (!raw_pull_is_done(i)) return; + if (i->roothash_job) + i->roothash_job->disk_fd = safe_close(i->roothash_job->disk_fd); if (i->settings_job) i->settings_job->disk_fd = safe_close(i->settings_job->disk_fd); + r = raw_pull_determine_path(i, ".raw", &i->final_path); + if (r < 0) + goto finish; + if (!i->raw_job->etag_exists) { /* This is a new download, verify it, and move it into place */ assert(i->raw_job->disk_fd >= 0); raw_pull_report_progress(i, RAW_VERIFYING); - r = pull_verify(i->raw_job, i->settings_job, i->checksum_job, i->signature_job); + r = pull_verify(i->raw_job, i->roothash_job, i->settings_job, i->checksum_job, i->signature_job); if (r < 0) goto finish; @@ -446,24 +539,18 @@ static void raw_pull_job_on_finished(PullJob *j) { i->temp_path = mfree(i->temp_path); - if (i->settings_job && - i->settings_job->error == 0 && - !i->settings_job->etag_exists) { - - assert(i->settings_temp_path); - assert(i->settings_path); - - r = import_make_read_only(i->settings_temp_path); + if (i->roothash_job && + i->roothash_job->error == 0) { + r = raw_pull_rename_auxiliary_file(i, ".roothash", &i->roothash_temp_path, &i->roothash_path); if (r < 0) goto finish; + } - r = rename_noreplace(AT_FDCWD, i->settings_temp_path, AT_FDCWD, i->settings_path); - if (r < 0) { - log_error_errno(r, "Failed to rename settings file: %m"); + if (i->settings_job && + i->settings_job->error == 0) { + r = raw_pull_rename_auxiliary_file(i, ".nspawn", &i->settings_temp_path, &i->settings_path); + if (r < 0) goto finish; - } - - i->settings_temp_path = mfree(i->settings_temp_path); } } @@ -482,6 +569,35 @@ finish: sd_event_exit(i->event, r); } +static int raw_pull_job_on_open_disk_generic( + RawPull *i, + PullJob *j, + const char *extra, + char **temp_path) { + + _cleanup_free_ char *p = NULL; + int r; + + assert(i); + assert(j); + assert(extra); + assert(temp_path); + + if (!*temp_path) { + r = tempfn_random_child(i->image_root, extra, temp_path); + if (r < 0) + return log_oom(); + } + + (void) mkdir_parents_label(*temp_path, 0700); + + j->disk_fd = open(*temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); + if (j->disk_fd < 0) + return log_error_errno(errno, "Failed to create %s: %m", *temp_path); + + return 0; +} + static int raw_pull_job_on_open_disk_raw(PullJob *j) { RawPull *i; int r; @@ -491,57 +607,40 @@ static int raw_pull_job_on_open_disk_raw(PullJob *j) { i = j->userdata; assert(i->raw_job == j); - assert(!i->final_path); - assert(!i->temp_path); - - r = pull_make_path(j->url, j->etag, i->image_root, ".raw-", ".raw", &i->final_path); - if (r < 0) - return log_oom(); - r = tempfn_random(i->final_path, NULL, &i->temp_path); + r = raw_pull_job_on_open_disk_generic(i, j, "raw", &i->temp_path); if (r < 0) - return log_oom(); - - (void) mkdir_parents_label(i->temp_path, 0700); - - j->disk_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); - if (j->disk_fd < 0) - return log_error_errno(errno, "Failed to create %s: %m", i->temp_path); + return r; r = chattr_fd(j->disk_fd, FS_NOCOW_FL, FS_NOCOW_FL); if (r < 0) - log_warning_errno(r, "Failed to set file attributes on %s: %m", i->temp_path); + log_warning_errno(r, "Failed to set file attributes on %s, ignoring: %m", i->temp_path); return 0; } -static int raw_pull_job_on_open_disk_settings(PullJob *j) { +static int raw_pull_job_on_open_disk_roothash(PullJob *j) { RawPull *i; - int r; assert(j); assert(j->userdata); i = j->userdata; - assert(i->settings_job == j); - assert(!i->settings_path); - assert(!i->settings_temp_path); + assert(i->roothash_job == j); - r = pull_make_path(j->url, j->etag, i->image_root, ".settings-", NULL, &i->settings_path); - if (r < 0) - return log_oom(); + return raw_pull_job_on_open_disk_generic(i, j, "roothash", &i->roothash_temp_path); +} - r = tempfn_random(i->settings_path, NULL, &i->settings_temp_path); - if (r < 0) - return log_oom(); +static int raw_pull_job_on_open_disk_settings(PullJob *j) { + RawPull *i; - mkdir_parents_label(i->settings_temp_path, 0700); + assert(j); + assert(j->userdata); - j->disk_fd = open(i->settings_temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664); - if (j->disk_fd < 0) - return log_error_errno(errno, "Failed to create %s: %m", i->settings_temp_path); + i = j->userdata; + assert(i->settings_job == j); - return 0; + return raw_pull_job_on_open_disk_generic(i, j, "settings", &i->settings_temp_path); } static void raw_pull_job_on_progress(PullJob *j) { @@ -561,7 +660,8 @@ int raw_pull_start( const char *local, bool force_local, ImportVerify verify, - bool settings) { + bool settings, + bool roothash) { int r; @@ -585,6 +685,7 @@ int raw_pull_start( i->force_local = force_local; i->verify = verify; i->settings = settings; + i->roothash = roothash; /* Queue job for the image itself */ r = pull_job_new(&i->raw_job, url, i->glue, i); @@ -601,18 +702,24 @@ int raw_pull_start( if (r < 0) return r; + if (roothash) { + r = pull_make_auxiliary_job(&i->roothash_job, url, raw_strip_suffixes, ".roothash", i->glue, raw_pull_job_on_finished, i); + if (r < 0) + return r; + + i->roothash_job->on_open_disk = raw_pull_job_on_open_disk_roothash; + i->roothash_job->on_progress = raw_pull_job_on_progress; + i->roothash_job->calc_checksum = verify != IMPORT_VERIFY_NO; + } + if (settings) { - r = pull_make_settings_job(&i->settings_job, url, i->glue, raw_pull_job_on_finished, i); + r = pull_make_auxiliary_job(&i->settings_job, url, raw_strip_suffixes, ".nspawn", i->glue, raw_pull_job_on_finished, i); if (r < 0) return r; i->settings_job->on_open_disk = raw_pull_job_on_open_disk_settings; i->settings_job->on_progress = raw_pull_job_on_progress; i->settings_job->calc_checksum = verify != IMPORT_VERIFY_NO; - - r = pull_find_old_etags(i->settings_job->url, i->image_root, DT_REG, ".settings-", NULL, &i->settings_job->old_etags); - if (r < 0) - return r; } r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, raw_pull_job_on_finished, i); @@ -623,6 +730,12 @@ int raw_pull_start( if (r < 0) return r; + if (i->roothash_job) { + r = pull_job_begin(i->roothash_job); + if (r < 0) + return r; + } + if (i->settings_job) { r = pull_job_begin(i->settings_job); if (r < 0) diff --git a/src/import/pull-raw.h b/src/import/pull-raw.h index 8f6d16eb3a..6954d98994 100644 --- a/src/import/pull-raw.h +++ b/src/import/pull-raw.h @@ -33,4 +33,4 @@ RawPull* raw_pull_unref(RawPull *pull); DEFINE_TRIVIAL_CLEANUP_FUNC(RawPull*, raw_pull_unref); -int raw_pull_start(RawPull *pull, const char *url, const char *local, bool force_local, ImportVerify verify, bool settings); +int raw_pull_start(RawPull *pull, const char *url, const char *local, bool force_local, ImportVerify verify, bool settings, bool roothash); diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c index 68e2397b02..375ee778e2 100644 --- a/src/import/pull-tar.c +++ b/src/import/pull-tar.c @@ -215,6 +215,24 @@ static void tar_pull_report_progress(TarPull *i, TarProgress p) { log_debug("Combined progress %u%%", percent); } +static int tar_pull_determine_path(TarPull *i, const char *suffix, char **field) { + int r; + + assert(i); + assert(field); + + if (*field) + return 0; + + assert(i->tar_job); + + r = pull_make_path(i->tar_job->url, i->tar_job->etag, i->image_root, ".tar-", suffix, field); + if (r < 0) + return log_oom(); + + return 1; +} + static int tar_pull_make_local_copy(TarPull *i) { int r; @@ -224,12 +242,6 @@ static int tar_pull_make_local_copy(TarPull *i) { if (!i->local) return 0; - if (!i->final_path) { - r = pull_make_path(i->tar_job->url, i->tar_job->etag, i->image_root, ".tar-", NULL, &i->final_path); - if (r < 0) - return log_oom(); - } - r = pull_make_local_copy(i->final_path, i->image_root, i->local, i->force_local); if (r < 0) return r; @@ -238,11 +250,9 @@ static int tar_pull_make_local_copy(TarPull *i) { const char *local_settings; assert(i->settings_job); - if (!i->settings_path) { - r = pull_make_path(i->settings_job->url, i->settings_job->etag, i->image_root, ".settings-", NULL, &i->settings_path); - if (r < 0) - return log_oom(); - } + r = tar_pull_determine_path(i, ".nspawn", &i->settings_path); + if (r < 0) + return r; local_settings = strjoina(i->image_root, "/", i->local, ".nspawn"); @@ -311,6 +321,10 @@ static void tar_pull_job_on_finished(PullJob *j) { if (i->settings_job) i->settings_job->disk_fd = safe_close(i->settings_job->disk_fd); + r = tar_pull_determine_path(i, NULL, &i->final_path); + if (r < 0) + goto finish; + if (i->tar_pid > 0) { r = wait_for_terminate_and_warn("tar", i->tar_pid, true); i->tar_pid = 0; @@ -327,7 +341,7 @@ static void tar_pull_job_on_finished(PullJob *j) { tar_pull_report_progress(i, TAR_VERIFYING); - r = pull_verify(i->tar_job, i->settings_job, i->checksum_job, i->signature_job); + r = pull_verify(i->tar_job, NULL, i->settings_job, i->checksum_job, i->signature_job); if (r < 0) goto finish; @@ -346,16 +360,18 @@ static void tar_pull_job_on_finished(PullJob *j) { i->temp_path = mfree(i->temp_path); if (i->settings_job && - i->settings_job->error == 0 && - !i->settings_job->etag_exists) { + i->settings_job->error == 0) { assert(i->settings_temp_path); assert(i->settings_path); - /* Also move the settings file into place, if - * it exist. Note that we do so only if we - * also moved the tar file in place, to keep - * things strictly in sync. */ + /* Also move the settings file into place, if it exist. Note that we do so only if we also + * moved the tar file in place, to keep things strictly in sync. */ + + i->settings_path = mfree(i->settings_path); + r = tar_pull_determine_path(i, ".nspawn", &i->settings_path); + if (r < 0) + goto finish; r = import_make_read_only(i->settings_temp_path); if (r < 0) @@ -395,17 +411,13 @@ static int tar_pull_job_on_open_disk_tar(PullJob *j) { i = j->userdata; assert(i->tar_job == j); - assert(!i->final_path); - assert(!i->temp_path); assert(i->tar_pid <= 0); - r = pull_make_path(j->url, j->etag, i->image_root, ".tar-", NULL, &i->final_path); - if (r < 0) - return log_oom(); - - r = tempfn_random(i->final_path, NULL, &i->temp_path); - if (r < 0) - return log_oom(); + if (!i->temp_path) { + r = tempfn_random_child(i->image_root, "tar", &i->temp_path); + if (r < 0) + return log_oom(); + } mkdir_parents_label(i->temp_path, 0700); @@ -434,16 +446,12 @@ static int tar_pull_job_on_open_disk_settings(PullJob *j) { i = j->userdata; assert(i->settings_job == j); - assert(!i->settings_path); - assert(!i->settings_temp_path); - r = pull_make_path(j->url, j->etag, i->image_root, ".settings-", NULL, &i->settings_path); - if (r < 0) - return log_oom(); - - r = tempfn_random(i->settings_path, NULL, &i->settings_temp_path); - if (r < 0) - return log_oom(); + if (!i->settings_temp_path) { + r = tempfn_random_child(i->image_root, "settings", &i->settings_temp_path); + if (r < 0) + return log_oom(); + } mkdir_parents_label(i->settings_temp_path, 0700); @@ -513,17 +521,13 @@ int tar_pull_start( /* Set up download job for the settings file (.nspawn) */ if (settings) { - r = pull_make_settings_job(&i->settings_job, url, i->glue, tar_pull_job_on_finished, i); + r = pull_make_auxiliary_job(&i->settings_job, url, tar_strip_suffixes, ".nspawn", i->glue, tar_pull_job_on_finished, i); if (r < 0) return r; i->settings_job->on_open_disk = tar_pull_job_on_open_disk_settings; i->settings_job->on_progress = tar_pull_job_on_progress; i->settings_job->calc_checksum = verify != IMPORT_VERIFY_NO; - - r = pull_find_old_etags(i->settings_job->url, i->image_root, DT_REG, ".settings-", NULL, &i->settings_job->old_etags); - if (r < 0) - return r; } /* Set up download of checksum/signature files */ diff --git a/src/import/pull.c b/src/import/pull.c index 53b1211965..4af5d9c853 100644 --- a/src/import/pull.c +++ b/src/import/pull.c @@ -37,6 +37,7 @@ static bool arg_force = false; static const char *arg_image_root = "/var/lib/machines"; static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE; static bool arg_settings = true; +static bool arg_roothash = true; static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { log_notice("Transfer aborted."); @@ -204,7 +205,7 @@ static int pull_raw(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to allocate puller: %m"); - r = raw_pull_start(pull, url, local, arg_force, arg_verify, arg_settings); + r = raw_pull_start(pull, url, local, arg_force, arg_verify, arg_settings, arg_roothash); if (r < 0) return log_error_errno(r, "Failed to pull image: %m"); @@ -226,6 +227,7 @@ static int help(int argc, char *argv[], void *userdata) { " --verify=MODE Verify downloaded image, one of: 'no',\n" " 'checksum', 'signature'\n" " --settings=BOOL Download settings file with image\n" + " --roothash=BOOL Download root hash file with image\n" " --image-root=PATH Image root directory\n\n" "Commands:\n" " tar URL [NAME] Download a TAR image\n" @@ -243,6 +245,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_IMAGE_ROOT, ARG_VERIFY, ARG_SETTINGS, + ARG_ROOTHASH, }; static const struct option options[] = { @@ -252,6 +255,7 @@ static int parse_argv(int argc, char *argv[]) { { "image-root", required_argument, NULL, ARG_IMAGE_ROOT }, { "verify", required_argument, NULL, ARG_VERIFY }, { "settings", required_argument, NULL, ARG_SETTINGS }, + { "roothash", required_argument, NULL, ARG_ROOTHASH }, {} }; @@ -295,6 +299,14 @@ static int parse_argv(int argc, char *argv[]) { arg_settings = r; break; + case ARG_ROOTHASH: + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --roothash= parameter '%s'", optarg); + + arg_roothash = r; + break; + case '?': return -EINVAL; diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 5c6941ebd6..8b92ea3def 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -283,17 +283,16 @@ static int open_journal( } static bool flushed_flag_is_set(void) { - return (access("/run/systemd/journal/flushed", F_OK) >= 0); + return access("/run/systemd/journal/flushed", F_OK) >= 0; } static int system_journal_open(Server *s, bool flush_requested) { - bool flushed = false; const char *fn; int r = 0; if (!s->system_journal && - (s->storage == STORAGE_PERSISTENT || s->storage == STORAGE_AUTO) && - (flush_requested || (flushed = flushed_flag_is_set()))) { + IN_SET(s->storage, STORAGE_PERSISTENT, STORAGE_AUTO) && + (flush_requested || flushed_flag_is_set())) { /* If in auto mode: first try to create the machine * path, but not the prefix. @@ -326,8 +325,8 @@ static int system_journal_open(Server *s, bool flush_requested) { * Perform an implicit flush to var, leaving the runtime * journal closed, now that the system journal is back. */ - if (s->runtime_journal && flushed) - (void) server_flush_to_var(s); + if (!flush_requested) + (void) server_flush_to_var(s, true); } if (!s->runtime_journal && @@ -1183,7 +1182,7 @@ finish: dispatch_message_real(s, iovec, n, m, ucred, tv, label, label_len, unit_id, priority, object_pid); } -int server_flush_to_var(Server *s) { +int server_flush_to_var(Server *s, bool require_flag_file) { sd_id128_t machine; sd_journal *j = NULL; char ts[FORMAT_TIMESPAN_MAX]; @@ -1193,13 +1192,15 @@ int server_flush_to_var(Server *s) { assert(s); - if (s->storage != STORAGE_AUTO && - s->storage != STORAGE_PERSISTENT) + if (!IN_SET(s->storage, STORAGE_AUTO, STORAGE_PERSISTENT)) return 0; if (!s->runtime_journal) return 0; + if (require_flag_file && !flushed_flag_is_set()) + return 0; + (void) system_journal_open(s, true); if (!s->system_journal) @@ -1411,7 +1412,7 @@ static int dispatch_sigusr1(sd_event_source *es, const struct signalfd_siginfo * log_info("Received request to flush runtime journal from PID " PID_FMT, si->ssi_pid); - (void) server_flush_to_var(s); + (void) server_flush_to_var(s, false); server_sync(s); server_vacuum(s, false); @@ -1532,60 +1533,93 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat assert(s); - if (streq(key, "systemd.journald.forward_to_syslog")) { + if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_syslog")) { + r = value ? parse_boolean(value) : true; if (r < 0) log_warning("Failed to parse forward to syslog switch \"%s\". Ignoring.", value); else s->forward_to_syslog = r; - } else if (streq(key, "systemd.journald.forward_to_kmsg")) { + + } else if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_kmsg")) { + r = value ? parse_boolean(value) : true; if (r < 0) log_warning("Failed to parse forward to kmsg switch \"%s\". Ignoring.", value); else s->forward_to_kmsg = r; - } else if (streq(key, "systemd.journald.forward_to_console")) { + + } else if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_console")) { + r = value ? parse_boolean(value) : true; if (r < 0) log_warning("Failed to parse forward to console switch \"%s\". Ignoring.", value); else s->forward_to_console = r; - } else if (streq(key, "systemd.journald.forward_to_wall")) { + + } else if (proc_cmdline_key_streq(key, "systemd.journald.forward_to_wall")) { + r = value ? parse_boolean(value) : true; if (r < 0) log_warning("Failed to parse forward to wall switch \"%s\". Ignoring.", value); else s->forward_to_wall = r; - } else if (streq(key, "systemd.journald.max_level_console") && value) { + + } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_console")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + r = log_level_from_string(value); if (r < 0) log_warning("Failed to parse max level console value \"%s\". Ignoring.", value); else s->max_level_console = r; - } else if (streq(key, "systemd.journald.max_level_store") && value) { + + } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_store")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + r = log_level_from_string(value); if (r < 0) log_warning("Failed to parse max level store value \"%s\". Ignoring.", value); else s->max_level_store = r; - } else if (streq(key, "systemd.journald.max_level_syslog") && value) { + + } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_syslog")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + r = log_level_from_string(value); if (r < 0) log_warning("Failed to parse max level syslog value \"%s\". Ignoring.", value); else s->max_level_syslog = r; - } else if (streq(key, "systemd.journald.max_level_kmsg") && value) { + + } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_kmsg")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + r = log_level_from_string(value); if (r < 0) log_warning("Failed to parse max level kmsg value \"%s\". Ignoring.", value); else s->max_level_kmsg = r; - } else if (streq(key, "systemd.journald.max_level_wall") && value) { + + } else if (proc_cmdline_key_streq(key, "systemd.journald.max_level_wall")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + r = log_level_from_string(value); if (r < 0) log_warning("Failed to parse max level wall value \"%s\". Ignoring.", value); else s->max_level_wall = r; + } else if (startswith(key, "systemd.journald")) log_warning("Unknown journald kernel command line option \"%s\". Ignoring.", key); @@ -1898,7 +1932,10 @@ int server_init(Server *s) { journal_reset_metrics(&s->runtime_storage.metrics); server_parse_config_file(s); - parse_proc_cmdline(parse_proc_cmdline_item, s, true); + + r = proc_cmdline_parse(parse_proc_cmdline_item, s, PROC_CMDLINE_STRIP_RD_PREFIX); + if (r < 0) + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); if (!!s->rate_limit_interval ^ !!s->rate_limit_burst) { log_debug("Setting both rate limit interval and burst from "USEC_FMT",%u to 0,0", diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h index d1520c45dd..716e758b7c 100644 --- a/src/journal/journald-server.h +++ b/src/journal/journald-server.h @@ -197,7 +197,7 @@ void server_sync(Server *s); int server_vacuum(Server *s, bool verbose); void server_rotate(Server *s); int server_schedule_sync(Server *s, int priority); -int server_flush_to_var(Server *s); +int server_flush_to_var(Server *s, bool require_flag_file); void server_maybe_append_tags(Server *s); int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void *userdata); void server_space_usage_message(Server *s, JournalStorage *storage); diff --git a/src/journal/journald.c b/src/journal/journald.c index fc26ef1785..54fd1f999d 100644 --- a/src/journal/journald.c +++ b/src/journal/journald.c @@ -52,7 +52,7 @@ int main(int argc, char *argv[]) { goto finish; server_vacuum(&server, false); - server_flush_to_var(&server); + server_flush_to_var(&server, true); server_flush_dev_kmsg(&server); log_debug("systemd-journald running as pid "PID_FMT, getpid()); diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c index a7b9d1f9ef..d5051007fc 100644 --- a/src/machine/image-dbus.c +++ b/src/machine/image-dbus.c @@ -335,7 +335,7 @@ static int raw_image_get_os_release(Image *image, char ***ret, sd_bus_error *err if (r < 0) return sd_bus_error_set_errnof(error, r, "Failed to set up loop block device for %s: %m", image->path); - r = dissect_image(d->fd, NULL, 0, &m); + r = dissect_image(d->fd, NULL, 0, DISSECT_IMAGE_REQUIRE_ROOT, &m); if (r == -ENOPKG) return sd_bus_error_set_errnof(error, r, "Disk image %s not understood: %m", image->path); if (r < 0) diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 462667cf37..31a40d47c3 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -2666,9 +2666,9 @@ static int help(int argc, char *argv[], void *userdata) { " -o --output=STRING Change journal output mode (short,\n" " short-monotonic, verbose, export, json,\n" " json-pretty, json-sse, cat)\n" - " --verify=MODE Verification mode for downloaded images (no,\n" + " --verify=MODE Verification mode for downloaded images (no,\n" " checksum, signature)\n" - " --force Download image even if already exists\n\n" + " --force Download image even if already exists\n\n" "Machine Commands:\n" " list List running VMs and containers\n" " status NAME... Show VM/container details\n" diff --git a/src/modules-load/modules-load.c b/src/modules-load/modules-load.c index 0901fea8dc..615998a6f6 100644 --- a/src/modules-load/modules-load.c +++ b/src/modules-load/modules-load.c @@ -62,7 +62,11 @@ static int add_modules(const char *p) { static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { int r; - if (streq(key, "modules-load") && value) { + if (proc_cmdline_key_streq(key, "modules_load")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + r = add_modules(value); if (r < 0) return r; @@ -226,7 +230,7 @@ int main(int argc, char *argv[]) { umask(0022); - r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, true); + r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX); if (r < 0) log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index aaa64a7ba8..72c007f204 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -1349,21 +1349,3 @@ fail: (void) rmdir(template); return r; } - -VolatileMode volatile_mode_from_string(const char *s) { - int b; - - if (isempty(s)) - return _VOLATILE_MODE_INVALID; - - b = parse_boolean(s); - if (b > 0) - return VOLATILE_YES; - if (b == 0) - return VOLATILE_NO; - - if (streq(s, "state")) - return VOLATILE_STATE; - - return _VOLATILE_MODE_INVALID; -} diff --git a/src/nspawn/nspawn-mount.h b/src/nspawn/nspawn-mount.h index 467082a737..6b33fbff57 100644 --- a/src/nspawn/nspawn-mount.h +++ b/src/nspawn/nspawn-mount.h @@ -22,6 +22,7 @@ #include <stdbool.h> #include "cgroup-util.h" +#include "volatile-util.h" typedef enum MountSettingsMask { MOUNT_FATAL = 1 << 0, /* if set, a mount error is considered fatal */ @@ -32,14 +33,6 @@ typedef enum MountSettingsMask { Works only if MOUNT_APPLY_APIVFS_RO is also set. */ } MountSettingsMask; -typedef enum VolatileMode { - VOLATILE_NO, - VOLATILE_YES, - VOLATILE_STATE, - _VOLATILE_MODE_MAX, - _VOLATILE_MODE_INVALID = -1 -} VolatileMode; - typedef enum CustomMountType { CUSTOM_MOUNT_BIND, CUSTOM_MOUNT_TMPFS, @@ -77,5 +70,3 @@ int mount_custom(const char *dest, CustomMount *mounts, unsigned n, bool userns, int setup_volatile(const char *directory, VolatileMode mode, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context); int setup_volatile_state(const char *directory, VolatileMode mode, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context); - -VolatileMode volatile_mode_from_string(const char *s); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index e366f642c7..6396a69c5c 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -1288,15 +1288,18 @@ static int setup_timezone(const char *dest) { return 0; } - r = unlink(where); - if (r < 0 && errno != ENOENT) { - log_error_errno(errno, "Failed to remove existing timezone info %s in container: %m", where); + if (unlink(where) < 0 && errno != ENOENT) { + log_full_errno(IN_SET(errno, EROFS, EACCES, EPERM) ? LOG_DEBUG : LOG_WARNING, /* Don't complain on read-only images */ + errno, + "Failed to remove existing timezone info %s in container, ignoring: %m", where); return 0; } what = strjoina("../usr/share/zoneinfo/", z); if (symlink(what, where) < 0) { - log_error_errno(errno, "Failed to correct timezone of container: %m"); + log_full_errno(IN_SET(errno, EROFS, EACCES, EPERM) ? LOG_DEBUG : LOG_WARNING, + errno, + "Failed to correct timezone of container, ignoring: %m"); return 0; } @@ -1308,31 +1311,43 @@ static int setup_timezone(const char *dest) { } static int setup_resolv_conf(const char *dest) { - const char *where = NULL; - int r; + _cleanup_free_ char *resolved = NULL, *etc = NULL; + const char *where; + int r, found; assert(dest); if (arg_private_network) return 0; - /* Fix resolv.conf, if possible */ - where = prefix_roota(dest, "/etc/resolv.conf"); + r = chase_symlinks("/etc", dest, CHASE_PREFIX_ROOT, &etc); + if (r < 0) { + log_warning_errno(r, "Failed to resolve /etc path in container, ignoring: %m"); + return 0; + } + + where = strjoina(etc, "/resolv.conf"); + found = chase_symlinks(where, dest, CHASE_NONEXISTENT, &resolved); + if (found < 0) { + log_warning_errno(found, "Failed to resolve /etc/resolv.conf path in container, ignoring: %m"); + return 0; + } if (access("/run/systemd/resolve/resolv.conf", F_OK) >= 0 && - access("/usr/lib/systemd/resolv.conf", F_OK) >= 0) { + access("/usr/lib/systemd/resolv.conf", F_OK) >= 0) { + /* resolved is enabled on the host. In this, case bind mount its static resolv.conf file into the * container, so that the container can use the host's resolver. Given that network namespacing is * disabled it's only natural of the container also uses the host's resolver. It also has the big * advantage that the container will be able to follow the host's DNS server configuration changes * transparently. */ - (void) touch(where); + if (found == 0) /* missing? */ + (void) touch(resolved); - r = mount_verbose(LOG_WARNING, "/usr/lib/systemd/resolv.conf", where, NULL, MS_BIND, NULL); + r = mount_verbose(LOG_DEBUG, "/usr/lib/systemd/resolv.conf", resolved, NULL, MS_BIND, NULL); if (r >= 0) - return mount_verbose(LOG_ERR, NULL, where, NULL, - MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NOSUID|MS_NODEV, NULL); + return mount_verbose(LOG_ERR, NULL, resolved, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NOSUID|MS_NODEV, NULL); } /* If that didn't work, let's copy the file */ @@ -1343,7 +1358,7 @@ static int setup_resolv_conf(const char *dest) { * * If the disk image is read-only, there's also no point in complaining. */ - log_full_errno(IN_SET(r, -ELOOP, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, + log_full_errno(IN_SET(r, -ELOOP, -EROFS, -EACCES, -EPERM) ? LOG_DEBUG : LOG_WARNING, r, "Failed to copy /etc/resolv.conf to %s, ignoring: %m", where); return 0; } @@ -2467,20 +2482,6 @@ static int outer_child( if (r < 0) return r; - /* Mark everything as shared so our mounts get propagated down. This is - * required to make new bind mounts available in systemd services - * inside the containter that create a new mount namespace. - * See https://github.com/systemd/systemd/issues/3860 - * Further submounts (such as /dev) done after this will inherit the - * shared propagation mode.*/ - r = mount_verbose(LOG_ERR, NULL, directory, NULL, MS_SHARED|MS_REC, NULL); - if (r < 0) - return r; - - r = recursive_chown(directory, arg_uid_shift, arg_uid_range); - if (r < 0) - return r; - r = setup_volatile( directory, arg_volatile_mode, @@ -2501,6 +2502,20 @@ static int outer_child( if (r < 0) return r; + /* Mark everything as shared so our mounts get propagated down. This is + * required to make new bind mounts available in systemd services + * inside the containter that create a new mount namespace. + * See https://github.com/systemd/systemd/issues/3860 + * Further submounts (such as /dev) done after this will inherit the + * shared propagation mode.*/ + r = mount_verbose(LOG_ERR, NULL, directory, NULL, MS_SHARED|MS_REC, NULL); + if (r < 0) + return r; + + r = recursive_chown(directory, arg_uid_shift, arg_uid_range); + if (r < 0) + return r; + r = base_filesystem_create(directory, arg_uid_shift, (gid_t) arg_uid_shift); if (r < 0) return r; @@ -3740,7 +3755,11 @@ int main(int argc, char *argv[]) { goto finish; } - r = dissect_image(loop->fd, arg_root_hash, arg_root_hash_size, &dissected_image); + r = dissect_image( + loop->fd, + arg_root_hash, arg_root_hash_size, + DISSECT_IMAGE_REQUIRE_ROOT, + &dissected_image); if (r == -ENOPKG) { log_error_errno(r, "Could not find a suitable file system or partition table in image: %s", arg_image); diff --git a/src/quotacheck/quotacheck.c b/src/quotacheck/quotacheck.c index 2714cde5c7..a42fce377e 100644 --- a/src/quotacheck/quotacheck.c +++ b/src/quotacheck/quotacheck.c @@ -34,7 +34,10 @@ static bool arg_force = false; static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { - if (streq(key, "quotacheck.mode") && value) { + if (streq(key, "quotacheck.mode")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; if (streq(value, "auto")) arg_force = arg_skip = false; @@ -88,7 +91,7 @@ int main(int argc, char *argv[]) { umask(0022); - r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, false); + r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0); if (r < 0) log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); @@ -104,9 +107,10 @@ int main(int argc, char *argv[]) { pid = fork(); if (pid < 0) { - log_error_errno(errno, "fork(): %m"); - return EXIT_FAILURE; - } else if (pid == 0) { + r = log_error_errno(errno, "fork(): %m"); + goto finish; + } + if (pid == 0) { /* Child */ @@ -120,5 +124,6 @@ int main(int argc, char *argv[]) { r = wait_for_terminate_and_warn("quotacheck", pid, true); +finish: return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/remount-fs/remount-fs.c b/src/remount-fs/remount-fs.c index c3bdcaf1da..0cb9bd9261 100644 --- a/src/remount-fs/remount-fs.c +++ b/src/remount-fs/remount-fs.c @@ -56,7 +56,7 @@ int main(int argc, char *argv[]) { umask(0022); - f = setmntent("/etc/fstab", "r"); + f = setmntent("/etc/fstab", "re"); if (!f) { if (errno == ENOENT) { r = 0; diff --git a/src/shared/base-filesystem.c b/src/shared/base-filesystem.c index f1fbce9dca..127cbe44e3 100644 --- a/src/shared/base-filesystem.c +++ b/src/shared/base-filesystem.c @@ -101,7 +101,7 @@ int base_filesystem_create(const char *root, uid_t uid, gid_t gid) { if (r < 0 && errno != EEXIST) return log_error_errno(errno, "Failed to create symlink at %s/%s: %m", root, table[i].dir); - if (uid != UID_INVALID || gid != UID_INVALID) { + if (uid_is_valid(uid) || gid_is_valid(gid)) { if (fchownat(fd, table[i].dir, uid, gid, AT_SYMLINK_NOFOLLOW) < 0) return log_error_errno(errno, "Failed to chown symlink at %s/%s: %m", root, table[i].dir); } diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index d3ba9b9dde..878cb008aa 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -84,7 +84,7 @@ not_found: #endif } -int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectedImage **ret) { +int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DissectedImage **ret) { #ifdef HAVE_BLKID sd_id128_t root_uuid = SD_ID128_NULL, verity_uuid = SD_ID128_NULL; @@ -95,7 +95,8 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte _cleanup_blkid_free_probe_ blkid_probe b = NULL; _cleanup_udev_unref_ struct udev *udev = NULL; _cleanup_free_ char *generic_node = NULL; - const char *pttype = NULL, *usage = NULL; + sd_id128_t generic_uuid = SD_ID128_NULL; + const char *pttype = NULL; struct udev_list_entry *first, *item; blkid_partlist pl; int r, generic_nr; @@ -147,8 +148,12 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte return -errno; } - blkid_probe_enable_superblocks(b, 1); - blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_USAGE); + if ((flags & DISSECT_IMAGE_GPT_ONLY) == 0) { + /* Look for file system superblocks, unless we only shall look for GPT partition tables */ + blkid_probe_enable_superblocks(b, 1); + blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_USAGE); + } + blkid_probe_enable_partitions(b, 1); blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); @@ -169,40 +174,45 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte if (!m) return -ENOMEM; - (void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL); - if (STRPTR_IN_SET(usage, "filesystem", "crypto")) { - _cleanup_free_ char *t = NULL, *n = NULL; - const char *fstype = NULL; + if (!(flags & DISSECT_IMAGE_GPT_ONLY) && + (flags & DISSECT_IMAGE_REQUIRE_ROOT)) { + const char *usage = NULL; - /* OK, we have found a file system, that's our root partition then. */ - (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL); + (void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL); + if (STRPTR_IN_SET(usage, "filesystem", "crypto")) { + _cleanup_free_ char *t = NULL, *n = NULL; + const char *fstype = NULL; - if (fstype) { - t = strdup(fstype); - if (!t) - return -ENOMEM; - } + /* OK, we have found a file system, that's our root partition then. */ + (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL); - if (asprintf(&n, "/dev/block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0) - return -ENOMEM; + if (fstype) { + t = strdup(fstype); + if (!t) + return -ENOMEM; + } + + if (asprintf(&n, "/dev/block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0) + return -ENOMEM; - m->partitions[PARTITION_ROOT] = (DissectedPartition) { - .found = true, - .rw = true, - .partno = -1, - .architecture = _ARCHITECTURE_INVALID, - .fstype = t, - .node = n, - }; + m->partitions[PARTITION_ROOT] = (DissectedPartition) { + .found = true, + .rw = true, + .partno = -1, + .architecture = _ARCHITECTURE_INVALID, + .fstype = t, + .node = n, + }; - t = n = NULL; + t = n = NULL; - m->encrypted = streq(fstype, "crypto_LUKS"); + m->encrypted = streq(fstype, "crypto_LUKS"); - *ret = m; - m = NULL; + *ret = m; + m = NULL; - return 0; + return 0; + } } (void) blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL); @@ -212,7 +222,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte is_gpt = streq_ptr(pttype, "gpt"); is_mbr = streq_ptr(pttype, "dos"); - if (!is_gpt && !is_mbr) + if (!is_gpt && ((flags & DISSECT_IMAGE_GPT_ONLY) || !is_mbr)) return -ENOPKG; errno = 0; @@ -300,7 +310,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte first = udev_enumerate_get_list_entry(e); udev_list_entry_foreach(item, first) { _cleanup_udev_device_unref_ struct udev_device *q; - unsigned long long flags; + unsigned long long pflags; blkid_partition pp; const char *node; dev_t qn; @@ -325,7 +335,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte if (!pp) continue; - flags = blkid_partition_get_flags(pp); + pflags = blkid_partition_get_flags(pp); nr = blkid_partition_get_partno(pp); if (nr < 0) @@ -337,7 +347,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte sd_id128_t type_id, id; bool rw = true; - if (flags & GPT_FLAG_NO_AUTO) + if (pflags & GPT_FLAG_NO_AUTO) continue; sid = blkid_partition_get_uuid(pp); @@ -354,10 +364,10 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte if (sd_id128_equal(type_id, GPT_HOME)) { designator = PARTITION_HOME; - rw = !(flags & GPT_FLAG_READ_ONLY); + rw = !(pflags & GPT_FLAG_READ_ONLY); } else if (sd_id128_equal(type_id, GPT_SRV)) { designator = PARTITION_SRV; - rw = !(flags & GPT_FLAG_READ_ONLY); + rw = !(pflags & GPT_FLAG_READ_ONLY); } else if (sd_id128_equal(type_id, GPT_ESP)) { designator = PARTITION_ESP; fstype = "vfat"; @@ -371,7 +381,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte designator = PARTITION_ROOT; architecture = native_architecture(); - rw = !(flags & GPT_FLAG_READ_ONLY); + rw = !(pflags & GPT_FLAG_READ_ONLY); } else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE_VERITY)) { m->can_verity = true; @@ -395,9 +405,8 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte designator = PARTITION_ROOT_SECONDARY; architecture = SECONDARY_ARCHITECTURE; - rw = !(flags & GPT_FLAG_READ_ONLY); + rw = !(pflags & GPT_FLAG_READ_ONLY); } else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY_VERITY)) { - m->can_verity = true; /* Ignore verity unless root has is specified */ @@ -419,7 +428,8 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte multiple_generic = true; else { generic_nr = nr; - generic_rw = !(flags & GPT_FLAG_READ_ONLY); + generic_rw = !(pflags & GPT_FLAG_READ_ONLY); + generic_uuid = id; generic_node = strdup(node); if (!generic_node) return -ENOMEM; @@ -450,6 +460,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte .architecture = architecture, .node = n, .fstype = t, + .uuid = id, }; n = t = NULL; @@ -457,7 +468,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte } else if (is_mbr) { - if (flags != 0x80) /* Bootable flag */ + if (pflags != 0x80) /* Bootable flag */ continue; if (blkid_partition_get_type(pp) != 0x83) /* Linux partition */ @@ -480,7 +491,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte * either, then check if there's a single generic one, and use that. */ if (m->partitions[PARTITION_ROOT_VERITY].found) - return -ENXIO; + return -EADDRNOTAVAIL; if (m->partitions[PARTITION_ROOT_SECONDARY].found) { m->partitions[PARTITION_ROOT] = m->partitions[PARTITION_ROOT_SECONDARY]; @@ -489,8 +500,19 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte m->partitions[PARTITION_ROOT_VERITY] = m->partitions[PARTITION_ROOT_SECONDARY_VERITY]; zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY]); - } else if (generic_node && !root_hash) { + } else if (flags & DISSECT_IMAGE_REQUIRE_ROOT) { + + /* If the root has was set, then we won't fallback to a generic node, because the root hash + * decides */ + if (root_hash) + return -EADDRNOTAVAIL; + + /* If we didn't find a generic node, then we can't fix this up either */ + if (!generic_node) + return -ENXIO; + /* If we didn't find a properly marked root partition, but we did find a single suitable + * generic Linux partition, then use this as root partition, if the caller asked for it. */ if (multiple_generic) return -ENOTUNIQ; @@ -500,17 +522,15 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, Dissecte .partno = generic_nr, .architecture = _ARCHITECTURE_INVALID, .node = generic_node, + .uuid = generic_uuid, }; generic_node = NULL; - } else - return -ENXIO; + } } - assert(m->partitions[PARTITION_ROOT].found); - if (root_hash) { - if (!m->partitions[PARTITION_ROOT_VERITY].found) + if (!m->partitions[PARTITION_ROOT_VERITY].found || !m->partitions[PARTITION_ROOT].found) return -EADDRNOTAVAIL; /* If we found the primary root with the hash, then we definitely want to suppress any secondary root diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index 175ddd8ea0..26319bd8e7 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -32,6 +32,7 @@ struct DissectedPartition { bool rw:1; int partno; /* -1 if there was no partition and the images contains a file system directly */ int architecture; /* Intended architecture: either native, secondary or unset (-1). */ + sd_id128_t uuid; /* Partition entry UUID as reported by the GPT */ char *fstype; char *node; char *decrypted_node; @@ -67,6 +68,8 @@ typedef enum DissectImageFlags { DISSECT_IMAGE_DISCARD_ANY = DISSECT_IMAGE_DISCARD_ON_LOOP | DISSECT_IMAGE_DISCARD | DISSECT_IMAGE_DISCARD_ON_CRYPTO, + DISSECT_IMAGE_GPT_ONLY = 16, /* Only recognize images with GPT partition tables */ + DISSECT_IMAGE_REQUIRE_ROOT = 32, /* Don't accept disks without root partition */ } DissectImageFlags; struct DissectedImage { @@ -76,7 +79,7 @@ struct DissectedImage { DissectedPartition partitions[_PARTITION_DESIGNATOR_MAX]; }; -int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectedImage **ret); +int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DissectedImage **ret); DissectedImage* dissected_image_unref(DissectedImage *m); DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref); diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c index f0bfb30bb5..87b520b540 100644 --- a/src/shared/fstab-util.c +++ b/src/shared/fstab-util.c @@ -38,7 +38,7 @@ bool fstab_is_mount_point(const char *mount) { _cleanup_endmntent_ FILE *f = NULL; struct mntent *m; - f = setmntent("/etc/fstab", "r"); + f = setmntent("/etc/fstab", "re"); if (!f) return false; diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c index 712aff65b9..7bc5c0a128 100644 --- a/src/shared/machine-image.c +++ b/src/shared/machine-image.c @@ -99,6 +99,16 @@ static char **image_settings_path(Image *image) { return ret; } +static char *image_roothash_path(Image *image) { + const char *fn; + + assert(image); + + fn = strjoina(image->name, ".roothash"); + + return file_in_same_dir(image->path, fn); +} + static int image_new( ImageType t, const char *pretty, @@ -397,6 +407,7 @@ void image_hashmap_free(Hashmap *map) { int image_remove(Image *i) { _cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT; _cleanup_strv_free_ char **settings = NULL; + _cleanup_free_ char *roothash = NULL; char **j; int r; @@ -409,6 +420,10 @@ int image_remove(Image *i) { if (!settings) return -ENOMEM; + roothash = image_roothash_path(i); + if (!roothash) + return -ENOMEM; + /* Make sure we don't interfere with a running nspawn */ r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock); if (r < 0) @@ -445,14 +460,17 @@ int image_remove(Image *i) { log_debug_errno(errno, "Failed to unlink %s, ignoring: %m", *j); } + if (unlink(roothash) < 0 && errno != ENOENT) + log_debug_errno(errno, "Failed to unlink %s, ignoring: %m", roothash); + return 0; } -static int rename_settings_file(const char *path, const char *new_name) { +static int rename_auxiliary_file(const char *path, const char *new_name, const char *suffix) { _cleanup_free_ char *rs = NULL; const char *fn; - fn = strjoina(new_name, ".nspawn"); + fn = strjoina(new_name, suffix); rs = file_in_same_dir(path, fn); if (!rs) @@ -463,7 +481,7 @@ static int rename_settings_file(const char *path, const char *new_name) { int image_rename(Image *i, const char *new_name) { _cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT, name_lock = LOCK_FILE_INIT; - _cleanup_free_ char *new_path = NULL, *nn = NULL; + _cleanup_free_ char *new_path = NULL, *nn = NULL, *roothash = NULL; _cleanup_strv_free_ char **settings = NULL; unsigned file_attr = 0; char **j; @@ -481,6 +499,10 @@ int image_rename(Image *i, const char *new_name) { if (!settings) return -ENOMEM; + roothash = image_roothash_path(i); + if (!roothash) + return -ENOMEM; + /* Make sure we don't interfere with a running nspawn */ r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock); if (r < 0) @@ -550,19 +572,23 @@ int image_rename(Image *i, const char *new_name) { nn = NULL; STRV_FOREACH(j, settings) { - r = rename_settings_file(*j, new_name); + r = rename_auxiliary_file(*j, new_name, ".nspawn"); if (r < 0 && r != -ENOENT) log_debug_errno(r, "Failed to rename settings file %s, ignoring: %m", *j); } + r = rename_auxiliary_file(roothash, new_name, ".roothash"); + if (r < 0 && r != -ENOENT) + log_debug_errno(r, "Failed to rename roothash file %s, ignoring: %m", roothash); + return 0; } -static int clone_settings_file(const char *path, const char *new_name) { +static int clone_auxiliary_file(const char *path, const char *new_name, const char *suffix) { _cleanup_free_ char *rs = NULL; const char *fn; - fn = strjoina(new_name, ".nspawn"); + fn = strjoina(new_name, suffix); rs = file_in_same_dir(path, fn); if (!rs) @@ -574,6 +600,7 @@ static int clone_settings_file(const char *path, const char *new_name) { int image_clone(Image *i, const char *new_name, bool read_only) { _cleanup_release_lock_file_ LockFile name_lock = LOCK_FILE_INIT; _cleanup_strv_free_ char **settings = NULL; + _cleanup_free_ char *roothash = NULL; const char *new_path; char **j; int r; @@ -587,6 +614,10 @@ int image_clone(Image *i, const char *new_name, bool read_only) { if (!settings) return -ENOMEM; + roothash = image_roothash_path(i); + if (!roothash) + return -ENOMEM; + /* Make sure nobody takes the new name, between the time we * checked it is currently unused in all search paths, and the * time we take possession of it */ @@ -636,11 +667,15 @@ int image_clone(Image *i, const char *new_name, bool read_only) { return r; STRV_FOREACH(j, settings) { - r = clone_settings_file(*j, new_name); + r = clone_auxiliary_file(*j, new_name, ".nspawn"); if (r < 0 && r != -ENOENT) log_debug_errno(r, "Failed to clone settings %s, ignoring: %m", *j); } + r = clone_auxiliary_file(roothash, new_name, ".roothash"); + if (r < 0 && r != -ENOENT) + log_debug_errno(r, "Failed to clone root hash file %s, ignoring: %m", roothash); + return 0; } diff --git a/src/shared/switch-root.c b/src/shared/switch-root.c index 4eff4f692e..afdf1ab5ad 100644 --- a/src/shared/switch-root.c +++ b/src/shared/switch-root.c @@ -28,123 +28,102 @@ #include "base-filesystem.h" #include "fd-util.h" +#include "fs-util.h" #include "log.h" #include "missing.h" #include "mkdir.h" +#include "mount-util.h" #include "path-util.h" #include "rm-rf.h" #include "stdio-util.h" #include "string-util.h" +#include "strv.h" #include "switch-root.h" #include "user-util.h" #include "util.h" -int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot, unsigned long mountflags) { - - /* Don't try to unmount/move the old "/", there's no way to do it. */ - static const char move_mounts[] = - "/dev\0" - "/proc\0" - "/sys\0" - "/run\0"; +int switch_root(const char *new_root, + const char *old_root_after, /* path below the new root, where to place the old root after the transition */ + bool unmount_old_root, + unsigned long mount_flags) { /* MS_MOVE or MS_BIND */ + _cleanup_free_ char *resolved_old_root_after = NULL; _cleanup_close_ int old_root_fd = -1; - struct stat new_root_stat; bool old_root_remove; - const char *i, *temporary_old_root; + const char *i; + int r; + + assert(new_root); + assert(old_root_after); if (path_equal(new_root, "/")) return 0; - temporary_old_root = strjoina(new_root, oldroot); - mkdir_p_label(temporary_old_root, 0755); - + /* Check if we shall remove the contents of the old root */ old_root_remove = in_initrd(); + if (old_root_remove) { + old_root_fd = open("/", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY|O_DIRECTORY); + if (old_root_fd < 0) + return log_error_errno(errno, "Failed to open root directory: %m"); + } - if (stat(new_root, &new_root_stat) < 0) - return log_error_errno(errno, "Failed to stat directory %s: %m", new_root); + /* Determine where we shall place the old root after the transition */ + r = chase_symlinks(old_root_after, new_root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &resolved_old_root_after); + if (r < 0) + return log_error_errno(r, "Failed to resolve %s/%s: %m", new_root, old_root_after); + if (r == 0) /* Doesn't exist yet. Let's create it */ + (void) mkdir_p_label(resolved_old_root_after, 0755); - /* Work-around for kernel design: the kernel refuses switching - * root if any file systems are mounted MS_SHARED. Hence + /* Work-around for kernel design: the kernel refuses MS_MOVE if any file systems are mounted MS_SHARED. Hence * remount them MS_PRIVATE here as a work-around. * * https://bugzilla.redhat.com/show_bug.cgi?id=847418 */ if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0) - log_warning_errno(errno, "Failed to make \"/\" private mount: %m"); - - NULSTR_FOREACH(i, move_mounts) { - char new_mount[PATH_MAX]; - struct stat sb; - size_t n; - - n = snprintf(new_mount, sizeof new_mount, "%s%s", new_root, i); - if (n >= sizeof new_mount) { - bool move = mountflags & MS_MOVE; - - log_warning("New path is too long, %s: %s%s", - move ? "forcing unmount instead" : "ignoring", - new_root, i); - - if (move) - if (umount2(i, MNT_FORCE) < 0) - log_warning_errno(errno, "Failed to unmount %s: %m", i); - continue; - } - - mkdir_p_label(new_mount, 0755); - - if (stat(new_mount, &sb) < 0 || - sb.st_dev != new_root_stat.st_dev) { - - /* Mount point seems to be mounted already or - * stat failed. Unmount the old mount point. */ - if (umount2(i, MNT_DETACH) < 0) - log_warning_errno(errno, "Failed to unmount %s: %m", i); - continue; - } - - if (mount(i, new_mount, NULL, mountflags, NULL) < 0) { - if (mountflags & MS_MOVE) { - log_error_errno(errno, "Failed to move mount %s to %s, forcing unmount: %m", i, new_mount); - - if (umount2(i, MNT_FORCE) < 0) - log_warning_errno(errno, "Failed to unmount %s: %m", i); - - } else if (mountflags & MS_BIND) - log_error_errno(errno, "Failed to bind mount %s to %s: %m", i, new_mount); - } + return log_error_errno(errno, "Failed to set \"/\" mount propagation to private: %m"); + + FOREACH_STRING(i, "/sys", "/dev", "/run", "/proc") { + _cleanup_free_ char *chased = NULL; + + r = chase_symlinks(i, new_root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &chased); + if (r < 0) + return log_error_errno(r, "Failed to resolve %s/%s: %m", new_root, i); + if (r > 0) { + /* Already exists. Let's see if it is a mount point already. */ + r = path_is_mount_point(chased, NULL, 0); + if (r < 0) + return log_error_errno(r, "Failed to determine whether %s is a mount point: %m", chased); + if (r > 0) /* If it is already mounted, then do nothing */ + continue; + } else + /* Doesn't exist yet? */ + (void) mkdir_p_label(chased, 0755); + + if (mount(i, chased, NULL, mount_flags, NULL) < 0) + return log_error_errno(r, "Failed to mount %s to %s: %m", i, chased); } - /* Do not fail, if base_filesystem_create() fails. Not all - * switch roots are like base_filesystem_create() wants them - * to look like. They might even boot, if they are RO and - * don't have the FS layout. Just ignore the error and - * switch_root() nevertheless. */ + /* Do not fail if base_filesystem_create() fails. Not all switch roots are like base_filesystem_create() wants + * them to look like. They might even boot, if they are RO and don't have the FS layout. Just ignore the error + * and switch_root() nevertheless. */ (void) base_filesystem_create(new_root, UID_INVALID, GID_INVALID); if (chdir(new_root) < 0) return log_error_errno(errno, "Failed to change directory to %s: %m", new_root); - if (old_root_remove) { - old_root_fd = open("/", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY|O_DIRECTORY); - if (old_root_fd < 0) - log_warning_errno(errno, "Failed to open root directory: %m"); - } - - /* We first try a pivot_root() so that we can umount the old - * root dir. In many cases (i.e. where rootfs is /), that's - * not possible however, and hence we simply overmount root */ - if (pivot_root(new_root, temporary_old_root) >= 0) { + /* We first try a pivot_root() so that we can umount the old root dir. In many cases (i.e. where rootfs is /), + * that's not possible however, and hence we simply overmount root */ + if (pivot_root(new_root, resolved_old_root_after) >= 0) { /* Immediately get rid of the old root, if detach_oldroot is set. * Since we are running off it we need to do this lazily. */ - if (detach_oldroot && umount2(oldroot, MNT_DETACH) < 0) - log_error_errno(errno, "Failed to lazily umount old root dir %s, %s: %m", - oldroot, - errno == ENOENT ? "ignoring" : "leaving it around"); + if (unmount_old_root) { + r = umount_recursive(old_root_after, MNT_DETACH); + if (r < 0) + log_warning_errno(r, "Failed to unmount old root directory tree, ignoring: %m"); + } } else if (mount(new_root, "/", NULL, MS_MOVE, NULL) < 0) - return log_error_errno(errno, "Failed to mount moving %s to /: %m", new_root); + return log_error_errno(errno, "Failed to move %s to /: %m", new_root); if (chroot(".") < 0) return log_error_errno(errno, "Failed to change root: %m"); diff --git a/src/shared/volatile-util.c b/src/shared/volatile-util.c new file mode 100644 index 0000000000..e7e9721411 --- /dev/null +++ b/src/shared/volatile-util.c @@ -0,0 +1,68 @@ +/*** + This file is part of systemd. + + Copyright 2015 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "alloc-util.h" +#include "macro.h" +#include "parse-util.h" +#include "proc-cmdline.h" +#include "string-util.h" +#include "volatile-util.h" + +VolatileMode volatile_mode_from_string(const char *s) { + int b; + + if (isempty(s)) + return _VOLATILE_MODE_INVALID; + + b = parse_boolean(s); + if (b > 0) + return VOLATILE_YES; + if (b == 0) + return VOLATILE_NO; + + if (streq(s, "state")) + return VOLATILE_STATE; + + return _VOLATILE_MODE_INVALID; +} + +int query_volatile_mode(VolatileMode *ret) { + _cleanup_free_ char *mode = NULL; + VolatileMode m = VOLATILE_NO; + int r; + + r = proc_cmdline_get_key("systemd.volatile", PROC_CMDLINE_VALUE_OPTIONAL, &mode); + if (r < 0) + return r; + if (r == 0) + goto finish; + + if (mode) { + m = volatile_mode_from_string(mode); + if (m < 0) + return -EINVAL; + } else + m = VOLATILE_YES; + + r = 1; + +finish: + *ret = m; + return r; +} diff --git a/src/shared/volatile-util.h b/src/shared/volatile-util.h new file mode 100644 index 0000000000..17930ba6ae --- /dev/null +++ b/src/shared/volatile-util.h @@ -0,0 +1,32 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2016 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +typedef enum VolatileMode { + VOLATILE_NO, + VOLATILE_YES, + VOLATILE_STATE, + _VOLATILE_MODE_MAX, + _VOLATILE_MODE_INVALID = -1 +} VolatileMode; + +VolatileMode volatile_mode_from_string(const char *s); + +int query_volatile_mode(VolatileMode *ret); diff --git a/src/test/test-dissect-image.c b/src/test/test-dissect-image.c index 0512a15e88..2bb68be0db 100644 --- a/src/test/test-dissect-image.c +++ b/src/test/test-dissect-image.c @@ -43,7 +43,7 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - r = dissect_image(d->fd, NULL, 0, &m); + r = dissect_image(d->fd, NULL, 0, DISSECT_IMAGE_REQUIRE_ROOT, &m); if (r < 0) { log_error_errno(r, "Failed to dissect image: %m"); return EXIT_FAILURE; diff --git a/src/test/test-execute.c b/src/test/test-execute.c index 4670458ffb..3254f0f231 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -33,6 +33,7 @@ #ifdef HAVE_SECCOMP #include "seccomp-util.h" #endif +#include "stat-util.h" #include "test-helper.h" #include "unit.h" #include "util.h" @@ -188,15 +189,27 @@ static void test_exec_protectkernelmodules(Manager *m) { } static void test_exec_readonlypaths(Manager *m) { + + if (path_is_read_only_fs("/var") > 0) + return; + test(m, "exec-readonlypaths.service", 0, CLD_EXITED); test(m, "exec-readonlypaths-mount-propagation.service", 0, CLD_EXITED); } static void test_exec_readwritepaths(Manager *m) { + + if (path_is_read_only_fs("/") > 0) + return; + test(m, "exec-readwritepaths-mount-propagation.service", 0, CLD_EXITED); } static void test_exec_inaccessiblepaths(Manager *m) { + + if (path_is_read_only_fs("/") > 0) + return; + test(m, "exec-inaccessiblepaths-mount-propagation.service", 0, CLD_EXITED); } diff --git a/src/test/test-hexdecoct.c b/src/test/test-hexdecoct.c index 276f25d091..fcae427e74 100644 --- a/src/test/test-hexdecoct.c +++ b/src/test/test-hexdecoct.c @@ -87,27 +87,19 @@ static void test_undecchar(void) { } static void test_unhexmem(void) { - const char *hex = "efa214921"; + const char *hex = "efa2149213"; const char *hex_invalid = "efa214921o"; _cleanup_free_ char *hex2 = NULL; _cleanup_free_ void *mem = NULL; size_t len; - assert_se(unhexmem(hex, strlen(hex), &mem, &len) == 0); - assert_se(unhexmem(hex, strlen(hex) + 1, &mem, &len) == -EINVAL); assert_se(unhexmem(hex_invalid, strlen(hex_invalid), &mem, &len) == -EINVAL); + assert_se(unhexmem(hex, strlen(hex) + 1, &mem, &len) == -EINVAL); + assert_se(unhexmem(hex, strlen(hex) - 1, &mem, &len) == -EINVAL); + assert_se(unhexmem(hex, strlen(hex), &mem, &len) == 0); assert_se((hex2 = hexmem(mem, len))); - - free(mem); - - assert_se(memcmp(hex, hex2, strlen(hex)) == 0); - - free(hex2); - - assert_se(unhexmem(hex, strlen(hex) - 1, &mem, &len) == 0); - assert_se((hex2 = hexmem(mem, len))); - assert_se(memcmp(hex, hex2, strlen(hex) - 1) == 0); + assert_se(streq(hex, hex2)); } /* https://tools.ietf.org/html/rfc4648#section-10 */ diff --git a/src/test/test-proc-cmdline.c b/src/test/test-proc-cmdline.c index 4101678f19..12dac8585b 100644 --- a/src/test/test-proc-cmdline.c +++ b/src/test/test-proc-cmdline.c @@ -35,8 +35,8 @@ static int parse_item(const char *key, const char *value, void *data) { return 0; } -static void test_parse_proc_cmdline(void) { - assert_se(parse_proc_cmdline(parse_item, &obj, true) >= 0); +static void test_proc_cmdline_parse(void) { + assert_se(proc_cmdline_parse(parse_item, &obj, true) >= 0); } static void test_runlevel_to_target(void) { @@ -55,11 +55,101 @@ static void test_runlevel_to_target(void) { assert_se(streq_ptr(runlevel_to_target("rd.rescue"), SPECIAL_RESCUE_TARGET)); } +static void test_proc_cmdline_get_key(void) { + _cleanup_free_ char *value = NULL; + + putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar=quux wuff-piep=tuet zumm"); + + assert_se(proc_cmdline_get_key("", 0, &value) == -EINVAL); + assert_se(proc_cmdline_get_key("abc", 0, NULL) == 0); + assert_se(proc_cmdline_get_key("abc", 0, &value) == 0 && value == NULL); + assert_se(proc_cmdline_get_key("abc", PROC_CMDLINE_VALUE_OPTIONAL, &value) == 0 && value == NULL); + + assert_se(proc_cmdline_get_key("foo_bar", 0, &value) > 0 && streq_ptr(value, "quux")); + value = mfree(value); + assert_se(proc_cmdline_get_key("foo_bar", PROC_CMDLINE_VALUE_OPTIONAL, &value) > 0 && streq_ptr(value, "quux")); + value = mfree(value); + assert_se(proc_cmdline_get_key("foo-bar", 0, &value) > 0 && streq_ptr(value, "quux")); + value = mfree(value); + assert_se(proc_cmdline_get_key("foo-bar", PROC_CMDLINE_VALUE_OPTIONAL, &value) > 0 && streq_ptr(value, "quux")); + value = mfree(value); + assert_se(proc_cmdline_get_key("foo-bar", 0, NULL) == 0); + assert_se(proc_cmdline_get_key("foo-bar", PROC_CMDLINE_VALUE_OPTIONAL, NULL) == -EINVAL); + + assert_se(proc_cmdline_get_key("wuff-piep", 0, &value) > 0 && streq_ptr(value, "tuet")); + value = mfree(value); + assert_se(proc_cmdline_get_key("wuff-piep", PROC_CMDLINE_VALUE_OPTIONAL, &value) > 0 && streq_ptr(value, "tuet")); + value = mfree(value); + assert_se(proc_cmdline_get_key("wuff_piep", 0, &value) > 0 && streq_ptr(value, "tuet")); + value = mfree(value); + assert_se(proc_cmdline_get_key("wuff_piep", PROC_CMDLINE_VALUE_OPTIONAL, &value) > 0 && streq_ptr(value, "tuet")); + value = mfree(value); + assert_se(proc_cmdline_get_key("wuff_piep", 0, NULL) == 0); + assert_se(proc_cmdline_get_key("wuff_piep", PROC_CMDLINE_VALUE_OPTIONAL, NULL) == -EINVAL); + + assert_se(proc_cmdline_get_key("zumm", 0, &value) == 0 && value == NULL); + assert_se(proc_cmdline_get_key("zumm", PROC_CMDLINE_VALUE_OPTIONAL, &value) > 0 && value == NULL); + assert_se(proc_cmdline_get_key("zumm", 0, NULL) > 0); +} + +static void test_proc_cmdline_get_bool(void) { + bool value = false; + + putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar bar-waldo=1 x_y-z=0 quux=miep"); + + assert_se(proc_cmdline_get_bool("", &value) == -EINVAL); + assert_se(proc_cmdline_get_bool("abc", &value) == 0 && value == false); + assert_se(proc_cmdline_get_bool("foo_bar", &value) > 0 && value == true); + assert_se(proc_cmdline_get_bool("foo-bar", &value) > 0 && value == true); + assert_se(proc_cmdline_get_bool("bar-waldo", &value) > 0 && value == true); + assert_se(proc_cmdline_get_bool("bar_waldo", &value) > 0 && value == true); + assert_se(proc_cmdline_get_bool("x_y-z", &value) > 0 && value == false); + assert_se(proc_cmdline_get_bool("x-y-z", &value) > 0 && value == false); + assert_se(proc_cmdline_get_bool("x-y_z", &value) > 0 && value == false); + assert_se(proc_cmdline_get_bool("x_y_z", &value) > 0 && value == false); + assert_se(proc_cmdline_get_bool("quux", &value) == -EINVAL && value == false); +} + +static void test_proc_cmdline_key_streq(void) { + + assert_se(proc_cmdline_key_streq("", "")); + assert_se(proc_cmdline_key_streq("a", "a")); + assert_se(!proc_cmdline_key_streq("", "a")); + assert_se(!proc_cmdline_key_streq("a", "")); + assert_se(proc_cmdline_key_streq("a", "a")); + assert_se(!proc_cmdline_key_streq("a", "b")); + assert_se(proc_cmdline_key_streq("x-y-z", "x-y-z")); + assert_se(proc_cmdline_key_streq("x-y-z", "x_y_z")); + assert_se(proc_cmdline_key_streq("x-y-z", "x-y_z")); + assert_se(proc_cmdline_key_streq("x-y-z", "x_y-z")); + assert_se(proc_cmdline_key_streq("x_y-z", "x-y_z")); + assert_se(!proc_cmdline_key_streq("x_y-z", "x-z_z")); +} + +static void test_proc_cmdline_key_startswith(void) { + + assert_se(proc_cmdline_key_startswith("", "")); + assert_se(proc_cmdline_key_startswith("x", "")); + assert_se(!proc_cmdline_key_startswith("", "x")); + assert_se(proc_cmdline_key_startswith("x", "x")); + assert_se(!proc_cmdline_key_startswith("x", "y")); + assert_se(!proc_cmdline_key_startswith("foo-bar", "quux")); + assert_se(proc_cmdline_key_startswith("foo-bar", "foo")); + assert_se(proc_cmdline_key_startswith("foo-bar", "foo-bar")); + assert_se(proc_cmdline_key_startswith("foo-bar", "foo_bar")); + assert_se(proc_cmdline_key_startswith("foo-bar", "foo_")); + assert_se(!proc_cmdline_key_startswith("foo-bar", "foo_xx")); +} + int main(void) { log_parse_environment(); log_open(); - test_parse_proc_cmdline(); + test_proc_cmdline_parse(); + test_proc_cmdline_key_streq(); + test_proc_cmdline_key_startswith(); + test_proc_cmdline_get_key(); + test_proc_cmdline_get_bool(); test_runlevel_to_target(); return 0; diff --git a/src/test/test-stat-util.c b/src/test/test-stat-util.c index 6c34250a01..a48dca99e1 100644 --- a/src/test/test-stat-util.c +++ b/src/test/test-stat-util.c @@ -18,12 +18,14 @@ ***/ #include <fcntl.h> +#include <linux/magic.h> #include <unistd.h> #include "alloc-util.h" #include "fd-util.h" #include "fileio.h" #include "macro.h" +#include "missing.h" #include "stat-util.h" static void test_files_same(void) { @@ -60,9 +62,33 @@ static void test_is_symlink(void) { unlink(name_link); } +static void test_path_is_os_tree(void) { + assert_se(path_is_os_tree("/") > 0); + assert_se(path_is_os_tree("/etc") == 0); + assert_se(path_is_os_tree("/idontexist") == -ENOENT); +} + +static void test_path_check_fstype(void) { + assert_se(path_check_fstype("/run", TMPFS_MAGIC) > 0); + assert_se(path_check_fstype("/run", BTRFS_SUPER_MAGIC) == 0); + assert_se(path_check_fstype("/proc", PROC_SUPER_MAGIC) > 0); + assert_se(path_check_fstype("/proc", BTRFS_SUPER_MAGIC) == 0); + assert_se(path_check_fstype("/proc", BTRFS_SUPER_MAGIC) == 0); + assert_se(path_check_fstype("/i-dont-exist", BTRFS_SUPER_MAGIC) == -ENOENT); +} + +static void test_path_is_temporary_fs(void) { + assert_se(path_is_temporary_fs("/run") > 0); + assert_se(path_is_temporary_fs("/proc") == 0); + assert_se(path_is_temporary_fs("/i-dont-exist") == -ENOENT); +} + int main(int argc, char *argv[]) { test_files_same(); test_is_symlink(); + test_path_is_os_tree(); + test_path_check_fstype(); + test_path_is_temporary_fs(); return 0; } diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index 1dca375279..3af87f1388 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -192,14 +192,9 @@ static int load_link(link_config_ctx *ctx, const char *filename) { } static bool enable_name_policy(void) { - _cleanup_free_ char *value = NULL; - int r; - - r = get_proc_cmdline_key("net.ifnames=", &value); - if (r > 0 && streq(value, "0")) - return false; + bool b; - return true; + return proc_cmdline_get_bool("net.ifnames", &b) <= 0 || b; } int link_config_load(link_config_ctx *ctx) { diff --git a/src/udev/udevd.c b/src/udev/udevd.c index 895c6f271b..dd23054b0d 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -1357,10 +1357,10 @@ static int listen_fds(int *rctrl, int *rnetlink) { /* * read the kernel command line, in case we need to get into debug mode - * udev.log-priority=<level> syslog priority - * 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.log_priority=<level> syslog priority + * 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 */ static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { int r = 0; @@ -1370,25 +1370,46 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat if (!value) return 0; - if (streq(key, "udev.log-priority") && value) { + if (proc_cmdline_key_streq(key, "udev.log_priority")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + r = util_log_priority(value); if (r >= 0) log_set_max_level(r); - } else if (streq(key, "udev.event-timeout") && value) { + + } else if (proc_cmdline_key_streq(key, "udev.event_timeout")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + r = safe_atou64(value, &arg_event_timeout_usec); if (r >= 0) { arg_event_timeout_usec *= USEC_PER_SEC; arg_event_timeout_warn_usec = (arg_event_timeout_usec / 3) ? : 1; } - } else if (streq(key, "udev.children-max") && value) + + } else if (proc_cmdline_key_streq(key, "udev.children_max")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + r = safe_atou(value, &arg_children_max); - else if (streq(key, "udev.exec-delay") && value) + + } else if (proc_cmdline_key_streq(key, "udev.exec_delay")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + r = safe_atoi(value, &arg_exec_delay); - else if (startswith(key, "udev.")) + + } else if (startswith(key, "udev.")) log_warning("Unknown udev kernel command line option \"%s\"", key); if (r < 0) log_warning_errno(r, "Failed to parse \"%s=%s\", ignoring: %m", key, value); + return 0; } @@ -1649,7 +1670,7 @@ int main(int argc, char *argv[]) { if (r <= 0) goto exit; - r = parse_proc_cmdline(parse_proc_cmdline_item, NULL, true); + r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX); if (r < 0) log_warning_errno(r, "failed to parse kernel command line, ignoring: %m"); diff --git a/src/veritysetup/Makefile b/src/veritysetup/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/veritysetup/Makefile @@ -0,0 +1 @@ +../Makefile
\ No newline at end of file diff --git a/src/veritysetup/veritysetup-generator.c b/src/veritysetup/veritysetup-generator.c new file mode 100644 index 0000000000..519ac050f9 --- /dev/null +++ b/src/veritysetup/veritysetup-generator.c @@ -0,0 +1,253 @@ +/*** + This file is part of systemd. + + Copyright 2016 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stdbool.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "alloc-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "fstab-util.h" +#include "hexdecoct.h" +#include "id128-util.h" +#include "mkdir.h" +#include "parse-util.h" +#include "proc-cmdline.h" +#include "string-util.h" +#include "unit-name.h" + +static char *arg_dest = NULL; +static bool arg_enabled = true; +static char *arg_root_hash = NULL; +static char *arg_data_what = NULL; +static char *arg_hash_what = NULL; + +static int create_device(void) { + _cleanup_free_ char *u = NULL, *v = NULL, *d = NULL, *e = NULL; + _cleanup_fclose_ FILE *f = NULL; + const char *p, *to; + int r; + + /* If all three pieces of information are missing, then verity is turned off */ + if (!arg_root_hash && !arg_data_what && !arg_hash_what) + return 0; + + /* if one of them is missing however, the data is simply incomplete and this is an error */ + if (!arg_root_hash) + log_error("Verity information incomplete, root hash unspecified."); + if (!arg_data_what) + log_error("Verity information incomplete, root data device unspecified."); + if (!arg_hash_what) + log_error("Verity information incomplete, root hash device unspecified."); + + if (!arg_root_hash || !arg_data_what || !arg_hash_what) + return -EINVAL; + + log_debug("Using root verity data device %s,\n" + " hash device %s,\n" + " and root hash %s.", arg_data_what, arg_hash_what, arg_root_hash); + + p = strjoina(arg_dest, "/systemd-veritysetup@root.service"); + + u = fstab_node_to_udev_node(arg_data_what); + if (!u) + return log_oom(); + v = fstab_node_to_udev_node(arg_hash_what); + if (!v) + return log_oom(); + + r = unit_name_from_path(u, ".device", &d); + if (r < 0) + return log_error_errno(r, "Failed to to generate unit name: %m"); + r = unit_name_from_path(v, ".device", &e); + if (r < 0) + return log_error_errno(r, "Failed to to generate unit name: %m"); + + f = fopen(p, "wxe"); + if (!f) + return log_error_errno(errno, "Failed to create unit file %s: %m", p); + + fprintf(f, + "# Automatically generated by systemd-veritysetup-generator\n\n" + "[Unit]\n" + "Description=Integrity Protection Setup for %%I\n" + "Documentation=man:systemd-veritysetup-generator(8) man:systemd-veritysetup@.service(8)\n" + "SourcePath=/proc/cmdline\n" + "DefaultDependencies=no\n" + "Conflicts=umount.target\n" + "BindsTo=%s %s\n" + "IgnoreOnIsolate=true\n" + "After=cryptsetup-pre.target %s %s\n" + "Before=cryptsetup.target umount.target\n" + "\n[Service]\n" + "Type=oneshot\n" + "RemainAfterExit=yes\n" + "ExecStart=" ROOTLIBEXECDIR "/systemd-veritysetup attach root '%s' '%s' '%s'\n" + "ExecStop=" ROOTLIBEXECDIR "/systemd-veritysetup detach root\n", + d, e, + d, e, + u, v, arg_root_hash); + + r = fflush_and_check(f); + if (r < 0) + return log_error_errno(r, "Failed to write file %s: %m", p); + + to = strjoina(arg_dest, "/cryptsetup.target.requires/systemd-veritysetup@root.service"); + + (void) mkdir_parents(to, 0755); + if (symlink("../systemd-veritysetup@root.service", to) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", to); + + return 0; +} + +static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { + int r; + + if (streq(key, "systemd.verity")) { + + r = value ? parse_boolean(value) : 1; + if (r < 0) + log_warning("Failed to parse verity= kernel command line switch %s. Ignoring.", value); + else + arg_enabled = r; + + } else if (streq(key, "roothash")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + + r = free_and_strdup(&arg_root_hash, value); + if (r < 0) + return log_oom(); + + } else if (streq(key, "systemd.verity_root_data")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + + r = free_and_strdup(&arg_data_what, value); + if (r < 0) + return log_oom(); + + } else if (streq(key, "systemd.verity_root_hash")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + + r = free_and_strdup(&arg_hash_what, value); + if (r < 0) + return log_oom(); + } + + return 0; +} + +static int determine_devices(void) { + _cleanup_free_ void *m = NULL; + sd_id128_t root_uuid, verity_uuid; + char ids[37]; + size_t l; + int r; + + /* Try to automatically derive the root data and hash device paths from the root hash */ + + if (!arg_root_hash) + return 0; + + if (arg_data_what && arg_hash_what) + return 0; + + r = unhexmem(arg_root_hash, strlen(arg_root_hash), &m, &l); + if (r < 0) + return log_error_errno(r, "Failed to parse root hash: %s", arg_root_hash); + if (l < sizeof(sd_id128_t)) { + log_debug("Root hash is shorter than 128 bits (32 characters), ignoring for discovering verity partition."); + return 0; + } + + if (!arg_data_what) { + memcpy(&root_uuid, m, sizeof(root_uuid)); + + arg_data_what = strjoin("/dev/disk/by-partuuid/", id128_to_uuid_string(root_uuid, ids)); + if (!arg_data_what) + return log_oom(); + } + + if (!arg_hash_what) { + memcpy(&verity_uuid, (uint8_t*) m + l - sizeof(verity_uuid), sizeof(verity_uuid)); + + arg_hash_what = strjoin("/dev/disk/by-partuuid/", id128_to_uuid_string(verity_uuid, ids)); + if (!arg_hash_what) + return log_oom(); + } + + return 1; +} + +int main(int argc, char *argv[]) { + int r; + + if (argc > 1 && argc != 4) { + log_error("This program takes three or no arguments."); + return EXIT_FAILURE; + } + + if (argc > 1) + arg_dest = argv[1]; + + log_set_target(LOG_TARGET_SAFE); + log_parse_environment(); + log_open(); + + umask(0022); + + r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX); + if (r < 0) { + log_warning_errno(r, "Failed to parse kernel command line: %m"); + goto finish; + } + + /* For now we only support the root device on verity. Later on we might want to add support for /etc/veritytab + * or similar to define additional mappings */ + + if (!arg_enabled) { + r = 0; + goto finish; + } + + r = determine_devices(); + if (r < 0) + goto finish; + + r = create_device(); + if (r < 0) + goto finish; + + r = 0; + +finish: + free(arg_root_hash); + free(arg_data_what); + free(arg_hash_what); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/veritysetup/veritysetup.c b/src/veritysetup/veritysetup.c new file mode 100644 index 0000000000..f809d51638 --- /dev/null +++ b/src/veritysetup/veritysetup.c @@ -0,0 +1,154 @@ +/*** + This file is part of systemd. + + Copyright 2016 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <libcryptsetup.h> +#include <stdio.h> +#include <sys/stat.h> + +#include "log.h" +#include "hexdecoct.h" +#include "string-util.h" +#include "alloc-util.h" + +static char *arg_root_hash = NULL; +static char *arg_data_what = NULL; +static char *arg_hash_what = NULL; + +static int help(void) { + printf("%s attach VOLUME DATADEVICE HASHDEVICE ROOTHASH\n" + "%s detach VOLUME\n\n" + "Attaches or detaches an integrity protected block device.\n", + program_invocation_short_name, + program_invocation_short_name); + + return 0; +} + +static void log_glue(int level, const char *msg, void *usrptr) { + log_debug("%s", msg); +} + +int main(int argc, char *argv[]) { + struct crypt_device *cd = NULL; + int r; + + if (argc <= 1) { + r = help(); + goto finish; + } + + if (argc < 3) { + log_error("This program requires at least two arguments."); + r = -EINVAL; + goto finish; + } + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + umask(0022); + + if (streq(argv[1], "attach")) { + _cleanup_free_ void *m = NULL; + crypt_status_info status; + size_t l; + + if (argc < 6) { + log_error("attach requires at least two arguments."); + r = -EINVAL; + goto finish; + } + + r = unhexmem(argv[5], strlen(argv[5]), &m, &l); + if (r < 0) { + log_error("Failed to parse root hash."); + goto finish; + } + + r = crypt_init(&cd, argv[4]); + if (r < 0) { + log_error_errno(r, "Failed to open verity device %s: %m", argv[4]); + goto finish; + } + + crypt_set_log_callback(cd, log_glue, NULL); + + status = crypt_status(cd, argv[2]); + if (status == CRYPT_ACTIVE || status == CRYPT_BUSY) { + log_info("Volume %s already active.", argv[2]); + r = 0; + goto finish; + } + + r = crypt_load(cd, CRYPT_VERITY, NULL); + if (r < 0) { + log_error_errno(r, "Failed to load verity superblock: %m"); + goto finish; + } + + r = crypt_set_data_device(cd, argv[3]); + if (r < 0) { + log_error_errno(r, "Failed to configure data device: %m"); + goto finish; + } + + r = crypt_activate_by_volume_key(cd, argv[2], m, l, CRYPT_ACTIVATE_READONLY); + if (r < 0) { + log_error_errno(r, "Failed to set up verity device: %m"); + goto finish; + } + + } else if (streq(argv[1], "detach")) { + + r = crypt_init_by_name(&cd, argv[2]); + if (r == -ENODEV) { + log_info("Volume %s already inactive.", argv[2]); + goto finish; + } else if (r < 0) { + log_error_errno(r, "crypt_init_by_name() failed: %m"); + goto finish; + } + + crypt_set_log_callback(cd, log_glue, NULL); + + r = crypt_deactivate(cd, argv[2]); + if (r < 0) { + log_error_errno(r, "Failed to deactivate: %m"); + goto finish; + } + + } else { + log_error("Unknown verb %s.", argv[1]); + r = -EINVAL; + goto finish; + } + + r = 0; + +finish: + if (cd) + crypt_free(cd); + + free(arg_root_hash); + free(arg_data_what); + free(arg_hash_what); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/volatile-root/Makefile b/src/volatile-root/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/volatile-root/Makefile @@ -0,0 +1 @@ +../Makefile
\ No newline at end of file diff --git a/src/volatile-root/volatile-root.c b/src/volatile-root/volatile-root.c new file mode 100644 index 0000000000..3c0b6fa1de --- /dev/null +++ b/src/volatile-root/volatile-root.c @@ -0,0 +1,157 @@ +/*** + This file is part of systemd. + + Copyright 2016 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <sys/mount.h> + +#include "alloc-util.h" +#include "fs-util.h" +#include "mkdir.h" +#include "mount-util.h" +#include "stat-util.h" +#include "volatile-util.h" +#include "string-util.h" +#include "path-util.h" + +static int make_volatile(const char *path) { + _cleanup_free_ char *old_usr = NULL; + int r; + + r = path_is_mount_point(path, NULL, AT_SYMLINK_FOLLOW); + if (r < 0) + return log_error_errno(r, "Couldn't determine whether %s is a mount point: %m", path); + if (r == 0) { + log_error("%s is not a mount point.", path); + return -EINVAL; + } + + r = path_is_temporary_fs(path); + if (r < 0) + return log_error_errno(r, "Couldn't determine whether %s is a temporary file system: %m", path); + if (r > 0) { + log_info("%s already is a temporary file system.", path); + return 0; + } + + r = chase_symlinks("/usr", path, CHASE_PREFIX_ROOT, &old_usr); + if (r < 0) + return log_error_errno(r, "/usr not available in old root: %m"); + + r = mkdir_p("/run/systemd/volatile-sysroot", 0700); + if (r < 0) + return log_error_errno(r, "Couldn't generate volatile sysroot directory: %m"); + + r = mount_verbose(LOG_ERR, "tmpfs", "/run/systemd/volatile-sysroot", "tmpfs", MS_STRICTATIME, "mode=755"); + if (r < 0) + goto finish_rmdir; + + if (mkdir("/run/systemd/volatile-sysroot/usr", 0755) < 0) { + r = -errno; + goto finish_umount; + } + + r = mount_verbose(LOG_ERR, old_usr, "/run/systemd/volatile-sysroot/usr", NULL, MS_BIND|MS_REC, NULL); + if (r < 0) + goto finish_umount; + + r = bind_remount_recursive("/run/systemd/volatile-sysroot/usr", true, NULL); + if (r < 0) + goto finish_umount; + + r = umount_recursive(path, 0); + if (r < 0) { + log_error_errno(r, "Failed to unmount %s: %m", path); + goto finish_umount; + } + + if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) + log_warning_errno(errno, "Failed to remount %s MS_SLAVE|MS_REC: %m", path); + + r = mount_verbose(LOG_ERR, "/run/systemd/volatile-sysroot", path, NULL, MS_MOVE, NULL); + +finish_umount: + (void) umount_recursive("/run/systemd/volatile-sysroot", 0); + +finish_rmdir: + (void) rmdir("/run/systemd/volatile-sysroot"); + + return r; +} + +int main(int argc, char *argv[]) { + VolatileMode m = _VOLATILE_MODE_INVALID; + const char *path; + int r; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + if (argc > 3) { + log_error("Too many arguments. Expected directory and mode."); + r = -EINVAL; + goto finish; + } + + r = query_volatile_mode(&m); + if (r < 0) { + log_error_errno(r, "Failed to determine volatile mode from kernel command line."); + goto finish; + } + if (r == 0 && argc >= 2) { + /* The kernel command line always wins. However if nothing was set there, the argument passed here wins instead. */ + m = volatile_mode_from_string(argv[1]); + if (m < 0) { + log_error("Couldn't parse volatile mode: %s", argv[1]); + r = -EINVAL; + goto finish; + } + } + + if (argc < 3) + path = "/sysroot"; + else { + path = argv[2]; + + if (isempty(path)) { + log_error("Directory name cannot be empty."); + r = -EINVAL; + goto finish; + } + if (!path_is_absolute(path)) { + log_error("Directory must be specified as absolute path."); + r = -EINVAL; + goto finish; + } + if (path_equal(path, "/")) { + log_error("Directory cannot be the root directory."); + r = -EINVAL; + goto finish; + } + } + + if (m != VOLATILE_YES) { + r = 0; + goto finish; + } + + r = make_volatile(path); + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} |