2015-06-04 01:29:22 +01:00
/*************************************************************************
* Copyright ( C ) 2008 by Volker Lanz < vl @ fidra . de > *
* *
* 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 "core/partition.h"
# include "core/device.h"
# include "fs/filesystem.h"
# include "fs/filesystemfactory.h"
# include "util/externalcommand.h"
# include "util/report.h"
# include <QString>
# include <QStringList>
# include <QDebug>
# include <KLocalizedString>
# include <KIOCore/KMountPoint>
/** Creates a new Partition object.
2015-07-22 14:48:03 +01:00
@ param parent the Partition ' s parent . May be another Partition ( for logicals ) or a PartitionTable . Must not be nullptr .
2015-07-13 15:16:36 +01:00
@ param device the Device this Partition is on .
@ param role the Partition ' s role ( s )
@ param fs pointer to the Partition ' s FileSystem object . The Partition object will take ownership of this .
@ param sectorStart the first sector of the Partition on its Device
@ param sectorEnd the last sector of the Partition on its Device
@ param partitionPath the Partition ' s path , e . g . / dev / sda4 or / dev / mmcblk0p1
@ param availableFlags the flags available for this Partition
@ param mountPoint mount point for this Partition
@ param mounted true if the Partition is mounted
@ param activeFlags active flags for this Partition
@ param state the Partition ' s state
2015-06-04 01:29:22 +01:00
*/
Partition : : Partition ( PartitionNode * parent , const Device & device , const PartitionRole & role , FileSystem * fs , qint64 sectorStart , qint64 sectorEnd , QString partitionPath , PartitionTable : : Flags availableFlags , const QString & mountPoint , bool mounted , PartitionTable : : Flags activeFlags , State state ) :
2015-07-13 15:16:36 +01:00
PartitionNode ( ) ,
m_Children ( ) ,
m_Parent ( parent ) ,
m_FileSystem ( fs ) ,
m_Roles ( role ) ,
m_FirstSector ( sectorStart ) ,
m_LastSector ( sectorEnd ) ,
m_DevicePath ( device . deviceNode ( ) ) ,
m_MountPoint ( mountPoint ) ,
m_AvailableFlags ( availableFlags ) ,
m_ActiveFlags ( activeFlags ) ,
m_IsMounted ( mounted ) ,
m_SectorSize ( device . logicalSectorSize ( ) ) ,
m_State ( state )
2015-06-04 01:29:22 +01:00
{
2015-07-13 15:16:36 +01:00
setPartitionPath ( partitionPath ) ;
Q_ASSERT ( m_Parent ) ;
2015-06-04 01:29:22 +01:00
}
/** Destroys a Partition, destroying its children and its FileSystem */
Partition : : ~ Partition ( )
{
2015-07-13 15:16:36 +01:00
// FIXME: Design flaw: Currently, there are two ways a partition node can get children: Either
// they're created and inserted as unallocated in PartitionTable (these unallocated then get
// "converted" to real, new partitions in the GUI) or they're created and appended in the
// backend plugin. There is however no defined way to remove partitions from parents. This might
// either cause leaks (a partition is removed from the parent's list of children but never
// deleted) or, worse, crashes (a partition is deleted but not removed from the parent's
// list of children). As a workaround, always remove a partition from its parent here in the dtor.
// This presumably fixes 232092, but backporting is too risky until we're sure this doesn't cause
// side-effects.
parent ( ) - > remove ( this ) ;
clearChildren ( ) ;
deleteFileSystem ( ) ;
2015-06-04 01:29:22 +01:00
}
2015-07-13 15:16:36 +01:00
/** @param other Partition to copy
2015-06-04 01:29:22 +01:00
*/
Partition : : Partition ( const Partition & other ) :
2015-07-13 15:16:36 +01:00
PartitionNode ( ) ,
m_Children ( ) ,
m_Parent ( other . m_Parent ) ,
m_FileSystem ( FileSystemFactory : : create ( other . fileSystem ( ) ) ) ,
m_Roles ( other . m_Roles ) ,
m_FirstSector ( other . m_FirstSector ) ,
m_LastSector ( other . m_LastSector ) ,
m_DevicePath ( other . m_DevicePath ) ,
m_MountPoint ( other . m_MountPoint ) ,
m_AvailableFlags ( other . m_AvailableFlags ) ,
m_ActiveFlags ( other . m_ActiveFlags ) ,
m_IsMounted ( other . m_IsMounted ) ,
m_SectorSize ( other . m_SectorSize ) ,
m_State ( other . m_State )
2015-06-04 01:29:22 +01:00
{
2015-07-13 15:16:36 +01:00
setPartitionPath ( other . m_PartitionPath ) ;
foreach ( const Partition * child , other . children ( ) ) {
Partition * p = new Partition ( * child ) ;
p - > setParent ( this ) ;
m_Children . append ( p ) ;
}
2015-06-04 01:29:22 +01:00
}
/** @param other Partition to assign from */
Partition & Partition : : operator = ( const Partition & other )
{
2015-07-13 15:16:36 +01:00
if ( & other = = this )
return * this ;
clearChildren ( ) ;
foreach ( const Partition * child , other . children ( ) ) {
Partition * p = new Partition ( * child ) ;
p - > setParent ( this ) ;
m_Children . append ( p ) ;
}
m_Number = other . m_Number ;
m_FileSystem = FileSystemFactory : : create ( other . fileSystem ( ) ) ;
m_Roles = other . m_Roles ;
m_FirstSector = other . m_FirstSector ;
m_LastSector = other . m_LastSector ;
m_DevicePath = other . m_DevicePath ;
m_PartitionPath = other . m_PartitionPath ;
m_MountPoint = other . m_MountPoint ;
m_AvailableFlags = other . m_AvailableFlags ;
m_ActiveFlags = other . m_ActiveFlags ;
m_IsMounted = other . m_IsMounted ;
m_SectorSize = other . m_SectorSize ;
m_State = other . m_State ;
return * this ;
2015-06-04 01:29:22 +01:00
}
bool Partition : : operator = = ( const Partition & other ) const
{
2015-07-13 15:16:36 +01:00
return other . deviceNode ( ) = = deviceNode ( ) ;
2015-06-04 01:29:22 +01:00
}
bool Partition : : operator ! = ( const Partition & other ) const
{
2015-07-13 15:16:36 +01:00
return ! ( other = = * this ) ;
2015-06-04 01:29:22 +01:00
}
/** @return a short descriptive text or, in case the Partition has StateNone, its device node. */
QString Partition : : deviceNode ( ) const
{
2015-07-13 15:16:36 +01:00
if ( roles ( ) . has ( PartitionRole : : None ) | | roles ( ) . has ( PartitionRole : : Unallocated ) )
return i18nc ( " @item partition name " , " unallocated " ) ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
if ( state ( ) = = StateNew )
return i18nc ( " @item partition name " , " New Partition " ) ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
if ( state ( ) = = StateRestore )
return i18nc ( " @item partition name " , " Restored Partition " ) ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
if ( state ( ) = = StateCopy )
return i18nc ( " @item partition name " , " Copy of %1 " , partitionPath ( ) ) ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
return partitionPath ( ) ;
2015-06-04 01:29:22 +01:00
}
/** @return the sectors used in the Partition's FileSystem or, in case of an extended partition, the sum of used sectors of the Partition's children */
qint64 Partition : : sectorsUsed ( ) const
{
2015-07-13 15:16:36 +01:00
if ( ! roles ( ) . has ( PartitionRole : : Extended ) )
return fileSystem ( ) . sectorsUsed ( ) ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
qint64 result = 0 ;
foreach ( const Partition * p , children ( ) )
if ( ! p - > roles ( ) . has ( PartitionRole : : Unallocated ) )
result + = p - > length ( ) ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
return result ;
2015-06-04 01:29:22 +01:00
}
/** @return the minimum number of sectors this Partition must be long */
qint64 Partition : : minimumSectors ( ) const
{
2015-07-13 15:16:36 +01:00
return fileSystem ( ) . minCapacity ( ) / sectorSize ( ) ;
2015-06-04 01:29:22 +01:00
}
/** @return the maximum number of sectors this Partition may be long */
qint64 Partition : : maximumSectors ( ) const
{
2015-07-13 15:16:36 +01:00
return fileSystem ( ) . maxCapacity ( ) / sectorSize ( ) ;
2015-06-04 01:29:22 +01:00
}
/** Adjusts the numbers of logical Partitions for an extended Partition.
2015-07-13 15:16:36 +01:00
This is required if a logical Partition is deleted or inserted because logicals must be numberd from
5 onwards without a gap . So if the user deletes Partition number 7 and there is a number 8 , 8 becomes the
" new " 7. And since this happens somewhere in the middle of a DeleteOperation , we have to adjust to that so the
next Job still finds the Partition it wants to deal with .
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
@ param deletedNumber the number of a deleted logical or - 1 if none has been deleted
@ param insertedNumber the number of an inserted logical or - 1 if none has been inserted
2015-06-04 01:29:22 +01:00
*/
void Partition : : adjustLogicalNumbers ( qint32 deletedNumber , qint32 insertedNumber )
{
2015-07-13 15:16:36 +01:00
if ( ! roles ( ) . has ( PartitionRole : : Extended ) )
return ;
foreach ( Partition * p , children ( ) ) {
QString path = p - > partitionPath ( ) ;
path . remove ( QRegExp ( QStringLiteral ( " ([0-9]+$) " ) ) ) ;
if ( deletedNumber > 4 & & p - > number ( ) > deletedNumber )
p - > setPartitionPath ( path + QString : : number ( p - > number ( ) - 1 ) ) ;
else if ( insertedNumber > 4 & & p - > number ( ) > = insertedNumber )
p - > setPartitionPath ( path + QString : : number ( p - > number ( ) + 1 ) ) ;
}
2015-06-04 01:29:22 +01:00
}
/** @return the highest sector number an extended Partition can begin at */
qint64 Partition : : maxFirstSector ( ) const
{
2015-07-13 15:16:36 +01:00
qint64 rval = - 1 ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
foreach ( const Partition * child , children ( ) )
if ( ! child - > roles ( ) . has ( PartitionRole : : Unallocated ) & & ( child - > firstSector ( ) < rval | | rval = = - 1 ) )
rval = child - > firstSector ( ) ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
return rval ;
2015-06-04 01:29:22 +01:00
}
/** @return the lowest sector number an extended Partition can end at */
qint64 Partition : : minLastSector ( ) const
{
2015-07-13 15:16:36 +01:00
qint64 rval = - 1 ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
foreach ( const Partition * child , children ( ) )
if ( ! child - > roles ( ) . has ( PartitionRole : : Unallocated ) & & child - > lastSector ( ) > rval )
rval = child - > lastSector ( ) ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
return rval ;
2015-06-04 01:29:22 +01:00
}
/** @return true if the Partition has children */
bool Partition : : hasChildren ( ) const
{
2015-07-13 15:16:36 +01:00
foreach ( const Partition * child , children ( ) )
if ( ! child - > roles ( ) . has ( PartitionRole : : Unallocated ) )
return true ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
return false ;
2015-06-04 01:29:22 +01:00
}
/** Sets an extended Partition to mounted if any of its children are mounted */
void Partition : : checkChildrenMounted ( )
{
2015-07-13 15:16:36 +01:00
setMounted ( isChildMounted ( ) ) ;
2015-06-04 01:29:22 +01:00
}
/** @return true if this Partition can be mounted */
bool Partition : : canMount ( ) const
{
2015-07-13 15:16:36 +01:00
// cannot mount if already mounted
if ( isMounted ( ) )
return false ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
// if the file system says we can mount without mount points, that's fine
// (this is the case for swap only, actually)
if ( fileSystem ( ) . canMount ( deviceNode ( ) ) )
return true ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
// cannot mount if we have no mount points
return ! mountPoint ( ) . isEmpty ( ) ;
2015-06-04 01:29:22 +01:00
}
/** @return true if this Partition can be unmounted */
bool Partition : : canUnmount ( ) const
{
2015-07-13 15:16:36 +01:00
return ! roles ( ) . has ( PartitionRole : : Extended ) & & isMounted ( ) ;
2015-06-04 01:29:22 +01:00
}
/** Tries to mount a Partition.
2015-07-13 15:16:36 +01:00
@ return true on success
2015-06-04 01:29:22 +01:00
*/
bool Partition : : mount ( Report & report )
{
2015-07-13 15:16:36 +01:00
if ( isMounted ( ) )
return false ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
bool success = false ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
if ( fileSystem ( ) . canMount ( deviceNode ( ) ) )
success = fileSystem ( ) . mount ( deviceNode ( ) ) ;
else {
ExternalCommand mountCmd ( report , QStringLiteral ( " mount " ) , QStringList ( ) < < QStringLiteral ( " -v " ) < < deviceNode ( ) < < mountPoint ( ) ) ;
if ( mountCmd . run ( ) & & mountCmd . exitCode ( ) = = 0 )
success = true ;
}
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
setMounted ( success ) ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
return success ;
2015-06-04 01:29:22 +01:00
}
/** Tries to unmount a Partition.
2015-07-13 15:16:36 +01:00
@ return true on success
2015-06-04 01:29:22 +01:00
*/
bool Partition : : unmount ( Report & report )
{
2015-07-13 15:16:36 +01:00
if ( ! isMounted ( ) )
return false ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
bool success = true ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
while ( success ) {
if ( fileSystem ( ) . canUnmount ( deviceNode ( ) ) ) {
success = fileSystem ( ) . unmount ( deviceNode ( ) ) ;
if ( success )
setMountPoint ( QString ( ) ) ;
} else {
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
ExternalCommand umountCmd ( report , QStringLiteral ( " umount " ) , QStringList ( ) < < QStringLiteral ( " -v " ) < < deviceNode ( ) ) ;
if ( ! umountCmd . run ( ) | | umountCmd . exitCode ( ) ! = 0 )
success = false ;
}
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
KMountPoint : : List mountPoints = KMountPoint : : currentMountPoints ( KMountPoint : : NeedRealDeviceName ) ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
if ( ! mountPoints . findByDevice ( deviceNode ( ) ) )
break ;
}
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
setMounted ( ! success ) ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
return success ;
2015-06-04 01:29:22 +01:00
}
void Partition : : deleteFileSystem ( )
{
2015-07-13 15:16:36 +01:00
delete m_FileSystem ;
2015-07-22 14:48:03 +01:00
m_FileSystem = nullptr ;
2015-06-04 01:29:22 +01:00
}
void Partition : : setPartitionPath ( const QString & s )
{
2015-07-13 15:16:36 +01:00
m_PartitionPath = s ;
QRegExp rxPartitionNumber ( QStringLiteral ( " ([0-9]+$) " )) ;
if ( rxPartitionNumber . indexIn ( partitionPath ( ) ) > - 1 ) {
setNumber ( rxPartitionNumber . cap ( ) . toInt ( ) ) ;
return ;
}
setNumber ( - 1 ) ;
2015-06-04 01:29:22 +01:00
}
void Partition : : setFileSystem ( FileSystem * fs )
{
2015-07-13 15:16:36 +01:00
m_FileSystem = fs ;
2015-06-04 01:29:22 +01:00
}
void Partition : : move ( qint64 newStartSector )
{
2015-07-13 15:16:36 +01:00
const qint64 savedLength = length ( ) ;
setFirstSector ( newStartSector ) ;
setLastSector ( newStartSector + savedLength - 1 ) ;
2015-06-04 01:29:22 +01:00
}
QTextStream & operator < < ( QTextStream & stream , const Partition & p )
{
2015-07-13 15:16:36 +01:00
QStringList flagList ;
foreach ( const PartitionTable : : Flag & f , PartitionTable : : flagList ( ) ) {
if ( p . activeFlags ( ) & f )
flagList . append ( PartitionTable : : flagName ( f ) ) ;
}
const QString sep ( QStringLiteral ( " ; " ) ) ;
// number - start - end - type - roles - label - flags
stream < < p . number ( ) < < sep
< < p . firstSector ( ) < < sep
< < p . lastSector ( ) < < sep
< < p . fileSystem ( ) . name ( ) < < sep
< < p . roles ( ) . toString ( ) < < sep
< < " \" " < < p . fileSystem ( ) . label ( ) < < QStringLiteral ( " \" " ) < < sep
< < " \" " < < flagList . join ( QStringLiteral ( " , " ) ) < < QStringLiteral ( " \" " )
< < " \n " ;
return stream ;
2015-06-04 01:29:22 +01:00
}