Skip to main content

ext4_lwext4/blockdev/
mod.rs

1//! Block device abstraction layer.
2//!
3//! This module provides the [`BlockDevice`] trait and implementations for
4//! various backing stores, as well as the internal bridge to lwext4's C API.
5
6mod file;
7mod memory;
8mod traits;
9
10pub use file::FileBlockDevice;
11pub use memory::MemoryBlockDevice;
12pub use traits::{BlockDevice, BlockDeviceExt};
13
14use crate::error::Error;
15use ext4_lwext4_sys::{ext4_blockdev, ext4_blockdev_iface};
16use std::ffi::c_void;
17use std::os::raw::c_int;
18use std::pin::Pin;
19use std::ptr;
20use std::cell::UnsafeCell;
21
22/// Internal wrapper that bridges a Rust BlockDevice to lwext4's C callbacks.
23///
24/// This structure holds the Rust device and provides the C-compatible
25/// ext4_blockdev and ext4_blockdev_iface structures that lwext4 expects.
26pub(crate) struct BlockDeviceWrapper {
27    /// The underlying Rust block device (type-erased)
28    device: UnsafeCell<Box<dyn BlockDevice>>,
29    /// Physical block buffer for lwext4
30    buffer: Pin<Box<[u8]>>,
31    /// Block device interface with callbacks
32    bdif: Pin<Box<ext4_blockdev_iface>>,
33    /// Block device structure for lwext4
34    bdev: Pin<Box<ext4_blockdev>>,
35}
36
37// Safety: We ensure thread safety through careful management of the wrapper
38unsafe impl Send for BlockDeviceWrapper {}
39unsafe impl Sync for BlockDeviceWrapper {}
40
41impl BlockDeviceWrapper {
42    /// Create a new wrapper for a BlockDevice.
43    pub fn new<B: BlockDevice + 'static>(device: B) -> Pin<Box<Self>> {
44        let block_size = device.block_size();
45        let block_count = device.block_count();
46
47        // Allocate buffer for physical block operations
48        let buffer = vec![0u8; block_size as usize].into_boxed_slice();
49        let buffer = Pin::new(buffer);
50
51        // Create the block device interface with callbacks
52        let bdif = Box::new(ext4_blockdev_iface {
53            open: Some(blockdev_open),
54            bread: Some(blockdev_bread),
55            bwrite: Some(blockdev_bwrite),
56            close: Some(blockdev_close),
57            lock: Some(blockdev_lock),
58            unlock: Some(blockdev_unlock),
59            ph_bsize: block_size,
60            ph_bcnt: block_count,
61            ph_bbuf: ptr::null_mut(), // Will be set after Pin
62            ph_refctr: 0,
63            bread_ctr: 0,
64            bwrite_ctr: 0,
65            p_user: ptr::null_mut(), // Will be set after Pin
66        });
67        let bdif = Pin::new(bdif);
68
69        // Create the block device structure
70        let bdev = Box::new(ext4_blockdev {
71            bdif: ptr::null_mut(), // Will be set after Pin
72            part_offset: 0,
73            part_size: block_count * block_size as u64,
74            bc: ptr::null_mut(),
75            lg_bsize: 0,
76            lg_bcnt: 0,
77            cache_write_back: 0,
78            fs: ptr::null_mut(),
79            journal: ptr::null_mut(),
80        });
81        let bdev = Pin::new(bdev);
82
83        let wrapper = Box::new(Self {
84            device: UnsafeCell::new(Box::new(device)),
85            buffer,
86            bdif,
87            bdev,
88        });
89
90        let wrapper = Pin::new(wrapper);
91
92        // Now fix up the pointers
93        unsafe {
94            let wrapper_ptr = &*wrapper as *const Self as *mut Self;
95
96            // Set buffer pointer in bdif
97            let bdif_ptr = &*wrapper.bdif as *const ext4_blockdev_iface as *mut ext4_blockdev_iface;
98            (*bdif_ptr).ph_bbuf = wrapper.buffer.as_ptr() as *mut u8;
99            (*bdif_ptr).p_user = wrapper_ptr as *mut c_void;
100
101            // Set bdif pointer in bdev
102            let bdev_ptr = &*wrapper.bdev as *const ext4_blockdev as *mut ext4_blockdev;
103            (*bdev_ptr).bdif = bdif_ptr;
104        }
105
106        wrapper
107    }
108
109    /// Get a pointer to the ext4_blockdev structure for use with lwext4.
110    pub fn as_bdev_ptr(&self) -> *mut ext4_blockdev {
111        &*self.bdev as *const ext4_blockdev as *mut ext4_blockdev
112    }
113
114    /// Get a mutable reference to the underlying device.
115    ///
116    /// # Safety
117    /// Caller must ensure no concurrent access to the device.
118    #[allow(dead_code)]
119    pub(crate) unsafe fn device_mut(&self) -> &mut dyn BlockDevice {
120        unsafe { (*self.device.get()).as_mut() }
121    }
122}
123
124// ============================================================================
125// C callback trampolines
126// ============================================================================
127
128/// Get the BlockDeviceWrapper from a blockdev pointer
129unsafe fn get_wrapper(bdev: *mut ext4_blockdev) -> &'static BlockDeviceWrapper {
130    unsafe {
131        let bdif = (*bdev).bdif;
132        let wrapper_ptr = (*bdif).p_user as *const BlockDeviceWrapper;
133        &*wrapper_ptr
134    }
135}
136
137/// Open callback
138unsafe extern "C" fn blockdev_open(bdev: *mut ext4_blockdev) -> c_int {
139    unsafe {
140        let wrapper = get_wrapper(bdev);
141        let device = (*wrapper.device.get()).as_mut();
142
143        match device.open() {
144            Ok(()) => 0,
145            Err(e) => match e {
146                Error::Io(io) => io.raw_os_error().unwrap_or(libc::EIO),
147                _ => libc::EIO,
148            },
149        }
150    }
151}
152
153/// Block read callback
154unsafe extern "C" fn blockdev_bread(
155    bdev: *mut ext4_blockdev,
156    buf: *mut c_void,
157    blk_id: u64,
158    blk_cnt: u32,
159) -> c_int {
160    unsafe {
161        let wrapper = get_wrapper(bdev);
162        let device = (*wrapper.device.get()).as_ref();
163        let block_size = device.block_size() as usize;
164        let total_size = block_size * blk_cnt as usize;
165
166        let slice = std::slice::from_raw_parts_mut(buf as *mut u8, total_size);
167
168        match device.read_blocks(blk_id, slice) {
169            Ok(_) => 0,
170            Err(e) => match e {
171                Error::Io(io) => io.raw_os_error().unwrap_or(libc::EIO),
172                _ => libc::EIO,
173            },
174        }
175    }
176}
177
178/// Block write callback
179unsafe extern "C" fn blockdev_bwrite(
180    bdev: *mut ext4_blockdev,
181    buf: *const c_void,
182    blk_id: u64,
183    blk_cnt: u32,
184) -> c_int {
185    unsafe {
186        let wrapper = get_wrapper(bdev);
187        let device = (*wrapper.device.get()).as_mut();
188        let block_size = device.block_size() as usize;
189        let total_size = block_size * blk_cnt as usize;
190
191        let slice = std::slice::from_raw_parts(buf as *const u8, total_size);
192
193        match device.write_blocks(blk_id, slice) {
194            Ok(_) => 0,
195            Err(e) => match e {
196                Error::Io(io) => io.raw_os_error().unwrap_or(libc::EIO),
197                _ => libc::EIO,
198            },
199        }
200    }
201}
202
203/// Close callback
204unsafe extern "C" fn blockdev_close(bdev: *mut ext4_blockdev) -> c_int {
205    unsafe {
206        let wrapper = get_wrapper(bdev);
207        let device = (*wrapper.device.get()).as_mut();
208
209        match device.close() {
210            Ok(()) => 0,
211            Err(e) => match e {
212                Error::Io(io) => io.raw_os_error().unwrap_or(libc::EIO),
213                _ => libc::EIO,
214            },
215        }
216    }
217}
218
219/// Lock callback (no-op for single-threaded use)
220unsafe extern "C" fn blockdev_lock(_bdev: *mut ext4_blockdev) -> c_int {
221    0
222}
223
224/// Unlock callback (no-op for single-threaded use)
225unsafe extern "C" fn blockdev_unlock(_bdev: *mut ext4_blockdev) -> c_int {
226    0
227}