From 4bd672f301ada9eab621d0c06421234012f29cb8 Mon Sep 17 00:00:00 2001 From: Ben Sarmiento Date: Sun, 5 Nov 2023 21:25:50 +0100 Subject: [PATCH] Add fuse mount without read --- cmd/zurg/main.go | 6 +- go.mod | 11 +-- go.sum | 16 +++- internal/zfs/fs.go | 14 +++- internal/zfs/mount.go | 26 +++++-- internal/zfs/object.go | 168 ++++++++++++++++++++++++++++++++++++----- 6 files changed, 198 insertions(+), 43 deletions(-) diff --git a/cmd/zurg/main.go b/cmd/zurg/main.go index d731138..7b7b90f 100644 --- a/cmd/zurg/main.go +++ b/cmd/zurg/main.go @@ -19,7 +19,7 @@ import ( func main() { rlog := logutil.NewLogger() - log := rlog.Named("main") + log := rlog.Named("zurg") config, configErr := config.LoadZurgConfig("./config.yml") if configErr != nil { @@ -66,7 +66,7 @@ func main() { } }() log.Infof("Mounting on %s", mountPoint) - if err := zfs.Mount(mountPoint); err != nil { + if err := zfs.Mount(mountPoint, config, t); err != nil { log.Panicf("Failed to mount: %v", err) } }() @@ -83,5 +83,5 @@ func main() { log.Errorf("Unmount error: %v\n", err) } - log.Info("Zurg signing off") + log.Info("BYE") } diff --git a/go.mod b/go.mod index 87e6b19..eefa01b 100644 --- a/go.mod +++ b/go.mod @@ -3,14 +3,11 @@ module github.com/debridmediamanager.com/zurg go 1.21.3 require ( + bazil.org/fuse v0.0.0-20230120002735-62a210ff1fd5 github.com/hashicorp/golang-lru/v2 v2.0.7 + go.uber.org/zap v1.26.0 + golang.org/x/sys v0.4.0 gopkg.in/yaml.v3 v3.0.1 ) -require ( - bazil.org/fuse v0.0.0-20230120002735-62a210ff1fd5 // indirect - github.com/mdlayher/sdnotify v1.0.0 // indirect - go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.26.0 // indirect - golang.org/x/sys v0.4.0 // indirect -) +require go.uber.org/multierr v1.10.0 // indirect diff --git a/go.sum b/go.sum index b4cea36..252ca36 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,19 @@ bazil.org/fuse v0.0.0-20230120002735-62a210ff1fd5 h1:A0NsYy4lDBZAC6QiYeJ4N+XuHIKBpyhAVRMHRQZKTeQ= bazil.org/fuse v0.0.0-20230120002735-62a210ff1fd5/go.mod h1:gG3RZAMXCa/OTes6rr9EwusmR1OH1tDDy+cg9c5YliY= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/mdlayher/sdnotify v1.0.0 h1:Ma9XeLVN/l0qpyx1tNeMSeTjCPH6NtuD6/N9XdTlQ3c= -github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c h1:u6SKchux2yDvFQnDHS3lPnIRmfVJ5Sxy3ao2SIdysLQ= +github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= diff --git a/internal/zfs/fs.go b/internal/zfs/fs.go index e866579..2967f1e 100644 --- a/internal/zfs/fs.go +++ b/internal/zfs/fs.go @@ -3,8 +3,12 @@ package zfs import ( "os" "sync" + "time" "bazil.org/fuse/fs" + "github.com/debridmediamanager.com/zurg/internal/config" + "github.com/debridmediamanager.com/zurg/internal/torrent" + "go.uber.org/zap" ) type FS struct { @@ -13,9 +17,17 @@ type FS struct { umask os.FileMode directIO bool lock sync.RWMutex + c config.ConfigInterface + t *torrent.TorrentManager + log *zap.SugaredLogger + initTime time.Time } // Root returns the root path func (f *FS) Root() (fs.Node, error) { - return nil, nil + return Object{ + fs: f, + objType: ROOT, + mtime: f.initTime, + }, nil } diff --git a/internal/zfs/mount.go b/internal/zfs/mount.go index 0304d6c..8dad4cf 100644 --- a/internal/zfs/mount.go +++ b/internal/zfs/mount.go @@ -2,14 +2,21 @@ package zfs import ( "os" + "time" "bazil.org/fuse" "bazil.org/fuse/fs" + "github.com/debridmediamanager.com/zurg/internal/config" + "github.com/debridmediamanager.com/zurg/internal/torrent" + "github.com/debridmediamanager.com/zurg/pkg/logutil" "golang.org/x/sys/unix" ) -func Mount(mountpoint string) error { +func Mount(mountpoint string, c config.ConfigInterface, t *torrent.TorrentManager) error { + rlog := logutil.NewLogger() + log := rlog.Named("zfs") + options := []fuse.MountOption{ fuse.AllowOther(), fuse.AllowNonEmptyMount(), @@ -17,21 +24,24 @@ func Mount(mountpoint string) error { fuse.DefaultPermissions(), fuse.FSName("zurgfs"), } - c, err := fuse.Mount(mountpoint, options...) + conn, err := fuse.Mount(mountpoint, options...) if err != nil { return err } - defer c.Close() + defer conn.Close() - srv := fs.New(c, nil) + srv := fs.New(conn, nil) filesys := &FS{ - uid: uint32(unix.Geteuid()), - gid: uint32(unix.Getegid()), - umask: os.FileMode(0), + uid: uint32(unix.Geteuid()), + gid: uint32(unix.Getegid()), + umask: os.FileMode(0), + c: c, + t: t, + log: log, + initTime: time.Now(), } - // Serve our tree via FUSE. if err := srv.Serve(filesys); err != nil { return err } diff --git a/internal/zfs/object.go b/internal/zfs/object.go index aa0fa94..469fb53 100644 --- a/internal/zfs/object.go +++ b/internal/zfs/object.go @@ -2,49 +2,177 @@ package zfs import ( "context" + "fmt" + "os" + "path/filepath" + "strings" + "syscall" + "time" "bazil.org/fuse" "bazil.org/fuse/fs" - "github.com/debridmediamanager.com/zurg/internal/torrent" +) + +// define variable as rootObject id +const ( + ROOT = 0 + DIRECTORY = 1 + TORRENT = 2 + FILE = 3 ) type Object struct { - fs *FS - objectID string -} - -func (o Object) GetObject() (object *torrent.Torrent, err error) { - return nil, nil + fs *FS + objType int + parentName string + name string + size uint64 + mtime time.Time } +// Attr returns the attributes for a directory func (o Object) Attr(ctx context.Context, attr *fuse.Attr) error { + if o.objType == FILE { + attr.Mode = 0644 + } else { + attr.Mode = os.ModeDir | 0755 + } + attr.Size = o.size + + attr.Uid = o.fs.uid + attr.Gid = o.fs.gid + + attr.Ctime = o.mtime + attr.Mtime = o.mtime + + attr.Blocks = (attr.Size + 511) >> 9 + return nil } +// ReadDirAll shows all files in the current directory func (o Object) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) { - return nil, nil + dirs := []fuse.Dirent{} + switch o.objType { + case ROOT: + for _, directory := range o.fs.c.GetDirectories() { + dirs = append(dirs, fuse.Dirent{ + Name: directory, + Type: fuse.DT_Dir, + }) + } + case DIRECTORY: + seen := make(map[string]bool) + for _, item := range o.fs.t.GetByDirectory(o.name) { + if item.Progress != 100 { + continue + } + if _, exists := seen[item.Name]; exists { + continue + } + seen[item.Name] = true + dirs = append(dirs, fuse.Dirent{ + Name: item.Name, + Type: fuse.DT_Dir, + }) + } + case TORRENT: + finalName := make(map[string]bool) + for _, item := range o.fs.t.FindAllTorrentsWithName(o.parentName, o.name) { + for _, file := range item.SelectedFiles { + if file.Link == "" { + // log.Println("File has no link, skipping", file.Path) + continue + } + filename := filepath.Base(file.Path) + if finalName[filename] { + // fragment := davextra.GetLinkFragment(file.Link) + // filename = davextra.InsertLinkFragment(filename, fragment) + continue + } + finalName[filename] = true + dirs = append(dirs, fuse.Dirent{ + Name: filename, + Type: fuse.DT_File, + }) + } + } + } + return dirs, nil } +// Lookup tests if a file is existent in the current directory func (o Object) Lookup(ctx context.Context, name string) (fs.Node, error) { - return nil, nil -} - -func (o Object) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error { - return nil + switch o.objType { + case ROOT: + for _, directory := range o.fs.c.GetDirectories() { + if directory == name { + return Object{ + fs: o.fs, + objType: DIRECTORY, + parentName: o.name, + name: name, + mtime: o.fs.initTime, + }, nil + } + } + case DIRECTORY: + for _, item := range o.fs.t.GetByDirectory(o.name) { + if item.Name == name && item.Progress == 100 { + return Object{ + fs: o.fs, + objType: TORRENT, + parentName: o.name, + name: name, + mtime: convertRFC3339toTime(item.Added), + }, nil + } + } + case TORRENT: + for _, item := range o.fs.t.FindAllTorrentsWithName(o.parentName, o.name) { + for _, file := range item.SelectedFiles { + if strings.HasSuffix(file.Path, name) && file.Link != "" { + return Object{ + fs: o.fs, + objType: FILE, + parentName: o.name, + name: name, + size: uint64(file.Bytes), + mtime: convertRFC3339toTime(item.Added), + }, nil + } + } + } + } + return nil, syscall.ENOENT } +// Open a file func (o Object) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fs.Handle, error) { - return nil, nil + resp.Flags |= fuse.OpenDirectIO + return o, nil } +// Read reads some bytes or the whole file +func (o Object) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error { + return fmt.Errorf("Read not yet implemented") +} + +// Remove deletes an element func (o Object) Remove(ctx context.Context, req *fuse.RemoveRequest) error { - return nil -} - -func (o Object) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) { - return nil, nil + return fmt.Errorf("Remove not yet implemented") } +// Rename renames an element func (o Object) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.Node) error { - return nil + return fmt.Errorf("Rename not yet implemented") +} + +func convertRFC3339toTime(input string) time.Time { + layout := "2006-01-02T15:04:05.000Z" + t, err := time.Parse(layout, input) + if err != nil { + return time.Now() + } + return t }