Add fuse mount without read
This commit is contained in:
@@ -19,7 +19,7 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
rlog := logutil.NewLogger()
|
rlog := logutil.NewLogger()
|
||||||
log := rlog.Named("main")
|
log := rlog.Named("zurg")
|
||||||
|
|
||||||
config, configErr := config.LoadZurgConfig("./config.yml")
|
config, configErr := config.LoadZurgConfig("./config.yml")
|
||||||
if configErr != nil {
|
if configErr != nil {
|
||||||
@@ -66,7 +66,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
log.Infof("Mounting on %s", mountPoint)
|
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)
|
log.Panicf("Failed to mount: %v", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -83,5 +83,5 @@ func main() {
|
|||||||
log.Errorf("Unmount error: %v\n", err)
|
log.Errorf("Unmount error: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Zurg signing off")
|
log.Info("BYE")
|
||||||
}
|
}
|
||||||
|
|||||||
11
go.mod
11
go.mod
@@ -3,14 +3,11 @@ module github.com/debridmediamanager.com/zurg
|
|||||||
go 1.21.3
|
go 1.21.3
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
bazil.org/fuse v0.0.0-20230120002735-62a210ff1fd5
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
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
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require go.uber.org/multierr v1.10.0 // indirect
|
||||||
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
|
|
||||||
)
|
|
||||||
|
|||||||
16
go.sum
16
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 h1:A0NsYy4lDBZAC6QiYeJ4N+XuHIKBpyhAVRMHRQZKTeQ=
|
||||||
bazil.org/fuse v0.0.0-20230120002735-62a210ff1fd5/go.mod h1:gG3RZAMXCa/OTes6rr9EwusmR1OH1tDDy+cg9c5YliY=
|
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 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/mdlayher/sdnotify v1.0.0/go.mod h1:HQUmpM4XgYkhDLtd+Uad8ZFK1T9D5+pNxnXQjCeJlGE=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
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 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
|
||||||
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
|
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
|
||||||
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
|
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
|
||||||
|
|||||||
@@ -3,8 +3,12 @@ package zfs
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"bazil.org/fuse/fs"
|
"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 {
|
type FS struct {
|
||||||
@@ -13,9 +17,17 @@ type FS struct {
|
|||||||
umask os.FileMode
|
umask os.FileMode
|
||||||
directIO bool
|
directIO bool
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
|
c config.ConfigInterface
|
||||||
|
t *torrent.TorrentManager
|
||||||
|
log *zap.SugaredLogger
|
||||||
|
initTime time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// Root returns the root path
|
// Root returns the root path
|
||||||
func (f *FS) Root() (fs.Node, error) {
|
func (f *FS) Root() (fs.Node, error) {
|
||||||
return nil, nil
|
return Object{
|
||||||
|
fs: f,
|
||||||
|
objType: ROOT,
|
||||||
|
mtime: f.initTime,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,21 @@ package zfs
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"bazil.org/fuse"
|
"bazil.org/fuse"
|
||||||
"bazil.org/fuse/fs"
|
"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"
|
"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{
|
options := []fuse.MountOption{
|
||||||
fuse.AllowOther(),
|
fuse.AllowOther(),
|
||||||
fuse.AllowNonEmptyMount(),
|
fuse.AllowNonEmptyMount(),
|
||||||
@@ -17,21 +24,24 @@ func Mount(mountpoint string) error {
|
|||||||
fuse.DefaultPermissions(),
|
fuse.DefaultPermissions(),
|
||||||
fuse.FSName("zurgfs"),
|
fuse.FSName("zurgfs"),
|
||||||
}
|
}
|
||||||
c, err := fuse.Mount(mountpoint, options...)
|
conn, err := fuse.Mount(mountpoint, options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer c.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
srv := fs.New(c, nil)
|
srv := fs.New(conn, nil)
|
||||||
|
|
||||||
filesys := &FS{
|
filesys := &FS{
|
||||||
uid: uint32(unix.Geteuid()),
|
uid: uint32(unix.Geteuid()),
|
||||||
gid: uint32(unix.Getegid()),
|
gid: uint32(unix.Getegid()),
|
||||||
umask: os.FileMode(0),
|
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 {
|
if err := srv.Serve(filesys); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,49 +2,177 @@ package zfs
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
"bazil.org/fuse"
|
"bazil.org/fuse"
|
||||||
"bazil.org/fuse/fs"
|
"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 {
|
type Object struct {
|
||||||
fs *FS
|
fs *FS
|
||||||
objectID string
|
objType int
|
||||||
}
|
parentName string
|
||||||
|
name string
|
||||||
func (o Object) GetObject() (object *torrent.Torrent, err error) {
|
size uint64
|
||||||
return nil, nil
|
mtime time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Attr returns the attributes for a directory
|
||||||
func (o Object) Attr(ctx context.Context, attr *fuse.Attr) error {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadDirAll shows all files in the current directory
|
||||||
func (o Object) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
|
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) {
|
func (o Object) Lookup(ctx context.Context, name string) (fs.Node, error) {
|
||||||
return nil, nil
|
switch o.objType {
|
||||||
}
|
case ROOT:
|
||||||
|
for _, directory := range o.fs.c.GetDirectories() {
|
||||||
func (o Object) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error {
|
if directory == name {
|
||||||
return nil
|
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) {
|
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 {
|
func (o Object) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
|
||||||
return nil
|
return fmt.Errorf("Remove not yet implemented")
|
||||||
}
|
|
||||||
|
|
||||||
func (o Object) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rename renames an element
|
||||||
func (o Object) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.Node) error {
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user