Commit dc4a180a authored by Nigel Tao's avatar Nigel Tao

webdav: allow opening the memFS root file "/".

Also fix memFile.Readdir(n) to return nil error when n <= 0, to match
package os. Fix memFile.Write to fail when writing to directories. Avoid
taking the node lock twice for memFile methods.

Change-Id: I72b0753c9376c3889972662e0454efe67d73479a
Reviewed-on: https://go-review.googlesource.com/2711Reviewed-by: 's avatarDave Cheney <dave@cheney.net>
Reviewed-by: 's avatarNick Cooper <nmvc@google.com>
parent ca657d0b
...@@ -126,6 +126,8 @@ type memFS struct { ...@@ -126,6 +126,8 @@ type memFS struct {
// - "/", "foo", false // - "/", "foo", false
// - "/foo/", "bar", false // - "/foo/", "bar", false
// - "/foo/bar/", "x", true // - "/foo/bar/", "x", true
// The frag argument will be empty only if dir is the root node and the walk
// ends at that root node.
func (fs *memFS) walk(op, fullname string, f func(dir *memFSNode, frag string, final bool) error) error { func (fs *memFS) walk(op, fullname string, f func(dir *memFSNode, frag string, final bool) error) error {
fs.mu.Lock() fs.mu.Lock()
defer fs.mu.Unlock() defer fs.mu.Unlock()
...@@ -147,6 +149,9 @@ func (fs *memFS) walk(op, fullname string, f func(dir *memFSNode, frag string, f ...@@ -147,6 +149,9 @@ func (fs *memFS) walk(op, fullname string, f func(dir *memFSNode, frag string, f
if !final { if !final {
frag, remaining = fullname[:i], fullname[i+1:] frag, remaining = fullname[:i], fullname[i+1:]
} }
if frag == "" && dir != &fs.root {
panic("webdav: empty path fragment for a clean path")
}
if err := f(dir, frag, final); err != nil { if err := f(dir, frag, final); err != nil {
return &os.PathError{ return &os.PathError{
Op: op, Op: op,
...@@ -204,32 +209,38 @@ func (fs *memFS) OpenFile(name string, flag int, perm os.FileMode) (File, error) ...@@ -204,32 +209,38 @@ func (fs *memFS) OpenFile(name string, flag int, perm os.FileMode) (File, error)
if !final { if !final {
return nil return nil
} }
var n *memFSNode
if frag == "" { if frag == "" {
return os.ErrInvalid if flag&(os.O_WRONLY|os.O_RDWR) != 0 {
} return os.ErrPermission
if flag&(os.O_SYNC|os.O_APPEND) != 0 {
return os.ErrInvalid
}
n := dir.children[frag]
if flag&os.O_CREATE != 0 {
if flag&os.O_EXCL != 0 && n != nil {
return os.ErrExist
} }
if n == nil { n = &fs.root
n = &memFSNode{
name: frag, } else {
mode: perm.Perm(), n = dir.children[frag]
if flag&(os.O_SYNC|os.O_APPEND) != 0 {
return os.ErrInvalid
}
if flag&os.O_CREATE != 0 {
if flag&os.O_EXCL != 0 && n != nil {
return os.ErrExist
}
if n == nil {
n = &memFSNode{
name: frag,
mode: perm.Perm(),
}
dir.children[frag] = n
} }
dir.children[frag] = n
} }
} if n == nil {
if n == nil { return os.ErrNotExist
return os.ErrNotExist }
} if flag&(os.O_WRONLY|os.O_RDWR) != 0 && flag&os.O_TRUNC != 0 {
if flag&(os.O_WRONLY|os.O_RDWR) != 0 && flag&os.O_TRUNC != 0 { n.mu.Lock()
n.mu.Lock() n.data = nil
n.data = nil n.mu.Unlock()
n.mu.Unlock() }
} }
children := make([]os.FileInfo, 0, len(n.children)) children := make([]os.FileInfo, 0, len(n.children))
...@@ -271,7 +282,8 @@ func (fs *memFS) Stat(name string) (os.FileInfo, error) { ...@@ -271,7 +282,8 @@ func (fs *memFS) Stat(name string) (os.FileInfo, error) {
return nil return nil
} }
if frag == "" { if frag == "" {
return os.ErrInvalid n = &fs.root
return nil
} }
n = dir.children[frag] n = dir.children[frag]
if n == nil { if n == nil {
...@@ -342,11 +354,11 @@ func (f *memFile) Close() error { ...@@ -342,11 +354,11 @@ func (f *memFile) Close() error {
} }
func (f *memFile) Read(p []byte) (int, error) { func (f *memFile) Read(p []byte) (int, error) {
if f.n.IsDir() {
return 0, os.ErrInvalid
}
f.n.mu.Lock() f.n.mu.Lock()
defer f.n.mu.Unlock() defer f.n.mu.Unlock()
if f.n.mode.IsDir() {
return 0, os.ErrInvalid
}
if f.pos >= len(f.n.data) { if f.pos >= len(f.n.data) {
return 0, io.EOF return 0, io.EOF
} }
...@@ -356,14 +368,19 @@ func (f *memFile) Read(p []byte) (int, error) { ...@@ -356,14 +368,19 @@ func (f *memFile) Read(p []byte) (int, error) {
} }
func (f *memFile) Readdir(count int) ([]os.FileInfo, error) { func (f *memFile) Readdir(count int) ([]os.FileInfo, error) {
if !f.n.IsDir() {
return nil, os.ErrInvalid
}
f.n.mu.Lock() f.n.mu.Lock()
defer f.n.mu.Unlock() defer f.n.mu.Unlock()
if !f.n.mode.IsDir() {
return nil, os.ErrInvalid
}
old := f.pos old := f.pos
if old >= len(f.children) { if old >= len(f.children) {
return nil, io.EOF // The os.File Readdir docs say that at the end of a directory,
// the error is io.EOF if count > 0 and nil if count <= 0.
if count > 0 {
return nil, io.EOF
}
return nil, nil
} }
if count > 0 { if count > 0 {
f.pos += count f.pos += count
...@@ -408,6 +425,9 @@ func (f *memFile) Write(p []byte) (int, error) { ...@@ -408,6 +425,9 @@ func (f *memFile) Write(p []byte) (int, error) {
f.n.mu.Lock() f.n.mu.Lock()
defer f.n.mu.Unlock() defer f.n.mu.Unlock()
if f.n.mode.IsDir() {
return 0, os.ErrInvalid
}
if f.pos < len(f.n.data) { if f.pos < len(f.n.data) {
n := copy(f.n.data[f.pos:], p) n := copy(f.n.data[f.pos:], p)
f.pos += n f.pos += n
......
...@@ -198,6 +198,61 @@ func TestWalk(t *testing.T) { ...@@ -198,6 +198,61 @@ func TestWalk(t *testing.T) {
} }
} }
func TestMemFSRoot(t *testing.T) {
fs := NewMemFS()
for i := 0; i < 5; i++ {
stat, err := fs.Stat("/")
if err != nil {
t.Fatalf("i=%d: Stat: %v", i, err)
}
if !stat.IsDir() {
t.Fatalf("i=%d: Stat.IsDir is false, want true", i)
}
f, err := fs.OpenFile("/", os.O_RDONLY, 0)
if err != nil {
t.Fatalf("i=%d: OpenFile: %v", i, err)
}
defer f.Close()
children, err := f.Readdir(-1)
if err != nil {
t.Fatalf("i=%d: Readdir: %v", i, err)
}
if len(children) != i {
t.Fatalf("i=%d: got %d children, want %d", i, len(children), i)
}
if _, err := f.Write(make([]byte, 1)); err == nil {
t.Fatalf("i=%d: Write: got nil error, want non-nil", i)
}
if err := fs.Mkdir(fmt.Sprintf("/dir%d", i), 0777); err != nil {
t.Fatalf("i=%d: Mkdir: %v", i, err)
}
}
}
func TestMemFileReaddir(t *testing.T) {
fs := NewMemFS()
if err := fs.Mkdir("/foo", 0777); err != nil {
t.Fatalf("Mkdir: %v", err)
}
readdir := func(count int) ([]os.FileInfo, error) {
f, err := fs.OpenFile("/foo", os.O_RDONLY, 0)
if err != nil {
t.Fatalf("OpenFile: %v", err)
}
defer f.Close()
return f.Readdir(count)
}
if got, err := readdir(-1); len(got) != 0 || err != nil {
t.Fatalf("readdir(-1): got %d fileInfos with err=%v, want 0, <nil>", len(got), err)
}
if got, err := readdir(+1); len(got) != 0 || err != io.EOF {
t.Fatalf("readdir(+1): got %d fileInfos with err=%v, want 0, EOF", len(got), err)
}
}
func TestMemFile(t *testing.T) { func TestMemFile(t *testing.T) {
testCases := []string{ testCases := []string{
"wantData ", "wantData ",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment