From dac23c90a173f857081181b1e79d806b3d63359c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20G=C3=A2teau?= Date: Tue, 5 Aug 2014 18:27:24 +0200 Subject: [PATCH] Implement MoveFileSystemJob: all testResizePartition tests pass --- src/modules/partition/MoveFileSystemJob.cpp | 191 +++++++++++++++---- src/modules/partition/MoveFileSystemJob.h | 11 +- src/modules/partition/ResizePartitionJob.cpp | 17 +- src/modules/partition/tests/JobTests.cpp | 5 + 4 files changed, 179 insertions(+), 45 deletions(-) diff --git a/src/modules/partition/MoveFileSystemJob.cpp b/src/modules/partition/MoveFileSystemJob.cpp index f704cb14c..bca301cdd 100644 --- a/src/modules/partition/MoveFileSystemJob.cpp +++ b/src/modules/partition/MoveFileSystemJob.cpp @@ -17,7 +17,9 @@ */ // This class is heavily based on the MoveFileSystemJob class from KDE Partition -// Manager. Original copyright follow: +// Manager. +// The copyBlock functions come from Partition Manager Job class. +// Original copyright follow: /*************************************************************************** * Copyright (C) 2008 by Volker Lanz * @@ -40,6 +42,8 @@ #include +#include + // CalaPM #include #include @@ -48,22 +52,12 @@ #include #include -static bool -copyBlocks( Report& report, CopyTargetDevice& target, CopySourceDevice& source ) -{ - return false; -} - -static bool -rollbackCopyBlocks( Report& report, CopyTargetDevice& target, CopySourceDevice& source ) -{ - return false; -} - -MoveFileSystemJob::MoveFileSystemJob( Device* device, Partition* partition, qint64 firstSector ) +MoveFileSystemJob::MoveFileSystemJob( Device* device, Partition* partition, qint64 oldFirstSector, qint64 newFirstSector, qint64 length ) : PartitionJob( partition ) , m_device( device ) - , m_firstSector( firstSector ) + , m_oldFirstSector( oldFirstSector ) + , m_newFirstSector( newFirstSector ) + , m_length( length ) {} QString @@ -77,49 +71,168 @@ MoveFileSystemJob::exec() { Report report( nullptr ); QString partitionPath = partition()->partitionPath(); - FileSystem& fs = partition()->fileSystem(); - CopySourceDevice moveSource( *m_device, fs.firstSector(), fs.lastSector() ); - CopyTargetDevice moveTarget( *m_device, m_firstSector, m_firstSector + fs.length() ); + CopySourceDevice moveSource( *m_device, m_oldFirstSector, m_oldFirstSector + m_length - 1 ); + CopyTargetDevice moveTarget( *m_device, m_newFirstSector, m_newFirstSector + m_length - 1 ); if ( !moveSource.open() ) return Calamares::JobResult::error( - QString(), - tr( "Could not open file system on partition %1 for moving." ).arg( partitionPath ) - ); + QString(), + tr( "Could not open file system on partition %1 for moving." ).arg( partitionPath ) + ); if ( !moveTarget.open() ) return Calamares::JobResult::error( - QString(), - tr( "Could not create target for moving file system on partition %1." ).arg( partitionPath ) - ); + QString(), + tr( "Could not create target for moving file system on partition %1." ).arg( partitionPath ) + ); bool ok = copyBlocks( report, moveTarget, moveSource ); if ( !ok ) { if ( rollbackCopyBlocks( report, moveTarget, moveSource ) ) return Calamares::JobResult::error( - QString(), - tr( "Moving of partition %1 failed, changes have been rolled back." ).arg( partitionPath ) - + '\n' + report.toText() - ); + QString(), + tr( "Moving of partition %1 failed, changes have been rolled back." ).arg( partitionPath ) + + '\n' + report.toText() + ); else return Calamares::JobResult::error( - QString(), - tr( "Moving of partition %1 failed. Roll back of the changes have failed." ).arg( partitionPath ) - + '\n' + report.toText() - ); + QString(), + tr( "Moving of partition %1 failed. Roll back of the changes have failed." ).arg( partitionPath ) + + '\n' + report.toText() + ); } - const qint64 savedLength = fs.length(); - fs.setFirstSector( m_firstSector ); - fs.setLastSector( m_firstSector + savedLength - 1 ); + FileSystem& fs = partition()->fileSystem(); + fs.setFirstSector( m_newFirstSector ); + fs.setLastSector( m_newFirstSector + m_length - 1 ); if ( !fs.updateBootSector( report, partitionPath ) ) return Calamares::JobResult::error( - QString(), - tr( "Updating boot sector after the moving of partition %1 failed." ).arg( partitionPath ) - + '\n' + report.toText() - ); + QString(), + tr( "Updating boot sector after the moving of partition %1 failed." ).arg( partitionPath ) + + '\n' + report.toText() + ); return Calamares::JobResult::ok(); } + +bool +MoveFileSystemJob::copyBlocks( Report& report, CopyTargetDevice& target, CopySourceDevice& source ) +{ + /** @todo copyBlocks() assumes that source.sectorSize() == target.sectorSize(). */ + + if ( source.sectorSize() != target.sectorSize() ) + { + report.line() << tr( "The logical sector sizes in the source and target for copying are not the same. This is currently unsupported." ); + return false; + } + + bool rval = true; + const qint64 blockSize = 16065 * 8; // number of sectors per block to copy + const qint64 blocksToCopy = source.length() / blockSize; + + qint64 readOffset = source.firstSector(); + qint64 writeOffset = target.firstSector(); + qint32 copyDir = 1; + + if ( target.firstSector() > source.firstSector() ) + { + readOffset = source.firstSector() + source.length() - blockSize; + writeOffset = target.firstSector() + source.length() - blockSize; + copyDir = -1; + } + + qint64 blocksCopied = 0; + + void* buffer = malloc( blockSize * source.sectorSize() ); + int percent = 0; + + while ( blocksCopied < blocksToCopy ) + { + rval = source.readSectors( buffer, readOffset + blockSize * blocksCopied * copyDir, blockSize ); + if ( !rval ) + break; + + rval = target.writeSectors( buffer, writeOffset + blockSize * blocksCopied * copyDir, blockSize ); + if ( !rval ) + break; + + if ( ++blocksCopied * 100 / blocksToCopy != percent ) + { + percent = blocksCopied * 100 / blocksToCopy; + progress( qreal( percent ) / 100. ); + } + } + + const qint64 lastBlock = source.length() % blockSize; + + // copy the remainder + if ( rval && lastBlock > 0 ) + { + Q_ASSERT( lastBlock < blockSize ); + + if ( lastBlock >= blockSize ) + cLog() << "warning: lastBlock: " << lastBlock << ", blockSize: " << blockSize; + + const qint64 lastBlockReadOffset = copyDir > 0 ? readOffset + blockSize * blocksCopied : source.firstSector(); + const qint64 lastBlockWriteOffset = copyDir > 0 ? writeOffset + blockSize * blocksCopied : target.firstSector(); + + rval = source.readSectors( buffer, lastBlockReadOffset, lastBlock ); + + if ( rval ) + rval = target.writeSectors( buffer, lastBlockWriteOffset, lastBlock ); + + if ( rval ) + emit progress( 1.0 ); + } + + free( buffer ); + + return rval; +} + +bool +MoveFileSystemJob::rollbackCopyBlocks( Report& report, CopyTargetDevice& origTarget, CopySourceDevice& origSource ) +{ + if ( !origSource.overlaps( origTarget ) ) + { + report.line() << tr( "Source and target for copying do not overlap: Rollback is not required." ); + return true; + } + + // default: use values as if we were copying from front to back. + qint64 undoSourceFirstSector = origTarget.firstSector(); + qint64 undoSourceLastSector = origTarget.firstSector() + origTarget.sectorsWritten() - 1; + + qint64 undoTargetFirstSector = origSource.firstSector(); + qint64 undoTargetLastSector = origSource.firstSector() + origTarget.sectorsWritten() - 1; + + if ( origTarget.firstSector() > origSource.firstSector() ) + { + // we were copying from back to front + undoSourceFirstSector = origTarget.firstSector() + origSource.length() - origTarget.sectorsWritten(); + undoSourceLastSector = origTarget.firstSector() + origSource.length() - 1; + + undoTargetFirstSector = origSource.lastSector() - origTarget.sectorsWritten() + 1; + undoTargetLastSector = origSource.lastSector(); + } + + CopySourceDevice undoSource( origTarget.device(), undoSourceFirstSector, undoSourceLastSector ); + if ( !undoSource.open() ) + { + report.line() << tr( "Could not open device %1 to rollback copying." ) + .arg( origTarget.device().deviceNode() ); + return false; + } + + CopyTargetDevice undoTarget( origSource.device(), undoTargetFirstSector, undoTargetLastSector ); + if ( !undoTarget.open() ) + { + report.line() << tr( "Could not open device %1 to rollback copying." ) + .arg( origSource.device().deviceNode() ); + return false; + } + + return copyBlocks( report, undoTarget, undoSource ); +} diff --git a/src/modules/partition/MoveFileSystemJob.h b/src/modules/partition/MoveFileSystemJob.h index 38c34ad83..1ae3534c6 100644 --- a/src/modules/partition/MoveFileSystemJob.h +++ b/src/modules/partition/MoveFileSystemJob.h @@ -42,13 +42,16 @@ #include +class CopySourceDevice; +class CopyTargetDevice; class Device; class Partition; +class Report; class MoveFileSystemJob : public PartitionJob { public: - MoveFileSystemJob( Device* device, Partition* partition, qint64 firstSector ); + MoveFileSystemJob( Device* device, Partition* partition, qint64 oldFirstSector, qint64 newFirstSector, qint64 length ); QString prettyName() const override; @@ -56,7 +59,11 @@ public: private: Device* m_device; - qint64 m_firstSector; + qint64 m_oldFirstSector; + qint64 m_newFirstSector; + qint64 m_length; + bool copyBlocks( Report& report, CopyTargetDevice& target, CopySourceDevice& source ); + bool rollbackCopyBlocks( Report& report, CopyTargetDevice& origTarget, CopySourceDevice& origSource ); }; #endif /* MOVEFILESYSTEMJOB_H */ diff --git a/src/modules/partition/ResizePartitionJob.cpp b/src/modules/partition/ResizePartitionJob.cpp index b2c63a19a..7596f13c4 100644 --- a/src/modules/partition/ResizePartitionJob.cpp +++ b/src/modules/partition/ResizePartitionJob.cpp @@ -203,6 +203,15 @@ ResizePartitionJob::exec() qint64 oldLength = m_oldLastSector - m_oldFirstSector + 1; qint64 newLength = m_newLastSector - m_newFirstSector + 1; + // Assuming updatePreview() has been called, `partition` uses its new + // position and size. Reset it to the old values: part of the libparted + // backend relies on this (for example: + // LibPartedPartitionTable::updateGeometry()) + // The jobs are responsible for updating the partition back when they are + // done. + m_partition->setFirstSector( m_oldFirstSector ); + m_partition->setLastSector( m_oldLastSector ); + // Setup context QString partitionPath = m_partition->partitionPath(); Context context( this ); @@ -228,12 +237,12 @@ ResizePartitionJob::exec() { bool shrink = newLength < oldLength; bool grow = newLength > oldLength; - bool moveRight = m_newFirstSector > m_partition->firstSector(); - bool moveLeft = m_newFirstSector < m_partition->firstSector(); + bool moveRight = m_newFirstSector > m_oldFirstSector; + bool moveLeft = m_newFirstSector < m_oldFirstSector; if ( shrink ) { jobs << Calamares::job_ptr( new ResizeFileSystemJob( &context, newLength ) ); - jobs << Calamares::job_ptr( new SetPartGeometryJob( &context, m_partition->firstSector(), newLength ) ); + jobs << Calamares::job_ptr( new SetPartGeometryJob( &context, m_oldFirstSector, newLength ) ); } if ( moveRight || moveLeft ) { @@ -241,7 +250,7 @@ ResizePartitionJob::exec() // shrunk, or to the original length (it may or may not then later be grown, we don't care here) const qint64 length = shrink ? newLength : oldLength; jobs << Calamares::job_ptr( new SetPartGeometryJob( &context, m_newFirstSector, length ) ); - jobs << Calamares::job_ptr( new MoveFileSystemJob( m_device, m_partition, m_newFirstSector ) ); + jobs << Calamares::job_ptr( new MoveFileSystemJob( m_device, m_partition, m_oldFirstSector, m_newFirstSector, length ) ); } if ( grow ) { diff --git a/src/modules/partition/tests/JobTests.cpp b/src/modules/partition/tests/JobTests.cpp index dc08d21e5..08337d68c 100644 --- a/src/modules/partition/tests/JobTests.cpp +++ b/src/modules/partition/tests/JobTests.cpp @@ -274,11 +274,16 @@ JobTests::testResizePartition() QVERIFY( m_device->partitionTable() ); Partition* partition = m_device->partitionTable()->findPartitionBySector( oldFirst, PartitionRole( PartitionRole::Primary ) ); QVERIFY( partition ); + QCOMPARE( partition->firstSector(), oldFirst ); + QCOMPARE( partition->lastSector(), oldLast ); ResizePartitionJob* job = new ResizePartitionJob( m_device.data(), partition, newFirst, newLast ); job->updatePreview(); m_queue.enqueue( job_ptr( job ) ); QVERIFY( m_runner.run() ); + + QCOMPARE( partition->firstSector(), newFirst ); + QCOMPARE( partition->lastSector(), newLast ); } // Test