Implement and test read only MemMapFs file handles

Fixes #53
This commit is contained in:
Martin Bertschler 2016-01-04 01:54:48 +01:00
parent 023640316a
commit f0b9fc1bdb
4 changed files with 76 additions and 7 deletions

View File

@ -13,7 +13,7 @@ func TestFilterReadOnly(t *testing.T) {
if err == nil { if err == nil {
t.Errorf("Did not fail to create file") t.Errorf("Did not fail to create file")
} }
t.Logf("ERR=%s", err) // t.Logf("ERR=%s", err)
} }
func TestFilterReadonlyRemoveAndRead(t *testing.T) { func TestFilterReadonlyRemoveAndRead(t *testing.T) {
@ -59,9 +59,10 @@ func TestFilterRegexp(t *testing.T) {
fs.AddFilter(NewRegexpFilter(regexp.MustCompile(`\.txt$`))) fs.AddFilter(NewRegexpFilter(regexp.MustCompile(`\.txt$`)))
_, err := fs.Create("/file.html") _, err := fs.Create("/file.html")
if err == nil { if err == nil {
t.Errorf("Did not fail to create file") t.Errorf("Did not fail to create file")
} }
t.Logf("ERR=%s", err) // t.Logf("ERR=%s", err)
} }
func TestFilterRORegexpChain(t *testing.T) { func TestFilterRORegexpChain(t *testing.T) {
@ -73,7 +74,7 @@ func TestFilterRORegexpChain(t *testing.T) {
if err == nil { if err == nil {
t.Errorf("Did not fail to create file") t.Errorf("Did not fail to create file")
} }
t.Logf("ERR=%s", err) // t.Logf("ERR=%s", err)
} }
func TestFilterRegexReadDir(t *testing.T) { func TestFilterRegexReadDir(t *testing.T) {

View File

@ -33,6 +33,7 @@ type File struct {
at int64 at int64
readDirCount int64 readDirCount int64
closed bool closed bool
readOnly bool
fileData *FileData fileData *FileData
} }
@ -40,6 +41,10 @@ func NewFileHandle(data *FileData) *File {
return &File{fileData: data} return &File{fileData: data}
} }
func NewReadOnlyFileHandle(data *FileData) *File {
return &File{fileData: data, readOnly: true}
}
func (f File) Data() *FileData { func (f File) Data() *FileData {
return f.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) { 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) n = len(b)
cur := atomic.LoadInt64(&f.at) cur := atomic.LoadInt64(&f.at)
f.fileData.Lock() f.fileData.Lock()

View File

@ -163,6 +163,22 @@ func normalizePath(path string) string {
} }
func (m *MemMapFs) Open(name string) (File, error) { 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) name = normalizePath(name)
m.mu.RLock() m.mu.RLock()
@ -171,7 +187,7 @@ func (m *MemMapFs) Open(name string) (File, error) {
if !ok { if !ok {
return nil, &os.PathError{"open", name, ErrFileNotFound} return nil, &os.PathError{"open", name, ErrFileNotFound}
} }
return mem.NewFileHandle(f), nil return f, nil
} }
func (m *MemMapFs) lockfreeOpen(name string) (*mem.FileData, error) { 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) { 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) { if os.IsNotExist(err) && (flag&os.O_CREATE > 0) {
file, err = m.Create(name) file, err = m.Create(name)
} }
if err != nil { if err != nil {
return nil, err return nil, err
} }
if flag == os.O_RDONLY {
file = mem.NewReadOnlyFileHandle(file.(*mem.File).Data())
}
if flag&os.O_APPEND > 0 { if flag&os.O_APPEND > 0 {
_, err = file.Seek(0, os.SEEK_END) _, err = file.Seek(0, os.SEEK_END)
if err != nil { if err != nil {

View File

@ -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 // Fails if multiple file objects use the same file.at counter in MemMapFs
func TestMultipleOpenFiles(t *testing.T) { func TestMultipleOpenFiles(t *testing.T) {
defer removeAllTestFiles(t) defer removeAllTestFiles(t)
const fileName = "./afero-demo2.txt" const fileName = "afero-demo2.txt"
var data = make([][]byte, len(Fss)) var data = make([][]byte, len(Fss))
@ -113,7 +113,7 @@ func TestMultipleOpenFiles(t *testing.T) {
path := filepath.Join(dir, fileName) path := filepath.Join(dir, fileName)
fh1, err := fs.Create(path) fh1, err := fs.Create(path)
if err != nil { if err != nil {
t.Error("os.Create failed: " + err.Error()) t.Error("fs.Create failed: " + err.Error())
} }
_, err = fh1.Write([]byte("test")) _, err = fh1.Write([]byte("test"))
if err != nil { 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()
}
}