diff options
Diffstat (limited to 'debian/patches/gptsync.patch')
-rw-r--r-- | debian/patches/gptsync.patch | 466 |
1 files changed, 466 insertions, 0 deletions
diff --git a/debian/patches/gptsync.patch b/debian/patches/gptsync.patch new file mode 100644 index 0000000..27820fe --- /dev/null +++ b/debian/patches/gptsync.patch @@ -0,0 +1,466 @@ +From aa55572c980da038e675fc963d98a90964a2ce33 Mon Sep 17 00:00:00 2001 +From: Matthew Garrett <mjg59@srcf.ucam.org> +Date: Fri, 28 Mar 2014 17:10:01 +0000 +Subject: GPT syncing for Intel Macs + +On Intel Mac systems, write a synced MBR rather than a protective MBR. + +From: Colin Watson <cjwatson@ubuntu.com> +Forwarded: no +Last-Update: 2022-04-19 + +Patch-Name: gptsync.patch +--- + libparted/labels/gpt.c | 205 +++++++++++++++++++++++++++++++++++++++-- + tests/Makefile.am | 1 + + tests/t0290-gptsync.sh | 175 +++++++++++++++++++++++++++++++++++ + 3 files changed, 371 insertions(+), 10 deletions(-) + create mode 100644 tests/t0290-gptsync.sh + +diff --git a/libparted/labels/gpt.c b/libparted/labels/gpt.c +index 130b9bd1..bebedb47 100644 +--- a/libparted/labels/gpt.c ++++ b/libparted/labels/gpt.c +@@ -369,6 +369,40 @@ typedef struct _GPTPartitionData + + static PedDiskType gpt_disk_type; + ++ ++#if (defined(__i386__) || defined(__x86_64__)) && defined(__linux__) ++# define USE_DMI ++#endif ++ ++#define APPLE_DMI "Apple Computer, Inc." ++#define APPLE_DMI_2 "Apple Inc." ++static int is_apple = 0; ++ ++static char * ++dmi_system_manufacturer (void) ++{ ++#ifdef USE_DMI ++ FILE *dmidecode; ++ char *manufacturer = NULL; ++ size_t manufacturer_len = 0; ++ ++ dmidecode = popen ("dmidecode -s system-manufacturer 2>/dev/null", "r"); ++ if (getline (&manufacturer, &manufacturer_len, dmidecode) < 0) { ++ /* ignore; will return NULL */ ++ } ++ pclose (dmidecode); ++ if (manufacturer) { ++ char *newline = strchr (manufacturer, '\n'); ++ if (newline) ++ *newline = '\0'; ++ } ++ return manufacturer; ++#else /* !USE_DMI */ ++ return NULL; ++#endif /* USE_DMI */ ++} ++ ++ + static inline uint32_t + pth_get_size (const PedDevice *dev) + { +@@ -544,16 +578,19 @@ gpt_probe (const PedDevice *dev) + if (dev->length <= 1) + return 0; + +- void *label; +- if (!ptt_read_sector (dev, 0, &label)) +- return 0; +- +- if (!_pmbr_is_valid (label)) ++ if (!is_apple) + { ++ void *label; ++ if (!ptt_read_sector (dev, 0, &label)) ++ return 0; ++ ++ if (!_pmbr_is_valid (label)) ++ { ++ free (label); ++ return 0; ++ } + free (label); +- return 0; + } +- free (label); + + void *pth_raw = ped_malloc (pth_get_size (dev)); + if (ped_device_read (dev, pth_raw, 1, GPT_HEADER_SECTORS) +@@ -973,6 +1010,10 @@ gpt_read_headers (PedDisk const *disk, + * warn if it's not there, and treat the disk as MSDOS, with a note + * for users to use Parted to "fix up" their disk if they + * really want it to be considered GPT. ++ * ++ * Of course, this is incompatible with how Apple handle things. For ++ * legacy BIOS compatibility on Apple machines, we need a valid legacy MBR ++ * rather than a protective one. Aren't standards wonderful? + ************************************************************/ + static int + gpt_read (PedDisk *disk) +@@ -1181,6 +1222,129 @@ _write_pmbr (PedDevice *dev, bool pmbr_boot) + return write_ok; + } + ++static void ++fill_raw_part (PartitionRecord_t* raw_part, PedPartition *part, PedSector offset, int number) ++{ ++ GPTPartitionData* gpt_part_data = part->disk_specific; ++ ++ if (part->fs_type) { ++ if (strncmp (part->fs_type->name, "fat", 3) == 0) ++ raw_part->OSType = 0x0b; ++ else if (strncmp (part->fs_type->name, "ntfs", 4) == 0) ++ raw_part->OSType = 0x07; ++ else if (strncmp (part->fs_type->name, "hfs", 3) == 0) ++ raw_part->OSType = 0xaf; ++ else if (strncmp (part->fs_type->name, "linux-swap", 10) == 0) ++ raw_part->OSType = 0x82; ++ else ++ raw_part->OSType = 0x83; ++ } else ++ raw_part->OSType = 0xda; ++ ++ /* Apple's firmware appears to become unhappy if the second partition ++ isn't bootable */ ++ ++ if (number == 2) ++ raw_part->BootIndicator = 0x80; ++ ++ raw_part->StartingLBA = PED_CPU_TO_LE32 ((part->geom.start - offset) ++ / (part->disk->dev->sector_size / 512)); ++ ++ raw_part->SizeInLBA = PED_CPU_TO_LE32 (part->geom.length ++ / (part->disk->dev->sector_size / 512)); ++ ++ /* EFI system partitions will have a FAT filesystem and ++ PARTITION_SYSTEM_GUID; however, it is not wise to rely on filesystem ++ probing */ ++ ++ if (number == 1) { ++ if (!guid_cmp (gpt_part_data->type, PARTITION_SYSTEM_GUID) || ++ !guid_cmp (gpt_part_data->type, PARTITION_BIOS_GRUB_GUID)) { ++ raw_part->OSType = EFI_PMBR_OSTYPE_EFI; ++ raw_part->OSType = EFI_PMBR_OSTYPE_EFI; ++ } ++ } ++ ++ /* Apple's firmware also appears to be unhappy if the EFI system ++ partition doesn't extend all the way to the start of the disk */ ++ ++ if (number == 1 && raw_part->OSType == EFI_PMBR_OSTYPE_EFI) { ++ raw_part->StartSector = 1; ++ raw_part->SizeInLBA += raw_part->StartingLBA - 1; ++ raw_part->StartingLBA = 1; ++ } else { ++ raw_part->StartHead = 0xfe; ++ raw_part->StartSector = 0xff; ++ raw_part->StartTrack = 0xff; ++ } ++ ++ raw_part->EndHead = 0xfe; ++ raw_part->EndSector = 0xff; ++ raw_part->EndTrack = 0xff; ++} ++ ++static int ++_gptsync (const PedDisk *disk) ++{ ++ void *s0; ++ PedPartition* part; ++ int i; ++ ++ if (!ptt_read_sector (disk->dev, GPT_PMBR_LBA, &s0)) ++ return 0; ++ LegacyMBR_t *pmbr = s0; ++ ++ int ok = 0; ++ ++ memset(&pmbr->PartitionRecord, 0, sizeof(pmbr->PartitionRecord)); ++ pmbr->Signature = PED_CPU_TO_LE16(MSDOS_MBR_SIGNATURE); ++ ++ bool prot = false; /* have we found a protective partition? */ ++ for (i=1; i<=4; i++) { ++ part = ped_disk_get_partition (disk, i); ++ if (!part) ++ continue; ++ ++ fill_raw_part (&pmbr->PartitionRecord [i - 1], part, 0, i); ++ if (pmbr->PartitionRecord[i - 1].OSType == EFI_PMBR_OSTYPE_EFI) ++ prot = true; ++ } ++ ++ if (!prot) { /* create one covering the gpt entries */ ++ uint32_t prot_size; ++ for (i=2; i>=0; i--) ++ pmbr->PartitionRecord[i + 1] = pmbr->PartitionRecord[i]; ++ memset (&pmbr->PartitionRecord[0], 0, sizeof pmbr->PartitionRecord[0]); ++ pmbr->PartitionRecord[0].OSType = EFI_PMBR_OSTYPE_EFI; ++ pmbr->PartitionRecord[0].StartSector = 1; ++ pmbr->PartitionRecord[0].EndHead = 0xfe; ++ pmbr->PartitionRecord[0].EndSector = 0xff; ++ pmbr->PartitionRecord[0].EndTrack = 0xff; ++ pmbr->PartitionRecord[0].StartingLBA = PED_CPU_TO_LE32 (1); ++ if ((disk->dev->length - 1ULL) > 0xFFFFFFFFULL) ++ prot_size = 0xFFFFFFFF; ++ else ++ prot_size = disk->dev->length - 1UL; ++ for (i=1; i<=3; i++) { ++ if (pmbr->PartitionRecord[i].StartingLBA) { ++ uint32_t starting_lba = ++ PED_LE32_TO_CPU (pmbr->PartitionRecord[i].StartingLBA); ++ if (starting_lba - 1 < prot_size) ++ prot_size = starting_lba - 1; ++ } ++ } ++ pmbr->PartitionRecord[0].SizeInLBA = PED_CPU_TO_LE32 (prot_size); ++ } ++ ++ if (!ped_device_write (disk->dev, pmbr, GPT_PMBR_LBA, GPT_PMBR_SECTORS)) ++ goto error; ++ ++ ok = ped_device_sync (disk->dev); ++error: ++ free (s0); ++ return ok; ++} ++ + static int + _generate_header (const PedDisk *disk, int alternate, uint32_t ptes_crc, + GuidPartitionTableHeader_t **gpt_p) +@@ -1287,9 +1451,15 @@ gpt_write (const PedDisk *disk) + + ptes_crc = efi_crc32 (ptes, ptes_bytes); + +- /* Write protective MBR */ +- if (!_write_pmbr (disk->dev, gpt_disk_data->pmbr_boot)) +- goto error_free_ptes; ++ if (is_apple) { ++ /* Write synced MBR */ ++ if (!_gptsync (disk)) ++ goto error_free_ptes; ++ } else { ++ /* Write protective MBR */ ++ if (!_write_pmbr (disk->dev, gpt_disk_data->pmbr_boot)) ++ goto error_free_ptes; ++ } + + /* Write PTH and PTEs */ + /* FIXME: Caution: this code is nearly identical to what's just below. */ +@@ -1938,6 +2108,21 @@ void + ped_disk_gpt_init () + { + ped_disk_type_register (&gpt_disk_type); ++ ++ char *force_gpt_apple = getenv ("PARTED_GPT_APPLE"); ++ if (force_gpt_apple) { ++ if (strcmp (force_gpt_apple, "1") == 0) ++ is_apple = 1; ++ } else { ++ char *manufacturer = dmi_system_manufacturer (); ++ if (manufacturer && ++ (strncasecmp (APPLE_DMI, manufacturer, ++ strlen (APPLE_DMI)) == 0 || ++ strncasecmp (APPLE_DMI_2, manufacturer, ++ strlen (APPLE_DMI_2)) == 0)) ++ is_apple = 1; ++ free (manufacturer); ++ } + } + + void +diff --git a/tests/Makefile.am b/tests/Makefile.am +index da093119..3c1f929a 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -32,6 +32,7 @@ TESTS = \ + t0282-gpt-move-backup.sh \ + t0283-overlap-partitions.sh \ + t0290-gpt-name.sh \ ++ t0290-gptsync.sh \ + t0300-dos-on-gpt.sh \ + t0301-overwrite-gpt-pmbr.sh \ + t0350-mac-PT-increases-sector-size.sh \ +diff --git a/tests/t0290-gptsync.sh b/tests/t0290-gptsync.sh +new file mode 100644 +index 00000000..367d61bd +--- /dev/null ++++ b/tests/t0290-gptsync.sh +@@ -0,0 +1,175 @@ ++#!/bin/sh ++# test GPT -> hybrid MBR syncing for Apple systems ++# http://www.rodsbooks.com/gdisk/hybrid.html ++ ++# Copyright (C) 2012 Canonical Ltd. ++ ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; either version 3 of the License, or ++# (at your option) any later version. ++ ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++ ++# You should have received a copy of the GNU General Public License ++# along with this program. If not, see <http://www.gnu.org/licenses/>. ++ ++if test "$VERBOSE" = yes; then ++ set -x ++ parted --version ++fi ++ ++: ${srcdir=.} ++. $srcdir/t-lib.sh ++ ++require_root_ ++require_scsi_debug_module_ ++ ++ss=$sector_size_ ++# must be big enough for a 32MiB partition in order to be big enough for FAT16 ++n_sectors=131072 ++bootcode_size=446 ++mbr_table_size=$((512 - $bootcode_size)) ++ ++dump_mbr_table () { ++ dd if=$dev bs=1 skip=$bootcode_size count=$mbr_table_size 2>/dev/null | od -v -An -tx1 ++} ++ ++# create memory-backed device ++sectors_per_MiB=$((1024 * 1024 / $ss)) ++n_MiB=$((($n_sectors + $sectors_per_MiB - 1) / $sectors_per_MiB)) ++scsi_debug_setup_ dev_size_mb=$n_MiB > dev-name || ++ skip_test_ 'failed to create scsi_debug device' ++dev=$(cat dev-name) ++ ++# force Apple mode ++export PARTED_GPT_APPLE=1 ++ ++# create gpt label ++parted -s $dev mklabel gpt > empty 2>&1 || fail=1 ++compare /dev/null empty || fail=1 ++ ++# print the empty table ++parted -m -s $dev unit s print > t 2>&1 || fail=1 ++sed "s,.*/$dev:,$dev:," t > out || fail=1 ++ ++# check for expected output ++printf "BYT;\n$dev:${n_sectors}s:scsi:$sector_size_:$sector_size_:gpt:Linux scsi_debug;\n" \ ++ > exp || fail=1 ++compare exp out || fail=1 ++ ++# the empty table should have a MBR containing only a protective entry ++dump_mbr_table > out || fail=1 ++cat <<EOF > exp || fail=1 ++ 00 00 01 00 ee fe ff ff 01 00 00 00 ff ff 01 00 ++ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ++ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ++ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ++ 55 aa ++EOF ++compare exp out || fail=1 ++ ++# create a 32MiB FAT16 EFI System Partition ++parted -s $dev mkpart p1 fat16 2048s 67583s set 1 boot on > empty 2>&1 || fail=1 ++compare /dev/null empty || fail=1 ++mkfs.vfat -F 16 ${dev}1 >/dev/null || skip_ "mkfs.vfat failed" ++ ++# this is represented as a protective partition, but now it only extends as ++# far as the end of the first partition rather than covering the whole disk ++# (matching refit gptsync's strategy); it still starts at sector one ++dump_mbr_table > out || fail=1 ++cat <<EOF > exp || fail=1 ++ 00 00 01 00 ee fe ff ff 01 00 00 00 ff 07 01 00 ++ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ++ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ++ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ++ 55 aa ++EOF ++compare exp out || fail=1 ++ ++# add a 2MiB ext3 partition ++parted -s $dev mkpart p2 ext3 67584s 71679s > empty 2>&1 || fail=1 ++compare /dev/null empty || fail=1 ++mkfs.ext3 ${dev}2 >/dev/null 2>&1 || skip_ "mkfs.ext3 failed" ++ ++# this should have an MBR representing both partitions; the second partition ++# should be marked bootable ++dump_mbr_table > out || fail=1 ++cat <<EOF > exp || fail=1 ++ 00 00 01 00 ee fe ff ff 01 00 00 00 ff 07 01 00 ++ 80 fe ff ff 83 fe ff ff 00 08 01 00 00 10 00 00 ++ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ++ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ++ 55 aa ++EOF ++compare exp out || fail=1 ++ ++# add a 1MiB partition with no filesystem ++parted -s $dev mkpart p3 71680s 73727s > empty 2>&1 || fail=1 ++compare /dev/null empty || fail=1 ++ ++# the new partition should be represented as 0xda (Non-FS data) ++dump_mbr_table > out || fail=1 ++cat <<EOF > exp || fail=1 ++ 00 00 01 00 ee fe ff ff 01 00 00 00 ff 07 01 00 ++ 80 fe ff ff 83 fe ff ff 00 08 01 00 00 10 00 00 ++ 00 fe ff ff da fe ff ff 00 18 01 00 00 08 00 00 ++ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ++ 55 aa ++EOF ++compare exp out || fail=1 ++ ++# add two more 1MiB partitions ++parted -s $dev mkpart p4 73728s 75775s > empty 2>&1 || fail=1 ++parted -s $dev mkpart p5 75776s 77823s > empty 2>&1 || fail=1 ++ ++# only the first four partitions will be represented ++dump_mbr_table > out || fail=1 ++cat <<EOF > exp || fail=1 ++ 00 00 01 00 ee fe ff ff 01 00 00 00 ff 07 01 00 ++ 80 fe ff ff 83 fe ff ff 00 08 01 00 00 10 00 00 ++ 00 fe ff ff da fe ff ff 00 18 01 00 00 08 00 00 ++ 00 fe ff ff da fe ff ff 00 20 01 00 00 08 00 00 ++ 55 aa ++EOF ++compare exp out || fail=1 ++ ++# convert first partition to a BIOS Boot Partition ++parted -s $dev set 1 boot off set 1 bios_grub on > empty 2>&1 || fail=1 ++compare /dev/null empty || fail=1 ++ ++# this should be represented in the same way as an EFI System Partition ++dump_mbr_table > out || fail=1 ++compare exp out || fail=1 ++ ++# convert first partition to an ordinary FAT partition ++parted -s $dev set 1 bios_grub off > empty 2>&1 || fail=1 ++compare /dev/null empty || fail=1 ++ ++# this should result in a protective partition covering the GPT data up to ++# the start of the first partition, and then representations of the first ++# three partitions ++dump_mbr_table > out || fail=1 ++cat <<EOF > exp || fail=1 ++ 00 00 01 00 ee fe ff ff 01 00 00 00 ff 07 00 00 ++ 00 fe ff ff 0b fe ff ff 00 08 00 00 00 00 01 00 ++ 80 fe ff ff 83 fe ff ff 00 08 01 00 00 10 00 00 ++ 00 fe ff ff da fe ff ff 00 18 01 00 00 08 00 00 ++ 55 aa ++EOF ++compare exp out || fail=1 ++ ++# convert third partition to a BIOS Boot Partition ++parted -s $dev set 3 bios_grub on > empty 2>&1 || fail=1 ++compare /dev/null empty || fail=1 ++ ++# since this isn't the first partition, it shouldn't become a protective ++# partition or have its starting LBA address set to 1 (and GRUB doesn't care ++# whether it's in the hybrid MBR anyway) ++dump_mbr_table > out || fail=1 ++compare exp out || fail=1 ++ ++Exit $fail |