@@ -34,6 +34,14 @@ import (
3434 "github.com/cert-manager/csi-lib/third_party/util"
3535)
3636
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+
3745const (
3846 readWriteUserFileMode = 0600
3947 readOnlyUserAndGroupFileMode = 0440
@@ -42,12 +50,9 @@ const (
4250type Filesystem struct {
4351 log logr.Logger
4452
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
4654 // about mounted volumes and mount points.
47- baseDir string
48-
49- // used by the 'read only' methods
50- fs fs.StatFS
55+ basePath string
5156
5257 // FSGroupVolumeAttributeKey is an optional well-known key in the volume
5358 // attributes. If this attribute is present in the context when writing
@@ -59,30 +64,44 @@ type Filesystem struct {
5964// Ensure the Filesystem implementation is fully featured
6065var _ Interface = & Filesystem {}
6166
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
6973 }
7074
71- isMnt , err := mount .New ("" ).IsMountPoint (f . tempfsPath () )
75+ isMnt , err := mount .New ("" ).IsMountPoint (inmemfsPath )
7276 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
7978 }
8079
8180 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 {
8382 return nil , fmt .Errorf ("mounting tmpfs: %w" , err )
8483 }
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
86105 }
87106
88107 return f , nil
@@ -93,18 +112,18 @@ func (f *Filesystem) PathForVolume(volumeID string) string {
93112}
94113
95114func (f * Filesystem ) RemoveVolume (volumeID string ) error {
96- return os .RemoveAll (filepath . Join ( f . tempfsPath (), volumeID ))
115+ return os .RemoveAll (f . volumePath ( volumeID ))
97116}
98117
99118func (f * Filesystem ) ListVolumes () ([]string , error ) {
100- dirs , err := fs .ReadDir (f .fs , f . tempfsPath () )
119+ dirs , err := os .ReadDir (f .basePath )
101120 if err != nil {
102121 return nil , fmt .Errorf ("listing volumes: %w" , err )
103122 }
104123
105124 var vols []string
106125 for _ , dir := range dirs {
107- _ , err := f . fs .Stat (f .metadataPathForVolumeID (dir .Name ()))
126+ _ , err := os .Stat (f .metadataPathForVolumeID (dir .Name ()))
108127 switch {
109128 case errors .Is (err , fs .ErrNotExist ):
110129 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) {
127146// Errors wrapping ErrNotFound will be returned if metadata for the ID cannot
128147// be found.
129148func (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 ))
131150 if err != nil {
132151 // don't leak through error types from fs.Open - wrap with ErrNotFound
133152 // if calling Open fails, as this indicates an invalid path
@@ -158,6 +177,11 @@ func (f *Filesystem) ReadMetadata(volumeID string) (metadata.Metadata, error) {
158177}
159178
160179func (f * Filesystem ) WriteMetadata (volumeID string , meta metadata.Metadata ) error {
180+ // Ensure the the volume directory exists.
181+ if err := f .ensureVolumeDirectory (volumeID ); err != nil {
182+ return err
183+ }
184+
161185 metaBytes , err := json .Marshal (meta )
162186 if err != nil {
163187 // 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
171195func (f * Filesystem ) RegisterMetadata (meta metadata.Metadata ) (bool , error ) {
172196 existingMeta , err := f .ReadMetadata (meta .VolumeID )
173197 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-
179198 if err := f .WriteMetadata (meta .VolumeID , meta ); err != nil {
180199 return false , err
181200 }
@@ -185,13 +204,6 @@ func (f *Filesystem) RegisterMetadata(meta metadata.Metadata) (bool, error) {
185204
186205 // If the volume context has changed, should write updated metadata
187206 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-
195207 f .log .WithValues ("volume_id" , meta .VolumeID ).Info ("volume context changed, updating file system metadata" )
196208 existingMeta .VolumeContext = meta .VolumeContext
197209 if err := f .WriteMetadata (existingMeta .VolumeID , existingMeta ); err != nil {
@@ -207,29 +219,14 @@ func (f *Filesystem) RegisterMetadata(meta metadata.Metadata) (bool, error) {
207219// ensureVolumeDirectory ensures the directory structure for the volume exists.
208220// If the directories already exist, it will do nothing.
209221func (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 )
220223}
221224
222225// WriteFiles writes the given data to filesystem files within the volume's
223226// data directory. Filesystem supports changing ownership of the data directory
224227// to a custom gid.
225228func (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 the volume directory exists.
233230 if err := f .ensureVolumeDirectory (meta .VolumeID ); err != nil {
234231 return err
235232 }
@@ -239,9 +236,14 @@ func (f *Filesystem) WriteFiles(meta metadata.Metadata, files map[string][]byte)
239236 return err
240237 }
241238
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+
242244 // If a fsGroup is defined, Chown the directory to that group.
243245 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 {
245247 return fmt .Errorf ("failed to chown data dir to gid %v: %w" , * fsGroup , err )
246248 }
247249 }
@@ -260,7 +262,7 @@ func (f *Filesystem) WriteFiles(meta metadata.Metadata, files map[string][]byte)
260262 // If a fsGroup is defined, Chown all files in the timestamped directory.
261263 for filename := range files {
262264 // 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 {
264266 return err
265267 }
266268 }
@@ -276,7 +278,7 @@ func (f *Filesystem) WriteFiles(meta metadata.Metadata, files map[string][]byte)
276278
277279// ReadFile reads the named file within the volume's data directory.
278280func (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 ))
280282 if err != nil {
281283 // don't leak through error types from fs.Open - wrap with ErrNotFound
282284 // if calling Open fails, as this indicates an invalid path
@@ -300,11 +302,7 @@ func (f *Filesystem) dataPathForVolumeID(id string) string {
300302}
301303
302304func (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 )
308306}
309307
310308func makePayload (in map [string ][]byte ) map [string ]util.FileProjection {
0 commit comments