github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fsimpl/ext/regular_file.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 ext
    16  
    17  import (
    18  	"io"
    19  
    20  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    21  	"github.com/SagerNet/gvisor/pkg/context"
    22  	"github.com/SagerNet/gvisor/pkg/errors/linuxerr"
    23  	"github.com/SagerNet/gvisor/pkg/safemem"
    24  	"github.com/SagerNet/gvisor/pkg/sentry/memmap"
    25  	"github.com/SagerNet/gvisor/pkg/sentry/vfs"
    26  	"github.com/SagerNet/gvisor/pkg/sync"
    27  	"github.com/SagerNet/gvisor/pkg/syserror"
    28  	"github.com/SagerNet/gvisor/pkg/usermem"
    29  )
    30  
    31  // regularFile represents a regular file's inode. This too follows the
    32  // inheritance pattern prevelant in the vfs layer described in
    33  // pkg/sentry/vfs/README.md.
    34  //
    35  // +stateify savable
    36  type regularFile struct {
    37  	inode inode
    38  
    39  	// This is immutable. The first field of fileReader implementations must be
    40  	// regularFile to ensure temporality.
    41  	// io.ReaderAt is more strict than io.Reader in the sense that a partial read
    42  	// is always accompanied by an error. If a read spans past the end of file, a
    43  	// partial read (within file range) is done and io.EOF is returned.
    44  	impl io.ReaderAt
    45  }
    46  
    47  // newRegularFile is the regularFile constructor. It figures out what kind of
    48  // file this is and initializes the fileReader.
    49  func newRegularFile(args inodeArgs) (*regularFile, error) {
    50  	if args.diskInode.Flags().Extents {
    51  		file, err := newExtentFile(args)
    52  		if err != nil {
    53  			return nil, err
    54  		}
    55  		return &file.regFile, nil
    56  	}
    57  
    58  	file, err := newBlockMapFile(args)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	return &file.regFile, nil
    63  }
    64  
    65  func (in *inode) isRegular() bool {
    66  	_, ok := in.impl.(*regularFile)
    67  	return ok
    68  }
    69  
    70  // directoryFD represents a directory file description. It implements
    71  // vfs.FileDescriptionImpl.
    72  //
    73  // +stateify savable
    74  type regularFileFD struct {
    75  	fileDescription
    76  	vfs.LockFD
    77  
    78  	// off is the file offset. off is accessed using atomic memory operations.
    79  	off int64
    80  
    81  	// offMu serializes operations that may mutate off.
    82  	offMu sync.Mutex `state:"nosave"`
    83  }
    84  
    85  // Release implements vfs.FileDescriptionImpl.Release.
    86  func (fd *regularFileFD) Release(context.Context) {}
    87  
    88  // PRead implements vfs.FileDescriptionImpl.PRead.
    89  func (fd *regularFileFD) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts vfs.ReadOptions) (int64, error) {
    90  	safeReader := safemem.FromIOReaderAt{
    91  		ReaderAt: fd.inode().impl.(*regularFile).impl,
    92  		Offset:   offset,
    93  	}
    94  
    95  	// Copies data from disk directly into usermem without any intermediate
    96  	// allocations (if dst is converted into BlockSeq such that it does not need
    97  	// safe copying).
    98  	return dst.CopyOutFrom(ctx, safeReader)
    99  }
   100  
   101  // Read implements vfs.FileDescriptionImpl.Read.
   102  func (fd *regularFileFD) Read(ctx context.Context, dst usermem.IOSequence, opts vfs.ReadOptions) (int64, error) {
   103  	n, err := fd.PRead(ctx, dst, fd.off, opts)
   104  	fd.offMu.Lock()
   105  	fd.off += n
   106  	fd.offMu.Unlock()
   107  	return n, err
   108  }
   109  
   110  // PWrite implements vfs.FileDescriptionImpl.PWrite.
   111  func (fd *regularFileFD) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts vfs.WriteOptions) (int64, error) {
   112  	// write(2) specifies that EBADF must be returned if the fd is not open for
   113  	// writing.
   114  	return 0, linuxerr.EBADF
   115  }
   116  
   117  // Write implements vfs.FileDescriptionImpl.Write.
   118  func (fd *regularFileFD) Write(ctx context.Context, src usermem.IOSequence, opts vfs.WriteOptions) (int64, error) {
   119  	n, err := fd.PWrite(ctx, src, fd.off, opts)
   120  	fd.offMu.Lock()
   121  	fd.off += n
   122  	fd.offMu.Unlock()
   123  	return n, err
   124  }
   125  
   126  // IterDirents implements vfs.FileDescriptionImpl.IterDirents.
   127  func (fd *regularFileFD) IterDirents(ctx context.Context, cb vfs.IterDirentsCallback) error {
   128  	return syserror.ENOTDIR
   129  }
   130  
   131  // Seek implements vfs.FileDescriptionImpl.Seek.
   132  func (fd *regularFileFD) Seek(ctx context.Context, offset int64, whence int32) (int64, error) {
   133  	fd.offMu.Lock()
   134  	defer fd.offMu.Unlock()
   135  	switch whence {
   136  	case linux.SEEK_SET:
   137  		// Use offset as specified.
   138  	case linux.SEEK_CUR:
   139  		offset += fd.off
   140  	case linux.SEEK_END:
   141  		offset += int64(fd.inode().diskInode.Size())
   142  	default:
   143  		return 0, linuxerr.EINVAL
   144  	}
   145  	if offset < 0 {
   146  		return 0, linuxerr.EINVAL
   147  	}
   148  	fd.off = offset
   149  	return offset, nil
   150  }
   151  
   152  // ConfigureMMap implements vfs.FileDescriptionImpl.ConfigureMMap.
   153  func (fd *regularFileFD) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error {
   154  	// TODO(b/134676337): Implement mmap(2).
   155  	return linuxerr.ENODEV
   156  }