public void Apply(DiskDevice dev, ProgressDelegate progress) { byte[] tmp = null; long total = 0, done = 0; foreach (var chg in _WriteOps) { total += chg.SizeInBytes; } foreach (var chg in _WriteOps) { if (tmp == null || tmp.Length < chg.SizeInBytes) { tmp = new byte[chg.SizeInBytes]; } if (chg.Zero) { for (int i = 0; i < chg.SizeInBytes; i++) { tmp[i] = 0; } } else { _File.Seek((long)chg.OffsetInFile, SeekOrigin.Begin); if (_File.Read(tmp, 0, chg.SizeInBytes) != chg.SizeInBytes) { throw new Exception("Failed to read change file at offset " + chg.OffsetInFile); } } dev.SeekAbs((long)chg.OffsetInDevice); if (dev.Write(tmp, chg.SizeInBytes) != chg.SizeInBytes) { throw new Exception("Failed to write change at offset " + chg.OffsetInDevice); } done += chg.SizeInBytes; if (progress != null) { progress(done, total); } } }
bool AttemptWrite(ThreadContext ctx, DiskDevice dev) { long totalSize = ctx.fs.Length, done = 0; const int bufferSize = 1024 * 1024; IntPtr buffer = IntPtr.Zero; var start = DateTime.Now; try { dev.SeekAbs(0); byte[] firstSector = null; //The very first sector will be written in the very end. Otherwise the partition driver might block us from writing the raw disk object. buffer = Marshal.AllocCoTaskMem(bufferSize); while (done < totalSize) { if (_AbortWriting) { throw new OperationCanceledException(); } int todo = (int)Math.Min(bufferSize, totalSize - done), cdone; if (!DeviceControl.ReadFile(ctx.fs.SafeFileHandle, buffer, todo, out cdone, IntPtr.Zero) || cdone != todo) { if (!AskRetry("Cannot read image file at offset " + done.ToString())) { throw new OperationCanceledException(); } else { return(false); } } if (firstSector == null) { firstSector = new byte[SectorSize]; Marshal.Copy(buffer, firstSector, 0, SectorSize); Marshal.Copy(new byte[SectorSize], 0, buffer, SectorSize); } if ((todo % SectorSize) != 0) { //If the image file is not aligned to the sector boundary, the device write would fail unless we manually align it todo = ((todo + SectorSize - 1) / SectorSize) * SectorSize; } if (dev.Write(buffer, todo) != todo) { if (!AskRetry("Cannot write medium at offset " + done.ToString())) { throw new OperationCanceledException(); } else { return(false); } } dev.SeekRel(todo); string statusText; long msec = (long)(DateTime.Now - start).TotalMilliseconds; if (msec < 5000) { statusText = string.Format("Writing ({0}B done)...", StringHelpers.FormatByteCount(done)); } else { long bps = (done * 1000) / msec; long bytesLeft = totalSize - done; if (bps == 0) { bps = 1; } int secEstimated = (int)(bytesLeft / bps); statusText = string.Format("Writing ({0}B done, {1}B/s, {2}:{3:d2} remaining)...", StringHelpers.FormatByteCount(done), StringHelpers.FormatByteCount(bps), secEstimated / 60, secEstimated % 60); } UpdateProgress(statusText, done, totalSize); done += todo; } if (firstSector == null) { throw new Exception("First sector not cached"); } if (ctx.ResizedPartition.HasValue) { string resizeDir = Path.Combine(Path.GetTempPath(), "resize2fs." + Process.GetCurrentProcess().Id); Directory.CreateDirectory(resizeDir); string resizer = Path.Combine(resizeDir, "resize2fs.exe"); using (var s = Assembly.GetExecutingAssembly().GetManifestResourceStream("WinFlashTool.resize2fs.exe")) { byte[] data = new byte[s.Length]; s.Read(data, 0, data.Length); File.WriteAllBytes(resizer, data); } var devLength = dev.QueryLength().Length; if ((ctx.ResizedPartition.Value.StartingLBA + ctx.ResizedPartition.Value.TotalSectorCount) * 512UL > devLength) { throw new ResizeSkippedException("Image is too small"); } var pt = ParsedChangeFile.ReadPartitionTable(firstSector); int partitionNumber = -1; for (int i = 0; i < pt.Length; i++) { if (pt[i].Equals(ctx.ResizedPartition.Value)) { partitionNumber = i; break; } } if (partitionNumber == -1) { throw new ResizeSkippedException("Matching partition not found in image"); } ulong newSizeInBytes = devLength - ctx.ResizedPartition.Value.StartingLBA * 512UL; int offsetInBootSector = 0x1BE + partitionNumber * 0x10 + 0x0c; if (BitConverter.ToUInt32(firstSector, offsetInBootSector) != ctx.ResizedPartition.Value.TotalSectorCount) { throw new ResizeSkippedException("Internal error: wrong partition table offset"); } BitConverter.GetBytes((int)(newSizeInBytes / 512)).CopyTo(firstSector, offsetInBootSector); UpdateProgress("Resizing file system...", 0, 0); var info = new ProcessStartInfo { FileName = resizer, Arguments = string.Format("-f \"{0}@{1}/{2}\"", ctx.FileName, ctx.ResizedPartition.Value.StartingLBA * 512L, newSizeInBytes), CreateNoWindow = true, UseShellExecute = false, }; info.EnvironmentVariables["RESIZE2FS_CHANGE_FILE_DIR"] = resizeDir; string chg = Path.Combine(resizeDir, Path.GetFileName(ctx.FileName) + ".chg"); var proc = Process.Start(info); proc.WaitForExit(); if (proc.ExitCode != 0) { throw new ResizeSkippedException("Failed to resize Ext2FS - exit code " + proc.ExitCode); } if (!File.Exists(chg)) { throw new ResizeSkippedException("Resize change file does not exist: " + chg); } UpdateProgress("Writing resized file system...", 0, 0); using (var chf = new ParsedChangeFile(chg, ctx.ResizedPartition.Value.StartingLBA * 512, devLength)) { chf.Apply(dev, (d, t) => UpdateProgress("Writing resized file system...", d, t)); } try { Directory.Delete(resizeDir, true); } catch { } } dev.SeekAbs(0); dev.Write(firstSector); } finally { if (buffer != IntPtr.Zero) { Marshal.FreeCoTaskMem(buffer); } } return(true); }