@@ -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 // FixedFSGroup is an optional field which will set the gid ownership of all
5358 // volume's data directories to this value.
@@ -65,30 +70,44 @@ type Filesystem struct {
6570// Ensure the Filesystem implementation is fully featured
6671var _ Interface = & Filesystem {}
6772
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
7579 }
7680
77- isMnt , err := mount .New ("" ).IsMountPoint (f . tempfsPath () )
81+ isMnt , err := mount .New ("" ).IsMountPoint (inmemfsPath )
7882 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
8584 }
8685
8786 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 {
8988 return nil , fmt .Errorf ("mounting tmpfs: %w" , err )
9089 }
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
92111 }
93112
94113 return f , nil
@@ -99,18 +118,18 @@ func (f *Filesystem) PathForVolume(volumeID string) string {
99118}
100119
101120func (f * Filesystem ) RemoveVolume (volumeID string ) error {
102- return os .RemoveAll (filepath . Join ( f . tempfsPath (), volumeID ))
121+ return os .RemoveAll (f . volumePath ( volumeID ))
103122}
104123
105124func (f * Filesystem ) ListVolumes () ([]string , error ) {
106- dirs , err := fs .ReadDir (f .fs , f . tempfsPath () )
125+ dirs , err := os .ReadDir (f .basePath )
107126 if err != nil {
108127 return nil , fmt .Errorf ("listing volumes: %w" , err )
109128 }
110129
111130 var vols []string
112131 for _ , dir := range dirs {
113- _ , err := f . fs .Stat (f .metadataPathForVolumeID (dir .Name ()))
132+ _ , err := os .Stat (f .metadataPathForVolumeID (dir .Name ()))
114133 switch {
115134 case errors .Is (err , fs .ErrNotExist ):
116135 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) {
133152// Errors wrapping ErrNotFound will be returned if metadata for the ID cannot
134153// be found.
135154func (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 ))
137156 if err != nil {
138157 // don't leak through error types from fs.Open - wrap with ErrNotFound
139158 // if calling Open fails, as this indicates an invalid path
@@ -164,6 +183,11 @@ func (f *Filesystem) ReadMetadata(volumeID string) (metadata.Metadata, error) {
164183}
165184
166185func (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+
167191 metaBytes , err := json .Marshal (meta )
168192 if err != nil {
169193 // 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
177201func (f * Filesystem ) RegisterMetadata (meta metadata.Metadata ) (bool , error ) {
178202 existingMeta , err := f .ReadMetadata (meta .VolumeID )
179203 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-
185204 if err := f .WriteMetadata (meta .VolumeID , meta ); err != nil {
186205 return false , err
187206 }
@@ -191,13 +210,6 @@ func (f *Filesystem) RegisterMetadata(meta metadata.Metadata) (bool, error) {
191210
192211 // If the volume context has changed, should write updated metadata
193212 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-
201213 f .log .WithValues ("volume_id" , meta .VolumeID ).Info ("volume context changed, updating file system metadata" )
202214 existingMeta .VolumeContext = meta .VolumeContext
203215 if err := f .WriteMetadata (existingMeta .VolumeID , existingMeta ); err != nil {
@@ -213,29 +225,14 @@ func (f *Filesystem) RegisterMetadata(meta metadata.Metadata) (bool, error) {
213225// ensureVolumeDirectory ensures the directory structure for the volume exists.
214226// If the directories already exist, it will do nothing.
215227func (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 )
226229}
227230
228231// WriteFiles writes the given data to filesystem files within the volume's
229232// data directory. Filesystem supports changing ownership of the data directory
230233// to a custom gid.
231234func (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.
239236 if err := f .ensureVolumeDirectory (meta .VolumeID ); err != nil {
240237 return err
241238 }
@@ -245,9 +242,14 @@ func (f *Filesystem) WriteFiles(meta metadata.Metadata, files map[string][]byte)
245242 return err
246243 }
247244
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+
248250 // If a fsGroup is defined, Chown the directory to that group.
249251 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 {
251253 return fmt .Errorf ("failed to chown data dir to gid %v: %w" , * fsGroup , err )
252254 }
253255 }
@@ -266,7 +268,7 @@ func (f *Filesystem) WriteFiles(meta metadata.Metadata, files map[string][]byte)
266268 // If a fsGroup is defined, Chown all files in the timestamped directory.
267269 for filename := range files {
268270 // 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 {
270272 return err
271273 }
272274 }
@@ -282,7 +284,7 @@ func (f *Filesystem) WriteFiles(meta metadata.Metadata, files map[string][]byte)
282284
283285// ReadFile reads the named file within the volume's data directory.
284286func (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 ))
286288 if err != nil {
287289 // don't leak through error types from fs.Open - wrap with ErrNotFound
288290 // if calling Open fails, as this indicates an invalid path
@@ -306,11 +308,7 @@ func (f *Filesystem) dataPathForVolumeID(id string) string {
306308}
307309
308310func (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 )
314312}
315313
316314func makePayload (in map [string ][]byte ) map [string ]util.FileProjection {
0 commit comments