Skip to content

A hard link can be made inside a bind mount or named volume - #35

Merged
chandarith-chea merged 2 commits into
mainfrom
mount-hardlink
Nov 14, 2026
Merged

chandarith-chea merged 2 commits into
mainfrom
mount-hardlink

Conversation

@chandarith-chea

Copy link
Copy Markdown
Contributor

Problem

On Dockiva 0.6.10:

docker --context dockiva run --rm -v somevol:/v alpine sh -c 'echo x > /v/a; ln /v/a /v/b'
ln: /v/b: No such file or directory

Linux makes the link. The same call in the container's own layer worked.

Cause

linkat in native/fs.h refused a source the walk found in neither image layer (old.layer==2) with ENOENT before asking the host. For a leaf inside a bind mount, ov_walk leaves ov_leaf.layer at 2 and ov_leaf.canon empty, because the layers do not apply there. So every source in a mount counted as missing whenever the container had image layers.

Fix

One condition: the check applies only when old.canon[0] is set (a leaf in the layers). In a mount the host's linkat reports a missing source. The neighbouring ov_prepare and ov_created calls already return early for an empty canon, and EXDEV across a mount boundary is decided earlier from the two paths' mounts, unchanged.

Test

tests/native/mountlink_guest.c, run by scripts/verify_overlay.py on every built runtime, with and without the memory layer (--lower, --root, --bind at /vol and /vol2):

  • a link inside the mount shares the source's inode (absolute path, descriptor of the mount's directory, symlink with AT_SYMLINK_FOLLOW)
  • missing source is ENOENT, taken name is EEXIST
  • a link to another mount, or to or from the layers (image file, new file), is EXDEV
  • an image file is still linked in the layers (copy-up); a missing layer source is still ENOENT
  • the verifier then checks the host directories: one inode under three names, nothing in the second mount

On origin/main the guest fails at the first in-mount link on all 8 runs.

Checks run locally

  • scripts/verify_overlay.py: 53/53 (4 runtime builds)
  • scripts/verify_fork_sockets.py: 100/100, twice. One earlier run failed hvf sleeper_guest --cpus 2 (a latency check) with the Mac at load 13; standalone it passes 6/6 on this branch and on origin/main.

Not checked: a bundle built from this branch with the docker run -v command above.

Note for #34

tests/native/rootdac_guest.c there makes hard links only in /w because of this bug. This branch touches scripts/verify_overlay.py at different places than #34 (no textual overlap expected).

docker run -v vol:/v alpine sh -c 'echo x > /v/a; ln /v/a /v/b' printed
"ln: /v/b: No such file or directory" (Dockiva 0.6.10); on Linux it works, and
so did the same link in the container's own layer.

linkat refused a source the walk found in neither image layer (layer 2) with
ENOENT before asking the host. For a leaf inside a bind mount the walk leaves
the layer at 2 and the canonical path empty: the layers do not apply there. So
every source in a mount counted as missing whenever the container had image
layers. The check now applies only to a leaf in the layers; in a mount the
host's linkat reports a missing source. Links across a mount's boundary are
EXDEV as before (decided earlier, by the mounts of the two paths).

tests/native/mountlink_guest.c (scripts/verify_overlay.py, every built
runtime, with and without the memory layer): a link inside a mount by absolute
path, by a descriptor of the mount's directory and through a symlink with
AT_SYMLINK_FOLLOW shares the source's inode; a missing source is ENOENT, a
taken name EEXIST; a link to another mount, or to or from the layers, is EXDEV;
links of image files still copy up. The verifier then checks the host's
directories: one inode under three names, nothing in the other mount.
@chandarith-chea
chandarith-chea merged commit 62efd52 into main Nov 14, 2026
2 checks passed
@chandarith-chea
chandarith-chea deleted the mount-hardlink branch November 14, 2026 09:04
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.

1 participant