1
2
3
4
5 package webdav
6
7 import (
8 "context"
9 "encoding/xml"
10 "io"
11 "net/http"
12 "os"
13 "path"
14 "path/filepath"
15 "runtime"
16 "strings"
17 "sync"
18 "time"
19 )
20
21
22
23 func slashClean(name string) string {
24 if name == "" || name[0] != '/' {
25 name = "/" + name
26 }
27 return path.Clean(name)
28 }
29
30
31
32
33
34
35
36
37
38
39
40 type FileSystem interface {
41 Mkdir(ctx context.Context, name string, perm os.FileMode) error
42 OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (File, error)
43 RemoveAll(ctx context.Context, name string) error
44 Rename(ctx context.Context, oldName, newName string) error
45 Stat(ctx context.Context, name string) (os.FileInfo, error)
46 }
47
48
49
50
51
52
53 type File interface {
54 http.File
55 io.Writer
56 }
57
58
59
60
61
62
63
64
65
66 type Dir string
67
68 func (d Dir) resolve(name string) string {
69
70 if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 ||
71 strings.Contains(name, "\x00") {
72 return ""
73 }
74 dir := string(d)
75 if dir == "" {
76 dir = "."
77 }
78 return filepath.Join(dir, filepath.FromSlash(slashClean(name)))
79 }
80
81 func (d Dir) Mkdir(ctx context.Context, name string, perm os.FileMode) error {
82 if name = d.resolve(name); name == "" {
83 return os.ErrNotExist
84 }
85 return os.Mkdir(name, perm)
86 }
87
88 func (d Dir) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (File, error) {
89 if name = d.resolve(name); name == "" {
90 return nil, os.ErrNotExist
91 }
92 f, err := os.OpenFile(name, flag, perm)
93 if err != nil {
94 return nil, err
95 }
96 return f, nil
97 }
98
99 func (d Dir) RemoveAll(ctx context.Context, name string) error {
100 if name = d.resolve(name); name == "" {
101 return os.ErrNotExist
102 }
103 if name == filepath.Clean(string(d)) {
104
105 return os.ErrInvalid
106 }
107 return os.RemoveAll(name)
108 }
109
110 func (d Dir) Rename(ctx context.Context, oldName, newName string) error {
111 if oldName = d.resolve(oldName); oldName == "" {
112 return os.ErrNotExist
113 }
114 if newName = d.resolve(newName); newName == "" {
115 return os.ErrNotExist
116 }
117 if root := filepath.Clean(string(d)); root == oldName || root == newName {
118
119 return os.ErrInvalid
120 }
121 return os.Rename(oldName, newName)
122 }
123
124 func (d Dir) Stat(ctx context.Context, name string) (os.FileInfo, error) {
125 if name = d.resolve(name); name == "" {
126 return nil, os.ErrNotExist
127 }
128 return os.Stat(name)
129 }
130
131
132 func NewMemFS() FileSystem {
133 return &memFS{
134 root: memFSNode{
135 children: make(map[string]*memFSNode),
136 mode: 0660 | os.ModeDir,
137 modTime: time.Now(),
138 },
139 }
140 }
141
142
143
144
145
146
147
148
149
150 type memFS struct {
151 mu sync.Mutex
152 root memFSNode
153 }
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169 func (fs *memFS) walk(op, fullname string, f func(dir *memFSNode, frag string, final bool) error) error {
170 original := fullname
171 fullname = slashClean(fullname)
172
173
174
175 if fullname[0] == '/' {
176 fullname = fullname[1:]
177 }
178 dir := &fs.root
179
180 for {
181 frag, remaining := fullname, ""
182 i := strings.IndexRune(fullname, '/')
183 final := i < 0
184 if !final {
185 frag, remaining = fullname[:i], fullname[i+1:]
186 }
187 if frag == "" && dir != &fs.root {
188 panic("webdav: empty path fragment for a clean path")
189 }
190 if err := f(dir, frag, final); err != nil {
191 return &os.PathError{
192 Op: op,
193 Path: original,
194 Err: err,
195 }
196 }
197 if final {
198 break
199 }
200 child := dir.children[frag]
201 if child == nil {
202 return &os.PathError{
203 Op: op,
204 Path: original,
205 Err: os.ErrNotExist,
206 }
207 }
208 if !child.mode.IsDir() {
209 return &os.PathError{
210 Op: op,
211 Path: original,
212 Err: os.ErrInvalid,
213 }
214 }
215 dir, fullname = child, remaining
216 }
217 return nil
218 }
219
220
221
222
223
224
225
226
227
228
229
230 func (fs *memFS) find(op, fullname string) (parent *memFSNode, frag string, err error) {
231 err = fs.walk(op, fullname, func(parent0 *memFSNode, frag0 string, final bool) error {
232 if !final {
233 return nil
234 }
235 if frag0 != "" {
236 parent, frag = parent0, frag0
237 }
238 return nil
239 })
240 return parent, frag, err
241 }
242
243 func (fs *memFS) Mkdir(ctx context.Context, name string, perm os.FileMode) error {
244 fs.mu.Lock()
245 defer fs.mu.Unlock()
246
247 dir, frag, err := fs.find("mkdir", name)
248 if err != nil {
249 return err
250 }
251 if dir == nil {
252
253 return os.ErrInvalid
254 }
255 if _, ok := dir.children[frag]; ok {
256 return os.ErrExist
257 }
258 dir.children[frag] = &memFSNode{
259 children: make(map[string]*memFSNode),
260 mode: perm.Perm() | os.ModeDir,
261 modTime: time.Now(),
262 }
263 return nil
264 }
265
266 func (fs *memFS) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (File, error) {
267 fs.mu.Lock()
268 defer fs.mu.Unlock()
269
270 dir, frag, err := fs.find("open", name)
271 if err != nil {
272 return nil, err
273 }
274 var n *memFSNode
275 if dir == nil {
276
277 if runtime.GOOS == "zos" {
278 if flag&os.O_WRONLY != 0 {
279 return nil, os.ErrPermission
280 }
281 } else {
282 if flag&(os.O_WRONLY|os.O_RDWR) != 0 {
283 return nil, os.ErrPermission
284 }
285 }
286 n, frag = &fs.root, "/"
287
288 } else {
289 n = dir.children[frag]
290 if flag&(os.O_SYNC|os.O_APPEND) != 0 {
291
292 return nil, os.ErrInvalid
293 }
294 if flag&os.O_CREATE != 0 {
295 if flag&os.O_EXCL != 0 && n != nil {
296 return nil, os.ErrExist
297 }
298 if n == nil {
299 n = &memFSNode{
300 mode: perm.Perm(),
301 }
302 dir.children[frag] = n
303 }
304 }
305 if n == nil {
306 return nil, os.ErrNotExist
307 }
308 if flag&(os.O_WRONLY|os.O_RDWR) != 0 && flag&os.O_TRUNC != 0 {
309 n.mu.Lock()
310 n.data = nil
311 n.mu.Unlock()
312 }
313 }
314
315 children := make([]os.FileInfo, 0, len(n.children))
316 for cName, c := range n.children {
317 children = append(children, c.stat(cName))
318 }
319 return &memFile{
320 n: n,
321 nameSnapshot: frag,
322 childrenSnapshot: children,
323 }, nil
324 }
325
326 func (fs *memFS) RemoveAll(ctx context.Context, name string) error {
327 fs.mu.Lock()
328 defer fs.mu.Unlock()
329
330 dir, frag, err := fs.find("remove", name)
331 if err != nil {
332 return err
333 }
334 if dir == nil {
335
336 return os.ErrInvalid
337 }
338 delete(dir.children, frag)
339 return nil
340 }
341
342 func (fs *memFS) Rename(ctx context.Context, oldName, newName string) error {
343 fs.mu.Lock()
344 defer fs.mu.Unlock()
345
346 oldName = slashClean(oldName)
347 newName = slashClean(newName)
348 if oldName == newName {
349 return nil
350 }
351 if strings.HasPrefix(newName, oldName+"/") {
352
353 return os.ErrInvalid
354 }
355
356 oDir, oFrag, err := fs.find("rename", oldName)
357 if err != nil {
358 return err
359 }
360 if oDir == nil {
361
362 return os.ErrInvalid
363 }
364
365 nDir, nFrag, err := fs.find("rename", newName)
366 if err != nil {
367 return err
368 }
369 if nDir == nil {
370
371 return os.ErrInvalid
372 }
373
374 oNode, ok := oDir.children[oFrag]
375 if !ok {
376 return os.ErrNotExist
377 }
378 if oNode.children != nil {
379 if nNode, ok := nDir.children[nFrag]; ok {
380 if nNode.children == nil {
381 return errNotADirectory
382 }
383 if len(nNode.children) != 0 {
384 return errDirectoryNotEmpty
385 }
386 }
387 }
388 delete(oDir.children, oFrag)
389 nDir.children[nFrag] = oNode
390 return nil
391 }
392
393 func (fs *memFS) Stat(ctx context.Context, name string) (os.FileInfo, error) {
394 fs.mu.Lock()
395 defer fs.mu.Unlock()
396
397 dir, frag, err := fs.find("stat", name)
398 if err != nil {
399 return nil, err
400 }
401 if dir == nil {
402
403 return fs.root.stat("/"), nil
404 }
405 if n, ok := dir.children[frag]; ok {
406 return n.stat(path.Base(name)), nil
407 }
408 return nil, os.ErrNotExist
409 }
410
411
412
413 type memFSNode struct {
414
415 children map[string]*memFSNode
416
417 mu sync.Mutex
418 data []byte
419 mode os.FileMode
420 modTime time.Time
421 deadProps map[xml.Name]Property
422 }
423
424 func (n *memFSNode) stat(name string) *memFileInfo {
425 n.mu.Lock()
426 defer n.mu.Unlock()
427 return &memFileInfo{
428 name: name,
429 size: int64(len(n.data)),
430 mode: n.mode,
431 modTime: n.modTime,
432 }
433 }
434
435 func (n *memFSNode) DeadProps() (map[xml.Name]Property, error) {
436 n.mu.Lock()
437 defer n.mu.Unlock()
438 if len(n.deadProps) == 0 {
439 return nil, nil
440 }
441 ret := make(map[xml.Name]Property, len(n.deadProps))
442 for k, v := range n.deadProps {
443 ret[k] = v
444 }
445 return ret, nil
446 }
447
448 func (n *memFSNode) Patch(patches []Proppatch) ([]Propstat, error) {
449 n.mu.Lock()
450 defer n.mu.Unlock()
451 pstat := Propstat{Status: http.StatusOK}
452 for _, patch := range patches {
453 for _, p := range patch.Props {
454 pstat.Props = append(pstat.Props, Property{XMLName: p.XMLName})
455 if patch.Remove {
456 delete(n.deadProps, p.XMLName)
457 continue
458 }
459 if n.deadProps == nil {
460 n.deadProps = map[xml.Name]Property{}
461 }
462 n.deadProps[p.XMLName] = p
463 }
464 }
465 return []Propstat{pstat}, nil
466 }
467
468 type memFileInfo struct {
469 name string
470 size int64
471 mode os.FileMode
472 modTime time.Time
473 }
474
475 func (f *memFileInfo) Name() string { return f.name }
476 func (f *memFileInfo) Size() int64 { return f.size }
477 func (f *memFileInfo) Mode() os.FileMode { return f.mode }
478 func (f *memFileInfo) ModTime() time.Time { return f.modTime }
479 func (f *memFileInfo) IsDir() bool { return f.mode.IsDir() }
480 func (f *memFileInfo) Sys() interface{} { return nil }
481
482
483
484
485 type memFile struct {
486 n *memFSNode
487 nameSnapshot string
488 childrenSnapshot []os.FileInfo
489
490 pos int
491 }
492
493
494 var _ DeadPropsHolder = (*memFile)(nil)
495
496 func (f *memFile) DeadProps() (map[xml.Name]Property, error) { return f.n.DeadProps() }
497 func (f *memFile) Patch(patches []Proppatch) ([]Propstat, error) { return f.n.Patch(patches) }
498
499 func (f *memFile) Close() error {
500 return nil
501 }
502
503 func (f *memFile) Read(p []byte) (int, error) {
504 f.n.mu.Lock()
505 defer f.n.mu.Unlock()
506 if f.n.mode.IsDir() {
507 return 0, os.ErrInvalid
508 }
509 if f.pos >= len(f.n.data) {
510 return 0, io.EOF
511 }
512 n := copy(p, f.n.data[f.pos:])
513 f.pos += n
514 return n, nil
515 }
516
517 func (f *memFile) Readdir(count int) ([]os.FileInfo, error) {
518 f.n.mu.Lock()
519 defer f.n.mu.Unlock()
520 if !f.n.mode.IsDir() {
521 return nil, os.ErrInvalid
522 }
523 old := f.pos
524 if old >= len(f.childrenSnapshot) {
525
526
527 if count > 0 {
528 return nil, io.EOF
529 }
530 return nil, nil
531 }
532 if count > 0 {
533 f.pos += count
534 if f.pos > len(f.childrenSnapshot) {
535 f.pos = len(f.childrenSnapshot)
536 }
537 } else {
538 f.pos = len(f.childrenSnapshot)
539 old = 0
540 }
541 return f.childrenSnapshot[old:f.pos], nil
542 }
543
544 func (f *memFile) Seek(offset int64, whence int) (int64, error) {
545 f.n.mu.Lock()
546 defer f.n.mu.Unlock()
547 npos := f.pos
548
549 switch whence {
550 case io.SeekStart:
551 npos = int(offset)
552 case io.SeekCurrent:
553 npos += int(offset)
554 case io.SeekEnd:
555 npos = len(f.n.data) + int(offset)
556 default:
557 npos = -1
558 }
559 if npos < 0 {
560 return 0, os.ErrInvalid
561 }
562 f.pos = npos
563 return int64(f.pos), nil
564 }
565
566 func (f *memFile) Stat() (os.FileInfo, error) {
567 return f.n.stat(f.nameSnapshot), nil
568 }
569
570 func (f *memFile) Write(p []byte) (int, error) {
571 lenp := len(p)
572 f.n.mu.Lock()
573 defer f.n.mu.Unlock()
574
575 if f.n.mode.IsDir() {
576 return 0, os.ErrInvalid
577 }
578 if f.pos < len(f.n.data) {
579 n := copy(f.n.data[f.pos:], p)
580 f.pos += n
581 p = p[n:]
582 } else if f.pos > len(f.n.data) {
583
584
585 if f.pos <= cap(f.n.data) {
586 oldLen := len(f.n.data)
587 f.n.data = f.n.data[:f.pos]
588 hole := f.n.data[oldLen:]
589 for i := range hole {
590 hole[i] = 0
591 }
592 } else {
593 d := make([]byte, f.pos, f.pos+len(p))
594 copy(d, f.n.data)
595 f.n.data = d
596 }
597 }
598
599 if len(p) > 0 {
600
601 f.n.data = append(f.n.data, p...)
602 f.pos = len(f.n.data)
603 }
604 f.n.modTime = time.Now()
605 return lenp, nil
606 }
607
608
609
610
611 func moveFiles(ctx context.Context, fs FileSystem, src, dst string, overwrite bool) (status int, err error) {
612 created := false
613 if _, err := fs.Stat(ctx, dst); err != nil {
614 if !os.IsNotExist(err) {
615 return http.StatusForbidden, err
616 }
617 created = true
618 } else if overwrite {
619
620
621
622
623 if err := fs.RemoveAll(ctx, dst); err != nil {
624 return http.StatusForbidden, err
625 }
626 } else {
627 return http.StatusPreconditionFailed, os.ErrExist
628 }
629 if err := fs.Rename(ctx, src, dst); err != nil {
630 return http.StatusForbidden, err
631 }
632 if created {
633 return http.StatusCreated, nil
634 }
635 return http.StatusNoContent, nil
636 }
637
638 func copyProps(dst, src File) error {
639 d, ok := dst.(DeadPropsHolder)
640 if !ok {
641 return nil
642 }
643 s, ok := src.(DeadPropsHolder)
644 if !ok {
645 return nil
646 }
647 m, err := s.DeadProps()
648 if err != nil {
649 return err
650 }
651 props := make([]Property, 0, len(m))
652 for _, prop := range m {
653 props = append(props, prop)
654 }
655 _, err = d.Patch([]Proppatch{{Props: props}})
656 return err
657 }
658
659
660
661
662 func copyFiles(ctx context.Context, fs FileSystem, src, dst string, overwrite bool, depth int, recursion int) (status int, err error) {
663 if recursion == 1000 {
664 return http.StatusInternalServerError, errRecursionTooDeep
665 }
666 recursion++
667
668
669
670
671 srcFile, err := fs.OpenFile(ctx, src, os.O_RDONLY, 0)
672 if err != nil {
673 if os.IsNotExist(err) {
674 return http.StatusNotFound, err
675 }
676 return http.StatusInternalServerError, err
677 }
678 defer srcFile.Close()
679 srcStat, err := srcFile.Stat()
680 if err != nil {
681 if os.IsNotExist(err) {
682 return http.StatusNotFound, err
683 }
684 return http.StatusInternalServerError, err
685 }
686 srcPerm := srcStat.Mode() & os.ModePerm
687
688 created := false
689 if _, err := fs.Stat(ctx, dst); err != nil {
690 if os.IsNotExist(err) {
691 created = true
692 } else {
693 return http.StatusForbidden, err
694 }
695 } else {
696 if !overwrite {
697 return http.StatusPreconditionFailed, os.ErrExist
698 }
699 if err := fs.RemoveAll(ctx, dst); err != nil && !os.IsNotExist(err) {
700 return http.StatusForbidden, err
701 }
702 }
703
704 if srcStat.IsDir() {
705 if err := fs.Mkdir(ctx, dst, srcPerm); err != nil {
706 return http.StatusForbidden, err
707 }
708 if depth == infiniteDepth {
709 children, err := srcFile.Readdir(-1)
710 if err != nil {
711 return http.StatusForbidden, err
712 }
713 for _, c := range children {
714 name := c.Name()
715 s := path.Join(src, name)
716 d := path.Join(dst, name)
717 cStatus, cErr := copyFiles(ctx, fs, s, d, overwrite, depth, recursion)
718 if cErr != nil {
719
720 return cStatus, cErr
721 }
722 }
723 }
724
725 } else {
726 dstFile, err := fs.OpenFile(ctx, dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, srcPerm)
727 if err != nil {
728 if os.IsNotExist(err) {
729 return http.StatusConflict, err
730 }
731 return http.StatusForbidden, err
732
733 }
734 _, copyErr := io.Copy(dstFile, srcFile)
735 propsErr := copyProps(dstFile, srcFile)
736 closeErr := dstFile.Close()
737 if copyErr != nil {
738 return http.StatusInternalServerError, copyErr
739 }
740 if propsErr != nil {
741 return http.StatusInternalServerError, propsErr
742 }
743 if closeErr != nil {
744 return http.StatusInternalServerError, closeErr
745 }
746 }
747
748 if created {
749 return http.StatusCreated, nil
750 }
751 return http.StatusNoContent, nil
752 }
753
754
755
756
757
758
759 func walkFS(ctx context.Context, fs FileSystem, depth int, name string, info os.FileInfo, walkFn filepath.WalkFunc) error {
760
761 err := walkFn(name, info, nil)
762 if err != nil {
763 if info.IsDir() && err == filepath.SkipDir {
764 return nil
765 }
766 return err
767 }
768 if !info.IsDir() || depth == 0 {
769 return nil
770 }
771 if depth == 1 {
772 depth = 0
773 }
774
775
776 f, err := fs.OpenFile(ctx, name, os.O_RDONLY, 0)
777 if err != nil {
778 return walkFn(name, info, err)
779 }
780 fileInfos, err := f.Readdir(0)
781 f.Close()
782 if err != nil {
783 return walkFn(name, info, err)
784 }
785
786 for _, fileInfo := range fileInfos {
787 filename := path.Join(name, fileInfo.Name())
788 fileInfo, err := fs.Stat(ctx, filename)
789 if err != nil {
790 if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir {
791 return err
792 }
793 } else {
794 err = walkFS(ctx, fs, depth, filename, fileInfo, walkFn)
795 if err != nil {
796 if !fileInfo.IsDir() || err != filepath.SkipDir {
797 return err
798 }
799 }
800 }
801 }
802 return nil
803 }
804
View as plain text