Skip to content

guest: pre-create nested mount points under read-only mounts - #2848

Open
Jie Chen (jiechen0826) wants to merge 1 commit into
microsoft:mainfrom
jiechen0826:lcow-nested-ro-mount
Open

guest: pre-create nested mount points under read-only mounts#2848
Jie Chen (jiechen0826) wants to merge 1 commit into
microsoft:mainfrom
jiechen0826:lcow-nested-ro-mount

Conversation

@jiechen0826

Copy link
Copy Markdown
Contributor

Problem

Containers fail to start with read-only file system (EROFS) when a volume is mounted into a subdirectory of another volume that is mounted read-only. For example:

  • an emptyDir at /mnt/data (readOnly) plus another volume at /mnt/data/subdir
  • a read-only configMap at /etc/coredns plus a custom config volume at /etc/coredns/custom

Root cause

runc applies spec.Mounts in order and remounts a bind mount read-only as soon as it processes it. When it then processes a child mount nested under it, runc must create the child's mount point under the now read-only parent, which fails with EROFS.

On a normal Kubernetes node the kubelet pre-creates these subdirectories on the host before handing the spec to the runtime. Inside an LCOW UVM the guest (GCS) owns the mount setup, so runc creates the mount point at runc create time and hits the read-only parent.

Reordering the mounts does not help: mounting the child before the parent makes the parent bind mount shadow the child.

Fix

ensureNestedMountTargets (new internal/guest/runtime/hcsv2/mount.go), called from Host.CreateContainer just before the spec is written for runc. For each mount whose destination is nested under a read-only bind mount, it pre-creates the mount point inside that parent's writable source (a directory, or an empty file when the child source is a file), so it already exists once runc makes the parent read-only. This mirrors what the kubelet does on a host node.

It is best effort: if a mount point cannot be pre-created it logs and continues, so runc's own error still surfaces and unaffected containers are unchanged.

Testing

  • Unit tests for the parent-selection and path logic.
  • Root-gated functional tests using real bind mounts: one reproduces the EROFS failure (control), and one verifies the fix makes the nested mount point usable under a read-only parent.

runc applies the OCI mounts in spec order and remounts a bind mount read-only as soon as it processes it. When it then processes a child mount whose destination is nested inside that read-only mount and the mount point does not already exist, runc must create it under the now read-only parent and fails with EROFS. This breaks valid configurations where a read-only volume has another volume mounted into a subdirectory of it, for example a read-only /etc/coredns configMap with a custom config volume at /etc/coredns/custom.

On a regular Kubernetes node the kubelet creates these subdirectories on the host before handing the spec to the runtime. Inside an LCOW UVM the guest owns the mount setup, so do the equivalent: for each nested mount, create the mount point inside the writable source of its read-only parent before the spec is handed to runc, so it already exists once the parent is made read-only. This is best effort; if a mount point cannot be pre-created it is logged and skipped so runc's own error still surfaces.

Signed-off-by: Jie Chen <jiechen3@microsoft.com>
@jiechen0826
Jie Chen (jiechen0826) marked this pull request as ready for review July 31, 2026 23:24
@jiechen0826
Jie Chen (jiechen0826) requested a review from a team as a code owner July 31, 2026 23:24
@rawahars

Copy link
Copy Markdown
Contributor

Which type of parent mounts are targeted here?
There can be SCSI VHD mounts which are attached in RO config to the UVM. If nested mounts are created under the same then that can potentially fail.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants