gparted/src/Partition.cc

508 lines
15 KiB
C++

/* Copyright (C) 2004 Bart
* Copyright (C) 2008, 2009, 2010 Curtis Gedak
*
* 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 2 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 "Partition.h"
namespace GParted
{
Partition::Partition()
{
Reset() ;
}
Partition * Partition::clone() const
{
// Virtual copy constructor method
// Reference:
// Wikibooks: More C++ Idioms / Virtual Constructor
// https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Virtual_Constructor
return new Partition( *this );
}
void Partition::Reset()
{
path.clear();
messages .clear() ;
status = STAT_REAL;
type = TYPE_UNALLOCATED;
alignment = ALIGN_STRICT ;
fstype = FS_UNALLOCATED;
have_filesystem_label = false;
uuid .clear() ;
name.clear();
partition_number = sector_start = sector_end = sectors_used = sectors_unused = -1;
sectors_unallocated = 0 ;
significant_threshold = 1 ;
free_space_before = -1 ;
sector_size = 0 ;
fs_block_size = -1;
inside_extended = busy = strict_start = false ;
fs_readonly = false;
logicals .clear() ;
flags .clear() ;
mountpoints .clear() ;
}
void Partition::Set( const Glib::ustring & device_path,
const Glib::ustring & partition,
int partition_number,
PartitionType type,
FSType fstype,
Sector sector_start,
Sector sector_end,
Byte_Value sector_size,
bool inside_extended,
bool busy )
{
this ->device_path = device_path ;
this->path = partition;
this ->partition_number = partition_number;
this ->type = type;
this->fstype = fstype;
this ->sector_start = sector_start;
this ->sector_end = sector_end;
this ->sector_size = sector_size;
this ->inside_extended = inside_extended;
this ->busy = busy;
}
//Set file system size and free space, which also calculates unallocated
// space. Set sectors_fs_size = -1 for unknown.
void Partition::set_sector_usage( Sector sectors_fs_size, Sector sectors_fs_unused )
{
Sector length = get_sector_length() ;
if ( 0 <= sectors_fs_size && sectors_fs_size <= length
&& 0 <= sectors_fs_unused && sectors_fs_unused <= sectors_fs_size
)
{
sectors_used = sectors_fs_size - sectors_fs_unused ;
sectors_unused = sectors_fs_unused ;
sectors_unallocated = length - sectors_fs_size ;
significant_threshold = calc_significant_unallocated_sectors() ;
}
else if ( sectors_fs_size == -1 )
{
if ( 0 <= sectors_fs_unused && sectors_fs_unused <= length )
{
sectors_used = length - sectors_fs_unused ;
sectors_unused = sectors_fs_unused ;
}
else
{
sectors_used = -1 ;
sectors_unused = -1 ;
}
sectors_unallocated = 0 ;
significant_threshold = 1 ;
}
}
bool Partition::sector_usage_known() const
{
return sectors_used >= 0 && sectors_unused >= 0 ;
}
Sector Partition::estimated_min_size() const
{
//Add unallocated sectors up to the significant threshold, to
// account for any intrinsic unallocated sectors in the
// file systems minimum partition size.
if ( sectors_used >= 0 )
return sectors_used + std::min( sectors_unallocated, significant_threshold ) ;
return -1 ;
}
//Return user displayable used sectors.
// Only add unallocated sectors up to the significant threshold to
// account for any intrinsic unallocated sectors in the file system.
// Above the threshold just return the used sectors figure.
Sector Partition::get_sectors_used() const
{
if ( sectors_used >= 0 )
{
if ( sectors_unallocated < significant_threshold )
return sectors_used + sectors_unallocated ;
else
return sectors_used ;
}
return -1 ;
}
//Return user displayable unused sectors.
Sector Partition::get_sectors_unused() const
{
return sectors_unused ;
}
//Return user displayable unallocated sectors.
// Return zero below the significant unallocated sectors threshold, as
// the figure has been added to the displayable used sectors. Above the
// threshold just return the unallocated sectors figure.
Sector Partition::get_sectors_unallocated() const
{
if ( sectors_unallocated < significant_threshold )
return 0 ;
else
return sectors_unallocated ;
}
// Update size, position and FS usage from new_size.
void Partition::resize( const Partition & new_size )
{
sector_start = new_size.sector_start;
sector_end = new_size.sector_end;
alignment = new_size.alignment;
free_space_before = new_size.free_space_before;
strict_start = new_size.strict_start;
Sector fs_size = new_size.sectors_used + new_size.sectors_unused;
Sector fs_unused = fs_size - new_size.sectors_used;
set_sector_usage( fs_size, fs_unused );
}
void Partition::Set_Unallocated( const Glib::ustring & device_path,
Sector sector_start,
Sector sector_end,
Byte_Value sector_size,
bool inside_extended )
{
Reset() ;
Set( device_path,
Utils::get_filesystem_string( FS_UNALLOCATED ),
-1,
TYPE_UNALLOCATED,
FS_UNALLOCATED,
sector_start,
sector_end,
sector_size,
inside_extended,
false );
status = STAT_REAL;
}
void Partition::set_unpartitioned( const Glib::ustring & device_path,
const Glib::ustring & partition_path,
FSType fstype,
Sector length,
Byte_Value sector_size,
bool busy )
{
Reset();
Set( device_path,
// The path from the parent Device object and this child Partition object
// spanning the whole device would appear to be the same. However the former
// may have come from the command line and the later is the canonicalised
// name returned from libparted. (See GParted_Core::set_device_from_disk()
// "loop" table and GParted_Core::set_device_one_partition() calls to
// set_unpartitioned()). Therefore they can be different.
//
// For unrecognised whole disk device partitions use "unallocated" as the
// partition path to match what Set_Unallocated() does when it created such
// whole disk device partitions.
( fstype == FS_UNALLOCATED ) ? Utils::get_filesystem_string( FS_UNALLOCATED )
: partition_path,
1,
TYPE_UNPARTITIONED,
fstype,
0LL,
length - 1LL,
sector_size,
false,
busy );
}
void Partition::Update_Number( int new_number )
{
unsigned int index = path.rfind( Utils::num_to_str( partition_number ) );
if ( index < path.length() )
path.replace( index,
Utils::num_to_str( partition_number ).length(),
Utils::num_to_str( new_number ) );
partition_number = new_number;
}
void Partition::set_path( const Glib::ustring & path )
{
this->path = path;
}
Byte_Value Partition::get_byte_length() const
{
if ( get_sector_length() >= 0 )
return get_sector_length() * sector_size ;
else
return -1 ;
}
Sector Partition::get_sector_length() const
{
if ( sector_start >= 0 && sector_end >= 0 )
return sector_end - sector_start + 1 ;
else
return -1 ;
}
const Glib::ustring& Partition::get_path() const
{
return path;
}
bool Partition::filesystem_label_known() const
{
return have_filesystem_label;
}
//Return the file system label or "" if unknown.
const Glib::ustring& Partition::get_filesystem_label() const
{
if ( have_filesystem_label )
return filesystem_label;
static Glib::ustring unknown_label;
return unknown_label;
}
void Partition::set_filesystem_label( const Glib::ustring & filesystem_label )
{
this->filesystem_label = filesystem_label;
have_filesystem_label = true;
}
bool Partition::operator==( const Partition & partition ) const
{
return device_path == partition .device_path &&
partition_number == partition .partition_number &&
sector_start == partition .sector_start &&
type == partition .type ;
}
bool Partition::operator!=( const Partition & partition ) const
{
return ! ( *this == partition ) ;
}
//Get the "best" display integers (pixels or percentages) from partition
// usage figures. Rounds the smaller two figures and then subtracts
// them from the desired total for the largest figure.
void Partition::get_usage_triple( int imax, int & i1, int & i2, int & i3 ) const
{
Sector s1 = get_sectors_used() ;
Sector s2 = get_sectors_unused() ;
Sector s3 = get_sectors_unallocated() ;
if ( s1 < 0 ) s1 = 0 ;
if ( s2 < 0 ) s2 = 0 ;
if ( s3 < 0 ) s3 = 0 ;
Sector stot = s1 + s2 + s3 ;
if ( s1 <= s2 && s1 <= s3 )
get_usage_triple_helper( stot, s1, s2, s3, imax, i1, i2, i3 ) ;
else if ( s2 < s1 && s2 <= s3 )
get_usage_triple_helper( stot, s2, s1, s3, imax, i2, i1, i3 ) ;
else if ( s3 < s1 && s3 < s2 )
get_usage_triple_helper( stot, s3, s1, s2, imax, i3, i1, i2 ) ;
}
//Calculate the "best" display integers when s1 <= s2 and s1 <= s3.
// Ensure i1 <= i2 and i1 <= i3.
void Partition::get_usage_triple_helper( Sector stot, Sector s1, Sector s2, Sector s3, int imax, int & i1, int & i2, int & i3 )
{
int t ;
i1 = Utils::round( static_cast<double>( s1 ) / stot * imax ) ;
if ( s2 <= s3 )
{
i2 = Utils::round( static_cast<double>( s2 ) / stot * imax ) ;
i3 = imax - i1 - i2 ;
if ( i1 > i3 )
{
// i1 rounded up making it larger than i3. Swap i1 with i3.
t = i1 ;
i1 = i3 ;
i3 = t ;
}
}
else
{
i3 = Utils::round( static_cast<double>( s3 ) / stot * imax ) ;
i2 = imax - i1 - i3 ;
if ( i1 > i2 )
{
// i1 rounded up making it larger than i2. Swap i1 with i2.
t = i1 ;
i1 = i2 ;
i2 = t ;
}
}
}
void Partition::add_mountpoint( const Glib::ustring & mountpoint )
{
this ->mountpoints .push_back( mountpoint ) ;
}
void Partition::add_mountpoints( const std::vector<Glib::ustring> & mountpoints )
{
this ->mountpoints .insert( this ->mountpoints .end(), mountpoints .begin(), mountpoints .end() ) ;
}
const Glib::ustring& Partition::get_mountpoint() const
{
if ( mountpoints .size() > 0 )
return mountpoints .front() ;
static Glib::ustring unknown_mountpoint;
return unknown_mountpoint;
}
const std::vector<Glib::ustring>& Partition::get_mountpoints() const
{
return mountpoints ;
}
Sector Partition::get_sector() const
{
return (sector_start + sector_end) / 2 ;
}
bool Partition::test_overlap( const Partition & partition ) const
{
return ( (partition .sector_start >= sector_start && partition .sector_start <= sector_end)
||
(partition .sector_end >= sector_start && partition .sector_end <= sector_end)
||
(partition .sector_start < sector_start && partition .sector_end > sector_end) ) ;
}
void Partition::clear_mountpoints()
{
mountpoints .clear() ;
}
const Glib::ustring Partition::get_partition_type_string(PartitionType type)
{
switch (type)
{
case TYPE_PRIMARY:
/* TO TRANSLATORS: Primary
* A "Primary" type of partition on a partitioned drive.
*/
return _("Primary");
case TYPE_LOGICAL:
/* TO TRANSLATORS: Logical
* A "Logical" type of partition on a partitioned drive.
*/
return _("Logical");
case TYPE_EXTENDED:
/* TO TRANSLATORS: Extended
* An "Extended" type of partition on a partitioned drive.
*/
return _("Extended");
case TYPE_UNALLOCATED:
/* TO TRANSLATORS: Unallocated
* Unused space outside of any partition on a partitioned drive.
*/
return _("Unallocated");
case TYPE_UNPARTITIONED:
/* TO TRANSLATORS: Unpartitioned
* A drive which has no partition table.
*/
return _("Unpartitioned");
default: return "";
}
}
// Return threshold of unallocated sectors below which the file system is considered to
// fill the partition, but at and above which is displayed to the user to show the file
// system doesn't fill the partition. Calculation is:
// %age of partition size , when
// 5% , ptn size <= 100 MiB
// linear scaling from 5% down to 2%, 100 MiB < ptn size <= 1 GiB
// 2% , 1 GiB < ptn size
//
// Quoting the commit message:
// "Enhance calculation of significant unallocated space (#499202)
//
// Many file systems report differing percentages of unallocated space over
// a range of sizes, as well differing figures using their own specific
// tools or using statvfs() system call when mounted.
//
// File systems reporting intrinsic unallocated space using their specific
// tools are: jfs, lvm2 pv and ntfs. LVM2 PV has the largest amount of
// unallocated space with its default Physical Extent size of 4 MiB. For a
// 100 MiB partition it has 4.0% unallocated space.
//
// File systems reporting intrinsic unallocated space using the statvfs()
// system call when mounted are: ext2/3/4, fat16/32, hfs, jfs, nilfs2,
// ntfs, reiserfs, and xfs. Xfs has the worst identified unallocated space
// of ~4.7% in a 100 MiB partition. Ext2/3 exhibit unusual behaviour by
// reporting unallocated space of ~4.6% in a 100 MiB partition falling to a
// constant percentage of ~1.8% for sizes of 1 GiB and above.
//
// Update the calculation for used to estimate the maximum size of
// intrinsic unallocated space. Limit is now 5% for partitions smaller
// than 100 MiB, 2% for partitions larger than 1 GiB and linear scaling of
// the percentage between. Will still get false unallocated space warnings
// for mounted xfs file systems and lvm2 pvs smaller than 100 MiB.
// "
Sector Partition::calc_significant_unallocated_sectors() const
{
const double HIGHER_UNALLOCATED_FRACTION = 0.05 ;
const double LOWER_UNALLOCATED_FRACTION = 0.02 ;
Sector length = get_sector_length() ;
Byte_Value byte_len = length * sector_size ;
Sector significant ;
if ( byte_len <= 0 )
{
significant = 1;
}
else if ( byte_len <= 100 * MEBIBYTE )
{
significant = Utils::round( length * HIGHER_UNALLOCATED_FRACTION ) ;
}
else if ( byte_len <= 1 * GIBIBYTE )
{
double fraction = ( HIGHER_UNALLOCATED_FRACTION - LOWER_UNALLOCATED_FRACTION ) -
( byte_len - 100 * MEBIBYTE ) * ( HIGHER_UNALLOCATED_FRACTION - LOWER_UNALLOCATED_FRACTION ) /
( 1 * GIBIBYTE - 100 * MEBIBYTE ) +
LOWER_UNALLOCATED_FRACTION ;
significant = Utils::round( length * fraction ) ;
}
else
{
significant = Utils::round( length * LOWER_UNALLOCATED_FRACTION ) ;
}
if ( significant <= 1 )
significant = 1;
return significant ;
}
Partition::~Partition()
{
}
} //GParted