]>
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"
41 #define memcpy(a, b, c) CopyMem(a, b, c)
47 static UINTN
check_mbr(VOID
)
50 BOOLEAN found
= FALSE
;
53 for (i
= 0; i
< mbr_part_count
; i
++) {
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
;
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
;
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)) {
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
)) {
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
;
87 } // UINTN check_mbr()
89 static UINTN
write_mbr(VOID
)
96 BOOLEAN have_bootcode
;
98 Print(L
"\nWriting new MBR...\n");
101 status
= read_sector(0, sector
);
105 // write partition table
106 *((UINT16
*)(sector
+ 510)) = 0xaa55;
108 table
= (MBR_PART_INFO
*)(sector
+ 446);
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
)
115 if (k
>= new_mbr_part_count
) {
118 table
[i
].start_chs
[0] = 0;
119 table
[i
].start_chs
[1] = 0;
120 table
[i
].start_chs
[2] = 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;
128 if (new_mbr_parts
[k
].active
) {
129 table
[i
].flags
= active
;
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;
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);
146 table
[i
].start_lba
= (UINT32
)lba
;
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);
153 table
[i
].size
= (UINT32
)lba
;
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
;
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
);
172 status
= write_sector(0, sector
);
176 Print(L
"MBR updated successfully!\n");
185 static UINTN
check_gpt(VOID
)
189 if (gpt_part_count
== 0) {
190 Print(L
"Status: No GPT partition table, no need to sync.\n");
191 return EFI_UNSUPPORTED
;
195 for (i
= 0; i
< gpt_part_count
; i
++) {
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
;
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
;
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
;
218 } // VOID check_gpt()
221 // compare GPT and MBR tables
224 #define ACTION_NONE (0)
225 #define ACTION_NOP (1)
226 #define ACTION_REWRITE (2)
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()
237 // A simple bubble sort for the MBR partitions.
238 static VOID
sort_mbr(PARTITION_INFO
*parts
) {
239 PARTITION_INFO one_part
;
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)) {
249 parts
[d
] = parts
[d
+ 1];
250 parts
[d
+ 1] = one_part
;
252 parts
[d
+ 1].index
= d
+ 1;
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
;
264 new_mbr_part_count
= 1;
265 first_used_lba
= (UINT64
) MAX_MBR_LBA
+ (UINT64
) 1;
267 // Copy partitions in three passes....
268 // First, do FAT and NTFS partitions....
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
;
279 new_mbr_part_count
++;
282 } while (i
< gpt_part_count
&& new_mbr_part_count
<= 3);
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
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
;
298 new_mbr_part_count
++;
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;
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
++;
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
;
332 new_mbr_parts
[i
].active
= mbr_parts
[k
].active
;
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;
342 sort_mbr(new_mbr_parts
);
344 // make sure there's exactly one active partition
345 for (iter
= 0; iter
< 3; iter
++) {
348 for (i
= 0; i
< new_mbr_part_count
; i
++)
349 if (new_mbr_parts
[i
].active
)
351 if (count_active
== 1)
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
;
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
;
372 } else if (count_active
> 1 && iter
> 0) {
373 // too many active partitions, deactivate all but the first one
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
;
383 } // VOID generate_hybrid_mbr()
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;
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
;
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))
411 Print(L
"Tables are synchronized, no need to sync.\n");
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
);
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
);
436 } // BOOLEAN should_rewrite()
438 static UINTN
analyze(VOID
)
440 UINTN i
, detected_parttype
;
444 new_mbr_part_count
= 0;
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");
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
);
457 Print(L
"Warning: Error %d when detecting filesystem type!\n", status
);
458 if (detected_parttype
)
459 gpt_parts
[i
].mbr_type
= detected_parttype
;
461 gpt_parts
[i
].mbr_type
= 0x0b; // fallback: FAT32
463 // NOTE: mbr_type may still be 0 if content detection fails for exotic GPT types or file systems
466 // generate the new table
467 generate_hybrid_mbr();
468 if (!should_rewrite())
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
));
488 // sync algorithm entry point
494 UINTN status_gpt
, status_mbr
;
495 BOOLEAN proceed
= FALSE
;
497 Print(L
"gptsync version %s\ncopyright (c) 2006-2007 Christoph Pfisterer & 2013 Roderick W. Smith\n", REFIND_VERSION
);
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
);
505 // cross-check current situation
507 status
= check_gpt(); // check GPT for consistency
510 status
= check_mbr(); // check MBR for consistency
513 status
= analyze(); // analyze the situation & compose new MBR table
516 if (new_mbr_part_count
== 0)
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
)
524 // adjust the MBR and write it back
525 status
= write_mbr();