diff --git a/man/sysupdate.d.xml b/man/sysupdate.d.xml index 5e8e9d6fe4c..b8c403aa7de 100644 --- a/man/sysupdate.d.xml +++ b/man/sysupdate.d.xml @@ -758,6 +758,12 @@ resources, as well as inline data that is encoded, must consist of the file and nothing else, other such sources are ignored. + The extended attributes in the user namespace listed in the + extendedAttributes field of a file object of a UAPI.16 manifest are applied to the + file or directory created when installing it. This is done on a best-effort basis: if the file system + does not support extended attributes, or they cannot be set for other reasons, the instance is + installed without them. + diff --git a/src/shared/forward.h b/src/shared/forward.h index d8c2c183125..3d9a1c03d6c 100644 --- a/src/shared/forward.h +++ b/src/shared/forward.h @@ -97,6 +97,7 @@ typedef struct Tpm2Handle Tpm2Handle; typedef struct Tpm2PCRValue Tpm2PCRValue; typedef struct Uapi16Contents Uapi16Contents; typedef struct Uapi16Object Uapi16Object; +typedef struct Uapi16Xattr Uapi16Xattr; typedef struct UnitInfo UnitInfo; typedef struct UnitResult UnitResult; typedef struct UserRecord UserRecord; diff --git a/src/sysupdate/sysupdate-instance.c b/src/sysupdate/sysupdate-instance.c index 7406f47f6d5..ef769bac103 100644 --- a/src/sysupdate/sysupdate-instance.c +++ b/src/sysupdate/sysupdate-instance.c @@ -63,5 +63,7 @@ Instance *instance_free(Instance *i) { uapi16_contents_done(s); free(i->sources); + uapi16_xattr_array_free(i->user_xattrs, i->n_user_xattrs); + return mfree(i); } diff --git a/src/sysupdate/sysupdate-instance.h b/src/sysupdate/sysupdate-instance.h index d64ed897959..584937159e4 100644 --- a/src/sysupdate/sysupdate-instance.h +++ b/src/sysupdate/sysupdate-instance.h @@ -53,6 +53,11 @@ struct Instance { Uapi16Contents *sources; size_t n_sources; + /* The "user" extended attributes listed in the manifest (for web backed sources), which we apply + * to the inode we create when installing the instance */ + Uapi16Xattr *user_xattrs; + size_t n_user_xattrs; + /* The current state of this instance */ bool is_partial; bool is_pending; diff --git a/src/sysupdate/sysupdate-resource.c b/src/sysupdate/sysupdate-resource.c index b076ec35132..4514982c040 100644 --- a/src/sysupdate/sysupdate-resource.c +++ b/src/sysupdate/sysupdate-resource.c @@ -899,6 +899,11 @@ static int uapi16_process_object(Uapi16Object *object, size_t index, void *userd instance->n_sources = object->n_contents; object->n_contents = 0; + /* Similar, for the extended attributes to apply when installing */ + instance->user_xattrs = TAKE_PTR(object->user_xattrs); + instance->n_user_xattrs = object->n_user_xattrs; + object->n_user_xattrs = 0; + return 0; } diff --git a/src/sysupdate/sysupdate-transfer.c b/src/sysupdate/sysupdate-transfer.c index 1b5c2f67801..faa7676f80d 100644 --- a/src/sysupdate/sysupdate-transfer.c +++ b/src/sysupdate/sysupdate-transfer.c @@ -51,6 +51,7 @@ #include "sysupdate-transfer.h" #include "time-util.h" #include "web-util.h" +#include "xattr-util.h" /* Default value for InstancesMax= for fs object targets */ #define DEFAULT_FILE_INSTANCES_MAX 3 @@ -1835,6 +1836,54 @@ static int transfer_acquire_source( t, i, cb, userdata); } +static int instance_apply_user_xattrs(Instance *i, const char *path) { + int r; + + assert(i); + assert(path); + + /* Applies the user extended attributes listed in the manifest to the installed inode, so that they + * are retained on the target. Failing to do so is not fatal, the instance is fully usable without + * them. Returns > 0 if any attribute was applied. */ + + if (i->n_user_xattrs == 0) + return 0; + + _cleanup_close_ int fd = xopenat(AT_FDCWD, path, O_PATH|O_NOFOLLOW); + if (fd < 0) { + log_warning_errno(fd, "Failed to open '%s' to apply extended attributes, ignoring: %m", path); + return 0; + } + + bool applied = false; + FOREACH_ARRAY(x, i->user_xattrs, i->n_user_xattrs) { + _cleanup_free_ char *n = strjoin("user.", x->name); + if (!n) + return log_oom(); + + r = xsetxattr_full( + fd, + /* path= */ NULL, + AT_EMPTY_PATH, + n, + x->value.iov_base, + x->value.iov_len, + /* xattr_flags= */ 0); + if (ERRNO_IS_NEG_NOT_SUPPORTED(r)) { + log_notice_errno(r, "File system backing '%s' does not support extended attributes, not applying them: %m", path); + break; + } + if (r < 0) { + log_warning_errno(r, "Failed to set extended attribute '%s' on '%s', ignoring: %m", n, path); + continue; + } + + applied = true; + } + + return applied; +} + int transfer_acquire_instance(Transfer *t, Instance *i, InstanceMetadata *f, TransferProgress cb, void *userdata) { char offset[DECIMAL_STR_MAX(uint64_t)+1], max_size[DECIMAL_STR_MAX(uint64_t)+1]; const char *where = NULL; @@ -2074,6 +2123,12 @@ int transfer_acquire_instance(Transfer *t, Instance *i, InstanceMetadata *f, Tra need_sync = true; } + r = instance_apply_user_xattrs(i, t->temporary_partial_path); + if (r < 0) + return r; + if (r > 0) + need_sync = true; + /* Synchronize */ if (t->context->sync && need_sync) { if (t->target.type == RESOURCE_REGULAR_FILE)