157 lines
3.3 KiB
Go
157 lines
3.3 KiB
Go
package fs
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/knusbaum/go9p/fs"
|
|
"github.com/knusbaum/go9p/proto"
|
|
|
|
"storrent/client"
|
|
)
|
|
|
|
type Dir struct {
|
|
stat proto.Stat
|
|
parent fs.Dir
|
|
man *client.Manager
|
|
fsys *fs.FS
|
|
sync.RWMutex
|
|
}
|
|
|
|
func New(e *client.Manager) *fs.FS {
|
|
fsys, root := fs.NewFS("storrent", "storrent", 0555)
|
|
root.AddChild(&fs.WrappedFile{
|
|
File: fs.NewBaseFile(fsys.NewStat("ctl", "storrent", "storrent", 0222)),
|
|
WriteF: func(fid uint64, offset uint64, data []byte) (uint32, error) {
|
|
cmd := strings.TrimSpace(string(data))
|
|
if strings.HasPrefix(cmd, "add ") {
|
|
path := strings.TrimSpace(cmd[4:])
|
|
_, err := e.Add(path)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return uint32(len(data)), nil
|
|
}
|
|
return 0, fmt.Errorf("unknown command: %s", cmd)
|
|
},
|
|
})
|
|
|
|
root.AddChild(fs.NewDynamicFile(
|
|
fsys.NewStat("list", "storrent", "storrent", 0444),
|
|
func() []byte {
|
|
data := e.List()
|
|
if len(data) > 0 {
|
|
return append(data, '\n')
|
|
}
|
|
return data
|
|
},
|
|
))
|
|
|
|
dir := &Dir{
|
|
stat: *fsys.NewStat("torrents", "storrent", "storrent", 0555|proto.DMDIR),
|
|
man: e,
|
|
fsys: fsys,
|
|
}
|
|
dir.stat.Qid.Qtype = uint8(dir.stat.Mode >> 24)
|
|
root.AddChild(dir)
|
|
return fsys
|
|
}
|
|
|
|
func (d *Dir) Stat() proto.Stat {
|
|
d.Lock()
|
|
defer d.Unlock()
|
|
return d.stat
|
|
}
|
|
|
|
func (d *Dir) WriteStat(s *proto.Stat) error {
|
|
d.Lock()
|
|
defer d.Unlock()
|
|
d.stat = *s
|
|
return nil
|
|
}
|
|
|
|
func (d *Dir) SetParent(p fs.Dir) {
|
|
d.Lock()
|
|
defer d.Unlock()
|
|
d.parent = p
|
|
}
|
|
|
|
func (d *Dir) Parent() fs.Dir {
|
|
d.RLock()
|
|
defer d.RUnlock()
|
|
return d.parent
|
|
}
|
|
|
|
func (d *Dir) Children() map[string]fs.FSNode {
|
|
data := d.man.List()
|
|
m := make(map[string]fs.FSNode)
|
|
if len(data) == 0 {
|
|
return m
|
|
}
|
|
for _, name := range strings.Split(string(data), "\n") {
|
|
id, err := strconv.Atoi(name)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
m[name] = d.newTorrentDir(id)
|
|
}
|
|
return m
|
|
}
|
|
|
|
func (d *Dir) newTorrentDir(id int) fs.FSNode {
|
|
dir := fs.NewStaticDir(d.fsys.NewStat(strconv.Itoa(id), "storrent", "storrent", 0555))
|
|
dir.AddChild(&fs.WrappedFile{
|
|
File: fs.NewBaseFile(d.fsys.NewStat("ctl", "storrent", "storrent", 0222)),
|
|
WriteF: func(fid uint64, offset uint64, data []byte) (uint32, error) {
|
|
cmd := strings.TrimSpace(string(data))
|
|
var err error
|
|
switch {
|
|
case cmd == "start":
|
|
err = d.man.Start(id)
|
|
case cmd == "stop":
|
|
err = d.man.Stop(id)
|
|
case cmd == "seed":
|
|
err = d.man.Seed(id)
|
|
case cmd == "remove":
|
|
err = d.man.Remove(id)
|
|
case strings.HasPrefix(cmd, "peer "):
|
|
err = d.man.AddPeer(id, strings.TrimSpace(cmd[5:]))
|
|
default:
|
|
return 0, fmt.Errorf("unknown command: %s", cmd)
|
|
}
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return uint32(len(data)), nil
|
|
},
|
|
})
|
|
|
|
dir.AddChild(d.newStatusFile(id, "name"))
|
|
dir.AddChild(d.newStatusFile(id, "state"))
|
|
dir.AddChild(d.newStatusFile(id, "progress"))
|
|
dir.AddChild(d.newStatusFile(id, "size"))
|
|
dir.AddChild(d.newStatusFile(id, "down"))
|
|
dir.AddChild(d.newStatusFile(id, "up"))
|
|
dir.AddChild(d.newStatusFile(id, "pieces"))
|
|
dir.AddChild(d.newStatusFile(id, "peers"))
|
|
return dir
|
|
}
|
|
|
|
func (d *Dir) newStatusFile(id int, field string) fs.FSNode {
|
|
return fs.NewDynamicFile(
|
|
d.fsys.NewStat(field, "storrent", "storrent", 0444),
|
|
func() []byte {
|
|
data, err := d.man.Status(id, field)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
if len(data) > 0 {
|
|
return append(data, '\n')
|
|
}
|
|
return data
|
|
},
|
|
)
|
|
}
|