mirror of
https://github.com/flatcar/scripts.git
synced 2026-05-04 11:51:14 +02:00
overlay coreos/user-patches: Regenerate patches for sys-apps/systemd
Signed-off-by: Krzesimir Nowak <knowak@microsoft.com>
This commit is contained in:
parent
281c8f3bb1
commit
d97ab840cd
@ -1,7 +1,7 @@
|
||||
From 6055d8b50c4a39d3e5f4fa0cf017a3b04786c5ba Mon Sep 17 00:00:00 2001
|
||||
From 3e713e019ab2e13e0d48bf30bab0ddaf3573458d Mon Sep 17 00:00:00 2001
|
||||
From: David Michael <dm0@redhat.com>
|
||||
Date: Tue, 16 Apr 2019 02:44:51 +0000
|
||||
Subject: [PATCH 01/20] wait-online: set --any by default
|
||||
Subject: [PATCH 01/14] wait-online: set --any by default
|
||||
|
||||
The systemd-networkd-wait-online command would normally continue
|
||||
waiting after a network interface is usable if other interfaces are
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
From 5bff53a23228b10d93d342510f0ffd41185e3011 Mon Sep 17 00:00:00 2001
|
||||
From d34fa493e6d69b97633e329d55413a549da8239d Mon Sep 17 00:00:00 2001
|
||||
From: Alex Crawford <alex.crawford@coreos.com>
|
||||
Date: Wed, 2 Mar 2016 10:46:33 -0800
|
||||
Subject: [PATCH 02/20] needs-update: don't require strictly newer usr
|
||||
Subject: [PATCH 02/14] needs-update: don't require strictly newer usr
|
||||
|
||||
Updates should be triggered whenever usr changes, not only when it is newer.
|
||||
---
|
||||
@ -23,7 +23,7 @@ index d9d78262a1..761bbdecca 100644
|
||||
This requires that updates to <filename>/usr/</filename> are always
|
||||
followed by an update of the modification time of
|
||||
diff --git a/src/shared/condition.c b/src/shared/condition.c
|
||||
index b09eff1bfb..3a170b1820 100644
|
||||
index 15e3ee9840..381378e77a 100644
|
||||
--- a/src/shared/condition.c
|
||||
+++ b/src/shared/condition.c
|
||||
@@ -817,7 +817,7 @@ static int condition_test_needs_update(Condition *c, char **env) {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
From df56cf2ad0c6c84a22e9fca8893c610b82b78377 Mon Sep 17 00:00:00 2001
|
||||
From 2cc519ebec4f01f76bcdcde61259ba23a810ea30 Mon Sep 17 00:00:00 2001
|
||||
From: Adrian Vladu <avladu@cloudbasesolutions.com>
|
||||
Date: Fri, 16 Feb 2024 11:22:08 +0000
|
||||
Subject: [PATCH 03/20] core: use max for DefaultTasksMax
|
||||
Subject: [PATCH 03/14] core: use max for DefaultTasksMax
|
||||
|
||||
Since systemd v228, systemd has a DefaultTasksMax which defaulted
|
||||
to 512, later 15% of the system's maximum number of PIDs. This
|
||||
@ -21,7 +21,7 @@ Signed-off-by: Adrian Vladu <avladu@cloudbasesolutions.com>
|
||||
3 files changed, 3 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/man/systemd-system.conf.xml b/man/systemd-system.conf.xml
|
||||
index cf5a3612f6..a0f9f8ba57 100644
|
||||
index b7fe53dc9c..175fe67139 100644
|
||||
--- a/man/systemd-system.conf.xml
|
||||
+++ b/man/systemd-system.conf.xml
|
||||
@@ -227,7 +227,7 @@
|
||||
@ -34,10 +34,10 @@ index cf5a3612f6..a0f9f8ba57 100644
|
||||
Kernel has a default value for <varname>kernel.pid_max=</varname> and an algorithm of counting in case of more than 32 cores.
|
||||
For example, with the default <varname>kernel.pid_max=</varname>, <varname>DefaultTasksMax=</varname> defaults to 4915,
|
||||
diff --git a/src/core/manager.c b/src/core/manager.c
|
||||
index 20a535f2f4..be1c352045 100644
|
||||
index a5a51023c5..ef0ce9e31d 100644
|
||||
--- a/src/core/manager.c
|
||||
+++ b/src/core/manager.c
|
||||
@@ -112,7 +112,7 @@
|
||||
@@ -113,7 +113,7 @@
|
||||
/* How many units and jobs to process of the bus queue before returning to the event loop. */
|
||||
#define MANAGER_BUS_MESSAGE_BUDGET 100U
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
From 38ef166d85928d1f806bc48f3d29f45563d1abde Mon Sep 17 00:00:00 2001
|
||||
From a8c18ecc95e15af2d669649115826430698dcc5d Mon Sep 17 00:00:00 2001
|
||||
From: Matthew Garrett <mjg59@coreos.com>
|
||||
Date: Tue, 20 Dec 2016 16:43:22 +0000
|
||||
Subject: [PATCH 04/20] systemd: Disable SELinux permissions checks
|
||||
Subject: [PATCH 04/14] systemd: Disable SELinux permissions checks
|
||||
|
||||
We don't care about the interaction between systemd and SELinux policy, so
|
||||
let's just disable these checks rather than having to incorporate policy
|
||||
@ -12,7 +12,7 @@ to limit containers and not anything running directly on the host.
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c
|
||||
index 8ccc31630d..34e9cebee8 100644
|
||||
index 7457b3d456..82afe343dd 100644
|
||||
--- a/src/core/selinux-access.c
|
||||
+++ b/src/core/selinux-access.c
|
||||
@@ -2,7 +2,7 @@
|
||||
@ -22,8 +22,8 @@ index 8ccc31630d..34e9cebee8 100644
|
||||
-#if HAVE_SELINUX
|
||||
+#if 0
|
||||
|
||||
#include <selinux/avc.h>
|
||||
#include <selinux/selinux.h>
|
||||
#include <unistd.h>
|
||||
|
||||
--
|
||||
2.52.0
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
From 4e071bef0713099cfe2540a5576744c0e5c41723 Mon Sep 17 00:00:00 2001
|
||||
From 33a603bb00fce6e4c3b4faf80157e8532932fb00 Mon Sep 17 00:00:00 2001
|
||||
From: Sayan Chowdhury <schowdhury@microsoft.com>
|
||||
Date: Fri, 16 Dec 2022 16:28:26 +0530
|
||||
Subject: [PATCH 05/20] Revert "getty: Pass tty to use by agetty via stdin"
|
||||
Subject: [PATCH 05/14] Revert "getty: Pass tty to use by agetty via stdin"
|
||||
|
||||
This reverts commit b4bf9007cbee7dc0b1356897344ae2a7890df84c.
|
||||
|
||||
@ -17,17 +17,17 @@ Signed-off-by: Sayan Chowdhury <schowdhury@microsoft.com>
|
||||
4 files changed, 12 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/units/console-getty.service.in b/units/console-getty.service.in
|
||||
index 967d8337ab..1f2d8b910f 100644
|
||||
index 278048724f..5731e68d8f 100644
|
||||
--- a/units/console-getty.service.in
|
||||
+++ b/units/console-getty.service.in
|
||||
@@ -20,12 +20,12 @@ Before=getty.target
|
||||
ConditionPathExists=/dev/console
|
||||
|
||||
[Service]
|
||||
-ExecStart=-/sbin/agetty --noreset --noclear --issue-file=/etc/issue:/etc/issue.d:/run/issue.d:/usr/lib/issue.d --keep-baud 115200,57600,38400,9600 - ${TERM}
|
||||
-ExecStart=-{{AGETTY}} --noreset --noclear --issue-file=/etc/issue:/etc/issue.d:/run/issue.d:/usr/lib/issue.d --keep-baud 115200,57600,38400,9600 - ${TERM}
|
||||
+# The '-o' option value tells agetty to replace 'login' arguments with '--' for
|
||||
+# safety, and then the entered username.
|
||||
+ExecStart=-/sbin/agetty -o '-- \\u' --noreset --noclear --keep-baud 115200,57600,38400,9600 console ${TERM}
|
||||
+ExecStart=-{{AGETTY}} -o '-- \\u' --noreset --noclear --keep-baud 115200,57600,38400,9600 console ${TERM}
|
||||
Type=idle
|
||||
Restart=always
|
||||
UtmpIdentifier=cons
|
||||
@ -37,17 +37,17 @@ index 967d8337ab..1f2d8b910f 100644
|
||||
TTYReset=yes
|
||||
TTYVHangup=yes
|
||||
diff --git a/units/container-getty@.service.in b/units/container-getty@.service.in
|
||||
index e0b27613df..5f27653d1f 100644
|
||||
index 18e5a98a7f..568fcd1e53 100644
|
||||
--- a/units/container-getty@.service.in
|
||||
+++ b/units/container-getty@.service.in
|
||||
@@ -25,13 +25,13 @@ Conflicts=rescue.service
|
||||
Before=rescue.service
|
||||
|
||||
[Service]
|
||||
-ExecStart=-/sbin/agetty --noreset --noclear --issue-file=/etc/issue:/etc/issue.d:/run/issue.d:/usr/lib/issue.d - ${TERM}
|
||||
-ExecStart=-{{AGETTY}} --noreset --noclear --issue-file=/etc/issue:/etc/issue.d:/run/issue.d:/usr/lib/issue.d - ${TERM}
|
||||
+# The '-o' option value tells agetty to replace 'login' arguments with '--' for
|
||||
+# safety, and then the entered username.
|
||||
+ExecStart=-/sbin/agetty -o '-- \\u' --noreset --noclear pts/%I ${TERM}
|
||||
+ExecStart=-{{AGETTY}} -o '-- \\u' --noreset --noclear pts/%I ${TERM}
|
||||
Type=idle
|
||||
Restart=always
|
||||
RestartSec=0
|
||||
@ -58,17 +58,17 @@ index e0b27613df..5f27653d1f 100644
|
||||
TTYReset=yes
|
||||
TTYVHangup=yes
|
||||
diff --git a/units/getty@.service.in b/units/getty@.service.in
|
||||
index 104c4acc96..1819627d1c 100644
|
||||
index 15f1a572fd..a3285d956e 100644
|
||||
--- a/units/getty@.service.in
|
||||
+++ b/units/getty@.service.in
|
||||
@@ -34,13 +34,13 @@ Before=rescue.service
|
||||
ConditionPathExists=/dev/tty0
|
||||
|
||||
[Service]
|
||||
-ExecStart=-/sbin/agetty --noreset --noclear --issue-file=/etc/issue:/etc/issue.d:/run/issue.d:/usr/lib/issue.d - ${TERM}
|
||||
-ExecStart=-{{AGETTY}} --noreset --noclear --issue-file=/etc/issue:/etc/issue.d:/run/issue.d:/usr/lib/issue.d - ${TERM}
|
||||
+# The '-o' option value tells agetty to replace 'login' arguments with '--' for
|
||||
+# safety, and then the entered username.
|
||||
+ExecStart=-/sbin/agetty -o '-- \\u' --noreset --noclear %I ${TERM}
|
||||
+ExecStart=-{{AGETTY}} -o '-- \\u' --noreset --noclear %I ${TERM}
|
||||
Type=idle
|
||||
Restart=always
|
||||
RestartSec=0
|
||||
@ -79,17 +79,17 @@ index 104c4acc96..1819627d1c 100644
|
||||
TTYReset=yes
|
||||
TTYVHangup=yes
|
||||
diff --git a/units/serial-getty@.service.in b/units/serial-getty@.service.in
|
||||
index 0134c83d48..ba4cbc0edb 100644
|
||||
index 8b5a63d681..29ab8a0533 100644
|
||||
--- a/units/serial-getty@.service.in
|
||||
+++ b/units/serial-getty@.service.in
|
||||
@@ -30,12 +30,12 @@ Conflicts=rescue.service
|
||||
Before=rescue.service
|
||||
|
||||
[Service]
|
||||
-ExecStart=-/sbin/agetty --noreset --noclear --issue-file=/etc/issue:/etc/issue.d:/run/issue.d:/usr/lib/issue.d --keep-baud 115200,57600,38400,9600 - ${TERM}
|
||||
-ExecStart=-{{AGETTY}} --noreset --noclear --issue-file=/etc/issue:/etc/issue.d:/run/issue.d:/usr/lib/issue.d --keep-baud 115200,57600,38400,9600 - ${TERM}
|
||||
+# The '-o' option value tells agetty to replace 'login' arguments with '--' for
|
||||
+# safety, and then the entered username.
|
||||
+ExecStart=-/sbin/agetty -o '-- \\u' --noreset --noclear --keep-baud 115200,57600,38400,9600 %I ${TERM}
|
||||
+ExecStart=-{{AGETTY}} -o '-- \\u' --noreset --noclear --keep-baud 115200,57600,38400,9600 %I ${TERM}
|
||||
Type=idle
|
||||
Restart=always
|
||||
UtmpIdentifier=%I
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
From b097e139801009d722c33a9580bcda23a4a7a1e1 Mon Sep 17 00:00:00 2001
|
||||
From 6c83b73ac087aaa1f08551c064cbac119ad92490 Mon Sep 17 00:00:00 2001
|
||||
From: Adrian Vladu <avladu@cloudbasesolutions.com>
|
||||
Date: Fri, 16 Feb 2024 11:29:04 +0000
|
||||
Subject: [PATCH 06/20] units: Keep using old journal file format
|
||||
Subject: [PATCH 06/14] units: Keep using old journal file format
|
||||
|
||||
Systemd 252 made an incompatible change in journal file format. Temporarily
|
||||
force journald to use the old journal format to give logging containers more
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
From 0ba9b9356861f8012c0e7794d9c61ebf21a9c6d7 Mon Sep 17 00:00:00 2001
|
||||
From 9d6db023c34d96b582e763da77c464629266f8e8 Mon Sep 17 00:00:00 2001
|
||||
From: Krzesimir Nowak <knowak@microsoft.com>
|
||||
Date: Wed, 22 Oct 2025 10:39:42 +0200
|
||||
Subject: [PATCH 07/20] tmpfiles.d: Fix DNS issues with default k8s
|
||||
Subject: [PATCH 07/14] tmpfiles.d: Fix DNS issues with default k8s
|
||||
configuration
|
||||
|
||||
The Kubelet takes /etc/resolv.conf for, e.g., CoreDNS which has dnsPolicy
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
From b3430348f5ae93251076fb4e3b4aecbfa02513b5 Mon Sep 17 00:00:00 2001
|
||||
From bbc8ff28a7f0208b6b1020d0d2ca900cdfb5a42f Mon Sep 17 00:00:00 2001
|
||||
From: Krzesimir Nowak <knowak@microsoft.com>
|
||||
Date: Fri, 24 Oct 2025 11:06:57 +0200
|
||||
Subject: [PATCH 08/20] units: Make multi-user.target the default target
|
||||
Subject: [PATCH 08/14] units: Make multi-user.target the default target
|
||||
|
||||
Signed-off-by: Krzesimir Nowak <knowak@microsoft.com>
|
||||
---
|
||||
@ -9,7 +9,7 @@ Signed-off-by: Krzesimir Nowak <knowak@microsoft.com>
|
||||
1 file changed, 5 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/units/meson.build b/units/meson.build
|
||||
index 4f47a3b2bd..63940a72be 100644
|
||||
index 12cf47d797..58de76dca9 100644
|
||||
--- a/units/meson.build
|
||||
+++ b/units/meson.build
|
||||
@@ -47,10 +47,7 @@ units = [
|
||||
@ -33,9 +33,9 @@ index 4f47a3b2bd..63940a72be 100644
|
||||
+ 'file' : 'multi-user.target',
|
||||
+ 'symlinks' : ['default.target'],
|
||||
+ },
|
||||
{ 'file' : 'network-online.target' },
|
||||
{ 'file' : 'network-pre.target' },
|
||||
{ 'file' : 'network.target' },
|
||||
{
|
||||
'file' : 'systemd-mute-console.socket',
|
||||
'symlinks' : ['sockets.target.wants/']
|
||||
--
|
||||
2.52.0
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
From 530ffcd9e3212e0c93002e752b682dd41a8889b1 Mon Sep 17 00:00:00 2001
|
||||
From b6e987ebcc0efd6ea5a68582fe194650ebc89636 Mon Sep 17 00:00:00 2001
|
||||
From: Kai Lueke <kailuke@microsoft.com>
|
||||
Date: Thu, 20 Nov 2025 23:43:55 +0900
|
||||
Subject: [PATCH 10/20] discover-image: Follow symlinks in a given root
|
||||
Subject: [PATCH 09/14] 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).
|
||||
@ -21,23 +21,23 @@ 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.
|
||||
---
|
||||
src/shared/discover-image.c | 162 +++++++++++++++++++++++++++---------
|
||||
1 file changed, 122 insertions(+), 40 deletions(-)
|
||||
src/shared/discover-image.c | 169 +++++++++++++++++++-----------------
|
||||
1 file changed, 90 insertions(+), 79 deletions(-)
|
||||
|
||||
diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c
|
||||
index 888f11f206..53ee30c3f8 100644
|
||||
index 41fcaa9f44..6366316088 100644
|
||||
--- a/src/shared/discover-image.c
|
||||
+++ b/src/shared/discover-image.c
|
||||
@@ -356,6 +356,8 @@ static int image_make(
|
||||
@@ -400,6 +400,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 dir_path and filename and an fd.
|
||||
+ * 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. */
|
||||
@@ -700,10 +702,7 @@ int image_find(RuntimeScope scope,
|
||||
@@ -736,10 +738,7 @@ int image_find(RuntimeScope scope,
|
||||
const char *root,
|
||||
Image **ret) {
|
||||
|
||||
@ -49,17 +49,15 @@ index 888f11f206..53ee30c3f8 100644
|
||||
|
||||
assert(scope < _RUNTIME_SCOPE_MAX && scope != RUNTIME_SCOPE_GLOBAL);
|
||||
assert(class >= 0);
|
||||
@@ -718,16 +717,24 @@ int image_find(RuntimeScope scope,
|
||||
@@ -754,32 +753,47 @@ int image_find(RuntimeScope scope,
|
||||
if (!names)
|
||||
return -ENOMEM;
|
||||
|
||||
+ _cleanup_close_ int root_fd = AT_FDCWD;
|
||||
+ _cleanup_close_ int rfd = AT_FDCWD;
|
||||
+ if (root) {
|
||||
+ root_fd = open(root, O_CLOEXEC|O_DIRECTORY|O_PATH);
|
||||
+ if (root_fd < 0) {
|
||||
+ log_debug_errno(errno, "Failed to open root directory '%s': %m", root);
|
||||
+ return -errno;
|
||||
+ }
|
||||
+ 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;
|
||||
@ -67,40 +65,47 @@ index 888f11f206..53ee30c3f8 100644
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
STRV_FOREACH(path, search) {
|
||||
STRV_FOREACH(s, search) {
|
||||
- _cleanup_free_ char *resolved = NULL;
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
+ _cleanup_free_ char *search_path = NULL;
|
||||
|
||||
- r = chase_and_opendir(*path, root, CHASE_PREFIX_ROOT, &resolved, &d);
|
||||
+ r = chase_and_opendir(*path, root, CHASE_PREFIX_ROOT, NULL, &d);
|
||||
- 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)
|
||||
@@ -736,11 +743,20 @@ int image_find(RuntimeScope scope,
|
||||
return r;
|
||||
|
||||
STRV_FOREACH(n, names) {
|
||||
_cleanup_free_ char *fname_buf = NULL;
|
||||
- _cleanup_free_ char *fname_buf = NULL;
|
||||
const char *fname = *n;
|
||||
+ _cleanup_free_ char *fname_path = NULL;
|
||||
+ _cleanup_free_ char *resolved_file = NULL;
|
||||
+ _cleanup_free_ char *fname_path = NULL, *chased_path = NULL, *resolved_file = NULL;
|
||||
+ _cleanup_close_ int fd = -EBADF;
|
||||
+ _cleanup_close_ int subdirfd = -EBADF;
|
||||
|
||||
- _cleanup_close_ int fd = openat(dirfd(d), fname, O_PATH|O_CLOEXEC|open_flags);
|
||||
+ fname_path = path_join(*path, fname);
|
||||
+ if (!fname_path)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ /* Follow symlinks only inside given root */
|
||||
+ fd = chase_and_open(fname_path, root, CHASE_PREFIX_ROOT, O_PATH|O_CLOEXEC, &resolved_file);
|
||||
if (fd < 0) {
|
||||
- if (fd < 0) {
|
||||
- if (errno != ENOENT)
|
||||
- return -errno;
|
||||
+ if (fd != -ENOENT)
|
||||
+ return fd;
|
||||
+ 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;
|
||||
}
|
||||
@@ -769,10 +785,6 @@ int image_find(RuntimeScope scope,
|
||||
- }
|
||||
+ 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)
|
||||
@@ -805,10 +819,6 @@ int image_find(RuntimeScope scope,
|
||||
|
||||
*ASSERT_PTR(endswith(suffix, ".v")) = 0;
|
||||
|
||||
@ -111,81 +116,75 @@ index 888f11f206..53ee30c3f8 100644
|
||||
PickFilter filter = {
|
||||
.type_mask = endswith(suffix, ".raw") ? (UINT32_C(1) << DT_REG) | (UINT32_C(1) << DT_BLK) : (UINT32_C(1) << DT_DIR),
|
||||
.basename = name,
|
||||
@@ -782,23 +794,27 @@ int image_find(RuntimeScope scope,
|
||||
@@ -818,48 +828,44 @@ int image_find(RuntimeScope scope,
|
||||
|
||||
_cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
|
||||
r = path_pick(root,
|
||||
- /* toplevel_fd= */ AT_FDCWD,
|
||||
- vp,
|
||||
+ root_fd,
|
||||
+ fname_path,
|
||||
+ 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", strempty(root), skip_leading_slash(fname_path));
|
||||
+ 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: %m", vp);
|
||||
+ log_debug("Found versioned directory '%s/%s', without matching entry, skipping.", strempty(root), skip_leading_slash(fname_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;
|
||||
- fd = safe_close(fd);
|
||||
+ /* Set subdirfd to indicate it should be used instead of dirfd(d).
|
||||
+ * We reuse the O_PATH fd because it's only used in openat style in image_make().
|
||||
+ * This subdirfd can be deleted with https://github.com/systemd/systemd/pull/39970 */
|
||||
+ subdirfd = TAKE_FD(fd);
|
||||
+ close_and_replace(fd, result.fd);
|
||||
|
||||
_cleanup_free_ char *bn = NULL;
|
||||
r = path_extract_filename(result.path, &bn);
|
||||
@@ -812,13 +828,38 @@ int image_find(RuntimeScope scope,
|
||||
return log_oom();
|
||||
|
||||
fname = fname_buf;
|
||||
+ fname_path = mfree(fname_path);
|
||||
+ fname_path = path_join(*path, fname);
|
||||
+ if (!fname_path)
|
||||
+ return log_oom();
|
||||
|
||||
+ resolved_file = mfree(resolved_file);
|
||||
close_and_replace(fd, result.fd);
|
||||
+ free(resolved_file);
|
||||
+ resolved_file = path_join(root, result.path);
|
||||
+ if (!resolved_file)
|
||||
+ return log_oom();
|
||||
+ 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;
|
||||
}
|
||||
|
||||
- r = image_make(class, name, dirfd(d), resolved, fname, fd, &st, ret);
|
||||
+ /* Only put resolved paths into the image entry.
|
||||
+ * Defending against symlink races is out of scope
|
||||
+ * and we trust a given root in that regard. */
|
||||
+ _cleanup_free_ char *resolved_dir = NULL;
|
||||
+ _cleanup_free_ char *resolved_fname = NULL;
|
||||
+
|
||||
+ r = path_extract_directory(resolved_file, &resolved_dir);
|
||||
+ if (r < 0) {
|
||||
+ log_debug_errno(r, "Failed to extract directory name of image path '%s', skipping: %m", resolved_file);
|
||||
+ continue;
|
||||
+ }
|
||||
+ r = path_extract_filename(resolved_file, &resolved_fname);
|
||||
+ if (r < 0) {
|
||||
+ log_debug_errno(r, "Failed to extract basename of image path '%s', skipping: %m", resolved_file);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ r = image_make(class, name, subdirfd == -EBADF ? dirfd(d) : subdirfd, resolved_dir, resolved_fname, fd, &st, ret);
|
||||
- _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)
|
||||
@@ -899,26 +940,31 @@ int image_discover(
|
||||
@@ -940,46 +946,58 @@ int image_discover(
|
||||
const char *root,
|
||||
Hashmap **images) {
|
||||
|
||||
@ -200,13 +199,11 @@ index 888f11f206..53ee30c3f8 100644
|
||||
assert(class < _IMAGE_CLASS_MAX);
|
||||
assert(images);
|
||||
|
||||
+ _cleanup_close_ int root_fd = AT_FDCWD;
|
||||
+ _cleanup_close_ int rfd = AT_FDCWD;
|
||||
+ if (root) {
|
||||
+ root_fd = open(root, O_CLOEXEC|O_DIRECTORY|O_PATH);
|
||||
+ if (root_fd < 0) {
|
||||
+ log_debug_errno(errno, "Failed to open root directory '%s': %m", root);
|
||||
+ return -errno;
|
||||
+ }
|
||||
+ 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;
|
||||
@ -214,43 +211,52 @@ index 888f11f206..53ee30c3f8 100644
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
STRV_FOREACH(path, search) {
|
||||
STRV_FOREACH(s, search) {
|
||||
- _cleanup_free_ char *resolved = NULL;
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
+ _cleanup_free_ char *search_path = NULL;
|
||||
|
||||
- r = chase_and_opendir(*path, root, CHASE_PREFIX_ROOT, &resolved, &d);
|
||||
+ r = chase_and_opendir(*path, root, CHASE_PREFIX_ROOT, NULL, &d);
|
||||
- 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)
|
||||
@@ -928,14 +974,23 @@ int image_discover(
|
||||
_cleanup_free_ char *pretty = NULL, *fname_buf = NULL;
|
||||
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_free_ char *fname_path = NULL;
|
||||
+ _cleanup_free_ char *resolved_file = NULL;
|
||||
+ _cleanup_close_ int fd = -EBADF;
|
||||
+ _cleanup_close_ int subdirfd = -EBADF;
|
||||
|
||||
if (dot_or_dot_dot(fname))
|
||||
continue;
|
||||
|
||||
- _cleanup_close_ int fd = openat(dirfd(d), fname, O_PATH|O_CLOEXEC|open_flags);
|
||||
+ fname_path = path_join(*path, fname);
|
||||
+ if (!fname_path)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ /* Follow symlinks only inside given root */
|
||||
+ fd = chase_and_open(fname_path, root, CHASE_PREFIX_ROOT, O_PATH|O_CLOEXEC, &resolved_file);
|
||||
if (fd < 0) {
|
||||
- if (fd < 0) {
|
||||
- if (errno != ENOENT)
|
||||
- return -errno;
|
||||
+ if (fd != -ENOENT)
|
||||
+ return fd;
|
||||
+ fname_path = path_join(search_path, fname);
|
||||
+ if (!fname_path)
|
||||
+ return -ENOMEM;
|
||||
|
||||
continue; /* Vanished while we were looking at it */
|
||||
}
|
||||
@@ -977,10 +1032,6 @@ int image_discover(
|
||||
- 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)
|
||||
@@ -1018,10 +1036,6 @@ int image_discover(
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -261,80 +267,74 @@ index 888f11f206..53ee30c3f8 100644
|
||||
PickFilter filter = {
|
||||
.type_mask = endswith(suffix, ".raw") ? (UINT32_C(1) << DT_REG) | (UINT32_C(1) << DT_BLK) : (UINT32_C(1) << DT_DIR),
|
||||
.basename = pretty,
|
||||
@@ -990,23 +1041,27 @@ int image_discover(
|
||||
@@ -1031,38 +1045,36 @@ int image_discover(
|
||||
|
||||
_cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
|
||||
r = path_pick(root,
|
||||
- /* toplevel_fd= */ AT_FDCWD,
|
||||
- vp,
|
||||
+ root_fd,
|
||||
+ fname_path,
|
||||
+ 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", strempty(root), skip_leading_slash(fname_path));
|
||||
+ 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: %m", vp);
|
||||
+ log_debug("Found versioned directory '%s/%s', without matching entry, skipping.", strempty(root), skip_leading_slash(fname_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;
|
||||
- fd = safe_close(fd);
|
||||
+ /* Set subdirfd to indicate it should be used instead of dirfd(d).
|
||||
+ * We reuse the O_PATH fd because it's only used in openat style in image_make().
|
||||
+ * This subdirfd can be deleted with https://github.com/systemd/systemd/pull/39970 */
|
||||
+ subdirfd = TAKE_FD(fd);
|
||||
+ close_and_replace(fd, result.fd);
|
||||
|
||||
_cleanup_free_ char *bn = NULL;
|
||||
r = path_extract_filename(result.path, &bn);
|
||||
@@ -1020,6 +1075,15 @@ int image_discover(
|
||||
return log_oom();
|
||||
|
||||
fname = fname_buf;
|
||||
+ fname_path = mfree(fname_path);
|
||||
+ fname_path = path_join(*path, fname);
|
||||
+ if (!fname_path)
|
||||
+ return log_oom();
|
||||
+
|
||||
+ resolved_file = mfree(resolved_file);
|
||||
close_and_replace(fd, result.fd);
|
||||
+ free(resolved_file);
|
||||
+ resolved_file = path_join(root, result.path);
|
||||
+ if (!resolved_file)
|
||||
+ return log_oom();
|
||||
+ 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,
|
||||
@@ -1052,7 +1116,25 @@ int image_discover(
|
||||
@@ -1095,11 +1107,10 @@ int image_discover(
|
||||
if (hashmap_contains(*images, pretty))
|
||||
continue;
|
||||
|
||||
- r = image_make(class, pretty, dirfd(d), resolved, fname, fd, &st, &image);
|
||||
- _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 out of scope
|
||||
+ * and we trust a given root in that regard. */
|
||||
+ _cleanup_free_ char *resolved_dir = NULL;
|
||||
+ _cleanup_free_ char *resolved_fname = NULL;
|
||||
+
|
||||
+ r = path_extract_directory(resolved_file, &resolved_dir);
|
||||
+ if (r < 0) {
|
||||
+ log_debug_errno(r, "Failed to extract directory name of image path '%s', skipping: %m", resolved_file);
|
||||
+ continue;
|
||||
+ }
|
||||
+ r = path_extract_filename(resolved_file, &resolved_fname);
|
||||
+ if (r < 0) {
|
||||
+ log_debug_errno(r, "Failed to extract basename of image path '%s', skipping: %m", resolved_file);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ r = image_make(class, pretty, subdirfd == -EBADF ? dirfd(d) : subdirfd, resolved_dir, resolved_fname, fd, &st, &image);
|
||||
+
|
||||
+ * 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)
|
||||
@ -1,33 +0,0 @@
|
||||
From 42b6a55f8d2bdf68ff93764219b3bedffb11f4e0 Mon Sep 17 00:00:00 2001
|
||||
From: Kai Lueke <kailuke@microsoft.com>
|
||||
Date: Thu, 20 Nov 2025 23:43:55 +0900
|
||||
Subject: [PATCH 09/20] 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.
|
||||
Change the openat call to use the chase helper function to resolve the
|
||||
symlink in the given root.
|
||||
---
|
||||
src/shared/vpick.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/shared/vpick.c b/src/shared/vpick.c
|
||||
index 07d9d9ffd8..b203609cc9 100644
|
||||
--- a/src/shared/vpick.c
|
||||
+++ b/src/shared/vpick.c
|
||||
@@ -471,9 +471,9 @@ static int make_choice(
|
||||
if (!p)
|
||||
return log_oom_debug();
|
||||
|
||||
- object_fd = openat(dir_fd, best_filename, O_CLOEXEC|O_PATH);
|
||||
+ object_fd = chase_and_openat(toplevel_fd, p, CHASE_AT_RESOLVE_IN_ROOT, O_PATH|O_CLOEXEC, NULL);
|
||||
if (object_fd < 0)
|
||||
- return log_debug_errno(errno, "Failed to open '%s/%s': %m",
|
||||
+ return log_debug_errno(object_fd, "Failed to open '%s/%s': %m",
|
||||
empty_to_root(toplevel_path), skip_leading_slash(inode_path));
|
||||
|
||||
return pin_choice(
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
From 6a95919888a99d92636e0aa28c68d0f95f16e48e Mon Sep 17 00:00:00 2001
|
||||
From 3a1d8cb52278fb7926d7666385d9b7ef120508d6 Mon Sep 17 00:00:00 2001
|
||||
From: Kai Lueke <kailuke@microsoft.com>
|
||||
Date: Thu, 20 Nov 2025 23:43:55 +0900
|
||||
Subject: [PATCH 11/20] sysext: Use correct image name for extension release
|
||||
Subject: [PATCH 10/14] sysext: Use correct image name for extension release
|
||||
checks
|
||||
|
||||
For the extension release check the image name is needed and was derived
|
||||
@ -21,10 +21,10 @@ device but directly the extension name we have at hand.
|
||||
2 files changed, 10 insertions(+)
|
||||
|
||||
diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c
|
||||
index 53ee30c3f8..2801793d6d 100644
|
||||
index 6366316088..34512ea6da 100644
|
||||
--- a/src/shared/discover-image.c
|
||||
+++ b/src/shared/discover-image.c
|
||||
@@ -1844,6 +1844,11 @@ int image_read_metadata(Image *i, const ImagePolicy *image_policy) {
|
||||
@@ -2172,6 +2172,11 @@ int image_read_metadata(Image *i, const ImagePolicy *image_policy, RuntimeScope
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to decrypt image '%s': %m", i->path);
|
||||
|
||||
@ -37,10 +37,10 @@ index 53ee30c3f8..2801793d6d 100644
|
||||
m,
|
||||
/* userns_fd= */ -EBADF,
|
||||
diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c
|
||||
index 5d432b42da..72da02cd89 100644
|
||||
index 2f6bee58c4..cb0a3046e1 100644
|
||||
--- a/src/sysext/sysext.c
|
||||
+++ b/src/sysext/sysext.c
|
||||
@@ -1819,6 +1819,11 @@ static int merge_subprocess(
|
||||
@@ -1885,6 +1885,11 @@ static int merge_subprocess(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -0,0 +1,67 @@
|
||||
From 0d5830bf407c218b898c7fec6153fc6ae4da645f Mon Sep 17 00:00:00 2001
|
||||
From: Kai Lueke <kailuke@microsoft.com>
|
||||
Date: Wed, 3 Dec 2025 00:02:32 +0900
|
||||
Subject: [PATCH 11/14] sysext: Create mutable directory with the right mode
|
||||
|
||||
When the mutable directory didn't exist but gets created with
|
||||
--mutable=yes then it used to get mode 700 and later it got patched by
|
||||
a chmod because it is the top layer and must match the target hierarchy.
|
||||
This meant one could not call the function to resolve the mutable
|
||||
directory twice before the mount because it has a check for a proper
|
||||
mode when the directory exists which is the case for the second call.
|
||||
Also, this resulted in /var/lib/extensions.mutable getting created with
|
||||
mode 700 which is not really required.
|
||||
|
||||
Don't rely on the chmod for the upper dir but directly create the
|
||||
directory with the right mode by first creating all missing directories
|
||||
with 755 as a sane default and then changing the mode as needed for the
|
||||
mutable directory.
|
||||
---
|
||||
src/sysext/sysext.c | 26 ++++++++++++++------------
|
||||
1 file changed, 14 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c
|
||||
index cb0a3046e1..b926ce4c83 100644
|
||||
--- a/src/sysext/sysext.c
|
||||
+++ b/src/sysext/sysext.c
|
||||
@@ -1011,23 +1011,25 @@ static int resolve_mutable_directory(
|
||||
}
|
||||
|
||||
if (IN_SET(arg_mutable, MUTABLE_YES, MUTABLE_EPHEMERAL, MUTABLE_EPHEMERAL_IMPORT)) {
|
||||
- _cleanup_free_ char *path_in_root = NULL;
|
||||
+ _cleanup_close_ int path_fd = -EBADF, chmod_fd = -EBADF;
|
||||
|
||||
- path_in_root = path_join(root, path);
|
||||
- if (!path_in_root)
|
||||
- return log_oom();
|
||||
-
|
||||
- r = mkdir_p(path_in_root, 0700);
|
||||
+ /* This also creates, e.g., /var/lib/extensions.mutable/usr if needed and all parent
|
||||
+ * directories plus it also works when the last part is a symlink to the real /usr but we
|
||||
+ * can't use chase_and_open here because it does not behave the same. */
|
||||
+ r = chase(path, root, CHASE_AT_RESOLVE_IN_ROOT|CHASE_MKDIR_0755|CHASE_MUST_BE_DIRECTORY|CHASE_PREFIX_ROOT, /* ret_path */ NULL, &path_fd);
|
||||
if (r < 0)
|
||||
- return log_error_errno(r, "Failed to create a directory '%s': %m", path_in_root);
|
||||
+ return log_error_errno(r, "Failed to chase/create base directory '%s/%s': %m", strempty(root), skip_leading_slash(path));
|
||||
+
|
||||
+ chmod_fd = fd_reopen(path_fd, O_CLOEXEC|O_DIRECTORY);
|
||||
+ if (chmod_fd < 0)
|
||||
+ return log_error_errno(chmod_fd, "Failed to reopen '%s/%s': %m", strempty(root), skip_leading_slash(path));
|
||||
|
||||
- _cleanup_close_ int atfd = open(path_in_root, O_DIRECTORY|O_CLOEXEC);
|
||||
- if (atfd < 0)
|
||||
- return log_error_errno(errno, "Failed to open directory '%s': %m", path_in_root);
|
||||
+ if (fchmod(chmod_fd, hierarchy_mode) < 0)
|
||||
+ return log_error_errno(errno, "Failed to chmod directory '%s/%s': %m", strempty(root), skip_leading_slash(path));
|
||||
|
||||
- r = mac_selinux_fix_full(atfd, /* inode_path= */ NULL, hierarchy, /* flags= */ 0);
|
||||
+ r = mac_selinux_fix_full(chmod_fd, /* inode_path= */ NULL, hierarchy, /* flags= */ 0);
|
||||
if (r < 0)
|
||||
- return log_error_errno(r, "Failed to fix SELinux label for '%s': %m", path_in_root);
|
||||
+ return log_error_errno(r, "Failed to fix SELinux label for '%s/%s': %m", strempty(root), skip_leading_slash(path));
|
||||
}
|
||||
|
||||
r = chase(path, root, CHASE_PREFIX_ROOT, &resolved_path, NULL);
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
From d8ccdfe333a2eda7770371112cf5dea0ae67598c Mon Sep 17 00:00:00 2001
|
||||
From 404b72c801f4b95e74b655f0771f08f63473d28a Mon Sep 17 00:00:00 2001
|
||||
From: Kai Lueke <kailuke@microsoft.com>
|
||||
Date: Wed, 26 Nov 2025 00:04:43 +0900
|
||||
Subject: [PATCH 14/20] sysext: Skip refresh if no changes are found
|
||||
Subject: [PATCH 12/14] sysext: Skip refresh if no changes are found
|
||||
|
||||
When the extensions for the final system are already set up from the
|
||||
initrd we should avoid disrupting the boot process with the remount
|
||||
@ -34,42 +34,38 @@ Luckily, we can rule out online modification of directories or image
|
||||
files because this is anyway not well supported with overlay mounts, so
|
||||
we don't do a file checksum nor do we recurse into a directory to look
|
||||
for the most recently touched files. But, as said, with the
|
||||
always-refresh flag one can force a reload. Another case that is not
|
||||
supported is changed values of SYSTEMD_SYSEXT_OVERLAYFS_MOUNT_OPTIONS=
|
||||
and these also need an explicit refresh to be applied.
|
||||
always-refresh flag one can force a reload.
|
||||
---
|
||||
man/systemd-sysext.xml | 14 ++
|
||||
man/systemd-sysext.xml | 13 ++
|
||||
shell-completion/bash/systemd-sysext | 1 +
|
||||
src/basic/mountpoint-util.c | 98 ++++++++-
|
||||
src/basic/mountpoint-util.h | 4 +-
|
||||
src/basic/mountpoint-util.c | 110 ++++++++-
|
||||
src/basic/mountpoint-util.h | 5 +-
|
||||
src/include/override/fcntl.h | 5 +
|
||||
src/shared/discover-image.c | 54 +++++
|
||||
src/shared/discover-image.c | 48 ++++
|
||||
src/shared/discover-image.h | 4 +
|
||||
src/shared/varlink-io.systemd.sysext.c | 1 +
|
||||
src/sysext/sysext.c | 271 ++++++++++++++++++++++---
|
||||
test/units/TEST-50-DISSECT.sysext.sh | 41 ++++
|
||||
10 files changed, 458 insertions(+), 35 deletions(-)
|
||||
src/sysext/sysext.c | 301 ++++++++++++++++++++++---
|
||||
9 files changed, 450 insertions(+), 38 deletions(-)
|
||||
|
||||
diff --git a/man/systemd-sysext.xml b/man/systemd-sysext.xml
|
||||
index 07e97071a5..3f60c85dba 100644
|
||||
index b5ab6826a2..d6bbc0141d 100644
|
||||
--- a/man/systemd-sysext.xml
|
||||
+++ b/man/systemd-sysext.xml
|
||||
@@ -366,6 +366,20 @@
|
||||
@@ -371,6 +371,19 @@
|
||||
<xi:include href="version-info.xml" xpointer="v248"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
+ <varlistentry>
|
||||
+ <term><option>--always-refresh=yes|no</option></term>
|
||||
+
|
||||
+ <listitem><para>When refreshing system extensions on <filename>/usr/</filename> and
|
||||
+ <filename>/opt/</filename> for sysext and <filename>/etc/</filename> for confext,
|
||||
+ ignore when the existing merged extensions already match what would be merged.
|
||||
+ By default the refresh is skipped when no changes are found. Note that changes
|
||||
+ done to an extension directory while it's merged are ignored without this flag
|
||||
+ (unless an other extension got changed). Note that changing the contents while
|
||||
+ merged is also undefined behavior in overlayfs.</para>
|
||||
+ <listitem><para>When refreshing system extensions on <filename>/usr/</filename> and <filename>/opt/</filename>
|
||||
+ for sysext and <filename>/etc/</filename> for confext, ignore when the existing merged extensions
|
||||
+ already match what would be merged.
|
||||
+ By default the refresh is skipped when no changes are found. Note that changes done to an extension
|
||||
+ directory while it's merged are ignored without this flag (unless an other extension got changed).
|
||||
+ Note that changing the contents while merged is also undefined behavior in overlayfs.</para>
|
||||
+
|
||||
+ <xi:include href="version-info.xml" xpointer="v250"/></listitem>
|
||||
+ <xi:include href="version-info.xml" xpointer="v260"/></listitem>
|
||||
+ </varlistentry>
|
||||
+
|
||||
<varlistentry>
|
||||
@ -88,10 +84,10 @@ index c605237ed6..69d786c33e 100644
|
||||
--mutable'
|
||||
)
|
||||
diff --git a/src/basic/mountpoint-util.c b/src/basic/mountpoint-util.c
|
||||
index b7c4870931..0d52d4cfea 100644
|
||||
index b7c4870931..aefbed6346 100644
|
||||
--- a/src/basic/mountpoint-util.c
|
||||
+++ b/src/basic/mountpoint-util.c
|
||||
@@ -51,12 +51,16 @@ int name_to_handle_at_loop(
|
||||
@@ -51,12 +51,13 @@ int name_to_handle_at_loop(
|
||||
const char *path,
|
||||
struct file_handle **ret_handle,
|
||||
int *ret_mnt_id,
|
||||
@ -103,13 +99,10 @@ index b7c4870931..0d52d4cfea 100644
|
||||
assert(fd >= 0 || fd == AT_FDCWD);
|
||||
- assert((flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH|AT_HANDLE_FID)) == 0);
|
||||
+ assert((flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH|AT_HANDLE_FID|AT_HANDLE_MNT_ID_UNIQUE)) == 0);
|
||||
+
|
||||
+ if (isempty(path))
|
||||
+ flags |= AT_EMPTY_PATH;
|
||||
|
||||
/* We need to invoke name_to_handle_at() in a loop, given that it might return EOVERFLOW when the specified
|
||||
* buffer is too small. Note that in contrast to what the docs might suggest, MAX_HANDLE_SZ is only good as a
|
||||
@@ -67,7 +71,8 @@ int name_to_handle_at_loop(
|
||||
@@ -67,7 +68,8 @@ int name_to_handle_at_loop(
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ struct file_handle *h = NULL;
|
||||
@ -119,30 +112,27 @@ index b7c4870931..0d52d4cfea 100644
|
||||
|
||||
h = malloc0(offsetof(struct file_handle, f_handle) + n);
|
||||
if (!h)
|
||||
@@ -75,12 +80,20 @@ int name_to_handle_at_loop(
|
||||
@@ -75,11 +77,18 @@ int name_to_handle_at_loop(
|
||||
|
||||
h->handle_bytes = n;
|
||||
|
||||
- if (name_to_handle_at(fd, strempty(path), h, &mnt_id, flags) >= 0) {
|
||||
+ if (flags & AT_HANDLE_MNT_ID_UNIQUE)
|
||||
+ if (FLAGS_SET(flags, AT_HANDLE_MNT_ID_UNIQUE))
|
||||
+ /* The kernel will still use this as uint64_t pointer */
|
||||
+ r = name_to_handle_at(fd, strempty(path), h, (int *) &unique_mnt_id, flags);
|
||||
+ else
|
||||
+ r = name_to_handle_at(fd, strempty(path), h, &mnt_id, flags);
|
||||
+
|
||||
+ if (r >= 0) {
|
||||
|
||||
if (ret_handle)
|
||||
*ret_handle = TAKE_PTR(h);
|
||||
|
||||
- if (ret_mnt_id)
|
||||
+ if (ret_unique_mnt_id && flags & AT_HANDLE_MNT_ID_UNIQUE)
|
||||
+ if (ret_unique_mnt_id)
|
||||
+ *ret_unique_mnt_id = unique_mnt_id;
|
||||
+ if (ret_mnt_id && (flags & AT_HANDLE_MNT_ID_UNIQUE) == 0)
|
||||
if (ret_mnt_id)
|
||||
*ret_mnt_id = mnt_id;
|
||||
|
||||
return 0;
|
||||
@@ -88,13 +101,16 @@ int name_to_handle_at_loop(
|
||||
@@ -88,13 +97,16 @@ int name_to_handle_at_loop(
|
||||
if (errno != EOVERFLOW)
|
||||
return -errno;
|
||||
|
||||
@ -154,24 +144,24 @@ index b7c4870931..0d52d4cfea 100644
|
||||
* be filled in, and the caller was interested in only the mount ID an nothing else. */
|
||||
|
||||
- *ret_mnt_id = mnt_id;
|
||||
+ if (ret_unique_mnt_id && flags & AT_HANDLE_MNT_ID_UNIQUE)
|
||||
+ if (ret_unique_mnt_id)
|
||||
+ *ret_unique_mnt_id = unique_mnt_id;
|
||||
+ else if (ret_mnt_id)
|
||||
+ if (ret_mnt_id)
|
||||
+ *ret_mnt_id = mnt_id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -128,11 +144,52 @@ int name_to_handle_at_try_fid(
|
||||
@@ -128,11 +140,55 @@ int name_to_handle_at_try_fid(
|
||||
* we'll try without the flag, in order to support older kernels that didn't have AT_HANDLE_FID
|
||||
* (i.e. older than Linux 6.5). */
|
||||
|
||||
- r = name_to_handle_at_loop(fd, path, ret_handle, ret_mnt_id, flags | AT_HANDLE_FID);
|
||||
+ r = name_to_handle_at_loop(fd, path, ret_handle, ret_mnt_id, NULL, flags | AT_HANDLE_FID);
|
||||
+ r = name_to_handle_at_loop(fd, path, ret_handle, ret_mnt_id, /* ret_unique_mnt_id= */ NULL, flags | AT_HANDLE_FID);
|
||||
if (r >= 0 || is_name_to_handle_at_fatal_error(r))
|
||||
return r;
|
||||
|
||||
- return name_to_handle_at_loop(fd, path, ret_handle, ret_mnt_id, flags & ~AT_HANDLE_FID);
|
||||
+ return name_to_handle_at_loop(fd, path, ret_handle, ret_mnt_id, NULL, flags & ~AT_HANDLE_FID);
|
||||
+ return name_to_handle_at_loop(fd, path, ret_handle, ret_mnt_id, /* ret_unique_mnt_id= */ NULL, flags & ~AT_HANDLE_FID);
|
||||
+}
|
||||
+
|
||||
+int name_to_handle_at_try_unique_mntid_fid(
|
||||
@ -188,7 +178,7 @@ index b7c4870931..0d52d4cfea 100644
|
||||
+ /* First issues name_to_handle_at() with AT_HANDLE_MNT_ID_UNIQUE and AT_HANDLE_FID.
|
||||
+ * If this fails and this is not a fatal error we'll try without the
|
||||
+ * AT_HANDLE_MNT_ID_UNIQUE flag because it's only available from Linux 6.12 onwards. */
|
||||
+ r = name_to_handle_at_loop(fd, path, ret_handle, NULL, ret_mnt_id, flags | AT_HANDLE_MNT_ID_UNIQUE | AT_HANDLE_FID);
|
||||
+ r = name_to_handle_at_loop(fd, path, ret_handle, /* ret_mnt_id= */ NULL, ret_mnt_id, flags | AT_HANDLE_MNT_ID_UNIQUE | AT_HANDLE_FID);
|
||||
+ if (r >= 0 || is_name_to_handle_at_fatal_error(r))
|
||||
+ return r;
|
||||
+
|
||||
@ -198,25 +188,51 @@ index b7c4870931..0d52d4cfea 100644
|
||||
+ * we'll try without the flag, in order to support older kernels that didn't have AT_HANDLE_FID
|
||||
+ * (i.e. older than Linux 6.5). */
|
||||
+
|
||||
+ r = name_to_handle_at_loop(fd, path, ret_handle, &mnt_id, NULL, flags | AT_HANDLE_FID);
|
||||
+ if (ret_mnt_id && mnt_id >= 0) {
|
||||
+ /* See if we can do better because statx can do unique mount IDs since Linux 6.8
|
||||
+ * and only if this doesn't work we use the non-unique mnt_id as returned.
|
||||
+ * The function only sets mnt_id after checking the error code, so omitted above. */
|
||||
+ if (path_get_unique_mnt_id_at(fd, path, ret_mnt_id) < 0)
|
||||
+ *ret_mnt_id = mnt_id;
|
||||
+ }
|
||||
+ if (r >= 0 || is_name_to_handle_at_fatal_error(r))
|
||||
+ r = name_to_handle_at_loop(fd, path, ret_handle, &mnt_id, /* ret_unique_mnt_id= */ NULL, flags | AT_HANDLE_FID);
|
||||
+ if (r < 0 && is_name_to_handle_at_fatal_error(r))
|
||||
+ return r;
|
||||
+ if (r >= 0) {
|
||||
+ if (ret_mnt_id && mnt_id >= 0) {
|
||||
+ /* See if we can do better because statx can do unique mount IDs since Linux 6.8
|
||||
+ * and only if this doesn't work we use the non-unique mnt_id as returned. */
|
||||
+ if (path_get_unique_mnt_id_at(fd, path, ret_mnt_id) < 0)
|
||||
+ *ret_mnt_id = mnt_id;
|
||||
+ }
|
||||
+
|
||||
+ r = name_to_handle_at_loop(fd, path, ret_handle, &mnt_id, NULL, flags & ~AT_HANDLE_FID);
|
||||
+ return r;
|
||||
+ }
|
||||
+
|
||||
+ r = name_to_handle_at_loop(fd, path, ret_handle, &mnt_id, /* ret_unique_mnt_id= */ NULL, flags & ~AT_HANDLE_FID);
|
||||
+ if (ret_mnt_id && mnt_id >= 0)
|
||||
+ *ret_mnt_id = mnt_id;
|
||||
+ return r;
|
||||
}
|
||||
|
||||
static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *ret_mnt_id) {
|
||||
@@ -373,7 +430,7 @@ int path_get_mnt_id_at_fallback(int dir_fd, const char *path, int *ret) {
|
||||
@@ -197,6 +253,22 @@ bool file_handle_equal(const struct file_handle *a, const struct file_handle *b)
|
||||
return memcmp_nn(a->f_handle, a->handle_bytes, b->f_handle, b->handle_bytes) == 0;
|
||||
}
|
||||
|
||||
+struct file_handle* file_handle_dup(const struct file_handle *fh) {
|
||||
+ _cleanup_free_ struct file_handle *fh_copy = NULL;
|
||||
+
|
||||
+ assert(fh);
|
||||
+
|
||||
+ fh_copy = malloc0(offsetof(struct file_handle, f_handle) + fh->handle_bytes);
|
||||
+ if (!fh_copy)
|
||||
+ return NULL;
|
||||
+
|
||||
+ fh_copy->handle_bytes = fh->handle_bytes;
|
||||
+ fh_copy->handle_type = fh->handle_type;
|
||||
+ memcpy(fh_copy->f_handle, fh->f_handle, fh->handle_bytes);
|
||||
+
|
||||
+ return TAKE_PTR(fh_copy);
|
||||
+}
|
||||
+
|
||||
int is_mount_point_at(int fd, const char *filename, int flags) {
|
||||
bool fd_is_self;
|
||||
int r;
|
||||
@@ -373,7 +445,7 @@ int path_get_mnt_id_at_fallback(int dir_fd, const char *path, int *ret) {
|
||||
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
|
||||
assert(ret);
|
||||
|
||||
@ -225,7 +241,7 @@ index b7c4870931..0d52d4cfea 100644
|
||||
if (r >= 0 || is_name_to_handle_at_fatal_error(r))
|
||||
return r;
|
||||
|
||||
@@ -403,6 +460,29 @@ int path_get_mnt_id_at(int dir_fd, const char *path, int *ret) {
|
||||
@@ -403,6 +475,28 @@ int path_get_mnt_id_at(int dir_fd, const char *path, int *ret) {
|
||||
return path_get_mnt_id_at_fallback(dir_fd, path, ret);
|
||||
}
|
||||
|
||||
@ -244,22 +260,21 @@ index b7c4870931..0d52d4cfea 100644
|
||||
+ &sx) < 0)
|
||||
+ return -errno;
|
||||
+
|
||||
+ if (FLAGS_SET(sx.stx_mask, STATX_MNT_ID_UNIQUE)) {
|
||||
+ *ret = sx.stx_mnt_id;
|
||||
+ return 0;
|
||||
+ }
|
||||
+ if (!FLAGS_SET(sx.stx_mask, STATX_MNT_ID_UNIQUE))
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
+ return -EOPNOTSUPP;
|
||||
+ *ret = sx.stx_mnt_id;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
bool fstype_is_network(const char *fstype) {
|
||||
const char *x;
|
||||
|
||||
diff --git a/src/basic/mountpoint-util.h b/src/basic/mountpoint-util.h
|
||||
index 004d2b2af5..2d3e8390ba 100644
|
||||
index 180d75343c..e70d2c40d0 100644
|
||||
--- a/src/basic/mountpoint-util.h
|
||||
+++ b/src/basic/mountpoint-util.h
|
||||
@@ -34,8 +34,9 @@
|
||||
@@ -34,16 +34,19 @@
|
||||
|
||||
bool is_name_to_handle_at_fatal_error(int err);
|
||||
|
||||
@ -269,8 +284,10 @@ index 004d2b2af5..2d3e8390ba 100644
|
||||
+int name_to_handle_at_try_unique_mntid_fid(int fd, const char *path, struct file_handle **ret_handle, uint64_t *ret_mnt_id, int flags);
|
||||
|
||||
bool file_handle_equal(const struct file_handle *a, const struct file_handle *b);
|
||||
+struct file_handle* file_handle_dup(const struct file_handle *fh);
|
||||
|
||||
@@ -44,6 +45,7 @@ int path_get_mnt_id_at(int dir_fd, const char *path, int *ret);
|
||||
int path_get_mnt_id_at_fallback(int dir_fd, const char *path, int *ret);
|
||||
int path_get_mnt_id_at(int dir_fd, const char *path, int *ret);
|
||||
static inline int path_get_mnt_id(const char *path, int *ret) {
|
||||
return path_get_mnt_id_at(AT_FDCWD, path, ret);
|
||||
}
|
||||
@ -279,7 +296,7 @@ index 004d2b2af5..2d3e8390ba 100644
|
||||
int is_mount_point_at(int fd, const char *filename, int flags);
|
||||
int path_is_mount_point_full(const char *path, const char *root, int flags);
|
||||
diff --git a/src/include/override/fcntl.h b/src/include/override/fcntl.h
|
||||
index 5f1d90ad79..f244ffa9f1 100644
|
||||
index f2b40a6a17..b41f364534 100644
|
||||
--- a/src/include/override/fcntl.h
|
||||
+++ b/src/include/override/fcntl.h
|
||||
@@ -17,3 +17,8 @@
|
||||
@ -289,23 +306,21 @@ index 5f1d90ad79..f244ffa9f1 100644
|
||||
+
|
||||
+/* This is defined since glibc-2.42. */
|
||||
+#ifndef AT_HANDLE_MNT_ID_UNIQUE
|
||||
+#define AT_HANDLE_MNT_ID_UNIQUE 0x001 /* Return the u64 unique mount ID. */
|
||||
+#define AT_HANDLE_MNT_ID_UNIQUE 0x001 /* Return the u64 unique mount ID. */
|
||||
+#endif
|
||||
diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c
|
||||
index 2801793d6d..192ed18687 100644
|
||||
index 34512ea6da..d480848473 100644
|
||||
--- a/src/shared/discover-image.c
|
||||
+++ b/src/shared/discover-image.c
|
||||
@@ -35,6 +35,9 @@
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "lock-util.h"
|
||||
#include "log.h"
|
||||
#include "loop-util.h"
|
||||
#include "mkdir.h"
|
||||
+#include "mountpoint-util.h"
|
||||
+#include "namespace-util.h"
|
||||
+#include "nsresource.h"
|
||||
#include "namespace-util.h"
|
||||
#include "nsresource.h"
|
||||
#include "nulstr-util.h"
|
||||
#include "os-util.h"
|
||||
#include "path-util.h"
|
||||
@@ -125,6 +128,8 @@ static Image* image_free(Image *i) {
|
||||
@@ -139,6 +140,8 @@ static Image* image_free(Image *i) {
|
||||
free(i->name);
|
||||
free(i->path);
|
||||
|
||||
@ -314,7 +329,7 @@ index 2801793d6d..192ed18687 100644
|
||||
free(i->hostname);
|
||||
strv_free(i->machine_info);
|
||||
strv_free(i->os_release);
|
||||
@@ -194,6 +199,9 @@ static int image_new(
|
||||
@@ -241,6 +244,9 @@ static int image_new(
|
||||
bool read_only,
|
||||
usec_t crtime,
|
||||
usec_t mtime,
|
||||
@ -324,7 +339,7 @@ index 2801793d6d..192ed18687 100644
|
||||
Image **ret) {
|
||||
|
||||
_cleanup_(image_unrefp) Image *i = NULL;
|
||||
@@ -215,12 +223,24 @@ static int image_new(
|
||||
@@ -262,12 +268,20 @@ static int image_new(
|
||||
.read_only = read_only,
|
||||
.crtime = crtime,
|
||||
.mtime = mtime,
|
||||
@ -337,37 +352,33 @@ index 2801793d6d..192ed18687 100644
|
||||
};
|
||||
|
||||
+ if (fh) {
|
||||
+ i->fh = malloc0(offsetof(struct file_handle, f_handle) + fh->handle_bytes);
|
||||
+ i->fh = file_handle_dup(fh);
|
||||
+ if (!i->fh)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ i->fh->handle_bytes = fh->handle_bytes;
|
||||
+ i->fh->handle_type = fh->handle_type;
|
||||
+ memcpy(i->fh->f_handle, fh->f_handle, fh->handle_bytes);
|
||||
+ }
|
||||
+
|
||||
i->name = strdup(pretty);
|
||||
if (!i->name)
|
||||
return -ENOMEM;
|
||||
@@ -391,6 +411,28 @@ static int image_make(
|
||||
(dir_path && path_startswith(dir_path, "/usr")) ||
|
||||
@@ -429,6 +443,28 @@ static int image_make(
|
||||
path_startswith(path, "/usr") ||
|
||||
(faccessat(fd, "", W_OK, AT_EACCESS|AT_EMPTY_PATH) < 0 && errno == EROFS);
|
||||
|
||||
+ uint64_t on_mount_id = 0;
|
||||
+ _cleanup_free_ struct file_handle *fh = NULL;
|
||||
+
|
||||
+ r = name_to_handle_at_try_unique_mntid_fid(fd, NULL, &fh, &on_mount_id, 0);
|
||||
+ r = name_to_handle_at_try_unique_mntid_fid(fd, /* path= */ NULL, &fh, &on_mount_id, /* flags= */ 0);
|
||||
+ if (r < 0) {
|
||||
+ if (is_name_to_handle_at_fatal_error(r))
|
||||
+ return r;
|
||||
+
|
||||
+ r = path_get_unique_mnt_id_at(fd, NULL, &on_mount_id);
|
||||
+ r = path_get_unique_mnt_id_at(fd, /* path= */ NULL, &on_mount_id);
|
||||
+ if (r < 0) {
|
||||
+ if (!ERRNO_IS_NEG_NOT_SUPPORTED(r))
|
||||
+ return r;
|
||||
+
|
||||
+ int on_mount_id_fallback = -1;
|
||||
+ r = path_get_mnt_id_at(fd, NULL, &on_mount_id_fallback);
|
||||
+ r = path_get_mnt_id_at(fd, /* path= */ NULL, &on_mount_id_fallback);
|
||||
+ if (r < 0)
|
||||
+ return r;
|
||||
+
|
||||
@ -378,7 +389,7 @@ index 2801793d6d..192ed18687 100644
|
||||
if (S_ISDIR(st->st_mode)) {
|
||||
unsigned file_attr = 0;
|
||||
usec_t crtime = 0;
|
||||
@@ -433,6 +475,9 @@ static int image_make(
|
||||
@@ -470,6 +506,9 @@ static int image_make(
|
||||
info.read_only || read_only,
|
||||
info.otime,
|
||||
info.ctime,
|
||||
@ -388,7 +399,7 @@ index 2801793d6d..192ed18687 100644
|
||||
ret);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -458,6 +503,9 @@ static int image_make(
|
||||
@@ -495,6 +534,9 @@ static int image_make(
|
||||
read_only || (file_attr & FS_IMMUTABLE_FL),
|
||||
crtime,
|
||||
0, /* we don't use mtime of stat() here, since it's not the time of last change of the tree, but only of the top-level dir */
|
||||
@ -398,7 +409,7 @@ index 2801793d6d..192ed18687 100644
|
||||
ret);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -495,6 +543,9 @@ static int image_make(
|
||||
@@ -532,6 +574,9 @@ static int image_make(
|
||||
!(st->st_mode & 0222) || read_only,
|
||||
crtime,
|
||||
timespec_load(&st->st_mtim),
|
||||
@ -408,7 +419,7 @@ index 2801793d6d..192ed18687 100644
|
||||
ret);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -553,6 +604,9 @@ static int image_make(
|
||||
@@ -589,6 +634,9 @@ static int image_make(
|
||||
!(st->st_mode & 0222) || read_only,
|
||||
0,
|
||||
0,
|
||||
@ -419,7 +430,7 @@ index 2801793d6d..192ed18687 100644
|
||||
if (r < 0)
|
||||
return r;
|
||||
diff --git a/src/shared/discover-image.h b/src/shared/discover-image.h
|
||||
index 60f5a4dce1..7b5593f08d 100644
|
||||
index 881a59fdc2..b28d961fb0 100644
|
||||
--- a/src/shared/discover-image.h
|
||||
+++ b/src/shared/discover-image.h
|
||||
@@ -27,6 +27,10 @@ typedef struct Image {
|
||||
@ -446,7 +457,7 @@ index 90eb8177d1..e48804c148 100644
|
||||
|
||||
static SD_VARLINK_DEFINE_METHOD_FULL(
|
||||
diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c
|
||||
index d63cf39fbb..bfe71f2267 100644
|
||||
index b926ce4c83..30e33a01e5 100644
|
||||
--- a/src/sysext/sysext.c
|
||||
+++ b/src/sysext/sysext.c
|
||||
@@ -8,6 +8,7 @@
|
||||
@ -457,15 +468,33 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
#include "sd-varlink.h"
|
||||
|
||||
#include "argv-util.h"
|
||||
@@ -87,6 +88,7 @@ static PagerFlags arg_pager_flags = 0;
|
||||
@@ -84,6 +85,17 @@ static const char* const mutable_mode_table[_MUTABLE_MAX] = {
|
||||
|
||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(mutable_mode, MutableMode, MUTABLE_YES);
|
||||
|
||||
+enum {
|
||||
+ MERGE_NOTHING_FOUND,
|
||||
+ MERGE_MOUNTED,
|
||||
+ MERGE_SKIP_REFRESH,
|
||||
+};
|
||||
+
|
||||
+enum {
|
||||
+ MERGE_EXIT_NOTHING_FOUND = 123,
|
||||
+ MERGE_EXIT_SKIP_REFRESH = 124,
|
||||
+};
|
||||
+
|
||||
static char **arg_hierarchies = NULL; /* "/usr" + "/opt" by default for sysext and /etc by default for confext */
|
||||
static char *arg_root = NULL;
|
||||
static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
|
||||
@@ -91,6 +103,7 @@ static PagerFlags arg_pager_flags = 0;
|
||||
static bool arg_legend = true;
|
||||
static bool arg_force = false;
|
||||
static bool arg_no_reload = false;
|
||||
+static bool arg_always_refresh = false;
|
||||
static int arg_noexec = -1;
|
||||
static ImagePolicy *arg_image_policy = NULL;
|
||||
static bool arg_varlink = false;
|
||||
@@ -1401,6 +1403,28 @@ static int write_extensions_file(ImageClass image_class, char **extensions, cons
|
||||
static bool arg_image_policy_set = false; /* Tracks initialization */
|
||||
@@ -1465,6 +1478,30 @@ static int write_extensions_file(ImageClass image_class, char **extensions, cons
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -484,7 +513,9 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
+ if (!hierarchy_path)
|
||||
+ return log_oom();
|
||||
+
|
||||
+ r = write_string_file_full(AT_FDCWD, f, strempty(origin_content), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755|WRITE_STRING_FILE_LABEL|WRITE_STRING_FILE_AVOID_NEWLINE, /* ts= */ NULL, hierarchy_path);
|
||||
+ r = write_string_file_full(AT_FDCWD, f, strempty(origin_content),
|
||||
+ WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755|WRITE_STRING_FILE_LABEL|WRITE_STRING_FILE_AVOID_NEWLINE,
|
||||
+ /* ts= */ NULL, hierarchy_path);
|
||||
+ if (r < 0)
|
||||
+ return log_error_errno(r, "Failed to write origin meta file '%s': %m", f);
|
||||
+
|
||||
@ -494,7 +525,7 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
static int write_dev_file(ImageClass image_class, const char *meta_path, const char *overlay_path, const char *hierarchy) {
|
||||
_cleanup_free_ char *f = NULL;
|
||||
struct stat st;
|
||||
@@ -1505,6 +1529,7 @@ static int write_work_dir_file(ImageClass image_class, const char *meta_path, co
|
||||
@@ -1569,6 +1606,7 @@ static int write_work_dir_file(ImageClass image_class, const char *meta_path, co
|
||||
static int store_info_in_meta(
|
||||
ImageClass image_class,
|
||||
char **extensions,
|
||||
@ -502,7 +533,7 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
const char *meta_path,
|
||||
const char *overlay_path,
|
||||
const char *work_dir,
|
||||
@@ -1537,6 +1562,10 @@ static int store_info_in_meta(
|
||||
@@ -1601,6 +1639,10 @@ static int store_info_in_meta(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -513,7 +544,7 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
r = write_dev_file(image_class, meta_path, overlay_path, hierarchy);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -1595,6 +1624,7 @@ static int merge_hierarchy(
|
||||
@@ -1659,6 +1701,7 @@ static int merge_hierarchy(
|
||||
int noexec,
|
||||
char **extensions,
|
||||
char **paths,
|
||||
@ -521,7 +552,7 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
const char *meta_path,
|
||||
const char *overlay_path,
|
||||
const char *workspace_path) {
|
||||
@@ -1640,7 +1670,7 @@ static int merge_hierarchy(
|
||||
@@ -1704,7 +1747,7 @@ static int merge_hierarchy(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -530,7 +561,7 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -1692,19 +1722,29 @@ static int merge_subprocess(
|
||||
@@ -1756,19 +1799,29 @@ static int merge_subprocess(
|
||||
ImageClass image_class,
|
||||
char **hierarchies,
|
||||
bool force,
|
||||
@ -543,7 +574,7 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
*host_os_release_version_id = NULL, *host_os_release_api_level = NULL,
|
||||
- *filename = NULL;
|
||||
+ *filename = NULL, *old_origin_content = NULL,
|
||||
+ *extensions_origin_content = NULL, *arg_root_resolved = NULL;
|
||||
+ *extensions_origin_content = NULL, *root_resolved = NULL;
|
||||
_cleanup_strv_free_ char **extensions = NULL, **extensions_v = NULL, **paths = NULL;
|
||||
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *extensions_origin_entries = NULL,
|
||||
+ *extensions_origin_json = NULL, *mutable_dir_entries = NULL;
|
||||
@ -553,7 +584,7 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
int r;
|
||||
|
||||
+ if (!isempty(arg_root)) {
|
||||
+ r = chase(arg_root, NULL, CHASE_MUST_BE_DIRECTORY, &arg_root_resolved, NULL);
|
||||
+ r = chase(arg_root, /* root= */ NULL, CHASE_MUST_BE_DIRECTORY, &root_resolved, /* ret_fd= */ NULL);
|
||||
+ if (r < 0)
|
||||
+ return log_error_errno(r, "Failed to resolve --root='%s': %m", strempty(arg_root));
|
||||
+ }
|
||||
@ -561,7 +592,7 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
assert(path_startswith(workspace, "/run/"));
|
||||
|
||||
/* Mark the whole of /run as MS_SLAVE, so that we can mount stuff below it that doesn't show up on
|
||||
@@ -1742,7 +1782,8 @@ static int merge_subprocess(
|
||||
@@ -1806,7 +1859,8 @@ static int merge_subprocess(
|
||||
|
||||
/* Let's now mount all images */
|
||||
HASHMAP_FOREACH(img, images) {
|
||||
@ -571,20 +602,20 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
|
||||
p = path_join(workspace, image_class_info[image_class].short_identifier_plural, img->name);
|
||||
if (!p)
|
||||
@@ -1841,6 +1882,12 @@ static int merge_subprocess(
|
||||
@@ -1905,6 +1959,12 @@ static int merge_subprocess(
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
+ if (verity_settings.root_hash) {
|
||||
+ r = sd_json_variant_new_hex(&verity_hash, verity_settings.root_hash, verity_settings.root_hash_size);
|
||||
+ if (iovec_is_set(&verity_settings.root_hash)) {
|
||||
+ r = sd_json_variant_new_hex(&verity_hash, verity_settings.root_hash.iov_base, verity_settings.root_hash.iov_len);
|
||||
+ if (r < 0)
|
||||
+ return log_error_errno(r, "Failed to create origin verity entry for '%s': %m", img->name);
|
||||
+ }
|
||||
+
|
||||
r = dissected_image_decrypt(m, /* passphrase= */ NULL, &verity_settings, flags);
|
||||
r = dissected_image_decrypt(m, /* passphrase= */ NULL, &verity_settings, pick_image_policy(img), flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -1910,6 +1957,59 @@ static int merge_subprocess(
|
||||
@@ -1974,6 +2034,60 @@ static int merge_subprocess(
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
@ -593,36 +624,37 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
+
|
||||
+ if (!isempty(arg_root)) {
|
||||
+ const char *without_root = NULL;
|
||||
+ without_root = path_startswith(img->path, arg_root_resolved);
|
||||
+ if (!isempty(without_root))
|
||||
+ without_root = path_startswith(img->path, root_resolved);
|
||||
+ if (!isempty(without_root)) {
|
||||
+ path_without_root = strjoin("/", without_root);
|
||||
+ if (!path_without_root)
|
||||
+ return log_oom();
|
||||
+ }
|
||||
+ }
|
||||
+ if (!path_without_root)
|
||||
+ if (!path_without_root) {
|
||||
+ path_without_root = strdup(img->path);
|
||||
+ if (!path_without_root)
|
||||
+ return log_oom();
|
||||
+ }
|
||||
+
|
||||
+ /* The verity hash is not available for all extension types,
|
||||
+ * thus, but only as fallback, also include data to check for
|
||||
+ * file/directory replacements through a file handle and unique
|
||||
+ * mount ID (or inode and mount ID as fallback).
|
||||
+ * A unique mount ID is best because st_dev gets reused too easily,
|
||||
+ * e.g., by a loop dev mount. For the mount ID to be valid it
|
||||
+ * has to be resolved before we enter the new mount namespace.
|
||||
+ * Thus, here it wouldn't work and so instead it gets provided
|
||||
+ * by the image dissect logic and handed over to this subprocess
|
||||
+ * we are in.
|
||||
+ * Online modification is not well supported with overlay
|
||||
+ * mounts, so we don't do a file checksum nor do we recurse
|
||||
+ * into a directory to look for touched files. If users want
|
||||
+ * modifications to be picked up, they need to set the
|
||||
+ * --always-refresh=yes flag (as will be printed out). */
|
||||
+ /* The verity hash is not available for all extension types, thus, but only as fallback,
|
||||
+ * also include data to check for file/directory replacements through a file handle and
|
||||
+ * unique mount ID (or inode and mount ID as fallback).
|
||||
+ * A unique mount ID is best because st_dev gets reused too easily, e.g., by a loop dev
|
||||
+ * mount. For the mount ID to be valid it has to be resolved before we enter the new mount
|
||||
+ * namespace. Thus, here it wouldn't work and so instead it gets provided by the image
|
||||
+ * dissect logic and handed over to this subprocess we are in.
|
||||
+ * Online modification is not well supported with overlay mounts, so we don't do a file
|
||||
+ * checksum nor do we recurse into a directory to look for touched files. If users want
|
||||
+ * modifications to be picked up, they need to set the --always-refresh=yes flag (as will be
|
||||
+ * printed out). */
|
||||
+
|
||||
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *origin_entry = NULL;
|
||||
+
|
||||
+ /* We suppress inclusion of weak identifiers when a strong one is there
|
||||
+ * so that, e.g., a confext image stored on /usr gets identified only
|
||||
+ * by the verity hash instead of also the mount ID because that changes
|
||||
+ * when a sysext overlay mount appears but since the verity hash is the
|
||||
+ * same for the confext it can actually be reused. */
|
||||
+ /* We suppress inclusion of weak identifiers when a strong one is there so that, e.g.,
|
||||
+ * a confext image stored on /usr gets identified only by the verity hash instead of also
|
||||
+ * the mount ID because that changes when a sysext overlay mount appears but since the
|
||||
+ * verity hash is the same for the confext it can actually be reused. */
|
||||
+ r = sd_json_buildo(&origin_entry,
|
||||
+ SD_JSON_BUILD_PAIR_STRING("path", path_without_root),
|
||||
+ SD_JSON_BUILD_PAIR_CONDITION(!!verity_hash, "verityHash", SD_JSON_BUILD_VARIANT(verity_hash)),
|
||||
@ -644,7 +676,15 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
n_extensions++;
|
||||
}
|
||||
|
||||
@@ -1926,6 +2026,96 @@ static int merge_subprocess(
|
||||
@@ -1983,13 +2097,112 @@ static int merge_subprocess(
|
||||
log_info("No suitable extensions found (%u ignored due to incompatible image(s)).", n_ignored);
|
||||
else
|
||||
log_info("No extensions found.");
|
||||
- return 0;
|
||||
+ return MERGE_NOTHING_FOUND;
|
||||
}
|
||||
|
||||
/* Order by version sort with strverscmp_improved() */
|
||||
typesafe_qsort(extensions, n_extensions, strverscmp_improvedp);
|
||||
typesafe_qsort(extensions_v, n_extensions, strverscmp_improvedp);
|
||||
|
||||
@ -659,12 +699,18 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
+
|
||||
+ if (op->resolved_mutable_directory && !isempty(arg_root)) {
|
||||
+ const char *without_root = NULL;
|
||||
+ without_root = path_startswith(op->resolved_mutable_directory, arg_root_resolved);
|
||||
+ if (!isempty(without_root))
|
||||
+ without_root = path_startswith(op->resolved_mutable_directory, root_resolved);
|
||||
+ if (!isempty(without_root)) {
|
||||
+ mutable_directory_without_root = strjoin("/", without_root);
|
||||
+ if (!mutable_directory_without_root)
|
||||
+ return log_oom();
|
||||
+ }
|
||||
+ }
|
||||
+ if (!mutable_directory_without_root && op->resolved_mutable_directory)
|
||||
+ if (!mutable_directory_without_root && op->resolved_mutable_directory) {
|
||||
+ mutable_directory_without_root = strdup(op->resolved_mutable_directory);
|
||||
+ if (!mutable_directory_without_root)
|
||||
+ return log_oom();
|
||||
+ }
|
||||
+
|
||||
+ if (mutable_directory_without_root) {
|
||||
+ r = sd_json_variant_set_field_string(&mutable_dir_entries, *h, mutable_directory_without_root);
|
||||
@ -673,7 +719,7 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
+ }
|
||||
+
|
||||
+ /* Find existing origin file for comparison. */
|
||||
+ r = chase(*h, arg_root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &resolved, NULL);
|
||||
+ r = chase(*h, arg_root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &resolved, /* ret_fd= */ NULL);
|
||||
+ if (r < 0)
|
||||
+ return log_error_errno(r, "Failed to resolve hierarchy '%s%s': %m", strempty(arg_root), *h);
|
||||
+
|
||||
@ -690,7 +736,7 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
+ if (old_origin_content)
|
||||
+ continue;
|
||||
+
|
||||
+ r = read_full_file(f, &buf, NULL);
|
||||
+ r = read_full_file(f, &buf, /* ret_size */ NULL);
|
||||
+ if (r < 0) {
|
||||
+ log_debug_errno(r, "Failed to open '%s', continuing search: %m", f);
|
||||
+ continue;
|
||||
@ -701,10 +747,13 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
+
|
||||
+ r = sd_json_buildo(&extensions_origin_json,
|
||||
+ SD_JSON_BUILD_PAIR_OBJECT("mutable",
|
||||
+ SD_JSON_BUILD_PAIR_INTEGER("mode", arg_mutable),
|
||||
+ SD_JSON_BUILD_PAIR_STRING("mode", mutable_mode_to_string(arg_mutable)),
|
||||
+ SD_JSON_BUILD_PAIR_CONDITION(!!mutable_dir_entries,
|
||||
+ "mutableDirs",
|
||||
+ SD_JSON_BUILD_VARIANT(mutable_dir_entries))),
|
||||
+ SD_JSON_BUILD_PAIR_CONDITION(!isempty(arg_overlayfs_mount_options),
|
||||
+ "mountOptions",
|
||||
+ SD_JSON_BUILD_STRING(arg_overlayfs_mount_options)),
|
||||
+ SD_JSON_BUILD_PAIR_CONDITION(!!extensions_origin_entries,
|
||||
+ "extensions",
|
||||
+ SD_JSON_BUILD_VARIANT(extensions_origin_entries)));
|
||||
@ -721,7 +770,7 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
+ _cleanup_(sd_json_variant_unrefp) sd_json_variant *old_origin_json = NULL;
|
||||
+
|
||||
+ log_debug("Old extension origin entry (unordered):\n%s\n", old_origin_content);
|
||||
+ r = sd_json_parse(old_origin_content, 0, &old_origin_json, NULL, NULL);
|
||||
+ r = sd_json_parse(old_origin_content, /* flags= */ 0, &old_origin_json, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
|
||||
+ if (r < 0)
|
||||
+ return log_error_errno(r, "Failed to parse existing extension origin content: %m");
|
||||
+
|
||||
@ -730,7 +779,7 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
+ if (!always_refresh) {
|
||||
+ /* This only happens during refresh, not merge, thus talk about refresh here. */
|
||||
+ log_info("Skipping extension refresh because no change was found, use --always-refresh=yes to always do a refresh.");
|
||||
+ return 2;
|
||||
+ return MERGE_SKIP_REFRESH;
|
||||
+ }
|
||||
+
|
||||
+ log_debug("No change found based on origin entry but continuing as requested by --always-refresh=yes.");
|
||||
@ -741,7 +790,7 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
if (n_extensions == 0) {
|
||||
assert(arg_mutable != MUTABLE_NO);
|
||||
log_info("No extensions found, proceeding in mutable mode.");
|
||||
@@ -2008,6 +2198,7 @@ static int merge_subprocess(
|
||||
@@ -2072,6 +2285,7 @@ static int merge_subprocess(
|
||||
noexec,
|
||||
extensions,
|
||||
paths,
|
||||
@ -749,7 +798,15 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
meta_path,
|
||||
overlay_path,
|
||||
merge_hierarchy_workspace);
|
||||
@@ -2059,6 +2250,7 @@ static int merge(ImageClass image_class,
|
||||
@@ -2116,13 +2330,14 @@ static int merge_subprocess(
|
||||
log_info("Merged extensions into '%s'.", resolved);
|
||||
}
|
||||
|
||||
- return 1;
|
||||
+ return MERGE_MOUNTED;
|
||||
}
|
||||
|
||||
static int merge(ImageClass image_class,
|
||||
char **hierarchies,
|
||||
bool force,
|
||||
bool no_reload,
|
||||
@ -757,7 +814,7 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
int noexec,
|
||||
Hashmap *images) {
|
||||
pid_t pid;
|
||||
@@ -2070,14 +2262,20 @@ static int merge(ImageClass image_class,
|
||||
@@ -2138,21 +2353,28 @@ static int merge(ImageClass image_class,
|
||||
if (r == 0) {
|
||||
/* Child with its own mount namespace */
|
||||
|
||||
@ -770,28 +827,29 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
* created below /run. Nice! */
|
||||
|
||||
- _exit(r > 0 ? EXIT_SUCCESS : 123); /* 123 means: didn't find any extensions */
|
||||
+ /* 0/123 means: didn't find any extensions, 2/124 means: skipped refresh */
|
||||
+ if (r < 0)
|
||||
+ _exit(EXIT_FAILURE);
|
||||
+ if (r == 0)
|
||||
+ _exit(123);
|
||||
+ if (r == 2)
|
||||
+ _exit(124);
|
||||
+ if (r == MERGE_NOTHING_FOUND)
|
||||
+ _exit(MERGE_EXIT_NOTHING_FOUND);
|
||||
+ if (r == MERGE_SKIP_REFRESH)
|
||||
+ _exit(MERGE_EXIT_SKIP_REFRESH);
|
||||
+
|
||||
+ _exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
r = wait_for_terminate_and_check("(sd-merge)", pid, WAIT_LOG_ABNORMAL);
|
||||
@@ -2085,6 +2283,8 @@ static int merge(ImageClass image_class,
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 123) /* exit code 123 means: didn't do anything */
|
||||
return 0;
|
||||
+ if (r == 124) /* exit code 124 means: skipped refresh */
|
||||
+ return 1;
|
||||
- if (r == 123) /* exit code 123 means: didn't do anything */
|
||||
- return 0;
|
||||
+ if (r == MERGE_EXIT_NOTHING_FOUND)
|
||||
+ return 0; /* Tell refresh to unmount */
|
||||
+ if (r == MERGE_EXIT_SKIP_REFRESH)
|
||||
+ return 1; /* Same return code as below when we have merged new */
|
||||
if (r > 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EPROTO), "Failed to merge hierarchies");
|
||||
|
||||
@@ -2182,6 +2382,7 @@ static int verb_merge(int argc, char **argv, void *userdata) {
|
||||
@@ -2250,6 +2472,7 @@ static int verb_merge(int argc, char **argv, void *userdata) {
|
||||
arg_hierarchies,
|
||||
arg_force,
|
||||
arg_no_reload,
|
||||
@ -799,7 +857,7 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
arg_noexec,
|
||||
images);
|
||||
}
|
||||
@@ -2190,16 +2391,18 @@ typedef struct MethodMergeParameters {
|
||||
@@ -2258,16 +2481,18 @@ typedef struct MethodMergeParameters {
|
||||
const char *class;
|
||||
int force;
|
||||
int no_reload;
|
||||
@ -822,7 +880,7 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -2215,6 +2418,7 @@ static int vl_method_merge(sd_varlink *link, sd_json_variant *parameters, sd_var
|
||||
@@ -2283,6 +2508,7 @@ static int vl_method_merge(sd_varlink *link, sd_json_variant *parameters, sd_var
|
||||
MethodMergeParameters p = {
|
||||
.force = -1,
|
||||
.no_reload = -1,
|
||||
@ -830,7 +888,7 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
.noexec = -1,
|
||||
};
|
||||
_cleanup_strv_free_ char **hierarchies = NULL;
|
||||
@@ -2249,6 +2453,7 @@ static int vl_method_merge(sd_varlink *link, sd_json_variant *parameters, sd_var
|
||||
@@ -2317,6 +2543,7 @@ static int vl_method_merge(sd_varlink *link, sd_json_variant *parameters, sd_var
|
||||
hierarchies ?: arg_hierarchies,
|
||||
p.force >= 0 ? p.force : arg_force,
|
||||
p.no_reload >= 0 ? p.no_reload : arg_no_reload,
|
||||
@ -838,7 +896,7 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
p.noexec >= 0 ? p.noexec : arg_noexec,
|
||||
images);
|
||||
if (r < 0)
|
||||
@@ -2262,6 +2467,7 @@ static int refresh(
|
||||
@@ -2330,6 +2557,7 @@ static int refresh(
|
||||
char **hierarchies,
|
||||
bool force,
|
||||
bool no_reload,
|
||||
@ -846,7 +904,7 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
int noexec) {
|
||||
|
||||
_cleanup_hashmap_free_ Hashmap *images = NULL;
|
||||
@@ -2272,9 +2478,10 @@ static int refresh(
|
||||
@@ -2340,9 +2568,10 @@ static int refresh(
|
||||
return r;
|
||||
|
||||
/* Returns > 0 if it did something, i.e. a new overlayfs is mounted now. When it does so it
|
||||
@ -859,7 +917,7 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) /* No images found? Then unmerge. The goal of --refresh is after all that after having
|
||||
@@ -2286,7 +2493,8 @@ static int refresh(
|
||||
@@ -2354,7 +2583,8 @@ static int refresh(
|
||||
* 1. If an overlayfs was mounted before and no extensions exist anymore, we'll have unmerged things.
|
||||
*
|
||||
* 2. If an overlayfs was mounted before, and there are still extensions installed' we'll have
|
||||
@ -869,7 +927,7 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
*
|
||||
* 3. If an overlayfs so far wasn't mounted, and there are extensions installed, we'll have it
|
||||
* mounted now.
|
||||
@@ -2310,6 +2518,7 @@ static int verb_refresh(int argc, char **argv, void *userdata) {
|
||||
@@ -2378,6 +2608,7 @@ static int verb_refresh(int argc, char **argv, void *userdata) {
|
||||
arg_hierarchies,
|
||||
arg_force,
|
||||
arg_no_reload,
|
||||
@ -877,7 +935,7 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
arg_noexec);
|
||||
}
|
||||
|
||||
@@ -2318,6 +2527,7 @@ static int vl_method_refresh(sd_varlink *link, sd_json_variant *parameters, sd_v
|
||||
@@ -2386,6 +2617,7 @@ static int vl_method_refresh(sd_varlink *link, sd_json_variant *parameters, sd_v
|
||||
MethodMergeParameters p = {
|
||||
.force = -1,
|
||||
.no_reload = -1,
|
||||
@ -885,7 +943,7 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
.noexec = -1,
|
||||
};
|
||||
_cleanup_strv_free_ char **hierarchies = NULL;
|
||||
@@ -2338,6 +2548,7 @@ static int vl_method_refresh(sd_varlink *link, sd_json_variant *parameters, sd_v
|
||||
@@ -2406,6 +2638,7 @@ static int vl_method_refresh(sd_varlink *link, sd_json_variant *parameters, sd_v
|
||||
hierarchies ?: arg_hierarchies,
|
||||
p.force >= 0 ? p.force : arg_force,
|
||||
p.no_reload >= 0 ? p.no_reload : arg_no_reload,
|
||||
@ -893,7 +951,7 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
p.noexec >= 0 ? p.noexec : arg_noexec);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -2456,6 +2667,8 @@ static int verb_help(int argc, char **argv, void *userdata) {
|
||||
@@ -2524,6 +2757,8 @@ static int verb_help(int argc, char **argv, void *userdata) {
|
||||
" Generate JSON output\n"
|
||||
" --force Ignore version incompatibilities\n"
|
||||
" --no-reload Do not reload the service manager\n"
|
||||
@ -902,7 +960,7 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
" --image-policy=POLICY\n"
|
||||
" Specify disk image dissection policy\n"
|
||||
" --noexec=BOOL Whether to mount extension overlay with noexec\n"
|
||||
@@ -2483,21 +2696,23 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
@@ -2551,21 +2786,23 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_IMAGE_POLICY,
|
||||
ARG_NOEXEC,
|
||||
ARG_NO_RELOAD,
|
||||
@ -937,7 +995,7 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -2561,6 +2776,12 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
@@ -2632,6 +2869,12 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_no_reload = true;
|
||||
break;
|
||||
|
||||
@ -948,60 +1006,8 @@ index d63cf39fbb..bfe71f2267 100644
|
||||
+ break;
|
||||
+
|
||||
case ARG_MUTABLE:
|
||||
r = parse_mutable_mode(optarg);
|
||||
if (r < 0)
|
||||
diff --git a/test/units/TEST-50-DISSECT.sysext.sh b/test/units/TEST-50-DISSECT.sysext.sh
|
||||
index 3eec224eb6..05f691b457 100755
|
||||
--- a/test/units/TEST-50-DISSECT.sysext.sh
|
||||
+++ b/test/units/TEST-50-DISSECT.sysext.sh
|
||||
@@ -1402,6 +1402,47 @@ rm -rf "$fake_root/var/lib/extensions/test-extension.raw.v" "$fake_root/var/othe
|
||||
|
||||
# Done with the above vpick symlink tests for --root= and without
|
||||
|
||||
+( init_trap
|
||||
+: "Check if refresh skips correctly"
|
||||
+fake_root=${roots_dir:+"$roots_dir/refresh-skip"}
|
||||
+hierarchy=/opt
|
||||
+
|
||||
+findmnt --kernel=listmount >/dev/null || {
|
||||
+ echo >&2 "Can't run test on old kernel, skipping test."
|
||||
+ exit 0
|
||||
+}
|
||||
+
|
||||
+prepare_root "$fake_root" "$hierarchy"
|
||||
+prepare_extension_image "$fake_root" "$hierarchy"
|
||||
+prepare_hierarchy "$fake_root" "$hierarchy"
|
||||
+
|
||||
+run_systemd_sysext "$fake_root" merge
|
||||
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
|
||||
+# The mountinfo ID gets reused and is useless here, we require a unique ID from listmount
|
||||
+MOUNTID1=$(findmnt --kernel=listmount -o UNIQ-ID --raw --noheadings --target "$fake_root$hierarchy")
|
||||
+run_systemd_sysext "$fake_root" refresh
|
||||
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
|
||||
+MOUNTID2=$(findmnt --kernel=listmount -o UNIQ-ID --raw --noheadings --target "$fake_root$hierarchy")
|
||||
+if [ "$MOUNTID1" != "$MOUNTID2" ]; then
|
||||
+ echo >&2 "Unexpected remount with 'refresh'"
|
||||
+ exit 1
|
||||
+fi
|
||||
+rm -rf "$fake_root/var/lib/extensions/test-extension2"
|
||||
+cp -ar "$fake_root/var/lib/extensions/test-extension" "$fake_root/var/lib/extensions/test-extension2"
|
||||
+rm -rf "$fake_root/var/lib/extensions/test-extension"
|
||||
+mv "$fake_root/var/lib/extensions/test-extension2" "$fake_root/var/lib/extensions/test-extension"
|
||||
+run_systemd_sysext "$fake_root" refresh
|
||||
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
|
||||
+MOUNTID3=$(findmnt --kernel=listmount -o UNIQ-ID --raw --noheadings --target "$fake_root$hierarchy")
|
||||
+if [ "$MOUNTID2" = "$MOUNTID3" ]; then
|
||||
+ echo >&2 "Unexpected skip with 'refresh'"
|
||||
+ exit 1
|
||||
+fi
|
||||
+
|
||||
+run_systemd_sysext "$fake_root" unmerge
|
||||
+extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
|
||||
+)
|
||||
+
|
||||
} # End of run_sysext_tests
|
||||
|
||||
|
||||
if (streq(optarg, "help")) {
|
||||
if (arg_legend)
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,335 +0,0 @@
|
||||
From 187e60032a26fb58b8944aac5c48a495f9de2644 Mon Sep 17 00:00:00 2001
|
||||
From: Kai Lueke <kailuke@microsoft.com>
|
||||
Date: Thu, 20 Nov 2025 23:43:55 +0900
|
||||
Subject: [PATCH 12/20] 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.
|
||||
---
|
||||
test/units/TEST-50-DISSECT.sysext.sh | 298 +++++++++++++++++++++++++++
|
||||
1 file changed, 298 insertions(+)
|
||||
|
||||
diff --git a/test/units/TEST-50-DISSECT.sysext.sh b/test/units/TEST-50-DISSECT.sysext.sh
|
||||
index ecf0b83b1d..3eec224eb6 100755
|
||||
--- a/test/units/TEST-50-DISSECT.sysext.sh
|
||||
+++ b/test/units/TEST-50-DISSECT.sysext.sh
|
||||
@@ -163,6 +163,24 @@ prepare_extension_image_with_matching_id_like() {
|
||||
prepend_trap "rm -rf ${ext_dir@Q}"
|
||||
}
|
||||
|
||||
+prepare_extension_image_raw() {
|
||||
+ local root=${1:-}
|
||||
+ local hierarchy=${2:?}
|
||||
+ local ext_dir ext_release name
|
||||
+
|
||||
+ name="test-extension"
|
||||
+ ext_dir="$root/var/lib/extensions/$name"
|
||||
+ ext_release="$ext_dir/usr/lib/extension-release.d/extension-release.$name"
|
||||
+ mkdir -p "${ext_release%/*}"
|
||||
+ echo "ID=_any" >"$ext_release"
|
||||
+ mkdir -p "$ext_dir/$hierarchy"
|
||||
+ touch "$ext_dir$hierarchy/preexisting-file-in-extension-image"
|
||||
+ mksquashfs "$ext_dir" "$ext_dir.raw" -all-root -noappend -quiet
|
||||
+ rm -rf "$ext_dir"
|
||||
+
|
||||
+ prepend_trap "rm -rf ${ext_dir@Q}.raw"
|
||||
+}
|
||||
+
|
||||
prepare_extension_mutable_dir() {
|
||||
local dir=${1:?}
|
||||
|
||||
@@ -1104,6 +1122,286 @@ fi
|
||||
rm "${fake_root}/etc/initrd-release"
|
||||
)
|
||||
|
||||
+# 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
|
||||
|
||||
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,45 +0,0 @@
|
||||
From 773073faa6582a0bbb6f3c4d3b35a1a81fbffd81 Mon Sep 17 00:00:00 2001
|
||||
From: Kai Lueke <kailuke@microsoft.com>
|
||||
Date: Wed, 3 Dec 2025 00:02:32 +0900
|
||||
Subject: [PATCH 13/20] sysext: Create mutable directory with the right mode
|
||||
|
||||
When the mutable directory didn't exist but gets created with
|
||||
--mutable=yes then it used to get mode 700 and later it got patched by
|
||||
a chmod because it is the top layer and must match the target hierarchy.
|
||||
This meant one could not call the function to resolve the mutable
|
||||
directory twice before the mount because it has a check for a proper
|
||||
mode when the directory exists which is the case for the second call.
|
||||
Also, this resulted in /var/lib/extensions.mutable getting created with
|
||||
mode 700 which is not really required.
|
||||
|
||||
Don't rely on the chmod for the upper dir but directly create the
|
||||
directory with the right mode by first creating all missing directories
|
||||
with 755 as a sane default and then changing the mode as needed for the
|
||||
mutable directory.
|
||||
---
|
||||
src/sysext/sysext.c | 6 +++++-
|
||||
1 file changed, 5 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c
|
||||
index 72da02cd89..d63cf39fbb 100644
|
||||
--- a/src/sysext/sysext.c
|
||||
+++ b/src/sysext/sysext.c
|
||||
@@ -952,10 +952,14 @@ static int resolve_mutable_directory(
|
||||
if (!path_in_root)
|
||||
return log_oom();
|
||||
|
||||
- r = mkdir_p(path_in_root, 0700);
|
||||
+ /* This also creates /var/lib/extensions.mutable if needed */
|
||||
+ r = mkdir_p(path_in_root, 0755);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create a directory '%s': %m", path_in_root);
|
||||
|
||||
+ if (chmod(path_in_root, hierarchy_mode) < 0)
|
||||
+ return log_error_errno(errno, "Failed to chmod directory '%s': %m", path_in_root);
|
||||
+
|
||||
_cleanup_close_ int atfd = open(path_in_root, O_DIRECTORY|O_CLOEXEC);
|
||||
if (atfd < 0)
|
||||
return log_error_errno(errno, "Failed to open directory '%s': %m", path_in_root);
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
From a228e6433b6febd4d252a3cb71bb0c2e63156b93 Mon Sep 17 00:00:00 2001
|
||||
From c910dda50a611fbada3f1166b9c1cf3b91091cc6 Mon Sep 17 00:00:00 2001
|
||||
From: Kai Lueke <kailuke@microsoft.com>
|
||||
Date: Thu, 27 Nov 2025 17:49:15 +0900
|
||||
Subject: [PATCH 15/20] sysext: Get verity user certs from given --root=
|
||||
Subject: [PATCH 13/14] sysext: Get verity user certs from given --root=
|
||||
|
||||
The verity user certs weren't looked up in the given --root= for
|
||||
systemd-sysext which made it fail to set up extensions with a strict
|
||||
@ -10,151 +10,150 @@ Look up verity user certs from inside the --root= when we operate on
|
||||
images in it. The main use case where this matters is when the initrd
|
||||
sets up the extensions for the final system and thus systemd-sysext
|
||||
should do the same thing as it would do in the final system.
|
||||
|
||||
Signed-off-by: Kai Lueke <kailuke@microsoft.com>
|
||||
---
|
||||
src/core/namespace.c | 1 +
|
||||
src/machine/image-dbus.c | 8 ++--
|
||||
src/machine/machined-varlink.c | 2 +-
|
||||
src/mountfsd/mountwork.c | 1 +
|
||||
src/portable/portabled-image-bus.c | 2 +-
|
||||
src/shared/discover-image.c | 3 +-
|
||||
src/shared/discover-image.h | 2 +-
|
||||
src/shared/dissect-image.c | 22 ++++++-----
|
||||
src/shared/dissect-image.h | 2 +-
|
||||
src/sysext/sysext.c | 4 +-
|
||||
test/units/TEST-50-DISSECT.sysext.sh | 58 ++++++++++++++++++++++++++++
|
||||
11 files changed, 85 insertions(+), 20 deletions(-)
|
||||
src/core/namespace.c | 3 ++-
|
||||
src/machine/image-dbus.c | 8 ++++----
|
||||
src/machine/machined-varlink.c | 2 +-
|
||||
src/mountfsd/mountwork.c | 1 +
|
||||
src/portable/portabled-image-bus.c | 2 +-
|
||||
src/shared/discover-image.c | 3 ++-
|
||||
src/shared/discover-image.h | 2 +-
|
||||
src/shared/dissect-image.c | 20 ++++++++++++--------
|
||||
src/shared/dissect-image.h | 2 +-
|
||||
src/sysext/sysext.c | 4 ++--
|
||||
10 files changed, 27 insertions(+), 20 deletions(-)
|
||||
|
||||
diff --git a/src/core/namespace.c b/src/core/namespace.c
|
||||
index 2e3b2a4177..95f8714ea6 100644
|
||||
index 0a78c4922a..ead9caafe6 100644
|
||||
--- a/src/core/namespace.c
|
||||
+++ b/src/core/namespace.c
|
||||
@@ -2593,6 +2593,7 @@ int setup_namespace(const NamespaceParameters *p, char **reterr_path) {
|
||||
@@ -2596,7 +2596,8 @@ int setup_namespace(const NamespaceParameters *p, char **reterr_path) {
|
||||
|
||||
r = dissected_image_decrypt(
|
||||
dissected_image,
|
||||
NULL,
|
||||
+ NULL,
|
||||
- NULL,
|
||||
+ /* root= */ NULL,
|
||||
+ /* passphrase= */ NULL,
|
||||
p->verity,
|
||||
p->root_image_policy,
|
||||
dissect_image_flags);
|
||||
if (r < 0)
|
||||
diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c
|
||||
index 8bc6565079..2857cd18be 100644
|
||||
index f7e3f7f93c..966948be44 100644
|
||||
--- a/src/machine/image-dbus.c
|
||||
+++ b/src/machine/image-dbus.c
|
||||
@@ -284,7 +284,7 @@ int bus_image_method_get_hostname(
|
||||
@@ -295,7 +295,7 @@ int bus_image_method_get_hostname(
|
||||
int r;
|
||||
|
||||
if (!image->metadata_valid) {
|
||||
- r = image_read_metadata(image, &image_policy_container);
|
||||
+ r = image_read_metadata(image, NULL, &image_policy_container);
|
||||
- r = image_read_metadata(image, &image_policy_container, m->runtime_scope);
|
||||
+ r = image_read_metadata(image, /* root= */ NULL, &image_policy_container, m->runtime_scope);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
|
||||
}
|
||||
@@ -302,7 +302,7 @@ int bus_image_method_get_machine_id(
|
||||
@@ -314,7 +314,7 @@ int bus_image_method_get_machine_id(
|
||||
int r;
|
||||
|
||||
if (!image->metadata_valid) {
|
||||
- r = image_read_metadata(image, &image_policy_container);
|
||||
+ r = image_read_metadata(image, NULL, &image_policy_container);
|
||||
- r = image_read_metadata(image, &image_policy_container, m->runtime_scope);
|
||||
+ r = image_read_metadata(image, /* root= */ NULL, &image_policy_container, m->runtime_scope);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
|
||||
}
|
||||
@@ -330,7 +330,7 @@ int bus_image_method_get_machine_info(
|
||||
@@ -343,7 +343,7 @@ int bus_image_method_get_machine_info(
|
||||
int r;
|
||||
|
||||
if (!image->metadata_valid) {
|
||||
- r = image_read_metadata(image, &image_policy_container);
|
||||
+ r = image_read_metadata(image, NULL, &image_policy_container);
|
||||
- r = image_read_metadata(image, &image_policy_container, m->runtime_scope);
|
||||
+ r = image_read_metadata(image, /* root= */ NULL, &image_policy_container, m->runtime_scope);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
|
||||
}
|
||||
@@ -347,7 +347,7 @@ int bus_image_method_get_os_release(
|
||||
@@ -361,7 +361,7 @@ int bus_image_method_get_os_release(
|
||||
int r;
|
||||
|
||||
if (!image->metadata_valid) {
|
||||
- r = image_read_metadata(image, &image_policy_container);
|
||||
+ r = image_read_metadata(image, NULL, &image_policy_container);
|
||||
- r = image_read_metadata(image, &image_policy_container, m->runtime_scope);
|
||||
+ r = image_read_metadata(image, /* root= */ NULL, &image_policy_container, m->runtime_scope);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
|
||||
}
|
||||
diff --git a/src/machine/machined-varlink.c b/src/machine/machined-varlink.c
|
||||
index 064ffab137..f3676e625c 100644
|
||||
index 56abcb9438..2697a87b6d 100644
|
||||
--- a/src/machine/machined-varlink.c
|
||||
+++ b/src/machine/machined-varlink.c
|
||||
@@ -621,7 +621,7 @@ static int list_image_one_and_maybe_read_metadata(sd_varlink *link, Image *image
|
||||
@@ -627,7 +627,7 @@ static int list_image_one_and_maybe_read_metadata(Manager *m, sd_varlink *link,
|
||||
assert(image);
|
||||
|
||||
if (should_acquire_metadata(am) && !image->metadata_valid) {
|
||||
- r = image_read_metadata(image, &image_policy_container);
|
||||
+ r = image_read_metadata(image, NULL, &image_policy_container);
|
||||
- r = image_read_metadata(image, &image_policy_container, m->runtime_scope);
|
||||
+ r = image_read_metadata(image, /* root= */ NULL, &image_policy_container, m->runtime_scope);
|
||||
if (r < 0 && am != ACQUIRE_METADATA_GRACEFUL)
|
||||
return log_debug_errno(r, "Failed to read image metadata: %m");
|
||||
if (r < 0)
|
||||
diff --git a/src/mountfsd/mountwork.c b/src/mountfsd/mountwork.c
|
||||
index bfb8c05c22..260a668525 100644
|
||||
index b078dc17a3..5ed4552d52 100644
|
||||
--- a/src/mountfsd/mountwork.c
|
||||
+++ b/src/mountfsd/mountwork.c
|
||||
@@ -495,6 +495,7 @@ static int vl_method_mount_image(
|
||||
@@ -538,6 +538,7 @@ static int vl_method_mount_image(
|
||||
|
||||
r = dissected_image_decrypt(
|
||||
di,
|
||||
+ NULL,
|
||||
+ /* root= */ NULL,
|
||||
p.password,
|
||||
&verity,
|
||||
dissect_flags);
|
||||
use_policy,
|
||||
diff --git a/src/portable/portabled-image-bus.c b/src/portable/portabled-image-bus.c
|
||||
index e8bcb900ef..380a6d5d45 100644
|
||||
index 21d7bab262..b5dc0f5955 100644
|
||||
--- a/src/portable/portabled-image-bus.c
|
||||
+++ b/src/portable/portabled-image-bus.c
|
||||
@@ -61,7 +61,7 @@ int bus_image_common_get_os_release(
|
||||
return 1;
|
||||
|
||||
if (!image->metadata_valid) {
|
||||
- r = image_read_metadata(image, &image_policy_service);
|
||||
+ r = image_read_metadata(image, NULL, &image_policy_service);
|
||||
- r = image_read_metadata(image, &image_policy_service, m->runtime_scope);
|
||||
+ r = image_read_metadata(image, /* root= */ NULL, &image_policy_service, m->runtime_scope);
|
||||
if (r < 0)
|
||||
return sd_bus_error_set_errnof(error, r, "Failed to read image metadata: %m");
|
||||
}
|
||||
diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c
|
||||
index 192ed18687..925bc6010b 100644
|
||||
index d480848473..03fffad37e 100644
|
||||
--- a/src/shared/discover-image.c
|
||||
+++ b/src/shared/discover-image.c
|
||||
@@ -1766,7 +1766,7 @@ int image_set_pool_limit(ImageClass class, uint64_t referenced_max) {
|
||||
@@ -2087,7 +2087,7 @@ int image_setup_pool(RuntimeScope scope, ImageClass class, bool use_btrfs_subvol
|
||||
return 0;
|
||||
}
|
||||
|
||||
-int image_read_metadata(Image *i, const ImagePolicy *image_policy) {
|
||||
+int image_read_metadata(Image *i, const char *root, const ImagePolicy *image_policy) {
|
||||
-int image_read_metadata(Image *i, const ImagePolicy *image_policy, RuntimeScope scope) {
|
||||
+int image_read_metadata(Image *i, const char *root, const ImagePolicy *image_policy, RuntimeScope scope) {
|
||||
_cleanup_(release_lock_file) LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT;
|
||||
int r;
|
||||
|
||||
@@ -1892,6 +1892,7 @@ int image_read_metadata(Image *i, const ImagePolicy *image_policy) {
|
||||
@@ -2213,6 +2213,7 @@ int image_read_metadata(Image *i, const ImagePolicy *image_policy, RuntimeScope
|
||||
|
||||
r = dissected_image_decrypt(
|
||||
m,
|
||||
+ root,
|
||||
/* passphrase= */ NULL,
|
||||
&verity,
|
||||
flags);
|
||||
image_policy,
|
||||
diff --git a/src/shared/discover-image.h b/src/shared/discover-image.h
|
||||
index 7b5593f08d..4d64a306c8 100644
|
||||
index b28d961fb0..c1838f9959 100644
|
||||
--- a/src/shared/discover-image.h
|
||||
+++ b/src/shared/discover-image.h
|
||||
@@ -73,7 +73,7 @@ int image_name_lock(const char *name, int operation, LockFile *ret);
|
||||
int image_set_limit(Image *i, uint64_t referenced_max);
|
||||
int image_set_pool_limit(ImageClass class, uint64_t referenced_max);
|
||||
@@ -78,7 +78,7 @@ int image_get_pool_usage(RuntimeScope scope, ImageClass class, uint64_t *ret);
|
||||
int image_get_pool_limit(RuntimeScope scope, ImageClass class, uint64_t *ret);
|
||||
int image_setup_pool(RuntimeScope scope, ImageClass class, bool use_btrfs_subvol, bool use_btrfs_quota);
|
||||
|
||||
-int image_read_metadata(Image *i, const ImagePolicy *image_policy);
|
||||
+int image_read_metadata(Image *i, const char *root, const ImagePolicy *image_policy);
|
||||
-int image_read_metadata(Image *i, const ImagePolicy *image_policy, RuntimeScope scope);
|
||||
+int image_read_metadata(Image *i, const char *root, const ImagePolicy *image_policy, RuntimeScope scope);
|
||||
|
||||
bool image_in_search_path(RuntimeScope scope, ImageClass class, const char *root, const char *image);
|
||||
|
||||
diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c
|
||||
index 64639000b1..cec4225e92 100644
|
||||
index 51725e6cfb..b2be12e842 100644
|
||||
--- a/src/shared/dissect-image.c
|
||||
+++ b/src/shared/dissect-image.c
|
||||
@@ -2740,7 +2740,7 @@ static char* dm_deferred_remove_clean(char *name) {
|
||||
@@ -2810,7 +2810,7 @@ static char* dm_deferred_remove_clean(char *name) {
|
||||
}
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(char *, dm_deferred_remove_clean);
|
||||
|
||||
@ -162,8 +161,8 @@ index 64639000b1..cec4225e92 100644
|
||||
+static int validate_signature_userspace(const VeritySettings *verity, const char *root, DissectImageFlags flags) {
|
||||
int r;
|
||||
|
||||
if (!FLAGS_SET(flags, DISSECT_IMAGE_ALLOW_USERSPACE_VERITY)) {
|
||||
@@ -2785,7 +2785,7 @@ static int validate_signature_userspace(const VeritySettings *verity, DissectIma
|
||||
/* Returns > 0 if signature checks out, == 0 if not, < 0 on unexpected errors */
|
||||
@@ -2855,7 +2855,7 @@ static int validate_signature_userspace(const VeritySettings *verity, DissectIma
|
||||
/* Because installing a signature certificate into the kernel chain is so messy, let's optionally do
|
||||
* userspace validation. */
|
||||
|
||||
@ -172,198 +171,120 @@ index 64639000b1..cec4225e92 100644
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to enumerate certificates: %m");
|
||||
if (strv_isempty(certs)) {
|
||||
@@ -2847,6 +2847,7 @@ static int validate_signature_userspace(const VeritySettings *verity, DissectIma
|
||||
@@ -2917,6 +2917,7 @@ static int validate_signature_userspace(const VeritySettings *verity, DissectIma
|
||||
|
||||
static int do_crypt_activate_verity(
|
||||
struct crypt_device *cd,
|
||||
+ const char *root,
|
||||
const char *name,
|
||||
const VeritySettings *verity,
|
||||
DissectImageFlags flags) {
|
||||
@@ -2894,7 +2895,7 @@ static int do_crypt_activate_verity(
|
||||
DissectImageFlags flags,
|
||||
@@ -2966,7 +2967,7 @@ static int do_crypt_activate_verity(
|
||||
|
||||
/* Preferably propagate the original kernel error, so that the fallback logic can work,
|
||||
* as the device-mapper is finicky around concurrent activations of the same volume */
|
||||
- k = validate_signature_userspace(verity, flags);
|
||||
+ k = validate_signature_userspace(verity, root, flags);
|
||||
if (k < 0)
|
||||
return r < 0 ? r : k;
|
||||
if (k == 0)
|
||||
@@ -2934,8 +2935,9 @@ static usec_t verity_timeout(void) {
|
||||
|
||||
static int verity_partition(
|
||||
return k;
|
||||
if (k == 0) {
|
||||
@@ -3026,6 +3027,7 @@ static int verity_partition(
|
||||
PartitionDesignator designator,
|
||||
- DissectedPartition *m,
|
||||
- DissectedPartition *v,
|
||||
+ DissectedPartition *m, /* data partition */
|
||||
+ DissectedPartition *v, /* verity partition */
|
||||
DissectedPartition *m, /* data partition */
|
||||
DissectedPartition *v, /* verity partition */
|
||||
+ const char *root, /* The root to get user verity certs from (for a sysext) */
|
||||
const VeritySettings *verity,
|
||||
DissectImageFlags flags,
|
||||
DecryptedImage *d) {
|
||||
@@ -3015,7 +3017,7 @@ static int verity_partition(
|
||||
PartitionPolicyFlags policy_flags,
|
||||
@@ -3111,7 +3113,7 @@ static int verity_partition(
|
||||
goto check; /* The device already exists. Let's check it. */
|
||||
|
||||
/* The symlink to the device node does not exist yet. Assume not activated, and let's activate it. */
|
||||
- r = do_crypt_activate_verity(cd, name, verity, flags);
|
||||
+ r = do_crypt_activate_verity(cd, root, name, verity, flags);
|
||||
- r = do_crypt_activate_verity(cd, name, verity, flags, policy_flags);
|
||||
+ r = do_crypt_activate_verity(cd, root, name, verity, flags, policy_flags);
|
||||
if (r >= 0)
|
||||
goto try_open; /* The device is activated. Let's open it. */
|
||||
/* libdevmapper can return EINVAL when the device is already in the activation stage.
|
||||
@@ -3109,7 +3111,7 @@ static int verity_partition(
|
||||
@@ -3205,7 +3207,7 @@ static int verity_partition(
|
||||
*/
|
||||
sym_crypt_free(cd);
|
||||
cd = NULL;
|
||||
- return verity_partition(designator, m, v, verity, flags & ~DISSECT_IMAGE_VERITY_SHARE, d);
|
||||
+ return verity_partition(designator, m, v, root, verity, flags & ~DISSECT_IMAGE_VERITY_SHARE, d);
|
||||
- return verity_partition(designator, m, v, verity, flags & ~DISSECT_IMAGE_VERITY_SHARE, policy_flags, d);
|
||||
+ return verity_partition(designator, m, v, root, verity, flags & ~DISSECT_IMAGE_VERITY_SHARE, policy_flags, d);
|
||||
}
|
||||
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EBUSY), "All attempts to activate verity device %s failed.", name);
|
||||
@@ -3129,6 +3131,7 @@ success:
|
||||
@@ -3225,6 +3227,7 @@ success:
|
||||
|
||||
int dissected_image_decrypt(
|
||||
DissectedImage *m,
|
||||
+ const char *root, /* The root to get user verity certs from (for a sysext) */
|
||||
const char *passphrase,
|
||||
const VeritySettings *verity,
|
||||
DissectImageFlags flags) {
|
||||
@@ -3176,7 +3179,7 @@ int dissected_image_decrypt(
|
||||
if (k >= 0) {
|
||||
flags |= getenv_bool("SYSTEMD_VERITY_SHARING") != 0 ? DISSECT_IMAGE_VERITY_SHARE : 0;
|
||||
const ImagePolicy *policy,
|
||||
@@ -3281,7 +3284,7 @@ int dissected_image_decrypt(
|
||||
|
||||
- r = verity_partition(i, p, m->partitions + k, verity, flags, d);
|
||||
+ r = verity_partition(i, p, m->partitions + k, root, verity, flags, d);
|
||||
k = partition_verity_hash_of(i);
|
||||
if (k >= 0) {
|
||||
- r = verity_partition(i, p, m->partitions + k, verity, flags, fl, d);
|
||||
+ r = verity_partition(i, p, m->partitions + k, root, verity, flags, fl, d);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@@ -3209,7 +3212,7 @@ int dissected_image_decrypt_interactively(
|
||||
@@ -3314,7 +3317,7 @@ int dissected_image_decrypt_interactively(
|
||||
n--;
|
||||
|
||||
for (;;) {
|
||||
- r = dissected_image_decrypt(m, passphrase, verity, flags);
|
||||
+ r = dissected_image_decrypt(m, NULL, passphrase, verity, flags);
|
||||
- r = dissected_image_decrypt(m, passphrase, verity, image_policy, flags);
|
||||
+ r = dissected_image_decrypt(m, /* root= */ NULL, passphrase, verity, image_policy, flags);
|
||||
if (r >= 0)
|
||||
return r;
|
||||
if (r == -EKEYREJECTED)
|
||||
@@ -4455,6 +4458,7 @@ int verity_dissect_and_mount(
|
||||
r = dissected_image_decrypt(
|
||||
dissected_image,
|
||||
NULL,
|
||||
+ NULL,
|
||||
verity,
|
||||
dissect_image_flags);
|
||||
if (r < 0)
|
||||
@@ -4564,7 +4567,8 @@ int verity_dissect_and_mount(
|
||||
|
||||
r = dissected_image_decrypt(
|
||||
dissected_image,
|
||||
- NULL,
|
||||
+ /* root= */ NULL,
|
||||
+ /* passphrase= */ NULL,
|
||||
verity,
|
||||
image_policy,
|
||||
dissect_image_flags);
|
||||
diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h
|
||||
index 97431bca67..004dc46dc3 100644
|
||||
index 951ae66055..674a87f684 100644
|
||||
--- a/src/shared/dissect-image.h
|
||||
+++ b/src/shared/dissect-image.h
|
||||
@@ -171,7 +171,7 @@ void dissected_image_close(DissectedImage *m);
|
||||
@@ -170,7 +170,7 @@ void dissected_image_close(DissectedImage *m);
|
||||
DissectedImage* dissected_image_unref(DissectedImage *m);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref);
|
||||
|
||||
-int dissected_image_decrypt(DissectedImage *m, const char *passphrase, const VeritySettings *verity, DissectImageFlags flags);
|
||||
+int dissected_image_decrypt(DissectedImage *m, const char *root, const char *passphrase, const VeritySettings *verity, DissectImageFlags flags);
|
||||
int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphrase, const VeritySettings *verity, DissectImageFlags flags);
|
||||
-int dissected_image_decrypt(DissectedImage *m, const char *passphrase, const VeritySettings *verity, const ImagePolicy *image_policy, DissectImageFlags flags);
|
||||
+int dissected_image_decrypt(DissectedImage *m, const char *root, const char *passphrase, const VeritySettings *verity, const ImagePolicy *image_policy, DissectImageFlags flags);
|
||||
int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphrase, const VeritySettings *verity, const ImagePolicy *image_policy, DissectImageFlags flags);
|
||||
int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift, uid_t uid_range, int userns_fd, DissectImageFlags flags);
|
||||
int dissected_image_mount_and_warn(DissectedImage *m, const char *where, uid_t uid_shift, uid_t uid_range, int userns_fd, DissectImageFlags flags);
|
||||
diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c
|
||||
index bfe71f2267..20acc60724 100644
|
||||
index 30e33a01e5..76d84f108a 100644
|
||||
--- a/src/sysext/sysext.c
|
||||
+++ b/src/sysext/sysext.c
|
||||
@@ -1888,7 +1888,7 @@ static int merge_subprocess(
|
||||
@@ -1965,7 +1965,7 @@ static int merge_subprocess(
|
||||
return log_error_errno(r, "Failed to create origin verity entry for '%s': %m", img->name);
|
||||
}
|
||||
|
||||
- r = dissected_image_decrypt(m, /* passphrase= */ NULL, &verity_settings, flags);
|
||||
+ r = dissected_image_decrypt(m, arg_root, /* passphrase= */ NULL, &verity_settings, flags);
|
||||
- r = dissected_image_decrypt(m, /* passphrase= */ NULL, &verity_settings, pick_image_policy(img), flags);
|
||||
+ r = dissected_image_decrypt(m, arg_root, /* passphrase= */ NULL, &verity_settings, pick_image_policy(img), flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -2312,7 +2312,7 @@ static int image_discover_and_read_metadata(ImageClass image_class, Hashmap **re
|
||||
@@ -2402,7 +2402,7 @@ static int image_discover_and_read_metadata(ImageClass image_class, Hashmap **re
|
||||
return log_error_errno(r, "Failed to discover images: %m");
|
||||
|
||||
HASHMAP_FOREACH(img, images) {
|
||||
- r = image_read_metadata(img, image_class_info[image_class].default_image_policy);
|
||||
+ r = image_read_metadata(img, arg_root, image_class_info[image_class].default_image_policy);
|
||||
- r = image_read_metadata(img, image_class_info[image_class].default_image_policy, RUNTIME_SCOPE_SYSTEM);
|
||||
+ r = image_read_metadata(img, arg_root, image_class_info[image_class].default_image_policy, RUNTIME_SCOPE_SYSTEM);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read metadata for image %s: %m", img->name);
|
||||
}
|
||||
diff --git a/test/units/TEST-50-DISSECT.sysext.sh b/test/units/TEST-50-DISSECT.sysext.sh
|
||||
index 05f691b457..6e64eea492 100755
|
||||
--- a/test/units/TEST-50-DISSECT.sysext.sh
|
||||
+++ b/test/units/TEST-50-DISSECT.sysext.sh
|
||||
@@ -181,6 +181,52 @@ prepare_extension_image_raw() {
|
||||
prepend_trap "rm -rf ${ext_dir@Q}.raw"
|
||||
}
|
||||
|
||||
+prepare_extension_image_raw_verity() {
|
||||
+ local root=${1:-}
|
||||
+ local hierarchy=${2:?}
|
||||
+ local ext_dir ext_release name tmpcrt
|
||||
+
|
||||
+ name="test-extension"
|
||||
+ ext_dir="$root/var/lib/extensions/$name"
|
||||
+ ext_release="$ext_dir/usr/lib/extension-release.d/extension-release.$name"
|
||||
+ tmpcrt=$(mktemp --directory "/tmp/test-sysext.crt.XXXXXXXXXX")
|
||||
+
|
||||
+ prepend_trap "rm -rf ${ext_dir@Q} ${ext_dir@Q}.raw '$root/etc/verity.d/test-ext.crt' '$tmpcrt'"
|
||||
+
|
||||
+ mkdir -p "${ext_release%/*}"
|
||||
+ echo "ID=_any" >"$ext_release"
|
||||
+ mkdir -p "$ext_dir/$hierarchy"
|
||||
+ touch "$ext_dir$hierarchy/preexisting-file-in-extension-image"
|
||||
+ tee >"$tmpcrt/verity.openssl.cnf" <<EOF
|
||||
+[ req ]
|
||||
+prompt = no
|
||||
+distinguished_name = req_distinguished_name
|
||||
+[ req_distinguished_name ]
|
||||
+C = DE
|
||||
+ST = Test State
|
||||
+L = Test Locality
|
||||
+O = Org Name
|
||||
+OU = Org Unit Name
|
||||
+CN = Common Name
|
||||
+emailAddress = test@email.com
|
||||
+EOF
|
||||
+ openssl req \
|
||||
+ -config "$tmpcrt/verity.openssl.cnf" \
|
||||
+ -new -x509 \
|
||||
+ -newkey rsa:1024 \
|
||||
+ -keyout "$tmpcrt/test-ext.key" \
|
||||
+ -out "$tmpcrt/test-ext.crt" \
|
||||
+ -days 365 \
|
||||
+ -nodes
|
||||
+ systemd-repart --make-ddi=sysext \
|
||||
+ --private-key="$tmpcrt/test-ext.key" --certificate="$tmpcrt/test-ext.crt" \
|
||||
+ --copy-source="$ext_dir" "$ext_dir.raw"
|
||||
+ rm -rf "$ext_dir"
|
||||
+ mkdir -p "$root/etc/verity.d"
|
||||
+ mv "$tmpcrt/test-ext.crt" "$root/etc/verity.d/"
|
||||
+ rm -rf "$tmpcrt"
|
||||
+}
|
||||
+
|
||||
prepare_extension_mutable_dir() {
|
||||
local dir=${1:?}
|
||||
|
||||
@@ -1439,6 +1485,18 @@ if [ "$MOUNTID2" = "$MOUNTID3" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
+( init_trap
|
||||
+: "Check if verity user certs get loaded from --root="
|
||||
+fake_root=${roots_dir:+"$roots_dir/verity-user-cert-from-root"}
|
||||
+hierarchy=/opt
|
||||
+
|
||||
+prepare_root "$fake_root" "$hierarchy"
|
||||
+prepare_extension_image_raw_verity "$fake_root" "$hierarchy"
|
||||
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
|
||||
+
|
||||
+run_systemd_sysext "$fake_root" merge --image-policy=root=signed+absent:usr=signed+absent
|
||||
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
|
||||
+
|
||||
run_systemd_sysext "$fake_root" unmerge
|
||||
extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
|
||||
)
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
From bc53b894296eb8650aa20f047335888dac75d258 Mon Sep 17 00:00:00 2001
|
||||
From: Krzesimir Nowak <knowak@microsoft.com>
|
||||
Date: Mon, 20 Apr 2026 17:47:14 +0200
|
||||
Subject: [PATCH 14/14] Fix name_to_handle_at_try_unique_mntid_fid for empty
|
||||
paths
|
||||
|
||||
Signed-off-by: Krzesimir Nowak <knowak@microsoft.com>
|
||||
---
|
||||
src/basic/mountpoint-util.c | 4 ++++
|
||||
1 file changed, 4 insertions(+)
|
||||
|
||||
diff --git a/src/basic/mountpoint-util.c b/src/basic/mountpoint-util.c
|
||||
index aefbed6346..ce5698b772 100644
|
||||
--- a/src/basic/mountpoint-util.c
|
||||
+++ b/src/basic/mountpoint-util.c
|
||||
@@ -156,6 +156,10 @@ int name_to_handle_at_try_unique_mntid_fid(
|
||||
|
||||
int mnt_id = -1, r;
|
||||
|
||||
+ if (isempty(path)) {
|
||||
+ flags |= AT_EMPTY_PATH;
|
||||
+ }
|
||||
+
|
||||
assert(fd >= 0 || fd == AT_FDCWD);
|
||||
|
||||
/* First issues name_to_handle_at() with AT_HANDLE_MNT_ID_UNIQUE and AT_HANDLE_FID.
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,89 +0,0 @@
|
||||
From aeacbbca05e0479c0768c4b368a2ea68668d20bc Mon Sep 17 00:00:00 2001
|
||||
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||||
Date: Thu, 17 Jul 2025 05:03:54 -0400
|
||||
Subject: [PATCH 16/20] sysext: introduce global config file
|
||||
|
||||
Introduce systemd/{sysext/confext}.conf and systemd/{sysext/confext}.conf.d to provide an
|
||||
alternative way of setting the cmdline options in systemd-sysext.
|
||||
|
||||
The config file has to have a [Sysext] or [Confext] option respectively,
|
||||
which will be overridden by the cmdline.
|
||||
|
||||
As an example of supported config, add Mutable= option.
|
||||
---
|
||||
src/sysext/sysext.c | 38 ++++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 38 insertions(+)
|
||||
|
||||
diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c
|
||||
index 20acc60724..332fc55bb3 100644
|
||||
--- a/src/sysext/sysext.c
|
||||
+++ b/src/sysext/sysext.c
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "bus-util.h"
|
||||
#include "capability-util.h"
|
||||
#include "chase.h"
|
||||
+#include "conf-parser.h"
|
||||
#include "devnum-util.h"
|
||||
#include "discover-image.h"
|
||||
#include "dissect-image.h"
|
||||
@@ -150,6 +151,36 @@ static int parse_mutable_mode(const char *p) {
|
||||
return mutable_mode_from_string(p);
|
||||
}
|
||||
|
||||
+static DEFINE_CONFIG_PARSE_ENUM(config_parse_mutable_mode, mutable_mode, MutableMode);
|
||||
+
|
||||
+static int parse_config_file(ImageClass image_class) {
|
||||
+ const char *section = image_class == IMAGE_SYSEXT ? "SysExt" : "ConfExt";
|
||||
+ const ConfigTableItem items[] = {
|
||||
+ { section, "Mutable", config_parse_mutable_mode, 0, &arg_mutable },
|
||||
+ {}
|
||||
+ };
|
||||
+ _cleanup_free_ char *config_file = NULL;
|
||||
+ int r;
|
||||
+
|
||||
+ config_file = strjoin("systemd/", image_class_info[image_class].short_identifier, ".conf");
|
||||
+ if (!config_file)
|
||||
+ return log_oom();
|
||||
+
|
||||
+ r = config_parse_standard_file_with_dropins_full(
|
||||
+ arg_root,
|
||||
+ config_file,
|
||||
+ image_class == IMAGE_SYSEXT ? "SysExt\0" : "ConfExt\0",
|
||||
+ config_item_table_lookup, items,
|
||||
+ CONFIG_PARSE_WARN,
|
||||
+ /* userdata = */ NULL,
|
||||
+ /* ret_stats_by_path = */ NULL,
|
||||
+ /* ret_dropin_files = */ NULL);
|
||||
+ if (r < 0)
|
||||
+ return r;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int is_our_mount_point(
|
||||
ImageClass image_class,
|
||||
const char *p) {
|
||||
@@ -2828,6 +2859,7 @@ static int run(int argc, char *argv[]) {
|
||||
|
||||
arg_image_class = invoked_as(argv, "systemd-confext") ? IMAGE_CONFEXT : IMAGE_SYSEXT;
|
||||
|
||||
+ /* Parse environment variable first */
|
||||
env_var = getenv(image_class_info[arg_image_class].mode_env);
|
||||
if (env_var) {
|
||||
r = parse_mutable_mode(env_var);
|
||||
@@ -2838,6 +2870,12 @@ static int run(int argc, char *argv[]) {
|
||||
arg_mutable = r;
|
||||
}
|
||||
|
||||
+ /* Parse configuration file */
|
||||
+ r = parse_config_file(arg_image_class);
|
||||
+ if (r < 0)
|
||||
+ log_warning_errno(r, "Failed to parse global config file, ignoring: %m");
|
||||
+
|
||||
+ /* Parse command line */
|
||||
r = parse_argv(argc, argv);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,156 +0,0 @@
|
||||
From d8eabd012273376febada7ad6c9481a360c2e113 Mon Sep 17 00:00:00 2001
|
||||
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||||
Date: Thu, 17 Jul 2025 05:28:21 -0400
|
||||
Subject: [PATCH 17/20] man/sysext.conf: add systemd-sysext config files
|
||||
|
||||
Add sysext.conf, which similar to other configs like coredump, will be
|
||||
searched in:
|
||||
/{etc run usr/lib}/systemd/{sysext/confext}.conf
|
||||
but also
|
||||
/{etc run usr/lib}/systemd/{sysext/confext}.conf.d/*
|
||||
|
||||
This config is an alternative to command line options, especially useful
|
||||
if we want to extend the service units without modifying them.
|
||||
---
|
||||
man/rules/meson.build | 1 +
|
||||
man/sysext.conf.xml | 89 ++++++++++++++++++++++++++++++++++++++++++
|
||||
man/systemd-sysext.xml | 8 +++-
|
||||
3 files changed, 97 insertions(+), 1 deletion(-)
|
||||
create mode 100644 man/sysext.conf.xml
|
||||
|
||||
diff --git a/man/rules/meson.build b/man/rules/meson.build
|
||||
index 33f44b0659..6284183756 100644
|
||||
--- a/man/rules/meson.build
|
||||
+++ b/man/rules/meson.build
|
||||
@@ -1138,6 +1138,7 @@ manpages = [
|
||||
'systemd-sysext-initrd.service',
|
||||
'systemd-sysext.service'],
|
||||
'ENABLE_SYSEXT'],
|
||||
+ ['sysext.conf', '5', ['confext.conf'], 'ENABLE_SYSEXT'],
|
||||
['systemd-system-update-generator', '8', [], ''],
|
||||
['systemd-system.conf',
|
||||
'5',
|
||||
diff --git a/man/sysext.conf.xml b/man/sysext.conf.xml
|
||||
new file mode 100644
|
||||
index 0000000000..cdd88f2447
|
||||
--- /dev/null
|
||||
+++ b/man/sysext.conf.xml
|
||||
@@ -0,0 +1,89 @@
|
||||
+<?xml version='1.0'?> <!--*-nxml-*-->
|
||||
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||||
+ "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
|
||||
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
|
||||
+
|
||||
+<refentry id="sysext.conf" conditional='ENABLE_SYSEXT'
|
||||
+ xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
+
|
||||
+ <refentryinfo>
|
||||
+ <title>sysext.conf</title>
|
||||
+ <productname>systemd</productname>
|
||||
+ </refentryinfo>
|
||||
+
|
||||
+ <refmeta>
|
||||
+ <refentrytitle>sysext.conf</refentrytitle>
|
||||
+ <manvolnum>5</manvolnum>
|
||||
+ </refmeta>
|
||||
+
|
||||
+ <refnamediv>
|
||||
+ <refname>sysext.conf</refname>
|
||||
+ <refname>confext.conf</refname>
|
||||
+ <refname>sysext.conf.d</refname>
|
||||
+ <refname>confext.conf.d</refname>
|
||||
+ <refpurpose>Configuration files for systemd-sysext</refpurpose>
|
||||
+ </refnamediv>
|
||||
+
|
||||
+ <refsynopsisdiv>
|
||||
+ <para><filename>/etc/systemd/sysext.conf</filename></para>
|
||||
+ <para><filename>/etc/systemd/sysext.conf.d/*.conf</filename></para>
|
||||
+ <para><filename>/run/systemd/sysext.conf</filename></para>
|
||||
+ <para><filename>/run/systemd/sysext.conf.d/*.conf</filename></para>
|
||||
+ <para><filename>/usr/lib/systemd/sysext.conf</filename></para>
|
||||
+ <para><filename>/usr/lib/systemd/sysext.conf.d/*.conf</filename></para>
|
||||
+ <para><filename>/etc/systemd/confext.conf</filename></para>
|
||||
+ <para><filename>/etc/systemd/confext.conf.d/*.conf</filename></para>
|
||||
+ <para><filename>/run/systemd/confext.conf</filename></para>
|
||||
+ <para><filename>/run/systemd/confext.conf.d/*.conf</filename></para>
|
||||
+ <para><filename>/usr/lib/systemd/confext.conf</filename></para>
|
||||
+ <para><filename>/usr/lib/systemd/confext.conf.d/*.conf</filename></para>
|
||||
+ </refsynopsisdiv>
|
||||
+
|
||||
+ <refsect1>
|
||||
+ <title>Description</title>
|
||||
+
|
||||
+ <para>These configuration files control the behavior of
|
||||
+ <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry> and
|
||||
+ <citerefentry><refentrytitle>systemd-confext</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
|
||||
+ They are especially useful when needing to customize the behavior of the
|
||||
+ respective extension service units.</para>
|
||||
+ </refsect1>
|
||||
+
|
||||
+ <xi:include href="standard-conf.xml" xpointer="main-conf" />
|
||||
+
|
||||
+ <refsect1>
|
||||
+ <title>Options</title>
|
||||
+
|
||||
+ <para>The following options are understood in both the <literal>[Sysext]</literal> and
|
||||
+ <literal>[Confext]</literal> sections:</para>
|
||||
+
|
||||
+ <refsect2>
|
||||
+ <title>Section Options</title>
|
||||
+
|
||||
+ <variablelist class='config-directives'>
|
||||
+ <varlistentry>
|
||||
+ <term><varname>Mutable=</varname></term>
|
||||
+ <listitem><para>Set the mutable mode for system extensions. Takes one of <literal>no</literal>,
|
||||
+ <literal>yes</literal>, <literal>auto</literal>, <literal>import</literal>,
|
||||
+ <literal>ephemeral</literal>, or <literal>ephemeral-import</literal>. For details about the modes,
|
||||
+ see the <option>--mutable=</option> option in
|
||||
+ <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
|
||||
+ Defaults to <literal>no</literal>.</para>
|
||||
+
|
||||
+ <xi:include href="version-info.xml" xpointer="v259"/>
|
||||
+ </listitem>
|
||||
+ </varlistentry>
|
||||
+ </variablelist>
|
||||
+ </refsect2>
|
||||
+ </refsect1>
|
||||
+
|
||||
+ <refsect1>
|
||||
+ <title>See Also</title>
|
||||
+ <para><simplelist type="inline">
|
||||
+ <member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
+ <member><citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
|
||||
+ <member><citerefentry><refentrytitle>systemd.syntax</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
|
||||
+ </simplelist></para>
|
||||
+ </refsect1>
|
||||
+
|
||||
+</refentry>
|
||||
diff --git a/man/systemd-sysext.xml b/man/systemd-sysext.xml
|
||||
index 3f60c85dba..6df2d94e9f 100644
|
||||
--- a/man/systemd-sysext.xml
|
||||
+++ b/man/systemd-sysext.xml
|
||||
@@ -74,7 +74,12 @@
|
||||
<para>System extension images are strictly read-only by default. On mutable host file systems,
|
||||
<filename>/usr/</filename> and <filename>/opt/</filename> hierarchies become read-only while extensions
|
||||
are merged, unless mutability is enabled. Mutability may be enabled via the <option>--mutable=</option>
|
||||
- option; see "Mutability" below for more information.</para>
|
||||
+ option and the <varname>Mutable=</varname> option in the configuration file;
|
||||
+ see "Mutability" below for more information.</para>
|
||||
+
|
||||
+ <para>Various command options can be configured globally via configuration files. See
|
||||
+ <citerefentry><refentrytitle>sysext.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
+ for details.</para>
|
||||
|
||||
<para>System extensions are supposed to be purely additive, i.e. they are supposed to include only files
|
||||
that do not exist in the underlying basic OS image. However, the underlying mechanism (overlayfs) also
|
||||
@@ -491,6 +496,7 @@
|
||||
<title>See Also</title>
|
||||
<para><simplelist type="inline">
|
||||
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
+ <member><citerefentry><refentrytitle>sysext.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
|
||||
<member><citerefentry><refentrytitle>importctl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,50 +0,0 @@
|
||||
From dccee58738d9602dd62f482ed11152f51b4da896 Mon Sep 17 00:00:00 2001
|
||||
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
|
||||
Date: Thu, 17 Jul 2025 10:16:24 -0400
|
||||
Subject: [PATCH 18/20] sysext: support ImagePolicy global config option
|
||||
|
||||
Just as Mutable=, support ImagePolicy in systemd/{sysext/confext}.conf and
|
||||
dropins in systemd/{sysext.confext}.conf.d/* configs.
|
||||
---
|
||||
man/sysext.conf.xml | 12 ++++++++++++
|
||||
src/sysext/sysext.c | 1 +
|
||||
2 files changed, 13 insertions(+)
|
||||
|
||||
diff --git a/man/sysext.conf.xml b/man/sysext.conf.xml
|
||||
index cdd88f2447..f717b74426 100644
|
||||
--- a/man/sysext.conf.xml
|
||||
+++ b/man/sysext.conf.xml
|
||||
@@ -73,6 +73,18 @@
|
||||
<xi:include href="version-info.xml" xpointer="v259"/>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
+
|
||||
+ <varlistentry>
|
||||
+ <term><varname>ImagePolicy=</varname></term>
|
||||
+ <listitem><para>Set the image policy. Takes an image policy string as argument, as per
|
||||
+ <citerefentry><refentrytitle>systemd.image-policy</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
|
||||
+ For details, see the <option>--image-policy=</option> option in
|
||||
+ <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
|
||||
+ </para>
|
||||
+
|
||||
+ <xi:include href="version-info.xml" xpointer="v259"/>
|
||||
+ </listitem>
|
||||
+ </varlistentry>
|
||||
</variablelist>
|
||||
</refsect2>
|
||||
</refsect1>
|
||||
diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c
|
||||
index 332fc55bb3..9656e975c4 100644
|
||||
--- a/src/sysext/sysext.c
|
||||
+++ b/src/sysext/sysext.c
|
||||
@@ -157,6 +157,7 @@ static int parse_config_file(ImageClass image_class) {
|
||||
const char *section = image_class == IMAGE_SYSEXT ? "SysExt" : "ConfExt";
|
||||
const ConfigTableItem items[] = {
|
||||
{ section, "Mutable", config_parse_mutable_mode, 0, &arg_mutable },
|
||||
+ { section, "ImagePolicy", config_parse_image_policy, 0, &arg_image_policy },
|
||||
{}
|
||||
};
|
||||
_cleanup_free_ char *config_file = NULL;
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,183 +0,0 @@
|
||||
From 5d8c8737ea0b44c50e4e60a9c93c7321051f7955 Mon Sep 17 00:00:00 2001
|
||||
From: Kai Lueke <kailuke@microsoft.com>
|
||||
Date: Thu, 11 Dec 2025 19:49:20 +0900
|
||||
Subject: [PATCH 19/20] sysext: Fix config file support with --root=
|
||||
|
||||
Config files for --root= weren't picked up as expected because the
|
||||
--root= flag got parsed after the config file.
|
||||
Switch the order of config file and CLI flag parsing while letting the
|
||||
CLI flags overwrite things set by the config files by tracking state
|
||||
during parsing.
|
||||
---
|
||||
src/sysext/sysext.c | 38 +++++++++++++++-----
|
||||
test/units/TEST-50-DISSECT.sysext.sh | 52 ++++++++++++++++++++++++++++
|
||||
2 files changed, 82 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c
|
||||
index 9656e975c4..1fb0c72e48 100644
|
||||
--- a/src/sysext/sysext.c
|
||||
+++ b/src/sysext/sysext.c
|
||||
@@ -92,8 +92,10 @@ static bool arg_no_reload = false;
|
||||
static bool arg_always_refresh = false;
|
||||
static int arg_noexec = -1;
|
||||
static ImagePolicy *arg_image_policy = NULL;
|
||||
+static bool arg_image_policy_set = false; /* Tracks initialization */
|
||||
static bool arg_varlink = false;
|
||||
static MutableMode arg_mutable = MUTABLE_NO;
|
||||
+static bool arg_mutable_set = false; /* Tracks initialization */
|
||||
|
||||
/* Is set to IMAGE_CONFEXT when systemd is called with the confext functionality instead of the default */
|
||||
static ImageClass arg_image_class = IMAGE_SYSEXT;
|
||||
@@ -154,10 +156,13 @@ static int parse_mutable_mode(const char *p) {
|
||||
static DEFINE_CONFIG_PARSE_ENUM(config_parse_mutable_mode, mutable_mode, MutableMode);
|
||||
|
||||
static int parse_config_file(ImageClass image_class) {
|
||||
+ _cleanup_(image_policy_freep) ImagePolicy *config_image_policy = NULL;
|
||||
+ MutableMode config_mutable = MUTABLE_NO;
|
||||
const char *section = image_class == IMAGE_SYSEXT ? "SysExt" : "ConfExt";
|
||||
+ const char *sections = image_class == IMAGE_SYSEXT ? "SysExt\0" : "ConfExt\0";
|
||||
const ConfigTableItem items[] = {
|
||||
- { section, "Mutable", config_parse_mutable_mode, 0, &arg_mutable },
|
||||
- { section, "ImagePolicy", config_parse_image_policy, 0, &arg_image_policy },
|
||||
+ { section, "Mutable", config_parse_mutable_mode, 0, &config_mutable },
|
||||
+ { section, "ImagePolicy", config_parse_image_policy, 0, &config_image_policy },
|
||||
{}
|
||||
};
|
||||
_cleanup_free_ char *config_file = NULL;
|
||||
@@ -170,7 +175,7 @@ static int parse_config_file(ImageClass image_class) {
|
||||
r = config_parse_standard_file_with_dropins_full(
|
||||
arg_root,
|
||||
config_file,
|
||||
- image_class == IMAGE_SYSEXT ? "SysExt\0" : "ConfExt\0",
|
||||
+ sections,
|
||||
config_item_table_lookup, items,
|
||||
CONFIG_PARSE_WARN,
|
||||
/* userdata = */ NULL,
|
||||
@@ -179,6 +184,17 @@ static int parse_config_file(ImageClass image_class) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
+ /* Because this runs after parse_argv we only overwrite when things aren't set yet. */
|
||||
+ if (!arg_mutable_set) {
|
||||
+ arg_mutable = config_mutable;
|
||||
+ arg_mutable_set = true;
|
||||
+ }
|
||||
+
|
||||
+ if (!arg_image_policy_set) {
|
||||
+ arg_image_policy = TAKE_PTR(config_image_policy);
|
||||
+ arg_image_policy_set = true;
|
||||
+ }
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2794,6 +2810,9 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
r = parse_image_policy_argument(optarg, &arg_image_policy);
|
||||
if (r < 0)
|
||||
return r;
|
||||
+ /* When the CLI flag is given we initialize even if NULL
|
||||
+ * so that the config file entry won't overwrite it */
|
||||
+ arg_image_policy_set = true;
|
||||
break;
|
||||
|
||||
case ARG_NOEXEC:
|
||||
@@ -2819,6 +2838,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse argument to --mutable=: %s", optarg);
|
||||
arg_mutable = r;
|
||||
+ arg_mutable_set = true;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
@@ -2871,11 +2891,6 @@ static int run(int argc, char *argv[]) {
|
||||
arg_mutable = r;
|
||||
}
|
||||
|
||||
- /* Parse configuration file */
|
||||
- r = parse_config_file(arg_image_class);
|
||||
- if (r < 0)
|
||||
- log_warning_errno(r, "Failed to parse global config file, ignoring: %m");
|
||||
-
|
||||
/* Parse command line */
|
||||
r = parse_argv(argc, argv);
|
||||
if (r <= 0)
|
||||
@@ -2888,6 +2903,13 @@ static int run(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse environment variable: %m");
|
||||
|
||||
+ /* Parse configuration file after argv because it needs --root=.
|
||||
+ * The config entries will not overwrite values set already by
|
||||
+ * env/argv because we track initialization. */
|
||||
+ r = parse_config_file(arg_image_class);
|
||||
+ if (r < 0)
|
||||
+ log_warning_errno(r, "Failed to parse global config file, ignoring: %m");
|
||||
+
|
||||
if (arg_varlink) {
|
||||
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *varlink_server = NULL;
|
||||
|
||||
diff --git a/test/units/TEST-50-DISSECT.sysext.sh b/test/units/TEST-50-DISSECT.sysext.sh
|
||||
index 6e64eea492..7a3146a0e7 100755
|
||||
--- a/test/units/TEST-50-DISSECT.sysext.sh
|
||||
+++ b/test/units/TEST-50-DISSECT.sysext.sh
|
||||
@@ -1501,6 +1501,58 @@ run_systemd_sysext "$fake_root" unmerge
|
||||
extension_verify_after_unmerge "$fake_root" "$hierarchy" -h
|
||||
)
|
||||
|
||||
+
|
||||
+( init_trap
|
||||
+: "Check config file support for --root="
|
||||
+fake_root=${roots_dir:+"$roots_dir/config-file"}
|
||||
+hierarchy=/opt
|
||||
+extension_data_dir="$fake_root/var/lib/extensions.mutable$hierarchy"
|
||||
+
|
||||
+[[ "$FSTYPE" == "fuseblk" ]] && exit 0
|
||||
+if [ "$roots_dir" = "" ]; then
|
||||
+ echo >&2 "Skipping test when --root= is not used"
|
||||
+ exit 0
|
||||
+fi
|
||||
+
|
||||
+prepare_root "$fake_root" "$hierarchy"
|
||||
+prepare_extension_image_raw "$fake_root" "$hierarchy"
|
||||
+prepare_extension_mutable_dir "$extension_data_dir"
|
||||
+prepare_read_only_hierarchy "$fake_root" "$hierarchy"
|
||||
+
|
||||
+mkdir -p "$fake_root/etc/systemd/"
|
||||
+{ echo "[SysExt]" ; echo "Mutable=auto" ; } > "$fake_root/etc/systemd/sysext.conf"
|
||||
+# Config file should be picked up with --root= set
|
||||
+run_systemd_sysext "$fake_root" merge
|
||||
+MNTOPT=$(findmnt "$fake_root$hierarchy" --first-only --direction backward --raw --noheadings -o VFS-OPTIONS | grep -o rw || true)
|
||||
+if [ "$MNTOPT" != "rw" ]; then
|
||||
+ echo >&2 "Merge did not pick up mutable setting from config file"
|
||||
+ exit 1
|
||||
+fi
|
||||
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h -u
|
||||
+run_systemd_sysext "$fake_root" unmerge
|
||||
+
|
||||
+# CLI arg should be able to overwrite config file
|
||||
+run_systemd_sysext "$fake_root" merge --mutable=no
|
||||
+MNTOPT=$(findmnt "$fake_root$hierarchy" --first-only --direction backward --raw --noheadings -o VFS-OPTIONS | grep -o ro || true)
|
||||
+if [ "$MNTOPT" != "ro" ]; then
|
||||
+ echo >&2 "Merge did not pick up CLI arg to overwrite mutable setting from config file"
|
||||
+ exit 1
|
||||
+fi
|
||||
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
|
||||
+run_systemd_sysext "$fake_root" unmerge
|
||||
+
|
||||
+{ echo "[SysExt]" ; echo "ImagePolicy=root=signed+absent:usr=signed+absent" ; } > "$fake_root/etc/systemd/sysext.conf"
|
||||
+# Config file should be picked up with --root= set
|
||||
+if run_systemd_sysext "$fake_root" merge; then
|
||||
+ echo >&2 "Merge did not fail with strict image policy in config file"
|
||||
+ exit 1
|
||||
+fi
|
||||
+# CLI arg should be able to overwrite config file
|
||||
+run_systemd_sysext "$fake_root" merge --image-policy="*"
|
||||
+extension_verify_after_merge "$fake_root" "$hierarchy" -e -h
|
||||
+run_systemd_sysext "$fake_root" unmerge
|
||||
+)
|
||||
+
|
||||
} # End of run_sysext_tests
|
||||
|
||||
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
From 4bf1282faa430669eba4169837657f00f2cba019 Mon Sep 17 00:00:00 2001
|
||||
From: Justin Kromlinger <hashworks@archlinux.org>
|
||||
Date: Wed, 8 Oct 2025 16:55:09 +0200
|
||||
Subject: [PATCH 20/20] Drop `machine-id` OSC event field if /etc/machine-id
|
||||
doesn't exist
|
||||
|
||||
While we can safely assume that `/proc/sys/kernel/random/boot_id`
|
||||
exists, the same can't be said for `/etc/machine-id` in environments
|
||||
where systemd is installed, but not running. An example would be OCI
|
||||
containers like with the official Arch Linux image, see [0].
|
||||
|
||||
Without this check the prompt would constantly output `/etc/machine-id:
|
||||
no such file or directory` with the OSC events introduced in dadbb34
|
||||
(v258).
|
||||
|
||||
[0] https://gitlab.archlinux.org/archlinux/archlinux-docker/-/issues/107
|
||||
|
||||
(cherry picked from commit 0fe45b98dd737da86fcbb703809ebf2163c397f3)
|
||||
---
|
||||
profile.d/80-systemd-osc-context.sh | 5 ++++-
|
||||
1 file changed, 4 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/profile.d/80-systemd-osc-context.sh b/profile.d/80-systemd-osc-context.sh
|
||||
index a0ac858828..ead61b6753 100644
|
||||
--- a/profile.d/80-systemd-osc-context.sh
|
||||
+++ b/profile.d/80-systemd-osc-context.sh
|
||||
@@ -28,7 +28,10 @@ __systemd_osc_context_escape() {
|
||||
}
|
||||
|
||||
__systemd_osc_context_common() {
|
||||
- printf ";user=%s;hostname=%s;machineid=%s;bootid=%s;pid=%s" "$USER" "$HOSTNAME" "$(</etc/machine-id)" "$(</proc/sys/kernel/random/boot_id)" "$$"
|
||||
+ if [ -f /etc/machine-id ]; then
|
||||
+ printf ";machineid=%s" "$(</etc/machine-id)"
|
||||
+ fi
|
||||
+ printf ";user=%s;hostname=%s;bootid=%s;pid=%s" "$USER" "$HOSTNAME" "$(</proc/sys/kernel/random/boot_id)" "$$"
|
||||
}
|
||||
|
||||
__systemd_osc_context_precmdline() {
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -21,18 +21,8 @@ Most of these patches are not really upstreamable:
|
||||
|
||||
These patches can be dropped after we update to systemd 260:
|
||||
|
||||
- `0009-vpick-Don-t-use-openat-directly-but-resolve-symlinks.patch`
|
||||
- `0010-discover-image-Follow-symlinks-in-a-given-root.patch`
|
||||
- `0011-sysext-Use-correct-image-name-for-extension-release-.patch`
|
||||
- `0012-test-Add-tests-for-handling-symlinks-with-systemd-sy.patch`
|
||||
- `0013-sysext-Create-mutable-directory-with-the-right-mode.patch`
|
||||
- `0014-sysext-Skip-refresh-if-no-changes-are-found.patch`
|
||||
- `0015-sysext-Get-verity-user-certs-from-given-root.patch`
|
||||
- `0016-sysext-introduce-global-config-file.patch`
|
||||
- `0017-man-sysext.conf-add-systemd-sysext-config-files.patch`
|
||||
- `0018-sysext-support-ImagePolicy-global-config-option.patch`
|
||||
- `0019-sysext-Fix-config-file-support-with-root.patch`
|
||||
|
||||
This patch can be dropped after updating to systemd 258.5:
|
||||
|
||||
- `0020-Drop-machine-id-OSC-event-field-if-etc-machine-id-do.patch`
|
||||
- `0009-discover-image-Follow-symlinks-in-a-given-root.patch`
|
||||
- `0010-sysext-Use-correct-image-name-for-extension-release-.patch`
|
||||
- `0011-sysext-Create-mutable-directory-with-the-right-mode.patch`
|
||||
- `0012-sysext-Skip-refresh-if-no-changes-are-found.patch`
|
||||
- `0013-sysext-Get-verity-user-certs-from-given-root.patch`
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user