machine: preserve read-only clone mode for foreign UID images
machinectl clone --read-only ignored the requested read-only mode when cloning a foreign-UID-owned directory because the unprivileged clone path did not receive the read_only argument. Pass the flag through and apply the immutable bit via the already-open destination directory fd after copying. Keep this best-effort, matching the ordinary directory clone fallback, so failure to set the attribute does not turn a completed copy into a reported clone failure. Add a regression test using foreign-0 rather than a fixed UID. Verify the cloned image's ReadOnly property, copied contents and ownership, and that the immutable directory rejects new entries. Run this subtest only when file attributes and the existing rootless nspawn prerequisites are available, leaving the other machined tests unchanged.
This commit is contained in:
committed by
Lennart Poettering
parent
0c8f57cae2
commit
f789245918
@@ -1687,7 +1687,7 @@ static int get_pool_directory(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unprivileged_clone(Image *i, const char *new_path) {
|
||||
static int unprivileged_clone(Image *i, const char *new_path, bool read_only) {
|
||||
int r;
|
||||
|
||||
assert(i);
|
||||
@@ -1741,7 +1741,15 @@ static int unprivileged_clone(Image *i, const char *new_path) {
|
||||
link = sd_varlink_unref(link);
|
||||
|
||||
/* Fork off child that moves into userns and does the copying */
|
||||
return copy_tree_at_foreign(tree_fd, target_fd, userns_fd);
|
||||
r = copy_tree_at_foreign(tree_fd, target_fd, userns_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Match the best-effort immutable bit handling of ordinary directory clones. */
|
||||
if (read_only)
|
||||
(void) chattr_fd(new_fd, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int image_clone(Image *i, const char *new_name, bool read_only, RuntimeScope scope) {
|
||||
@@ -1784,7 +1792,7 @@ int image_clone(Image *i, const char *new_name, bool read_only, RuntimeScope sco
|
||||
return r;
|
||||
|
||||
if (i->foreign_uid_owned)
|
||||
r = unprivileged_clone(i, new_path);
|
||||
r = unprivileged_clone(i, new_path, read_only);
|
||||
else {
|
||||
r = btrfs_subvol_snapshot_at(
|
||||
AT_FDCWD, i->path,
|
||||
|
||||
@@ -181,6 +181,24 @@ if lsattr -d /var/lib/machines >/dev/null; then
|
||||
[[ "$(machinectl show-image --property=ReadOnly --value clone2)" == yes ]]
|
||||
machinectl read-only clone2 no
|
||||
[[ "$(machinectl show-image --property=ReadOnly --value clone2)" == no ]]
|
||||
|
||||
# Foreign-UID cloning also needs mountfsd/nsresourced and user namespace delegation.
|
||||
if can_do_rootless_nspawn; then
|
||||
mkdir /var/lib/machines/foreign-source
|
||||
echo "foreign clone contents" >/var/lib/machines/foreign-source/payload
|
||||
chown foreign-0:foreign-0 /var/lib/machines/foreign-source{,/payload}
|
||||
machinectl clone --read-only foreign-source foreign-clone
|
||||
[[ "$(machinectl show-image --property=ReadOnly --value foreign-clone)" == yes ]]
|
||||
cmp /var/lib/machines/foreign-source/payload /var/lib/machines/foreign-clone/payload
|
||||
assert_eq "$(stat -c %u:%g /var/lib/machines/foreign-source)" \
|
||||
"$(stat -c %u:%g /var/lib/machines/foreign-clone)"
|
||||
assert_eq "$(stat -c %u:%g /var/lib/machines/foreign-source/payload)" \
|
||||
"$(stat -c %u:%g /var/lib/machines/foreign-clone/payload)"
|
||||
(! touch /var/lib/machines/foreign-clone/new-file)
|
||||
machinectl remove foreign-clone foreign-source
|
||||
else
|
||||
echo "Skipping foreign-UID read-only clone test: rootless nspawn prerequisites not met"
|
||||
fi
|
||||
fi
|
||||
machinectl remove clone2
|
||||
for i in {0..4}; do
|
||||
|
||||
Reference in New Issue
Block a user