package archive import ( "context" "io" "io/fs" "path" "strings" "sync" "time" "git.kmsign.ru/royalcat/tstor/src/vfs" ) // Unlink implements Filesystem. func (a *ArchiveFS) Unlink(ctx context.Context, filename string) error { return vfs.ErrNotImplemented } func (a *ArchiveFS) Open(ctx context.Context, filename string) (vfs.File, error) { if filename == vfs.Separator { return vfs.NewDirFile(filename), nil } f, ok := a.files[filename] if ok { return f.open(ctx) } for p := range a.files { if strings.HasPrefix(p, filename) { return vfs.NewDirFile(filename), nil } } return nil, vfs.ErrNotExist } func (a *ArchiveFS) ReadDir(ctx context.Context, path string) ([]fs.DirEntry, error) { infos := make(map[string]fs.FileInfo, len(a.files)) for k, v := range a.files { infos[k] = v } return vfs.ListDirFromInfo(infos, path) } // Stat implements Filesystem. func (afs *ArchiveFS) Stat(ctx context.Context, filename string) (fs.FileInfo, error) { if entry, ok := afs.files[filename]; ok { return entry, nil } for p, _ := range afs.files { if strings.HasPrefix(p, filename) { return vfs.NewDirInfo(path.Base(filename), time.Time{}), nil } } return nil, vfs.ErrNotExist } // Info implements Filesystem. func (a *ArchiveFS) Info() (fs.FileInfo, error) { return a, nil } // IsDir implements Filesystem. func (a *ArchiveFS) IsDir() bool { return true } // Name implements Filesystem. func (a *ArchiveFS) Name() string { return a.name } // Type implements Filesystem. func (a *ArchiveFS) Type() fs.FileMode { return fs.ModeDir } var _ vfs.File = (*archiveFile)(nil) func newArchiveFile(name string, size int64, rr *randomReaderFromLinear) *archiveFile { return &archiveFile{ name: name, size: size, rr: rr, } } type archiveFile struct { name string size int64 m sync.Mutex offset int64 rr *randomReaderFromLinear } // Seek implements File. func (d *archiveFile) Seek(offset int64, whence int) (int64, error) { switch whence { case io.SeekStart: d.offset = offset case io.SeekCurrent: d.offset += offset case io.SeekEnd: d.offset = d.size + offset } return d.offset, nil } // Name implements File. func (d *archiveFile) Name() string { return d.name } // Type implements File. func (d *archiveFile) Type() fs.FileMode { return vfs.ModeFileRO } func (d *archiveFile) Info() (fs.FileInfo, error) { return vfs.NewFileInfo(d.name, d.size, time.Time{}), nil } func (d *archiveFile) Size() int64 { return d.size } func (d *archiveFile) IsDir() bool { return false } func (d *archiveFile) Read(ctx context.Context, p []byte) (n int, err error) { ctx, span := tracer.Start(ctx, "archive.File.Read") defer span.End() n, err = d.rr.ReadAt(ctx, p, d.offset) d.offset += int64(n) return n, err } func (d *archiveFile) ReadAt(ctx context.Context, p []byte, off int64) (n int, err error) { d.m.Lock() defer d.m.Unlock() return d.rr.ReadAt(ctx, p, off) } func (d *archiveFile) Close(ctx context.Context) error { // FIXME close should do nothing as archive fs currently reuse the same file instances return nil }