tstor/fs/archive.go
2021-11-29 11:07:54 +01:00

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)
}