use snafu::{ensure, ResultExt, Snafu};
use std::convert::TryFrom;
use std::fs::OpenOptions;
use std::os::unix::io::AsRawFd;
use std::path::Path;
#[derive(Debug, Snafu)]
pub struct Error(error::Error);
type Result<T> = std::result::Result<T, Error>;
mod ioctl {
use nix::ioctl_read;
ioctl_read!(blkgetsize64, 0x12, 114, u64);
}
pub(crate) fn get_block_device_size(path: &Path) -> Result<i64> {
let file = OpenOptions::new()
.read(true)
.open(path)
.context(error::OpenFileSnafu { path })?;
let mut block_device_size = 0;
let result = unsafe { ioctl::blkgetsize64(file.as_raw_fd(), &mut block_device_size) }
.context(error::GetBlockDeviceSizeSnafu { path })?;
ensure!(result == 0, error::InvalidBlockDeviceSizeSnafu { result });
let block_device_size =
i64::try_from(block_device_size).with_context(|_| error::ConvertNumberSnafu {
what: "block device size",
number: block_device_size.to_string(),
target: "i64",
})?;
Ok(block_device_size)
}
mod error {
use snafu::Snafu;
use std::path::PathBuf;
#[derive(Debug, Snafu)]
#[snafu(visibility(pub(super)))]
pub(super) enum Error {
#[snafu(display("Failed to open '{}': {}", path.display(), source))]
OpenFile {
path: PathBuf,
source: std::io::Error,
},
#[snafu(display("Failed to get block device size for '{}': {}", path.display(), source))]
GetBlockDeviceSize { path: PathBuf, source: nix::Error },
#[snafu(display("Invalid block device size: {}", result))]
InvalidBlockDeviceSize { result: i32 },
#[snafu(display("Failed to convert {} {} to {}: {}", what, number, target, source))]
ConvertNumber {
what: String,
number: String,
target: String,
source: std::num::TryFromIntError,
},
}
}