Skip to main content

ext4_lwext4/blockdev/
file.rs

1//! File-backed block device implementation.
2
3use crate::error::{Error, Result};
4use crate::blockdev::traits::BlockDevice;
5use std::fs::{File, OpenOptions};
6use std::io::{Read, Seek, SeekFrom, Write};
7use std::path::Path;
8use std::sync::Mutex;
9
10/// A block device backed by a file on the host filesystem.
11///
12/// This is useful for working with disk images or loopback devices.
13pub struct FileBlockDevice {
14    file: Mutex<File>,
15    block_size: u32,
16    block_count: u64,
17}
18
19impl FileBlockDevice {
20    /// Create a new disk image file with the specified size.
21    ///
22    /// # Arguments
23    /// * `path` - Path to create the file at
24    /// * `size` - Total size in bytes
25    /// * `block_size` - Block size (default 512)
26    ///
27    /// # Returns
28    /// A new FileBlockDevice wrapping the created file
29    pub fn create<P: AsRef<Path>>(path: P, size: u64) -> Result<Self> {
30        Self::create_with_block_size(path, size, 512)
31    }
32
33    /// Create a new disk image file with custom block size.
34    pub fn create_with_block_size<P: AsRef<Path>>(
35        path: P,
36        size: u64,
37        block_size: u32,
38    ) -> Result<Self> {
39        let file = OpenOptions::new()
40            .read(true)
41            .write(true)
42            .create(true)
43            .truncate(true)
44            .open(path.as_ref())?;
45
46        // Set file size
47        file.set_len(size)?;
48
49        let block_count = size / block_size as u64;
50
51        Ok(Self {
52            file: Mutex::new(file),
53            block_size,
54            block_count,
55        })
56    }
57
58    /// Open an existing disk image file.
59    ///
60    /// # Arguments
61    /// * `path` - Path to the file
62    ///
63    /// # Returns
64    /// A FileBlockDevice wrapping the file
65    pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
66        Self::open_with_block_size(path, 512)
67    }
68
69    /// Open an existing disk image file with custom block size.
70    pub fn open_with_block_size<P: AsRef<Path>>(path: P, block_size: u32) -> Result<Self> {
71        let file = OpenOptions::new()
72            .read(true)
73            .write(true)
74            .open(path.as_ref())?;
75
76        let size = file.metadata()?.len();
77        let block_count = size / block_size as u64;
78
79        Ok(Self {
80            file: Mutex::new(file),
81            block_size,
82            block_count,
83        })
84    }
85
86    /// Open an existing disk image file in read-only mode.
87    pub fn open_read_only<P: AsRef<Path>>(path: P) -> Result<Self> {
88        Self::open_read_only_with_block_size(path, 512)
89    }
90
91    /// Open an existing disk image file in read-only mode with custom block size.
92    pub fn open_read_only_with_block_size<P: AsRef<Path>>(
93        path: P,
94        block_size: u32,
95    ) -> Result<Self> {
96        let file = OpenOptions::new()
97            .read(true)
98            .open(path.as_ref())?;
99
100        let size = file.metadata()?.len();
101        let block_count = size / block_size as u64;
102
103        Ok(Self {
104            file: Mutex::new(file),
105            block_size,
106            block_count,
107        })
108    }
109}
110
111impl BlockDevice for FileBlockDevice {
112    fn read_blocks(&self, block_id: u64, buf: &mut [u8]) -> Result<u32> {
113        let offset = block_id * self.block_size as u64;
114        let block_count = buf.len() as u32 / self.block_size;
115
116        let mut file = self.file.lock().map_err(|_| {
117            Error::Io(std::io::Error::new(
118                std::io::ErrorKind::Other,
119                "lock poisoned",
120            ))
121        })?;
122
123        file.seek(SeekFrom::Start(offset))?;
124        file.read_exact(buf)?;
125
126        Ok(block_count)
127    }
128
129    fn write_blocks(&mut self, block_id: u64, buf: &[u8]) -> Result<u32> {
130        let offset = block_id * self.block_size as u64;
131        let block_count = buf.len() as u32 / self.block_size;
132
133        let mut file = self.file.lock().map_err(|_| {
134            Error::Io(std::io::Error::new(
135                std::io::ErrorKind::Other,
136                "lock poisoned",
137            ))
138        })?;
139
140        file.seek(SeekFrom::Start(offset))?;
141        file.write_all(buf)?;
142
143        Ok(block_count)
144    }
145
146    fn flush(&mut self) -> Result<()> {
147        let file = self.file.lock().map_err(|_| {
148            Error::Io(std::io::Error::new(
149                std::io::ErrorKind::Other,
150                "lock poisoned",
151            ))
152        })?;
153        file.sync_all()?;
154        Ok(())
155    }
156
157    fn block_size(&self) -> u32 {
158        self.block_size
159    }
160
161    fn block_count(&self) -> u64 {
162        self.block_count
163    }
164}