Skip to content

overlay: a metacopy=on probe failure makes the driver unusable, even though metacopy is never supported for unprivileged mounts #1053

Description

@michelemodolo

Description

On a host where unprivileged overlay mounts are permitted but metacopy=on is not, the
overlay graphdriver refuses to initialise at all, even though it would work perfectly with
metacopy disabled.

This is the normal state of affairs inside an unprivileged user namespace: metacopy records
copy-up metadata in trusted.overlay.* extended attributes, and an unprivileged mount cannot
set trusted.* xattrs, so the kernel rejects metacopy=on. A rootless container engine
therefore can never use metacopy — which the driver already knows how to cope with, since
metacopy is optional and doesMetacopy() returning false is a perfectly good answer.

The bug is that a probe failure is treated as fatal rather than as "unsupported".

Steps to reproduce

Any environment where an unprivileged overlay mount succeeds but metacopy=on is denied. Ours
is a Kubernetes/OpenShift pod under a restricted SCC (SELinux type container_t, all
capabilities dropped at the container level, no subuid/subgid ranges so buildah's own user
namespace maps a single ID):

# storage root must be on a non-overlayfs filesystem (the kernel refuses an overlay
# upperdir on overlay); in our pods only the /builds emptyDir (xfs) qualifies.
buildah --root /builds/store/root --runroot /builds/store/run \
        --storage-driver overlay --storage-opt overlay.ignore_chown_errors=true \
        info

Actual result

Error: failed to mount overlay for metacopy check with "" options: permission denied

The driver is unusable. Every subsequent build fails.

Crucially, the runroot shows that the basic overlay mount had already succeeded:

$ ls /builds/store/run/overlay/
overlay-true

checkAndRecordOverlaySupport() runs before the metacopy check, performs a real overlay
mount, and records overlay-true. Reaching the metacopy error therefore proves that
unprivileged overlay works in this environment — only the metacopy=on variant is refused.

Expected result

doesMetacopy() returning EPERM should be recorded as metacopy unsupported and the
driver should initialise with metacopy off, exactly as it does on hosts where the probe
returns a clean negative.

Analysis

In drivers/overlay/overlay.go, Init():

supportsDType, err = checkAndRecordOverlaySupport(home, runhome)   // real overlay mount
if err != nil {
    return nil, err                                               // different error text
}
feature := fmt.Sprintf("metacopy(%s)", opts.mountOptions)
metacopyCacheResult, _, err := cachedFeatureCheck(runhome, feature)

The first check is fatal-on-failure and produces a distinct message
(kernel does not support overlay fs / 'overlay' is not supported over %s). Because we see
the metacopy message instead, the basic mount succeeded. The metacopy probe's error is then
propagated out of Init() rather than being interpreted.

Evidence that this is a probe-classification problem and not an environment problem: on an
ordinary developer workstation (no SELinux, rootless podman), the same probe yields a clean
negative and the cache file metacopy()-false is written — overlay then works normally, with
metacopy off. The pod differs only in that the refusal surfaces as EPERM rather than as an
unsupported result.

Suggested fix

Treat a permission error from the metacopy probe as false rather than as a hard error —
i.e. record the negative result and continue, in the same way a clean negative is handled.
EPERM from metacopy=on is expected and unremarkable for any unprivileged mount, so it
carries no information beyond "metacopy is not available here".

Workaround (in use)

The probe result is cached in the runroot as a zero-length file named
<feature>-<result>, and cachedFeatureCheck() short-circuits when it is present. Seeding it
before the first buildah call skips the failing probe entirely:

mkdir -p "$RUNROOT/overlay"
: > "$RUNROOT/overlay/metacopy()-false"

The parentheses are literal and the argument is empty because mountOptions is empty; setting
--storage-opt overlay.mountopt=<opts> changes the key to metacopy(<opts>) and the probe
runs again, so that is not an alternative workaround.

This is an undocumented internal and we would rather not depend on it — hence this report.

Version

buildah v1.43.1 (vendoring go.podman.io/storage v1.62.0), compiled from the upstream
release tarball. Node kernel 5.14.0-570.117.1.el9_6 (RHCOS), UBI9 runner image.

Environment

  • GitLab Runner Kubernetes executor on OpenShift, restricted SCC.
  • Pod: container_t SELinux type (unchanged inside buildah's own user namespace), container
    CapEff 0000000000000000, buildah's namespace single-ID (uid_map: 0 1000 1) with
    /proc/self/setgroups = deny.
  • --isolation chroot, --format docker, overlay.ignore_chown_errors=true.
  • Storage root on an xfs emptyDir; the container rootfs, /tmp and /var/tmp are all
    overlayfs, so the store must be relocated for overlay to be legal at all.
  • /dev/fuse absent, so fuse-overlayfs is not an alternative.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions