tstor/daemons/archive/archive.go
royalcat 832f5b9710
Some checks failed
docker / build-docker (push) Has been cancelled
wip
2025-01-20 05:18:15 +03:00

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
}