discover-image: Follow symlinks in a given root (#39843)

This is needed to set up extension images from the initrd with
`systemd-sysext --root=/sysroot/ merge`.

- vpick: Don't use openat directly but resolve symlinks in given root
    
With systemd-sysext --root= all symlinks should be followed relative to
    the given root and direct openat usage doesn't work.
    Remove the openat call and let pin_choice do the work with the chase
    helper function to resolve the symlink in the given root.
- discover-image: Follow symlinks in a given root
    
    So far systemd-sysext with --root= specified didn't follow extension
symlinks (such as the "current" symlinks managed by systemd-sysupdate).
The main use case is running systemd-sysext --root=/sysroot for setting
    up the overlay mounts already from the initrd.
    
Resolve symlinks correctly but don't defend against later symlink races
    that would access a path outside of the given root. Malicous live
modifications are not a realistic threat model and anyway for that one
would need to rework how the image entry is passed over up to the point
when the loop device is set up. This change here does not introduce this
weakness nor does it expose it more than before. Thus, make it explicit
    that setting up the extensions for a given --root= implies a certain
trust into this given root tree that it does not try do race conditions
with symlinks to trick systemd-sysext to mount a file outside --root=.
Without a strict --image-policy= set we would anyway mount filesystems
right away which is another attack vector but, again, the main use case
    is to do this for the final system which is trusted at this stage.
- sysext: Use correct image name for extension release checks
    
For the extension release check the image name is needed and was derived
    from the backing file of the loop device. However, this can have a
different name when symlinks were resolved. The surprising behavior was
that it worked when the target name started with the extension name and
_ because that's what's supported to chop off version suffixes. However,
we should not have such strict requirements for the target name and also
allow - as version separator and entirely different names/prefixes, the
    same way as we also do for directories instead of raw images.
    
    Do not use the image name derived from the backing file of the loop
    device but directly the extension name we have at hand.
- test: Add tests for handling symlinks with systemd-sysext
    
    When we now allow following symlinks inside a --root= we should also
test that it works in various cases from simple relative and absolute
    symlinks to .v being a symlink itself or its contents, both for
directory and for .raw image extensions. While at it, also add a simple
    test for .v without symlinks which wasn't there for direct usage of
    systemd-sysext.
This commit is contained in:
Yu Watanabe
2026-01-30 22:36:20 +09:00
committed by GitHub
4 changed files with 384 additions and 83 deletions
+95 -79
View File
@@ -408,6 +408,8 @@ static int image_make(
/* We explicitly *do* follow symlinks here, since we want to allow symlinking trees, raw files and block
* devices into /var/lib/machines/, and treat them normally.
* Note that if the caller does not want to follow symlinks (and does not care about symlink races)
* then the caller should pass in a resolved path and an fd.
*
* This function returns -ENOENT if we can't find the image after all, and -EMEDIUMTYPE if it's not a file we
* recognize. */
@@ -744,10 +746,7 @@ int image_find(RuntimeScope scope,
const char *root,
Image **ret) {
/* As mentioned above, we follow symlinks on this fstatat(), because we want to permit people to
* symlink block devices into the search path. (For now, we disable that when operating relative to
* some root directory.) */
int open_flags = root ? O_NOFOLLOW : 0, r;
int r;
assert(scope < _RUNTIME_SCOPE_MAX && scope != RUNTIME_SCOPE_GLOBAL);
assert(class >= 0);
@@ -762,32 +761,47 @@ int image_find(RuntimeScope scope,
if (!names)
return -ENOMEM;
_cleanup_close_ int rfd = XAT_FDROOT; /* We only expect absolute paths */
if (root) {
rfd = open(root, O_CLOEXEC|O_DIRECTORY|O_PATH);
if (rfd < 0)
return log_debug_errno(errno, "Failed to open root directory '%s': %m", root);
}
_cleanup_strv_free_ char **search = NULL;
r = pick_image_search_path(scope, class, root, &search);
if (r < 0)
return r;
STRV_FOREACH(s, search) {
_cleanup_free_ char *resolved = NULL;
_cleanup_closedir_ DIR *d = NULL;
_cleanup_free_ char *search_path = NULL;
r = chase_and_opendir(*s, root, CHASE_PREFIX_ROOT, &resolved, &d);
r = chase_and_opendirat(rfd, *s, CHASE_AT_RESOLVE_IN_ROOT, &search_path, &d);
if (r == -ENOENT)
continue;
if (r < 0)
return r;
STRV_FOREACH(n, names) {
_cleanup_free_ char *fname_buf = NULL;
const char *fname = *n;
_cleanup_free_ char *fname_path = NULL, *chased_path = NULL, *resolved_file = NULL;
_cleanup_close_ int fd = -EBADF;
_cleanup_close_ int fd = openat(dirfd(d), fname, O_PATH|O_CLOEXEC|open_flags);
if (fd < 0) {
if (errno != ENOENT)
return -errno;
fname_path = path_join(search_path, fname);
if (!fname_path)
return -ENOMEM;
/* Follow symlinks only inside given root */
r = chaseat(rfd, fname_path, CHASE_AT_RESOLVE_IN_ROOT, &chased_path, &fd);
if (r == -ENOENT)
continue;
}
if (r < 0)
return r;
r = chaseat_prefix_root(chased_path, root, &resolved_file);
if (r < 0)
return r;
struct stat st;
if (fstat(fd, &st) < 0)
@@ -813,10 +827,6 @@ int image_find(RuntimeScope scope,
*ASSERT_PTR(endswith(suffix, ".v")) = 0;
_cleanup_free_ char *vp = path_join(resolved, fname);
if (!vp)
return -ENOMEM;
PickFilter filter = {
.type_mask = endswith(suffix, ".raw") ? (UINT32_C(1) << DT_REG) | (UINT32_C(1) << DT_BLK) : (UINT32_C(1) << DT_DIR),
.basename = name,
@@ -826,48 +836,44 @@ int image_find(RuntimeScope scope,
_cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
r = path_pick(root,
/* toplevel_fd= */ AT_FDCWD,
vp,
rfd,
fname_path, /* This has to be the unresolved entry with the .v suffix */
&filter,
/* n_filters= */ 1,
PICK_ARCHITECTURE|PICK_TRIES,
PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE,
&result);
if (r < 0) {
log_debug_errno(r, "Failed to pick versioned image on '%s', skipping: %m", vp);
log_debug_errno(r, "Failed to pick versioned image on '%s%s', skipping: %m", empty_to_root(root), skip_leading_slash(fname_path));
continue;
}
if (!result.path) {
log_debug("Found versioned directory '%s', without matching entry, skipping.", vp);
log_debug("Found versioned directory '%s%s', without matching entry, skipping.", empty_to_root(root), skip_leading_slash(fname_path));
continue;
}
/* Refresh the stat data for the discovered target */
st = result.st;
close_and_replace(fd, result.fd);
free(resolved_file);
resolved_file = path_join(root, result.path);
if (!resolved_file)
return -ENOMEM;
_cleanup_free_ char *bn = NULL;
r = path_extract_filename(result.path, &bn);
if (r < 0) {
log_debug_errno(r, "Failed to extract basename of image path '%s', skipping: %m", result.path);
continue;
}
fname_buf = path_join(fname, bn);
if (!fname_buf)
return log_oom();
fname = fname_buf;
/* fname and fname_path are invalid now because they would need to be set
* from result.path by extracting the filename to set
* fname = path_join(fname, filename) and then
* fname_path = path_join(*s, fname) but since they are unused we don't do it */
fname = NULL;
fname_path = mfree(fname_path);
} else if (!S_ISDIR(st.st_mode) && !S_ISBLK(st.st_mode)) {
log_debug("Ignoring non-directory and non-block device file '%s' without suffix.", fname);
continue;
}
_cleanup_free_ char *path = path_join(resolved, fname);
if (!path)
return -ENOMEM;
r = image_make(class, name, fd, path, &st, ret);
/* Only put resolved paths into the image entry (incl. --root=).
* Defending against symlink races is not done
* and would be a TODO. */
r = image_make(class, name, fd, resolved_file, &st, ret);
if (IN_SET(r, -ENOENT, -EMEDIUMTYPE))
continue;
if (r < 0)
@@ -948,46 +954,58 @@ int image_discover(
const char *root,
Hashmap **images) {
/* As mentioned above, we follow symlinks on this fstatat(), because we want to permit people to
* symlink block devices into the search path. (For now, we disable that when operating relative to
* some root directory.) */
int open_flags = root ? O_NOFOLLOW : 0, r;
int r;
assert(scope < _RUNTIME_SCOPE_MAX && scope != RUNTIME_SCOPE_GLOBAL);
assert(class >= 0);
assert(class < _IMAGE_CLASS_MAX);
assert(images);
_cleanup_close_ int rfd = XAT_FDROOT; /* We only expect absolute paths */
if (root) {
rfd = open(root, O_CLOEXEC|O_DIRECTORY|O_PATH);
if (rfd < 0)
return log_debug_errno(errno, "Failed to open root directory '%s': %m", root);
}
_cleanup_strv_free_ char **search = NULL;
r = pick_image_search_path(scope, class, root, &search);
if (r < 0)
return r;
STRV_FOREACH(s, search) {
_cleanup_free_ char *resolved = NULL;
_cleanup_closedir_ DIR *d = NULL;
_cleanup_free_ char *search_path = NULL;
r = chase_and_opendir(*s, root, CHASE_PREFIX_ROOT, &resolved, &d);
r = chase_and_opendirat(rfd, *s, CHASE_AT_RESOLVE_IN_ROOT, &search_path, &d);
if (r == -ENOENT)
continue;
if (r < 0)
return r;
FOREACH_DIRENT_ALL(de, d, return -errno) {
_cleanup_free_ char *pretty = NULL, *fname_buf = NULL;
_cleanup_free_ char *pretty = NULL, *fname_path = NULL, *chased_path = NULL, *resolved_file = NULL;
_cleanup_(image_unrefp) Image *image = NULL;
const char *fname = de->d_name;
_cleanup_close_ int fd = -EBADF;
if (dot_or_dot_dot(fname))
continue;
_cleanup_close_ int fd = openat(dirfd(d), fname, O_PATH|O_CLOEXEC|open_flags);
if (fd < 0) {
if (errno != ENOENT)
return -errno;
fname_path = path_join(search_path, fname);
if (!fname_path)
return -ENOMEM;
continue; /* Vanished while we were looking at it */
}
/* Follow symlinks only inside given root */
r = chaseat(rfd, fname_path, CHASE_AT_RESOLVE_IN_ROOT, &chased_path, &fd);
if (r == -ENOENT)
continue;
if (r < 0)
return r;
r = chaseat_prefix_root(chased_path, root, &resolved_file);
if (r < 0)
return r;
struct stat st;
if (fstat(fd, &st) < 0)
@@ -1026,10 +1044,6 @@ int image_discover(
continue;
}
_cleanup_free_ char *vp = path_join(resolved, fname);
if (!vp)
return -ENOMEM;
PickFilter filter = {
.type_mask = endswith(suffix, ".raw") ? (UINT32_C(1) << DT_REG) | (UINT32_C(1) << DT_BLK) : (UINT32_C(1) << DT_DIR),
.basename = pretty,
@@ -1039,38 +1053,36 @@ int image_discover(
_cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
r = path_pick(root,
/* toplevel_fd= */ AT_FDCWD,
vp,
rfd,
fname_path, /* This has to be the unresolved entry with the .v suffix */
&filter,
/* n_filters= */ 1,
PICK_ARCHITECTURE|PICK_TRIES,
PICK_ARCHITECTURE|PICK_TRIES|PICK_RESOLVE,
&result);
if (r < 0) {
log_debug_errno(r, "Failed to pick versioned image on '%s', skipping: %m", vp);
log_debug_errno(r, "Failed to pick versioned image on '%s%s', skipping: %m", empty_to_root(root), skip_leading_slash(fname_path));
continue;
}
if (!result.path) {
log_debug("Found versioned directory '%s', without matching entry, skipping.", vp);
log_debug("Found versioned directory '%s%s', without matching entry, skipping.", empty_to_root(root), skip_leading_slash(fname_path));
continue;
}
/* Refresh the stat data for the discovered target */
st = result.st;
close_and_replace(fd, result.fd);
free(resolved_file);
resolved_file = path_join(root, result.path);
if (!resolved_file)
return -ENOMEM;
_cleanup_free_ char *bn = NULL;
r = path_extract_filename(result.path, &bn);
if (r < 0) {
log_debug_errno(r, "Failed to extract basename of image path '%s', skipping: %m", result.path);
continue;
}
fname_buf = path_join(fname, bn);
if (!fname_buf)
return log_oom();
fname = fname_buf;
/* fname and fname_path are invalid now because they would need to
* be set from result.path by extracting the filename to set
* fname = path_join(fname, filename) and then
* fname_path = path_join(*s, fname) but since they are unused we
* don't do it */
fname = NULL;
fname_path = mfree(fname_path);
} else {
r = extract_image_basename(
fname,
@@ -1103,11 +1115,10 @@ int image_discover(
if (hashmap_contains(*images, pretty))
continue;
_cleanup_free_ char *path = path_join(resolved, fname);
if (!path)
return -ENOMEM;
r = image_make(class, pretty, fd, path, &st, &image);
/* Only put resolved paths into the image entry.
* Defending against symlink races is not done
* and would be a TODO. */
r = image_make(class, pretty, fd, resolved_file, &st, &image);
if (IN_SET(r, -ENOENT, -EMEDIUMTYPE))
continue;
if (r < 0)
@@ -2148,6 +2159,11 @@ int image_read_metadata(Image *i, const char *root, const ImagePolicy *image_pol
if (r < 0)
return log_debug_errno(r, "Failed to decrypt image '%s': %m", i->path);
/* Do not use the image name derived from the backing file of the loop device */
r = free_and_strdup(&m->image_name, i->name);
if (r < 0)
return r;
r = dissected_image_acquire_metadata(
m,
/* userns_fd= */ -EBADF,
+4 -4
View File
@@ -193,7 +193,7 @@ static int pin_choice(
_cleanup_free_ char *resolved_path = NULL;
int r;
assert(toplevel_fd >= 0 || toplevel_fd == AT_FDCWD);
assert(toplevel_fd >= 0 || IN_SET(toplevel_fd, AT_FDCWD, XAT_FDROOT));
assert(inode_path);
assert(filter);
assert(ret);
@@ -324,7 +324,7 @@ static int make_choice(
_cleanup_close_ int inode_fd = TAKE_FD(_inode_fd);
int r;
assert(toplevel_fd >= 0 || toplevel_fd == AT_FDCWD);
assert(toplevel_fd >= 0 || IN_SET(toplevel_fd, AT_FDCWD, XAT_FDROOT));
assert(inode_path);
assert(filter);
assert(ret);
@@ -516,7 +516,7 @@ static int path_pick_one(
uint32_t filter_type_mask;
int r;
assert(toplevel_fd >= 0 || toplevel_fd == AT_FDCWD);
assert(toplevel_fd >= 0 || IN_SET(toplevel_fd, AT_FDCWD, XAT_FDROOT));
assert(path);
assert(filter);
assert(ret);
@@ -663,7 +663,7 @@ int path_pick(const char *toplevel_path,
_cleanup_(pick_result_done) PickResult best = PICK_RESULT_NULL;
int r;
assert(toplevel_fd >= 0 || toplevel_fd == AT_FDCWD);
assert(toplevel_fd >= 0 || IN_SET(toplevel_fd, AT_FDCWD, XAT_FDROOT));
assert(path);
assert(filters || n_filters == 0);
assert(ret);
+5
View File
@@ -1911,6 +1911,11 @@ static int merge_subprocess(
if (r < 0)
return r;
/* Do not use the image name derived from the backing file of the loop device */
r = free_and_strdup(&m->image_name, img->name);
if (r < 0)
return r;
r = dissected_image_load_verity_sig_partition(
m,
d->fd,
+280
View File
@@ -1307,6 +1307,286 @@ run_systemd_sysext "$fake_root" unmerge
extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
)
# A couple of symlink tests follow below
( init_trap
: "Check if following a relative extension directory symlink works with and without --root="
fake_root=${roots_dir:+"$roots_dir/follow-relative-dir-symlink"}
hierarchy=/opt
prepare_root "$fake_root" "$hierarchy"
prepare_extension_image "$fake_root" "$hierarchy"
mv -T "$fake_root/var/lib/extensions/test-extension" "$fake_root/var/othername-extension"
ln -s "../../othername-extension" "$fake_root/var/lib/extensions/test-extension"
prepare_read_only_hierarchy "$fake_root" "$hierarchy"
run_systemd_sysext "$fake_root" merge
extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
run_systemd_sysext "$fake_root" unmerge
extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
rm -rf "$fake_root/var/othername-extension"
)
( init_trap
: "Check if following an absolute extension directory symlink works with and without --root="
fake_root=${roots_dir:+"$roots_dir/follow-absolute-dir-symlink"}
hierarchy=/opt
prepare_root "$fake_root" "$hierarchy"
prepare_extension_image "$fake_root" "$hierarchy"
mv -T "$fake_root/var/lib/extensions/test-extension" "$fake_root/var/othername-extension"
ln -s "/var/othername-extension" "$fake_root/var/lib/extensions/test-extension"
prepare_read_only_hierarchy "$fake_root" "$hierarchy"
run_systemd_sysext "$fake_root" merge
extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
run_systemd_sysext "$fake_root" unmerge
extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
rm -rf "$fake_root/var/othername-extension"
)
( init_trap
: "Check if following a relative extension image symlink works with and without --root="
fake_root=${roots_dir:+"$roots_dir/follow-relative-image-symlink"}
hierarchy=/opt
prepare_root "$fake_root" "$hierarchy"
prepare_extension_image_raw "$fake_root" "$hierarchy"
mv "$fake_root/var/lib/extensions/test-extension.raw" "$fake_root/var/othername-extension.raw"
ln -s "../../othername-extension.raw" "$fake_root/var/lib/extensions/test-extension.raw"
prepare_read_only_hierarchy "$fake_root" "$hierarchy"
run_systemd_sysext "$fake_root" merge
extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
run_systemd_sysext "$fake_root" unmerge
extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
rm -rf "$fake_root/var/othername-extension.raw"
)
( init_trap
: "Check if following an absolute extension image symlink works with and without --root="
fake_root=${roots_dir:+"$roots_dir/follow-absolute-image-symlink"}
hierarchy=/opt
prepare_root "$fake_root" "$hierarchy"
prepare_extension_image_raw "$fake_root" "$hierarchy"
mv "$fake_root/var/lib/extensions/test-extension.raw" "$fake_root/var/othername-extension.raw"
ln -s "/var/othername-extension.raw" "$fake_root/var/lib/extensions/test-extension.raw"
prepare_read_only_hierarchy "$fake_root" "$hierarchy"
run_systemd_sysext "$fake_root" merge
extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
run_systemd_sysext "$fake_root" unmerge
extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
rm -rf "$fake_root/var/othername-extension.raw"
)
# And now a couple of vpick tests, including following symlinks
( init_trap
: "Check if vpick works for directory extensions"
fake_root=${roots_dir:+"$roots_dir/vpick-dir"}
hierarchy=/opt
prepare_root "$fake_root" "$hierarchy"
prepare_extension_image "$fake_root" "$hierarchy"
mkdir -p "$fake_root/var/lib/extensions/test-extension.v"
mv -T "$fake_root/var/lib/extensions/test-extension" "$fake_root/var/lib/extensions/test-extension.v/test-extension_1.0"
prepare_read_only_hierarchy "$fake_root" "$hierarchy"
run_systemd_sysext "$fake_root" merge
extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
run_systemd_sysext "$fake_root" unmerge
extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
rm -rf "$fake_root/var/lib/extensions/test-extension.v"
)
( init_trap
: "Check if vpick works for image extensions"
fake_root=${roots_dir:+"$roots_dir/vpick-image"}
hierarchy=/opt
prepare_root "$fake_root" "$hierarchy"
prepare_extension_image_raw "$fake_root" "$hierarchy"
mkdir -p "$fake_root/var/lib/extensions/test-extension.raw.v"
mv "$fake_root/var/lib/extensions/test-extension.raw" "$fake_root/var/lib/extensions/test-extension.raw.v/test-extension_1.0.raw"
prepare_read_only_hierarchy "$fake_root" "$hierarchy"
run_systemd_sysext "$fake_root" merge
extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
run_systemd_sysext "$fake_root" unmerge
extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
rm -rf "$fake_root/var/lib/extensions/test-extension.raw.v"
)
( init_trap
: "Check if vpick works for directory extensions if .v is a relative symlink"
fake_root=${roots_dir:+"$roots_dir/vpick-dir-relative-symlink"}
hierarchy=/opt
prepare_root "$fake_root" "$hierarchy"
prepare_extension_image "$fake_root" "$hierarchy"
mkdir -p "$fake_root/var/test-extension-vpick"
mv -T "$fake_root/var/lib/extensions/test-extension" "$fake_root/var/test-extension-vpick/test-extension_1.0"
ln -s "../../test-extension-vpick" "$fake_root/var/lib/extensions/test-extension.v"
prepare_read_only_hierarchy "$fake_root" "$hierarchy"
run_systemd_sysext "$fake_root" merge
extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
run_systemd_sysext "$fake_root" unmerge
extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
rm -rf "$fake_root/var/lib/extensions/test-extension.v" "$fake_root/var/test-extension-vpick"
)
( init_trap
: "Check if vpick works for directory extensions if .v is an absolute symlink"
fake_root=${roots_dir:+"$roots_dir/vpick-dir-absolute-symlink"}
hierarchy=/opt
prepare_root "$fake_root" "$hierarchy"
prepare_extension_image "$fake_root" "$hierarchy"
mkdir -p "$fake_root/var/test-extension-vpick"
mv -T "$fake_root/var/lib/extensions/test-extension" "$fake_root/var/test-extension-vpick/test-extension_1.0"
ln -s "/var/test-extension-vpick" "$fake_root/var/lib/extensions/test-extension.v"
prepare_read_only_hierarchy "$fake_root" "$hierarchy"
run_systemd_sysext "$fake_root" merge
extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
run_systemd_sysext "$fake_root" unmerge
extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
rm -rf "$fake_root/var/lib/extensions/test-extension.v" "$fake_root/var/test-extension-vpick"
)
( init_trap
: "Check if vpick works for image extensions if .v is a relative symlink"
fake_root=${roots_dir:+"$roots_dir/vpick-image-relative-symlink"}
hierarchy=/opt
prepare_root "$fake_root" "$hierarchy"
prepare_extension_image_raw "$fake_root" "$hierarchy"
mkdir -p "$fake_root/var/test-extension-vpick"
mv "$fake_root/var/lib/extensions/test-extension.raw" "$fake_root/var/test-extension-vpick/test-extension_1.0.raw"
ln -s "../../test-extension-vpick" "$fake_root/var/lib/extensions/test-extension.raw.v"
prepare_read_only_hierarchy "$fake_root" "$hierarchy"
run_systemd_sysext "$fake_root" merge
extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
run_systemd_sysext "$fake_root" unmerge
extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
rm -rf "$fake_root/var/lib/extensions/test-extension.raw.v" "$fake_root/var/test-extension-vpick"
)
( init_trap
: "Check if vpick works for image extensions if .v is an absolute symlink"
fake_root=${roots_dir:+"$roots_dir/vpick-image-absolute-symlink"}
hierarchy=/opt
prepare_root "$fake_root" "$hierarchy"
prepare_extension_image_raw "$fake_root" "$hierarchy"
mkdir -p "$fake_root/var/test-extension-vpick"
mv "$fake_root/var/lib/extensions/test-extension.raw" "$fake_root/var/test-extension-vpick/test-extension_1.0.raw"
ln -s "/var/test-extension-vpick" "$fake_root/var/lib/extensions/test-extension.raw.v"
prepare_read_only_hierarchy "$fake_root" "$hierarchy"
run_systemd_sysext "$fake_root" merge
extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
run_systemd_sysext "$fake_root" unmerge
extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
rm -rf "$fake_root/var/lib/extensions/test-extension.raw.v" "$fake_root/var/test-extension-vpick"
)
( init_trap
: "Check if vpick works for directory extensions if inside a .v there is a relative symlink"
fake_root=${roots_dir:+"$roots_dir/vpick-dir-relative-symlink-inside"}
hierarchy=/opt
prepare_root "$fake_root" "$hierarchy"
prepare_extension_image "$fake_root" "$hierarchy"
mv -T "$fake_root/var/lib/extensions/test-extension" "$fake_root/var/othername-extension"
mkdir -p "$fake_root/var/lib/extensions/test-extension.v"
ln -s "../../../othername-extension" "$fake_root/var/lib/extensions/test-extension.v/test-extension_1.0"
prepare_read_only_hierarchy "$fake_root" "$hierarchy"
run_systemd_sysext "$fake_root" merge
extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
run_systemd_sysext "$fake_root" unmerge
extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
rm -rf "$fake_root/var/lib/extensions/test-extension.v" "$fake_root/var/othername-extension"
)
( init_trap
: "Check if vpick works for directory extensions if inside a .v there is an absolute symlink"
fake_root=${roots_dir:+"$roots_dir/vpick-dir-absolute-symlink-inside"}
hierarchy=/opt
prepare_root "$fake_root" "$hierarchy"
prepare_extension_image "$fake_root" "$hierarchy"
mv -T "$fake_root/var/lib/extensions/test-extension" "$fake_root/var/othername-extension"
mkdir -p "$fake_root/var/lib/extensions/test-extension.v"
ln -s "/var/othername-extension" "$fake_root/var/lib/extensions/test-extension.v/test-extension_1.0"
prepare_read_only_hierarchy "$fake_root" "$hierarchy"
run_systemd_sysext "$fake_root" merge
extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
run_systemd_sysext "$fake_root" unmerge
extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
rm -rf "$fake_root/var/lib/extensions/test-extension.v" "$fake_root/var/othername-extension"
)
( init_trap
: "Check if vpick works for image extensions if inside a .v there is a relative symlink"
fake_root=${roots_dir:+"$roots_dir/vpick-image-relative-symlink-inside"}
hierarchy=/opt
prepare_root "$fake_root" "$hierarchy"
prepare_extension_image_raw "$fake_root" "$hierarchy"
mv "$fake_root/var/lib/extensions/test-extension.raw" "$fake_root/var/othername-extension.raw"
mkdir -p "$fake_root/var/lib/extensions/test-extension.raw.v"
ln -s "../../../othername-extension.raw" "$fake_root/var/lib/extensions/test-extension.raw.v/test-extension_1.0.raw"
prepare_read_only_hierarchy "$fake_root" "$hierarchy"
run_systemd_sysext "$fake_root" merge
extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
run_systemd_sysext "$fake_root" unmerge
extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
rm -rf "$fake_root/var/lib/extensions/test-extension.raw.v" "$fake_root/var/othername-extension.raw"
)
( init_trap
: "Check if vpick works for image extensions if inside a .v there is an absolute symlink"
fake_root=${roots_dir:+"$roots_dir/vpick-image-absolute-symlink-inside"}
hierarchy=/opt
prepare_root "$fake_root" "$hierarchy"
prepare_extension_image_raw "$fake_root" "$hierarchy"
mv "$fake_root/var/lib/extensions/test-extension.raw" "$fake_root/var/othername-extension.raw"
mkdir -p "$fake_root/var/lib/extensions/test-extension.raw.v"
ln -s "/var/othername-extension.raw" "$fake_root/var/lib/extensions/test-extension.raw.v/test-extension_1.0.raw"
prepare_read_only_hierarchy "$fake_root" "$hierarchy"
run_systemd_sysext "$fake_root" merge
extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
run_systemd_sysext "$fake_root" unmerge
extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
rm -rf "$fake_root/var/lib/extensions/test-extension.raw.v" "$fake_root/var/othername-extension.raw"
)
# Done with the above vpick symlink tests for --root= and without
} # End of run_sysext_tests