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 }