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(DiskGroupDatabase database, DynamicVolume volume, DynamicDiskExtent sourceExtent, DiskExtent relocatedExtent, ref long bytesCopied) { // 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); }
public static DiskGroupLockResult ExtendDynamicVolume(List <DynamicDisk> diskGroup, DynamicVolume volume, long numberOfAdditionalExtentSectors) { if (volume is StripedVolume) { numberOfAdditionalExtentSectors -= numberOfAdditionalExtentSectors % ((StripedVolume)volume).SectorsPerStripe; } if (volume is Raid5Volume) { numberOfAdditionalExtentSectors -= numberOfAdditionalExtentSectors % ((Raid5Volume)volume).SectorsPerStripe; } DiskGroupLockResult result = DiskGroupHelper.LockDiskGroup(diskGroup); if (result != DiskGroupLockResult.Success) { return(result); } DiskGroupDatabase database = DiskGroupDatabase.ReadFromDisks(diskGroup, volume.DiskGroupGuid); ExtendHelper.ExtendDynamicVolume((DynamicVolume)volume, numberOfAdditionalExtentSectors, database); DiskGroupHelper.UnlockDiskGroup(diskGroup); return(DiskGroupLockResult.Success); }
private void ResumeAdd(AddDiskOperationResumeRecord resumeRecord) { // the RAID-5 volume was temporarily converted to striped volume if (m_volume is StripedVolume) { StripedVolume stripedVolume = (StripedVolume)m_volume; 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) { long bytesTotal = stripedVolume.Size / stripedVolume.NumberOfColumns * (stripedVolume.NumberOfColumns - 2); long bytesCopied = 0; Thread workerThread = new Thread(delegate() { m_isWorking = true; List <DynamicDisk> diskGroup = WindowsDynamicDiskHelper.GetPhysicalDynamicDisks(stripedVolume.DiskGroupGuid); DiskGroupDatabase database = DiskGroupDatabase.ReadFromDisks(diskGroup, stripedVolume.DiskGroupGuid); AddDiskToArrayHelper.ResumeAddDiskToRaid5Volume(database, stripedVolume, resumeRecord, 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(); } } }
public static void AddDiskToRaid5Volume(DiskGroupDatabase database, Raid5Volume volume, DiskExtent newExtent, ref long bytesCopied) { // 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); AddDiskOperationResumeRecord resumeRecord = new AddDiskOperationResumeRecord(); 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(volume.BytesPerSector)); ResumeAddDiskToRaid5Volume(database, volume, new DynamicDiskExtent(newExtent, newExtentID), resumeRecord, ref bytesCopied); }
private static void MoveExtentLeft(DiskGroupDatabase database, DynamicVolume volume, MoveExtentOperationResumeRecord resumeRecord, ref long bytesCopied) { 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(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); }
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); }
private void ResumeMove(MoveExtentOperationResumeRecord resumeRecord) { 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) { int extentIndex = DynamicDiskExtentHelper.GetIndexOfExtentID(m_volume.DynamicExtents, (resumeRecord).ExtentID); DynamicDiskExtent sourceExtent = m_volume.DynamicExtents[extentIndex]; long bytesTotal = sourceExtent.Size; long bytesCopied = 0; Thread workerThread = new Thread(delegate() { m_isWorking = true; List <DynamicDisk> diskGroup = WindowsDynamicDiskHelper.GetPhysicalDynamicDisks(m_volume.DiskGroupGuid); DiskGroupDatabase database = DiskGroupDatabase.ReadFromDisks(diskGroup, m_volume.DiskGroupGuid); MoveExtentHelper.ResumeMoveExtent(database, m_volume, resumeRecord, 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(); } }
public static void ResumeAddDiskToRaid5Volume(DiskGroupDatabase database, StripedVolume stripedVolume, AddDiskOperationResumeRecord resumeRecord, ref long bytesCopied) { List <DynamicColumn> columns = stripedVolume.Columns; DynamicDiskExtent newExtent = columns[columns.Count - 1].Extents[0]; columns.RemoveAt(columns.Count - 1); Raid5Volume volume = new Raid5Volume(columns, stripedVolume.SectorsPerStripe, stripedVolume.VolumeGuid, stripedVolume.DiskGroupGuid); volume.VolumeID = stripedVolume.VolumeID; volume.Name = stripedVolume.Name; volume.DiskGroupName = stripedVolume.DiskGroupName; ResumeAddDiskToRaid5Volume(database, volume, newExtent, resumeRecord, ref bytesCopied); }
public static List <DynamicVolume> GetDynamicVolumes(List <DynamicDisk> disks) { List <DynamicVolume> result = new List <DynamicVolume>(); List <DiskGroupDatabase> diskGroupDatabases = DiskGroupDatabase.ReadFromDisks(disks); foreach (DiskGroupDatabase database in diskGroupDatabases) { foreach (VolumeRecord volumeRecord in database.VolumeRecords) { DynamicVolume volume = GetVolume(disks, database, volumeRecord); result.Add(volume); } } return(result); }
public static void ResumeMoveExtent(DiskGroupDatabase database, DynamicVolume volume, MoveExtentOperationResumeRecord resumeRecord, ref long bytesCopied) { if (resumeRecord.OldStartSector == resumeRecord.NewStartSector) { throw new InvalidDataException("Invalid move record"); } if (resumeRecord.RestoreFromBuffer) { // we need to use the backup buffer to restore the data that may have been overwritten DynamicDiskExtent sourceExtent = GetVolumeExtent(volume, resumeRecord.ExtentID); byte[] backupBuffer = sourceExtent.Disk.ReadSectors((long)resumeRecord.BackupBufferStartSector, BackupBufferSizeLBA); if (resumeRecord.OldStartSector < resumeRecord.NewStartSector) { // move right long readCount = (long)resumeRecord.NumberOfCommittedSectors; int sectorsToRead = BackupBufferSizeLBA; long sectorIndex = sourceExtent.TotalSectors - readCount - sectorsToRead; sourceExtent.WriteSectors(sectorIndex, backupBuffer); System.Diagnostics.Debug.WriteLine("Restored to " + sectorIndex); } else { // move left long sectorIndex = (long)resumeRecord.NumberOfCommittedSectors; sourceExtent.WriteSectors(sectorIndex, backupBuffer); System.Diagnostics.Debug.WriteLine("Restored to " + sectorIndex); } } if (resumeRecord.OldStartSector < resumeRecord.NewStartSector) { MoveExtentRight(database, volume, resumeRecord, ref bytesCopied); } else { MoveExtentLeft(database, volume, resumeRecord, ref bytesCopied); } }
public static void ExtendVolume(Volume volume, long numberOfAdditionalExtentSectors, DiskGroupDatabase database) { if (volume is Partition) { ExtendPartition((Partition)volume, numberOfAdditionalExtentSectors); } else if (volume is DynamicVolume) { ExtendDynamicVolume((DynamicVolume)volume, numberOfAdditionalExtentSectors, database); } }
public static List <DiskGroupDatabase> ReadFromPhysicalDisks() { List <DynamicDisk> dynamicDisks = WindowsDynamicDiskHelper.GetPhysicalDynamicDisks(); return(DiskGroupDatabase.ReadFromDisks(dynamicDisks)); }
public static DiskGroupDatabase ReadFromPhysicalDisks(Guid diskGroupGuid) { List <DynamicDisk> dynamicDisks = WindowsDynamicDiskHelper.GetPhysicalDynamicDisks(diskGroupGuid); return(DiskGroupDatabase.ReadFromDisks(dynamicDisks, diskGroupGuid)); }
/// <summary> /// Move extent to a new location on the same disk /// </summary> public static void MoveExtentWithinSameDisk(DiskGroupDatabase database, DynamicVolume volume, DynamicDiskExtent sourceExtent, DiskExtent relocatedExtent, ref long bytesCopied) { MoveExtentOperationResumeRecord resumeRecord = new MoveExtentOperationResumeRecord(); // 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 = PrivateRegionHelper.FindUnusedSector(dynamicDisk.PrivateHeader, dynamicDisk.TOCBlock); 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 = (long)Math.Abs((double)resumeRecord.NewStartSector - resumeRecord.OldStartSector); if (distanceLBA < MoveHelper.BufferedModeThresholdLBA) { long backupBufferStartSector = PrivateRegionHelper.FindUnusedRegion(dynamicDisk.PrivateHeader, dynamicDisk.TOCBlock, 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(volume.BytesPerSector)); if (sourceExtent.FirstSector < relocatedExtent.FirstSector) { // move right MoveExtentRight(database, 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(relocatedExtent.BytesPerSector)); 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(database.Disks, volume.VolumeGuid); MoveExtentLeft(database, volume, resumeRecord, ref bytesCopied); } }
public static void ExtendDynamicVolume(DynamicVolume volume, long numberOfAdditionalExtentSectors, DiskGroupDatabase database) { if (volume is SimpleVolume) { SimpleVolume simpleVolume = (SimpleVolume)volume; VolumeManagerDatabaseHelper.ExtendSimpleVolume(database, simpleVolume, numberOfAdditionalExtentSectors); } else if (volume is StripedVolume) { StripedVolume stripedVolume = (StripedVolume)volume; VolumeManagerDatabaseHelper.ExtendStripedVolume(database, stripedVolume, numberOfAdditionalExtentSectors); } else if (volume is Raid5Volume) { Raid5Volume raid5Volume = (Raid5Volume)volume; VolumeManagerDatabaseHelper.ExtendRAID5Volume(database, raid5Volume, numberOfAdditionalExtentSectors); } }
private static void ResumeAddDiskToRaid5Volume(DiskGroupDatabase database, Raid5Volume volume, DynamicDiskExtent newExtent, AddDiskOperationResumeRecord resumeRecord, ref long bytesCopied) { // When reading from the volume, we must use the old volume (without the new disk) // However, when writing the boot sector to the volume, we must use the new volume or otherwise parity information will be invalid List <DynamicColumn> newVolumeColumns = new List <DynamicColumn>(); newVolumeColumns.AddRange(volume.Columns); newVolumeColumns.Add(new DynamicColumn(newExtent)); Raid5Volume newVolume = new Raid5Volume(newVolumeColumns, volume.SectorsPerStripe, volume.VolumeGuid, volume.DiskGroupGuid); int oldColumnCount = volume.Columns.Count; int newColumnCount = oldColumnCount + 1; long resumeFromStripe = (long)resumeRecord.NumberOfCommittedSectors / volume.SectorsPerStripe; // it would be prudent to write the new extent before committing to the operation, however, it would take much longer. // The number of sectors in extent / column is always a multiple of SectorsPerStripe. // We read enough stripes to write a vertical stripe segment in the new array, // We will read MaximumTransferSizeLBA and make sure maximumStripesToTransfer is multiple of (NumberOfColumns - 1). int maximumStripesToTransfer = (Settings.MaximumTransferSizeLBA / volume.SectorsPerStripe) / (newColumnCount - 1) * (newColumnCount - 1); long totalStripesInVolume = volume.TotalStripes; long stripeIndexInVolume = resumeFromStripe; while (stripeIndexInVolume < totalStripesInVolume) { // When we add a column, the distance between the stripes we read (later in the column) to thes one we write (earlier), // Is growing constantly (because we can stack more stripes in each vertical stripe), so we increment the number of stripes we // can safely transfer as we go. // (We assume that the segment we write will be corrupted if there will be a power failure) long stripeToReadIndexInColumn = stripeIndexInVolume / (oldColumnCount - 1); long stripeToWriteIndexInColumn = stripeIndexInVolume / (newColumnCount - 1); long numberOfStripesSafeToTransfer = (stripeToReadIndexInColumn - stripeToWriteIndexInColumn) * (newColumnCount - 1); bool verticalStripeAtRisk = (numberOfStripesSafeToTransfer == 0); if (numberOfStripesSafeToTransfer == 0) { // The first few stripes in each column are 'at rist', meaning that we may overwrite crucial data (that is only stored in memory) // when writing the segment that will be lost forever if a power failure will occur during the write operation. // Note: The number of 'at risk' vertical stripes is equal to the number of columns in the old array - 1 numberOfStripesSafeToTransfer = (newColumnCount - 1); } int numberOfStripesToTransfer = (int)Math.Min(numberOfStripesSafeToTransfer, maximumStripesToTransfer); long stripesLeft = totalStripesInVolume - stripeIndexInVolume; numberOfStripesToTransfer = (int)Math.Min(numberOfStripesToTransfer, stripesLeft); byte[] segmentData = volume.ReadStripes(stripeIndexInVolume, numberOfStripesToTransfer); if (numberOfStripesToTransfer % (newColumnCount - 1) > 0) { // this is the last segment and we need to zero-fill it for the write: int numberOfStripesToWrite = (int)Math.Ceiling((double)numberOfStripesToTransfer / (newColumnCount - 1)) * (newColumnCount - 1); byte[] temp = new byte[numberOfStripesToWrite * volume.BytesPerStripe]; Array.Copy(segmentData, temp, segmentData.Length); segmentData = temp; } long firstStripeIndexInColumn = stripeIndexInVolume / (newColumnCount - 1); if (verticalStripeAtRisk) { // we write 'at risk' stripes one at a time to the new volume, this will make sure they will not overwrite crucial data // (because they will be written in an orderly fashion, and not in bulk from the first column to the last) newVolume.WriteStripes(stripeIndexInVolume, segmentData); } else { WriteSegment(volume, newExtent, firstStripeIndexInColumn, segmentData); } // update resume record resumeRecord.NumberOfCommittedSectors += (ulong)(numberOfStripesToTransfer * volume.SectorsPerStripe); bytesCopied = (long)resumeRecord.NumberOfCommittedSectors * volume.BytesPerSector; newVolume.WriteSectors(0, resumeRecord.GetBytes(newVolume.BytesPerSector)); stripeIndexInVolume += numberOfStripesToTransfer; } // we're done, let's restore the filesystem boot record byte[] filesystemBootRecord = newExtent.ReadSector(newExtent.TotalSectors - 1); newVolume.WriteSectors(0, filesystemBootRecord); VolumeManagerDatabaseHelper.ConvertStripedVolumeToRaid(database, volume.VolumeGuid); }
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(); } }
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(); } }
private void btnOK_Click(object sender, EventArgs e) { int diskCount = listDisks.CheckedIndices.Count; if (rbSimple.Checked && diskCount != 1) { MessageBox.Show("You must select a single disk in order to create a simple volume.", "Error"); return; } if (rbRaid5.Checked) { if (chkDegraded.Checked && diskCount < 2) { MessageBox.Show("You must select at least 2 disks in order to create a degraded RAID-5 volume.", "Error"); return; } if (!chkDegraded.Checked && diskCount < 3) { MessageBox.Show("You must select at least 3 disks in order to create a RAID-5 volume.", "Error"); return; } } long extentSizeInBytes = (long)numericExtentSize.Value * 1024 * 1024; List <DiskExtent> extents = new List <DiskExtent>(); foreach (int checkedIndex in listDisks.CheckedIndices) { DynamicDisk dynamicDisk = m_diskGroup[checkedIndex]; DiskExtent extent = DynamicDiskHelper.FindExtentAllocation(dynamicDisk, extentSizeInBytes); if (extent == null) { MessageBox.Show("One of the disks does not contain enough free space", "Error"); return; } extents.Add(extent); } 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) { DiskGroupDatabase database = DiskGroupDatabase.ReadFromDisks(m_diskGroup)[0]; if (rbSimple.Checked) { VolumeManagerDatabaseHelper.CreateSimpleVolume(database, extents[0]); } else { VolumeManagerDatabaseHelper.CreateRAID5Volume(database, extents, chkDegraded.Checked); } DiskGroupHelper.UnlockDiskGroup(m_diskGroup); this.DialogResult = DialogResult.OK; this.Close(); } }