]> code.delx.au - refind/blob - gptsync/gptsync.c
2696e66d0a336b308b1fabfd5d597d89d73e30dc
[refind] / gptsync / gptsync.c
1 /*
2 * gptsync/gptsync.c
3 * Platform-independent code for syncing GPT and MBR
4 *
5 * Copyright (c) 2006-2007 Christoph Pfisterer
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the
18 * distribution.
19 *
20 * * Neither the name of Christoph Pfisterer nor the names of the
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 */
36 /* Changes copyright (c) 2013 Roderick W. Smith */
37
38 #include "gptsync.h"
39
40 #include "syslinux_mbr.h"
41 #define memcpy(a, b, c) CopyMem(a, b, c)
42
43 //
44 // MBR functions
45 //
46
47 static UINTN check_mbr(VOID)
48 {
49 UINTN i, k;
50 BOOLEAN found = FALSE;
51
52 // check each entry
53 for (i = 0; i < mbr_part_count; i++) {
54 // check for overlap
55 for (k = 0; k < mbr_part_count; k++) {
56 if (k != i && !(mbr_parts[i].start_lba > mbr_parts[k].end_lba || mbr_parts[k].start_lba > mbr_parts[i].end_lba)) {
57 Print(L"Status: MBR partition table is invalid, partitions overlap.\n");
58 return EFI_UNSUPPORTED;
59 }
60 }
61
62 // check for extended partitions
63 if (mbr_parts[i].mbr_type == 0x05 || mbr_parts[i].mbr_type == 0x0f || mbr_parts[i].mbr_type == 0x85) {
64 Print(L"Status: Extended partition found in MBR table, will not touch this disk.\n",
65 gpt_parts[i].gpt_parttype->name);
66 return EFI_UNSUPPORTED;
67 }
68
69 // Check for matching GPT partitition; if not found, flag error
70 if ((mbr_parts[i].mbr_type != 0xEE) && (mbr_parts[i].mbr_type != 0x00)) {
71 found = FALSE;
72 for (k = 0; (k < gpt_part_count) && !found; k++) {
73 if ((mbr_parts[i].start_lba == gpt_parts[k].start_lba) && (mbr_parts[i].end_lba == gpt_parts[k].end_lba)) {
74 found = TRUE;
75 } // if
76 } // for
77 if (!found) {
78 Print(L"Status: Found MBR partition with no matching GPT partition. Re-syncing could\n");
79 Print(L"destroy data; will not touch this disk.\n");
80 return EFI_UNSUPPORTED;
81 } // if
82 } // if
83
84 } // for
85
86 return 0;
87 } // UINTN check_mbr()
88
89 static UINTN write_mbr(VOID)
90 {
91 UINTN status;
92 UINTN i, k;
93 UINT8 active;
94 UINT64 lba;
95 MBR_PART_INFO *table;
96 BOOLEAN have_bootcode;
97
98 Print(L"\nWriting new MBR...\n");
99
100 // read MBR data
101 status = read_sector(0, sector);
102 if (status != 0)
103 return status;
104
105 // write partition table
106 *((UINT16 *)(sector + 510)) = 0xaa55;
107
108 table = (MBR_PART_INFO *)(sector + 446);
109 active = 0x80;
110 for (i = 0; i < 4; i++) {
111 for (k = 0; k < new_mbr_part_count; k++) {
112 if (new_mbr_parts[k].index == i)
113 break;
114 }
115 if (k >= new_mbr_part_count) {
116 // unused entry
117 table[i].flags = 0;
118 table[i].start_chs[0] = 0;
119 table[i].start_chs[1] = 0;
120 table[i].start_chs[2] = 0;
121 table[i].type = 0;
122 table[i].end_chs[0] = 0;
123 table[i].end_chs[1] = 0;
124 table[i].end_chs[2] = 0;
125 table[i].start_lba = 0;
126 table[i].size = 0;
127 } else {
128 if (new_mbr_parts[k].active) {
129 table[i].flags = active;
130 active = 0x00;
131 } else
132 table[i].flags = 0x00;
133 table[i].start_chs[0] = 0xfe;
134 table[i].start_chs[1] = 0xff;
135 table[i].start_chs[2] = 0xff;
136 table[i].type = new_mbr_parts[k].mbr_type;
137 table[i].end_chs[0] = 0xfe;
138 table[i].end_chs[1] = 0xff;
139 table[i].end_chs[2] = 0xff;
140
141 lba = new_mbr_parts[k].start_lba;
142 if (lba > MAX_MBR_LBA) {
143 Print(L"Warning: Partition %d starts beyond 2 TiB limit\n", i+1);
144 lba = MAX_MBR_LBA;
145 }
146 table[i].start_lba = (UINT32)lba;
147
148 lba = new_mbr_parts[k].end_lba + 1 - new_mbr_parts[k].start_lba;
149 if (lba > MAX_MBR_LBA) {
150 Print(L"Warning: Partition %d extends beyond 2 TiB limit\n", i+1);
151 lba = MAX_MBR_LBA;
152 }
153 table[i].size = (UINT32)lba;
154 }
155 }
156
157 // add boot code if necessary
158 have_bootcode = FALSE;
159 for (i = 0; i < MBR_BOOTCODE_SIZE; i++) {
160 if (sector[i] != 0) {
161 have_bootcode = TRUE;
162 break;
163 }
164 }
165 if (!have_bootcode) {
166 // no boot code found in the MBR, add the syslinux MBR code
167 SetMem(sector, MBR_BOOTCODE_SIZE, 0);
168 CopyMem(sector, syslinux_mbr, SYSLINUX_MBR_SIZE);
169 }
170
171 // write MBR data
172 status = write_sector(0, sector);
173 if (status != 0)
174 return status;
175
176 Print(L"MBR updated successfully!\n");
177
178 return 0;
179 }
180
181 //
182 // GPT functions
183 //
184
185 static UINTN check_gpt(VOID)
186 {
187 UINTN i, k;
188
189 if (gpt_part_count == 0) {
190 Print(L"Status: No GPT partition table, no need to sync.\n");
191 return EFI_UNSUPPORTED;
192 }
193
194 // check each entry
195 for (i = 0; i < gpt_part_count; i++) {
196 // check sanity
197 if (gpt_parts[i].end_lba < gpt_parts[i].start_lba) {
198 Print(L"Status: GPT partition table is invalid.\n");
199 return EFI_UNSUPPORTED;
200 }
201 // check for overlap
202 for (k = 0; k < gpt_part_count; k++) {
203 if (k != i && !(gpt_parts[i].start_lba > gpt_parts[k].end_lba || gpt_parts[k].start_lba > gpt_parts[i].end_lba)) {
204 Print(L"Status: GPT partition table is invalid, partitions overlap.\n");
205 return EFI_UNSUPPORTED;
206 }
207 }
208
209 // check for partitions kind
210 if (gpt_parts[i].gpt_parttype->kind == GPT_KIND_FATAL) {
211 Print(L"Status: GPT partition of type '%s' found, will not touch this disk.\n",
212 gpt_parts[i].gpt_parttype->name);
213 return EFI_UNSUPPORTED;
214 }
215 }
216
217 return 0;
218 } // VOID check_gpt()
219
220 //
221 // compare GPT and MBR tables
222 //
223
224 #define ACTION_NONE (0)
225 #define ACTION_NOP (1)
226 #define ACTION_REWRITE (2)
227
228 // Copy a single GPT entry to the new_mbr_parts array.
229 static VOID copy_gpt_to_new_mbr(UINTN gpt_num, UINTN mbr_num) {
230 new_mbr_parts[mbr_num].index = mbr_num;
231 new_mbr_parts[mbr_num].start_lba = gpt_parts[gpt_num].start_lba;
232 new_mbr_parts[mbr_num].end_lba = gpt_parts[gpt_num].end_lba;
233 new_mbr_parts[mbr_num].mbr_type = gpt_parts[gpt_num].mbr_type;
234 new_mbr_parts[mbr_num].active = FALSE;
235 } // VOID copy_gpt_to_new_mbr()
236
237 // A simple bubble sort for the MBR partitions.
238 static VOID sort_mbr(PARTITION_INFO *parts) {
239 PARTITION_INFO one_part;
240 int c, d;
241
242 if (parts == NULL)
243 return;
244
245 for (c = 0 ; c < 3; c++) {
246 for (d = 1 ; d < 3 - c; d++) {
247 if ((parts[d].start_lba > parts[d + 1].start_lba) && (parts[d].start_lba > 0) && (parts[d + 1].start_lba > 0)) {
248 one_part = parts[d];
249 parts[d] = parts[d + 1];
250 parts[d + 1] = one_part;
251 parts[d].index = d;
252 parts[d + 1].index = d + 1;
253 } // if
254 } // for
255 } // for
256 } // VOID sort_mbr()
257
258 // Generate a hybrid MBR based on the current GPT. Stores the result in the
259 // new_mbr_parts[] array.
260 static VOID generate_hybrid_mbr(VOID) {
261 UINTN i, k, iter, count_active;
262 UINT64 first_used_lba;
263
264 new_mbr_part_count = 1;
265 first_used_lba = (UINT64) MAX_MBR_LBA + (UINT64) 1;
266
267 // Copy partitions in three passes....
268 // First, do FAT and NTFS partitions....
269 i = 0;
270 do {
271 if ((gpt_parts[i].start_lba > 0) && (gpt_parts[i].end_lba > 0) &&
272 (gpt_parts[i].end_lba <= MAX_MBR_LBA) && /* Within MBR limits */
273 (gpt_parts[i].gpt_parttype->kind == GPT_KIND_BASIC_DATA) && /* MS Basic Data GPT type code */
274 (gpt_parts[i].mbr_type != 0x83)) { /* Not containing Linux filesystem */
275 copy_gpt_to_new_mbr(i, new_mbr_part_count);
276 if (new_mbr_parts[new_mbr_part_count].start_lba < first_used_lba)
277 first_used_lba = new_mbr_parts[new_mbr_part_count].start_lba;
278
279 new_mbr_part_count++;
280 }
281 i++;
282 } while (i < gpt_part_count && new_mbr_part_count <= 3);
283
284 // Second, do Linux partitions. Note that we start from the END of the
285 // partition list, so as to maximize the space covered by the 0xEE
286 // partition if there are several Linux partitions before other hybridized
287 // partitions.
288 i = gpt_part_count - 1; // Note that gpt_part_count can't be 0; filtered by check_gpt()
289 while (i < gpt_part_count && new_mbr_part_count <= 3) { // if too few GPT partitions, i loops around to a huge value
290 if ((gpt_parts[i].start_lba > 0) && (gpt_parts[i].end_lba > 0) &&
291 (gpt_parts[i].end_lba <= MAX_MBR_LBA) &&
292 ((gpt_parts[i].gpt_parttype->kind == GPT_KIND_DATA) || (gpt_parts[i].gpt_parttype->kind == GPT_KIND_BASIC_DATA)) &&
293 (gpt_parts[i].mbr_type == 0x83)) {
294 copy_gpt_to_new_mbr(i, new_mbr_part_count);
295 if (new_mbr_parts[new_mbr_part_count].start_lba < first_used_lba)
296 first_used_lba = new_mbr_parts[new_mbr_part_count].start_lba;
297
298 new_mbr_part_count++;
299 }
300 i--;
301 } // while
302
303 // Third, do anything that's left to cover uncovered spaces; but this requires
304 // first creating the EFI protective entry, since we don't want to bother with
305 // anything already covered by this entry....
306 new_mbr_parts[0].index = 0;
307 new_mbr_parts[0].start_lba = 1;
308 new_mbr_parts[0].end_lba = (disk_size() > first_used_lba) ? (first_used_lba - 1) : disk_size() - 1;
309 if (new_mbr_parts[0].end_lba > MAX_MBR_LBA)
310 new_mbr_parts[0].end_lba = MAX_MBR_LBA;
311 new_mbr_parts[0].mbr_type = 0xEE;
312 i = 0;
313 while (i < gpt_part_count && new_mbr_part_count <= 3) {
314 if ((gpt_parts[i].start_lba > new_mbr_parts[0].end_lba) && (gpt_parts[i].end_lba > 0) &&
315 (gpt_parts[i].end_lba <= MAX_MBR_LBA) &&
316 (gpt_parts[i].gpt_parttype->kind != GPT_KIND_BASIC_DATA) &&
317 (gpt_parts[i].mbr_type != 0x83)) {
318 copy_gpt_to_new_mbr(i, new_mbr_part_count);
319 new_mbr_part_count++;
320 }
321 i++;
322 } // while
323
324 // find matching partitions in the old MBR table, copy undetected details....
325 for (i = 1; i < new_mbr_part_count; i++) {
326 for (k = 0; k < mbr_part_count; k++) {
327 if (mbr_parts[k].start_lba == new_mbr_parts[i].start_lba) {
328 // keep type if not detected
329 if (new_mbr_parts[i].mbr_type == 0)
330 new_mbr_parts[i].mbr_type = mbr_parts[k].mbr_type;
331 // keep active flag
332 new_mbr_parts[i].active = mbr_parts[k].active;
333 break;
334 } // if
335 } // for (k...)
336 if (new_mbr_parts[i].mbr_type == 0) {
337 // final fallback: set to a (hopefully) unused type
338 new_mbr_parts[i].mbr_type = 0xc0;
339 } // if
340 } // for (i...)
341
342 sort_mbr(new_mbr_parts);
343
344 // make sure there's exactly one active partition
345 for (iter = 0; iter < 3; iter++) {
346 // check
347 count_active = 0;
348 for (i = 0; i < new_mbr_part_count; i++)
349 if (new_mbr_parts[i].active)
350 count_active++;
351 if (count_active == 1)
352 break;
353
354 // set active on the first matching partition
355 if (count_active == 0) {
356 for (i = 0; i < new_mbr_part_count; i++) {
357 if (((new_mbr_parts[i].mbr_type == 0x07 || // NTFS
358 new_mbr_parts[i].mbr_type == 0x0b || // FAT32
359 new_mbr_parts[i].mbr_type == 0x0c)) || // FAT32 (LBA)
360 (iter >= 1 && (new_mbr_parts[i].mbr_type == 0x83)) || // Linux
361 (iter >= 2 && i > 0)) {
362 new_mbr_parts[i].active = TRUE;
363 break;
364 }
365 }
366 } else if (count_active > 1 && iter == 0) {
367 // too many active partitions, try deactivating the ESP / EFI Protective entry
368 if ((new_mbr_parts[0].mbr_type == 0xee || new_mbr_parts[0].mbr_type == 0xef) &&
369 new_mbr_parts[0].active) {
370 new_mbr_parts[0].active = FALSE;
371 }
372 } else if (count_active > 1 && iter > 0) {
373 // too many active partitions, deactivate all but the first one
374 count_active = 0;
375 for (i = 0; i < new_mbr_part_count; i++)
376 if (new_mbr_parts[i].active) {
377 if (count_active > 0)
378 new_mbr_parts[i].active = FALSE;
379 count_active++;
380 }
381 }
382 } // for
383 } // VOID generate_hybrid_mbr()
384
385 // Examine partitions and decide whether a rewrite is in order.
386 // Note that this function MAY ask user for advice.
387 // Note that this function assumes the new hybrid MBR has already
388 // been computed and stored in new_mbr_parts[].
389 static BOOLEAN should_rewrite(VOID) {
390 BOOLEAN retval = TRUE, all_identical = TRUE, invalid;
391 UINTN i, num_existing_hybrid = 0, num_new_hybrid = 0;
392
393 // Check to see if the proposed table is identical to the current one;
394 // if so, synchronizing is pointless....
395 for (i = 0; i < 4; i++) {
396 if ((new_mbr_parts[i].mbr_type != 0xEE) && (mbr_parts[i].mbr_type != 0xEE) &&
397 ((new_mbr_parts[i].active != mbr_parts[i].active) ||
398 (new_mbr_parts[i].start_lba != mbr_parts[i].start_lba) ||
399 (new_mbr_parts[i].end_lba != mbr_parts[i].end_lba) ||
400 (new_mbr_parts[i].mbr_type != mbr_parts[i].mbr_type)))
401 all_identical = FALSE;
402
403 // while we're looping, count the number of old & new hybrid partitions....
404 if ((mbr_parts[i].mbr_type != 0x00) && (mbr_parts[i].mbr_type != 0xEE))
405 num_existing_hybrid++;
406 if ((new_mbr_parts[i].mbr_type != 0x00) && (new_mbr_parts[i].mbr_type != 0xEE))
407 num_new_hybrid++;
408 } // for
409
410 if (all_identical) {
411 Print(L"Tables are synchronized, no need to sync.\n");
412 return FALSE;
413 }
414
415 // If there's nothing to hybridize, but an existing hybrid MBR exists, offer to replace
416 // the hybrid MBR with a protective MBR.
417 if ((num_new_hybrid == 0) && (num_existing_hybrid > 0)) {
418 Print(L"Found no partitions that could be hybridized, but an existing hybrid MBR exists.\n");
419 Print(L"If you proceed, a fresh protective MBR will be created. Do you want to create\n");
420 invalid = input_boolean(STR("this new protective MBR, erasing the hybrid MBR? [y/N] "), &retval);
421 if (invalid)
422 retval = FALSE;
423 } // if
424
425 // If existing hybrid MBR that's NOT identical to the new one, ask the user
426 // before overwriting the old one.
427 if ((num_new_hybrid > 0) && (num_existing_hybrid > 0)) {
428 Print(L"Existing hybrid MBR detected, but it's not identical to what this program\n");
429 Print(L"would generate. Do you want to see the hybrid MBR that this program would\n");
430 invalid = input_boolean(STR("generate? [y/N] "), &retval);
431 if (invalid)
432 retval = FALSE;
433 } // if
434
435 return retval;
436 } // BOOLEAN should_rewrite()
437
438 static UINTN analyze(VOID)
439 {
440 UINTN i, detected_parttype;
441 CHARN *fsname;
442 UINTN status;
443
444 new_mbr_part_count = 0;
445
446 // determine correct MBR types for GPT partitions
447 if (gpt_part_count == 0) {
448 Print(L"Status: No GPT partitions defined, nothing to sync.\n");
449 return 0;
450 }
451 for (i = 0; i < gpt_part_count; i++) {
452 gpt_parts[i].mbr_type = gpt_parts[i].gpt_parttype->mbr_type;
453 if (gpt_parts[i].gpt_parttype->kind == GPT_KIND_BASIC_DATA) {
454 // Basic Data: need to look at data in the partition
455 status = detect_mbrtype_fs(gpt_parts[i].start_lba, &detected_parttype, &fsname);
456 if (status != 0)
457 Print(L"Warning: Error %d when detecting filesystem type!\n", status);
458 if (detected_parttype)
459 gpt_parts[i].mbr_type = detected_parttype;
460 else
461 gpt_parts[i].mbr_type = 0x0b; // fallback: FAT32
462 }
463 // NOTE: mbr_type may still be 0 if content detection fails for exotic GPT types or file systems
464 } // for
465
466 // generate the new table
467 generate_hybrid_mbr();
468 if (!should_rewrite())
469 return EFI_ABORTED;
470
471 // display table
472 Print(L"\nProposed new MBR partition table:\n");
473 Print(L" # A Start LBA End LBA Type\n");
474 for (i = 0; i < new_mbr_part_count; i++) {
475 Print(L" %d %s %12lld %12lld %02x %s\n",
476 new_mbr_parts[i].index + 1,
477 new_mbr_parts[i].active ? STR("*") : STR(" "),
478 new_mbr_parts[i].start_lba,
479 new_mbr_parts[i].end_lba,
480 new_mbr_parts[i].mbr_type,
481 mbr_parttype_name(new_mbr_parts[i].mbr_type));
482 }
483
484 return 0;
485 } // UINTN analyze()
486
487 //
488 // sync algorithm entry point
489 //
490
491 UINTN gptsync(VOID)
492 {
493 UINTN status = 0;
494 UINTN status_gpt, status_mbr;
495 BOOLEAN proceed = FALSE;
496
497 Print(L"gptsync version %s\ncopyright (c) 2006-2007 Christoph Pfisterer & 2013 Roderick W. Smith\n", VERSION);
498
499 // get full information from disk
500 status_gpt = read_gpt();
501 status_mbr = read_mbr();
502 if (status_gpt != 0 || status_mbr != 0)
503 return (status_gpt || status_mbr);
504
505 // cross-check current situation
506 Print(L"\n");
507 status = check_gpt(); // check GPT for consistency
508 if (status != 0)
509 return status;
510 status = check_mbr(); // check MBR for consistency
511 if (status != 0)
512 return status;
513 status = analyze(); // analyze the situation & compose new MBR table
514 if (status != 0)
515 return status;
516 if (new_mbr_part_count == 0)
517 return status;
518
519 // offer user the choice what to do
520 status = input_boolean(STR("\nMay I update the MBR as printed above? [y/N] "), &proceed);
521 if (status != 0 || proceed != TRUE)
522 return status;
523
524 // adjust the MBR and write it back
525 status = write_mbr();
526 if (status != 0)
527 return status;
528
529 return status;
530 }