diff --git a/DiskImager.Installer/DiskImager.Installer.vdproj b/DiskImager.Installer/DiskImager.Installer.vdproj
index fefc81b..b28bdef 100644
--- a/DiskImager.Installer/DiskImager.Installer.vdproj
+++ b/DiskImager.Installer/DiskImager.Installer.vdproj
@@ -62,6 +62,19 @@
"PrerequisitesLocation" = "2:1"
"Url" = "8:"
"ComponentsUrl" = "8:"
+ "Items"
+ {
+ "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:.NETFramework,Version=v4.0,Profile=Client"
+ {
+ "Name" = "8:Microsoft .NET Framework 4 Client Profile (x86 and x64)"
+ "ProductCode" = "8:.NETFramework,Version=v4.0,Profile=Client"
+ }
+ "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:Microsoft.Windows.Installer.3.1"
+ {
+ "Name" = "8:Windows Installer 3.1"
+ "ProductCode" = "8:Microsoft.Windows.Installer.3.1"
+ }
+ }
}
}
"Release"
@@ -86,6 +99,19 @@
"PrerequisitesLocation" = "2:1"
"Url" = "8:"
"ComponentsUrl" = "8:"
+ "Items"
+ {
+ "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:.NETFramework,Version=v4.0,Profile=Client"
+ {
+ "Name" = "8:Microsoft .NET Framework 4 Client Profile (x86 and x64)"
+ "ProductCode" = "8:.NETFramework,Version=v4.0,Profile=Client"
+ }
+ "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:Microsoft.Windows.Installer.3.1"
+ {
+ "Name" = "8:Windows Installer 3.1"
+ "ProductCode" = "8:Microsoft.Windows.Installer.3.1"
+ }
+ }
}
}
}
@@ -203,15 +229,15 @@
{
"Name" = "8:Microsoft Visual Studio"
"ProductName" = "8:Disk Imager"
- "ProductCode" = "8:{4B773E0E-8A61-4441-9E75-7D48A42C7581}"
- "PackageCode" = "8:{1346B501-A133-428C-9788-B6BD06B7B55B}"
+ "ProductCode" = "8:{E986C6D0-49F6-4FA9-BA4E-C4B25069CB4E}"
+ "PackageCode" = "8:{4D46D4A9-7CFC-46C6-9539-5C2365A79E66}"
"UpgradeCode" = "8:{A2F957D8-23F6-44DB-A22C-CCA8275E1FCE}"
"AspNetVersion" = "8:4.0.30319.0"
"RestartWWWService" = "11:FALSE"
"RemovePreviousVersions" = "11:FALSE"
"DetectNewerInstalledVersion" = "11:TRUE"
"InstallAllUsers" = "11:FALSE"
- "ProductVersion" = "8:1.0.1"
+ "ProductVersion" = "8:1.0.2"
"Manufacturer" = "8:Dynamic Devices"
"ARPHELPTELEPHONE" = "8:"
"ARPHELPLINK" = "8:"
@@ -779,6 +805,34 @@
{
}
}
+ "{5259A561-127C-4D43-A0A1-72F10C7B3BF8}:_9B462EF766884DF9B8B000AB7F833D6D"
+ {
+ "SourcePath" = "8:"
+ "TargetName" = "8:"
+ "Tag" = "8:"
+ "Folder" = "8:_072AE4B10D3C471AA2B16D68E3691837"
+ "Condition" = "8:"
+ "Transitive" = "11:FALSE"
+ "Vital" = "11:TRUE"
+ "ReadOnly" = "11:FALSE"
+ "Hidden" = "11:FALSE"
+ "System" = "11:FALSE"
+ "Permanent" = "11:FALSE"
+ "SharedLegacy" = "11:FALSE"
+ "PackageAs" = "3:1"
+ "Register" = "3:1"
+ "Exclude" = "11:FALSE"
+ "IsDependency" = "11:FALSE"
+ "IsolateTo" = "8:"
+ "ProjectOutputGroupRegister" = "3:1"
+ "OutputConfiguration" = "8:"
+ "OutputGroupCanonicalName" = "8:ContentFiles"
+ "OutputProjectGuid" = "8:{4A73C63C-2BF2-4F85-AA55-A5CA581A33B4}"
+ "ShowKeyOutput" = "11:TRUE"
+ "ExcludeFilters"
+ {
+ }
+ }
}
}
}
diff --git a/DiskImager/Disk.cs b/DiskImager/Disk.cs
index 5c56842..c91cf0b 100644
--- a/DiskImager/Disk.cs
+++ b/DiskImager/Disk.cs
@@ -1,7 +1,11 @@
using System;
using System.IO;
+using System.Linq;
using System.Management;
using System.Runtime.InteropServices;
+using ICSharpCode.SharpZipLib.GZip;
+using ICSharpCode.SharpZipLib.Tar;
+using ICSharpCode.SharpZipLib.Zip;
using Microsoft.Win32.SafeHandles;
namespace DynamicDevices.DiskWriter
@@ -23,12 +27,12 @@ namespace DynamicDevices.DiskWriter
///
///
///
+ ///
///
- public bool WriteDrive(string driveLetter, string fileName)
+ public bool WriteDrive(string driveLetter, string fileName, EnumCompressionType eCompType)
{
var success = false;
int intOut;
- long driveSize = 0;
IsCancelling = false;
@@ -52,45 +56,47 @@ namespace DynamicDevices.DiskWriter
"\"} where assocclass=Win32_LogicalDiskToPartition");
searcher = new ManagementObjectSearcher(scope, associators);
var disks = searcher.Get();
- foreach (ManagementObject disk in disks)
+ if (
+ !(from ManagementObject disk in disks select (string) disk["deviceid"]).Any(
+ thisDisk => thisDisk == driveLetter)) continue;
+ diskIndex = (int)(UInt32)current["diskindex"]; ;
+
+ //
+ // Unmount partition (Todo: Note that we currntly only handle unmounting of one partition, which is the usual case for SD Cards)
+ //
+
+ //
+ // Open the volume
+ ///
+ partitionHandle = NativeMethods.CreateFile(@"\\.\" + driveLetter, NativeMethods.GENERIC_READ, NativeMethods.FILE_SHARE_READ, IntPtr.Zero, NativeMethods.OPEN_EXISTING, 0, IntPtr.Zero);
+ if (partitionHandle.IsInvalid)
{
- var thisDisk = (string)disk["deviceid"];
- if (thisDisk == driveLetter)
- {
- // Grab physical drive and size
- diskIndex = (int)(UInt32)current["diskindex"]; ;
+ OnLogMsg(this, @"Failed to open device");
+ partitionHandle.Dispose();
+ return false;
+ }
- // Unmount partition (todo: Note that we currntly only handle unmounting of one partition, which is the usual case for SD Cards)
- partitionHandle = NativeMethods.CreateFile(@"\\.\" + driveLetter, NativeMethods.GENERIC_READ, NativeMethods.FILE_SHARE_READ, IntPtr.Zero, NativeMethods.OPEN_EXISTING, 0, IntPtr.Zero);
- if (partitionHandle.IsInvalid)
- {
- OnLogMsg(this, @"Failed to open device");
-// NativeMethods.CloseHandle(partitionHandle);
- partitionHandle.Dispose();
- return false;
- }
+ //
+ // Lock it
+ //
+ success = NativeMethods.DeviceIoControl(partitionHandle, NativeMethods.FSCTL_LOCK_VOLUME, null, 0, null, 0, out intOut, IntPtr.Zero);
+ if (!success)
+ {
+ OnLogMsg(this, @"Failed to lock device");
+ partitionHandle.Dispose();
+ return false;
+ }
- success = NativeMethods.DeviceIoControl(partitionHandle, NativeMethods.FSCTL_LOCK_VOLUME, null, 0, null, 0, out intOut, IntPtr.Zero);
- if (!success)
- {
- OnLogMsg(this, @"Failed to lock device");
-// NativeMethods.CloseHandle(partitionHandle);
- partitionHandle.Dispose();
- return false;
- }
-
- success = NativeMethods.DeviceIoControl(partitionHandle, NativeMethods.FSCTL_DISMOUNT_VOLUME, null, 0, null, 0, out intOut, IntPtr.Zero);
- if (!success)
- {
- OnLogMsg(this, @"Error dismounting volume: " + Marshal.GetHRForLastWin32Error());
- NativeMethods.DeviceIoControl(partitionHandle, NativeMethods.FSCTL_UNLOCK_VOLUME, null, 0, null, 0, out intOut, IntPtr.Zero);
-// NativeMethods.CloseHandle(partitionHandle);
- partitionHandle.Dispose();
- return false;
- }
-
- break;
- }
+ //
+ // Dismount it
+ //
+ success = NativeMethods.DeviceIoControl(partitionHandle, NativeMethods.FSCTL_DISMOUNT_VOLUME, null, 0, null, 0, out intOut, IntPtr.Zero);
+ if (!success)
+ {
+ OnLogMsg(this, @"Error dismounting volume: " + Marshal.GetHRForLastWin32Error());
+ NativeMethods.DeviceIoControl(partitionHandle, NativeMethods.FSCTL_UNLOCK_VOLUME, null, 0, null, 0, out intOut, IntPtr.Zero);
+ partitionHandle.Dispose();
+ return false;
}
}
@@ -104,6 +110,9 @@ namespace DynamicDevices.DiskWriter
var physicalDrive = @"\\.\PhysicalDrive" + diskIndex;
+ //
+ // Now that we've dismounted the logical volume mounted on the removable drive we can open up the physical disk to write
+ //
var diskHandle = NativeMethods.CreateFile(physicalDrive, NativeMethods.GENERIC_WRITE, 0, IntPtr.Zero, NativeMethods.OPEN_EXISTING, 0, IntPtr.Zero);
if (diskHandle.IsInvalid)
{
@@ -115,13 +124,12 @@ namespace DynamicDevices.DiskWriter
// Get drive size (NOTE: that WMI and IOCTL_DISK_GET_DRIVE_GEOMETRY don't give us the right value so we do it this way)
//
- driveSize = GetDiskSize(diskHandle);
+ var driveSize = GetDiskSize(diskHandle);
success = NativeMethods.DeviceIoControl(diskHandle, NativeMethods.FSCTL_LOCK_VOLUME, null, 0, null, 0, out intOut, IntPtr.Zero);
if (!success)
{
OnLogMsg(this, @"Failed to lock device");
-// NativeMethods.CloseHandle(diskHandle);
diskHandle.Dispose();
return false;
}
@@ -129,28 +137,128 @@ namespace DynamicDevices.DiskWriter
var buffer = new byte[Globals.MaxBufferSize];
long offset = 0;
- using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
+ using (var basefs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
- using (var bw = new BinaryReader(fs))
+
+ Stream fs;
+
+ switch (eCompType)
+ {
+ case EnumCompressionType.Zip:
+ {
+ var zipFile = new ZipFile(basefs);
+
+ Stream zis = null;
+
+ foreach(ZipEntry zipEntry in zipFile)
+ {
+ if (!zipEntry.IsFile)
+ continue;
+
+ zis = zipFile.GetInputStream(zipEntry);
+ break;
+ }
+
+ if(zis == null)
+ {
+ OnLogMsg(this, @"Error reading zip input stream");
+ goto readfail2;
+ }
+
+ fs = zis;
+ }
+ break;
+
+ case EnumCompressionType.Gzip:
+ {
+ var gzis = new GZipInputStream(basefs) {IsStreamOwner = true};
+
+ fs = gzis;
+ }
+ break;
+
+ case EnumCompressionType.Targzip:
+ {
+ var gzos = new GZipInputStream(basefs);
+ gzos.IsStreamOwner = true;
+
+ var tis = new TarInputStream(gzos);
+
+ TarEntry tarEntry;
+ do
+ {
+ tarEntry = tis.GetNextEntry();
+ } while (tarEntry.IsDirectory);
+
+ fs = tis;
+ }
+ break;
+
+ default:
+
+ // No compression - direct to file stream
+ fs = basefs;
+ break;
+ }
+
+ var bufferOffset = 0;
+
+ using (var br = new BinaryReader(fs))
{
while (offset < driveSize && !IsCancelling)
{
- var readBytes = bw.Read(buffer, 0, buffer.Length);
-
+ // Note: There's a problem writing certain lengths to the underlying physical drive.
+ // This appears when we try to read from a compressed stream as it gives us
+ // "strange" lengths which then fail to be written via Writefile() so try to build
+ // up a decent block of bytes here...
+ int readBytes = 0;
+ do
+ {
+ readBytes = br.Read(buffer, bufferOffset, buffer.Length - bufferOffset);
+ bufferOffset += readBytes;
+ } while (bufferOffset < Globals.MaxBufferSize && readBytes != 0);
+
int wroteBytes;
+ var bytesToWrite = bufferOffset;
+ var trailingBytes = 0;
- if (NativeMethods.WriteFile(diskHandle, buffer, readBytes, out wroteBytes, IntPtr.Zero) < 0)
+ // Assume that the underlying physical drive will at least accept powers of two!
+ if(!IsPowerOfTwo((ulong)bufferOffset))
+ {
+ // Find highest bit (32-bit max)
+ var highBit = 31;
+ for (; ((bufferOffset & (1 << highBit)) == 0) && highBit >= 0; highBit--)
+ ;
+
+ // Work out trailing bytes after last power of two
+ var lastPowerOf2 = 1 << highBit;
+
+ bytesToWrite = lastPowerOf2;
+ trailingBytes = bufferOffset - lastPowerOf2;
+ }
+
+ if (NativeMethods.WriteFile(diskHandle, buffer, bytesToWrite, out wroteBytes, IntPtr.Zero) < 0)
{
OnLogMsg(this, @"Error writing data to drive: " + Marshal.GetHRForLastWin32Error());
goto readfail1;
}
- if (wroteBytes != readBytes)
+ if (wroteBytes != bytesToWrite)
{
OnLogMsg(this, @"Error writing data to drive - past EOF?");
goto readfail1;
}
+ // Move trailing bytes up - Todo: Suboptimal
+ if (trailingBytes > 0)
+ {
+ Buffer.BlockCopy(buffer, bufferOffset - trailingBytes, buffer, 0, trailingBytes);
+ bufferOffset = trailingBytes;
+ }
+ else
+ {
+ bufferOffset = 0;
+ }
offset += (uint)wroteBytes;
var percentDone = (int)(100 * offset / driveSize);
@@ -166,23 +274,14 @@ namespace DynamicDevices.DiskWriter
}
}
- success = true;
-
readfail1:
NativeMethods.DeviceIoControl(diskHandle, NativeMethods.FSCTL_UNLOCK_VOLUME, null, 0, null, 0, out intOut, IntPtr.Zero);
- readfail2:
- if (diskHandle != null)
- {
-// NativeMethods.CloseHandle(diskHandle);
- diskHandle.Dispose();
- }
+ readfail2:
+ diskHandle.Dispose();
readfail3:
if (partitionHandle != null)
- {
-// NativeMethods.CloseHandle(partitionHandle);
partitionHandle.Dispose();
- }
var tstotalTime = DateTime.Now.Subtract(dtStart);
@@ -199,12 +298,12 @@ namespace DynamicDevices.DiskWriter
///
///
///
+ ///
///
- public bool ReadDrive(string driveLetter, string fileName)
+ public bool ReadDrive(string driveLetter, string fileName, EnumCompressionType eCompType)
{
var success = false;
int intOut;
- long driveSize = 0;
IsCancelling = false;
@@ -235,11 +334,11 @@ namespace DynamicDevices.DiskWriter
//
// Get drive size (NOTE: that WMI and IOCTL_DISK_GET_DRIVE_GEOMETRY don't give us the right value so we do it this way)
//
- driveSize = GetDiskSize(diskHandle);
+ var driveSize = GetDiskSize(diskHandle);
if(driveSize <= 0)
{
OnLogMsg(this, @"Failed to get device size");
- NativeMethods.CloseHandle(diskHandle);
+ diskHandle.Dispose();
return false;
}
@@ -250,7 +349,7 @@ namespace DynamicDevices.DiskWriter
if (!success)
{
OnLogMsg(this, @"Failed to lock device");
- NativeMethods.CloseHandle(diskHandle);
+ diskHandle.Dispose();
return false;
}
@@ -261,8 +360,75 @@ namespace DynamicDevices.DiskWriter
var buffer = new byte[Globals.MaxBufferSize];
var offset = 0L;
- using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
+
+ using(var basefs = (Stream)new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
+ Stream fs;
+
+ switch (eCompType)
+ {
+ case EnumCompressionType.Zip:
+ {
+ var zfs = new ZipOutputStream(basefs);
+
+ // Default to middle of the range compression
+ zfs.SetLevel(Globals.CompressionLevel);
+
+ var fi = new FileInfo(fileName);
+ var entryName = fi.Name;
+ entryName = entryName.ToLower().Replace(".zip", "");
+ entryName = ZipEntry.CleanName(entryName);
+ var zipEntry = new ZipEntry(entryName) {DateTime = fi.LastWriteTime};
+ zfs.IsStreamOwner = true;
+
+ // Todo: Consider whether size needs setting for older utils ?
+
+ zfs.PutNextEntry(zipEntry);
+
+ fs = zfs;
+ }
+ break;
+
+ case EnumCompressionType.Gzip:
+ {
+ var gzos = new GZipOutputStream(basefs);
+ gzos.SetLevel(Globals.CompressionLevel);
+ gzos.IsStreamOwner = true;
+
+ fs = gzos;
+ }
+ break;
+
+ case EnumCompressionType.Targzip:
+ {
+ var gzos = new GZipOutputStream(basefs);
+ gzos.SetLevel(Globals.CompressionLevel);
+ gzos.IsStreamOwner = true;
+
+ var tos = new TarOutputStream(gzos);
+
+ var fi = new FileInfo(fileName);
+ var entryName = fi.Name;
+ entryName = entryName.ToLower().Replace(".tar.gz", "");
+ entryName = entryName.ToLower().Replace(".tgz", "");
+
+ var tarEntry = TarEntry.CreateTarEntry(entryName);
+ tarEntry.Size = driveSize;
+ tarEntry.ModTime = DateTime.SpecifyKind(fi.LastWriteTime, DateTimeKind.Utc);
+
+ tos.PutNextEntry(tarEntry);
+
+ fs = tos;
+ }
+ break;
+
+ default:
+
+ // No compression - direct to file stream
+ fs = basefs;
+ break;
+ }
+
using (var bw = new BinaryWriter(fs))
{
while (offset < driveSize && !IsCancelling)
@@ -271,13 +437,17 @@ namespace DynamicDevices.DiskWriter
// seem to do a partial read. Deal with this by reading the remaining bytes at the end of the
// drive if necessary
- var readMaxLength = (int)((((ulong)driveSize - (ulong)offset) < (ulong)buffer.Length) ? ((ulong)driveSize - (ulong)offset) : (ulong)buffer.Length);
+ var readMaxLength =
+ (int)
+ ((((ulong) driveSize - (ulong) offset) < (ulong) buffer.Length)
+ ? ((ulong) driveSize - (ulong) offset)
+ : (ulong) buffer.Length);
int readBytes;
if (NativeMethods.ReadFile(diskHandle, buffer, readMaxLength, out readBytes, IntPtr.Zero) < 0)
{
OnLogMsg(this, @"Error reading data from drive: " +
- Marshal.GetHRForLastWin32Error());
+ Marshal.GetHRForLastWin32Error());
goto readfail1;
}
@@ -290,27 +460,32 @@ namespace DynamicDevices.DiskWriter
goto readfail1;
}
- offset += (uint)readBytes;
+ offset += (uint) readBytes;
- var percentDone = (int)(100 * offset / driveSize);
+ var percentDone = (int) (100*offset/driveSize);
var tsElapsed = DateTime.Now.Subtract(dtStart);
- var bytesPerSec = offset / tsElapsed.TotalSeconds;
+ var bytesPerSec = offset/tsElapsed.TotalSeconds;
OnProgress(this, percentDone);
- OnLogMsg(this, @"Read " + percentDone + @"%, " + (offset / (1024 * 1024)) + @" MB / " +
- (driveSize / (1024 * 1024) + " MB, " +
- string.Format("{0:F}", (bytesPerSec / (1024 * 1024))) + @" MB/sec, Elapsed time: " + tsElapsed.ToString(@"dd\.hh\:mm\:ss")));
+ OnLogMsg(this, @"Read " + percentDone + @"%, " + (offset/(1024*1024)) + @" MB / " +
+ (driveSize/(1024*1024) + " MB, " +
+ string.Format("{0:F}", (bytesPerSec/(1024*1024))) + @" MB/sec, Elapsed time: " +
+ tsElapsed.ToString(@"dd\.hh\:mm\:ss")));
}
+
+ // Todo: Do we need this?
+ if(fs is ZipOutputStream)
+ ((ZipOutputStream)fs).CloseEntry();
+ if(fs is TarOutputStream)
+ ((TarOutputStream)fs).CloseEntry();
}
- }
- success = true;
+ }
readfail1:
NativeMethods.DeviceIoControl(diskHandle, NativeMethods.FSCTL_UNLOCK_VOLUME, null, 0, null, 0, out intOut, IntPtr.Zero);
readfail2:
-// NativeMethods.CloseHandle(diskHandle);
diskHandle.Dispose();
readfail3:
var tstotalTime = DateTime.Now.Subtract(dtStart);
@@ -330,7 +505,7 @@ namespace DynamicDevices.DiskWriter
///
///
///
- private int GetDiskIndex(string driveLetter)
+ private static int GetDiskIndex(string driveLetter)
{
int diskIndex = -1;
@@ -346,16 +521,10 @@ namespace DynamicDevices.DiskWriter
"\"} where assocclass=Win32_LogicalDiskToPartition");
searcher = new ManagementObjectSearcher(scope, associators);
var disks = searcher.Get();
- foreach (ManagementObject disk in disks)
- {
- var thisDisk = (string)disk["deviceid"];
- if (thisDisk == driveLetter)
- {
- // Grab physical drive and size
- diskIndex = (int)(UInt32)current["diskindex"]; ;
- break;
- }
- }
+ if (
+ !(from ManagementObject disk in disks select (string) disk["deviceid"]).Any(
+ thisDisk => thisDisk == driveLetter)) continue;
+ diskIndex = (int)(UInt32)current["diskindex"];
}
return diskIndex;
@@ -366,7 +535,7 @@ namespace DynamicDevices.DiskWriter
///
///
///
- private long GetDiskSize(SafeFileHandle diskHandle)
+ private static long GetDiskSize(SafeFileHandle diskHandle)
{
long size = -1;
@@ -387,6 +556,11 @@ namespace DynamicDevices.DiskWriter
return size;
}
+ bool IsPowerOfTwo(ulong x)
+ {
+ return (x != 0) && ((x & (x - 1)) == 0);
+ }
+
#endregion
}
}
\ No newline at end of file
diff --git a/DiskImager/DiskImager.csproj b/DiskImager/DiskImager.csproj
index 2318d65..49d3127 100644
--- a/DiskImager/DiskImager.csproj
+++ b/DiskImager/DiskImager.csproj
@@ -1,4 +1,4 @@
-
+
Debug
@@ -13,6 +13,21 @@
v4.0
Client
512
+ publish\
+ true
+ Disk
+ false
+ Foreground
+ 7
+ Days
+ false
+ false
+ true
+ 0
+ 1.0.0.%2a
+ false
+ false
+ true
x86
@@ -42,6 +57,13 @@
Always
+
+ false
+
+
+
+
+
..\Libs\SharpZipLib\SharpZipLib.dll
@@ -61,6 +83,7 @@
+
Form
@@ -99,6 +122,28 @@
+
+
+ False
+ Microsoft .NET Framework 4 Client Profile %28x86 and x64%29
+ true
+
+
+ False
+ .NET Framework 3.5 SP1 Client Profile
+ false
+
+
+ False
+ .NET Framework 3.5 SP1
+ false
+
+
+ False
+ Windows Installer 3.1
+ true
+
+