From 3d63b901c6217a0ea7f169f5e4c66ca1d4ea1088 Mon Sep 17 00:00:00 2001
From: Tony Asleson <tasleson@redhat.com>
Date: Wed, 1 May 2024 15:48:57 -0500
Subject: [PATCH] Handle mount with FS with 1 device

If the user passes a single device node during the mount and
we have no information for it in the udev db, we read up the
super block.  When we do this, if the FS only has 1 block device
we will simply go ahead and do the mount instead of walking
all the block devices and reading up super blocks looking for
devices with a matching FS UUID.

Signed-off-by: Tony Asleson <tasleson@redhat.com>
---
 bch_bindgen/src/bcachefs.rs |  4 ++++
 src/commands/mount.rs       | 34 +++++++++++++++++++++++++---------
 2 files changed, 29 insertions(+), 9 deletions(-)

diff --git a/bch_bindgen/src/bcachefs.rs b/bch_bindgen/src/bcachefs.rs
index 837b0272..7b8a03df 100644
--- a/bch_bindgen/src/bcachefs.rs
+++ b/bch_bindgen/src/bcachefs.rs
@@ -59,6 +59,10 @@ impl bch_sb {
         uuid::Uuid::from_bytes(self.user_uuid.b)
     }
 
+    pub fn number_of_devices(&self) -> u8 {
+        self.nr_devices
+    }
+
     /// Get the nonce used to encrypt the superblock
     pub fn nonce(&self) -> nonce {
         use byteorder::{LittleEndian, ReadBytesExt};
diff --git a/src/commands/mount.rs b/src/commands/mount.rs
index 03215fcd..c6e0a37d 100644
--- a/src/commands/mount.rs
+++ b/src/commands/mount.rs
@@ -208,7 +208,7 @@ fn get_devices_by_uuid(
 fn get_uuid_for_dev_node(
     udev_bcachefs: &HashMap<String, Vec<String>>,
     device: &std::path::PathBuf,
-) -> anyhow::Result<Option<Uuid>> {
+) -> anyhow::Result<(Option<Uuid>, Option<(PathBuf, bch_sb_handle)>)> {
     let canonical = fs::canonicalize(device)?;
 
     if !udev_bcachefs.is_empty() {
@@ -216,12 +216,14 @@ fn get_uuid_for_dev_node(
 
         if udev_bcachefs.contains_key(&dev_node_str) && udev_bcachefs[&dev_node_str].len() == 1 {
             let uuid_str = udev_bcachefs[&dev_node_str][0].clone();
-            return Ok(Some(Uuid::parse_str(&uuid_str)?));
+            return Ok((Some(Uuid::parse_str(&uuid_str)?), None));
         }
     } else {
-        return read_super_silent(&canonical).map_or(Ok(None), |sb| Ok(Some(sb.sb().uuid())));
+        return read_super_silent(&canonical).map_or(Ok((None, None)), |sb| {
+            Ok((Some(sb.sb().uuid()), Some((canonical, sb))))
+        });
     }
-    Ok(None)
+    Ok((None, None))
 }
 
 /// Mount a bcachefs filesystem by its UUID.
@@ -289,12 +291,26 @@ fn devs_str_sbs_from_device(
     udev_info: &HashMap<String, Vec<String>>,
     device: &std::path::PathBuf,
 ) -> anyhow::Result<(String, Vec<bch_sb_handle>)> {
-    let uuid = get_uuid_for_dev_node(udev_info, device)?;
+    let (uuid, sb_info) = get_uuid_for_dev_node(udev_info, device)?;
 
-    if let Some(bcache_fs_uuid) = uuid {
-        devs_str_sbs_from_uuid(udev_info, bcache_fs_uuid.to_string())
-    } else {
-        Ok((String::new(), Vec::new()))
+    match (uuid, sb_info) {
+        (Some(uuid), Some((path, sb))) => {
+            // If we have a super block, it implies we aren't using udev db.  If we only need
+            // 1 device to mount, we'll simply return it as we're done, else we'll use the uuid
+            // to walk through all the block devices.
+            debug!(
+                "number of devices in this FS = {}",
+                sb.sb().number_of_devices()
+            );
+            if sb.sb().number_of_devices() == 1 {
+                let dev = path.into_os_string().into_string().unwrap();
+                Ok((dev, vec![sb]))
+            } else {
+                devs_str_sbs_from_uuid(udev_info, uuid.to_string())
+            }
+        }
+        (Some(uuid), None) => devs_str_sbs_from_uuid(udev_info, uuid.to_string()),
+        _ => Ok((String::new(), Vec::new())),
     }
 }