#![warn(missing_docs)]
use std::{
ffi::OsStr,
fs,
io,
os::{
fd::AsRawFd,
unix::{ffi::OsStrExt, fs::MetadataExt},
},
path::{Path, PathBuf},
ptr,
};
use nix::ioctl_readwrite;
cfg_if::cfg_if! {
if #[cfg(target_pointer_width = "64")] {
mod ffi64;
use ffi64 as ffi;
} else if #[cfg(target_pointer_width = "32")] {
mod ffi32;
use ffi32 as ffi;
}
}
mod ioctl {
use super::*;
ioctl_readwrite!(mdiocattach, 'm', 0, ffi::md_ioctl);
ioctl_readwrite!(mdiocdetach, 'm', 1, ffi::md_ioctl);
ioctl_readwrite!(mdiocresize, 'm', 4, ffi::md_ioctl);
}
macro_rules! set_bool {
( $field:expr, $val:expr, $bit:expr) => {
if $val {
$field |= $bit;
} else {
$field &= !$bit;
}
};
}
#[derive(Debug)]
pub struct Builder {
filename: Option<PathBuf>,
label: Option<Vec<u8>>,
mdio: ffi::md_ioctl,
}
impl Builder {
fn new() -> Self {
let mdio = ffi::md_ioctl {
md_version: ffi::MDIOVERSION,
md_unit: 0,
md_type: 0,
md_file: ptr::null_mut(),
md_mediasize: 0,
md_sectorsize: 0,
md_options: ffi::MD_AUTOUNIT | ffi::MD_COMPRESS,
md_base: 0,
md_fwheads: 0,
md_fwsectors: 0,
md_label: ptr::null_mut(),
md_pad: [0; ffi::MDNPAD as usize],
};
Builder {
mdio,
filename: None,
label: None,
}
}
pub fn malloc(size: u64) -> Self {
let mut builder = Self::new();
builder.mdio.md_type = ffi::md_types_MD_MALLOC;
builder.mdio.md_mediasize = size as i64;
builder
}
pub fn null(size: u64) -> Self {
let mut builder = Self::new();
builder.mdio.md_type = ffi::md_types_MD_NULL;
builder.mdio.md_mediasize = size as i64;
builder
}
pub fn vnode(path: &Path) -> Self {
let mut builder = Self::new();
builder.mdio.md_type = ffi::md_types_MD_VNODE;
builder.mdio.md_options |= ffi::MD_CLUSTER;
builder.filename = Some(path.to_owned());
builder
}
pub fn swap(size: u64) -> Self {
let mut builder = Self::new();
builder.mdio.md_type = ffi::md_types_MD_SWAP;
builder.mdio.md_mediasize = size as i64;
builder.mdio.md_options |= ffi::MD_CLUSTER;
builder
}
#[doc(alias = "async")]
pub fn async_(mut self, async_: bool) -> Self {
set_bool!(self.mdio.md_options, async_, ffi::MD_ASYNC);
self
}
pub fn cache(mut self, cache: bool) -> Self {
set_bool!(self.mdio.md_options, cache, ffi::MD_CACHE);
self
}
pub fn compress(mut self, compress: bool) -> Self {
set_bool!(self.mdio.md_options, compress, ffi::MD_COMPRESS);
self
}
pub fn heads_per_cylinder(mut self, heads: i32) -> Self {
self.mdio.md_fwheads = heads;
self
}
pub fn label(mut self, label: &str) -> Self {
let mut clabel = Vec::with_capacity(libc::PATH_MAX as usize);
clabel.extend_from_slice(OsStr::new(label).as_bytes());
clabel.resize(libc::PATH_MAX as usize, 0);
self.label = Some(clabel);
self
}
pub fn mustdealloc(mut self, mustdealloc: bool) -> Self {
set_bool!(self.mdio.md_options, mustdealloc, ffi::MD_MUSTDEALLOC);
self
}
pub fn reserve(mut self, reserve: bool) -> Self {
set_bool!(self.mdio.md_options, reserve, ffi::MD_RESERVE);
self
}
pub fn readonly(mut self, readonly: bool) -> Self {
set_bool!(self.mdio.md_options, readonly, ffi::MD_READONLY);
self
}
pub fn sectors_per_track(mut self, sectors: i32) -> Self {
self.mdio.md_fwsectors = sectors;
self
}
pub fn sectorsize(mut self, sectorsize: u32) -> Self {
self.mdio.md_sectorsize = sectorsize;
self
}
pub fn size(mut self, size: libc::off_t) -> Self {
self.mdio.md_mediasize = size;
self
}
pub fn unit(mut self, unit: u32) -> Self {
self.mdio.md_unit = unit;
self.mdio.md_options &= !ffi::MD_AUTOUNIT;
self
}
pub fn verify(mut self, verify: bool) -> Self {
set_bool!(self.mdio.md_options, verify, ffi::MD_VERIFY);
self
}
pub fn create(mut self) -> io::Result<Md> {
let devmd = fs::File::open("/dev/mdctl")?;
let mut _storage = None;
if let Some(filename) = self.filename {
let md = fs::metadata(&filename)?;
if self.mdio.md_mediasize == 0 {
self.mdio.md_mediasize = md.size() as libc::off_t;
}
let mut v = Vec::with_capacity(libc::PATH_MAX as usize);
v.extend_from_slice(OsStr::new(&filename).as_bytes());
v.resize(libc::PATH_MAX as usize, 0);
self.mdio.md_file = v.as_mut_ptr() as *mut libc::c_char;
_storage = Some(v);
}
if let Some(label) = self.label.as_mut() {
self.mdio.md_label = label.as_mut_ptr() as *mut libc::c_char;
}
unsafe { ioctl::mdiocattach(devmd.as_raw_fd(), &mut self.mdio)? };
let name = format!("md{}", self.mdio.md_unit);
let path = Path::new("/dev").join(&name);
Ok(Md {
name,
path,
unit: self.mdio.md_unit,
})
}
}
#[derive(Debug)]
pub struct Md {
name: String,
path: PathBuf,
unit: u32,
}
impl Md {
fn detach(&mut self, force: bool) -> io::Result<()> {
let md_options = if force { ffi::MD_FORCE } else { 0 };
let mut mdio = ffi::md_ioctl {
md_version: ffi::MDIOVERSION,
md_unit: self.unit,
md_type: 0,
md_file: ptr::null_mut(),
md_mediasize: 0,
md_sectorsize: 0,
md_options,
md_base: 0,
md_fwheads: 0,
md_fwsectors: 0,
md_label: ptr::null_mut(),
md_pad: [0; ffi::MDNPAD as usize],
};
let mddev = fs::File::open("/dev/mdctl")?;
unsafe { ioctl::mdiocdetach(mddev.as_raw_fd(), &mut mdio) }?;
Ok(())
}
pub fn name(&self) -> &str {
&self.name
}
pub fn path(&self) -> &Path {
self.path.as_path()
}
pub fn resize(&self, newsize: libc::off_t, force: bool) -> io::Result<()> {
let mut mdio = ffi::md_ioctl {
md_version: ffi::MDIOVERSION,
md_unit: self.unit,
md_type: 0,
md_file: ptr::null_mut(),
md_mediasize: newsize,
md_sectorsize: 0,
md_options: 0,
md_base: 0,
md_fwheads: 0,
md_fwsectors: 0,
md_label: ptr::null_mut(),
md_pad: [0; ffi::MDNPAD as usize],
};
if force {
mdio.md_options |= ffi::MD_FORCE;
}
let devmd = fs::File::open("/dev/mdctl")?;
unsafe {
ioctl::mdiocresize(devmd.as_raw_fd(), &mut mdio)?;
}
Ok(())
}
pub fn try_destroy(mut self) -> std::result::Result<(), (Self, io::Error)> {
match self.detach(false) {
Ok(()) => {
std::mem::forget(self);
Ok(())
}
Err(e) => Err((self, e)),
}
}
pub fn unit(&self) -> u32 {
self.unit
}
}
impl Drop for Md {
fn drop(&mut self) {
let r = self.detach(true);
if !std::thread::panicking() {
r.expect("Error during MDIOCDETACH during drop");
}
}
}