file.go 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. // Copyright 2014 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package webdav
  5. import (
  6. "io"
  7. "net/http"
  8. "os"
  9. "path"
  10. "path/filepath"
  11. "strings"
  12. "sync"
  13. "time"
  14. )
  15. // A FileSystem implements access to a collection of named files. The elements
  16. // in a file path are separated by slash ('/', U+002F) characters, regardless
  17. // of host operating system convention.
  18. //
  19. // Each method has the same semantics as the os package's function of the same
  20. // name.
  21. type FileSystem interface {
  22. Mkdir(name string, perm os.FileMode) error
  23. OpenFile(name string, flag int, perm os.FileMode) (File, error)
  24. RemoveAll(name string) error
  25. Stat(name string) (os.FileInfo, error)
  26. }
  27. // A File is returned by a FileSystem's OpenFile method and can be served by a
  28. // Handler.
  29. type File interface {
  30. http.File
  31. io.Writer
  32. }
  33. // A Dir implements FileSystem using the native file system restricted to a
  34. // specific directory tree.
  35. //
  36. // While the FileSystem.OpenFile method takes '/'-separated paths, a Dir's
  37. // string value is a filename on the native file system, not a URL, so it is
  38. // separated by filepath.Separator, which isn't necessarily '/'.
  39. //
  40. // An empty Dir is treated as ".".
  41. type Dir string
  42. func (d Dir) resolve(name string) string {
  43. // This implementation is based on Dir.Open's code in the standard net/http package.
  44. if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 ||
  45. strings.Contains(name, "\x00") {
  46. return ""
  47. }
  48. dir := string(d)
  49. if dir == "" {
  50. dir = "."
  51. }
  52. return filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name)))
  53. }
  54. func (d Dir) Mkdir(name string, perm os.FileMode) error {
  55. if name = d.resolve(name); name == "" {
  56. return os.ErrNotExist
  57. }
  58. return os.Mkdir(name, perm)
  59. }
  60. func (d Dir) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
  61. if name = d.resolve(name); name == "" {
  62. return nil, os.ErrNotExist
  63. }
  64. return os.OpenFile(name, flag, perm)
  65. }
  66. func (d Dir) RemoveAll(name string) error {
  67. if name = d.resolve(name); name == "" {
  68. return os.ErrNotExist
  69. }
  70. if name == filepath.Clean(string(d)) {
  71. // Prohibit removing the virtual root directory.
  72. return os.ErrInvalid
  73. }
  74. return os.RemoveAll(name)
  75. }
  76. func (d Dir) Stat(name string) (os.FileInfo, error) {
  77. if name = d.resolve(name); name == "" {
  78. return nil, os.ErrNotExist
  79. }
  80. return os.Stat(name)
  81. }
  82. // NewMemFS returns a new in-memory FileSystem implementation.
  83. func NewMemFS() FileSystem {
  84. return &memFS{
  85. root: memFSNode{
  86. children: make(map[string]*memFSNode),
  87. mode: 0660 | os.ModeDir,
  88. modTime: time.Now(),
  89. },
  90. }
  91. }
  92. // A memFS implements FileSystem, storing all metadata and actual file data
  93. // in-memory. No limits on filesystem size are used, so it is not recommended
  94. // this be used where the clients are untrusted.
  95. //
  96. // Concurrent access is permitted. The tree structure is protected by a mutex,
  97. // and each node's contents and metadata are protected by a per-node mutex.
  98. //
  99. // TODO: Enforce file permissions.
  100. type memFS struct {
  101. mu sync.Mutex
  102. root memFSNode
  103. }
  104. // walk walks the directory tree for the fullname, calling f at each step. If f
  105. // returns an error, the walk will be aborted and return that same error.
  106. //
  107. // Each walk is atomic: fs's mutex is held for the entire operation, including
  108. // all calls to f.
  109. //
  110. // dir is the directory at that step, frag is the name fragment, and final is
  111. // whether it is the final step. For example, walking "/foo/bar/x" will result
  112. // in 3 calls to f:
  113. // - "/", "foo", false
  114. // - "/foo/", "bar", false
  115. // - "/foo/bar/", "x", true
  116. func (fs *memFS) walk(op, fullname string, f func(dir *memFSNode, frag string, final bool) error) error {
  117. fs.mu.Lock()
  118. defer fs.mu.Unlock()
  119. original := fullname
  120. fullname = path.Clean("/" + fullname)
  121. // Strip any leading "/"s to make fullname a relative path, as the walk
  122. // starts at fs.root.
  123. if fullname[0] == '/' {
  124. fullname = fullname[1:]
  125. }
  126. dir := &fs.root
  127. for {
  128. frag, remaining := fullname, ""
  129. i := strings.IndexRune(fullname, '/')
  130. final := i < 0
  131. if !final {
  132. frag, remaining = fullname[:i], fullname[i+1:]
  133. }
  134. if err := f(dir, frag, final); err != nil {
  135. return &os.PathError{
  136. Op: op,
  137. Path: original,
  138. Err: err,
  139. }
  140. }
  141. if final {
  142. break
  143. }
  144. child := dir.children[frag]
  145. if child == nil {
  146. return &os.PathError{
  147. Op: op,
  148. Path: original,
  149. Err: os.ErrNotExist,
  150. }
  151. }
  152. if !child.IsDir() {
  153. return &os.PathError{
  154. Op: op,
  155. Path: original,
  156. Err: os.ErrInvalid,
  157. }
  158. }
  159. dir, fullname = child, remaining
  160. }
  161. return nil
  162. }
  163. func (fs *memFS) Mkdir(name string, perm os.FileMode) error {
  164. return fs.walk("mkdir", name, func(dir *memFSNode, frag string, final bool) error {
  165. if !final {
  166. return nil
  167. }
  168. if frag == "" {
  169. return os.ErrInvalid
  170. }
  171. if _, ok := dir.children[frag]; ok {
  172. return os.ErrExist
  173. }
  174. dir.children[frag] = &memFSNode{
  175. name: frag,
  176. children: make(map[string]*memFSNode),
  177. mode: perm.Perm() | os.ModeDir,
  178. modTime: time.Now(),
  179. }
  180. return nil
  181. })
  182. }
  183. func (fs *memFS) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
  184. var ret *memFile
  185. err := fs.walk("open", name, func(dir *memFSNode, frag string, final bool) error {
  186. if !final {
  187. return nil
  188. }
  189. if frag == "" {
  190. return os.ErrInvalid
  191. }
  192. if flag&(os.O_SYNC|os.O_APPEND) != 0 {
  193. return os.ErrInvalid
  194. }
  195. n := dir.children[frag]
  196. if flag&os.O_CREATE != 0 {
  197. if flag&os.O_EXCL != 0 && n != nil {
  198. return os.ErrExist
  199. }
  200. if n == nil {
  201. n = &memFSNode{
  202. name: frag,
  203. mode: perm.Perm(),
  204. }
  205. dir.children[frag] = n
  206. }
  207. }
  208. if n == nil {
  209. return os.ErrNotExist
  210. }
  211. if flag&(os.O_WRONLY|os.O_RDWR) != 0 && flag&os.O_TRUNC != 0 {
  212. n.mu.Lock()
  213. n.data = nil
  214. n.mu.Unlock()
  215. }
  216. children := make([]os.FileInfo, 0, len(n.children))
  217. for _, c := range n.children {
  218. children = append(children, c)
  219. }
  220. ret = &memFile{
  221. n: n,
  222. children: children,
  223. }
  224. return nil
  225. })
  226. if err != nil {
  227. return nil, err
  228. }
  229. return ret, nil
  230. }
  231. func (fs *memFS) RemoveAll(name string) error {
  232. return fs.walk("remove", name, func(dir *memFSNode, frag string, final bool) error {
  233. if !final {
  234. return nil
  235. }
  236. if frag == "" {
  237. return os.ErrInvalid
  238. }
  239. if _, ok := dir.children[frag]; !ok {
  240. return os.ErrNotExist
  241. }
  242. delete(dir.children, frag)
  243. return nil
  244. })
  245. }
  246. func (fs *memFS) Stat(name string) (os.FileInfo, error) {
  247. var n *memFSNode
  248. err := fs.walk("stat", name, func(dir *memFSNode, frag string, final bool) error {
  249. if !final {
  250. return nil
  251. }
  252. if frag == "" {
  253. return os.ErrInvalid
  254. }
  255. n = dir.children[frag]
  256. if n == nil {
  257. return os.ErrNotExist
  258. }
  259. return nil
  260. })
  261. if err != nil {
  262. return nil, err
  263. }
  264. return n, nil
  265. }
  266. // A memFSNode represents a single entry in the in-memory filesystem and also
  267. // implements os.FileInfo.
  268. type memFSNode struct {
  269. name string
  270. mu sync.Mutex
  271. modTime time.Time
  272. mode os.FileMode
  273. children map[string]*memFSNode
  274. data []byte
  275. }
  276. func (n *memFSNode) Name() string {
  277. return n.name
  278. }
  279. func (n *memFSNode) Size() int64 {
  280. n.mu.Lock()
  281. defer n.mu.Unlock()
  282. return int64(len(n.data))
  283. }
  284. func (n *memFSNode) Mode() os.FileMode {
  285. n.mu.Lock()
  286. defer n.mu.Unlock()
  287. return n.mode
  288. }
  289. func (n *memFSNode) ModTime() time.Time {
  290. n.mu.Lock()
  291. defer n.mu.Unlock()
  292. return n.modTime
  293. }
  294. func (n *memFSNode) IsDir() bool {
  295. return n.Mode().IsDir()
  296. }
  297. func (n *memFSNode) Sys() interface{} {
  298. return nil
  299. }
  300. // A memFile is a File implementation for a memFSNode. It is a per-file (not
  301. // per-node) read/write position, and if the node is a directory, a snapshot of
  302. // that node's children.
  303. type memFile struct {
  304. n *memFSNode
  305. children []os.FileInfo
  306. // Changes to pos are guarded by n.mu.
  307. pos int
  308. }
  309. func (f *memFile) Close() error {
  310. return nil
  311. }
  312. func (f *memFile) Read(p []byte) (int, error) {
  313. if f.n.IsDir() {
  314. return 0, os.ErrInvalid
  315. }
  316. f.n.mu.Lock()
  317. defer f.n.mu.Unlock()
  318. if f.pos >= len(f.n.data) {
  319. return 0, io.EOF
  320. }
  321. n := copy(p, f.n.data[f.pos:])
  322. f.pos += n
  323. return n, nil
  324. }
  325. func (f *memFile) Readdir(count int) ([]os.FileInfo, error) {
  326. if !f.n.IsDir() {
  327. return nil, os.ErrInvalid
  328. }
  329. f.n.mu.Lock()
  330. defer f.n.mu.Unlock()
  331. old := f.pos
  332. if old >= len(f.children) {
  333. return nil, io.EOF
  334. }
  335. if count > 0 {
  336. f.pos += count
  337. if f.pos > len(f.children) {
  338. f.pos = len(f.children)
  339. }
  340. } else {
  341. f.pos = len(f.children)
  342. old = 0
  343. }
  344. return f.children[old:f.pos], nil
  345. }
  346. func (f *memFile) Seek(offset int64, whence int) (int64, error) {
  347. f.n.mu.Lock()
  348. defer f.n.mu.Unlock()
  349. npos := f.pos
  350. // TODO: How to handle offsets greater than the size of system int?
  351. switch whence {
  352. case os.SEEK_SET:
  353. npos = int(offset)
  354. case os.SEEK_CUR:
  355. npos += int(offset)
  356. case os.SEEK_END:
  357. npos = len(f.n.data) + int(offset)
  358. default:
  359. npos = -1
  360. }
  361. if npos < 0 {
  362. return 0, os.ErrInvalid
  363. }
  364. f.pos = npos
  365. return int64(f.pos), nil
  366. }
  367. func (f *memFile) Stat() (os.FileInfo, error) {
  368. return f.n, nil
  369. }
  370. func (f *memFile) Write(p []byte) (int, error) {
  371. f.n.mu.Lock()
  372. defer f.n.mu.Unlock()
  373. epos := len(p) + f.pos
  374. if epos > len(f.n.data) {
  375. d := make([]byte, epos)
  376. copy(d, f.n.data)
  377. f.n.data = d
  378. }
  379. copy(f.n.data[f.pos:], p)
  380. f.n.modTime = time.Now()
  381. return len(p), nil
  382. }