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/2711
Reviewed-by: Dave Cheney <dave@cheney.net>
Reviewed-by: Nick Cooper <nmvc@google.com>
diff --git a/webdav/file.go b/webdav/file.go
index 4a0c7ff..782f03d 100644
--- a/webdav/file.go
+++ b/webdav/file.go
@@ -126,6 +126,8 @@
// - "/", "foo", false
// - "/foo/", "bar", false
// - "/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 {
fs.mu.Lock()
defer fs.mu.Unlock()
@@ -147,6 +149,9 @@
if !final {
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 {
return &os.PathError{
Op: op,
@@ -204,32 +209,38 @@
if !final {
return nil
}
+ var n *memFSNode
if frag == "" {
- return os.ErrInvalid
- }
- 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 flag&(os.O_WRONLY|os.O_RDWR) != 0 {
+ return os.ErrPermission
+ }
+ n = &fs.root
+
+ } else {
+ 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
+ }
}
if n == nil {
- n = &memFSNode{
- name: frag,
- mode: perm.Perm(),
- }
- dir.children[frag] = n
+ return os.ErrNotExist
}
- }
- if n == nil {
- return os.ErrNotExist
- }
- if flag&(os.O_WRONLY|os.O_RDWR) != 0 && flag&os.O_TRUNC != 0 {
- n.mu.Lock()
- n.data = nil
- n.mu.Unlock()
+ if flag&(os.O_WRONLY|os.O_RDWR) != 0 && flag&os.O_TRUNC != 0 {
+ n.mu.Lock()
+ n.data = nil
+ n.mu.Unlock()
+ }
}
children := make([]os.FileInfo, 0, len(n.children))
@@ -271,7 +282,8 @@
return nil
}
if frag == "" {
- return os.ErrInvalid
+ n = &fs.root
+ return nil
}
n = dir.children[frag]
if n == nil {
@@ -342,11 +354,11 @@
}
func (f *memFile) Read(p []byte) (int, error) {
- if f.n.IsDir() {
- return 0, os.ErrInvalid
- }
f.n.mu.Lock()
defer f.n.mu.Unlock()
+ if f.n.mode.IsDir() {
+ return 0, os.ErrInvalid
+ }
if f.pos >= len(f.n.data) {
return 0, io.EOF
}
@@ -356,14 +368,19 @@
}
func (f *memFile) Readdir(count int) ([]os.FileInfo, error) {
- if !f.n.IsDir() {
- return nil, os.ErrInvalid
- }
f.n.mu.Lock()
defer f.n.mu.Unlock()
+ if !f.n.mode.IsDir() {
+ return nil, os.ErrInvalid
+ }
old := f.pos
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 {
f.pos += count
@@ -408,6 +425,9 @@
f.n.mu.Lock()
defer f.n.mu.Unlock()
+ if f.n.mode.IsDir() {
+ return 0, os.ErrInvalid
+ }
if f.pos < len(f.n.data) {
n := copy(f.n.data[f.pos:], p)
f.pos += n