Exemple #1
0
        private static List <DynamicColumn> GetDynamicVolumeColumns(DiskGroupDatabase database, ComponentRecord componentRecord, VolumeRecord volumeRecord)
        {
            // extentRecords are sorted by offset in column
            List <ExtentRecord> extentRecords = database.FindExtentsByComponentID(componentRecord.ComponentId);

            if (componentRecord.NumberOfExtents != extentRecords.Count || extentRecords.Count == 0)
            {
                // database record is invalid
                throw new InvalidDataException("Number of extents in component record does not match actual number of extent records");
            }

            SortedList <uint, List <DynamicDiskExtent> > columns = new SortedList <uint, List <DynamicDiskExtent> >();

            foreach (ExtentRecord extentRecord in extentRecords)
            {
                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);

                if (columns.ContainsKey(extentRecord.ColumnIndex))
                {
                    columns[extentRecord.ColumnIndex].Add(extent);
                }
                else
                {
                    List <DynamicDiskExtent> list = new List <DynamicDiskExtent>();
                    list.Add(extent);
                    columns.Add(extentRecord.ColumnIndex, list);
                }
            }

            List <DynamicColumn> result = new List <DynamicColumn>();

            foreach (List <DynamicDiskExtent> extents in columns.Values)
            {
                result.Add(new DynamicColumn(extents));
            }
            return(result);
        }
        /// <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);
        }
Exemple #3
0
        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>
        /// 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);
            }
        }
Exemple #5
0
        public static List <DynamicDisk> GetPhysicalDynamicDisks(Guid diskGroupGuid)
        {
            List <DynamicDisk> dynamicDisks = GetPhysicalDynamicDisks();

            return(DynamicDiskHelper.FindDiskGroup(dynamicDisks, diskGroupGuid));
        }