gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/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 "gvisor.dev/gvisor/pkg/context" 23 "gvisor.dev/gvisor/pkg/refs" 24 "gvisor.dev/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 // saveMountPromises is called by stateify. 112 func (vfs *VirtualFilesystem) saveMountPromises() map[VirtualDentry]*mountPromise { 113 m := make(map[VirtualDentry]*mountPromise) 114 vfs.mountPromises.Range(func(key any, val any) bool { 115 m[key.(VirtualDentry)] = val.(*mountPromise) 116 return true 117 }) 118 return m 119 } 120 121 // loadMounts is called by stateify. 122 func (vfs *VirtualFilesystem) loadMounts(_ goContext.Context, mounts []*Mount) { 123 if mounts == nil { 124 return 125 } 126 vfs.mounts.Init() 127 for _, mount := range mounts { 128 vfs.mounts.Insert(mount) 129 } 130 } 131 132 // loadKey is called by stateify. 133 func (mnt *Mount) loadKey(_ goContext.Context, vd VirtualDentry) { mnt.setKey(vd) } 134 135 // loadMountPromises is called by stateify. 136 func (vfs *VirtualFilesystem) loadMountPromises(_ goContext.Context, mps map[VirtualDentry]*mountPromise) { 137 for vd, mp := range mps { 138 vfs.mountPromises.Store(vd, mp) 139 } 140 } 141 142 // afterLoad is called by stateify. 143 func (mnt *Mount) afterLoad(goContext.Context) { 144 if mnt.refs.Load() != 0 { 145 refs.Register(mnt) 146 } 147 } 148 149 // afterLoad is called by stateify. 150 func (epi *epollInterest) afterLoad(goContext.Context) { 151 // Mark all epollInterests as ready after restore so that the next call to 152 // EpollInstance.ReadEvents() rechecks their readiness. 153 epi.waiter.NotifyEvent(waiter.EventMaskFromLinux(epi.mask)) 154 } 155 156 // RestoreID is a unique ID that is used to identify resources between save/restore sessions. 157 // Example of resources are host files, gofer connection for mount points, etc. 158 // 159 // +stateify savable 160 type RestoreID struct { 161 // ContainerName is the name of the container that the resource belongs to. 162 ContainerName string 163 // Path is the path of the resource. 164 Path string 165 } 166 167 func (f RestoreID) String() string { 168 return fmt.Sprintf("%s:%s", f.ContainerName, f.Path) 169 }