]>
code.delx.au - refind/blob - gptsync/gptsync.c
3 * Platform-independent code for syncing GPT and MBR
5 * Copyright (c) 2006-2007 Christoph Pfisterer
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
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
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.
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.
36 /* Changes copyright (c) 2013 Roderick W. Smith */
40 #include "syslinux_mbr.h"
46 static UINTN
check_mbr(VOID
)
49 BOOLEAN found
= FALSE
;
52 for (i
= 0; i
< mbr_part_count
; i
++) {
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
;
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
;
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)) {
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
)) {
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
;
86 } // UINTN check_mbr()
88 static UINTN
write_mbr(VOID
)
95 BOOLEAN have_bootcode
;
97 Print(L
"\nWriting new MBR...\n");
100 status
= read_sector(0, sector
);
104 // write partition table
105 *((UINT16
*)(sector
+ 510)) = 0xaa55;
107 table
= (MBR_PART_INFO
*)(sector
+ 446);
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
)
114 if (k
>= new_mbr_part_count
) {
117 table
[i
].start_chs
[0] = 0;
118 table
[i
].start_chs
[1] = 0;
119 table
[i
].start_chs
[2] = 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;
127 if (new_mbr_parts
[k
].active
) {
128 table
[i
].flags
= active
;
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;
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);
145 table
[i
].start_lba
= (UINT32
)lba
;
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);
152 table
[i
].size
= (UINT32
)lba
;
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
;
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
);
171 status
= write_sector(0, sector
);
175 Print(L
"MBR updated successfully!\n");
184 static UINTN
check_gpt(VOID
)
188 if (gpt_part_count
== 0) {
189 Print(L
"Status: No GPT partition table, no need to sync.\n");
190 return EFI_UNSUPPORTED
;
194 for (i
= 0; i
< gpt_part_count
; i
++) {
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
;
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
;
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
;
217 } // VOID check_gpt()
220 // compare GPT and MBR tables
223 #define ACTION_NONE (0)
224 #define ACTION_NOP (1)
225 #define ACTION_REWRITE (2)
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()
236 // A simple bubble sort for the MBR partitions.
237 static VOID
sort_mbr(PARTITION_INFO
*parts
) {
238 PARTITION_INFO one_part
;
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)) {
248 parts
[d
] = parts
[d
+ 1];
249 parts
[d
+ 1] = one_part
;
251 parts
[d
+ 1].index
= d
+ 1;
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
;
263 new_mbr_part_count
= 1;
264 first_used_lba
= (UINT64
) MAX_MBR_LBA
+ (UINT64
) 1;
266 // Copy partitions in three passes....
267 // First, do FAT and NTFS partitions....
270 if ((gpt_parts
[i
].start_lba
> 0) && (gpt_parts
[i
].end_lba
> 0) &&
271 (gpt_parts
[i
].end_lba
<= MAX_MBR_LBA
) && /* Within MBR limits */
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
;
278 new_mbr_part_count
++;
281 } while (i
< gpt_part_count
&& new_mbr_part_count
<= 3);
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 before other hybridized
287 i
= gpt_part_count
- 1; // Note that gpt_part_count can't be 0; filtered by check_gpt()
288 while (i
< gpt_part_count
&& new_mbr_part_count
<= 3) { // if too few GPT partitions, i loops around to a huge value
289 if ((gpt_parts
[i
].start_lba
> 0) && (gpt_parts
[i
].end_lba
> 0) &&
290 (gpt_parts
[i
].end_lba
<= MAX_MBR_LBA
) &&
291 ((gpt_parts
[i
].gpt_parttype
->kind
== GPT_KIND_DATA
) || (gpt_parts
[i
].gpt_parttype
->kind
== GPT_KIND_BASIC_DATA
)) &&
292 (gpt_parts
[i
].mbr_type
== 0x83)) {
293 copy_gpt_to_new_mbr(i
, new_mbr_part_count
);
294 if (new_mbr_parts
[new_mbr_part_count
].start_lba
< first_used_lba
)
295 first_used_lba
= new_mbr_parts
[new_mbr_part_count
].start_lba
;
297 new_mbr_part_count
++;
302 // Third, do anything that's left to cover uncovered spaces; but this requires
303 // first creating the EFI protective entry, since we don't want to bother with
304 // anything already covered by this entry....
305 new_mbr_parts
[0].index
= 0;
306 new_mbr_parts
[0].start_lba
= 1;
307 new_mbr_parts
[0].end_lba
= (disk_size() > first_used_lba
) ? (first_used_lba
- 1) : disk_size() - 1;
308 if (new_mbr_parts
[0].end_lba
> MAX_MBR_LBA
)
309 new_mbr_parts
[0].end_lba
= MAX_MBR_LBA
;
310 new_mbr_parts
[0].mbr_type
= 0xEE;
312 while (i
< gpt_part_count
&& new_mbr_part_count
<= 3) {
313 if ((gpt_parts
[i
].start_lba
> new_mbr_parts
[0].end_lba
) && (gpt_parts
[i
].end_lba
> 0) &&
314 (gpt_parts
[i
].end_lba
<= MAX_MBR_LBA
) &&
315 (gpt_parts
[i
].gpt_parttype
->kind
!= GPT_KIND_BASIC_DATA
) &&
316 (gpt_parts
[i
].mbr_type
!= 0x83)) {
317 copy_gpt_to_new_mbr(i
, new_mbr_part_count
);
318 new_mbr_part_count
++;
323 // find matching partitions in the old MBR table, copy undetected details....
324 for (i
= 1; i
< new_mbr_part_count
; i
++) {
325 for (k
= 0; k
< mbr_part_count
; k
++) {
326 if (mbr_parts
[k
].start_lba
== new_mbr_parts
[i
].start_lba
) {
327 // keep type if not detected
328 if (new_mbr_parts
[i
].mbr_type
== 0)
329 new_mbr_parts
[i
].mbr_type
= mbr_parts
[k
].mbr_type
;
331 new_mbr_parts
[i
].active
= mbr_parts
[k
].active
;
335 if (new_mbr_parts
[i
].mbr_type
== 0) {
336 // final fallback: set to a (hopefully) unused type
337 new_mbr_parts
[i
].mbr_type
= 0xc0;
341 sort_mbr(new_mbr_parts
);
343 // make sure there's exactly one active partition
344 for (iter
= 0; iter
< 3; iter
++) {
347 for (i
= 0; i
< new_mbr_part_count
; i
++)
348 if (new_mbr_parts
[i
].active
)
350 if (count_active
== 1)
353 // set active on the first matching partition
354 if (count_active
== 0) {
355 for (i
= 0; i
< new_mbr_part_count
; i
++) {
356 if (((new_mbr_parts
[i
].mbr_type
== 0x07 || // NTFS
357 new_mbr_parts
[i
].mbr_type
== 0x0b || // FAT32
358 new_mbr_parts
[i
].mbr_type
== 0x0c)) || // FAT32 (LBA)
359 (iter
>= 1 && (new_mbr_parts
[i
].mbr_type
== 0x83)) || // Linux
360 (iter
>= 2 && i
> 0)) {
361 new_mbr_parts
[i
].active
= TRUE
;
365 } else if (count_active
> 1 && iter
== 0) {
366 // too many active partitions, try deactivating the ESP / EFI Protective entry
367 if ((new_mbr_parts
[0].mbr_type
== 0xee || new_mbr_parts
[0].mbr_type
== 0xef) &&
368 new_mbr_parts
[0].active
) {
369 new_mbr_parts
[0].active
= FALSE
;
371 } else if (count_active
> 1 && iter
> 0) {
372 // too many active partitions, deactivate all but the first one
374 for (i
= 0; i
< new_mbr_part_count
; i
++)
375 if (new_mbr_parts
[i
].active
) {
376 if (count_active
> 0)
377 new_mbr_parts
[i
].active
= FALSE
;
382 } // VOID generate_hybrid_mbr()
384 // Examine partitions and decide whether a rewrite is in order.
385 // Note that this function MAY ask user for advice.
386 // Note that this function assumes the new hybrid MBR has already
387 // been computed and stored in new_mbr_parts[].
388 static BOOLEAN
should_rewrite(VOID
) {
389 BOOLEAN retval
= TRUE
, all_identical
= TRUE
, invalid
;
390 UINTN i
, num_existing_hybrid
= 0, num_new_hybrid
= 0;
392 // Check to see if the proposed table is identical to the current one;
393 // if so, synchronizing is pointless....
394 for (i
= 0; i
< 4; i
++) {
395 if ((new_mbr_parts
[i
].mbr_type
!= 0xEE) && (mbr_parts
[i
].mbr_type
!= 0xEE) &&
396 ((new_mbr_parts
[i
].active
!= mbr_parts
[i
].active
) ||
397 (new_mbr_parts
[i
].start_lba
!= mbr_parts
[i
].start_lba
) ||
398 (new_mbr_parts
[i
].end_lba
!= mbr_parts
[i
].end_lba
) ||
399 (new_mbr_parts
[i
].mbr_type
!= mbr_parts
[i
].mbr_type
)))
400 all_identical
= FALSE
;
402 // while we're looping, count the number of old & new hybrid partitions....
403 if ((mbr_parts
[i
].mbr_type
!= 0x00) && (mbr_parts
[i
].mbr_type
!= 0xEE))
404 num_existing_hybrid
++;
405 if ((new_mbr_parts
[i
].mbr_type
!= 0x00) && (new_mbr_parts
[i
].mbr_type
!= 0xEE))
410 Print(L
"Tables are synchronized, no need to sync.\n");
414 // If there's nothing to hybridize, but an existing hybrid MBR exists, offer to replace
415 // the hybrid MBR with a protective MBR.
416 if ((num_new_hybrid
== 0) && (num_existing_hybrid
> 0)) {
417 Print(L
"Found no partitions that could be hybridized, but an existing hybrid MBR exists.\n");
418 Print(L
"If you proceed, a fresh protective MBR will be created. Do you want to create\n");
419 invalid
= input_boolean(STR("this new protective MBR, erasing the hybrid MBR? [y/N] "), &retval
);
424 // If existing hybrid MBR that's NOT identical to the new one, ask the user
425 // before overwriting the old one.
426 if ((num_new_hybrid
> 0) && (num_existing_hybrid
> 0)) {
427 Print(L
"Existing hybrid MBR detected, but it's not identical to what this program\n");
428 Print(L
"would generate. Do you want to see the hybrid MBR that this program would\n");
429 invalid
= input_boolean(STR("generate? [y/N] "), &retval
);
435 } // BOOLEAN should_rewrite()
437 static UINTN
analyze(VOID
)
439 UINTN i
, detected_parttype
;
443 new_mbr_part_count
= 0;
445 // determine correct MBR types for GPT partitions
446 if (gpt_part_count
== 0) {
447 Print(L
"Status: No GPT partitions defined, nothing to sync.\n");
450 for (i
= 0; i
< gpt_part_count
; i
++) {
451 gpt_parts
[i
].mbr_type
= gpt_parts
[i
].gpt_parttype
->mbr_type
;
452 if (gpt_parts
[i
].gpt_parttype
->kind
== GPT_KIND_BASIC_DATA
) {
453 // Basic Data: need to look at data in the partition
454 status
= detect_mbrtype_fs(gpt_parts
[i
].start_lba
, &detected_parttype
, &fsname
);
456 Print(L
"Warning: Error %d when detecting filesystem type!\n", status
);
457 if (detected_parttype
)
458 gpt_parts
[i
].mbr_type
= detected_parttype
;
460 gpt_parts
[i
].mbr_type
= 0x0b; // fallback: FAT32
462 // NOTE: mbr_type may still be 0 if content detection fails for exotic GPT types or file systems
465 // generate the new table
466 generate_hybrid_mbr();
467 if (!should_rewrite())
471 Print(L
"\nProposed new MBR partition table:\n");
472 Print(L
" # A Start LBA End LBA Type\n");
473 for (i
= 0; i
< new_mbr_part_count
; i
++) {
474 Print(L
" %d %s %12lld %12lld %02x %s\n",
475 new_mbr_parts
[i
].index
+ 1,
476 new_mbr_parts
[i
].active
? STR("*") : STR(" "),
477 new_mbr_parts
[i
].start_lba
,
478 new_mbr_parts
[i
].end_lba
,
479 new_mbr_parts
[i
].mbr_type
,
480 mbr_parttype_name(new_mbr_parts
[i
].mbr_type
));
487 // sync algorithm entry point
493 UINTN status_gpt
, status_mbr
;
494 BOOLEAN proceed
= FALSE
;
496 Print(L
"gptsync version %s\ncopyright (c) 2006-2007 Christoph Pfisterer & 2013 Roderick W. Smith\n", VERSION
);
498 // get full information from disk
499 status_gpt
= read_gpt();
500 status_mbr
= read_mbr();
501 if (status_gpt
!= 0 || status_mbr
!= 0)
502 return (status_gpt
|| status_mbr
);
504 // cross-check current situation
506 status
= check_gpt(); // check GPT for consistency
509 status
= check_mbr(); // check MBR for consistency
512 status
= analyze(); // analyze the situation & compose new MBR table
515 if (new_mbr_part_count
== 0)
518 // offer user the choice what to do
519 status
= input_boolean(STR("\nMay I update the MBR as printed above? [y/N] "), &proceed
);
520 if (status
!= 0 || proceed
!= TRUE
)
523 // adjust the MBR and write it back
524 status
= write_mbr();