Start implementing torrentfs binary
This commit is contained in:
parent
5dbd675be6
commit
68e189d477
86
client.go
86
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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
Loading…
Reference in New Issue