Skip to content

Commit

Permalink
Merge pull request #565 from alanmcgovern/more-fixes-for-empty-files
Browse files Browse the repository at this point in the history
More fixes for empty files
  • Loading branch information
alanmcgovern authored Oct 31, 2022
2 parents 82160bf + 3e04af0 commit 169865c
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 5 deletions.
26 changes: 22 additions & 4 deletions src/MonoTorrent.Client/MonoTorrent.Client.Modes/StartingMode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
using MonoTorrent.BEncoding;
using MonoTorrent.Logging;

using ReusableTasks;

namespace MonoTorrent.Client.Modes
{
class StartingMode : Mode
Expand All @@ -62,6 +64,8 @@ public async Task WaitForStartingToComplete ()
throw new TorrentException ("Torrents with no metadata must use 'MetadataMode', not 'StartingMode'.");

try {
// If the torrent contains any files of length 0, ensure we create them now.
await CreateEmptyFiles ();
await VerifyHashState ();
Cancellation.Token.ThrowIfCancellationRequested ();
Manager.PieceManager.Initialise ();
Expand Down Expand Up @@ -123,6 +127,23 @@ public async Task WaitForStartingToComplete ()
await Manager.LocalPeerAnnounceAsync ();
}

async ReusableTask CreateEmptyFiles ()
{
foreach (var file in Manager.Files) {
if (file.Length == 0) {
var fileInfo = new FileInfo (file.FullPath);
if (fileInfo.Exists && fileInfo.Length == 0)
continue;

await MainLoop.SwitchToThreadpool ();
Directory.CreateDirectory (Path.GetDirectoryName (file.FullPath)!);
// Ensure file on disk is always 0 bytes, as it's supposed to be.
using (var stream = File.OpenWrite (file.FullPath))
stream.SetLength (0);
}
}
}

async void SendAnnounces ()
{
try {
Expand Down Expand Up @@ -156,12 +177,9 @@ async Task TryLoadV2HashesFromCache()
async Task VerifyHashState ()
{
// If we do not have metadata or the torrent needs a hash check, fast exit.
if (!Manager.HasMetadata || !Manager.HashChecked)
if (!Manager.HashChecked)
return;

// FIXME: I should really just ensure that zero length files always exist on disk. If the first file is
// a zero length file and someone deletes it after the first piece has been written to disk, it will
// never be recreated. If the downloaded data requires this file to exist, we have an issue.
foreach (ITorrentManagerFile file in Manager.Files) {
if (!file.BitField.AllFalse && file.Length > 0) {
if (!await DiskManager.CheckFileExistsAsync (file)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -670,10 +670,13 @@ internal void SetMetadata (Torrent torrent)
}

var currentPath = File.Exists (downloadCompleteFullPath) ? downloadCompleteFullPath : downloadIncompleteFullPath;
return new TorrentFileInfo (file, currentPath) {
var torrentFileInfo = new TorrentFileInfo (file, currentPath) {
DownloadCompleteFullPath = downloadCompleteFullPath,
DownloadIncompleteFullPath = downloadIncompleteFullPath
};
if (file.Length == 0)
torrentFileInfo.BitField[0] = true;
return torrentFileInfo;
}).Cast<ITorrentManagerFile> ().ToList ().AsReadOnly ();

PieceHashes = Torrent.CreatePieceHashes ();
Expand Down Expand Up @@ -941,6 +944,9 @@ internal void OnPieceHashed (int index, bool hashPassed, int piecesHashed, int t
var files = Files;
var fileIndex = files.FindFileByPieceIndex (index);
for (int i = fileIndex; i < files.Count && files[i].StartPieceIndex <= index; i++) {
// Empty files always have all bits set to 'true' as they're treated as being downloaded as soon as they exist on disk.
if (files[i].Length == 0)
continue;
((TorrentFileInfo) files[i]).BitField[index - files[i].StartPieceIndex] = hashPassed;

// If we're only hashing 1 piece then we can start moving files now. This occurs when a torrent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,28 @@ public async Task SaveRestoreState_OneTorrentFile_NoContainingDirectory ()
}
}

[Test]
public async Task StartAsyncAlwaysCreatesEmptyFiles ()
{
var files = TorrentFile.Create (Constants.BlockSize * 4, 0, 1, 2, 3);
using var writer = new TestWriter ();
using var rig = TestRig.CreateMultiFile (files, Constants.BlockSize * 4, writer);

for (int i = 0; i < 2; i++) {
var downloadingState = rig.Manager.WaitForState (TorrentState.Downloading);
var stoppedState = rig.Manager.WaitForState (TorrentState.Stopped);

await rig.Manager.StartAsync ();
Assert.IsTrue (downloadingState.Wait (5000), "Started");
Assert.IsTrue (File.Exists (rig.Manager.Files[0].FullPath));
Assert.IsTrue (rig.Manager.Files[0].BitField.AllTrue);

await rig.Manager.StopAsync ();
Assert.IsTrue (stoppedState.Wait (5000), "Stopped");
File.Delete (rig.Manager.Files[0].FullPath);
}
}

[Test]
public async Task StopTest ()
{
Expand Down

0 comments on commit 169865c

Please sign in to comment.