Пример #1
0
        public DiskExtent[] GetDiskExtents(SafeHandle hDevice)
        {
            SafeDiskExtentHandle diskPtr = null;

            try {
                diskPtr = new SafeDiskExtentHandle();
                bool success = DeviceIoControl(hDevice, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
                                               IntPtr.Zero, 0, diskPtr, diskPtr.SizeOf, out uint bytesReturns, IntPtr.Zero);
                if (!success || bytesReturns == 0)
                {
                    m_Win32Error = Marshal.GetLastWin32Error();
                    return(null);
                }

                DISK_EXTENT[] diskExtents = diskPtr.ToStructure();
                DiskExtent[]  extents     = new DiskExtent[diskExtents.Length];
                for (int i = 0; i < diskExtents.Length; i++)
                {
                    extents[i] = new DiskExtent()
                    {
                        Device         = string.Format("\\\\.\\PhysicalDrive{0}", diskExtents[i].DeviceNumber),
                        StartingOffset = diskExtents[i].StartingOffset,
                        ExtentLength   = diskExtents[i].ExtentLength
                    };
                }
                return(extents);
            } finally {
                if (diskPtr != null)
                {
                    diskPtr.Close();
                }
            }
        }
Пример #2
0
        private void listDisks_SelectedIndexChanged(object sender, EventArgs e)
        {
            DynamicDisk   dynamicDisk        = (DynamicDisk)listDisks.SelectedValue;
            PrivateHeader privateHeader      = dynamicDisk.PrivateHeader;
            long          publicRegionEndLBA = (long)(privateHeader.PublicRegionStartLBA + privateHeader.PublicRegionSizeLBA);

            numericDiskOffset.Minimum = (long)privateHeader.PublicRegionStartLBA * dynamicDisk.BytesPerSector;
            numericDiskOffset.Maximum = publicRegionEndLBA * dynamicDisk.BytesPerSector - m_extent.Size;
            if (dynamicDisk.Disk != m_extent.Disk)
            {
                DiskExtent allocation = DynamicDiskHelper.FindExtentAllocation(dynamicDisk, m_extent.Size);
                numericDiskOffset.Enabled = (allocation != null);
                btnOK.Enabled             = (allocation != null);
                if (allocation != null)
                {
                    numericDiskOffset.Value    = allocation.FirstSector * allocation.BytesPerSector;
                    m_previousSuffixIndex      = 0;
                    listSuffixes.SelectedIndex = 0;
                    CompactNumericOffset();
                }
            }
            else
            {
                numericDiskOffset.Enabled  = true;
                btnOK.Enabled              = true;
                numericDiskOffset.Value    = m_extent.FirstSector * m_extent.Disk.BytesPerSector;
                m_previousSuffixIndex      = 0;
                listSuffixes.SelectedIndex = 0;
                CompactNumericOffset();
            }
        }
Пример #3
0
        private static void MoveExtentLeft(List<DynamicDisk> disks, DynamicVolume volume, MoveExtentOperationBootRecord resumeRecord, ref long bytesCopied)
        {
            DiskGroupDatabase database = DiskGroupDatabase.ReadFromDisks(disks, volume.DiskGroupGuid);
            if (database == null)
            {
                throw new DatabaseNotFoundException();
            }

            DynamicDiskExtent relocatedExtent = DynamicDiskExtentHelper.GetByExtentID(volume.DynamicExtents, resumeRecord.ExtentID);
            if (resumeRecord.OldStartSector == (ulong)relocatedExtent.FirstSector)
            { 
                // the database update was not completed (this must be a resume operation)
                relocatedExtent = new DynamicDiskExtent(relocatedExtent.Disk, (long)resumeRecord.NewStartSector, relocatedExtent.Size, resumeRecord.ExtentID);
                VolumeManagerDatabaseHelper.UpdateExtentLocation(database, volume, relocatedExtent);
            }

            DiskExtent sourceExtent = new DiskExtent(relocatedExtent.Disk, (long)resumeRecord.OldStartSector, relocatedExtent.Size);

            MoveHelper.MoveExtentDataLeft(volume, sourceExtent, relocatedExtent, resumeRecord, ref bytesCopied);
            
            // if this is a resume, then volume is StripedVolume, otherwise it is a Raid5Volume
            if (resumeRecord.RestoreRAID5)
            {
                VolumeManagerDatabaseHelper.ConvertStripedVolumeToRaid(database, volume.VolumeGuid);
                // get the updated volume (we just reconverted to RAID-5)
                volume = DynamicVolumeHelper.GetVolumeByGuid(disks, volume.VolumeGuid);
            }
            
            // restore the filesystem boot sector
            byte[] filesystemBootRecord = relocatedExtent.Disk.ReadSector((long)resumeRecord.BootRecordBackupSector);
            volume.WriteSectors(0, filesystemBootRecord);

            ClearBackupData(relocatedExtent.Disk, resumeRecord);
        }
        private static void MoveExtentRight(DiskGroupDatabase database, DynamicVolume volume, MoveExtentOperationResumeRecord resumeRecord, ref long bytesCopied)
        {
            DynamicDiskExtent sourceExtent    = GetVolumeExtent(volume, resumeRecord.ExtentID);
            DiskExtent        relocatedExtent = new DiskExtent(sourceExtent.Disk, (long)resumeRecord.NewStartSector, sourceExtent.Size);

            MoveHelper.MoveExtentDataRight(volume, sourceExtent, relocatedExtent, resumeRecord, ref bytesCopied);

            // even if the database update won't complete, the resume record was copied

            // update the database
            DynamicDiskExtent dynamicRelocatedExtent = new DynamicDiskExtent(relocatedExtent, sourceExtent.ExtentID);

            dynamicRelocatedExtent.Name     = sourceExtent.Name;
            dynamicRelocatedExtent.DiskGuid = sourceExtent.DiskGuid;
            VolumeManagerDatabaseHelper.UpdateExtentLocation(database, volume, dynamicRelocatedExtent);

            // if this is a resume, then volume is StripedVolume, otherwise it is a Raid5Volume
            if (resumeRecord.RestoreRAID5)
            {
                VolumeManagerDatabaseHelper.ConvertStripedVolumeToRaid(database, volume.VolumeGuid);
            }
            // get the updated volume (we moved an extent and possibly reconverted to RAID-5)
            volume = DynamicVolumeHelper.GetVolumeByGuid(database.Disks, volume.VolumeGuid);

            // restore the filesystem boot sector
            byte[] filesystemBootRecord = relocatedExtent.Disk.ReadSector((long)resumeRecord.BootRecordBackupSector);
            volume.WriteSectors(0, filesystemBootRecord);

            ClearBackupData(relocatedExtent.Disk, resumeRecord);
        }
Пример #5
0
        public static DiskExtent GetAlignedDiskExtent(DiskExtent extent, long alignInSectors)
        {
            long alignedStartSector = (long)Math.Ceiling((double)extent.FirstSector / alignInSectors) * alignInSectors;
            long lossDueToAlignment = (alignedStartSector - extent.FirstSector) * extent.BytesPerSector;

            return(new DiskExtent(extent.Disk, alignedStartSector, extent.Size - lossDueToAlignment));
        }
        /// <returns>In bytes</returns>
        public static long GetMaxNewExtentLength(DynamicDisk disk, long alignInSectors)
        {
            List <DiskExtent> unallocatedExtents = GetUnallocatedExtents(disk);

            if (unallocatedExtents == null)
            {
                return(-1);
            }

            long result = 0;

            for (int index = 0; index < unallocatedExtents.Count; index++)
            {
                DiskExtent extent = unallocatedExtents[index];
                if (alignInSectors > 1)
                {
                    extent = DiskExtentHelper.GetAlignedDiskExtent(extent, alignInSectors);
                }
                if (extent.Size > result)
                {
                    result = extent.Size;
                }
            }
            return(result);
        }
Пример #7
0
        public static string GetExtentLabel(Volume volume, DiskExtent extent, int width)
        {
            StringBuilder builder = new StringBuilder();

            if (volume != null)
            {
                Guid?volumeGuid = WindowsVolumeHelper.GetWindowsVolumeGuid(volume);
                if (volumeGuid.HasValue)
                {
                    List <string> mountPoints = WindowsVolumeManager.GetMountPoints(volumeGuid.Value);
                    if (mountPoints.Count > 0)
                    {
                        builder.AppendLine(mountPoints[0]);
                    }
                }
            }
            long size = extent.Size;

            if (width <= 60)
            {
                builder.AppendLine(FormattingHelper.GetCompactSizeString(extent.Size));
            }
            else
            {
                builder.AppendLine(FormattingHelper.GetStandardSizeString(extent.Size));
            }
            if (volume != null)
            {
                string statusString = VolumeHelper.GetVolumeStatusString(volume);
                builder.AppendLine(statusString);
            }

            return(builder.ToString());
        }
Пример #8
0
        public static ulong CreateSimpleVolume(DiskGroupDatabase database, DiskExtent extent)
        {
            List <DatabaseRecord> records = new List <DatabaseRecord>();

            VolumeRecord volumeRecord = new VolumeRecord();

            volumeRecord.Id                 = database.AllocateNewRecordID();
            volumeRecord.Name               = GetNextSimpleVolumeName(database.VolumeRecords);
            volumeRecord.VolumeTypeString   = "gen";
            volumeRecord.StateString        = "ACTIVE";
            volumeRecord.ReadPolicy         = ReadPolicyName.Select;
            volumeRecord.VolumeNumber       = GetNextVolumeNumber(database.VolumeRecords);
            volumeRecord.VolumeFlags        = VolumeFlags.Writeback | VolumeFlags.DefaultUnknown;
            volumeRecord.NumberOfComponents = 1;
            volumeRecord.SizeLBA            = (ulong)PublicRegionHelper.TranslateToPublicRegionSizeLBA(extent.TotalSectors, extent.BytesPerSector);
            volumeRecord.PartitionType      = PartitionType.RAW;
            volumeRecord.VolumeGuid         = Guid.NewGuid();
            records.Add(volumeRecord);

            ComponentRecord componentRecord = new ComponentRecord();

            componentRecord.Id                    = database.AllocateNewRecordID();
            componentRecord.Name                  = volumeRecord.Name + "-01";
            componentRecord.StateString           = "ACTIVE";
            componentRecord.ExtentLayout          = ExtentLayoutName.Concatenated;
            componentRecord.NumberOfExtents       = 1;
            componentRecord.VolumeId              = volumeRecord.VolumeId;
            componentRecord.HasStripedExtentsFlag = false;
            componentRecord.NumberOfColumns       = 0;
            records.Add(componentRecord);

            // we should update the disk record
            PrivateHeader privateHeader = PrivateHeader.ReadFromDisk(extent.Disk);
            DiskRecord    diskRecord    = database.FindDiskByDiskGuid(privateHeader.DiskGuid);

            diskRecord = (DiskRecord)diskRecord.Clone();
            records.Add(diskRecord);

            ExtentRecord extentRecord = new ExtentRecord();

            extentRecord.Name          = GetNextExtentName(database.ExtentRecords, diskRecord.Name);
            extentRecord.DiskOffsetLBA = (ulong)PublicRegionHelper.TranslateToPublicRegionLBA(extent.FirstSector, privateHeader);
            extentRecord.SizeLBA       = volumeRecord.SizeLBA;
            extentRecord.ComponentId   = componentRecord.ComponentId;
            extentRecord.DiskId        = diskRecord.DiskId;

            extentRecord.HasColumnIndexFlag = false;

            records.Add(extentRecord);

            database.UpdateDatabase(records);

            return(volumeRecord.VolumeId);
        }
        private void extentPropertiesMenuItem_Click(object sender, EventArgs e)
        {
            Volume     volume = ((KeyValuePair <Volume, DiskExtent>)extentContextMenu.Tag).Key;
            DiskExtent extent = ((KeyValuePair <Volume, DiskExtent>)extentContextMenu.Tag).Value;

            StringBuilder builder = new StringBuilder();

            builder.AppendLine("Volume information:");
            builder.Append(VolumeInfo.GetVolumeInformation(volume));
            builder.AppendLine();

            MessageBox.Show(builder.ToString(), "Volume Properties");
        }
Пример #10
0
        public static void MoveExtentDataRight(Volume volume, DiskExtent sourceExtent, DiskExtent relocatedExtent, MoveExtentOperationBootRecord resumeRecord, ref long bytesCopied)
        {
            // we make sure no data will be overwritten too soon:
            long distanceLBA  = (long)(resumeRecord.NewStartSector - resumeRecord.OldStartSector);
            bool bufferedMode = false;

            if (distanceLBA < BufferedModeThresholdLBA)
            {
                bufferedMode = true;
            }

            int transferSizeLBA;

            if (bufferedMode)
            {
                transferSizeLBA = (int)resumeRecord.BackupBufferSizeLBA;
            }
            else
            {
                transferSizeLBA = (int)Math.Min(Settings.MaximumTransferSizeLBA, distanceLBA);
            }

            // move the data
            for (long readCount = (long)resumeRecord.NumberOfCommittedSectors; readCount < relocatedExtent.TotalSectors; readCount += transferSizeLBA)
            {
                // we read (and write) from the end of the extent and progress to the left
                long sectorsLeft   = relocatedExtent.TotalSectors - readCount;
                int  sectorsToRead = (int)Math.Min(transferSizeLBA, sectorsLeft);

                long sectorIndex = relocatedExtent.TotalSectors - readCount - sectorsToRead;

                byte[] data = sourceExtent.ReadSectors(sectorIndex, sectorsToRead);

                if (bufferedMode)
                {
                    // we write the data to the buffer for recovery purposes
                    relocatedExtent.Disk.WriteSectors((long)resumeRecord.BackupBufferStartSector, data);
                    resumeRecord.RestoreFromBuffer = true;
                    // Note: if the extent we move is the first in the volume, we will write the resume record to
                    // the source extent, which is the one that the database is still referring to
                    volume.WriteSectors(0, resumeRecord.GetBytes());
                }
                relocatedExtent.WriteSectors(sectorIndex, data);

                // update the resume record
                resumeRecord.RestoreFromBuffer         = false;
                resumeRecord.NumberOfCommittedSectors += (ulong)sectorsToRead;
                volume.WriteSectors(0, resumeRecord.GetBytes());
                bytesCopied = (long)resumeRecord.NumberOfCommittedSectors * sourceExtent.BytesPerSector;
            }
        }
        /// <param name="targetOffset">in bytes</param>
        public static bool IsMoveLocationValid(DynamicDiskExtent sourceExtent, DynamicDisk targetDisk, long targetOffset)
        {
            bool isSameDisk = (sourceExtent.Disk == targetDisk.Disk);
            List <DynamicDiskExtent> extents = GetDiskExtents(targetDisk);

            // extents are sorted by first sector
            if (extents == null)
            {
                return(false);
            }

            PrivateHeader privateHeader = targetDisk.PrivateHeader;

            if (sourceExtent.BytesPerSector != targetDisk.BytesPerSector)
            {
                // We must not move an extent to another disk that has different sector size
                return(false);
            }
            if (targetOffset % privateHeader.BytesPerSector > 0)
            {
                return(false);
            }
            long       targetSector = targetOffset / targetDisk.BytesPerSector;
            DiskExtent targetExtent = new DiskExtent(targetDisk.Disk, targetSector, sourceExtent.Size);

            List <DiskExtent> usedExtents = new List <DiskExtent>();

            foreach (DynamicDiskExtent extent in extents)
            {
                if (!isSameDisk || extent.FirstSector != sourceExtent.FirstSector)
                {
                    usedExtents.Add(extent);
                }
            }

            long publicRegionStartSector         = (long)privateHeader.PublicRegionStartLBA;
            long publicRegionSize                = (long)privateHeader.PublicRegionSizeLBA * targetDisk.BytesPerSector;
            List <DiskExtent> unallocatedExtents = DiskExtentsHelper.GetUnallocatedExtents(targetDisk.Disk, publicRegionStartSector, publicRegionSize, usedExtents);

            foreach (DiskExtent extent in unallocatedExtents)
            {
                if (extent.FirstSector <= targetExtent.FirstSector && targetExtent.LastSector <= extent.LastSector)
                {
                    return(true);
                }
            }
            return(false);
        }
        private void createVolumeMenuItem_Click(object sender, EventArgs e)
        {
            DiskExtent extent = (DiskExtent)((KeyValuePair <Volume, DiskExtent>)extentContextMenu.Tag).Value;

            List <DynamicDisk> dynamicDisks = GetDynamicDisks();
            DynamicDisk        disk         = DynamicDisk.ReadFromDisk(extent.Disk);
            List <DynamicDisk> diskGroup    = DynamicDiskHelper.FindDiskGroup(dynamicDisks, disk.DiskGroupGuid);

            CreateVolumeForm createVolume = new CreateVolumeForm(diskGroup, extent);
            DialogResult     result       = createVolume.ShowDialog();

            if (result == DialogResult.OK)
            {
                UpdateView();
            }
        }
Пример #13
0
        public static void MoveExtentDataLeft(Volume volume, DiskExtent sourceExtent, DiskExtent relocatedExtent, MoveExtentOperationBootRecord resumeRecord, ref long bytesCopied)
        {
            // we make sure no data will be overwritten too soon:
            long distanceLBA  = (long)(resumeRecord.OldStartSector - resumeRecord.NewStartSector);
            bool bufferedMode = false;

            if (distanceLBA < BufferedModeThresholdLBA)
            {
                bufferedMode = true;
            }

            int transferSizeLBA;

            if (bufferedMode)
            {
                transferSizeLBA = (int)resumeRecord.BackupBufferSizeLBA;;
            }
            else
            {
                transferSizeLBA = (int)Math.Min(Settings.MaximumTransferSizeLBA, distanceLBA);
            }

            // move the data
            for (long sectorIndex = (long)resumeRecord.NumberOfCommittedSectors; sectorIndex < relocatedExtent.TotalSectors; sectorIndex += transferSizeLBA)
            {
                long sectorsLeft   = relocatedExtent.TotalSectors - sectorIndex;
                int  sectorsToRead = (int)Math.Min(transferSizeLBA, sectorsLeft);

                byte[] data = sourceExtent.ReadSectors(sectorIndex, sectorsToRead);
                if (bufferedMode)
                {
                    // we write the data to the buffer for recovery purposes
                    relocatedExtent.Disk.WriteSectors((long)resumeRecord.BackupBufferStartSector, data);
                    resumeRecord.RestoreFromBuffer = true;
                    relocatedExtent.WriteSectors(0, resumeRecord.GetBytes());
                }
                relocatedExtent.WriteSectors(sectorIndex, data);

                // update the resume record
                resumeRecord.RestoreFromBuffer         = false;
                resumeRecord.NumberOfCommittedSectors += (ulong)sectorsToRead;
                volume.WriteSectors(0, resumeRecord.GetBytes());
                bytesCopied = (long)resumeRecord.NumberOfCommittedSectors * sourceExtent.BytesPerSector;
            }
        }
        /// <param name="allocationLength">In bytes</param>
        /// <param name="alignInSectors">0 or 1 for no alignment</param>
        /// <returns>Allocated DiskExtent or null if there is not enough free disk space</returns>
        public static DiskExtent FindExtentAllocation(DynamicDisk disk, long allocationLength, long alignInSectors)
        {
            List <DiskExtent> unallocatedExtents = DynamicDiskHelper.GetUnallocatedExtents(disk);

            if (unallocatedExtents == null)
            {
                return(null);
            }

            for (int index = 0; index < unallocatedExtents.Count; index++)
            {
                DiskExtent extent = unallocatedExtents[index];
                if (alignInSectors > 1)
                {
                    extent = DiskExtentHelper.GetAlignedDiskExtent(extent, alignInSectors);
                }
                if (extent.Size >= allocationLength)
                {
                    return(new DiskExtent(extent.Disk, extent.FirstSector, allocationLength));
                }
            }
            return(null);
        }
Пример #15
0
        public static void ListExtents()
        {
            if (m_selectedVolume != null)
            {
                Console.WriteLine("Extent ##  ID    Name       Size     DiskID  Offset   Start Sector");
                Console.WriteLine("---------  ----  ---------  -------  ------  -------  ------------");

                for (int index = 0; index < m_selectedVolume.Extents.Count; index++)
                {
                    DiskExtent extent = m_selectedVolume.Extents[index];

                    string extentNumber = index.ToString().PadLeft(2);
                    ulong  extentID     = 0;
                    ulong  diskID       = 0;
                    string name         = String.Empty;

                    if (extent is DynamicDiskExtent)
                    {
                        extentID = ((DynamicDiskExtent)extent).ExtentID;
                        name     = ((DynamicDiskExtent)extent).Name;

                        if (extent.Disk != null)
                        {
                            VolumeManagerDatabase database = VolumeManagerDatabase.ReadFromDisk(extent.Disk);
                            if (database != null)
                            {
                                ExtentRecord extentRecord = database.FindExtentByExtentID(extentID);
                                diskID = extentRecord.DiskId;
                            }
                        }
                    }

                    string offsetString;
                    if (extent.Disk != null)
                    {
                        long offset = extent.FirstSector * extent.Disk.BytesPerSector;
                        offsetString = GetStandardSizeString(offset);
                    }
                    else
                    {
                        offsetString = "    N/A";
                    }
                    long size = extent.Size;

                    name = name.ToString().PadRight(9);

                    string extentIDString = String.Empty;
                    if (extentID != 0)
                    {
                        extentIDString = extentID.ToString();
                    }
                    extentIDString = extentIDString.PadLeft(4);

                    string diskIDString = String.Empty;
                    if (diskID != 0)
                    {
                        diskIDString = diskID.ToString();
                    }
                    diskIDString = diskIDString.PadLeft(6);

                    string startSector = extent.FirstSector.ToString().PadLeft(12);

                    Console.WriteLine("Extent {0}  {1}  {2}  {3}  {4}  {5}  {6}", extentNumber, extentIDString, name, GetStandardSizeString(size), diskIDString, offsetString, startSector);
                }
            }
            else
            {
                Console.WriteLine("No volume has been selected");
            }
        }
Пример #16
0
 public Partition(DiskExtent extent)
 {
     m_extent = extent;
 }
Пример #17
0
 public Partition(Disk disk, long firstSector, long size)
 {
     m_extent = new DiskExtent(disk, firstSector, size);
 }
 public GPTPartition(Guid volumeGuid, Guid typeGuid, string name, DiskExtent extent) : base(extent)
 {
     m_volumeGuid = volumeGuid;
     m_typeGuid   = typeGuid;
 }
Пример #19
0
        public static void AddDiskToRaid5Volume(List <DynamicDisk> disks, Raid5Volume volume, DiskExtent newExtent, ref long bytesCopied)
        {
            DiskGroupDatabase database = DiskGroupDatabase.ReadFromDisks(disks, volume.DiskGroupGuid);

            if (database == null)
            {
                throw new DatabaseNotFoundException();
            }
            // If there will be a power failure during the conversion, our RAID volume will resync during boot,
            // To prevent destruction of the data, we temporarily convert the array to striped volume
            VolumeManagerDatabaseHelper.ConvertRaidToStripedVolume(database, volume.VolumeGuid);
            ulong newExtentID = VolumeManagerDatabaseHelper.AddNewExtentToVolume(database, volume, newExtent);

            // Backup the first sector of the first extent to the last sector of the new extent
            // (We replace the filesystem boot record with our own sector for recovery purposes)
            byte[] filesystemBootRecord = volume.Extents[0].ReadSector(0);
            newExtent.WriteSectors(newExtent.TotalSectors - 1, filesystemBootRecord);

            AddDiskOperationBootRecord resumeRecord = new AddDiskOperationBootRecord();

            resumeRecord.VolumeGuid = volume.VolumeGuid;
            PrivateHeader privateHeader = PrivateHeader.ReadFromDisk(newExtent.Disk);

            // privateHeader cannot be null at this point
            resumeRecord.NumberOfCommittedSectors = 0;

            // we use volume.WriteSectors so that the parity information will be update
            // this way, we could recover the first sector of each extent if a disk will fail
            volume.WriteSectors(0, resumeRecord.GetBytes());

            ResumeAddDiskToRaid5Volume(disks, volume, new DynamicDiskExtent(newExtent, newExtentID), resumeRecord, ref bytesCopied);
        }
Пример #20
0
        public const int BackupBufferSizeLBA = 128; // there are about 180 contiguous free sectors in a private region

        /// <summary>
        /// Move extent to another disk
        /// </summary>
        public static void MoveExtentToAnotherDisk(List<DynamicDisk> disks, DynamicVolume volume, DynamicDiskExtent sourceExtent, DiskExtent relocatedExtent, ref long bytesCopied)
        {
            DiskGroupDatabase database = DiskGroupDatabase.ReadFromDisks(disks, volume.DiskGroupGuid);
            if (database == null)
            {
                throw new DatabaseNotFoundException();
            }

            // copy the data
            long transferSizeLBA = Settings.MaximumTransferSizeLBA;
            for (long sectorIndex = 0; sectorIndex < relocatedExtent.TotalSectors; sectorIndex += transferSizeLBA)
            {
                long sectorsLeft = relocatedExtent.TotalSectors - sectorIndex;
                int sectorsToRead = (int)Math.Min(transferSizeLBA, sectorsLeft);

                byte[] data = sourceExtent.ReadSectors(sectorIndex, sectorsToRead);
                
                relocatedExtent.WriteSectors(sectorIndex, data);

                bytesCopied += sectorsToRead * sourceExtent.BytesPerSector;
            }

            // Update the database to point to the relocated extent
            DynamicDisk targetDisk = DynamicDisk.ReadFromDisk(relocatedExtent.Disk);
            DynamicDiskExtent dynamicRelocatedExtent = new DynamicDiskExtent(relocatedExtent, sourceExtent.ExtentID);
            dynamicRelocatedExtent.Name = sourceExtent.Name;
            dynamicRelocatedExtent.DiskGuid = targetDisk.DiskGuid;
            VolumeManagerDatabaseHelper.UpdateExtentLocation(database, volume, dynamicRelocatedExtent);
        }
Пример #21
0
        /// <summary>
        /// Move extent to a new location on the same disk
        /// </summary>
        public static void MoveExtentWithinSameDisk(List<DynamicDisk> disks, DynamicVolume volume, DynamicDiskExtent sourceExtent, DiskExtent relocatedExtent, ref long bytesCopied)
        {
            DiskGroupDatabase database = DiskGroupDatabase.ReadFromDisks(disks, volume.DiskGroupGuid);
            if (database == null)
            {
                throw new DatabaseNotFoundException();
            }

            MoveExtentOperationBootRecord resumeRecord = new MoveExtentOperationBootRecord();
            // If there will be a power failure during the move, a RAID volume will resync during boot,
            // To prevent destruction of the data, we temporarily convert the array to striped volume
            if (volume is Raid5Volume)
            {
                VolumeManagerDatabaseHelper.ConvertRaidToStripedVolume(database, volume.VolumeGuid);
                resumeRecord.RestoreRAID5 = true;
            }

            // We want to write our own volume boot sector for recovery purposes, so we must find where to backup the old boot sector.
            // We don't want to store the backup in the range of the existing or relocated extent, because then we would have to move
            // the backup around during the move operation, other options include:
            // 1. Store it between sectors 1-62 (cons: Could be in use, Windows occasionally start a volume from sector 1)
            // 2. Find an easily compressible sector (e.g. zero-filled) within the existing extent, overwrite it with the backup, and restore it when the operation is done.
            // 3. use the LDM private region to store the sector.

            DynamicDisk dynamicDisk = DynamicDisk.ReadFromDisk(relocatedExtent.Disk);
            // Note: backupSectorIndex will be from the beginning of the private region while backupBufferStartSector will be from the end
            // so there is no need to allocate them
            long backupSectorIndex = DynamicDiskHelper.FindUnusedSectorInPrivateRegion(dynamicDisk);

            resumeRecord.VolumeGuid = volume.VolumeGuid;
            resumeRecord.NumberOfCommittedSectors = 0;
            resumeRecord.ExtentID = sourceExtent.ExtentID;
            resumeRecord.OldStartSector = (ulong)sourceExtent.FirstSector;
            resumeRecord.NewStartSector = (ulong)relocatedExtent.FirstSector;
            resumeRecord.BootRecordBackupSector = (ulong)backupSectorIndex;

            long distanceLBA = Math.Abs((long)resumeRecord.NewStartSector - (long)resumeRecord.OldStartSector);
            if (distanceLBA < MoveHelper.BufferedModeThresholdLBA)
            {
                long backupBufferStartSector = DynamicDiskHelper.FindUnusedRegionInPrivateRegion(dynamicDisk, BackupBufferSizeLBA);
                if (backupBufferStartSector == -1)
                {
                    throw new Exception("Private region is full");
                }

                if (backupBufferStartSector <= backupSectorIndex)
                {
                    throw new Exception("Private region structure is unknown");
                }
                resumeRecord.BackupBufferStartSector = (ulong)backupBufferStartSector;
                resumeRecord.BackupBufferSizeLBA = BackupBufferSizeLBA;
            }

            // Backup the first sector of the first extent
            // (We replace the filesystem boot record with our own sector for recovery purposes)
            byte[] filesystemBootRecord = volume.ReadSector(0);
            relocatedExtent.Disk.WriteSectors(backupSectorIndex, filesystemBootRecord);

            // we write the resume record instead of the boot record
            volume.WriteSectors(0, resumeRecord.GetBytes());

            if (sourceExtent.FirstSector < relocatedExtent.FirstSector)
            {
                // move right
                MoveExtentRight(disks, volume, resumeRecord, ref bytesCopied);
            }
            else
            { 
                // move left

                // we write the resume record at the new location as well (to be able to resume if a power failure will occur immediately after updating the database)
                relocatedExtent.WriteSectors(0, resumeRecord.GetBytes());
                DynamicDiskExtent dynamicRelocatedExtent = new DynamicDiskExtent(relocatedExtent, sourceExtent.ExtentID);
                dynamicRelocatedExtent.Name = sourceExtent.Name;
                dynamicRelocatedExtent.DiskGuid = sourceExtent.DiskGuid;
                VolumeManagerDatabaseHelper.UpdateExtentLocation(database, volume, dynamicRelocatedExtent);
                int extentIndex = DynamicDiskExtentHelper.GetIndexOfExtentID(volume.DynamicExtents, sourceExtent.ExtentID);
                // get the updated volume (we just moved an extent)
                volume = DynamicVolumeHelper.GetVolumeByGuid(disks, volume.VolumeGuid);
                MoveExtentLeft(disks, volume, resumeRecord, ref bytesCopied);
            }
        }
Пример #22
0
        /// <summary>
        /// Update the database (add the new extent)
        /// </summary>
        /// <param name="volume">RAID-5 or striped volume</param>
        /// <returns>new extent ID</returns>
        public static ulong AddNewExtentToVolume(DiskGroupDatabase database, DynamicVolume volume, DiskExtent newExtent)
        {
            PrivateHeader privateHeader = PrivateHeader.ReadFromDisk(newExtent.Disk);

            List <DatabaseRecord> records = new List <DatabaseRecord>();

            VolumeRecord volumeRecord = database.FindVolumeByVolumeGuid(volume.VolumeGuid);

            if (volumeRecord == null)
            {
                throw new MissingDatabaseRecordException("Volume record is missing");
            }
            volumeRecord          = (VolumeRecord)volumeRecord.Clone();
            volumeRecord.SizeLBA += (ulong)PublicRegionHelper.TranslateToPublicRegionSizeLBA(newExtent.TotalSectors, privateHeader);
            records.Add(volumeRecord);

            ComponentRecord componentRecord = database.FindComponentsByVolumeID(volumeRecord.VolumeId)[0];

            if (componentRecord == null)
            {
                throw new MissingDatabaseRecordException("Component record is missing");
            }
            componentRecord = (ComponentRecord)componentRecord.Clone();
            componentRecord.NumberOfExtents++;
            componentRecord.NumberOfColumns++;
            records.Add(componentRecord);

            DiskRecord diskRecord = database.FindDiskByDiskGuid(privateHeader.DiskGuid);

            if (diskRecord == null)
            {
                throw new MissingDatabaseRecordException("Disk record is missing");
            }
            diskRecord = (DiskRecord)diskRecord.Clone();
            records.Add(diskRecord);

            ExtentRecord newExtentRecord = new ExtentRecord();

            newExtentRecord.Name               = GetNextExtentName(database.ExtentRecords, diskRecord.Name);
            newExtentRecord.ComponentId        = componentRecord.ComponentId;
            newExtentRecord.DiskId             = diskRecord.DiskId;
            newExtentRecord.DiskOffsetLBA      = (ulong)PublicRegionHelper.TranslateToPublicRegionLBA(newExtent.FirstSector, privateHeader);
            newExtentRecord.SizeLBA            = (ulong)PublicRegionHelper.TranslateToPublicRegionSizeLBA(newExtent.TotalSectors, privateHeader);
            newExtentRecord.HasColumnIndexFlag = true;
            newExtentRecord.ColumnIndex        = (uint)volume.Columns.Count; // zero based

            records.Add(newExtentRecord);

            // we should update the disk records and extent records
            foreach (DynamicDiskExtent extent in volume.Extents)
            {
                ExtentRecord extentRecord = database.FindExtentByExtentID(extent.ExtentID);
                if (extentRecord == null)
                {
                    throw new MissingDatabaseRecordException("Extent record is missing");
                }
                extentRecord = (ExtentRecord)extentRecord.Clone();
                records.Add(extentRecord);

                diskRecord = database.FindDiskByDiskID(extentRecord.DiskId);
                if (diskRecord == null)
                {
                    throw new MissingDatabaseRecordException("Disk record is missing");
                }
                // there could be multiple extents on the same disk, make sure we only add each disk once
                if (!records.Contains(diskRecord))
                {
                    diskRecord = (DiskRecord)diskRecord.Clone();
                    records.Add(diskRecord);
                }
            }

            database.UpdateDatabase(records);

            return(newExtentRecord.ExtentId);
        }
Пример #23
0
 public DynamicDiskExtent(DiskExtent diskExtent, ulong extentID) : base(diskExtent.Disk, diskExtent.FirstSector, diskExtent.Size)
 {
     m_extentID = extentID;
 }
Пример #24
0
        public static ulong CreateRAID5Volume(DiskGroupDatabase database, List <DiskExtent> extents, bool isDegraded)
        {
            int numberOfColumns;

            if (isDegraded)
            {
                numberOfColumns = extents.Count + 1;
            }
            else
            {
                numberOfColumns = extents.Count;
            }

            List <DatabaseRecord> records = new List <DatabaseRecord>();

            VolumeRecord volumeRecord = new VolumeRecord();

            volumeRecord.Id                 = database.AllocateNewRecordID();
            volumeRecord.Name               = GetNextRAIDVolumeName(database.VolumeRecords);
            volumeRecord.VolumeTypeString   = "raid5";
            volumeRecord.StateString        = "ACTIVE";
            volumeRecord.ReadPolicy         = ReadPolicyName.RAID;
            volumeRecord.VolumeNumber       = GetNextVolumeNumber(database.VolumeRecords);
            volumeRecord.VolumeFlags        = VolumeFlags.Writeback | VolumeFlags.Writecopy | VolumeFlags.DefaultUnknown;
            volumeRecord.NumberOfComponents = 1;
            volumeRecord.SizeLBA            = (ulong)PublicRegionHelper.TranslateToPublicRegionSizeLBA(extents[0].TotalSectors * (numberOfColumns - 1), extents[0].BytesPerSector);
            volumeRecord.PartitionType      = PartitionType.RAW;
            volumeRecord.VolumeGuid         = Guid.NewGuid();
            records.Add(volumeRecord);

            ComponentRecord componentRecord = new ComponentRecord();

            componentRecord.Id                    = database.AllocateNewRecordID();
            componentRecord.Name                  = volumeRecord.Name + "-01";
            componentRecord.StateString           = "ACTIVE";
            componentRecord.ExtentLayout          = ExtentLayoutName.RAID5;
            componentRecord.NumberOfExtents       = (uint)numberOfColumns;
            componentRecord.VolumeId              = volumeRecord.VolumeId;
            componentRecord.HasStripedExtentsFlag = true;
            componentRecord.StripeSizeLBA         = 128; // 64KB - the default
            componentRecord.NumberOfColumns       = (uint)numberOfColumns;
            records.Add(componentRecord);

            for (int index = 0; index < extents.Count; index++)
            {
                DiskExtent extent = extents[index];

                // we should update the disk records
                PrivateHeader privateHeader = PrivateHeader.ReadFromDisk(extent.Disk);
                DiskRecord    diskRecord    = database.FindDiskByDiskGuid(privateHeader.DiskGuid);
                diskRecord = (DiskRecord)diskRecord.Clone();
                records.Add(diskRecord);

                ExtentRecord extentRecord = new ExtentRecord();
                extentRecord.Name          = GetNextExtentName(database.ExtentRecords, diskRecord.Name);
                extentRecord.DiskOffsetLBA = (ulong)PublicRegionHelper.TranslateToPublicRegionLBA(extent.FirstSector, privateHeader);
                extentRecord.SizeLBA       = (ulong)PublicRegionHelper.TranslateToPublicRegionSizeLBA(extent.TotalSectors, extent.BytesPerSector);
                extentRecord.ComponentId   = componentRecord.ComponentId;
                extentRecord.DiskId        = diskRecord.DiskId;

                extentRecord.HasColumnIndexFlag = (index > 0);
                extentRecord.ColumnIndex        = (uint)index; // zero based

                records.Add(extentRecord);
            }

            if (isDegraded)
            {
                // we have to make-up a disk
                // The DiskFlags and ExtentFlags are not necessary (they will be added later anyway when the disk group is reimported)
                DiskRecord diskRecord = new DiskRecord();
                diskRecord.Id        = database.AllocateNewRecordID();
                diskRecord.Name      = "Miss" + new Random().Next(100);
                diskRecord.DiskGuid  = Guid.NewGuid();
                diskRecord.DiskFlags = DiskFlags.Detached;
                records.Add(diskRecord);

                ExtentRecord extentRecord = new ExtentRecord();
                extentRecord.Name               = diskRecord.Name + "-01";
                extentRecord.ExtentFlags        = ExtentFlags.Recover;
                extentRecord.SizeLBA            = (ulong)PublicRegionHelper.TranslateToPublicRegionSizeLBA(extents[0].TotalSectors, extents[0].BytesPerSector);
                extentRecord.ComponentId        = componentRecord.ComponentId;
                extentRecord.DiskId             = diskRecord.DiskId;
                extentRecord.HasColumnIndexFlag = true;
                extentRecord.ColumnIndex        = (uint)extents.Count; // zero based

                records.Add(extentRecord);
            }

            database.UpdateDatabase(records);

            return(volumeRecord.VolumeId);
        }
        public Volume Volume; // set to null for unallocated extents

        public VisualDiskExtent(int visualDiskIndex, DiskExtent extent, Volume volume)
        {
            VisualDiskIndex = visualDiskIndex;
            Extent          = extent;
            Volume          = volume;
        }
Пример #26
0
        private void btnOK_Click(object sender, EventArgs e)
        {
            DynamicDisk targetDisk = (DynamicDisk)listDisks.SelectedValue;
            long        offset     = GetDiskOffset();
            bool        isSameDisk = (targetDisk.Disk == m_extent.Disk);

            if (!DynamicDiskHelper.IsMoveLocationValid(m_extent, targetDisk, offset))
            {
                StringBuilder builder = new StringBuilder();
                builder.AppendLine("Invalid offset specified.");
                builder.AppendLine();
                builder.AppendLine("The following conditions must be met:");
                builder.AppendLine("1. The destination must reside inside the data portion of the disk.");
                builder.AppendLine("2. The destination must not be used by any other extents.");
                builder.AppendLine("3. The offset must be aligned to sector size.");
                builder.AppendLine("4. Source and destination disk must have the same sector size.");
                MessageBox.Show(builder.ToString(), "Error");
                return;
            }

            if (isSameDisk && offset == m_extent.FirstSector * m_extent.BytesPerSector)
            {
                MessageBox.Show("Source and destination are the same.", "Error");
                return;
            }

            DiskGroupDatabase database = DiskGroupDatabase.ReadFromDisks(m_diskGroup, targetDisk.DiskGroupGuid);

            if (database.AreDisksMissing)
            {
                DialogResult disksMissingResult = MessageBox.Show("Some of the disks in this disk group are missing, Continue anyway?", "Warning", MessageBoxButtons.YesNo);
                if (disksMissingResult != DialogResult.Yes)
                {
                    return;
                }
            }

            DiskGroupLockResult result = DiskGroupHelper.LockDiskGroup(m_diskGroup);

            if (result == DiskGroupLockResult.CannotLockDisk)
            {
                MessageBox.Show("Unable to lock all disks!", "Error");
            }
            else if (result == DiskGroupLockResult.CannotLockVolume)
            {
                MessageBox.Show("Unable to lock all volumes!", "Error");
            }
            else if (result == DiskGroupLockResult.OneOrMoreDisksAreOfflineOrReadonly)
            {
                MessageBox.Show("One or more disks are offline or set to readonly.", "Error");
            }
            else if (result == DiskGroupLockResult.CannotTakeDiskOffline)
            {
                MessageBox.Show("Failed to take all dynamic disks offline!", "Error");
            }
            else if (result == DiskGroupLockResult.Success)
            {
                listDisks.Enabled         = false;
                numericDiskOffset.Enabled = false;
                listSuffixes.Enabled      = false;
                btnCancel.Enabled         = false;
                btnOK.Enabled             = false;
                progressBar.Visible       = true;

                long       firstSector  = offset / targetDisk.BytesPerSector;
                DiskExtent targetExtent = new DiskExtent(targetDisk.Disk, firstSector, m_extent.Size);
                long       bytesTotal   = m_extent.Size;
                long       bytesCopied  = 0;
                Thread     workerThread = new Thread(delegate()
                {
                    m_isWorking = true;
                    if (isSameDisk)
                    {
                        MoveExtentHelper.MoveExtentWithinSameDisk(database, m_volume, m_extent, targetExtent, ref bytesCopied);
                    }
                    else
                    {
                        MoveExtentHelper.MoveExtentToAnotherDisk(database, m_volume, m_extent, targetExtent, ref bytesCopied);
                    }
                    m_isWorking = false;
                });
                workerThread.Start();

                new Thread(delegate()
                {
                    while (workerThread.IsAlive)
                    {
                        Thread.Sleep(250);
                        int progress = (int)(100 * (double)bytesCopied / bytesTotal);
                        this.Invoke((MethodInvoker) delegate()
                        {
                            progressBar.Value = progress;
                        });
                    }

                    this.Invoke((MethodInvoker) delegate()
                    {
                        DiskGroupHelper.UnlockDiskGroup(m_diskGroup);
                        this.DialogResult = DialogResult.OK;
                        this.Close();
                    });
                }).Start();
            }
        }
Пример #27
0
 public RemovableVolume(DiskExtent extent) : base(extent)
 {
 }
Пример #28
0
 public MBRPartition(byte partitionType, DiskExtent extent) : base(extent)
 {
     m_partitionType = partitionType;
 }
        private void btnOK_Click(object sender, EventArgs e)
        {
            DynamicDisk targetDynamicDisk = (DynamicDisk)listDisks.SelectedValue;
            Raid5Volume raid5Volume       = (Raid5Volume)m_volume;
            DiskExtent  newExtent         = DynamicDiskHelper.FindExtentAllocation(targetDynamicDisk, raid5Volume.ColumnSize);

            if (newExtent == null)
            {
                MessageBox.Show("The disk specified does not contain enough free space.", "Error");
                return;
            }

            List <DynamicDisk> diskGroup = WindowsDynamicDiskHelper.GetPhysicalDynamicDisks(raid5Volume.DiskGroupGuid);
            DiskGroupDatabase  database  = DiskGroupDatabase.ReadFromDisks(diskGroup, raid5Volume.DiskGroupGuid);

            if (database.AreDisksMissing)
            {
                DialogResult disksMissingResult = MessageBox.Show("Some of the disks in this disk group are missing, Continue anyway?", "Warning", MessageBoxButtons.YesNo);
                if (disksMissingResult != DialogResult.Yes)
                {
                    return;
                }
            }

            DiskGroupLockResult result = DiskGroupHelper.LockDiskGroup(m_diskGroup);

            if (result == DiskGroupLockResult.CannotLockDisk)
            {
                MessageBox.Show("Unable to lock all disks!", "Error");
            }
            else if (result == DiskGroupLockResult.CannotLockVolume)
            {
                MessageBox.Show("Unable to lock all volumes!", "Error");
            }
            else if (result == DiskGroupLockResult.OneOrMoreDisksAreOfflineOrReadonly)
            {
                MessageBox.Show("One or more disks are offline or set to readonly.", "Error");
            }
            else if (result == DiskGroupLockResult.CannotTakeDiskOffline)
            {
                MessageBox.Show("Failed to take all dynamic disks offline!", "Error");
            }
            else if (result == DiskGroupLockResult.Success)
            {
                listDisks.Enabled   = false;
                btnCancel.Enabled   = false;
                btnOK.Enabled       = false;
                progressBar.Visible = true;

                long   bytesTotal   = raid5Volume.Size;
                long   bytesCopied  = 0;
                Thread workerThread = new Thread(delegate()
                {
                    m_isWorking = true;
                    AddDiskToArrayHelper.AddDiskToRaid5Volume(database, raid5Volume, newExtent, ref bytesCopied);
                    m_isWorking = false;
                });
                workerThread.Start();

                new Thread(delegate()
                {
                    while (workerThread.IsAlive)
                    {
                        Thread.Sleep(250);
                        int progress = (int)(100 * (double)bytesCopied / bytesTotal);
                        this.Invoke((MethodInvoker) delegate()
                        {
                            progressBar.Value = progress;
                        });
                    }

                    this.Invoke((MethodInvoker) delegate()
                    {
                        DiskGroupHelper.UnlockDiskGroup(m_diskGroup);
                        this.DialogResult = DialogResult.OK;
                        this.Close();
                    });
                }).Start();
            }
        }
Пример #30
0
        // A Volume could be on many physical drives.
        // Returns a list of string containing each physical drive the volume uses.
        // For CD Drives with no disc in it will return an empty list.
        private List <string> GetPhysicalDriveStrings(DriveInfo driveInfo)
        {
            SafeFileHandle sfh            = null;
            List <string>  physicalDrives = new List <string>(1);
            string         path           = "\\\\.\\" + driveInfo.RootDirectory.ToString().TrimEnd('\\');

            try
            {
                sfh = NativeMethods.CreateFile(path, 0, FileShareRead | Filesharewrite, IntPtr.Zero, OpenExisting, 0, IntPtr.Zero);

                int         bytesReturned  = 0;
                DiskExtents de1            = new DiskExtents();
                int         numDiskExtents = 0;
                bool        result         = NativeMethods.DeviceIoControl(sfh
                                                                           , IoctlVolumeGetVolumeDiskExtents
                                                                           , IntPtr.Zero
                                                                           , 0
                                                                           , ref de1
                                                                           , Marshal.SizeOf(de1)
                                                                           , ref bytesReturned, IntPtr.Zero);
                DiskExtents de1Cast = (DiskExtents)de1;
                if (result == true)
                {
                    // there was only one disk extent. So the volume lies on 1 physical drive.
                    physicalDrives.Add("\\\\.\\PhysicalDrive" + de1Cast.first.DiskNumber.ToString());
                    return(physicalDrives);
                }
                if (Marshal.GetLastWin32Error() == IncorrectFunction)
                {
                    // The drive is removable and removed, like a CDRom with nothing in it.
                    return(physicalDrives);
                }
                if (Marshal.GetLastWin32Error() == MoreDataIsAvailable)
                {
                    // This drive is part of a mirror or volume - handle it below.
                }
                else if (Marshal.GetLastWin32Error() != ErrorInsufficientBuffer)
                {
                    throw new Win32Exception();
                }
                // Houston, we have a spanner. The volume is on multiple disks.
                // Untested...
                // We need a blob of memory for the DISK_EXTENTS structure, and all the DISK_EXTENTS

                int    blobSize = Marshal.SizeOf(typeof(DiskExtents)) + (de1Cast.numberOfExtents - 1) * Marshal.SizeOf(typeof(DiskExtent));
                IntPtr pBlob    = Marshal.AllocHGlobal(blobSize);
                result = NativeMethods.DeviceIoControl(sfh, IoctlVolumeGetVolumeDiskExtents, IntPtr.Zero, 0, pBlob, blobSize, ref bytesReturned, IntPtr.Zero);
                if (result == false)
                {
                    throw new Win32Exception();
                }
                // Read them out one at a time.
                IntPtr pNext = new IntPtr(pBlob.ToInt64() + 8);
                // is this always ok on 64 bit OSes? ToInt64?
                for (int i = 0; i <= de1Cast.numberOfExtents - 1; i++)
                {
                    DiskExtent diskExtentN = (DiskExtent)Marshal.PtrToStructure(pNext, typeof(DiskExtent));
                    physicalDrives.Add("\\\\.\\PhysicalDrive" + diskExtentN.DiskNumber.ToString());
                    pNext = new IntPtr(pNext.ToInt32() + Marshal.SizeOf(typeof(DiskExtent)));
                }
                return(physicalDrives);
            }
            finally
            {
                if (sfh != null)
                {
                    if (sfh.IsInvalid == false)
                    {
                        sfh.Close();
                    }
                    sfh.Dispose();
                }
            }
        }