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