2015-06-04 01:29:22 +01:00
/*************************************************************************
* Copyright ( C ) 2008 by Volker Lanz < vl @ fidra . de > *
2016-03-02 19:00:31 +00:00
* Copyright ( C ) 2016 by Andrius Š tikonas < andrius @ stikonas . eu > *
2015-06-04 01:29:22 +01:00
* *
* This program is free software ; you can redistribute it and / or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation ; either version 3 of *
* the License , or ( at your option ) any later version . *
* *
* This program is distributed in the hope that it will be useful , *
* but WITHOUT ANY WARRANTY ; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the *
* GNU General Public License for more details . *
* *
* You should have received a copy of the GNU General Public License *
* along with this program . If not , see < http : //www.gnu.org/licenses/>.*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "ops/copyoperation.h"
# include "core/partition.h"
# include "core/device.h"
# include "jobs/createpartitionjob.h"
# include "jobs/deletepartitionjob.h"
# include "jobs/checkfilesystemjob.h"
# include "jobs/copyfilesystemjob.h"
# include "jobs/resizefilesystemjob.h"
# include "fs/filesystemfactory.h"
# include "util/capacity.h"
# include "util/report.h"
# include <QDebug>
# include <QString>
# include <KLocalizedString>
/** Creates a new CopyOperation.
2015-07-13 15:16:36 +01:00
@ param targetdevice the Device to copy the Partition to
2015-07-22 14:48:03 +01:00
@ param copiedpartition pointer to the new Partition object on the target Device . May not be nullptr .
2015-07-13 15:16:36 +01:00
@ param sourcedevice the Device where to copy from
2015-07-22 14:48:03 +01:00
@ param sourcepartition pointer to the Partition to copy from . May not be nullptr .
2015-06-04 01:29:22 +01:00
*/
CopyOperation : : CopyOperation ( Device & targetdevice , Partition * copiedpartition , Device & sourcedevice , Partition * sourcepartition ) :
2015-07-13 15:16:36 +01:00
Operation ( ) ,
m_TargetDevice ( targetdevice ) ,
m_CopiedPartition ( copiedpartition ) ,
m_SourceDevice ( sourcedevice ) ,
m_SourcePartition ( sourcepartition ) ,
2015-07-22 14:48:03 +01:00
m_OverwrittenPartition ( nullptr ) ,
2015-07-13 15:16:36 +01:00
m_MustDeleteOverwritten ( false ) ,
2015-07-22 14:48:03 +01:00
m_CheckSourceJob ( nullptr ) ,
m_CreatePartitionJob ( nullptr ) ,
m_CopyFSJob ( nullptr ) ,
m_CheckTargetJob ( nullptr ) ,
m_MaximizeJob ( nullptr ) ,
2015-07-13 15:16:36 +01:00
m_Description ( updateDescription ( ) )
2015-06-04 01:29:22 +01:00
{
2015-07-13 15:16:36 +01:00
Q_ASSERT ( targetDevice ( ) . partitionTable ( ) ) ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
Partition * dest = targetDevice ( ) . partitionTable ( ) - > findPartitionBySector ( copiedPartition ( ) . firstSector ( ) , PartitionRole ( PartitionRole : : Primary | PartitionRole : : Logical | PartitionRole : : Unallocated ) ) ;
2015-06-04 01:29:22 +01:00
2015-07-22 14:48:03 +01:00
if ( dest = = nullptr )
2015-07-13 15:16:36 +01:00
qWarning ( ) < < " destination partition not found at sector " < < copiedPartition ( ) . firstSector ( ) ;
2015-06-04 01:29:22 +01:00
2015-07-23 16:49:05 +01:00
Q_ASSERT ( dest ) ;
2015-07-13 15:16:36 +01:00
if ( dest & & ! dest - > roles ( ) . has ( PartitionRole : : Unallocated ) ) {
copiedPartition ( ) . setLastSector ( dest - > lastSector ( ) ) ;
setOverwrittenPartition ( dest ) ;
}
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
addJob ( m_CheckSourceJob = new CheckFileSystemJob ( sourcePartition ( ) ) ) ;
2015-06-04 01:29:22 +01:00
2015-07-22 14:48:03 +01:00
if ( overwrittenPartition ( ) = = nullptr )
2015-07-13 15:16:36 +01:00
addJob ( m_CreatePartitionJob = new CreatePartitionJob ( targetDevice ( ) , copiedPartition ( ) ) ) ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
addJob ( m_CopyFSJob = new CopyFileSystemJob ( targetDevice ( ) , copiedPartition ( ) , sourceDevice ( ) , sourcePartition ( ) ) ) ;
addJob ( m_CheckTargetJob = new CheckFileSystemJob ( copiedPartition ( ) ) ) ;
addJob ( m_MaximizeJob = new ResizeFileSystemJob ( targetDevice ( ) , copiedPartition ( ) ) ) ;
2015-06-04 01:29:22 +01:00
}
CopyOperation : : ~ CopyOperation ( )
{
2015-07-13 15:16:36 +01:00
if ( status ( ) = = StatusPending )
delete m_CopiedPartition ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
if ( status ( ) = = StatusFinishedSuccess | | status ( ) = = StatusFinishedWarning | | status ( ) = = StatusError )
cleanupOverwrittenPartition ( ) ;
2015-06-04 01:29:22 +01:00
}
bool CopyOperation : : targets ( const Device & d ) const
{
2015-07-13 15:16:36 +01:00
return d = = targetDevice ( ) ;
2015-06-04 01:29:22 +01:00
}
bool CopyOperation : : targets ( const Partition & p ) const
{
2015-07-13 15:16:36 +01:00
return p = = copiedPartition ( ) ;
2015-06-04 01:29:22 +01:00
}
void CopyOperation : : preview ( )
{
2015-07-13 15:16:36 +01:00
if ( overwrittenPartition ( ) )
removePreviewPartition ( targetDevice ( ) , * overwrittenPartition ( ) ) ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
insertPreviewPartition ( targetDevice ( ) , copiedPartition ( ) ) ;
2015-06-04 01:29:22 +01:00
}
void CopyOperation : : undo ( )
{
2015-07-13 15:16:36 +01:00
removePreviewPartition ( targetDevice ( ) , copiedPartition ( ) ) ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
if ( overwrittenPartition ( ) )
insertPreviewPartition ( targetDevice ( ) , * overwrittenPartition ( ) ) ;
2015-06-04 01:29:22 +01:00
}
bool CopyOperation : : execute ( Report & parent )
{
2015-07-13 15:16:36 +01:00
bool rval = false ;
bool warning = false ;
Report * report = parent . newChild ( description ( ) ) ;
// check the source first
if ( ( rval = checkSourceJob ( ) - > run ( * report ) ) ) {
// At this point, if the target partition is to be created and not overwritten, it
// will still have the wrong device path (the one of the source device). We need
// to adjust that before we're creating it.
copiedPartition ( ) . setDevicePath ( targetDevice ( ) . deviceNode ( ) ) ;
// either we have no partition to create (because we're overwriting) or creating
// must be successful
if ( ! createPartitionJob ( ) | | ( rval = createPartitionJob ( ) - > run ( * report ) ) ) {
// set the state of the target partition from StateCopy to StateNone or checking
// it will fail (because its deviceNode() will still be "Copy of sdXn"). This is
// only required for overwritten partitions, but doesn't hurt in any case.
copiedPartition ( ) . setState ( Partition : : StateNone ) ;
// if we have overwritten a partition, reset device path and number
if ( overwrittenPartition ( ) ) {
copiedPartition ( ) . setDevicePath ( overwrittenPartition ( ) - > devicePath ( ) ) ;
2016-05-19 12:11:58 +01:00
copiedPartition ( ) . setPartitionPath ( overwrittenPartition ( ) - > partitionPath ( ) ) ;
2015-07-13 15:16:36 +01:00
}
// now run the copy job itself
if ( ( rval = copyFSJob ( ) - > run ( * report ) ) ) {
// and if the copy job succeeded, check the target
if ( ( rval = checkTargetJob ( ) - > run ( * report ) ) ) {
// ok, everything went well
rval = true ;
// if maximizing doesn't work, just warn the user, don't fail
if ( ! maximizeJob ( ) - > run ( * report ) ) {
2016-07-18 12:50:52 +01:00
report - > line ( ) < < xi18nc ( " @info:status " , " <warning>Maximizing file system on target partition <filename>%1</filename> to the size of the partition failed.</warning> " , copiedPartition ( ) . deviceNode ( ) ) ;
2015-07-13 15:16:36 +01:00
warning = true ;
}
} else
2016-07-17 23:41:00 +01:00
report - > line ( ) < < xi18nc ( " @info:status " , " Checking target partition <filename>%1</filename> after copy failed. " , copiedPartition ( ) . deviceNode ( ) ) ;
2015-07-13 15:16:36 +01:00
} else {
if ( createPartitionJob ( ) ) {
DeletePartitionJob deleteJob ( targetDevice ( ) , copiedPartition ( ) ) ;
deleteJob . run ( * report ) ;
}
2016-07-17 23:41:00 +01:00
report - > line ( ) < < xi18nc ( " @info:status " , " Copying source to target partition failed. " ) ;
2015-07-13 15:16:36 +01:00
}
} else
2016-07-17 23:41:00 +01:00
report - > line ( ) < < xi18nc ( " @info:status " , " Creating target partition for copying failed. " ) ;
2015-07-13 15:16:36 +01:00
} else
2016-07-17 23:41:00 +01:00
report - > line ( ) < < xi18nc ( " @info:status " , " Checking source partition <filename>%1</filename> failed. " , sourcePartition ( ) . deviceNode ( ) ) ;
2015-07-13 15:16:36 +01:00
if ( rval )
setStatus ( warning ? StatusFinishedWarning : StatusFinishedSuccess ) ;
else
setStatus ( StatusError ) ;
2016-07-17 23:41:00 +01:00
report - > setStatus ( xi18nc ( " @info:status (success, error, warning...) of operation " , " %1: %2 " , description ( ) , statusText ( ) ) ) ;
2015-07-13 15:16:36 +01:00
return rval ;
2015-06-04 01:29:22 +01:00
}
QString CopyOperation : : updateDescription ( ) const
{
2015-07-13 15:16:36 +01:00
if ( overwrittenPartition ( ) ) {
if ( copiedPartition ( ) . length ( ) = = overwrittenPartition ( ) - > length ( ) )
2016-07-17 23:41:00 +01:00
return xi18nc ( " @info:status " , " Copy partition <filename>%1</filename> (%2, %3) to <filename>%4</filename> (%5, %6) " ,
2015-07-13 15:16:36 +01:00
sourcePartition ( ) . deviceNode ( ) ,
Capacity : : formatByteSize ( sourcePartition ( ) . capacity ( ) ) ,
sourcePartition ( ) . fileSystem ( ) . name ( ) ,
overwrittenPartition ( ) - > deviceNode ( ) ,
Capacity : : formatByteSize ( overwrittenPartition ( ) - > capacity ( ) ) ,
overwrittenPartition ( ) - > fileSystem ( ) . name ( )
) ;
2016-07-17 23:41:00 +01:00
return xi18nc ( " @info:status " , " Copy partition <filename>%1</filename> (%2, %3) to <filename>%4</filename> (%5, %6) and grow it to %7 " ,
2015-07-13 15:16:36 +01:00
sourcePartition ( ) . deviceNode ( ) ,
Capacity : : formatByteSize ( sourcePartition ( ) . capacity ( ) ) ,
sourcePartition ( ) . fileSystem ( ) . name ( ) ,
overwrittenPartition ( ) - > deviceNode ( ) ,
Capacity : : formatByteSize ( overwrittenPartition ( ) - > capacity ( ) ) ,
overwrittenPartition ( ) - > fileSystem ( ) . name ( ) ,
Capacity : : formatByteSize ( copiedPartition ( ) . capacity ( ) )
) ;
}
if ( copiedPartition ( ) . length ( ) = = sourcePartition ( ) . length ( ) )
2016-07-17 23:41:00 +01:00
return xi18nc ( " @info:status " , " Copy partition <filename>%1</filename> (%2, %3) to unallocated space (starting at %4) on <filename>%5</filename> " ,
2015-07-13 15:16:36 +01:00
sourcePartition ( ) . deviceNode ( ) ,
Capacity : : formatByteSize ( sourcePartition ( ) . capacity ( ) ) ,
sourcePartition ( ) . fileSystem ( ) . name ( ) ,
2016-05-31 19:28:31 +01:00
Capacity : : formatByteSize ( copiedPartition ( ) . firstSector ( ) * targetDevice ( ) . logicalSize ( ) ) ,
2015-07-13 15:16:36 +01:00
targetDevice ( ) . deviceNode ( )
) ;
2016-07-17 23:41:00 +01:00
return xi18nc ( " @info:status " , " Copy partition <filename>%1</filename> (%2, %3) to unallocated space (starting at %4) on <filename>%5</filename> and grow it to %6 " ,
2015-07-13 15:16:36 +01:00
sourcePartition ( ) . deviceNode ( ) ,
Capacity : : formatByteSize ( sourcePartition ( ) . capacity ( ) ) ,
sourcePartition ( ) . fileSystem ( ) . name ( ) ,
2016-05-31 19:28:31 +01:00
Capacity : : formatByteSize ( copiedPartition ( ) . firstSector ( ) * targetDevice ( ) . logicalSize ( ) ) ,
2015-07-13 15:16:36 +01:00
targetDevice ( ) . deviceNode ( ) ,
Capacity : : formatByteSize ( copiedPartition ( ) . capacity ( ) )
) ;
2015-06-04 01:29:22 +01:00
}
void CopyOperation : : setOverwrittenPartition ( Partition * p )
{
2015-07-13 15:16:36 +01:00
// this code is also in RestoreOperation.
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
cleanupOverwrittenPartition ( ) ;
m_OverwrittenPartition = p ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
// If the overwritten partition has no other operation that owns it (e.g., an OperationNew or
// an OperationRestore), we're the new owner. So remember that, because after the operations all
// have executed and we're asked to clean up after ourselves, the state of the overwritten partition
// might have changed: If it was a new one and the NewOperation has successfully run, the state will
// then be StateNone.
m_MustDeleteOverwritten = ( p & & p - > state ( ) = = Partition : : StateNone ) ;
2015-06-04 01:29:22 +01:00
}
void CopyOperation : : cleanupOverwrittenPartition ( )
{
2015-07-13 15:16:36 +01:00
if ( mustDeleteOverwritten ( ) ) {
delete overwrittenPartition ( ) ;
2015-07-22 14:48:03 +01:00
m_OverwrittenPartition = nullptr ;
2015-07-13 15:16:36 +01:00
}
2015-06-04 01:29:22 +01:00
}
/** Creates a new copied Partition.
2015-07-13 15:16:36 +01:00
@ param target the target Partition to copy to ( may be unallocated )
@ param source the source Partition to copy
@ return pointer to the newly created Partition object
2015-06-04 01:29:22 +01:00
*/
Partition * CopyOperation : : createCopy ( const Partition & target , const Partition & source )
{
2015-07-13 15:16:36 +01:00
Partition * p = target . roles ( ) . has ( PartitionRole : : Unallocated ) ? new Partition ( source ) : new Partition ( target ) ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
p - > setDevicePath ( source . devicePath ( ) ) ;
p - > setPartitionPath ( source . partitionPath ( ) ) ;
p - > setState ( Partition : : StateCopy ) ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
p - > deleteFileSystem ( ) ;
p - > setFileSystem ( FileSystemFactory : : create ( source . fileSystem ( ) ) ) ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
p - > fileSystem ( ) . setFirstSector ( p - > firstSector ( ) ) ;
p - > fileSystem ( ) . setLastSector ( p - > lastSector ( ) ) ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
p - > setFlags ( PartitionTable : : FlagNone ) ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
return p ;
2015-06-04 01:29:22 +01:00
}
/** Can a Partition be copied?
2015-07-22 14:48:03 +01:00
@ param p the Partition in question , may be nullptr .
2015-07-13 15:16:36 +01:00
@ return true if @ p p can be copied .
2015-06-04 01:29:22 +01:00
*/
bool CopyOperation : : canCopy ( const Partition * p )
{
2015-07-22 14:48:03 +01:00
if ( p = = nullptr )
2015-07-13 15:16:36 +01:00
return false ;
2015-06-04 01:29:22 +01:00
2016-05-18 13:51:38 +01:00
if ( p - > state ( ) = = Partition : : StateNew & & p - > roles ( ) . has ( PartitionRole : : Luks ) )
return false ;
2015-07-13 15:16:36 +01:00
if ( p - > isMounted ( ) )
return false ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
// Normally, copying partitions that have not been written to disk yet should
// be forbidden here. The operation stack, however, will take care of these
// problematic cases when pushing the CopyOperation onto the stack.
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
return p - > fileSystem ( ) . supportCopy ( ) ! = FileSystem : : cmdSupportNone ;
2015-06-04 01:29:22 +01:00
}
/** Can a Partition be pasted on another one?
2015-07-22 14:48:03 +01:00
@ param p the Partition to be pasted to , may be nullptr
@ param source the Partition to be pasted , may be nullptr
2015-07-13 15:16:36 +01:00
@ return true if @ p source can be pasted on @ p p
2015-06-04 01:29:22 +01:00
*/
bool CopyOperation : : canPaste ( const Partition * p , const Partition * source )
{
2015-07-22 14:48:03 +01:00
if ( p = = nullptr | | source = = nullptr )
2015-07-13 15:16:36 +01:00
return false ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
if ( p - > isMounted ( ) )
return false ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
if ( p - > roles ( ) . has ( PartitionRole : : Extended ) )
return false ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
if ( p = = source )
return false ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
if ( source - > length ( ) > p - > length ( ) )
return false ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
if ( ! p - > roles ( ) . has ( PartitionRole : : Unallocated ) & & p - > capacity ( ) > source - > fileSystem ( ) . maxCapacity ( ) )
return false ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
return true ;
2015-06-04 01:29:22 +01:00
}