diff options
Diffstat (limited to 'pkg/sentry/fs/file_overlay_test.go')
-rw-r--r-- | pkg/sentry/fs/file_overlay_test.go | 83 |
1 files changed, 83 insertions, 0 deletions
diff --git a/pkg/sentry/fs/file_overlay_test.go b/pkg/sentry/fs/file_overlay_test.go index 38762d8a1..830458ff9 100644 --- a/pkg/sentry/fs/file_overlay_test.go +++ b/pkg/sentry/fs/file_overlay_test.go @@ -174,6 +174,89 @@ func TestReaddirRevalidation(t *testing.T) { } } +// TestReaddirOverlayFrozen tests that calling Readdir on an overlay file with +// a frozen dirent tree does not make Readdir calls to the underlying files. +func TestReaddirOverlayFrozen(t *testing.T) { + ctx := contexttest.Context(t) + + // Create an overlay with two directories, each with two files. + upper := newTestRamfsDir(ctx, []dirContent{{name: "upper-file1"}, {name: "upper-file2"}}, nil) + lower := newTestRamfsDir(ctx, []dirContent{{name: "lower-file1"}, {name: "lower-file2"}}, nil) + overlayInode := fs.NewTestOverlayDir(ctx, upper, lower, false) + + // Set that overlay as the root. + root := fs.NewDirent(overlayInode, "root") + ctx = &rootContext{ + Context: ctx, + root: root, + } + + // Check that calling Readdir on the root now returns all 4 files (2 + // from each layer in the overlay). + rootFile, err := root.Inode.GetFile(ctx, root, fs.FileFlags{Read: true}) + if err != nil { + t.Fatalf("root.Inode.GetFile failed: %v", err) + } + defer rootFile.DecRef() + ser := &fs.CollectEntriesSerializer{} + if err := rootFile.Readdir(ctx, ser); err != nil { + t.Fatalf("rootFile.Readdir failed: %v", err) + } + if got, want := ser.Order, []string{".", "..", "lower-file1", "lower-file2", "upper-file1", "upper-file2"}; !reflect.DeepEqual(got, want) { + t.Errorf("Readdir got names %v, want %v", got, want) + } + + // Readdir should have been called on upper and lower. + upperDir := upper.InodeOperations.(*dir) + lowerDir := lower.InodeOperations.(*dir) + if !upperDir.ReaddirCalled { + t.Errorf("upperDir.ReaddirCalled got %v, want true", upperDir.ReaddirCalled) + } + if !lowerDir.ReaddirCalled { + t.Errorf("lowerDir.ReaddirCalled got %v, want true", lowerDir.ReaddirCalled) + } + + // Reset. + upperDir.ReaddirCalled = false + lowerDir.ReaddirCalled = false + + // Take references on "upper-file1" and "lower-file1", pinning them in + // the dirent tree. + for _, name := range []string{"upper-file1", "lower-file1"} { + if _, err := root.Walk(ctx, root, name); err != nil { + t.Fatalf("root.Walk(%q) failed: %v", name, err) + } + // Don't drop a reference on the returned dirent so that it + // will stay in the tree. + } + + // Freeze the dirent tree. + root.Freeze() + + // Seek back to the beginning of the file. + if _, err := rootFile.Seek(ctx, fs.SeekSet, 0); err != nil { + t.Fatalf("error seeking to beginning of directory: %v", err) + } + + // Calling Readdir on the root now will return only the pinned + // children. + ser = &fs.CollectEntriesSerializer{} + if err := rootFile.Readdir(ctx, ser); err != nil { + t.Fatalf("rootFile.Readdir failed: %v", err) + } + if got, want := ser.Order, []string{".", "..", "lower-file1", "upper-file1"}; !reflect.DeepEqual(got, want) { + t.Errorf("Readdir got names %v, want %v", got, want) + } + + // Readdir should NOT have been called on upper or lower. + if upperDir.ReaddirCalled { + t.Errorf("upperDir.ReaddirCalled got %v, want false", upperDir.ReaddirCalled) + } + if lowerDir.ReaddirCalled { + t.Errorf("lowerDir.ReaddirCalled got %v, want false", lowerDir.ReaddirCalled) + } +} + type rootContext struct { context.Context root *fs.Dirent |