use atty::Stream;
-use bch_bindgen::{bcachefs, bcachefs::bch_sb_handle, debug, error, info};
-use clap::Parser;
-use colored::Colorize;
+use bch_bindgen::{bcachefs, bcachefs::bch_sb_handle, opt_set};
+use log::{info, debug, error, LevelFilter};
+use clap::{Parser};
use uuid::Uuid;
use std::path::PathBuf;
use crate::key;
-use crate::key::KeyLoc;
-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<()> {
/// 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
}
});
- use itertools::Itertools;
(
if opts.len() == 0 {
None
} else {
- Some(opts.iter().join(","))
+ Some(opts.join(","))
},
flags,
)
// 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)>> {
Ok(devs)
}
-fn stdout_isatty() -> &'static str {
- if atty::is(Stream::Stdout) {
- "true"
- } else {
- "false"
- }
-}
-
/// 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:
/// "fail" - don't ask for password, fail if filesystem is encrypted;
/// "wait" - wait for password to become available before mounting;
/// "ask" - prompt the user for password;
- #[arg(short, long, default_value = "", verbatim_doc_comment)]
- key_location: KeyLoc,
+ #[arg(short, long, default_value = "ask", verbatim_doc_comment)]
+ key_location: KeyLocation,
/// Device, or UUID=<UUID>
dev: String,
/// 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 = "")]
options: String,
/// Force color on/off. Default: autodetect tty
- #[arg(short, long, action = clap::ArgAction::Set, default_value=stdout_isatty())]
+ #[arg(short, long, action = clap::ArgAction::Set, default_value_t=atty::is(Stream::Stdout))]
colorize: bool,
- #[arg(short = 'v', long, action = clap::ArgAction::Count)]
+ /// Verbose mode
+ #[arg(short, long, action = clap::ArgAction::Count)]
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);
- bch_bindgen::log::set_verbose_level(opt.verbose + bch_bindgen::log::ERROR);
+
+ // @TODO : more granular log levels via mount option
+ log::set_max_level(match opt.verbose {
+ 0 => LevelFilter::Warn,
+ 1 => LevelFilter::Trace,
+ 2_u8..=u8::MAX => todo!(),
+ });
+
colored::control::set_override(opt.colorize);
if let Err(e) = cmd_mount_inner(opt) {
error!("Fatal error: {}", e);
+ 1
+ } else {
+ info!("Successfully mounted");
+ 0
}
}