morpheus_core/disk/gpt_ops/
find.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
// GPT operations using gpt-disk-rs

use super::{FreeRegion, GptError};
use gpt_disk_io::{BlockIo, Disk};

/// Scan disk for GPT and populate partition table
pub fn find_free_space<B: BlockIo>(
    block_io: B,
    block_size_bytes: usize,
) -> Result<[Option<FreeRegion>; 16], GptError> {
    let mut disk = Disk::new(block_io).map_err(|_| GptError::IoError)?;

    let header = disk
        .read_primary_gpt_header(&mut [0u8; 512])
        .map_err(|_| GptError::InvalidHeader)?;

    let first_usable = header.first_usable_lba.to_u64();
    let last_usable = header.last_usable_lba.to_u64();

    // Get partition layout
    let layout = header
        .get_partition_entry_array_layout()
        .map_err(|_| GptError::InvalidHeader)?;

    // Read partitions to find used ranges
    let mut entry_buf = [0u8; 4096];
    let entry_buffer = &mut entry_buf[..block_size_bytes];

    let iter = disk
        .gpt_partition_entry_array_iter(layout, entry_buffer)
        .map_err(|_| GptError::IoError)?;

    let mut used_ranges: [(u64, u64); 16] = [(0, 0); 16];
    let mut used_count = 0;

    for entry_result in iter {
        let entry = entry_result.map_err(|_| GptError::IoError)?;

        if !entry.is_used() {
            continue;
        }

        if used_count < 16 {
            used_ranges[used_count] = (entry.starting_lba.to_u64(), entry.ending_lba.to_u64());
            used_count += 1;
        }
    }

    let mut regions = [None; 16];
    let mut region_count = 0;

    // Sort by start LBA
    for i in 0..used_count {
        for j in i + 1..used_count {
            if used_ranges[j].0 < used_ranges[i].0 {
                used_ranges.swap(i, j);
            }
        }
    }

    // Find gaps
    let mut current = first_usable;

    for i in 0..used_count {
        let (start, end) = used_ranges[i];

        if current < start && region_count < 16 {
            regions[region_count] = Some(FreeRegion {
                start_lba: current,
                end_lba: start - 1,
            });
            region_count += 1;
        }

        current = end + 1;
    }

    // Add final region if space left
    if current <= last_usable && region_count < 16 {
        regions[region_count] = Some(FreeRegion {
            start_lba: current,
            end_lba: last_usable,
        });
    }

    Ok(regions)
}