gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/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 "gvisor.dev/gvisor/pkg/context" 19 "gvisor.dev/gvisor/pkg/sentry/vfs" 20 "gvisor.dev/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 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, ds) 57 rp.Release(ctx) 58 return err 59 } 60 61 // revalidateOne does the same as revalidatePath, but checks a single dentry. 62 // 63 // Preconditions: 64 // - fs.renameMu must be locked. 65 // - parent must have up to date metadata. 66 func (fs *filesystem) revalidateOne(ctx context.Context, vfsObj *vfs.VirtualFilesystem, parent *dentry, name string, ds **[]*dentry) error { 67 // Skip revalidation for interop mode different than InteropModeShared or 68 // if the parent is synthetic (child must be synthetic too, but it cannot be 69 // replaced without first replacing the parent). 70 if parent.cachedMetadataAuthoritative() { 71 return nil 72 } 73 74 parent.childrenMu.Lock() 75 child, ok := parent.children[name] 76 parent.childrenMu.Unlock() 77 if !ok { 78 return nil 79 } 80 81 state := makeRevalidateState(parent, false /* refreshStart */) 82 defer state.release() 83 // Note that child can not be nil, because we don't cache negative entries 84 // when InteropModeShared is in effect. 85 state.add(child) 86 return state.doRevalidation(ctx, vfsObj, ds) 87 } 88 89 // revalidate revalidates path components in rp until done returns true, or 90 // until a mount point or symlink is reached. It may send multiple MultiGetAttr 91 // calls to the gofer to handle ".." in the path. 92 // 93 // Preconditions: 94 // - fs.renameMu must be locked. 95 // - InteropModeShared is in effect. 96 func (fs *filesystem) revalidate(ctx context.Context, rp resolvingPath, start *dentry, ds **[]*dentry) error { 97 state := makeRevalidateState(start, true /* refreshStart */) 98 defer state.release() 99 100 done: 101 for cur := start; !rp.done(); { 102 var err error 103 cur, err = fs.revalidateStep(ctx, rp, cur, state) 104 if err != nil { 105 switch err.(type) { 106 case errPartialRevalidation: 107 if err := state.doRevalidation(ctx, rp.VirtualFilesystem(), ds); err != nil { 108 return err 109 } 110 111 // Reset state to release any remaining locks and restart from where 112 // stepping stopped. 113 state.reset(cur /* start */, true /* refreshStart */) 114 115 case errRevalidationStepDone: 116 break done 117 118 default: 119 return err 120 } 121 } 122 } 123 return state.doRevalidation(ctx, rp.VirtualFilesystem(), ds) 124 } 125 126 // revalidateStep walks one element of the path and updates revalidationState 127 // with the dentry if needed. It may also stop the stepping or ask for a 128 // partial revalidation. Partial revalidation requires the caller to revalidate 129 // the current revalidationState, release all locks, and resume stepping. 130 // In case a symlink is hit, revalidation stops and the caller is responsible 131 // for calling revalidate again after the symlink is resolved. Revalidation may 132 // also stop for other reasons, like hitting a child not in the cache. 133 // 134 // Returns: 135 // - (dentry, nil): step worked, continue stepping.` 136 // - (dentry, errPartialRevalidation): revalidation should be done with the 137 // state gathered so far. Then continue stepping with the remainder of the 138 // path, starting at `dentry`. 139 // - (nil, errRevalidationStepDone): revalidation doesn't need to step any 140 // further. It hit a symlink, a mount point, or an uncached dentry. 141 // 142 // Preconditions: 143 // - fs.renameMu must be locked. 144 // - !rp.Done(). 145 // - InteropModeShared is in effect (assumes no negative dentries). 146 func (fs *filesystem) revalidateStep(ctx context.Context, rp resolvingPath, d *dentry, state *revalidateState) (*dentry, error) { 147 switch name := rp.Component(); name { 148 case ".": 149 // Do nothing. 150 151 case "..": 152 // Partial revalidation is required when ".." is hit because metadata locks 153 // can only be acquired from parent to child to avoid deadlocks. 154 if isRoot, err := rp.CheckRoot(ctx, &d.vfsd); err != nil { 155 return nil, errRevalidationStepDone{} 156 } else if isRoot || d.parent.Load() == nil { 157 rp.Advance() 158 return d, errPartialRevalidation{} 159 } 160 // We must assume that d.parent is correct, because if d has been moved 161 // elsewhere in the remote filesystem so that its parent has changed, 162 // we have no way of determining its new parent's location in the 163 // filesystem. 164 // 165 // Call rp.CheckMount() before updating d.parent's metadata, since if 166 // we traverse to another mount then d.parent's metadata is irrelevant. 167 if err := rp.CheckMount(ctx, &d.parent.Load().vfsd); err != nil { 168 return nil, errRevalidationStepDone{} 169 } 170 rp.Advance() 171 return d.parent.Load(), errPartialRevalidation{} 172 173 default: 174 d.childrenMu.Lock() 175 child, ok := d.children[name] 176 d.childrenMu.Unlock() 177 if !ok { 178 // child is not cached, no need to validate any further. 179 return nil, errRevalidationStepDone{} 180 } 181 182 // Note that child can not be nil, because we don't cache negative entries 183 // when InteropModeShared is in effect. 184 state.add(child) 185 186 // Symlink must be resolved before continuing with revalidation. 187 if child.isSymlink() { 188 return nil, errRevalidationStepDone{} 189 } 190 191 d = child 192 } 193 194 rp.Advance() 195 return d, nil 196 } 197 198 // Precondition: fs.renameMu must be locked. 199 func (d *dentry) invalidate(ctx context.Context, vfsObj *vfs.VirtualFilesystem, ds **[]*dentry) { 200 // Remove d from its parent. 201 func() { 202 parent := d.parent.Load() 203 parent.opMu.RLock() 204 defer parent.opMu.RUnlock() 205 parent.childrenMu.Lock() 206 defer parent.childrenMu.Unlock() 207 208 if d.isSynthetic() { 209 // Normally we don't mark invalidated dentries as deleted since 210 // they may still exist (but at a different path), and also for 211 // consistency with Linux. However, synthetic files are guaranteed 212 // to become unreachable if their dentries are invalidated, so 213 // treat their invalidation as deletion. 214 d.deleteSynthetic(parent, ds) 215 } 216 217 // Since the opMu was just reacquired above, re-check that the 218 // parent's child with this name is still the same. Do not touch it if 219 // it has been replaced with a different one. 220 if child := parent.children[d.name]; child == d { 221 // Invalidate dentry so it gets reloaded next time it's accessed. 222 delete(parent.children, d.name) 223 } 224 }() 225 226 // Invalidate d and its descendants. 227 toInvalidate := []*dentry{d} 228 for len(toInvalidate) != 0 { 229 d := toInvalidate[len(toInvalidate)-1] 230 toInvalidate = toInvalidate[:len(toInvalidate)-1] 231 232 // If the dentry is a mountpoint, InvalidateDentry may drop the 233 // last reference on it, resulting in lock recursion. To avoid 234 // this, take a dentry reference first, then drop it while 235 // deferring the call to dentry.checkCachingLocked(). 236 d.IncRef() 237 rcs := vfsObj.InvalidateDentry(ctx, &d.vfsd) 238 for _, rc := range rcs { 239 rc.DecRef(ctx) 240 } 241 d.decRefNoCaching() 242 243 // Re-evaluate its caching status (i.e. if it has 0 references, drop it). 244 // The dentry will be reloaded next time it's accessed. 245 *ds = appendDentry(*ds, d) 246 247 if d.isDir() { 248 toInvalidate = d.disownAllChildrenForInvalidation(ctx, vfsObj, toInvalidate, ds) 249 } 250 } 251 } 252 253 // +checklocks:parent.childrenMu 254 func (d *dentry) deleteSynthetic(parent *dentry, ds **[]*dentry) { 255 d.setDeleted() 256 d.decRefNoCaching() 257 *ds = appendDentry(*ds, d) 258 parent.syntheticChildren-- 259 parent.clearDirentsLocked() 260 } 261 262 // disownAllChildrenForInvalidation removes all child dentries from d, appends 263 // them to children, and returns an updated slice. Consistent with 264 // dentry.invalidate(), removed synthetic dentries are marked deleted. 265 // 266 // Precondition: fs.renameMu must be locked. 267 func (d *dentry) disownAllChildrenForInvalidation(ctx context.Context, vfsObj *vfs.VirtualFilesystem, children []*dentry, ds **[]*dentry) []*dentry { 268 d.opMu.RLock() 269 defer d.opMu.RUnlock() 270 d.childrenMu.Lock() 271 defer d.childrenMu.Unlock() 272 for name, child := range d.children { 273 children = append(children, child) 274 delete(d.children, name) 275 if child.isSynthetic() { 276 child.deleteSynthetic(d, ds) 277 } 278 } 279 return children 280 } 281 282 // revalidateStatePool caches revalidateState instances to save array 283 // allocations for dentries and names. 284 var revalidateStatePool = sync.Pool{ 285 New: func() any { 286 return &revalidateState{} 287 }, 288 } 289 290 // revalidateState keeps state related to a revalidation request. It keeps track 291 // of {name, dentry} list being revalidated, as well as metadata locks on the 292 // dentries. The list must be in ancestry order, in other words `n` must be 293 // `n-1` child. 294 type revalidateState struct { 295 // start is the dentry where to start the revalidation of dentries. 296 start *dentry 297 298 // refreshStart indicates whether the attributes of the start dentry should 299 // be refreshed. 300 refreshStart bool 301 302 // names is just a slice of names which can be used while making LISAFS RPCs. 303 // This exists to avoid the cost of repeated string slice allocation to make 304 // RPCs. 305 names []string 306 307 // dentries is the list of dentries that need to be revalidated. The first 308 // dentry is a child of start and each successive dentry is a child of the 309 // previous. 310 dentries []*dentry 311 } 312 313 func makeRevalidateState(start *dentry, refreshStart bool) *revalidateState { 314 r := revalidateStatePool.Get().(*revalidateState) 315 r.start = start 316 r.refreshStart = refreshStart 317 return r 318 } 319 320 // release must be called after the caller is done with this object. It releases 321 // all metadata locks and resources. 322 func (r *revalidateState) release() { 323 r.reset(nil /* start */, false /* refreshStart */) 324 revalidateStatePool.Put(r) 325 } 326 327 // Preconditions: 328 // - d != nil. 329 // - d is a descendant of all dentries in r.dentries. 330 func (r *revalidateState) add(d *dentry) { 331 r.dentries = append(r.dentries, d) 332 } 333 334 // reset releases all metadata locks and resets all fields to allow this 335 // instance to be reused. 336 // +checklocksignore 337 func (r *revalidateState) reset(start *dentry, refreshStart bool) { 338 r.start = start 339 r.refreshStart = refreshStart 340 r.names = r.names[:0] 341 r.dentries = r.dentries[:0] 342 }