x/net/webdav: add a Dir type, analogous to http.Dir.
LGTM=nmvc, dave
R=dave, nmvc
CC=bradfitz, dr.volker.dobler, golang-codereviews, robert.stepanek
https://golang.org/cl/173100044
diff --git a/webdav/file.go b/webdav/file.go
index a2953ae..097fe6b 100644
--- a/webdav/file.go
+++ b/webdav/file.go
@@ -8,21 +8,84 @@
"io"
"net/http"
"os"
+ "path"
+ "path/filepath"
+ "strings"
)
-// TODO: comment that paths are always "/"-separated, even for Windows servers.
-
+// A FileSystem implements access to a collection of named files. The elements
+// in a file path are separated by slash ('/', U+002F) characters, regardless
+// of host operating system convention.
+//
+// Each method has the same semantics as the os package's function of the same
+// name.
type FileSystem interface {
- Mkdir(path string, perm os.FileMode) error
- OpenFile(path string, flag int, perm os.FileMode) (File, error)
- RemoveAll(path string) error
- Stat(path string) (os.FileInfo, error)
+ Mkdir(name string, perm os.FileMode) error
+ OpenFile(name string, flag int, perm os.FileMode) (File, error)
+ RemoveAll(name string) error
+ Stat(name string) (os.FileInfo, error)
}
+// A File is returned by a FileSystem's OpenFile method and can be served by a
+// Handler.
type File interface {
http.File
io.Writer
}
+// A Dir implements FileSystem using the native file system restricted to a
+// specific directory tree.
+//
+// While the FileSystem.OpenFile method takes '/'-separated paths, a Dir's
+// string value is a filename on the native file system, not a URL, so it is
+// separated by filepath.Separator, which isn't necessarily '/'.
+//
+// An empty Dir is treated as ".".
+type Dir string
+
+func (d Dir) resolve(name string) string {
+ // This implementation is based on Dir.Open's code in the standard net/http package.
+ if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 ||
+ strings.Contains(name, "\x00") {
+ return ""
+ }
+ dir := string(d)
+ if dir == "" {
+ dir = "."
+ }
+ return filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name)))
+}
+
+func (d Dir) Mkdir(name string, perm os.FileMode) error {
+ if name = d.resolve(name); name == "" {
+ return os.ErrNotExist
+ }
+ return os.Mkdir(name, perm)
+}
+
+func (d Dir) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
+ if name = d.resolve(name); name == "" {
+ return nil, os.ErrNotExist
+ }
+ return os.OpenFile(name, flag, perm)
+}
+
+func (d Dir) RemoveAll(name string) error {
+ if name = d.resolve(name); name == "" {
+ return os.ErrNotExist
+ }
+ if name == filepath.Clean(string(d)) {
+ // Prohibit removing the virtual root directory.
+ return os.ErrInvalid
+ }
+ return os.RemoveAll(name)
+}
+
+func (d Dir) Stat(name string) (os.FileInfo, error) {
+ if name = d.resolve(name); name == "" {
+ return nil, os.ErrNotExist
+ }
+ return os.Stat(name)
+}
+
// TODO: a MemFS implementation.
-// TODO: a RealFS implementation, backed by the real, OS-provided file system.