github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/sentry/vfs/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 vfs
    16  
    17  import (
    18  	goContext "context"
    19  	"fmt"
    20  	"sync/atomic"
    21  
    22  	"github.com/metacubex/gvisor/pkg/context"
    23  	"github.com/metacubex/gvisor/pkg/refs"
    24  	"github.com/metacubex/gvisor/pkg/waiter"
    25  )
    26  
    27  // ErrCorruption indicates a failed restore due to external file system state in
    28  // corruption.
    29  type ErrCorruption struct {
    30  	// Err is the wrapped error.
    31  	Err error
    32  }
    33  
    34  // Error returns a sensible description of the restore error.
    35  func (e ErrCorruption) Error() string {
    36  	return "restore failed due to external file system state in corruption: " + e.Err.Error()
    37  }
    38  
    39  // FilesystemImplSaveRestoreExtension is an optional extension to
    40  // FilesystemImpl.
    41  type FilesystemImplSaveRestoreExtension interface {
    42  	// PrepareSave prepares this filesystem for serialization.
    43  	PrepareSave(ctx context.Context) error
    44  
    45  	// CompleteRestore completes restoration from checkpoint for this
    46  	// filesystem after deserialization.
    47  	CompleteRestore(ctx context.Context, opts CompleteRestoreOptions) error
    48  }
    49  
    50  // PrepareSave prepares all filesystems for serialization.
    51  func (vfs *VirtualFilesystem) PrepareSave(ctx context.Context) error {
    52  	for fs := range vfs.getFilesystems() {
    53  		if ext, ok := fs.impl.(FilesystemImplSaveRestoreExtension); ok {
    54  			if err := ext.PrepareSave(ctx); err != nil {
    55  				fs.DecRef(ctx)
    56  				return err
    57  			}
    58  		}
    59  		fs.DecRef(ctx)
    60  	}
    61  	return nil
    62  }
    63  
    64  // CompleteRestore completes restoration from checkpoint for all filesystems
    65  // after deserialization.
    66  func (vfs *VirtualFilesystem) CompleteRestore(ctx context.Context, opts *CompleteRestoreOptions) error {
    67  	for fs := range vfs.getFilesystems() {
    68  		if ext, ok := fs.impl.(FilesystemImplSaveRestoreExtension); ok {
    69  			if err := ext.CompleteRestore(ctx, *opts); err != nil {
    70  				fs.DecRef(ctx)
    71  				return err
    72  			}
    73  		}
    74  		fs.DecRef(ctx)
    75  	}
    76  	return nil
    77  }
    78  
    79  // CompleteRestoreOptions contains options to
    80  // VirtualFilesystem.CompleteRestore() and
    81  // FilesystemImplSaveRestoreExtension.CompleteRestore().
    82  type CompleteRestoreOptions struct {
    83  	// If ValidateFileSizes is true, filesystem implementations backed by
    84  	// remote filesystems should verify that file sizes have not changed
    85  	// between checkpoint and restore.
    86  	ValidateFileSizes bool
    87  
    88  	// If ValidateFileModificationTimestamps is true, filesystem
    89  	// implementations backed by remote filesystems should validate that file
    90  	// mtimes have not changed between checkpoint and restore.
    91  	ValidateFileModificationTimestamps bool
    92  }
    93  
    94  // saveMounts is called by stateify.
    95  func (vfs *VirtualFilesystem) saveMounts() []*Mount {
    96  	if atomic.LoadPointer(&vfs.mounts.slots) == nil {
    97  		// vfs.Init() was never called.
    98  		return nil
    99  	}
   100  	var mounts []*Mount
   101  	vfs.mounts.Range(func(mount *Mount) bool {
   102  		mounts = append(mounts, mount)
   103  		return true
   104  	})
   105  	return mounts
   106  }
   107  
   108  // saveKey is called by stateify.
   109  func (mnt *Mount) saveKey() VirtualDentry { return mnt.getKey() }
   110  
   111  // loadMounts is called by stateify.
   112  func (vfs *VirtualFilesystem) loadMounts(_ goContext.Context, mounts []*Mount) {
   113  	if mounts == nil {
   114  		return
   115  	}
   116  	vfs.mounts.Init()
   117  	for _, mount := range mounts {
   118  		vfs.mounts.Insert(mount)
   119  	}
   120  }
   121  
   122  // loadKey is called by stateify.
   123  func (mnt *Mount) loadKey(_ goContext.Context, vd VirtualDentry) { mnt.setKey(vd) }
   124  
   125  // afterLoad is called by stateify.
   126  func (mnt *Mount) afterLoad(goContext.Context) {
   127  	if mnt.refs.Load() != 0 {
   128  		refs.Register(mnt)
   129  	}
   130  }
   131  
   132  // afterLoad is called by stateify.
   133  func (epi *epollInterest) afterLoad(goContext.Context) {
   134  	// Mark all epollInterests as ready after restore so that the next call to
   135  	// EpollInstance.ReadEvents() rechecks their readiness.
   136  	epi.waiter.NotifyEvent(waiter.EventMaskFromLinux(epi.mask))
   137  }
   138  
   139  // RestoreID is a unique ID that is used to identify resources between save/restore sessions.
   140  // Example of resources are host files, gofer connection for mount points, etc.
   141  //
   142  // +stateify savable
   143  type RestoreID struct {
   144  	// ContainerName is the name of the container that the resource belongs to.
   145  	ContainerName string
   146  	// Path is the path of the resource.
   147  	Path string
   148  }
   149  
   150  func (f RestoreID) String() string {
   151  	return fmt.Sprintf("%s:%s", f.ContainerName, f.Path)
   152  }