use crate::blockdev::{BlockDevice, BlockDeviceWrapper};
use crate::error::{check_errno, Result};
use crate::types::FsType;
use ext4_lwext4_sys::{ext4_fs, ext4_mkfs, ext4_mkfs_info, UUID_SIZE};
use std::ffi::CString;
use std::ptr;
#[derive(Debug, Clone)]
pub struct MkfsOptions {
pub fs_type: FsType,
pub block_size: u32,
pub inode_size: u32,
pub journal: bool,
pub label: Option<String>,
pub uuid: Option<[u8; 16]>,
}
impl Default for MkfsOptions {
fn default() -> Self {
Self {
fs_type: FsType::Ext4,
block_size: 4096,
inode_size: 256,
journal: true,
label: None,
uuid: None,
}
}
}
impl MkfsOptions {
pub fn ext2() -> Self {
Self {
fs_type: FsType::Ext2,
journal: false,
..Default::default()
}
}
pub fn ext3() -> Self {
Self {
fs_type: FsType::Ext3,
..Default::default()
}
}
pub fn ext4() -> Self {
Self::default()
}
pub fn with_label(mut self, label: impl Into<String>) -> Self {
self.label = Some(label.into());
self
}
pub fn with_uuid(mut self, uuid: [u8; 16]) -> Self {
self.uuid = Some(uuid);
self
}
pub fn with_block_size(mut self, size: u32) -> Self {
self.block_size = size;
self
}
pub fn with_journal(mut self, enabled: bool) -> Self {
self.journal = enabled;
self
}
}
pub fn mkfs<B: BlockDevice + 'static>(device: B, options: &MkfsOptions) -> Result<()> {
let wrapper = BlockDeviceWrapper::new(device);
let bdev_ptr = wrapper.as_bdev_ptr();
let label_cstring = options
.label
.as_ref()
.map(|l| CString::new(l.as_str()).unwrap());
let mut info = ext4_mkfs_info {
len: unsafe { (*bdev_ptr).part_size },
block_size: options.block_size,
blocks_per_group: 0, inodes_per_group: 0, inode_size: options.inode_size,
inodes: 0, journal_blocks: 0, feat_ro_compat: 0,
feat_compat: 0,
feat_incompat: 0,
bg_desc_reserve_blocks: 0,
dsc_size: 0,
uuid: options.uuid.unwrap_or([0u8; UUID_SIZE]),
journal: options.journal && options.fs_type != FsType::Ext2,
label: label_cstring
.as_ref()
.map(|c| c.as_ptr())
.unwrap_or(ptr::null()),
};
let fs_size = unsafe { ext4_lwext4_sys::lwext4_sizeof_ext4_fs() };
let fs_buf = vec![0u8; fs_size];
let fs_ptr = fs_buf.as_ptr() as *mut ext4_fs;
let ret = unsafe {
ext4_mkfs(
fs_ptr,
bdev_ptr,
&mut info,
options.fs_type.to_raw(),
)
};
check_errno(ret)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mkfs_options_builder() {
let opts = MkfsOptions::ext4()
.with_label("test_disk")
.with_block_size(4096)
.with_journal(true);
assert_eq!(opts.fs_type, FsType::Ext4);
assert_eq!(opts.label, Some("test_disk".to_string()));
assert_eq!(opts.block_size, 4096);
assert!(opts.journal);
}
}