morpheus_core/disk/
gpt_writer.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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
// GPT creation and manipulation

use super::gpt::GptHeader;

pub struct PartitionEditor {
    entries: [u8; 16384], // 128 entries x 128 bytes
    modified: bool,
}

impl PartitionEditor {
    pub fn new() -> Self {
        Self {
            entries: [0u8; 16384],
            modified: false,
        }
    }

    pub fn load_from_buffer(&mut self, buffer: &[u8]) {
        let len = buffer.len().min(16384);
        self.entries[..len].copy_from_slice(&buffer[..len]);
        self.modified = false;
    }

    pub fn get_buffer(&self) -> &[u8] {
        &self.entries
    }

    pub fn find_free_slot(&self) -> Option<usize> {
        for i in 0..128 {
            let offset = i * 128;
            let entry_type = &self.entries[offset..offset + 16];
            if entry_type == &[0u8; 16] {
                return Some(i);
            }
        }
        None
    }

    pub fn add_partition(
        &mut self,
        slot: usize,
        type_guid: [u8; 16],
        start_lba: u64,
        end_lba: u64,
        name: &str,
    ) -> Result<(), ()> {
        if slot >= 128 {
            return Err(());
        }

        let entry = create_partition_entry(type_guid, start_lba, end_lba, name);
        let offset = slot * 128;
        self.entries[offset..offset + 128].copy_from_slice(&entry);
        self.modified = true;

        Ok(())
    }

    pub fn delete_partition(&mut self, slot: usize) -> Result<(), ()> {
        if slot >= 128 {
            return Err(());
        }

        let offset = slot * 128;
        self.entries[offset..offset + 128].fill(0);
        self.modified = true;

        Ok(())
    }

    pub fn is_modified(&self) -> bool {
        self.modified
    }
}

pub fn find_free_space(partitions: &[(u64, u64)], disk_size_lba: u64) -> Option<(u64, u64)> {
    let first_usable = 34u64;
    let last_usable = disk_size_lba.saturating_sub(34);

    if partitions.is_empty() {
        return Some((first_usable, last_usable));
    }

    // Sort by start LBA
    let mut sorted: [(u64, u64); 16] = [(0, 0); 16];
    let count = partitions.len().min(16);
    sorted[..count].copy_from_slice(&partitions[..count]);

    for _ in 0..count {
        for j in 0..count - 1 {
            if sorted[j].0 > sorted[j + 1].0 {
                sorted.swap(j, j + 1);
            }
        }
    }

    // Check gap before first partition
    if sorted[0].0 > first_usable + 1024 {
        return Some((first_usable, sorted[0].0 - 1));
    }

    // Check gaps between partitions
    for i in 0..count - 1 {
        let gap_start = sorted[i].1 + 1;
        let gap_end = sorted[i + 1].0 - 1;
        if gap_end > gap_start + 1024 {
            return Some((gap_start, gap_end));
        }
    }

    // Check gap after last partition
    if sorted[count - 1].1 < last_usable - 1024 {
        return Some((sorted[count - 1].1 + 1, last_usable));
    }

    None
}

/// Create a blank GPT header for a disk
pub fn create_gpt_header(disk_size_lba: u64) -> GptHeader {
    GptHeader {
        signature: *b"EFI PART",
        revision: 0x00010000, // GPT 1.0
        header_size: 92,
        header_crc32: 0, // Calculate later
        reserved: 0,
        current_lba: 1,
        backup_lba: disk_size_lba - 1,
        first_usable_lba: 34,
        last_usable_lba: disk_size_lba - 34,
        disk_guid: [0u8; 16], // Generate random later
        partition_entry_lba: 2,
        num_partition_entries: 128,
        partition_entry_size: 128,
        partition_array_crc32: 0, // Calculate later
    }
}

/// Write GPT header to a 512-byte buffer
pub fn write_gpt_header(header: &GptHeader, buffer: &mut [u8; 512]) {
    buffer.fill(0);

    // Signature
    buffer[0..8].copy_from_slice(&header.signature);

    // Revision (little-endian u32)
    buffer[8..12].copy_from_slice(&header.revision.to_le_bytes());

    // Header size
    buffer[12..16].copy_from_slice(&header.header_size.to_le_bytes());

    // CRC32 (placeholder)
    buffer[16..20].copy_from_slice(&header.header_crc32.to_le_bytes());

    // Reserved
    buffer[20..24].copy_from_slice(&header.reserved.to_le_bytes());

    // Current LBA
    buffer[24..32].copy_from_slice(&header.current_lba.to_le_bytes());

    // Backup LBA
    buffer[32..40].copy_from_slice(&header.backup_lba.to_le_bytes());

    // First usable LBA
    buffer[40..48].copy_from_slice(&header.first_usable_lba.to_le_bytes());

    // Last usable LBA
    buffer[48..56].copy_from_slice(&header.last_usable_lba.to_le_bytes());

    // Disk GUID
    buffer[56..72].copy_from_slice(&header.disk_guid);

    // Partition entry LBA
    buffer[72..80].copy_from_slice(&header.partition_entry_lba.to_le_bytes());

    // Number of partition entries
    buffer[80..84].copy_from_slice(&header.num_partition_entries.to_le_bytes());

    // Partition entry size
    buffer[84..88].copy_from_slice(&header.partition_entry_size.to_le_bytes());

    // Partition array CRC32
    buffer[88..92].copy_from_slice(&header.partition_array_crc32.to_le_bytes());
}

/// Create a blank partition entry
pub fn create_partition_entry(
    type_guid: [u8; 16],
    start_lba: u64,
    end_lba: u64,
    name: &str,
) -> [u8; 128] {
    let mut entry = [0u8; 128];

    // Type GUID
    entry[0..16].copy_from_slice(&type_guid);

    // Partition GUID (random, for now just use non-zero)
    entry[16] = 1;

    // Starting LBA
    entry[32..40].copy_from_slice(&start_lba.to_le_bytes());

    // Ending LBA
    entry[40..48].copy_from_slice(&end_lba.to_le_bytes());

    // Attributes
    entry[48..56].fill(0);

    // Partition name (UTF-16LE, max 36 characters)
    let name_bytes = name.as_bytes();
    for (i, &byte) in name_bytes.iter().take(36).enumerate() {
        entry[56 + i * 2] = byte;
        entry[56 + i * 2 + 1] = 0; // UTF-16 high byte
    }

    entry
}