/// <summary> /// Update the database to point to the new extent location (same or different disk) /// </summary> public static void UpdateExtentLocation(DiskGroupDatabase database, DynamicVolume volume, DynamicDiskExtent relocatedExtent) { PrivateHeader privateHeader = PrivateHeader.ReadFromDisk(relocatedExtent.Disk); DiskRecord targetDiskRecord = database.FindDiskByDiskGuid(privateHeader.DiskGuid); VolumeRecord volumeRecord = database.FindVolumeByVolumeGuid(volume.VolumeGuid); List <DatabaseRecord> records = new List <DatabaseRecord>(); ExtentRecord sourceExtentRecord = database.FindExtentByExtentID(relocatedExtent.ExtentID); ExtentRecord relocatedExtentRecord = (ExtentRecord)sourceExtentRecord.Clone(); relocatedExtentRecord.DiskId = targetDiskRecord.DiskId; relocatedExtentRecord.DiskOffsetLBA = (ulong)PublicRegionHelper.TranslateToPublicRegionLBA(relocatedExtent.FirstSector, privateHeader); records.Add(relocatedExtentRecord); // we should update the disk records foreach (DynamicDiskExtent extent in volume.Extents) { DiskRecord diskRecord = database.FindDiskByDiskID(relocatedExtentRecord.DiskId); // 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); } } // when moving to a new disk, we should update the new disk record as well if (!records.Contains(targetDiskRecord)) { records.Add(targetDiskRecord.Clone()); } database.UpdateDatabase(records); }
public static void ExtendRAID5Volume(DiskGroupDatabase database, Raid5Volume volume, long numberOfAdditionalExtentSectors) { if (numberOfAdditionalExtentSectors % volume.SectorsPerStripe > 0) { throw new ArgumentException("Number of additional sectors must be multiple of stripes per sector"); } List <DatabaseRecord> records = new List <DatabaseRecord>(); VolumeRecord volumeRecord = database.FindVolumeByVolumeGuid(volume.VolumeGuid); volumeRecord = (VolumeRecord)volumeRecord.Clone(); volumeRecord.SizeLBA += (ulong)PublicRegionHelper.TranslateToPublicRegionSizeLBA(numberOfAdditionalExtentSectors * (volume.NumberOfColumns - 1), volume.BytesPerSector); records.Add(volumeRecord); foreach (DynamicColumn column in volume.Columns) { DynamicDiskExtent lastExtent = column.Extents[column.Extents.Count - 1]; ExtentRecord extentRecord = database.FindExtentByExtentID(lastExtent.ExtentID); extentRecord = (ExtentRecord)extentRecord.Clone(); extentRecord.SizeLBA += (ulong)PublicRegionHelper.TranslateToPublicRegionSizeLBA(numberOfAdditionalExtentSectors, volume.BytesPerSector); records.Add(extentRecord); DiskRecord diskRecord = database.FindDiskByDiskID(extentRecord.DiskId); // we should update the disk, see Database.cs diskRecord = (DiskRecord)diskRecord.Clone(); records.Add(diskRecord); } database.UpdateDatabase(records); }
public static void ExtendStripedVolume(DiskGroupDatabase database, StripedVolume volume, long additionalNumberOfExtentSectors) { if (additionalNumberOfExtentSectors % volume.SectorsPerStripe > 0) { throw new ArgumentException("Number of additional sectors must be multiple of stripes per sector"); } List <DatabaseRecord> records = new List <DatabaseRecord>(); VolumeRecord volumeRecord = database.FindVolumeByVolumeGuid(volume.VolumeGuid); volumeRecord = (VolumeRecord)volumeRecord.Clone(); volumeRecord.SizeLBA += (ulong)(additionalNumberOfExtentSectors * volume.NumberOfColumns); records.Add(volumeRecord); // we only want to extend the last extent in each column foreach (DynamicColumn column in volume.Columns) { DynamicDiskExtent lastExtent = column.Extents[column.Extents.Count - 1]; ExtentRecord extentRecord = database.FindExtentByExtentID(lastExtent.ExtentID); extentRecord = (ExtentRecord)extentRecord.Clone(); extentRecord.SizeLBA += (ulong)additionalNumberOfExtentSectors; records.Add(extentRecord); DiskRecord diskRecord = database.FindDiskByDiskID(extentRecord.DiskId); // we should update the disk, see Database.cs diskRecord = (DiskRecord)diskRecord.Clone(); records.Add(diskRecord); } database.UpdateDatabase(records); }
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); }
/// <summary> /// Support null disks /// </summary> public static long GetExtentStartSector(DynamicDisk disk, ExtentRecord extentRecord) { long publicRegionStartLBA = 0; int bytesPerDiskSector = DynamicColumn.DefaultBytesPerSector; // default for missing disks if (disk != null) { bytesPerDiskSector = disk.BytesPerSector; PrivateHeader privateHeader = disk.PrivateHeader; publicRegionStartLBA = (long)privateHeader.PublicRegionStartLBA; } return(PublicRegionHelper.TranslateFromPublicRegionLBA((long)extentRecord.DiskOffsetLBA, publicRegionStartLBA, bytesPerDiskSector)); }
/// <summary> /// Support null disks /// </summary> public static DynamicDiskExtent GetDiskExtent(DynamicDisk dynamicDisk, ExtentRecord extentRecord) { long extentStartSector = GetExtentStartSector(dynamicDisk, extentRecord); long extentSize = (long)extentRecord.SizeLBA * PublicRegionHelper.BytesPerPublicRegionSector; Disk disk = null; Guid diskGuid = Guid.Empty; if (dynamicDisk != null) { disk = dynamicDisk.Disk; diskGuid = dynamicDisk.DiskGuid; } DynamicDiskExtent extent = new DynamicDiskExtent(disk, extentStartSector, extentSize, extentRecord.ExtentId); extent.Name = extentRecord.Name; extent.DiskGuid = diskGuid; return(extent); }
/// <param name="fragments">Must be sorted</param> public static DatabaseRecord GetDatabaseRecord(List <DatabaseRecordFragment> fragments) { DatabaseRecord result = null; if (fragments.Count != 0) { // Make sure we have all the records and that the first record is at the top of the fragment list if (fragments[0].NumberInGroup == 0 && fragments[0].FragmentCount == fragments.Count) { RecordType recordType = (RecordType)(fragments[0].Data[0x03] & 0xF); switch (recordType) { case RecordType.Volume: result = new VolumeRecord(fragments); break; case RecordType.Component: result = new ComponentRecord(fragments); break; case RecordType.Extent: result = new ExtentRecord(fragments); break; case RecordType.Disk: result = new DiskRecord(fragments); break; case RecordType.DiskGroup: result = new DiskGroupRecord(fragments); break; default: throw new NotImplementedException("Unrecognized record type: " + recordType); } } else { throw new InvalidDataException("Incomplete or unsorted record"); } } return(result); }
private static List <ExtentRecord> GetRetainedExtentsOnDisk(DynamicDisk disk, out long bootPartitionStartLBA) { VolumeManagerDatabase database = VolumeManagerDatabase.ReadFromDisk(disk); DiskRecord diskRecord = database.FindDiskByDiskGuid(disk.DiskGuid); bootPartitionStartLBA = -1; List <ExtentRecord> result = new List <ExtentRecord>(); foreach (VolumeRecord volume in database.VolumeRecords) { if ((volume.VolumeFlags & VolumeFlags.RetainPartition) > 0) { List <ComponentRecord> components = database.FindComponentsByVolumeID(volume.VolumeId); foreach (ComponentRecord componentRecord in components) { if (componentRecord.ExtentLayout == ExtentLayoutName.Concatenated) { if (componentRecord.NumberOfExtents == 1) { List <ExtentRecord> extents = database.FindExtentsByComponentID(componentRecord.ComponentId); if (extents.Count == 1) { ExtentRecord extent = extents[0]; if (extent.DiskId == diskRecord.DiskId) { result.Add(extent); if ((volume.VolumeFlags & VolumeFlags.BootVolume) > 0) { bootPartitionStartLBA = (long)(disk.PrivateHeader.PublicRegionStartLBA + extent.DiskOffsetLBA); } } } } } } } } return(result); }
public static void ExtendSimpleVolume(DiskGroupDatabase database, SimpleVolume volume, long numberOfAdditionalSectors) { VolumeRecord volumeRecord = database.FindVolumeByVolumeGuid(volume.VolumeGuid); volumeRecord = (VolumeRecord)volumeRecord.Clone(); volumeRecord.SizeLBA += (ulong)PublicRegionHelper.TranslateToPublicRegionSizeLBA(numberOfAdditionalSectors, volume.BytesPerSector); ExtentRecord extentRecord = database.FindExtentByExtentID(volume.DiskExtent.ExtentID); extentRecord = (ExtentRecord)extentRecord.Clone(); extentRecord.SizeLBA += (ulong)PublicRegionHelper.TranslateToPublicRegionSizeLBA(numberOfAdditionalSectors, volume.BytesPerSector); DiskRecord diskRecord = database.FindDiskByDiskID(extentRecord.DiskId); // we should update the disk, see Database.cs diskRecord = (DiskRecord)diskRecord.Clone(); List <DatabaseRecord> records = new List <DatabaseRecord>(); records.Add(volumeRecord); records.Add(extentRecord); records.Add(diskRecord); database.UpdateDatabase(records); }
public static void ExtendSimpleVolume(DiskGroupDatabase database, SimpleVolume volume, long additionalNumberOfSectors) { VolumeRecord volumeRecord = database.FindVolumeByVolumeGuid(volume.VolumeGuid); volumeRecord = (VolumeRecord)volumeRecord.Clone(); volumeRecord.SizeLBA += (ulong)additionalNumberOfSectors; ExtentRecord extentRecord = database.FindExtentByExtentID(volume.DiskExtent.ExtentID); extentRecord = (ExtentRecord)extentRecord.Clone(); extentRecord.SizeLBA += (ulong)additionalNumberOfSectors; DiskRecord diskRecord = database.FindDiskByDiskID(extentRecord.DiskId); // we should update the disk, see Database.cs diskRecord = (DiskRecord)diskRecord.Clone(); List <DatabaseRecord> records = new List <DatabaseRecord>(); records.Add(volumeRecord); records.Add(extentRecord); records.Add(diskRecord); database.UpdateDatabase(records); }
private static SimpleVolume GetSimpleVolume(DiskGroupDatabase database, ComponentRecord componentRecord, VolumeRecord volumeRecord) { List <ExtentRecord> extentRecords = database.FindExtentsByComponentID(componentRecord.ComponentId); if (extentRecords.Count == 1) { ExtentRecord extentRecord = extentRecords[0]; DiskRecord diskRecord = database.FindDiskByDiskID(extentRecord.DiskId); DynamicDisk disk = DynamicDiskHelper.FindDisk(database.Disks, diskRecord.DiskGuid); // we add nulls as well DynamicDiskExtent extent = DynamicDiskExtentHelper.GetDiskExtent(disk, extentRecord); SimpleVolume volume = new SimpleVolume(extent, volumeRecord.VolumeGuid, database.DiskGroupGuid); volume.VolumeID = volumeRecord.VolumeId; volume.Name = volumeRecord.Name; volume.DiskGroupName = database.DiskGroupName; return(volume); } else { // component / extent records are invalid throw new InvalidDataException("Number of extents in component record does not match actual number of extent records"); } }
/// <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 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); }
private static int CompareOffsetInColumn(ExtentRecord x, ExtentRecord y) { return(x.OffsetInColumnLBA.CompareTo(y.OffsetInColumnLBA)); }
private static int CompareByColumnIndex(ExtentRecord x, ExtentRecord y) { return(x.ColumnIndex.CompareTo(y.ColumnIndex)); }