webdav: make the memFS (not the memFSNode) the canonical source of a
file name.
This fixes inconsistent stat names after a file is renamed.
Change-Id: Ie90f8abaa31d46a87834266053b61d7770f854e2
Reviewed-on: https://go-review.googlesource.com/3051
Reviewed-by: Nick Cooper <nmvc@google.com>
Reviewed-by: Dave Cheney <dave@cheney.net>
diff --git a/webdav/file.go b/webdav/file.go
index eac339f..eb6abd5 100644
--- a/webdav/file.go
+++ b/webdav/file.go
@@ -194,7 +194,7 @@
Err: os.ErrNotExist,
}
}
- if !child.IsDir() {
+ if !child.mode.IsDir() {
return &os.PathError{
Op: op,
Path: original,
@@ -245,7 +245,6 @@
return os.ErrExist
}
dir.children[frag] = &memFSNode{
- name: frag,
children: make(map[string]*memFSNode),
mode: perm.Perm() | os.ModeDir,
modTime: time.Now(),
@@ -267,7 +266,7 @@
if flag&(os.O_WRONLY|os.O_RDWR) != 0 {
return nil, os.ErrPermission
}
- n = &fs.root
+ n, frag = &fs.root, "/"
} else {
n = dir.children[frag]
@@ -281,7 +280,6 @@
}
if n == nil {
n = &memFSNode{
- name: frag,
mode: perm.Perm(),
}
dir.children[frag] = n
@@ -298,11 +296,12 @@
}
children := make([]os.FileInfo, 0, len(n.children))
- for _, c := range n.children {
- children = append(children, c)
+ for cName, c := range n.children {
+ children = append(children, c.stat(cName))
}
return &memFile{
n: n,
+ nameSnapshot: frag,
childrenSnapshot: children,
}, nil
}
@@ -359,12 +358,9 @@
if !ok {
return os.ErrNotExist
}
- if oNode.IsDir() {
+ if oNode.children != nil {
if nNode, ok := nDir.children[nFrag]; ok {
- nNode.mu.Lock()
- isDir := nNode.mode.IsDir()
- nNode.mu.Unlock()
- if !isDir {
+ if nNode.children == nil {
return errNotADirectory
}
if len(nNode.children) != 0 {
@@ -387,10 +383,10 @@
}
if dir == nil {
// We're stat'ting the root.
- return &fs.root, nil
+ return fs.root.stat("/"), nil
}
if n, ok := dir.children[frag]; ok {
- return n, nil
+ return n.stat(path.Base(name)), nil
}
return nil, os.ErrNotExist
}
@@ -398,53 +394,46 @@
// A memFSNode represents a single entry in the in-memory filesystem and also
// implements os.FileInfo.
type memFSNode struct {
- name string
-
// children is protected by memFS.mu.
children map[string]*memFSNode
mu sync.Mutex
- modTime time.Time
- mode os.FileMode
data []byte
+ mode os.FileMode
+ modTime time.Time
}
-func (n *memFSNode) Name() string {
- return n.name
-}
-
-func (n *memFSNode) Size() int64 {
+func (n *memFSNode) stat(name string) *memFileInfo {
n.mu.Lock()
defer n.mu.Unlock()
- return int64(len(n.data))
+ return &memFileInfo{
+ name: name,
+ size: int64(len(n.data)),
+ mode: n.mode,
+ modTime: n.modTime,
+ }
}
-func (n *memFSNode) Mode() os.FileMode {
- n.mu.Lock()
- defer n.mu.Unlock()
- return n.mode
+type memFileInfo struct {
+ name string
+ size int64
+ mode os.FileMode
+ modTime time.Time
}
-func (n *memFSNode) ModTime() time.Time {
- n.mu.Lock()
- defer n.mu.Unlock()
- return n.modTime
-}
-
-func (n *memFSNode) IsDir() bool {
- return n.Mode().IsDir()
-}
-
-func (n *memFSNode) Sys() interface{} {
- return nil
-}
+func (f *memFileInfo) Name() string { return f.name }
+func (f *memFileInfo) Size() int64 { return f.size }
+func (f *memFileInfo) Mode() os.FileMode { return f.mode }
+func (f *memFileInfo) ModTime() time.Time { return f.modTime }
+func (f *memFileInfo) IsDir() bool { return f.mode.IsDir() }
+func (f *memFileInfo) Sys() interface{} { return nil }
// A memFile is a File implementation for a memFSNode. It is a per-file (not
-// per-node) read/write position, and if the node is a directory, a snapshot of
-// that node's children.
+// per-node) read/write position, and a snapshot of the memFS' tree structure
+// (a node's name and children) for that node.
type memFile struct {
- n *memFSNode
- // childrenSnapshot is a snapshot of n.children.
+ n *memFSNode
+ nameSnapshot string
childrenSnapshot []os.FileInfo
// pos is protected by n.mu.
pos int
@@ -518,7 +507,7 @@
}
func (f *memFile) Stat() (os.FileInfo, error) {
- return f.n, nil
+ return f.n.stat(f.nameSnapshot), nil
}
func (f *memFile) Write(p []byte) (int, error) {