Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions src/MonoTorrent.Client/MonoTorrent/Torrent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -484,8 +484,26 @@ void ProcessInfo (BEncodedDictionary dictionary, ref PieceHashesV1? hashesV1, re
if (v1File.Length != v2File.Length)
throw new TorrentException ("Inconsistent hybrid torrent, file length mismatch.");

if (v1File.Padding != v2File.Padding)
throw new TorrentException ("Inconsistent hybrid torrent, file padding mismatch.");
if (v1File.Padding != v2File.Padding) {
// BEP47 says padding is there so the *subsequent* file aligns with a piece start boundary.
// By a literal reading, and in line with the rest of the bittorrent spec, the last file
// can and should be considered the 'end' of the torrent (obviously :p) and so does not
// have a subsequent file, and so does not need padding. Similar to how blocks are requested
// in 16kB chunks, except for the final block which is just wahtever bytes are left over.
//
// Requested a clarification on the BEP. However both variants will need to be supported
// regardless of what the spec says because both are in the wild.
// Issue: https://github.com/bittorrent/bittorrent.org/issues/160
//
// If padding is mandatory for the last file, then remove the code which strips it out
// inside 'LoadTorrentFilesV2'.
if (v1File == v1Files.Last () && v2File == v2Files.Last ()) {
var mutableFiles = v2Files.ToList ();
mutableFiles[v2Files.Count - 1] = new TorrentFile (v2File.Path, v2File.Length, v2File.StartPieceIndex, v2File.EndPieceIndex, v2File.OffsetInTorrent, v2File.PiecesRoot, TorrentFileAttributes.None, v1File.Padding);
v2Files = Array.AsReadOnly (mutableFiles.ToArray ());
} else
throw new TorrentException ("Inconsistent hybrid torrent, file padding mismatch.");
}
}

Files = v2Files;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,74 @@ public async Task HybridTorrentWithPadding ()
Assert.IsTrue (sha256.AsSpan ().SequenceEqual (torrent.CreatePieceHashes ().GetHash (1).V2Hash.Span));
}

[Test]
public void HybridTorrent_FinalFileHasUnexpectedPadding ([Values(true, false)] bool hasFinalFilePadding)
{
// Test validating both variants of torrent can be loaded
//
// https://github.com/bittorrent/bittorrent.org/issues/160
//
var v1Files = new BEncodedList {
new BEncodedDictionary {
{ "length", (BEncodedNumber)9 },
{ "path", new BEncodedList{ (BEncodedString)"file1.txt" } },
},
new BEncodedDictionary {
{ "attr", (BEncodedString) "p"},
{ "length", (BEncodedNumber)32759 },
{ "path", new BEncodedList{ (BEncodedString)".pad32759" } },
},

new BEncodedDictionary {
{ "length", (BEncodedNumber) 14 },
{ "path", new BEncodedList{ (BEncodedString)"file2.txt" } },
}
};

if (hasFinalFilePadding)
v1Files.Add (new BEncodedDictionary {
{ "attr", (BEncodedString) "p" },
{ "length", (BEncodedNumber)32754 },
{ "path", new BEncodedList{ (BEncodedString)".pad32754" } },
});

var v2Files = new BEncodedDictionary {
{ "file1.txt", new BEncodedDictionary {
{"", new BEncodedDictionary {
{ "length" , (BEncodedNumber) 9 },
{ "pieces root", (BEncodedString) Enumerable.Repeat<byte>(0, 32).ToArray () }
} }
} },

{ "file2.txt", new BEncodedDictionary {
{"", new BEncodedDictionary {
{ "length", (BEncodedNumber) 14 },
{ "pieces root", (BEncodedString) Enumerable.Repeat<byte>(1, 32).ToArray () }
} }
} },
};

var infoDict = new BEncodedDictionary {
{"files", v1Files },
{"file tree", v2Files },
{ "meta version", (BEncodedNumber) 2 },
{ "name", (BEncodedString) "padding test"},
{ "piece length", (BEncodedNumber) 32768},
{ "pieces", (BEncodedString) new byte[40] }
};

var dict = new BEncodedDictionary {
{ "info", infoDict }
};

var torrent = Torrent.Load (dict);
Assert.AreEqual (2, torrent.Files.Count);
Assert.AreEqual (9, torrent.Files[0].Length);
Assert.AreEqual (32768 - 9, torrent.Files[0].Padding);
Assert.AreEqual (14, torrent.Files[1].Length);
Assert.AreEqual (hasFinalFilePadding ? 32768 - 14 : 0, torrent.Files[1].Padding);
}

static BEncodedString SHA1SumZeros (long length)
{
using var hasher = IncrementalHash.CreateHash (HashAlgorithmName.SHA1);
Expand Down