]> code.delx.au - refind/blob - gptsync/gptsync.c
2ba441845df9fd195c403ad2f24dcbafe1ccc8c9
[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 > 0xffffffffULL) {
142 Print(L"Warning: Partition %d starts beyond 2 TiB limit\n", i+1);
143 lba = 0xffffffffULL;
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 > 0xffffffffULL) {
149 Print(L"Warning: Partition %d extends beyond 2 TiB limit\n", i+1);
150 lba = 0xffffffffULL;
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 }
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 i = 0;
264 new_mbr_part_count = 1;
265 first_used_lba = 0xFFFFFFFF;
266
267 // Copy partitions in three passes....
268 // First, do FAT and NTFS partitions....
269 do {
270 if ((gpt_parts[i].start_lba > 0) && (gpt_parts[i].end_lba > 0) &&
271 (gpt_parts[i].gpt_parttype->kind == GPT_KIND_BASIC_DATA) && /* MS Basic Data GPT type code */
272 (gpt_parts[i].mbr_type != 0x83)) { /* Not containing Linux filesystem */
273 copy_gpt_to_new_mbr(i, new_mbr_part_count);
274 if (new_mbr_parts[new_mbr_part_count].start_lba < first_used_lba)
275 first_used_lba = new_mbr_parts[new_mbr_part_count].start_lba;
276
277 new_mbr_part_count++;
278 }
279 i++;
280 } while (i < gpt_part_count && new_mbr_part_count <= 3);
281
282 // Second, do Linux partitions....
283 i = 0;
284 while (i < gpt_part_count && new_mbr_part_count <= 3) {
285 if ((gpt_parts[i].start_lba > 0) && (gpt_parts[i].end_lba > 0) &&
286 ((gpt_parts[i].gpt_parttype->kind == GPT_KIND_DATA) || (gpt_parts[i].gpt_parttype->kind == GPT_KIND_BASIC_DATA)) &&
287 (gpt_parts[i].mbr_type == 0x83)) {
288 copy_gpt_to_new_mbr(i, new_mbr_part_count);
289 if (new_mbr_parts[new_mbr_part_count].start_lba < first_used_lba)
290 first_used_lba = new_mbr_parts[new_mbr_part_count].start_lba;
291
292 new_mbr_part_count++;
293 }
294 i++;
295 } // while
296
297 // Third, do anything that's left to cover uncovered spaces; but this requires
298 // first creating the EFI protective entry, since we don't want to bother with
299 // anything already covered by this entry....
300 new_mbr_parts[0].index = 0;
301 new_mbr_parts[0].start_lba = 1;
302 new_mbr_parts[0].end_lba = (disk_size() > first_used_lba) ? (first_used_lba - 1) : disk_size() - 1;
303 new_mbr_parts[0].mbr_type = 0xee;
304 i = 0;
305 while (i < gpt_part_count && new_mbr_part_count <= 3) {
306 if ((gpt_parts[i].start_lba > new_mbr_parts[0].end_lba) && (gpt_parts[i].end_lba > 0) &&
307 (gpt_parts[i].gpt_parttype->kind != GPT_KIND_BASIC_DATA) &&
308 (gpt_parts[i].mbr_type != 0x83)) {
309 copy_gpt_to_new_mbr(i, new_mbr_part_count);
310 new_mbr_part_count++;
311 }
312 i++;
313 } // while
314
315 // find matching partitions in the old MBR table, copy undetected details....
316 for (i = 1; i < new_mbr_part_count; i++) {
317 for (k = 0; k < mbr_part_count; k++) {
318 if (mbr_parts[k].start_lba == new_mbr_parts[i].start_lba) {
319 // keep type if not detected
320 if (new_mbr_parts[i].mbr_type == 0)
321 new_mbr_parts[i].mbr_type = mbr_parts[k].mbr_type;
322 // keep active flag
323 new_mbr_parts[i].active = mbr_parts[k].active;
324 break;
325 } // if
326 } // for (k...)
327 if (new_mbr_parts[i].mbr_type == 0) {
328 // final fallback: set to a (hopefully) unused type
329 new_mbr_parts[i].mbr_type = 0xc0;
330 } // if
331 } // for (i...)
332
333 sort_mbr(new_mbr_parts);
334
335 // make sure there's exactly one active partition
336 for (iter = 0; iter < 3; iter++) {
337 // check
338 count_active = 0;
339 for (i = 0; i < new_mbr_part_count; i++)
340 if (new_mbr_parts[i].active)
341 count_active++;
342 if (count_active == 1)
343 break;
344
345 // set active on the first matching partition
346 if (count_active == 0) {
347 for (i = 0; i < new_mbr_part_count; i++) {
348 if ((iter >= 0 && (new_mbr_parts[i].mbr_type == 0x07 || // NTFS
349 new_mbr_parts[i].mbr_type == 0x0b || // FAT32
350 new_mbr_parts[i].mbr_type == 0x0c)) || // FAT32 (LBA)
351 (iter >= 1 && (new_mbr_parts[i].mbr_type == 0x83)) || // Linux
352 (iter >= 2 && i > 0)) {
353 new_mbr_parts[i].active = TRUE;
354 break;
355 }
356 }
357 } else if (count_active > 1 && iter == 0) {
358 // too many active partitions, try deactivating the ESP / EFI Protective entry
359 if ((new_mbr_parts[0].mbr_type == 0xee || new_mbr_parts[0].mbr_type == 0xef) &&
360 new_mbr_parts[0].active) {
361 new_mbr_parts[0].active = FALSE;
362 }
363 } else if (count_active > 1 && iter > 0) {
364 // too many active partitions, deactivate all but the first one
365 count_active = 0;
366 for (i = 0; i < new_mbr_part_count; i++)
367 if (new_mbr_parts[i].active) {
368 if (count_active > 0)
369 new_mbr_parts[i].active = FALSE;
370 count_active++;
371 }
372 }
373 }
374 } // VOID generate_hybrid_mbr()
375
376 // Examine partitions and decide whether a rewrite is in order.
377 // Note that this function MAY ask user for advice.
378 // Note that this function assumes the new hybrid MBR has already
379 // been computed and stored in new_mbr_parts[].
380 static BOOLEAN should_rewrite(VOID) {
381 BOOLEAN retval = TRUE, all_identical = TRUE, invalid;
382 UINTN i, num_existing_hybrid = 0, num_new_hybrid = 0;
383
384 // Check to see if the proposed table is identical to the current one;
385 // if so, synchronizing is pointless....
386 for (i = 0; i < 4; i++) {
387 if ((new_mbr_parts[i].mbr_type != 0xEE) && (mbr_parts[i].mbr_type != 0xEE) &&
388 ((new_mbr_parts[i].active != mbr_parts[i].active) ||
389 (new_mbr_parts[i].start_lba != mbr_parts[i].start_lba) ||
390 (new_mbr_parts[i].end_lba != mbr_parts[i].end_lba) ||
391 (new_mbr_parts[i].mbr_type != mbr_parts[i].mbr_type)))
392 all_identical = FALSE;
393
394 // while we're looping, count the number of old & new hybrid partitions....
395 if ((mbr_parts[i].mbr_type != 0x00) && (mbr_parts[i].mbr_type != 0xEE))
396 num_existing_hybrid++;
397 if ((new_mbr_parts[i].mbr_type != 0x00) && (new_mbr_parts[i].mbr_type != 0xEE))
398 num_new_hybrid++;
399 } // for
400
401 if (all_identical) {
402 Print(L"Tables are synchronized, no need to sync.\n");
403 return FALSE;
404 }
405
406 // If there's nothing to hybridize, but an existing hybrid MBR exists, offer to replace
407 // the hybrid MBR with a protective MBR.
408 if ((num_new_hybrid == 0) && (num_existing_hybrid > 0)) {
409 Print(L"Found no partitions that could be hybridized, but an existing hybrid MBR exists.\n");
410 Print(L"If you proceed, a fresh protective MBR will be created. Do you want to create\n");
411 invalid = input_boolean(STR("this new protective MBR, erasing the hybrid MBR? [y/N] "), &retval);
412 if (invalid)
413 retval = FALSE;
414 } // if
415
416 // If existing hybrid MBR that's NOT identical to the new one, ask the user
417 // before overwriting the old one.
418 if ((num_new_hybrid > 0) && (num_existing_hybrid > 0)) {
419 Print(L"Existing hybrid MBR detected, but it's not identical to what this program\n");
420 Print(L"would generate. Do you want to see the hybrid MBR that this program would\n");
421 invalid = input_boolean(STR("generate? [y/N] "), &retval);
422 if (invalid)
423 retval = FALSE;
424 } // if
425
426 return retval;
427 } // BOOLEAN should_rewrite()
428
429 static UINTN analyze(VOID)
430 {
431 UINTN i, detected_parttype;
432 CHARN *fsname;
433 UINTN status;
434
435 new_mbr_part_count = 0;
436
437 // determine correct MBR types for GPT partitions
438 if (gpt_part_count == 0) {
439 Print(L"Status: No GPT partitions defined, nothing to sync.\n");
440 return 0;
441 }
442 for (i = 0; i < gpt_part_count; i++) {
443 gpt_parts[i].mbr_type = gpt_parts[i].gpt_parttype->mbr_type;
444 if (gpt_parts[i].gpt_parttype->kind == GPT_KIND_BASIC_DATA) {
445 // Basic Data: need to look at data in the partition
446 status = detect_mbrtype_fs(gpt_parts[i].start_lba, &detected_parttype, &fsname);
447 if (status != 0)
448 Print(L"Warning: Error %d when detecting filesystem type!\n", status);
449 if (detected_parttype)
450 gpt_parts[i].mbr_type = detected_parttype;
451 else
452 gpt_parts[i].mbr_type = 0x0b; // fallback: FAT32
453 }
454 // NOTE: mbr_type may still be 0 if content detection fails for exotic GPT types or file systems
455 } // for
456
457 // generate the new table
458 generate_hybrid_mbr();
459 if (!should_rewrite())
460 return EFI_ABORTED;
461
462 // display table
463 Print(L"\nProposed new MBR partition table:\n");
464 Print(L" # A Start LBA End LBA Type\n");
465 for (i = 0; i < new_mbr_part_count; i++) {
466 Print(L" %d %s %12lld %12lld %02x %s\n",
467 new_mbr_parts[i].index + 1,
468 new_mbr_parts[i].active ? STR("*") : STR(" "),
469 new_mbr_parts[i].start_lba,
470 new_mbr_parts[i].end_lba,
471 new_mbr_parts[i].mbr_type,
472 mbr_parttype_name(new_mbr_parts[i].mbr_type));
473 }
474
475 return 0;
476 } // UINTN analyze()
477
478 //
479 // sync algorithm entry point
480 //
481
482 UINTN gptsync(VOID)
483 {
484 UINTN status = 0;
485 UINTN status_gpt, status_mbr;
486 BOOLEAN proceed = FALSE;
487
488 Print(L"gptsync version %s\ncopyright (c) 2006-2007 Christoph Pfisterer & 2013 Roderick W. Smith\n", VERSION);
489
490 // get full information from disk
491 status_gpt = read_gpt();
492 status_mbr = read_mbr();
493 if (status_gpt != 0 || status_mbr != 0)
494 return (status_gpt || status_mbr);
495
496 // cross-check current situation
497 Print(L"\n");
498 status = check_gpt(); // check GPT for consistency
499 if (status != 0)
500 return status;
501 status = check_mbr(); // check MBR for consistency
502 if (status != 0)
503 return status;
504 status = analyze(); // analyze the situation & compose new MBR table
505 if (status != 0)
506 return status;
507 if (new_mbr_part_count == 0)
508 return status;
509
510 // offer user the choice what to do
511 status = input_boolean(STR("\nMay I update the MBR as printed above? [y/N] "), &proceed);
512 if (status != 0 || proceed != TRUE)
513 return status;
514
515 // adjust the MBR and write it back
516 status = write_mbr();
517 if (status != 0)
518 return status;
519
520 return status;
521 }