Merge pull request #4879 from poettering/systemd

This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2017-01-14 21:29:27 -05:00
87 changed files with 2788 additions and 1142 deletions
+10 -29
View File
@@ -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 = proc_cmdline_get_bool("systemd.unified_cgroup_hierarchy", &b);
if (r < 0)
return false;
r = get_proc_cmdline_key("systemd.unified_cgroup_hierarchy=", &value);
if (r < 0)
return false;
if (r == 0)
return (wanted = 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 = proc_cmdline_get_bool("systemd.legacy_systemd_cgroup_controller", &b);
if (r < 0)
return false;
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;
}
return wanted;
return (wanted = r > 0 ? b : false);
}
bool cg_is_legacy_systemd_controller_wanted(void) {
+4 -2
View File
@@ -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;
}
+7 -6
View File
@@ -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;
}
+15 -10
View File
@@ -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)
+3
View File
@@ -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));
+122 -25
View File
@@ -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;
value = strchr(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(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[] = {
+29 -4
View File
@@ -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;
}
+28 -10
View File
@@ -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);
/* 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;
/* 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;
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. */
p = strjoina(path, "/etc/os-release");
r = access(p, F_OK);
/* 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);
}
+1
View File
@@ -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
+23 -11
View File
@@ -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 (access(p, X_OK) < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified init binary %s does not exist.", p);
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);
return sd_bus_error_set_errnof(error, r, "Could not check whether init binary %s is executable: %m", init);
}
}
rt = strdup(root);
+1 -1
View File
@@ -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.",
+75 -50
View File
@@ -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 (STR_IN_SET(key, "systemd.unit", "rd.systemd.unit")) {
if (!in_initrd())
return free_and_strdup(&arg_default_unit, value);
if (proc_cmdline_value_missing(key, value))
return 0;
} else if (streq(key, "rd.systemd.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();
}
if (in_initrd())
return free_and_strdup(&arg_default_unit, value);
} else if (proc_cmdline_key_streq(key, "systemd.dump_core")) {
} else if (streq(key, "systemd.dump_core") && value) {
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) {
} else if (proc_cmdline_key_streq(key, "systemd.confirm_spawn")) {
char *s;
arg_confirm_spawn = mfree(arg_confirm_spawn);
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 (streq(key, "systemd.default_standard_output") && value) {
} else if (proc_cmdline_key_streq(key, "systemd.default_standard_output")) {
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")) {
r = set_machine_id(value);
if (r < 0)
log_warning("MachineID '%s' is not valid. Ignoring.", value);
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);
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");
}
+11 -13
View File
@@ -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));
+56 -46
View File
@@ -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;
}
+33 -33
View File
@@ -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;
}
+29 -32
View File
@@ -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");
+5 -2
View File
@@ -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);
+12
View File
@@ -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;
+9 -3
View File
@@ -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");
+131 -19
View File
@@ -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")) {
} else if (streq(key, "mount.usr") && value) {
if (proc_cmdline_value_missing(key, value))
return 0;
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);
+158 -314
View File
@@ -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);
_cleanup_close_ int fd = -1;
_cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
int r, k;
r = open_parent(devnum, &fd);
if (r <= 0)
return r;
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;
}
errno = 0;
pl = blkid_probe_get_partitions(b);
if (!pl) {
if (errno == 0)
return log_oom();
return log_error_errno(errno, "%s: failed to list partitions: %m", node);
}
e = udev_enumerate_new(udev);
if (!e)
return log_oom();
r = udev_enumerate_add_match_parent(e, parent);
if (r < 0)
return log_oom();
return log_error_errno(r, "Failed to dissect: %m");
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)) {
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 (boot) {
k = add_boot(boot);
if (m->partitions[PARTITION_SWAP].found) {
k = add_swap(m->partitions[PARTITION_SWAP].node);
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_ESP].found) {
k = add_esp(m->partitions + PARTITION_ESP);
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_HOME].found) {
k = add_partition_mount(m->partitions + PARTITION_HOME, "home", "/home", "Home Partition");
if (k < 0)
r = k;
}
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;
@@ -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;
}
+67 -62
View File
@@ -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,7 +323,56 @@ int pull_make_verification_jobs(
return 0;
}
static int verify_one(PullJob *checksum_job, PullJob *job) {
_cleanup_free_ char *fn = NULL;
const char *line, *p;
int r;
assert(checksum_job);
if (!job)
return 0;
assert(IN_SET(job->state, PULL_JOB_DONE, PULL_JOB_FAILED));
/* 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;
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 server-side file name.");
return -EBADMSG;
}
line = strjoina(job->checksum, " *", fn, "\n");
p = memmem(checksum_job->payload,
checksum_job->payload_size,
line,
strlen(line));
if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) {
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.", job->url);
return 1;
}
int pull_verify(PullJob *main_job,
PullJob *roothash_job,
PullJob *settings_job,
PullJob *checksum_job,
PullJob *signature_job) {
@@ -328,7 +380,6 @@ int pull_verify(PullJob *main_job,
_cleanup_close_pair_ int gpg_pipe[2] = { -1, -1 };
_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;
@@ -342,6 +393,7 @@ int pull_verify(PullJob *main_job,
assert(main_job->calc_checksum);
assert(main_job->checksum);
assert(checksum_job->state == PULL_JOB_DONE);
if (!checksum_job->payload || checksum_job->payload_size <= 0) {
@@ -349,64 +401,17 @@ int pull_verify(PullJob *main_job,
return -EBADMSG;
}
r = import_url_last_component(main_job->url, &fn);
r = verify_one(checksum_job, main_job);
if (r < 0)
return log_oom();
return r;
if (!filename_is_valid(fn)) {
log_error("Cannot verify checksum, could not determine valid server-side file name.");
return -EBADMSG;
}
r = verify_one(checksum_job, roothash_job);
if (r < 0)
return r;
line = strjoina(main_job->checksum, " *", fn, "\n");
p = memmem(checksum_job->payload,
checksum_job->payload_size,
line,
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.");
return -EBADMSG;
}
log_info("SHA256 checksum of %s is valid.", main_job->url);
assert(!settings_job || IN_SET(settings_job->state, PULL_JOB_DONE, PULL_JOB_FAILED));
if (settings_job &&
settings_job->state == PULL_JOB_DONE &&
settings_job->error == 0 &&
!settings_job->etag_exists) {
_cleanup_free_ char *settings_fn = NULL;
assert(settings_job->calc_checksum);
assert(settings_job->checksum);
r = import_url_last_component(settings_job->url, &settings_fn);
if (r < 0)
return log_oom();
if (!filename_is_valid(settings_fn)) {
log_error("Cannot verify checksum, could not determine server-side settings file name.");
return -EBADMSG;
}
line = strjoina(settings_job->checksum, " *", settings_fn, "\n");
p = memmem(checksum_job->payload,
checksum_job->payload_size,
line,
strlen(line));
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;
}
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;
+2 -2
View File
@@ -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);
+194 -81
View File
@@ -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->roothash) {
r = raw_pull_copy_auxiliary_file(i, ".roothash", &i->roothash_path);
if (r < 0)
return r;
}
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");
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);
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);
r = raw_pull_job_on_open_disk_generic(i, j, "raw", &i->temp_path);
if (r < 0)
return log_oom();
r = tempfn_random(i->final_path, NULL, &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_roothash(PullJob *j) {
RawPull *i;
assert(j);
assert(j->userdata);
i = j->userdata;
assert(i->roothash_job == j);
return raw_pull_job_on_open_disk_generic(i, j, "roothash", &i->roothash_temp_path);
}
static int raw_pull_job_on_open_disk_settings(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);
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();
mkdir_parents_label(i->settings_temp_path, 0700);
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);
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)
+1 -1
View File
@@ -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);
+45 -41
View File
@@ -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 */
+13 -1
View File
@@ -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;
+57 -20
View File
@@ -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",
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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());
+1 -1
View File
@@ -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)
+2 -2
View File
@@ -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"
+6 -2
View File
@@ -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");
-18
View File
@@ -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;
}
+1 -10
View File
@@ -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);
+48 -29
View File
@@ -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);
+10 -5
View File
@@ -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;
}
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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);
}
+72 -52
View File
@@ -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)
/* OK, we have found a file system, that's our root partition then. */
(void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
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,
};
t = n = NULL;
m->encrypted = streq(fstype, "crypto_LUKS");
*ret = m;
m = NULL;
return 0;
}
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,
};
t = n = NULL;
m->encrypted = streq(fstype, "crypto_LUKS");
*ret = m;
m = NULL;
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
+4 -1
View File
@@ -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);
+1 -1
View File
@@ -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;
+42 -7
View File
@@ -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;
}
+56 -77
View File
@@ -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");
return log_error_errno(errno, "Failed to set \"/\" mount propagation to private: %m");
NULSTR_FOREACH(i, move_mounts) {
char new_mount[PATH_MAX];
struct stat sb;
size_t n;
FOREACH_STRING(i, "/sys", "/dev", "/run", "/proc") {
_cleanup_free_ char *chased = NULL;
n = snprintf(new_mount, sizeof new_mount, "%s%s", new_root, i);
if (n >= sizeof new_mount) {
bool move = mountflags & MS_MOVE;
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);
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);
}
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");
+68
View File
@@ -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;
}
+32
View File
@@ -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);
+1 -1
View File
@@ -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;
+13
View File
@@ -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);
}
+5 -13
View File
@@ -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 */
+93 -3
View File
@@ -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;
+26
View File
@@ -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;
}
+2 -7
View File
@@ -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;
bool b;
r = get_proc_cmdline_key("net.ifnames=", &value);
if (r > 0 && streq(value, "0"))
return false;
return true;
return proc_cmdline_get_bool("net.ifnames", &b) <= 0 || b;
}
int link_config_load(link_config_ctx *ctx) {
+31 -10
View File
@@ -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");
+1
View File
@@ -0,0 +1 @@
../Makefile
+253
View File
@@ -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;
}
+154
View File
@@ -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;
}
+1
View File
@@ -0,0 +1 @@
../Makefile
+157
View File
@@ -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;
}