Start implementing torrentfs binary

This commit is contained in:
Matt Joiner 2013-10-06 18:01:39 +11:00
parent 5dbd675be6
commit 68e189d477
2 changed files with 141 additions and 38 deletions

View File

@ -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
}

93
cmd/torrentfs/main.go Normal file
View File

@ -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)
}