diff options
author | Nicolas Lacasse <nlacasse@google.com> | 2018-08-10 17:15:27 -0700 |
---|---|---|
committer | Shentubot <shentubot@google.com> | 2018-08-10 17:16:38 -0700 |
commit | a2ec391dfbc5a03077b73078777a9347c372dece (patch) | |
tree | 037d59bfbb29c2bb4bee25678060be65139e8bb7 /pkg/sentry/fs/file_overlay_test.go | |
parent | ae6f092fe117a738df34e072ef5ba01a41c89222 (diff) |
fs: Allow overlays to revalidate files from the upper fs.
Previously, an overlay would panic if either the upper or lower fs required
revalidation for a given Dirent. Now, we allow revalidation from the upper
file, but not the lower.
If a cached overlay inode does need revalidation (because the upper needs
revalidation), then the entire overlay Inode will be discarded and a new
overlay Inode will be built with a fresh copy of the upper file.
As a side effect of this change, Revalidate must take an Inode instead of a
Dirent, since an overlay needs to revalidate individual Inodes.
PiperOrigin-RevId: 208293638
Change-Id: Ic8f8d1ffdc09114721745661a09522b54420c5f1
Diffstat (limited to 'pkg/sentry/fs/file_overlay_test.go')
-rw-r--r-- | pkg/sentry/fs/file_overlay_test.go | 70 |
1 files changed, 62 insertions, 8 deletions
diff --git a/pkg/sentry/fs/file_overlay_test.go b/pkg/sentry/fs/file_overlay_test.go index 407ba8562..38762d8a1 100644 --- a/pkg/sentry/fs/file_overlay_test.go +++ b/pkg/sentry/fs/file_overlay_test.go @@ -21,6 +21,7 @@ import ( "gvisor.googlesource.com/gvisor/pkg/sentry/context" "gvisor.googlesource.com/gvisor/pkg/sentry/context/contexttest" "gvisor.googlesource.com/gvisor/pkg/sentry/fs" + ramfstest "gvisor.googlesource.com/gvisor/pkg/sentry/fs/ramfs/test" ) func TestReaddir(t *testing.T) { @@ -48,7 +49,7 @@ func TestReaddir(t *testing.T) { {name: "a"}, {name: "b"}, }, nil), /* lower */ - ), + false /* revalidate */), names: []string{".", "..", "a", "b"}, }, { @@ -59,7 +60,7 @@ func TestReaddir(t *testing.T) { {name: "b"}, }, nil), /* upper */ nil, /* lower */ - ), + false /* revalidate */), names: []string{".", "..", "a", "b"}, }, { @@ -67,11 +68,11 @@ func TestReaddir(t *testing.T) { dir: fs.NewTestOverlayDir(ctx, newTestRamfsDir(ctx, []dirContent{ {name: "a"}, - }, nil), /* lower */ + }, nil), /* upper */ newTestRamfsDir(ctx, []dirContent{ {name: "b"}, }, nil), /* lower */ - ), + false /* revalidate */), names: []string{".", "..", "a", "b"}, }, { @@ -79,11 +80,11 @@ func TestReaddir(t *testing.T) { dir: fs.NewTestOverlayDir(ctx, newTestRamfsDir(ctx, []dirContent{ {name: "a"}, - }, []string{"b"}), /* lower */ + }, []string{"b"}), /* upper */ newTestRamfsDir(ctx, []dirContent{ {name: "c"}, }, nil), /* lower */ - ), + false /* revalidate */), names: []string{".", "..", "a", "c"}, }, { @@ -91,12 +92,12 @@ func TestReaddir(t *testing.T) { dir: fs.NewTestOverlayDir(ctx, newTestRamfsDir(ctx, []dirContent{ {name: "a"}, - }, []string{"b"}), /* lower */ + }, []string{"b"}), /* upper */ newTestRamfsDir(ctx, []dirContent{ {name: "b"}, /* will be masked */ {name: "c"}, }, nil), /* lower */ - ), + false /* revalidate */), names: []string{".", "..", "a", "c"}, }, } { @@ -120,6 +121,59 @@ func TestReaddir(t *testing.T) { } } +func TestReaddirRevalidation(t *testing.T) { + ctx := contexttest.Context(t) + ctx = &rootContext{ + Context: ctx, + root: fs.NewDirent(newTestRamfsDir(ctx, nil, nil), "root"), + } + + // Create an overlay with two directories, each with one file. + upper := newTestRamfsDir(ctx, []dirContent{{name: "a"}}, nil) + lower := newTestRamfsDir(ctx, []dirContent{{name: "b"}}, nil) + overlay := fs.NewTestOverlayDir(ctx, upper, lower, true /* revalidate */) + + // Get a handle to the dirent in the upper filesystem so that we can + // modify it without going through the dirent. + upperDir := upper.InodeOperations.(*dir).InodeOperations.(*ramfstest.Dir) + + // Check that overlay returns the files from both upper and lower. + openDir, err := overlay.GetFile(ctx, fs.NewDirent(overlay, "stub"), fs.FileFlags{Read: true}) + if err != nil { + t.Fatalf("GetFile got error %v, want nil", err) + } + ser := &fs.CollectEntriesSerializer{} + if err := openDir.Readdir(ctx, ser); err != nil { + t.Fatalf("Readdir got error %v, want nil", err) + } + got, want := ser.Order, []string{".", "..", "a", "b"} + if !reflect.DeepEqual(got, want) { + t.Errorf("Readdir got names %v, want %v", got, want) + } + + // Remove "a" from the upper and add "c". + if err := upperDir.Remove(ctx, upper, "a"); err != nil { + t.Fatalf("error removing child: %v", err) + } + upperDir.AddChild(ctx, "c", fs.NewInode(ramfstest.NewFile(ctx, fs.FilePermissions{}), + upper.MountSource, fs.StableAttr{Type: fs.RegularFile})) + + // Seek to beginning of the directory and do the readdir again. + if _, err := openDir.Seek(ctx, fs.SeekSet, 0); err != nil { + t.Fatalf("error seeking to beginning of dir: %v", err) + } + ser = &fs.CollectEntriesSerializer{} + if err := openDir.Readdir(ctx, ser); err != nil { + t.Fatalf("Readdir got error %v, want nil", err) + } + + // Readdir should return the updated children. + got, want = ser.Order, []string{".", "..", "b", "c"} + if !reflect.DeepEqual(got, want) { + t.Errorf("Readdir got names %v, want %v", got, want) + } +} + type rootContext struct { context.Context root *fs.Dirent |