|
@@ -2,124 +2,18 @@ package vessel
|
|
|
|
|
|
import (
|
|
|
"archive/zip"
|
|
|
- "io"
|
|
|
+ "fmt"
|
|
|
"net/http"
|
|
|
"os"
|
|
|
+ "path"
|
|
|
"path/filepath"
|
|
|
- "time"
|
|
|
+ "strings"
|
|
|
)
|
|
|
|
|
|
-// File implements an http.File
|
|
|
-type File struct {
|
|
|
- r io.ReadCloser
|
|
|
- zip *zip.File
|
|
|
- offset int64
|
|
|
-}
|
|
|
-
|
|
|
-// verify we implement the http.File interface
|
|
|
-var _ http.File = (*File)(nil)
|
|
|
-
|
|
|
-func newFile(z *zip.File) (*File, error) {
|
|
|
- r, err := z.Open()
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
- return &File{
|
|
|
- r: r,
|
|
|
- zip: z,
|
|
|
- }, nil
|
|
|
-}
|
|
|
-
|
|
|
-// Readdir implements http.File
|
|
|
-func (f *File) Readdir(count int) ([]os.FileInfo, error) {
|
|
|
- log.Log("read dir: %s\n", f.zip.Name)
|
|
|
- return nil, os.ErrPermission
|
|
|
-}
|
|
|
-
|
|
|
-// Stat implements http.File
|
|
|
-func (f *File) Stat() (os.FileInfo, error) {
|
|
|
- return f.zip.FileInfo(), nil
|
|
|
-}
|
|
|
-
|
|
|
-// Seek implements io.Seeker
|
|
|
-func (f *File) Seek(offset int64, whence int) (int64, error) {
|
|
|
- return f.offset, os.ErrInvalid
|
|
|
-}
|
|
|
-
|
|
|
-// Read implements io.Reader
|
|
|
-func (f *File) Read(p []byte) (n int, err error) {
|
|
|
- c, err := f.r.Read(p)
|
|
|
- if err != nil {
|
|
|
- f.offset += int64(c)
|
|
|
- }
|
|
|
- return c, err
|
|
|
-}
|
|
|
-
|
|
|
-// Close implements io.Closer
|
|
|
-func (f *File) Close() error {
|
|
|
- return f.r.Close()
|
|
|
-}
|
|
|
-
|
|
|
-// File implements an http.File
|
|
|
-type Dir struct {
|
|
|
- name string
|
|
|
- mode os.FileMode
|
|
|
- files []os.FileInfo
|
|
|
-}
|
|
|
-
|
|
|
-// verify we implement the http.File interface
|
|
|
-var _ http.File = (*Dir)(nil)
|
|
|
-var _ os.FileInfo = (*Dir)(nil)
|
|
|
-
|
|
|
-// Readdir implements http.File
|
|
|
-func (f *Dir) Readdir(count int) ([]os.FileInfo, error) {
|
|
|
- return f.files[:count], nil
|
|
|
-}
|
|
|
-
|
|
|
-// Stat implements http.File
|
|
|
-func (f *Dir) Stat() (os.FileInfo, error) {
|
|
|
- return f, nil
|
|
|
-}
|
|
|
-
|
|
|
-// Seek implements io.Seeker
|
|
|
-func (f *Dir) Seek(offset int64, whence int) (int64, error) {
|
|
|
- return 0, os.ErrInvalid
|
|
|
-}
|
|
|
-
|
|
|
-// Read implements io.Reader
|
|
|
-func (f *Dir) Read(p []byte) (n int, err error) {
|
|
|
- return 0, io.EOF
|
|
|
-}
|
|
|
-
|
|
|
-// Close implements io.Closer
|
|
|
-func (f *Dir) Close() error {
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
-// implement file info interface as well
|
|
|
-func (f *Dir) Name() string {
|
|
|
- return f.name
|
|
|
-}
|
|
|
-
|
|
|
-func (f *Dir) Size() int64 {
|
|
|
- return 0
|
|
|
-}
|
|
|
-func (f *Dir) Mode() os.FileMode {
|
|
|
- return f.mode
|
|
|
-}
|
|
|
-func (f *Dir) ModTime() time.Time {
|
|
|
- return time.Now()
|
|
|
-}
|
|
|
-func (f *Dir) IsDir() bool {
|
|
|
- return true
|
|
|
-}
|
|
|
-func (f *Dir) Sys() interface{} {
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
// FileSystem implements an http filesystem access to the bundle
|
|
|
type FileSystem struct {
|
|
|
- b *Bundle
|
|
|
+ b *Bundle
|
|
|
+ files map[string]os.FileInfo
|
|
|
}
|
|
|
|
|
|
// verify we implement the http.FileSystem interface
|
|
@@ -143,18 +37,18 @@ func (f *FileSystem) get(name string) (*zip.File, error) {
|
|
|
|
|
|
// Open opens a file in the filesystem
|
|
|
func (f *FileSystem) Open(name string) (http.File, error) {
|
|
|
- log.Log("vessel.FileSystem Open: %s\n", name)
|
|
|
- if filepath.Dir(name) == name {
|
|
|
- // directory fetch
|
|
|
- files, err := f.Dir(name)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
+ log.Log("vessel.FileSystem Open: %s|%s\n", name, filepath.Dir(name))
|
|
|
|
|
|
+ v, ok := f.files[name]
|
|
|
+ if !ok {
|
|
|
+ return nil, os.ErrNotExist
|
|
|
+ }
|
|
|
+
|
|
|
+ if v.IsDir() {
|
|
|
return &Dir{
|
|
|
- name: name,
|
|
|
- mode: os.ModeDir,
|
|
|
- files: files,
|
|
|
+ name: name,
|
|
|
+ mode: os.ModeDir,
|
|
|
+ fs: f,
|
|
|
}, nil
|
|
|
}
|
|
|
|
|
@@ -172,35 +66,78 @@ func (f *FileSystem) Open(name string) (http.File, error) {
|
|
|
|
|
|
// Stat returns file info for a file in the bundle filesystem
|
|
|
func (f *FileSystem) Stat(name string) (os.FileInfo, error) {
|
|
|
- z, err := f.get(name)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
+ v, ok := f.files[name]
|
|
|
+ if !ok {
|
|
|
+ return nil, os.ErrNotExist
|
|
|
}
|
|
|
- return z.FileInfo(), nil
|
|
|
+
|
|
|
+ return v, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (f *FileSystem) isDir(path string) bool {
|
|
|
+ if path == string(os.PathSeparator) {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ v, ok := f.files[path]
|
|
|
+ if !ok {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ return v.IsDir()
|
|
|
}
|
|
|
|
|
|
// Dir return a list of files contained in a subdirectory of our bundle
|
|
|
func (f *FileSystem) Dir(path string) ([]os.FileInfo, error) {
|
|
|
- zr, err := zip.NewReader(f.b, f.b.Size())
|
|
|
- if err != nil {
|
|
|
+ if f.isDir(path) == false {
|
|
|
return nil, os.ErrNotExist
|
|
|
}
|
|
|
|
|
|
- found := false
|
|
|
ls := make([]os.FileInfo, 0)
|
|
|
- for idx := range zr.File {
|
|
|
- z := zr.File[idx]
|
|
|
+ for k := range f.files {
|
|
|
+ if k == path {
|
|
|
+ continue
|
|
|
+ }
|
|
|
|
|
|
- d := filepath.Dir(z.Name)
|
|
|
- if d == path && !z.FileInfo().IsDir() {
|
|
|
- found = true
|
|
|
- ls = append(ls, z.FileInfo())
|
|
|
+ d := filepath.Dir(k)
|
|
|
+ if d == path {
|
|
|
+ fmt.Printf("+%s\n", k)
|
|
|
+ ls = append(ls, f.files[k])
|
|
|
}
|
|
|
}
|
|
|
+ return ls, nil
|
|
|
+}
|
|
|
|
|
|
- if !found {
|
|
|
- return nil, os.ErrNotExist
|
|
|
+func (f *FileSystem) initCaches() error {
|
|
|
+ zr, err := zip.NewReader(f.b, f.b.Size())
|
|
|
+ if err != nil {
|
|
|
+ return os.ErrNotExist
|
|
|
}
|
|
|
|
|
|
- return ls, nil
|
|
|
+ files := make(map[string]os.FileInfo)
|
|
|
+ for idx := range zr.File {
|
|
|
+ z := zr.File[idx]
|
|
|
+ files[z.Name] = z.FileInfo()
|
|
|
+ }
|
|
|
+
|
|
|
+ // zip files don't contain directories
|
|
|
+ for k, v := range files {
|
|
|
+ if v.IsDir() {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ dir := filepath.Dir(k)
|
|
|
+ chunks := strings.Split(dir, string(os.PathSeparator))
|
|
|
+ for i := 2; i <= len(chunks); i++ {
|
|
|
+ dir := fmt.Sprintf("/%s", path.Join(chunks[:i]...))
|
|
|
+ if _, ok := files[dir]; !ok {
|
|
|
+ files[dir] = &Dir{
|
|
|
+ name: dir,
|
|
|
+ mode: os.ModeDir,
|
|
|
+ fs: f,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ f.files = files
|
|
|
+ return nil
|
|
|
}
|