]> Sergey Matveev's repositories - btrtrc.git/commitdiff
Rework setting of info bytes
authorMatt Joiner <anacrolix@gmail.com>
Mon, 9 May 2016 05:47:39 +0000 (15:47 +1000)
committerMatt Joiner <anacrolix@gmail.com>
Mon, 9 May 2016 05:47:39 +0000 (15:47 +1000)
client.go
client_test.go
t.go
torrent.go

index 6876f6b13ae0d10e3ab7587f8885991453c106f2..6201f3872ffce120e6ff06a711c8d5f41cc500b3 100644 (file)
--- a/client.go
+++ b/client.go
@@ -1421,16 +1421,6 @@ func (cl *Client) addPeers(t *Torrent, peers []Peer) {
        }
 }
 
-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.
@@ -1444,8 +1434,6 @@ func (cl *Client) newTorrent(ih metainfo.Hash) (t *Torrent) {
                closing:           make(chan struct{}),
                ceasingNetworking: make(chan struct{}),
 
-               gotMetainfo: make(chan struct{}),
-
                halfOpen:          make(map[string]struct{}),
                pieceStateChanges: pubsub.NewPubSub(),
 
@@ -1552,6 +1540,13 @@ func (cl *Client) AddTorrentInfoHash(infoHash metainfo.Hash) (t *Torrent, new bo
        }
        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
 }
 
@@ -1562,35 +1557,24 @@ func (cl *Client) AddTorrentInfoHash(infoHash metainfo.Hash) (t *Torrent, new bo
 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
 }
 
index 2fedb5a952bc52a8db90cfa98b1cd118385bf580..28392e610e02ae15e7974d0b74104a87679c11cf 100644 (file)
@@ -93,7 +93,7 @@ func TestTorrentInitialState(t *testing.T) {
        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)
@@ -487,16 +487,19 @@ func TestCompletedPieceWrongSize(t *testing.T) {
        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()
@@ -835,14 +838,17 @@ func TestPeerInvalidHave(t *testing.T) {
        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)
diff --git a/t.go b/t.go
index 3702e656e3131464be01d51d7763bc4224935f77..afc8feb29e76fd1733ef5732b578ca0fd35b9d2e 100644 (file)
--- a/t.go
+++ b/t.go
@@ -18,7 +18,7 @@ func (t *Torrent) InfoHash() metainfo.Hash {
 // 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.
index d89f25c6dbbbf75efc1b9fe781609b1e636cb774..056f4b4747d0237e7a4169c6f3404e646b1f9b32 100644 (file)
@@ -2,7 +2,6 @@ package torrent
 
 import (
        "container/heap"
-       "crypto/sha1"
        "fmt"
        "io"
        "log"
@@ -84,8 +83,8 @@ type Torrent struct {
        // 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{}
 
@@ -216,27 +215,36 @@ func infoPieceHashes(info *metainfo.Info) (ret []string) {
 }
 
 // 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]
@@ -260,7 +268,7 @@ func (t *Torrent) setMetadata(md *metainfo.Info, infoBytes []byte) (err error) {
                        t.verifyPiece(i)
                }
        }()
-       return
+       return nil
 }
 
 func (t *Torrent) verifyPiece(piece int) {
@@ -1037,25 +1045,9 @@ func (t *Torrent) maybeMetadataCompleted() {
                // 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()
@@ -1106,3 +1098,12 @@ func (t *Torrent) bytesCompleted() int64 {
        }
        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)
+}