[gparted] Read file system size for mounted ext2/3/4 from superblock (#683255)



commit 3828019030f981cc5b3411badc40704f5525fdb3
Author: Mike Fleetwood <mike fleetwood googlemail com>
Date:   Wed Sep 12 23:51:49 2012 +0100

    Read file system size for mounted ext2/3/4 from superblock (#683255)
    
    A user had a 190 MiB partition containing an old ext2 file system.  When
    unmounted it was reported as filling the partition, but when mounted it
    was reported as having 6% unallocated space.  The file system's inode
    tables were approximately twice the size of those created by default
    with the current mkfs.ext2 defaults.
    
    To create an equivalent file system in a 190 MiB partition:
        mkfs.ext2 -N 97344 /dev/sda15
    
    It turns out that for ext2, ext3 and ext4 file systems what was
    described as intrinsic unallocated space during the development of
    Bug #499202 is actually file system overhead.  When mounted the ext2/3/4
    kernel code reports the size of the file system after subtracting the
    overhead.  Overhead is made up of superblock backups, group descriptors,
    allocation bitmaps and largest of all inode tables.  E2fsprogs tools
    don't subtract this overhead when reporting the file system size.
    
    References:
    
     *  The Second Extended File System, Internal Layout, by Dave Poirier
        http://www.nongnu.org/ext2-doc/ext2.html
    
     *  Linux ext2_statfs() function
        http://lxr.linux.no/#linux+v3.5.3/fs/ext2/super.c#L1311
    
    Call the file system specific method for reading the usage of ext2, ext3
    and ext4 file systems while mounted.  Make it read the file system size
    from the on disk superblock to avoid subtraction of overhead and use the
    statvfs() system call to return an up to date free space figure.
    
    Bug #683255 - ext2: statvfs differs from dumpe2fs (x MB unallocated
                  space within the partition)

 include/Utils.h     |    3 +++
 src/GParted_Core.cc |   19 ++++++++-----------
 src/Utils.cc        |   22 ++++++++++++++++++++++
 src/ext2.cc         |   43 +++++++++++++++++++++++++++++++++----------
 src/ext3.cc         |   43 +++++++++++++++++++++++++++++++++----------
 src/ext4.cc         |   43 +++++++++++++++++++++++++++++++++----------
 6 files changed, 132 insertions(+), 41 deletions(-)
---
diff --git a/include/Utils.h b/include/Utils.h
index 116d7f4..c0e00b6 100644
--- a/include/Utils.h
+++ b/include/Utils.h
@@ -188,6 +188,9 @@ public:
 	                   const Glib::ustring& delimiters     ) ;
 	static int convert_to_int(const Glib::ustring & src);
 	static Glib::ustring generate_uuid(void);
+	static int get_mounted_filesystem_usage( const Glib::ustring & mountpoint,
+	                                         Byte_Value & fs_size, Byte_Value & fs_free,
+	                                         Glib::ustring error_message ) ;
 
 private:
 	static bool get_kernel_version( int & major_ver, int & minor_ver, int & patch_ver ) ;
diff --git a/src/GParted_Core.cc b/src/GParted_Core.cc
index d5f2b5b..88f7be8 100644
--- a/src/GParted_Core.cc
+++ b/src/GParted_Core.cc
@@ -52,7 +52,6 @@
 #include <set>
 #include <cerrno>
 #include <cstring>
-#include <sys/statvfs.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
@@ -1597,19 +1596,17 @@ void GParted_Core::set_used_sectors( std::vector<Partition> & partitions )
 
 void GParted_Core::mounted_set_used_sectors( Partition & partition )
 {
-	struct statvfs sfs ;
-
 	if ( partition .get_mountpoints() .size() > 0 )
 	{
-		if ( statvfs( partition .get_mountpoint() .c_str(), &sfs ) == 0 )
-		{
-			Sector fs_size = static_cast<Sector>( sfs .f_blocks ) * sfs .f_frsize / partition .sector_size ;
-			Sector fs_free = static_cast<Sector>( sfs .f_bfree ) * sfs .f_bsize / partition .sector_size ;
-			partition .set_sector_usage( fs_size, fs_free ) ;
-		}
+		Byte_Value fs_size ;
+		Byte_Value fs_free ;
+		Glib::ustring error_message ;
+		if ( Utils::get_mounted_filesystem_usage( partition .get_mountpoint(),
+		                                          fs_size, fs_free, error_message ) == 0 )
+			partition .set_sector_usage( fs_size / partition .sector_size,
+			                             fs_free / partition .sector_size ) ;
 		else
-			partition .messages .push_back( "statvfs (" + partition .get_mountpoint() + "): " +
-			                                Glib::strerror( errno ) ) ;
+			partition .messages .push_back( error_message ) ;
 	}
 }
 
diff --git a/src/Utils.cc b/src/Utils.cc
index 91196cd..affbee4 100644
--- a/src/Utils.cc
+++ b/src/Utils.cc
@@ -24,6 +24,8 @@
 #include <glibmm/regex.h>
 #include <locale.h>
 #include <uuid/uuid.h>
+#include <cerrno>
+#include <sys/statvfs.h>
 
 
 namespace GParted
@@ -606,6 +608,26 @@ Glib::ustring Utils::generate_uuid(void)
 	return uuid_str;
 }
 
+//Wrapper around statvfs() system call to get mounted file system size
+//  and free space, both in bytes.
+int Utils::get_mounted_filesystem_usage( const Glib::ustring & mountpoint,
+                                         Byte_Value & fs_size, Byte_Value & fs_free,
+                                         Glib::ustring error_message )
+{
+	struct statvfs sfs ;
+	int ret ;
+	ret = statvfs( mountpoint .c_str(), &sfs ) ;
+	if ( ret == 0 )
+	{
+		fs_size = static_cast<Byte_Value>( sfs .f_blocks ) * sfs .f_frsize ;
+		fs_free = static_cast<Byte_Value>( sfs .f_bfree ) * sfs .f_bsize ;
+	}
+	else
+		error_message = "statvfs (" + mountpoint + "): " + Glib::strerror( errno ) ;
+
+	return ret ;
+}
+
 //private functions ...
 
 //Read kernel version, reporting success or failure
diff --git a/src/ext2.cc b/src/ext2.cc
index 722851a..ad62208 100644
--- a/src/ext2.cc
+++ b/src/ext2.cc
@@ -59,13 +59,18 @@ FS ext2::get_filesystem_support()
 		fs .move = FS::GPARTED ;
 	}
 
-	fs .online_read = FS::GPARTED ;
+	fs .online_read = FS::EXTERNAL ;
 
 	return fs ;
 }
 
 void ext2::set_used_sectors( Partition & partition ) 
 {
+	//Called when file system is unmounted *and* when mounted.  Always read
+	//  the file system size from the on disk superblock using dumpe2fs to
+	//  avoid overhead subtraction.  Read the free space from the kernel via
+	//  the statvfs() system call when mounted and from the superblock when
+	//  unmounted.
 	if ( ! Utils::execute_command( "dumpe2fs -h " + partition .get_path(), output, error, true ) )
 	{
 		index = output .find( "Block count:" ) ;
@@ -73,22 +78,40 @@ void ext2::set_used_sectors( Partition & partition )
 		     sscanf( output.substr( index ) .c_str(), "Block count: %Ld", &T ) != 1 )
 			T = -1 ;
 
-		index = output .find( "Free blocks:" ) ;
-		if ( index >= output .length() ||
-		     sscanf( output.substr( index ) .c_str(), "Free blocks: %Ld", &N ) != 1 )   
-			N = -1 ;
-	
 		index = output .find( "Block size:" ) ;
 		if ( index >= output.length() || 
 		     sscanf( output.substr( index ) .c_str(), "Block size: %Ld", &S ) != 1 )  
 			S = -1 ;
 
-		if ( T > -1 && N > -1 && S > -1 )
-		{
+		if ( T > -1 && S > -1 )
 			T = Utils::round( T * ( S / double(partition .sector_size) ) ) ;
-			N = Utils::round( N * ( S / double(partition .sector_size) ) ) ;
-			partition .set_sector_usage( T, N ) ;
+
+		if ( partition .busy )
+		{
+			Byte_Value ignored ;
+			Byte_Value fs_free ;
+			if ( Utils::get_mounted_filesystem_usage( partition .get_mountpoint(),
+			                                          ignored, fs_free, error ) == 0 )
+				N = Utils::round( fs_free / double(partition .sector_size) ) ;
+			else
+			{
+				N = -1 ;
+				partition .messages .push_back( error ) ;
+			}
 		}
+		else
+		{
+			index = output .find( "Free blocks:" ) ;
+			if ( index >= output .length() ||
+			     sscanf( output.substr( index ) .c_str(), "Free blocks: %Ld", &N ) != 1 )
+				N = -1 ;
+
+			if ( N > -1 && S > -1 )
+				N = Utils::round( N * ( S / double(partition .sector_size) ) ) ;
+		}
+
+		if ( T > -1 && N > -1 )
+			partition .set_sector_usage( T, N ) ;
 	}
 	else
 	{
diff --git a/src/ext3.cc b/src/ext3.cc
index 8698347..a6d6d42 100644
--- a/src/ext3.cc
+++ b/src/ext3.cc
@@ -60,13 +60,18 @@ FS ext3::get_filesystem_support()
 		fs .move = GParted::FS::GPARTED ;
 	}
 
-	fs .online_read = FS::GPARTED ;
+	fs .online_read = FS::EXTERNAL ;
 
 	return fs ;
 }
 
 void ext3::set_used_sectors( Partition & partition ) 
 {
+	//Called when file system is unmounted *and* when mounted.  Always read
+	//  the file system size from the on disk superblock using dumpe2fs to
+	//  avoid overhead subtraction.  Read the free space from the kernel via
+	//  the statvfs() system call when mounted and from the superblock when
+	//  unmounted.
 	if ( ! Utils::execute_command( "dumpe2fs -h " + partition .get_path(), output, error, true ) )
 	{
 		index = output .find( "Block count: " ) ;
@@ -74,22 +79,40 @@ void ext3::set_used_sectors( Partition & partition )
 		     sscanf( output .substr( index ) .c_str(), "Block count: %Ld", &T ) != 1 )
 			T = -1 ;
 
-		index = output .find( "Free blocks:" ) ;
-		if ( index >= output .length() ||
-		     sscanf( output.substr( index ) .c_str(), "Free blocks: %Ld", &N ) != 1 )   
-			N = -1 ;
-	
 		index = output .find( "Block size:" ) ;
 		if ( index >= output.length() || 
 		     sscanf( output.substr( index ) .c_str(), "Block size: %Ld", &S ) != 1 )  
 			S = -1 ;
 
-		if ( T > -1 && N > -1 && S > -1 )
-		{
+		if ( T > -1 && S > -1 )
 			T = Utils::round( T * ( S / double(partition .sector_size) ) ) ;
-			N = Utils::round( N * ( S / double(partition .sector_size) ) ) ;
-			partition .set_sector_usage( T, N ) ;
+
+		if ( partition .busy )
+		{
+			Byte_Value ignored ;
+			Byte_Value fs_free ;
+			if ( Utils::get_mounted_filesystem_usage( partition .get_mountpoint(),
+			                                          ignored, fs_free, error ) == 0 )
+				N = Utils::round( fs_free / double(partition .sector_size) ) ;
+			else
+			{
+				N = -1 ;
+				partition .messages .push_back( error ) ;
+			}
 		}
+		else
+		{
+			index = output .find( "Free blocks:" ) ;
+			if ( index >= output .length() ||
+			     sscanf( output.substr( index ) .c_str(), "Free blocks: %Ld", &N ) != 1 )
+				N = -1 ;
+
+			if ( N > -1 && S > -1 )
+				N = Utils::round( N * ( S / double(partition .sector_size) ) ) ;
+		}
+
+		if ( T > -1 && N > -1 )
+			partition .set_sector_usage( T, N ) ;
 	}
 	else
 	{
diff --git a/src/ext4.cc b/src/ext4.cc
index 6a9d1a8..674903a 100644
--- a/src/ext4.cc
+++ b/src/ext4.cc
@@ -63,13 +63,18 @@ FS ext4::get_filesystem_support()
 		}
 	}
 
-	fs .online_read = FS::GPARTED ;
+	fs .online_read = FS::EXTERNAL ;
 
 	return fs ;
 }
 
 void ext4::set_used_sectors( Partition & partition ) 
 {
+	//Called when file system is unmounted *and* when mounted.  Always read
+	//  the file system size from the on disk superblock using dumpe2fs to
+	//  avoid overhead subtraction.  Read the free space from the kernel via
+	//  the statvfs() system call when mounted and from the superblock when
+	//  unmounted.
 	if ( ! Utils::execute_command( "dumpe2fs -h " + partition .get_path(), output, error, true ) )
 	{
 		index = output .find( "Block count:" ) ;
@@ -77,22 +82,40 @@ void ext4::set_used_sectors( Partition & partition )
 		     sscanf( output.substr( index ) .c_str(), "Block count: %Ld", &T ) != 1 )
 			T = -1 ;
 
-		index = output .find( "Free blocks:" ) ;
-		if ( index >= output .length() ||
-		     sscanf( output.substr( index ) .c_str(), "Free blocks: %Ld", &N ) != 1 )   
-			N = -1 ;
-	
 		index = output .find( "Block size:" ) ;
 		if ( index >= output.length() || 
 		     sscanf( output.substr( index ) .c_str(), "Block size: %Ld", &S ) != 1 )  
 			S = -1 ;
 
-		if ( T > -1 && N > -1 && S > -1 )
-		{
+		if ( T > -1 && S > -1 )
 			T = Utils::round( T * ( S / double(partition .sector_size) ) ) ;
-			N = Utils::round( N * ( S / double(partition .sector_size) ) ) ;
-			partition .set_sector_usage( T, N ) ;
+
+		if ( partition .busy )
+		{
+			Byte_Value ignored ;
+			Byte_Value fs_free ;
+			if ( Utils::get_mounted_filesystem_usage( partition .get_mountpoint(),
+			                                          ignored, fs_free, error ) == 0 )
+				N = Utils::round( fs_free / double(partition .sector_size) ) ;
+			else
+			{
+				N = -1 ;
+				partition .messages .push_back( error ) ;
+			}
 		}
+		else
+		{
+			index = output .find( "Free blocks:" ) ;
+			if ( index >= output .length() ||
+			     sscanf( output.substr( index ) .c_str(), "Free blocks: %Ld", &N ) != 1 )
+				N = -1 ;
+
+			if ( N > -1 && S > -1 )
+				N = Utils::round( N * ( S / double(partition .sector_size) ) ) ;
+		}
+
+		if ( T > -1 && N > -1 )
+			partition .set_sector_usage( T, N ) ;
 	}
 	else
 	{



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]