nsresourced: re-link GID delegation file after atomic UID file write
userns_registry_remove() restores a sub-delegated UID range by writing the previous owner's data to u<UID>.delegate with WRITE_STRING_FILE_ATOMIC. Atomic writes go via a temp file and rename, which replaces the directory entry with a fresh inode and severs the hardlink to g<GID>.delegate. The stale GID side then keeps pointing at the prior inode with outdated owner and ancestor data, so subsequent lookups via GID return wrong results. Re-create the hardlink after the atomic write so the two views stay in sync, matching what userns_registry_store() already does after writing a new delegation.
This commit is contained in:
committed by
Luca Boccassi
parent
a442440315
commit
95e7ead268
@@ -838,9 +838,11 @@ int userns_registry_remove(int dir_fd, UserNamespaceInfo *info) {
|
||||
continue;
|
||||
}
|
||||
|
||||
_cleanup_free_ char *delegate_uid_fn = NULL;
|
||||
_cleanup_free_ char *delegate_uid_fn = NULL, *delegate_gid_fn = NULL;
|
||||
if (asprintf(&delegate_uid_fn, "u" UID_FMT ".delegate", delegate->start_uid) < 0)
|
||||
return log_oom_debug();
|
||||
if (asprintf(&delegate_gid_fn, "g" GID_FMT ".delegate", delegate->start_gid) < 0)
|
||||
return log_oom_debug();
|
||||
|
||||
if (existing.n_ancestor_userns > 0) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *delegate_def = NULL, *ancestor_array = NULL;
|
||||
@@ -876,10 +878,18 @@ int userns_registry_remove(int dir_fd, UserNamespaceInfo *info) {
|
||||
return log_debug_errno(r, "Failed to format delegation JSON object: %m");
|
||||
|
||||
r = write_string_file_at(dir_fd, delegate_uid_fn, delegate_buf, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
|
||||
if (r < 0)
|
||||
if (r < 0) {
|
||||
RET_GATHER(ret, log_debug_errno(r, "Failed to write restored delegation data to '%s' in registry: %m", delegate_uid_fn));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* The atomic write above replaced the UID file with a new inode, so the
|
||||
* hardlink to the GID file is now broken. Re-create it to keep the two in
|
||||
* sync. */
|
||||
r = linkat_replace(dir_fd, delegate_uid_fn, dir_fd, delegate_gid_fn);
|
||||
if (r < 0)
|
||||
RET_GATHER(ret, log_debug_errno(r, "Failed to re-link '%s' to '%s' in registry: %m", delegate_uid_fn, delegate_gid_fn));
|
||||
|
||||
/* GID link already points to the UID file, no need to update it */
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -891,10 +901,6 @@ int userns_registry_remove(int dir_fd, UserNamespaceInfo *info) {
|
||||
if (r < 0)
|
||||
RET_GATHER(ret, log_debug_errno(r, "Failed to remove %s: %m", delegate_uid_fn));
|
||||
|
||||
_cleanup_free_ char *delegate_gid_fn = NULL;
|
||||
if (asprintf(&delegate_gid_fn, "g" GID_FMT ".delegate", delegate->start_gid) < 0)
|
||||
return log_oom_debug();
|
||||
|
||||
r = RET_NERRNO(unlinkat(dir_fd, delegate_gid_fn, 0));
|
||||
if (r < 0)
|
||||
RET_GATHER(ret, log_debug_errno(r, "Failed to remove %s: %m", delegate_gid_fn));
|
||||
|
||||
Reference in New Issue
Block a user