Don't show intrinsic unallocated space (#499202)

Most file systems report intrinsic unallocated space using the statvfs()
system call when mounted, but not using their own tools.  They are:
ext2/3/4, fat16/32, hfs, nilfs2, reiserfs and xfs.  Showing either a
little or no unallocated space, depending on whether a file system is
mounted or not, could be confusing to the user.

When all file systems are created filling their partitions the unused
figure reported by statvfs() and their own tools are the same or very
close.  Also the used plus unallocated figure from statvfs() agrees with
the used figure from their own tools.

For all file systems don't display intrinsic unallocated space (that
below the threshold of 2 to 5%), instead include it as used space.  As
soon as the amount of unallocated space becomes significant display it
everywhere and also trigger the warning.

For display purposes always use the new Partition methods:
get_sectors_used(), get_sectors_unused(), and get_sectors_unallocated().
When calculating new usage figures during Paste and Resize/Move
operations directly access sectors_used, sectors_unused and
sectors_unallocated members.

Bug #499202 - gparted does not see the difference if partition size
              differs from filesystem size
This commit is contained in:
Mike Fleetwood 2012-06-15 13:16:30 +01:00 committed by Curtis Gedak
parent b5c80f18a9
commit 7ebedc4bb3
9 changed files with 115 additions and 48 deletions

25
HACKING
View File

@ -24,10 +24,15 @@ partition usage and unallocated space
===================================== =====================================
After addressing bug #499202 GParted now also displays unallocated space After addressing bug #499202 GParted now also displays unallocated space
as well as used and unused space for recognised file systems. The as well as used and unused space for recognised file systems. Many file
unallocated space is shown graphically and the numeric figure is shown systems which actually fill their partition report a small amount of
in the Information Dialog if it is greater than zero. Additionally a intrinsic unallocated space when mounted, but not when unmounted. To
warning is reported if the unallocated space is considered too much. avoid confusing the user and unnecessarily alarming them don't show
unallocated space below a threshold, 2 to 5% depending on partition
size. It is just included in the used figure instead. (This is
effectively how GParted behaved before bug #499202 was addressed). When
above the threshold unallocated space is shown graphically, the numeric
figure is shown in the Information Dialog and a is warning displayed.
See the code and commit messages for more details. See the code and commit messages for more details.
Worked example of partition and file system usage for a newly created Worked example of partition and file system usage for a newly created
@ -54,8 +59,14 @@ used = fs_size - fs_free = 407128 - 406808 = 320 = 160.00 KiB
unused = fs_free = 406808 = 198.64 MiB unused = fs_free = 406808 = 198.64 MiB
unallocated = ptn_size - fs_size = 409600 - 407128 = 2472 = 1.21 MiB unallocated = ptn_size - fs_size = 409600 - 407128 = 2472 = 1.21 MiB
(Threshold %age for 200 MiB partition is approximately 4.675%)
threshold = ptn_size * pct = 19150 = 9.35 MiB
(Unallocated is below threshold so is included in used figure)
disp_used = used + unallocated = 320 + 2472 = 2792 = 1.36 MiB
disp_unused = unused = 406808 = 198.64 MiB
Figures displayed in the Information dialog: Figures displayed in the Information dialog:
Size: 200.00 MiB Size: 200.00 MiB
Used: 160.00 KiB ( 0% ) Used: 1.36 MiB ( 1% )
Unused: 198.64 MiB ( 99% ) Unused: 198.64 KiB ( 99% )
Unallocated: 1.21 MiB ( 1% )

View File

@ -73,8 +73,11 @@ public:
bool busy ) ; bool busy ) ;
void set_sector_usage( Sector sectors_fs_size, Sector sectors_fs_unused ) ; void set_sector_usage( Sector sectors_fs_size, Sector sectors_fs_unused ) ;
bool significant_unallocated_space() const ; bool sector_usage_known() const ;
Sector estimated_min_size() const ; Sector estimated_min_size() const ;
Sector get_sectors_used() const ;
Sector get_sectors_unused() const ;
Sector get_sectors_unallocated() const ;
void Set_Unallocated( const Glib::ustring & device_path, void Set_Unallocated( const Glib::ustring & device_path,
Sector sector_start, Sector sector_start,
@ -116,6 +119,7 @@ public:
Sector sectors_used; Sector sectors_used;
Sector sectors_unused; Sector sectors_unused;
Sector sectors_unallocated; //Difference between the size of the partition and the file system Sector sectors_unallocated; //Difference between the size of the partition and the file system
Sector significant_threshold; //Threshold from intrinsic to significant unallocated sectors
Gdk::Color color; Gdk::Color color;
bool inside_extended; bool inside_extended;
bool busy; bool busy;
@ -131,7 +135,7 @@ public:
private: private:
void sort_paths_and_remove_duplicates() ; void sort_paths_and_remove_duplicates() ;
Sector get_significant_unallocated_sectors() const ; Sector calc_significant_unallocated_sectors() const ;
static bool compare_paths( const Glib::ustring & A, const Glib::ustring & B ) ; static bool compare_paths( const Glib::ustring & A, const Glib::ustring & B ) ;

View File

@ -136,10 +136,10 @@ void Dialog_Partition_Info::init_drawingarea()
unused = 0 ; unused = 0 ;
unallocated = 400 - BORDER *2 ; unallocated = 400 - BORDER *2 ;
} }
else if ( partition .sectors_used >= 0 && partition .sectors_unused >= 0 ) else if ( partition .sector_usage_known() )
{ {
used = Utils::round( ( 400 - BORDER *2 ) / ( dlength / partition .sectors_used ) ) ; used = Utils::round( ( 400 - BORDER *2 ) / ( dlength / partition .get_sectors_used() ) ) ;
unused = Utils::round( ( 400 - BORDER *2 ) / ( dlength / partition .sectors_unused ) ) ; unused = Utils::round( ( 400 - BORDER *2 ) / ( dlength / partition .get_sectors_unused() ) ) ;
unallocated = 400 - BORDER *2 - used - unused ; unallocated = 400 - BORDER *2 - used - unused ;
} }
else else
@ -202,11 +202,14 @@ void Dialog_Partition_Info::Display_Info()
top++, bottom++, top++, bottom++,
Gtk::FILL ) ; Gtk::FILL ) ;
if ( partition.sectors_used != -1 ) if ( partition .sector_usage_known() )
{ {
Sector used = partition .get_sectors_used() ;
Sector unused = partition .get_sectors_unused() ;
Sector unallocated = partition .get_sectors_unallocated() ;
//calculate relative diskusage //calculate relative diskusage
int percent_unused = Utils::round( partition .sectors_unused * 100.0 / ptn_sectors ) ; int percent_unused = Utils::round( unused * 100.0 / ptn_sectors ) ;
int percent_unallocated = Utils::round( partition .sectors_unallocated * 100.0 / ptn_sectors ) ; int percent_unallocated = Utils::round( unallocated * 100.0 / ptn_sectors ) ;
int percent_used = 100 - percent_unallocated - percent_unused ; int percent_used = 100 - percent_unallocated - percent_unused ;
//Used //Used
@ -214,7 +217,7 @@ void Dialog_Partition_Info::Display_Info()
0, 1, 0, 1,
top, bottom, top, bottom,
Gtk::FILL ) ; Gtk::FILL ) ;
table ->attach( * Utils::mk_label( Utils::format_size( partition .sectors_used, partition .sector_size ), true, Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false, true ), table ->attach( * Utils::mk_label( Utils::format_size( used, partition .sector_size ), true, Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false, true ),
1, 2, 1, 2,
top, bottom, top, bottom,
Gtk::FILL ) ; Gtk::FILL ) ;
@ -228,7 +231,7 @@ void Dialog_Partition_Info::Display_Info()
0, 1, 0, 1,
top, bottom, top, bottom,
Gtk::FILL ) ; Gtk::FILL ) ;
table ->attach( * Utils::mk_label( Utils::format_size( partition .sectors_unused, partition .sector_size ), true, Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false, true ), table ->attach( * Utils::mk_label( Utils::format_size( unused, partition .sector_size ), true, Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false, true ),
1, 2, 1, 2,
top, bottom, top, bottom,
Gtk::FILL ) ; Gtk::FILL ) ;
@ -238,13 +241,13 @@ void Dialog_Partition_Info::Display_Info()
Gtk::FILL ) ; Gtk::FILL ) ;
//unallocated //unallocated
if ( partition .sectors_unallocated > 0 ) if ( unallocated > 0 )
{ {
table ->attach( * Utils::mk_label( "<b>" + Glib::ustring( _("Unallocated:") ) + "</b>" ), table ->attach( * Utils::mk_label( "<b>" + Glib::ustring( _("Unallocated:") ) + "</b>" ),
0, 1, 0, 1,
top, bottom, top, bottom,
Gtk::FILL ) ; Gtk::FILL ) ;
table ->attach( * Utils::mk_label( Utils::format_size( partition .sectors_unallocated, partition .sector_size ), true, Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false, true ), table ->attach( * Utils::mk_label( Utils::format_size( unallocated, partition .sector_size ), true, Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false, true ),
1, 2, 1, 2,
top, bottom, top, bottom,
Gtk::FILL ) ; Gtk::FILL ) ;

View File

@ -63,7 +63,7 @@ void Dialog_Partition_Resize_Move::Set_Data( const Partition & selected_partitio
void Dialog_Partition_Resize_Move::Resize_Move_Normal( const std::vector<Partition> & partitions ) void Dialog_Partition_Resize_Move::Resize_Move_Normal( const std::vector<Partition> & partitions )
{ {
//little bit of paranoia ;) //little bit of paranoia ;)
if ( selected_partition .sectors_used == -1 && if ( ! selected_partition .sector_usage_known() &&
selected_partition .status != STAT_NEW && selected_partition .status != STAT_NEW &&
selected_partition .filesystem != FS_LINUX_SWAP ) selected_partition .filesystem != FS_LINUX_SWAP )
fs .shrink = GParted::FS::NONE ; fs .shrink = GParted::FS::NONE ;

View File

@ -108,20 +108,21 @@ void DrawingAreaVisualDisk::set_static_data( const std::vector<Partition> & part
{ {
//Use sum_sectors as the denominator to calculate fraction_used and //Use sum_sectors as the denominator to calculate fraction_used and
// fraction_unallocated in case it doesn't equal partition_length. // fraction_unallocated in case it doesn't equal partition_length.
Sector sum_sectors = partitions[ t ] .sectors_used Sector used = partitions[ t ] .get_sectors_used() ;
+ partitions[ t ] .sectors_unused Sector unused = partitions[ t ] .get_sectors_unused() ;
+ partitions[ t ] .sectors_unallocated ; Sector unallocated = partitions[ t ] .get_sectors_unallocated() ;
if ( partitions[ t ] .sectors_unallocated > 0 ) Sector sum_sectors = used + unused + unallocated ;
if ( unallocated > 0 )
visual_partitions .back() .fraction_unallocated = visual_partitions .back() .fraction_unallocated =
partitions[ t ] .sectors_unallocated / static_cast<double>( sum_sectors ) ; unallocated / static_cast<double>( sum_sectors ) ;
else else
visual_partitions .back() .fraction_unallocated = 0.0 ; visual_partitions .back() .fraction_unallocated = 0.0 ;
//Calculate fraction used from free space so any hidden overhead is counted as used //Calculate fraction used from free space so any hidden overhead is counted as used
if ( partitions[ t ] .sectors_unused >= 0 ) if ( unused >= 0 )
visual_partitions .back() .fraction_used = visual_partitions .back() .fraction_used =
1.0 - visual_partitions .back() .fraction_unallocated 1.0 - visual_partitions .back() .fraction_unallocated
- partitions[ t ] .sectors_unused / static_cast<double>( sum_sectors ) ; - unused / static_cast<double>( sum_sectors ) ;
} }
visual_partitions .back() .color = partitions[ t ] .color; visual_partitions .back() .color = partitions[ t ] .color;

View File

@ -604,6 +604,11 @@ bool GParted_Core::snap_to_alignment( const Device & device, Partition & partiti
return false ; return false ;
} }
//FIXME: I think that this if condition should be impossible because Partition::set_sector_usage(),
// and ::set_used() and ::Set_Unused() before that, don't allow setting file usage figures to be
// larger than the partition size. A btrfs file system spanning muiltiple partitions will have
// usage figures larger than any single partition but the figures will won't be set because of
// the above reasoning. Confirm condition is impossible and consider removing this code.
if ( partition .get_sector_length() < partition .sectors_used ) if ( partition .get_sector_length() < partition .sectors_used )
{ {
error = String::ucompose( error = String::ucompose(
@ -1522,7 +1527,8 @@ void GParted_Core::set_used_sectors( std::vector<Partition> & partitions )
} }
} }
if ( partitions[ t ] .sectors_used == -1 ) Sector unallocated ;
if ( ! partitions[ t ] .sector_usage_known() )
{ {
temp = _("Unable to read the contents of this file system!") ; temp = _("Unable to read the contents of this file system!") ;
temp += "\n" ; temp += "\n" ;
@ -1540,11 +1546,11 @@ void GParted_Core::set_used_sectors( std::vector<Partition> & partitions )
} }
partitions[ t ] .messages .push_back( temp ) ; partitions[ t ] .messages .push_back( temp ) ;
} }
else if ( partitions[ t ] .significant_unallocated_space() ) else if ( ( unallocated = partitions[ t ] .get_sectors_unallocated() ) > 0 )
{ {
/* TO TRANSLATORS: looks like 1.28GiB of unallocated space within the partition. */ /* TO TRANSLATORS: looks like 1.28GiB of unallocated space within the partition. */
temp = String::ucompose( _("%1 of unallocated space within the partition."), temp = String::ucompose( _("%1 of unallocated space within the partition."),
Utils::format_size( partitions[ t ] .sectors_unallocated, partitions[ t ] .sector_size ) ) ; Utils::format_size( unallocated, partitions[ t ] .sector_size ) ) ;
FS fs = get_fs( partitions[ t ] .filesystem ) ; FS fs = get_fs( partitions[ t ] .filesystem ) ;
if ( fs .check != GParted::FS::NONE if ( fs .check != GParted::FS::NONE
&& fs .grow != GParted::FS::NONE ) && fs .grow != GParted::FS::NONE )

View File

@ -45,6 +45,7 @@ void Partition::Reset()
uuid .clear() ; uuid .clear() ;
partition_number = sector_start = sector_end = sectors_used = sectors_unused = -1; partition_number = sector_start = sector_end = sectors_used = sectors_unused = -1;
sectors_unallocated = 0 ; sectors_unallocated = 0 ;
significant_threshold = 1 ;
free_space_before = -1 ; free_space_before = -1 ;
sector_size = 0 ; sector_size = 0 ;
color .set( "black" ) ; color .set( "black" ) ;
@ -93,6 +94,7 @@ void Partition::set_sector_usage( Sector sectors_fs_size, Sector sectors_fs_unus
sectors_used = sectors_fs_size - sectors_fs_unused ; sectors_used = sectors_fs_size - sectors_fs_unused ;
sectors_unused = sectors_fs_unused ; sectors_unused = sectors_fs_unused ;
sectors_unallocated = length - sectors_fs_size ; sectors_unallocated = length - sectors_fs_size ;
significant_threshold = calc_significant_unallocated_sectors() ;
} }
else if ( sectors_fs_size == -1 ) else if ( sectors_fs_size == -1 )
{ {
@ -107,14 +109,13 @@ void Partition::set_sector_usage( Sector sectors_fs_size, Sector sectors_fs_unus
sectors_unused = -1 ; sectors_unused = -1 ;
} }
sectors_unallocated = 0 ; sectors_unallocated = 0 ;
significant_threshold = 1 ;
} }
} }
bool Partition::significant_unallocated_space() const bool Partition::sector_usage_known() const
{ {
if ( get_sector_length() >= 0 && sectors_unallocated > 0 ) return sectors_used >= 0 && sectors_unused >= 0 ;
return sectors_unallocated >= get_significant_unallocated_sectors() ;
return false ;
} }
Sector Partition::estimated_min_size() const Sector Partition::estimated_min_size() const
@ -122,11 +123,45 @@ Sector Partition::estimated_min_size() const
//Add unallocated sectors up to the significant threshold, to //Add unallocated sectors up to the significant threshold, to
// account for any intrinsic unallocated sectors in the // account for any intrinsic unallocated sectors in the
// file systems minimum partition size. // file systems minimum partition size.
if ( sectors_used > 0 ) if ( sectors_used >= 0 )
return sectors_used + std::min( sectors_unallocated, get_significant_unallocated_sectors() ) ; return sectors_used + std::min( sectors_unallocated, significant_threshold ) ;
return -1 ; 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 ;
}
void Partition::Set_Unallocated( const Glib::ustring & device_path, void Partition::Set_Unallocated( const Glib::ustring & device_path,
Sector sector_start, Sector sector_start,
Sector sector_end, Sector sector_end,
@ -297,19 +332,21 @@ bool Partition::compare_paths( const Glib::ustring & A, const Glib::ustring & B
// 5% , ptn size <= 100 MiB // 5% , ptn size <= 100 MiB
// linear scaling from 5% down to 2%, 100 MiB < ptn size <= 1 GiB // linear scaling from 5% down to 2%, 100 MiB < ptn size <= 1 GiB
// 2% , 1 GiB < ptn size // 2% , 1 GiB < ptn size
Sector Partition::get_significant_unallocated_sectors() const Sector Partition::calc_significant_unallocated_sectors() const
{ {
const double HIGHER_UNALLOCATED_FRACTION = 0.05 ; const double HIGHER_UNALLOCATED_FRACTION = 0.05 ;
const double LOWER_UNALLOCATED_FRACTION = 0.02 ; const double LOWER_UNALLOCATED_FRACTION = 0.02 ;
Sector length = get_sector_length() ; Sector length = get_sector_length() ;
Byte_Value byte_len = length * sector_size ; Byte_Value byte_len = length * sector_size ;
Sector significant ;
if ( byte_len <= 0 ) if ( byte_len <= 0 )
{ {
return 0 ; significant = 1;
} }
else if ( byte_len <= 100 * MEBIBYTE ) else if ( byte_len <= 100 * MEBIBYTE )
{ {
return Utils::round( length * HIGHER_UNALLOCATED_FRACTION ) ; significant = Utils::round( length * HIGHER_UNALLOCATED_FRACTION ) ;
} }
else if ( byte_len <= 1 * GIBIBYTE ) else if ( byte_len <= 1 * GIBIBYTE )
{ {
@ -317,12 +354,15 @@ Sector Partition::get_significant_unallocated_sectors() const
( byte_len - 100 * MEBIBYTE ) * ( HIGHER_UNALLOCATED_FRACTION - LOWER_UNALLOCATED_FRACTION ) / ( byte_len - 100 * MEBIBYTE ) * ( HIGHER_UNALLOCATED_FRACTION - LOWER_UNALLOCATED_FRACTION ) /
( 1 * GIBIBYTE - 100 * MEBIBYTE ) + ( 1 * GIBIBYTE - 100 * MEBIBYTE ) +
LOWER_UNALLOCATED_FRACTION ; LOWER_UNALLOCATED_FRACTION ;
return Utils::round( length * fraction ) ; significant = Utils::round( length * fraction ) ;
} }
else else
{ {
return Utils::round( length * LOWER_UNALLOCATED_FRACTION ) ; significant = Utils::round( length * LOWER_UNALLOCATED_FRACTION ) ;
} }
if ( significant <= 1 )
significant = 1;
return significant ;
} }
Partition::~Partition() Partition::~Partition()

View File

@ -190,12 +190,14 @@ void TreeView_Detail::create_row( const Gtk::TreeRow & treerow, const Partition
treerow[ treeview_detail_columns .size ] = Utils::format_size( partition .get_sector_length(), partition .sector_size ) ; treerow[ treeview_detail_columns .size ] = Utils::format_size( partition .get_sector_length(), partition .sector_size ) ;
//used //used
Sector used = partition .get_sectors_used() ;
treerow[ treeview_detail_columns .used ] = treerow[ treeview_detail_columns .used ] =
partition .sectors_used == -1 ? "---" : Utils::format_size( partition .sectors_used, partition .sector_size ) ; used == -1 ? "---" : Utils::format_size( used, partition .sector_size ) ;
//unused //unused
Sector unused = partition .get_sectors_unused() ;
treerow[ treeview_detail_columns .unused ] = treerow[ treeview_detail_columns .unused ] =
partition .sectors_unused == -1 ? "---" : Utils::format_size( partition .sectors_unused, partition .sector_size ) ; unused == -1 ? "---" : Utils::format_size( unused, partition .sector_size ) ;
//flags //flags
treerow[ treeview_detail_columns .flags ] = treerow[ treeview_detail_columns .flags ] =

View File

@ -969,7 +969,7 @@ void Win_GParted::set_valid_operations()
{ {
Byte_Value required_size ; Byte_Value required_size ;
if ( copied_partition .filesystem == GParted::FS_XFS ) if ( copied_partition .filesystem == GParted::FS_XFS )
required_size = copied_partition .sectors_used * copied_partition .sector_size; required_size = copied_partition .estimated_min_size() * copied_partition .sector_size;
else else
required_size = copied_partition .get_byte_length() ; required_size = copied_partition .get_byte_length() ;