extern crate failure;
#[macro_use]
extern crate failure_derive;
extern crate libparted;
use libparted::*;
use std::env;
use std::io;
use std::num::ParseIntError;
use std::process::{exit, Command, Stdio};
use std::str::{self, FromStr};
enum Unit {
Sectors(u64),
Mebibytes(u64),
Megabytes(u64),
}
impl Unit {
pub fn into_sectors(self, sector_size: u64) -> u64 {
match self {
Unit::Sectors(sectors) => sectors,
Unit::Megabytes(mb) => mb * 1000 * 1000 / sector_size,
Unit::Mebibytes(mib) => mib * 1024 * 1024 / sector_size,
}
}
}
impl FromStr for Unit {
type Err = ParseIntError;
fn from_str(string: &str) -> Result<Self, Self::Err> {
if let Some(stripped) = string.strip_suffix("MB") {
stripped.parse::<u64>().map(Unit::Megabytes)
} else if let Some(stripped) = string.strip_suffix("MiB") {
stripped.parse::<u64>().map(Unit::Mebibytes)
} else if let Some(stripped) = string.strip_suffix('M') {
stripped.parse::<u64>().map(Unit::Megabytes)
} else {
string.parse::<u64>().map(Unit::Sectors)
}
}
}
fn get_config<I: Iterator<Item = String>>(
mut args: I,
) -> io::Result<(String, Unit, Unit, Option<String>)> {
fn config_err(msg: &'static str) -> io::Error {
io::Error::new(io::ErrorKind::InvalidData, msg)
}
let device = args
.next()
.ok_or_else(|| config_err("no device provided"))?;
let start_str = args.next().ok_or_else(|| config_err("no start provided"))?;
let length_str = args
.next()
.ok_or_else(|| config_err("no length provided"))?;
let start = start_str
.parse::<Unit>()
.map_err(|_| config_err("invalid start value"))?;
let length = length_str
.parse::<Unit>()
.map_err(|_| config_err("invalid sector length"))?;
Ok((device, start, length, args.next()))
}
#[derive(Debug, Fail)]
pub enum PartedError {
#[fail(display = "unable to open device: {}", why)]
OpenDevice { why: io::Error },
#[fail(display = "unable to create new geometry: {}", why)]
CreateGeometry { why: io::Error },
#[fail(display = "unable to create new disk: {}", why)]
CreateDisk { why: io::Error },
#[fail(display = "unable to create new partition: {}", why)]
CreatePartition { why: io::Error },
#[fail(display = "unable to get exact constraint from geometry")]
ExactConstraint,
#[fail(display = "unable to add partition to disk: {}", why)]
AddPartition { why: io::Error },
#[fail(display = "unable to commit changes to disk: {}", why)]
CommitChanges { why: io::Error },
#[fail(display = "invalid file system type")]
InvalidFileSystemType,
#[fail(display = "unable to sync device: {}", why)]
SyncErr { why: io::Error },
#[fail(display = "unable to set disk flag")]
DiskFlagErr,
#[fail(display = "unable to get constraint: {}", why)]
GetConstraint { why: io::Error },
#[fail(display = "unable to intersect constraints")]
ConstraintIntersect,
#[fail(display = "unable to ind newly-created partition")]
FindPartition,
#[fail(display = "unable to format partition: {}", why)]
FormatPartition { why: io::Error },
}
fn mkfs(device: &str, fs: &str) -> io::Result<()> {
let (command, args): (&str, &[&str]) = match fs {
"fat16" => ("mkfs.fat", &["-F", "16"]),
"fat32" => ("mkfs.fat", &["-F", "32"]),
"ext2" => ("mkfs.ext2", &["-F", "-q"]),
"ext4" => ("mkfs.ext4", &["-F", "-q"]),
"btrfs" => ("mkfs.btrfs", &["-f"]),
"ntfs" => ("mkfs.ntfs", &["-F"]),
"xfs" => ("mkfs.xfs", &["-f"]),
"swap" => ("mkswap", &["-f"]),
_ => return Err(io::Error::new(io::ErrorKind::InvalidData, "unsupported fs")),
};
let status = Command::new(command)
.stdout(Stdio::null())
.stderr(Stdio::null())
.args(args)
.arg(device)
.status()?;
if status.success() {
Ok(())
} else {
Err(io::Error::new(
io::ErrorKind::Other,
format!("mkfs for {} failed with {}", fs, status),
))
}
}
fn create_partition(
device: &str,
start: Unit,
length: Unit,
fs: Option<String>,
) -> Result<(), PartedError> {
let mut dev = Device::new(&device).map_err(|why| PartedError::OpenDevice { why })?;
let sector_size = dev.sector_size();
let start = start.into_sectors(sector_size);
let length = length.into_sectors(sector_size);
let geometry = Geometry::new(&dev, start as i64, length as i64)
.map_err(|why| PartedError::CreateGeometry { why })?;
let fs = fs.unwrap_or_else(|| "ext2".into());
let fs_type = match FileSystemType::get(&fs) {
Some(fs) => fs,
None => {
eprintln!("invalid fs provided: {}", fs);
exit(1);
}
};
{
let mut disk = Disk::new(&mut dev).map_err(|why| PartedError::CreateDisk { why })?;
let part_type = PartitionType::PED_PARTITION_NORMAL;
let mut partition = Partition::new(
&disk,
part_type,
Some(&fs_type),
geometry.start(),
geometry.start() + geometry.length(),
)
.map_err(|why| PartedError::CreatePartition { why })?;
let constraint = geometry.exact().unwrap();
if let Err(why) = disk.add_partition(&mut partition, &constraint) {
return Err(PartedError::AddPartition { why });
}
if let Err(why) = disk.commit() {
return Err(PartedError::CommitChanges { why });
}
}
if let Err(why) = dev.sync() {
return Err(PartedError::SyncErr { why });
}
let device_path = dev.path().to_path_buf();
{
let disk = Disk::new(&mut dev).map_err(|why| PartedError::CreateDisk { why })?;
{
let new_part = disk
.get_partition_by_sector(start as i64)
.ok_or(PartedError::FindPartition)?;
let device_path = format!("{}{}", device_path.display(), new_part.num());
eprintln!("mkpart: formatting '{}' with '{}'", device_path, fs);
mkfs(&device_path, &fs).map_err(|why| PartedError::FormatPartition { why })?;
}
}
drop(dev);
let mut dev = Device::get(&device).map_err(|why| PartedError::OpenDevice { why })?;
let disk = Disk::new(&mut dev).map_err(|why| PartedError::CreateDisk { why })?;
println!("New Partition Scheme:");
for (part_i, part) in disk.parts().enumerate() {
let name = part.type_get_name();
if name == "metadata" || name == "free" {
continue;
}
println!("Part: {}", part_i);
println!(" Path: {:?}", part.get_path());
println!(" FS: {:?}", part.fs_type_name());
println!(" Start: {}", part.geom_start());
println!(" End: {}", part.geom_end());
println!(" Length: {}", part.geom_length());
}
Ok(())
}
fn main() {
let (device, start, length, fs) = match get_config(env::args().skip(1)) {
Ok(config) => config,
Err(why) => {
eprintln!("mkpart error: {}", why);
eprintln!("\tUsage: mkpart <device_path> <start_sector> <length_in_sectors> [<fs>]");
eprintln!(
"\t mkpart <device_path< <start_sector> <length_in_units>[M | MB] [<fs>]"
);
exit(1);
}
};
match create_partition(&device, start, length, fs) {
Ok(()) => (),
Err(why) => {
eprintln!("mkpart: {} errored: {}", device, why);
exit(1);
}
}
}