diff --git a/CKAN/CKAN/ModuleInstaller.cs b/CKAN/CKAN/ModuleInstaller.cs
index cf6d02adf1..f3608aa63a 100644
--- a/CKAN/CKAN/ModuleInstaller.cs
+++ b/CKAN/CKAN/ModuleInstaller.cs
@@ -6,6 +6,7 @@
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using System.Threading;
+using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.Zip;
using System.Transactions;
using ChinhDo.Transactions;
@@ -755,11 +756,17 @@ internal static void CopyZipEntry(ZipFile zipfile, ZipEntry entry, string fullPa
file_transaction.CreateDirectory(directory);
}
+ // Snapshot whatever was there before. If there's nothing, this will just
+ // remove our file on rollback.
+ file_transaction.Snapshot(fullPath);
+
// It's a file! Prepare the streams
using (Stream zipStream = zipfile.GetInputStream(entry))
- using (StreamReader reader = new StreamReader(zipStream))
+ using (FileStream writer = File.Create(fullPath))
{
- file_transaction.WriteAllText(fullPath, reader.ReadToEnd());
+ // 4k is the block size on practically every disk and OS.
+ byte[] buffer = new byte[4096];
+ StreamUtils.Copy(zipStream, writer, buffer);
}
}
diff --git a/CKAN/Tests/CKAN/ModuleInstaller.cs b/CKAN/Tests/CKAN/ModuleInstaller.cs
index e3a1ce2d73..4547bce264 100644
--- a/CKAN/Tests/CKAN/ModuleInstaller.cs
+++ b/CKAN/Tests/CKAN/ModuleInstaller.cs
@@ -1,6 +1,7 @@
using NUnit.Framework;
using System;
using System.IO;
+using System.Transactions;
using System.Collections.Generic;
using ICSharpCode.SharpZipLib.Zip;
using CKAN;
@@ -100,14 +101,9 @@ public void No_Installable_Files()
// GH #205, make sure we write in *binary*, not text.
public void BinaryNotText_205()
{
- string dogezip = Tests.TestData.DogeCoinFlagZip();
- ZipFile zipfile = new ZipFile(dogezip);
-
- ZipEntry entry = zipfile.GetEntry("DogeCoinFlag-1.01/GameData/DogeCoinFlag/Flags/dogecoin.png");
- string tmpfile = Path.GetTempFileName();
-
- CKAN.ModuleInstaller.CopyZipEntry(zipfile, entry, tmpfile, false);
-
+ // Use CopyZipEntry (via CopyDogeFromZip) and make sure it
+ // comes out the right size.
+ string tmpfile = CopyDogeFromZip();
long size = new System.IO.FileInfo(tmpfile).Length;
try
@@ -122,6 +118,38 @@ public void BinaryNotText_205()
}
}
+ [Test()]
+ // Make sure when we roll-back a transaction, files written with CopyZipEntry go
+ // back to their pre-transaction state.
+ public void FileSysRollBack()
+ {
+ string file;
+
+ using (var scope = new TransactionScope())
+ {
+ file = CopyDogeFromZip();
+ Assert.IsTrue(new System.IO.FileInfo(file).Length > 0);
+ scope.Dispose(); // Rollback
+ }
+
+ // CopyDogeFromZip creates a tempfile, so we check to make sure it's empty
+ // again on transaction rollback.
+ Assert.AreEqual(0, new System.IO.FileInfo(file).Length);
+ }
+
+ private static string CopyDogeFromZip()
+ {
+ string dogezip = Tests.TestData.DogeCoinFlagZip();
+ ZipFile zipfile = new ZipFile(dogezip);
+
+ ZipEntry entry = zipfile.GetEntry("DogeCoinFlag-1.01/GameData/DogeCoinFlag/Flags/dogecoin.png");
+ string tmpfile = Path.GetTempFileName();
+
+ CKAN.ModuleInstaller.CopyZipEntry(zipfile, entry, tmpfile, false);
+
+ return tmpfile;
+ }
+
private void TestDogeCoinStanza(ModuleInstallDescriptor stanza)
{
Assert.AreEqual("GameData", stanza.install_to);
diff --git a/CKAN/Tests/Tests.csproj b/CKAN/Tests/Tests.csproj
index e560f9fcd0..e70028958a 100644
--- a/CKAN/Tests/Tests.csproj
+++ b/CKAN/Tests/Tests.csproj
@@ -39,6 +39,7 @@
..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll
+