]> git.sesse.net Git - bcachefs-tools-debian/blobdiff - rust-src/src/cmd_mount.rs
cmd_mount: Use noxcl for opening block devices
[bcachefs-tools-debian] / rust-src / src / cmd_mount.rs
index af370ef64cafc7c3189e190d45c64b2bec890c8c..eccfe6d0301d2ccb6d4e3d5d63cb3d2373fe2505 100644 (file)
@@ -1,21 +1,19 @@
 use atty::Stream;
-use bch_bindgen::{bcachefs, bcachefs::bch_sb_handle};
-use log::{info, warn, debug, error, trace, LevelFilter};
-use clap::Parser;
+use bch_bindgen::{bcachefs, bcachefs::bch_sb_handle, opt_set};
+use log::{info, debug, error, LevelFilter};
+use clap::{Parser};
 use uuid::Uuid;
-use std::convert::TryInto;
 use std::path::PathBuf;
 use crate::key;
-use crate::key::KeyLoc;
-use crate::logger::SimpleLogger;
-use std::ffi::{CStr, CString, OsStr, c_int, c_char, c_void};
+use crate::key::KeyLocation;
+use std::ffi::{CString, c_int, c_char, c_void, OsStr};
 use std::os::unix::ffi::OsStrExt;
 
 fn mount_inner(
     src: String,
     target: impl AsRef<std::path::Path>,
     fstype: &str,
-    mountflags: u64,
+    mountflags: libc::c_ulong,
     data: Option<String>,
 ) -> anyhow::Result<()> {
 
@@ -46,7 +44,7 @@ fn mount_inner(
 
 /// Parse a comma-separated mount options and split out mountflags and filesystem
 /// specific options.
-fn parse_mount_options(options: impl AsRef<str>) -> (Option<String>, u64) {
+fn parse_mount_options(options: impl AsRef<str>) -> (Option<String>, libc::c_ulong) {
     use either::Either::*;
     debug!("parsing mount options: {}", options.as_ref());
     let (opts, flags) = options
@@ -78,12 +76,11 @@ fn parse_mount_options(options: impl AsRef<str>) -> (Option<String>, u64) {
             }
         });
 
-    use itertools::Itertools;
     (
         if opts.len() == 0 {
             None
         } else {
-            Some(opts.iter().join(","))
+            Some(opts.join(","))
         },
         flags,
     )
@@ -107,7 +104,10 @@ fn read_super_silent(path: &std::path::PathBuf) -> anyhow::Result<bch_sb_handle>
     // Stop libbcachefs from spamming the output
     let _gag = gag::BufferRedirect::stdout().unwrap();
 
-    bch_bindgen::rs::read_super(&path)
+    let mut opts = bcachefs::bch_opts::default();
+    opt_set!(opts, noexcl, 1);
+
+    bch_bindgen::rs::read_super_opts(&path, opts)
 }
 
 fn get_devices_by_uuid(uuid: Uuid) -> anyhow::Result<Vec<(PathBuf, bch_sb_handle)>> {
@@ -130,7 +130,7 @@ fn get_devices_by_uuid(uuid: Uuid) -> anyhow::Result<Vec<(PathBuf, bch_sb_handle
 /// Mount a bcachefs filesystem by its UUID.
 #[derive(Parser, Debug)]
 #[command(author, version, about, long_about = None)]
-struct Cli {
+pub struct Cli {
     /// Where the password would be loaded from.
     ///
     /// Possible values are:
@@ -138,7 +138,7 @@ struct Cli {
     /// "wait" - wait for password to become available before mounting;
     /// "ask" -  prompt the user for password;
     #[arg(short, long, default_value = "ask", verbatim_doc_comment)]
-    key_location:   KeyLoc,
+    key_location:   KeyLocation,
 
     /// Device, or UUID=<UUID>
     dev:            String,
@@ -146,7 +146,7 @@ struct Cli {
     /// Where the filesystem should be mounted. If not set, then the filesystem
     /// won't actually be mounted. But all steps preceeding mounting the
     /// filesystem (e.g. asking for passphrase) will still be performed.
-    mountpoint:     std::path::PathBuf,
+    mountpoint:     Option<std::path::PathBuf>,
 
     /// Mount options
     #[arg(short, default_value = "")]
@@ -161,51 +161,70 @@ struct Cli {
     verbose:        u8,
 }
 
+fn devs_str_sbs_from_uuid(uuid: String) -> anyhow::Result<(String, Vec<bch_sb_handle>)> {
+    debug!("enumerating devices with UUID {}", uuid);
+
+    let devs_sbs = Uuid::parse_str(&uuid)
+        .map(|uuid| get_devices_by_uuid(uuid))??;
+
+    let devs_str = devs_sbs
+        .iter()
+        .map(|(dev, _)| dev.to_str().unwrap())
+        .collect::<Vec<_>>()
+        .join(":");
+
+    let sbs: Vec<bch_sb_handle> = devs_sbs.iter().map(|(_, sb)| *sb).collect();
+
+    Ok((devs_str, sbs))
+
+}
+
 fn cmd_mount_inner(opt: Cli) -> anyhow::Result<()> {
     let (devs, sbs) = if opt.dev.starts_with("UUID=") {
         let uuid = opt.dev.replacen("UUID=", "", 1);
-        let uuid = Uuid::parse_str(&uuid)?;
-        let devs_sbs = get_devices_by_uuid(uuid)?;
-
-        let devs_strs: Vec<_> = devs_sbs.iter().map(|(dev, _)| dev.clone().into_os_string().into_string().unwrap()).collect();
-        let devs_str = devs_strs.join(":");
-        let sbs = devs_sbs.iter().map(|(_, sb)| *sb).collect();
-
-        (devs_str, sbs)
+        devs_str_sbs_from_uuid(uuid)?
+    } else if opt.dev.starts_with("OLD_BLKID_UUID=") {
+        let uuid = opt.dev.replacen("OLD_BLKID_UUID=", "", 1);
+        devs_str_sbs_from_uuid(uuid)?
     } else {
         let mut sbs = Vec::new();
 
         for dev in opt.dev.split(':') {
             let dev = PathBuf::from(dev);
-            sbs.push(bch_bindgen::rs::read_super(&dev)?);
+            sbs.push(read_super_silent(&dev)?);
         }
 
         (opt.dev, sbs)
     };
 
-    if unsafe { bcachefs::bch2_sb_is_encrypted(sbs[0].sb) } {
-        let key = opt
-            .key_location
-            .0
-            .ok_or_else(|| anyhow::anyhow!("no keyoption specified for locked filesystem"))?;
+    if sbs.len() == 0 {
+        Err(anyhow::anyhow!("No device found from specified parameters"))?;
+    } else if unsafe { bcachefs::bch2_sb_is_encrypted(sbs[0].sb) } {
+        key::prepare_key(&sbs[0], opt.key_location)?;
+    }
+
+    if let Some(mountpoint) = opt.mountpoint {
+        info!(
+            "mounting with params: device: {}, target: {}, options: {}",
+            devs,
+            mountpoint.to_string_lossy(),
+            &opt.options
+        );
 
-        key::prepare_key(&sbs[0], key)?;
+        mount(devs, mountpoint, &opt.options)?;
+    } else {
+        info!(
+            "would mount with params: device: {}, options: {}",
+            devs,
+            &opt.options
+        );
     }
 
-    mount(devs, &opt.mountpoint, &opt.options)?;
     Ok(())
 }
 
-#[no_mangle]
-pub extern "C" fn cmd_mount(argc: c_int, argv: *const *const c_char) {
-    let argv: Vec<_> = (0..argc)
-        .map(|i| unsafe { CStr::from_ptr(*argv.add(i as usize)) })
-        .map(|i| OsStr::from_bytes(i.to_bytes()))
-        .collect();
-
+pub fn cmd_mount(argv: Vec<&OsStr>) -> c_int {
     let opt = Cli::parse_from(argv);
-    
-    log::set_boxed_logger(Box::new(SimpleLogger)).unwrap();
 
     // @TODO : more granular log levels via mount option
     log::set_max_level(match opt.verbose {
@@ -217,7 +236,9 @@ pub extern "C" fn cmd_mount(argc: c_int, argv: *const *const c_char) {
     colored::control::set_override(opt.colorize);
     if let Err(e) = cmd_mount_inner(opt) {
         error!("Fatal error: {}", e);
+        1
     } else {
         info!("Successfully mounted");
+        0
     }
 }