Reputation: 15571
I am trying to find a way to clone an active Windows system disk to another disk without forcing the user to take the disk offline, like Clonezilla requires.
I am thinking of Microsoft Volume Shadow Copy Service (VSS) for at least part of the answer. (I am totally open to a better thought/solution.)
Before going on with the code sample, I am a bit hesitant with VSS (.Net interface uses AlphaVSS available through NuGet Package Manager), as two utilities, which use VSS (Shadow Copy and one other tool), did not copy the drive properly. Not all files were copied, especially the sys files in the root properly, and full permissions not copied either. A simple Araxis disk comparison shows the inaccuracy of the copy. Furthermore, I cannot boot off the cloned drive. I get a screen saying that the drive cannot boot and has to be repaired.
The sample is a bit hard to follow. I was expecting to see the inclusion of switches (sub directories vs. not, permissions, etc., etc., etc.)
I did look at dotNetDiskImager, but that is USB to image file and image file to USB disk. Modifying the code, so that one goes drive to dive was a complete failure. One cannot just stupidly copy sectors. One has to know what you are copying and make modifications. It is not a simple task to just copy sectors, hence going back to VSS.
Any better thoughts?
Here is an updated AlphaVSS code, which fixes the compiler errors as a result of AlphaVSS going synchronous and not asynchronous. I also translated the code to C# from VB.
[System.Runtime.InteropServices.DllImport("kernel32.dll", EntryPoint="CopyFileEx", ExactSpelling=false, CharSet=System.Runtime.InteropServices.CharSet.Auto, SetLastError=true)]
private static extern int CopyFileEx(string lpExistingFileName, string lpNewFileName, CopyProgressRoutine_Delegate lpProgressRoutine, Int32 lpData, Int32 lpBool, Int32 dwCopyFlags);
private delegate long CopyProgressRoutine_Delegate(long totalFileSize, long totalBytesTransferred, long streamSize, long streamBytesTransferred, Int32 dwStreamNumber, Int32 dwCallbackReason, Int32 hSourceFile, Int32 hDestinationFile, Int32 lpData);
private CopyProgressRoutine_Delegate m_oCPR;
public void Main()
{
//
// Display Setting
Console.BackgroundColor = ConsoleColor.White;
Console.Clear();
Console.SetWindowSize(90, 30);
Console.SetWindowPosition(0, 0);
Console.ForegroundColor = ConsoleColor.DarkGreen;
// Prevent example from ending if CTL+C is pressed.
Console.TreatControlCAsInput = true;
StartPoint:
Console.Clear();
Console.WriteLine("*******************************************************************");
Console.WriteLine("** **");
Console.WriteLine("** Greetings, **");
Console.WriteLine("** **");
Console.WriteLine("** This small app is only for primary basic Testing **");
Console.WriteLine("** of VSS using Dot.Net Technology **");
Console.WriteLine("** The app is base on the AlphaVSS open project: **");
Console.Write("** ");
Console.ForegroundColor = ConsoleColor.Blue;
Console.Write("http://www.codeplex.com/alphavss");
Console.ForegroundColor = ConsoleColor.DarkGreen;
Console.WriteLine(" **");
Console.WriteLine("** **");
Console.WriteLine("** The application is designed only for internal purposes!! **");
Console.WriteLine("** In no way use on production servers. **");
Console.Write("** ");
Console.ForegroundColor = ConsoleColor.DarkRed;
Console.Write("Any use is at your own risk!!!");
Console.ForegroundColor = ConsoleColor.DarkGreen;
Console.WriteLine(" **");
Console.WriteLine("** **");
Console.WriteLine("** Have Fun, **");
Console.WriteLine("** Ariely Ronen **");
Console.WriteLine("** ____________________________________________________________ **");
Console.WriteLine("** **");
Console.WriteLine("** Do you want to start the TEST **");
Console.WriteLine("** by selecting the file you want to copy? (y/n) **");
Console.WriteLine("** **");
Console.WriteLine("*******************************************************************");
ConsoleKeyInfo MyConsoleKeyInfo = new ConsoleKeyInfo();
MyConsoleKeyInfo = Console.ReadKey();
if (!(MyConsoleKeyInfo.Key.ToString() == "Y"))
{
return;
}
SelectFileToCopy:
Console.Clear();
Console.Write("Starting Test Ver 0.01");
FolderBrowserDialog sDestFolder = new FolderBrowserDialog();
string sDestFolderPath = null;
sDestFolder.Description = "Select a destination folder (where the file will be copy to).";
// Do not show the button for new folder
sDestFolder.ShowNewFolderButton = false;
DialogResult dlgResult = sDestFolder.ShowDialog();
if (dlgResult == Windows.Forms.DialogResult.OK)
{
sDestFolderPath = sDestFolder.SelectedPath;
}
else
{
sDestFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments).ToString();
}
OpenFileDialog oOFD = new OpenFileDialog();
string sFileName = null;
// copy progress handler
// we add the function "CopyProgressRoutine" to the Delegate "CopyProgressRoutine_Delegate"
// so every time the event come the function run
m_oCPR = new CopyProgressRoutine_Delegate(CopyProgressRoutine);
oOFD.ValidateNames = false;
oOFD.Title = "Select an in-use file you would like to copy.";
oOFD.Filter = "All files (*.*)|*.*";
if (oOFD.ShowDialog == DialogResult.OK)
{
sFileName = oOFD.FileName;
Console.Clear();
Console.WriteLine("File to Copy: ");
Console.WriteLine(sFileName);
Console.WriteLine();
Console.WriteLine("The file will be copy to:");
Console.WriteLine(sDestFolderPath);
Console.WriteLine();
Console.WriteLine("Press any key to continue.");
Console.ReadKey();
Console.Clear();
// Lets Start to copy
PerformVSSCopy(sFileName, sDestFolderPath);
}
Console.WriteLine();
Console.WriteLine("Press 1 to go to Start Point,2 to go to Select new File To Copy, any other key to exit.");
MyConsoleKeyInfo = Console.ReadKey();
switch (MyConsoleKeyInfo.Key.ToString())
{
case "D1":
goto StartPoint;
case "D2":
goto SelectFileToCopy;
default:
break;
}
}
private Int32 CopyProgressRoutine(long totalFileSize, long totalBytesTransferred, long streamSize, long streamBytesTransferred, Int32 dwStreamNumber, Int32 dwCallbackReason, Int32 hSourceFile, Int32 hDestinationFile, Int32 lpData)
{
double dPerc = 100;
if (totalFileSize != 0)
{
dPerc = (totalBytesTransferred / (double)totalFileSize) * 100;
}
Console.CursorLeft = 0;
Console.Write("Copied " + totalBytesTransferred + " of " + totalFileSize + " [" + dPerc.ToString("0.0") + "%]");
return 0;
}
private void PerformVSSCopy(string sFilename, string sDestFolder)
{
// Based on the documention from
// http://msdn.microsoft.com/en-us/library/aa384589(VS.85).aspx
// and the help of those on
// http://www.codeplex.com/alphavss
// The parts I have marked and INFO ONLY are not required for the
// VSS Copy to work, they just give insight into the process
Alphaleonis.Win32.Vss.IVssImplementation oVSSImpl = null;
Alphaleonis.Win32.Vss.IVssBackupComponents oVSS = null;
string sVolume = null;
IO.FileInfo oFI = new IO.FileInfo(sFilename);
Guid gSnapshot = new Guid();
Guid gSnapshotSet = new Guid();
string sVSSFile = null;
string sDestFile = null;
Alphaleonis.Win32.Vss.VssSnapshotProperties oProps = null;
try
{
// Load the Implementation specifi for this machine i.e Windowx XP , Vista, 32 or 64 Bit
oVSSImpl = Alphaleonis.Win32.Vss.VssUtils.LoadImplementation();
// This is the main man for VSS Backups.
Console.WriteLine("Initializing VssBackupComponents Object");
oVSS = oVSSImpl.CreateVssBackupComponents;
oVSS.InitializeForBackup(null);
// Tell VSS that we are requesting a backup with particular options
Console.WriteLine("Setting Backup State");
oVSS.SetBackupState(false, true, Alphaleonis.Win32.Vss.VssBackupType.Full, false);
// Tell all VSS Writers that we want their MetaData. We wait until all Writers have responded.
Console.WriteLine("Gat Writers Metadata");
oVSS.GatherWriterMetadata();
// INFO ONLY : Enumerate who responded to our GatherWriterMetadata request
// Create the Snapshot Set that we will place all our Snapshotted volumes into. (even tho here will only snapshot one volume)
Console.WriteLine("Starting Snapshot Set");
gSnapshotSet = oVSS.StartSnapshotSet();
// Add a Snapshot for the required Volume
sVolume = oFI.Directory.Root.Name;
Console.WriteLine("Add To Snapshot Set the Volume: " + sVolume);
gSnapshot = oVSS.AddToSnapshotSet(sVolume, Guid.Empty);
// Notify all VSS Writers that the backup is about to start, we wait untuil they have indicated they are ready.
Console.WriteLine("VSS Writers Prepare For Backup .");
oVSS.PrepareForBackup();
//Request that the Snapshot are created and wait until they are ready.
Console.WriteLine("Do Snapshot Set [this can take some time]");
oVSS.DoSnapshotSet();
// --------------------------------------------------------------
// --------------------------------------------------------------
// Now we can start copying the file.
// --------------------------------------------------------------
// We need the properties to tell us how to access our files in the snapshot
oProps = oVSS.GetSnapshotProperties(gSnapshot);
sVSSFile = sFilename.Replace(sVolume, oProps.SnapshotDeviceObject + "\\");
sDestFile = IO.Path.Combine(sDestFolder, IO.Path.GetFileName(sFilename));
// INFO ONLY Lets check the Properties of the snapshot set
// Copy, but the normal .NET routines will not work here. Back to the API !!!
Console.WriteLine("Copying file using API Routines.");
Console.WriteLine();
CopyFileEx(sVSSFile, sDestFile, m_oCPR, 0, 0, 0);
Console.WriteLine();
Console.WriteLine("Done !");
// --------------------------------------------------------------
// --------------------------------------------------------------
// Release the snapshot set, we could also just call Dispose on the VssBackupComponents object
// For clarity, I'll do both here.
Console.WriteLine("Deleting Snapshot Set.");
oVSS.DeleteSnapshotSet(gSnapshotSet, true);
}
catch (Exception ex)
{
Console.WriteLine();
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
Console.WriteLine();
}
finally
{
oVSS.Dispose();
}
}
In short, I would like to take a live system disk, which usually has the "System Reserved", "main", and recovery partitions, and make a live clone to another disk in the system.
I would ensure that the new disk has sufficient space, is already partitioned and formatted to 3 NTFS partitions.
Upvotes: 1
Views: 1984