iofs.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. //go:build go1.16
  2. // +build go1.16
  3. package afero
  4. import (
  5. "io"
  6. "io/fs"
  7. "os"
  8. "path"
  9. "sort"
  10. "time"
  11. "github.com/spf13/afero/internal/common"
  12. )
  13. // IOFS adopts afero.Fs to stdlib io/fs.FS
  14. type IOFS struct {
  15. Fs
  16. }
  17. func NewIOFS(fs Fs) IOFS {
  18. return IOFS{Fs: fs}
  19. }
  20. var (
  21. _ fs.FS = IOFS{}
  22. _ fs.GlobFS = IOFS{}
  23. _ fs.ReadDirFS = IOFS{}
  24. _ fs.ReadFileFS = IOFS{}
  25. _ fs.StatFS = IOFS{}
  26. _ fs.SubFS = IOFS{}
  27. )
  28. func (iofs IOFS) Open(name string) (fs.File, error) {
  29. const op = "open"
  30. // by convention for fs.FS implementations we should perform this check
  31. if !fs.ValidPath(name) {
  32. return nil, iofs.wrapError(op, name, fs.ErrInvalid)
  33. }
  34. file, err := iofs.Fs.Open(name)
  35. if err != nil {
  36. return nil, iofs.wrapError(op, name, err)
  37. }
  38. // file should implement fs.ReadDirFile
  39. if _, ok := file.(fs.ReadDirFile); !ok {
  40. file = readDirFile{file}
  41. }
  42. return file, nil
  43. }
  44. func (iofs IOFS) Glob(pattern string) ([]string, error) {
  45. const op = "glob"
  46. // afero.Glob does not perform this check but it's required for implementations
  47. if _, err := path.Match(pattern, ""); err != nil {
  48. return nil, iofs.wrapError(op, pattern, err)
  49. }
  50. items, err := Glob(iofs.Fs, pattern)
  51. if err != nil {
  52. return nil, iofs.wrapError(op, pattern, err)
  53. }
  54. return items, nil
  55. }
  56. func (iofs IOFS) ReadDir(name string) ([]fs.DirEntry, error) {
  57. f, err := iofs.Fs.Open(name)
  58. if err != nil {
  59. return nil, iofs.wrapError("readdir", name, err)
  60. }
  61. defer f.Close()
  62. if rdf, ok := f.(fs.ReadDirFile); ok {
  63. items, err := rdf.ReadDir(-1)
  64. if err != nil {
  65. return nil, iofs.wrapError("readdir", name, err)
  66. }
  67. sort.Slice(items, func(i, j int) bool { return items[i].Name() < items[j].Name() })
  68. return items, nil
  69. }
  70. items, err := f.Readdir(-1)
  71. if err != nil {
  72. return nil, iofs.wrapError("readdir", name, err)
  73. }
  74. sort.Sort(byName(items))
  75. ret := make([]fs.DirEntry, len(items))
  76. for i := range items {
  77. ret[i] = common.FileInfoDirEntry{FileInfo: items[i]}
  78. }
  79. return ret, nil
  80. }
  81. func (iofs IOFS) ReadFile(name string) ([]byte, error) {
  82. const op = "readfile"
  83. if !fs.ValidPath(name) {
  84. return nil, iofs.wrapError(op, name, fs.ErrInvalid)
  85. }
  86. bytes, err := ReadFile(iofs.Fs, name)
  87. if err != nil {
  88. return nil, iofs.wrapError(op, name, err)
  89. }
  90. return bytes, nil
  91. }
  92. func (iofs IOFS) Sub(dir string) (fs.FS, error) { return IOFS{NewBasePathFs(iofs.Fs, dir)}, nil }
  93. func (IOFS) wrapError(op, path string, err error) error {
  94. if _, ok := err.(*fs.PathError); ok {
  95. return err // don't need to wrap again
  96. }
  97. return &fs.PathError{
  98. Op: op,
  99. Path: path,
  100. Err: err,
  101. }
  102. }
  103. // readDirFile provides adapter from afero.File to fs.ReadDirFile needed for correct Open
  104. type readDirFile struct {
  105. File
  106. }
  107. var _ fs.ReadDirFile = readDirFile{}
  108. func (r readDirFile) ReadDir(n int) ([]fs.DirEntry, error) {
  109. items, err := r.File.Readdir(n)
  110. if err != nil {
  111. return nil, err
  112. }
  113. ret := make([]fs.DirEntry, len(items))
  114. for i := range items {
  115. ret[i] = common.FileInfoDirEntry{FileInfo: items[i]}
  116. }
  117. return ret, nil
  118. }
  119. // FromIOFS adopts io/fs.FS to use it as afero.Fs
  120. // Note that io/fs.FS is read-only so all mutating methods will return fs.PathError with fs.ErrPermission
  121. // To store modifications you may use afero.CopyOnWriteFs
  122. type FromIOFS struct {
  123. fs.FS
  124. }
  125. var _ Fs = FromIOFS{}
  126. func (f FromIOFS) Create(name string) (File, error) { return nil, notImplemented("create", name) }
  127. func (f FromIOFS) Mkdir(name string, perm os.FileMode) error { return notImplemented("mkdir", name) }
  128. func (f FromIOFS) MkdirAll(path string, perm os.FileMode) error {
  129. return notImplemented("mkdirall", path)
  130. }
  131. func (f FromIOFS) Open(name string) (File, error) {
  132. file, err := f.FS.Open(name)
  133. if err != nil {
  134. return nil, err
  135. }
  136. return fromIOFSFile{File: file, name: name}, nil
  137. }
  138. func (f FromIOFS) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
  139. return f.Open(name)
  140. }
  141. func (f FromIOFS) Remove(name string) error {
  142. return notImplemented("remove", name)
  143. }
  144. func (f FromIOFS) RemoveAll(path string) error {
  145. return notImplemented("removeall", path)
  146. }
  147. func (f FromIOFS) Rename(oldname, newname string) error {
  148. return notImplemented("rename", oldname)
  149. }
  150. func (f FromIOFS) Stat(name string) (os.FileInfo, error) { return fs.Stat(f.FS, name) }
  151. func (f FromIOFS) Name() string { return "fromiofs" }
  152. func (f FromIOFS) Chmod(name string, mode os.FileMode) error {
  153. return notImplemented("chmod", name)
  154. }
  155. func (f FromIOFS) Chown(name string, uid, gid int) error {
  156. return notImplemented("chown", name)
  157. }
  158. func (f FromIOFS) Chtimes(name string, atime time.Time, mtime time.Time) error {
  159. return notImplemented("chtimes", name)
  160. }
  161. type fromIOFSFile struct {
  162. fs.File
  163. name string
  164. }
  165. func (f fromIOFSFile) ReadAt(p []byte, off int64) (n int, err error) {
  166. readerAt, ok := f.File.(io.ReaderAt)
  167. if !ok {
  168. return -1, notImplemented("readat", f.name)
  169. }
  170. return readerAt.ReadAt(p, off)
  171. }
  172. func (f fromIOFSFile) Seek(offset int64, whence int) (int64, error) {
  173. seeker, ok := f.File.(io.Seeker)
  174. if !ok {
  175. return -1, notImplemented("seek", f.name)
  176. }
  177. return seeker.Seek(offset, whence)
  178. }
  179. func (f fromIOFSFile) Write(p []byte) (n int, err error) {
  180. return -1, notImplemented("write", f.name)
  181. }
  182. func (f fromIOFSFile) WriteAt(p []byte, off int64) (n int, err error) {
  183. return -1, notImplemented("writeat", f.name)
  184. }
  185. func (f fromIOFSFile) Name() string { return f.name }
  186. func (f fromIOFSFile) Readdir(count int) ([]os.FileInfo, error) {
  187. rdfile, ok := f.File.(fs.ReadDirFile)
  188. if !ok {
  189. return nil, notImplemented("readdir", f.name)
  190. }
  191. entries, err := rdfile.ReadDir(count)
  192. if err != nil {
  193. return nil, err
  194. }
  195. ret := make([]os.FileInfo, len(entries))
  196. for i := range entries {
  197. ret[i], err = entries[i].Info()
  198. if err != nil {
  199. return nil, err
  200. }
  201. }
  202. return ret, nil
  203. }
  204. func (f fromIOFSFile) Readdirnames(n int) ([]string, error) {
  205. rdfile, ok := f.File.(fs.ReadDirFile)
  206. if !ok {
  207. return nil, notImplemented("readdir", f.name)
  208. }
  209. entries, err := rdfile.ReadDir(n)
  210. if err != nil {
  211. return nil, err
  212. }
  213. ret := make([]string, len(entries))
  214. for i := range entries {
  215. ret[i] = entries[i].Name()
  216. }
  217. return ret, nil
  218. }
  219. func (f fromIOFSFile) Sync() error { return nil }
  220. func (f fromIOFSFile) Truncate(size int64) error {
  221. return notImplemented("truncate", f.name)
  222. }
  223. func (f fromIOFSFile) WriteString(s string) (ret int, err error) {
  224. return -1, notImplemented("writestring", f.name)
  225. }
  226. func notImplemented(op, path string) error {
  227. return &fs.PathError{Op: op, Path: path, Err: fs.ErrPermission}
  228. }