file_system/partition/
device.rs

1//! Partition device implementation for accessing individual partitions.
2
3//!
4//! This module provides [`Partition_device_type`], 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 crate::{Device, DeviceTrait, Result, Size};
11
12/// A device implementation that represents a partition within a larger storage device.
13///
14/// This type wraps a base device and provides access to only a specific region (partition)
15/// of that device. It maintains its own position cursor and ensures all operations stay
16/// within the partition boundaries. This allows file systems to operate on individual
17/// partitions without needing to know about the partition layout.
18///
19/// # Thread Safety
20///
21/// The partition device is thread-safe and uses atomic operations for position management.
22/// Multiple threads can safely access the same partition device simultaneously.
23///
24/// # Examples
25///
26/// ```rust
27/// # extern crate alloc;
28/// # use file_system::*;
29///
30/// // Create a memory device for testing
31/// let base_device = create_device!(Memory_device_type::<512>::new(1024 * 1024));
32///
33/// // Create a partition device for sectors 100-199 (50KB partition)
34/// let partition = Partition_device_type::New_from_lba(base_device, 100, 100);
35/// let partition_device = create_device!(partition);
36///
37/// // Now you can use partition_device like any other device
38/// let data = b"Hello, Partition!";
39/// partition_device.Write(data).unwrap();
40/// ```
41pub struct PartitionDevice {
42    /// Base device containing this partition
43    base_device: Device,
44    /// Byte offset from the beginning of the base device
45    offset: u64,
46    /// Size of this partition in bytes
47    size: u64,
48    /// Current position within this partition (atomic for thread safety)
49    position: core::sync::atomic::AtomicU64,
50}
51
52impl PartitionDevice {
53    /// Create a new partition device with explicit byte offset and size.
54    ///
55    /// # Arguments
56    ///
57    /// * `base_device` - The underlying storage device
58    /// * `offset` - Byte offset from the beginning of the base device
59    /// * `size` - Size of the partition in bytes
60    ///
61    /// # Examples
62    ///
63    /// ```rust
64    /// # extern crate alloc;
65    /// # use file_system::*;
66    ///
67    /// let base_device = create_device!(Memory_device_type::<512>::new(1024 * 1024));
68    /// // Create a partition starting at 64KB with 128KB size
69    /// let partition = Partition_device_type::new(base_device, 64 * 1024, 128 * 1024);
70    /// ```
71    pub fn new(base_device: Device, offset: u64, size: u64) -> Self {
72        Self {
73            base_device,
74            offset,
75            size,
76            position: core::sync::atomic::AtomicU64::new(0),
77        }
78    }
79
80    /// Create a partition device from LBA (Logical Block Address) parameters.
81    ///
82    /// This is a convenience method for creating partition devices using standard
83    /// disk partitioning terminology. It assumes 512-byte sectors.
84    ///
85    /// # Arguments
86    ///
87    /// * `base_device` - The underlying storage device
88    /// * `start_lba` - Starting logical block address (sector number)
89    /// * `sector_count` - Number of 512-byte sectors in this partition
90    ///
91    /// # Examples
92    ///
93    /// ```rust
94    /// # extern crate alloc;
95    /// # use file_system::*;
96    ///
97    /// let base_device = create_device!(Memory_device_type::<512>::new(1024 * 1024));
98    /// // Create a partition starting at sector 2048 with 1024 sectors (512KB)
99    /// let partition = Partition_device_type::New_from_lba(base_device, 2048, 1024);
100    /// ```
101    pub fn new_from_lba(base_device: Device, start_lba: u32, sector_count: u32) -> Self {
102        let offset = start_lba as u64 * 512;
103        let size = sector_count as u64 * 512;
104        Self::new(base_device, offset, size)
105    }
106
107    /// Get the byte offset of this partition within the base device.
108    ///
109    /// # Returns
110    ///
111    /// The absolute byte offset from the beginning of the base device.
112    ///
113    /// # Examples
114    ///
115    /// ```rust
116    /// # extern crate alloc;
117    /// # use file_system::*;
118    ///
119    /// let base_device = create_device!(Memory_device_type::<512>::new(1024 * 1024));
120    /// let partition = Partition_device_type::New_from_lba(base_device, 100, 50);
121    /// assert_eq!(partition.get_offset(), 100 * 512);
122    /// ```
123    pub fn get_offset(&self) -> u64 {
124        self.offset
125    }
126
127    /// Get the size of this partition in bytes.
128    ///
129    /// # Returns
130    ///
131    /// The total size of the partition in bytes.
132    ///
133    /// # Examples
134    ///
135    /// ```rust
136    /// # extern crate alloc;
137    /// # use file_system::*;
138    ///
139    /// let base_device = create_device!(Memory_device_type::<512>::new(1024 * 1024));
140    /// let partition = Partition_device_type::New_from_lba(base_device, 100, 50);
141    /// assert_eq!(partition.get_partition_size(), 50 * 512);
142    /// ```
143    pub fn get_partition_size(&self) -> u64 {
144        self.size
145    }
146
147    /// Get the starting LBA (Logical Block Address) of this partition.
148    ///
149    /// # Returns
150    ///
151    /// The sector number where this partition starts (assuming 512-byte sectors).
152    ///
153    /// # Examples
154    ///
155    /// ```rust
156    /// # extern crate alloc;
157    /// # use file_system::*;
158    ///
159    /// let base_device = create_device!(Memory_device_type::<512>::new(1024 * 1024));
160    /// let partition = Partition_device_type::New_from_lba(base_device, 100, 50);
161    /// assert_eq!(partition.get_start_lba(), 100);
162    /// ```
163    pub fn get_start_lba(&self) -> u32 {
164        (self.offset / 512) as u32
165    }
166
167    /// Get the size in sectors of this partition.
168    ///
169    /// # Returns
170    ///
171    /// The number of 512-byte sectors this partition contains.
172    ///
173    /// # Examples
174    ///
175    /// ```rust
176    /// # extern crate alloc;
177    /// # use file_system::*;
178    ///
179    /// let base_device = create_device!(Memory_device_type::<512>::new(1024 * 1024));
180    /// let partition = Partition_device_type::New_from_lba(base_device, 100, 50);
181    /// assert_eq!(partition.get_sector_count(), 50);
182    /// ```
183    pub fn get_sector_count(&self) -> u32 {
184        (self.size / 512) as u32
185    }
186
187    /// Get the current position within the partition
188    pub fn get_position(&self) -> u64 {
189        self.position.load(core::sync::atomic::Ordering::Relaxed)
190    }
191
192    /// Get the base device
193    pub fn get_base_device(&self) -> &Device {
194        &self.base_device
195    }
196
197    /// Check if the partition device is valid (non-zero size)
198    pub fn is_valid(&self) -> bool {
199        self.size > 0
200    }
201
202    /// Get remaining bytes that can be read/written from current position
203    pub fn get_remaining_bytes(&self) -> u64 {
204        let current_pos = self.get_position();
205        self.size.saturating_sub(current_pos)
206    }
207
208    /// Check if we're at the end of the partition
209    pub fn is_at_end(&self) -> bool {
210        self.get_position() >= self.size
211    }
212}
213
214impl Clone for PartitionDevice {
215    fn clone(&self) -> Self {
216        Self {
217            base_device: self.base_device.clone(),
218            offset: self.offset,
219            size: self.size,
220            position: core::sync::atomic::AtomicU64::new(0), // Reset position for clone
221        }
222    }
223}
224
225impl DeviceTrait for PartitionDevice {
226    fn read(&self, buffer: &mut [u8]) -> Result<Size> {
227        let current_pos = self.position.load(core::sync::atomic::Ordering::Relaxed);
228
229        if current_pos >= self.size {
230            return Ok(Size::new(0));
231        }
232
233        let available = (self.size - current_pos).min(buffer.len() as u64);
234        let read_size = available as usize;
235
236        // Set position in base device
237        let absolute_pos = self.offset + current_pos;
238        self.base_device
239            .set_position(&crate::Position::Start(absolute_pos))?;
240
241        // Read from base device
242        let bytes_read = self.base_device.read(&mut buffer[..read_size])?;
243
244        // Update position
245        self.position.store(
246            current_pos + bytes_read.as_u64(),
247            core::sync::atomic::Ordering::Relaxed,
248        );
249
250        Ok(bytes_read)
251    }
252
253    fn write(&self, buffer: &[u8]) -> Result<Size> {
254        let current_pos = self.position.load(core::sync::atomic::Ordering::Relaxed);
255
256        if current_pos >= self.size {
257            return Ok(Size::new(0));
258        }
259
260        let available = (self.size - current_pos).min(buffer.len() as u64);
261        let write_size = available as usize;
262
263        // Set position in base device
264        let absolute_pos = self.offset + current_pos;
265        self.base_device
266            .set_position(&crate::Position::Start(absolute_pos))?;
267
268        // Write to base device
269        let bytes_written = self.base_device.write(&buffer[..write_size])?;
270
271        // Update position
272        self.position.store(
273            current_pos + bytes_written.as_u64(),
274            core::sync::atomic::Ordering::Relaxed,
275        );
276
277        Ok(bytes_written)
278    }
279
280    fn get_size(&self) -> Result<Size> {
281        Ok(Size::new(self.size))
282    }
283
284    fn set_position(&self, position: &crate::Position) -> Result<Size> {
285        use crate::Position;
286
287        let new_pos = match position {
288            Position::Start(offset) => *offset,
289            Position::Current(offset) => {
290                let current = self.position.load(core::sync::atomic::Ordering::Relaxed);
291                if *offset >= 0 {
292                    current.saturating_add(*offset as u64)
293                } else {
294                    current.saturating_sub((-*offset) as u64)
295                }
296            }
297            Position::End(offset) => {
298                if *offset >= 0 {
299                    self.size.saturating_add(*offset as u64)
300                } else {
301                    self.size.saturating_sub((-*offset) as u64)
302                }
303            }
304        };
305
306        let clamped_pos = new_pos.min(self.size);
307        self.position
308            .store(clamped_pos, core::sync::atomic::Ordering::Relaxed);
309
310        Ok(Size::new(clamped_pos))
311    }
312
313    fn flush(&self) -> Result<()> {
314        self.base_device.flush()
315    }
316
317    fn is_a_block_device(&self) -> bool {
318        self.base_device.is_a_block_device()
319    }
320
321    fn get_block_size(&self) -> Result<usize> {
322        self.base_device.get_block_size()
323    }
324
325    fn is_a_terminal(&self) -> bool {
326        false // Partition devices are never terminals
327    }
328
329    fn erase(&self) -> Result<()> {
330        // For partition devices, we delegate erase to the base device
331        // But we need to be careful about the range
332        self.base_device.erase()
333    }
334}
335
336impl fmt::Debug for PartitionDevice {
337    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
338        formatter
339            .debug_struct("Partition_device_type")
340            .field("offset", &self.offset)
341            .field("size", &self.size)
342            .field("start_lba", &self.get_start_lba())
343            .field("sector_count", &self.get_sector_count())
344            .field("position", &self.get_position())
345            .field("remaining_bytes", &self.get_remaining_bytes())
346            .field("is_valid", &self.is_valid())
347            .finish()
348    }
349}
350
351impl fmt::Display for PartitionDevice {
352    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
353        write!(
354            formatter,
355            "Partition Device: Start LBA={}, Sectors={}, Size={} bytes, Position={}/{}",
356            self.get_start_lba(),
357            self.get_sector_count(),
358            self.size,
359            self.get_position(),
360            self.size
361        )
362    }
363}
364
365#[cfg(test)]
366mod tests {
367    use super::PartitionDevice;
368    use crate::{Device, DeviceTrait, MemoryDevice, Position};
369
370    /// Create a mock memory device for testing
371    fn create_test_device() -> Device {
372        let memory_device = MemoryDevice::<512>::new(4096);
373        crate::create_device!(memory_device)
374    }
375
376    #[test]
377    fn test_partition_device_creation() {
378        let base_device = create_test_device();
379        let partition = PartitionDevice::new(base_device, 512, 1024);
380
381        assert_eq!(partition.get_offset(), 512);
382        assert_eq!(partition.get_partition_size(), 1024);
383        assert_eq!(partition.get_position(), 0);
384        assert!(partition.is_valid());
385    }
386
387    #[test]
388    fn test_partition_device_from_lba() {
389        let base_device = create_test_device();
390        let partition = PartitionDevice::new_from_lba(base_device, 4, 8);
391
392        assert_eq!(partition.get_offset(), 4 * 512); // 2048
393        assert_eq!(partition.get_partition_size(), 8 * 512); // 4096
394        assert_eq!(partition.get_start_lba(), 4);
395        assert_eq!(partition.get_sector_count(), 8);
396    }
397
398    #[test]
399    fn test_partition_device_lba_calculations() {
400        let base_device = create_test_device();
401        let partition = PartitionDevice::new(base_device, 1024, 2048);
402
403        assert_eq!(partition.get_start_lba(), 2); // 1024 / 512
404        assert_eq!(partition.get_sector_count(), 4); // 2048 / 512
405    }
406
407    #[test]
408    fn test_partition_device_validity() {
409        let base_device = create_test_device();
410
411        let valid_partition = PartitionDevice::new(base_device.clone(), 0, 1024);
412        assert!(valid_partition.is_valid());
413
414        let invalid_partition = PartitionDevice::new(base_device, 0, 0);
415        assert!(!invalid_partition.is_valid());
416    }
417
418    #[test]
419    fn test_partition_device_remaining_bytes() {
420        let base_device = create_test_device();
421        let partition = PartitionDevice::new(base_device, 0, 1000);
422
423        // Initially, all bytes are available
424        assert_eq!(partition.get_remaining_bytes(), 1000);
425        assert!(!partition.is_at_end());
426
427        // Simulate advancing position
428        let _ = partition.set_position(&Position::Start(500));
429        assert_eq!(partition.get_remaining_bytes(), 500);
430        assert!(!partition.is_at_end());
431
432        // Move to end
433        let _ = partition.set_position(&Position::Start(1000));
434        assert_eq!(partition.get_remaining_bytes(), 0);
435        assert!(partition.is_at_end());
436
437        // Beyond end
438        let _ = partition.set_position(&Position::Start(1500));
439        assert_eq!(partition.get_remaining_bytes(), 0);
440        assert!(partition.is_at_end());
441    }
442
443    #[test]
444    fn test_partition_device_position_setting() {
445        let base_device = create_test_device();
446        let partition = PartitionDevice::new(base_device, 0, 1000);
447
448        // Test Start position
449        let result = partition.set_position(&Position::Start(100));
450        assert!(result.is_ok());
451        assert_eq!(result.unwrap().as_u64(), 100);
452        assert_eq!(partition.get_position(), 100);
453
454        // Test Current position (positive offset)
455        let result = partition.set_position(&Position::Current(50));
456        assert!(result.is_ok());
457        assert_eq!(result.unwrap().as_u64(), 150);
458        assert_eq!(partition.get_position(), 150);
459
460        // Test Current position (negative offset)
461        let result = partition.set_position(&Position::Current(-30));
462        assert!(result.is_ok());
463        assert_eq!(result.unwrap().as_u64(), 120);
464        assert_eq!(partition.get_position(), 120);
465
466        // Test End position (negative offset)
467        let result = partition.set_position(&Position::End(-200));
468        assert!(result.is_ok());
469        assert_eq!(result.unwrap().as_u64(), 800);
470        assert_eq!(partition.get_position(), 800);
471
472        // Test End position (positive offset) - should clamp to size
473        let result = partition.set_position(&Position::End(500));
474        assert!(result.is_ok());
475        assert_eq!(result.unwrap().as_u64(), 1000);
476        assert_eq!(partition.get_position(), 1000);
477
478        // Test position beyond partition size - should clamp
479        let result = partition.set_position(&Position::Start(2000));
480        assert!(result.is_ok());
481        assert_eq!(result.unwrap().as_u64(), 1000);
482        assert_eq!(partition.get_position(), 1000);
483    }
484
485    #[test]
486    fn test_partition_device_get_size() {
487        let base_device = create_test_device();
488        let partition = PartitionDevice::new(base_device, 100, 1500);
489
490        let size_result = partition.get_size();
491        assert!(size_result.is_ok());
492        assert_eq!(size_result.unwrap().as_u64(), 1500);
493    }
494
495    #[test]
496    fn test_partition_device_read() {
497        let base_device = create_test_device();
498        let partition = PartitionDevice::new(base_device, 0, 100);
499
500        // Test normal read
501        let mut buffer = [0u8; 50];
502        let result = partition.read(&mut buffer);
503        assert!(result.is_ok());
504        assert_eq!(result.unwrap().as_u64(), 50);
505        assert_eq!(partition.get_position(), 50);
506
507        // Test read at end of partition
508        let mut buffer = [0u8; 100];
509        let result = partition.read(&mut buffer);
510        assert!(result.is_ok());
511        assert_eq!(result.unwrap().as_u64(), 50); // Only 50 bytes remaining
512        assert_eq!(partition.get_position(), 100);
513
514        // Test read beyond end
515        let mut buffer = [0u8; 10];
516        let result = partition.read(&mut buffer);
517        assert!(result.is_ok());
518        assert_eq!(result.unwrap().as_u64(), 0); // No bytes to read
519        assert_eq!(partition.get_position(), 100);
520    }
521
522    #[test]
523    fn test_partition_device_write() {
524        let base_device = create_test_device();
525        let partition = PartitionDevice::new(base_device, 0, 100);
526
527        // Test normal write
528        let buffer = [0x42u8; 30];
529        let result = partition.write(&buffer);
530        assert!(result.is_ok());
531        assert_eq!(result.unwrap().as_u64(), 30);
532        assert_eq!(partition.get_position(), 30);
533
534        // Test write at end of partition
535        let _ = partition.set_position(&Position::Start(80));
536        let buffer = [0x42u8; 30];
537        let result = partition.write(&buffer);
538        assert!(result.is_ok());
539        assert_eq!(result.unwrap().as_u64(), 20); // Only 20 bytes available
540        assert_eq!(partition.get_position(), 100);
541
542        // Test write beyond end
543        let buffer = [0x42u8; 10];
544        let result = partition.write(&buffer);
545        assert!(result.is_ok());
546        assert_eq!(result.unwrap().as_u64(), 0); // No bytes to write
547        assert_eq!(partition.get_position(), 100);
548    }
549
550    #[test]
551    fn test_partition_device_block_operations() {
552        let base_device = create_test_device();
553        let partition = PartitionDevice::new(base_device, 0, 1000);
554
555        // Test block device properties (should delegate to base device)
556        let is_block = partition.is_a_block_device();
557        let block_size = partition.get_block_size();
558
559        // These depend on the base device implementation
560        // For memory devices, typically not block devices
561        assert!(!is_block);
562        assert!(block_size.is_ok());
563
564        // Test terminal property
565        assert!(!partition.is_a_terminal());
566    }
567
568    #[test]
569    fn test_partition_device_flush_and_erase() {
570        let base_device = create_test_device();
571        let partition = PartitionDevice::new(base_device, 0, 1000);
572
573        // Test flush (should delegate to base device)
574        let flush_result = partition.flush();
575        assert!(flush_result.is_ok());
576
577        // Test erase (should delegate to base device)
578        let erase_result = partition.erase();
579        assert!(erase_result.is_ok());
580    }
581
582    #[test]
583    fn test_partition_device_debug_display() {
584        let base_device = create_test_device();
585        let partition = PartitionDevice::new_from_lba(base_device, 10, 20);
586
587        // Test Debug formatting
588        let debug_str = alloc::format!("{partition:?}");
589        assert!(debug_str.contains("Partition_device_type"));
590        assert!(debug_str.contains("offset"));
591        assert!(debug_str.contains("size"));
592        assert!(debug_str.contains("start_lba"));
593
594        // Test Display formatting
595        let display_str = alloc::format!("{partition}");
596        assert!(display_str.contains("Partition Device"));
597        assert!(display_str.contains("Start LBA=10"));
598        assert!(display_str.contains("Sectors=20"));
599    }
600
601    #[test]
602    fn test_partition_device_edge_cases() {
603        let base_device = create_test_device();
604
605        // Test zero offset partition
606        let partition1 = PartitionDevice::new(base_device.clone(), 0, 512);
607        assert_eq!(partition1.get_start_lba(), 0);
608        assert_eq!(partition1.get_sector_count(), 1);
609
610        // Test single byte partition
611        let partition2 = PartitionDevice::new(base_device.clone(), 512, 1);
612        assert_eq!(partition2.get_partition_size(), 1);
613        assert!(partition2.is_valid());
614
615        // Test large LBA values
616        let partition3 = PartitionDevice::new_from_lba(base_device, 0xFFFFFFFF - 1, 1);
617        assert_eq!(partition3.get_start_lba(), 0xFFFFFFFF - 1);
618        assert_eq!(partition3.get_sector_count(), 1);
619    }
620
621    #[test]
622    fn test_partition_device_concurrent_access() {
623        let base_device = create_test_device();
624        let partition = PartitionDevice::new(base_device, 0, 1000);
625
626        // Test that position is thread-safe (atomic operations)
627        let _ = partition.set_position(&Position::Start(100));
628        assert_eq!(partition.get_position(), 100);
629
630        // Position should be consistent across multiple reads
631        for _ in 0..10 {
632            assert_eq!(partition.get_position(), 100);
633        }
634    }
635
636    #[test]
637    fn test_partition_device_clone() {
638        let base_device = create_test_device();
639        let original = PartitionDevice::new(base_device, 512, 1024);
640        let cloned = original.clone();
641
642        // Test that cloned partition has same properties
643        assert_eq!(original.get_offset(), cloned.get_offset());
644        assert_eq!(original.get_partition_size(), cloned.get_partition_size());
645        assert_eq!(original.get_start_lba(), cloned.get_start_lba());
646        assert_eq!(original.get_sector_count(), cloned.get_sector_count());
647
648        // Position should be independent after clone
649        let _ = original.set_position(&Position::Start(100));
650        assert_eq!(original.get_position(), 100);
651        assert_eq!(cloned.get_position(), 0); // Cloned device should start at 0
652    }
653}