file_system/devices/
partition_device.rs

1//! Partition device implementation for accessing individual partitions.
2
3//!
4//! This module provides [`PartitionDevice`], which allows treating individual
5//! partitions on a storage device as separate devices. This is essential for file
6//! systems that need to operate on specific partitions rather than entire disks.
7
8use core::fmt;
9
10use shared::AnyByLayout;
11
12use crate::{
13    BaseOperations, ControlCommand, ControlCommandIdentifier, DirectBaseOperations,
14    DirectBlockDevice, Error, MountOperations, Position, Result, Size,
15    block_device::{self, GET_BLOCK_COUNT, GET_BLOCK_SIZE},
16};
17
18/// A device implementation that represents a partition within a larger storage device.
19///
20/// This type wraps a base device and provides access to only a specific region (partition)
21/// of that device. It maintains its own position cursor and ensures all operations stay
22/// within the partition boundaries. This allows file systems to operate on individual
23/// partitions without needing to know about the partition layout.
24///
25///
26/// Note: The partition device does not manage position state internally and does not use atomic operations.
27/// Thread safety depends on the underlying base device implementation.
28///
29///
30/// # Examples
31///
32/// ```rust
33/// # extern crate alloc;
34/// # use file_system::{MemoryDevice, PartitionDevice, DirectBaseOperations};
35///
36/// // Create a memory device for testing
37/// let base_device = MemoryDevice::<512>::new(1024 * 1024);
38///
39/// // Create a partition device for blocks 100-199 (100 blocks of 512 bytes = 51.2KB)
40/// let partition_device = PartitionDevice::new(&base_device, 100, 100, 512);
41///
42/// // Now you can use partition_device like any other device
43/// let data = b"Hello, Partition!";
44/// partition_device.write(data, 0).unwrap();
45/// ```
46pub struct PartitionDevice<'a, D> {
47    /// Base device containing this partition
48    base_device: &'a D,
49    /// Block size
50    block_size: u32,
51    /// Byte offset from the beginning of the base device
52    offset: Size,
53    /// Size of this partition in bytes
54    size: Size,
55}
56
57impl<'a, D: BaseOperations> PartitionDevice<'a, D> {
58    /// Create a new partition device with explicit byte offset and size.
59    ///
60    /// # Arguments
61    ///
62    /// * `base_device` - The underlying storage device
63    /// * `start_block` - Block index where the partition starts
64    /// * `block_count` - Number of blocks in the partition
65    /// * `block_size` - Size of each block in bytes
66    ///
67    /// # Examples
68    ///
69    /// ```rust
70    /// # extern crate alloc;
71    /// # use file_system::{MemoryDevice, PartitionDevice};
72    ///
73    /// let base_device = MemoryDevice::<512>::new(1024 * 1024);
74    /// // Create a partition starting at block 128 (64KB) with 256 blocks (128KB)
75    /// let partition = PartitionDevice::new(&base_device, 128, 256, 512);
76    /// ```
77    pub fn new(base_device: &'a D, start_block: Size, block_count: Size, block_size: u32) -> Self {
78        Self {
79            base_device,
80            block_size,
81            offset: start_block * (block_size as Size),
82            size: block_count * (block_size as Size),
83        }
84    }
85    /// Get the number of blocks in this partition.
86    ///
87    /// # Returns
88    ///
89    /// The number of blocks in this partition, where each block is of the partition's configured block size.
90    /// The number of 512-byte sectors this partition contains.
91    ///
92    /// # Examples
93    ///
94    /// ```rust
95    /// # extern crate alloc;
96    /// # use file_system::{MemoryDevice, PartitionDevice};
97    ///
98    /// let base_device = MemoryDevice::<512>::new(1024 * 1024);
99    /// let partition = PartitionDevice::new(&base_device, 100, 50, 512);
100    /// assert_eq!(partition.get_block_count(), 50);
101    /// ```
102    pub const fn get_block_count(&self) -> u32 {
103        self.size as u32 / self.block_size
104    }
105
106    pub const fn get_start_lba(&self) -> Size {
107        self.offset / (self.block_size as Size)
108    }
109
110    /// Get the base device
111    pub const fn get_base_device(&self) -> &D {
112        self.base_device
113    }
114
115    /// Check if the partition device is valid (non-zero size)
116    pub const fn is_valid(&self) -> bool {
117        self.size > 0
118    }
119
120    const fn get_device_position(&self, absolute_position: Size) -> Option<Size> {
121        if absolute_position >= self.size {
122            return None;
123        }
124
125        let device_position = match self.offset.checked_add(absolute_position) {
126            Some(pos) => pos,
127            None => return None,
128        };
129
130        Some(device_position)
131    }
132
133    const fn get_total_buffer_length(
134        &self,
135        absolute_position: Size,
136        buffer_length: usize,
137    ) -> usize {
138        let remaining_size = self.size.saturating_sub(absolute_position) as usize;
139
140        if buffer_length > remaining_size {
141            remaining_size
142        } else {
143            buffer_length
144        }
145    }
146}
147
148impl<'a, D: DirectBaseOperations> DirectBaseOperations for PartitionDevice<'a, D> {
149    fn read(&self, buffer: &mut [u8], absolute_position: Size) -> Result<usize> {
150        let device_position = match self.get_device_position(absolute_position) {
151            Some(pos) => pos,
152            None => return Ok(0),
153        };
154
155        let read_size = self.get_total_buffer_length(absolute_position, buffer.len());
156
157        // Read from base device
158        let bytes_read = self
159            .base_device
160            .read(&mut buffer[..read_size], device_position)?;
161
162        Ok(bytes_read)
163    }
164
165    fn write(&self, buffer: &[u8], absolute_position: Size) -> Result<usize> {
166        let device_position = match self.get_device_position(absolute_position) {
167            Some(pos) => pos,
168            None => return Ok(0),
169        };
170
171        let write_size = self.get_total_buffer_length(absolute_position, buffer.len());
172
173        // Write to base device
174        let bytes_written = self
175            .base_device
176            .write(&buffer[..write_size], device_position)?;
177
178        Ok(bytes_written)
179    }
180
181    fn write_pattern(
182        &self,
183        pattern: &[u8],
184        count: usize,
185        absolute_position: Size,
186    ) -> Result<usize> {
187        let device_position = match self.get_device_position(absolute_position) {
188            Some(pos) => pos,
189            None => return Ok(0),
190        };
191
192        let total_write_size = pattern.len() * count;
193        let maximum_write_size = self.get_total_buffer_length(absolute_position, total_write_size);
194
195        let adjusted_count = if total_write_size > maximum_write_size {
196            // Adjust count to fit within partition boundaries
197            maximum_write_size / pattern.len()
198        } else {
199            count
200        };
201
202        self.base_device
203            .write_pattern(pattern, adjusted_count, device_position)
204    }
205
206    fn set_position(&self, current_position: Size, position: &Position) -> Result<Size> {
207        let position = block_device::set_position(current_position, position, self.size)?;
208
209        let device_position = self
210            .get_device_position(position)
211            .ok_or(Error::InvalidParameter)?;
212
213        self.base_device
214            .set_position(device_position, &Position::Start(0))?;
215
216        Ok(position)
217    }
218
219    fn flush(&self) -> Result<()> {
220        self.base_device.flush()
221    }
222
223    fn control(
224        &self,
225        command: ControlCommandIdentifier,
226        input: &AnyByLayout,
227        output: &mut AnyByLayout,
228    ) -> Result<()> {
229        match command {
230            GET_BLOCK_SIZE::IDENTIFIER => {
231                let output = GET_BLOCK_SIZE::cast_output(output)?;
232
233                *output = self.block_size;
234            }
235            GET_BLOCK_COUNT::IDENTIFIER => {
236                *output
237                    .cast_mutable::<Size>()
238                    .ok_or(Error::InvalidParameter)? = self.get_block_count() as Size;
239            }
240            _ => return self.base_device.control(command, input, output),
241        }
242
243        Ok(())
244    }
245}
246
247impl<'a, D: MountOperations> MountOperations for PartitionDevice<'a, D> {}
248
249impl<'a, D: DirectBlockDevice> DirectBlockDevice for PartitionDevice<'a, D> {}
250
251impl<'a, D> fmt::Debug for PartitionDevice<'a, D> {
252    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
253        formatter
254            .debug_struct("PartitionDevice")
255            .field("block_size", &self.block_size)
256            .field("offset", &self.offset)
257            .field("size", &self.size)
258            .finish()
259    }
260}
261
262#[cfg(test)]
263mod tests {
264    use alloc::boxed::Box;
265
266    use crate::{MemoryDevice, implement_block_device_tests};
267
268    use super::*;
269
270    implement_block_device_tests!(PartitionDevice::new(
271        Box::leak(Box::new(MemoryDevice::<512>::new(1024 * 1024))),
272        0,
273        1024 * 512,
274        512
275    ));
276}