1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gofer
import (
"fmt"
"gvisor.googlesource.com/gvisor/pkg/sentry/fs"
)
// cachePolicy is a 9p cache policy. It has methods that determine what to
// cache (if anything) for a given inode.
type cachePolicy int
const (
// Cache nothing.
cacheNone cachePolicy = iota
// Use virtual file system cache for everything.
cacheAll
// Use virtual file system cache for everything, but send writes to the
// fs agent immediately.
cacheAllWritethrough
// Use the (host) page cache for reads/writes, but don't cache anything
// else. This allows the sandbox filesystem to stay in sync with any
// changes to the remote filesystem.
//
// This policy should *only* be used with remote filesystems that
// donate their host FDs to the sandbox and thus use the host page
// cache, otherwise the dirent state will be inconsistent.
cacheRemoteRevalidating
)
// String returns the string name of the cache policy.
func (cp cachePolicy) String() string {
switch cp {
case cacheNone:
return "cacheNone"
case cacheAll:
return "cacheAll"
case cacheAllWritethrough:
return "cacheAllWritethrough"
case cacheRemoteRevalidating:
return "cacheRemoteRevalidating"
default:
return "unknown"
}
}
func parseCachePolicy(policy string) (cachePolicy, error) {
switch policy {
case "fscache":
return cacheAll, nil
case "none":
return cacheNone, nil
case "fscache_writethrough":
return cacheAllWritethrough, nil
case "remote_revalidating":
return cacheRemoteRevalidating, nil
}
return cacheNone, fmt.Errorf("unsupported cache mode: %s", policy)
}
// cacheUAtters determines whether unstable attributes should be cached for the
// given inode.
func (cp cachePolicy) cacheUAttrs(inode *fs.Inode) bool {
if !fs.IsFile(inode.StableAttr) && !fs.IsDir(inode.StableAttr) {
return false
}
return cp == cacheAll || cp == cacheAllWritethrough
}
// cacheReaddir determines whether readdir results should be cached.
func (cp cachePolicy) cacheReaddir() bool {
return cp == cacheAll || cp == cacheAllWritethrough
}
// usePageCache determines whether the page cache should be used for the given
// inode. If the remote filesystem donates host FDs to the sentry, then the
// host kernel's page cache will be used, otherwise we will use a
// sentry-internal page cache.
func (cp cachePolicy) usePageCache(inode *fs.Inode) bool {
// Do cached IO for regular files only. Some "character devices" expect
// no caching.
if !fs.IsFile(inode.StableAttr) {
return false
}
return cp == cacheAll || cp == cacheAllWritethrough || cp == cacheRemoteRevalidating
}
// writeThough indicates whether writes to the file should be synced to the
// gofer immediately.
func (cp cachePolicy) writeThrough(inode *fs.Inode) bool {
return cp == cacheNone || cp == cacheAllWritethrough
}
// revalidateDirent indicates that a dirent should be revalidated after a
// lookup, because the looked up version may be stale.
func (cp cachePolicy) revalidateDirent() bool {
if cp == cacheAll || cp == cacheAllWritethrough {
return false
}
// TODO: The cacheRemoteRevalidating policy should only
// return true if the remote file's attributes have changed.
return true
}
// keepDirent indicates that dirents should be kept pinned in the dirent tree
// even if there are no application references on the file.
func (cp cachePolicy) keepDirent(inode *fs.Inode) bool {
if cp == cacheNone {
return false
}
sattr := inode.StableAttr
// NOTE: Only cache files, directories, and symlinks.
return fs.IsFile(sattr) || fs.IsDir(sattr) || fs.IsSymlink(sattr)
}
// cacheNegativeDirents indicates that negative dirents should be held in the
// dirent tree.
func (cp cachePolicy) cacheNegativeDirents() bool {
return cp == cacheAll || cp == cacheAllWritethrough
}
|