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(); } } }
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(); } }
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); }
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); }
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()); }
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"); }
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(); } }
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); }
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"); } }
public Partition(DiskExtent extent) { m_extent = extent; }
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; }
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); }
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); }
/// <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); } }
/// <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); }
public DynamicDiskExtent(DiskExtent diskExtent, ulong extentID) : base(diskExtent.Disk, diskExtent.FirstSector, diskExtent.Size) { m_extentID = extentID; }
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; }
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(); } }
public RemovableVolume(DiskExtent extent) : base(extent) { }
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(); } }
// 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(); } } }