github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fsimpl/gofer/revalidate.go (about) 1 // Copyright 2021 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 "github.com/SagerNet/gvisor/pkg/context" 19 "github.com/SagerNet/gvisor/pkg/sentry/vfs" 20 "github.com/SagerNet/gvisor/pkg/sync" 21 ) 22 23 type errPartialRevalidation struct{} 24 25 // Error implements error.Error. 26 func (errPartialRevalidation) Error() string { 27 return "partial revalidation" 28 } 29 30 type errRevalidationStepDone struct{} 31 32 // Error implements error.Error. 33 func (errRevalidationStepDone) Error() string { 34 return "stop revalidation" 35 } 36 37 // revalidatePath checks cached dentries for external modification. File 38 // attributes are refreshed and cache is invalidated in case the dentry has been 39 // deleted, or a new file/directory created in its place. 40 // 41 // Revalidation stops at symlinks and mount points. The caller is responsible 42 // for revalidating again after symlinks are resolved and after changing to 43 // different mounts. 44 // 45 // Preconditions: 46 // * fs.renameMu must be locked. 47 func (fs *filesystem) revalidatePath(ctx context.Context, rpOrig *vfs.ResolvingPath, start *dentry, ds **[]*dentry) error { 48 // Revalidation is done even if start is synthetic in case the path is 49 // something like: ../non_synthetic_file. 50 if fs.opts.interop != InteropModeShared { 51 return nil 52 } 53 54 // Copy resolving path to walk the path for revalidation. 55 rp := rpOrig.Copy() 56 err := fs.revalidate(ctx, rp, start, rp.Done, ds) 57 rp.Release(ctx) 58 return err 59 } 60 61 // revalidateParentDir does the same as revalidatePath, but stops at the parent. 62 // 63 // Preconditions: 64 // * fs.renameMu must be locked. 65 func (fs *filesystem) revalidateParentDir(ctx context.Context, rpOrig *vfs.ResolvingPath, start *dentry, ds **[]*dentry) error { 66 // Revalidation is done even if start is synthetic in case the path is 67 // something like: ../non_synthetic_file and parent is non synthetic. 68 if fs.opts.interop != InteropModeShared { 69 return nil 70 } 71 72 // Copy resolving path to walk the path for revalidation. 73 rp := rpOrig.Copy() 74 err := fs.revalidate(ctx, rp, start, rp.Final, ds) 75 rp.Release(ctx) 76 return err 77 } 78 79 // revalidateOne does the same as revalidatePath, but checks a single dentry. 80 // 81 // Preconditions: 82 // * fs.renameMu must be locked. 83 func (fs *filesystem) revalidateOne(ctx context.Context, vfsObj *vfs.VirtualFilesystem, parent *dentry, name string, ds **[]*dentry) error { 84 // Skip revalidation for interop mode different than InteropModeShared or 85 // if the parent is synthetic (child must be synthetic too, but it cannot be 86 // replaced without first replacing the parent). 87 if parent.cachedMetadataAuthoritative() { 88 return nil 89 } 90 91 parent.dirMu.Lock() 92 child, ok := parent.children[name] 93 parent.dirMu.Unlock() 94 if !ok { 95 return nil 96 } 97 98 state := makeRevalidateState(parent) 99 defer state.release() 100 101 state.add(name, child) 102 return fs.revalidateHelper(ctx, vfsObj, state, ds) 103 } 104 105 // revalidate revalidates path components in rp until done returns true, or 106 // until a mount point or symlink is reached. It may send multiple MultiGetAttr 107 // calls to the gofer to handle ".." in the path. 108 // 109 // Preconditions: 110 // * fs.renameMu must be locked. 111 // * InteropModeShared is in effect. 112 func (fs *filesystem) revalidate(ctx context.Context, rp *vfs.ResolvingPath, start *dentry, done func() bool, ds **[]*dentry) error { 113 state := makeRevalidateState(start) 114 defer state.release() 115 116 // Skip synthetic dentries because the start dentry cannot be replaced in case 117 // it has been created in the remote file system. 118 if !start.isSynthetic() { 119 state.add("", start) 120 } 121 122 done: 123 for cur := start; !done(); { 124 var err error 125 cur, err = fs.revalidateStep(ctx, rp, cur, state) 126 if err != nil { 127 switch err.(type) { 128 case errPartialRevalidation: 129 if err := fs.revalidateHelper(ctx, rp.VirtualFilesystem(), state, ds); err != nil { 130 return err 131 } 132 133 // Reset state to release any remaining locks and restart from where 134 // stepping stopped. 135 state.reset() 136 state.start = cur 137 138 // Skip synthetic dentries because the start dentry cannot be replaced in 139 // case it has been created in the remote file system. 140 if !cur.isSynthetic() { 141 state.add("", cur) 142 } 143 144 case errRevalidationStepDone: 145 break done 146 147 default: 148 return err 149 } 150 } 151 } 152 return fs.revalidateHelper(ctx, rp.VirtualFilesystem(), state, ds) 153 } 154 155 // revalidateStep walks one element of the path and updates revalidationState 156 // with the dentry if needed. It may also stop the stepping or ask for a 157 // partial revalidation. Partial revalidation requires the caller to revalidate 158 // the current revalidationState, release all locks, and resume stepping. 159 // In case a symlink is hit, revalidation stops and the caller is responsible 160 // for calling revalidate again after the symlink is resolved. Revalidation may 161 // also stop for other reasons, like hitting a child not in the cache. 162 // 163 // Returns: 164 // * (dentry, nil): step worked, continue stepping.` 165 // * (dentry, errPartialRevalidation): revalidation should be done with the 166 // state gathered so far. Then continue stepping with the remainder of the 167 // path, starting at `dentry`. 168 // * (nil, errRevalidationStepDone): revalidation doesn't need to step any 169 // further. It hit a symlink, a mount point, or an uncached dentry. 170 // 171 // Preconditions: 172 // * fs.renameMu must be locked. 173 // * !rp.Done(). 174 // * InteropModeShared is in effect (assumes no negative dentries). 175 func (fs *filesystem) revalidateStep(ctx context.Context, rp *vfs.ResolvingPath, d *dentry, state *revalidateState) (*dentry, error) { 176 switch name := rp.Component(); name { 177 case ".": 178 // Do nothing. 179 180 case "..": 181 // Partial revalidation is required when ".." is hit because metadata locks 182 // can only be acquired from parent to child to avoid deadlocks. 183 if isRoot, err := rp.CheckRoot(ctx, &d.vfsd); err != nil { 184 return nil, errRevalidationStepDone{} 185 } else if isRoot || d.parent == nil { 186 rp.Advance() 187 return d, errPartialRevalidation{} 188 } 189 // We must assume that d.parent is correct, because if d has been moved 190 // elsewhere in the remote filesystem so that its parent has changed, 191 // we have no way of determining its new parent's location in the 192 // filesystem. 193 // 194 // Call rp.CheckMount() before updating d.parent's metadata, since if 195 // we traverse to another mount then d.parent's metadata is irrelevant. 196 if err := rp.CheckMount(ctx, &d.parent.vfsd); err != nil { 197 return nil, errRevalidationStepDone{} 198 } 199 rp.Advance() 200 return d.parent, errPartialRevalidation{} 201 202 default: 203 d.dirMu.Lock() 204 child, ok := d.children[name] 205 d.dirMu.Unlock() 206 if !ok { 207 // child is not cached, no need to validate any further. 208 return nil, errRevalidationStepDone{} 209 } 210 211 state.add(name, child) 212 213 // Symlink must be resolved before continuing with revalidation. 214 if child.isSymlink() { 215 return nil, errRevalidationStepDone{} 216 } 217 218 d = child 219 } 220 221 rp.Advance() 222 return d, nil 223 } 224 225 // revalidateHelper calls the gofer to stat all dentries in `state`. It will 226 // update or invalidate dentries in the cache based on the result. 227 // 228 // Preconditions: 229 // * fs.renameMu must be locked. 230 // * InteropModeShared is in effect. 231 func (fs *filesystem) revalidateHelper(ctx context.Context, vfsObj *vfs.VirtualFilesystem, state *revalidateState, ds **[]*dentry) error { 232 if len(state.names) == 0 { 233 return nil 234 } 235 // Lock metadata on all dentries *before* getting attributes for them. 236 state.lockAllMetadata() 237 stats, err := state.start.file.multiGetAttr(ctx, state.names) 238 if err != nil { 239 return err 240 } 241 242 i := -1 243 for d := state.popFront(); d != nil; d = state.popFront() { 244 i++ 245 found := i < len(stats) 246 if i == 0 && len(state.names[0]) == 0 { 247 if found && !d.isSynthetic() { 248 // First dentry is where the search is starting, just update attributes 249 // since it cannot be replaced. 250 d.updateFromP9AttrsLocked(stats[i].Valid, &stats[i].Attr) // +checklocksforce: acquired by lockAllMetadata. 251 } 252 d.metadataMu.Unlock() // +checklocksforce: see above. 253 continue 254 } 255 256 // Note that synthetic dentries will always fails the comparison check 257 // below. 258 if !found || d.qidPath != stats[i].QID.Path { 259 d.metadataMu.Unlock() // +checklocksforce: see above. 260 if !found && d.isSynthetic() { 261 // We have a synthetic file, and no remote file has arisen to replace 262 // it. 263 return nil 264 } 265 // The file at this path has changed or no longer exists. Mark the 266 // dentry invalidated, and re-evaluate its caching status (i.e. if it 267 // has 0 references, drop it). The dentry will be reloaded next time it's 268 // accessed. 269 vfsObj.InvalidateDentry(ctx, &d.vfsd) 270 271 name := state.names[i] 272 d.parent.dirMu.Lock() 273 274 if d.isSynthetic() { 275 // Normally we don't mark invalidated dentries as deleted since 276 // they may still exist (but at a different path), and also for 277 // consistency with Linux. However, synthetic files are guaranteed 278 // to become unreachable if their dentries are invalidated, so 279 // treat their invalidation as deletion. 280 d.setDeleted() 281 d.decRefNoCaching() 282 *ds = appendDentry(*ds, d) 283 284 d.parent.syntheticChildren-- 285 d.parent.dirents = nil 286 } 287 288 // Since the dirMu was released and reacquired, re-check that the 289 // parent's child with this name is still the same. Do not touch it if 290 // it has been replaced with a different one. 291 if child := d.parent.children[name]; child == d { 292 // Invalidate dentry so it gets reloaded next time it's accessed. 293 delete(d.parent.children, name) 294 } 295 d.parent.dirMu.Unlock() 296 297 return nil 298 } 299 300 // The file at this path hasn't changed. Just update cached metadata. 301 d.updateFromP9AttrsLocked(stats[i].Valid, &stats[i].Attr) // +checklocksforce: see above. 302 d.metadataMu.Unlock() 303 } 304 305 return nil 306 } 307 308 // revalidateStatePool caches revalidateState instances to save array 309 // allocations for dentries and names. 310 var revalidateStatePool = sync.Pool{ 311 New: func() interface{} { 312 return &revalidateState{} 313 }, 314 } 315 316 // revalidateState keeps state related to a revalidation request. It keeps track 317 // of {name, dentry} list being revalidated, as well as metadata locks on the 318 // dentries. The list must be in ancestry order, in other words `n` must be 319 // `n-1` child. 320 type revalidateState struct { 321 // start is the dentry where to start the attributes search. 322 start *dentry 323 324 // List of names of entries to refresh attributes. Names length must be the 325 // same as detries length. They are kept in separate slices because names is 326 // used to call File.MultiGetAttr(). 327 names []string 328 329 // dentries is the list of dentries that correspond to the names above. 330 // dentry.metadataMu is acquired as each dentry is added to this list. 331 dentries []*dentry 332 333 // locked indicates if metadata lock has been acquired on dentries. 334 locked bool 335 } 336 337 func makeRevalidateState(start *dentry) *revalidateState { 338 r := revalidateStatePool.Get().(*revalidateState) 339 r.start = start 340 return r 341 } 342 343 // release must be called after the caller is done with this object. It releases 344 // all metadata locks and resources. 345 func (r *revalidateState) release() { 346 r.reset() 347 revalidateStatePool.Put(r) 348 } 349 350 // Preconditions: 351 // * d is a descendant of all dentries in r.dentries. 352 func (r *revalidateState) add(name string, d *dentry) { 353 r.names = append(r.names, name) 354 r.dentries = append(r.dentries, d) 355 } 356 357 // +checklocksignore 358 func (r *revalidateState) lockAllMetadata() { 359 for _, d := range r.dentries { 360 d.metadataMu.Lock() 361 } 362 r.locked = true 363 } 364 365 func (r *revalidateState) popFront() *dentry { 366 if len(r.dentries) == 0 { 367 return nil 368 } 369 d := r.dentries[0] 370 r.dentries = r.dentries[1:] 371 return d 372 } 373 374 // reset releases all metadata locks and resets all fields to allow this 375 // instance to be reused. 376 // +checklocksignore 377 func (r *revalidateState) reset() { 378 if r.locked { 379 // Unlock any remaining dentries. 380 for _, d := range r.dentries { 381 d.metadataMu.Unlock() 382 } 383 r.locked = false 384 } 385 r.start = nil 386 r.names = r.names[:0] 387 r.dentries = r.dentries[:0] 388 }