From 3bbe1c34f0dcd92fb5ba8fa673fd85ffa942952b Mon Sep 17 00:00:00 2001 From: Matt Joiner Date: Tue, 11 Jun 2024 16:21:04 +1000 Subject: [PATCH] Correctly hash hybrid torrents with trailing v1 padding file Fixes #949. --- issue-949_test.go | 27 +++++++++++++++++++++++++++ metainfo/info.go | 11 ++++++++--- metainfo/piece.go | 2 +- testdata/issue-949.torrent | Bin 0 -> 20522 bytes torrent.go | 4 ++-- 5 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 issue-949_test.go create mode 100644 testdata/issue-949.torrent diff --git a/issue-949_test.go b/issue-949_test.go new file mode 100644 index 00000000..f04bd557 --- /dev/null +++ b/issue-949_test.go @@ -0,0 +1,27 @@ +package torrent + +import ( + "github.com/anacrolix/torrent/metainfo" + qt "github.com/frankban/quicktest" + "testing" +) + +func TestIssue949LastPieceZeroPadding(t *testing.T) { + // This torrent has a padding file after the last file listed in the v2 info file tree. + mi, err := metainfo.LoadFromFile("testdata/issue-949.torrent") + if err != nil { + panic(err) + } + info, err := mi.UnmarshalInfo() + if err != nil { + panic(err) + } + lastPiece := info.Piece(info.NumPieces() - 1) + c := qt.New(t) + c.Assert(info.FilesArePieceAligned(), qt.IsTrue) + // Check the v1 piece length includes the trailing padding file. + c.Check(lastPiece.V1Length(), qt.Equals, info.PieceLength) + // The v2 piece should only include the file data, which fits inside the piece length for this + // file. + c.Check(lastPiece.Length(), qt.Equals, int64(3677645)) +} diff --git a/metainfo/info.go b/metainfo/info.go index 3f14b08b..5252e209 100644 --- a/metainfo/info.go +++ b/metainfo/info.go @@ -150,9 +150,8 @@ func (info *Info) IsDir() bool { return len(info.Files) != 0 } -// The files field, converted up from the old single-file in the parent info -// dict if necessary. This is a helper to avoid having to conditionally handle -// single and multi-file torrent infos. +// The files field, converted up from the old single-file in the parent info 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(info.PieceLength, func(fi FileInfo) { @@ -160,6 +159,12 @@ func (info *Info) UpvertedFiles() (files []FileInfo) { }) return } + return info.UpvertedV1Files() +} + +// UpvertedFiles but specific to the files listed in the v1 info fields. This will include padding +// files for example that wouldn't appear in v2 file trees. +func (info *Info) UpvertedV1Files() (files []FileInfo) { if len(info.Files) == 0 { return []FileInfo{{ Length: info.Length, diff --git a/metainfo/piece.go b/metainfo/piece.go index c4101a6d..946e874b 100644 --- a/metainfo/piece.go +++ b/metainfo/piece.go @@ -45,7 +45,7 @@ func (p Piece) V1Length() int64 { case 0 <= i && i < lastPiece: return p.Info.PieceLength case lastPiece >= 0 && i == lastPiece: - files := p.Info.UpvertedFiles() + files := p.Info.UpvertedV1Files() lastFile := files[len(files)-1] length := lastFile.TorrentOffset + lastFile.Length - int64(i)*p.Info.PieceLength if length <= 0 || length > p.Info.PieceLength { diff --git a/testdata/issue-949.torrent b/testdata/issue-949.torrent new file mode 100644 index 0000000000000000000000000000000000000000..7ebf67d6b3fefbae0244da29a53300c07ddf0ac8 GIT binary patch literal 20522 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