github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/fsutil/file.go (about)

     1  // Copyright 2018 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 fsutil
    16  
    17  import (
    18  	"io"
    19  
    20  	"github.com/SagerNet/gvisor/pkg/context"
    21  	"github.com/SagerNet/gvisor/pkg/errors/linuxerr"
    22  	"github.com/SagerNet/gvisor/pkg/sentry/arch"
    23  	"github.com/SagerNet/gvisor/pkg/sentry/fs"
    24  	"github.com/SagerNet/gvisor/pkg/sentry/memmap"
    25  	"github.com/SagerNet/gvisor/pkg/syserror"
    26  	"github.com/SagerNet/gvisor/pkg/usermem"
    27  	"github.com/SagerNet/gvisor/pkg/waiter"
    28  )
    29  
    30  // FileNoopRelease implements fs.FileOperations.Release for files that have no
    31  // resources to release.
    32  type FileNoopRelease struct{}
    33  
    34  // Release is a no-op.
    35  func (FileNoopRelease) Release(context.Context) {}
    36  
    37  // SeekWithDirCursor is used to implement fs.FileOperations.Seek.  If dirCursor
    38  // is not nil and the seek was on a directory, the cursor will be updated.
    39  //
    40  // Currently only seeking to 0 on a directory is supported.
    41  //
    42  // FIXME(b/33075855): Lift directory seeking limitations.
    43  func SeekWithDirCursor(ctx context.Context, file *fs.File, whence fs.SeekWhence, offset int64, dirCursor *string) (int64, error) {
    44  	inode := file.Dirent.Inode
    45  	current := file.Offset()
    46  
    47  	// Does the Inode represents a non-seekable type?
    48  	if fs.IsPipe(inode.StableAttr) || fs.IsSocket(inode.StableAttr) {
    49  		return current, linuxerr.ESPIPE
    50  	}
    51  
    52  	// Does the Inode represent a character device?
    53  	if fs.IsCharDevice(inode.StableAttr) {
    54  		// Ignore seek requests.
    55  		//
    56  		// FIXME(b/34716638): This preserves existing
    57  		// behavior but is not universally correct.
    58  		return 0, nil
    59  	}
    60  
    61  	// Otherwise compute the new offset.
    62  	switch whence {
    63  	case fs.SeekSet:
    64  		switch inode.StableAttr.Type {
    65  		case fs.RegularFile, fs.SpecialFile, fs.BlockDevice:
    66  			if offset < 0 {
    67  				return current, linuxerr.EINVAL
    68  			}
    69  			return offset, nil
    70  		case fs.Directory, fs.SpecialDirectory:
    71  			if offset != 0 {
    72  				return current, linuxerr.EINVAL
    73  			}
    74  			// SEEK_SET to 0 moves the directory "cursor" to the beginning.
    75  			if dirCursor != nil {
    76  				*dirCursor = ""
    77  			}
    78  			return 0, nil
    79  		default:
    80  			return current, linuxerr.EINVAL
    81  		}
    82  	case fs.SeekCurrent:
    83  		switch inode.StableAttr.Type {
    84  		case fs.RegularFile, fs.SpecialFile, fs.BlockDevice:
    85  			if current+offset < 0 {
    86  				return current, linuxerr.EINVAL
    87  			}
    88  			return current + offset, nil
    89  		case fs.Directory, fs.SpecialDirectory:
    90  			if offset != 0 {
    91  				return current, linuxerr.EINVAL
    92  			}
    93  			return current, nil
    94  		default:
    95  			return current, linuxerr.EINVAL
    96  		}
    97  	case fs.SeekEnd:
    98  		switch inode.StableAttr.Type {
    99  		case fs.RegularFile, fs.BlockDevice:
   100  			// Allow the file to determine the end.
   101  			uattr, err := inode.UnstableAttr(ctx)
   102  			if err != nil {
   103  				return current, err
   104  			}
   105  			sz := uattr.Size
   106  			if sz+offset < 0 {
   107  				return current, linuxerr.EINVAL
   108  			}
   109  			return sz + offset, nil
   110  		// FIXME(b/34778850): This is not universally correct.
   111  		// Remove SpecialDirectory.
   112  		case fs.SpecialDirectory:
   113  			if offset != 0 {
   114  				return current, linuxerr.EINVAL
   115  			}
   116  			// SEEK_END to 0 moves the directory "cursor" to the end.
   117  			//
   118  			// FIXME(b/35442290): The ensures that after the seek,
   119  			// reading on the directory will get EOF. But it is not
   120  			// correct in general because the directory can grow in
   121  			// size; attempting to read those new entries will be
   122  			// futile (EOF will always be the result).
   123  			return fs.FileMaxOffset, nil
   124  		default:
   125  			return current, linuxerr.EINVAL
   126  		}
   127  	}
   128  
   129  	// Not a valid seek request.
   130  	return current, linuxerr.EINVAL
   131  }
   132  
   133  // FileGenericSeek implements fs.FileOperations.Seek for files that use a
   134  // generic seek implementation.
   135  type FileGenericSeek struct{}
   136  
   137  // Seek implements fs.FileOperations.Seek.
   138  func (FileGenericSeek) Seek(ctx context.Context, file *fs.File, whence fs.SeekWhence, offset int64) (int64, error) {
   139  	return SeekWithDirCursor(ctx, file, whence, offset, nil)
   140  }
   141  
   142  // FileZeroSeek implements fs.FileOperations.Seek for files that maintain a
   143  // constant zero-value offset and require a no-op Seek.
   144  type FileZeroSeek struct{}
   145  
   146  // Seek implements fs.FileOperations.Seek.
   147  func (FileZeroSeek) Seek(context.Context, *fs.File, fs.SeekWhence, int64) (int64, error) {
   148  	return 0, nil
   149  }
   150  
   151  // FileNoSeek implements fs.FileOperations.Seek to return EINVAL.
   152  type FileNoSeek struct{}
   153  
   154  // Seek implements fs.FileOperations.Seek.
   155  func (FileNoSeek) Seek(context.Context, *fs.File, fs.SeekWhence, int64) (int64, error) {
   156  	return 0, linuxerr.EINVAL
   157  }
   158  
   159  // FilePipeSeek implements fs.FileOperations.Seek and can be used for files
   160  // that behave like pipes (seeking is not supported).
   161  type FilePipeSeek struct{}
   162  
   163  // Seek implements fs.FileOperations.Seek.
   164  func (FilePipeSeek) Seek(context.Context, *fs.File, fs.SeekWhence, int64) (int64, error) {
   165  	return 0, linuxerr.ESPIPE
   166  }
   167  
   168  // FileNotDirReaddir implements fs.FileOperations.Readdir for non-directories.
   169  type FileNotDirReaddir struct{}
   170  
   171  // Readdir implements fs.FileOperations.FileNotDirReaddir.
   172  func (FileNotDirReaddir) Readdir(context.Context, *fs.File, fs.DentrySerializer) (int64, error) {
   173  	return 0, syserror.ENOTDIR
   174  }
   175  
   176  // FileNoFsync implements fs.FileOperations.Fsync for files that don't support
   177  // syncing.
   178  type FileNoFsync struct{}
   179  
   180  // Fsync implements fs.FileOperations.Fsync.
   181  func (FileNoFsync) Fsync(context.Context, *fs.File, int64, int64, fs.SyncType) error {
   182  	return linuxerr.EINVAL
   183  }
   184  
   185  // FileNoopFsync implements fs.FileOperations.Fsync for files that don't need
   186  // to synced.
   187  type FileNoopFsync struct{}
   188  
   189  // Fsync implements fs.FileOperations.Fsync.
   190  func (FileNoopFsync) Fsync(context.Context, *fs.File, int64, int64, fs.SyncType) error {
   191  	return nil
   192  }
   193  
   194  // FileNoopFlush implements fs.FileOperations.Flush as a no-op.
   195  type FileNoopFlush struct{}
   196  
   197  // Flush implements fs.FileOperations.Flush.
   198  func (FileNoopFlush) Flush(context.Context, *fs.File) error {
   199  	return nil
   200  }
   201  
   202  // FileNoMMap implements fs.FileOperations.Mappable for files that cannot
   203  // be memory mapped.
   204  type FileNoMMap struct{}
   205  
   206  // ConfigureMMap implements fs.FileOperations.ConfigureMMap.
   207  func (FileNoMMap) ConfigureMMap(context.Context, *fs.File, *memmap.MMapOpts) error {
   208  	return linuxerr.ENODEV
   209  }
   210  
   211  // GenericConfigureMMap implements fs.FileOperations.ConfigureMMap for most
   212  // filesystems that support memory mapping.
   213  func GenericConfigureMMap(file *fs.File, m memmap.Mappable, opts *memmap.MMapOpts) error {
   214  	opts.Mappable = m
   215  	opts.MappingIdentity = file
   216  	file.IncRef()
   217  	return nil
   218  }
   219  
   220  // FileNoIoctl implements fs.FileOperations.Ioctl for files that don't
   221  // implement the ioctl syscall.
   222  type FileNoIoctl struct{}
   223  
   224  // Ioctl implements fs.FileOperations.Ioctl.
   225  func (FileNoIoctl) Ioctl(context.Context, *fs.File, usermem.IO, arch.SyscallArguments) (uintptr, error) {
   226  	return 0, syserror.ENOTTY
   227  }
   228  
   229  // FileNoSplice implements fs.FileOperations.ReadFrom and
   230  // fs.FileOperations.WriteTo for files that don't support splice.
   231  type FileNoSplice struct{}
   232  
   233  // WriteTo implements fs.FileOperations.WriteTo.
   234  func (FileNoSplice) WriteTo(context.Context, *fs.File, io.Writer, int64, bool) (int64, error) {
   235  	return 0, syserror.ENOSYS
   236  }
   237  
   238  // ReadFrom implements fs.FileOperations.ReadFrom.
   239  func (FileNoSplice) ReadFrom(context.Context, *fs.File, io.Reader, int64) (int64, error) {
   240  	return 0, syserror.ENOSYS
   241  }
   242  
   243  // DirFileOperations implements most of fs.FileOperations for directories,
   244  // except for Readdir and UnstableAttr which the embedding type must implement.
   245  type DirFileOperations struct {
   246  	waiter.AlwaysReady
   247  	FileGenericSeek
   248  	FileNoIoctl
   249  	FileNoMMap
   250  	FileNoopFlush
   251  	FileNoopFsync
   252  	FileNoopRelease
   253  	FileNoSplice
   254  }
   255  
   256  // Read implements fs.FileOperations.Read
   257  func (*DirFileOperations) Read(context.Context, *fs.File, usermem.IOSequence, int64) (int64, error) {
   258  	return 0, syserror.EISDIR
   259  }
   260  
   261  // Write implements fs.FileOperations.Write.
   262  func (*DirFileOperations) Write(context.Context, *fs.File, usermem.IOSequence, int64) (int64, error) {
   263  	return 0, syserror.EISDIR
   264  }
   265  
   266  // StaticDirFileOperations implements fs.FileOperations for directories with
   267  // static children.
   268  //
   269  // +stateify savable
   270  type StaticDirFileOperations struct {
   271  	DirFileOperations        `state:"nosave"`
   272  	FileUseInodeUnstableAttr `state:"nosave"`
   273  
   274  	// dentryMap is a SortedDentryMap used to implement Readdir.
   275  	dentryMap *fs.SortedDentryMap
   276  
   277  	// dirCursor contains the name of the last directory entry that was
   278  	// serialized.
   279  	dirCursor string
   280  }
   281  
   282  // NewStaticDirFileOperations returns a new StaticDirFileOperations that will
   283  // iterate the given denty map.
   284  func NewStaticDirFileOperations(dentries *fs.SortedDentryMap) *StaticDirFileOperations {
   285  	return &StaticDirFileOperations{
   286  		dentryMap: dentries,
   287  	}
   288  }
   289  
   290  // IterateDir implements DirIterator.IterateDir.
   291  func (sdfo *StaticDirFileOperations) IterateDir(ctx context.Context, d *fs.Dirent, dirCtx *fs.DirCtx, offset int) (int, error) {
   292  	n, err := fs.GenericReaddir(dirCtx, sdfo.dentryMap)
   293  	return offset + n, err
   294  }
   295  
   296  // Readdir implements fs.FileOperations.Readdir.
   297  func (sdfo *StaticDirFileOperations) Readdir(ctx context.Context, file *fs.File, serializer fs.DentrySerializer) (int64, error) {
   298  	root := fs.RootFromContext(ctx)
   299  	if root != nil {
   300  		defer root.DecRef(ctx)
   301  	}
   302  	dirCtx := &fs.DirCtx{
   303  		Serializer: serializer,
   304  		DirCursor:  &sdfo.dirCursor,
   305  	}
   306  	return fs.DirentReaddir(ctx, file.Dirent, sdfo, root, dirCtx, file.Offset())
   307  }
   308  
   309  // NoReadWriteFile is a file that does not support reading or writing.
   310  //
   311  // +stateify savable
   312  type NoReadWriteFile struct {
   313  	waiter.AlwaysReady       `state:"nosave"`
   314  	FileGenericSeek          `state:"nosave"`
   315  	FileNoIoctl              `state:"nosave"`
   316  	FileNoMMap               `state:"nosave"`
   317  	FileNoopFsync            `state:"nosave"`
   318  	FileNoopFlush            `state:"nosave"`
   319  	FileNoopRelease          `state:"nosave"`
   320  	FileNoRead               `state:"nosave"`
   321  	FileNoWrite              `state:"nosave"`
   322  	FileNotDirReaddir        `state:"nosave"`
   323  	FileUseInodeUnstableAttr `state:"nosave"`
   324  	FileNoSplice             `state:"nosave"`
   325  }
   326  
   327  var _ fs.FileOperations = (*NoReadWriteFile)(nil)
   328  
   329  // FileStaticContentReader is a helper to implement fs.FileOperations.Read with
   330  // static content.
   331  //
   332  // +stateify savable
   333  type FileStaticContentReader struct {
   334  	// content is immutable.
   335  	content []byte
   336  }
   337  
   338  // NewFileStaticContentReader initializes a FileStaticContentReader with the
   339  // given content.
   340  func NewFileStaticContentReader(b []byte) FileStaticContentReader {
   341  	return FileStaticContentReader{
   342  		content: b,
   343  	}
   344  }
   345  
   346  // Read implements fs.FileOperations.Read.
   347  func (scr *FileStaticContentReader) Read(ctx context.Context, _ *fs.File, dst usermem.IOSequence, offset int64) (int64, error) {
   348  	if offset < 0 {
   349  		return 0, linuxerr.EINVAL
   350  	}
   351  	if offset >= int64(len(scr.content)) {
   352  		return 0, nil
   353  	}
   354  	n, err := dst.CopyOut(ctx, scr.content[offset:])
   355  	return int64(n), err
   356  }
   357  
   358  // FileNoopWrite implements fs.FileOperations.Write as a noop.
   359  type FileNoopWrite struct{}
   360  
   361  // Write implements fs.FileOperations.Write.
   362  func (FileNoopWrite) Write(_ context.Context, _ *fs.File, src usermem.IOSequence, _ int64) (int64, error) {
   363  	return src.NumBytes(), nil
   364  }
   365  
   366  // FileNoRead implements fs.FileOperations.Read to return EINVAL.
   367  type FileNoRead struct{}
   368  
   369  // Read implements fs.FileOperations.Read.
   370  func (FileNoRead) Read(context.Context, *fs.File, usermem.IOSequence, int64) (int64, error) {
   371  	return 0, linuxerr.EINVAL
   372  }
   373  
   374  // FileNoWrite implements fs.FileOperations.Write to return EINVAL.
   375  type FileNoWrite struct{}
   376  
   377  // Write implements fs.FileOperations.Write.
   378  func (FileNoWrite) Write(context.Context, *fs.File, usermem.IOSequence, int64) (int64, error) {
   379  	return 0, linuxerr.EINVAL
   380  }
   381  
   382  // FileNoopRead implement fs.FileOperations.Read as a noop.
   383  type FileNoopRead struct{}
   384  
   385  // Read implements fs.FileOperations.Read.
   386  func (FileNoopRead) Read(context.Context, *fs.File, usermem.IOSequence, int64) (int64, error) {
   387  	return 0, nil
   388  }
   389  
   390  // FileUseInodeUnstableAttr implements fs.FileOperations.UnstableAttr by calling
   391  // InodeOperations.UnstableAttr.
   392  type FileUseInodeUnstableAttr struct{}
   393  
   394  // UnstableAttr implements fs.FileOperations.UnstableAttr.
   395  func (FileUseInodeUnstableAttr) UnstableAttr(ctx context.Context, file *fs.File) (fs.UnstableAttr, error) {
   396  	return file.Dirent.Inode.UnstableAttr(ctx)
   397  }