traversessymlink.go 1.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. // Copyright (C) 2016 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. package osutil
  7. import (
  8. "path/filepath"
  9. "github.com/syncthing/syncthing/lib/fs"
  10. )
  11. // TraversesSymlinkError is an error indicating symlink traversal
  12. type TraversesSymlinkError struct {
  13. path string
  14. }
  15. func (e *TraversesSymlinkError) Error() string {
  16. return "traverses symlink: " + e.path
  17. }
  18. // NotADirectoryError is an error indicating an expected path is not a directory
  19. type NotADirectoryError struct {
  20. path string
  21. }
  22. func (e *NotADirectoryError) Error() string {
  23. return "not a directory: " + e.path
  24. }
  25. // TraversesSymlink returns an error if any path component of name (including name
  26. // itself) traverses a symlink.
  27. func TraversesSymlink(filesystem fs.Filesystem, name string) error {
  28. var err error
  29. name, err = fs.Canonicalize(name)
  30. if err != nil {
  31. return err
  32. }
  33. if name == "." {
  34. // The result of calling TraversesSymlink(filesystem, filepath.Dir("foo"))
  35. return nil
  36. }
  37. var path string
  38. for _, part := range fs.PathComponents(name) {
  39. path = filepath.Join(path, part)
  40. info, err := filesystem.Lstat(path)
  41. if err != nil {
  42. if fs.IsNotExist(err) {
  43. return nil
  44. }
  45. return err
  46. }
  47. if info.IsSymlink() {
  48. return &TraversesSymlinkError{
  49. path: path,
  50. }
  51. }
  52. if !info.IsDir() {
  53. return &NotADirectoryError{
  54. path: path,
  55. }
  56. }
  57. }
  58. return nil
  59. }