Skip to content

Commit 7cf6603

Browse files
committed
dissect-image: support images without rootfs but with /usr/
Let's add support for images that include an /usr/ file system but no root fs. Mount a tmpfs as root for images like this, all controlled by a new flag DISSECT_IMAGE_USR_NO_ROOT. This is useful for entirely stateless images, that come up pristine on every single boot.
1 parent efd3be9 commit 7cf6603

File tree

2 files changed

+115
-56
lines changed

2 files changed

+115
-56
lines changed

src/shared/dissect-image.c

Lines changed: 114 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,64 +1100,85 @@ int dissect_image(
11001100
m->partitions[PARTITION_ROOT_SECONDARY_VERITY].found = false;
11011101
m->partitions[PARTITION_USR_SECONDARY].found = false;
11021102
m->partitions[PARTITION_USR_SECONDARY_VERITY].found = false;
1103-
} else {
1104-
/* No root partition found? Then let's see if ther's one for the secondary architecture. And if not
1105-
* either, then check if there's a single generic one, and use that. */
11061103

1107-
if (m->partitions[PARTITION_ROOT_VERITY].found)
1108-
return -EADDRNOTAVAIL;
1104+
} else if (m->partitions[PARTITION_ROOT_VERITY].found)
1105+
return -EADDRNOTAVAIL; /* Verity found but no matching rootfs? Something is off, refuse. */
11091106

1110-
/* We didn't find a primary architecture root, but we found a primary architecture /usr? Refuse that for now. */
1111-
if (m->partitions[PARTITION_USR].found || m->partitions[PARTITION_USR_VERITY].found)
1112-
return -EADDRNOTAVAIL;
1107+
else if (m->partitions[PARTITION_ROOT_SECONDARY].found) {
11131108

1114-
if (m->partitions[PARTITION_ROOT_SECONDARY].found) {
1115-
/* Upgrade secondary arch to first */
1116-
m->partitions[PARTITION_ROOT] = m->partitions[PARTITION_ROOT_SECONDARY];
1117-
zero(m->partitions[PARTITION_ROOT_SECONDARY]);
1118-
m->partitions[PARTITION_ROOT_VERITY] = m->partitions[PARTITION_ROOT_SECONDARY_VERITY];
1119-
zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY]);
1120-
1121-
m->partitions[PARTITION_USR] = m->partitions[PARTITION_USR_SECONDARY];
1122-
zero(m->partitions[PARTITION_USR_SECONDARY]);
1123-
m->partitions[PARTITION_USR_VERITY] = m->partitions[PARTITION_USR_SECONDARY_VERITY];
1124-
zero(m->partitions[PARTITION_USR_SECONDARY_VERITY]);
1125-
1126-
} else if (flags & DISSECT_IMAGE_REQUIRE_ROOT) {
1127-
_cleanup_free_ char *o = NULL;
1128-
const char *options = NULL;
1129-
1130-
/* If the root hash was set, then we won't fall back to a generic node, because the
1131-
* root hash decides. */
1132-
if (verity && verity->root_hash)
1133-
return -EADDRNOTAVAIL;
1109+
/* No root partition found but there's one for the secondary architecture? Then upgrade
1110+
* secondary arch to first */
11341111

1135-
/* If we didn't find a generic node, then we can't fix this up either */
1136-
if (!generic_node)
1137-
return -ENXIO;
1112+
m->partitions[PARTITION_ROOT] = m->partitions[PARTITION_ROOT_SECONDARY];
1113+
zero(m->partitions[PARTITION_ROOT_SECONDARY]);
1114+
m->partitions[PARTITION_ROOT_VERITY] = m->partitions[PARTITION_ROOT_SECONDARY_VERITY];
1115+
zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY]);
11381116

1139-
/* If we didn't find a properly marked root partition, but we did find a single suitable
1140-
* generic Linux partition, then use this as root partition, if the caller asked for it. */
1141-
if (multiple_generic)
1142-
return -ENOTUNIQ;
1117+
m->partitions[PARTITION_USR] = m->partitions[PARTITION_USR_SECONDARY];
1118+
zero(m->partitions[PARTITION_USR_SECONDARY]);
1119+
m->partitions[PARTITION_USR_VERITY] = m->partitions[PARTITION_USR_SECONDARY_VERITY];
1120+
zero(m->partitions[PARTITION_USR_SECONDARY_VERITY]);
11431121

1144-
options = mount_options_from_designator(mount_options, PARTITION_ROOT);
1145-
if (options) {
1146-
o = strdup(options);
1147-
if (!o)
1148-
return -ENOMEM;
1149-
}
1122+
} else if (m->partitions[PARTITION_ROOT_SECONDARY_VERITY].found)
1123+
return -EADDRNOTAVAIL; /* as above */
11501124

1151-
m->partitions[PARTITION_ROOT] = (DissectedPartition) {
1152-
.found = true,
1153-
.rw = generic_rw,
1154-
.partno = generic_nr,
1155-
.architecture = _ARCHITECTURE_INVALID,
1156-
.node = TAKE_PTR(generic_node),
1157-
.uuid = generic_uuid,
1158-
.mount_options = TAKE_PTR(o),
1159-
};
1125+
else if (m->partitions[PARTITION_USR].found) {
1126+
1127+
/* Invalidate secondary arch /usr/ if we found the primary arch */
1128+
m->partitions[PARTITION_USR_SECONDARY].found = false;
1129+
m->partitions[PARTITION_USR_SECONDARY_VERITY].found = false;
1130+
1131+
} else if (m->partitions[PARTITION_USR_VERITY].found)
1132+
return -EADDRNOTAVAIL; /* as above */
1133+
1134+
else if (m->partitions[PARTITION_USR_SECONDARY].found) {
1135+
1136+
/* Upgrade secondary arch to primary */
1137+
m->partitions[PARTITION_USR] = m->partitions[PARTITION_USR_SECONDARY];
1138+
zero(m->partitions[PARTITION_USR_SECONDARY]);
1139+
m->partitions[PARTITION_USR_VERITY] = m->partitions[PARTITION_USR_SECONDARY_VERITY];
1140+
zero(m->partitions[PARTITION_USR_SECONDARY_VERITY]);
1141+
1142+
} else if (m->partitions[PARTITION_USR_SECONDARY_VERITY].found)
1143+
return -EADDRNOTAVAIL; /* as above */
1144+
1145+
else if (flags & DISSECT_IMAGE_REQUIRE_ROOT) {
1146+
_cleanup_free_ char *o = NULL;
1147+
const char *options = NULL;
1148+
1149+
/* OK, we found nothing usable, then check if there's a single generic one distro, and use
1150+
* that. */
1151+
1152+
/* If the root hash was set, then we won't fall back to a generic node, because the root hash
1153+
* decides. */
1154+
if (verity && verity->root_hash)
1155+
return -EADDRNOTAVAIL;
1156+
1157+
/* If we didn't find a generic node, then we can't fix this up either */
1158+
if (!generic_node)
1159+
return -ENXIO;
1160+
1161+
/* If we didn't find a properly marked root partition, but we did find a single suitable
1162+
* generic Linux partition, then use this as root partition, if the caller asked for it. */
1163+
if (multiple_generic)
1164+
return -ENOTUNIQ;
1165+
1166+
options = mount_options_from_designator(mount_options, PARTITION_ROOT);
1167+
if (options) {
1168+
o = strdup(options);
1169+
if (!o)
1170+
return -ENOMEM;
11601171
}
1172+
1173+
m->partitions[PARTITION_ROOT] = (DissectedPartition) {
1174+
.found = true,
1175+
.rw = generic_rw,
1176+
.partno = generic_nr,
1177+
.architecture = _ARCHITECTURE_INVALID,
1178+
.node = TAKE_PTR(generic_node),
1179+
.uuid = generic_uuid,
1180+
.mount_options = TAKE_PTR(o),
1181+
};
11611182
}
11621183

11631184
/* Refuse if we found a verity partition for /usr but no matching file system partition */
@@ -1397,6 +1418,32 @@ static int mount_partition(
13971418
return 1;
13981419
}
13991420

1421+
static int mount_root_tmpfs(const char *where, uid_t uid_shift, DissectImageFlags flags) {
1422+
_cleanup_free_ char *options = NULL;
1423+
int r;
1424+
1425+
assert(where);
1426+
1427+
/* For images that contain /usr/ but no rootfs, let's mount rootfs as tmpfs */
1428+
1429+
if (FLAGS_SET(flags, DISSECT_IMAGE_MKDIR)) {
1430+
r = mkdir_p(where, 0755);
1431+
if (r < 0)
1432+
return r;
1433+
}
1434+
1435+
if (uid_is_valid(uid_shift)) {
1436+
if (asprintf(&options, "uid=" UID_FMT ",gid=" GID_FMT, uid_shift, (gid_t) uid_shift) < 0)
1437+
return -ENOMEM;
1438+
}
1439+
1440+
r = mount_nofollow_verbose(LOG_DEBUG, "rootfs", where, "tmpfs", MS_NODEV, options);
1441+
if (r < 0)
1442+
return r;
1443+
1444+
return 1;
1445+
}
1446+
14001447
int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift, DissectImageFlags flags) {
14011448
int r, xbootldr_mounted;
14021449

@@ -1413,16 +1460,20 @@ int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift,
14131460
* -EAFNOSUPPORT → File system type not supported or not known
14141461
*/
14151462

1416-
if (!m->partitions[PARTITION_ROOT].found)
1417-
return -ENXIO;
1463+
if (!(m->partitions[PARTITION_ROOT].found ||
1464+
(m->partitions[PARTITION_USR].found && FLAGS_SET(flags, DISSECT_IMAGE_USR_NO_ROOT))))
1465+
return -ENXIO; /* Require a root fs or at least a /usr/ fs (the latter is subject to a flag of its own) */
14181466

14191467
if ((flags & DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY) == 0) {
1420-
r = mount_partition(m->partitions + PARTITION_ROOT, where, NULL, uid_shift, flags);
1468+
1469+
/* First mount the root fs. If there's none we use a tmpfs. */
1470+
if (m->partitions[PARTITION_ROOT].found)
1471+
r = mount_partition(m->partitions + PARTITION_ROOT, where, NULL, uid_shift, flags);
1472+
else
1473+
r = mount_root_tmpfs(where, uid_shift, flags);
14211474
if (r < 0)
14221475
return r;
1423-
}
14241476

1425-
if ((flags & DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY) == 0) {
14261477
/* For us mounting root always means mounting /usr as well */
14271478
r = mount_partition(m->partitions + PARTITION_USR, where, "/usr", uid_shift, flags);
14281479
if (r < 0)
@@ -2305,7 +2356,14 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
23052356
if (r == 0) {
23062357
error_pipe[0] = safe_close(error_pipe[0]);
23072358

2308-
r = dissected_image_mount(m, t, UID_INVALID, DISSECT_IMAGE_READ_ONLY|DISSECT_IMAGE_MOUNT_ROOT_ONLY|DISSECT_IMAGE_VALIDATE_OS);
2359+
r = dissected_image_mount(
2360+
m,
2361+
t,
2362+
UID_INVALID,
2363+
DISSECT_IMAGE_READ_ONLY|
2364+
DISSECT_IMAGE_MOUNT_ROOT_ONLY|
2365+
DISSECT_IMAGE_VALIDATE_OS|
2366+
DISSECT_IMAGE_USR_NO_ROOT);
23092367
if (r < 0) {
23102368
/* Let parent know the error */
23112369
(void) write(error_pipe[1], &r, sizeof(r));

src/shared/dissect-image.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ typedef enum DissectImageFlags {
8787
DISSECT_IMAGE_NO_PARTITION_TABLE = 1 << 12, /* Only recognize single file system images */
8888
DISSECT_IMAGE_VERITY_SHARE = 1 << 13, /* When activating a verity device, reuse existing one if already open */
8989
DISSECT_IMAGE_MKDIR = 1 << 14, /* Make top-level directory to mount right before mounting, if missing */
90+
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 */
9091
} DissectImageFlags;
9192

9293
struct DissectedImage {

0 commit comments

Comments
 (0)