Calculate mounted JFS size accurately (!50)

With the same minimum sized 16 MiB JFS used in the previous commit, now
mounted, GParted once again reports 1.20 MiB of unallocated space.  This
is because the kernel JFS driver is also just reporting the size of the
Aggregate Disk Map (dmap) as the size of the file system [1].

Fix by reading the on disk JFS superblock to calculate the size of the
file system, but query the free space from the kernel using statvfs().
Need to query mounted JFS free space from the kernel because the on disk
dmap is not updated immediately so doesn't reflect recently used or
freed disk space.

For example, start with the 16 MiB JFS empty and mounted.

    # echo -e 'dmap\nx\nquit' | jfs_debugfs /dev/sdb1 | fgrep dn_nfree
    [2] dn_nfree:           0x00000000eaa   [10] dn_agwidth:        1
    # df -k /mnt/1
    Filesystem     1K-blocks  Used Available Use% Mounted on
    /dev/sdb1          15152   136     15016   1% /mnt/1

Write 10 MiB of data to it:

    # dd if=/dev/zero bs=1M count=10 of=/mnt/1/file_10M
    10+0 records in
    10+0 records out
    1048760 bytes (10 MB, 10 MiB) copied, 0.0415676 s, 252 MB/s

Query the file system free space from the kernel and by reading the on
disk dmap figure:

    # df -k /mnt/1
    Filesystem     1K-blocks  Used Available Use% Mounted on
    /dev/sdb1          15152 10376      4776  69% /mnt/1
    # echo -e 'dmap\nx\nquit' | jfs_debugfs /dev/sdb1 | fgrep dn_nfree
    [2] dn_nfree:           0x00000000eaa   [10] dn_agwidth:        1

    # sync
    # echo -e 'dmap\nx\nquit' | jfs_debugfs /dev/sdb1 | fgrep dn_nfree
    [2] dn_nfree:           0x00000000eaa   [10] dn_agwidth:        1

    # umount /mnt/1
    # echo -e 'dmap\nx\nquit' | jfs_debugfs /dev/sdb1 | fgrep dn_nfree
    [2] dn_nfree:           0x000000004aa   [10] dn_agwidth:        1

The kernel reports the updated usage straight away, but the on disk dmap
record doesn't get updated even by sync, only after unmounting.

This is the same fix as was previously done for EXT2/3/4 [2].

[1] Linux jfs_statfs() function
    https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/jfs/super.c?h=v3.10#n142

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

Closes !50 - Calculate JFS size accurately
This commit is contained in:
Mike Fleetwood 2019-11-08 14:01:11 +00:00 committed by Curtis Gedak
parent e55d10b919
commit 2c0572e296
1 changed files with 41 additions and 13 deletions

View File

@ -34,6 +34,7 @@ FS jfs::get_filesystem_support()
if ( ! Glib::find_program_in_path( "jfs_debugfs" ) .empty() ) {
fs.read = FS::EXTERNAL;
fs.online_read = FS::EXTERNAL;
}
if ( ! Glib::find_program_in_path( "jfs_tune" ) .empty() ) {
@ -67,7 +68,6 @@ FS jfs::get_filesystem_support()
fs.copy = FS::GPARTED;
}
fs .online_read = FS::GPARTED ;
#ifdef ENABLE_ONLINE_RESIZE
if ( Utils::kernel_version_at_least( 3, 6, 0 ) )
fs .online_grow = fs .grow ;
@ -81,6 +81,11 @@ FS jfs::get_filesystem_support()
void jfs::set_used_sectors( Partition & partition )
{
// Called when the file system is unmounted *and* when mounted. Always reads the
// on disk superblock using jfs_debugfs to accurately calculate the overall size
// of the file system. Read free space from the kernel via statvfs() when mounted
// for the up to date figure, and from the on disk Aggregate Disk Map (dmap) when
// unmounted.
exit_status = Utils::execute_command("jfs_debugfs " + Glib::shell_quote(partition.get_path()),
"superblock\nx\ndmap\nx\nquit\n", output, error, true);
if (exit_status != 0)
@ -122,20 +127,43 @@ void jfs::set_used_sectors( Partition & partition )
if (index < output.length())
sscanf(output.substr(index).c_str(), "s_fsckpxd.len: %lld", &fsck_len);
// dn_nfree - Number of free (s_bsize) blocks in Aggregate
long long free_blocks = -1;
index = output.find("dn_nfree:");
if (index < output.length())
sscanf(output.substr(index).c_str(), "dn_nfree: %llx", &free_blocks);
if (agg_size > -1 && agg_block_size > -1 && phys_block_size > -1 &&
log_len > -1 && fsck_len > -1 && free_blocks > -1 )
Sector fs_size_sectors = -1;
if (agg_size > -1 && agg_block_size > -1 && phys_block_size > -1 && log_len > -1 && fsck_len > -1)
{
Sector fs_size_sectors = ( agg_size * phys_block_size
+ log_len * agg_block_size
+ fsck_len * agg_block_size) / partition.sector_size;
Sector fs_free_sectors = free_blocks * agg_block_size / partition.sector_size;
fs_size_sectors = ( agg_size * phys_block_size
+ log_len * agg_block_size
+ fsck_len * agg_block_size) / partition.sector_size;
}
Sector fs_free_sectors = -1;
if (partition.busy)
{
Byte_Value ignored;
Byte_Value fs_free_bytes;
if (Utils::get_mounted_filesystem_usage(partition.get_mountpoint(), ignored, fs_free_bytes, error) != 0)
{
partition.push_back_message(error);
return;
}
fs_free_sectors = fs_free_bytes / partition.sector_size;
}
else // (! partition.busy)
{
// dn_nfree - Number of free (s_bsize) blocks in Aggregate
long long free_blocks = -1;
index = output.find("dn_nfree:");
if (index < output.length())
sscanf(output.substr(index).c_str(), "dn_nfree: %llx", &free_blocks);
if (agg_block_size && free_blocks > -1)
{
fs_free_sectors = free_blocks * agg_block_size / partition.sector_size;
}
}
if (fs_size_sectors > -1 && fs_free_sectors > -1 && agg_block_size > -1)
{
partition.set_sector_usage(fs_size_sectors, fs_free_sectors);
partition.fs_block_size = agg_block_size;
}