diff --git a/cleanse.go b/cleanse.go index a6793d3..e8a2638 100644 --- a/cleanse.go +++ b/cleanse.go @@ -1,17 +1,17 @@ package memoryfs import ( - "path/filepath" - "strings" + "path/filepath" + "strings" ) func cleanse(path string) string { - path = strings.ReplaceAll(path, "/", separator) - path = filepath.Clean(path) - path = strings.TrimPrefix(path, "."+separator) - path = strings.TrimPrefix(path, separator) - if path == "." { - return "" - } - return path + path = strings.ReplaceAll(path, "/", separator) + path = filepath.Clean(path) + path = strings.TrimPrefix(path, "."+separator) + path = strings.TrimPrefix(path, separator) + if path == "." { + return "" + } + return path } diff --git a/dir.go b/dir.go index e7b421c..e698e38 100644 --- a/dir.go +++ b/dir.go @@ -1,6 +1,7 @@ package memoryfs import ( + "fmt" "io" "io/fs" "path/filepath" @@ -189,13 +190,15 @@ func (d *dir) MkdirAll(path string, perm fs.FileMode) error { } d.Lock() + if perm&fs.ModeDir == 0 { + perm |= fs.ModeDir + } if _, ok := d.dirs[parts[0]]; !ok { d.dirs[parts[0]] = &dir{ info: fileinfo{ name: parts[0], size: 0x100, modified: time.Now(), - isDir: true, mode: perm, }, dirs: map[string]*dir{}, @@ -217,6 +220,10 @@ func (d *dir) MkdirAll(path string, perm fs.FileMode) error { func (d *dir) WriteFile(path string, data []byte, perm fs.FileMode) error { parts := strings.Split(path, separator) + if perm&fs.ModeDir != 0 { + return fmt.Errorf("invalid perm: %v", perm) + } + if len(parts) == 1 { max := bufferSize if len(data) > max { @@ -236,7 +243,6 @@ func (d *dir) WriteFile(path string, data []byte, perm fs.FileMode) error { name: parts[0], size: int64(len(buffer)), modified: time.Now(), - isDir: false, mode: perm, }, content: buffer, @@ -304,6 +310,10 @@ func (d *dir) glob(pattern string) ([]string, error) { func (d *dir) WriteLazyFile(path string, opener LazyOpener, perm fs.FileMode) error { parts := strings.Split(path, separator) + if perm&fs.ModeDir != 0 { + return fmt.Errorf("invalid perm: %v", perm) + } + if len(parts) == 1 { d.Lock() defer d.Unlock() @@ -312,7 +322,6 @@ func (d *dir) WriteLazyFile(path string, opener LazyOpener, perm fs.FileMode) er name: parts[0], size: 0, modified: time.Now(), - isDir: false, mode: perm, }, opener: opener, diff --git a/file.go b/file.go index e3606b1..4035c3b 100644 --- a/file.go +++ b/file.go @@ -21,7 +21,6 @@ type fileAccess struct { reader io.Reader } - // LazyOpener provides an io.Reader that can be used to access the content of a file, whatever the actual storage medium. // If the LazyOpener returns an io.ReadCloser, it will be closed after each read. type LazyOpener func() (io.Reader, error) diff --git a/fileinfo.go b/fileinfo.go index dcc5810..cb55dbf 100644 --- a/fileinfo.go +++ b/fileinfo.go @@ -9,7 +9,6 @@ type fileinfo struct { name string size int64 modified time.Time - isDir bool mode fs.FileMode } @@ -46,7 +45,7 @@ func (f fileinfo) ModTime() time.Time { // IsDir reports whether the entry describes a directory. func (f fileinfo) IsDir() bool { - return f.isDir + return f.Mode().IsDir() } // Sys is the underlying data source of the file (always nil) diff --git a/fs.go b/fs.go index 876025f..e7de22c 100644 --- a/fs.go +++ b/fs.go @@ -21,8 +21,7 @@ func New() *FS { name: ".", size: 0x100, modified: time.Now(), - isDir: true, - mode: 0o700, + mode: 0o0700 | fs.ModeDir, }, dirs: map[string]*dir{}, files: map[string]*file{}, diff --git a/fs_test.go b/fs_test.go index 79940b6..0891ded 100644 --- a/fs_test.go +++ b/fs_test.go @@ -39,6 +39,12 @@ func Test_AllOperations(t *testing.T) { require.NoError(t, memfs.WriteFile("files/a/b/c/.secret", []byte("secret file!"), 0o644)) require.NoError(t, memfs.WriteFile("files/a/b/c/note.txt", []byte(":)"), 0o644)) require.NoError(t, memfs.WriteFile("files/a/middle.txt", []byte(":("), 0o644)) + require.NoError(t, memfs.MkdirAll("files/xyz", 0o700|fs.ModeDir)) + + require.Error(t, memfs.WriteFile("test.txt", []byte("hello world"), 0o644|fs.ModeDir)) + require.Error(t, memfs.WriteLazyFile("test.txt", func() (io.Reader, error) { + return strings.NewReader("hello"), nil + }, 0o644|fs.ModeDir)) t.Run("Open file", func(t *testing.T) { f, err := memfs.Open("test.txt") @@ -78,6 +84,7 @@ func Test_AllOperations(t *testing.T) { require.NoError(t, err) assert.Equal(t, "test.txt", info.Name()) assert.Equal(t, fs.FileMode(0o644), info.Mode()) + assert.Equal(t, false, info.Mode().IsDir()) assert.Equal(t, false, info.IsDir()) assert.Equal(t, int64(11), info.Size()) }) @@ -87,6 +94,7 @@ func Test_AllOperations(t *testing.T) { require.NoError(t, err) assert.Equal(t, ".secret", info.Name()) assert.Equal(t, fs.FileMode(0o644), info.Mode()) + assert.Equal(t, false, info.Mode().IsDir()) assert.Equal(t, false, info.IsDir()) assert.Equal(t, int64(12), info.Size()) }) @@ -97,6 +105,16 @@ func Test_AllOperations(t *testing.T) { assert.Nil(t, info) }) + t.Run("Stat directory", func(t *testing.T) { + info, err := memfs.Stat("files/xyz") + require.NoError(t, err) + assert.Equal(t, "xyz", info.Name()) + assert.Equal(t, fs.FileMode(0o700|fs.ModeDir), info.Mode()) + assert.Equal(t, true, info.IsDir()) + assert.Equal(t, true, info.Mode().IsDir()) + assert.Equal(t, int64(256), info.Size()) + }) + t.Run("List directory at root", func(t *testing.T) { entries, err := fs.ReadDir(memfs, ".") require.NoError(t, err) @@ -125,8 +143,9 @@ func Test_AllOperations(t *testing.T) { info, err := memfs.Stat(".") require.NoError(t, err) assert.Equal(t, ".", info.Name()) - assert.Equal(t, fs.FileMode(0o700), info.Mode()) + assert.Equal(t, fs.FileMode(0o700|fs.ModeDir), info.Mode()) assert.Equal(t, true, info.IsDir()) + assert.Equal(t, true, info.Mode().IsDir()) }) t.Run("ReadFile", func(t *testing.T) {