8 fusefs "bazil.org/fuse/fs"
9 "bitbucket.org/anacrolix/go.torrent"
10 metainfo "github.com/nsf/libtorgo/torrent"
17 type torrentFS struct {
18 Client *torrent.Client
21 var _ fusefs.NodeForgetter = rootNode{}
23 type rootNode struct {
29 metaInfo *metainfo.MetaInfo
31 InfoHash torrent.InfoHash
34 type fileNode struct {
40 func (fn fileNode) Attr() (attr fuse.Attr) {
42 attr.Mode = defaultMode
46 func (fn fileNode) Read(req *fuse.ReadRequest, resp *fuse.ReadResponse, intr fusefs.Intr) fuse.Error {
50 data := make([]byte, func() int {
51 _len := int64(fn.size) - req.Offset
52 if int64(req.Size) < _len {
55 // limit read to the end of the file
62 infoHash := torrent.BytesInfoHash(fn.metaInfo.InfoHash)
63 torrentOff := fn.TorrentOffset + req.Offset
64 log.Print(torrentOff, len(data), fn.TorrentOffset)
65 if err := fn.FS.Client.PrioritizeDataRegion(infoHash, torrentOff, int64(len(data))); err != nil {
69 dataWaiter := fn.FS.Client.DataWaiter()
70 n, err := fn.FS.Client.TorrentReadAt(infoHash, torrentOff, data)
75 case torrent.ErrDataNotReady:
93 _ fusefs.HandleReadDirer = dirNode{}
95 _ fusefs.HandleReader = fileNode{}
98 func isSubPath(parent, child []string) bool {
99 if len(child) <= len(parent) {
102 for i := range parent {
103 if parent[i] != child[i] {
110 func (dn dirNode) ReadDir(intr fusefs.Intr) (des []fuse.Dirent, err fuse.Error) {
111 names := map[string]bool{}
112 for _, fi := range dn.metaInfo.Files {
113 if !isSubPath(dn.path, fi.Path) {
116 name := fi.Path[len(dn.path)]
124 if len(fi.Path) == len(dn.path)+1 {
125 de.Type = fuse.DT_File
127 de.Type = fuse.DT_Dir
129 des = append(des, de)
134 func (dn dirNode) Lookup(name string, intr fusefs.Intr) (_node fusefs.Node, err fuse.Error) {
135 var torrentOffset int64
136 for _, fi := range dn.metaInfo.Files {
137 if !isSubPath(dn.path, fi.Path) {
138 torrentOffset += fi.Length
141 if fi.Path[len(dn.path)] != name {
142 torrentOffset += fi.Length
146 __node.path = append(__node.path, name)
147 if len(fi.Path) == len(dn.path)+1 {
150 size: uint64(fi.Length),
151 TorrentOffset: torrentOffset,
154 _node = dirNode{__node}
164 func (dn dirNode) Attr() (attr fuse.Attr) {
165 attr.Mode = os.ModeDir | defaultMode
169 func isSingleFileTorrent(mi *metainfo.MetaInfo) bool {
170 return len(mi.Files) == 1 && mi.Files[0].Path == nil
173 func (me rootNode) Lookup(name string, intr fusefs.Intr) (_node fusefs.Node, err fuse.Error) {
174 for _, _torrent := range me.fs.Client.Torrents() {
175 metaInfo := _torrent.MetaInfo
176 if metaInfo.Name == name {
180 InfoHash: torrent.BytesInfoHash(metaInfo.InfoHash),
182 if isSingleFileTorrent(metaInfo) {
183 _node = fileNode{__node, uint64(metaInfo.Files[0].Length), 0}
185 _node = dirNode{__node}
196 func (me rootNode) ReadDir(intr fusefs.Intr) (dirents []fuse.Dirent, err fuse.Error) {
197 for _, _torrent := range me.fs.Client.Torrents() {
198 metaInfo := _torrent.MetaInfo
199 dirents = append(dirents, fuse.Dirent{
201 Type: func() fuse.DirentType {
202 if isSingleFileTorrent(metaInfo) {
213 func (rootNode) Attr() fuse.Attr {
219 // TODO(anacrolix): Why should rootNode implement this?
220 func (rootNode) Forget() {
223 func (tfs *torrentFS) Root() (fusefs.Node, fuse.Error) {
224 return rootNode{tfs}, nil
227 func New(cl *torrent.Client) *torrentFS {