Exemplo n.º 1
0
        protected override void DoRun()
        {
            if (!IsAdministrator())
            {
                Console.WriteLine("\nThis utility must be run as an administrator!\n");
                Environment.Exit(1);
            }

            DiskImageBuilder builder = DiskImageBuilder.GetBuilder(OutputDiskType, OutputDiskVariant);
            builder.GenericAdapterType = AdapterType;

            string[] sourceVolume = _volumes.Values;

            uint diskNumber;

            List<CloneVolume> cloneVolumes = GatherVolumes(sourceVolume, out diskNumber);

            if (!Quiet)
            {
                Console.WriteLine("Inspecting Disk...");
            }

            // Construct a stream representing the contents of the cloned disk.
            BiosPartitionedDiskBuilder contentBuilder;
            Geometry biosGeometry;
            Geometry ideGeometry;
            long capacity;
            using (Disk disk = new Disk(diskNumber))
            {
                contentBuilder = new BiosPartitionedDiskBuilder(disk);
                biosGeometry = disk.BiosGeometry;
                ideGeometry = disk.Geometry;
                capacity = disk.Capacity;
            }

            // Preserve the IDE (aka Physical) geometry
            builder.Geometry = ideGeometry;

            // Translate the BIOS (aka Logical) geometry
            GeometryTranslation translation = _translation.EnumValue;
            if (builder.PreservesBiosGeometry && translation == GeometryTranslation.Auto)
            {
                // If the new format preserves BIOS geometry, then take no action if asked for 'auto'
                builder.BiosGeometry = biosGeometry;
                translation = GeometryTranslation.None;
            }
            else
            {
                builder.BiosGeometry = ideGeometry.TranslateToBios(0, translation);
            }

            if (translation != GeometryTranslation.None)
            {
                contentBuilder.UpdateBiosGeometry(builder.BiosGeometry);
            }

            IVssBackupComponents backupCmpnts;
            int status;
            if (Marshal.SizeOf(typeof(IntPtr)) == 4)
            {
                status = NativeMethods.CreateVssBackupComponents(out backupCmpnts);
            }
            else
            {
                status = NativeMethods.CreateVssBackupComponents64(out backupCmpnts);
            }

            Guid snapshotSetId = CreateSnapshotSet(cloneVolumes, backupCmpnts);

            if (!Quiet)
            {
                Console.Write("Copying Disk...");
            }

            foreach (var sv in cloneVolumes)
            {
                Volume sourceVol = new Volume(sv.SnapshotProperties.SnapshotDeviceObject, sv.SourceExtent.ExtentLength);

                SnapshotStream rawVolStream = new SnapshotStream(sourceVol.Content, Ownership.None);
                rawVolStream.Snapshot();

                byte[] volBitmap;
                int clusterSize;
                using (NtfsFileSystem ntfs = new NtfsFileSystem(rawVolStream))
                {
                    ntfs.NtfsOptions.HideSystemFiles = false;
                    ntfs.NtfsOptions.HideHiddenFiles = false;
                    ntfs.NtfsOptions.HideMetafiles = false;

                    // Remove VSS snapshot files (can be very large)
                    foreach (string filePath in ntfs.GetFiles(@"\System Volume Information", "*{3808876B-C176-4e48-B7AE-04046E6CC752}"))
                    {
                        ntfs.DeleteFile(filePath);
                    }

                    // Remove the page file
                    if (ntfs.FileExists(@"\Pagefile.sys"))
                    {
                        ntfs.DeleteFile(@"\Pagefile.sys");
                    }

                    // Remove the hibernation file
                    if (ntfs.FileExists(@"\hiberfil.sys"))
                    {
                        ntfs.DeleteFile(@"\hiberfil.sys");
                    }

                    using (Stream bitmapStream = ntfs.OpenFile(@"$Bitmap", FileMode.Open))
                    {
                        volBitmap = new byte[bitmapStream.Length];

                        int totalRead = 0;
                        int numRead = bitmapStream.Read(volBitmap, 0, volBitmap.Length - totalRead);
                        while (numRead > 0)
                        {
                            totalRead += numRead;
                            numRead = bitmapStream.Read(volBitmap, totalRead, volBitmap.Length - totalRead);
                        }
                    }

                    clusterSize = (int)ntfs.ClusterSize;

                    if (translation != GeometryTranslation.None)
                    {
                        ntfs.UpdateBiosGeometry(builder.BiosGeometry);
                    }
                }

                List<StreamExtent> extents = new List<StreamExtent>(BitmapToRanges(volBitmap, clusterSize));
                SparseStream partSourceStream = SparseStream.FromStream(rawVolStream, Ownership.None, extents);

                for (int i = 0; i < contentBuilder.PartitionTable.Partitions.Count; ++i)
                {
                    var part = contentBuilder.PartitionTable.Partitions[i];
                    if (part.FirstSector * 512 == sv.SourceExtent.StartingOffset)
                    {
                        contentBuilder.SetPartitionContent(i, partSourceStream);
                    }
                }
            }
            SparseStream contentStream = contentBuilder.Build();

            // Write out the disk images
            string dir = Path.GetDirectoryName(_destDisk.Value);
            string file = Path.GetFileNameWithoutExtension(_destDisk.Value);

            builder.Content = contentStream;
            DiskImageFileSpecification[] fileSpecs = builder.Build(file);

            for (int i = 0; i < fileSpecs.Length; ++i)
            {
                // Construct the destination file path from the directory of the primary file.
                string outputPath = Path.Combine(dir, fileSpecs[i].Name);

                // Force the primary file to the be one from the command-line.
                if (i == 0)
                {
                    outputPath = _destDisk.Value;
                }

                using (SparseStream vhdStream = fileSpecs[i].OpenStream())
                using (FileStream fs = new FileStream(outputPath, FileMode.Create, FileAccess.ReadWrite))
                {
                    StreamPump pump = new StreamPump()
                    {
                        InputStream = vhdStream,
                        OutputStream = fs,
                    };

                    long totalBytes = 0;
                    foreach (var se in vhdStream.Extents)
                    {
                        totalBytes += se.Length;
                    }

                    if (!Quiet)
                    {
                        Console.WriteLine();
                        DateTime now = DateTime.Now;
                        pump.ProgressEvent += (o, e) => { ShowProgress(fileSpecs[i].Name, totalBytes, now, o, e); };
                    }

                    pump.Run();

                    if (!Quiet)
                    {
                        Console.WriteLine();
                    }
                }
            }

            // Complete - tidy up
            CallAsyncMethod(backupCmpnts.BackupComplete);

            long numDeleteFailed;
            Guid deleteFailed;
            backupCmpnts.DeleteSnapshots(snapshotSetId, 2 /*VSS_OBJECT_SNAPSHOT_SET*/, true, out numDeleteFailed, out deleteFailed);

            Marshal.ReleaseComObject(backupCmpnts);
        }
Exemplo n.º 2
0
        private List<CloneVolume> GatherVolumes(string[] sourceVolume, out uint diskNumber)
        {
            diskNumber = uint.MaxValue;

            List<CloneVolume> cloneVolumes = new List<CloneVolume>(sourceVolume.Length);

            if (!Quiet)
            {
                Console.WriteLine("Inspecting Volumes...");
            }

            for (int i = 0; i < sourceVolume.Length; ++i)
            {
                using (Volume vol = new Volume(sourceVolume[i], 0))
                {
                    NativeMethods.DiskExtent[] sourceExtents = vol.GetDiskExtents();
                    if (sourceExtents.Length > 1)
                    {
                        Console.Error.WriteLine("Volume '{0}' is made up of multiple extents, which is not supported", sourceVolume[i]);
                        Environment.Exit(1);
                    }

                    if (diskNumber == uint.MaxValue)
                    {
                        diskNumber = sourceExtents[0].DiskNumber;
                    }
                    else if (diskNumber != sourceExtents[0].DiskNumber)
                    {
                        Console.Error.WriteLine("Specified volumes span multiple disks, which is not supported");
                        Environment.Exit(1);
                    }

                    string volPath = sourceVolume[i];
                    if (volPath[volPath.Length - 1] != '\\')
                    {
                        volPath += @"\";
                    }

                    cloneVolumes.Add(new CloneVolume { Path = volPath, SourceExtent = sourceExtents[0] });
                }
            }

            return cloneVolumes;
        }