本站源代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

181 lines
4.9KB

  1. package afero
  2. import (
  3. "os"
  4. "path/filepath"
  5. "runtime"
  6. "strings"
  7. "time"
  8. )
  9. var _ Lstater = (*BasePathFs)(nil)
  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. type BasePathFile struct {
  23. File
  24. path string
  25. }
  26. func (f *BasePathFile) Name() string {
  27. sourcename := f.File.Name()
  28. return strings.TrimPrefix(sourcename, filepath.Clean(f.path))
  29. }
  30. func NewBasePathFs(source Fs, path string) Fs {
  31. return &BasePathFs{source: source, path: path}
  32. }
  33. // on a file outside the base path it returns the given file name and an error,
  34. // else the given file with the base path prepended
  35. func (b *BasePathFs) RealPath(name string) (path string, err error) {
  36. if err := validateBasePathName(name); err != nil {
  37. return name, err
  38. }
  39. bpath := filepath.Clean(b.path)
  40. path = filepath.Clean(filepath.Join(bpath, name))
  41. if !strings.HasPrefix(path, bpath) {
  42. return name, os.ErrNotExist
  43. }
  44. return path, nil
  45. }
  46. func validateBasePathName(name string) error {
  47. if runtime.GOOS != "windows" {
  48. // Not much to do here;
  49. // the virtual file paths all look absolute on *nix.
  50. return nil
  51. }
  52. // On Windows a common mistake would be to provide an absolute OS path
  53. // We could strip out the base part, but that would not be very portable.
  54. if filepath.IsAbs(name) {
  55. return os.ErrNotExist
  56. }
  57. return nil
  58. }
  59. func (b *BasePathFs) Chtimes(name string, atime, mtime time.Time) (err error) {
  60. if name, err = b.RealPath(name); err != nil {
  61. return &os.PathError{Op: "chtimes", Path: name, Err: err}
  62. }
  63. return b.source.Chtimes(name, atime, mtime)
  64. }
  65. func (b *BasePathFs) Chmod(name string, mode os.FileMode) (err error) {
  66. if name, err = b.RealPath(name); err != nil {
  67. return &os.PathError{Op: "chmod", Path: name, Err: err}
  68. }
  69. return b.source.Chmod(name, mode)
  70. }
  71. func (b *BasePathFs) Name() string {
  72. return "BasePathFs"
  73. }
  74. func (b *BasePathFs) Stat(name string) (fi os.FileInfo, err error) {
  75. if name, err = b.RealPath(name); err != nil {
  76. return nil, &os.PathError{Op: "stat", Path: name, Err: err}
  77. }
  78. return b.source.Stat(name)
  79. }
  80. func (b *BasePathFs) Rename(oldname, newname string) (err error) {
  81. if oldname, err = b.RealPath(oldname); err != nil {
  82. return &os.PathError{Op: "rename", Path: oldname, Err: err}
  83. }
  84. if newname, err = b.RealPath(newname); err != nil {
  85. return &os.PathError{Op: "rename", Path: newname, Err: err}
  86. }
  87. return b.source.Rename(oldname, newname)
  88. }
  89. func (b *BasePathFs) RemoveAll(name string) (err error) {
  90. if name, err = b.RealPath(name); err != nil {
  91. return &os.PathError{Op: "remove_all", Path: name, Err: err}
  92. }
  93. return b.source.RemoveAll(name)
  94. }
  95. func (b *BasePathFs) Remove(name string) (err error) {
  96. if name, err = b.RealPath(name); err != nil {
  97. return &os.PathError{Op: "remove", Path: name, Err: err}
  98. }
  99. return b.source.Remove(name)
  100. }
  101. func (b *BasePathFs) OpenFile(name string, flag int, mode os.FileMode) (f File, err error) {
  102. if name, err = b.RealPath(name); err != nil {
  103. return nil, &os.PathError{Op: "openfile", Path: name, Err: err}
  104. }
  105. sourcef, err := b.source.OpenFile(name, flag, mode)
  106. if err != nil {
  107. return nil, err
  108. }
  109. return &BasePathFile{sourcef, b.path}, nil
  110. }
  111. func (b *BasePathFs) Open(name string) (f File, err error) {
  112. if name, err = b.RealPath(name); err != nil {
  113. return nil, &os.PathError{Op: "open", Path: name, Err: err}
  114. }
  115. sourcef, err := b.source.Open(name)
  116. if err != nil {
  117. return nil, err
  118. }
  119. return &BasePathFile{File: sourcef, path: b.path}, nil
  120. }
  121. func (b *BasePathFs) Mkdir(name string, mode os.FileMode) (err error) {
  122. if name, err = b.RealPath(name); err != nil {
  123. return &os.PathError{Op: "mkdir", Path: name, Err: err}
  124. }
  125. return b.source.Mkdir(name, mode)
  126. }
  127. func (b *BasePathFs) MkdirAll(name string, mode os.FileMode) (err error) {
  128. if name, err = b.RealPath(name); err != nil {
  129. return &os.PathError{Op: "mkdir", Path: name, Err: err}
  130. }
  131. return b.source.MkdirAll(name, mode)
  132. }
  133. func (b *BasePathFs) Create(name string) (f File, err error) {
  134. if name, err = b.RealPath(name); err != nil {
  135. return nil, &os.PathError{Op: "create", Path: name, Err: err}
  136. }
  137. sourcef, err := b.source.Create(name)
  138. if err != nil {
  139. return nil, err
  140. }
  141. return &BasePathFile{File: sourcef, path: b.path}, nil
  142. }
  143. func (b *BasePathFs) LstatIfPossible(name string) (os.FileInfo, bool, error) {
  144. name, err := b.RealPath(name)
  145. if err != nil {
  146. return nil, false, &os.PathError{Op: "lstat", Path: name, Err: err}
  147. }
  148. if lstater, ok := b.source.(Lstater); ok {
  149. return lstater.LstatIfPossible(name)
  150. }
  151. fi, err := b.source.Stat(name)
  152. return fi, false, err
  153. }
  154. // vim: ts=4 sw=4 noexpandtab nolist syn=go
上海开阖软件有限公司 沪ICP备12045867号-1