}
}
-func (cl *Client) setMetaData(t *Torrent, md *metainfo.Info, bytes []byte) (err error) {
- err = t.setMetadata(md, bytes)
- if err != nil {
- return
- }
- cl.event.Broadcast()
- close(t.gotMetainfo)
- return
-}
-
// Prepare a Torrent without any attachment to a Client. That means we can
// initialize fields all fields that don't require the Client without locking
// it.
closing: make(chan struct{}),
ceasingNetworking: make(chan struct{}),
- gotMetainfo: make(chan struct{}),
-
halfOpen: make(map[string]struct{}),
pieceStateChanges: pubsub.NewPubSub(),
}
new = true
t = cl.newTorrent(infoHash)
+ if !cl.config.DisableTrackers {
+ go cl.announceTorrentTrackers(t)
+ }
+ if cl.dHT != nil {
+ go cl.announceTorrentDHT(t, true)
+ }
+ cl.torrents[infoHash] = t
return
}
func (cl *Client) AddTorrentSpec(spec *TorrentSpec) (t *Torrent, new bool, err error) {
t, new = cl.AddTorrentInfoHash(spec.InfoHash)
- cl.mu.Lock()
- defer cl.mu.Unlock()
-
if spec.DisplayName != "" {
- t.setDisplayName(spec.DisplayName)
+ t.SetDisplayName(spec.DisplayName)
}
- // Try to merge in info we have on the torrent. Any err left will
- // terminate the function.
- if t.info == nil && spec.Info != nil {
- err = cl.setMetaData(t, &spec.Info.Info, spec.Info.Bytes)
- }
- if err != nil {
- return
+
+ if spec.Info != nil {
+ err = t.SetInfoBytes(spec.Info.Bytes)
+ if err != nil {
+ return
+ }
}
+
+ cl.mu.Lock()
+ defer cl.mu.Unlock()
+
t.addTrackers(spec.Trackers)
- cl.torrents[spec.InfoHash] = t
t.maybeNewConns()
- // From this point onwards, we can consider the torrent a part of the
- // client.
- if new {
- if !cl.config.DisableTrackers {
- go cl.announceTorrentTrackers(t)
- }
- if cl.dHT != nil {
- go cl.announceTorrentDHT(t, true)
- }
- }
return
}
tor.storageOpener = storage.NewFile(dir)
// Needed to lock for asynchronous piece verification.
tor.cl = new(Client)
- err := tor.setMetadata(&mi.Info.Info, mi.Info.Bytes)
+ err := tor.setInfoBytes(mi.Info.Bytes)
require.NoError(t, err)
require.Len(t, tor.pieces, 3)
tor.pendAllChunkSpecs(0)
cfg.DefaultStorage = badStorage{}
cl, _ := NewClient(&cfg)
defer cl.Close()
- tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
- Info: &metainfo.InfoEx{
- Info: metainfo.Info{
- PieceLength: 15,
- Pieces: make([]byte, 20),
- Files: []metainfo.FileInfo{
- metainfo.FileInfo{Path: []string{"greeting"}, Length: 13},
- },
+ ie := metainfo.InfoEx{
+ Info: metainfo.Info{
+ PieceLength: 15,
+ Pieces: make([]byte, 20),
+ Files: []metainfo.FileInfo{
+ metainfo.FileInfo{Path: []string{"greeting"}, Length: 13},
},
},
+ }
+ ie.UpdateBytes()
+ tt, new, err := cl.AddTorrentSpec(&TorrentSpec{
+ Info: &ie,
+ InfoHash: ie.Hash(),
})
require.NoError(t, err)
defer tt.Drop()
cl, err := NewClient(&TestingConfig)
require.NoError(t, err)
defer cl.Close()
- tt, _new, err := cl.AddTorrentSpec(&TorrentSpec{
- Info: &metainfo.InfoEx{
- Info: metainfo.Info{
- PieceLength: 1,
- Pieces: make([]byte, 20),
- Files: []metainfo.FileInfo{{Length: 1}},
- },
+ ie := metainfo.InfoEx{
+ Info: metainfo.Info{
+ PieceLength: 1,
+ Pieces: make([]byte, 20),
+ Files: []metainfo.FileInfo{{Length: 1}},
},
+ }
+ ie.UpdateBytes()
+ tt, _new, err := cl.AddTorrentSpec(&TorrentSpec{
+ Info: &ie,
+ InfoHash: ie.Hash(),
})
require.NoError(t, err)
assert.True(t, _new)
// Returns a channel that is closed when the info (.Info()) for the torrent
// has become available.
func (t *Torrent) GotInfo() <-chan struct{} {
- return t.gotMetainfo
+ return t.gotMetainfo.C()
}
// Returns the metainfo info dictionary, or nil if it's not yet available.
import (
"container/heap"
- "crypto/sha1"
"fmt"
"io"
"log"
// received that piece.
metadataCompletedChunks []bool
- // Closed when .Info is set.
- gotMetainfo chan struct{}
+ // Set when .Info is obtained.
+ gotMetainfo missinggo.Event
readers map[*Reader]struct{}
}
// Called when metadata for a torrent becomes available.
-func (t *Torrent) setMetadata(md *metainfo.Info, infoBytes []byte) (err error) {
- err = validateInfo(md)
+func (t *Torrent) setInfoBytes(b []byte) error {
+ if t.haveInfo() {
+ return nil
+ }
+ var ie *metainfo.InfoEx
+ err := bencode.Unmarshal(b, &ie)
if err != nil {
- err = fmt.Errorf("bad info: %s", err)
- return
+ return fmt.Errorf("error unmarshalling info bytes: %s", err)
}
- t.info = &metainfo.InfoEx{
- Info: *md,
- Bytes: infoBytes,
+ if ie.Hash() != t.infoHash {
+ return fmt.Errorf("info bytes have wrong hash")
+ }
+ err = validateInfo(&ie.Info)
+ if err != nil {
+ return fmt.Errorf("bad info: %s", err)
}
+ t.info = ie
+ t.cl.event.Broadcast()
+ t.gotMetainfo.Set()
t.storage, err = t.storageOpener.OpenTorrent(t.info)
if err != nil {
- return
+ return fmt.Errorf("error opening torrent storage: %s", err)
}
t.length = 0
for _, f := range t.info.UpvertedFiles() {
t.length += f.Length
}
- t.metadataBytes = infoBytes
+ t.metadataBytes = b
t.metadataCompletedChunks = nil
- hashes := infoPieceHashes(md)
+ hashes := infoPieceHashes(&t.info.Info)
t.pieces = make([]piece, len(hashes))
for i, hash := range hashes {
piece := &t.pieces[i]
t.verifyPiece(i)
}
}()
- return
+ return nil
}
func (t *Torrent) verifyPiece(piece int) {
// Don't have enough metadata pieces.
return
}
- h := sha1.New()
- h.Write(t.metadataBytes)
- var ih metainfo.Hash
- missinggo.CopyExact(&ih, h.Sum(nil))
- if ih != t.infoHash {
- log.Printf("torrent %q: metadata bytes failed hash check", t)
- t.invalidateMetadata()
- return
- }
- var info metainfo.Info
- err := bencode.Unmarshal(t.metadataBytes, &info)
- if err != nil {
- log.Printf("error unmarshalling metadata: %s", err)
- t.invalidateMetadata()
- return
- }
// TODO(anacrolix): If this fails, I think something harsher should be
// done.
- err = t.cl.setMetaData(t, &info, t.metadataBytes)
+ err := t.setInfoBytes(t.metadataBytes)
if err != nil {
log.Printf("error setting metadata: %s", err)
t.invalidateMetadata()
}
return t.info.TotalLength() - t.bytesLeft()
}
+
+func (t *Torrent) SetInfoBytes(b []byte) (err error) {
+ t.cl.mu.Lock()
+ defer t.cl.mu.Unlock()
+ if t.info != nil {
+ return
+ }
+ return t.setInfoBytes(b)
+}