/*************************************************************************** * * part.c : library for reading and writing partition tables - uses * libparted for the heavy lifting * * Copyright (C) 2006 David Zeuthen, * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * **************************************************************************/ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include #define BLKGETSIZE64 _IOR(0x12,114,size_t) #include "../hald/logger.h" #include "partutil.h" #ifdef USE_PARTED #include #endif const char * part_get_scheme_name (PartitionScheme scheme) { const char *s; switch (scheme) { case PART_TYPE_GPT: s = "gpt"; break; case PART_TYPE_MSDOS: s = "mbr"; break; case PART_TYPE_MSDOS_EXTENDED: s = "embr"; break; case PART_TYPE_APPLE: s = "apm"; break; default: s = NULL; break; } return s; } struct PartitionEntry_s; typedef struct PartitionEntry_s PartitionEntry; struct PartitionEntry_s { gboolean is_part_table; /* NULL iff is_part_table==FALSE */ PartitionTable *part_table; /* these are always set */ guint8 *data; int length; /* offset _on disk_ where the entry starts */ guint64 offset; }; struct PartitionTable_s { /* partitioning scheme used */ PartitionScheme scheme; /* offset of table on disk */ guint64 offset; guint64 size; /* entries in partition table */ GSList *entries; }; void part_table_find (PartitionTable *p, guint64 offset, PartitionTable **out_part_table, int *out_entry) { int n; int num_entries; *out_part_table = p; *out_entry = -1; num_entries = part_table_get_num_entries (p); for (n = 0; n < num_entries; n++) { guint64 pe_offset; guint64 pe_size; pe_offset = part_table_entry_get_offset (p, n); pe_size = part_table_entry_get_size (p, n); if ((offset >= pe_offset) && (offset < pe_offset + pe_size)) { PartitionTable *part_table_nested; part_table_nested = part_table_entry_get_nested (p, n); /* return the extended partition only if the offset points to it - otherwise * look for a logical partition */ if (part_table_nested != NULL && offset > pe_offset) { part_table_find (part_table_nested, offset, out_part_table, out_entry); } else { *out_entry = n; } /* and we're done... */ break; } } } static guint16 get_le16 (const void *buf) { guint16 i; memcpy (&i, buf, sizeof (i)); return GUINT16_FROM_LE (i); } static guint32 get_le32 (const void *buf) { guint32 i; memcpy (&i, buf, sizeof (i)); return GUINT32_FROM_LE (i); } static guint64 get_le64 (const void *buf) { guint64 i; memcpy (&i, buf, sizeof (i)); return GUINT64_FROM_LE (i); } static guint32 get_be32 (const void *buf) { guint32 i; memcpy (&i, buf, sizeof (i)); return GUINT32_FROM_BE (i); } /* see http://en.wikipedia.org/wiki/Globally_Unique_Identifier - excerpt * * Guids are most commonly written in text as a sequence of hexadecimal digits as such: * * 3F2504E0-4F89-11D3-9A0C-0305E82C3301 * * This text notation follows from the data structure defined above. The sequence is * * 1. Data1 (8 characters) * 2. Hyphen * 3. Data2 (4 characters) * 4. Hyphen * 5. Data3 (4 characters) * 6. Hyphen * 7. Initial two items from Data4 (4 characters) * 8. Hyphen * 9. Remaining six items from Data4 (12 characters) * * Often braces are added to enclose the above format, as such: * * {3F2504E0-4F89-11D3-9A0C-0305E82C3301} * * When printing fewer characters is desired guids are sometimes encoded * into a base64 string of 22 to 24 characters (depending on * padding). For instance: * * 7QDBkvCA1+B9K/U0vrQx1A * 7QDBkvCA1+B9K/U0vrQx1A== */ typedef struct efi_guid_s { guint32 data1; guint16 data2; guint16 data3; guint8 data4[8]; } __attribute__ ((packed)) efi_guid; static char * get_le_guid (const guint8 *buf) { efi_guid *guid = (efi_guid *) buf; return g_strdup_printf("%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", get_le32 (&(guid->data1)), get_le16 (&(guid->data2)), get_le16 (&(guid->data3)), guid->data4[0], guid->data4[1], guid->data4[2], guid->data4[3], guid->data4[4], guid->data4[5], guid->data4[6], guid->data4[7]); } #ifdef USE_PARTED static gboolean set_le_guid (guint8 *buf, const char *source) { efi_guid *guid = (efi_guid *) buf; guint32 __attribute__((__unused__)) data1; guint16 __attribute__((__unused__)) data2; guint16 __attribute__((__unused__)) data3; guint8 __attribute__((__unused__)) data4[8]; gboolean ret; int n; n = sscanf (source, "%x-%hx-%hx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", &guid->data1, &guid->data2, &guid->data3, &(guid->data4[0]), &(guid->data4[1]), &(guid->data4[2]), &(guid->data4[3]), &(guid->data4[4]), &(guid->data4[5]), &(guid->data4[6]), &(guid->data4[7])); if (n != 11) { HAL_INFO (("guid '%s' is not valid")); goto out; } #if 0 HAL_INFO (("source = %s", source)); HAL_INFO (("data1 = %08x", guid->data1)); HAL_INFO (("data2 = %04x", guid->data2)); HAL_INFO (("data3 = %04x", guid->data3)); HAL_INFO (("data4[0] = %02x", guid->data4[0])); HAL_INFO (("data4[1] = %02x", guid->data4[1])); HAL_INFO (("data4[2] = %02x", guid->data4[2])); HAL_INFO (("data4[3] = %02x", guid->data4[3])); HAL_INFO (("data4[4] = %02x", guid->data4[4])); HAL_INFO (("data4[5] = %02x", guid->data4[5])); HAL_INFO (("data4[6] = %02x", guid->data4[6])); HAL_INFO (("data4[7] = %02x", guid->data4[7])); #endif guid->data1 = GUINT32_TO_LE (guid->data1); guid->data2 = GUINT16_TO_LE (guid->data2); guid->data3 = GUINT16_TO_LE (guid->data3); ret = TRUE; out: return ret; } #endif static PartitionEntry * part_entry_new (PartitionTable *e_part_table, const guint8 *data, int length, guint64 offset) { PartitionEntry *pe; pe = g_new0 (PartitionEntry, 1); pe->is_part_table = (e_part_table != NULL); pe->part_table = e_part_table; pe->offset = offset; pe->length = length; pe->data = g_new0 (guint8, length); memcpy (pe->data, data, length); return pe; } static void part_entry_free (PartitionEntry *pe) { if (pe->part_table != NULL) { part_table_free (pe->part_table); } g_free (pe->data); g_free (pe); } static PartitionTable * part_table_new_empty (PartitionScheme scheme) { PartitionTable *p; p = g_new0 (PartitionTable, 1); p->scheme = scheme; p->offset = 0; p->entries = NULL; return p; } void part_table_free (PartitionTable *p) { GSList *i; if (p == NULL) return; for (i = p->entries; i != NULL; i = i->next) { PartitionEntry *pe = i->data; part_entry_free (pe); } g_slist_free (p->entries); g_free (p); } #if 0 static PartitionTable * part_table_parse_bsd (int fd, guint64 offset, guint64 size) { PartitionTable *p; p = NULL; /* TODO */ return p; } #endif #define MSDOS_MAGIC "\x55\xaa" #define MSDOS_PARTTABLE_OFFSET 0x1be #define MSDOS_SIG_OFF 0x1fe #if 0 static void hexdump (const guint8 *mem, int size) { int i; int j; int n; const guint8 *buf = (const guint8 *) mem; n = 0; printf ("Dumping %d=0x%x bytes\n", size, size); while (n < size) { printf ("0x%04x: ", n); j = n; for (i = 0; i < 16; i++) { if (j >= size) break; printf ("%02x ", buf[j]); j++; } for ( ; i < 16; i++) { printf (" "); } printf (" "); j = n; for (i = 0; i < 16; i++) { if (j >= size) break; printf ("%c", isprint(buf[j]) ? buf[j] : '.'); j++; } printf ("\n"); n += 16; } } #endif static PartitionTable * part_table_parse_msdos_extended (int fd, guint64 offset, guint64 size) { int n; PartitionTable *p; guint64 next; //HAL_INFO (("Entering MS-DOS extended parser")); p = NULL; next = offset; while (next != 0) { guint64 readfrom; const guint8 embr[512]; readfrom = next; next = 0; //HAL_INFO (("readfrom = %lld", readfrom)); if (lseek (fd, readfrom, SEEK_SET) < 0) { HAL_INFO (("lseek failed (%s)", strerror (errno))); goto out; } if (read (fd, &embr, sizeof (embr)) != sizeof (embr)) { HAL_INFO (("read failed (%s)", strerror (errno))); goto out; } if (memcmp (&embr[MSDOS_SIG_OFF], MSDOS_MAGIC, 2) != 0) { HAL_INFO (("No MSDOS_MAGIC found")); goto out; } //HAL_INFO (("MSDOS_MAGIC found")); if (p == NULL) { p = part_table_new_empty (PART_TYPE_MSDOS_EXTENDED); p->offset = offset; p->size = size; } for (n = 0; n < 2; n++) { PartitionEntry *pe; guint64 pstart; guint64 psize; pstart = 0x200 * ((guint64) get_le32 (&(embr[MSDOS_PARTTABLE_OFFSET + n * 16 + 8]))); psize = 0x200 * ((guint64) get_le32 (&(embr[MSDOS_PARTTABLE_OFFSET + n * 16 + 12]))); if (psize == 0) continue; pe = NULL; if (n == 0) { //HAL_INFO (("part %d (offset %lld, size %lld, type 0x%02x)", // n, readfrom + pstart, psize, ptype)); //HAL_INFO (("pstart = %lld", pstart)); //hexdump (&(embr[MSDOS_PARTTABLE_OFFSET + n * 16]), 16); pe = part_entry_new (NULL, &(embr[MSDOS_PARTTABLE_OFFSET + n * 16]), 16, readfrom + MSDOS_PARTTABLE_OFFSET + n * 16); } else { if (pstart != 0) { //HAL_INFO (("found chain at offset %lld", offset + pstart); next = offset + pstart; } } //HAL_INFO (("pe = %p", pe)); if (pe != NULL) { p->entries = g_slist_append (p->entries, pe); } } } out: //HAL_INFO (("Exiting MS-DOS extended parser")); return p; } static PartitionTable * part_table_parse_msdos (int fd, guint64 offset, guint64 size, gboolean *found_gpt) { int n; const guint8 mbr[512] __attribute__ ((aligned)); PartitionTable *p; //HAL_INFO (("Entering MS-DOS parser")); *found_gpt = FALSE; p = NULL; if (lseek (fd, offset, SEEK_SET) < 0) { HAL_INFO (("lseek failed (%s)", strerror (errno))); goto out; } if (read (fd, &mbr, sizeof (mbr)) != sizeof (mbr)) { HAL_INFO (("read failed (%s)", strerror (errno))); goto out; } if (memcmp (&mbr[MSDOS_SIG_OFF], MSDOS_MAGIC, 2) != 0) { HAL_INFO (("No MSDOS_MAGIC found")); goto out; } //HAL_INFO (("MSDOS_MAGIC found")); /* sanity checks */ for (n = 0; n < 4; n++) { if (mbr[MSDOS_PARTTABLE_OFFSET + n * 16 + 0] != 0 && mbr[MSDOS_PARTTABLE_OFFSET + n * 16 + 0] != 0x80) { HAL_INFO (("partitioning flag for part %d is not 0x00 or 0x80", n)); goto out; } /* protective MBR for GPT => GPT, not MS-DOS */ if (mbr[MSDOS_PARTTABLE_OFFSET + n * 16 + 4] == 0xee) { HAL_INFO (("found partition type 0xee => protective MBR for GPT", n)); *found_gpt = TRUE; goto out; } } p = part_table_new_empty (PART_TYPE_MSDOS); p->offset = offset; p->size = size; /* we _always_ want to create four partitions */ for (n = 0; n < 4; n++) { PartitionEntry *pe; guint64 pstart; guint64 psize; guint8 ptype; PartitionTable *e_part_table; pstart = 0x200 * ((guint64) get_le32 (&(mbr[MSDOS_PARTTABLE_OFFSET + n * 16 + 8]))); psize = 0x200 * ((guint64) get_le32 (&(mbr[MSDOS_PARTTABLE_OFFSET + n * 16 + 12]))); ptype = mbr[MSDOS_PARTTABLE_OFFSET + n * 16 + 4]; //HAL_INFO (("looking at part %d (offset %lld, size %lld, type 0x%02x)", n, pstart, psize, ptype)); pe = NULL; e_part_table = NULL; /* look for embedded partition tables */ switch (ptype) { /* extended partitions */ case 0x05: /* MS-DOS */ case 0x0f: /* Win95 */ case 0x85: /* Linux */ e_part_table = part_table_parse_msdos_extended (fd, pstart, psize); if (e_part_table != NULL) { pe = part_entry_new (e_part_table, &(mbr[MSDOS_PARTTABLE_OFFSET + n * 16]), 16, offset + MSDOS_PARTTABLE_OFFSET + n * 16); } break; case 0xa5: /* FreeBSD */ case 0xa6: /* OpenBSD */ case 0xa9: /* NetBSD */ //e_part_table = part_table_parse_bsd (fd, pstart, psize); //break; default: //HAL_INFO (("new part entry")); pe = part_entry_new (NULL, &(mbr[MSDOS_PARTTABLE_OFFSET + n * 16]), 16, offset + MSDOS_PARTTABLE_OFFSET + n * 16); break; } //HAL_INFO (("pe = %p", pe)); if (pe != NULL) { p->entries = g_slist_append (p->entries, pe); } } out: //HAL_INFO (("Exiting MS-DOS parser")); return p; } #define GPT_MAGIC "EFI PART" #define GPT_PART_TYPE_GUID_EMPTY "00000000-0000-0000-0000-000000000000" static PartitionTable * part_table_parse_gpt (int fd, guint64 offset, guint64 size) { int n; PartitionTable *p; guint8 buf[16]; guint64 partition_entry_lba; int num_entries; int size_of_entry; HAL_INFO (("Entering EFI GPT parser")); /* by way of getting here, we've already checked for a protective MBR */ p = NULL; /* Check GPT signature */ if (lseek (fd, offset + 512 + 0, SEEK_SET) < 0) { HAL_INFO (("lseek failed (%s)", strerror (errno))); goto out; } if (read (fd, buf, 8) != 8) { HAL_INFO (("read failed (%s)", strerror (errno))); goto out; } if (memcmp (buf, GPT_MAGIC, 8) != 0) { HAL_INFO (("No GPT_MAGIC found")); goto out; } HAL_INFO (("GPT magic found")); /* Disk UUID */ if (lseek (fd, offset + 512 + 56, SEEK_SET) < 0) { HAL_INFO (("lseek failed (%s)", strerror (errno))); goto out; } if (read (fd, buf, 16) != 16) { HAL_INFO (("read failed (%s)", strerror (errno))); goto out; } //hexdump ((guint8*) buf, 16); if (lseek (fd, offset + 512 + 72, SEEK_SET) < 0) { HAL_INFO (("lseek failed (%s)", strerror (errno))); goto out; } if (read (fd, buf, 8) != 8) { HAL_INFO (("read failed (%s)", strerror (errno))); goto out; } partition_entry_lba = get_le64 (buf); if (lseek (fd, offset + 512 + 80, SEEK_SET) < 0) { HAL_INFO (("lseek failed (%s)", strerror (errno))); goto out; } if (read (fd, buf, 4) != 4) { HAL_INFO (("read failed (%s)", strerror (errno))); goto out; } num_entries = get_le32 (buf); if (lseek (fd, offset + 512 + 84, SEEK_SET) < 0) { HAL_INFO (("lseek failed (%s)", strerror (errno))); goto out; } if (read (fd, buf, 4) != 4) { HAL_INFO (("read failed (%s)", strerror (errno))); goto out; } size_of_entry = get_le32(buf); p = part_table_new_empty (PART_TYPE_GPT); p->offset = offset; p->size = size; HAL_INFO (("partition_entry_lba=%d", partition_entry_lba)); HAL_INFO (("num_entries=%d", num_entries)); HAL_INFO (("size_of_entry=%d", size_of_entry)); for (n = 0; n < num_entries; n++) { PartitionEntry *pe; struct { guint8 partition_type_guid[16]; guint8 partition_guid[16]; guint8 starting_lba[8]; guint8 ending_lba[8]; guint8 attributes[8]; guint8 partition_name[72]; } gpt_part_entry; char *partition_type_guid; if (lseek (fd, offset + partition_entry_lba * 512 + n * size_of_entry, SEEK_SET) < 0) { HAL_INFO (("lseek failed (%s)", strerror (errno))); goto out; } if (read (fd, &gpt_part_entry, 128) != 128) { HAL_INFO (("read failed (%s)", strerror (errno))); goto out; } partition_type_guid = get_le_guid (gpt_part_entry.partition_type_guid); if (strcmp (partition_type_guid, GPT_PART_TYPE_GUID_EMPTY) == 0) continue; pe = part_entry_new (NULL, (guint8*) &gpt_part_entry, 128, offset + partition_entry_lba * 512 + n * size_of_entry); p->entries = g_slist_append (p->entries, pe); g_free (partition_type_guid); //hexdump ((guint8 *) &gpt_part_entry, 128); } out: HAL_INFO (("Leaving EFI GPT parser")); return p; } #define MAC_MAGIC "ER" #define MAC_PART_MAGIC "PM" static PartitionTable * part_table_parse_apple (int fd, guint64 offset, guint64 size) { int n; PartitionTable *p; struct { guint16 signature; guint16 block_size; guint32 block_count; /* more stuff */ } __attribute__ ((packed)) mac_header; struct { guint16 signature; guint16 res1; guint32 map_count; guint32 start_block; guint32 block_count; char name[32]; char type[32]; guint32 data_start; guint32 data_count; guint32 status; guint32 boot_start; guint32 boot_size; guint32 boot_load; guint32 boot_load2; guint32 boot_entry; guint32 boot_entry2; guint32 boot_cksum; char processor[16]; /* identifies ISA of boot */ /* more stuff */ } __attribute__ ((packed)) mac_part; int block_size; int map_count; HAL_INFO (("Entering Apple parser")); p = NULL; /* Check Mac start of disk signature */ if (lseek (fd, offset + 0, SEEK_SET) < 0) { HAL_INFO (("lseek failed (%s)", strerror (errno))); goto out; } if (read (fd, &mac_header, sizeof (mac_header)) != sizeof (mac_header)) { HAL_INFO (("read failed (%s)", strerror (errno))); goto out; } if (memcmp (&(mac_header.signature), MAC_MAGIC, 2) != 0) { HAL_INFO (("No MAC_MAGIC found")); goto out; } block_size = GUINT16_FROM_BE (mac_header.block_size); HAL_INFO (("Mac MAGIC found, block_size=%d", block_size)); p = part_table_new_empty (PART_TYPE_APPLE); p->offset = offset; p->size = size; /* get number of entries from first entry */ if (lseek (fd, offset + block_size, SEEK_SET) < 0) { HAL_INFO (("lseek failed (%s)", strerror (errno))); goto out; } if (read (fd, &mac_part, sizeof (mac_part)) != sizeof (mac_part)) { HAL_INFO (("read failed (%s)", strerror (errno))); goto out; } map_count = GUINT32_FROM_BE (mac_part.map_count); /* num blocks in part map */ HAL_INFO (("map_count = %d", map_count)); for (n = 0; n < map_count; n++) { PartitionEntry *pe; if (memcmp (&(mac_part.signature), MAC_PART_MAGIC, 2) != 0) { HAL_INFO (("No MAC_PART_MAGIC found")); break; } if (lseek (fd, offset + (n + 1) * block_size, SEEK_SET) < 0) { HAL_INFO (("lseek failed (%s)", strerror (errno))); goto out; } if (read (fd, &mac_part, sizeof (mac_part)) != sizeof (mac_part)) { HAL_INFO (("read failed (%s)", strerror (errno))); goto out; } pe = part_entry_new (NULL, (guint8*) &mac_part, sizeof (mac_part), offset + (n + 1) * block_size); p->entries = g_slist_append (p->entries, pe); } out: HAL_INFO (("Leaving Apple parser")); return p; } PartitionTable * part_table_load_from_disk (char *device) { int fd; guint64 size; PartitionTable *p; gboolean found_gpt; p = NULL; fd = open (device, O_RDONLY); if (fd < 0) { HAL_INFO (("Cannot open device %s", device)); goto out; } if (ioctl (fd, BLKGETSIZE64, &size) != 0) { HAL_INFO (("Cannot determine size of device")); goto out; } p = part_table_parse_msdos (fd, 0, size, &found_gpt); if (p != NULL) { HAL_INFO (("MSDOS partition table detected")); goto out; } if (found_gpt) { p = part_table_parse_gpt (fd, 0, size); if (p != NULL) { HAL_INFO (("EFI GPT partition table detected")); goto out; } } p = part_table_parse_apple (fd, 0, size); if (p != NULL) { HAL_INFO (("Apple partition table detected")); goto out; } HAL_INFO (("No known partition table found")); out: if (fd >= 0) close (fd); return p; } PartitionScheme part_table_get_scheme (PartitionTable *p) { return p->scheme; } int part_table_get_num_entries (PartitionTable *p) { return g_slist_length (p->entries); } guint64 part_table_get_offset (PartitionTable *p) { return p->offset; } guint64 part_table_get_size (PartitionTable *p) { return p->size; } PartitionTable * part_table_entry_get_nested (PartitionTable *p, int entry) { PartitionEntry *pe; if (p == NULL) return NULL; if ((pe = g_slist_nth_data (p->entries, entry)) == NULL) return NULL; if (pe->is_part_table) return pe->part_table; else return NULL; } /**************************************************************************/ char * part_table_entry_get_type (PartitionTable *p, int entry) { char *s = NULL; PartitionEntry *pe; if (p == NULL) goto out; if ((pe = g_slist_nth_data (p->entries, entry)) == NULL) goto out; switch (p->scheme) { case PART_TYPE_GPT: s = get_le_guid (&(pe->data[0])); break; case PART_TYPE_MSDOS: case PART_TYPE_MSDOS_EXTENDED: s = g_strdup_printf ("0x%02x", pe->data[4]); break; case PART_TYPE_APPLE: s = g_strdup ((char *) pe->data + 2*2 + 3*4 + 32); g_strchomp (s); break; default: break; } out: if (s != NULL) { g_strchomp (s); } return s; } char * part_table_entry_get_uuid (PartitionTable *p, int entry) { char *s = NULL; PartitionEntry *pe; if (p == NULL) goto out; if ((pe = g_slist_nth_data (p->entries, entry)) == NULL) goto out; switch (p->scheme) { case PART_TYPE_GPT: s = get_le_guid (&(pe->data[16])); break; default: break; } out: if (s != NULL) { g_strchomp (s); } return s; } char * part_table_entry_get_label (PartitionTable *p, int entry) { char *s = NULL; PartitionEntry *pe; if (p == NULL) goto out; if ((pe = g_slist_nth_data (p->entries, entry)) == NULL) goto out; switch (p->scheme) { case PART_TYPE_GPT: s = g_utf16_to_utf8 ((const gunichar2 *) &(pe->data[56]), 36, NULL, NULL, NULL); break; case PART_TYPE_APPLE: s = g_strdup ((char *) pe->data + 2*2 + 3*4); g_strchomp (s); break; default: break; } out: if (s != NULL) { g_strchomp (s); } return s; } char ** part_table_entry_get_flags (PartitionTable *p, int entry) { int n; char **ss = NULL; guint32 apm_status; guint64 gpt_attributes; PartitionEntry *pe; if (p == NULL) goto out; if ((pe = g_slist_nth_data (p->entries, entry)) == NULL) goto out; ss = g_new0 (char*, 6 + 1); /* hard coded to max items we'll return */ ss[0] = NULL; n = 0; switch (p->scheme) { case PART_TYPE_GPT: gpt_attributes = get_le64 (&(pe->data[48])); /* From Table 16 of EFI 2.0 spec, bit zero means: * * "Required for the platform to function. The system * cannot function normally if this partition is * removed. This partition should be considered as * part of the hardware of the system, and if it is * removed the system may not boot. It may contain * diagnostics, recovery tools, or other code or data * that is critical to the functioning of a system * independent of any OS." * */ if (gpt_attributes & (1<<0)) { ss[n++] = g_strdup ("required"); } /* TODO: handle partition type specific attributes * * Found on the Internet: "For basic data partitions, the following attribute is * defined:0x8000000000000000 prevents the partition from having a drive letter automatically * assigned. By default, each partition is assigned a new drive letter. Setting this * attribute ensures that when a disk is moved to a new computer, a new drive letter * will not be automatically generated. Instead, the user can manually assign drive * letters. Note: Other attributes can be added at any time." */ break; case PART_TYPE_MSDOS: case PART_TYPE_MSDOS_EXTENDED: if (pe->data[0] == 0x80) { ss[n++] = g_strdup ("boot"); } break; case PART_TYPE_APPLE: apm_status = get_be32 (&(pe->data[2*2 + 3*4 + 2*32 + 2*4])); if (apm_status&(1<<1)) ss[n++] = g_strdup ("allocated"); if (apm_status&(1<<2)) ss[n++] = g_strdup ("in_use"); if (apm_status&(1<<3)) ss[n++] = g_strdup ("boot"); if (apm_status&(1<<4)) ss[n++] = g_strdup ("allow_read"); if (apm_status&(1<<5)) ss[n++] = g_strdup ("allow_write"); if (apm_status&(1<<6)) ss[n++] = g_strdup ("boot_code_is_pic"); break; default: break; } ss[n] = NULL; out: return ss; } guint64 part_table_entry_get_offset (PartitionTable *p, int entry) { guint64 val; PartitionEntry *pe; val = G_MAXUINT64; if (p == NULL) goto out; if ((pe = g_slist_nth_data (p->entries, entry)) == NULL) goto out; switch (p->scheme) { case PART_TYPE_GPT: val = 0x200 * ((guint64) get_le64 (pe->data + 32)); break; case PART_TYPE_MSDOS: val = 0x200 * ((guint64) get_le32 (pe->data + 8)); break; case PART_TYPE_MSDOS_EXTENDED: /* tricky here.. the offset in the EMBR is from the start of the EMBR and they are * scattered around the ext partition... Hence, just use the entry's offset and subtract * it's offset from the EMBR.. */ val = 0x200 * ((guint64) get_le32 (pe->data + 8)) + pe->offset - MSDOS_PARTTABLE_OFFSET; break; case PART_TYPE_APPLE: val = 0x200 * ((guint64) get_be32 (pe->data + 2*2 + 1*4)); break; default: break; } out: return val; } guint64 part_table_entry_get_size (PartitionTable *p, int entry) { guint64 val; PartitionEntry *pe; val = G_MAXUINT64; if (p == NULL) goto out; if ((pe = g_slist_nth_data (p->entries, entry)) == NULL) goto out; switch (p->scheme) { case PART_TYPE_GPT: val = 0x200 * (((guint64) get_le64 (pe->data + 40)) - ((guint64) get_le64 (pe->data + 32)) + 1); break; case PART_TYPE_MSDOS: case PART_TYPE_MSDOS_EXTENDED: val = 0x200 * ((guint64) get_le32 (pe->data + 12)); break; case PART_TYPE_APPLE: val = 0x200 * ((guint64) get_be32 (pe->data + 2*2 + 2*4)); break; default: break; } out: return val; } /**************************************************************************/ #ifdef USE_PARTED /* internal function to both add OR change a partition - if size==0, * then we're changing, otherwise we're adding */ static gboolean part_add_change_partition (char *device_file, guint64 start, guint64 size, guint64 new_start, guint64 new_size, guint64 *out_start, guint64 *out_size, char *type, char *label, char **flags, int geometry_hps, int geometry_spt) { int n; gboolean is_change; gboolean res; PedDevice *device; PedDisk *disk; PedPartition *part; PedConstraint* constraint; PedPartitionType ped_type; guint64 start_sector; guint64 end_sector; guint64 new_start_sector; guint64 new_end_sector; PartitionTable *p; PartitionTable *container_p; int container_entry; PartitionScheme scheme; guint8 mbr_flags = 0; guint8 mbr_part_type = 0; char *endp; guint64 gpt_attributes = 0; guint32 apm_status = 0; res = FALSE; is_change = FALSE; if (size == 0) { is_change = TRUE; } if (is_change) { HAL_INFO (("In part_change_partition: device_file=%s, start=%lld, new_start=%lld, new_size=%lld, type=%s", device_file, start, new_start, new_size, type)); } else { HAL_INFO (("In part_add_partition: device_file=%s, start=%lld, size=%lld, type=%s", device_file, start, size, type)); } /* first, find the kind of (embedded) partition table the new partition is going to be part of */ p = part_table_load_from_disk (device_file); if (p == NULL) { HAL_INFO (("Cannot load partition table from %s", device_file)); goto out; } part_table_find (p, start + 512, &container_p, &container_entry); scheme = part_table_get_scheme (container_p); if (is_change) { /* if changing, make sure there is a partition to change */ if (container_entry < 0) { HAL_INFO (("Couldn't find partition to change")); goto out; } } else { /* if adding, make sure there is no partition in the way... */ if (container_entry >= 0) { char *part_type; /* this might be Apple_Free if we're on PART_TYPE_APPLE */ part_type = part_table_entry_get_type (p, container_entry); if (! (p->scheme == PART_TYPE_APPLE && part_type != NULL && (strcmp (part_type, "Apple_Free") == 0))) { part_table_free (p); HAL_INFO (("There is a partition in the way on %s", device_file)); goto out; } } } HAL_INFO (("containing partition table scheme = %d", scheme)); part_table_free (p); p = NULL; if (!is_change) { if (type == NULL) { HAL_INFO (("No type specified")); goto out; } } /* now that we know the partitoning scheme, sanity check type and flags */ switch (scheme) { case PART_TYPE_MSDOS: case PART_TYPE_MSDOS_EXTENDED: mbr_flags = 0; if (flags != NULL) { for (n = 0; flags[n] != NULL; n++) { if (strcmp (flags[n], "boot") == 0) { mbr_flags |= 0x80; } else { HAL_INFO (("unknown flag '%s'", flags[n])); goto out; } } } if (type != NULL) { mbr_part_type = (guint8) (strtol (type, &endp, 0)); if (*endp != '\0') { HAL_INFO (("invalid type '%s' given", type)); goto out; } } if (label != NULL) { HAL_INFO (("labeled partitions not supported on MSDOS or MSDOS_EXTENDED")); goto out; } break; case PART_TYPE_GPT: gpt_attributes = 0; if (flags != NULL) { for (n = 0; flags[n] != NULL; n++) { if (strcmp (flags[n], "required") == 0) { gpt_attributes |= 1; } else { HAL_INFO (("unknown flag '%s'", flags[n])); goto out; } } } break; case PART_TYPE_APPLE: apm_status = 0; if (flags != NULL) { for (n = 0; flags[n] != NULL; n++) { if (strcmp (flags[n], "allocated") == 0) { apm_status |= (1<<1); } else if (strcmp (flags[n], "in_use") == 0) { apm_status |= (1<<2); } else if (strcmp (flags[n], "boot") == 0) { apm_status |= (1<<3); } else if (strcmp (flags[n], "allow_read") == 0) { apm_status |= (1<<4); } else if (strcmp (flags[n], "allow_write") == 0) { apm_status |= (1<<5); } else if (strcmp (flags[n], "boot_code_is_pic") == 0) { apm_status |= (1<<6); } else { HAL_INFO (("unknown flag '%s'", flags[n])); goto out; } } } break; default: HAL_INFO (("partitioning scheme %d not supported", scheme)); goto out; } switch (scheme) { case PART_TYPE_MSDOS: if (mbr_part_type == 0x05 || mbr_part_type == 0x85 || mbr_part_type == 0x0f) { ped_type = PED_PARTITION_EXTENDED; } else { ped_type = PED_PARTITION_NORMAL; } break; case PART_TYPE_MSDOS_EXTENDED: ped_type = PED_PARTITION_LOGICAL; if (mbr_part_type == 0x05 || mbr_part_type == 0x85 || mbr_part_type == 0x0f) { HAL_INFO (("Cannot create an extended partition inside an extended partition")); goto out; } break; default: ped_type = PED_PARTITION_NORMAL; break; } /* now, create the partition */ start_sector = start / 512; end_sector = (start + size) / 512 - 1; new_start_sector = new_start / 512; new_end_sector = (new_start + new_size) / 512 - 1; device = ped_device_get (device_file); if (device == NULL) { HAL_INFO (("ped_device_get() failed")); goto out; } HAL_INFO (("got it")); /* set drive geometry on libparted object if the user requested it */ if (geometry_hps > 0 && geometry_spt > 0 ) { /* not sure this is authorized use of libparted, but, eh, it seems to work */ device->hw_geom.cylinders = device->bios_geom.cylinders = device->length / geometry_hps / geometry_spt; device->hw_geom.heads = device->bios_geom.heads = geometry_hps; device->hw_geom.sectors = device->bios_geom.sectors = geometry_spt; } disk = ped_disk_new (device); if (disk == NULL) { HAL_INFO (("ped_disk_new() failed")); goto out_ped_device; } HAL_INFO (("got disk")); if (!is_change) { part = ped_partition_new (disk, ped_type, NULL, start_sector, end_sector); if (part == NULL) { HAL_INFO (("ped_partition_new() failed")); goto out_ped_disk; } HAL_INFO (("new partition")); } else { part = ped_disk_get_partition_by_sector (disk, start_sector); if (part == NULL) { HAL_INFO (("ped_partition_get_by_sector() failed")); goto out_ped_disk; } HAL_INFO (("got partition")); } /* TODO HACK XXX FIXME UGLY BAD: This is super ugly abuse of * libparted - we poke at their internal data structures - but * there ain't nothing we can do about it until libparted * provides API for this... */ if (scheme == PART_TYPE_GPT) { struct { efi_guid type; efi_guid uuid; char name[37]; int lvm; int raid; int boot; int hp_service; int hidden; /* more stuff */ } *gpt_data = (void *) part->disk_specific; if (type != NULL) { if (!set_le_guid ((guint8*) &gpt_data->type, type)) { HAL_INFO (("type '%s' for GPT appear to be malformed", type)); goto out_ped_partition; } } if (flags != NULL) { if (gpt_attributes & 1) { gpt_data->hidden = 1; } else { gpt_data->hidden = 0; } } } else if (scheme == PART_TYPE_MSDOS || scheme == PART_TYPE_MSDOS_EXTENDED) { struct { unsigned char system; int boot; /* more stuff */ } *dos_data = (void *) part->disk_specific; if (type != NULL) { dos_data->system = mbr_part_type; } if (flags != NULL) { if (mbr_flags & 0x80) { dos_data->boot = 1; } else { dos_data->boot = 0; } } } else if (scheme == PART_TYPE_APPLE) { struct { char volume_name[33]; /* eg: "Games" */ char system_name[33]; /* eg: "Apple_Unix_SVR2" */ char processor_name[17]; int is_boot; int is_driver; int has_driver; int is_root; int is_swap; int is_lvm; int is_raid; PedSector data_region_length; PedSector boot_region_length; guint32 boot_base_address; guint32 boot_entry_address; guint32 boot_checksum; guint32 status; /* more stuff */ } *mac_data = (void *) part->disk_specific; if (type != NULL) { memset (mac_data->system_name, 0, 33); strncpy (mac_data->system_name, type, 32); } if (flags != NULL) { mac_data->status = apm_status; } } if (label != NULL) { ped_partition_set_name (part, label); } if (geometry_hps > 0 && geometry_spt > 0 ) { /* respect drive geometry */ constraint = ped_constraint_any (device); } else if (geometry_hps == -1 && geometry_spt == -1 ) { /* undocumented (or is it?) libparted usage again.. it appears that * the probed geometry is stored in hw_geom */ device->bios_geom.cylinders = device->hw_geom.cylinders; device->bios_geom.heads = device->hw_geom.heads; device->bios_geom.sectors = device->hw_geom.sectors; constraint = ped_constraint_any (device); } else { PedGeometry *geo_start; PedGeometry *geo_end; /* ignore drive geometry */ if (is_change) { geo_start = ped_geometry_new (device, new_start_sector, 1); geo_end = ped_geometry_new (device, new_end_sector, 1); } else { geo_start = ped_geometry_new (device, start_sector, 1); geo_end = ped_geometry_new (device, end_sector, 1); } constraint = ped_constraint_new (ped_alignment_any, ped_alignment_any, geo_start, geo_end, 1, device->length); } try_change_again: if (is_change) { if (ped_disk_set_partition_geom (disk, part, constraint, new_start_sector, new_end_sector) == 0) { HAL_INFO (("ped_disk_set_partition_geom() failed")); goto out_ped_constraint; } } else { if (ped_disk_add_partition (disk, part, constraint) == 0) { HAL_INFO (("ped_disk_add_partition() failed")); goto out_ped_constraint; } } *out_start = part->geom.start * 512; *out_size = part->geom.length * 512; if (is_change) { /* make sure the resulting size is never smaller than requested * (this is because one will resize the FS and *then* change the partition table) */ if (*out_size < new_size) { HAL_INFO (("new_size=%lld but resulting size, %lld, smaller than requested", new_size, *out_size)); new_end_sector++; goto try_change_again; } else { HAL_INFO (("changed partition to start=%lld size=%lld", *out_start, *out_size)); } } else { HAL_INFO (("added partition start=%lld size=%lld", *out_start, *out_size)); } /* hmm, if we don't do this libparted crashes.. I assume that * ped_disk_add_partition assumes ownership of the * PedPartition when adding it... sadly this is not documented * anywhere.. sigh.. */ part = NULL; /* use commit_to_dev rather than just commit to avoid * libparted sending BLKRRPART to the kernel - we want to do * this ourselves... */ if (ped_disk_commit_to_dev (disk) == 0) { HAL_INFO (("ped_disk_commit_to_dev() failed")); goto out_ped_constraint; } HAL_INFO (("committed to disk")); res = TRUE; ped_constraint_destroy (constraint); ped_disk_destroy (disk); ped_device_destroy (device); goto out; out_ped_constraint: ped_constraint_destroy (constraint); out_ped_partition: if (part != NULL) { ped_partition_destroy (part); } out_ped_disk: ped_disk_destroy (disk); out_ped_device: ped_device_destroy (device); out: return res; } gboolean part_add_partition (char *device_file, guint64 start, guint64 size, guint64 *out_start, guint64 *out_size, char *type, char *label, char **flags, int geometry_hps, int geometry_spt) { return part_add_change_partition (device_file, start, size, 0, 0, out_start, out_size, type, label, flags, geometry_hps, geometry_spt); } gboolean part_change_partition (char *device_file, guint64 start, guint64 new_start, guint64 new_size, guint64 *out_start, guint64 *out_size, char *type, char *label, char **flags, int geometry_hps, int geometry_spt) { return part_add_change_partition (device_file, start, 0, new_start, new_size, out_start, out_size, type, label, flags, geometry_hps, geometry_spt); } gboolean part_del_partition (char *device_file, guint64 offset) { gboolean ret; PedDevice *device; PedDisk *disk; PedPartition *part; PartitionTable *p; gboolean is_extended; int n; HAL_INFO (("In part_del_partition: device_file=%s, offset=%lld", device_file, offset)); ret = FALSE; /* sigh.. one would think that if you passed the sector of where the * the beginning of the extended partition starts, then _by_sector * would return the same as _extended_partition. * * Sadly it's not so.. * * So, check if the passed offset actually corresponds to a nested * partition table... */ is_extended = FALSE; p = part_table_load_from_disk (device_file); if (p == NULL) { HAL_INFO (("Cannot load partition table from %s", device_file)); goto out; } for (n = 0; n < part_table_get_num_entries (p); n++) { PartitionTable *nested; nested = part_table_entry_get_nested (p, n); if (nested != NULL) { if (part_table_get_offset (nested) == offset) { HAL_INFO (("partition to delete is an extended partition")); is_extended = TRUE; } } } part_table_free (p); device = ped_device_get (device_file); if (device == NULL) { HAL_INFO (("ped_device_get() failed")); goto out; } HAL_INFO (("got it")); disk = ped_disk_new (device); if (disk == NULL) { HAL_INFO (("ped_disk_new() failed")); goto out_ped_device; } HAL_INFO (("got disk")); if (is_extended) { part = ped_disk_extended_partition (disk); } else { part = ped_disk_get_partition_by_sector (disk, offset / 512); } if (part == NULL) { HAL_INFO (("ped_disk_get_partition_by_sector() failed")); goto out_ped_disk; } HAL_INFO (("got partition - part->type=%d", part->type)); /* allow only to delete primary, logical and extended partitions */ if (! ((part->type == PED_PARTITION_NORMAL) || (part->type == PED_PARTITION_LOGICAL) || (part->type == PED_PARTITION_EXTENDED))) { HAL_INFO (("no data partition at given offset %lld for device %s", offset, device_file)); goto out_ped_disk; } if (ped_disk_delete_partition (disk, part) == 0) { HAL_INFO (("ped_disk_delete_partition() failed")); goto out_ped_disk; } /* use commit_to_dev rather than just commit to avoid * libparted sending BLKRRPART to the kernel - we want to do * this ourselves... */ if (ped_disk_commit_to_dev (disk) == 0) { HAL_INFO (("ped_disk_commit_to_dev() failed")); goto out_ped_disk; } HAL_INFO (("committed to disk")); ret = TRUE; ped_disk_destroy (disk); ped_device_destroy (device); goto out; out_ped_disk: ped_disk_destroy (disk); out_ped_device: ped_device_destroy (device); out: return ret; } gboolean part_create_partition_table (char *device_file, PartitionScheme scheme) { PedDevice *device; PedDisk *disk; PedDiskType *disk_type; gboolean ret; ret = FALSE; HAL_INFO (("In part_create_partition_table: device_file=%s, scheme=%d", device_file, scheme)); device = ped_device_get (device_file); if (device == NULL) { HAL_INFO (("ped_device_get() failed")); goto out; } HAL_INFO (("got it")); switch (scheme) { case PART_TYPE_MSDOS: disk_type = ped_disk_type_get ("msdos"); break; case PART_TYPE_APPLE: disk_type = ped_disk_type_get ("mac"); break; case PART_TYPE_GPT: disk_type = ped_disk_type_get ("gpt"); break; default: disk_type = NULL; break; } if (disk_type == NULL) { HAL_INFO (("Unknown or unsupported partitioning scheme %d", scheme)); goto out; } disk = ped_disk_new_fresh (device, disk_type); if (disk == NULL) { HAL_INFO (("ped_disk_new_fresh() failed")); goto out_ped_device; } HAL_INFO (("got disk")); if (ped_disk_commit_to_dev (disk) == 0) { HAL_INFO (("ped_disk_commit_to_dev() failed")); goto out_ped_disk; } HAL_INFO (("committed to disk")); ret = TRUE; ped_disk_destroy (disk); ped_device_destroy (device); goto out; out_ped_disk: ped_disk_destroy (disk); out_ped_device: ped_device_destroy (device); out: return ret; } #endif /* USE_PARTED */ /**************************************************************************/