summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bcachefs.rs1
-rw-r--r--src/commands/mount.rs172
-rw-r--r--src/device_scan.rs228
-rw-r--r--src/rust_to_c.h17
4 files changed, 253 insertions, 165 deletions
diff --git a/src/bcachefs.rs b/src/bcachefs.rs
index f49f9195..4fb0b14c 100644
--- a/src/bcachefs.rs
+++ b/src/bcachefs.rs
@@ -3,6 +3,7 @@ mod key;
mod dump_stack;
mod logging;
mod wrappers;
+mod device_scan;
use std::{
ffi::{c_char, CString},
diff --git a/src/commands/mount.rs b/src/commands/mount.rs
index 883a0d11..d0fd7168 100644
--- a/src/commands/mount.rs
+++ b/src/commands/mount.rs
@@ -1,6 +1,4 @@
use std::{
- collections::HashMap,
- env,
ffi::CString,
io::{stdout, IsTerminal},
path::{Path, PathBuf},
@@ -8,11 +6,10 @@ use std::{
};
use anyhow::{ensure, Result};
-use bch_bindgen::{bcachefs, bcachefs::bch_sb_handle, opt_set, path_to_cstr};
-use bcachefs::bch_opts;
+use bch_bindgen::{bcachefs, bcachefs::bch_sb_handle, path_to_cstr};
use clap::Parser;
use log::{debug, error, info};
-use uuid::Uuid;
+use crate::device_scan;
use crate::{
key::{KeyHandle, Passphrase, UnlockPolicy},
@@ -121,141 +118,6 @@ fn parse_mountflag_options(options: impl AsRef<str>) -> (Option<String>, libc::c
)
}
-fn read_super_silent(path: impl AsRef<Path>, mut opts: bch_opts) -> anyhow::Result<bch_sb_handle> {
- opt_set!(opts, noexcl, 1);
-
- bch_bindgen::sb_io::read_super_silent(path.as_ref(), opts)
-}
-
-fn device_property_map(dev: &udev::Device) -> HashMap<String, String> {
- let rc: HashMap<_, _> = dev
- .properties()
- .map(|i| {
- (
- String::from(i.name().to_string_lossy()),
- String::from(i.value().to_string_lossy()),
- )
- })
- .collect();
- rc
-}
-
-fn udev_bcachefs_info() -> anyhow::Result<HashMap<String, Vec<String>>> {
- let mut info = HashMap::new();
-
- if env::var("BCACHEFS_BLOCK_SCAN").is_ok() {
- debug!("Checking all block devices for bcachefs super block!");
- return Ok(info);
- }
-
- let mut udev = udev::Enumerator::new()?;
-
- debug!("Walking udev db!");
-
- udev.match_subsystem("block")?;
- udev.match_property("ID_FS_TYPE", "bcachefs")?;
-
- for m in udev
- .scan_devices()?
- .filter(udev::Device::is_initialized)
- .map(|dev| device_property_map(&dev))
- .filter(|m| m.contains_key("ID_FS_UUID") && m.contains_key("DEVNAME"))
- {
- let fs_uuid = m["ID_FS_UUID"].clone();
- let dev_node = m["DEVNAME"].clone();
- info.insert(dev_node.clone(), vec![fs_uuid.clone()]);
- info.entry(fs_uuid).or_insert(vec![]).push(dev_node.clone());
- }
-
- Ok(info)
-}
-
-fn get_super_blocks(uuid: Uuid, devices: &[String], opts: &bch_opts) -> Vec<(PathBuf, bch_sb_handle)> {
- devices
- .iter()
- .filter_map(|dev| {
- read_super_silent(PathBuf::from(dev), *opts)
- .ok()
- .map(|sb| (PathBuf::from(dev), sb))
- })
- .filter(|(_, sb)| sb.sb().uuid() == uuid)
- .collect::<Vec<_>>()
-}
-
-fn get_all_block_devnodes() -> anyhow::Result<Vec<String>> {
- let mut udev = udev::Enumerator::new()?;
- udev.match_subsystem("block")?;
-
- let devices = udev
- .scan_devices()?
- .filter_map(|dev| {
- if dev.is_initialized() {
- dev.devnode().map(|dn| dn.to_string_lossy().into_owned())
- } else {
- None
- }
- })
- .collect::<Vec<_>>();
- Ok(devices)
-}
-
-fn get_devices_by_uuid(
- udev_bcachefs: &HashMap<String, Vec<String>>,
- uuid: Uuid,
- opts: &bch_opts
-) -> anyhow::Result<Vec<(PathBuf, bch_sb_handle)>> {
- let devices = {
- if !udev_bcachefs.is_empty() {
- let uuid_string = uuid.hyphenated().to_string();
- if let Some(devices) = udev_bcachefs.get(&uuid_string) {
- devices.clone()
- } else {
- Vec::new()
- }
- } else {
- get_all_block_devnodes()?
- }
- };
-
- Ok(get_super_blocks(uuid, &devices, opts))
-}
-
-fn devs_str_sbs_from_uuid(
- udev_info: &HashMap<String, Vec<String>>,
- uuid: &str,
- opts: &bch_opts
-) -> 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(udev_info, uuid, opts))??;
-
- 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 devs_str_sbs_from_device(
- udev_info: &HashMap<String, Vec<String>>,
- device: &Path,
- opts: &bch_opts
-) -> anyhow::Result<(String, Vec<bch_sb_handle>)> {
- let dev_sb = read_super_silent(device, *opts)?;
-
- if dev_sb.sb().number_of_devices() == 1 {
- Ok((device.as_os_str().to_str().unwrap().to_string(), vec![dev_sb]))
- } else {
- let uuid = dev_sb.sb().uuid();
-
- devs_str_sbs_from_uuid(udev_info, &uuid.to_string(), opts)
- }
-}
-
/// If a user explicitly specifies `unlock_policy` or `passphrase_file` then use
/// that without falling back to other mechanisms. If these options are not
/// used, then search for the key or ask for it.
@@ -278,40 +140,20 @@ fn cmd_mount_inner(cli: &Cli) -> Result<()> {
let opts = bch_bindgen::opts::parse_mount_opts(None, optstr.as_deref(), true)
.unwrap_or_default();
- // Grab the udev information once
- let udev_info = udev_bcachefs_info()?;
-
- let (devices, mut sbs) =
- if let Some(("UUID" | "OLD_BLKID_UUID", uuid)) = cli.dev.split_once('=') {
- devs_str_sbs_from_uuid(&udev_info, uuid, &opts)?
- } else if cli.dev.contains(':') {
- // If the device string contains ":" we will assume the user knows the
- // entire list. If they supply a single device it could be either the FS
- // only has 1 device or it's only 1 of a number of devices which are
- // part of the FS. This appears to be the case when we get called during
- // fstab mount processing and the fstab specifies a UUID.
-
- let sbs = cli
- .dev
- .split(':')
- .map(|path| read_super_silent(path, opts))
- .collect::<Result<Vec<_>>>()?;
-
- (cli.dev.clone(), sbs)
- } else {
- devs_str_sbs_from_device(&udev_info, Path::new(&cli.dev), &opts)?
- };
+ let mut sbs = device_scan::scan_sbs(&cli.dev, &opts)?;
ensure!(!sbs.is_empty(), "No device(s) to mount specified");
- let first_sb = &sbs[0];
+ let devices = device_scan::joined_device_str(&sbs);
+
+ let first_sb = &sbs[0].1;
if unsafe { bcachefs::bch2_sb_is_encrypted(first_sb.sb) } {
handle_unlock(cli, first_sb)?;
}
for sb in &mut sbs {
unsafe {
- bch_bindgen::sb_io::bch2_free_super(sb);
+ bch_bindgen::sb_io::bch2_free_super(&mut sb.1);
}
}
drop(sbs);
diff --git a/src/device_scan.rs b/src/device_scan.rs
new file mode 100644
index 00000000..a6b62290
--- /dev/null
+++ b/src/device_scan.rs
@@ -0,0 +1,228 @@
+use std::{
+ ffi::{CStr, CString, c_char, c_int},
+ collections::HashMap,
+ env,
+ path::{Path, PathBuf},
+ str,
+};
+
+use anyhow::Result;
+use bch_bindgen::{bcachefs, opt_set};
+use bcachefs::{
+ bch_sb_handle,
+ sb_name,
+ sb_names,
+};
+use bcachefs::bch_opts;
+use uuid::Uuid;
+use log::debug;
+
+fn read_super_silent(path: impl AsRef<Path>, mut opts: bch_opts) -> anyhow::Result<bch_sb_handle> {
+ opt_set!(opts, noexcl, 1);
+
+ bch_bindgen::sb_io::read_super_silent(path.as_ref(), opts)
+}
+
+fn device_property_map(dev: &udev::Device) -> HashMap<String, String> {
+ let rc: HashMap<_, _> = dev
+ .properties()
+ .map(|i| {
+ (
+ String::from(i.name().to_string_lossy()),
+ String::from(i.value().to_string_lossy()),
+ )
+ })
+ .collect();
+ rc
+}
+
+fn udev_bcachefs_info() -> anyhow::Result<HashMap<String, Vec<String>>> {
+ let mut info = HashMap::new();
+
+ if env::var("BCACHEFS_BLOCK_SCAN").is_ok() {
+ debug!("Checking all block devices for bcachefs super block!");
+ return Ok(info);
+ }
+
+ let mut udev = udev::Enumerator::new()?;
+
+ debug!("Walking udev db!");
+
+ udev.match_subsystem("block")?;
+ udev.match_property("ID_FS_TYPE", "bcachefs")?;
+
+ for m in udev
+ .scan_devices()?
+ .filter(udev::Device::is_initialized)
+ .map(|dev| device_property_map(&dev))
+ .filter(|m| m.contains_key("ID_FS_UUID") && m.contains_key("DEVNAME"))
+ {
+ let fs_uuid = m["ID_FS_UUID"].clone();
+ let dev_node = m["DEVNAME"].clone();
+ info.insert(dev_node.clone(), vec![fs_uuid.clone()]);
+ info.entry(fs_uuid).or_insert(vec![]).push(dev_node.clone());
+ }
+
+ Ok(info)
+}
+
+fn get_super_blocks(uuid: Uuid, devices: &[String], opts: &bch_opts) -> Vec<(PathBuf, bch_sb_handle)> {
+ devices
+ .iter()
+ .filter_map(|dev| {
+ read_super_silent(PathBuf::from(dev), *opts)
+ .ok()
+ .map(|sb| (PathBuf::from(dev), sb))
+ })
+ .filter(|(_, sb)| sb.sb().uuid() == uuid)
+ .collect::<Vec<_>>()
+}
+
+fn get_all_block_devnodes() -> anyhow::Result<Vec<String>> {
+ let mut udev = udev::Enumerator::new()?;
+ udev.match_subsystem("block")?;
+
+ let devices = udev
+ .scan_devices()?
+ .filter_map(|dev| {
+ if dev.is_initialized() {
+ dev.devnode().map(|dn| dn.to_string_lossy().into_owned())
+ } else {
+ None
+ }
+ })
+ .collect::<Vec<_>>();
+ Ok(devices)
+}
+
+fn get_devices_by_uuid(
+ udev_bcachefs: &HashMap<String, Vec<String>>,
+ uuid: Uuid,
+ opts: &bch_opts
+) -> anyhow::Result<Vec<(PathBuf, bch_sb_handle)>> {
+ let devices = {
+ if !udev_bcachefs.is_empty() {
+ let uuid_string = uuid.hyphenated().to_string();
+ if let Some(devices) = udev_bcachefs.get(&uuid_string) {
+ devices.clone()
+ } else {
+ Vec::new()
+ }
+ } else {
+ get_all_block_devnodes()?
+ }
+ };
+
+ Ok(get_super_blocks(uuid, &devices, opts))
+}
+
+fn devs_str_sbs_from_uuid(
+ udev_info: &HashMap<String, Vec<String>>,
+ uuid: &str,
+ opts: &bch_opts
+) -> Result<Vec<(PathBuf, bch_sb_handle)>> {
+ debug!("enumerating devices with UUID {}", uuid);
+
+ Uuid::parse_str(uuid).map(|uuid| get_devices_by_uuid(udev_info, uuid, opts))?
+}
+
+fn devs_str_sbs_from_device(
+ udev_info: &HashMap<String, Vec<String>>,
+ device: &Path,
+ opts: &bch_opts
+) -> anyhow::Result<Vec<(PathBuf, bch_sb_handle)>> {
+ let dev_sb = read_super_silent(device, *opts)?;
+
+ if dev_sb.sb().number_of_devices() == 1 {
+ Ok(vec![(device.to_path_buf(), dev_sb)])
+ } else {
+ let uuid = dev_sb.sb().uuid();
+
+ get_devices_by_uuid(udev_info, uuid, opts)
+ }
+}
+
+pub fn scan_sbs(device: &String, opts: &bch_opts) -> Result<Vec<(PathBuf, bch_sb_handle)>> {
+ // Grab the udev information once
+ let udev_info = udev_bcachefs_info()?;
+
+ if let Some(("UUID" | "OLD_BLKID_UUID", uuid)) = device.split_once('=') {
+ devs_str_sbs_from_uuid(&udev_info, uuid, opts)
+ } else if device.contains(':') {
+ // If the device string contains ":" we will assume the user knows the
+ // entire list. If they supply a single device it could be either the FS
+ // only has 1 device or it's only 1 of a number of devices which are
+ // part of the FS. This appears to be the case when we get called during
+ // fstab mount processing and the fstab specifies a UUID.
+
+ let devices = device.split(':')
+ .map(PathBuf::from)
+ .collect::<Vec<_>>();
+ let sbs = devices
+ .iter()
+ .map(|path| read_super_silent(path, *opts))
+ .collect::<Result<Vec<_>>>()?;
+
+ Ok(devices.iter().zip(sbs.iter())
+ .map(|(x, y)| (PathBuf::from(x), y.clone()))
+ .collect::<Vec<_>>())
+ } else {
+ devs_str_sbs_from_device(&udev_info, Path::new(device), opts)
+ }
+}
+
+pub fn joined_device_str(sbs: &Vec<(PathBuf, bch_sb_handle)>) -> String {
+ sbs.iter()
+ .map(|sb| sb.0.clone().into_os_string().into_string().unwrap())
+ .collect::<Vec<_>>()
+ .join(":")
+}
+
+pub fn scan_devices(device: &String, opts: &bch_opts) -> Result<String> {
+ let mut sbs = scan_sbs(device, opts)?;
+
+ for sb in &mut sbs {
+ unsafe {
+ bch_bindgen::sb_io::bch2_free_super(&mut sb.1);
+ }
+ }
+
+ Ok(joined_device_str(&sbs))
+}
+
+#[no_mangle]
+pub extern "C" fn bch2_scan_device_sbs(device: *const c_char, ret: *mut sb_names) -> c_int {
+ let device = unsafe { CStr::from_ptr(device) };
+ let device = device.to_str().unwrap().to_string();
+
+ // how to initialize to default/empty?
+ let opts = bch_bindgen::opts::parse_mount_opts(None, None, true).unwrap_or_default();
+
+ let sbs = scan_sbs(&device, &opts).unwrap();
+
+ let mut sbs = sbs.iter()
+ .map(|(name, sb)| sb_name {
+ name: CString::new(name.clone().into_os_string().into_string().unwrap()).unwrap().into_raw(),
+ sb: *sb } )
+ .collect::<Vec<sb_name>>();
+
+ unsafe {
+ (*ret).data = sbs.as_mut_ptr();
+ (*ret).nr = sbs.len();
+ (*ret).size = sbs.capacity();
+
+ std::mem::forget(sbs);
+ }
+ 0
+}
+
+#[no_mangle]
+pub extern "C" fn bch2_scan_devices(device: *const c_char) -> *mut c_char {
+ let device = unsafe { CStr::from_ptr(device) };
+ let device = device.to_str().unwrap().to_string();
+
+ // how to initialize to default/empty?
+ let opts = bch_bindgen::opts::parse_mount_opts(None, None, true).unwrap_or_default();
+
+ CString::new(scan_devices(&device, &opts).unwrap()).unwrap().into_raw()
+}
diff --git a/src/rust_to_c.h b/src/rust_to_c.h
new file mode 100644
index 00000000..b64059c4
--- /dev/null
+++ b/src/rust_to_c.h
@@ -0,0 +1,17 @@
+#ifndef _BCACHEFS_TOOLS_RUST_TO_C_H
+#define _BCACHEFS_TOOLS_RUST_TO_C_H
+
+#include "libbcachefs/super_types.h"
+#include "libbcachefs/darray.h"
+
+struct sb_name {
+ const char *name;
+ struct bch_sb_handle sb;
+};
+typedef DARRAY(struct sb_name) sb_names;
+
+int bch2_scan_device_sbs(char *, sb_names *ret);
+
+char *bch2_scan_devices(char *);
+
+#endif /* _BCACHEFS_TOOLS_RUST_TO_C_H */