mount-util: Compact list of sub mounts after dropping

When nested mounts appear under a sysext hierarchy like this:
  mkdir -p /opt/trigger/
  mount -t tmpfs tmpfs /opt/trigger
  mkdir -p /opt/trigger/inner
  mount -t tmpfs tmpfs /opt/trigger/inner
Then systemd-sysext merge hit an assertion reported in
https://github.com/flatcar/Flatcar/issues/2111 because when it iterates
over the list of sub mounts it doesn't expect entries with NULL in the
path from the dropped entries.
Instead of having to deal with entries with path NULL, better sort the
holes from dropping to the end and then reduce the array length.
This commit is contained in:
Kai Lüke
2026-07-10 03:34:54 +02:00
committed by Kai Lüke
parent b5102ae48e
commit 7eabeaed0e
2 changed files with 72 additions and 8 deletions
+23 -8
View File
@@ -1754,21 +1754,36 @@ DEFINE_ARRAY_FREE_FUNC(sub_mount_array_free, SubMount, sub_mount_clear);
static int sub_mount_compare(const SubMount *a, const SubMount *b) {
assert(a);
assert(b);
assert(a->path);
assert(b->path);
/* sub_mount_drop() creates NULL paths which we order to the end so that after the sort we can
* truncate the array. Done manually because path_compare() orders NULL before non-NULL. */
if (!a->path || !b->path)
return CMP(!a->path, !b->path);
return path_compare(a->path, b->path);
}
static void sub_mount_drop(SubMount *s, size_t n) {
assert(s || n == 0);
static void sub_mount_drop(SubMount *s, size_t *n) {
assert(n);
assert(s || *n == 0);
for (size_t m = 0, i = 1; i < n; i++) {
/* Works on a sorted array. Drops mounts that are covered by the preceding entry's recursive
* open_tree() clone, clearing the slot in place. Then sorts again for the NULL paths to be shifted
* past the kept count. */
size_t kept = *n > 0;
for (size_t m = 0, i = 1; i < *n; i++)
if (path_startswith(s[i].path, s[m].path))
sub_mount_clear(s + i);
else
else {
m = i;
}
kept++;
}
if (kept < *n)
typesafe_qsort(s, *n, sub_mount_compare);
*n = kept;
}
#endif
@@ -1843,7 +1858,7 @@ int get_sub_mounts(const char *prefix, SubMount **ret_mounts, size_t *ret_n_moun
}
typesafe_qsort(mounts, n, sub_mount_compare);
sub_mount_drop(mounts, n);
sub_mount_drop(mounts, &n);
*ret_mounts = TAKE_PTR(mounts);
*ret_n_mounts = n;