Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion plugins/snapshots/erofs/erofs.go
Original file line number Diff line number Diff line change
Expand Up @@ -521,13 +521,14 @@ func (s *snapshotter) mounts(snap storage.Snapshot, info snapshots.Info) ([]moun
return []mount.Mount{m}, nil
}

// first marks the start of the lowerdir range. A merged fsmeta ends the
// range but never moves its start: lowers stacked above it stay in range.
first := len(mounts)
for i := range snap.ParentIDs {
// If a merged fsmeta is valid for this layer, skip the remaining bottom layers.
// Why? Because bottom layers have been flattened with the thin fsmeta.
if m, ok := s.mountFsMeta(snap, i); ok {
mounts = append(mounts, m)
first = len(mounts) - 1
break
}

Expand Down
82 changes: 82 additions & 0 deletions plugins/snapshots/erofs/erofs_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,88 @@ func TestMountFsMeta(t *testing.T) {
})
}

// TestMountsWithMergedFsMeta covers s.mounts()'s assembly of the overlay
// lowerdir when a merged fsmeta is present on a parent below the top of the
// chain, i.e. one or more plain (non-merged) layers are stacked on top of a
// merged fsmeta lower.
func TestMountsWithMergedFsMeta(t *testing.T) {
root := t.TempDir()
s := &snapshotter{root: root}

// Chain (top to bottom): p0, p1, p2, p3. A merged fsmeta is only
// present for p2, flattening the sub-chain [p2, p3]. p0 and p1 are
// plain layers stacked on top of that merged base.
parents := []string{"p0", "p1", "p2", "p3"}
for _, id := range parents {
require.NoError(t, os.MkdirAll(filepath.Join(root, "snapshots", id), 0755))
require.NoError(t, os.WriteFile(s.layerBlobPath(id), []byte("layer"), 0644))
}
require.NoError(t, os.WriteFile(s.fsMetaPath("p2"), []byte("merged"), 0644))

snap := storage.Snapshot{Kind: snapshots.KindView, ParentIDs: parents}
info := snapshots.Info{}

mounts, err := s.mounts(snap, info)
require.NoError(t, err)

// Expect: [erofs(p0), erofs(p1), erofs(fsmeta p2, device=p3,p2), overlay]
require.Len(t, mounts, 4)

assert.Equal(t, "erofs", mounts[0].Type)
assert.Equal(t, s.layerBlobPath("p0"), mounts[0].Source)

assert.Equal(t, "erofs", mounts[1].Type)
assert.Equal(t, s.layerBlobPath("p1"), mounts[1].Source)

assert.Equal(t, "erofs", mounts[2].Type)
assert.Equal(t, s.fsMetaPath("p2"), mounts[2].Source)
assert.Equal(t, []string{
"ro", "loop",
"device=" + s.layerBlobPath("p3"),
"device=" + s.layerBlobPath("p2"),
}, mounts[2].Options)

// The overlay must span all three lowers (indices 0-2): the two plain
// top layers plus the merged fsmeta.
overlay := mounts[3]
assert.Equal(t, "format/mkdir/overlay", overlay.Type)
assert.Contains(t, overlay.Options, "lowerdir={{ overlay 0 2 }}")
}

// TestMountsWithMergedFsMetaOnTopParent covers the case where the merged
// fsmeta is valid for the topmost parent, so it is the only lower. Since
// overlayfs rejects a lowerdir with no upperdir, this collapses to a plain
// bind mount instead.
func TestMountsWithMergedFsMetaOnTopParent(t *testing.T) {
root := t.TempDir()
s := &snapshotter{root: root}

parents := []string{"p0", "p1"}
for _, id := range parents {
require.NoError(t, os.MkdirAll(filepath.Join(root, "snapshots", id), 0755))
require.NoError(t, os.WriteFile(s.layerBlobPath(id), []byte("layer"), 0644))
}
require.NoError(t, os.WriteFile(s.fsMetaPath("p0"), []byte("merged"), 0644))

snap := storage.Snapshot{Kind: snapshots.KindView, ParentIDs: parents}
info := snapshots.Info{}

mounts, err := s.mounts(snap, info)
require.NoError(t, err)

require.Len(t, mounts, 2)
assert.Equal(t, "erofs", mounts[0].Type)
assert.Equal(t, s.fsMetaPath("p0"), mounts[0].Source)
assert.Equal(t, []string{
"ro", "loop",
"device=" + s.layerBlobPath("p1"),
"device=" + s.layerBlobPath("p0"),
}, mounts[0].Options)

assert.Equal(t, "format/bind", mounts[1].Type)
assert.Equal(t, "{{ mount 0 }}", mounts[1].Source)
}

// --- layer content cache tests ---

const (
Expand Down
Loading