github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/sentry/fsimpl/erofs/regular_file.go (about)

     1  // Copyright 2023 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 erofs
    16  
    17  import (
    18  	"io"
    19  	"sync"
    20  
    21  	"github.com/metacubex/gvisor/pkg/abi/linux"
    22  	"github.com/metacubex/gvisor/pkg/context"
    23  	"github.com/metacubex/gvisor/pkg/erofs"
    24  	"github.com/metacubex/gvisor/pkg/errors/linuxerr"
    25  	"github.com/metacubex/gvisor/pkg/hostarch"
    26  	"github.com/metacubex/gvisor/pkg/safemem"
    27  	"github.com/metacubex/gvisor/pkg/sentry/memmap"
    28  	"github.com/metacubex/gvisor/pkg/sentry/vfs"
    29  	"github.com/metacubex/gvisor/pkg/usermem"
    30  )
    31  
    32  // +stateify savable
    33  type regularFileFD struct {
    34  	fileDescription
    35  
    36  	// offMu protects off.
    37  	offMu sync.Mutex `state:"nosave"`
    38  
    39  	// off is the file offset.
    40  	// +checklocks:offMu
    41  	off int64
    42  }
    43  
    44  // PRead implements vfs.FileDescriptionImpl.PRead.
    45  func (fd *regularFileFD) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts vfs.ReadOptions) (int64, error) {
    46  	if offset < 0 {
    47  		return 0, linuxerr.EINVAL
    48  	}
    49  
    50  	// Check that flags are supported.
    51  	//
    52  	// TODO(gvisor.dev/issue/2601): Support select preadv2 flags.
    53  	if opts.Flags&^linux.RWF_HIPRI != 0 {
    54  		return 0, linuxerr.EOPNOTSUPP
    55  	}
    56  
    57  	if dst.NumBytes() == 0 {
    58  		return 0, nil
    59  	}
    60  
    61  	data, err := fd.inode().Data()
    62  	if err != nil {
    63  		return 0, err
    64  	}
    65  	r := &regularFileReader{
    66  		data: data,
    67  		off:  uint64(offset),
    68  	}
    69  	return dst.CopyOutFrom(ctx, r)
    70  }
    71  
    72  type regularFileReader struct {
    73  	data safemem.BlockSeq
    74  	off  uint64
    75  }
    76  
    77  // ReadToBlocks implements safemem.Reader.ReadToBlocks.
    78  func (r *regularFileReader) ReadToBlocks(dsts safemem.BlockSeq) (uint64, error) {
    79  	if r.off >= r.data.NumBytes() {
    80  		return 0, io.EOF
    81  	}
    82  	cp, err := safemem.CopySeq(dsts, r.data.DropFirst(int(r.off)))
    83  	r.off += cp
    84  	return cp, err
    85  }
    86  
    87  // Read implements vfs.FileDescriptionImpl.Read.
    88  func (fd *regularFileFD) Read(ctx context.Context, dst usermem.IOSequence, opts vfs.ReadOptions) (int64, error) {
    89  	fd.offMu.Lock()
    90  	n, err := fd.PRead(ctx, dst, fd.off, opts)
    91  	fd.off += n
    92  	fd.offMu.Unlock()
    93  	return n, err
    94  }
    95  
    96  // PWrite implements vfs.FileDescriptionImpl.PWrite.
    97  func (fd *regularFileFD) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts vfs.WriteOptions) (int64, error) {
    98  	return 0, linuxerr.EROFS
    99  }
   100  
   101  // Write implements vfs.FileDescriptionImpl.Write.
   102  func (fd *regularFileFD) Write(ctx context.Context, src usermem.IOSequence, opts vfs.WriteOptions) (int64, error) {
   103  	return 0, linuxerr.EROFS
   104  }
   105  
   106  // Seek implements vfs.FileDescriptionImpl.Seek.
   107  func (fd *regularFileFD) Seek(ctx context.Context, offset int64, whence int32) (int64, error) {
   108  	fd.offMu.Lock()
   109  	defer fd.offMu.Unlock()
   110  	switch whence {
   111  	case linux.SEEK_SET:
   112  		// use offset as specified
   113  	case linux.SEEK_CUR:
   114  		offset += fd.off
   115  	case linux.SEEK_END:
   116  		offset += int64(fd.inode().Size())
   117  	default:
   118  		return 0, linuxerr.EINVAL
   119  	}
   120  	if offset < 0 {
   121  		return 0, linuxerr.EINVAL
   122  	}
   123  	fd.off = offset
   124  	return offset, nil
   125  }
   126  
   127  // ConfigureMMap implements vfs.FileDescriptionImpl.ConfigureMMap.
   128  func (fd *regularFileFD) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error {
   129  	return vfs.GenericConfigureMMap(&fd.vfsfd, fd.inode(), opts)
   130  }
   131  
   132  // AddMapping implements memmap.Mappable.AddMapping.
   133  func (i *inode) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) error {
   134  	i.mapsMu.Lock()
   135  	i.mappings.AddMapping(ms, ar, offset, writable)
   136  	i.mapsMu.Unlock()
   137  	return nil
   138  }
   139  
   140  // RemoveMapping implements memmap.Mappable.RemoveMapping.
   141  func (i *inode) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) {
   142  	i.mapsMu.Lock()
   143  	i.mappings.RemoveMapping(ms, ar, offset, writable)
   144  	i.mapsMu.Unlock()
   145  }
   146  
   147  // CopyMapping implements memmap.Mappable.CopyMapping.
   148  func (i *inode) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR hostarch.AddrRange, offset uint64, writable bool) error {
   149  	i.AddMapping(ctx, ms, dstAR, offset, writable)
   150  	return nil
   151  }
   152  
   153  // Translate implements memmap.Mappable.Translate.
   154  func (i *inode) Translate(ctx context.Context, required, optional memmap.MappableRange, at hostarch.AccessType) ([]memmap.Translation, error) {
   155  	pgend, _ := hostarch.PageRoundUp(i.Size())
   156  	if required.End > pgend {
   157  		if required.Start >= pgend {
   158  			return nil, &memmap.BusError{io.EOF}
   159  		}
   160  		required.End = pgend
   161  	}
   162  	if optional.End > pgend {
   163  		optional.End = pgend
   164  	}
   165  	if at.Write {
   166  		return nil, &memmap.BusError{linuxerr.EROFS}
   167  	}
   168  	offset, err := i.DataOffset()
   169  	if err != nil {
   170  		return nil, &memmap.BusError{err}
   171  	}
   172  	mr := optional
   173  	return []memmap.Translation{
   174  		{
   175  			Source: mr,
   176  			File:   &i.fs.mf,
   177  			Offset: mr.Start + offset,
   178  			Perms:  at,
   179  		},
   180  	}, nil
   181  }
   182  
   183  // InvalidateUnsavable implements memmap.Mappable.InvalidateUnsavable.
   184  func (i *inode) InvalidateUnsavable(ctx context.Context) error {
   185  	i.mapsMu.Lock()
   186  	i.mappings.InvalidateAll(memmap.InvalidateOpts{})
   187  	i.mapsMu.Unlock()
   188  	return nil
   189  }
   190  
   191  // +stateify savable
   192  type imageMemmapFile struct {
   193  	image *erofs.Image
   194  }
   195  
   196  // IncRef implements memmap.File.IncRef.
   197  func (mf *imageMemmapFile) IncRef(fr memmap.FileRange, memCgID uint32) {}
   198  
   199  // DecRef implements memmap.File.DecRef.
   200  func (mf *imageMemmapFile) DecRef(fr memmap.FileRange) {}
   201  
   202  // MapInternal implements memmap.File.MapInternal.
   203  func (mf *imageMemmapFile) MapInternal(fr memmap.FileRange, at hostarch.AccessType) (safemem.BlockSeq, error) {
   204  	if at.Write {
   205  		return safemem.BlockSeq{}, &memmap.BusError{linuxerr.EROFS}
   206  	}
   207  	bytes, err := mf.image.BytesAt(fr.Start, fr.Length())
   208  	if err != nil {
   209  		return safemem.BlockSeq{}, &memmap.BusError{err}
   210  	}
   211  	return safemem.BlockSeqOf(safemem.BlockFromSafeSlice(bytes)), nil
   212  }
   213  
   214  // FD implements memmap.File.FD.
   215  func (mf *imageMemmapFile) FD() int {
   216  	return mf.image.FD()
   217  }