summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/fs/fsutil/handle_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/sentry/fs/fsutil/handle_test.go')
-rw-r--r--pkg/sentry/fs/fsutil/handle_test.go227
1 files changed, 227 insertions, 0 deletions
diff --git a/pkg/sentry/fs/fsutil/handle_test.go b/pkg/sentry/fs/fsutil/handle_test.go
new file mode 100644
index 000000000..d94c3eb0d
--- /dev/null
+++ b/pkg/sentry/fs/fsutil/handle_test.go
@@ -0,0 +1,227 @@
+// Copyright 2018 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fsutil_test
+
+import (
+ "io"
+ "syscall"
+ "testing"
+
+ "gvisor.googlesource.com/gvisor/pkg/sentry/context"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/context/contexttest"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/fs"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/fs/fsutil"
+ ramfstest "gvisor.googlesource.com/gvisor/pkg/sentry/fs/ramfs/test"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
+)
+
+type testInodeOperations struct {
+ fs.InodeOperations
+ fs.InodeType
+ FileSize int64
+ writes uint
+ reads uint
+}
+
+func (t *testInodeOperations) UnstableAttr(ctx context.Context, inode *fs.Inode) (fs.UnstableAttr, error) {
+ return fs.UnstableAttr{Size: t.FileSize}, nil
+}
+
+// Check implements InodeOperations.Check.
+func (t *testInodeOperations) Check(ctx context.Context, inode *fs.Inode, p fs.PermMask) bool {
+ return fs.ContextCanAccessFile(ctx, inode, p)
+}
+
+func (t *testInodeOperations) DeprecatedPreadv(ctx context.Context, dst usermem.IOSequence, offset int64) (int64, error) {
+ t.reads++
+ return t.InodeOperations.DeprecatedPreadv(ctx, dst, offset)
+}
+
+func (t *testInodeOperations) DeprecatedPwritev(ctx context.Context, src usermem.IOSequence, offset int64) (int64, error) {
+ t.writes++
+ return t.InodeOperations.DeprecatedPwritev(ctx, src, offset)
+}
+
+// testHandle returns a handle for a test node.
+//
+// The size of the node is fixed at 20 bytes.
+func testHandle(t *testing.T, flags fs.FileFlags, nt fs.InodeType) (*fs.File, *testInodeOperations) {
+ ctx := contexttest.Context(t)
+ m := fs.NewNonCachingMountSource(nil, fs.MountSourceFlags{})
+ n := &testInodeOperations{
+ InodeOperations: ramfstest.NewFile(ctx, fs.FilePermissions{User: fs.PermMask{Read: true, Write: true}}),
+ FileSize: 20,
+ }
+ d := fs.NewDirent(fs.NewInode(n, m, fs.StableAttr{Type: nt}), "test")
+ return fsutil.NewHandle(ctx, d, flags, d.Inode.HandleOps()), n
+}
+
+func TestHandleOps(t *testing.T) {
+ h, n := testHandle(t, fs.FileFlags{Read: true, Write: true}, fs.RegularFile)
+ defer h.DecRef()
+
+ // Make sure a write request works.
+ if n, err := h.Writev(contexttest.Context(t), usermem.BytesIOSequence([]byte("a"))); n != 1 || err != nil {
+ t.Fatalf("Writev: got (%d, %v), wanted (1, nil)", n, err)
+ }
+ if n.writes != 1 {
+ t.Errorf("found %d writes, expected 1", n.writes)
+ }
+
+ // Make sure a read request works.
+ dst := make([]byte, 1)
+ if n, err := h.Preadv(contexttest.Context(t), usermem.BytesIOSequence(dst), 0); n != 1 || (err != nil && err != io.EOF) {
+ t.Errorf("Preadv: got (%d, %v), wanted (1, nil or EOF)", n, err)
+ }
+ if dst[0] != 'a' {
+ t.Errorf("Preadv: read %q, wanted 'a'", dst[0])
+ }
+ if n.reads != 1 {
+ t.Errorf("found %d reads, expected 1", n.reads)
+ }
+}
+
+type seekTest struct {
+ whence fs.SeekWhence
+ offset int64
+ result int64
+ err error
+}
+
+type seekSuite struct {
+ nodeType fs.InodeType
+ cases []seekTest
+}
+
+// FIXME: This is currently missing fs.SeekEnd tests due to the
+// fact that NullInodeOperations returns an error on stat.
+func TestHandleSeek(t *testing.T) {
+ ts := []seekSuite{
+ {
+ nodeType: fs.RegularFile,
+ cases: []seekTest{
+ {fs.SeekSet, 0, 0, nil},
+ {fs.SeekSet, 10, 10, nil},
+ {fs.SeekSet, -5, 10, syscall.EINVAL},
+ {fs.SeekCurrent, -1, 9, nil},
+ {fs.SeekCurrent, 2, 11, nil},
+ {fs.SeekCurrent, -12, 11, syscall.EINVAL},
+ {fs.SeekEnd, -1, 19, nil},
+ {fs.SeekEnd, 0, 20, nil},
+ {fs.SeekEnd, 2, 22, nil},
+ },
+ },
+ {
+ nodeType: fs.Directory,
+ cases: []seekTest{
+ {fs.SeekSet, 0, 0, nil},
+ {fs.SeekSet, 10, 0, syscall.EINVAL},
+ {fs.SeekSet, -5, 0, syscall.EINVAL},
+ {fs.SeekCurrent, 0, 0, nil},
+ {fs.SeekCurrent, 11, 0, syscall.EINVAL},
+ {fs.SeekCurrent, -6, 0, syscall.EINVAL},
+ {fs.SeekEnd, 0, 0, syscall.EINVAL},
+ {fs.SeekEnd, -1, 0, syscall.EINVAL},
+ {fs.SeekEnd, 2, 0, syscall.EINVAL},
+ },
+ },
+ {
+ nodeType: fs.Symlink,
+ cases: []seekTest{
+ {fs.SeekSet, 5, 0, syscall.EINVAL},
+ {fs.SeekSet, -5, 0, syscall.EINVAL},
+ {fs.SeekSet, 0, 0, syscall.EINVAL},
+ {fs.SeekCurrent, 5, 0, syscall.EINVAL},
+ {fs.SeekCurrent, -5, 0, syscall.EINVAL},
+ {fs.SeekCurrent, 0, 0, syscall.EINVAL},
+ {fs.SeekEnd, 5, 0, syscall.EINVAL},
+ {fs.SeekEnd, -5, 0, syscall.EINVAL},
+ {fs.SeekEnd, 0, 0, syscall.EINVAL},
+ },
+ },
+ {
+ nodeType: fs.Pipe,
+ cases: []seekTest{
+ {fs.SeekSet, 5, 0, syscall.ESPIPE},
+ {fs.SeekSet, -5, 0, syscall.ESPIPE},
+ {fs.SeekSet, 0, 0, syscall.ESPIPE},
+ {fs.SeekCurrent, 5, 0, syscall.ESPIPE},
+ {fs.SeekCurrent, -5, 0, syscall.ESPIPE},
+ {fs.SeekCurrent, 0, 0, syscall.ESPIPE},
+ {fs.SeekEnd, 5, 0, syscall.ESPIPE},
+ {fs.SeekEnd, -5, 0, syscall.ESPIPE},
+ {fs.SeekEnd, 0, 0, syscall.ESPIPE},
+ },
+ },
+ {
+ nodeType: fs.Socket,
+ cases: []seekTest{
+ {fs.SeekSet, 5, 0, syscall.ESPIPE},
+ {fs.SeekSet, -5, 0, syscall.ESPIPE},
+ {fs.SeekSet, 0, 0, syscall.ESPIPE},
+ {fs.SeekCurrent, 5, 0, syscall.ESPIPE},
+ {fs.SeekCurrent, -5, 0, syscall.ESPIPE},
+ {fs.SeekCurrent, 0, 0, syscall.ESPIPE},
+ {fs.SeekEnd, 5, 0, syscall.ESPIPE},
+ {fs.SeekEnd, -5, 0, syscall.ESPIPE},
+ {fs.SeekEnd, 0, 0, syscall.ESPIPE},
+ },
+ },
+ {
+ nodeType: fs.CharacterDevice,
+ cases: []seekTest{
+ {fs.SeekSet, 5, 0, nil},
+ {fs.SeekSet, -5, 0, nil},
+ {fs.SeekSet, 0, 0, nil},
+ {fs.SeekCurrent, 5, 0, nil},
+ {fs.SeekCurrent, -5, 0, nil},
+ {fs.SeekCurrent, 0, 0, nil},
+ {fs.SeekEnd, 5, 0, nil},
+ {fs.SeekEnd, -5, 0, nil},
+ {fs.SeekEnd, 0, 0, nil},
+ },
+ },
+ {
+ nodeType: fs.BlockDevice,
+ cases: []seekTest{
+ {fs.SeekSet, 0, 0, nil},
+ {fs.SeekSet, 10, 10, nil},
+ {fs.SeekSet, -5, 10, syscall.EINVAL},
+ {fs.SeekCurrent, -1, 9, nil},
+ {fs.SeekCurrent, 2, 11, nil},
+ {fs.SeekCurrent, -12, 11, syscall.EINVAL},
+ {fs.SeekEnd, -1, 19, nil},
+ {fs.SeekEnd, 0, 20, nil},
+ {fs.SeekEnd, 2, 22, nil},
+ },
+ },
+ }
+
+ for _, s := range ts {
+ h, _ := testHandle(t, fs.FileFlags{Read: true, Write: true}, s.nodeType)
+ defer h.DecRef()
+
+ for _, c := range s.cases {
+ // Try the given seek.
+ offset, err := h.Seek(contexttest.Context(t), c.whence, c.offset)
+ if err != c.err {
+ t.Errorf("seek(%s, %d) on %s had unexpected error: expected %v, got %v", c.whence, c.offset, s.nodeType, c.err, err)
+ }
+ if err == nil && offset != c.result {
+ t.Errorf("seek(%s, %d) on %s had bad result: expected %v, got %v", c.whence, c.offset, s.nodeType, c.result, offset)
+ }
+ }
+ }
+}