tstor/src/host/vfs/archive.go

262 lines
4.7 KiB
Go
Raw Normal View History

2023-10-16 09:18:40 +00:00
package vfs
2021-11-29 10:07:54 +00:00
import (
"archive/zip"
"io"
2023-12-21 23:15:39 +00:00
"io/fs"
2021-11-29 10:07:54 +00:00
"os"
2023-12-21 23:15:39 +00:00
"path"
2021-11-29 10:07:54 +00:00
"path/filepath"
2023-12-21 23:15:39 +00:00
"strings"
2021-11-29 10:07:54 +00:00
"sync"
2023-10-08 16:46:03 +00:00
"git.kmsign.ru/royalcat/tstor/src/iio"
2021-11-29 10:07:54 +00:00
"github.com/bodgit/sevenzip"
"github.com/nwaples/rardecode/v2"
)
2023-10-16 09:18:40 +00:00
var ArchiveFactories = map[string]FsFactory{
".zip": func(f File) (Filesystem, error) {
return NewArchive(f, f.Size(), ZipLoader), nil
},
".rar": func(f File) (Filesystem, error) {
return NewArchive(f, f.Size(), RarLoader), nil
},
".7z": func(f File) (Filesystem, error) {
return NewArchive(f, f.Size(), SevenZipLoader), nil
},
}
type ArchiveLoader func(r iio.Reader, size int64) (map[string]*archiveFile, error)
var _ Filesystem = &archive{}
type archive struct {
r iio.Reader
size int64
files func() (map[string]*archiveFile, error)
}
func NewArchive(r iio.Reader, size int64, loader ArchiveLoader) *archive {
return &archive{
r: r,
size: size,
files: sync.OnceValues(func() (map[string]*archiveFile, error) {
return loader(r, size)
}),
}
}
2023-12-25 22:11:03 +00:00
// Unlink implements Filesystem.
func (a *archive) Unlink(filename string) error {
return ErrNotImplemented
}
2023-12-21 23:15:39 +00:00
2023-10-16 09:18:40 +00:00
func (a *archive) Open(filename string) (File, error) {
files, err := a.files()
if err != nil {
return nil, err
}
return getFile(files, filename)
}
2023-12-21 23:15:39 +00:00
func (fs *archive) ReadDir(path string) ([]fs.DirEntry, error) {
2023-10-16 09:18:40 +00:00
files, err := fs.files()
if err != nil {
return nil, err
}
2023-12-21 23:15:39 +00:00
return listDirFromFiles(files, path)
}
// Stat implements Filesystem.
func (afs *archive) Stat(filename string) (fs.FileInfo, error) {
files, err := afs.files()
if err != nil {
return nil, err
}
if file, ok := files[filename]; ok {
return newFileInfo(path.Base(filename), file.Size()), nil
}
for p, _ := range files {
if strings.HasPrefix(p, filename) {
return newDirInfo(path.Base(filename)), nil
}
}
return nil, ErrNotExist
2023-10-16 09:18:40 +00:00
}
var _ File = &archiveFile{}
2023-12-21 23:15:39 +00:00
func NewArchiveFile(name string, readerFunc func() (iio.Reader, error), size int64) *archiveFile {
2023-10-16 09:18:40 +00:00
return &archiveFile{
2023-12-21 23:15:39 +00:00
name: name,
2023-10-16 09:18:40 +00:00
readerFunc: readerFunc,
2023-12-21 23:15:39 +00:00
size: size,
2023-10-16 09:18:40 +00:00
}
}
2021-11-29 10:07:54 +00:00
2023-10-16 09:18:40 +00:00
type archiveFile struct {
2023-12-21 23:15:39 +00:00
name string
2023-10-16 09:18:40 +00:00
readerFunc func() (iio.Reader, error)
reader iio.Reader
2023-12-21 23:15:39 +00:00
size int64
}
func (d *archiveFile) Stat() (fs.FileInfo, error) {
return newFileInfo(d.name, d.size), nil
2021-11-29 10:07:54 +00:00
}
2023-10-16 09:18:40 +00:00
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 {
2023-12-21 23:15:39 +00:00
return d.size
2023-10-16 09:18:40 +00:00
}
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)
}
var _ ArchiveLoader = ZipLoader
func ZipLoader(reader iio.Reader, size int64) (map[string]*archiveFile, error) {
2021-11-29 10:07:54 +00:00
zr, err := zip.NewReader(reader, size)
if err != nil {
return nil, err
}
2023-10-16 09:18:40 +00:00
out := make(map[string]*archiveFile)
2021-11-29 10:07:54 +00:00
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)
2023-12-21 23:15:39 +00:00
af := NewArchiveFile(f.Name, rf, f.FileInfo().Size())
2021-11-29 10:07:54 +00:00
out[n] = af
}
return out, nil
}
2023-10-16 09:18:40 +00:00
var _ ArchiveLoader = SevenZipLoader
2021-11-29 10:07:54 +00:00
2023-10-16 09:18:40 +00:00
func SevenZipLoader(reader iio.Reader, size int64) (map[string]*archiveFile, error) {
2021-11-29 10:07:54 +00:00
r, err := sevenzip.NewReader(reader, size)
if err != nil {
return nil, err
}
2023-10-16 09:18:40 +00:00
out := make(map[string]*archiveFile)
2021-11-29 10:07:54 +00:00
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)
}
2023-12-21 23:15:39 +00:00
af := NewArchiveFile(f.Name, rf, f.FileInfo().Size())
2021-11-29 10:07:54 +00:00
n := filepath.Join(string(os.PathSeparator), f.Name)
out[n] = af
}
return out, nil
}
2023-10-16 09:18:40 +00:00
var _ ArchiveLoader = RarLoader
2021-11-29 10:07:54 +00:00
2023-10-16 09:18:40 +00:00
func RarLoader(reader iio.Reader, size int64) (map[string]*archiveFile, error) {
2021-11-29 10:07:54 +00:00
r, err := rardecode.NewReader(iio.NewSeekerWrapper(reader, size))
if err != nil {
return nil, err
}
2023-10-16 09:18:40 +00:00
out := make(map[string]*archiveFile)
2021-11-29 10:07:54 +00:00
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)
2023-12-21 23:15:39 +00:00
af := NewArchiveFile(header.Name, rf, header.UnPackedSize)
2021-11-29 10:07:54 +00:00
out[n] = af
}
return out, nil
}