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