2015-06-04 01:29:22 +01:00
/*************************************************************************
* Copyright ( C ) 2008 by Volker Lanz < vl @ fidra . de > *
2016-03-31 17:43:38 +01:00
* Copyright ( C ) 2015 by Teo Mrnjavac < teo @ kde . org > *
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 "core/partition.h"
# include "core/device.h"
# include "fs/filesystem.h"
# include "fs/filesystemfactory.h"
2016-10-30 02:31:46 +00:00
# include "fs/luks.h"
2015-06-04 01:29:22 +01:00
# include "util/externalcommand.h"
# include "util/report.h"
2016-05-12 16:37:37 +01:00
# include <QRegularExpression>
2017-09-08 17:58:27 +01:00
# include <QStorageInfo>
2015-06-04 01:29:22 +01:00
# include <QString>
# include <QStringList>
2016-10-30 02:37:48 +00:00
# include <QTextStream>
2015-06-04 01:29:22 +01:00
# include <KLocalizedString>
/** 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_State ( state )
2015-06-04 01:29:22 +01:00
{
2015-07-13 15:16:36 +01:00
setPartitionPath ( partitionPath ) ;
Q_ASSERT ( m_Parent ) ;
2016-05-31 19:28:31 +01:00
m_SectorSize = device . logicalSize ( ) ;
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.
2016-04-27 15:52:37 +01:00
if ( m_Parent )
parent ( ) - > remove ( this ) ;
2015-07-13 15:16:36 +01:00
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
*/
2016-12-01 18:00:15 +00:00
Partition : : Partition ( const Partition & other , PartitionNode * parent ) :
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
{
2016-12-01 18:00:15 +00:00
if ( parent )
m_Parent = parent ;
2015-07-13 15:16:36 +01:00
setPartitionPath ( other . m_PartitionPath ) ;
2016-08-11 14:26:54 +01:00
for ( const auto & child : other . children ( ) ) {
2016-12-01 18:00:15 +00:00
Partition * p = new Partition ( * child , this ) ;
2015-07-13 15:16:36 +01:00
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 ( ) ;
2016-08-11 14:26:54 +01:00
for ( const auto & child : other . children ( ) ) {
2015-07-13 15:16:36 +01:00
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 ) )
2016-07-17 23:41:00 +01:00
return xi18nc ( " @item partition name " , " unallocated " ) ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
if ( state ( ) = = StateNew )
2016-07-17 23:41:00 +01:00
return xi18nc ( " @item partition name " , " New Partition " ) ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
if ( state ( ) = = StateRestore )
2016-07-17 23:41:00 +01:00
return xi18nc ( " @item partition name " , " Restored Partition " ) ;
2015-06-04 01:29:22 +01:00
2015-07-13 15:16:36 +01:00
if ( state ( ) = = StateCopy )
2016-07-17 23:41:00 +01:00
return xi18nc ( " @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
{
2016-11-07 17:49:30 +00:00
// Make sure file system exists. In some cases (due to bugs elsewhere?) file system pointer did not exist, especially for unallocated space.
2016-11-07 17:45:22 +00:00
if ( m_FileSystem = = nullptr )
return - 1 ;
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 ;
2016-08-11 14:26:54 +01:00
for ( const auto & p : children ( ) )
2016-06-01 21:00:31 +01:00
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
{
2016-05-20 20:13:11 +01:00
if ( roles ( ) . has ( PartitionRole : : Luks ) )
2016-07-23 13:28:46 +01:00
return ( fileSystem ( ) . minCapacity ( ) + ( 4096 * 512 ) ) / sectorSize ( ) ; // 4096 is the default cryptsetup payload offset
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
*/
2016-08-11 14:26:54 +01:00
void Partition : : adjustLogicalNumbers ( qint32 deletedNumber , qint32 insertedNumber ) const
2015-06-04 01:29:22 +01:00
{
2015-07-13 15:16:36 +01:00
if ( ! roles ( ) . has ( PartitionRole : : Extended ) )
return ;
2016-08-11 14:26:54 +01:00
for ( const auto & p : children ( ) ) {
2015-07-13 15:16:36 +01:00
QString path = p - > partitionPath ( ) ;
2016-05-12 16:37:37 +01:00
path . remove ( QRegularExpression ( QStringLiteral ( " ( \\ d+$) " ) ) ) ;
2015-07-13 15:16:36 +01:00
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
2016-08-11 14:26:54 +01:00
for ( const auto & child : children ( ) )
2016-06-01 21:00:31 +01:00
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
2016-08-11 14:26:54 +01:00
for ( const auto & child : children ( ) )
2016-06-01 21:00:31 +01:00
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
{
2016-08-11 14:26:54 +01:00
for ( const auto & child : children ( ) )
2016-06-01 21:00:31 +01:00
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
2016-05-13 17:58:05 +01:00
if ( isMounted ( ) ) {
2015-07-13 15:16:36 +01:00
return false ;
2016-05-13 17:58:05 +01:00
}
2015-06-04 01:29:22 +01:00
2016-05-13 17:58:05 +01:00
if ( fileSystem ( ) . canMount ( deviceNode ( ) , mountPoint ( ) ) ) {
2015-07-13 15:16:36 +01:00
return true ;
2016-05-13 17:58:05 +01:00
}
2015-06-04 01:29:22 +01:00
2016-05-13 17:58:05 +01:00
return false ;
2015-06-04 01:29:22 +01:00
}
/** @return true if this Partition can be unmounted */
bool Partition : : canUnmount ( ) const
{
2016-10-30 02:31:46 +00:00
return ! roles ( ) . has ( PartitionRole : : Extended ) & & isMounted ( ) & & fileSystem ( ) . canUnmount ( deviceNode ( ) ) ;
}
void Partition : : setMounted ( bool b ) {
m_IsMounted = b ;
if ( roles ( ) . has ( PartitionRole : : Luks ) )
static_cast < FS : : luks * > ( m_FileSystem ) - > setMounted ( b ) ;
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
2016-05-13 17:58:05 +01:00
if ( fileSystem ( ) . canMount ( deviceNode ( ) , mountPoint ( ) ) ) {
success = fileSystem ( ) . mount ( report , deviceNode ( ) , mountPoint ( ) ) ;
2015-07-13 15:16:36 +01:00
}
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
2017-09-08 17:58:27 +01:00
bool success = false ;
2015-06-04 01:29:22 +01:00
2017-09-08 17:58:27 +01:00
if ( fileSystem ( ) . canUnmount ( deviceNode ( ) ) ) {
success = fileSystem ( ) . unmount ( report , deviceNode ( ) ) ;
}
2015-06-04 01:29:22 +01:00
2017-09-08 17:58:27 +01:00
const QList < QStorageInfo > mountedVolumes = QStorageInfo : : mountedVolumes ( ) ;
2017-09-14 01:27:38 +01:00
for ( const QStorageInfo & storage : mountedVolumes ) {
2017-09-08 17:58:27 +01:00
if ( QString : : fromUtf8 ( storage . device ( ) ) = = deviceNode ( ) ) {
success = false ;
2015-07-13 15:16:36 +01:00
break ;
2017-09-08 17:58:27 +01:00
}
2015-07-13 15:16:36 +01:00
}
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 ;
2016-05-12 16:46:25 +01:00
QRegularExpression re ( QStringLiteral ( " ( \\ d+$) " )) ;
QRegularExpressionMatch rePartitionNumber = re . match ( partitionPath ( ) ) ;
if ( rePartitionNumber . hasMatch ( ) ) {
setNumber ( rePartitionNumber . captured ( ) . toInt ( ) ) ;
2015-07-13 15:16:36 +01:00
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 ;
2016-08-11 14:26:54 +01:00
for ( const auto & f : PartitionTable : : flagList ( ) ) {
2015-07-13 15:16:36 +01:00
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
2017-09-15 12:47:01 +01:00
< < p . fileSystem ( ) . name ( { QStringLiteral ( " C " ) } ) < < sep
2015-07-13 15:16:36 +01:00
< < p . roles ( ) . toString ( ) < < sep
< < " \" " < < p . fileSystem ( ) . label ( ) < < QStringLiteral ( " \" " ) < < sep
< < " \" " < < flagList . join ( QStringLiteral ( " , " ) ) < < QStringLiteral ( " \" " )
< < " \n " ;
return stream ;
2015-06-04 01:29:22 +01:00
}