123 lines
2.7 KiB
Go
123 lines
2.7 KiB
Go
package archive
|
|
|
|
import (
|
|
"context"
|
|
"io/fs"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.kmsign.ru/royalcat/tstor/src/vfs"
|
|
)
|
|
|
|
var ArchiveFactories = map[string]vfs.FsFactory{
|
|
".zip": func(ctx context.Context, sourcePath string, f vfs.File) (vfs.Filesystem, error) {
|
|
stat, err := f.Info()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return NewArchive(ctx, sourcePath, stat.Name(), f, stat.Size(), ZipLoader)
|
|
},
|
|
".rar": func(ctx context.Context, sourcePath string, f vfs.File) (vfs.Filesystem, error) {
|
|
stat, err := f.Info()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return NewArchive(ctx, sourcePath, stat.Name(), f, stat.Size(), RarLoader)
|
|
},
|
|
".7z": func(ctx context.Context, sourcePath string, f vfs.File) (vfs.Filesystem, error) {
|
|
stat, err := f.Info()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return NewArchive(ctx, sourcePath, stat.Name(), f, stat.Size(), SevenZipLoader)
|
|
},
|
|
}
|
|
|
|
type archiveLoader func(ctx context.Context, archivePath string, r vfs.File, size int64) (map[string]fileEntry, error)
|
|
|
|
var _ vfs.Filesystem = &ArchiveFS{}
|
|
|
|
type fileEntry struct {
|
|
fs.FileInfo
|
|
open func(ctx context.Context) (vfs.File, error)
|
|
}
|
|
|
|
type ArchiveFS struct {
|
|
name string
|
|
size int64
|
|
files map[string]fileEntry
|
|
}
|
|
|
|
// Rename implements Filesystem.
|
|
func (a *ArchiveFS) Rename(ctx context.Context, oldpath string, newpath string) error {
|
|
return vfs.ErrNotImplemented
|
|
}
|
|
|
|
// ModTime implements Filesystem.
|
|
func (a *ArchiveFS) ModTime() time.Time {
|
|
return time.Time{}
|
|
}
|
|
|
|
// Mode implements Filesystem.
|
|
func (a *ArchiveFS) Mode() fs.FileMode {
|
|
return fs.ModeDir
|
|
}
|
|
|
|
// Size implements Filesystem.
|
|
func (a *ArchiveFS) Size() int64 {
|
|
return int64(a.size)
|
|
}
|
|
|
|
// Sys implements Filesystem.
|
|
func (a *ArchiveFS) Sys() any {
|
|
return nil
|
|
}
|
|
|
|
// FsName implements Filesystem.
|
|
func (a *ArchiveFS) FsName() string {
|
|
return "archivefs"
|
|
}
|
|
|
|
func NewArchive(ctx context.Context, archivePath, name string, f vfs.File, size int64, loader archiveLoader) (*ArchiveFS, error) {
|
|
archiveFiles, err := loader(ctx, archivePath, f, size)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// TODO make optional
|
|
singleDir := true
|
|
for k := range archiveFiles {
|
|
if !strings.HasPrefix(k, "/"+name+"/") {
|
|
singleDir = false
|
|
break
|
|
}
|
|
}
|
|
|
|
files := make(map[string]fileEntry, len(archiveFiles))
|
|
for k, v := range archiveFiles {
|
|
// TODO make optional
|
|
if strings.Contains(k, "/__MACOSX/") {
|
|
continue
|
|
}
|
|
|
|
if singleDir {
|
|
k, _ = strings.CutPrefix(k, "/"+name)
|
|
}
|
|
|
|
files[k] = v
|
|
}
|
|
|
|
// FIXME configurable
|
|
files["/.forcegallery"] = fileEntry{
|
|
FileInfo: vfs.NewFileInfo("/.forcegallery", 0, time.Time{}),
|
|
open: func(ctx context.Context) (vfs.File, error) {
|
|
return vfs.NewMemoryFile(".forcegallery", []byte{}), nil
|
|
},
|
|
}
|
|
|
|
return &ArchiveFS{
|
|
name: name,
|
|
size: size,
|
|
files: files,
|
|
}, nil
|
|
}
|