github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/gofer/cache_policy.go (about) 1 // Copyright 2018 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 20 "github.com/SagerNet/gvisor/pkg/context" 21 "github.com/SagerNet/gvisor/pkg/sentry/fs" 22 ) 23 24 // cachePolicy is a 9p cache policy. It has methods that determine what to 25 // cache (if anything) for a given inode. 26 type cachePolicy int 27 28 const ( 29 // Cache nothing. 30 cacheNone cachePolicy = iota 31 32 // Use virtual file system cache for everything. 33 cacheAll 34 35 // Use virtual file system cache for everything, but send writes to the 36 // fs agent immediately. 37 cacheAllWritethrough 38 39 // Use the (host) page cache for reads/writes, but don't cache anything 40 // else. This allows the sandbox filesystem to stay in sync with any 41 // changes to the remote filesystem. 42 // 43 // This policy should *only* be used with remote filesystems that 44 // donate their host FDs to the sandbox and thus use the host page 45 // cache, otherwise the dirent state will be inconsistent. 46 cacheRemoteRevalidating 47 ) 48 49 // String returns the string name of the cache policy. 50 func (cp cachePolicy) String() string { 51 switch cp { 52 case cacheNone: 53 return "cacheNone" 54 case cacheAll: 55 return "cacheAll" 56 case cacheAllWritethrough: 57 return "cacheAllWritethrough" 58 case cacheRemoteRevalidating: 59 return "cacheRemoteRevalidating" 60 default: 61 return "unknown" 62 } 63 } 64 65 func parseCachePolicy(policy string) (cachePolicy, error) { 66 switch policy { 67 case "fscache": 68 return cacheAll, nil 69 case "none": 70 return cacheNone, nil 71 case "fscache_writethrough": 72 return cacheAllWritethrough, nil 73 case "remote_revalidating": 74 return cacheRemoteRevalidating, nil 75 } 76 return cacheNone, fmt.Errorf("unsupported cache mode: %s", policy) 77 } 78 79 // cacheUAtters determines whether unstable attributes should be cached for the 80 // given inode. 81 func (cp cachePolicy) cacheUAttrs(inode *fs.Inode) bool { 82 if !fs.IsFile(inode.StableAttr) && !fs.IsDir(inode.StableAttr) { 83 return false 84 } 85 return cp == cacheAll || cp == cacheAllWritethrough 86 } 87 88 // cacheReaddir determines whether readdir results should be cached. 89 func (cp cachePolicy) cacheReaddir() bool { 90 return cp == cacheAll || cp == cacheAllWritethrough 91 } 92 93 // useCachingInodeOps determines whether the page cache should be used for the 94 // given inode. If the remote filesystem donates host FDs to the sentry, then 95 // the host kernel's page cache will be used, otherwise we will use a 96 // sentry-internal page cache. 97 func (cp cachePolicy) useCachingInodeOps(inode *fs.Inode) bool { 98 // Do cached IO for regular files only. Some "character devices" expect 99 // no caching. 100 if !fs.IsFile(inode.StableAttr) { 101 return false 102 } 103 return cp == cacheAll || cp == cacheAllWritethrough 104 } 105 106 // writeThough indicates whether writes to the file should be synced to the 107 // gofer immediately. 108 func (cp cachePolicy) writeThrough(inode *fs.Inode) bool { 109 return cp == cacheNone || cp == cacheAllWritethrough 110 } 111 112 // revalidate revalidates the child Inode if the cache policy allows it. 113 // 114 // Depending on the cache policy, revalidate will walk from the parent to the 115 // child inode, and if any unstable attributes have changed, will update the 116 // cached attributes on the child inode. If the walk fails, or the returned 117 // inode id is different from the one being revalidated, then the entire Dirent 118 // must be reloaded. 119 func (cp cachePolicy) revalidate(ctx context.Context, name string, parent, child *fs.Inode) bool { 120 if cp == cacheAll || cp == cacheAllWritethrough { 121 return false 122 } 123 124 if cp == cacheNone { 125 return true 126 } 127 128 childIops, ok := child.InodeOperations.(*inodeOperations) 129 if !ok { 130 if _, ok := child.InodeOperations.(*fifo); ok { 131 return false 132 } 133 panic(fmt.Sprintf("revalidating inode operations of unknown type %T", child.InodeOperations)) 134 } 135 parentIops, ok := parent.InodeOperations.(*inodeOperations) 136 if !ok { 137 panic(fmt.Sprintf("revalidating inode operations with parent of unknown type %T", parent.InodeOperations)) 138 } 139 140 // Walk from parent to child again. 141 // 142 // NOTE(b/112031682): If we have a directory FD in the parent 143 // inodeOperations, then we can use fstatat(2) to get the inode 144 // attributes instead of making this RPC. 145 qids, f, mask, attr, err := parentIops.fileState.file.walkGetAttr(ctx, []string{name}) 146 if err != nil { 147 // Can't look up the name. Trigger reload. 148 return true 149 } 150 f.close(ctx) 151 152 // If the Path has changed, then we are not looking at the file file. 153 // We must reload. 154 if qids[0].Path != childIops.fileState.key.Inode { 155 return true 156 } 157 158 // If we are not caching unstable attrs, then there is nothing to 159 // update on this inode. 160 if !cp.cacheUAttrs(child) { 161 return false 162 } 163 164 // Update the inode's cached unstable attrs. 165 s := childIops.session() 166 childIops.cachingInodeOps.UpdateUnstable(unstable(ctx, mask, attr, s.mounter, s.client)) 167 168 return false 169 } 170 171 // keep indicates that dirents should be kept pinned in the dirent tree even if 172 // there are no application references on the file. 173 func (cp cachePolicy) keep(d *fs.Dirent) bool { 174 if cp == cacheNone { 175 return false 176 } 177 sattr := d.Inode.StableAttr 178 // NOTE(b/31979197): Only cache files, directories, and symlinks. 179 return fs.IsFile(sattr) || fs.IsDir(sattr) || fs.IsSymlink(sattr) 180 } 181 182 // cacheNegativeDirents indicates that negative dirents should be held in the 183 // dirent tree. 184 func (cp cachePolicy) cacheNegativeDirents() bool { 185 return cp == cacheAll || cp == cacheAllWritethrough 186 }