|
| 1 | +#![cfg(target_os = "linux")] |
| 2 | + |
| 3 | +use std::fs; |
| 4 | +use std::io; |
| 5 | +use std::os::linux::fs::MetadataExt; |
| 6 | +use std::os::unix::io::AsRawFd; |
| 7 | + |
| 8 | +mod ioctl { |
| 9 | + ioctl_read_bad!(blksszget, 0x1268, u64); |
| 10 | + ioctl_none!(blkrrpart, 0x12, 95); |
| 11 | +} |
| 12 | + |
| 13 | +const S_IFMT: u32 = 0o170_000; |
| 14 | +const S_IFBLK: u32 = 0o60_000; |
| 15 | + |
| 16 | +/// An error that can happen while doing an ioctl call with a block device |
| 17 | +#[derive(Debug, Error)] |
| 18 | +pub enum BlockError { |
| 19 | + /// An error that occurs when the metadata of the input file couldn't be retrieved |
| 20 | + #[error(display = "failed to get metadata of device fd")] |
| 21 | + Metadata(#[error(cause)] io::Error), |
| 22 | + /// An error that occurs when the partition table could not be reloaded by the OS |
| 23 | + #[error(display = "failed to reload partition table of device")] |
| 24 | + RereadTable(#[error(cause)] nix::Error), |
| 25 | + /// An error that occurs when an invalid return code has been received from an ioctl call |
| 26 | + #[error(display = "invalid return value of ioctl ({} != 0)", _0)] |
| 27 | + InvalidReturnValue(i32), |
| 28 | + /// An error that occurs when the file provided is not a block device |
| 29 | + #[error(display = "not a block device")] |
| 30 | + NotBlock, |
| 31 | +} |
| 32 | + |
| 33 | +/// Makes an ioctl call to make the OS reread the partition table of a block device |
| 34 | +pub fn reread_partition_table(file: &mut fs::File) -> Result<(), BlockError> { |
| 35 | + let metadata = file.metadata().map_err(BlockError::Metadata)?; |
| 36 | + |
| 37 | + if metadata.st_mode() & S_IFMT == S_IFBLK { |
| 38 | + match unsafe { ioctl::blkrrpart(file.as_raw_fd()) } { |
| 39 | + Err(err) => Err(BlockError::RereadTable(err)), |
| 40 | + Ok(0) => Ok(()), |
| 41 | + Ok(r) => Err(BlockError::InvalidReturnValue(r)), |
| 42 | + } |
| 43 | + } else { |
| 44 | + Err(BlockError::NotBlock) |
| 45 | + } |
| 46 | +} |
| 47 | + |
| 48 | +/// Makes an ioctl call to obtain the sector size of a block device |
| 49 | +pub fn get_sector_size(file: &mut fs::File) -> Result<u64, BlockError> { |
| 50 | + let metadata = file.metadata().map_err(BlockError::Metadata)?; |
| 51 | + let mut sector_size = 512; |
| 52 | + |
| 53 | + if metadata.st_mode() & S_IFMT == S_IFBLK { |
| 54 | + match unsafe { ioctl::blksszget(file.as_raw_fd(), &mut sector_size) } { |
| 55 | + Err(err) => Err(BlockError::RereadTable(err)), |
| 56 | + Ok(0) => Ok(sector_size), |
| 57 | + Ok(r) => Err(BlockError::InvalidReturnValue(r)), |
| 58 | + } |
| 59 | + } else { |
| 60 | + Err(BlockError::NotBlock) |
| 61 | + } |
| 62 | +} |
0 commit comments