use anyhow::{Result, bail};
use clap::Parser;
use uuid::Uuid;
mod args;
pub mod items;
pub mod layout;
pub mod mkfs;
pub mod superblock;
pub mod tree;
pub mod write;
use args::Arguments;
fn main() -> Result<()> {
let args = Arguments::parse();
if args.devices.len() > 1 {
bail!("multi-device mkfs is not yet implemented");
}
let path = &args.devices[0];
let nodesize = args.nodesize.map(|s| s.0 as u32).unwrap_or(16384);
let sectorsize = args.sectorsize.map(|s| s.0 as u32).unwrap_or(4096);
if !nodesize.is_power_of_two() || nodesize < sectorsize || nodesize > 65536
{
bail!(
"invalid nodesize {nodesize}: must be a power of 2, \
>= sectorsize ({sectorsize}), and <= 64K"
);
}
if !sectorsize.is_power_of_two() || sectorsize < 4096 {
bail!("invalid sectorsize {sectorsize}: must be a power of 2 >= 4096");
}
if let Some(ref label) = args.label
&& label.len() >= 256
{
bail!("label too long: {} bytes (max 255)", label.len());
}
let total_bytes = if let Some(byte_count) = args.byte_count {
byte_count.0
} else {
mkfs::device_size(path)?
};
let fs_uuid = args.filesystem_uuid.unwrap_or_else(Uuid::new_v4);
let dev_uuid = args.device_uuid.unwrap_or_else(Uuid::new_v4);
let chunk_tree_uuid = Uuid::new_v4();
let cfg = mkfs::MkfsConfig {
nodesize,
sectorsize,
total_bytes,
label: args.label,
fs_uuid,
dev_uuid,
chunk_tree_uuid,
incompat_flags: mkfs::MkfsConfig::default_incompat_flags(),
compat_ro_flags: mkfs::MkfsConfig::default_compat_ro_flags(),
};
if !args.quiet {
eprintln!("Creating btrfs filesystem on {}", path.display());
eprintln!(
" Label: {}",
cfg.label.as_deref().unwrap_or("(none)")
);
eprintln!(" UUID: {}", cfg.fs_uuid);
eprintln!(" Node size: {}", cfg.nodesize);
eprintln!(" Sector size: {}", cfg.sectorsize);
eprintln!(
" Filesystem size: {} ({} bytes)",
human_size(cfg.total_bytes),
cfg.total_bytes
);
}
mkfs::make_btrfs(path, &cfg)?;
if !args.quiet {
eprintln!("Done.");
}
Ok(())
}
fn human_size(bytes: u64) -> String {
const UNITS: &[&str] = &["B", "KiB", "MiB", "GiB", "TiB", "PiB"];
let mut value = bytes as f64;
for &unit in UNITS {
if value < 1024.0 {
return if value.fract() == 0.0 {
format!("{:.0} {unit}", value)
} else {
format!("{:.2} {unit}", value)
};
}
value /= 1024.0;
}
format!("{:.2} EiB", value)
}