cmd/torrent2/main.go | 21 +++++++++++++++++++++ merkle/hash.go | 9 +++++++-- metainfo/file-tree.go | 20 ++++++++++++++++---- metainfo/fileinfo.go | 3 ++- metainfo/info.go | 2 +- metainfo/piece.go | 2 +- segments/segments.go | 14 +++----------- spec.go | 8 ++++---- torrent.go | 6 +++++- diff --git a/cmd/torrent2/main.go b/cmd/torrent2/main.go index 0d57f0133bbd7149f52d08c9e802692c92e609a6..6017464ff2ba95e4a38f83426a3981884269e985 100644 --- a/cmd/torrent2/main.go +++ b/cmd/torrent2/main.go @@ -4,6 +4,7 @@ // here. package main import ( + "fmt" "os" "github.com/anacrolix/torrent/metainfo" @@ -38,6 +39,26 @@ bail("not a v2 torrent") } err = metainfo.ValidatePieceLayers(mi.PieceLayers, &info.FileTree, info.PieceLength) assertOk(err) + }, + "pprint": func() { + mi, err := metainfo.LoadFromFile(args[2]) + assertOk(err) + info, err := mi.UnmarshalInfo() + assertOk(err) + files := info.UpvertedFiles() + pieceIndex := 0 + for _, f := range files { + numPieces := int((f.Length + info.PieceLength - 1) / info.PieceLength) + endIndex := pieceIndex + numPieces + fmt.Printf( + "%x: %q: pieces (%v-%v)\n", + f.PiecesRoot.Unwrap(), + f.BestPath(), + pieceIndex, + endIndex-1, + ) + pieceIndex = endIndex + } }, }[args[1]]() }, diff --git a/merkle/hash.go b/merkle/hash.go index 1a8f67a92288a243a772892bf88813620e3bb261..5984547aab981982fbfbce19a49c03cf4420fbe6 100644 --- a/merkle/hash.go +++ b/merkle/hash.go @@ -3,12 +3,14 @@ import ( "crypto/sha256" "hash" + "unsafe" ) func NewHash() *Hash { - return &Hash{ + h := &Hash{ nextBlock: sha256.New(), } + return h } type Hash struct { @@ -41,7 +43,9 @@ return } func (h *Hash) nextBlockSum() (sum [32]byte) { - h.nextBlock.Sum(sum[:0]) + if unsafe.SliceData(h.nextBlock.Sum(sum[:0])) != unsafe.SliceData(sum[:]) { + panic("go sux") + } return } @@ -57,6 +61,7 @@ func (h *Hash) Reset() { h.blocks = h.blocks[:0] h.nextBlock.Reset() + h.written = 0 } func (h *Hash) Size() int { diff --git a/metainfo/file-tree.go b/metainfo/file-tree.go index bfb7229e9a8d1691f6d7edd9bf72ce31894397b2..0d7e1caf3b485610ca62a7646b08b1946651d97d 100644 --- a/metainfo/file-tree.go +++ b/metainfo/file-tree.go @@ -64,23 +64,35 @@ sort.Strings(keys) return keys } -func (ft *FileTree) UpvertedFiles(path []string, out func(fi FileInfo)) { +func (ft *FileTree) upvertedFiles(pieceLength int64, out func(fi FileInfo)) { + var offset int64 + ft.upvertedFilesInner(pieceLength, nil, &offset, out) +} + +func (ft *FileTree) upvertedFilesInner( + pieceLength int64, + path []string, + offset *int64, + out func(fi FileInfo), +) { if ft.IsDir() { for _, key := range ft.orderedKeys() { if key == FileTreePropertiesKey { continue } sub := g.MapMustGet(ft.Dir, key) - sub.UpvertedFiles(append(path, key), out) + sub.upvertedFilesInner(pieceLength, append(path, key), offset, out) } } else { out(FileInfo{ Length: ft.File.Length, Path: append([]string(nil), path...), // BEP 52 requires paths be UTF-8 if possible. - PathUtf8: append([]string(nil), path...), - PiecesRoot: ft.PiecesRootAsByteArray(), + PathUtf8: append([]string(nil), path...), + PiecesRoot: ft.PiecesRootAsByteArray(), + TorrentOffset: *offset, }) + *offset += (ft.File.Length + pieceLength - 1) / pieceLength * pieceLength } } diff --git a/metainfo/fileinfo.go b/metainfo/fileinfo.go index 66ee2da8b808f66f6c8916de657b80c1a904b63a..25c262529b5b94aa13ecd41075c7244ada081cd6 100644 --- a/metainfo/fileinfo.go +++ b/metainfo/fileinfo.go @@ -18,7 +18,8 @@ ExtendedFileAttrs // BEP 52. This isn't encoded in a v1 FileInfo, but is exposed here for APIs that expect to deal // v1 files. - PiecesRoot g.Option[[32]byte] `bencode:"-"` + PiecesRoot g.Option[[32]byte] `bencode:"-"` + TorrentOffset int64 `bencode:"-"` } func (fi *FileInfo) DisplayPath(info *Info) string { diff --git a/metainfo/info.go b/metainfo/info.go index d1b54d44d61a93e8087fe53641dd886a0b5739bb..8b8749c7c13219b257be72257464771d652cf167 100644 --- a/metainfo/info.go +++ b/metainfo/info.go @@ -155,7 +155,7 @@ // dict if necessary. This is a helper to avoid having to conditionally handle // single and multi-file torrent infos. func (info *Info) UpvertedFiles() (files []FileInfo) { if info.HasV2() { - info.FileTree.UpvertedFiles(nil, func(fi FileInfo) { + info.FileTree.upvertedFiles(info.PieceLength, func(fi FileInfo) { files = append(files, fi) }) return diff --git a/metainfo/piece.go b/metainfo/piece.go index c7377f5db643b9e372010c7e7f39043316495a24..4972e529267e4f2bb3901dbb5c41ecb49fac6b93 100644 --- a/metainfo/piece.go +++ b/metainfo/piece.go @@ -13,7 +13,7 @@ var offset int64 pieceLength := p.Info.PieceLength lastFileEnd := int64(0) done := false - p.Info.FileTree.UpvertedFiles(nil, func(fi FileInfo) { + p.Info.FileTree.upvertedFiles(pieceLength, func(fi FileInfo) { if done { return } diff --git a/segments/segments.go b/segments/segments.go index 90e77ce0d7eb21d98cc4300f5fcfdde856971c29..3870042589446957782e141c958b6aaad7fb0ada 100644 --- a/segments/segments.go +++ b/segments/segments.go @@ -4,16 +4,6 @@ type Int = int64 type Length = Int -func min(i Int, rest ...Int) Int { - ret := i - for _, i := range rest { - if i < ret { - ret = i - } - } - return ret -} - type Extent struct { Start, Length Int } @@ -23,10 +13,12 @@ return e.Start + e.Length } type ( - Callback = func(int, Extent) bool + Callback = func(segmentIndex int, segmentBounds Extent) bool LengthIter = func() (Length, bool) ) +// Returns true if callback returns false early, or all segments in the haystack for the needle are +// found. func Scan(haystack LengthIter, needle Extent, callback Callback) bool { i := 0 for needle.Length != 0 { diff --git a/spec.go b/spec.go index 49a1104edec007bae6c1fc4562fdd04f30c42247..f54b325b5cee9708da43656aade5a21ffb5094dc 100644 --- a/spec.go +++ b/spec.go @@ -71,13 +71,13 @@ info, err := mi.UnmarshalInfo() if err != nil { err = fmt.Errorf("unmarshalling info: %w", err) } - v1Ih := mi.HashInfoBytes() + var v1Ih metainfo.Hash + if info.HasV1() { + v1Ih = mi.HashInfoBytes() + } var v2Infohash g.Option[infohash_v2.T] if info.HasV2() { v2Infohash.Set(infohash_v2.HashBytes(mi.InfoBytes)) - if !info.HasV1() { - v1Ih = *v2Infohash.Value.ToShort() - } } return &TorrentSpec{ diff --git a/torrent.go b/torrent.go index e7b579b3c0e89a38d4bc5fc72cf533b054c56f82..64e8066012ca015c50f32dc52e46c06035fc572b 100644 --- a/torrent.go +++ b/torrent.go @@ -2489,11 +2489,15 @@ } func (t *Torrent) pieceHasher(index pieceIndex) { p := t.piece(index) + // Do we really need to spell out that it's a copy error? If it's a failure to hash the hash + // will just be wrong. correct, failedPeers, copyErr := t.hashPiece(index) switch copyErr { case nil, io.EOF: default: - log.Fmsg("piece %v (%s) hash failure copy error: %v", p, p.hash.HexString(), copyErr).Log(t.logger) + t.logger.Levelf( + log.Warning, + "error hashing piece %v: %v", index, copyErr) } t.storageLock.RUnlock() t.cl.lock()