11 fusefs "bazil.org/fuse/fs"
12 "golang.org/x/net/context"
14 "github.com/anacrolix/torrent"
15 "github.com/anacrolix/torrent/metainfo"
23 torrentfsReadRequests = expvar.NewInt("torrentfsReadRequests")
24 torrentfsDelayedReadRequests = expvar.NewInt("torrentfsDelayedReadRequests")
25 interruptedReads = expvar.NewInt("interruptedReads")
28 type TorrentFS struct {
29 Client *torrent.Client
30 destroyed chan struct{}
37 _ fusefs.FSDestroyer = &TorrentFS{}
39 _ fusefs.NodeForgetter = rootNode{}
40 _ fusefs.HandleReadDirAller = rootNode{}
41 _ fusefs.HandleReadDirAller = dirNode{}
44 type rootNode struct {
50 metadata *metainfo.Info
60 _ fusefs.HandleReadDirAller = dirNode{}
63 func isSubPath(parent, child string) bool {
67 if !strings.HasPrefix(child, parent) {
70 s := child[len(parent):]
77 func (dn dirNode) ReadDirAll(ctx context.Context) (des []fuse.Dirent, err error) {
78 names := map[string]bool{}
79 for _, fi := range dn.metadata.Files {
80 if !isSubPath(dn.path, strings.Join(fi.Path, "/")) {
83 name := fi.Path[len(dn.path)]
91 if len(fi.Path) == len(dn.path)+1 {
92 de.Type = fuse.DT_File
101 func (dn dirNode) Lookup(ctx context.Context, name string) (_node fusefs.Node, err error) {
102 var torrentOffset int64
103 for _, fi := range dn.metadata.Files {
104 if !isSubPath(dn.path, strings.Join(fi.Path, "/")) {
105 torrentOffset += fi.Length
108 if fi.Path[len(dn.path)] != name {
109 torrentOffset += fi.Length
113 __node.path = path.Join(__node.path, name)
114 if len(fi.Path) == len(dn.path)+1 {
117 size: uint64(fi.Length),
118 TorrentOffset: torrentOffset,
121 _node = dirNode{__node}
131 func (dn dirNode) Attr(ctx context.Context, attr *fuse.Attr) error {
132 attr.Mode = os.ModeDir | defaultMode
136 func (rn rootNode) Lookup(ctx context.Context, name string) (_node fusefs.Node, err error) {
137 for _, t := range rn.fs.Client.Torrents() {
139 if t.Name() != name || info == nil {
148 _node = fileNode{__node, uint64(info.Length), 0}
150 _node = dirNode{__node}
160 func (rn rootNode) ReadDirAll(ctx context.Context) (dirents []fuse.Dirent, err error) {
161 for _, t := range rn.fs.Client.Torrents() {
166 dirents = append(dirents, fuse.Dirent{
168 Type: func() fuse.DirentType {
180 func (rn rootNode) Attr(ctx context.Context, attr *fuse.Attr) error {
181 attr.Mode = os.ModeDir
185 // TODO(anacrolix): Why should rootNode implement this?
186 func (rn rootNode) Forget() {
190 func (tfs *TorrentFS) Root() (fusefs.Node, error) {
191 return rootNode{tfs}, nil
194 func (tfs *TorrentFS) Destroy() {
197 case <-tfs.destroyed:
204 func New(cl *torrent.Client) *TorrentFS {
207 destroyed: make(chan struct{}),