public static void AddDiskToRaid5Volume(DiskGroupDatabase database, Raid5Volume volume, DiskExtent newExtent, ref long bytesCopied)
        {
            // If there will be a power failure during the conversion, our RAID volume will resync during boot,
            // To prevent destruction of the data, we temporarily convert the array to striped volume
            VolumeManagerDatabaseHelper.ConvertRaidToStripedVolume(database, volume.VolumeGuid);
            ulong newExtentID = VolumeManagerDatabaseHelper.AddNewExtentToVolume(database, volume, newExtent);

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

            AddDiskOperationResumeRecord resumeRecord = new AddDiskOperationResumeRecord();

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

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

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

            ResumeAddDiskToRaid5Volume(database, volume, new DynamicDiskExtent(newExtent, newExtentID), resumeRecord, ref bytesCopied);
        }
        /// <summary>
        /// Move extent to a new location on the same disk
        /// </summary>
        public static void MoveExtentWithinSameDisk(DiskGroupDatabase database, DynamicVolume volume, DynamicDiskExtent sourceExtent, DiskExtent relocatedExtent, ref long bytesCopied)
        {
            MoveExtentOperationResumeRecord resumeRecord = new MoveExtentOperationResumeRecord();

            // If there will be a power failure during the move, a RAID volume will resync during boot,
            // To prevent destruction of the data, we temporarily convert the array to striped volume
            if (volume is Raid5Volume)
            {
                VolumeManagerDatabaseHelper.ConvertRaidToStripedVolume(database, volume.VolumeGuid);
                resumeRecord.RestoreRAID5 = true;
            }

            // We want to write our own volume boot sector for recovery purposes, so we must find where to backup the old boot sector.
            // We don't want to store the backup in the range of the existing or relocated extent, because then we would have to move
            // the backup around during the move operation, other options include:
            // 1. Store it between sectors 1-62 (cons: Could be in use, Windows occasionally start a volume from sector 1)
            // 2. Find an easily compressible sector (e.g. zero-filled) within the existing extent, overwrite it with the backup, and restore it when the operation is done.
            // 3. use the LDM private region to store the sector.

            DynamicDisk dynamicDisk = DynamicDisk.ReadFromDisk(relocatedExtent.Disk);
            // Note: backupSectorIndex will be from the beginning of the private region while backupBufferStartSector will be from the end
            // so there is no need to allocate them
            long backupSectorIndex = PrivateRegionHelper.FindUnusedSector(dynamicDisk.PrivateHeader, dynamicDisk.TOCBlock);

            resumeRecord.VolumeGuid = volume.VolumeGuid;
            resumeRecord.NumberOfCommittedSectors = 0;
            resumeRecord.ExtentID               = sourceExtent.ExtentID;
            resumeRecord.OldStartSector         = (ulong)sourceExtent.FirstSector;
            resumeRecord.NewStartSector         = (ulong)relocatedExtent.FirstSector;
            resumeRecord.BootRecordBackupSector = (ulong)backupSectorIndex;

            long distanceLBA = (long)Math.Abs((double)resumeRecord.NewStartSector - resumeRecord.OldStartSector);

            if (distanceLBA < MoveHelper.BufferedModeThresholdLBA)
            {
                long backupBufferStartSector = PrivateRegionHelper.FindUnusedRegion(dynamicDisk.PrivateHeader, dynamicDisk.TOCBlock, BackupBufferSizeLBA);
                if (backupBufferStartSector == -1)
                {
                    throw new Exception("Private region is full");
                }

                if (backupBufferStartSector <= backupSectorIndex)
                {
                    throw new Exception("Private region structure is unknown");
                }
                resumeRecord.BackupBufferStartSector = (ulong)backupBufferStartSector;
                resumeRecord.BackupBufferSizeLBA     = BackupBufferSizeLBA;
            }

            // Backup the first sector of the first extent
            // (We replace the filesystem boot record with our own sector for recovery purposes)
            byte[] filesystemBootRecord = volume.ReadSector(0);
            relocatedExtent.Disk.WriteSectors(backupSectorIndex, filesystemBootRecord);

            // we write the resume record instead of the boot record
            volume.WriteSectors(0, resumeRecord.GetBytes(volume.BytesPerSector));

            if (sourceExtent.FirstSector < relocatedExtent.FirstSector)
            {
                // move right
                MoveExtentRight(database, volume, resumeRecord, ref bytesCopied);
            }
            else
            {
                // move left

                // we write the resume record at the new location as well (to be able to resume if a power failure will occur immediately after updating the database)
                relocatedExtent.WriteSectors(0, resumeRecord.GetBytes(relocatedExtent.BytesPerSector));
                DynamicDiskExtent dynamicRelocatedExtent = new DynamicDiskExtent(relocatedExtent, sourceExtent.ExtentID);
                dynamicRelocatedExtent.Name     = sourceExtent.Name;
                dynamicRelocatedExtent.DiskGuid = sourceExtent.DiskGuid;
                VolumeManagerDatabaseHelper.UpdateExtentLocation(database, volume, dynamicRelocatedExtent);
                int extentIndex = DynamicDiskExtentHelper.GetIndexOfExtentID(volume.DynamicExtents, sourceExtent.ExtentID);
                // get the updated volume (we just moved an extent)
                volume = DynamicVolumeHelper.GetVolumeByGuid(database.Disks, volume.VolumeGuid);
                MoveExtentLeft(database, volume, resumeRecord, ref bytesCopied);
            }
        }