From f0b9fc1bdbc068e03f333e7d7e0cdb76d8b13a52 Mon Sep 17 00:00:00 2001 From: Martin Bertschler Date: Mon, 4 Jan 2016 01:54:48 +0100 Subject: [PATCH] Implement and test read only MemMapFs file handles Fixes #53 --- filter_test.go | 7 ++++--- mem/file.go | 8 ++++++++ memmap.go | 23 +++++++++++++++++++++-- memmap_test.go | 45 +++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 76 insertions(+), 7 deletions(-) diff --git a/filter_test.go b/filter_test.go index dcbc6f4..679cf92 100644 --- a/filter_test.go +++ b/filter_test.go @@ -13,7 +13,7 @@ func TestFilterReadOnly(t *testing.T) { if err == nil { t.Errorf("Did not fail to create file") } - t.Logf("ERR=%s", err) + // t.Logf("ERR=%s", err) } func TestFilterReadonlyRemoveAndRead(t *testing.T) { @@ -59,9 +59,10 @@ func TestFilterRegexp(t *testing.T) { fs.AddFilter(NewRegexpFilter(regexp.MustCompile(`\.txt$`))) _, err := fs.Create("/file.html") if err == nil { + t.Errorf("Did not fail to create file") } - t.Logf("ERR=%s", err) + // t.Logf("ERR=%s", err) } func TestFilterRORegexpChain(t *testing.T) { @@ -73,7 +74,7 @@ func TestFilterRORegexpChain(t *testing.T) { if err == nil { t.Errorf("Did not fail to create file") } - t.Logf("ERR=%s", err) + // t.Logf("ERR=%s", err) } func TestFilterRegexReadDir(t *testing.T) { diff --git a/mem/file.go b/mem/file.go index e92d943..7d43a52 100644 --- a/mem/file.go +++ b/mem/file.go @@ -33,6 +33,7 @@ type File struct { at int64 readDirCount int64 closed bool + readOnly bool fileData *FileData } @@ -40,6 +41,10 @@ func NewFileHandle(data *FileData) *File { return &File{fileData: data} } +func NewReadOnlyFileHandle(data *FileData) *File { + return &File{fileData: data, readOnly: true} +} + func (f File) Data() *FileData { return f.fileData } @@ -203,6 +208,9 @@ func (f *File) Seek(offset int64, whence int) (int64, error) { } func (f *File) Write(b []byte) (n int, err error) { + if f.readOnly { + return 0, &os.PathError{"write", f.fileData.name, errors.New("file handle is read only")} + } n = len(b) cur := atomic.LoadInt64(&f.at) f.fileData.Lock() diff --git a/memmap.go b/memmap.go index 2c1ba39..2f2c612 100644 --- a/memmap.go +++ b/memmap.go @@ -163,6 +163,22 @@ func normalizePath(path string) string { } func (m *MemMapFs) Open(name string) (File, error) { + f, err := m.open(name) + if f != nil { + return mem.NewReadOnlyFileHandle(f), err + } + return nil, err +} + +func (m *MemMapFs) openWrite(name string) (File, error) { + f, err := m.open(name) + if f != nil { + return mem.NewFileHandle(f), err + } + return nil, err +} + +func (m *MemMapFs) open(name string) (*mem.FileData, error) { name = normalizePath(name) m.mu.RLock() @@ -171,7 +187,7 @@ func (m *MemMapFs) Open(name string) (File, error) { if !ok { return nil, &os.PathError{"open", name, ErrFileNotFound} } - return mem.NewFileHandle(f), nil + return f, nil } func (m *MemMapFs) lockfreeOpen(name string) (*mem.FileData, error) { @@ -185,13 +201,16 @@ func (m *MemMapFs) lockfreeOpen(name string) (*mem.FileData, error) { } func (m *MemMapFs) OpenFile(name string, flag int, perm os.FileMode) (File, error) { - file, err := m.Open(name) + file, err := m.openWrite(name) if os.IsNotExist(err) && (flag&os.O_CREATE > 0) { file, err = m.Create(name) } if err != nil { return nil, err } + if flag == os.O_RDONLY { + file = mem.NewReadOnlyFileHandle(file.(*mem.File).Data()) + } if flag&os.O_APPEND > 0 { _, err = file.Seek(0, os.SEEK_END) if err != nil { diff --git a/memmap_test.go b/memmap_test.go index d8de243..c0e6825 100644 --- a/memmap_test.go +++ b/memmap_test.go @@ -104,7 +104,7 @@ func checkPathError(t *testing.T, err error, op string) { // Fails if multiple file objects use the same file.at counter in MemMapFs func TestMultipleOpenFiles(t *testing.T) { defer removeAllTestFiles(t) - const fileName = "./afero-demo2.txt" + const fileName = "afero-demo2.txt" var data = make([][]byte, len(Fss)) @@ -113,7 +113,7 @@ func TestMultipleOpenFiles(t *testing.T) { path := filepath.Join(dir, fileName) fh1, err := fs.Create(path) if err != nil { - t.Error("os.Create failed: " + err.Error()) + t.Error("fs.Create failed: " + err.Error()) } _, err = fh1.Write([]byte("test")) if err != nil { @@ -167,3 +167,44 @@ func TestMultipleOpenFiles(t *testing.T) { } } } + +// Test if file.Write() fails when opened as read only +func TestReadOnly(t *testing.T) { + defer removeAllTestFiles(t) + const fileName = "afero-demo.txt" + + for _, fs := range Fss { + dir := testDir(fs) + path := filepath.Join(dir, fileName) + + f, err := fs.Create(path) + if err != nil { + t.Error(fs.Name()+":", "fs.Create failed: "+err.Error()) + } + _, err = f.Write([]byte("test")) + if err != nil { + t.Error(fs.Name()+":", "Write failed: "+err.Error()) + } + f.Close() + + f, err = fs.Open(path) + if err != nil { + t.Error("fs.Open failed: " + err.Error()) + } + _, err = f.Write([]byte("data")) + if err == nil { + t.Error(fs.Name()+":", "No write error") + } + f.Close() + + f, err = fs.OpenFile(path, os.O_RDONLY, 0644) + if err != nil { + t.Error("fs.Open failed: " + err.Error()) + } + _, err = f.Write([]byte("data")) + if err == nil { + t.Error(fs.Name()+":", "No write error") + } + f.Close() + } +}