/*
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 .
*/
#include
#include
#include
#include
#include
#if ENABLE_NLS
# include
# 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);
}