diff --git a/man/systemd-dissect.xml b/man/systemd-dissect.xml
index e72c66386a..b65a2c39f1 100644
--- a/man/systemd-dissect.xml
+++ b/man/systemd-dissect.xml
@@ -124,9 +124,7 @@
project='man-pages'>fstab5. For
details see below.
- In place of the image path a .v/ versioned directory may be specified, see
- systemd.v7 for
- details.
+
diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml
index f39f1a8ca6..29edbba4cc 100644
--- a/man/systemd-nspawn.xml
+++ b/man/systemd-nspawn.xml
@@ -217,9 +217,7 @@
machinectl1
section "Files and Directories" for the precise search path.
- In place of the directory path a .v/ versioned directory may be specified, see
- systemd.v7 for
- details.
+ If neither , , nor
are specified, the current directory will be used. May not be specified
@@ -317,9 +315,7 @@
Any other partitions, such as foreign partitions or swap partitions are not mounted. May not be specified
together with , .
- In place of the image path a .v/ versioned directory may be specified, see
- systemd.v7 for
- details.
+
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index dac19d6537..05f5f20f44 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -155,9 +155,7 @@
BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
- In place of the directory path a .v/ versioned directory may be specified,
- see systemd.v7 for
- details.
+
@@ -195,9 +193,7 @@
systemd-soft-reboot.service8),
in case the service is configured to survive it.
- In place of the image path a .v/ versioned directory may be specified, see
- systemd.v7 for
- details.
+
@@ -555,6 +551,8 @@
PrivateDevices= below, as it may change the setting of
DevicePolicy=.
+
+
@@ -590,6 +588,8 @@
Note that usage from user units requires overlayfs support in unprivileged user namespaces,
which was first introduced in kernel v5.11.
+
+
diff --git a/man/systemd.v.xml b/man/systemd.v.xml
index b29d32f69a..43e77bdba2 100644
--- a/man/systemd.v.xml
+++ b/man/systemd.v.xml
@@ -26,7 +26,7 @@
In various places systemd components accept paths whose trailing components have the
.v/ suffix, pointing to a directory. These components will then automatically look for
suitable files inside the directory, do a version comparison and open the newest file found (by
- version). Specifically, two expressions are supported:
+ version). Available since version v256. Specifically, two expressions are supported:
diff --git a/man/vpick.xml b/man/vpick.xml
new file mode 100644
index 0000000000..e212895947
--- /dev/null
+++ b/man/vpick.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+ In place of the image path a .v/ versioned directory may be specified, see
+ systemd.v7 for
+ details.
+
+ In place of the directory path a .v/ versioned directory may be specified,
+ see systemd.v7 for
+ details.
+
diff --git a/src/core/namespace.c b/src/core/namespace.c
index d87079ccbc..e55982ffa5 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -47,6 +47,7 @@
#include "tmpfile-util.h"
#include "umask-util.h"
#include "user-util.h"
+#include "vpick.h"
#define DEV_MOUNT_OPTIONS (MS_NOSUID|MS_STRICTATIME|MS_NOEXEC)
@@ -500,9 +501,24 @@ 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; i++) {
+ _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
_cleanup_free_ char *mount_point = NULL;
const MountImage *m = mount_images + i;
+ r = path_pick(/* toplevel_path= */ NULL,
+ /* toplevel_fd= */ AT_FDCWD,
+ m->source,
+ &pick_filter_image_raw,
+ PICK_ARCHITECTURE|PICK_TRIES,
+ &result);
+ if (r < 0)
+ return r;
+ if (!result.path)
+ return log_debug_errno(
+ SYNTHETIC_ERRNO(ENOENT),
+ "No matching entry in .v/ directory %s found.",
+ m->source);
+
if (asprintf(&mount_point, "%s/%zu", extension_dir, i) < 0)
return -ENOMEM;
@@ -524,7 +540,7 @@ static int append_extensions(
.path_malloc = TAKE_PTR(mount_point),
.image_options_const = m->mount_options,
.ignore = m->ignore_enoent,
- .source_const = m->source,
+ .source_malloc = TAKE_PTR(result.path),
.mode = MOUNT_EXTENSION_IMAGE,
.has_prefix = true,
};
@@ -534,7 +550,8 @@ static int append_extensions(
* Bind mount them in the same location as the ExtensionImages, so that we
* can check that they are valid trees (extension-release.d). */
STRV_FOREACH(extension_directory, extension_directories) {
- _cleanup_free_ char *mount_point = NULL, *source = NULL;
+ _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
+ _cleanup_free_ char *mount_point = NULL;
const char *e = *extension_directory;
bool ignore_enoent = false;
@@ -551,9 +568,19 @@ static int append_extensions(
if (startswith(e, "+"))
e++;
- source = strdup(e);
- if (!source)
- return -ENOMEM;
+ r = path_pick(/* toplevel_path= */ NULL,
+ /* toplevel_fd= */ AT_FDCWD,
+ e,
+ &pick_filter_image_dir,
+ PICK_ARCHITECTURE|PICK_TRIES,
+ &result);
+ if (r < 0)
+ return r;
+ if (!result.path)
+ return log_debug_errno(
+ SYNTHETIC_ERRNO(ENOENT),
+ "No matching entry in .v/ directory %s found.",
+ e);
for (size_t j = 0; hierarchies && hierarchies[j]; ++j) {
char *prefixed_hierarchy = path_join(mount_point, hierarchies[j]);
@@ -571,7 +598,7 @@ static int append_extensions(
*me = (MountEntry) {
.path_malloc = TAKE_PTR(mount_point),
- .source_malloc = TAKE_PTR(source),
+ .source_malloc = TAKE_PTR(result.path),
.mode = MOUNT_EXTENSION_DIRECTORY,
.ignore = ignore_enoent,
.has_prefix = true,
diff --git a/test/units/testsuite-50.sh b/test/units/testsuite-50.sh
index af379a4d8b..e34cbd915b 100755
--- a/test/units/testsuite-50.sh
+++ b/test/units/testsuite-50.sh
@@ -435,6 +435,18 @@ EOF
systemctl start testservice-50e.service
systemctl is-active testservice-50e.service
+# Check vpick support in ExtensionImages=
+VBASE="vtest$RANDOM"
+VDIR="/tmp/${VBASE}.v"
+mkdir "$VDIR"
+
+ln -s /usr/share/app0.raw "$VDIR/${VBASE}_0.raw"
+ln -s /usr/share/app1.raw "$VDIR/${VBASE}_1.raw"
+
+systemd-run -P -p ExtensionImages="$VDIR" bash -c '/opt/script1.sh | grep ID'
+
+rm -rf "$VDIR"
+
# ExtensionDirectories will set up an overlay
mkdir -p "${image_dir}/app0" "${image_dir}/app1" "${image_dir}/app-nodistro" "${image_dir}/service-scoped-test"
(! systemd-run -P --property ExtensionDirectories="${image_dir}/nonexistent" --property RootImage="${image}.raw" cat /opt/script0.sh)
@@ -467,6 +479,19 @@ RemainAfterExit=yes
EOF
systemctl start testservice-50f.service
systemctl is-active testservice-50f.service
+
+# Check vpick support in ExtensionDirectories=
+VBASE="vtest$RANDOM"
+VDIR="/tmp/${VBASE}.v"
+mkdir "$VDIR"
+
+ln -s "${image_dir}/app0" "$VDIR/${VBASE}_0"
+ln -s "${image_dir}/app1" "$VDIR/${VBASE}_1"
+
+systemd-run -P --property ExtensionDirectories="$VDIR" cat /opt/script1.sh | grep -q -F "extension-release.app2"
+
+rm -rf "$VDIR"
+
systemd-dissect --umount "${image_dir}/app0"
systemd-dissect --umount "${image_dir}/app1"