update
This commit is contained in:
parent
7b1863109c
commit
ef751771d2
107 changed files with 9435 additions and 850 deletions
src/host/vfs
|
@ -2,28 +2,84 @@ package vfs
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log/slog"
|
||||
"path"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.kmsign.ru/royalcat/tstor/pkg/rlog"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
type ResolverFS struct {
|
||||
rootFS Filesystem
|
||||
resolver *resolver
|
||||
|
||||
log *slog.Logger
|
||||
}
|
||||
|
||||
func NewResolveFS(rootFs Filesystem, factories map[string]FsFactory) *ResolverFS {
|
||||
return &ResolverFS{
|
||||
rootFS: rootFs,
|
||||
resolver: newResolver(factories),
|
||||
log: rlog.ComponentLog("fs/resolverfs"),
|
||||
}
|
||||
}
|
||||
|
||||
// ModTime implements Filesystem.
|
||||
func (r *ResolverFS) ModTime() time.Time {
|
||||
return time.Time{}
|
||||
}
|
||||
|
||||
// Mode implements Filesystem.
|
||||
func (r *ResolverFS) Mode() fs.FileMode {
|
||||
return fs.ModeDir
|
||||
}
|
||||
|
||||
// Size implements Filesystem.
|
||||
func (r *ResolverFS) Size() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Sys implements Filesystem.
|
||||
func (r *ResolverFS) Sys() any {
|
||||
return nil
|
||||
}
|
||||
|
||||
// FsName implements Filesystem.
|
||||
func (r *ResolverFS) FsName() string {
|
||||
return "resolverfs"
|
||||
}
|
||||
|
||||
func (fs *ResolverFS) traceAttrs(add ...attribute.KeyValue) trace.SpanStartOption {
|
||||
return trace.WithAttributes(append([]attribute.KeyValue{
|
||||
attribute.String("fs", fs.FsName()),
|
||||
}, add...)...)
|
||||
}
|
||||
|
||||
func (r *ResolverFS) ResolvablesExtensions() []string {
|
||||
return maps.Keys(r.resolver.factories)
|
||||
}
|
||||
|
||||
// Open implements Filesystem.
|
||||
func (r *ResolverFS) Open(ctx context.Context, filename string) (File, error) {
|
||||
ctx, span := tracer.Start(ctx, "Open",
|
||||
r.traceAttrs(attribute.String("filename", filename)),
|
||||
)
|
||||
defer span.End()
|
||||
|
||||
if path.Clean(filename) == Separator {
|
||||
return newDirFile(r.Name()), nil
|
||||
}
|
||||
|
||||
fsPath, nestedFs, nestedFsPath, err := r.resolver.resolvePath(ctx, filename, r.rootFS.Open)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -37,6 +93,11 @@ func (r *ResolverFS) Open(ctx context.Context, filename string) (File, error) {
|
|||
|
||||
// ReadDir implements Filesystem.
|
||||
func (r *ResolverFS) ReadDir(ctx context.Context, dir string) ([]fs.DirEntry, error) {
|
||||
ctx, span := tracer.Start(ctx, "ReadDir",
|
||||
r.traceAttrs(attribute.String("name", dir)),
|
||||
)
|
||||
defer span.End()
|
||||
|
||||
fsPath, nestedFs, nestedFsPath, err := r.resolver.resolvePath(ctx, dir, r.rootFS.Open)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -57,8 +118,14 @@ func (r *ResolverFS) ReadDir(ctx context.Context, dir string) ([]fs.DirEntry, er
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close(ctx)
|
||||
nestedfs, err := r.resolver.nestedFs(ctx, filepath, file)
|
||||
if err != nil {
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
r.log.ErrorContext(ctx, "creating fs timed out", "filename", e.Name())
|
||||
continue
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -72,11 +139,23 @@ func (r *ResolverFS) ReadDir(ctx context.Context, dir string) ([]fs.DirEntry, er
|
|||
|
||||
// Stat implements Filesystem.
|
||||
func (r *ResolverFS) Stat(ctx context.Context, filename string) (fs.FileInfo, error) {
|
||||
ctx, span := tracer.Start(ctx, "Stat",
|
||||
r.traceAttrs(attribute.String("filename", filename)),
|
||||
)
|
||||
defer span.End()
|
||||
|
||||
if isRoot(filename) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
fsPath, nestedFs, nestedFsPath, err := r.resolver.resolvePath(ctx, filename, r.rootFS.Open)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
span.SetAttributes(attribute.String("fsPath", fsPath), attribute.String("nestedFsPath", nestedFsPath))
|
||||
|
||||
if nestedFs != nil {
|
||||
span.AddEvent("calling nested fs")
|
||||
return nestedFs.Stat(ctx, nestedFsPath)
|
||||
}
|
||||
|
||||
|
@ -98,7 +177,7 @@ func (r *ResolverFS) Unlink(ctx context.Context, filename string) error {
|
|||
|
||||
// Info implements Filesystem.
|
||||
func (r *ResolverFS) Info() (fs.FileInfo, error) {
|
||||
return newDirInfo(r.rootFS.Name()), nil
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// IsDir implements Filesystem.
|
||||
|
@ -108,7 +187,7 @@ func (r *ResolverFS) IsDir() bool {
|
|||
|
||||
// Name implements Filesystem.
|
||||
func (r *ResolverFS) Name() string {
|
||||
return r.Name()
|
||||
return r.rootFS.Name()
|
||||
}
|
||||
|
||||
// Type implements Filesystem.
|
||||
|
@ -120,8 +199,6 @@ var _ Filesystem = &ResolverFS{}
|
|||
|
||||
type FsFactory func(ctx context.Context, f File) (Filesystem, error)
|
||||
|
||||
const Separator = "/"
|
||||
|
||||
func newResolver(factories map[string]FsFactory) *resolver {
|
||||
return &resolver{
|
||||
factories: factories,
|
||||
|
@ -171,6 +248,9 @@ func (r *resolver) nestedFs(ctx context.Context, fsPath string, file File) (File
|
|||
|
||||
// open requeue raw open, without resolver call
|
||||
func (r *resolver) resolvePath(ctx context.Context, name string, rawOpen openFile) (fsPath string, nestedFs Filesystem, nestedFsPath string, err error) {
|
||||
ctx, span := tracer.Start(ctx, "resolvePath")
|
||||
defer span.End()
|
||||
|
||||
name = path.Clean(name)
|
||||
name = strings.TrimPrefix(name, Separator)
|
||||
parts := strings.Split(name, Separator)
|
||||
|
@ -205,8 +285,12 @@ PARTS_LOOP:
|
|||
defer r.m.Unlock()
|
||||
|
||||
if nestedFs, ok := r.fsmap[fsPath]; ok {
|
||||
span.AddEvent("fs loaded from cache", trace.WithAttributes(attribute.String("nestedFs", reflect.TypeOf(nestedFs).Name())))
|
||||
return fsPath, nestedFs, nestedFsPath, nil
|
||||
} else {
|
||||
ctx, span := tracer.Start(ctx, "CreateFS")
|
||||
defer span.End()
|
||||
|
||||
fsFile, err := rawOpen(ctx, fsPath)
|
||||
if err != nil {
|
||||
return "", nil, "", fmt.Errorf("error opening filesystem file: %s with error: %w", fsPath, err)
|
||||
|
@ -217,6 +301,8 @@ PARTS_LOOP:
|
|||
}
|
||||
r.fsmap[fsPath] = nestedFs
|
||||
|
||||
span.AddEvent("fs created", trace.WithAttributes(attribute.String("nestedFs", reflect.TypeOf(nestedFs).Name())))
|
||||
|
||||
return fsPath, nestedFs, nestedFsPath, nil
|
||||
}
|
||||
|
||||
|
@ -226,7 +312,7 @@ var ErrNotExist = fs.ErrNotExist
|
|||
|
||||
func getFile[F File](m map[string]F, name string) (File, error) {
|
||||
if name == Separator {
|
||||
return NewDir(name), nil
|
||||
return newDirFile(name), nil
|
||||
}
|
||||
|
||||
f, ok := m[name]
|
||||
|
@ -236,7 +322,7 @@ func getFile[F File](m map[string]F, name string) (File, error) {
|
|||
|
||||
for p := range m {
|
||||
if strings.HasPrefix(p, name) {
|
||||
return NewDir(name), nil
|
||||
return newDirFile(name), nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue