246 lines
4 KiB
Go
246 lines
4 KiB
Go
package fs
|
|
|
|
import (
|
|
"archive/zip"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
|
|
"github.com/bodgit/sevenzip"
|
|
"github.com/distribyted/distribyted/iio"
|
|
"github.com/nwaples/rardecode/v2"
|
|
)
|
|
|
|
var _ loader = &Zip{}
|
|
|
|
type Zip struct {
|
|
}
|
|
|
|
func (fs *Zip) getFiles(reader iio.Reader, size int64) (map[string]*ArchiveFile, error) {
|
|
zr, err := zip.NewReader(reader, size)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
out := make(map[string]*ArchiveFile)
|
|
for _, f := range zr.File {
|
|
f := f
|
|
if f.FileInfo().IsDir() {
|
|
continue
|
|
}
|
|
|
|
rf := func() (iio.Reader, error) {
|
|
zr, err := f.Open()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return iio.NewDiskTeeReader(zr)
|
|
}
|
|
|
|
n := filepath.Join(string(os.PathSeparator), f.Name)
|
|
af := NewArchiveFile(rf, f.FileInfo().Size())
|
|
|
|
out[n] = af
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
var _ loader = &SevenZip{}
|
|
|
|
type SevenZip struct {
|
|
}
|
|
|
|
func (fs *SevenZip) getFiles(reader iio.Reader, size int64) (map[string]*ArchiveFile, error) {
|
|
r, err := sevenzip.NewReader(reader, size)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
out := make(map[string]*ArchiveFile)
|
|
for _, f := range r.File {
|
|
f := f
|
|
if f.FileInfo().IsDir() {
|
|
continue
|
|
}
|
|
|
|
rf := func() (iio.Reader, error) {
|
|
zr, err := f.Open()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return iio.NewDiskTeeReader(zr)
|
|
}
|
|
|
|
af := NewArchiveFile(rf, f.FileInfo().Size())
|
|
n := filepath.Join(string(os.PathSeparator), f.Name)
|
|
|
|
out[n] = af
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
var _ loader = &Rar{}
|
|
|
|
type Rar struct {
|
|
}
|
|
|
|
func (fs *Rar) getFiles(reader iio.Reader, size int64) (map[string]*ArchiveFile, error) {
|
|
r, err := rardecode.NewReader(iio.NewSeekerWrapper(reader, size))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
out := make(map[string]*ArchiveFile)
|
|
for {
|
|
header, err := r.Next()
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
rf := func() (iio.Reader, error) {
|
|
return iio.NewDiskTeeReader(r)
|
|
}
|
|
|
|
n := filepath.Join(string(os.PathSeparator), header.Name)
|
|
|
|
af := NewArchiveFile(rf, header.UnPackedSize)
|
|
|
|
out[n] = af
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
type loader interface {
|
|
getFiles(r iio.Reader, size int64) (map[string]*ArchiveFile, error)
|
|
}
|
|
|
|
var _ Filesystem = &archive{}
|
|
|
|
type archive struct {
|
|
r iio.Reader
|
|
s *storage
|
|
|
|
size int64
|
|
once sync.Once
|
|
l loader
|
|
}
|
|
|
|
func NewArchive(r iio.Reader, size int64, l loader) *archive {
|
|
return &archive{
|
|
r: r,
|
|
s: newStorage(nil),
|
|
size: size,
|
|
l: l,
|
|
}
|
|
}
|
|
|
|
func (fs *archive) loadOnce() error {
|
|
var errOut error
|
|
fs.once.Do(func() {
|
|
files, err := fs.l.getFiles(fs.r, fs.size)
|
|
if err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
|
|
for name, file := range files {
|
|
if err := fs.s.Add(file, name); err != nil {
|
|
errOut = err
|
|
return
|
|
}
|
|
}
|
|
})
|
|
|
|
return errOut
|
|
}
|
|
|
|
func (fs *archive) Open(filename string) (File, error) {
|
|
if filename == string(os.PathSeparator) {
|
|
return &Dir{}, nil
|
|
}
|
|
|
|
if err := fs.loadOnce(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return fs.s.Get(filename)
|
|
}
|
|
|
|
func (fs *archive) ReadDir(path string) (map[string]File, error) {
|
|
if err := fs.loadOnce(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return fs.s.Children(path)
|
|
}
|
|
|
|
var _ File = &ArchiveFile{}
|
|
|
|
func NewArchiveFile(readerFunc func() (iio.Reader, error), len int64) *ArchiveFile {
|
|
return &ArchiveFile{
|
|
readerFunc: readerFunc,
|
|
len: len,
|
|
}
|
|
}
|
|
|
|
type ArchiveFile struct {
|
|
readerFunc func() (iio.Reader, error)
|
|
reader iio.Reader
|
|
len int64
|
|
}
|
|
|
|
func (d *ArchiveFile) load() error {
|
|
if d.reader != nil {
|
|
return nil
|
|
}
|
|
r, err := d.readerFunc()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
d.reader = r
|
|
|
|
return nil
|
|
}
|
|
|
|
func (d *ArchiveFile) Size() int64 {
|
|
return d.len
|
|
}
|
|
|
|
func (d *ArchiveFile) IsDir() bool {
|
|
return false
|
|
}
|
|
|
|
func (d *ArchiveFile) Close() (err error) {
|
|
if d.reader != nil {
|
|
err = d.reader.Close()
|
|
d.reader = nil
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (d *ArchiveFile) Read(p []byte) (n int, err error) {
|
|
if err := d.load(); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return d.reader.Read(p)
|
|
}
|
|
|
|
func (d *ArchiveFile) ReadAt(p []byte, off int64) (n int, err error) {
|
|
if err := d.load(); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
return d.reader.ReadAt(p, off)
|
|
}
|