From: Matt Joiner Date: Sun, 6 Oct 2013 07:01:39 +0000 (+1100) Subject: Start implementing torrentfs binary X-Git-Tag: v1.0.0~1802 X-Git-Url: http://www.git.stargrave.org/?a=commitdiff_plain;h=68e189d47700d5bcd87603094867cc80438b7f84;p=btrtrc.git Start implementing torrentfs binary --- diff --git a/client.go b/client.go index f2d76215..6514748b 100644 --- a/client.go +++ b/client.go @@ -166,7 +166,7 @@ func (conn *connection) writeOptimizer() { } } -type torrent struct { +type Torrent struct { InfoHash InfoHash Pieces []piece Data MMapSpan @@ -175,19 +175,19 @@ type torrent struct { 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, @@ -203,7 +203,7 @@ func (t *torrent) pieceChunkSpecs(index int) (cs map[chunkSpec]struct{}) { 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 { @@ -219,7 +219,7 @@ type Peer struct { 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 } @@ -229,11 +229,11 @@ func (t *torrent) PieceSize(piece int) (size int64) { 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 { @@ -246,31 +246,29 @@ func (t *torrent) HashPiece(piece int) (ps pieceSum) { 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()), } @@ -331,7 +329,7 @@ func mmapTorrentData(metaInfo *metainfo.MetaInfo, location string) (mms MMapSpan 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 @@ -340,7 +338,7 @@ func (me *client) torrent(ih InfoHash) *torrent { return nil } -func (me *client) initiateConn(peer Peer, torrent *torrent) { +func (me *Client) initiateConn(peer Peer, torrent *Torrent) { if peer.Id == me.PeerId { return } @@ -363,7 +361,7 @@ func (me *client) initiateConn(peer Peer, torrent *torrent) { }() } -func (me *torrent) haveAnyPieces() bool { +func (me *Torrent) haveAnyPieces() bool { for _, piece := range me.Pieces { if piece.State == pieceStateComplete { return true @@ -372,7 +370,7 @@ func (me *torrent) haveAnyPieces() bool { 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, @@ -438,7 +436,7 @@ func (me *client) handshake(sock net.Conn, torrent *torrent, peerId [20]byte) { }) } -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)) } @@ -449,15 +447,15 @@ func (me *client) peerGotPiece(torrent *torrent, conn *connection, piece int) { } } -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, @@ -539,7 +537,7 @@ func (me *client) runConnection(torrent *torrent, conn *connection) error { } } -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 { @@ -555,7 +553,7 @@ func (me *client) dropConnection(torrent *torrent, conn *connection) { 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 @@ -565,7 +563,7 @@ func (me *client) addConnection(t *torrent, c *connection) bool { 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 { @@ -578,7 +576,7 @@ func (me *client) openNewConns() { } } -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 { @@ -591,8 +589,8 @@ func (me *client) AddPeers(infoHash InfoHash, peers []Peer) (err error) { 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() { @@ -614,18 +612,18 @@ func (me *client) AddTorrent(metaInfo *metainfo.MetaInfo) error { 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 } @@ -652,7 +650,7 @@ func (me *client) replenishConnRequests(torrent *torrent, conn *connection) { } -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 { @@ -683,14 +681,14 @@ func (me *client) pieceHashed(ih InfoHash, piece int, correct bool) { } } -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 { @@ -715,3 +713,15 @@ func (me *client) run() { } } } + +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 +} diff --git a/cmd/torrentfs/main.go b/cmd/torrentfs/main.go new file mode 100644 index 00000000..d71a538b --- /dev/null +++ b/cmd/torrentfs/main.go @@ -0,0 +1,93 @@ +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) +}