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:
Zbigniew Jędrzejewski-Szmek 2021-03-31 10:31:32 +02:00 committed by GitHub
commit b880ac2c15
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 264 additions and 92 deletions

9
TODO
View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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) &&

View file

@ -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"

View file

@ -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)

View file

@ -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);

View file

@ -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;

View file

@ -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,

View file

@ -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);

View file

@ -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)

View file

@ -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;

View file

@ -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));

View file

@ -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 {

View file

@ -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)

View file

@ -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);

View file

@ -187,7 +187,6 @@ static void test_protect_kernel_logs(void) {
NULL,
NULL,
NULL,
0,
NULL);
assert_se(r == 0);

View file

@ -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");

View file

@ -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);

View file

@ -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