snapshotter/erofs: avoid using overlay if fsmerge is enabled and no upperdir

If fsmerge is enabled and no write is needed, it can return an overlay
mount with a single lowerdir, which is illegal for overlayfs.

For example, it can cause the following Nerdctl error:
 : I'm not sure why ctr works, but the issue is real.

```bash
$ nerdctl run --runtime io.containerd.kata.v2 --snapshotter=erofs -it --rm nginx:latest /bin/bash
FATA[0000] failed to mount {Type:overlay Source:overlay Target: Options:[lowerdir=/run/containerd/
io.containerd.mount-manager.v1.bolt/t/7/1]} on "/tmp/initialC2039543827": mount source: "overlay",
target: "/tmp/initialC2039543827", fstype: overlay, flags: 0, data: "lowerdir=/run/containerd/io.
containerd.mount-manager.v1.bolt/t/7/1", err: invalid argument
```

Switch to using a bind mount instead.

Fixes: 9a7500a974 ("Add support for EROFS fsmerge feature")
Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
This commit is contained in:
Gao Xiang
2026-04-13 17:26:08 +08:00
parent 997f813b5c
commit 3b357da496

View File

@@ -389,7 +389,10 @@ func (s *snapshotter) mounts(snap storage.Snapshot, info snapshots.Info) ([]moun
}
}
var mounts []mount.Mount
var (
mounts []mount.Mount
writable bool
)
if snap.Kind == snapshots.KindActive {
if s.blockMode {
mounts = append(mounts, mount.Mount{
@@ -416,6 +419,7 @@ func (s *snapshotter) mounts(snap storage.Snapshot, info snapshots.Info) ([]moun
fmt.Sprintf("upperdir=%s", s.upperPath(snap.ID)),
)
}
writable = true
} else if len(snap.ParentIDs) == 1 {
layerBlob, err := s.lowerPath(snap.ParentIDs[0])
if err != nil {
@@ -452,11 +456,6 @@ func (s *snapshotter) mounts(snap storage.Snapshot, info snapshots.Info) ([]moun
mounts = append(mounts, m)
}
if (len(mounts) - first) == 1 {
options = append(options, fmt.Sprintf("lowerdir={{ mount %d }}", first))
} else {
options = append(options, fmt.Sprintf("lowerdir={{ overlay %d %d }}", first, len(mounts)-1))
}
if s.remapIDs {
if v, ok := info.Labels[snapshots.LabelSnapshotUIDMapping]; ok {
@@ -467,6 +466,20 @@ func (s *snapshotter) mounts(snap storage.Snapshot, info snapshots.Info) ([]moun
}
}
if (len(mounts) - first) == 1 {
// End up with one single lowerdir (e.g. fsmerge on):
// it's unsupported by overlayfs
if !writable {
return append(mounts, mount.Mount{
Type: "format/bind",
Source: fmt.Sprintf("{{ mount %d }}", first),
Options: append(options, "ro", "rbind"),
}), nil
}
options = append(options, fmt.Sprintf("lowerdir={{ mount %d }}", first))
} else {
options = append(options, fmt.Sprintf("lowerdir={{ overlay %d %d }}", first, len(mounts)-1))
}
options = append(options, s.ovlOptions...)
return append(mounts, mount.Mount{