mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-03-08 20:58:20 +03:00
Merge pull request #18958 from poettering/dissect-no-root
dissect-image: support images without rootfs but with /usr partition + support simple partition versioning via strverscmp() on part label
This commit is contained in:
commit
b880ac2c15
9
TODO
9
TODO
@ -321,12 +321,6 @@ Features:
|
||||
* busctl: maybe expose a verb "ping" for pinging a dbus service to see if it
|
||||
exists and responds.
|
||||
|
||||
* when systemd-nspawn and suchlike dissect an OS image, and there are multiple
|
||||
root partitions, do an strverscmp() on the partition label and boot
|
||||
first. That is inspired how sd-boot figures out which kernel to boot, and
|
||||
thus allows defining OS images which can be A/B updated and we default to the
|
||||
newest version automatically, both in nspawn and in sd-boot
|
||||
|
||||
* systemd-gpt-auto should probably set x-systemd.growfs on the mounts it
|
||||
creates
|
||||
|
||||
@ -848,9 +842,6 @@ Features:
|
||||
|
||||
* journalctl: make sure -f ends when the container indicated by -M terminates
|
||||
|
||||
* mount: automatically search for "main" partition of an image has multiple
|
||||
partitions
|
||||
|
||||
* in nss-systemd, if we run inside of RootDirectory= with PrivateUsers= set,
|
||||
find a way to map the User=/Group= of the service to the right name. This way
|
||||
a user/group for a service only has to exist on the host for the right
|
||||
|
@ -162,7 +162,14 @@ partition is listed in `/etc/fstab` or with `root=` on the kernel command line,
|
||||
it _must_ take precedence over automatically discovered partitions. If a
|
||||
`/home/`, `/usr/`, `/srv/`, `/boot/`, `/var/`, `/var/tmp/`, `/efi/` or `/boot/`
|
||||
directory is found to be populated already in the root partition, the automatic
|
||||
discovery _must not_ mount any discovered file system over it.
|
||||
discovery _must not_ mount any discovered file system over it. Optionally, in
|
||||
case of the root, `/usr/` and their Verity partitions instead of strictly
|
||||
mounting the first suitable partition an OS might choose to mount the partition
|
||||
whose label compares the highest according to `strverscmp()` or a similar
|
||||
logic, in order to implement a simple partition-based A/B versioning
|
||||
scheme. The precise rules are left for the implementation to decide, but when
|
||||
in doubt earlier partitions (by their index) should always win over later
|
||||
partitions if the label comparison is inconclusive.
|
||||
|
||||
A *container* *manager* should automatically discover and mount the root,
|
||||
`/usr/`, `/home/`, `/srv/`, `/var/`, `/var/tmp/` partitions inside a container
|
||||
|
@ -3247,7 +3247,6 @@ static int apply_mount_namespace(
|
||||
propagate_dir,
|
||||
incoming_dir,
|
||||
root_dir || root_image ? params->notify_socket : NULL,
|
||||
DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK,
|
||||
error_path);
|
||||
|
||||
/* If we couldn't set up the namespace this is probably due to a missing capability. setup_namespace() reports
|
||||
|
@ -1802,7 +1802,6 @@ int setup_namespace(
|
||||
const char *propagate_dir,
|
||||
const char *incoming_dir,
|
||||
const char *notify_socket,
|
||||
DissectImageFlags dissect_image_flags,
|
||||
char **error_path) {
|
||||
|
||||
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
|
||||
@ -1813,6 +1812,13 @@ int setup_namespace(
|
||||
MountEntry *m = NULL, *mounts = NULL;
|
||||
bool require_prefix = false, setup_propagate = false;
|
||||
const char *root, *extension_dir = "/run/systemd/unit-extensions";
|
||||
DissectImageFlags dissect_image_flags =
|
||||
DISSECT_IMAGE_GENERIC_ROOT |
|
||||
DISSECT_IMAGE_REQUIRE_ROOT |
|
||||
DISSECT_IMAGE_DISCARD_ON_LOOP |
|
||||
DISSECT_IMAGE_RELAX_VAR_CHECK |
|
||||
DISSECT_IMAGE_FSCK |
|
||||
DISSECT_IMAGE_USR_NO_ROOT;
|
||||
size_t n_mounts;
|
||||
int r;
|
||||
|
||||
@ -1825,8 +1831,6 @@ int setup_namespace(
|
||||
mount_flags = MS_SHARED;
|
||||
|
||||
if (root_image) {
|
||||
dissect_image_flags |= DISSECT_IMAGE_REQUIRE_ROOT;
|
||||
|
||||
/* Make the whole image read-only if we can determine that we only access it in a read-only fashion. */
|
||||
if (root_read_only(read_only_paths,
|
||||
ns_info->protect_system) &&
|
||||
|
@ -143,7 +143,6 @@ int setup_namespace(
|
||||
const char *propagate_dir,
|
||||
const char *incoming_dir,
|
||||
const char *notify_socket,
|
||||
DissectImageFlags dissected_image_flags,
|
||||
char **error_path);
|
||||
|
||||
#define RUN_SYSTEMD_EMPTY "/run/systemd/empty"
|
||||
|
@ -44,7 +44,12 @@ static const char *arg_image = NULL;
|
||||
static const char *arg_path = NULL;
|
||||
static const char *arg_source = NULL;
|
||||
static const char *arg_target = NULL;
|
||||
static DissectImageFlags arg_flags = DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK;
|
||||
static DissectImageFlags arg_flags =
|
||||
DISSECT_IMAGE_GENERIC_ROOT |
|
||||
DISSECT_IMAGE_DISCARD_ON_LOOP |
|
||||
DISSECT_IMAGE_RELAX_VAR_CHECK |
|
||||
DISSECT_IMAGE_FSCK |
|
||||
DISSECT_IMAGE_USR_NO_ROOT;
|
||||
static VeritySettings arg_verity_settings = VERITY_SETTINGS_DEFAULT;
|
||||
static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
|
||||
static PagerFlags arg_pager_flags = 0;
|
||||
@ -293,6 +298,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
arg_image = argv[optind];
|
||||
arg_path = argv[optind + 1];
|
||||
arg_flags |= DISSECT_IMAGE_REQUIRE_ROOT;
|
||||
break;
|
||||
|
||||
case ACTION_COPY_FROM:
|
||||
@ -304,7 +310,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_source = argv[optind + 1];
|
||||
arg_target = argc > optind + 2 ? argv[optind + 2] : "-" /* this means stdout */ ;
|
||||
|
||||
arg_flags |= DISSECT_IMAGE_READ_ONLY;
|
||||
arg_flags |= DISSECT_IMAGE_READ_ONLY | DISSECT_IMAGE_REQUIRE_ROOT;
|
||||
break;
|
||||
|
||||
case ACTION_COPY_TO:
|
||||
@ -322,6 +328,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_target = argv[optind + 1];
|
||||
}
|
||||
|
||||
arg_flags |= DISSECT_IMAGE_REQUIRE_ROOT;
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -460,7 +467,7 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) {
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
t = table_new("rw", "designator", "partition uuid", "fstype", "architecture", "verity", "node", "partno");
|
||||
t = table_new("rw", "designator", "partition uuid", "partition label", "fstype", "architecture", "verity", "node", "partno");
|
||||
if (!t)
|
||||
return log_oom();
|
||||
|
||||
@ -489,6 +496,7 @@ static int action_dissect(DissectedImage *m, LoopDevice *d) {
|
||||
|
||||
r = table_add_many(
|
||||
t,
|
||||
TABLE_STRING, p->label,
|
||||
TABLE_STRING, p->fstype,
|
||||
TABLE_STRING, architecture_to_string(p->architecture));
|
||||
if (r < 0)
|
||||
|
@ -1291,7 +1291,11 @@ static int run(int argc, char *argv[]) {
|
||||
|
||||
r = mount_image_privately_interactively(
|
||||
arg_image,
|
||||
DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_VALIDATE_OS|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK,
|
||||
DISSECT_IMAGE_GENERIC_ROOT |
|
||||
DISSECT_IMAGE_REQUIRE_ROOT |
|
||||
DISSECT_IMAGE_VALIDATE_OS |
|
||||
DISSECT_IMAGE_RELAX_VAR_CHECK |
|
||||
DISSECT_IMAGE_FSCK,
|
||||
&unlink_dir,
|
||||
&loop_device,
|
||||
&decrypted_image);
|
||||
|
@ -665,7 +665,13 @@ static int enumerate_partitions(dev_t devnum) {
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
r = dissect_image(fd, NULL, NULL, DISSECT_IMAGE_GPT_ONLY|DISSECT_IMAGE_NO_UDEV, &m);
|
||||
r = dissect_image(
|
||||
fd,
|
||||
NULL, NULL,
|
||||
DISSECT_IMAGE_GPT_ONLY|
|
||||
DISSECT_IMAGE_NO_UDEV|
|
||||
DISSECT_IMAGE_USR_NO_ROOT,
|
||||
&m);
|
||||
if (r == -ENOPKG) {
|
||||
log_debug_errno(r, "No suitable partition table found, ignoring.");
|
||||
return 0;
|
||||
|
@ -2150,7 +2150,10 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
r = mount_image_privately_interactively(
|
||||
arg_image,
|
||||
DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_VALIDATE_OS|DISSECT_IMAGE_RELAX_VAR_CHECK|
|
||||
DISSECT_IMAGE_GENERIC_ROOT |
|
||||
DISSECT_IMAGE_REQUIRE_ROOT |
|
||||
DISSECT_IMAGE_VALIDATE_OS |
|
||||
DISSECT_IMAGE_RELAX_VAR_CHECK |
|
||||
(arg_action == ACTION_UPDATE_CATALOG ? DISSECT_IMAGE_FSCK : DISSECT_IMAGE_READ_ONLY),
|
||||
&unlink_dir,
|
||||
&loop_device,
|
||||
|
@ -3581,8 +3581,12 @@ static int outer_child(
|
||||
* makes sure ESP partitions and userns are compatible. */
|
||||
|
||||
r = dissected_image_mount_and_warn(
|
||||
dissected_image, directory, arg_uid_shift,
|
||||
DISSECT_IMAGE_MOUNT_ROOT_ONLY|DISSECT_IMAGE_DISCARD_ON_LOOP|
|
||||
dissected_image,
|
||||
directory,
|
||||
arg_uid_shift,
|
||||
DISSECT_IMAGE_MOUNT_ROOT_ONLY|
|
||||
DISSECT_IMAGE_DISCARD_ON_LOOP|
|
||||
DISSECT_IMAGE_USR_NO_ROOT|
|
||||
(arg_read_only ? DISSECT_IMAGE_READ_ONLY : DISSECT_IMAGE_FSCK)|
|
||||
(arg_start_mode == START_BOOT ? DISSECT_IMAGE_VALIDATE_OS : 0));
|
||||
if (r < 0)
|
||||
@ -3669,8 +3673,14 @@ static int outer_child(
|
||||
|
||||
if (dissected_image) {
|
||||
/* Now we know the uid shift, let's now mount everything else that might be in the image. */
|
||||
r = dissected_image_mount(dissected_image, directory, arg_uid_shift,
|
||||
DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY|DISSECT_IMAGE_DISCARD_ON_LOOP|(arg_read_only ? DISSECT_IMAGE_READ_ONLY : DISSECT_IMAGE_FSCK));
|
||||
r = dissected_image_mount(
|
||||
dissected_image,
|
||||
directory,
|
||||
arg_uid_shift,
|
||||
DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY|
|
||||
DISSECT_IMAGE_DISCARD_ON_LOOP|
|
||||
DISSECT_IMAGE_USR_NO_ROOT|
|
||||
(arg_read_only ? DISSECT_IMAGE_READ_ONLY : DISSECT_IMAGE_FSCK));
|
||||
if (r == -EUCLEAN)
|
||||
return log_error_errno(r, "File system check for image failed: %m");
|
||||
if (r < 0)
|
||||
@ -5378,7 +5388,11 @@ static int run(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
} else {
|
||||
DissectImageFlags dissect_image_flags = DISSECT_IMAGE_REQUIRE_ROOT | DISSECT_IMAGE_RELAX_VAR_CHECK;
|
||||
DissectImageFlags dissect_image_flags =
|
||||
DISSECT_IMAGE_GENERIC_ROOT |
|
||||
DISSECT_IMAGE_REQUIRE_ROOT |
|
||||
DISSECT_IMAGE_RELAX_VAR_CHECK |
|
||||
DISSECT_IMAGE_USR_NO_ROOT;
|
||||
assert(arg_image);
|
||||
assert(!arg_template);
|
||||
|
||||
|
@ -380,7 +380,16 @@ static int portable_extract_by_path(
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to create temporary directory: %m");
|
||||
|
||||
r = dissect_image(d->fd, NULL, NULL, DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_DISCARD_ON_LOOP|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
|
||||
r = dissect_image(
|
||||
d->fd,
|
||||
NULL, NULL,
|
||||
DISSECT_IMAGE_READ_ONLY |
|
||||
DISSECT_IMAGE_GENERIC_ROOT |
|
||||
DISSECT_IMAGE_REQUIRE_ROOT |
|
||||
DISSECT_IMAGE_DISCARD_ON_LOOP |
|
||||
DISSECT_IMAGE_RELAX_VAR_CHECK |
|
||||
DISSECT_IMAGE_USR_NO_ROOT,
|
||||
&m);
|
||||
if (r == -ENOPKG)
|
||||
sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Couldn't identify a suitable partition table or file system in '%s'.", path);
|
||||
else if (r == -EADDRNOTAVAIL)
|
||||
|
@ -1198,7 +1198,13 @@ int image_read_metadata(Image *i) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dissect_image(d->fd, NULL, NULL, DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_RELAX_VAR_CHECK, &m);
|
||||
r = dissect_image(
|
||||
d->fd,
|
||||
NULL, NULL,
|
||||
DISSECT_IMAGE_GENERIC_ROOT |
|
||||
DISSECT_IMAGE_REQUIRE_ROOT |
|
||||
DISSECT_IMAGE_RELAX_VAR_CHECK |
|
||||
DISSECT_IMAGE_USR_NO_ROOT, &m);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -456,6 +456,22 @@ static int device_wait_for_initialization_harder(
|
||||
|
||||
#define DEVICE_TIMEOUT_USEC (45 * USEC_PER_SEC)
|
||||
|
||||
static void dissected_partition_done(DissectedPartition *p) {
|
||||
assert(p);
|
||||
|
||||
free(p->fstype);
|
||||
free(p->node);
|
||||
free(p->label);
|
||||
free(p->decrypted_fstype);
|
||||
free(p->decrypted_node);
|
||||
free(p->mount_options);
|
||||
|
||||
*p = (DissectedPartition) {
|
||||
.partno = -1,
|
||||
.architecture = -1
|
||||
};
|
||||
}
|
||||
|
||||
int dissect_image(
|
||||
int fd,
|
||||
const VeritySettings *verity,
|
||||
@ -490,7 +506,9 @@ int dissect_image(
|
||||
/* Probes a disk image, and returns information about what it found in *ret.
|
||||
*
|
||||
* Returns -ENOPKG if no suitable partition table or file system could be found.
|
||||
* Returns -EADDRNOTAVAIL if a root hash was specified but no matching root/verity partitions found. */
|
||||
* Returns -EADDRNOTAVAIL if a root hash was specified but no matching root/verity partitions found.
|
||||
* Returns -ENXIO if we couldn't find any partition suitable as root or /usr partition
|
||||
* Returns -ENOTUNIQ if we only found multiple generic partitions and thus don't know what to do with that */
|
||||
|
||||
if (verity && verity->root_hash) {
|
||||
sd_id128_t fsuuid, vuuid;
|
||||
@ -612,7 +630,7 @@ int dissect_image(
|
||||
}
|
||||
|
||||
if ((!(flags & DISSECT_IMAGE_GPT_ONLY) &&
|
||||
(flags & DISSECT_IMAGE_REQUIRE_ROOT)) ||
|
||||
(flags & DISSECT_IMAGE_GENERIC_ROOT)) ||
|
||||
(flags & DISSECT_IMAGE_NO_PARTITION_TABLE)) {
|
||||
const char *usage = NULL;
|
||||
|
||||
@ -727,7 +745,7 @@ int dissect_image(
|
||||
if (is_gpt) {
|
||||
PartitionDesignator designator = _PARTITION_DESIGNATOR_INVALID;
|
||||
int architecture = _ARCHITECTURE_INVALID;
|
||||
const char *stype, *sid, *fstype = NULL;
|
||||
const char *stype, *sid, *fstype = NULL, *label;
|
||||
sd_id128_t type_id, id;
|
||||
bool rw = true;
|
||||
|
||||
@ -743,6 +761,8 @@ int dissect_image(
|
||||
if (sd_id128_from_string(stype, &type_id) < 0)
|
||||
continue;
|
||||
|
||||
label = blkid_partition_get_name(pp); /* libblkid returns NULL here if empty */
|
||||
|
||||
if (sd_id128_equal(type_id, GPT_HOME)) {
|
||||
|
||||
check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
|
||||
@ -997,12 +1017,21 @@ int dissect_image(
|
||||
}
|
||||
|
||||
if (designator != _PARTITION_DESIGNATOR_INVALID) {
|
||||
_cleanup_free_ char *t = NULL, *n = NULL, *o = NULL;
|
||||
_cleanup_free_ char *t = NULL, *n = NULL, *o = NULL, *l = NULL;
|
||||
const char *options = NULL;
|
||||
|
||||
/* First one wins */
|
||||
if (m->partitions[designator].found)
|
||||
continue;
|
||||
if (m->partitions[designator].found) {
|
||||
/* For most partition types the first one we see wins. Except for the
|
||||
* rootfs and /usr, where we do a version compare of the label, and
|
||||
* let the newest version win. This permits a simple A/B versioning
|
||||
* scheme in OS images. */
|
||||
|
||||
if (!PARTITION_DESIGNATOR_VERSIONED(designator) ||
|
||||
strverscmp_improved(m->partitions[designator].label, label) >= 0)
|
||||
continue;
|
||||
|
||||
dissected_partition_done(m->partitions + designator);
|
||||
}
|
||||
|
||||
if (fstype) {
|
||||
t = strdup(fstype);
|
||||
@ -1014,6 +1043,12 @@ int dissect_image(
|
||||
if (!n)
|
||||
return -ENOMEM;
|
||||
|
||||
if (label) {
|
||||
l = strdup(label);
|
||||
if (!l)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
options = mount_options_from_designator(mount_options, designator);
|
||||
if (options) {
|
||||
o = strdup(options);
|
||||
@ -1028,6 +1063,7 @@ int dissect_image(
|
||||
.architecture = architecture,
|
||||
.node = TAKE_PTR(n),
|
||||
.fstype = TAKE_PTR(t),
|
||||
.label = TAKE_PTR(l),
|
||||
.uuid = id,
|
||||
.mount_options = TAKE_PTR(o),
|
||||
};
|
||||
@ -1100,46 +1136,64 @@ int dissect_image(
|
||||
m->partitions[PARTITION_ROOT_SECONDARY_VERITY].found = false;
|
||||
m->partitions[PARTITION_USR_SECONDARY].found = false;
|
||||
m->partitions[PARTITION_USR_SECONDARY_VERITY].found = false;
|
||||
} else {
|
||||
/* No root partition found? Then let's see if ther's one for the secondary architecture. And if not
|
||||
* either, then check if there's a single generic one, and use that. */
|
||||
|
||||
if (m->partitions[PARTITION_ROOT_VERITY].found)
|
||||
return -EADDRNOTAVAIL;
|
||||
} else if (m->partitions[PARTITION_ROOT_VERITY].found)
|
||||
return -EADDRNOTAVAIL; /* Verity found but no matching rootfs? Something is off, refuse. */
|
||||
|
||||
/* We didn't find a primary architecture root, but we found a primary architecture /usr? Refuse that for now. */
|
||||
if (m->partitions[PARTITION_USR].found || m->partitions[PARTITION_USR_VERITY].found)
|
||||
return -EADDRNOTAVAIL;
|
||||
else if (m->partitions[PARTITION_ROOT_SECONDARY].found) {
|
||||
|
||||
if (m->partitions[PARTITION_ROOT_SECONDARY].found) {
|
||||
/* Upgrade secondary arch to first */
|
||||
m->partitions[PARTITION_ROOT] = m->partitions[PARTITION_ROOT_SECONDARY];
|
||||
zero(m->partitions[PARTITION_ROOT_SECONDARY]);
|
||||
m->partitions[PARTITION_ROOT_VERITY] = m->partitions[PARTITION_ROOT_SECONDARY_VERITY];
|
||||
zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY]);
|
||||
/* No root partition found but there's one for the secondary architecture? Then upgrade
|
||||
* secondary arch to first */
|
||||
|
||||
m->partitions[PARTITION_USR] = m->partitions[PARTITION_USR_SECONDARY];
|
||||
zero(m->partitions[PARTITION_USR_SECONDARY]);
|
||||
m->partitions[PARTITION_USR_VERITY] = m->partitions[PARTITION_USR_SECONDARY_VERITY];
|
||||
zero(m->partitions[PARTITION_USR_SECONDARY_VERITY]);
|
||||
m->partitions[PARTITION_ROOT] = m->partitions[PARTITION_ROOT_SECONDARY];
|
||||
zero(m->partitions[PARTITION_ROOT_SECONDARY]);
|
||||
m->partitions[PARTITION_ROOT_VERITY] = m->partitions[PARTITION_ROOT_SECONDARY_VERITY];
|
||||
zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY]);
|
||||
|
||||
} else if (flags & DISSECT_IMAGE_REQUIRE_ROOT) {
|
||||
m->partitions[PARTITION_USR] = m->partitions[PARTITION_USR_SECONDARY];
|
||||
zero(m->partitions[PARTITION_USR_SECONDARY]);
|
||||
m->partitions[PARTITION_USR_VERITY] = m->partitions[PARTITION_USR_SECONDARY_VERITY];
|
||||
zero(m->partitions[PARTITION_USR_SECONDARY_VERITY]);
|
||||
|
||||
} else if (m->partitions[PARTITION_ROOT_SECONDARY_VERITY].found)
|
||||
return -EADDRNOTAVAIL; /* as above */
|
||||
|
||||
else if (m->partitions[PARTITION_USR].found) {
|
||||
|
||||
/* Invalidate secondary arch /usr/ if we found the primary arch */
|
||||
m->partitions[PARTITION_USR_SECONDARY].found = false;
|
||||
m->partitions[PARTITION_USR_SECONDARY_VERITY].found = false;
|
||||
|
||||
} else if (m->partitions[PARTITION_USR_VERITY].found)
|
||||
return -EADDRNOTAVAIL; /* as above */
|
||||
|
||||
else if (m->partitions[PARTITION_USR_SECONDARY].found) {
|
||||
|
||||
/* Upgrade secondary arch to primary */
|
||||
m->partitions[PARTITION_USR] = m->partitions[PARTITION_USR_SECONDARY];
|
||||
zero(m->partitions[PARTITION_USR_SECONDARY]);
|
||||
m->partitions[PARTITION_USR_VERITY] = m->partitions[PARTITION_USR_SECONDARY_VERITY];
|
||||
zero(m->partitions[PARTITION_USR_SECONDARY_VERITY]);
|
||||
|
||||
} else if (m->partitions[PARTITION_USR_SECONDARY_VERITY].found)
|
||||
return -EADDRNOTAVAIL; /* as above */
|
||||
|
||||
else if ((flags & DISSECT_IMAGE_GENERIC_ROOT) &&
|
||||
(!verity || !verity->root_hash)) {
|
||||
|
||||
/* OK, we found nothing usable, then check if there's a single generic one distro, and use
|
||||
* that. If the root hash was set however, then we won't fall back to a generic node, because
|
||||
* the root hash decides. */
|
||||
|
||||
/* 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;
|
||||
|
||||
/* If we didn't find a generic node, then we can't fix this up either */
|
||||
if (generic_node) {
|
||||
_cleanup_free_ char *o = NULL;
|
||||
const char *options = NULL;
|
||||
|
||||
/* If the root hash was set, then we won't fall back to a generic node, because the
|
||||
* root hash decides. */
|
||||
if (verity && verity->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;
|
||||
const char *options;
|
||||
|
||||
options = mount_options_from_designator(mount_options, PARTITION_ROOT);
|
||||
if (options) {
|
||||
@ -1160,6 +1214,11 @@ int dissect_image(
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if we have a root fs if we are told to do check. /usr alone is fine too, but only if appropriate flag for that is set too */
|
||||
if (FLAGS_SET(flags, DISSECT_IMAGE_REQUIRE_ROOT) &&
|
||||
!(m->partitions[PARTITION_ROOT].found || (m->partitions[PARTITION_USR].found && FLAGS_SET(flags, DISSECT_IMAGE_USR_NO_ROOT))))
|
||||
return -ENXIO;
|
||||
|
||||
/* Refuse if we found a verity partition for /usr but no matching file system partition */
|
||||
if (!m->partitions[PARTITION_USR].found && m->partitions[PARTITION_USR_VERITY].found)
|
||||
return -EADDRNOTAVAIL;
|
||||
@ -1221,13 +1280,8 @@ DissectedImage* dissected_image_unref(DissectedImage *m) {
|
||||
if (!m)
|
||||
return NULL;
|
||||
|
||||
for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
|
||||
free(m->partitions[i].fstype);
|
||||
free(m->partitions[i].node);
|
||||
free(m->partitions[i].decrypted_fstype);
|
||||
free(m->partitions[i].decrypted_node);
|
||||
free(m->partitions[i].mount_options);
|
||||
}
|
||||
for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++)
|
||||
dissected_partition_done(m->partitions + i);
|
||||
|
||||
free(m->image_name);
|
||||
free(m->hostname);
|
||||
@ -1398,6 +1452,32 @@ static int mount_partition(
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int mount_root_tmpfs(const char *where, uid_t uid_shift, DissectImageFlags flags) {
|
||||
_cleanup_free_ char *options = NULL;
|
||||
int r;
|
||||
|
||||
assert(where);
|
||||
|
||||
/* For images that contain /usr/ but no rootfs, let's mount rootfs as tmpfs */
|
||||
|
||||
if (FLAGS_SET(flags, DISSECT_IMAGE_MKDIR)) {
|
||||
r = mkdir_p(where, 0755);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (uid_is_valid(uid_shift)) {
|
||||
if (asprintf(&options, "uid=" UID_FMT ",gid=" GID_FMT, uid_shift, (gid_t) uid_shift) < 0)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
r = mount_nofollow_verbose(LOG_DEBUG, "rootfs", where, "tmpfs", MS_NODEV, options);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift, DissectImageFlags flags) {
|
||||
int r, xbootldr_mounted;
|
||||
|
||||
@ -1414,16 +1494,20 @@ int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift,
|
||||
* -EAFNOSUPPORT → File system type not supported or not known
|
||||
*/
|
||||
|
||||
if (!m->partitions[PARTITION_ROOT].found)
|
||||
return -ENXIO;
|
||||
if (!(m->partitions[PARTITION_ROOT].found ||
|
||||
(m->partitions[PARTITION_USR].found && FLAGS_SET(flags, DISSECT_IMAGE_USR_NO_ROOT))))
|
||||
return -ENXIO; /* Require a root fs or at least a /usr/ fs (the latter is subject to a flag of its own) */
|
||||
|
||||
if ((flags & DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY) == 0) {
|
||||
r = mount_partition(m->partitions + PARTITION_ROOT, where, NULL, uid_shift, flags);
|
||||
|
||||
/* First mount the root fs. If there's none we use a tmpfs. */
|
||||
if (m->partitions[PARTITION_ROOT].found)
|
||||
r = mount_partition(m->partitions + PARTITION_ROOT, where, NULL, uid_shift, flags);
|
||||
else
|
||||
r = mount_root_tmpfs(where, uid_shift, flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if ((flags & DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY) == 0) {
|
||||
/* For us mounting root always means mounting /usr as well */
|
||||
r = mount_partition(m->partitions + PARTITION_USR, where, "/usr", uid_shift, flags);
|
||||
if (r < 0)
|
||||
@ -2306,7 +2390,14 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
|
||||
if (r == 0) {
|
||||
error_pipe[0] = safe_close(error_pipe[0]);
|
||||
|
||||
r = dissected_image_mount(m, t, UID_INVALID, DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_MOUNT_ROOT_ONLY|DISSECT_IMAGE_VALIDATE_OS);
|
||||
r = dissected_image_mount(
|
||||
m,
|
||||
t,
|
||||
UID_INVALID,
|
||||
DISSECT_IMAGE_READ_ONLY|
|
||||
DISSECT_IMAGE_MOUNT_ROOT_ONLY|
|
||||
DISSECT_IMAGE_VALIDATE_OS|
|
||||
DISSECT_IMAGE_USR_NO_ROOT);
|
||||
if (r < 0) {
|
||||
/* Let parent know the error */
|
||||
(void) write(error_pipe[1], &r, sizeof(r));
|
||||
|
@ -23,6 +23,7 @@ struct DissectedPartition {
|
||||
sd_id128_t uuid; /* Partition entry UUID as reported by the GPT */
|
||||
char *fstype;
|
||||
char *node;
|
||||
char *label;
|
||||
char *decrypted_node;
|
||||
char *decrypted_fstype;
|
||||
char *mount_options;
|
||||
@ -48,6 +49,23 @@ typedef enum PartitionDesignator {
|
||||
_PARTITION_DESIGNATOR_INVALID = -EINVAL,
|
||||
} PartitionDesignator;
|
||||
|
||||
static inline bool PARTITION_DESIGNATOR_VERSIONED(PartitionDesignator d) {
|
||||
/* Returns true for all designators where we want to support a concept of "versioning", i.e. which
|
||||
* likely contain software binaries (or hashes thereof) that make sense to be versioned as a
|
||||
* whole. We use this check to automatically pick the newest version of these partitions, by version
|
||||
* comparing the partition labels. */
|
||||
|
||||
return IN_SET(d,
|
||||
PARTITION_ROOT,
|
||||
PARTITION_ROOT_SECONDARY,
|
||||
PARTITION_USR,
|
||||
PARTITION_USR_SECONDARY,
|
||||
PARTITION_ROOT_VERITY,
|
||||
PARTITION_ROOT_SECONDARY_VERITY,
|
||||
PARTITION_USR_VERITY,
|
||||
PARTITION_USR_SECONDARY_VERITY);
|
||||
}
|
||||
|
||||
static inline PartitionDesignator PARTITION_VERITY_OF(PartitionDesignator p) {
|
||||
switch (p) {
|
||||
|
||||
@ -77,7 +95,7 @@ typedef enum DissectImageFlags {
|
||||
DISSECT_IMAGE_DISCARD |
|
||||
DISSECT_IMAGE_DISCARD_ON_CRYPTO,
|
||||
DISSECT_IMAGE_GPT_ONLY = 1 << 4, /* Only recognize images with GPT partition tables */
|
||||
DISSECT_IMAGE_REQUIRE_ROOT = 1 << 5, /* Don't accept disks without root partition (and if no partition table or only single generic partition, assume it's root) */
|
||||
DISSECT_IMAGE_GENERIC_ROOT = 1 << 5, /* If no partition table or only single generic partition, assume it's the root fs */
|
||||
DISSECT_IMAGE_MOUNT_ROOT_ONLY = 1 << 6, /* Mount only the root and /usr partitions */
|
||||
DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY = 1 << 7, /* Mount only the non-root and non-/usr partitions */
|
||||
DISSECT_IMAGE_VALIDATE_OS = 1 << 8, /* Refuse mounting images that aren't identifiable as OS images */
|
||||
@ -87,6 +105,8 @@ typedef enum DissectImageFlags {
|
||||
DISSECT_IMAGE_NO_PARTITION_TABLE = 1 << 12, /* Only recognize single file system images */
|
||||
DISSECT_IMAGE_VERITY_SHARE = 1 << 13, /* When activating a verity device, reuse existing one if already open */
|
||||
DISSECT_IMAGE_MKDIR = 1 << 14, /* Make top-level directory to mount right before mounting, if missing */
|
||||
DISSECT_IMAGE_USR_NO_ROOT = 1 << 15, /* If no root fs is in the image, but /usr is, then allow this (so that we can mount the rootfs as tmpfs or so */
|
||||
DISSECT_IMAGE_REQUIRE_ROOT = 1 << 16, /* Don't accept disks without root partition (or at least /usr partition if DISSECT_IMAGE_USR_NO_ROOT is set) */
|
||||
} DissectImageFlags;
|
||||
|
||||
struct DissectedImage {
|
||||
|
@ -509,7 +509,12 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
|
||||
_cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
|
||||
_cleanup_(decrypted_image_unrefp) DecryptedImage *di = NULL;
|
||||
_cleanup_(verity_settings_done) VeritySettings verity_settings = VERITY_SETTINGS_DEFAULT;
|
||||
DissectImageFlags flags = DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_MOUNT_ROOT_ONLY;
|
||||
DissectImageFlags flags =
|
||||
DISSECT_IMAGE_READ_ONLY |
|
||||
DISSECT_IMAGE_GENERIC_ROOT |
|
||||
DISSECT_IMAGE_REQUIRE_ROOT |
|
||||
DISSECT_IMAGE_MOUNT_ROOT_ONLY |
|
||||
DISSECT_IMAGE_USR_NO_ROOT;
|
||||
|
||||
r = verity_settings_load(&verity_settings, img->path, NULL, NULL);
|
||||
if (r < 0)
|
||||
|
@ -1946,7 +1946,11 @@ static int run(int argc, char *argv[]) {
|
||||
|
||||
r = mount_image_privately_interactively(
|
||||
arg_image,
|
||||
DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_VALIDATE_OS|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK,
|
||||
DISSECT_IMAGE_GENERIC_ROOT |
|
||||
DISSECT_IMAGE_REQUIRE_ROOT |
|
||||
DISSECT_IMAGE_VALIDATE_OS |
|
||||
DISSECT_IMAGE_RELAX_VAR_CHECK |
|
||||
DISSECT_IMAGE_FSCK,
|
||||
&unlink_dir,
|
||||
&loop_device,
|
||||
&decrypted_image);
|
||||
|
@ -187,7 +187,6 @@ static void test_protect_kernel_logs(void) {
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
NULL);
|
||||
assert_se(r == 0);
|
||||
|
||||
|
@ -107,7 +107,6 @@ int main(int argc, char *argv[]) {
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
NULL);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to set up namespace: %m");
|
||||
|
@ -3431,7 +3431,11 @@ static int run(int argc, char *argv[]) {
|
||||
|
||||
r = mount_image_privately_interactively(
|
||||
arg_image,
|
||||
DISSECT_IMAGE_REQUIRE_ROOT|DISSECT_IMAGE_VALIDATE_OS|DISSECT_IMAGE_RELAX_VAR_CHECK|DISSECT_IMAGE_FSCK,
|
||||
DISSECT_IMAGE_GENERIC_ROOT |
|
||||
DISSECT_IMAGE_REQUIRE_ROOT |
|
||||
DISSECT_IMAGE_VALIDATE_OS |
|
||||
DISSECT_IMAGE_RELAX_VAR_CHECK |
|
||||
DISSECT_IMAGE_FSCK,
|
||||
&unlink_dir,
|
||||
&loop_device,
|
||||
&decrypted_image);
|
||||
|
@ -30,13 +30,13 @@ roothash="$(cat ${image}.roothash)"
|
||||
|
||||
os_release=$(test -e /etc/os-release && echo /etc/os-release || echo /usr/lib/os-release)
|
||||
|
||||
systemd-dissect --json=short ${image}.raw | grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"fstype":"squashfs","architecture":null,"verity":"external"'
|
||||
systemd-dissect --json=short ${image}.raw | grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"'
|
||||
systemd-dissect ${image}.raw | grep -q -F "MARKER=1"
|
||||
systemd-dissect ${image}.raw | grep -q -F -f $os_release
|
||||
|
||||
mv ${image}.verity ${image}.fooverity
|
||||
mv ${image}.roothash ${image}.foohash
|
||||
systemd-dissect --json=short ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"fstype":"squashfs","architecture":null,"verity":"external"'
|
||||
systemd-dissect --json=short ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F '{"rw":"ro","designator":"root","partition_uuid":null,"partition_label":null,"fstype":"squashfs","architecture":null,"verity":"external"'
|
||||
systemd-dissect ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F "MARKER=1"
|
||||
systemd-dissect ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F -f $os_release
|
||||
mv ${image}.fooverity ${image}.verity
|
||||
@ -127,8 +127,8 @@ losetup -d ${loop}
|
||||
ROOT_UUID=$(systemd-id128 -u show $(head -c 32 ${image}.roothash) -u | tail -n 1 | cut -b 6-)
|
||||
VERITY_UUID=$(systemd-id128 -u show $(tail -c 32 ${image}.roothash) -u | tail -n 1 | cut -b 6-)
|
||||
|
||||
systemd-dissect --json=short --root-hash ${roothash} ${image}.gpt | grep -q '{"rw":"ro","designator":"root","partition_uuid":"'$ROOT_UUID'","fstype":"squashfs","architecture":"'$architecture'","verity":"yes","node":'
|
||||
systemd-dissect --json=short --root-hash ${roothash} ${image}.gpt | grep -q '{"rw":"ro","designator":"root-verity","partition_uuid":"'$VERITY_UUID'","fstype":"DM_verity_hash","architecture":"'$architecture'","verity":null,"node":'
|
||||
systemd-dissect --json=short --root-hash ${roothash} ${image}.gpt | grep -q '{"rw":"ro","designator":"root","partition_uuid":"'$ROOT_UUID'","partition_label":"Root Partition","fstype":"squashfs","architecture":"'$architecture'","verity":"yes","node":'
|
||||
systemd-dissect --json=short --root-hash ${roothash} ${image}.gpt | grep -q '{"rw":"ro","designator":"root-verity","partition_uuid":"'$VERITY_UUID'","partition_label":"Verity Partition","fstype":"DM_verity_hash","architecture":"'$architecture'","verity":null,"node":'
|
||||
systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q -F "MARKER=1"
|
||||
systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q -F -f $os_release
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user