Exemple #1
0
        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 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);
        }
Exemple #3
0
        public static void ConvertStripedVolumeToRaid(DiskGroupDatabase database, Guid volumeGuid)
        {
            List <DatabaseRecord> records = new List <DatabaseRecord>();

            VolumeRecord volumeRecord = database.FindVolumeByVolumeGuid(volumeGuid);

            if (volumeRecord == null)
            {
                throw new MissingDatabaseRecordException("Volume record is missing");
            }
            volumeRecord = (VolumeRecord)volumeRecord.Clone();
            volumeRecord.VolumeTypeString = "raid5";
            volumeRecord.ReadPolicy       = ReadPolicyName.RAID;
            volumeRecord.VolumeFlags      = VolumeFlags.DefaultUnknown | VolumeFlags.Writeback | VolumeFlags.Writecopy;
            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.ExtentLayout = ExtentLayoutName.RAID5;
            records.Add(componentRecord);

            database.UpdateDatabase(records);
        }
Exemple #4
0
        public static DynamicVolume GetVolume(DiskGroupDatabase database, VolumeRecord volumeRecord)
        {
            List <ComponentRecord> componentRecords = database.FindComponentsByVolumeID(volumeRecord.VolumeId);

            if (volumeRecord.NumberOfComponents != (ulong)componentRecords.Count || componentRecords.Count == 0)
            {
                // database record is invalid
                throw new InvalidDataException("Number of components in volume record does not match actual number of component records");
            }

            if (componentRecords.Count == 1)
            {
                ComponentRecord componentRecord = componentRecords[0];
                return(GetVolume(database, volumeRecord, componentRecord));
            }
            else // Mirrored volume
            {
                // Mirrored Simple Volume is the only kind of mirror suppored by Windows (only 2-way mirror is supported)
                // Veritas also supports Mirrored Stripe / Mirrored RAID-5 / Mirrored Spanned Volume (up to 32-way mirror is supported)
                List <DynamicVolume> volumes = new List <DynamicVolume>();
                foreach (ComponentRecord componentRecord in componentRecords)
                {
                    DynamicVolume volume = GetVolume(database, volumeRecord, componentRecord);
                    volumes.Add(volume);
                }

                MirroredVolume mirroredVolume = new MirroredVolume(volumes, volumeRecord.VolumeGuid, database.DiskGroupGuid);
                mirroredVolume.VolumeID = volumeRecord.VolumeId;
                mirroredVolume.Name     = volumeRecord.Name;
                return(mirroredVolume);
            }
        }
Exemple #5
0
        /// <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);
        }
Exemple #6
0
 private static DynamicVolume GetVolume(DiskGroupDatabase database, VolumeRecord volumeRecord, ComponentRecord componentRecord)
 {
     if (componentRecord.ExtentLayout == ExtentLayoutName.Concatenated)
     {
         if (componentRecord.NumberOfExtents == 1)
         {
             // Simple volume
             return(GetSimpleVolume(database, componentRecord, volumeRecord));;
         }
         else
         {
             // spanned volume
             SpannedVolume volume = GetSpannedVolume(database, componentRecord, volumeRecord);
             return(volume);
         }
     }
     else if (componentRecord.ExtentLayout == ExtentLayoutName.Stripe)
     {
         // striped volume
         StripedVolume volume = GetStripedVolume(database, componentRecord, volumeRecord);
         return(volume);
     }
     else if (componentRecord.ExtentLayout == ExtentLayoutName.RAID5)
     {
         Raid5Volume volume = GetRAID5Volume(database, componentRecord, volumeRecord);
         return(volume);
     }
     else
     {
         return(null);
     }
 }
Exemple #7
0
        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);
        }
Exemple #8
0
        public static void AddDiskToRaid5Volume(List <DynamicDisk> disks, Raid5Volume volume, DiskExtent newExtent, ref long bytesCopied)
        {
            DiskGroupDatabase database = DiskGroupDatabase.ReadFromDisks(disks, volume.DiskGroupGuid);

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

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

            AddDiskOperationBootRecord resumeRecord = new AddDiskOperationBootRecord();

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

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

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

            ResumeAddDiskToRaid5Volume(disks, volume, new DynamicDiskExtent(newExtent, newExtentID), resumeRecord, ref bytesCopied);
        }
Exemple #9
0
        public static List <DiskGroupDatabase> ReadFromDisks(List <DynamicDisk> disks)
        {
            Dictionary <Guid, List <DynamicDisk> > groups = new Dictionary <Guid, List <DynamicDisk> >();

            foreach (DynamicDisk disk in disks)
            {
                Guid diskGroupGuid = disk.PrivateHeader.DiskGroupGuid;
                if (groups.ContainsKey(diskGroupGuid))
                {
                    groups[diskGroupGuid].Add(disk);
                }
                else
                {
                    List <DynamicDisk> list = new List <DynamicDisk>();
                    list.Add(disk);
                    groups.Add(diskGroupGuid, list);
                }
            }

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

            foreach (List <DynamicDisk> groupDisks in groups.Values)
            {
                VolumeManagerDatabase database = VolumeManagerDatabase.ReadFromDisk(groupDisks[0]);
                if (database != null) // if there is issue with one disk (such as unsupported version) we skip the group entirely
                {
                    DiskGroupDatabase groupDatabase = new DiskGroupDatabase(groupDisks, database.DatabaseHeader, database.DatabaseRecords, database.KernelUpdateLog);
                    result.Add(groupDatabase);
                }
            }

            return(result);
        }
        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);
        }
Exemple #11
0
        private static SpannedVolume GetSpannedVolume(DiskGroupDatabase database, ComponentRecord componentRecord, VolumeRecord volumeRecord)
        {
            List <DynamicColumn> columns = GetDynamicVolumeColumns(database, componentRecord, volumeRecord);

            SpannedVolume volume = new SpannedVolume(columns[0], volumeRecord.VolumeGuid, database.DiskGroupGuid);

            volume.VolumeID      = volumeRecord.VolumeId;
            volume.Name          = volumeRecord.Name;
            volume.DiskGroupName = database.DiskGroupName;
            return(volume);
        }
Exemple #12
0
        public static ulong CreateSimpleVolume(DiskGroupDatabase database, DiskExtent extent)
        {
            List <DatabaseRecord> records = new List <DatabaseRecord>();

            VolumeRecord volumeRecord = new VolumeRecord();

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

            ComponentRecord componentRecord = new ComponentRecord();

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

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

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

            ExtentRecord extentRecord = new ExtentRecord();

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

            extentRecord.HasColumnIndexFlag = false;

            records.Add(extentRecord);

            database.UpdateDatabase(records);

            return(volumeRecord.VolumeId);
        }
Exemple #13
0
        private static StripedVolume GetStripedVolume(DiskGroupDatabase database, ComponentRecord componentRecord, VolumeRecord volumeRecord)
        {
            List <DynamicColumn> columns   = GetDynamicVolumeColumns(database, componentRecord, volumeRecord);
            int           bytesPerSector   = DynamicVolume.GetBytesPerSector(columns, DynamicColumn.DefaultBytesPerSector);
            int           sectorsPerStripe = (int)PublicRegionHelper.TranslateFromPublicRegionSizeLBA((int)componentRecord.StripeSizeLBA, bytesPerSector);
            StripedVolume volume           = new StripedVolume(columns, sectorsPerStripe, volumeRecord.VolumeGuid, database.DiskGroupGuid);

            volume.VolumeID      = volumeRecord.VolumeId;
            volume.Name          = volumeRecord.Name;
            volume.DiskGroupName = database.DiskGroupName;
            return(volume);
        }
Exemple #14
0
        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(database, volumeRecord);
                    result.Add(volume);
                }
            }

            return(result);
        }
Exemple #15
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);
        }
Exemple #16
0
        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);
        }
Exemple #17
0
        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);
        }
Exemple #18
0
        private static void MoveExtentRight(List <DynamicDisk> disks, DynamicVolume volume, MoveExtentOperationBootRecord resumeRecord, ref long bytesCopied)
        {
            DiskGroupDatabase database = DiskGroupDatabase.ReadFromDisks(disks, volume.DiskGroupGuid);

            if (database == null)
            {
                throw new DatabaseNotFoundException();
            }

            int extentIndex = DynamicDiskExtentHelper.GetIndexOfExtentID(volume.DynamicExtents, resumeRecord.ExtentID);
            DynamicDiskExtent sourceExtent    = volume.DynamicExtents[extentIndex];
            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(disks, volume.VolumeGuid);

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

            ClearBackupData(relocatedExtent.Disk, resumeRecord);
        }
Exemple #19
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");
            }
        }
Exemple #20
0
        private static void ResumeAddDiskToRaid5Volume(List <DynamicDisk> disks, Raid5Volume volume, DynamicDiskExtent newExtent, AddDiskOperationBootRecord 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((decimal)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());

                stripeIndexInVolume += numberOfStripesToTransfer;
            }

            // we're done, let's restore the filesystem boot record
            byte[] filesystemBootRecord = newExtent.ReadSector(newExtent.TotalSectors - 1);
            newVolume.WriteSectors(0, filesystemBootRecord);
            DiskGroupDatabase database = DiskGroupDatabase.ReadFromDisks(disks, volume.DiskGroupGuid);

            VolumeManagerDatabaseHelper.ConvertStripedVolumeToRaid(database, volume.VolumeGuid);
        }
Exemple #21
0
        public static ulong CreateRAID5Volume(DiskGroupDatabase database, List <DiskExtent> extents, bool isDegraded)
        {
            int numberOfColumns;

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

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

            VolumeRecord volumeRecord = new VolumeRecord();

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

            ComponentRecord componentRecord = new ComponentRecord();

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

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

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

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

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

                records.Add(extentRecord);
            }

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

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

                records.Add(extentRecord);
            }

            database.UpdateDatabase(records);

            return(volumeRecord.VolumeId);
        }
Exemple #22
0
        /// <summary>
        /// Update the database (add the new extent)
        /// </summary>
        /// <param name="volume">RAID-5 or striped volume</param>
        /// <returns>new extent ID</returns>
        public static ulong AddNewExtentToVolume(DiskGroupDatabase database, DynamicVolume volume, DiskExtent newExtent)
        {
            PrivateHeader privateHeader = PrivateHeader.ReadFromDisk(newExtent.Disk);

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

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

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

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

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

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

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

            ExtentRecord newExtentRecord = new ExtentRecord();

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

            records.Add(newExtentRecord);

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

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

            database.UpdateDatabase(records);

            return(newExtentRecord.ExtentId);
        }
        /// <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);
            }
        }