github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/vfs/file_description_impl_util_test.go (about)

     1  // Copyright 2019 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package vfs
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"io"
    21  	"sync/atomic"
    22  	"testing"
    23  
    24  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    25  	"github.com/SagerNet/gvisor/pkg/context"
    26  	"github.com/SagerNet/gvisor/pkg/errors/linuxerr"
    27  	"github.com/SagerNet/gvisor/pkg/sentry/contexttest"
    28  	"github.com/SagerNet/gvisor/pkg/syserror"
    29  	"github.com/SagerNet/gvisor/pkg/usermem"
    30  )
    31  
    32  // fileDescription is the common fd struct which a filesystem implementation
    33  // embeds in all of its file description implementations as required.
    34  type fileDescription struct {
    35  	vfsfd FileDescription
    36  	FileDescriptionDefaultImpl
    37  	NoLockFD
    38  }
    39  
    40  // genCount contains the number of times its DynamicBytesSource.Generate()
    41  // implementation has been called.
    42  type genCount struct {
    43  	count uint64 // accessed using atomic memory ops
    44  }
    45  
    46  // Generate implements DynamicBytesSource.Generate.
    47  func (g *genCount) Generate(ctx context.Context, buf *bytes.Buffer) error {
    48  	fmt.Fprintf(buf, "%d", atomic.AddUint64(&g.count, 1))
    49  	return nil
    50  }
    51  
    52  type storeData struct {
    53  	data string
    54  }
    55  
    56  var _ WritableDynamicBytesSource = (*storeData)(nil)
    57  
    58  // Generate implements DynamicBytesSource.
    59  func (d *storeData) Generate(ctx context.Context, buf *bytes.Buffer) error {
    60  	buf.WriteString(d.data)
    61  	return nil
    62  }
    63  
    64  // Generate implements WritableDynamicBytesSource.
    65  func (d *storeData) Write(ctx context.Context, src usermem.IOSequence, offset int64) (int64, error) {
    66  	buf := make([]byte, src.NumBytes())
    67  	n, err := src.CopyIn(ctx, buf)
    68  	if err != nil {
    69  		return 0, err
    70  	}
    71  
    72  	d.data = string(buf[:n])
    73  	return 0, nil
    74  }
    75  
    76  // testFD is a read-only FileDescriptionImpl representing a regular file.
    77  type testFD struct {
    78  	fileDescription
    79  	DynamicBytesFileDescriptionImpl
    80  
    81  	data DynamicBytesSource
    82  }
    83  
    84  func newTestFD(ctx context.Context, vfsObj *VirtualFilesystem, statusFlags uint32, data DynamicBytesSource) *FileDescription {
    85  	vd := vfsObj.NewAnonVirtualDentry("genCountFD")
    86  	defer vd.DecRef(ctx)
    87  	var fd testFD
    88  	fd.vfsfd.Init(&fd, statusFlags, vd.Mount(), vd.Dentry(), &FileDescriptionOptions{})
    89  	fd.DynamicBytesFileDescriptionImpl.SetDataSource(data)
    90  	return &fd.vfsfd
    91  }
    92  
    93  // Release implements FileDescriptionImpl.Release.
    94  func (fd *testFD) Release(context.Context) {
    95  }
    96  
    97  // SetStatusFlags implements FileDescriptionImpl.SetStatusFlags.
    98  // Stat implements FileDescriptionImpl.Stat.
    99  func (fd *testFD) Stat(ctx context.Context, opts StatOptions) (linux.Statx, error) {
   100  	// Note that Statx.Mask == 0 in the return value.
   101  	return linux.Statx{}, nil
   102  }
   103  
   104  // SetStat implements FileDescriptionImpl.SetStat.
   105  func (fd *testFD) SetStat(ctx context.Context, opts SetStatOptions) error {
   106  	return linuxerr.EPERM
   107  }
   108  
   109  func TestGenCountFD(t *testing.T) {
   110  	ctx := contexttest.Context(t)
   111  
   112  	vfsObj := &VirtualFilesystem{}
   113  	if err := vfsObj.Init(ctx); err != nil {
   114  		t.Fatalf("VFS init: %v", err)
   115  	}
   116  	fd := newTestFD(ctx, vfsObj, linux.O_RDWR, &genCount{})
   117  	defer fd.DecRef(ctx)
   118  
   119  	// The first read causes Generate to be called to fill the FD's buffer.
   120  	buf := make([]byte, 2)
   121  	ioseq := usermem.BytesIOSequence(buf)
   122  	n, err := fd.Read(ctx, ioseq, ReadOptions{})
   123  	if n != 1 || (err != nil && err != io.EOF) {
   124  		t.Fatalf("first Read: got (%d, %v), wanted (1, nil or EOF)", n, err)
   125  	}
   126  	if want := byte('1'); buf[0] != want {
   127  		t.Errorf("first Read: got byte %c, wanted %c", buf[0], want)
   128  	}
   129  
   130  	// A second read without seeking is still at EOF.
   131  	n, err = fd.Read(ctx, ioseq, ReadOptions{})
   132  	if n != 0 || err != io.EOF {
   133  		t.Fatalf("second Read: got (%d, %v), wanted (0, EOF)", n, err)
   134  	}
   135  
   136  	// Seeking to the beginning of the file causes it to be regenerated.
   137  	n, err = fd.Seek(ctx, 0, linux.SEEK_SET)
   138  	if n != 0 || err != nil {
   139  		t.Fatalf("Seek: got (%d, %v), wanted (0, nil)", n, err)
   140  	}
   141  	n, err = fd.Read(ctx, ioseq, ReadOptions{})
   142  	if n != 1 || (err != nil && err != io.EOF) {
   143  		t.Fatalf("Read after Seek: got (%d, %v), wanted (1, nil or EOF)", n, err)
   144  	}
   145  	if want := byte('2'); buf[0] != want {
   146  		t.Errorf("Read after Seek: got byte %c, wanted %c", buf[0], want)
   147  	}
   148  
   149  	// PRead at the beginning of the file also causes it to be regenerated.
   150  	n, err = fd.PRead(ctx, ioseq, 0, ReadOptions{})
   151  	if n != 1 || (err != nil && err != io.EOF) {
   152  		t.Fatalf("PRead: got (%d, %v), wanted (1, nil or EOF)", n, err)
   153  	}
   154  	if want := byte('3'); buf[0] != want {
   155  		t.Errorf("PRead: got byte %c, wanted %c", buf[0], want)
   156  	}
   157  
   158  	// Write and PWrite fails.
   159  	if _, err := fd.Write(ctx, ioseq, WriteOptions{}); !linuxerr.Equals(linuxerr.EIO, err) {
   160  		t.Errorf("Write: got err %v, wanted %v", err, syserror.EIO)
   161  	}
   162  	if _, err := fd.PWrite(ctx, ioseq, 0, WriteOptions{}); !linuxerr.Equals(linuxerr.EIO, err) {
   163  		t.Errorf("Write: got err %v, wanted %v", err, syserror.EIO)
   164  	}
   165  }
   166  
   167  func TestWritable(t *testing.T) {
   168  	ctx := contexttest.Context(t)
   169  
   170  	vfsObj := &VirtualFilesystem{}
   171  	if err := vfsObj.Init(ctx); err != nil {
   172  		t.Fatalf("VFS init: %v", err)
   173  	}
   174  	fd := newTestFD(ctx, vfsObj, linux.O_RDWR, &storeData{data: "init"})
   175  	defer fd.DecRef(ctx)
   176  
   177  	buf := make([]byte, 10)
   178  	ioseq := usermem.BytesIOSequence(buf)
   179  	if n, err := fd.Read(ctx, ioseq, ReadOptions{}); n != 4 && err != io.EOF {
   180  		t.Fatalf("Read: got (%v, %v), wanted (4, EOF)", n, err)
   181  	}
   182  	if want := "init"; want == string(buf) {
   183  		t.Fatalf("Read: got %v, wanted %v", string(buf), want)
   184  	}
   185  
   186  	// Test PWrite.
   187  	want := "write"
   188  	writeIOSeq := usermem.BytesIOSequence([]byte(want))
   189  	if n, err := fd.PWrite(ctx, writeIOSeq, 0, WriteOptions{}); int(n) != len(want) && err != nil {
   190  		t.Errorf("PWrite: got err (%v, %v), wanted (%v, nil)", n, err, len(want))
   191  	}
   192  	if n, err := fd.PRead(ctx, ioseq, 0, ReadOptions{}); int(n) != len(want) && err != io.EOF {
   193  		t.Fatalf("PRead: got (%v, %v), wanted (%v, EOF)", n, err, len(want))
   194  	}
   195  	if want == string(buf) {
   196  		t.Fatalf("PRead: got %v, wanted %v", string(buf), want)
   197  	}
   198  
   199  	// Test Seek to 0 followed by Write.
   200  	want = "write2"
   201  	writeIOSeq = usermem.BytesIOSequence([]byte(want))
   202  	if n, err := fd.Seek(ctx, 0, linux.SEEK_SET); n != 0 && err != nil {
   203  		t.Errorf("Seek: got err (%v, %v), wanted (0, nil)", n, err)
   204  	}
   205  	if n, err := fd.Write(ctx, writeIOSeq, WriteOptions{}); int(n) != len(want) && err != nil {
   206  		t.Errorf("Write: got err (%v, %v), wanted (%v, nil)", n, err, len(want))
   207  	}
   208  	if n, err := fd.PRead(ctx, ioseq, 0, ReadOptions{}); int(n) != len(want) && err != io.EOF {
   209  		t.Fatalf("PRead: got (%v, %v), wanted (%v, EOF)", n, err, len(want))
   210  	}
   211  	if want == string(buf) {
   212  		t.Fatalf("PRead: got %v, wanted %v", string(buf), want)
   213  	}
   214  
   215  	// Test failure if offset != 0.
   216  	if n, err := fd.Seek(ctx, 1, linux.SEEK_SET); n != 0 && err != nil {
   217  		t.Errorf("Seek: got err (%v, %v), wanted (0, nil)", n, err)
   218  	}
   219  	if n, err := fd.Write(ctx, writeIOSeq, WriteOptions{}); n != 0 && !linuxerr.Equals(linuxerr.EINVAL, err) {
   220  		t.Errorf("Write: got err (%v, %v), wanted (0, EINVAL)", n, err)
   221  	}
   222  	if n, err := fd.PWrite(ctx, writeIOSeq, 2, WriteOptions{}); n != 0 && !linuxerr.Equals(linuxerr.EINVAL, err) {
   223  		t.Errorf("PWrite: got err (%v, %v), wanted (0, EINVAL)", n, err)
   224  	}
   225  }