github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/fsimpl/overlay/regular_file.go (about)

     1  // Copyright 2020 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 overlay
    16  
    17  import (
    18  	"github.com/nicocha30/gvisor-ligolo/pkg/abi/linux"
    19  	"github.com/nicocha30/gvisor-ligolo/pkg/context"
    20  	"github.com/nicocha30/gvisor-ligolo/pkg/errors/linuxerr"
    21  	"github.com/nicocha30/gvisor-ligolo/pkg/hostarch"
    22  	"github.com/nicocha30/gvisor-ligolo/pkg/log"
    23  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/arch"
    24  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel/auth"
    25  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/memmap"
    26  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/vfs"
    27  	"github.com/nicocha30/gvisor-ligolo/pkg/usermem"
    28  	"github.com/nicocha30/gvisor-ligolo/pkg/waiter"
    29  )
    30  
    31  func (d *dentry) isRegularFile() bool {
    32  	return d.mode.Load()&linux.S_IFMT == linux.S_IFREG
    33  }
    34  
    35  func (d *dentry) isSymlink() bool {
    36  	return d.mode.Load()&linux.S_IFMT == linux.S_IFLNK
    37  }
    38  
    39  func (d *dentry) readlink(ctx context.Context) (string, error) {
    40  	layerVD := d.topLayer()
    41  	return d.fs.vfsfs.VirtualFilesystem().ReadlinkAt(ctx, d.fs.creds, &vfs.PathOperation{
    42  		Root:  layerVD,
    43  		Start: layerVD,
    44  	})
    45  }
    46  
    47  // +stateify savable
    48  type regularFileFD struct {
    49  	fileDescription
    50  
    51  	// If copiedUp is false, cachedFD represents
    52  	// fileDescription.dentry().lowerVDs[0]; otherwise, cachedFD represents
    53  	// fileDescription.dentry().upperVD. cachedFlags is the last known value of
    54  	// cachedFD.StatusFlags(). copiedUp, cachedFD, and cachedFlags are
    55  	// protected by mu.
    56  	mu          regularFileFDMutex `state:"nosave"`
    57  	copiedUp    bool
    58  	cachedFD    *vfs.FileDescription
    59  	cachedFlags uint32
    60  }
    61  
    62  func (fd *regularFileFD) getCurrentFD(ctx context.Context) (*vfs.FileDescription, error) {
    63  	fd.mu.Lock()
    64  	defer fd.mu.Unlock()
    65  	wrappedFD, err := fd.currentFDLocked(ctx)
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  	wrappedFD.IncRef()
    70  	return wrappedFD, nil
    71  }
    72  
    73  func (fd *regularFileFD) currentFDLocked(ctx context.Context) (*vfs.FileDescription, error) {
    74  	d := fd.dentry()
    75  	statusFlags := fd.vfsfd.StatusFlags()
    76  	if !fd.copiedUp && d.isCopiedUp() {
    77  		// Switch to the copied-up file.
    78  		upperVD := d.topLayer()
    79  		upperFD, err := fd.filesystem().vfsfs.VirtualFilesystem().OpenAt(ctx, d.fs.creds, &vfs.PathOperation{
    80  			Root:  upperVD,
    81  			Start: upperVD,
    82  		}, &vfs.OpenOptions{
    83  			Flags: statusFlags,
    84  		})
    85  		if err != nil {
    86  			return nil, err
    87  		}
    88  		oldOff, oldOffErr := fd.cachedFD.Seek(ctx, 0, linux.SEEK_CUR)
    89  		if oldOffErr == nil {
    90  			if _, err := upperFD.Seek(ctx, oldOff, linux.SEEK_SET); err != nil {
    91  				upperFD.DecRef(ctx)
    92  				return nil, err
    93  			}
    94  		}
    95  		fd.cachedFD.DecRef(ctx)
    96  		fd.copiedUp = true
    97  		fd.cachedFD = upperFD
    98  		fd.cachedFlags = statusFlags
    99  	} else if fd.cachedFlags != statusFlags {
   100  		if err := fd.cachedFD.SetStatusFlags(ctx, d.fs.creds, statusFlags); err != nil {
   101  			return nil, err
   102  		}
   103  		fd.cachedFlags = statusFlags
   104  	}
   105  	return fd.cachedFD, nil
   106  }
   107  
   108  // Release implements vfs.FileDescriptionImpl.Release.
   109  func (fd *regularFileFD) Release(ctx context.Context) {
   110  	fd.cachedFD.DecRef(ctx)
   111  	fd.cachedFD = nil
   112  }
   113  
   114  // OnClose implements vfs.FileDescriptionImpl.OnClose.
   115  func (fd *regularFileFD) OnClose(ctx context.Context) error {
   116  	// Linux doesn't define ovl_file_operations.flush at all (i.e. its
   117  	// equivalent to OnClose is a no-op). We pass through to
   118  	// fd.cachedFD.OnClose() without upgrading if fd.dentry() has been
   119  	// copied-up, since OnClose is mostly used to define post-close writeback,
   120  	// and if fd.cachedFD hasn't been updated then it can't have been used to
   121  	// mutate fd.dentry() anyway.
   122  	fd.mu.Lock()
   123  	if statusFlags := fd.vfsfd.StatusFlags(); fd.cachedFlags != statusFlags {
   124  		if err := fd.cachedFD.SetStatusFlags(ctx, fd.filesystem().creds, statusFlags); err != nil {
   125  			fd.mu.Unlock()
   126  			return err
   127  		}
   128  		fd.cachedFlags = statusFlags
   129  	}
   130  	wrappedFD := fd.cachedFD
   131  	fd.mu.Unlock()
   132  	return wrappedFD.OnClose(ctx)
   133  }
   134  
   135  // Stat implements vfs.FileDescriptionImpl.Stat.
   136  func (fd *regularFileFD) Stat(ctx context.Context, opts vfs.StatOptions) (linux.Statx, error) {
   137  	var stat linux.Statx
   138  	if layerMask := opts.Mask &^ statInternalMask; layerMask != 0 {
   139  		wrappedFD, err := fd.getCurrentFD(ctx)
   140  		if err != nil {
   141  			return linux.Statx{}, err
   142  		}
   143  		stat, err = wrappedFD.Stat(ctx, vfs.StatOptions{
   144  			Mask: layerMask,
   145  			Sync: opts.Sync,
   146  		})
   147  		wrappedFD.DecRef(ctx)
   148  		if err != nil {
   149  			return linux.Statx{}, err
   150  		}
   151  	}
   152  	fd.dentry().statInternalTo(ctx, &opts, &stat)
   153  	return stat, nil
   154  }
   155  
   156  // Allocate implements vfs.FileDescriptionImpl.Allocate.
   157  func (fd *regularFileFD) Allocate(ctx context.Context, mode, offset, length uint64) error {
   158  	wrappedFD, err := fd.getCurrentFD(ctx)
   159  	if err != nil {
   160  		return err
   161  	}
   162  	defer wrappedFD.DecRef(ctx)
   163  	return wrappedFD.Allocate(ctx, mode, offset, length)
   164  }
   165  
   166  // SetStat implements vfs.FileDescriptionImpl.SetStat.
   167  func (fd *regularFileFD) SetStat(ctx context.Context, opts vfs.SetStatOptions) error {
   168  	d := fd.dentry()
   169  	mode := linux.FileMode(d.mode.Load())
   170  	if err := vfs.CheckSetStat(ctx, auth.CredentialsFromContext(ctx), &opts, mode, auth.KUID(d.uid.Load()), auth.KGID(d.gid.Load())); err != nil {
   171  		return err
   172  	}
   173  	mnt := fd.vfsfd.Mount()
   174  	if err := mnt.CheckBeginWrite(); err != nil {
   175  		return err
   176  	}
   177  	defer mnt.EndWrite()
   178  	if err := d.copyUpLocked(ctx); err != nil {
   179  		return err
   180  	}
   181  	// Changes to d's attributes are serialized by d.copyMu.
   182  	d.copyMu.Lock()
   183  	defer d.copyMu.Unlock()
   184  	wrappedFD, err := fd.currentFDLocked(ctx)
   185  	if err != nil {
   186  		return err
   187  	}
   188  	if err := wrappedFD.SetStat(ctx, opts); err != nil {
   189  		return err
   190  	}
   191  
   192  	// Changing owners or truncating may clear one or both of the setuid and
   193  	// setgid bits, so we may have to update opts before setting d.mode.
   194  	if opts.Stat.Mask&(linux.STATX_UID|linux.STATX_GID|linux.STATX_SIZE) != 0 {
   195  		stat, err := wrappedFD.Stat(ctx, vfs.StatOptions{
   196  			Mask: linux.STATX_MODE,
   197  		})
   198  		if err != nil {
   199  			return err
   200  		}
   201  		opts.Stat.Mode = stat.Mode
   202  		opts.Stat.Mask |= linux.STATX_MODE
   203  	}
   204  
   205  	d.updateAfterSetStatLocked(&opts)
   206  	return nil
   207  }
   208  
   209  // StatFS implements vfs.FileDescriptionImpl.StatFS.
   210  func (fd *regularFileFD) StatFS(ctx context.Context) (linux.Statfs, error) {
   211  	return fd.filesystem().statFS(ctx)
   212  }
   213  
   214  // Readiness implements waiter.Waitable.Readiness.
   215  func (fd *regularFileFD) Readiness(mask waiter.EventMask) waiter.EventMask {
   216  	ctx := context.Background()
   217  	wrappedFD, err := fd.getCurrentFD(ctx)
   218  	if err != nil {
   219  		// TODO(b/171089913): Just use fd.cachedFD since Readiness can't return
   220  		// an error. This is obviously wrong, but at least consistent with
   221  		// VFS1.
   222  		log.Warningf("overlay.regularFileFD.Readiness: currentFDLocked failed: %v", err)
   223  		fd.mu.Lock()
   224  		wrappedFD = fd.cachedFD
   225  		wrappedFD.IncRef()
   226  		fd.mu.Unlock()
   227  	}
   228  	defer wrappedFD.DecRef(ctx)
   229  	return wrappedFD.Readiness(mask)
   230  }
   231  
   232  // EventRegister implements waiter.Waitable.EventRegister.
   233  func (fd *regularFileFD) EventRegister(e *waiter.Entry) error {
   234  	fd.mu.Lock()
   235  	defer fd.mu.Unlock()
   236  	wrappedFD, err := fd.currentFDLocked(context.Background())
   237  	if err != nil {
   238  		// TODO(b/171089913): Just use fd.cachedFD for backward compatibility
   239  		// with VFS1.
   240  		log.Warningf("overlay.regularFileFD.EventRegister: currentFDLocked failed: %v", err)
   241  		wrappedFD = fd.cachedFD
   242  	}
   243  	return wrappedFD.EventRegister(e)
   244  }
   245  
   246  // EventUnregister implements waiter.Waitable.EventUnregister.
   247  func (fd *regularFileFD) EventUnregister(e *waiter.Entry) {
   248  	fd.mu.Lock()
   249  	defer fd.mu.Unlock()
   250  	fd.cachedFD.EventUnregister(e)
   251  }
   252  
   253  // Epollable implements FileDescriptionImpl.Epollable.
   254  func (fd *regularFileFD) Epollable() bool {
   255  	fd.mu.Lock()
   256  	defer fd.mu.Unlock()
   257  	wrappedFD, err := fd.currentFDLocked(context.Background())
   258  	if err != nil {
   259  		// TODO(b/171089913): Just use fd.cachedFD since EventRegister can't
   260  		// return an error. This is obviously wrong, but at least consistent
   261  		// with VFS1.
   262  		log.Warningf("overlay.regularFileFD.Epollable: currentFDLocked failed: %v", err)
   263  		wrappedFD = fd.cachedFD
   264  	}
   265  	return wrappedFD.Epollable()
   266  }
   267  
   268  // PRead implements vfs.FileDescriptionImpl.PRead.
   269  func (fd *regularFileFD) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts vfs.ReadOptions) (int64, error) {
   270  	wrappedFD, err := fd.getCurrentFD(ctx)
   271  	if err != nil {
   272  		return 0, err
   273  	}
   274  	defer wrappedFD.DecRef(ctx)
   275  	return wrappedFD.PRead(ctx, dst, offset, opts)
   276  }
   277  
   278  // Read implements vfs.FileDescriptionImpl.Read.
   279  func (fd *regularFileFD) Read(ctx context.Context, dst usermem.IOSequence, opts vfs.ReadOptions) (int64, error) {
   280  	// Hold fd.mu during the read to serialize the file offset.
   281  	fd.mu.Lock()
   282  	defer fd.mu.Unlock()
   283  	wrappedFD, err := fd.currentFDLocked(ctx)
   284  	if err != nil {
   285  		return 0, err
   286  	}
   287  	return wrappedFD.Read(ctx, dst, opts)
   288  }
   289  
   290  // PWrite implements vfs.FileDescriptionImpl.PWrite.
   291  func (fd *regularFileFD) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts vfs.WriteOptions) (int64, error) {
   292  	wrappedFD, err := fd.getCurrentFD(ctx)
   293  	if err != nil {
   294  		return 0, err
   295  	}
   296  	defer wrappedFD.DecRef(ctx)
   297  	n, err := wrappedFD.PWrite(ctx, src, offset, opts)
   298  	if err != nil {
   299  		return n, err
   300  	}
   301  	return fd.updateSetUserGroupIDs(ctx, wrappedFD, n)
   302  }
   303  
   304  // Write implements vfs.FileDescriptionImpl.Write.
   305  func (fd *regularFileFD) Write(ctx context.Context, src usermem.IOSequence, opts vfs.WriteOptions) (int64, error) {
   306  	// Hold fd.mu during the write to serialize the file offset.
   307  	fd.mu.Lock()
   308  	defer fd.mu.Unlock()
   309  	wrappedFD, err := fd.currentFDLocked(ctx)
   310  	if err != nil {
   311  		return 0, err
   312  	}
   313  	n, err := wrappedFD.Write(ctx, src, opts)
   314  	if err != nil {
   315  		return n, err
   316  	}
   317  	return fd.updateSetUserGroupIDs(ctx, wrappedFD, n)
   318  }
   319  
   320  func (fd *regularFileFD) updateSetUserGroupIDs(ctx context.Context, wrappedFD *vfs.FileDescription, written int64) (int64, error) {
   321  	// Writing can clear the setuid and/or setgid bits. We only have to
   322  	// check this if something was written and one of those bits was set.
   323  	dentry := fd.dentry()
   324  	if written == 0 || dentry.mode.Load()&(linux.S_ISUID|linux.S_ISGID) == 0 {
   325  		return written, nil
   326  	}
   327  	stat, err := wrappedFD.Stat(ctx, vfs.StatOptions{Mask: linux.STATX_MODE})
   328  	if err != nil {
   329  		return written, err
   330  	}
   331  	dentry.copyMu.Lock()
   332  	defer dentry.copyMu.Unlock()
   333  	dentry.mode.Store(uint32(stat.Mode))
   334  	return written, nil
   335  }
   336  
   337  // Seek implements vfs.FileDescriptionImpl.Seek.
   338  func (fd *regularFileFD) Seek(ctx context.Context, offset int64, whence int32) (int64, error) {
   339  	// Hold fd.mu during the seek to serialize the file offset.
   340  	fd.mu.Lock()
   341  	defer fd.mu.Unlock()
   342  	wrappedFD, err := fd.currentFDLocked(ctx)
   343  	if err != nil {
   344  		return 0, err
   345  	}
   346  	return wrappedFD.Seek(ctx, offset, whence)
   347  }
   348  
   349  // Sync implements vfs.FileDescriptionImpl.Sync.
   350  func (fd *regularFileFD) Sync(ctx context.Context) error {
   351  	fd.mu.Lock()
   352  	if !fd.dentry().isCopiedUp() {
   353  		fd.mu.Unlock()
   354  		return nil
   355  	}
   356  	wrappedFD, err := fd.currentFDLocked(ctx)
   357  	if err != nil {
   358  		fd.mu.Unlock()
   359  		return err
   360  	}
   361  	wrappedFD.IncRef()
   362  	defer wrappedFD.DecRef(ctx)
   363  	fd.mu.Unlock()
   364  	return wrappedFD.Sync(ctx)
   365  }
   366  
   367  // Ioctl implements vfs.FileDescriptionImpl.Ioctl.
   368  func (fd *regularFileFD) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) {
   369  	wrappedFD, err := fd.getCurrentFD(ctx)
   370  	if err != nil {
   371  		return 0, err
   372  	}
   373  	defer wrappedFD.DecRef(ctx)
   374  	return wrappedFD.Ioctl(ctx, uio, sysno, args)
   375  }
   376  
   377  // ConfigureMMap implements vfs.FileDescriptionImpl.ConfigureMMap.
   378  func (fd *regularFileFD) ConfigureMMap(ctx context.Context, opts *memmap.MMapOpts) error {
   379  	if err := fd.ensureMappable(ctx, opts); err != nil {
   380  		return err
   381  	}
   382  	return vfs.GenericConfigureMMap(&fd.vfsfd, fd.dentry(), opts)
   383  }
   384  
   385  // ensureMappable ensures that fd.dentry().wrappedMappable is not nil.
   386  func (fd *regularFileFD) ensureMappable(ctx context.Context, opts *memmap.MMapOpts) error {
   387  	d := fd.dentry()
   388  
   389  	// Fast path if we already have a Mappable for the current top layer.
   390  	if d.isMappable.Load() != 0 {
   391  		return nil
   392  	}
   393  
   394  	// Only permit mmap of regular files, since other file types may have
   395  	// unpredictable behavior when mmapped (e.g. /dev/zero).
   396  	if d.mode.Load()&linux.S_IFMT != linux.S_IFREG {
   397  		return linuxerr.ENODEV
   398  	}
   399  
   400  	// Get a Mappable for the current top layer.
   401  	fd.mu.Lock()
   402  	defer fd.mu.Unlock()
   403  	d.copyMu.RLock()
   404  	defer d.copyMu.RUnlock()
   405  	if d.isMappable.Load() != 0 {
   406  		return nil
   407  	}
   408  	wrappedFD, err := fd.currentFDLocked(ctx)
   409  	if err != nil {
   410  		return err
   411  	}
   412  	if err := wrappedFD.ConfigureMMap(ctx, opts); err != nil {
   413  		return err
   414  	}
   415  	if opts.MappingIdentity != nil {
   416  		opts.MappingIdentity.DecRef(ctx)
   417  		opts.MappingIdentity = nil
   418  	}
   419  	// Use this Mappable for all mappings of this layer (unless we raced with
   420  	// another call to ensureMappable).
   421  	d.mapsMu.Lock()
   422  	defer d.mapsMu.Unlock()
   423  	d.dataMu.Lock()
   424  	defer d.dataMu.Unlock()
   425  	if d.wrappedMappable == nil {
   426  		d.wrappedMappable = opts.Mappable
   427  		d.isMappable.Store(1)
   428  	}
   429  	return nil
   430  }
   431  
   432  // AddMapping implements memmap.Mappable.AddMapping.
   433  func (d *dentry) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) error {
   434  	d.mapsMu.Lock()
   435  	defer d.mapsMu.Unlock()
   436  	if err := d.wrappedMappable.AddMapping(ctx, ms, ar, offset, writable); err != nil {
   437  		return err
   438  	}
   439  	if !d.isCopiedUp() {
   440  		d.lowerMappings.AddMapping(ms, ar, offset, writable)
   441  	}
   442  	return nil
   443  }
   444  
   445  // RemoveMapping implements memmap.Mappable.RemoveMapping.
   446  func (d *dentry) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) {
   447  	d.mapsMu.Lock()
   448  	defer d.mapsMu.Unlock()
   449  	d.wrappedMappable.RemoveMapping(ctx, ms, ar, offset, writable)
   450  	if !d.isCopiedUp() {
   451  		d.lowerMappings.RemoveMapping(ms, ar, offset, writable)
   452  	}
   453  }
   454  
   455  // CopyMapping implements memmap.Mappable.CopyMapping.
   456  func (d *dentry) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR hostarch.AddrRange, offset uint64, writable bool) error {
   457  	d.mapsMu.Lock()
   458  	defer d.mapsMu.Unlock()
   459  	if err := d.wrappedMappable.CopyMapping(ctx, ms, srcAR, dstAR, offset, writable); err != nil {
   460  		return err
   461  	}
   462  	if !d.isCopiedUp() {
   463  		d.lowerMappings.AddMapping(ms, dstAR, offset, writable)
   464  	}
   465  	return nil
   466  }
   467  
   468  // Translate implements memmap.Mappable.Translate.
   469  func (d *dentry) Translate(ctx context.Context, required, optional memmap.MappableRange, at hostarch.AccessType) ([]memmap.Translation, error) {
   470  	d.dataMu.RLock()
   471  	defer d.dataMu.RUnlock()
   472  	return d.wrappedMappable.Translate(ctx, required, optional, at)
   473  }
   474  
   475  // InvalidateUnsavable implements memmap.Mappable.InvalidateUnsavable.
   476  func (d *dentry) InvalidateUnsavable(ctx context.Context) error {
   477  	d.mapsMu.Lock()
   478  	defer d.mapsMu.Unlock()
   479  	return d.wrappedMappable.InvalidateUnsavable(ctx)
   480  }