Logo Search packages:      
Sourcecode: parted version File versions

disk_mac.c

/*
    libparted - a library for manipulating disk partitions
    Copyright (C) 2000, 2002 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 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "config.h"

#include <parted/parted.h>
#include <parted/debug.h>
#include <parted/endian.h>
#include <string.h>

#if ENABLE_NLS
#  include <libintl.h>
#  define _(String) dgettext (PACKAGE, String)
#else
#  define _(String) (String)
#endif /* ENABLE_NLS */

/* 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/techpubs/mac/Devices/Devices-126.html
 *    http://developer.apple.com/techpubs/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
 *    Apple_UNIX_SVR2         UNIX file system (UFS?)
 *    Apple_PRODOS            ProDOS file system 
 *    Apple_Free        unused space
 *    Apple_Scratch           empty
 *    Apple_Void        an unused partition map entry
 *    Apple_Extra       an unused partition map entry (AFAICT)
 *
 * 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 {
      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];
} __attribute__ ((packed)) MacRawPartition;

/* Driver descriptor structure, in block 0 */
typedef struct {
      uint16_t    signature;      /* expected to be MAC_DRIVER_MAGIC */
      uint16_t    block_size; /* usually 512  (occassionaly 2048?) */
      uint32_t    block_count;      /* size of device in blocks */
      uint16_t    dev_type;   /* unused? */
      uint16_t    dev_id;           /* unused? */
      uint32_t    data;       /* unused? */
      uint16_t    driver_count;     /* # of driver descriptor entries */
      uint32_t    start;            /* first driver's starting block */
      uint16_t    size;       /* size of driver in blocks */
      uint16_t    os_type;          /* operating system type (MacOS = 1) */
      char        reserved[486];    /* space reserved for the driver */
} __attribute__ ((packed)) MacRawDisk;

typedef 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         is_root;
      int         is_swap;

      PedSector   data_region_length;
      PedSector   boot_region_length;

      long long   boot_base_address;
      long long   boot_entry_address;
      int         boot_checksum;

      uint32_t    status;
      uint32_t    driver_sig;
} MacPartitionData;

typedef struct {
      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 */
} MacDiskData;

static PedDiskType mac_disk_type;

static int
_check_signature (MacRawDisk* 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 (PedDevice * dev)
{
      MacRawDisk  buf;

      PED_ASSERT (dev != NULL, return 0);

      if (!ped_device_read (dev, &buf, 0, 1))
            return 0;
      return _check_signature (&buf);
}

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);

      new_part = ped_partition_new (disk, 0, 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;
}

PedDisk*
mac_alloc (PedDevice* dev)
{
      PedDisk*          disk;
      MacDiskData*            mac_disk_data;

      PED_ASSERT (dev != NULL, return 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 = disk->dev->sector_size / 512;
      mac_disk_data->active_part_entry_count = 0;
      mac_disk_data->free_part_entry_count = 1;
      mac_disk_data->last_part_entry_num = 1;

      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;
      PedPartition*     partition_map;
       
      new_disk = ped_disk_new_fresh (disk->dev, &mac_disk_type);
      if (!new_disk)
            goto error;

      new_mac_data = (MacDiskData*) new_disk->disk_specific;

      /* remove the partition map partition - it will be duplicated
       * later.
       */
      partition_map = ped_disk_get_partition_by_sector (new_disk, 1);
      PED_ASSERT (partition_map != NULL, return 0);
      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;

error_free_new_disk:
      _ped_disk_free (new_disk);
error:
      return NULL;
}

static void
mac_free (PedDisk* disk)
{
      MacDiskData*      mac_disk_data = disk->disk_specific;

      _ped_disk_free (disk);
      ped_free (mac_disk_data);
}

#ifndef DISCOVER_ONLY
static int
_clobber_part_map (PedDevice* dev)
{
      MacRawPartition         raw_part;
      PedSector         sector;

      for (sector=1; 1; sector++) {
            if (!ped_device_read (dev, &raw_part, sector, 1))
                  return 0;
            if (!_rawpart_check_signature (&raw_part))
                  return 1;
            memset (&raw_part, 0, 512);
            if (!ped_device_write (dev, &raw_part, sector, 1))
                  return 0;
      }
}

static int
mac_clobber (PedDevice* dev)
{
      MacRawDisk        raw_disk;

      if (!ped_device_read (dev, &raw_disk, 0, 1))
            return 0;
      if (!_check_signature (&raw_disk))
            return 0;
      memset (&raw_disk, 0, 512);
      if (!ped_device_write (dev, &raw_disk, 0, 1))
            return 0;

      return _clobber_part_map (dev);
}
#endif /* !DISCOVER_ONLY */

static int
_rawpart_cmp_type (MacRawPartition* raw_part, char* type)
{
      return strcasecmp (raw_part->type, type) == 0;
}

static int
_rawpart_is_partition_map (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; needle[i] && i < n - needle_size; i++) {
            if (strcasecmp (haystack + i, needle) == 0)
                  return 1;
      }

      return 0;
}

static int
_rawpart_is_boot (MacRawPartition* raw_part)
{
      return !strcasecmp (raw_part->type, "Apple_Bootstrap");
}

static int
_rawpart_is_driver (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
_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_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", it is treated
 * as a "real" partition.
 */
static int
_rawpart_is_active (MacRawPartition* raw_part)
{
      if (_rawpart_cmp_type (raw_part, "Apple_Void"))
            return 0;
      if (_rawpart_cmp_type (raw_part, "Apple_Free"))
            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         block_size;
      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;
      block_size = disk->dev->sector_size / 512;

      start = PED_BE32_TO_CPU (raw_part->start_block) * block_size;
      length = PED_BE32_TO_CPU (raw_part->block_count) * block_size;
      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, 0, 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);
      mac_part_data->is_root = _rawpart_is_root (raw_part);
      mac_part_data->is_swap = _rawpart_is_swap (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) * block_size;

      /* boot region - we have no idea what this is for, but Mac OSX
       * seems to put garbage here, and doesn't pay any attention to
       * it afterwards.  [clausen, dan burcaw]
       */
#if 0
      if (raw_part->boot_start) {
            ped_exception_throw (
                  PED_EXCEPTION_ERROR,
                  PED_EXCEPTION_CANCEL,
                  _("The boot region doesn't start at the start "
                    "of the partition"));
            goto error_destroy_part;
      }
#endif
      mac_part_data->boot_region_length
            = PED_BE32_TO_CPU (raw_part->boot_count) * block_size;

#ifndef DISCOVER_ONLY
      if (mac_part_data->is_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) {
                  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 */

      /* FIXME: should this add the high 32 bits (64 bit Macs?) */
      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   sector_size = disk->dev->sector_size / 512;
      PedSector   part_map_start;
      PedSector   part_map_end;

      part_map_start = mac_disk_data->ghost_size;
      part_map_end = sector_size * 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;
      MacRawPartition         raw_part;
      int               i;

      for (i = 1; i < 64; i *= 2) {
            if (!ped_device_read (disk->dev, &raw_part, i, 1))
                  return 0;
            if (_rawpart_check_signature (&raw_part)
                && !_rawpart_is_void (&raw_part)) {
                  mac_disk_data->ghost_size = i;
                  PED_ASSERT (i <= disk->dev->sector_size / 512,
                            return 0);
                  return 1;
            }
      }

#ifndef DISCOVER_ONLY
      ped_exception_throw (
            PED_EXCEPTION_ERROR,
            PED_EXCEPTION_CANCEL,
            _("No valid partition map found."));
#endif
      return 0;
}

static int
mac_read (PedDisk* disk)
{
      MacRawDisk        raw_disk;
      MacRawPartition         raw_part;
      MacDiskData*            mac_disk_data;
      PedPartition*           part;
      int               num;
      PedSector         ghost_size;
      PedConstraint*          constraint_exact;
      int               last_part_entry_num = 0;

      PED_ASSERT (disk != NULL, return 0);

      mac_disk_data = disk->disk_specific;
      mac_disk_data->part_map_entry_num = 0;          /* 0 == none */

      if (!ped_device_read (disk->dev, &raw_disk, 0, 1))
            goto error;
      if (!_check_signature (&raw_disk))
            goto error;

      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;

      for (num=1; num==1 || num <= last_part_entry_num; num++) {
            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's %d, but entry %d says its "
                          "%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);
            constraint_exact = ped_constraint_exact (&part->geom);
            if (!ped_disk_add_partition (disk, part, constraint_exact))
                  goto error_delete_all;
            ped_constraint_destroy (constraint_exact);

            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 - 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);
      }
      return 1;

error_delete_all:
      ped_disk_delete_all (disk);
error:
      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;
      MacRawPartition         ghost_entry;
      int               i;

      memset (&ghost_entry, 0, sizeof (ghost_entry));
      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++)
            memcpy (&part_map [i + (num - 1) * mac_disk_data->ghost_size],
                  &ghost_entry, sizeof (MacRawPartition));

      return 1;
}

static int
_generate_raw_part (PedDisk* disk, PedPartition* part,
                      MacRawPartition* part_map)
{
      MacDiskData*            mac_disk_data;
      MacPartitionData* mac_part_data;
      MacRawPartition*  part_map_entry;
      PedSector         block_size = disk->dev->sector_size / 512;

      PED_ASSERT (part->num > 0, goto error);

      mac_disk_data = disk->disk_specific;
      mac_part_data = part->disk_specific;

      part_map_entry = &part_map [part->num * mac_disk_data->ghost_size - 1];

      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 / block_size);
      part_map_entry->block_count
            = PED_CPU_TO_BE32 (part->geom.length / block_size);
      strcpy (part_map_entry->name, mac_part_data->volume_name);
      strcpy (part_map_entry->type, mac_part_data->system_name);

      if (mac_part_data->is_driver)
            mac_part_data->boot_region_length = part->geom.length;
      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 / block_size);
      part_map_entry->boot_count = PED_CPU_TO_BE32 (
                  mac_part_data->boot_region_length / block_size);
      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, 16);

      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;
      MacRawPartition*  part_map_entry;
      PedSector         block_size = disk->dev->sector_size / 512;

      PED_ASSERT (num > 0, goto error);

      part_map_entry = &part_map [num * mac_disk_data->ghost_size - 1];

      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 / block_size);
      part_map_entry->block_count
            = PED_CPU_TO_BE32 (geom->length / block_size);
      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;
      MacRawPartition*  part_map_entry;

      PED_ASSERT (num > 0, return 0);

      part_map_entry = &part_map [num * mac_disk_data->ghost_size - 1];
      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
_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++) {
            if (!part_map[i * mac_disk_data->ghost_size - 1].signature)
                  return i;
      }

      return 0;
}

static int
write_block_zero (PedDisk* disk)
{
      PedDevice*  dev = disk->dev;
      MacRawDisk  raw_disk;

      if (!ped_device_read (dev, &raw_disk, 0, 1))
            return 0;

      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 / (dev->sector_size / 512));

      return ped_device_write (dev, &raw_disk, 0, 1);
}

static int
mac_write (PedDisk* disk)
{
      MacRawPartition*  part_map;
      MacDiskData*            mac_disk_data;
      PedPartition*           part;
      int               num;

      PED_ASSERT (disk != NULL, return 0);
      PED_ASSERT (disk->disk_specific != NULL, return 0);
      PED_ASSERT (disk->dev != NULL, return 0);
      PED_ASSERT (!disk->update_mode, return 0);

      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;
      }

      part_map = (MacRawPartition*)
                  ped_malloc (mac_disk_data->part_map_entry_count * 512);
      if (!part_map)
            goto error;
      memset (part_map, 0, mac_disk_data->part_map_entry_count * 512);

/* 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))
                  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;
      ped_free (part_map);
      return write_block_zero (disk);

error_free_part_map:
      ped_free (part_map);
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));

            mac_data->data_region_length = 0;
            mac_data->boot_region_length = 0;
            mac_data->is_driver = 0;
            mac_data->is_boot = 0;
            mac_data->is_root = 0;
            mac_data->is_swap = 0;

            strcpy (mac_data->volume_name, "untitled");
      } else {
            part->disk_specific = NULL;
      }
      return part;

error_free_mac_data:
      ped_free (mac_data);
error_free_part:
      ped_free (part);
error:
      return 0;
}

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, return);

      if (ped_partition_is_active (part))
            ped_free (part->disk_specific);
      ped_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 && !strcmp (fs_type->name, "linux-swap"))
            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")) {
            strcpy (mac_data->system_name, "Apple_HFS");
            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)
{
      PedFileSystemType*      hfs = ped_file_system_type_get ("hfs");
      MacPartitionData* mac_data;

      PED_ASSERT (part != NULL, return 0);
      PED_ASSERT (part->disk_specific != NULL, return 0);

      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);
                  
            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;

      default:
            return 0;
      }
}

static int
mac_partition_get_flag (const PedPartition* part, PedPartitionFlag flag)
{
      MacPartitionData* mac_data;

      PED_ASSERT (part != NULL, return 0);
      PED_ASSERT (part->disk_specific != NULL, return 0);

      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;

      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:
            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, return);
      PED_ASSERT (part->disk_specific != NULL, return);
      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*
mac_partition_get_name (const PedPartition* part)
{
      MacPartitionData* mac_data;

      PED_ASSERT (part != NULL, return NULL);
      PED_ASSERT (part->disk_specific != NULL, return NULL);
      mac_data = part->disk_specific;

      return mac_data->volume_name;
}

static PedConstraint*
_primary_constraint (PedDisk* disk)
{
      PedAlignment      start_align;
      PedAlignment      end_align;
      PedGeometry max_geom;
      PedSector   sector_size;

      sector_size = disk->dev->sector_size / 512;

      if (!ped_alignment_init (&start_align, 0, sector_size))
            return NULL;
      if (!ped_alignment_init (&end_align, -1, sector_size))
            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, return 0);

      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, return 0);
      PED_ASSERT (part->disk != NULL, return 0);

      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 - 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, return 0);

      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, return 0);

      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)
{
      MacDiskData*            mac_disk_data;

      PED_ASSERT (disk != NULL, return 0);
      PED_ASSERT (disk->disk_specific != NULL, return 0);
      PED_ASSERT (disk->dev != NULL, return 0);

      mac_disk_data = disk->disk_specific;

      if (!add_metadata_part (disk, 0, disk->dev->sector_size / 512 - 1))
            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 PedDiskOps mac_disk_ops = {
      probe:                  mac_probe,
#ifndef DISCOVER_ONLY
      clobber:          mac_clobber,
#else
      clobber:          NULL,
#endif
      alloc:                  mac_alloc,
      duplicate:        mac_duplicate,
      free:             mac_free,
      read:             mac_read,
#ifndef DISCOVER_ONLY
      write:                  mac_write,
#else
      write:                  NULL,
#endif

      partition_new:          mac_partition_new,
      partition_duplicate:    mac_partition_duplicate,
      partition_destroy:      mac_partition_destroy,
      partition_set_system:   mac_partition_set_system,
      partition_set_flag:     mac_partition_set_flag,
      partition_get_flag:     mac_partition_get_flag,
      partition_is_flag_available:  mac_partition_is_flag_available,
      partition_set_name:     mac_partition_set_name,
      partition_get_name:     mac_partition_get_name,
      partition_align:  mac_partition_align,
      partition_enumerate:    mac_partition_enumerate,

      alloc_metadata:         mac_alloc_metadata,
      get_max_primary_partition_count:
                        mac_get_max_primary_partition_count
};

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, return);
      PED_ASSERT (sizeof (MacRawDisk) == 512, return);

      ped_register_disk_type (&mac_disk_type);
}

void
ped_disk_mac_done ()
{
      ped_unregister_disk_type (&mac_disk_type);
}


Generated by  Doxygen 1.6.0   Back to index