core: deduplicate identical dm-verity ExtensionImages=
It turns out OverlayFS doesn't handle gracefully when the same source is specified multiple times in lowerdir= and it fails with ELOOP: Failed to mount overlay (type overlay) on /run/systemd/mount-rootfs/opt (MS_RDONLY "lowerdir=/run/systemd/unit-extensions/1/opt:/run/systemd/unit-extensions/0/opt:/run/systemd/mount-rootfs/opt"): Too many levels of symbolic links This happens even if we mount each image in a different internal mount path, as OverlayFS will resolve it and look for the backing device, which will be the same device mapper entity, and return a hard error. This error does not appear if dm-verity is not used, so it is very confusing for users, and unnecessary. When mounting ExtensionImages, check if an image is dm-veritied, and drop duplicates if the root hashes match, to avoid this user-unfriendly hard error.
This commit is contained in:
+46
-1
@@ -112,6 +112,7 @@ typedef struct MountEntry {
|
||||
unsigned n_followed;
|
||||
LIST_HEAD(MountOptions, image_options_const);
|
||||
char **overlay_layers;
|
||||
VeritySettings verity;
|
||||
} MountEntry;
|
||||
|
||||
typedef struct MountList {
|
||||
@@ -343,6 +344,7 @@ static void mount_entry_done(MountEntry *p) {
|
||||
p->source_malloc = mfree(p->source_malloc);
|
||||
p->options_malloc = mfree(p->options_malloc);
|
||||
p->overlay_layers = strv_free(p->overlay_layers);
|
||||
verity_settings_done(&p->verity);
|
||||
}
|
||||
|
||||
static void mount_list_done(MountList *ml) {
|
||||
@@ -505,6 +507,7 @@ static int append_extensions(
|
||||
/* First, prepare a mount for each image, but these won't be visible to the unit, instead
|
||||
* they will be mounted in our propagate directory, and used as a source for the overlay. */
|
||||
for (size_t i = 0; i < n_mount_images; i++) {
|
||||
_cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT;
|
||||
_cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
|
||||
_cleanup_free_ char *mount_point = NULL;
|
||||
const MountImage *m = mount_images + i;
|
||||
@@ -523,6 +526,10 @@ static int append_extensions(
|
||||
"No matching entry in .v/ directory %s found.",
|
||||
m->source);
|
||||
|
||||
r = verity_settings_load(&verity, result.path, /* root_hash_path= */ NULL, /* root_hash_sig_path= */ NULL);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to check verity root hash of %s: %m", result.path);
|
||||
|
||||
if (asprintf(&mount_point, "%s/unit-extensions/%zu", private_namespace_dir, i) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -547,6 +554,7 @@ static int append_extensions(
|
||||
.source_malloc = TAKE_PTR(result.path),
|
||||
.mode = MOUNT_EXTENSION_IMAGE,
|
||||
.has_prefix = true,
|
||||
.verity = TAKE_GENERIC(verity, VeritySettings, VERITY_SETTINGS_DEFAULT),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -787,6 +795,36 @@ static int prefix_where_needed(MountList *ml, const char *root_directory) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool verity_has_later_duplicates(MountList *ml, const MountEntry *needle) {
|
||||
|
||||
assert(ml);
|
||||
assert(needle);
|
||||
assert(needle >= ml->mounts && needle < ml->mounts + ml->n_mounts);
|
||||
assert(needle->mode == MOUNT_EXTENSION_IMAGE);
|
||||
|
||||
if (needle->verity.root_hash_size == 0)
|
||||
return false;
|
||||
|
||||
/* Overlayfs rejects supplying the same directory inode twice as determined by filesystem UUID and
|
||||
* file handle in lowerdir=, even if they are mounted on different paths, as it resolves each mount
|
||||
* to its source filesystem, so drop duplicates, and keep the last one. This only covers non-DDI
|
||||
* verity images. Note that the list is ordered, so we only check for the reminder of the list for
|
||||
* each item, rather than the full list from the beginning, as any earlier duplicates will have
|
||||
* already been pruned. */
|
||||
|
||||
for (const MountEntry *m = needle + 1; m < ml->mounts + ml->n_mounts; m++) {
|
||||
if (m->mode != MOUNT_EXTENSION_IMAGE)
|
||||
continue;
|
||||
if (memcmp_nn(m->verity.root_hash,
|
||||
m->verity.root_hash_size,
|
||||
needle->verity.root_hash,
|
||||
needle->verity.root_hash_size) == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void drop_duplicates(MountList *ml) {
|
||||
MountEntry *f, *t, *previous;
|
||||
|
||||
@@ -810,6 +848,12 @@ static void drop_duplicates(MountList *ml) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (f->mode == MOUNT_EXTENSION_IMAGE && verity_has_later_duplicates(ml, f)) {
|
||||
log_debug("Skipping duplicate extension image %s", mount_entry_source(f));
|
||||
mount_entry_done(f);
|
||||
continue;
|
||||
}
|
||||
|
||||
*t = *f;
|
||||
previous = t;
|
||||
t++;
|
||||
@@ -1347,7 +1391,7 @@ static int mount_mqueuefs(const MountEntry *m) {
|
||||
}
|
||||
|
||||
static int mount_image(
|
||||
const MountEntry *m,
|
||||
MountEntry *m,
|
||||
const char *root_directory,
|
||||
const ImagePolicy *image_policy) {
|
||||
|
||||
@@ -1387,6 +1431,7 @@ static int mount_image(
|
||||
host_os_release_sysext_level,
|
||||
host_os_release_confext_level,
|
||||
/* required_sysext_scope= */ NULL,
|
||||
&m->verity,
|
||||
/* ret_image= */ NULL);
|
||||
if (r == -ENOENT && m->ignore)
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user