From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from list by lists.gnu.org with archive (Exim 4.71) id 1Qv2Hq-0007aw-W5 for mharc-grub-devel@gnu.org; Sun, 21 Aug 2011 03:17:38 -0400 Received: from eggs.gnu.org ([140.186.70.92]:51522) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Qv2Hl-0007XZ-Q0 for grub-devel@gnu.org; Sun, 21 Aug 2011 03:17:36 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Qv2Hi-0008NO-En for grub-devel@gnu.org; Sun, 21 Aug 2011 03:17:33 -0400 Received: from mail-yx0-f169.google.com ([209.85.213.169]:37098) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Qv2Hi-0008NK-6w for grub-devel@gnu.org; Sun, 21 Aug 2011 03:17:30 -0400 Received: by yxn35 with SMTP id 35so2329346yxn.0 for ; Sun, 21 Aug 2011 00:17:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=message-id:date:from:user-agent:mime-version:to:subject :x-enigmail-version:content-type; bh=nTQNFADvqG7iACrZxwsDE0w+q8H6TitSLXktwdaKqzo=; b=m1xpCN86YPHzZSZnlNMP26GnTPBcrEURHpC/pZaf8cvT3NUC5afRdISiCGfObNeF5Y NTSYbbVrwCj1UYkaXfA9w0LIBDMPoA+5mRa8uToHuFTjS5btdilPit9x0JKuiBt3m/mG 0oL7ZJ+mkvPxRPqn137/qTTyEWx3FSJKl67FA= Received: by 10.150.150.17 with SMTP id x17mr1122347ybd.383.1313911049475; Sun, 21 Aug 2011 00:17:29 -0700 (PDT) Received: from [192.168.1.106] (c114-77-214-254.rivrw3.nsw.optusnet.com.au [114.77.214.254]) by mx.google.com with ESMTPS id l2sm4135373anm.24.2011.08.21.00.17.26 (version=SSLv3 cipher=OTHER); Sun, 21 Aug 2011 00:17:28 -0700 (PDT) Message-ID: <4E50B103.7070902@gmail.com> Date: Sun, 21 Aug 2011 17:17:23 +1000 From: David Joshua Geary User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.15) Gecko/20110419 Lightning/1.0b2 Thunderbird/3.1.9 MIME-Version: 1.0 To: grub-devel@gnu.org Subject: Request for review of attached patches for hybrid GPT/MBR X-Enigmail-Version: 1.1.2 Content-Type: multipart/mixed; boundary="------------020006020604060506030007" X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 2) X-Received-From: 209.85.213.169 X-BeenThere: grub-devel@gnu.org X-Mailman-Version: 2.1.14 Precedence: list Reply-To: The development of GNU GRUB List-Id: The development of GNU GRUB List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 21 Aug 2011 07:17:36 -0000 This is a multi-part message in MIME format. --------------020006020604060506030007 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 with aim for inclusion in mainline Grub2. Grub2 1.99 is base. Patches to gpt.c and msdos.c in /grub-core/partmap/. Diffs attached with original code and patched code. Goal: To make the disk bootable on: PCs using BIOS, PCs using UEFI, Intel Macintoshes using BIOS, and Intel Macintoshes using EFI, as well as having the (HybridData) partition readable by both Windows and MacOSX. ExaExample partition layout:mple partition layout: Physical: MBR GPT (HybridData) (EFI) (BIOS) MBR: 1: 0C (FAT32) : FAT32 (HybridData) 2: EF (EFI) : FAT32 (EFI) 3: EE (GPT) : (Protective) GPT: 1: 0700 (Data) : FAT32 (HybridData) 2: ef00 (EFI) : FAT32 (EFI) 3: 0f02 (BIOS) : Unformatted (BIOS) Current behaviour: Grub2 detects an MBR and checks if the first MBR primary partition is GPT protective partition. The first MBR primary partition is not a GPT protective partition, so it treats the disk as a pure MBR disk and so refuses to install the boot loader kernel in the post-MBR gap because there is no post-MBR gap because it is taken by GPT protective partition. Proposed behaviour: Grub2 detects an MBR and checks if any of the MBR primary partitions are GPT protective partitions. It finds a GPT protective partition (MBR primary partition 3 in the example), and concludes that the disk is a GPT disk and looks for a BIOS boot partition (Hah!IdontNeedEFI) (GPT partition 3 in the example) in which to install the boot loader kernel. Questions, Comments, and Feedback welcome. See also: http://perditauranus.dyndns.org/tdiary/?date=20110803 See also: http://www.rodsbooks.com/gdisk/hybrid.html#bootloaders - -- David Joshua Geary UNE Linux User Group: linux@une.edu.au I don't care what software you use so long as we only exchange files in open data formats Open-Document Ogg PDF - -----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1.4.10 (GNU/Linux) mQINBE0JY54BEAD2D3ckM+FTElSG6w89L1PkeB+m8+8K5ZJZKfLJXWv7NNTRS5DC uJd/OrlQwC4Ecwkp4HqmjF8jYzCeLEnC7SR6VU3j05l1vR7pZvphUx7lYBbhXbV/ ykBQBK9AWQwf1ve3G2+hGRAeYmhSzen3Px06aBq4/nr3adF1JFekV5CyCuHxDriT p7HMqEwCvRExnaJD45VKzCQiCYLDYkgVc5u+yBWHd92Jk0DHtm6T/CuoLsoYPIgU Jy8sNFdGE9cC3Nb6Ay/Je4FUlKqu9xNej7KGF0SNVjfD7+bMlB37uaDSk1vLGkmg rhnR78YTdZ2iE2hcbae8l/N9aFWUHfxnuHEew/imTJgaBOMOp6k3FGIFG+1qznnG UlXBMR7gY5jyPKTLe0iJo5MCylKeS56uyDgGv8FtGD73MRqGxktR5b8Zv4Wjv0cz S4yBT8dvAYohw8UeRF4EKQlmxX7rusaipXCe/lGyuytfNFJ77EHMESzVkM/da0qk Zt2sotf83XXE+h6lsUDee1whNDtNvaYmTziKZVHJ3olP3kg+FglgoWKG/y5QQHtW ASa4tnlnNFVGJaOBVnVOq5RsrmQ+YSmYsm/OfjzAthc3QM5BhQhB381y4lKtFo1S G7qChNSpA29ea1Muqgz3fDlP3m4iHo1AFvBTgpvEIh/Eb8MXSmeHxmEk0QARAQAB tD9EYXZpZCBHZWFyeSAoR01haWwgRW1haWwgU2lnbmluZyBLZXkpIDxiaWFuY2Eu dXJhbnVzQGdtYWlsLmNvbT6JAjgEEwECACIFAk0JY54CGyMGCwkIBwMCBhUIAgkK CwQWAgMBAh4BAheAAAoJEMR6DgDuR4IfvsUQALCnZfpyz39BQT609n0m+wLZg8CX uTnCf1m+oZj5W11oqqZd0XcHc1o4n6F6VNvSULHCgNYP6kOnJHliBX53Qdr7muJz qQSwtywaS6oQUHWJk++youkvBqV/A4tiggEsXKzdQx/F+o2sIWQ4VGR3NlLkOJa4 xddVAGLuYHmOt1OenwfFXUFodmcPSWwnyVu9E8cm7iZxdg66u/fEraY4pC2yI2Zi T0c1NRVA6ytqo/t4V2wmysZLj9r+O3q88ncUTg1qcVNS32eEVxZJS1R0y7MyM+Gr lh6OI+VrhkfQ/DawvsAqHxxgMWMrzBQUa9ss7mPlawZWOSBiBLtckYHpPJje21ze EPzhPub7EnXfenLWIHG2+O0FIkPIu5OSRwHtAgkTXBXtrzzyMIU/cFsuI0D2Da6m w4QSlEAb8hWGS5LqaM/8i+eaTDL7XgZ9Z7OibS4lzcUnZZOW5dzHIqDVK77c57If rG2cin/GChsYrlckN1XrvlulSXYu08D/xL4f39sUZexiyYZtLoWas+MvOboGgc8o MfE7U92gXb1l4hm3iKXHzaYdBC1YqxMdgy9RBDaSbHq3n0pFpCGW8SURtuOlkVbq kgwPq6v1obb0ZvpE3cLPN8PhqSWxbM4sEYYo6jDAaTRkv/Cy/faEQuCJ+wmVT2bu deYspeWy73rGANavuQINBE0JY54BEADD+h4XyQmbyfNrsmPjHlqJtmZHojtiZZKZ nKoawdLex/L7XJBXfNhTu1kwCV2fW1HibyUW/a5m41H9ked5gScR8bEn02liXc6c Ad6rAMSY9H2/5+FXcGPJqV/9VJTNC8FTEADmJzHzENLDcpil1jXkYDwSn5wYYsmS 1GtT4jsRkGZYzXerPs3JfpxTFuVQWTsodyzfs+YwnNVIcAX4yBrg5WSRPlbHC0WP VQxqFVdDDq0RN11+2sM6BYVvnL1fX69TN4MAPhBjbYalIt0Ls9dGjn/aiB0E1nEn QfRcQb939gJQV5LLKxICc9Qa/GfMFjt6OYArZ8RzMvIs7WGdFXG0GuU/DjHRI65U f7Ed7bGZw9S0ZAjawH8q1/JeUkNtbMCz5oRvvhYTXOw3WLXCeTPALJ6XU7fRTY48 rIDxpvoxyE4mMoNnn1ZE5rcIbCza0tDD3FUPae6i6Mixnk0hXGirwnwPuJyFi3Io zibtMzuoju7l2gKzhZVcp4HlERhGTcEPfCaVIGy3j0k0nfc+hCySA59fXfs6UK5T URhSKLb8+R/Ne0Fyiyg8ASdS8HPIs8Njw4CppfWX5Ds5NgwF5OALlYHkIKjn07Kc +bGO+WWJvnZfo2fdjgPPs4ECaJL7L5RuVuy26QnPMLgAUNJLgGjugGoE0pOLSwmj f3xOB4GlowARAQABiQIfBBgBAgAJBQJNCWOeAhsMAAoJEMR6DgDuR4If6LAQAIAx WHngmPZ1vzraRVadFnQrYr4bpBT+Hznwm91HtAup8HgutSiulOY1Kbpra1avjmQi MYCRQqDeOsFGirOomterbU18sXX7YBFbAT0c+rHoSdtFP2JTkSzN0Ocs1mZZuLfu Dw/DSAaxlpkKQUJ5xn+qAOPr0fX2HbNcTc9kKYPNJJ9n7WtPhG6hC7ehjHis2jSz Am3Ik+5gNZjaNkHsanaGTfx3mo/jf2AUGydB2quYPz8DFyTYN6q+VZ6+Vzy08RGz MLWwM/SSMhT+GXrhSeuAiO26a70T0XTbhliccUDlG6ORPQ+ydzkffNQB35OfIkbA +SoiDf1txduWDeJxblZ3feYqwBcjBBJU/ofEigH32z0ZOsFpWcJTsXSB2RyoJT+Y Pb426uZYYTi2ZtWGcDtwRzK2lXM3WIHnoduFY5LIXq51c+cP2u8a461pmrlTvixE y+IfX/zHfvSUnJDpnO4sA770oCHBUsbmU14oPdr+Kla67jqpjujap1ITRA96bcNm K7O3vOKqbkAoy4w9ogCbBHrH1/8M+iRWA+0Iim+zhYSeEmjLszeecII9MH3u8CS0 DjLhpLrX6WG7F9fr0xAnc3Dui+qBm/q1dKenCKauUYWRgDPUXzP32oIiw/bXBp/4 qKbhUOJTXVVns4OnvPNzFoCvcNS6G/3ZGlX/6duU =kv+y - -----END PGP PUBLIC KEY BLOCK----- -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/ iQIcBAEBAgAGBQJOULD7AAoJEMR6DgDuR4If/vkP/3K3gczRr5LpChbtDKuDdu3/ SGCtRahsnwsR9hmyxwCRKjuiYFDFw+K7aybX/bI9doJxQfR3rL5rclgkuvxvl4nK fhEF+FmIANVvAHVQU4rJNHu1xpxgwplJ2AhBpuNbMWyyVrA8yBwezEgd1YYDedMs tf2G1e4HW+RYvF1oqDZARhJpfgXZJW1oqeVrNA8ZBdyj05nVDm8yB7Os3JawsFE9 E9fvypowlydLDzM1U//3a4CjJ8Q09QTa0GzGiE07pCOWENEehdW2pDJXnQB/BXUY 91FxIcLsMXuY5qg2PVaEOpYt8yQsAVzqNEYX/0nPHf5/++jOtLUiYgXRtd/vq+90 l7kYxo1rm+sRusmy2y0S5BKD8LsqB/I8Y/PP0vKwkKujzFKTcbYRKxyqTvgFLyia HopvTZxEwLzvjsn9k2h2kDYVYOXnMiylpyyMlWBlT1J5xwS70RsVlGwfxYqebpLg S6FZBN9SU20SX7LaRcGwedfjhrHAJk5MLvy3m8cuECZp7HZr3fzBe/a4hJfiD2dG nqHNiDhAFSJLh6lRCELO0noMVLfuJ5nsdubIi0esWIBLrf0aGmkwUwWu+w+txfdU T3YpKfPzCrnnVDV1jc81uuXrnKrKMyQTpd33fnd3Rc5DpnvgBhlkN8WyeljA/lqo 2XLjGbJyhTZjlIh3SJG0 =/0rL -----END PGP SIGNATURE----- --------------020006020604060506030007 Content-Type: text/x-csrc; name="gpt.c" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="gpt.c" /* gpt.c - Read GUID Partition Tables (GPT). */ /* * GRUB -- GRand Unified Bootloader * Copyright (C) 2002,2005,2006,2007,2008 Free Software Foundation, Inc. * * GRUB 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. * * GRUB 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 GRUB. If not, see . */ #include #include #include #include #include #include #include GRUB_MOD_LICENSE ("GPLv3+"); static grub_uint8_t grub_gpt_magic[8] = { 0x45, 0x46, 0x49, 0x20, 0x50, 0x41, 0x52, 0x54 }; static const grub_gpt_part_type_t grub_gpt_partition_type_empty = GRUB_GPT_PARTITION_TYPE_EMPTY; #ifdef GRUB_UTIL static const grub_gpt_part_type_t grub_gpt_partition_type_bios_boot = GRUB_GPT_PARTITION_TYPE_BIOS_BOOT; #endif /* 512 << 7 = 65536 byte sectors. */ #define MAX_SECTOR_LOG 7 static struct grub_partition_map grub_gpt_partition_map; static grub_err_t gpt_partition_map_iterate (grub_disk_t disk, int (*hook) (grub_disk_t disk, const grub_partition_t partition)) { struct grub_partition part; struct grub_gpt_header gpt; struct grub_gpt_partentry entry; struct grub_msdos_partition_mbr mbr; grub_uint64_t entries; unsigned int i; int last_offset = 0; int sector_log = 0; /* Read the protective MBR. */ if (grub_disk_read (disk, 0, 0, sizeof (mbr), &mbr)) return grub_errno; /* Check if it is valid. */ if (mbr.signature != grub_cpu_to_le16 (GRUB_PC_PARTITION_SIGNATURE)) return grub_error (GRUB_ERR_BAD_PART_TABLE, "no signature"); /* Make sure the MBR is a protective or hybrid MBR and not a normal MBR. */ for (i = 0; i < 4; ++i) if (mbr.entries[i].type != GRUB_PC_PARTITION_TYPE_GPT_DISK) { break; } if (i == 4) return grub_error (GRUB_ERR_BAD_PART_TABLE, "no GPT partition map found"); /* Read the GPT header. */ for (sector_log = 0; sector_log < MAX_SECTOR_LOG; sector_log++) { if (grub_disk_read (disk, 1 << sector_log, 0, sizeof (gpt), &gpt)) return grub_errno; if (grub_memcmp (gpt.magic, grub_gpt_magic, sizeof (grub_gpt_magic)) == 0) break; } if (sector_log == MAX_SECTOR_LOG) return grub_error (GRUB_ERR_BAD_PART_TABLE, "no valid GPT header"); grub_dprintf ("gpt", "Read a valid GPT header\n"); entries = grub_le_to_cpu64 (gpt.partitions) << sector_log; for (i = 0; i < grub_le_to_cpu32 (gpt.maxpart); i++) { if (grub_disk_read (disk, entries, last_offset, sizeof (entry), &entry)) return grub_errno; if (grub_memcmp (&grub_gpt_partition_type_empty, &entry.type, sizeof (grub_gpt_partition_type_empty))) { /* Calculate the first block and the size of the partition. */ part.start = grub_le_to_cpu64 (entry.start) << sector_log; part.len = (grub_le_to_cpu64 (entry.end) - grub_le_to_cpu64 (entry.start) + 1) << sector_log; part.offset = entries; part.number = i; part.index = last_offset; part.partmap = &grub_gpt_partition_map; part.parent = disk->partition; grub_dprintf ("gpt", "GPT entry %d: start=%lld, length=%lld\n", i, (unsigned long long) part.start, (unsigned long long) part.len); if (hook (disk, &part)) return grub_errno; } last_offset += grub_le_to_cpu32 (gpt.partentry_size); if (last_offset == GRUB_DISK_SECTOR_SIZE) { last_offset = 0; entries++; } } return GRUB_ERR_NONE; } #ifdef GRUB_UTIL static grub_err_t gpt_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors, grub_embed_type_t embed_type, grub_disk_addr_t **sectors) { grub_disk_addr_t start = 0, len = 0; unsigned i; grub_err_t err; auto int NESTED_FUNC_ATTR find_usable_region (grub_disk_t disk, const grub_partition_t p); int NESTED_FUNC_ATTR find_usable_region (grub_disk_t disk __attribute__ ((unused)), const grub_partition_t p) { struct grub_gpt_partentry gptdata; disk->partition = p->parent; if (grub_disk_read (disk, p->offset, p->index, sizeof (gptdata), &gptdata)) return 0; /* If there's an embed region, it is in a dedicated partition. */ if (! grub_memcmp (&gptdata.type, &grub_gpt_partition_type_bios_boot, 16)) { start = p->start; len = p->len; return 1; } return 0; } if (embed_type != GRUB_EMBED_PCBIOS) return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "GPT curently supports only PC-BIOS embedding"); err = gpt_partition_map_iterate (disk, find_usable_region); if (err) return err; if (len == 0) return grub_error (GRUB_ERR_FILE_NOT_FOUND, "This GPT partition label has no BIOS Boot Partition;" " embedding won't be possible!"); if (len < *nsectors) return grub_error (GRUB_ERR_OUT_OF_RANGE, "Your BIOS Boot Partition is too small;" " embedding won't be possible!"); *nsectors = len; *sectors = grub_malloc (*nsectors * sizeof (**sectors)); if (!*sectors) return grub_errno; for (i = 0; i < *nsectors; i++) (*sectors)[i] = start + i; return GRUB_ERR_NONE; } #endif /* Partition map type. */ static struct grub_partition_map grub_gpt_partition_map = { .name = "gpt", .iterate = gpt_partition_map_iterate, #ifdef GRUB_UTIL .embed = gpt_partition_map_embed #endif }; GRUB_MOD_INIT(part_gpt) { grub_partition_map_register (&grub_gpt_partition_map); } GRUB_MOD_FINI(part_gpt) { grub_partition_map_unregister (&grub_gpt_partition_map); } --------------020006020604060506030007 Content-Type: text/x-patch; name="gpt.c.diff" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="gpt.c.diff" 70,71c70,75 < /* Make sure the MBR is a protective MBR and not a normal MBR. */ < if (mbr.entries[0].type != GRUB_PC_PARTITION_TYPE_GPT_DISK) --- > /* Make sure the MBR is a protective or hybrid MBR and not a normal MBR. */ > for (i = 0; i < 4; ++i) > if (mbr.entries[i].type != GRUB_PC_PARTITION_TYPE_GPT_DISK) { > break; > } > if (i == 4) --------------020006020604060506030007 Content-Type: text/plain; name="gpt.c.original" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="gpt.c.original" /* gpt.c - Read GUID Partition Tables (GPT). */ /* * GRUB -- GRand Unified Bootloader * Copyright (C) 2002,2005,2006,2007,2008 Free Software Foundation, Inc. * * GRUB 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. * * GRUB 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 GRUB. If not, see . */ #include #include #include #include #include #include #include GRUB_MOD_LICENSE ("GPLv3+"); static grub_uint8_t grub_gpt_magic[8] = { 0x45, 0x46, 0x49, 0x20, 0x50, 0x41, 0x52, 0x54 }; static const grub_gpt_part_type_t grub_gpt_partition_type_empty = GRUB_GPT_PARTITION_TYPE_EMPTY; #ifdef GRUB_UTIL static const grub_gpt_part_type_t grub_gpt_partition_type_bios_boot = GRUB_GPT_PARTITION_TYPE_BIOS_BOOT; #endif /* 512 << 7 = 65536 byte sectors. */ #define MAX_SECTOR_LOG 7 static struct grub_partition_map grub_gpt_partition_map; static grub_err_t gpt_partition_map_iterate (grub_disk_t disk, int (*hook) (grub_disk_t disk, const grub_partition_t partition)) { struct grub_partition part; struct grub_gpt_header gpt; struct grub_gpt_partentry entry; struct grub_msdos_partition_mbr mbr; grub_uint64_t entries; unsigned int i; int last_offset = 0; int sector_log = 0; /* Read the protective MBR. */ if (grub_disk_read (disk, 0, 0, sizeof (mbr), &mbr)) return grub_errno; /* Check if it is valid. */ if (mbr.signature != grub_cpu_to_le16 (GRUB_PC_PARTITION_SIGNATURE)) return grub_error (GRUB_ERR_BAD_PART_TABLE, "no signature"); /* Make sure the MBR is a protective MBR and not a normal MBR. */ if (mbr.entries[0].type != GRUB_PC_PARTITION_TYPE_GPT_DISK) return grub_error (GRUB_ERR_BAD_PART_TABLE, "no GPT partition map found"); /* Read the GPT header. */ for (sector_log = 0; sector_log < MAX_SECTOR_LOG; sector_log++) { if (grub_disk_read (disk, 1 << sector_log, 0, sizeof (gpt), &gpt)) return grub_errno; if (grub_memcmp (gpt.magic, grub_gpt_magic, sizeof (grub_gpt_magic)) == 0) break; } if (sector_log == MAX_SECTOR_LOG) return grub_error (GRUB_ERR_BAD_PART_TABLE, "no valid GPT header"); grub_dprintf ("gpt", "Read a valid GPT header\n"); entries = grub_le_to_cpu64 (gpt.partitions) << sector_log; for (i = 0; i < grub_le_to_cpu32 (gpt.maxpart); i++) { if (grub_disk_read (disk, entries, last_offset, sizeof (entry), &entry)) return grub_errno; if (grub_memcmp (&grub_gpt_partition_type_empty, &entry.type, sizeof (grub_gpt_partition_type_empty))) { /* Calculate the first block and the size of the partition. */ part.start = grub_le_to_cpu64 (entry.start) << sector_log; part.len = (grub_le_to_cpu64 (entry.end) - grub_le_to_cpu64 (entry.start) + 1) << sector_log; part.offset = entries; part.number = i; part.index = last_offset; part.partmap = &grub_gpt_partition_map; part.parent = disk->partition; grub_dprintf ("gpt", "GPT entry %d: start=%lld, length=%lld\n", i, (unsigned long long) part.start, (unsigned long long) part.len); if (hook (disk, &part)) return grub_errno; } last_offset += grub_le_to_cpu32 (gpt.partentry_size); if (last_offset == GRUB_DISK_SECTOR_SIZE) { last_offset = 0; entries++; } } return GRUB_ERR_NONE; } #ifdef GRUB_UTIL static grub_err_t gpt_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors, grub_embed_type_t embed_type, grub_disk_addr_t **sectors) { grub_disk_addr_t start = 0, len = 0; unsigned i; grub_err_t err; auto int NESTED_FUNC_ATTR find_usable_region (grub_disk_t disk, const grub_partition_t p); int NESTED_FUNC_ATTR find_usable_region (grub_disk_t disk __attribute__ ((unused)), const grub_partition_t p) { struct grub_gpt_partentry gptdata; disk->partition = p->parent; if (grub_disk_read (disk, p->offset, p->index, sizeof (gptdata), &gptdata)) return 0; /* If there's an embed region, it is in a dedicated partition. */ if (! grub_memcmp (&gptdata.type, &grub_gpt_partition_type_bios_boot, 16)) { start = p->start; len = p->len; return 1; } return 0; } if (embed_type != GRUB_EMBED_PCBIOS) return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "GPT curently supports only PC-BIOS embedding"); err = gpt_partition_map_iterate (disk, find_usable_region); if (err) return err; if (len == 0) return grub_error (GRUB_ERR_FILE_NOT_FOUND, "This GPT partition label has no BIOS Boot Partition;" " embedding won't be possible!"); if (len < *nsectors) return grub_error (GRUB_ERR_OUT_OF_RANGE, "Your BIOS Boot Partition is too small;" " embedding won't be possible!"); *nsectors = len; *sectors = grub_malloc (*nsectors * sizeof (**sectors)); if (!*sectors) return grub_errno; for (i = 0; i < *nsectors; i++) (*sectors)[i] = start + i; return GRUB_ERR_NONE; } #endif /* Partition map type. */ static struct grub_partition_map grub_gpt_partition_map = { .name = "gpt", .iterate = gpt_partition_map_iterate, #ifdef GRUB_UTIL .embed = gpt_partition_map_embed #endif }; GRUB_MOD_INIT(part_gpt) { grub_partition_map_register (&grub_gpt_partition_map); } GRUB_MOD_FINI(part_gpt) { grub_partition_map_unregister (&grub_gpt_partition_map); } --------------020006020604060506030007 Content-Type: text/x-csrc; name="msdos.c" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="msdos.c" /* pc.c - Read PC style partition tables. */ /* * GRUB -- GRand Unified Bootloader * Copyright (C) 2002,2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc. * * GRUB 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. * * GRUB 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 GRUB. If not, see . */ #include #include #include #include #include #include GRUB_MOD_LICENSE ("GPLv3+"); static struct grub_partition_map grub_msdos_partition_map; grub_err_t grub_partition_msdos_iterate (grub_disk_t disk, int (*hook) (grub_disk_t disk, const grub_partition_t partition)) { struct grub_partition p; struct grub_msdos_partition_mbr mbr; int labeln = 0; grub_disk_addr_t lastaddr; grub_disk_addr_t ext_offset; grub_disk_addr_t delta = 0; if (disk->partition && disk->partition->partmap == &grub_msdos_partition_map) { if (disk->partition->msdostype == GRUB_PC_PARTITION_TYPE_LINUX_MINIX) delta = disk->partition->start; else return grub_error (GRUB_ERR_BAD_PART_TABLE, "no embedding supported"); } p.offset = 0; ext_offset = 0; p.number = -1; p.partmap = &grub_msdos_partition_map; /* Any value different than `p.offset' will satisfy the check during first loop. */ lastaddr = !p.offset; while (1) { int i; struct grub_msdos_partition_entry *e; /* Read the MBR. */ if (grub_disk_read (disk, p.offset, 0, sizeof (mbr), &mbr)) goto finish; /* This is our loop-detection algorithm. It works the following way: It saves last position which was a power of two. Then it compares the saved value with a current one. This way it's guaranteed that the loop will be broken by at most third walk. */ if (labeln && lastaddr == p.offset) return grub_error (GRUB_ERR_BAD_PART_TABLE, "loop detected"); labeln++; if ((labeln & (labeln - 1)) == 0) lastaddr = p.offset; /* Check if it is valid. */ if (mbr.signature != grub_cpu_to_le16 (GRUB_PC_PARTITION_SIGNATURE)) return grub_error (GRUB_ERR_BAD_PART_TABLE, "no signature"); for (i = 0; i < 4; i++) if (mbr.entries[i].flag & 0x7f) return grub_error (GRUB_ERR_BAD_PART_TABLE, "bad boot flag"); /* First pass to find GPT partitions. */ for (p.index = 0; p.index < 4; p.index++) { e = mbr.entries + p.index; /* If this is a GPT partition, this MBR is just a dummy. */ if (e->type == GRUB_PC_PARTITION_TYPE_GPT_DISK) return grub_error (GRUB_ERR_BAD_PART_TABLE, "dummy mbr"); } /* Second pass to analyze DOS partitions. */ for (p.index = 0; p.index < 4; p.index++) { e = mbr.entries + p.index; p.start = p.offset + grub_le_to_cpu32 (e->start) - delta; p.len = grub_le_to_cpu32 (e->length); p.msdostype = e->type; grub_dprintf ("partition", "partition %d: flag 0x%x, type 0x%x, start 0x%llx, len 0x%llx\n", p.index, e->flag, e->type, (unsigned long long) p.start, (unsigned long long) p.len); /* If this partition is a normal one, call the hook. */ if (! grub_msdos_partition_is_empty (e->type) && ! grub_msdos_partition_is_extended (e->type)) { p.number++; if (hook (disk, &p)) return grub_errno; } else if (p.number < 4) /* If this partition is a logical one, shouldn't increase the partition number. */ p.number++; } /* Find an extended partition. */ for (i = 0; i < 4; i++) { e = mbr.entries + i; if (grub_msdos_partition_is_extended (e->type)) { p.offset = ext_offset + grub_le_to_cpu32 (e->start); if (! ext_offset) ext_offset = p.offset; break; } } /* If no extended partition, the end. */ if (i == 4) break; } finish: return grub_errno; } #ifdef GRUB_UTIL static grub_err_t pc_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors, grub_embed_type_t embed_type, grub_disk_addr_t **sectors) { grub_disk_addr_t end = ~0ULL; struct grub_msdos_partition_mbr mbr; int labeln = 0; /* Any value different than `p.offset' will satisfy the check during first loop. */ grub_disk_addr_t lastaddr = 1; grub_disk_addr_t ext_offset = 0; grub_disk_addr_t offset = 0; if (embed_type != GRUB_EMBED_PCBIOS) return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "PC-style partitions curently support " "only PC-BIOS embedding"); if (disk->partition) return grub_error (GRUB_ERR_OUT_OF_RANGE, "Embedding on MSDOS subpartition isn't supported"); while (1) { int i; struct grub_msdos_partition_entry *e; grub_err_t err; /* Read the MBR. */ err = grub_disk_read (disk, offset, 0, sizeof (mbr), &mbr); if (err) return err; /* This is our loop-detection algorithm. It works the following way: It saves last position which was a power of two. Then it compares the saved value with a current one. This way it's guaranteed that the loop will be broken by at most third walk. */ if (labeln && lastaddr == offset) return grub_error (GRUB_ERR_BAD_PART_TABLE, "loop detected"); labeln++; if ((labeln & (labeln - 1)) == 0) lastaddr = offset; /* Check if it is valid. */ if (mbr.signature != grub_cpu_to_le16 (GRUB_PC_PARTITION_SIGNATURE)) return grub_error (GRUB_ERR_BAD_PART_TABLE, "no signature"); for (i = 0; i < 4; i++) if (mbr.entries[i].flag & 0x7f) return grub_error (GRUB_ERR_BAD_PART_TABLE, "bad boot flag"); /* Analyze DOS partitions. */ for (i = 0; i < 4; i++) { e = mbr.entries + i; if (!grub_msdos_partition_is_empty (e->type) && end > offset + grub_le_to_cpu32 (e->start)) end = offset + grub_le_to_cpu32 (e->start); /* If this is a GPT partition, this MBR is just a dummy. */ if (e->type == GRUB_PC_PARTITION_TYPE_GPT_DISK) return grub_error (GRUB_ERR_BAD_PART_TABLE, "dummy mbr"); } /* Find an extended partition. */ for (i = 0; i < 4; i++) { e = mbr.entries + i; if (grub_msdos_partition_is_extended (e->type)) { offset = ext_offset + grub_le_to_cpu32 (e->start); if (! ext_offset) ext_offset = offset; break; } } /* If no extended partition, the end. */ if (i == 4) break; } if (end >= *nsectors + 2) { unsigned i; *nsectors = end - 2; *sectors = grub_malloc (*nsectors * sizeof (**sectors)); if (!*sectors) return grub_errno; for (i = 0; i < *nsectors; i++) (*sectors)[i] = 1 + i; return GRUB_ERR_NONE; } if (end <= 1) return grub_error (GRUB_ERR_FILE_NOT_FOUND, "This msdos-style partition label has no " "post-MBR gap; embedding won't be possible!"); if (*nsectors > 62) return grub_error (GRUB_ERR_OUT_OF_RANGE, "Your core.img is unusually large. " "It won't fit in the embedding area."); return grub_error (GRUB_ERR_OUT_OF_RANGE, "Your embedding area is unusually small. " "core.img won't fit in it."); } #endif /* Partition map type. */ static struct grub_partition_map grub_msdos_partition_map = { .name = "msdos", .iterate = grub_partition_msdos_iterate, #ifdef GRUB_UTIL .embed = pc_partition_map_embed #endif }; GRUB_MOD_INIT(part_msdos) { grub_partition_map_register (&grub_msdos_partition_map); } GRUB_MOD_FINI(part_msdos) { grub_partition_map_unregister (&grub_msdos_partition_map); } --------------020006020604060506030007 Content-Type: text/x-patch; name="msdos.c.diff" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="msdos.c.diff" 90c90,100 < /* Analyze DOS partitions. */ --- > /* First pass to find GPT partitions. */ > for (p.index = 0; p.index < 4; p.index++) > { > e = mbr.entries + p.index; > > /* If this is a GPT partition, this MBR is just a dummy. */ > if (e->type == GRUB_PC_PARTITION_TYPE_GPT_DISK) > return grub_error (GRUB_ERR_BAD_PART_TABLE, "dummy mbr"); > } > > /* Second pass to analyze DOS partitions. */ 105,108d114 < /* If this is a GPT partition, this MBR is just a dummy. */ < if (e->type == GRUB_PC_PARTITION_TYPE_GPT_DISK && p.index == 0) < return grub_error (GRUB_ERR_BAD_PART_TABLE, "dummy mbr"); < 213c219 < if (e->type == GRUB_PC_PARTITION_TYPE_GPT_DISK && i == 0) --- > if (e->type == GRUB_PC_PARTITION_TYPE_GPT_DISK) --------------020006020604060506030007 Content-Type: text/plain; name="msdos.c.original" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="msdos.c.original" /* pc.c - Read PC style partition tables. */ /* * GRUB -- GRand Unified Bootloader * Copyright (C) 2002,2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc. * * GRUB 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. * * GRUB 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 GRUB. If not, see . */ #include #include #include #include #include #include GRUB_MOD_LICENSE ("GPLv3+"); static struct grub_partition_map grub_msdos_partition_map; grub_err_t grub_partition_msdos_iterate (grub_disk_t disk, int (*hook) (grub_disk_t disk, const grub_partition_t partition)) { struct grub_partition p; struct grub_msdos_partition_mbr mbr; int labeln = 0; grub_disk_addr_t lastaddr; grub_disk_addr_t ext_offset; grub_disk_addr_t delta = 0; if (disk->partition && disk->partition->partmap == &grub_msdos_partition_map) { if (disk->partition->msdostype == GRUB_PC_PARTITION_TYPE_LINUX_MINIX) delta = disk->partition->start; else return grub_error (GRUB_ERR_BAD_PART_TABLE, "no embedding supported"); } p.offset = 0; ext_offset = 0; p.number = -1; p.partmap = &grub_msdos_partition_map; /* Any value different than `p.offset' will satisfy the check during first loop. */ lastaddr = !p.offset; while (1) { int i; struct grub_msdos_partition_entry *e; /* Read the MBR. */ if (grub_disk_read (disk, p.offset, 0, sizeof (mbr), &mbr)) goto finish; /* This is our loop-detection algorithm. It works the following way: It saves last position which was a power of two. Then it compares the saved value with a current one. This way it's guaranteed that the loop will be broken by at most third walk. */ if (labeln && lastaddr == p.offset) return grub_error (GRUB_ERR_BAD_PART_TABLE, "loop detected"); labeln++; if ((labeln & (labeln - 1)) == 0) lastaddr = p.offset; /* Check if it is valid. */ if (mbr.signature != grub_cpu_to_le16 (GRUB_PC_PARTITION_SIGNATURE)) return grub_error (GRUB_ERR_BAD_PART_TABLE, "no signature"); for (i = 0; i < 4; i++) if (mbr.entries[i].flag & 0x7f) return grub_error (GRUB_ERR_BAD_PART_TABLE, "bad boot flag"); /* Analyze DOS partitions. */ for (p.index = 0; p.index < 4; p.index++) { e = mbr.entries + p.index; p.start = p.offset + grub_le_to_cpu32 (e->start) - delta; p.len = grub_le_to_cpu32 (e->length); p.msdostype = e->type; grub_dprintf ("partition", "partition %d: flag 0x%x, type 0x%x, start 0x%llx, len 0x%llx\n", p.index, e->flag, e->type, (unsigned long long) p.start, (unsigned long long) p.len); /* If this is a GPT partition, this MBR is just a dummy. */ if (e->type == GRUB_PC_PARTITION_TYPE_GPT_DISK && p.index == 0) return grub_error (GRUB_ERR_BAD_PART_TABLE, "dummy mbr"); /* If this partition is a normal one, call the hook. */ if (! grub_msdos_partition_is_empty (e->type) && ! grub_msdos_partition_is_extended (e->type)) { p.number++; if (hook (disk, &p)) return grub_errno; } else if (p.number < 4) /* If this partition is a logical one, shouldn't increase the partition number. */ p.number++; } /* Find an extended partition. */ for (i = 0; i < 4; i++) { e = mbr.entries + i; if (grub_msdos_partition_is_extended (e->type)) { p.offset = ext_offset + grub_le_to_cpu32 (e->start); if (! ext_offset) ext_offset = p.offset; break; } } /* If no extended partition, the end. */ if (i == 4) break; } finish: return grub_errno; } #ifdef GRUB_UTIL static grub_err_t pc_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors, grub_embed_type_t embed_type, grub_disk_addr_t **sectors) { grub_disk_addr_t end = ~0ULL; struct grub_msdos_partition_mbr mbr; int labeln = 0; /* Any value different than `p.offset' will satisfy the check during first loop. */ grub_disk_addr_t lastaddr = 1; grub_disk_addr_t ext_offset = 0; grub_disk_addr_t offset = 0; if (embed_type != GRUB_EMBED_PCBIOS) return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "PC-style partitions curently support " "only PC-BIOS embedding"); if (disk->partition) return grub_error (GRUB_ERR_OUT_OF_RANGE, "Embedding on MSDOS subpartition isn't supported"); while (1) { int i; struct grub_msdos_partition_entry *e; grub_err_t err; /* Read the MBR. */ err = grub_disk_read (disk, offset, 0, sizeof (mbr), &mbr); if (err) return err; /* This is our loop-detection algorithm. It works the following way: It saves last position which was a power of two. Then it compares the saved value with a current one. This way it's guaranteed that the loop will be broken by at most third walk. */ if (labeln && lastaddr == offset) return grub_error (GRUB_ERR_BAD_PART_TABLE, "loop detected"); labeln++; if ((labeln & (labeln - 1)) == 0) lastaddr = offset; /* Check if it is valid. */ if (mbr.signature != grub_cpu_to_le16 (GRUB_PC_PARTITION_SIGNATURE)) return grub_error (GRUB_ERR_BAD_PART_TABLE, "no signature"); for (i = 0; i < 4; i++) if (mbr.entries[i].flag & 0x7f) return grub_error (GRUB_ERR_BAD_PART_TABLE, "bad boot flag"); /* Analyze DOS partitions. */ for (i = 0; i < 4; i++) { e = mbr.entries + i; if (!grub_msdos_partition_is_empty (e->type) && end > offset + grub_le_to_cpu32 (e->start)) end = offset + grub_le_to_cpu32 (e->start); /* If this is a GPT partition, this MBR is just a dummy. */ if (e->type == GRUB_PC_PARTITION_TYPE_GPT_DISK && i == 0) return grub_error (GRUB_ERR_BAD_PART_TABLE, "dummy mbr"); } /* Find an extended partition. */ for (i = 0; i < 4; i++) { e = mbr.entries + i; if (grub_msdos_partition_is_extended (e->type)) { offset = ext_offset + grub_le_to_cpu32 (e->start); if (! ext_offset) ext_offset = offset; break; } } /* If no extended partition, the end. */ if (i == 4) break; } if (end >= *nsectors + 2) { unsigned i; *nsectors = end - 2; *sectors = grub_malloc (*nsectors * sizeof (**sectors)); if (!*sectors) return grub_errno; for (i = 0; i < *nsectors; i++) (*sectors)[i] = 1 + i; return GRUB_ERR_NONE; } if (end <= 1) return grub_error (GRUB_ERR_FILE_NOT_FOUND, "This msdos-style partition label has no " "post-MBR gap; embedding won't be possible!"); if (*nsectors > 62) return grub_error (GRUB_ERR_OUT_OF_RANGE, "Your core.img is unusually large. " "It won't fit in the embedding area."); return grub_error (GRUB_ERR_OUT_OF_RANGE, "Your embedding area is unusually small. " "core.img won't fit in it."); } #endif /* Partition map type. */ static struct grub_partition_map grub_msdos_partition_map = { .name = "msdos", .iterate = grub_partition_msdos_iterate, #ifdef GRUB_UTIL .embed = pc_partition_map_embed #endif }; GRUB_MOD_INIT(part_msdos) { grub_partition_map_register (&grub_msdos_partition_map); } GRUB_MOD_FINI(part_msdos) { grub_partition_map_unregister (&grub_msdos_partition_map); } --------------020006020604060506030007 Content-Type: application/pgp-signature; name="gpt.c.sig" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="gpt.c.sig" iQIcBAABAgAGBQJOULEAAAoJEMR6DgDuR4IfdNwP/1bYkBzoSfO3GpidpMEEQN5WGeTQabIK mUppOfq7njbDQ26lPdP7hUujIX0fN9WVfyuzn+9eXu4cf2QshznMh0x1UEKsLBT6Myk7KHdA 2NYTHe1x8bS0+bra6rjfF+Fpxt5J7W3JJ44FXyuHLgobsvZdK5Jpa+Z1LX1bkVMfjosNxkJE NyUh+sOgQ3ZhlUbPI9kchHeQQ4EO2RgWRtC/dmp9m48MMcYOn4G5oRbfNDS62QypMH8LOPVg MPoEoes6M4BpikiuwkSyVbuI0EBjGWK7bnX5GlQlq/JwriOSTlOlZ8nqzTPVy53NIem6IImX eO1kwKv1YnS9fJFs7Ydx3Js3itCgWCRdp3bPV0zPamzRrB8wMnLb8+N8WWgovdfuw9KfkfmO XgHG9EfKJlX5vvp85EoplIlLX3CU/M3jqCGEPF4mc8EFiQ+e3xDAM4QzV9c0SF6w2h5SXO+7 cmF1cxFeQvgezKpdz7K2sdoM+FZvpAxey82pKrYCypA1eQuO/lUN40WOMsk945JIsR/N18uc 0hKBM+gFC7Jbm2ftY+HAXvsxfEN0hUyUS9jvVLS75EZ8t5GaqwPSqhSFs1slGoXCnQN/DHcH PhfgH8HDyBupaAY2k9Mh5Q8vfmqpA+ZRS9shOrHwkTyPS1+W4wd2q9PQIIqUnkVPtmw2qqxG MkUV --------------020006020604060506030007 Content-Type: application/pgp-signature; name="gpt.c.diff.sig" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="gpt.c.diff.sig" iQIcBAABAgAGBQJOULEBAAoJEMR6DgDuR4IfC1wQANHANIMXoVll9YguREFeBWoZtn5F2U3H 6glAicBIJOZCclFxuCGkHjLE8oZRG88ic2I3jWheTwsIq08ZfKKnkNJtT+wo7ptas2k38yQX GW4u9Lwe1D82bk3IEIJoyjRbUQWjmONCw95gLfXmG6HLcedtdlcr+lT2gr7J0LOyoz3P+aDX /Kl5Q4rxmWmEDTKo3RQCvluEm0+mMEC+TGEvS48GhCXKRmulMau+rpXqJVDDBa50gP3kS9r0 op3rKPy+hM0MVnqK/NRe6CWnXoLqPmE5UWqFcxDQVsD7alglgMpC8IfyYtffv6xOYF0hz5AT tOO87A6zRDuOZDG4m/uacQvGtlaZtiIoWuJGNWTM8a9jPjGfK4GkeabOm9AP6eJYpE6ueVCQ lza5NYqN3sWzI9OuIFkeRwrpPhc/nAVqlLOKpF7BFkfr10gZ3OE3NJcXK1rKkDdyraN7hbMc tfxfynwCgh3KkPPm4tPZezYHGfiPUw581YUuHVtdbiVJXWbvODL+2oNrpw4iEZx3+l7pxalV 9bcihK+pTJ+2MWhvGRO/PXNPL9qg3jAipkjLU/Um0mD17BlIOK8I6y/lpd6keCN7YYZoLYb0 lqdg4xQIL8TKRyJM6Nh/eUiyM56ayBnULn9cwIYQTB0zOjQPPQa2IawoCV+JXOoBFe46Lxoy qWVD --------------020006020604060506030007 Content-Type: application/pgp-signature; name="gpt.c.original.sig" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="gpt.c.original.sig" iQIcBAABAgAGBQJOULEBAAoJEMR6DgDuR4IfdgIP/1rRWBpewI6Zu/s6AXyIrM5cJj0NhDMj vU1jDMO5ZyT/RIqepwqhFLUYU4YvWVRCHc+GDxUdUKmKAD4h3S1TbySV9nO2Q7/SeI4Af1Lf C7PeQy4dEfgHXpeM8YZx7vgJHraK/fucgBaAu8NnB6o9/VmWRFhPhV6JSghCtYzTNzJxF4gj NLyCwLuOwabdrisB89Bjo9GWpR+EeL3kLDySTK3By60wMkRqVJ0qwbHx8TJaAbBneNlIMHOa 7qx7G7F4slxOfYmLeuMGhk82qGyFJyIC1aTCSZ4LNih7cSYy6hkkJXRPSfnsk/bCKXeG7OUu z3lC4vdazcYoNGmV8SemJlK6GoRt81v5eE3D5bIQSrnMrJ1DGyUnKib7+JNMyN6s2WWDNCrV otFBkVRd9OXHGqbw4ByaVpElL22D0IVWeiIGrIhlKv+yDMGtbnyVEFXlxEMFXLJA8Af+zaKz 9MW2KTvo4MO/u99l6eGPsgpwFQBxDrE3SggO4+E3e5PrbTWQGpM+oKrh4gCOMjH7z9RWd47K rr1Dci+qkrvUFkr3Xvz6QrWwuZRaMyUp+nc1Ds+hyVjdr9t6pC3Q9JepYZXaoUzQCdEEIwxR y44ap37t5AqOaPZldNgCqHyT1KpZRemDPIDBpePEnATrXElQ7WBAkJLAJ2W45xpaiiaMIwjw QYBh --------------020006020604060506030007 Content-Type: application/pgp-signature; name="msdos.c.sig" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="msdos.c.sig" iQIcBAABAgAGBQJOULECAAoJEMR6DgDuR4IfJqkQAKlvvABITUL0x0zWDfzRa2DC4CVHy/wA hDBAhMzlI8p7W8Wy7347hS05k5wp2HlSAzoINcsAWnFSb9LHflLL2p4Tuq0BZrbn2+79f/AC pLPKU2zGiaQgWSySqr0M8e/qszTcFH92spsNfTpwnrLu+W9/viYktySx7qixJcSqvSxYax2b SxIeXDI/N7M3tgLOzp1sc7KoZU36w+tFUSQTSlnXM+/rLRyfjZngkdMG5k4O7hOdQrN31UBt WJJLIycI2E3iA5Cd/g/BYqMmo24+LSyE4v55BFFoYY7MDTeuUaKCJHA52Zw47wClV2XkvgDC VYVPjHsY4xEHRQvm0z8ecDoiZ1CHDMqcMuyyl6UF2QHVg3JwdbYRpNrAmNHnCDvqPSICwNqU 27/x81mfK8tEQDF89Skge2pPhXdLu0FyrsTbYhLnNY9XE8mPIOyTpVOU4EF1WtICORPYYMwn bjfbHJvVIHNr6WQ5b2uarhDAJ1T3wJO8JquK9KOSuzyL3jqpkXnHLYi20gLgJz12ZD5JMPaE w6hgVUTT+yEYEoRYTvp7LRj1gA4y+vXu4L6B7eQMwvOyRfE83BQegSTXHS5X0PUkY4UAB5g8 tvC5DKoZnsZfGNv1cgwP9SA00lFmXbpCT61/dzvc1mLE7yvSuo74hcaaCEIET8+p24MdPrP9 Ggid --------------020006020604060506030007 Content-Type: application/pgp-signature; name="msdos.c.diff.sig" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="msdos.c.diff.sig" iQIcBAABAgAGBQJOULECAAoJEMR6DgDuR4IfbW4QAI+piWJ03iBdfnrVjKIuc7ylTSF3e1Ac TOUodKv+Gu1hueG0x7bnxRhdDVmp/lyBWNLeSGzsZfdB6a2sresQQkVlVa81yZ2MTYNP+zkb ES+mPjh7Wg+SAd+iFJk8/aFmL3Dgxsvm3iujxyt8l6zvO7HEQ35BH+yvKlEMtAO3j3g0iElR AditJ/FUBWOY9opmEZd9tMxhbvpn+1YxxV9dfsdlIABPqAFguL5ECk4I+VdBF7ZHNfxgxk20 Xd0zRR5JMfrP7aVW+wjNZT7WpmTLC5X2NLZT5xi7iWsWW4E4c79LDoV+4sw+3yNZ/36Q9XLN znbjtOeB4XnvlkQiEIpigRH3la1LOyBL6RnS1kDaHndlnhRp9HckJkwnm4AScwcK2TmdmMzU qW+ApQhFujPt5iZ3Uo00FYUDe+7q8g3ptO8ZkzZdkpmh+DBhEjwi8kEAaxZp2M3RHyNxzPC+ z1APGpDBofeL21KC3Qoq8vUAE6WzbAZ954x9RHqbjeP0oP7YbPXVxJ+bqJcBC6tsne0HNIDc T4PJazXAVrj7UNrTh/LjhP70eJk0zwgTY7P4k2ve3WwOV5JZ6VptpBzIayUSCr86MF+lznBW f6H+nx/8Z+ZCja06PuqJEooRUhpRyCMAT599J6Hq/d5hn13CIMLQk79RuHSM69NymgUn3qkR c20v --------------020006020604060506030007 Content-Type: application/pgp-signature; name="msdos.c.original.sig" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="msdos.c.original.sig" iQIcBAABAgAGBQJOULEDAAoJEMR6DgDuR4IfVdcP/1bYnK5DbXfeSvv5igNWobVuZCgkausT rMr3TJmyNLl4K8BorbniDFLQipJSDks8DB1NFKTx3G0zw7OkqMTXQsfJgTovyTqCWhKRib9H xd/EVZtRkZEGfjYbhHGPLD5XY7pwx9a9pguzb72TFbTY+2pIdKSFaCFN+B022l5KhiuftLod +lavBHP8c3KyxHP/EyxN7opkW9L4KkYY3iAurDCHXorrkLuRHqSaIV6imYzFsz5RM8v45Iy/ gJUqRosn5uHn0OjqiFZib+6fBMWcHWzJqDwrzu3Hk8i7ocmqtuoSP/O25BlzmgBXdVFRdyqZ qZQGixFuYDuiTa9dm8a3Uej/+WTers5N0a6Oh/kAkNJB5zJDGAD0UI/7JByYck72VW22gTQL 4C43tkUcchzZpYWl9Vw2KtzDuKhAOzguBMa4/ca+6qMu3KJJbZdshAOnh+wOsj67mRpzqOeH diIKfJGIL0IwKm327zHSOUjx49zrzCjgIuYbf58kkNYEKlzN0aezesDWWpNuy5xn0NvPpEzR +mAKr+KDSpYSu0hcq+txaLxKv+vVBxQ2cJUXHXpwm+ZT1fj0NKv4oolz1KU2tc4a2OGE3Cy+ Q67OSV0Sjla6vpEuqyA52/tTLte9Fu0UuVCaownaNbADAwZASAPFjBHAwVrL4t0J6Z3rRH9t BuG4 --------------020006020604060506030007--