From 7180d9a2d066c93cce009ce53db0eea48f6c5fba Mon Sep 17 00:00:00 2001 From: li-nkSN Date: Sat, 31 Jan 2026 20:44:23 -0800 Subject: [PATCH] zfs: add idmapped mount support for rootless containers Apply idmapped mounts to ZFS storage layers when UID/GID mappings are provided. This translates file ownership through the user namespace mapping, allowing containers to perform chown operations that appear as root inside the container but map to unprivileged UIDs on the host. The overlay and btrfs drivers already support idmapped mounts; this brings ZFS to feature parity for rootless container support. This works in conjunction with ZFS's zoned_uid property (proposed separately to OpenZFS) which delegates dataset operations to user namespaces, enabling full rootless podman/docker with native ZFS storage. Signed-off-by: Colin K. Williams --- storage/drivers/zfs/zfs.go | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/storage/drivers/zfs/zfs.go b/storage/drivers/zfs/zfs.go index b994278bb2..2c94409576 100644 --- a/storage/drivers/zfs/zfs.go +++ b/storage/drivers/zfs/zfs.go @@ -18,6 +18,7 @@ import ( graphdriver "go.podman.io/storage/drivers" "go.podman.io/storage/internal/tempdir" "go.podman.io/storage/pkg/directory" + "go.podman.io/storage/pkg/idmap" "go.podman.io/storage/pkg/idtools" "go.podman.io/storage/pkg/mount" "go.podman.io/storage/pkg/parsers" @@ -470,6 +471,19 @@ func (d *Driver) Get(id string, options graphdriver.MountOpts) (_ string, retErr } } + // Apply idmapped mount if UID/GID mappings are explicitly provided + // This enables rootless container support by translating UIDs/GIDs + if len(options.UidMaps) > 0 || len(options.GidMaps) > 0 { + logrus.WithField("storage-driver", "zfs").Debugf("applying idmapped mount to %s with %d uid maps, %d gid maps", mountpoint, len(options.UidMaps), len(options.GidMaps)) + if err := applyIDMappedMount(mountpoint, options.UidMaps, options.GidMaps); err != nil { + logrus.WithField("storage-driver", "zfs").Warnf("failed to apply idmapped mount to %s: %v", mountpoint, err) + if unmountErr := detachUnmount(mountpoint); unmountErr != nil { + logrus.WithField("storage-driver", "zfs").Warnf("failed to unmount %s after idmap failure: %v", mountpoint, unmountErr) + } + return "", fmt.Errorf("applying idmapped mount for zfs: %w", err) + } + } + return mountpoint, nil } @@ -494,6 +508,24 @@ func (d *Driver) Put(id string) error { return nil } +// applyIDMappedMount creates an idmapped mount over the ZFS mount to translate UIDs/GIDs +// for rootless container support. This uses the Linux mount_setattr() syscall with +// MOUNT_ATTR_IDMAP to create a view of the filesystem with translated ownership. +func applyIDMappedMount(mountpoint string, uidMaps, gidMaps []idtools.IDMap) error { + // Create a user namespace process with the desired UID/GID mappings + pid, cleanupFunc, err := idmap.CreateUsernsProcess(uidMaps, gidMaps) + if err != nil { + return fmt.Errorf("creating user namespace process for idmap: %w", err) + } + defer cleanupFunc() + + // Apply the idmapped mount using the user namespace + if err := idmap.CreateIDMappedMount(mountpoint, mountpoint, pid); err != nil { + return fmt.Errorf("creating idmapped mount at %s: %w", mountpoint, err) + } + return nil +} + // ReadWriteDiskUsage returns the disk usage of the writable directory for the ID. // For ZFS, it queries the full mount path for this ID. func (d *Driver) ReadWriteDiskUsage(id string) (*directory.DiskUsage, error) {