use crate::{BtrfsFuse, args::MountArgs};
use anyhow::{Context, Result, anyhow};
use btrfs_fs::{CacheConfig, Filesystem, SubvolId};
use fuser::{Config, MountOption, SessionACL};
use std::{fs::File, path::Path};
pub fn run_mount(args: &MountArgs) -> Result<()> {
let target_subvol = if let Some(id) = args.subvolid {
Some(SubvolId(id))
} else if let Some(path) = args.subvol.as_deref() {
Some(resolve_subvol_path(&args.image, path)?)
} else {
None
};
let caches = CacheConfig {
tree_blocks: args.cache_tree_blocks,
inodes: args.cache_inodes,
extent_maps: args.cache_extent_maps,
};
let file = File::open(&args.image)
.with_context(|| format!("opening {}", args.image.display()))?;
let fs = match target_subvol {
Some(id) => BtrfsFuse::open_subvol_with_caches(file, id, caches)
.with_context(|| format!("opening subvolume {}", id.0))?,
None => BtrfsFuse::open_with_caches(file, caches)
.context("bootstrapping btrfs filesystem")?,
};
let mut config = Config::default();
let mut mount_options = vec![
MountOption::RO,
MountOption::FSName("btrfs-fuse".to_string()),
MountOption::Subtype("btrfs".to_string()),
];
if !args.no_default_permissions {
mount_options.push(MountOption::DefaultPermissions);
}
config.mount_options = mount_options;
config.acl = if args.allow_other {
SessionACL::All
} else {
SessionACL::Owner
};
let _ = args.foreground;
fuser::mount2(fs, &args.mountpoint, &config).with_context(|| {
format!("mounting at {}", args.mountpoint.display())
})?;
Ok(())
}
fn resolve_subvol_path(image: &Path, path: &str) -> Result<SubvolId> {
let file = File::open(image)
.with_context(|| format!("opening {}", image.display()))?;
let fs = Filesystem::open(file)
.context("bootstrapping btrfs filesystem to resolve --subvol")?;
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.context("creating temporary tokio runtime")?;
runtime
.block_on(fs.resolve_subvol_path(path))
.context("resolving subvolume path")?
.ok_or_else(|| {
anyhow!("subvolume path {path:?} not found on {}", image.display())
})
}