github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fsimpl/gofer/save_restore.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 gofer
    16  
    17  import (
    18  	"fmt"
    19  	"io"
    20  	"sync/atomic"
    21  
    22  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    23  	"github.com/SagerNet/gvisor/pkg/context"
    24  	"github.com/SagerNet/gvisor/pkg/errors/linuxerr"
    25  	"github.com/SagerNet/gvisor/pkg/fdnotifier"
    26  	"github.com/SagerNet/gvisor/pkg/hostarch"
    27  	"github.com/SagerNet/gvisor/pkg/p9"
    28  	"github.com/SagerNet/gvisor/pkg/refsvfs2"
    29  	"github.com/SagerNet/gvisor/pkg/safemem"
    30  	"github.com/SagerNet/gvisor/pkg/sentry/vfs"
    31  )
    32  
    33  type saveRestoreContextID int
    34  
    35  const (
    36  	// CtxRestoreServerFDMap is a Context.Value key for a map[string]int
    37  	// mapping filesystem unique IDs (cf. InternalFilesystemOptions.UniqueID)
    38  	// to host FDs.
    39  	CtxRestoreServerFDMap saveRestoreContextID = iota
    40  )
    41  
    42  // +stateify savable
    43  type savedDentryRW struct {
    44  	read  bool
    45  	write bool
    46  }
    47  
    48  // PreprareSave implements vfs.FilesystemImplSaveRestoreExtension.PrepareSave.
    49  func (fs *filesystem) PrepareSave(ctx context.Context) error {
    50  	if len(fs.iopts.UniqueID) == 0 {
    51  		return fmt.Errorf("gofer.filesystem with no UniqueID cannot be saved")
    52  	}
    53  
    54  	// Purge cached dentries, which may not be reopenable after restore due to
    55  	// permission changes.
    56  	fs.renameMu.Lock()
    57  	fs.evictAllCachedDentriesLocked(ctx)
    58  	fs.renameMu.Unlock()
    59  
    60  	// Buffer pipe data so that it's available for reading after restore. (This
    61  	// is a legacy VFS1 feature.)
    62  	fs.syncMu.Lock()
    63  	for sffd := range fs.specialFileFDs {
    64  		if sffd.dentry().fileType() == linux.S_IFIFO && sffd.vfsfd.IsReadable() {
    65  			if err := sffd.savePipeData(ctx); err != nil {
    66  				fs.syncMu.Unlock()
    67  				return err
    68  			}
    69  		}
    70  	}
    71  	fs.syncMu.Unlock()
    72  
    73  	// Flush local state to the remote filesystem.
    74  	if err := fs.Sync(ctx); err != nil {
    75  		return err
    76  	}
    77  
    78  	fs.savedDentryRW = make(map[*dentry]savedDentryRW)
    79  	return fs.root.prepareSaveRecursive(ctx)
    80  }
    81  
    82  // Preconditions:
    83  // * fd represents a pipe.
    84  // * fd is readable.
    85  func (fd *specialFileFD) savePipeData(ctx context.Context) error {
    86  	fd.bufMu.Lock()
    87  	defer fd.bufMu.Unlock()
    88  	var buf [hostarch.PageSize]byte
    89  	for {
    90  		n, err := fd.handle.readToBlocksAt(ctx, safemem.BlockSeqOf(safemem.BlockFromSafeSlice(buf[:])), ^uint64(0))
    91  		if n != 0 {
    92  			fd.buf = append(fd.buf, buf[:n]...)
    93  		}
    94  		if err != nil {
    95  			if err == io.EOF || linuxerr.Equals(linuxerr.EAGAIN, err) {
    96  				break
    97  			}
    98  			return err
    99  		}
   100  	}
   101  	if len(fd.buf) != 0 {
   102  		atomic.StoreUint32(&fd.haveBuf, 1)
   103  	}
   104  	return nil
   105  }
   106  
   107  func (d *dentry) prepareSaveRecursive(ctx context.Context) error {
   108  	if d.isRegularFile() && !d.cachedMetadataAuthoritative() {
   109  		// Get updated metadata for d in case we need to perform metadata
   110  		// validation during restore.
   111  		if err := d.updateFromGetattr(ctx); err != nil {
   112  			return err
   113  		}
   114  	}
   115  	if !d.readFile.isNil() || !d.writeFile.isNil() {
   116  		d.fs.savedDentryRW[d] = savedDentryRW{
   117  			read:  !d.readFile.isNil(),
   118  			write: !d.writeFile.isNil(),
   119  		}
   120  	}
   121  	d.dirMu.Lock()
   122  	defer d.dirMu.Unlock()
   123  	for _, child := range d.children {
   124  		if child != nil {
   125  			if err := child.prepareSaveRecursive(ctx); err != nil {
   126  				return err
   127  			}
   128  		}
   129  	}
   130  	return nil
   131  }
   132  
   133  // beforeSave is invoked by stateify.
   134  func (d *dentry) beforeSave() {
   135  	if d.vfsd.IsDead() {
   136  		panic(fmt.Sprintf("gofer.dentry(%q).beforeSave: deleted and invalidated dentries can't be restored", genericDebugPathname(d)))
   137  	}
   138  }
   139  
   140  // afterLoad is invoked by stateify.
   141  func (d *dentry) afterLoad() {
   142  	d.readFD = -1
   143  	d.writeFD = -1
   144  	d.mmapFD = -1
   145  	if atomic.LoadInt64(&d.refs) != -1 {
   146  		refsvfs2.Register(d)
   147  	}
   148  }
   149  
   150  // afterLoad is invoked by stateify.
   151  func (d *dentryPlatformFile) afterLoad() {
   152  	if d.hostFileMapper.IsInited() {
   153  		// Ensure that we don't call d.hostFileMapper.Init() again.
   154  		d.hostFileMapperInitOnce.Do(func() {})
   155  	}
   156  }
   157  
   158  // afterLoad is invoked by stateify.
   159  func (fd *specialFileFD) afterLoad() {
   160  	fd.handle.fd = -1
   161  }
   162  
   163  // CompleteRestore implements
   164  // vfs.FilesystemImplSaveRestoreExtension.CompleteRestore.
   165  func (fs *filesystem) CompleteRestore(ctx context.Context, opts vfs.CompleteRestoreOptions) error {
   166  	fdmapv := ctx.Value(CtxRestoreServerFDMap)
   167  	if fdmapv == nil {
   168  		return fmt.Errorf("no server FD map available")
   169  	}
   170  	fdmap := fdmapv.(map[string]int)
   171  	fd, ok := fdmap[fs.iopts.UniqueID]
   172  	if !ok {
   173  		return fmt.Errorf("no server FD available for filesystem with unique ID %q", fs.iopts.UniqueID)
   174  	}
   175  	fs.opts.fd = fd
   176  	if err := fs.dial(ctx); err != nil {
   177  		return err
   178  	}
   179  	fs.inoByQIDPath = make(map[uint64]uint64)
   180  
   181  	// Restore the filesystem root.
   182  	ctx.UninterruptibleSleepStart(false)
   183  	attached, err := fs.client.Attach(fs.opts.aname)
   184  	ctx.UninterruptibleSleepFinish(false)
   185  	if err != nil {
   186  		return err
   187  	}
   188  	attachFile := p9file{attached}
   189  	qid, attrMask, attr, err := attachFile.getAttr(ctx, dentryAttrMask())
   190  	if err != nil {
   191  		return err
   192  	}
   193  	if err := fs.root.restoreFile(ctx, attachFile, qid, attrMask, &attr, &opts); err != nil {
   194  		return err
   195  	}
   196  
   197  	// Restore remaining dentries.
   198  	if err := fs.root.restoreDescendantsRecursive(ctx, &opts); err != nil {
   199  		return err
   200  	}
   201  
   202  	// Re-open handles for specialFileFDs. Unlike the initial open
   203  	// (dentry.openSpecialFile()), pipes are always opened without blocking;
   204  	// non-readable pipe FDs are opened last to ensure that they don't get
   205  	// ENXIO if another specialFileFD represents the read end of the same pipe.
   206  	// This is consistent with VFS1.
   207  	haveWriteOnlyPipes := false
   208  	for fd := range fs.specialFileFDs {
   209  		if fd.dentry().fileType() == linux.S_IFIFO && !fd.vfsfd.IsReadable() {
   210  			haveWriteOnlyPipes = true
   211  			continue
   212  		}
   213  		if err := fd.completeRestore(ctx); err != nil {
   214  			return err
   215  		}
   216  	}
   217  	if haveWriteOnlyPipes {
   218  		for fd := range fs.specialFileFDs {
   219  			if fd.dentry().fileType() == linux.S_IFIFO && !fd.vfsfd.IsReadable() {
   220  				if err := fd.completeRestore(ctx); err != nil {
   221  					return err
   222  				}
   223  			}
   224  		}
   225  	}
   226  
   227  	// Discard state only required during restore.
   228  	fs.savedDentryRW = nil
   229  
   230  	return nil
   231  }
   232  
   233  func (d *dentry) restoreFile(ctx context.Context, file p9file, qid p9.QID, attrMask p9.AttrMask, attr *p9.Attr, opts *vfs.CompleteRestoreOptions) error {
   234  	d.file = file
   235  
   236  	// Gofers do not preserve QID across checkpoint/restore, so:
   237  	//
   238  	// - We must assume that the remote filesystem did not change in a way that
   239  	// would invalidate dentries, since we can't revalidate dentries by
   240  	// checking QIDs.
   241  	//
   242  	// - We need to associate the new QID.Path with the existing d.ino.
   243  	d.qidPath = qid.Path
   244  	d.fs.inoMu.Lock()
   245  	d.fs.inoByQIDPath[qid.Path] = d.ino
   246  	d.fs.inoMu.Unlock()
   247  
   248  	// Check metadata stability before updating metadata.
   249  	d.metadataMu.Lock()
   250  	defer d.metadataMu.Unlock()
   251  	if d.isRegularFile() {
   252  		if opts.ValidateFileSizes {
   253  			if !attrMask.Size {
   254  				return fmt.Errorf("gofer.dentry(%q).restoreFile: file size validation failed: file size not available", genericDebugPathname(d))
   255  			}
   256  			if d.size != attr.Size {
   257  				return fmt.Errorf("gofer.dentry(%q).restoreFile: file size validation failed: size changed from %d to %d", genericDebugPathname(d), d.size, attr.Size)
   258  			}
   259  		}
   260  		if opts.ValidateFileModificationTimestamps {
   261  			if !attrMask.MTime {
   262  				return fmt.Errorf("gofer.dentry(%q).restoreFile: mtime validation failed: mtime not available", genericDebugPathname(d))
   263  			}
   264  			if want := dentryTimestampFromP9(attr.MTimeSeconds, attr.MTimeNanoSeconds); d.mtime != want {
   265  				return fmt.Errorf("gofer.dentry(%q).restoreFile: mtime validation failed: mtime changed from %+v to %+v", genericDebugPathname(d), linux.NsecToStatxTimestamp(d.mtime), linux.NsecToStatxTimestamp(want))
   266  			}
   267  		}
   268  	}
   269  	if !d.cachedMetadataAuthoritative() {
   270  		d.updateFromP9AttrsLocked(attrMask, attr)
   271  	}
   272  
   273  	if rw, ok := d.fs.savedDentryRW[d]; ok {
   274  		if err := d.ensureSharedHandle(ctx, rw.read, rw.write, false /* trunc */); err != nil {
   275  			return err
   276  		}
   277  	}
   278  
   279  	return nil
   280  }
   281  
   282  // Preconditions: d is not synthetic.
   283  func (d *dentry) restoreDescendantsRecursive(ctx context.Context, opts *vfs.CompleteRestoreOptions) error {
   284  	for _, child := range d.children {
   285  		if child == nil {
   286  			continue
   287  		}
   288  		if _, ok := d.fs.syncableDentries[child]; !ok {
   289  			// child is synthetic.
   290  			continue
   291  		}
   292  		if err := child.restoreRecursive(ctx, opts); err != nil {
   293  			return err
   294  		}
   295  	}
   296  	return nil
   297  }
   298  
   299  // Preconditions: d is not synthetic (but note that since this function
   300  // restores d.file, d.file.isNil() is always true at this point, so this can
   301  // only be detected by checking filesystem.syncableDentries). d.parent has been
   302  // restored.
   303  func (d *dentry) restoreRecursive(ctx context.Context, opts *vfs.CompleteRestoreOptions) error {
   304  	qid, file, attrMask, attr, err := d.parent.file.walkGetAttrOne(ctx, d.name)
   305  	if err != nil {
   306  		return err
   307  	}
   308  	if err := d.restoreFile(ctx, file, qid, attrMask, &attr, opts); err != nil {
   309  		return err
   310  	}
   311  	return d.restoreDescendantsRecursive(ctx, opts)
   312  }
   313  
   314  func (fd *specialFileFD) completeRestore(ctx context.Context) error {
   315  	d := fd.dentry()
   316  	h, err := openHandle(ctx, d.file, fd.vfsfd.IsReadable(), fd.vfsfd.IsWritable(), false /* trunc */)
   317  	if err != nil {
   318  		return err
   319  	}
   320  	fd.handle = h
   321  
   322  	ftype := d.fileType()
   323  	fd.haveQueue = (ftype == linux.S_IFIFO || ftype == linux.S_IFSOCK) && fd.handle.fd >= 0
   324  	if fd.haveQueue {
   325  		if err := fdnotifier.AddFD(fd.handle.fd, &fd.queue); err != nil {
   326  			return err
   327  		}
   328  	}
   329  
   330  	return nil
   331  }