@@ -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
// FixedFSGroup is an optional field which will set the gid ownership of all
53
58
// volume's data directories to this value.
@@ -65,30 +70,44 @@ type Filesystem struct {
65
70
// Ensure the Filesystem implementation is fully featured
66
71
var _ Interface = & Filesystem {}
67
72
68
- func NewFilesystem (log logr.Logger , baseDir string ) (* Filesystem , error ) {
69
- f := & Filesystem {
70
- log : log ,
71
- baseDir : baseDir ,
72
- // Use the rootfs as the DirFS so that paths passed to both read &
73
- // write methods on this struct use a consistent root.
74
- fs : os .DirFS ("/" ).(fs.StatFS ),
73
+ func NewFilesystem (log logr.Logger , basePath string ) (* Filesystem , error ) {
74
+ inmemfsPath := filepath .Join (basePath , "inmemfs" )
75
+
76
+ f , err := NewFilesystemOnDisk (log , inmemfsPath )
77
+ if err != nil {
78
+ return nil , err
75
79
}
76
80
77
- isMnt , err := mount .New ("" ).IsMountPoint (f . tempfsPath () )
81
+ isMnt , err := mount .New ("" ).IsMountPoint (inmemfsPath )
78
82
if err != nil {
79
- if ! os .IsNotExist (err ) {
80
- return nil , err
81
- }
82
- if err := os .MkdirAll (f .tempfsPath (), 0700 ); err != nil {
83
- return nil , err
84
- }
83
+ return nil , err
85
84
}
86
85
87
86
if ! isMnt {
88
- if err := mount .New ("" ).Mount ("tmpfs" , f . tempfsPath () , "tmpfs" , []string {}); err != nil {
87
+ if err := mount .New ("" ).Mount ("tmpfs" , inmemfsPath , "tmpfs" , []string {}); err != nil {
89
88
return nil , fmt .Errorf ("mounting tmpfs: %w" , err )
90
89
}
91
- log .Info ("Mounted new tmpfs" , "path" , f .tempfsPath ())
90
+ log .Info ("Mounted new tmpfs" , "path" , inmemfsPath )
91
+ }
92
+
93
+ return f , nil
94
+ }
95
+
96
+ // WARNING: should be used only for testing purposes only, as we don't want to store
97
+ // any sensitive data on disk in production.
98
+ func NewFilesystemOnDisk (log logr.Logger , basePath string ) (* Filesystem , error ) {
99
+ if ! filepath .IsAbs (basePath ) {
100
+ return nil , fmt .Errorf ("baseDir must be an absolute path" )
101
+ }
102
+
103
+ f := & Filesystem {
104
+ log : log ,
105
+ basePath : basePath ,
106
+ }
107
+
108
+ // Create the base directory if it does not exist.
109
+ if err := os .MkdirAll (basePath , parentFolderMode ); err != nil {
110
+ return nil , err
92
111
}
93
112
94
113
return f , nil
@@ -99,18 +118,18 @@ func (f *Filesystem) PathForVolume(volumeID string) string {
99
118
}
100
119
101
120
func (f * Filesystem ) RemoveVolume (volumeID string ) error {
102
- return os .RemoveAll (filepath . Join ( f . tempfsPath (), volumeID ))
121
+ return os .RemoveAll (f . volumePath ( volumeID ))
103
122
}
104
123
105
124
func (f * Filesystem ) ListVolumes () ([]string , error ) {
106
- dirs , err := fs .ReadDir (f .fs , f . tempfsPath () )
125
+ dirs , err := os .ReadDir (f .basePath )
107
126
if err != nil {
108
127
return nil , fmt .Errorf ("listing volumes: %w" , err )
109
128
}
110
129
111
130
var vols []string
112
131
for _ , dir := range dirs {
113
- _ , err := f . fs .Stat (f .metadataPathForVolumeID (dir .Name ()))
132
+ _ , err := os .Stat (f .metadataPathForVolumeID (dir .Name ()))
114
133
switch {
115
134
case errors .Is (err , fs .ErrNotExist ):
116
135
f .log .Info ("Directory exists but does not contain a metadata file - deleting directory and its contents" , "volume_id" , dir .Name ())
@@ -133,7 +152,7 @@ func (f *Filesystem) ListVolumes() ([]string, error) {
133
152
// Errors wrapping ErrNotFound will be returned if metadata for the ID cannot
134
153
// be found.
135
154
func (f * Filesystem ) ReadMetadata (volumeID string ) (metadata.Metadata , error ) {
136
- file , err := f . fs .Open (f .metadataPathForVolumeID (volumeID ))
155
+ file , err := os .Open (f .metadataPathForVolumeID (volumeID ))
137
156
if err != nil {
138
157
// don't leak through error types from fs.Open - wrap with ErrNotFound
139
158
// if calling Open fails, as this indicates an invalid path
@@ -164,6 +183,11 @@ func (f *Filesystem) ReadMetadata(volumeID string) (metadata.Metadata, error) {
164
183
}
165
184
166
185
func (f * Filesystem ) WriteMetadata (volumeID string , meta metadata.Metadata ) error {
186
+ // Ensure the the volume directory exists.
187
+ if err := f .ensureVolumeDirectory (volumeID ); err != nil {
188
+ return err
189
+ }
190
+
167
191
metaBytes , err := json .Marshal (meta )
168
192
if err != nil {
169
193
// if it's an error type we don't recognise, wrap it with %v to prevent
@@ -177,11 +201,6 @@ func (f *Filesystem) WriteMetadata(volumeID string, meta metadata.Metadata) erro
177
201
func (f * Filesystem ) RegisterMetadata (meta metadata.Metadata ) (bool , error ) {
178
202
existingMeta , err := f .ReadMetadata (meta .VolumeID )
179
203
if errors .Is (err , ErrNotFound ) {
180
- // Ensure directory structure for the volume exists
181
- if err := f .ensureVolumeDirectory (meta .VolumeID ); err != nil {
182
- return false , err
183
- }
184
-
185
204
if err := f .WriteMetadata (meta .VolumeID , meta ); err != nil {
186
205
return false , err
187
206
}
@@ -191,13 +210,6 @@ func (f *Filesystem) RegisterMetadata(meta metadata.Metadata) (bool, error) {
191
210
192
211
// If the volume context has changed, should write updated metadata
193
212
if ! apiequality .Semantic .DeepEqual (existingMeta .VolumeContext , meta .VolumeContext ) {
194
- // Ensure directory structure for the volume exists - this will probably do
195
- // nothing, but it helps avoid any weird edge cases we could find ourselves in &
196
- // is an inexpensive operation.
197
- if err := f .ensureVolumeDirectory (meta .VolumeID ); err != nil {
198
- return false , err
199
- }
200
-
201
213
f .log .WithValues ("volume_id" , meta .VolumeID ).Info ("volume context changed, updating file system metadata" )
202
214
existingMeta .VolumeContext = meta .VolumeContext
203
215
if err := f .WriteMetadata (existingMeta .VolumeID , existingMeta ); err != nil {
@@ -213,29 +225,14 @@ func (f *Filesystem) RegisterMetadata(meta metadata.Metadata) (bool, error) {
213
225
// ensureVolumeDirectory ensures the directory structure for the volume exists.
214
226
// If the directories already exist, it will do nothing.
215
227
func (f * Filesystem ) ensureVolumeDirectory (volumeID string ) error {
216
- if err := os .MkdirAll (f .volumePath (volumeID ), 0644 ); err != nil {
217
- return err
218
- }
219
-
220
- // Data directory should be read and execute only to the fs user and group.
221
- if err := os .MkdirAll (f .dataPathForVolumeID (volumeID ), 0550 ); err != nil {
222
- return err
223
- }
224
-
225
- return nil
228
+ return os .MkdirAll (f .volumePath (volumeID ), volumeFolderMode )
226
229
}
227
230
228
231
// WriteFiles writes the given data to filesystem files within the volume's
229
232
// data directory. Filesystem supports changing ownership of the data directory
230
233
// to a custom gid.
231
234
func (f * Filesystem ) WriteFiles (meta metadata.Metadata , files map [string ][]byte ) error {
232
- // Ensure the full directory structure for the volume exists.
233
- // This already happens in RegisterMetadata, however, when a driver starts up and reads
234
- // the metadata files from the existing tmpfs to re-populate the manager, RegisterMetadata
235
- // is not called again (it is only invoked by driver/nodeserver.go when a pod is first processed
236
- // during NodePublishVolume).
237
- // There is a very slim chance we could end out in a weird situation where the metadata
238
- // file exists but the data directory does not, so re-run ensureVolumeDirectory just to be safe.
235
+ // Ensure the the volume directory exists.
239
236
if err := f .ensureVolumeDirectory (meta .VolumeID ); err != nil {
240
237
return err
241
238
}
@@ -245,9 +242,14 @@ func (f *Filesystem) WriteFiles(meta metadata.Metadata, files map[string][]byte)
245
242
return err
246
243
}
247
244
245
+ // Data directory should be read and execute only to the fs user and group.
246
+ if err := os .MkdirAll (f .dataPathForVolumeID (meta .VolumeID ), volumeDataFolderMode ); err != nil {
247
+ return err
248
+ }
249
+
248
250
// If a fsGroup is defined, Chown the directory to that group.
249
251
if fsGroup != nil {
250
- if err := os .Chown (f .dataPathForVolumeID (meta .VolumeID ), - 1 , int (* fsGroup )); err != nil {
252
+ if err := os .Lchown (f .dataPathForVolumeID (meta .VolumeID ), - 1 , int (* fsGroup )); err != nil {
251
253
return fmt .Errorf ("failed to chown data dir to gid %v: %w" , * fsGroup , err )
252
254
}
253
255
}
@@ -266,7 +268,7 @@ func (f *Filesystem) WriteFiles(meta metadata.Metadata, files map[string][]byte)
266
268
// If a fsGroup is defined, Chown all files in the timestamped directory.
267
269
for filename := range files {
268
270
// Set the uid to -1 which means don't change ownership in Go.
269
- if err := os .Chown (filepath .Join (f .dataPathForVolumeID (meta .VolumeID ), tsDirName , filename ), - 1 , int (* fsGroup )); err != nil {
271
+ if err := os .Lchown (filepath .Join (f .dataPathForVolumeID (meta .VolumeID ), tsDirName , filename ), - 1 , int (* fsGroup )); err != nil {
270
272
return err
271
273
}
272
274
}
@@ -282,7 +284,7 @@ func (f *Filesystem) WriteFiles(meta metadata.Metadata, files map[string][]byte)
282
284
283
285
// ReadFile reads the named file within the volume's data directory.
284
286
func (f * Filesystem ) ReadFile (volumeID , name string ) ([]byte , error ) {
285
- file , err := f . fs .Open (filepath .Join (f .dataPathForVolumeID (volumeID ), name ))
287
+ file , err := os .Open (filepath .Join (f .dataPathForVolumeID (volumeID ), name ))
286
288
if err != nil {
287
289
// don't leak through error types from fs.Open - wrap with ErrNotFound
288
290
// if calling Open fails, as this indicates an invalid path
@@ -306,11 +308,7 @@ func (f *Filesystem) dataPathForVolumeID(id string) string {
306
308
}
307
309
308
310
func (f * Filesystem ) volumePath (id string ) string {
309
- return filepath .Join (f .tempfsPath (), id )
310
- }
311
-
312
- func (f * Filesystem ) tempfsPath () string {
313
- return filepath .Join (f .baseDir , "inmemfs" )
311
+ return filepath .Join (f .basePath , id )
314
312
}
315
313
316
314
func makePayload (in map [string ][]byte ) map [string ]util.FileProjection {
0 commit comments