github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/net/webdav/file.go (about) 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 5 package webdav 6 7 import ( 8 "io" 9 "net/http" 10 "os" 11 "path" 12 "path/filepath" 13 "strings" 14 "sync" 15 "time" 16 17 "golang.org/x/net/webdav/internal/xml" 18 ) 19 20 // slashClean is equivalent to but slightly more efficient than 21 // path.Clean("/" + name). 22 func slashClean(name string) string { 23 if name == "" || name[0] != '/' { 24 name = "/" + name 25 } 26 return path.Clean(name) 27 } 28 29 // A FileSystem implements access to a collection of named files. The elements 30 // in a file path are separated by slash ('/', U+002F) characters, regardless 31 // of host operating system convention. 32 // 33 // Each method has the same semantics as the os package's function of the same 34 // name. 35 // 36 // Note that the os.Rename documentation says that "OS-specific restrictions 37 // might apply". In particular, whether or not renaming a file or directory 38 // overwriting another existing file or directory is an error is OS-dependent. 39 type FileSystem interface { 40 Mkdir(name string, perm os.FileMode) error 41 OpenFile(name string, flag int, perm os.FileMode) (File, error) 42 RemoveAll(name string) error 43 Rename(oldName, newName string) error 44 Stat(name string) (os.FileInfo, error) 45 } 46 47 // A File is returned by a FileSystem's OpenFile method and can be served by a 48 // Handler. 49 // 50 // A File may optionally implement the DeadPropsHolder interface, if it can 51 // load and save dead properties. 52 type File interface { 53 http.File 54 io.Writer 55 } 56 57 // A Dir implements FileSystem using the native file system restricted to a 58 // specific directory tree. 59 // 60 // While the FileSystem.OpenFile method takes '/'-separated paths, a Dir's 61 // string value is a filename on the native file system, not a URL, so it is 62 // separated by filepath.Separator, which isn't necessarily '/'. 63 // 64 // An empty Dir is treated as ".". 65 type Dir string 66 67 func (d Dir) resolve(name string) string { 68 // This implementation is based on Dir.Open's code in the standard net/http package. 69 if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 || 70 strings.Contains(name, "\x00") { 71 return "" 72 } 73 dir := string(d) 74 if dir == "" { 75 dir = "." 76 } 77 return filepath.Join(dir, filepath.FromSlash(slashClean(name))) 78 } 79 80 func (d Dir) Mkdir(name string, perm os.FileMode) error { 81 if name = d.resolve(name); name == "" { 82 return os.ErrNotExist 83 } 84 return os.Mkdir(name, perm) 85 } 86 87 func (d Dir) OpenFile(name string, flag int, perm os.FileMode) (File, error) { 88 if name = d.resolve(name); name == "" { 89 return nil, os.ErrNotExist 90 } 91 f, err := os.OpenFile(name, flag, perm) 92 if err != nil { 93 return nil, err 94 } 95 return f, nil 96 } 97 98 func (d Dir) RemoveAll(name string) error { 99 if name = d.resolve(name); name == "" { 100 return os.ErrNotExist 101 } 102 if name == filepath.Clean(string(d)) { 103 // Prohibit removing the virtual root directory. 104 return os.ErrInvalid 105 } 106 return os.RemoveAll(name) 107 } 108 109 func (d Dir) Rename(oldName, newName string) error { 110 if oldName = d.resolve(oldName); oldName == "" { 111 return os.ErrNotExist 112 } 113 if newName = d.resolve(newName); newName == "" { 114 return os.ErrNotExist 115 } 116 if root := filepath.Clean(string(d)); root == oldName || root == newName { 117 // Prohibit renaming from or to the virtual root directory. 118 return os.ErrInvalid 119 } 120 return os.Rename(oldName, newName) 121 } 122 123 func (d Dir) Stat(name string) (os.FileInfo, error) { 124 if name = d.resolve(name); name == "" { 125 return nil, os.ErrNotExist 126 } 127 return os.Stat(name) 128 } 129 130 // NewMemFS returns a new in-memory FileSystem implementation. 131 func NewMemFS() FileSystem { 132 return &memFS{ 133 root: memFSNode{ 134 children: make(map[string]*memFSNode), 135 mode: 0660 | os.ModeDir, 136 modTime: time.Now(), 137 }, 138 } 139 } 140 141 // A memFS implements FileSystem, storing all metadata and actual file data 142 // in-memory. No limits on filesystem size are used, so it is not recommended 143 // this be used where the clients are untrusted. 144 // 145 // Concurrent access is permitted. The tree structure is protected by a mutex, 146 // and each node's contents and metadata are protected by a per-node mutex. 147 // 148 // TODO: Enforce file permissions. 149 type memFS struct { 150 mu sync.Mutex 151 root memFSNode 152 } 153 154 // TODO: clean up and rationalize the walk/find code. 155 156 // walk walks the directory tree for the fullname, calling f at each step. If f 157 // returns an error, the walk will be aborted and return that same error. 158 // 159 // dir is the directory at that step, frag is the name fragment, and final is 160 // whether it is the final step. For example, walking "/foo/bar/x" will result 161 // in 3 calls to f: 162 // - "/", "foo", false 163 // - "/foo/", "bar", false 164 // - "/foo/bar/", "x", true 165 // The frag argument will be empty only if dir is the root node and the walk 166 // ends at that root node. 167 func (fs *memFS) walk(op, fullname string, f func(dir *memFSNode, frag string, final bool) error) error { 168 original := fullname 169 fullname = slashClean(fullname) 170 171 // Strip any leading "/"s to make fullname a relative path, as the walk 172 // starts at fs.root. 173 if fullname[0] == '/' { 174 fullname = fullname[1:] 175 } 176 dir := &fs.root 177 178 for { 179 frag, remaining := fullname, "" 180 i := strings.IndexRune(fullname, '/') 181 final := i < 0 182 if !final { 183 frag, remaining = fullname[:i], fullname[i+1:] 184 } 185 if frag == "" && dir != &fs.root { 186 panic("webdav: empty path fragment for a clean path") 187 } 188 if err := f(dir, frag, final); err != nil { 189 return &os.PathError{ 190 Op: op, 191 Path: original, 192 Err: err, 193 } 194 } 195 if final { 196 break 197 } 198 child := dir.children[frag] 199 if child == nil { 200 return &os.PathError{ 201 Op: op, 202 Path: original, 203 Err: os.ErrNotExist, 204 } 205 } 206 if !child.mode.IsDir() { 207 return &os.PathError{ 208 Op: op, 209 Path: original, 210 Err: os.ErrInvalid, 211 } 212 } 213 dir, fullname = child, remaining 214 } 215 return nil 216 } 217 218 // find returns the parent of the named node and the relative name fragment 219 // from the parent to the child. For example, if finding "/foo/bar/baz" then 220 // parent will be the node for "/foo/bar" and frag will be "baz". 221 // 222 // If the fullname names the root node, then parent, frag and err will be zero. 223 // 224 // find returns an error if the parent does not already exist or the parent 225 // isn't a directory, but it will not return an error per se if the child does 226 // not already exist. The error returned is either nil or an *os.PathError 227 // whose Op is op. 228 func (fs *memFS) find(op, fullname string) (parent *memFSNode, frag string, err error) { 229 err = fs.walk(op, fullname, func(parent0 *memFSNode, frag0 string, final bool) error { 230 if !final { 231 return nil 232 } 233 if frag0 != "" { 234 parent, frag = parent0, frag0 235 } 236 return nil 237 }) 238 return parent, frag, err 239 } 240 241 func (fs *memFS) Mkdir(name string, perm os.FileMode) error { 242 fs.mu.Lock() 243 defer fs.mu.Unlock() 244 245 dir, frag, err := fs.find("mkdir", name) 246 if err != nil { 247 return err 248 } 249 if dir == nil { 250 // We can't create the root. 251 return os.ErrInvalid 252 } 253 if _, ok := dir.children[frag]; ok { 254 return os.ErrExist 255 } 256 dir.children[frag] = &memFSNode{ 257 children: make(map[string]*memFSNode), 258 mode: perm.Perm() | os.ModeDir, 259 modTime: time.Now(), 260 } 261 return nil 262 } 263 264 func (fs *memFS) OpenFile(name string, flag int, perm os.FileMode) (File, error) { 265 fs.mu.Lock() 266 defer fs.mu.Unlock() 267 268 dir, frag, err := fs.find("open", name) 269 if err != nil { 270 return nil, err 271 } 272 var n *memFSNode 273 if dir == nil { 274 // We're opening the root. 275 if flag&(os.O_WRONLY|os.O_RDWR) != 0 { 276 return nil, os.ErrPermission 277 } 278 n, frag = &fs.root, "/" 279 280 } else { 281 n = dir.children[frag] 282 if flag&(os.O_SYNC|os.O_APPEND) != 0 { 283 // memFile doesn't support these flags yet. 284 return nil, os.ErrInvalid 285 } 286 if flag&os.O_CREATE != 0 { 287 if flag&os.O_EXCL != 0 && n != nil { 288 return nil, os.ErrExist 289 } 290 if n == nil { 291 n = &memFSNode{ 292 mode: perm.Perm(), 293 } 294 dir.children[frag] = n 295 } 296 } 297 if n == nil { 298 return nil, os.ErrNotExist 299 } 300 if flag&(os.O_WRONLY|os.O_RDWR) != 0 && flag&os.O_TRUNC != 0 { 301 n.mu.Lock() 302 n.data = nil 303 n.mu.Unlock() 304 } 305 } 306 307 children := make([]os.FileInfo, 0, len(n.children)) 308 for cName, c := range n.children { 309 children = append(children, c.stat(cName)) 310 } 311 return &memFile{ 312 n: n, 313 nameSnapshot: frag, 314 childrenSnapshot: children, 315 }, nil 316 } 317 318 func (fs *memFS) RemoveAll(name string) error { 319 fs.mu.Lock() 320 defer fs.mu.Unlock() 321 322 dir, frag, err := fs.find("remove", name) 323 if err != nil { 324 return err 325 } 326 if dir == nil { 327 // We can't remove the root. 328 return os.ErrInvalid 329 } 330 delete(dir.children, frag) 331 return nil 332 } 333 334 func (fs *memFS) Rename(oldName, newName string) error { 335 fs.mu.Lock() 336 defer fs.mu.Unlock() 337 338 oldName = slashClean(oldName) 339 newName = slashClean(newName) 340 if oldName == newName { 341 return nil 342 } 343 if strings.HasPrefix(newName, oldName+"/") { 344 // We can't rename oldName to be a sub-directory of itself. 345 return os.ErrInvalid 346 } 347 348 oDir, oFrag, err := fs.find("rename", oldName) 349 if err != nil { 350 return err 351 } 352 if oDir == nil { 353 // We can't rename from the root. 354 return os.ErrInvalid 355 } 356 357 nDir, nFrag, err := fs.find("rename", newName) 358 if err != nil { 359 return err 360 } 361 if nDir == nil { 362 // We can't rename to the root. 363 return os.ErrInvalid 364 } 365 366 oNode, ok := oDir.children[oFrag] 367 if !ok { 368 return os.ErrNotExist 369 } 370 if oNode.children != nil { 371 if nNode, ok := nDir.children[nFrag]; ok { 372 if nNode.children == nil { 373 return errNotADirectory 374 } 375 if len(nNode.children) != 0 { 376 return errDirectoryNotEmpty 377 } 378 } 379 } 380 delete(oDir.children, oFrag) 381 nDir.children[nFrag] = oNode 382 return nil 383 } 384 385 func (fs *memFS) Stat(name string) (os.FileInfo, error) { 386 fs.mu.Lock() 387 defer fs.mu.Unlock() 388 389 dir, frag, err := fs.find("stat", name) 390 if err != nil { 391 return nil, err 392 } 393 if dir == nil { 394 // We're stat'ting the root. 395 return fs.root.stat("/"), nil 396 } 397 if n, ok := dir.children[frag]; ok { 398 return n.stat(path.Base(name)), nil 399 } 400 return nil, os.ErrNotExist 401 } 402 403 // A memFSNode represents a single entry in the in-memory filesystem and also 404 // implements os.FileInfo. 405 type memFSNode struct { 406 // children is protected by memFS.mu. 407 children map[string]*memFSNode 408 409 mu sync.Mutex 410 data []byte 411 mode os.FileMode 412 modTime time.Time 413 deadProps map[xml.Name]Property 414 } 415 416 func (n *memFSNode) stat(name string) *memFileInfo { 417 n.mu.Lock() 418 defer n.mu.Unlock() 419 return &memFileInfo{ 420 name: name, 421 size: int64(len(n.data)), 422 mode: n.mode, 423 modTime: n.modTime, 424 } 425 } 426 427 func (n *memFSNode) DeadProps() (map[xml.Name]Property, error) { 428 n.mu.Lock() 429 defer n.mu.Unlock() 430 if len(n.deadProps) == 0 { 431 return nil, nil 432 } 433 ret := make(map[xml.Name]Property, len(n.deadProps)) 434 for k, v := range n.deadProps { 435 ret[k] = v 436 } 437 return ret, nil 438 } 439 440 func (n *memFSNode) Patch(patches []Proppatch) ([]Propstat, error) { 441 n.mu.Lock() 442 defer n.mu.Unlock() 443 pstat := Propstat{Status: http.StatusOK} 444 for _, patch := range patches { 445 for _, p := range patch.Props { 446 pstat.Props = append(pstat.Props, Property{XMLName: p.XMLName}) 447 if patch.Remove { 448 delete(n.deadProps, p.XMLName) 449 continue 450 } 451 if n.deadProps == nil { 452 n.deadProps = map[xml.Name]Property{} 453 } 454 n.deadProps[p.XMLName] = p 455 } 456 } 457 return []Propstat{pstat}, nil 458 } 459 460 type memFileInfo struct { 461 name string 462 size int64 463 mode os.FileMode 464 modTime time.Time 465 } 466 467 func (f *memFileInfo) Name() string { return f.name } 468 func (f *memFileInfo) Size() int64 { return f.size } 469 func (f *memFileInfo) Mode() os.FileMode { return f.mode } 470 func (f *memFileInfo) ModTime() time.Time { return f.modTime } 471 func (f *memFileInfo) IsDir() bool { return f.mode.IsDir() } 472 func (f *memFileInfo) Sys() interface{} { return nil } 473 474 // A memFile is a File implementation for a memFSNode. It is a per-file (not 475 // per-node) read/write position, and a snapshot of the memFS' tree structure 476 // (a node's name and children) for that node. 477 type memFile struct { 478 n *memFSNode 479 nameSnapshot string 480 childrenSnapshot []os.FileInfo 481 // pos is protected by n.mu. 482 pos int 483 } 484 485 // A *memFile implements the optional DeadPropsHolder interface. 486 var _ DeadPropsHolder = (*memFile)(nil) 487 488 func (f *memFile) DeadProps() (map[xml.Name]Property, error) { return f.n.DeadProps() } 489 func (f *memFile) Patch(patches []Proppatch) ([]Propstat, error) { return f.n.Patch(patches) } 490 491 func (f *memFile) Close() error { 492 return nil 493 } 494 495 func (f *memFile) Read(p []byte) (int, error) { 496 f.n.mu.Lock() 497 defer f.n.mu.Unlock() 498 if f.n.mode.IsDir() { 499 return 0, os.ErrInvalid 500 } 501 if f.pos >= len(f.n.data) { 502 return 0, io.EOF 503 } 504 n := copy(p, f.n.data[f.pos:]) 505 f.pos += n 506 return n, nil 507 } 508 509 func (f *memFile) Readdir(count int) ([]os.FileInfo, error) { 510 f.n.mu.Lock() 511 defer f.n.mu.Unlock() 512 if !f.n.mode.IsDir() { 513 return nil, os.ErrInvalid 514 } 515 old := f.pos 516 if old >= len(f.childrenSnapshot) { 517 // The os.File Readdir docs say that at the end of a directory, 518 // the error is io.EOF if count > 0 and nil if count <= 0. 519 if count > 0 { 520 return nil, io.EOF 521 } 522 return nil, nil 523 } 524 if count > 0 { 525 f.pos += count 526 if f.pos > len(f.childrenSnapshot) { 527 f.pos = len(f.childrenSnapshot) 528 } 529 } else { 530 f.pos = len(f.childrenSnapshot) 531 old = 0 532 } 533 return f.childrenSnapshot[old:f.pos], nil 534 } 535 536 func (f *memFile) Seek(offset int64, whence int) (int64, error) { 537 f.n.mu.Lock() 538 defer f.n.mu.Unlock() 539 npos := f.pos 540 // TODO: How to handle offsets greater than the size of system int? 541 switch whence { 542 case os.SEEK_SET: 543 npos = int(offset) 544 case os.SEEK_CUR: 545 npos += int(offset) 546 case os.SEEK_END: 547 npos = len(f.n.data) + int(offset) 548 default: 549 npos = -1 550 } 551 if npos < 0 { 552 return 0, os.ErrInvalid 553 } 554 f.pos = npos 555 return int64(f.pos), nil 556 } 557 558 func (f *memFile) Stat() (os.FileInfo, error) { 559 return f.n.stat(f.nameSnapshot), nil 560 } 561 562 func (f *memFile) Write(p []byte) (int, error) { 563 lenp := len(p) 564 f.n.mu.Lock() 565 defer f.n.mu.Unlock() 566 567 if f.n.mode.IsDir() { 568 return 0, os.ErrInvalid 569 } 570 if f.pos < len(f.n.data) { 571 n := copy(f.n.data[f.pos:], p) 572 f.pos += n 573 p = p[n:] 574 } else if f.pos > len(f.n.data) { 575 // Write permits the creation of holes, if we've seek'ed past the 576 // existing end of file. 577 if f.pos <= cap(f.n.data) { 578 oldLen := len(f.n.data) 579 f.n.data = f.n.data[:f.pos] 580 hole := f.n.data[oldLen:] 581 for i := range hole { 582 hole[i] = 0 583 } 584 } else { 585 d := make([]byte, f.pos, f.pos+len(p)) 586 copy(d, f.n.data) 587 f.n.data = d 588 } 589 } 590 591 if len(p) > 0 { 592 // We should only get here if f.pos == len(f.n.data). 593 f.n.data = append(f.n.data, p...) 594 f.pos = len(f.n.data) 595 } 596 f.n.modTime = time.Now() 597 return lenp, nil 598 } 599 600 // moveFiles moves files and/or directories from src to dst. 601 // 602 // See section 9.9.4 for when various HTTP status codes apply. 603 func moveFiles(fs FileSystem, src, dst string, overwrite bool) (status int, err error) { 604 created := false 605 if _, err := fs.Stat(dst); err != nil { 606 if !os.IsNotExist(err) { 607 return http.StatusForbidden, err 608 } 609 created = true 610 } else if overwrite { 611 // Section 9.9.3 says that "If a resource exists at the destination 612 // and the Overwrite header is "T", then prior to performing the move, 613 // the server must perform a DELETE with "Depth: infinity" on the 614 // destination resource. 615 if err := fs.RemoveAll(dst); err != nil { 616 return http.StatusForbidden, err 617 } 618 } else { 619 return http.StatusPreconditionFailed, os.ErrExist 620 } 621 if err := fs.Rename(src, dst); err != nil { 622 return http.StatusForbidden, err 623 } 624 if created { 625 return http.StatusCreated, nil 626 } 627 return http.StatusNoContent, nil 628 } 629 630 func copyProps(dst, src File) error { 631 d, ok := dst.(DeadPropsHolder) 632 if !ok { 633 return nil 634 } 635 s, ok := src.(DeadPropsHolder) 636 if !ok { 637 return nil 638 } 639 m, err := s.DeadProps() 640 if err != nil { 641 return err 642 } 643 props := make([]Property, 0, len(m)) 644 for _, prop := range m { 645 props = append(props, prop) 646 } 647 _, err = d.Patch([]Proppatch{{Props: props}}) 648 return err 649 } 650 651 // copyFiles copies files and/or directories from src to dst. 652 // 653 // See section 9.8.5 for when various HTTP status codes apply. 654 func copyFiles(fs FileSystem, src, dst string, overwrite bool, depth int, recursion int) (status int, err error) { 655 if recursion == 1000 { 656 return http.StatusInternalServerError, errRecursionTooDeep 657 } 658 recursion++ 659 660 // TODO: section 9.8.3 says that "Note that an infinite-depth COPY of /A/ 661 // into /A/B/ could lead to infinite recursion if not handled correctly." 662 663 srcFile, err := fs.OpenFile(src, os.O_RDONLY, 0) 664 if err != nil { 665 if os.IsNotExist(err) { 666 return http.StatusNotFound, err 667 } 668 return http.StatusInternalServerError, err 669 } 670 defer srcFile.Close() 671 srcStat, err := srcFile.Stat() 672 if err != nil { 673 if os.IsNotExist(err) { 674 return http.StatusNotFound, err 675 } 676 return http.StatusInternalServerError, err 677 } 678 srcPerm := srcStat.Mode() & os.ModePerm 679 680 created := false 681 if _, err := fs.Stat(dst); err != nil { 682 if os.IsNotExist(err) { 683 created = true 684 } else { 685 return http.StatusForbidden, err 686 } 687 } else { 688 if !overwrite { 689 return http.StatusPreconditionFailed, os.ErrExist 690 } 691 if err := fs.RemoveAll(dst); err != nil && !os.IsNotExist(err) { 692 return http.StatusForbidden, err 693 } 694 } 695 696 if srcStat.IsDir() { 697 if err := fs.Mkdir(dst, srcPerm); err != nil { 698 return http.StatusForbidden, err 699 } 700 if depth == infiniteDepth { 701 children, err := srcFile.Readdir(-1) 702 if err != nil { 703 return http.StatusForbidden, err 704 } 705 for _, c := range children { 706 name := c.Name() 707 s := path.Join(src, name) 708 d := path.Join(dst, name) 709 cStatus, cErr := copyFiles(fs, s, d, overwrite, depth, recursion) 710 if cErr != nil { 711 // TODO: MultiStatus. 712 return cStatus, cErr 713 } 714 } 715 } 716 717 } else { 718 dstFile, err := fs.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, srcPerm) 719 if err != nil { 720 if os.IsNotExist(err) { 721 return http.StatusConflict, err 722 } 723 return http.StatusForbidden, err 724 725 } 726 _, copyErr := io.Copy(dstFile, srcFile) 727 propsErr := copyProps(dstFile, srcFile) 728 closeErr := dstFile.Close() 729 if copyErr != nil { 730 return http.StatusInternalServerError, copyErr 731 } 732 if propsErr != nil { 733 return http.StatusInternalServerError, propsErr 734 } 735 if closeErr != nil { 736 return http.StatusInternalServerError, closeErr 737 } 738 } 739 740 if created { 741 return http.StatusCreated, nil 742 } 743 return http.StatusNoContent, nil 744 } 745 746 // walkFS traverses filesystem fs starting at name up to depth levels. 747 // 748 // Allowed values for depth are 0, 1 or infiniteDepth. For each visited node, 749 // walkFS calls walkFn. If a visited file system node is a directory and 750 // walkFn returns filepath.SkipDir, walkFS will skip traversal of this node. 751 func walkFS(fs FileSystem, depth int, name string, info os.FileInfo, walkFn filepath.WalkFunc) error { 752 // This implementation is based on Walk's code in the standard path/filepath package. 753 err := walkFn(name, info, nil) 754 if err != nil { 755 if info.IsDir() && err == filepath.SkipDir { 756 return nil 757 } 758 return err 759 } 760 if !info.IsDir() || depth == 0 { 761 return nil 762 } 763 if depth == 1 { 764 depth = 0 765 } 766 767 // Read directory names. 768 f, err := fs.OpenFile(name, os.O_RDONLY, 0) 769 if err != nil { 770 return walkFn(name, info, err) 771 } 772 fileInfos, err := f.Readdir(0) 773 f.Close() 774 if err != nil { 775 return walkFn(name, info, err) 776 } 777 778 for _, fileInfo := range fileInfos { 779 filename := path.Join(name, fileInfo.Name()) 780 fileInfo, err := fs.Stat(filename) 781 if err != nil { 782 if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir { 783 return err 784 } 785 } else { 786 err = walkFS(fs, depth, filename, fileInfo, walkFn) 787 if err != nil { 788 if !fileInfo.IsDir() || err != filepath.SkipDir { 789 return err 790 } 791 } 792 } 793 } 794 return nil 795 }