Skip to content

Commit ca97148

Browse files
committed
refactor filesystem.go and adapt tests to use a real file system
Signed-off-by: Tim Ramlot <[email protected]>
1 parent 0e8fa55 commit ca97148

File tree

2 files changed

+162
-93
lines changed

2 files changed

+162
-93
lines changed

storage/filesystem.go

Lines changed: 60 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
3745
const (
3846
readWriteUserFileMode = 0600
3947
readOnlyUserAndGroupFileMode = 0440
@@ -42,12 +50,9 @@ const (
4250
type 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
6065
var _ 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

95114
func (f *Filesystem) RemoveVolume(volumeID string) error {
96-
return os.RemoveAll(filepath.Join(f.tempfsPath(), volumeID))
115+
return os.RemoveAll(f.volumePath(volumeID))
97116
}
98117

99118
func (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.
129148
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))
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

160179
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+
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
171195
func (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.
209221
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)
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.
225228
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.
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.
278280
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))
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

302304
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)
308306
}
309307

310308
func makePayload(in map[string][]byte) map[string]util.FileProjection {

0 commit comments

Comments
 (0)