overlay.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. package vessel
  2. import (
  3. "io/fs"
  4. "net/http"
  5. "os"
  6. )
  7. // OverlayFS allows us to stack multiple overlapping http.FileSystems and search for a file in them in order
  8. type OverlayFS struct {
  9. search []http.FileSystem
  10. }
  11. // verify we implement the http.FileSystem interface
  12. var _ http.FileSystem = (*OverlayFS)(nil)
  13. // NewOverlayFS returns a new overlay FileSystem
  14. func NewOverlayFS() *OverlayFS {
  15. return &OverlayFS{}
  16. }
  17. // Append appends a filesystem to our overlay group.
  18. // Filesystems are searched in the order they are appended
  19. func (f *OverlayFS) Append(fs http.FileSystem) {
  20. f.search = append(f.search, fs)
  21. }
  22. // Open implements http.FileSystem
  23. func (f *OverlayFS) Open(name string) (http.File, error) {
  24. dir := &OverlayDir{}
  25. for _, source := range f.search {
  26. file, err := source.Open(name)
  27. if err == nil {
  28. s, err := file.Stat()
  29. if err != nil {
  30. continue
  31. }
  32. if !s.IsDir() {
  33. return file, nil
  34. }
  35. dir.files = append(dir.files, file)
  36. }
  37. }
  38. if len(dir.files) != 0 {
  39. return dir, nil
  40. }
  41. return nil, os.ErrNotExist
  42. }
  43. type OverlayDir struct {
  44. files []http.File
  45. }
  46. // Readdir implements http.FileSystem Readdir
  47. func (f *OverlayDir) Readdir(count int) ([]fs.FileInfo, error) {
  48. // for read dir we need all of the content
  49. content := make(map[string]fs.FileInfo)
  50. for _, dir := range f.files {
  51. list, err := dir.Readdir(count)
  52. if err != nil {
  53. continue
  54. }
  55. for idx, x := range list {
  56. if _, ok := content[x.Name()]; ok {
  57. continue
  58. }
  59. content[x.Name()] = list[idx]
  60. }
  61. }
  62. files := make([]fs.FileInfo, 0, count)
  63. for k := range content {
  64. files = append(files, content[k])
  65. }
  66. if len(files) > count {
  67. return files[0:count], nil
  68. }
  69. return files, nil
  70. }
  71. // Stat implements http.FileSystem Stat
  72. func (f *OverlayDir) Stat() (fs.FileInfo, error) {
  73. return f.files[0].Stat()
  74. }
  75. // Seek implements http.FileSystem Seek
  76. func (f *OverlayDir) Seek(offset int64, whence int) (int64, error) {
  77. return f.files[0].Seek(offset, whence)
  78. }
  79. // Read implements http.FileSystem Read
  80. func (f *OverlayDir) Read(p []byte) (n int, err error) {
  81. return f.files[0].Read(p)
  82. }
  83. // Close implements http.FileSystem Close
  84. func (f *OverlayDir) Close() error {
  85. return f.files[0].Close()
  86. }