This commit is contained in:
Dmytro Manchynskyi 2022-05-27 16:39:53 +03:00
parent 100c9a6d7b
commit 0ec4cd15a0
4 changed files with 111 additions and 4 deletions

View File

@ -13,6 +13,7 @@ import (
type File struct { type File struct {
fs *Fs fs *Fs
zipfile *zip.File zipfile *zip.File
pseudodir *pseudoDir
reader io.ReadCloser reader io.ReadCloser
offset int64 offset int64
isdir, closed bool isdir, closed bool
@ -106,6 +107,9 @@ func (f *File) WriteAt(p []byte, off int64) (n int, err error) { return 0, sysca
func (f *File) Name() string { func (f *File) Name() string {
if f.zipfile == nil { if f.zipfile == nil {
if f.pseudodir != nil {
return f.pseudodir.path
}
return string(filepath.Separator) return string(filepath.Separator)
} }
return filepath.Join(splitpath(f.zipfile.Name)) return filepath.Join(splitpath(f.zipfile.Name))
@ -128,8 +132,12 @@ func (f *File) Readdir(count int) (fi []os.FileInfo, err error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, zipfile := range zipfiles { for d, zipfile := range zipfiles {
fi = append(fi, zipfile.FileInfo()) if zipfile == nil {
fi = append(fi, pseudoDir{d})
} else {
fi = append(fi, zipfile.FileInfo())
}
if count > 0 && len(fi) >= count { if count > 0 && len(fi) >= count {
break break
} }
@ -153,6 +161,9 @@ func (f *File) Readdirnames(count int) (names []string, err error) {
func (f *File) Stat() (os.FileInfo, error) { func (f *File) Stat() (os.FileInfo, error) {
if f.zipfile == nil { if f.zipfile == nil {
if f.pseudodir != nil {
return f.pseudodir, nil
}
return &pseudoRoot{}, nil return &pseudoRoot{}, nil
} }
return f.zipfile.FileInfo(), nil return f.zipfile.FileInfo(), nil

View File

@ -42,6 +42,21 @@ func New(r *zip.Reader) afero.Fs {
fs.files[dirname] = make(map[string]*zip.File) fs.files[dirname] = make(map[string]*zip.File)
} }
} }
dv := d
for {
d, f = splitpath(dv)
if _, ok := fs.files[d]; !ok {
fs.files[d] = make(map[string]*zip.File)
}
if f == "" {
break
}
if _, ok := fs.files[d][f]; !ok {
fs.files[d][f] = nil
}
dv = d
}
} }
return fs return fs
} }
@ -64,6 +79,13 @@ func (fs *Fs) Open(name string) (afero.File, error) {
if !ok { if !ok {
return nil, &os.PathError{Op: "stat", Path: name, Err: syscall.ENOENT} return nil, &os.PathError{Op: "stat", Path: name, Err: syscall.ENOENT}
} }
if file == nil {
return &File{
fs: fs,
pseudodir: &pseudoDir{path: filepath.Join(d, f)},
isdir: true,
}, nil
}
return &File{fs: fs, zipfile: file, isdir: file.FileInfo().IsDir()}, nil return &File{fs: fs, zipfile: file, isdir: file.FileInfo().IsDir()}, nil
} }
@ -89,6 +111,17 @@ func (p *pseudoRoot) ModTime() time.Time { return time.Now() }
func (p *pseudoRoot) IsDir() bool { return true } func (p *pseudoRoot) IsDir() bool { return true }
func (p *pseudoRoot) Sys() interface{} { return nil } func (p *pseudoRoot) Sys() interface{} { return nil }
type pseudoDir struct {
path string
}
func (fi pseudoDir) Name() string { return filepath.Base(fi.path) }
func (fi pseudoDir) Size() int64 { return 0 }
func (fi pseudoDir) IsDir() bool { return true }
func (fi pseudoDir) ModTime() time.Time { return time.Now() }
func (fi pseudoDir) Mode() os.FileMode { return os.ModeDir | os.ModePerm }
func (fi pseudoDir) Sys() interface{} { return nil }
func (fs *Fs) Stat(name string) (os.FileInfo, error) { func (fs *Fs) Stat(name string) (os.FileInfo, error) {
d, f := splitpath(name) d, f := splitpath(name)
if f == "" { if f == "" {
@ -101,6 +134,9 @@ func (fs *Fs) Stat(name string) (os.FileInfo, error) {
if !ok { if !ok {
return nil, &os.PathError{Op: "stat", Path: name, Err: syscall.ENOENT} return nil, &os.PathError{Op: "stat", Path: name, Err: syscall.ENOENT}
} }
if file == nil {
return pseudoDir{name}, nil
}
return file.FileInfo(), nil return file.FileInfo(), nil
} }

BIN
zipfs/testdata/no_dir_entry.zip vendored Normal file

Binary file not shown.

View File

@ -1,12 +1,13 @@
package zipfs package zipfs
import ( import (
"github.com/spf13/afero"
"archive/zip" "archive/zip"
"io/fs"
"path/filepath" "path/filepath"
"reflect" "reflect"
"testing" "testing"
"github.com/spf13/afero"
) )
func TestZipFS(t *testing.T) { func TestZipFS(t *testing.T) {
@ -101,3 +102,62 @@ func TestZipFS(t *testing.T) {
} }
} }
} }
func TestZipFsNoDirEntry(t *testing.T) {
zrc, err := zip.OpenReader("testdata/no_dir_entry.zip")
if err != nil {
t.Fatal(err)
}
zfs := New(&zrc.Reader)
// Test Walk
expected := map[string]bool{
"": true,
"sub": true,
"sub/testDir2": true,
"sub/testDir2/testFile": false,
"testDir1": true,
"testDir1/testFile": false,
}
err = afero.Walk(zfs, "", func(path string, info fs.FileInfo, err error) error {
path = filepath.ToSlash(path)
if isDir, ok := expected[path]; ok {
if isDir != info.IsDir() {
t.Error("file", path, "isDir:", info.IsDir(), "but expected:", isDir)
}
delete(expected, path)
} else {
t.Error("Unexpected file", path, "isDir:", info.IsDir())
}
return nil
})
if err != nil {
t.Error(err)
}
if len(expected) > 0 {
t.Errorf("Files %v is not found in zip", expected)
}
// Test ReadDir
dir, err := afero.ReadDir(zfs, "/")
if err != nil {
t.Fatal(err)
}
expected = map[string]bool{
"sub": true,
"testDir1": true,
}
for _, d := range dir {
if isDir, ok := expected[d.Name()]; ok {
if isDir != d.IsDir() {
t.Error("file", d.Name(), "isDir:", d.IsDir(), "but expected:", isDir)
}
delete(expected, d.Name())
} else {
t.Error("Unexpected file", d.Name(), "isDir:", d.IsDir())
}
}
if len(expected) > 0 {
t.Errorf("Files %v is not found in zip", expected)
}
}