ext4-mkfs-sys 0.1.0

FFI bindings to lwext4 for ext4 filesystem creation
Documentation
//! Low-level FFI bindings to lwext4 library.
//!
//! This crate provides raw FFI bindings for the mkfs functionality of lwext4.

#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(dead_code)]

use std::os::raw::{c_char, c_int, c_void};

/// UUID size constant
pub const UUID_SIZE: usize = 16;

/// Filesystem type constants
pub const F_SET_EXT2: c_int = 2;
pub const F_SET_EXT3: c_int = 3;
pub const F_SET_EXT4: c_int = 4;

/// Block cache descriptor (opaque)
#[repr(C)]
pub struct ext4_bcache {
    _opaque: [u8; 0],
}

/// Filesystem descriptor (opaque, large structure)
#[repr(C)]
pub struct ext4_fs {
    _opaque: [u8; 0],
}

/// Block device interface with callbacks
#[repr(C)]
pub struct ext4_blockdev_iface {
    /// Open device function
    pub open: Option<unsafe extern "C" fn(bdev: *mut ext4_blockdev) -> c_int>,

    /// Block read function
    pub bread: Option<
        unsafe extern "C" fn(
            bdev: *mut ext4_blockdev,
            buf: *mut c_void,
            blk_id: u64,
            blk_cnt: u32,
        ) -> c_int,
    >,

    /// Block write function
    pub bwrite: Option<
        unsafe extern "C" fn(
            bdev: *mut ext4_blockdev,
            buf: *const c_void,
            blk_id: u64,
            blk_cnt: u32,
        ) -> c_int,
    >,

    /// Close device function
    pub close: Option<unsafe extern "C" fn(bdev: *mut ext4_blockdev) -> c_int>,

    /// Lock block device (optional)
    pub lock: Option<unsafe extern "C" fn(bdev: *mut ext4_blockdev) -> c_int>,

    /// Unlock block device (optional)
    pub unlock: Option<unsafe extern "C" fn(bdev: *mut ext4_blockdev) -> c_int>,

    /// Physical block size (bytes)
    pub ph_bsize: u32,

    /// Physical block count
    pub ph_bcnt: u64,

    /// Physical block buffer
    pub ph_bbuf: *mut u8,

    /// Reference counter
    pub ph_refctr: u32,

    /// Physical read counter
    pub bread_ctr: u32,

    /// Physical write counter
    pub bwrite_ctr: u32,

    /// User data pointer
    pub p_user: *mut c_void,
}

/// Block device descriptor
#[repr(C)]
pub struct ext4_blockdev {
    /// Block device interface
    pub bdif: *mut ext4_blockdev_iface,

    /// Offset in bdif (for multi partition mode)
    pub part_offset: u64,

    /// Part size in bdif (for multi partition mode)
    pub part_size: u64,

    /// Block cache
    pub bc: *mut ext4_bcache,

    /// Logical block size (bytes)
    pub lg_bsize: u32,

    /// Logical block count
    pub lg_bcnt: u64,

    /// Cache write back mode reference counter
    pub cache_write_back: u32,

    /// The filesystem this block device belongs to
    pub fs: *mut ext4_fs,

    /// Journal (opaque)
    pub journal: *mut c_void,
}

/// mkfs configuration info
#[repr(C)]
pub struct ext4_mkfs_info {
    /// Total length in bytes
    pub len: u64,

    /// Block size in bytes
    pub block_size: u32,

    /// Blocks per group
    pub blocks_per_group: u32,

    /// Inodes per group
    pub inodes_per_group: u32,

    /// Inode size
    pub inode_size: u32,

    /// Total inodes
    pub inodes: u32,

    /// Journal blocks
    pub journal_blocks: u32,

    /// Read-only compatible features
    pub feat_ro_compat: u32,

    /// Compatible features
    pub feat_compat: u32,

    /// Incompatible features
    pub feat_incompat: u32,

    /// Block group descriptor reserve blocks
    pub bg_desc_reserve_blocks: u32,

    /// Descriptor size
    pub dsc_size: u16,

    /// UUID (128-bit)
    pub uuid: [u8; UUID_SIZE],

    /// Enable journal
    pub journal: bool,

    /// Volume label
    pub label: *const c_char,
}

extern "C" {
    /// Initialize block device
    pub fn ext4_block_init(bdev: *mut ext4_blockdev) -> c_int;

    /// Finalize block device
    pub fn ext4_block_fini(bdev: *mut ext4_blockdev) -> c_int;

    /// Bind block cache to block device
    pub fn ext4_block_bind_bcache(bdev: *mut ext4_blockdev, bc: *mut ext4_bcache) -> c_int;

    /// Initialize block cache dynamically
    pub fn ext4_bcache_init_dynamic(bc: *mut ext4_bcache, cnt: u32, itemsize: u32) -> c_int;

    /// Finalize block cache
    pub fn ext4_bcache_fini_dynamic(bc: *mut ext4_bcache) -> c_int;

    /// Read mkfs info from existing filesystem
    pub fn ext4_mkfs_read_info(bdev: *mut ext4_blockdev, info: *mut ext4_mkfs_info) -> c_int;

    /// Create ext2/3/4 filesystem
    pub fn ext4_mkfs(
        fs: *mut ext4_fs,
        bd: *mut ext4_blockdev,
        info: *mut ext4_mkfs_info,
        fs_type: c_int,
    ) -> c_int;
}

impl Default for ext4_blockdev_iface {
    fn default() -> Self {
        Self {
            open: None,
            bread: None,
            bwrite: None,
            close: None,
            lock: None,
            unlock: None,
            ph_bsize: 0,
            ph_bcnt: 0,
            ph_bbuf: std::ptr::null_mut(),
            ph_refctr: 0,
            bread_ctr: 0,
            bwrite_ctr: 0,
            p_user: std::ptr::null_mut(),
        }
    }
}

impl Default for ext4_blockdev {
    fn default() -> Self {
        Self {
            bdif: std::ptr::null_mut(),
            part_offset: 0,
            part_size: 0,
            bc: std::ptr::null_mut(),
            lg_bsize: 0,
            lg_bcnt: 0,
            cache_write_back: 0,
            fs: std::ptr::null_mut(),
            journal: std::ptr::null_mut(),
        }
    }
}

impl Default for ext4_mkfs_info {
    fn default() -> Self {
        Self {
            len: 0,
            block_size: 4096,
            blocks_per_group: 0,
            inodes_per_group: 0,
            inode_size: 256,
            inodes: 0,
            journal_blocks: 0,
            feat_ro_compat: 0,
            feat_compat: 0,
            feat_incompat: 0,
            bg_desc_reserve_blocks: 0,
            dsc_size: 0,
            uuid: [0u8; UUID_SIZE],
            journal: false,
            label: std::ptr::null(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_struct_default_construction() {
        // Verify we can construct the types with default values
        let _iface = ext4_blockdev_iface::default();
        let _bdev = ext4_blockdev::default();
        let _info = ext4_mkfs_info::default();
    }

    #[test]
    fn test_constants() {
        assert_eq!(UUID_SIZE, 16);
        assert_eq!(F_SET_EXT2, 2);
        assert_eq!(F_SET_EXT3, 3);
        assert_eq!(F_SET_EXT4, 4);
    }

    #[test]
    fn test_blockdev_iface_default_values() {
        let iface = ext4_blockdev_iface::default();

        assert!(iface.open.is_none());
        assert!(iface.bread.is_none());
        assert!(iface.bwrite.is_none());
        assert!(iface.close.is_none());
        assert!(iface.lock.is_none());
        assert!(iface.unlock.is_none());
        assert_eq!(iface.ph_bsize, 0);
        assert_eq!(iface.ph_bcnt, 0);
        assert!(iface.ph_bbuf.is_null());
        assert_eq!(iface.ph_refctr, 0);
        assert_eq!(iface.bread_ctr, 0);
        assert_eq!(iface.bwrite_ctr, 0);
        assert!(iface.p_user.is_null());
    }

    #[test]
    fn test_blockdev_default_values() {
        let bdev = ext4_blockdev::default();

        assert!(bdev.bdif.is_null());
        assert_eq!(bdev.part_offset, 0);
        assert_eq!(bdev.part_size, 0);
        assert!(bdev.bc.is_null());
        assert_eq!(bdev.lg_bsize, 0);
        assert_eq!(bdev.lg_bcnt, 0);
        assert_eq!(bdev.cache_write_back, 0);
        assert!(bdev.fs.is_null());
        assert!(bdev.journal.is_null());
    }

    #[test]
    fn test_mkfs_info_default_values() {
        let info = ext4_mkfs_info::default();

        assert_eq!(info.len, 0);
        assert_eq!(info.block_size, 4096);
        assert_eq!(info.blocks_per_group, 0);
        assert_eq!(info.inodes_per_group, 0);
        assert_eq!(info.inode_size, 256);
        assert_eq!(info.inodes, 0);
        assert_eq!(info.journal_blocks, 0);
        assert_eq!(info.feat_ro_compat, 0);
        assert_eq!(info.feat_compat, 0);
        assert_eq!(info.feat_incompat, 0);
        assert_eq!(info.bg_desc_reserve_blocks, 0);
        assert_eq!(info.dsc_size, 0);
        assert_eq!(info.uuid, [0u8; UUID_SIZE]);
        assert!(!info.journal);
        assert!(info.label.is_null());
    }

    #[test]
    fn test_blockdev_iface_custom_values() {
        let mut buffer = vec![0u8; 512];
        let mut iface = ext4_blockdev_iface::default();

        iface.ph_bsize = 512;
        iface.ph_bcnt = 1024;
        iface.ph_bbuf = buffer.as_mut_ptr();

        assert_eq!(iface.ph_bsize, 512);
        assert_eq!(iface.ph_bcnt, 1024);
        assert!(!iface.ph_bbuf.is_null());
    }

    #[test]
    fn test_mkfs_info_custom_values() {
        let mut info = ext4_mkfs_info::default();

        info.len = 10 * 1024 * 1024; // 10MB
        info.block_size = 1024;
        info.inode_size = 128;
        info.uuid = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
        info.journal = true;

        assert_eq!(info.len, 10 * 1024 * 1024);
        assert_eq!(info.block_size, 1024);
        assert_eq!(info.inode_size, 128);
        assert_eq!(info.uuid[0], 1);
        assert_eq!(info.uuid[15], 16);
        assert!(info.journal);
    }
}