@@ -34,6 +34,14 @@ import (
34
34
"github.com/cert-manager/csi-lib/third_party/util"
35
35
)
36
36
37
+ // These variables can be overridden by tests, this makes it easier to test
38
+ // the filesystem implementation as a non-root user.
39
+ var (
40
+ parentFolderMode fs.FileMode = 0700
41
+ volumeFolderMode fs.FileMode = 0644
42
+ volumeDataFolderMode fs.FileMode = 0550
43
+ )
44
+
37
45
const (
38
46
readWriteUserFileMode = 0600
39
47
readOnlyUserAndGroupFileMode = 0440
@@ -42,12 +50,9 @@ const (
42
50
type Filesystem struct {
43
51
log logr.Logger
44
52
45
- // baseDir is the absolute path to a directory used to store all metadata
53
+ // basePath is the absolute path to a directory used to store all metadata
46
54
// about mounted volumes and mount points.
47
- baseDir string
48
-
49
- // used by the 'read only' methods
50
- fs fs.StatFS
55
+ basePath string
51
56
52
57
// FSGroupVolumeAttributeKey is an optional well-known key in the volume
53
58
// attributes. If this attribute is present in the context when writing
@@ -59,30 +64,44 @@ type Filesystem struct {
59
64
// Ensure the Filesystem implementation is fully featured
60
65
var _ Interface = & Filesystem {}
61
66
62
- func NewFilesystem (log logr.Logger , baseDir string ) (* Filesystem , error ) {
63
- f := & Filesystem {
64
- log : log ,
65
- baseDir : baseDir ,
66
- // Use the rootfs as the DirFS so that paths passed to both read &
67
- // write methods on this struct use a consistent root.
68
- fs : os .DirFS ("/" ).(fs.StatFS ),
67
+ func NewFilesystem (log logr.Logger , basePath string ) (* Filesystem , error ) {
68
+ inmemfsPath := filepath .Join (basePath , "inmemfs" )
69
+
70
+ f , err := NewFilesystemOnDisk (log , inmemfsPath )
71
+ if err != nil {
72
+ return nil , err
69
73
}
70
74
71
- isMnt , err := mount .New ("" ).IsMountPoint (f . tempfsPath () )
75
+ isMnt , err := mount .New ("" ).IsMountPoint (inmemfsPath )
72
76
if err != nil {
73
- if ! os .IsNotExist (err ) {
74
- return nil , err
75
- }
76
- if err := os .MkdirAll (f .tempfsPath (), 0700 ); err != nil {
77
- return nil , err
78
- }
77
+ return nil , err
79
78
}
80
79
81
80
if ! isMnt {
82
- if err := mount .New ("" ).Mount ("tmpfs" , f . tempfsPath () , "tmpfs" , []string {}); err != nil {
81
+ if err := mount .New ("" ).Mount ("tmpfs" , inmemfsPath , "tmpfs" , []string {}); err != nil {
83
82
return nil , fmt .Errorf ("mounting tmpfs: %w" , err )
84
83
}
85
- log .Info ("Mounted new tmpfs" , "path" , f .tempfsPath ())
84
+ log .Info ("Mounted new tmpfs" , "path" , inmemfsPath )
85
+ }
86
+
87
+ return f , nil
88
+ }
89
+
90
+ // WARNING: should be used only for testing purposes only, as we don't want to store
91
+ // any sensitive data on disk in production.
92
+ func NewFilesystemOnDisk (log logr.Logger , basePath string ) (* Filesystem , error ) {
93
+ if ! filepath .IsAbs (basePath ) {
94
+ return nil , fmt .Errorf ("baseDir must be an absolute path" )
95
+ }
96
+
97
+ f := & Filesystem {
98
+ log : log ,
99
+ basePath : basePath ,
100
+ }
101
+
102
+ // Create the base directory if it does not exist.
103
+ if err := os .MkdirAll (basePath , parentFolderMode ); err != nil {
104
+ return nil , err
86
105
}
87
106
88
107
return f , nil
@@ -93,18 +112,18 @@ func (f *Filesystem) PathForVolume(volumeID string) string {
93
112
}
94
113
95
114
func (f * Filesystem ) RemoveVolume (volumeID string ) error {
96
- return os .RemoveAll (filepath . Join ( f . tempfsPath (), volumeID ))
115
+ return os .RemoveAll (f . volumePath ( volumeID ))
97
116
}
98
117
99
118
func (f * Filesystem ) ListVolumes () ([]string , error ) {
100
- dirs , err := fs .ReadDir (f .fs , f . tempfsPath () )
119
+ dirs , err := os .ReadDir (f .basePath )
101
120
if err != nil {
102
121
return nil , fmt .Errorf ("listing volumes: %w" , err )
103
122
}
104
123
105
124
var vols []string
106
125
for _ , dir := range dirs {
107
- _ , err := f . fs .Stat (f .metadataPathForVolumeID (dir .Name ()))
126
+ _ , err := os .Stat (f .metadataPathForVolumeID (dir .Name ()))
108
127
switch {
109
128
case errors .Is (err , fs .ErrNotExist ):
110
129
f .log .Info ("Directory exists but does not contain a metadata file - deleting directory and its contents" , "volume_id" , dir .Name ())
@@ -127,7 +146,7 @@ func (f *Filesystem) ListVolumes() ([]string, error) {
127
146
// Errors wrapping ErrNotFound will be returned if metadata for the ID cannot
128
147
// be found.
129
148
func (f * Filesystem ) ReadMetadata (volumeID string ) (metadata.Metadata , error ) {
130
- file , err := f . fs .Open (f .metadataPathForVolumeID (volumeID ))
149
+ file , err := os .Open (f .metadataPathForVolumeID (volumeID ))
131
150
if err != nil {
132
151
// don't leak through error types from fs.Open - wrap with ErrNotFound
133
152
// if calling Open fails, as this indicates an invalid path
@@ -158,6 +177,11 @@ func (f *Filesystem) ReadMetadata(volumeID string) (metadata.Metadata, error) {
158
177
}
159
178
160
179
func (f * Filesystem ) WriteMetadata (volumeID string , meta metadata.Metadata ) error {
180
+ // Ensure the volume directory exists.
181
+ if err := f .ensureVolumeDirectory (volumeID ); err != nil {
182
+ return err
183
+ }
184
+
161
185
metaBytes , err := json .Marshal (meta )
162
186
if err != nil {
163
187
// if it's an error type we don't recognise, wrap it with %v to prevent
@@ -171,11 +195,6 @@ func (f *Filesystem) WriteMetadata(volumeID string, meta metadata.Metadata) erro
171
195
func (f * Filesystem ) RegisterMetadata (meta metadata.Metadata ) (bool , error ) {
172
196
existingMeta , err := f .ReadMetadata (meta .VolumeID )
173
197
if errors .Is (err , ErrNotFound ) {
174
- // Ensure directory structure for the volume exists
175
- if err := f .ensureVolumeDirectory (meta .VolumeID ); err != nil {
176
- return false , err
177
- }
178
-
179
198
if err := f .WriteMetadata (meta .VolumeID , meta ); err != nil {
180
199
return false , err
181
200
}
@@ -185,13 +204,6 @@ func (f *Filesystem) RegisterMetadata(meta metadata.Metadata) (bool, error) {
185
204
186
205
// If the volume context has changed, should write updated metadata
187
206
if ! apiequality .Semantic .DeepEqual (existingMeta .VolumeContext , meta .VolumeContext ) {
188
- // Ensure directory structure for the volume exists - this will probably do
189
- // nothing, but it helps avoid any weird edge cases we could find ourselves in &
190
- // is an inexpensive operation.
191
- if err := f .ensureVolumeDirectory (meta .VolumeID ); err != nil {
192
- return false , err
193
- }
194
-
195
207
f .log .WithValues ("volume_id" , meta .VolumeID ).Info ("volume context changed, updating file system metadata" )
196
208
existingMeta .VolumeContext = meta .VolumeContext
197
209
if err := f .WriteMetadata (existingMeta .VolumeID , existingMeta ); err != nil {
@@ -207,29 +219,14 @@ func (f *Filesystem) RegisterMetadata(meta metadata.Metadata) (bool, error) {
207
219
// ensureVolumeDirectory ensures the directory structure for the volume exists.
208
220
// If the directories already exist, it will do nothing.
209
221
func (f * Filesystem ) ensureVolumeDirectory (volumeID string ) error {
210
- if err := os .MkdirAll (f .volumePath (volumeID ), 0644 ); err != nil {
211
- return err
212
- }
213
-
214
- // Data directory should be read and execute only to the fs user and group.
215
- if err := os .MkdirAll (f .dataPathForVolumeID (volumeID ), 0550 ); err != nil {
216
- return err
217
- }
218
-
219
- return nil
222
+ return os .MkdirAll (f .volumePath (volumeID ), volumeFolderMode )
220
223
}
221
224
222
225
// WriteFiles writes the given data to filesystem files within the volume's
223
226
// data directory. Filesystem supports changing ownership of the data directory
224
227
// to a custom gid.
225
228
func (f * Filesystem ) WriteFiles (meta metadata.Metadata , files map [string ][]byte ) error {
226
- // Ensure the full directory structure for the volume exists.
227
- // This already happens in RegisterMetadata, however, when a driver starts up and reads
228
- // the metadata files from the existing tmpfs to re-populate the manager, RegisterMetadata
229
- // is not called again (it is only invoked by driver/nodeserver.go when a pod is first processed
230
- // during NodePublishVolume).
231
- // There is a very slim chance we could end out in a weird situation where the metadata
232
- // file exists but the data directory does not, so re-run ensureVolumeDirectory just to be safe.
229
+ // Ensure the volume directory exists.
233
230
if err := f .ensureVolumeDirectory (meta .VolumeID ); err != nil {
234
231
return err
235
232
}
@@ -239,9 +236,14 @@ func (f *Filesystem) WriteFiles(meta metadata.Metadata, files map[string][]byte)
239
236
return err
240
237
}
241
238
239
+ // Data directory should be read and execute only to the fs user and group.
240
+ if err := os .MkdirAll (f .dataPathForVolumeID (meta .VolumeID ), volumeDataFolderMode ); err != nil {
241
+ return err
242
+ }
243
+
242
244
// If a fsGroup is defined, Chown the directory to that group.
243
245
if fsGroup != nil {
244
- if err := os .Chown (f .dataPathForVolumeID (meta .VolumeID ), - 1 , int (* fsGroup )); err != nil {
246
+ if err := os .Lchown (f .dataPathForVolumeID (meta .VolumeID ), - 1 , int (* fsGroup )); err != nil {
245
247
return fmt .Errorf ("failed to chown data dir to gid %v: %w" , * fsGroup , err )
246
248
}
247
249
}
@@ -260,7 +262,7 @@ func (f *Filesystem) WriteFiles(meta metadata.Metadata, files map[string][]byte)
260
262
// If a fsGroup is defined, Chown all files in the timestamped directory.
261
263
for filename := range files {
262
264
// Set the uid to -1 which means don't change ownership in Go.
263
- if err := os .Chown (filepath .Join (f .dataPathForVolumeID (meta .VolumeID ), tsDirName , filename ), - 1 , int (* fsGroup )); err != nil {
265
+ if err := os .Lchown (filepath .Join (f .dataPathForVolumeID (meta .VolumeID ), tsDirName , filename ), - 1 , int (* fsGroup )); err != nil {
264
266
return err
265
267
}
266
268
}
@@ -276,7 +278,7 @@ func (f *Filesystem) WriteFiles(meta metadata.Metadata, files map[string][]byte)
276
278
277
279
// ReadFile reads the named file within the volume's data directory.
278
280
func (f * Filesystem ) ReadFile (volumeID , name string ) ([]byte , error ) {
279
- file , err := f . fs .Open (filepath .Join (f .dataPathForVolumeID (volumeID ), name ))
281
+ file , err := os .Open (filepath .Join (f .dataPathForVolumeID (volumeID ), name ))
280
282
if err != nil {
281
283
// don't leak through error types from fs.Open - wrap with ErrNotFound
282
284
// if calling Open fails, as this indicates an invalid path
@@ -300,11 +302,7 @@ func (f *Filesystem) dataPathForVolumeID(id string) string {
300
302
}
301
303
302
304
func (f * Filesystem ) volumePath (id string ) string {
303
- return filepath .Join (f .tempfsPath (), id )
304
- }
305
-
306
- func (f * Filesystem ) tempfsPath () string {
307
- return filepath .Join (f .baseDir , "inmemfs" )
305
+ return filepath .Join (f .basePath , id )
308
306
}
309
307
310
308
func makePayload (in map [string ][]byte ) map [string ]util.FileProjection {
0 commit comments