sysext: Skip refresh if no changes are found (#39980)
When the extensions for the final system are already set up from the
initrd we should avoid disrupting the boot process with the remount
(which currently isn't atomic) and the daemon reload for
systemd-confext and systemd-sysext. Similarly, when sysupdate ran and
updated extensions it's best to avoid the remount and daemon reload if
no changes are found.
To do this, encode the current extension state in more detail than
before where only the names of the extensions where encoded in the
overlay mount. This can also be used to provide more details about the
extension origin in "systemd-sysext status (--json=)". During the
refresh add a check whether the old state matches the new state and in
this case skip the refresh unless the user provides a flag to always
refresh. Besides the extension name and the resolved path the best
method for identification is the verity hash but that is not available
for plain image files or directories. Therefore, also include data to
check for file/directory replacements. The creation/modification times
are not always real on reproducible images or extracted archive content.
The file handle together with the unique mount ID is the next best
identifier we can use when we have no verity hash. Fall back to an inode
when we get no handle. With the creation/modification time and the path
this should be good enough. Using a unique mount ID is important (with
a fallback to the regular non-unique mount ID) instead of st_dev because
st_dev gets reused too easily, e.g., by a loop device mount and the
mount ID helps to catch this. For the mount ID to be valid it has to be
resolved before we enter the new mount namespace. Thus, it gets provided
by the image dissect logic and handed over to the sysext subprocess
which runs in a new mount namespace.
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.
This commit is contained in:
@@ -35,6 +35,7 @@
|
||||
#include "lock-util.h"
|
||||
#include "log.h"
|
||||
#include "loop-util.h"
|
||||
#include "mountpoint-util.h"
|
||||
#include "namespace-util.h"
|
||||
#include "nsresource.h"
|
||||
#include "nulstr-util.h"
|
||||
@@ -148,6 +149,8 @@ static Image* image_free(Image *i) {
|
||||
free(i->name);
|
||||
free(i->path);
|
||||
|
||||
free(i->fh);
|
||||
|
||||
free(i->hostname);
|
||||
strv_free(i->machine_info);
|
||||
strv_free(i->os_release);
|
||||
@@ -249,6 +252,9 @@ static int image_new(
|
||||
bool read_only,
|
||||
usec_t crtime,
|
||||
usec_t mtime,
|
||||
struct file_handle *fh,
|
||||
uint64_t on_mount_id,
|
||||
uint64_t inode,
|
||||
Image **ret) {
|
||||
|
||||
_cleanup_(image_unrefp) Image *i = NULL;
|
||||
@@ -270,12 +276,20 @@ static int image_new(
|
||||
.read_only = read_only,
|
||||
.crtime = crtime,
|
||||
.mtime = mtime,
|
||||
.on_mount_id = on_mount_id,
|
||||
.inode = inode,
|
||||
.usage = UINT64_MAX,
|
||||
.usage_exclusive = UINT64_MAX,
|
||||
.limit = UINT64_MAX,
|
||||
.limit_exclusive = UINT64_MAX,
|
||||
};
|
||||
|
||||
if (fh) {
|
||||
i->fh = file_handle_dup(fh);
|
||||
if (!i->fh)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
i->name = strdup(pretty);
|
||||
if (!i->name)
|
||||
return -ENOMEM;
|
||||
@@ -437,6 +451,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, /* 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, /* 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, /* path= */ NULL, &on_mount_id_fallback);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
on_mount_id = on_mount_id_fallback;
|
||||
}
|
||||
}
|
||||
|
||||
if (S_ISDIR(st->st_mode)) {
|
||||
unsigned file_attr = 0;
|
||||
usec_t crtime = 0;
|
||||
@@ -478,6 +514,9 @@ static int image_make(
|
||||
info.read_only || read_only,
|
||||
info.otime,
|
||||
info.ctime,
|
||||
fh,
|
||||
on_mount_id,
|
||||
(uint64_t) st->st_ino,
|
||||
ret);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -503,6 +542,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 */
|
||||
fh,
|
||||
on_mount_id,
|
||||
(uint64_t) st->st_ino,
|
||||
ret);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -540,6 +582,9 @@ static int image_make(
|
||||
!(st->st_mode & 0222) || read_only,
|
||||
crtime,
|
||||
timespec_load(&st->st_mtim),
|
||||
fh,
|
||||
on_mount_id,
|
||||
(uint64_t) st->st_ino,
|
||||
ret);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -597,6 +642,9 @@ static int image_make(
|
||||
!(st->st_mode & 0222) || read_only,
|
||||
0,
|
||||
0,
|
||||
fh,
|
||||
on_mount_id,
|
||||
(uint64_t) st->st_ino,
|
||||
ret);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -27,6 +27,10 @@ typedef struct Image {
|
||||
usec_t crtime;
|
||||
usec_t mtime;
|
||||
|
||||
struct file_handle *fh;
|
||||
uint64_t on_mount_id;
|
||||
uint64_t inode;
|
||||
|
||||
uint64_t usage;
|
||||
uint64_t usage_exclusive;
|
||||
uint64_t limit;
|
||||
|
||||
@@ -34,6 +34,7 @@ static SD_VARLINK_DEFINE_METHOD(
|
||||
SD_VARLINK_DEFINE_INPUT_BY_TYPE(class, ImageClass, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_INPUT(force, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_INPUT(noReload, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_INPUT(alwaysRefresh, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
|
||||
SD_VARLINK_DEFINE_INPUT(noexec, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
|
||||
VARLINK_DEFINE_POLKIT_INPUT);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user