}
}
-type torrent struct {
+type Torrent struct {
InfoHash InfoHash
Pieces []piece
Data MMapSpan
Peers []Peer
}
-func (t *torrent) WriteChunk(piece int, begin int64, data []byte) (err error) {
+func (t *Torrent) WriteChunk(piece int, begin int64, data []byte) (err error) {
_, err = t.Data.WriteAt(data, int64(piece)*t.MetaInfo.PieceLength+begin)
return
}
-func (t *torrent) bitfield() (bf []bool) {
+func (t *Torrent) bitfield() (bf []bool) {
for _, p := range t.Pieces {
bf = append(bf, p.State == pieceStateComplete)
}
return
}
-func (t *torrent) pieceChunkSpecs(index int) (cs map[chunkSpec]struct{}) {
+func (t *Torrent) pieceChunkSpecs(index int) (cs map[chunkSpec]struct{}) {
cs = make(map[chunkSpec]struct{}, (t.MetaInfo.PieceLength+chunkSize-1)/chunkSize)
c := chunkSpec{
Begin: 0,
return
}
-func (t *torrent) requestHeat() (ret map[request]int) {
+func (t *Torrent) requestHeat() (ret map[request]int) {
ret = make(map[request]int)
for _, conn := range t.Conns {
for req, _ := range conn.Requests {
Port int
}
-func (t *torrent) PieceSize(piece int) (size int64) {
+func (t *Torrent) PieceSize(piece int) (size int64) {
if piece == len(t.Pieces)-1 {
size = t.Data.Size() % t.MetaInfo.PieceLength
}
return
}
-func (t *torrent) PieceReader(piece int) io.Reader {
+func (t *Torrent) PieceReader(piece int) io.Reader {
return io.NewSectionReader(t.Data, int64(piece)*t.MetaInfo.PieceLength, t.MetaInfo.PieceLength)
}
-func (t *torrent) HashPiece(piece int) (ps pieceSum) {
+func (t *Torrent) HashPiece(piece int) (ps pieceSum) {
hash := PieceHash.New()
n, err := io.Copy(hash, t.PieceReader(piece))
if err != nil {
return
}
-// func (t *torrent) bitfield
-
-type client struct {
+type Client struct {
DataDir string
HalfOpenLimit int
PeerId [20]byte
halfOpen int
- torrents map[InfoHash]*torrent
+ torrents map[InfoHash]*Torrent
noTorrents chan struct{}
- addTorrent chan *torrent
+ addTorrent chan *Torrent
torrentFinished chan InfoHash
actorTask chan func()
}
-func NewClient(dataDir string) *client {
- c := &client{
+func NewClient(dataDir string) *Client {
+ c := &Client{
DataDir: dataDir,
HalfOpenLimit: 10,
- torrents: make(map[InfoHash]*torrent),
+ torrents: make(map[InfoHash]*Torrent),
noTorrents: make(chan struct{}),
- addTorrent: make(chan *torrent),
+ addTorrent: make(chan *Torrent),
torrentFinished: make(chan InfoHash),
actorTask: make(chan func()),
}
return
}
-func (me *client) torrent(ih InfoHash) *torrent {
+func (me *Client) torrent(ih InfoHash) *Torrent {
for _, t := range me.torrents {
if t.InfoHash == ih {
return t
return nil
}
-func (me *client) initiateConn(peer Peer, torrent *torrent) {
+func (me *Client) initiateConn(peer Peer, torrent *Torrent) {
if peer.Id == me.PeerId {
return
}
}()
}
-func (me *torrent) haveAnyPieces() bool {
+func (me *Torrent) haveAnyPieces() bool {
for _, piece := range me.Pieces {
if piece.State == pieceStateComplete {
return true
return false
}
-func (me *client) handshake(sock net.Conn, torrent *torrent, peerId [20]byte) {
+func (me *Client) handshake(sock net.Conn, torrent *Torrent, peerId [20]byte) {
conn := &connection{
Socket: sock,
Choked: true,
})
}
-func (me *client) peerGotPiece(torrent *torrent, conn *connection, piece int) {
+func (me *Client) peerGotPiece(torrent *Torrent, conn *connection, piece int) {
if conn.PeerPieces == nil {
conn.PeerPieces = make([]bool, len(torrent.Pieces))
}
}
}
-func (t *torrent) wantPiece(index int) bool {
+func (t *Torrent) wantPiece(index int) bool {
return t.Pieces[index].State == pieceStateIncomplete
}
-func (me *client) peerUnchoked(torrent *torrent, conn *connection) {
+func (me *Client) peerUnchoked(torrent *Torrent, conn *connection) {
me.replenishConnRequests(torrent, conn)
}
-func (me *client) runConnection(torrent *torrent, conn *connection) error {
+func (me *Client) runConnection(torrent *Torrent, conn *connection) error {
decoder := peer_protocol.Decoder{
R: bufio.NewReader(conn.Socket),
MaxLength: 256 * 1024,
}
}
-func (me *client) dropConnection(torrent *torrent, conn *connection) {
+func (me *Client) dropConnection(torrent *Torrent, conn *connection) {
conn.Socket.Close()
for i0, c := range torrent.Conns {
if c != conn {
panic("no such connection")
}
-func (me *client) addConnection(t *torrent, c *connection) bool {
+func (me *Client) addConnection(t *Torrent, c *connection) bool {
for _, c := range t.Conns {
if c.PeerId == c.PeerId {
return false
return true
}
-func (me *client) openNewConns() {
+func (me *Client) openNewConns() {
for _, t := range me.torrents {
for len(t.Peers) != 0 {
if me.halfOpen >= me.HalfOpenLimit {
}
}
-func (me *client) AddPeers(infoHash InfoHash, peers []Peer) (err error) {
+func (me *Client) AddPeers(infoHash InfoHash, peers []Peer) (err error) {
me.withContext(func() {
t := me.torrent(infoHash)
if t == nil {
return
}
-func (me *client) AddTorrent(metaInfo *metainfo.MetaInfo) error {
- torrent := &torrent{
+func (me *Client) AddTorrent(metaInfo *metainfo.MetaInfo) error {
+ torrent := &Torrent{
InfoHash: BytesInfoHash(metaInfo.InfoHash),
}
for offset := 0; offset < len(metaInfo.Pieces); offset += PieceHash.Size() {
return nil
}
-func (me *client) WaitAll() {
+func (me *Client) WaitAll() {
<-me.noTorrents
}
-func (me *client) Close() {
+func (me *Client) Close() {
}
-func (me *client) withContext(f func()) {
+func (me *Client) withContext(f func()) {
me.actorTask <- f
}
-func (me *client) replenishConnRequests(torrent *torrent, conn *connection) {
+func (me *Client) replenishConnRequests(torrent *Torrent, conn *connection) {
if len(conn.Requests) >= maxRequests {
return
}
}
-func (me *client) pieceHashed(ih InfoHash, piece int, correct bool) {
+func (me *Client) pieceHashed(ih InfoHash, piece int, correct bool) {
torrent := me.torrents[ih]
newState := func() pieceState {
if correct {
}
}
-func (me *client) verifyPiece(torrent *torrent, index int) {
+func (me *Client) verifyPiece(torrent *Torrent, index int) {
sum := torrent.HashPiece(index)
me.withContext(func() {
me.pieceHashed(torrent.InfoHash, index, sum == torrent.Pieces[index].Hash)
})
}
-func (me *client) run() {
+func (me *Client) run() {
for {
noTorrents := me.noTorrents
if len(me.torrents) != 0 {
}
}
}
+
+func (me *Client) Torrents() (ret []*Torrent) {
+ done := make(chan struct{})
+ me.withContext(func() {
+ for _, t := range me.torrents {
+ ret = append(ret, t)
+ }
+ close(done)
+ })
+ <-done
+ return
+}
--- /dev/null
+package main
+
+import (
+ "bazil.org/fuse"
+ fusefs "bazil.org/fuse/fs"
+ "bitbucket.org/anacrolix/go.torrent"
+ "flag"
+ metainfo "github.com/nsf/libtorgo/torrent"
+ "log"
+ "os"
+ "os/user"
+ "path/filepath"
+)
+
+var (
+ downloadDir string
+ torrentPath string
+ mountDir string
+)
+
+func init() {
+ flag.StringVar(&downloadDir, "downloadDir", "", "location to save torrent data")
+ flag.StringVar(&torrentPath, "torrentPath", func() string {
+ _user, err := user.Current()
+ if err != nil {
+ log.Fatal(err)
+ }
+ return filepath.Join(_user.HomeDir, ".config/transmission/torrents")
+ }(), "torrent files in this location describe the contents of the mounted filesystem")
+ flag.StringVar(&mountDir, "mountDir", "", "location the torrent contents are made available")
+}
+
+type TorrentFS struct {
+ Client *torrent.Client
+}
+
+type rootNode struct {
+ fs *TorrentFS
+}
+
+func (me rootNode) ReadDir(intr fusefs.Intr) (dirents []fuse.Dirent, err fuse.Error) {
+ for _, _torrent := range me.fs.Client.Torrents() {
+ metaInfo := _torrent.MetaInfo
+ dirents = append(dirents, fuse.Dirent{
+ Name: metaInfo.Name,
+ Type: func() fuse.DirentType {
+ if len(metaInfo.Files) == 1 && metaInfo.Files[0].Path == nil {
+ return fuse.DT_File
+ } else {
+ return fuse.DT_Dir
+ }
+ }(),
+ })
+ }
+ return
+}
+
+func (rootNode) Attr() fuse.Attr {
+ return fuse.Attr{
+ Mode: os.ModeDir,
+ }
+}
+
+func (tfs *TorrentFS) Root() (fusefs.Node, fuse.Error) {
+ return rootNode{tfs}, nil
+}
+
+func main() {
+ flag.Parse()
+ client := torrent.NewClient(downloadDir)
+ torrentDir, err := os.Open(torrentPath)
+ defer torrentDir.Close()
+ if err != nil {
+ log.Fatal(err)
+ }
+ names, err := torrentDir.Readdirnames(-1)
+ if err != nil {
+ log.Fatal(err)
+ }
+ for _, name := range names {
+ metaInfo, err := metainfo.LoadFromFile(filepath.Join(torrentPath, name))
+ if err != nil {
+ log.Print(err)
+ }
+ client.AddTorrent(metaInfo)
+ }
+ conn, err := fuse.Mount(mountDir)
+ if err != nil {
+ log.Fatal(err)
+ }
+ fs := &TorrentFS{client}
+ fusefs.Serve(conn, fs)
+}