file_system/partition/
entry.rs

1//! Partition entry structures for MBR partition tables.
2//!
3//! This module provides the [`Partition_entry_type`] structure which represents
4//! individual partition entries in Master Boot Record (MBR) partition tables.
5//! Each entry contains information about a partition's location, size, type, and bootability.
6
7use core::fmt;
8
9/// MBR partition table entry structure (16 bytes).
10///
11/// This structure represents a single partition entry in an MBR partition table.
12/// Each MBR contains exactly 4 partition entries, defining up to 4 primary partitions.
13/// The structure follows the traditional PC BIOS partition table format.
14///
15/// # Memory Layout
16///
17/// The structure is packed and has a fixed 16-byte layout for MBR compatibility:
18/// - Bytes 0: Boot indicator
19/// - Bytes 1-3: CHS start address (legacy)
20/// - Byte 4: Partition type ID
21/// - Bytes 5-7: CHS end address (legacy)
22/// - Bytes 8-11: LBA start address (little-endian)
23/// - Bytes 12-15: Partition size in sectors (little-endian)
24///
25/// # Examples
26///
27/// ```rust
28/// # extern crate alloc;
29/// use file_system::*;
30///
31/// // Create a new bootable FAT32 partition
32/// let partition = Partition_entry_type::New_with_params(
33///     true,
34///     Partition_type_type::Fat32_lba,
35///     2048,
36///     204800
37/// );
38///
39/// assert!(partition.is_bootable());
40/// assert_eq!(partition.get_start_lba(), 2048);
41/// assert_eq!(partition.get_size_sectors(), 204800);
42/// ```
43#[derive(Debug, Clone, Copy)]
44#[repr(C, packed)]
45pub struct PartitionEntry {
46    /// Boot indicator (0x80 = bootable, 0x00 = non-bootable)
47    pub bootable: u8,
48    /// Starting head
49    pub start_head: u8,
50    /// Starting sector (bits 5-0) and cylinder high bits (bits 7-6)
51    pub start_sector: u8,
52    /// Starting cylinder (low 8 bits)
53    pub start_cylinder: u8,
54    /// Partition type ID
55    pub partition_type: u8,
56    /// Ending head
57    pub end_head: u8,
58    /// Ending sector (bits 5-0) and cylinder high bits (bits 7-6)
59    pub end_sector: u8,
60    /// Ending cylinder (low 8 bits)
61    pub end_cylinder: u8,
62    /// Starting LBA (Logical Block Address)
63    pub start_lba: u32,
64    /// Size in sectors
65    pub size_sectors: u32,
66}
67
68impl PartitionEntry {
69    /// Create a new empty (invalid) partition entry.
70    ///
71    /// All fields are initialized to zero, making this an invalid partition entry
72    /// that will not be recognized by the MBR parser.
73    ///
74    /// # Examples
75    ///
76    /// ```rust
77    /// # extern crate alloc;
78    /// use file_system::*;
79    ///
80    /// let partition = Partition_entry_type::new();
81    /// assert!(!partition.is_valid());
82    /// assert!(!partition.is_bootable());
83    /// ```
84    pub fn new() -> Self {
85        Self {
86            bootable: 0,
87            start_head: 0,
88            start_sector: 0,
89            start_cylinder: 0,
90            partition_type: 0,
91            end_head: 0,
92            end_sector: 0,
93            end_cylinder: 0,
94            start_lba: 0,
95            size_sectors: 0,
96        }
97    }
98
99    /// Create a new partition entry with specified parameters.
100    ///
101    /// This constructor creates a valid partition entry with the specified type,
102    /// location, and size. The CHS (Cylinder-Head-Sector) fields are not set
103    /// as modern systems use LBA addressing.
104    ///
105    /// # Arguments
106    ///
107    /// * `Bootable` - Whether this partition should be marked as bootable
108    /// * `Partition_type` - The type of partition (FAT32, Linux, etc.)
109    /// * `Start_lba` - Starting logical block address (sector number)
110    /// * `Size_sectors` - Size of the partition in 512-byte sectors
111    ///
112    /// # Examples
113    ///
114    /// ```rust
115    /// # extern crate alloc;
116    /// use file_system::*;
117    ///
118    /// // Create a 100MB FAT32 partition starting at sector 2048
119    /// let partition = Partition_entry_type::New_with_params(
120    ///     true,
121    ///     Partition_type_type::Fat32_lba,
122    ///     2048,
123    ///     204800
124    /// );
125    ///
126    /// assert!(partition.is_valid());
127    /// assert!(partition.is_bootable());
128    /// ```
129    pub fn new_with_params(
130        bootable: bool,
131        partition_type: crate::PartitionKind,
132        start_lba: u32,
133        size_sectors: u32,
134    ) -> Self {
135        let mut entry = Self::new();
136        entry.bootable = if bootable { 0x80 } else { 0x00 };
137        entry.set_partition_type(partition_type);
138        entry.start_lba = start_lba.to_le();
139        entry.size_sectors = size_sectors.to_le();
140        entry
141    }
142
143    /// Check if this partition entry is valid (non-zero)
144    pub fn is_valid(&self) -> bool {
145        self.partition_type != 0 && self.size_sectors > 0
146    }
147
148    /// Check if this partition is bootable
149    pub fn is_bootable(&self) -> bool {
150        self.bootable == 0x80
151    }
152
153    /// Set the bootable flag
154    pub fn set_bootable(&mut self, bootable: bool) {
155        self.bootable = if bootable { 0x80 } else { 0x00 };
156    }
157
158    /// Get the starting LBA of this partition
159    pub fn get_start_lba(&self) -> u32 {
160        u32::from_le(self.start_lba)
161    }
162
163    /// Set the starting LBA of this partition
164    pub fn set_start_lba(&mut self, start_lba: u32) {
165        self.start_lba = start_lba.to_le();
166    }
167
168    /// Get the size in sectors of this partition
169    pub fn get_size_sectors(&self) -> u32 {
170        u32::from_le(self.size_sectors)
171    }
172
173    /// Set the size in sectors of this partition
174    pub fn set_size_sectors(&mut self, size_sectors: u32) {
175        self.size_sectors = size_sectors.to_le();
176    }
177
178    /// Get the partition type as an enum
179    pub fn get_partition_type(&self) -> crate::PartitionKind {
180        crate::PartitionKind::from_u8(self.partition_type)
181    }
182
183    /// Set the partition type from an enum
184    pub fn set_partition_type(&mut self, partition_type: crate::PartitionKind) {
185        self.partition_type = partition_type.to_u8();
186    }
187
188    /// Get the partition type as a human-readable string
189    pub fn get_partition_type_name(&self) -> &'static str {
190        self.get_partition_type().get_name()
191    }
192
193    /// Get the end LBA of this partition (start + size - 1)
194    pub fn get_end_lba(&self) -> u32 {
195        self.get_start_lba() + self.get_size_sectors() - 1
196    }
197
198    /// Get the size in bytes of this partition
199    pub fn get_size_bytes(&self) -> u64 {
200        self.get_size_sectors() as u64 * 512
201    }
202
203    /// Check if this partition overlaps with another partition
204    pub fn overlaps_with(&self, other: &Self) -> bool {
205        if !self.is_valid() || !other.is_valid() {
206            return false;
207        }
208
209        let self_start = self.get_start_lba();
210        let self_end = self.get_end_lba();
211        let other_start = other.get_start_lba();
212        let other_end = other.get_end_lba();
213
214        !(self_end < other_start || other_end < self_start)
215    }
216
217    /// Check if a given LBA is within this partition
218    pub fn contains_lba(&self, lba: u32) -> bool {
219        if !self.is_valid() {
220            return false;
221        }
222
223        let start = self.get_start_lba();
224        let end = self.get_end_lba();
225        lba >= start && lba <= end
226    }
227
228    /// Clear the partition entry (make it empty)
229    pub fn clear(&mut self) {
230        *self = Self::new();
231    }
232}
233
234impl Default for PartitionEntry {
235    fn default() -> Self {
236        Self::new()
237    }
238}
239
240impl fmt::Display for PartitionEntry {
241    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
242        if !self.is_valid() {
243            write!(formatter, "Empty partition")
244        } else {
245            write!(
246                formatter,
247                "Partition: Type={:02X} ({}), Start_LBA={}, Size={} sectors ({} MB), Bootable={}",
248                self.partition_type,
249                self.get_partition_type_name(),
250                self.get_start_lba(),
251                self.get_size_sectors(),
252                self.get_size_bytes() / (1024 * 1024),
253                self.is_bootable()
254            )
255        }
256    }
257}
258
259#[cfg(test)]
260mod tests {
261    use super::PartitionEntry;
262    use crate::PartitionKind;
263    use alloc::format;
264
265    fn create_test_partition() -> PartitionEntry {
266        PartitionEntry::new_with_params(
267            true,                    // Bootable
268            PartitionKind::Fat32Lba, // Type
269            2048,                    // Start LBA
270            204800,                  // Size in sectors (100MB)
271        )
272    }
273
274    #[test]
275    fn test_partition_entry_new() {
276        let entry = PartitionEntry::new();
277        assert!(!entry.is_valid());
278        assert!(!entry.is_bootable());
279        assert_eq!(entry.get_start_lba(), 0);
280        assert_eq!(entry.get_size_sectors(), 0);
281        assert_eq!(entry.get_partition_type(), PartitionKind::Empty);
282    }
283
284    #[test]
285    fn test_partition_entry_new_with_params() {
286        let entry = create_test_partition();
287        assert!(entry.is_valid());
288        assert!(entry.is_bootable());
289        assert_eq!(entry.get_start_lba(), 2048);
290        assert_eq!(entry.get_size_sectors(), 204800);
291        assert_eq!(entry.get_partition_type(), PartitionKind::Fat32Lba);
292    }
293
294    #[test]
295    fn test_partition_entry_bootable() {
296        let mut entry = PartitionEntry::new();
297        assert!(!entry.is_bootable());
298
299        entry.set_bootable(true);
300        assert!(entry.is_bootable());
301        assert_eq!(entry.bootable, 0x80);
302
303        entry.set_bootable(false);
304        assert!(!entry.is_bootable());
305        assert_eq!(entry.bootable, 0x00);
306    }
307
308    #[test]
309    fn test_partition_entry_lba() {
310        let mut entry = PartitionEntry::new();
311        assert_eq!(entry.get_start_lba(), 0);
312
313        entry.set_start_lba(12345);
314        assert_eq!(entry.get_start_lba(), 12345);
315    }
316
317    #[test]
318    fn test_partition_entry_size() {
319        let mut entry = PartitionEntry::new();
320        assert_eq!(entry.get_size_sectors(), 0);
321
322        entry.set_size_sectors(67890);
323        assert_eq!(entry.get_size_sectors(), 67890);
324        assert_eq!(entry.get_size_bytes(), 67890 * 512);
325    }
326
327    #[test]
328    fn test_partition_entry_type() {
329        let mut entry = PartitionEntry::new();
330        assert_eq!(entry.get_partition_type(), PartitionKind::Empty);
331
332        entry.set_partition_type(PartitionKind::Linux);
333        assert_eq!(entry.get_partition_type(), PartitionKind::Linux);
334        assert_eq!(entry.partition_type, 0x83);
335    }
336
337    #[test]
338    fn test_partition_entry_end_lba() {
339        let entry = create_test_partition();
340        assert_eq!(entry.get_end_lba(), 2048 + 204800 - 1);
341    }
342
343    #[test]
344    fn test_partition_entry_overlaps() {
345        let partition1 = PartitionEntry::new_with_params(false, PartitionKind::Fat32, 1000, 2000);
346        let partition2 = PartitionEntry::new_with_params(false, PartitionKind::Linux, 2400, 1000);
347        let partition3 =
348            PartitionEntry::new_with_params(false, PartitionKind::LinuxSwap, 1500, 1000);
349
350        // Partition1: 1000-2999, Partition2: 2400-3399, Partition3: 1500-2499
351        assert!(partition1.overlaps_with(&partition3)); // 1000-2999 overlaps 1500-2499
352        assert!(partition2.overlaps_with(&partition3)); // 2400-3399 overlaps 1500-2499 (overlap: 2400-2499)
353        assert!(partition1.overlaps_with(&partition2)); // 1000-2999 overlaps 2400-3399 (overlap: 2400-2999)
354    }
355
356    #[test]
357    fn test_partition_entry_no_overlap() {
358        let partition1 = PartitionEntry::new_with_params(false, PartitionKind::Fat32, 1000, 1000);
359        let partition2 = PartitionEntry::new_with_params(false, PartitionKind::Linux, 2000, 1000);
360
361        // Partition1: 1000-1999, Partition2: 2000-2999
362        assert!(!partition1.overlaps_with(&partition2));
363        assert!(!partition2.overlaps_with(&partition1));
364    }
365
366    #[test]
367    fn test_partition_entry_contains_lba() {
368        let entry = create_test_partition();
369
370        assert!(!entry.contains_lba(2047)); // Before start
371        assert!(entry.contains_lba(2048)); // At start
372        assert!(entry.contains_lba(100000)); // In middle
373        assert!(entry.contains_lba(206847)); // At end (2048 + 204800 - 1)
374        assert!(!entry.contains_lba(206848)); // After end
375    }
376
377    #[test]
378    fn test_partition_entry_clear() {
379        let mut entry = create_test_partition();
380        assert!(entry.is_valid());
381
382        entry.clear();
383        assert!(!entry.is_valid());
384        assert!(!entry.is_bootable());
385        assert_eq!(entry.get_start_lba(), 0);
386        assert_eq!(entry.get_size_sectors(), 0);
387    }
388
389    #[test]
390    fn test_partition_entry_default() {
391        let entry = PartitionEntry::default();
392        assert!(!entry.is_valid());
393        assert_eq!(entry.get_partition_type(), PartitionKind::Empty);
394    }
395
396    #[test]
397    fn test_partition_entry_display() {
398        let entry = create_test_partition();
399        let display_string = format!("{entry}");
400
401        assert!(display_string.contains("Type=0C"));
402        assert!(display_string.contains("FAT32 LBA"));
403        assert!(display_string.contains("Start_LBA=2048"));
404        assert!(display_string.contains("Size=204800"));
405        assert!(display_string.contains("Bootable=true"));
406
407        let empty_entry = PartitionEntry::new();
408        let empty_string = format!("{empty_entry}");
409        assert!(empty_string.contains("Empty partition"));
410    }
411
412    #[test]
413    fn test_partition_entry_size_bytes() {
414        let entry = PartitionEntry::new_with_params(false, PartitionKind::Linux, 0, 2048);
415        assert_eq!(entry.get_size_bytes(), 2048 * 512); // 1MB
416    }
417
418    #[test]
419    fn test_partition_entry_validity() {
420        // Valid partition must have non-zero type and size
421        let valid = PartitionEntry::new_with_params(false, PartitionKind::Linux, 100, 200);
422        assert!(valid.is_valid());
423
424        // Zero size makes it invalid
425        let zero_size = PartitionEntry::new_with_params(false, PartitionKind::Linux, 100, 0);
426        assert!(!zero_size.is_valid());
427
428        // Empty type makes it invalid
429        let empty_type = PartitionEntry::new_with_params(false, PartitionKind::Empty, 100, 200);
430        assert!(!empty_type.is_valid());
431    }
432}