[gparted] Always use directory mount point when resizing btrfs (#193)



commit 59b3fd068f22147603169f8b75b81800fa79c9b8
Author: Mike Fleetwood <mike fleetwood googlemail com>
Date:   Sun May 22 09:30:24 2022 +0100

    Always use directory mount point when resizing btrfs (#193)
    
    A user received the following error when attempting to resize a mounted
    btrfs file system on their NixOS distribution:
    
        Shrink /dev/nvme0n1p3 from 933.38 GiB to 894.32 GiB        (ERROR)
        + calibrate /dev/nvme0n1p3  00:00:00                       (SUCCESS)
        + btrfs filesystem resize 1:937759744K '/etc/machine-id'   (ERROR)
            ERROR: not a directory: /etc/machine-id
            ERROR: resize works on mounted filesystems and accepts only
            directories as argument. Passing file containing a btrfs image
            would resize the underlying filesystem instead of the image.
    
    In the partition table section of the gparted_details /dev/nvme0n1p3 was
    reported with these mount points:
        /etc/machine-id, /etc/NetworkManager/system-connections,
        /etc/ssh/ssh_host_ed25519_key, /etc/ssh/ssh_host_ed25519_key.pub,
        /etc/ssh/ssh_host_rsa_key, /etc/ssh/ssh_host_rsa_key.pub, /home,
        /nix, /nix/store, /state, /var
    
    The user had a common configuration of NixOS which boots with an empty
    tmpfs as root with a few bind mounted files and directories to provide
    the needed persistent data [1][2].
    
    Re-create an equivalent situation:
    1. Create a btrfs file system and mount it:
        # mkfs.btrfs /dev/sdb1
        # mkdir /mnt/store
        # mount /dev/sdb1 /mnt/store
    
    2. Bind mount a file from this file system else where in the hierarchy.
       The only criteria is that this mount point sorts before /mnt/store.
        # echo 'Test contents' > /mnt/store/test
        # touch /boot/test
        # mount --bind /mnt/store/test /boot/test
    
      The kernel reports these mount mounts:
        # grep sdb1 /proc/mounts
        /dev/sdb1 /mnt/store btrfs rw,seclabel,relatime,space_cache=v2,subvolid=5,subvol=/ 0 0
        /dev/sdb1 /boot/test btrfs rw,seclabel,relatime,space_cache=v2,subvolid=5,subvol=/ 0 0
    
    3. Use GParted to resize this mounted btrfs file system.  It fails with
       the above error.
    
    GParted read the mount points from /proc/mounts and sorted them.  (See
    the end of Mount_Info::load_cache() for the sorting).  When resizing the
    btrfs file system GParted just used the first sorted mount point.  This
    was the file /etc/machine-id for the user and file /boot/test in the
    re-creation, hence the error.
    
    Fix by selecting the first directory mount point to pass to the btrfs
    resize command.
    
    [1] NixOS tmpfs as root
        https://elis.nu/blog/2020/05/nixos-tmpfs-as-root/
    [2] Erase your darlings
        https://grahamc.com/blog/erase-your-darlings
    
    Closes #193 - path used to resize btrfs needs to be a directory

 include/Utils.h |  1 +
 src/Utils.cc    | 13 +++++++++++++
 src/btrfs.cc    | 12 +++++++++++-
 3 files changed, 25 insertions(+), 1 deletion(-)
---
diff --git a/include/Utils.h b/include/Utils.h
index 477411e6..d887250e 100644
--- a/include/Utils.h
+++ b/include/Utils.h
@@ -184,6 +184,7 @@ public:
                                                 Byte_Value & fs_size, Byte_Value & fs_free,
                                                 Glib::ustring & error_message ) ;
        static bool is_dev_busy(const Glib::ustring& path);
+       static const Glib::ustring& first_directory(const std::vector<Glib::ustring>& paths);
        static Byte_Value floor_size( Byte_Value value, Byte_Value rounding_size ) ;
        static Byte_Value ceil_size( Byte_Value value, Byte_Value rounding_size ) ;
 
diff --git a/src/Utils.cc b/src/Utils.cc
index f3f3b281..8089b198 100644
--- a/src/Utils.cc
+++ b/src/Utils.cc
@@ -30,6 +30,7 @@
 #include <glibmm/ustring.h>
 #include <glibmm/stringutils.h>
 #include <glibmm/shell.h>
+#include <glibmm/fileutils.h>
 #include <gtkmm/main.h>
 #include <gtkmm/enums.h>
 #include <gtkmm/stock.h>
@@ -943,6 +944,18 @@ bool Utils::is_dev_busy(const Glib::ustring& path)
 }
 
 
+// Return the first path that is a directory, or the empty string.
+const Glib::ustring& Utils::first_directory(const std::vector<Glib::ustring>& paths)
+{
+       for (unsigned int i = 0; i < paths.size(); i++)
+               if (file_test(paths[i], Glib::FILE_TEST_IS_DIR))
+                       return paths[i];
+
+       static const Glib::ustring not_found;
+       return not_found;
+}
+
+
 //Round down to multiple of rounding_size
 Byte_Value Utils::floor_size( Byte_Value value, Byte_Value rounding_size )
 {
diff --git a/src/btrfs.cc b/src/btrfs.cc
index b465908a..99461347 100644
--- a/src/btrfs.cc
+++ b/src/btrfs.cc
@@ -306,7 +306,17 @@ bool btrfs::resize( const Partition & partition_new, OperationDetail & operation
                                              operationdetail, EXEC_CHECK_STATUS );
        }
        else
-               mount_point = partition_new .get_mountpoint() ;
+       {
+               mount_point = Utils::first_directory(partition_new.get_mountpoints());
+               if (mount_point.empty())
+               {
+                       Glib::ustring mount_list = Glib::build_path(", ", partition_new.get_mountpoints());
+                       operationdetail.add_child(OperationDetail(
+                                       Glib::ustring::compose(_("No directory mount point found in %1"), 
mount_list),
+                                       STATUS_ERROR));
+                       return false;
+               }
+       }
 
        if ( success )
        {


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