gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/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 goContext "context" 19 "fmt" 20 "io" 21 22 "gvisor.dev/gvisor/pkg/abi/linux" 23 "gvisor.dev/gvisor/pkg/atomicbitops" 24 "gvisor.dev/gvisor/pkg/context" 25 "gvisor.dev/gvisor/pkg/errors/linuxerr" 26 "gvisor.dev/gvisor/pkg/fdnotifier" 27 "gvisor.dev/gvisor/pkg/hostarch" 28 "gvisor.dev/gvisor/pkg/refs" 29 "gvisor.dev/gvisor/pkg/safemem" 30 "gvisor.dev/gvisor/pkg/sentry/pgalloc" 31 "gvisor.dev/gvisor/pkg/sentry/vfs" 32 ) 33 34 // +stateify savable 35 type savedDentryRW struct { 36 read bool 37 write bool 38 } 39 40 // PrepareSave implements vfs.FilesystemImplSaveRestoreExtension.PrepareSave. 41 func (fs *filesystem) PrepareSave(ctx context.Context) error { 42 if len(fs.iopts.UniqueID.Path) == 0 { 43 return fmt.Errorf("gofer.filesystem with no UniqueID cannot be saved") 44 } 45 46 // Purge cached dentries, which may not be reopenable after restore due to 47 // permission changes. 48 fs.renameMu.Lock() 49 fs.evictAllCachedDentriesLocked(ctx) 50 fs.renameMu.Unlock() 51 52 // Buffer pipe data so that it's available for reading after restore. (This 53 // is a legacy VFS1 feature.) 54 fs.syncMu.Lock() 55 for sffd := fs.specialFileFDs.Front(); sffd != nil; sffd = sffd.Next() { 56 if sffd.dentry().fileType() == linux.S_IFIFO && sffd.vfsfd.IsReadable() { 57 if err := sffd.savePipeData(ctx); err != nil { 58 fs.syncMu.Unlock() 59 return err 60 } 61 } 62 } 63 fs.syncMu.Unlock() 64 65 // Flush local state to the remote filesystem. 66 if err := fs.Sync(ctx); err != nil { 67 return err 68 } 69 70 fs.savedDentryRW = make(map[*dentry]savedDentryRW) 71 return fs.root.prepareSaveRecursive(ctx) 72 } 73 74 // Preconditions: 75 // - fd represents a pipe. 76 // - fd is readable. 77 func (fd *specialFileFD) savePipeData(ctx context.Context) error { 78 fd.bufMu.Lock() 79 defer fd.bufMu.Unlock() 80 var buf [hostarch.PageSize]byte 81 for { 82 n, err := fd.handle.readToBlocksAt(ctx, safemem.BlockSeqOf(safemem.BlockFromSafeSlice(buf[:])), ^uint64(0)) 83 if n != 0 { 84 fd.buf = append(fd.buf, buf[:n]...) 85 } 86 if err != nil { 87 if err == io.EOF || linuxerr.Equals(linuxerr.EAGAIN, err) { 88 break 89 } 90 return err 91 } 92 } 93 if len(fd.buf) != 0 { 94 fd.haveBuf.Store(1) 95 } 96 return nil 97 } 98 99 func (d *dentry) prepareSaveRecursive(ctx context.Context) error { 100 if d.isRegularFile() && !d.cachedMetadataAuthoritative() { 101 // Get updated metadata for d in case we need to perform metadata 102 // validation during restore. 103 if err := d.updateMetadata(ctx); err != nil { 104 return err 105 } 106 } 107 if d.isReadHandleOk() || d.isWriteHandleOk() { 108 d.fs.savedDentryRW[d] = savedDentryRW{ 109 read: d.isReadHandleOk(), 110 write: d.isWriteHandleOk(), 111 } 112 } 113 d.childrenMu.Lock() 114 defer d.childrenMu.Unlock() 115 for childName, child := range d.children { 116 if child == nil { 117 // Unsaved filesystem state may change across save/restore. Remove 118 // negative entries from d.children to ensure that files created 119 // after save are visible after restore. 120 delete(d.children, childName) 121 continue 122 } 123 if err := child.prepareSaveRecursive(ctx); err != nil { 124 return err 125 } 126 } 127 return nil 128 } 129 130 // beforeSave is invoked by stateify. 131 func (d *dentry) beforeSave() { 132 if d.vfsd.IsDead() { 133 panic(fmt.Sprintf("gofer.dentry(%q).beforeSave: deleted and invalidated dentries can't be restored", genericDebugPathname(d))) 134 } 135 } 136 137 // afterLoad is invoked by stateify. 138 func (fs *filesystem) afterLoad(ctx goContext.Context) { 139 fs.mf = pgalloc.MemoryFileFromContext(ctx) 140 } 141 142 // afterLoad is invoked by stateify. 143 func (d *dentry) afterLoad(goContext.Context) { 144 d.readFD = atomicbitops.FromInt32(-1) 145 d.writeFD = atomicbitops.FromInt32(-1) 146 d.mmapFD = atomicbitops.FromInt32(-1) 147 if d.refs.Load() != -1 { 148 refs.Register(d) 149 } 150 } 151 152 // afterLoad is invoked by stateify. 153 func (d *directfsDentry) afterLoad(goContext.Context) { 154 d.controlFD = -1 155 } 156 157 // afterLoad is invoked by stateify. 158 func (d *dentryPlatformFile) afterLoad(goContext.Context) { 159 if d.hostFileMapper.IsInited() { 160 // Ensure that we don't call d.hostFileMapper.Init() again. 161 d.hostFileMapperInitOnce.Do(func() {}) 162 } 163 } 164 165 // afterLoad is invoked by stateify. 166 func (fd *specialFileFD) afterLoad(goContext.Context) { 167 fd.handle.fd = -1 168 if fd.hostFileMapper.IsInited() { 169 // Ensure that we don't call fd.hostFileMapper.Init() again. 170 fd.hostFileMapperInitOnce.Do(func() {}) 171 } 172 } 173 174 // saveParent is called by stateify. 175 func (d *dentry) saveParent() *dentry { 176 return d.parent.Load() 177 } 178 179 // loadParent is called by stateify. 180 func (d *dentry) loadParent(_ goContext.Context, parent *dentry) { 181 d.parent.Store(parent) 182 } 183 184 // CompleteRestore implements 185 // vfs.FilesystemImplSaveRestoreExtension.CompleteRestore. 186 func (fs *filesystem) CompleteRestore(ctx context.Context, opts vfs.CompleteRestoreOptions) error { 187 fdmap := vfs.RestoreFilesystemFDMapFromContext(ctx) 188 if fdmap == nil { 189 return fmt.Errorf("no server FD map available") 190 } 191 fd, ok := fdmap[fs.iopts.UniqueID] 192 if !ok { 193 return fmt.Errorf("no server FD available for filesystem with unique ID %+v, map: %v", fs.iopts.UniqueID, fdmap) 194 } 195 fs.opts.fd = fd 196 fs.inoByKey = make(map[inoKey]uint64) 197 198 if err := fs.restoreRoot(ctx, &opts); err != nil { 199 return err 200 } 201 202 // Restore remaining dentries. 203 if err := fs.root.restoreDescendantsRecursive(ctx, &opts); err != nil { 204 return err 205 } 206 207 // Re-open handles for specialFileFDs. Unlike the initial open 208 // (dentry.openSpecialFile()), pipes are always opened without blocking; 209 // non-readable pipe FDs are opened last to ensure that they don't get 210 // ENXIO if another specialFileFD represents the read end of the same pipe. 211 // This is consistent with VFS1. 212 haveWriteOnlyPipes := false 213 for fd := fs.specialFileFDs.Front(); fd != nil; fd = fd.Next() { 214 if fd.dentry().fileType() == linux.S_IFIFO && !fd.vfsfd.IsReadable() { 215 haveWriteOnlyPipes = true 216 continue 217 } 218 if err := fd.completeRestore(ctx); err != nil { 219 return err 220 } 221 } 222 if haveWriteOnlyPipes { 223 for fd := fs.specialFileFDs.Front(); fd != nil; fd = fd.Next() { 224 if fd.dentry().fileType() == linux.S_IFIFO && !fd.vfsfd.IsReadable() { 225 if err := fd.completeRestore(ctx); err != nil { 226 return err 227 } 228 } 229 } 230 } 231 232 // Discard state only required during restore. 233 fs.savedDentryRW = nil 234 235 return nil 236 } 237 238 // Preconditions: d is not synthetic. 239 func (d *dentry) restoreDescendantsRecursive(ctx context.Context, opts *vfs.CompleteRestoreOptions) error { 240 d.childrenMu.Lock() 241 defer d.childrenMu.Unlock() 242 for _, child := range d.children { 243 if child == nil { 244 continue 245 } 246 if child.isSynthetic() { 247 continue 248 } 249 if err := child.restoreFile(ctx, opts); err != nil { 250 return err 251 } 252 if err := child.restoreDescendantsRecursive(ctx, opts); err != nil { 253 return err 254 } 255 } 256 return nil 257 } 258 259 func (fd *specialFileFD) completeRestore(ctx context.Context) error { 260 d := fd.dentry() 261 h, err := d.openHandle(ctx, fd.vfsfd.IsReadable(), fd.vfsfd.IsWritable(), false /* trunc */) 262 if err != nil { 263 return err 264 } 265 fd.handle = h 266 267 ftype := d.fileType() 268 fd.haveQueue = (ftype == linux.S_IFIFO || ftype == linux.S_IFSOCK) && fd.handle.fd >= 0 269 if fd.haveQueue { 270 if err := fdnotifier.AddFD(fd.handle.fd, &fd.queue); err != nil { 271 return err 272 } 273 } 274 275 return nil 276 }