basepath.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. package afero
  2. import (
  3. "errors"
  4. "os"
  5. "path/filepath"
  6. "runtime"
  7. "strings"
  8. "time"
  9. )
  10. // The BasePathFs restricts all operations to a given path within an Fs.
  11. // The given file name to the operations on this Fs will be prepended with
  12. // the base path before calling the base Fs.
  13. // Any file name (after filepath.Clean()) outside this base path will be
  14. // treated as non existing file.
  15. //
  16. // Note that it does not clean the error messages on return, so you may
  17. // reveal the real path on errors.
  18. type BasePathFs struct {
  19. source Fs
  20. path string
  21. }
  22. func NewBasePathFs(source Fs, path string) Fs {
  23. return &BasePathFs{source: source, path: path}
  24. }
  25. // on a file outside the base path it returns the given file name and an error,
  26. // else the given file with the base path prepended
  27. func (b *BasePathFs) RealPath(name string) (path string, err error) {
  28. if err := validateBasePathName(name); err != nil {
  29. return "", err
  30. }
  31. bpath := filepath.Clean(b.path)
  32. path = filepath.Clean(filepath.Join(bpath, name))
  33. if !strings.HasPrefix(path, bpath) {
  34. return name, os.ErrNotExist
  35. }
  36. return path, nil
  37. }
  38. func validateBasePathName(name string) error {
  39. if runtime.GOOS != "windows" {
  40. // Not much to do here;
  41. // the virtual file paths all look absolute on *nix.
  42. return nil
  43. }
  44. // On Windows a common mistake would be to provide an absolute OS path
  45. // We could strip out the base part, but that would not be very portable.
  46. if filepath.IsAbs(name) {
  47. return &os.PathError{"realPath", name, errors.New("got a real OS path instead of a virtual")}
  48. }
  49. return nil
  50. }
  51. func (b *BasePathFs) Chtimes(name string, atime, mtime time.Time) (err error) {
  52. if name, err = b.RealPath(name); err != nil {
  53. return &os.PathError{"chtimes", name, err}
  54. }
  55. return b.source.Chtimes(name, atime, mtime)
  56. }
  57. func (b *BasePathFs) Chmod(name string, mode os.FileMode) (err error) {
  58. if name, err = b.RealPath(name); err != nil {
  59. return &os.PathError{"chmod", name, err}
  60. }
  61. return b.source.Chmod(name, mode)
  62. }
  63. func (b *BasePathFs) Name() string {
  64. return "BasePathFs"
  65. }
  66. func (b *BasePathFs) Stat(name string) (fi os.FileInfo, err error) {
  67. if name, err = b.RealPath(name); err != nil {
  68. return nil, &os.PathError{"stat", name, err}
  69. }
  70. return b.source.Stat(name)
  71. }
  72. func (b *BasePathFs) Rename(oldname, newname string) (err error) {
  73. if oldname, err = b.RealPath(oldname); err != nil {
  74. return &os.PathError{"rename", oldname, err}
  75. }
  76. if newname, err = b.RealPath(newname); err != nil {
  77. return &os.PathError{"rename", newname, err}
  78. }
  79. return b.source.Rename(oldname, newname)
  80. }
  81. func (b *BasePathFs) RemoveAll(name string) (err error) {
  82. if name, err = b.RealPath(name); err != nil {
  83. return &os.PathError{"remove_all", name, err}
  84. }
  85. return b.source.RemoveAll(name)
  86. }
  87. func (b *BasePathFs) Remove(name string) (err error) {
  88. if name, err = b.RealPath(name); err != nil {
  89. return &os.PathError{"remove", name, err}
  90. }
  91. return b.source.Remove(name)
  92. }
  93. func (b *BasePathFs) OpenFile(name string, flag int, mode os.FileMode) (f File, err error) {
  94. if name, err = b.RealPath(name); err != nil {
  95. return nil, &os.PathError{"openfile", name, err}
  96. }
  97. return b.source.OpenFile(name, flag, mode)
  98. }
  99. func (b *BasePathFs) Open(name string) (f File, err error) {
  100. if name, err = b.RealPath(name); err != nil {
  101. return nil, &os.PathError{"open", name, err}
  102. }
  103. return b.source.Open(name)
  104. }
  105. func (b *BasePathFs) Mkdir(name string, mode os.FileMode) (err error) {
  106. if name, err = b.RealPath(name); err != nil {
  107. return &os.PathError{"mkdir", name, err}
  108. }
  109. return b.source.Mkdir(name, mode)
  110. }
  111. func (b *BasePathFs) MkdirAll(name string, mode os.FileMode) (err error) {
  112. if name, err = b.RealPath(name); err != nil {
  113. return &os.PathError{"mkdir", name, err}
  114. }
  115. return b.source.MkdirAll(name, mode)
  116. }
  117. func (b *BasePathFs) Create(name string) (f File, err error) {
  118. if name, err = b.RealPath(name); err != nil {
  119. return nil, &os.PathError{"create", name, err}
  120. }
  121. return b.source.Create(name)
  122. }
  123. // vim: ts=4 sw=4 noexpandtab nolist syn=go