diff options
Diffstat (limited to 'libparted/labels/mac.c')
-rw-r--r-- | libparted/labels/mac.c | 1604 |
1 files changed, 1604 insertions, 0 deletions
diff --git a/libparted/labels/mac.c b/libparted/labels/mac.c new file mode 100644 index 0000000..6711d87 --- /dev/null +++ b/libparted/labels/mac.c @@ -0,0 +1,1604 @@ +/* + libparted - a library for manipulating disk partitions + Copyright (C) 2000, 2002, 2004, 2007-2014, 2019-2023 Free Software + Foundation, Inc. + + 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/>. +*/ + +#include <config.h> + +#include <parted/parted.h> +#include <parted/debug.h> +#include <parted/endian.h> +#include <stdbool.h> + +#if ENABLE_NLS +# include <libintl.h> +# define _(String) dgettext (PACKAGE, String) +#else +# define _(String) (String) +#endif /* ENABLE_NLS */ + +#include "misc.h" +#include "pt-tools.h" + +/* struct's hacked from Linux source: fs/partitions/mac.h + * I believe it was originally written by Paul Mackerras (from comments in + * Quik source) + * + * See also: + * http://developer.apple.com/documentation/mac/Devices/Devices-126.html + * http://developer.apple.com/documentation/mac/Devices/Devices-121.html + * http://devworld.apple.com/technotes/tn/tn1189.html + * + * Partition types: + * Apple_Bootstrap new-world (HFS) boot partition + * Apple_partition_map partition map (table) + * Apple_Driver device driver + * Apple_Driver43 SCSI Manager 4.3 device driver + * Apple_MFS original Macintosh File System + * Apple_HFS Hierarchical File System (and +) + * Apple_HFSX HFS+ with case sensitivity and more + * Apple_UNIX_SVR2 UNIX file system (UFS?) + * Apple_PRODOS ProDOS file system + * Apple_Free unused space + * Apple_Scratch empty + * Apple_Void padding for iso9660 + * Apple_Extra an unused partition map entry + * + * Quick explanation: + * ------------------ + * Terminology: + * + * Parted Apple + * ------ ----- + * device disk/device + * disk no equivalent. + * partition volume or partition + * sector block + * + * * All space must be accounted for, except block 0 (driver block) and + * block 1-X (the partition map: i.e. lots of MacRawPartitions) + * + * * It's really hard to grow/shrink the number of MacRawPartition + * entries in the partition map, because the first partition starts + * immediately after the partition map. When we can move the start of + * HFS and ext2 partitions, this problem will disappear ;-) + */ + +#define MAC_PARTITION_MAGIC_1 0x5453 /* old */ +#define MAC_PARTITION_MAGIC_2 0x504d +#define MAC_DISK_MAGIC 0x4552 + +#define MAC_STATUS_BOOTABLE 8 /* partition is bootable */ + +typedef struct _MacRawPartition MacRawPartition; +typedef struct _MacRawDisk MacRawDisk; +typedef struct _MacDeviceDriver MacDeviceDriver; +typedef struct _MacPartitionData MacPartitionData; +typedef struct _MacDiskData MacDiskData; + +struct __attribute__ ((packed)) _MacRawPartition { + uint16_t signature; /* expected to be MAC_PARTITION_MAGIC */ + uint16_t res1; + uint32_t map_count; /* # blocks in partition map */ + uint32_t start_block; /* absolute starting block # of partition */ + uint32_t block_count; /* number of blocks in partition */ + char name[32]; /* partition name */ + char type[32]; /* string type description */ + uint32_t data_start; /* rel block # of first data block */ + uint32_t data_count; /* number of data blocks */ + uint32_t status; /* partition status bits */ + uint32_t boot_start; + uint32_t boot_count; + uint32_t boot_load; + uint32_t boot_load2; + uint32_t boot_entry; + uint32_t boot_entry2; + uint32_t boot_cksum; + char processor[16]; /* Contains 680x0, x=0,2,3,4; or empty */ + uint32_t driver_sig; + char _padding[372]; +}; + +/* Driver descriptor structure, in block 0 */ +struct __attribute__ ((packed)) _MacRawDisk { + uint16_t signature; /* expected to be MAC_DRIVER_MAGIC */ + uint16_t block_size; /* physical sector size */ + uint32_t block_count; /* size of device in blocks */ + uint16_t dev_type; /* reserved */ + uint16_t dev_id; /* reserved */ + uint32_t data; /* reserved */ + uint16_t driver_count; /* # of driver descriptor entries */ + uint8_t driverlist[488];/* info about available drivers */ + uint16_t padding[3]; /* pad to 512 bytes */ +}; + +struct __attribute__ ((packed)) _MacDeviceDriver { + uint32_t block; /* startblock in MacRawDisk->block_size units */ + uint16_t size; /* size in 512 byte units */ + uint16_t type; /* operating system type (MacOS = 1) */ +}; + +struct _MacPartitionData { + 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; + + uint32_t boot_base_address; + uint32_t boot_entry_address; + uint32_t boot_checksum; + + uint32_t status; + uint32_t driver_sig; +}; + +struct _MacDiskData { + int ghost_size; /* sectors per "driver" block */ + int part_map_entry_count; /* # entries (incl. ghost) */ + int part_map_entry_num; /* partition map location */ + + int active_part_entry_count; /* # real partitions */ + int free_part_entry_count; /* # free space */ + int last_part_entry_num; /* last entry number */ + + uint16_t block_size; /* physical sector size */ + uint16_t driver_count; + MacDeviceDriver driverlist[1 + 60]; /* 488 bytes */ +}; + +static PedDiskType mac_disk_type; + +static int +_check_signature (MacRawDisk const *raw_disk) +{ + if (PED_BE16_TO_CPU (raw_disk->signature) != MAC_DISK_MAGIC) { +#ifdef DISCOVER_ONLY + return 0; +#else + return ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Invalid signature %x for Mac disk labels."), + (int) PED_BE16_TO_CPU (raw_disk->signature)) + == PED_EXCEPTION_IGNORE; +#endif + } + + return 1; +} + +static int +_rawpart_check_signature (MacRawPartition* raw_part) +{ + int sig = (int) PED_BE16_TO_CPU (raw_part->signature); + return sig == MAC_PARTITION_MAGIC_1 || sig == MAC_PARTITION_MAGIC_2; +} + +static int +mac_probe (const PedDevice * dev) +{ + PED_ASSERT (dev != NULL); + + if (dev->sector_size < sizeof (MacRawDisk)) + return 0; + + void *label; + if (!ptt_read_sector (dev, 0, &label)) + return 0; + + int valid = _check_signature (label); + + free (label); + return valid; +} + +static int +_disk_add_part_map_entry (PedDisk* disk, int warn) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + PedPartition* new_part; + MacPartitionData* mac_part_data; + PedSector part_map_size; + PedConstraint* constraint_any = ped_constraint_any (disk->dev); + +#ifndef DISCOVER_ONLY + if (warn && ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_FIX | PED_EXCEPTION_CANCEL, + _("Partition map has no partition map entry!")) + != PED_EXCEPTION_FIX) + goto error; +#endif /* !DISCOVER_ONLY */ + + part_map_size + = ped_round_up_to (mac_disk_data->last_part_entry_num, 64); + if (part_map_size == 0) + part_map_size = 64; + + new_part = ped_partition_new (disk, PED_PARTITION_NORMAL, NULL, + 1, part_map_size - 1); + if (!new_part) + goto error; + + mac_part_data = new_part->disk_specific; + strcpy (mac_part_data->volume_name, "Apple"); + strcpy (mac_part_data->system_name, "Apple_partition_map"); + + if (!ped_disk_add_partition (disk, new_part, constraint_any)) + goto error_destroy_new_part; + + mac_disk_data->part_map_entry_num = new_part->num; + mac_disk_data->part_map_entry_count + = new_part->geom.end - mac_disk_data->ghost_size; + ped_constraint_destroy (constraint_any); + return 1; + +error_destroy_new_part: + ped_partition_destroy (new_part); +error: + ped_constraint_destroy (constraint_any); + return 0; +} + +static PedDisk* +mac_alloc (const PedDevice* dev) +{ + PedDisk* disk; + MacDiskData* mac_disk_data; + + PED_ASSERT (dev != NULL); + +#ifndef DISCOVER_ONLY + if (dev->length < 256) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("%s is too small for a Mac disk label!"), + dev->path); + goto error; + } +#endif + + disk = _ped_disk_alloc (dev, &mac_disk_type); + if (!disk) + goto error; + + mac_disk_data = (MacDiskData*) ped_malloc (sizeof (MacDiskData)); + if (!mac_disk_data) + goto error_free_disk; + disk->disk_specific = mac_disk_data; + mac_disk_data->ghost_size = 1; + mac_disk_data->active_part_entry_count = 0; + mac_disk_data->free_part_entry_count = 1; + mac_disk_data->last_part_entry_num = 1; + mac_disk_data->block_size = 0; + mac_disk_data->driver_count = 0; + memset(&mac_disk_data->driverlist[0], 0, sizeof(mac_disk_data->driverlist)); + + if (!_disk_add_part_map_entry (disk, 0)) + goto error_free_disk; + return disk; + +error_free_disk: + _ped_disk_free (disk); +error: + return NULL; +} + +static PedDisk* +mac_duplicate (const PedDisk* disk) +{ + PedDisk* new_disk; + MacDiskData* new_mac_data; + MacDiskData* old_mac_data = (MacDiskData*) disk->disk_specific; + + new_disk = ped_disk_new_fresh (disk->dev, &mac_disk_type); + if (!new_disk) + return NULL; + + new_mac_data = (MacDiskData*) new_disk->disk_specific; + + /* remove the partition map partition - it will be duplicated + * later. + */ + PedSector first_part_map_sector = old_mac_data->ghost_size; + PedPartition *partition_map + = ped_disk_get_partition_by_sector (new_disk, first_part_map_sector); + PED_ASSERT (partition_map != NULL); + + /* ped_disk_remove_partition may be used only to delete a "normal" + partition. Trying to delete at least "freespace" or "metadata" + partitions leads to a violation of assumptions in + ped_disk_remove_partition, since it calls _disk_push_update_mode, + which destroys all "freespace" and "metadata" partitions, and + depends on that destruction not freeing its PART parameter. */ + PED_ASSERT (partition_map->type == PED_PARTITION_NORMAL); + ped_disk_remove_partition (new_disk, partition_map); + + /* ugly, but C is ugly :p */ + memcpy (new_mac_data, old_mac_data, sizeof (MacDiskData)); + return new_disk; +} + +static void +mac_free (PedDisk* disk) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + + _ped_disk_free (disk); + free (mac_disk_data); +} + +static int +_rawpart_cmp_type (const MacRawPartition* raw_part, const char* type) +{ + return strncasecmp (raw_part->type, type, 32) == 0; +} + +static int +_rawpart_cmp_name (const MacRawPartition* raw_part, const char* name) +{ + return strncasecmp (raw_part->name, name, 32) == 0; +} + +static int +_rawpart_is_partition_map (const MacRawPartition* raw_part) +{ + return _rawpart_cmp_type (raw_part, "Apple_partition_map"); +} + +static int +strncasestr (const char* haystack, const char* needle, int n) +{ + int needle_size = strlen (needle); + int i; + + for (i = 0; haystack[i] && i < n - needle_size; i++) { + if (strncasecmp (haystack + i, needle, needle_size) == 0) + return 1; + } + + return 0; +} + +static int +_rawpart_is_boot (const MacRawPartition* raw_part) +{ + if (!strcasecmp(raw_part->type, "Apple_Bootstrap")) + return 1; + + if (!strcasecmp(raw_part->type, "Apple_Boot")) + return 1; + + return 0; +} + +static int +_rawpart_is_driver (const MacRawPartition* raw_part) +{ + if (strncmp (raw_part->type, "Apple_", 6) != 0) + return 0; + if (!strncasestr (raw_part->type, "driver", 32)) + return 0; + return 1; +} + +static int _GL_ATTRIBUTE_PURE +_rawpart_has_driver (const MacRawPartition* raw_part, MacDiskData* mac_disk_data) +{ + MacDeviceDriver *driverlist; + uint16_t i; + uint32_t start_block, block_count; + + start_block = PED_BE32_TO_CPU(raw_part->start_block); + block_count = PED_BE32_TO_CPU(raw_part->block_count); + driverlist = &mac_disk_data->driverlist[0]; + for (i = 0; i < mac_disk_data->driver_count; i++) { + if (start_block == PED_BE32_TO_CPU(driverlist->block) && + block_count == PED_BE16_TO_CPU(driverlist->size)) + return 1; + driverlist++; + } + return 0; +} + +static int +_rawpart_is_root (MacRawPartition* raw_part) +{ + if (!_rawpart_cmp_type (raw_part, "Apple_UNIX_SVR2")) + return 0; + if (strcmp (raw_part->name, "root") != 0) + return 0; + return 1; +} + +static int +_rawpart_is_swap (MacRawPartition* raw_part) +{ + if (!_rawpart_cmp_type (raw_part, "Apple_UNIX_SVR2")) + return 0; + if (strcmp (raw_part->name, "swap") != 0) + return 0; + return 1; +} + +static int +_rawpart_is_lvm (MacRawPartition* raw_part) +{ + if (strcmp (raw_part->type, "Linux_LVM") != 0) + return 0; + return 1; +} + +static int +_rawpart_is_raid (MacRawPartition* raw_part) +{ + if (strcmp (raw_part->type, "Linux_RAID") != 0) + return 0; + return 1; +} + +static int +_rawpart_is_void (MacRawPartition* raw_part) +{ + return _rawpart_cmp_type (raw_part, "Apple_Void"); +} + +/* returns 1 if the raw_part represents a partition that is "unused space", or + * doesn't represent a partition at all. NOTE: some people make Apple_Free + * partitions with MacOS, because they can't select another type. So, if the + * name is anything other than "Extra" or "", it is treated as a "real" + * partition. + */ +static int +_rawpart_is_active (MacRawPartition* raw_part) +{ + if (_rawpart_cmp_type (raw_part, "Apple_Free") + && (_rawpart_cmp_name (raw_part, "Extra") + || _rawpart_cmp_name (raw_part, ""))) + return 0; + if (_rawpart_cmp_type (raw_part, "Apple_Void")) + return 0; + if (_rawpart_cmp_type (raw_part, "Apple_Scratch")) + return 0; + if (_rawpart_cmp_type (raw_part, "Apple_Extra")) + return 0; + + return 1; +} + +static PedPartition* +_rawpart_analyse (MacRawPartition* raw_part, PedDisk* disk, int num) +{ + MacDiskData* mac_disk_data; + PedPartition* part; + MacPartitionData* mac_part_data; + PedSector start, length; + + if (!_rawpart_check_signature (raw_part)) { +#ifndef DISCOVER_ONLY + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("Partition %d has an invalid signature %x."), + num, + (int) PED_BE16_TO_CPU (raw_part->signature)) + != PED_EXCEPTION_IGNORE) +#endif + goto error; + } + + mac_disk_data = (MacDiskData*) disk->disk_specific; + + start = PED_BE32_TO_CPU (raw_part->start_block); + length = PED_BE32_TO_CPU (raw_part->block_count); + if (length == 0) { +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Partition %d has an invalid length of 0 bytes!"), + num); +#endif + return NULL; + } + part = ped_partition_new (disk, PED_PARTITION_NORMAL, NULL, + start, start + length - 1); + if (!part) + goto error; + + mac_part_data = part->disk_specific; + + strncpy (mac_part_data->volume_name, raw_part->name, 32); + strncpy (mac_part_data->system_name, raw_part->type, 32); + strncpy (mac_part_data->processor_name, raw_part->processor, 16); + + mac_part_data->is_boot = _rawpart_is_boot (raw_part); + mac_part_data->is_driver = _rawpart_is_driver (raw_part); + if (mac_part_data->is_driver) + mac_part_data->has_driver = _rawpart_has_driver(raw_part, mac_disk_data); + mac_part_data->is_root = _rawpart_is_root (raw_part); + mac_part_data->is_swap = _rawpart_is_swap (raw_part); + mac_part_data->is_lvm = _rawpart_is_lvm (raw_part); + mac_part_data->is_raid = _rawpart_is_raid (raw_part); + + /* "data" region */ +#ifndef DISCOVER_ONLY + if (raw_part->data_start) { + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("The data region doesn't start at the start " + "of the partition.")); + goto error_destroy_part; + } +#endif /* !DISCOVER_ONLY */ + mac_part_data->data_region_length + = PED_BE32_TO_CPU (raw_part->data_count); + + mac_part_data->boot_region_length + = PED_BE32_TO_CPU (raw_part->boot_count); + +#ifndef DISCOVER_ONLY + if (mac_part_data->has_driver) { + if (mac_part_data->boot_region_length < part->geom.length) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("The partition's boot region doesn't occupy " + "the entire partition.")) + != PED_EXCEPTION_IGNORE) + goto error_destroy_part; + } + } else { + if (mac_part_data->data_region_length < part->geom.length && + !mac_part_data->is_boot) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("The partition's data region doesn't occupy " + "the entire partition.")) + != PED_EXCEPTION_IGNORE) + goto error_destroy_part; + } + } +#endif /* !DISCOVER_ONLY */ + + mac_part_data->boot_base_address + = PED_BE32_TO_CPU (raw_part->boot_load); + mac_part_data->boot_entry_address + = PED_BE32_TO_CPU (raw_part->boot_entry); + mac_part_data->boot_checksum + = PED_BE32_TO_CPU (raw_part->boot_cksum); + + mac_part_data->status = PED_BE32_TO_CPU (raw_part->status); + mac_part_data->driver_sig = PED_BE32_TO_CPU (raw_part->driver_sig); + + return part; + +error_destroy_part: + ped_partition_destroy (part); +error: + return NULL; +} + +/* looks at the partition map size field in a mac raw partition, and calculates + * what the size of the partition map should be, from it + */ +static int +_rawpart_get_partmap_size (MacRawPartition* raw_part, PedDisk* disk) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + PedSector part_map_start; + PedSector part_map_end; + + part_map_start = mac_disk_data->ghost_size; + part_map_end = PED_BE32_TO_CPU (raw_part->map_count); + + return part_map_end - part_map_start + 1; +} + +static int +_disk_analyse_block_size (PedDisk* disk, MacRawDisk* raw_disk) +{ + PedSector block_size; + + if (PED_BE16_TO_CPU (raw_disk->block_size) % 512) { +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Weird block size on device descriptor: %d bytes is " + "not divisible by 512."), + (int) PED_BE16_TO_CPU (raw_disk->block_size)); +#endif + goto error; + } + + block_size = PED_BE16_TO_CPU (raw_disk->block_size) / 512; + if (block_size != disk->dev->sector_size / 512) { +#ifndef DISCOVER_ONLY + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("The driver descriptor says the physical block size " + "is %d bytes, but Linux says it is %d bytes."), + (int) block_size * 512, + (int) disk->dev->sector_size) + != PED_EXCEPTION_IGNORE) + goto error; +#endif + disk->dev->sector_size = block_size * 512; + } + + return 1; + +error: + return 0; +} + +/* Tries to figure out the block size used by the drivers, for the ghost + * partitioning scheme. Ghost partitioning works like this: the OpenFirmware + * (OF) sees 512 byte blocks, but some drivers use 2048 byte blocks (and, + * perhaps, some other number?). To remain compatible, the partition map + * only has "real" partition map entries on ghost-aligned block numbers (and + * the others are padded with Apple_Void partitions). This function tries + * to figure out what the "ghost-aligned" size is... (which, believe-it-or-not, + * doesn't always equal 2048!!!) + */ +static int +_disk_analyse_ghost_size (PedDisk* disk) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + + void *buf = ped_malloc (disk->dev->sector_size); + if (!buf) + return 0; + + int i; + int found = 0; + for (i = 1; i < 64; i *= 2) { + if (!ped_device_read (disk->dev, buf, i, 1)) + break; + if (_rawpart_check_signature (buf) + && !_rawpart_is_void (buf)) { + mac_disk_data->ghost_size = i; + found = (i <= disk->dev->sector_size / 512); + break; + } + } + free (buf); + +#ifndef DISCOVER_ONLY + if (!found) + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("No valid partition map found.")); +#endif + return found; +} + +static int +mac_read (PedDisk* disk) +{ + MacDiskData* mac_disk_data; + PedPartition* part; + int num; + PedSector ghost_size; + int last_part_entry_num = 0; + + PED_ASSERT (disk != NULL); + + mac_disk_data = disk->disk_specific; + mac_disk_data->part_map_entry_num = 0; /* 0 == none */ + + void *buf; + if (!ptt_read_sector (disk->dev, 0, &buf)) + return 0; + + MacRawDisk *raw_disk = buf; + + if (!_check_signature (raw_disk)) + goto error; + + /* Record the original sector size; this function may change it. */ + PedSector ss0 = disk->dev->sector_size; + if (!_disk_analyse_block_size (disk, raw_disk)) + goto error; + + if (!_disk_analyse_ghost_size (disk)) + goto error; + ghost_size = mac_disk_data->ghost_size; + + if (!ped_disk_delete_all (disk)) + goto error; + + if (PED_BE16_TO_CPU(raw_disk->driver_count) && + PED_BE16_TO_CPU(raw_disk->driver_count) < 62) { + memcpy(&mac_disk_data->driverlist[0], &raw_disk->driverlist[0], + sizeof(mac_disk_data->driverlist)); + mac_disk_data->driver_count = PED_BE16_TO_CPU(raw_disk->driver_count); + mac_disk_data->block_size = PED_BE16_TO_CPU(raw_disk->block_size); + } + + /* If _disk_analyse_block_size has increased the sector_size, + reallocate this buffer, so we can still read a sector into it. */ + if (ss0 < disk->dev->sector_size) { + free (buf); + buf = ped_malloc (disk->dev->sector_size); + if (buf == NULL) + goto error; + } + + for (num=1; num==1 || num <= last_part_entry_num; num++) { + void *raw_part = buf; + if (!ped_device_read (disk->dev, raw_part, + num * ghost_size, 1)) + goto error_delete_all; + + if (!_rawpart_check_signature (raw_part)) + continue; + + if (num == 1) + last_part_entry_num + = _rawpart_get_partmap_size (raw_part, disk); + if (_rawpart_get_partmap_size (raw_part, disk) + != last_part_entry_num) { + if (ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Conflicting partition map entry sizes! " + "Entry 1 says it is %d, but entry %d says " + "it is %d!"), + last_part_entry_num, + _rawpart_get_partmap_size (raw_part, disk)) + != PED_EXCEPTION_IGNORE) + goto error_delete_all; + } + + if (!_rawpart_is_active (raw_part)) + continue; + + part = _rawpart_analyse (raw_part, disk, num); + if (!part) + goto error_delete_all; + part->num = num; + part->fs_type = ped_file_system_probe (&part->geom); + PedConstraint *constraint_exact + = ped_constraint_exact (&part->geom); + if (constraint_exact == NULL) + goto error_delete_all; + bool ok = ped_disk_add_partition (disk, part, constraint_exact); + ped_constraint_destroy (constraint_exact); + if (!ok) + goto error_delete_all; + + if (_rawpart_is_partition_map (raw_part)) { + if (mac_disk_data->part_map_entry_num + && ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_IGNORE_CANCEL, + _("Weird! There are 2 partitions " + "map entries!")) + != PED_EXCEPTION_IGNORE) + goto error_delete_all; + + mac_disk_data->part_map_entry_num = num; + mac_disk_data->part_map_entry_count + = part->geom.end - ghost_size + 1; + } + } + + if (!mac_disk_data->part_map_entry_num) { + if (!_disk_add_part_map_entry (disk, 1)) + goto error_delete_all; + ped_disk_commit_to_dev (disk); + } + free (buf); + return 1; + +error_delete_all: + ped_disk_delete_all (disk); +error: + free (buf); + return 0; +} + +#ifndef DISCOVER_ONLY +/* The Ghost partition: is a blank entry, used to pad out each block (where + * there physical block size > 512 bytes). This is because OpenFirmware uses + * 512 byte blocks, but device drivers Think Different TM, with a different + * lbock size, so we need to do this to avoid a clash (!) + */ +static int +_pad_raw_part (PedDisk* disk, int num, MacRawPartition* part_map) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + int i; + + size_t ss = disk->dev->sector_size; + void *buf = ped_calloc (ss); + if (!buf) + return 0; + + MacRawPartition *ghost_entry = buf; + ghost_entry->signature = PED_CPU_TO_BE16 (MAC_PARTITION_MAGIC_2); + strcpy (ghost_entry->type, "Apple_Void"); + ghost_entry->map_count + = PED_CPU_TO_BE32 (mac_disk_data->last_part_entry_num); + + for (i=0; i < mac_disk_data->ghost_size - 1; i++) { + PedSector idx = i + (num - 1) * mac_disk_data->ghost_size; + memcpy ((char*)part_map + idx * ss, ghost_entry, ss); + } + + free (buf); + return 1; +} + +static void +_update_driver_count (MacRawPartition* part_map_entry, + MacDiskData *mac_driverdata, const MacDiskData* mac_disk_data) +{ + uint16_t i; + uint32_t start_block, block_count; + + start_block = PED_BE32_TO_CPU(part_map_entry->start_block); + block_count = PED_BE32_TO_CPU(part_map_entry->block_count); + + for (i = 0; i < mac_disk_data->driver_count; i++) { + if (start_block == PED_BE32_TO_CPU(mac_disk_data->driverlist[i].block) && + block_count == PED_BE16_TO_CPU(mac_disk_data->driverlist[i].size)) { + uint16_t count_cur = mac_driverdata->driver_count; + mac_driverdata->driverlist[count_cur].block + = mac_disk_data->driverlist[i].block; + mac_driverdata->driverlist[count_cur].size + = mac_disk_data->driverlist[i].size; + mac_driverdata->driverlist[count_cur].type + = mac_disk_data->driverlist[i].type; + mac_driverdata->driver_count++; + break; + } + } +} + +static MacRawPartition * +get_pme (MacRawPartition const *part_map, PedSector i, PedDisk const *disk) +{ + MacDiskData const *mac_disk_data = disk->disk_specific; + PedSector idx = i * mac_disk_data->ghost_size - 1; + return (MacRawPartition *) ((char*)part_map + + idx * disk->dev->sector_size); +} + +/* Initialize the disk->dev->sector_size bytes of part_map[part->num]. */ +static int +_generate_raw_part (PedDisk* disk, PedPartition* part, + MacRawPartition* part_map, MacDiskData *mac_driverdata) +{ + MacDiskData* mac_disk_data; + MacPartitionData* mac_part_data; + + PED_ASSERT (part->num > 0); + + mac_disk_data = disk->disk_specific; + mac_part_data = part->disk_specific; + + MacRawPartition *part_map_entry = get_pme (part_map, part->num, disk); + memset (part_map_entry, 0, disk->dev->sector_size); + + part_map_entry->signature = PED_CPU_TO_BE16 (MAC_PARTITION_MAGIC_2); + part_map_entry->map_count + = PED_CPU_TO_BE32 (mac_disk_data->last_part_entry_num); + part_map_entry->start_block = PED_CPU_TO_BE32 (part->geom.start); + part_map_entry->block_count = PED_CPU_TO_BE32 (part->geom.length); + strncpy (part_map_entry->name, mac_part_data->volume_name, 31); + part_map_entry->name[31] = '\0'; + strncpy (part_map_entry->type, mac_part_data->system_name, 31); + part_map_entry->type[31] = '\0'; + + if (mac_part_data->is_driver) { + if (mac_part_data->has_driver) + _update_driver_count(part_map_entry, mac_driverdata, + mac_disk_data); + } else + mac_part_data->data_region_length = part->geom.length; + part_map_entry->data_count = PED_CPU_TO_BE32 ( + mac_part_data->data_region_length); + part_map_entry->boot_count = PED_CPU_TO_BE32 ( + mac_part_data->boot_region_length); + part_map_entry->status = PED_CPU_TO_BE32 (mac_part_data->status); + part_map_entry->driver_sig + = PED_CPU_TO_BE32 (mac_part_data->driver_sig); + + part_map_entry->boot_load = + PED_CPU_TO_BE32 (mac_part_data->boot_base_address); + part_map_entry->boot_entry = + PED_CPU_TO_BE32 (mac_part_data->boot_entry_address); + part_map_entry->boot_cksum = + PED_CPU_TO_BE32 (mac_part_data->boot_checksum); + + strncpy (part_map_entry->processor, mac_part_data->processor_name, 15); + part_map_entry->processor[15] = '\0'; + + if (!_pad_raw_part (disk, part->num, part_map)) + goto error; + + return 1; + +error: + return 0; +} + +static int +_generate_raw_freespace_part (PedDisk* disk, PedGeometry* geom, int num, + MacRawPartition* part_map) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + + PED_ASSERT (num > 0); + + MacRawPartition *part_map_entry = get_pme (part_map, num, disk); + + part_map_entry->signature = PED_CPU_TO_BE16 (MAC_PARTITION_MAGIC_2); + part_map_entry->map_count + = PED_CPU_TO_BE32 (mac_disk_data->last_part_entry_num); + part_map_entry->start_block = PED_CPU_TO_BE32 (geom->start); + part_map_entry->block_count = PED_CPU_TO_BE32 (geom->length); + strcpy (part_map_entry->name, "Extra"); + strcpy (part_map_entry->type, "Apple_Free"); + + part_map_entry->data_count = PED_CPU_TO_BE32 (geom->length); + part_map_entry->status = 0; + part_map_entry->driver_sig = 0; + + if (!_pad_raw_part (disk, num, part_map)) + goto error; + + return 1; + +error: + return 0; +} + +static int +_generate_empty_part (PedDisk* disk, int num, MacRawPartition* part_map) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + + PED_ASSERT (num > 0); + + MacRawPartition *part_map_entry = get_pme (part_map, num, disk); + part_map_entry->signature = PED_CPU_TO_BE16 (MAC_PARTITION_MAGIC_2); + part_map_entry->map_count + = PED_CPU_TO_BE32 (mac_disk_data->last_part_entry_num); + strcpy (part_map_entry->type, "Apple_Void"); + + return _pad_raw_part (disk, num, part_map); +} + +/* returns the first empty entry in the partition map */ +static int _GL_ATTRIBUTE_PURE +_get_first_empty_part_entry (PedDisk* disk, MacRawPartition* part_map) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + int i; + + for (i=1; i <= mac_disk_data->last_part_entry_num; i++) { + MacRawPartition *part_map_entry = get_pme (part_map, i, disk); + if (!part_map_entry->signature) + return i; + } + + return 0; +} + +static int +write_block_zero (PedDisk* disk, MacDiskData* mac_driverdata) +{ + PedDevice* dev = disk->dev; + void *s0; + if (!ptt_read_sector (dev, 0, &s0)) + return 0; + MacRawDisk *raw_disk = (MacRawDisk *) s0; + + raw_disk->signature = PED_CPU_TO_BE16 (MAC_DISK_MAGIC); + raw_disk->block_size = PED_CPU_TO_BE16 (dev->sector_size); + raw_disk->block_count = PED_CPU_TO_BE32 (dev->length); + + raw_disk->driver_count = PED_CPU_TO_BE16(mac_driverdata->driver_count); + memcpy(&raw_disk->driverlist[0], &mac_driverdata->driverlist[0], + sizeof(raw_disk->driverlist)); + + int write_ok = ped_device_write (dev, raw_disk, 0, 1); + free (s0); + return write_ok; +} + +static int +mac_write (PedDisk* disk) +{ + MacRawPartition* part_map; + MacDiskData* mac_disk_data; + MacDiskData* mac_driverdata; /* updated driver list */ + PedPartition* part; + int num; + + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->disk_specific != NULL); + PED_ASSERT (disk->dev != NULL); + PED_ASSERT (!disk->update_mode); + + mac_disk_data = disk->disk_specific; + + if (!ped_disk_get_partition (disk, mac_disk_data->part_map_entry_num)) { + if (!_disk_add_part_map_entry (disk, 1)) + goto error; + } + + mac_driverdata = ped_malloc(sizeof(MacDiskData)); + if (!mac_driverdata) + goto error; + memset (mac_driverdata, 0, sizeof(MacDiskData)); + + size_t pmap_bytes = (mac_disk_data->part_map_entry_count + * mac_disk_data->ghost_size + * disk->dev->sector_size); + part_map = (MacRawPartition*) ped_calloc (pmap_bytes); + if (!part_map) + goto error_free_driverdata; + +/* write (to memory) the "real" partitions */ + for (part = ped_disk_next_partition (disk, NULL); part; + part = ped_disk_next_partition (disk, part)) { + if (!ped_partition_is_active (part)) + continue; + if (!_generate_raw_part (disk, part, part_map, mac_driverdata)) + goto error_free_part_map; + } + +/* write the "free space" partitions */ + for (part = ped_disk_next_partition (disk, NULL); part; + part = ped_disk_next_partition (disk, part)) { + if (part->type != PED_PARTITION_FREESPACE) + continue; + num = _get_first_empty_part_entry (disk, part_map); + if (!_generate_raw_freespace_part (disk, &part->geom, num, + part_map)) + goto error_free_part_map; + } + +/* write the "void" (empty) partitions */ + for (num = _get_first_empty_part_entry (disk, part_map); num; + num = _get_first_empty_part_entry (disk, part_map)) + _generate_empty_part (disk, num, part_map); + +/* write to disk */ + if (!ped_device_write (disk->dev, part_map, 1, + mac_disk_data->part_map_entry_count)) + goto error_free_part_map; + free (part_map); + int write_ok = write_block_zero (disk, mac_driverdata); + free (mac_driverdata); + return write_ok; + +error_free_part_map: + free (part_map); +error_free_driverdata: + free (mac_driverdata); +error: + return 0; +} +#endif /* !DISCOVER_ONLY */ + +static PedPartition* +mac_partition_new ( + const PedDisk* disk, PedPartitionType part_type, + const PedFileSystemType* fs_type, PedSector start, PedSector end) +{ + PedPartition* part; + MacPartitionData* mac_data; + + part = _ped_partition_alloc (disk, part_type, fs_type, start, end); + if (!part) + goto error; + + if (ped_partition_is_active (part)) { + part->disk_specific + = mac_data = ped_malloc (sizeof (MacPartitionData)); + if (!mac_data) + goto error_free_part; + + memset (mac_data, 0, sizeof (MacPartitionData)); + strcpy (mac_data->volume_name, "untitled"); + } else { + part->disk_specific = NULL; + } + return part; + +error_free_part: + free (part); +error: + return NULL; +} + +static PedPartition* +mac_partition_duplicate (const PedPartition* part) +{ + PedPartition* new_part; + MacPartitionData* new_mac_data; + MacPartitionData* old_mac_data; + + new_part = ped_partition_new (part->disk, part->type, + part->fs_type, part->geom.start, + part->geom.end); + if (!new_part) + return NULL; + new_part->num = part->num; + + old_mac_data = (MacPartitionData*) part->disk_specific; + new_mac_data = (MacPartitionData*) new_part->disk_specific; + + /* ugly, but C is ugly :p */ + memcpy (new_mac_data, old_mac_data, sizeof (MacPartitionData)); + return new_part; +} + +static void +mac_partition_destroy (PedPartition* part) +{ + PED_ASSERT (part != NULL); + + if (ped_partition_is_active (part)) + free (part->disk_specific); + free (part); +} + +static int +mac_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type) +{ + MacPartitionData* mac_data = part->disk_specific; + + part->fs_type = fs_type; + + if (fs_type && is_linux_swap (fs_type->name)) + ped_partition_set_flag (part, PED_PARTITION_SWAP, 1); + + if (mac_data->is_boot) { + strcpy (mac_data->system_name, "Apple_Bootstrap"); + mac_data->status = 0x33; + return 1; + } + + if (fs_type && (!strcmp (fs_type->name, "hfs") + || !strcmp (fs_type->name, "hfs+"))) { + strcpy (mac_data->system_name, "Apple_HFS"); + mac_data->status |= 0x7f; + } else if (fs_type && !strcmp (fs_type->name, "hfsx")) { + strcpy (mac_data->system_name, "Apple_HFSX"); + mac_data->status |= 0x7f; + } else { + strcpy (mac_data->system_name, "Apple_UNIX_SVR2"); + mac_data->status = 0x33; + } + + return 1; +} + +static int +mac_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state) +{ + MacPartitionData* mac_data; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + + mac_data = part->disk_specific; + + switch (flag) { + case PED_PARTITION_BOOT: + mac_data->is_boot = state; + + if (part->fs_type) + return mac_partition_set_system (part, part->fs_type); + + if (state) { + strcpy (mac_data->system_name, "Apple_Bootstrap"); + mac_data->status = 0x33; + } + return 1; + + case PED_PARTITION_ROOT: + if (state) { + strcpy (mac_data->volume_name, "root"); + mac_data->is_swap = 0; + } else { + if (mac_data->is_root) + strcpy (mac_data->volume_name, "untitled"); + } + mac_data->is_root = state; + return 1; + + case PED_PARTITION_SWAP: + if (state) { + strcpy (mac_data->volume_name, "swap"); + mac_data->is_root = 0; + } else { + if (mac_data->is_swap) + strcpy (mac_data->volume_name, "untitled"); + } + mac_data->is_swap = state; + return 1; + + case PED_PARTITION_LVM: + if (state) { + strcpy (mac_data->system_name, "Linux_LVM"); + mac_data->is_lvm = state; + } else { + if (mac_data->is_lvm) + mac_partition_set_system (part, part->fs_type); + } + return 1; + + case PED_PARTITION_RAID: + if (state) { + strcpy (mac_data->system_name, "Linux_RAID"); + mac_data->is_raid = state; + } else { + if (mac_data->is_raid) + mac_partition_set_system (part, part->fs_type); + } + return 1; + + default: + return 0; + } +} + +static int _GL_ATTRIBUTE_PURE +mac_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) +{ + MacPartitionData* mac_data; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + + mac_data = part->disk_specific; + switch (flag) { + case PED_PARTITION_BOOT: + return mac_data->is_boot; + + case PED_PARTITION_ROOT: + return mac_data->is_root; + + case PED_PARTITION_SWAP: + return mac_data->is_swap; + + case PED_PARTITION_LVM: + return mac_data->is_lvm; + + case PED_PARTITION_RAID: + return mac_data->is_raid; + + default: + return 0; + } +} + +static int +mac_partition_is_flag_available ( + const PedPartition* part, PedPartitionFlag flag) +{ + switch (flag) { + case PED_PARTITION_BOOT: + case PED_PARTITION_ROOT: + case PED_PARTITION_SWAP: + case PED_PARTITION_LVM: + case PED_PARTITION_RAID: + return 1; + + default: + return 0; + } +} + +static void +mac_partition_set_name (PedPartition* part, const char* name) +{ + MacPartitionData* mac_data; + int i; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + mac_data = part->disk_specific; + +#ifndef DISCOVER_ONLY + if (mac_data->is_root || mac_data->is_swap) { + if (ped_exception_throw ( + PED_EXCEPTION_WARNING, + PED_EXCEPTION_IGNORE_CANCEL, + _("Changing the name of a root or swap partition " + "will prevent Linux from recognising it as such.")) + != PED_EXCEPTION_IGNORE) + return; + mac_data->is_root = mac_data->is_swap = 0; + } +#endif + + strncpy (mac_data->volume_name, name, 32); + mac_data->volume_name [32] = 0; + for (i = strlen (mac_data->volume_name) - 1; + mac_data->volume_name[i] == ' '; i--) + mac_data->volume_name [i] = 0; +} + +static const char* _GL_ATTRIBUTE_PURE +mac_partition_get_name (const PedPartition* part) +{ + MacPartitionData* mac_data; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk_specific != NULL); + mac_data = part->disk_specific; + + return mac_data->volume_name; +} + +static PedAlignment* +mac_get_partition_alignment(const PedDisk *disk) +{ + return ped_alignment_new(0, 1); +} + +static PedConstraint* +_primary_constraint (PedDisk* disk) +{ + PedAlignment start_align; + PedAlignment end_align; + PedGeometry max_geom; + + if (!ped_alignment_init (&start_align, 0, 1)) + return NULL; + if (!ped_alignment_init (&end_align, -1, 1)) + return NULL; + if (!ped_geometry_init (&max_geom, disk->dev, 1, disk->dev->length - 1)) + return NULL; + + return ped_constraint_new (&start_align, &end_align, &max_geom, + &max_geom, 1, disk->dev->length); +} + +static int +mac_partition_align (PedPartition* part, const PedConstraint* constraint) +{ + PED_ASSERT (part != NULL); + + if (_ped_partition_attempt_align (part, constraint, + _primary_constraint (part->disk))) + return 1; + +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Unable to satisfy all constraints on the partition.")); +#endif + return 0; +} + +static int +mac_partition_enumerate (PedPartition* part) +{ + PedDisk* disk; + MacDiskData* mac_disk_data; + int i; + int max_part_count; + + PED_ASSERT (part != NULL); + PED_ASSERT (part->disk != NULL); + + disk = part->disk; + mac_disk_data = (MacDiskData*) disk->disk_specific; + + max_part_count = ped_disk_get_max_primary_partition_count (disk); + + if (part->num > 0 && part->num <= mac_disk_data->part_map_entry_count) + return 1; + + for (i = 1; i <= max_part_count; i++) { + if (!ped_disk_get_partition (disk, i)) { + part->num = i; + return 1; + } + } + +#ifndef DISCOVER_ONLY + ped_exception_throw ( + PED_EXCEPTION_ERROR, + PED_EXCEPTION_CANCEL, + _("Can't add another partition -- the partition map is too " + "small!")); +#endif + + return 0; +} + +static int +_disk_count_partitions (PedDisk* disk) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + PedPartition* part = NULL; + PedPartition* last = NULL; + + PED_ASSERT (disk->update_mode); + + mac_disk_data->active_part_entry_count = 0; + mac_disk_data->free_part_entry_count = 0; + mac_disk_data->last_part_entry_num = 0; + + /* subtle: we only care about free space after the partition map. + * the partition map is an "active" partition, BTW... */ + for (part = ped_disk_next_partition (disk, part); part; + part = ped_disk_next_partition (disk, part)) { + if (!ped_partition_is_active (part)) + continue; + + mac_disk_data->active_part_entry_count++; + if (last && last->geom.end + 1 < part->geom.start) + mac_disk_data->free_part_entry_count++; + mac_disk_data->last_part_entry_num + = PED_MAX (mac_disk_data->last_part_entry_num, + part->num); + + last = part; + } + + if (last && last->geom.end < disk->dev->length - 1) + mac_disk_data->free_part_entry_count++; + + mac_disk_data->last_part_entry_num + = PED_MAX (mac_disk_data->last_part_entry_num, + mac_disk_data->active_part_entry_count + + mac_disk_data->free_part_entry_count); + return 1; +} + +static int +add_metadata_part (PedDisk* disk, PedSector start, PedSector end) +{ + PedPartition* new_part; + PedConstraint* constraint_any = ped_constraint_any (disk->dev); + + PED_ASSERT (disk != NULL); + + new_part = ped_partition_new (disk, PED_PARTITION_METADATA, NULL, + start, end); + if (!new_part) + goto error; + if (!ped_disk_add_partition (disk, new_part, constraint_any)) + goto error_destroy_new_part; + + ped_constraint_destroy (constraint_any); + return 1; + +error_destroy_new_part: + ped_partition_destroy (new_part); +error: + ped_constraint_destroy (constraint_any); + return 0; +} + +static int +mac_alloc_metadata (PedDisk* disk) +{ + PED_ASSERT (disk != NULL); + PED_ASSERT (disk->disk_specific != NULL); + PED_ASSERT (disk->dev != NULL); + + if (!add_metadata_part (disk, 0, 0)) + return 0; + + /* hack: this seems to be a good place, to update the partition map + * entry count, since mac_alloc_metadata() gets called during + * _disk_pop_update_mode() + */ + return _disk_count_partitions (disk); +} + +static int +mac_get_max_primary_partition_count (const PedDisk* disk) +{ + MacDiskData* mac_disk_data = disk->disk_specific; + PedPartition* part_map_partition; + + part_map_partition = ped_disk_get_partition (disk, + mac_disk_data->part_map_entry_num); + + /* HACK: if we haven't found the partition map partition (yet), + * we return this. + */ + if (!part_map_partition) { + mac_disk_data->part_map_entry_num = 0; + return 65536; + } + + /* HACK: since Mac labels need an entry for free-space regions, we + * must allow half plus 1 entries for free-space partitions. I hate + * this, but things get REALLY complicated, otherwise. + * (I'm prepared to complicate things later, but I want to get + * everything working, first) + */ + return mac_disk_data->part_map_entry_count / mac_disk_data->ghost_size + - mac_disk_data->free_part_entry_count + 1; +} + +static bool +mac_get_max_supported_partition_count (const PedDisk* disk, int *max_n) +{ + *max_n = 65536; + return true; +} + +#include "pt-common.h" +PT_define_limit_functions (mac) + +static PedDiskOps mac_disk_ops = { + clobber: NULL, + /* FIXME: remove this cast, once mac_write is fixed not to + modify its *DISK parameter. */ + write: NULL_IF_DISCOVER_ONLY ((int (*) (const PedDisk*)) mac_write), + + partition_set_name: mac_partition_set_name, + partition_get_name: mac_partition_get_name, + + get_partition_alignment: mac_get_partition_alignment, + + PT_op_function_initializers (mac) +}; + +static PedDiskType mac_disk_type = { + next: NULL, + name: "mac", + ops: &mac_disk_ops, + features: PED_DISK_TYPE_PARTITION_NAME +}; + +void +ped_disk_mac_init () +{ + PED_ASSERT (sizeof (MacRawPartition) == 512); + PED_ASSERT (sizeof (MacRawDisk) == 512); + + ped_disk_type_register (&mac_disk_type); +} + +void +ped_disk_mac_done () +{ + ped_disk_type_unregister (&mac_disk_type); +} |