Logo Search packages:      
Sourcecode: parted version File versions

disk_amiga.c

/* -*- Mode: c; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-

    libparted - a library for manipulating disk partitions
    disk_amiga.c - libparted module to manipulate amiga RDB partition tables.
    Copyright (C) 2000, 2001, 2004 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

    Contributor:  Sven Luther <luther@debian.org>
*/

#include "config.h"

#include <string.h>

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

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

/* String manipulation */
static void _amiga_set_bstr (const char *cstr, char *bstr, int maxsize) {
      int size = strlen (cstr);
      int i;

      if (size >= maxsize) return;
      bstr[0] = size;
      for (i = 0; i<size; i++) bstr[i+1] = cstr[i];
}
static const char * _amiga_get_bstr (char * bstr) {
      char * cstr = bstr + 1;
      int size = bstr[0];
      
      cstr[size] = '\0';
      return cstr;
}

#define     IDNAME_RIGIDDISK  (uint32_t)0x5244534B    /* 'RDSK' */
#define IDNAME_BADBLOCK       (uint32_t)0x42414442    /* 'BADB' */
#define     IDNAME_PARTITION  (uint32_t)0x50415254    /* 'PART' */
#define IDNAME_FILESYSHEADER  (uint32_t)0x46534844    /* 'FSHD' */
#define IDNAME_LOADSEG        (uint32_t)0x4C534547    /* 'LSEG' */
#define IDNAME_BOOT           (uint32_t)0x424f4f54    /* 'BOOT' */
#define IDNAME_FREE           (uint32_t)0xffffffff    

static const char *
_amiga_block_id (uint32_t id) {
      switch (id) {
            case IDNAME_RIGIDDISK :
                  return "RDSK";
            case IDNAME_BADBLOCK :
                  return "BADB";
            case IDNAME_PARTITION :
                  return "PART";
            case IDNAME_FILESYSHEADER :
                  return "FSHD";
            case IDNAME_LOADSEG :
                  return "LSEG";
            case IDNAME_BOOT :
                  return "BOOT";
            case IDNAME_FREE :
                  return "<free>";
            default :
                  return "<unknown>";
      }
}
static int
_amiga_valid_block_id (uint32_t id) {
      switch (id) {
            case IDNAME_RIGIDDISK :
            case IDNAME_BADBLOCK :
            case IDNAME_PARTITION :
            case IDNAME_FILESYSHEADER :
            case IDNAME_LOADSEG :
            case IDNAME_BOOT :
                  return 1;
            case IDNAME_FREE :
            default :
                  return 0;
      }
}

struct AmigaIds {
      uint32_t ID;
      struct AmigaIds *next;
};

static struct AmigaIds *
_amiga_add_id (uint32_t id, struct AmigaIds *ids) {
      struct AmigaIds *newid;

      if ((newid=ped_malloc(sizeof (struct AmigaIds)))==NULL)
            return 0;
      newid->ID = id;
      newid->next = ids;
      return newid;
}

static void
_amiga_free_ids (struct AmigaIds *ids) {
      struct AmigaIds *current, *next;

      for (current = ids; current != NULL; current = next) {
            next = current->next;
            ped_free (current);
      }
}
static int
_amiga_id_in_list (uint32_t id, struct AmigaIds *ids) {
      struct AmigaIds *current;

      for (current = ids; current != NULL; current = current->next) {
            if (id == current->ID)
                  return 1;
      }
      return 0;
}

struct AmigaBlock {
      uint32_t    amiga_ID;         /* Identifier 32 bit word */
      uint32_t    amiga_SummedLongss;     /* Size of the structure for checksums */
      int32_t           amiga_ChkSum;           /* Checksum of the structure */
};
#define AMIGA(pos) ((struct AmigaBlock *)(pos)) 

static int
_amiga_checksum (struct AmigaBlock *blk) {
      uint32_t *rdb = (uint32_t *) blk;
      uint32_t sum;
      int i, end;

      sum = PED_BE32_TO_CPU (rdb[0]);
      end = PED_BE32_TO_CPU (rdb[1]);

      if (end > PED_SECTOR_SIZE) end = PED_SECTOR_SIZE;

      for (i = 1; i < end; i++) sum += PED_BE32_TO_CPU (rdb[i]);

      return sum;
}

static void
_amiga_calculate_checksum (struct AmigaBlock *blk) {
      blk->amiga_ChkSum = PED_CPU_TO_BE32(
            PED_BE32_TO_CPU(blk->amiga_ChkSum) -
            _amiga_checksum((struct AmigaBlock *) blk));
      return;     
}

static struct AmigaBlock *
_amiga_read_block (PedDevice *dev, struct AmigaBlock *blk, PedSector block, struct AmigaIds *ids) {
      if (!ped_device_read (dev, blk, block, 1))
            return NULL;
      if (ids && !_amiga_id_in_list(PED_BE32_TO_CPU(blk->amiga_ID), ids))
            return NULL;
      if (_amiga_checksum (blk) != 0) {
            switch (ped_exception_throw(PED_EXCEPTION_ERROR,
                  PED_EXCEPTION_FIX | PED_EXCEPTION_IGNORE | PED_EXCEPTION_CANCEL,
                  _("%s : Bad checksum on block %llu of type %s."),
                  __func__, block, _amiga_block_id(PED_BE32_TO_CPU(blk->amiga_ID))))
            {
                  case PED_EXCEPTION_CANCEL :
                        return NULL;
                  case PED_EXCEPTION_FIX :
                        _amiga_calculate_checksum(AMIGA(blk));
                        if (!ped_device_write (dev, blk, block, 1))
                              return NULL;
                  case PED_EXCEPTION_IGNORE :
                  case PED_EXCEPTION_UNHANDLED :
                  default : 
                        return blk;
            }
      }
      return blk;
}

struct RigidDiskBlock {
    uint32_t      rdb_ID;                 /* Identifier 32 bit word : 'RDSK' */
    uint32_t      rdb_SummedLongs;  /* Size of the structure for checksums */
    int32_t rdb_ChkSum;       /* Checksum of the structure */
    uint32_t      rdb_HostID;       /* SCSI Target ID of host, not really used */
    uint32_t      rdb_BlockBytes;         /* Size of disk blocks */
    uint32_t      rdb_Flags;        /* RDB Flags */
    /* block list heads */
    uint32_t      rdb_BadBlockList; /* Bad block list */
    uint32_t      rdb_PartitionList;      /* Partition list */
    uint32_t      rdb_FileSysHeaderList;  /* File system header list */
    uint32_t      rdb_DriveInit;          /* Drive specific init code */
    uint32_t      rdb_BootBlockList;      /* Amiga OS 4 Boot Blocks */
    uint32_t      rdb_Reserved1[5]; /* Unused word, need to be set to $ffffffff */
    /* physical drive characteristics */
    uint32_t      rdb_Cylinders;          /* Number of the cylinders of the drive */
    uint32_t      rdb_Sectors;            /* Number of sectors of the drive */
    uint32_t      rdb_Heads;        /* Number of heads of the drive */
    uint32_t      rdb_Interleave;         /* Interleave */
    uint32_t      rdb_Park;         /* Head parking cylinder */
    uint32_t      rdb_Reserved2[3]; /* Unused word, need to be set to $ffffffff */
    uint32_t      rdb_WritePreComp; /* Starting cylinder of write precompensation */
    uint32_t      rdb_ReducedWrite; /* Starting cylinder of reduced write current */
    uint32_t      rdb_StepRate;           /* Step rate of the drive */
    uint32_t      rdb_Reserved3[5]; /* Unused word, need to be set to $ffffffff */
    /* logical drive characteristics */
    uint32_t      rdb_RDBBlocksLo;  /* low block of range reserved for hardblocks */
    uint32_t      rdb_RDBBlocksHi;  /* high block of range for these hardblocks */
    uint32_t      rdb_LoCylinder;         /* low cylinder of partitionable disk area */
    uint32_t      rdb_HiCylinder;         /* high cylinder of partitionable data area */
    uint32_t      rdb_CylBlocks;          /* number of blocks available per cylinder */
    uint32_t      rdb_AutoParkSeconds;    /* zero for no auto park */
    uint32_t      rdb_HighRDSKBlock;      /* highest block used by RDSK */
                              /* (not including replacement bad blocks) */
    uint32_t      rdb_Reserved4;
    /* drive identification */
    char    rdb_DiskVendor[8];
    char    rdb_DiskProduct[16];
    char    rdb_DiskRevision[4];
    char    rdb_ControllerVendor[8];
    char    rdb_ControllerProduct[16];
    char    rdb_ControllerRevision[4];
    uint32_t      rdb_Reserved5[10];
};

#define RDSK(pos) ((struct RigidDiskBlock *)(pos)) 

#define AMIGA_RDB_NOT_FOUND ((uint32_t)0xffffffff)
#define     RDB_LOCATION_LIMIT      16
#define AMIGA_MAX_PARTITIONS 128
#define MAX_RDB_BLOCK (RDB_LOCATION_LIMIT + 2 * AMIGA_MAX_PARTITIONS + 2)

static uint32_t
_amiga_find_rdb (PedDevice *dev, struct RigidDiskBlock *rdb) {
      int i;
      struct AmigaIds *ids;

      ids = _amiga_add_id (IDNAME_RIGIDDISK, NULL);

      for (i = 0; i<RDB_LOCATION_LIMIT; i++) {
            if (!_amiga_read_block (dev, AMIGA(rdb), i, ids)) {
                  continue;
            }
            if (PED_BE32_TO_CPU (rdb->rdb_ID) == IDNAME_RIGIDDISK) {
                  _amiga_free_ids (ids);
                  return i;
            }
      }
      _amiga_free_ids (ids);
      return AMIGA_RDB_NOT_FOUND;
}

struct PartitionBlock {
    uint32_t      pb_ID;                  /* Identifier 32 bit word : 'PART' */
    uint32_t      pb_SummedLongs;         /* Size of the structure for checksums */
    int32_t pb_ChkSum;        /* Checksum of the structure */
    uint32_t      pb_HostID;        /* SCSI Target ID of host, not really used */
    uint32_t      pb_Next;          /* Block number of the next PartitionBlock */
    uint32_t      pb_Flags;         /* Part Flags (NOMOUNT and BOOTABLE) */
    uint32_t      pb_Reserved1[2];
    uint32_t      pb_DevFlags;            /* Preferred flags for OpenDevice */
    uint8_t pb_DriveName[32]; /* Preferred DOS device name: BSTR form */
    uint32_t      pb_Reserved2[15];
    uint32_t      de_TableSize;           /* Size of Environment vector */
    uint32_t      de_SizeBlock;           /* Size of the blocks in 32 bit words, usually 128 */
    uint32_t      de_SecOrg;        /* Not used; must be 0 */
    uint32_t      de_Surfaces;            /* Number of heads (surfaces) */
    uint32_t      de_SectorPerBlock;      /* Disk sectors per block, used with SizeBlock, usually 1 */
    uint32_t      de_BlocksPerTrack;      /* Blocks per track. drive specific */
    uint32_t      de_Reserved;            /* DOS reserved blocks at start of partition. */
    uint32_t      de_PreAlloc;            /* DOS reserved blocks at end of partition */
    uint32_t      de_Interleave;          /* Not used, usually 0 */
    uint32_t      de_LowCyl;        /* First cylinder of the partition */
    uint32_t      de_HighCyl;       /* Last cylinder of the partition */
    uint32_t      de_NumBuffers;          /* Initial # DOS of buffers.  */
    uint32_t      de_BufMemType;          /* Type of mem to allocate for buffers */
    uint32_t      de_MaxTransfer;         /* Max number of bytes to transfer at a time */
    uint32_t      de_Mask;          /* Address Mask to block out certain memory */
    int32_t de_BootPri;       /* Boot priority for autoboot */
    uint32_t      de_DosType;       /* Dostype of the filesystem */
    uint32_t      de_Baud;          /* Baud rate for serial handler */
    uint32_t      de_Control;       /* Control word for handler/filesystem */
    uint32_t      de_BootBlocks;          /* Number of blocks containing boot code */
    uint32_t      pb_EReserved[12];
};

#define PART(pos) ((struct PartitionBlock *)(pos))

#define     PBFB_BOOTABLE     0     /* this partition is intended to be bootable */
#define     PBFF_BOOTABLE     1L    /*   (expected directories and files exist) */
#define     PBFB_NOMOUNT      1     /* do not mount this partition (e.g. manually */
#define     PBFF_NOMOUNT      2L    /*   mounted, but space reserved here) */
#define     PBFB_RAID   2     /* this partition is intended to be part of */
#define     PBFF_RAID   4L    /*   a RAID array */
#define     PBFB_LVM    3     /* this partition is intended to be part of */
#define     PBFF_LVM    8L    /*   a LVM volume group */


struct LinkedBlock {
    uint32_t      lk_ID;                  /* Identifier 32 bit word */
    uint32_t      lk_SummedLongs;         /* Size of the structure for checksums */
    int32_t lk_ChkSum;        /* Checksum of the structure */
    uint32_t      pb_HostID;        /* SCSI Target ID of host, not really used */
    uint32_t      lk_Next;          /* Block number of the next PartitionBlock */
};
struct Linked2Block {
    uint32_t      lk2_ID;                 /* Identifier 32 bit word */
    uint32_t      lk2_SummedLongs;        /* Size of the structure for checksums */
    int32_t lk2_ChkSum;       /* Checksum of the structure */
    uint32_t      lk2_HostID;       /* SCSI Target ID of host, not really used */
    uint32_t      lk2_Next;         /* Block number of the next PartitionBlock */
    uint32_t      lk2_Reverved[13];
    uint32_t      lk2_Linked;       /* Secondary linked list */
};
#define LINK_END  (uint32_t)0xffffffff
#define LNK(pos)  ((struct LinkedBlock *)(pos))
#define LNK2(pos) ((struct Linked2Block *)(pos))


static PedDiskType amiga_disk_type;

static int
amiga_probe (PedDevice *dev)
{
      struct RigidDiskBlock *rdb;
      uint32_t found;
      PED_ASSERT(dev != NULL, return 0);

      if ((rdb=RDSK(ped_malloc(PED_SECTOR_SIZE)))==NULL)
            return 0;
      found = _amiga_find_rdb (dev, rdb);
      ped_free (rdb);

      return (found == AMIGA_RDB_NOT_FOUND ? 0 : 1);
}
            
static PedDisk*
amiga_alloc (PedDevice* dev)
{
      PedDisk *disk;
      struct AmigaDisk *adsk;
      struct RigidDiskBlock *rdb;
      PedSector cyl_size;
      int highest_cylinder, highest_block;

      PED_ASSERT(dev != NULL, return NULL);
      cyl_size = dev->hw_geom.sectors * dev->hw_geom.heads;

      if (!(disk = _ped_disk_alloc (dev, &amiga_disk_type)))
            return NULL;

      if (!(disk->disk_specific = ped_malloc (PED_SECTOR_SIZE))) {
            ped_free (disk);
            return NULL;
      }
      rdb = disk->disk_specific;

      memset(rdb, 0, sizeof(struct RigidDiskBlock));

      rdb->rdb_ID = PED_CPU_TO_BE32 (IDNAME_RIGIDDISK);
      rdb->rdb_SummedLongs = PED_CPU_TO_BE32 (64);
      rdb->rdb_HostID = PED_CPU_TO_BE32 (0);
      rdb->rdb_BlockBytes = PED_CPU_TO_BE32 (PED_SECTOR_SIZE);
      rdb->rdb_Flags = PED_CPU_TO_BE32 (0);

      /* Block lists */
      rdb->rdb_BadBlockList = PED_CPU_TO_BE32 (LINK_END);
      rdb->rdb_PartitionList = PED_CPU_TO_BE32 (LINK_END);
      rdb->rdb_FileSysHeaderList = PED_CPU_TO_BE32 (LINK_END);
      rdb->rdb_DriveInit = PED_CPU_TO_BE32 (LINK_END);
      rdb->rdb_BootBlockList = PED_CPU_TO_BE32 (LINK_END);
      
      /* Physical drive characteristics */
      rdb->rdb_Cylinders = PED_CPU_TO_BE32 (dev->hw_geom.cylinders);
      rdb->rdb_Sectors = PED_CPU_TO_BE32 (dev->hw_geom.sectors);
      rdb->rdb_Heads = PED_CPU_TO_BE32 (dev->hw_geom.heads);
      rdb->rdb_Interleave = PED_CPU_TO_BE32 (0);
      rdb->rdb_Park = PED_CPU_TO_BE32 (dev->hw_geom.cylinders);
      rdb->rdb_WritePreComp = PED_CPU_TO_BE32 (dev->hw_geom.cylinders);
      rdb->rdb_ReducedWrite = PED_CPU_TO_BE32 (dev->hw_geom.cylinders);
      rdb->rdb_StepRate = PED_CPU_TO_BE32 (0);

      highest_cylinder = 1 + MAX_RDB_BLOCK / cyl_size;
      highest_block = highest_cylinder * cyl_size - 1;

      /* Logical driver characteristics */
      rdb->rdb_RDBBlocksLo = PED_CPU_TO_BE32 (0);
      rdb->rdb_RDBBlocksHi = PED_CPU_TO_BE32 (highest_block);
      rdb->rdb_LoCylinder = PED_CPU_TO_BE32 (highest_cylinder);
      rdb->rdb_HiCylinder = PED_CPU_TO_BE32 (dev->hw_geom.cylinders -1);
      rdb->rdb_CylBlocks = PED_CPU_TO_BE32 (cyl_size);
      rdb->rdb_AutoParkSeconds = PED_CPU_TO_BE32 (0);
      /* rdb_HighRDSKBlock will only be set when writing */
      rdb->rdb_HighRDSKBlock = PED_CPU_TO_BE32 (0);

      /* Driver identification */
      _amiga_set_bstr("", rdb->rdb_DiskVendor, 8);
      _amiga_set_bstr(dev->model, rdb->rdb_DiskProduct, 16);
      _amiga_set_bstr("", rdb->rdb_DiskRevision, 4);
      _amiga_set_bstr("", rdb->rdb_ControllerVendor, 8);
      _amiga_set_bstr("", rdb->rdb_ControllerProduct, 16);
      _amiga_set_bstr("", rdb->rdb_ControllerRevision, 4);

      /* And calculate the checksum */
      _amiga_calculate_checksum ((struct AmigaBlock *) rdb);
      
      return disk;
}

static PedDisk*
amiga_duplicate (const PedDisk* disk)
{
      PedDisk*    new_disk;
      struct RigidDiskBlock * new_rdb;
      struct RigidDiskBlock * old_rdb;
      PED_ASSERT(disk != NULL, return NULL);
      PED_ASSERT(disk->dev != NULL, return NULL);
      PED_ASSERT(disk->disk_specific != NULL, return NULL);

      old_rdb = (struct RigidDiskBlock *) disk->disk_specific;
       
      if (!(new_disk = ped_disk_new_fresh (disk->dev, &amiga_disk_type)))
            return NULL;

      new_rdb = (struct RigidDiskBlock *) new_disk->disk_specific;
      memcpy (new_rdb, old_rdb, 256);
      return new_disk;
}

static void
amiga_free (PedDisk* disk)
{
      PED_ASSERT(disk != NULL, return);
      PED_ASSERT(disk->disk_specific != NULL, return);

      ped_free (disk->disk_specific);
      _ped_disk_free (disk);
}

#ifndef DISCOVER_ONLY
static int
amiga_clobber (PedDevice* dev)
{
      struct RigidDiskBlock *rdb;
      uint32_t i;
      int result = 0;
      PED_ASSERT(dev != NULL, return 0);

      if ((rdb=RDSK(ped_malloc(PED_SECTOR_SIZE)))==NULL)
            return 0;

      while ((i = _amiga_find_rdb (dev, rdb)) != AMIGA_RDB_NOT_FOUND) {
            rdb->rdb_ID = PED_CPU_TO_BE32 (0);
            result = ped_device_write (dev, (void*) rdb, i, 1);
      }

      ped_free (rdb);

      return result;
}
#endif /* !DISCOVER_ONLY */

static int
_amiga_loop_check (uint32_t block, uint32_t * blocklist, uint32_t max)
{
      uint32_t i;

      for (i = 0; i < max; i++)
            if (block == blocklist[i]) {
                  /* We are looping, let's stop.  */
                  return 1;
            }
      blocklist[max] = block;
      return 0;
}

/* We have already allocated a rdb, we are now reading it from the disk */
static int
amiga_read (PedDisk* disk)
{
      struct RigidDiskBlock *rdb;
      struct PartitionBlock *partition;
      uint32_t partblock;
      uint32_t partlist[AMIGA_MAX_PARTITIONS];
      PedSector cylblocks;
      int i;

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

      if (_amiga_find_rdb (disk->dev, rdb) == AMIGA_RDB_NOT_FOUND) {
            ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
                  _("%s : Didn't find rdb block, should never happen."), __func__);
            return 0;
      }

      /* Let's copy the rdb read geometry to the dev */
      /* FIXME: should this go into disk->dev->bios_geom instead? */
      disk->dev->hw_geom.cylinders = PED_BE32_TO_CPU (rdb->rdb_Cylinders);
      disk->dev->hw_geom.heads = PED_BE32_TO_CPU (rdb->rdb_Heads);
      disk->dev->hw_geom.sectors = PED_BE32_TO_CPU (rdb->rdb_Sectors);
      cylblocks = (PedSector) PED_BE32_TO_CPU (rdb->rdb_Heads) *
            (PedSector) PED_BE32_TO_CPU (rdb->rdb_Sectors);

      /* Remove all partitions in the former in memory table */
      ped_disk_delete_all (disk);

      /* Let's allocate a partition block */
      if (!(partition = ped_malloc (PED_SECTOR_SIZE)))
            return 0;

      /* We initialize the hardblock free list to detect loops */
      for (i = 0; i < AMIGA_MAX_PARTITIONS; i++) partlist[i] = LINK_END;

      for (i = 1, partblock = PED_BE32_TO_CPU(rdb->rdb_PartitionList);
            i < AMIGA_MAX_PARTITIONS && partblock != LINK_END;
            i++, partblock = PED_BE32_TO_CPU(partition->pb_Next))
      {
            PedPartition *part;
            PedSector start, end;
            struct DosEnvec *de;
            PedConstraint *constraint_exact;
            int j; 

            /* Let's look for loops in the partition table */
            if (_amiga_loop_check(partblock, partlist, i)) {
                  break;
            }

            /* Let's allocate and read a partition block to get its geometry*/
            if (!_amiga_read_block (disk->dev, AMIGA(partition), (PedSector)partblock, NULL)) {
                  ped_free(partition);
                  return 0;
            }

            start = ((PedSector) PED_BE32_TO_CPU (partition->de_LowCyl))
                  * cylblocks;
            end = (((PedSector) PED_BE32_TO_CPU (partition->de_HighCyl))
                  + 1) * cylblocks - 1;

            /* We can now construct a new partition */
            if (!(part = ped_partition_new (disk, 0, NULL, start, end))) {
                  ped_free(partition);
                  return 0;
            }
            /* And copy over the partition block */
            memcpy(part->disk_specific, partition, 256);

            part->num = i;
            part->type = 0;
            /* Let's probe what filesystem is present on the disk */
            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)) {
                  ped_partition_destroy(part);
                  ped_free(partition);
                  return 0;
            }
            ped_constraint_destroy (constraint_exact);
      }
      return 1;
}

static int
_amiga_find_free_blocks(PedDisk *disk, uint32_t *table,
      struct LinkedBlock *block, uint32_t first, uint32_t type)
{
      PedSector next;

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

      for (next = first; next != LINK_END; next = PED_BE32_TO_CPU(block->lk_Next)) {
            if (table[next] != IDNAME_FREE) {
                  switch (ped_exception_throw(PED_EXCEPTION_ERROR,
                        PED_EXCEPTION_FIX | PED_EXCEPTION_IGNORE | PED_EXCEPTION_CANCEL,
                        _("%s : Loop detected at block %d."), __func__, next))
                  {
                        case PED_EXCEPTION_CANCEL :
                              return 0;
                        case PED_EXCEPTION_FIX :
                              /* TODO : Need to add fixing code */
                        case PED_EXCEPTION_IGNORE :
                        case PED_EXCEPTION_UNHANDLED :
                        default : 
                              return 1;
                  }
            }

            if (!_amiga_read_block (disk->dev, AMIGA(block), next, NULL)) {
                  return 0;
            }
            if (PED_BE32_TO_CPU(block->lk_ID) != type) {
                  switch (ped_exception_throw(PED_EXCEPTION_ERROR,
                        PED_EXCEPTION_CANCEL,
                        _("%s : The %s list seems bad at block %s."),
                        __func__, _amiga_block_id(PED_BE32_TO_CPU(block->lk_ID)), next))
                  {
                        /* TODO : to more subtile things here */
                        case PED_EXCEPTION_CANCEL :
                        case PED_EXCEPTION_UNHANDLED :
                        default : 
                              return 0;
                  }
            }
            table[next] = type;
            if (PED_BE32_TO_CPU(block->lk_ID) == IDNAME_FILESYSHEADER) {
                  if (_amiga_find_free_blocks(disk, table, block,
                        PED_BE32_TO_CPU(LNK2(block)->lk2_Linked),
                        IDNAME_LOADSEG) == 0) return 0;
            }
      }
      return 1;
}
static uint32_t
_amiga_next_free_block(uint32_t *table, uint32_t start, uint32_t type) {
      int i;

      for (i = start; table[i] != type && table[i] != IDNAME_FREE; i++);
      return i;
}
static PedPartition *
_amiga_next_real_partition(PedDisk *disk, PedPartition *part) {
      PedPartition *next;

      for (next = ped_disk_next_partition (disk, part);
            next != NULL && !ped_partition_is_active (next);
            next = ped_disk_next_partition (disk, next));
      return next;
}
#ifndef DISCOVER_ONLY
static int
amiga_write (PedDisk* disk)
{
      struct RigidDiskBlock *rdb;
      struct LinkedBlock *block;
      struct PartitionBlock *partition;
      PedPartition *part, *next_part;
      PedSector cylblocks, first_hb, last_hb, last_used_hb;
      uint32_t * table;
      uint32_t i, rdb_block, max_part;
      uint32_t rdb_num, part_num, block_num, next_num;

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

      if (!(rdb = ped_malloc (PED_SECTOR_SIZE)))
            return 0;

      /* Let's read the rdb */
      if ((rdb_num = _amiga_find_rdb (disk->dev, rdb)) == AMIGA_RDB_NOT_FOUND) {
            rdb_num = 2;
      } else {
            memcpy (RDSK(disk->disk_specific), rdb, PED_SECTOR_SIZE);
      }
      ped_free (rdb);
      rdb = RDSK(disk->disk_specific);

      cylblocks = (PedSector) PED_BE32_TO_CPU (rdb->rdb_Heads) *
            (PedSector) PED_BE32_TO_CPU (rdb->rdb_Sectors);
      first_hb = (PedSector) PED_BE32_TO_CPU (rdb->rdb_RDBBlocksLo);
      last_hb = (PedSector) PED_BE32_TO_CPU (rdb->rdb_RDBBlocksHi);
      last_used_hb = (PedSector) PED_BE32_TO_CPU (rdb->rdb_HighRDSKBlock);

      /* let's allocate a free block table and initialize it */
      if (!(table = ped_malloc ((last_hb - first_hb + 1) * sizeof(uint32_t))))
            return 0;

      memset(table, 0xff, (last_hb - first_hb + 1) * sizeof(uint32_t));
      for (i = 0; i<=rdb_num; i++) table[i] = IDNAME_RIGIDDISK;
      
      /* Let's allocate a partition block */
      if (!(block = ped_malloc (PED_SECTOR_SIZE))) {
            ped_free (table);
            return 0;
      }

      /* And fill the free block table */
      if (_amiga_find_free_blocks(disk, table, block,
            PED_BE32_TO_CPU (rdb->rdb_BadBlockList), IDNAME_BADBLOCK) == 0)
      {
            ped_exception_throw(PED_EXCEPTION_ERROR,
                  PED_EXCEPTION_CANCEL,
                  _("%s : Failed to list badblocks."), __func__);
            goto error_free_table;
      }
      if (_amiga_find_free_blocks(disk, table, block,
            PED_BE32_TO_CPU (rdb->rdb_PartitionList), IDNAME_PARTITION) == 0)
      {
            ped_exception_throw(PED_EXCEPTION_ERROR,
                  PED_EXCEPTION_CANCEL,
                  _("%s : Failed to list partitionblocks."), __func__);
            goto error_free_table;
      }
      if (_amiga_find_free_blocks(disk, table, block,
            PED_BE32_TO_CPU (rdb->rdb_FileSysHeaderList), IDNAME_FILESYSHEADER) == 0)
      {
            ped_exception_throw(PED_EXCEPTION_ERROR,
                  PED_EXCEPTION_CANCEL,
                  _("%s : Failed to list filesystemblocks."), __func__);
            goto error_free_table;
      }
      if (_amiga_find_free_blocks(disk, table, block,
            PED_BE32_TO_CPU (rdb->rdb_BootBlockList), IDNAME_BOOT) == 0)
      {
            ped_exception_throw(PED_EXCEPTION_ERROR,
                  PED_EXCEPTION_CANCEL,
                  _("%s : Failed to list bootblocks."), __func__);
            goto error_free_table;
      }

      block_num = next_num = part_num = _amiga_next_free_block(table, rdb_num+1, IDNAME_PARTITION);
      part = _amiga_next_real_partition(disk, NULL);
      rdb->rdb_PartitionList = PED_CPU_TO_BE32(part ? part_num : LINK_END);
      for (; part != NULL; part = next_part, block_num = next_num) {
            PED_ASSERT(part->disk_specific != NULL, return 0);
            PED_ASSERT(part->geom.start % cylblocks == 0, return 0);
            PED_ASSERT((part->geom.end + 1) % cylblocks == 0, return 0);

            next_part = _amiga_next_real_partition(disk, part);
            next_num = _amiga_next_free_block(table, block_num+1, IDNAME_PARTITION);

            partition = PART(part->disk_specific);
            partition->pb_Next = next_part==NULL ? PED_CPU_TO_BE32(LINK_END):PED_CPU_TO_BE32(next_num);
            partition->de_LowCyl = PED_CPU_TO_BE32(part->geom.start/cylblocks);
            partition->de_HighCyl = PED_CPU_TO_BE32((part->geom.end+1)/cylblocks-1);
            _amiga_calculate_checksum(AMIGA(partition));
            if (!ped_device_write (disk->dev, (void*) partition, block_num, 1)) {
                  ped_exception_throw(PED_EXCEPTION_ERROR,
                        PED_EXCEPTION_CANCEL,
                        _("Failed to write partition block at %d."),
                        block_num);
                  goto error_free_table;
                  /* WARNING : If we fail here, we stop everything,
                   * and the partition table is lost. A Better
                   * solution should be found, using the second
                   * half of the hardblocks to not overwrite the
                   * old partition table. It becomes problematic
                   * if we use more than half of the hardblocks. */
            }
      }

      if (block_num > PED_BE32_TO_CPU (rdb->rdb_HighRDSKBlock))
            rdb->rdb_HighRDSKBlock = PED_CPU_TO_BE32(block_num);

      _amiga_calculate_checksum(AMIGA(rdb));
      if (!ped_device_write (disk->dev, (void*) disk->disk_specific, rdb_num, 1))
            goto error_free_table;

      ped_free (table);
      ped_free (block);
      return ped_device_sync (disk->dev);

error_free_table:
      ped_free (table);
error_free_block:
      ped_free (block);
error:
      return 0;
}
#endif /* !DISCOVER_ONLY */

static PedPartition*
amiga_partition_new (const PedDisk* disk, PedPartitionType part_type,
               const PedFileSystemType* fs_type,
               PedSector start, PedSector end)
{
      PedPartition *part;
      PedDevice *dev;
      PedSector cyl;
      struct PartitionBlock *partition;
      struct RigidDiskBlock *rdb;

      PED_ASSERT(disk != NULL, return NULL);
      PED_ASSERT(disk->dev != NULL, return NULL);
      PED_ASSERT(disk->disk_specific != NULL, return NULL);
      dev = disk->dev;
      cyl = (PedSector) (dev->hw_geom.sectors * dev->hw_geom.heads);
      rdb = RDSK(disk->disk_specific);

      if (!(part = _ped_partition_alloc (disk, part_type, fs_type, start, end)))
            return NULL;

      if (ped_partition_is_active (part)) {
            if (!(part->disk_specific = ped_malloc (PED_SECTOR_SIZE))) {
                  ped_free (part);
                  return NULL;
            }
            partition = PART(part->disk_specific);
            memset(partition, 0, sizeof(struct PartitionBlock));

            partition->pb_ID = PED_CPU_TO_BE32(IDNAME_PARTITION);
            partition->pb_SummedLongs = PED_CPU_TO_BE32(64);
            partition->pb_HostID = rdb->rdb_HostID;
            partition->pb_Flags = PED_CPU_TO_BE32(0);
            /* TODO : use a scheme including the device name and the
             * partition number, if it is possible */
            _amiga_set_bstr("dhx", partition->pb_DriveName, 32);

            partition->de_TableSize = PED_CPU_TO_BE32(19);
            partition->de_SizeBlock = PED_CPU_TO_BE32(128);
            partition->de_SecOrg = PED_CPU_TO_BE32(0);
            partition->de_Surfaces = PED_CPU_TO_BE32(dev->hw_geom.heads);
            partition->de_SectorPerBlock = PED_CPU_TO_BE32(1);
            partition->de_BlocksPerTrack
                  = PED_CPU_TO_BE32(dev->hw_geom.sectors);
            partition->de_Reserved = PED_CPU_TO_BE32(2);
            partition->de_PreAlloc = PED_CPU_TO_BE32(0);
            partition->de_Interleave = PED_CPU_TO_BE32(0);
            partition->de_LowCyl = PED_CPU_TO_BE32(start/cyl);
            partition->de_HighCyl = PED_CPU_TO_BE32((end+1)/cyl-1);
            partition->de_NumBuffers = PED_CPU_TO_BE32(30);
            partition->de_BufMemType = PED_CPU_TO_BE32(0);
            partition->de_MaxTransfer = PED_CPU_TO_BE32(0x7fffffff);
            partition->de_Mask = PED_CPU_TO_BE32(0xffffffff);
            partition->de_BootPri = PED_CPU_TO_BE32(0);
            partition->de_DosType = PED_CPU_TO_BE32(0x4c4e5800);
            partition->de_Baud = PED_CPU_TO_BE32(0);
            partition->de_Control = PED_CPU_TO_BE32(0);
            partition->de_BootBlocks = PED_CPU_TO_BE32(0);

      } else {
            part->disk_specific = NULL;
      }
      return part;
}

static PedPartition*
amiga_partition_duplicate (const PedPartition* part)
{
      PedPartition *new_part;
      struct PartitionBlock *new_amiga_part;
      struct PartitionBlock *old_amiga_part;

      PED_ASSERT(part != NULL, return NULL);
      PED_ASSERT(part->disk != NULL, return NULL);
      PED_ASSERT(part->disk_specific != NULL, return NULL);
      old_amiga_part = (struct PartitionBlock *) part->disk_specific;

      new_part = ped_partition_new (part->disk, part->type,
                              part->fs_type, part->geom.start,
                              part->geom.end);
      if (!new_part)
            return NULL;

      new_amiga_part = (struct PartitionBlock *) new_part->disk_specific;
      memcpy (new_amiga_part, old_amiga_part, 256);

      return new_part;
}

static void
amiga_partition_destroy (PedPartition* part)
{
      PED_ASSERT (part != NULL, return);

      if (ped_partition_is_active (part)) {
            PED_ASSERT (part->disk_specific != NULL, return);
            ped_free (part->disk_specific);
      }
      _ped_partition_free (part);
}

static int
amiga_partition_set_system (PedPartition* part, const PedFileSystemType* fs_type)
{
      struct PartitionBlock *partition;

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

      part->fs_type = fs_type;

      if (!fs_type)
            partition->de_DosType = PED_CPU_TO_BE32(0x4c4e5800); /* 'LNX\0' */
      else if (!strcmp (fs_type->name, "ext2"))
            partition->de_DosType = PED_CPU_TO_BE32(0x4c4e5800); /* 'LNX\0' */
      else if (!strcmp (fs_type->name, "ext3"))
            partition->de_DosType = PED_CPU_TO_BE32(0x45585403); /* 'EXT\3' */
      else if (!strcmp (fs_type->name, "linux-swap"))
            partition->de_DosType = PED_CPU_TO_BE32(0x53575000); /* 'SWP\0' */
      else if (!strcmp (fs_type->name, "fat16"))
            partition->de_DosType = PED_CPU_TO_BE32(0x46415400); /* 'FAT\0' */
      else if (!strcmp (fs_type->name, "fat32"))
            partition->de_DosType = PED_CPU_TO_BE32(0x46415401); /* 'FAT\1'*/
      else if (!strcmp (fs_type->name, "hfs"))
            partition->de_DosType = PED_CPU_TO_BE32(0x48465300); /* 'HFS\0' */
      else if (!strcmp (fs_type->name, "jfs"))
            partition->de_DosType = PED_CPU_TO_BE32(0x4a465300); /* 'JFS\0' */
      else if (!strcmp (fs_type->name, "ntfs"))
            partition->de_DosType = PED_CPU_TO_BE32(0x4e544653); /* 'NTFS' */
      else if (!strcmp (fs_type->name, "reiserfs"))
            partition->de_DosType = PED_CPU_TO_BE32(0x52465300); /* 'RFS\0' */
      else if (!strcmp (fs_type->name, "sun-ufs"))
            partition->de_DosType = PED_CPU_TO_BE32(0x53554653); /* 'SUFS' */
      else if (!strcmp (fs_type->name, "hp-ufs"))
            partition->de_DosType = PED_CPU_TO_BE32(0x48554653); /* 'HUFS' */
      else if (!strcmp (fs_type->name, "xfs"))
            partition->de_DosType = PED_CPU_TO_BE32(0x58465300); /* 'XFS\0' */
      else
            partition->de_DosType = PED_CPU_TO_BE32(0x00000000); /* unknown */
      return 1;
}

static int
amiga_partition_set_flag (PedPartition* part, PedPartitionFlag flag, int state)
{
      struct PartitionBlock *partition;

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

      partition = PART(part->disk_specific);

      switch (flag) {
            case PED_PARTITION_BOOT:
                  if (state) partition->pb_Flags |= PED_CPU_TO_BE32(PBFF_BOOTABLE);
                  else partition->pb_Flags &= ~(PED_CPU_TO_BE32(PBFF_BOOTABLE));
                  return 1;
            case PED_PARTITION_HIDDEN:
                  if (state) partition->pb_Flags |= PED_CPU_TO_BE32(PBFF_NOMOUNT);
                  else partition->pb_Flags &= ~(PED_CPU_TO_BE32(PBFF_NOMOUNT));
                  return 1;
            case PED_PARTITION_RAID:
                  if (state) partition->pb_Flags |= PED_CPU_TO_BE32(PBFF_RAID);
                  else partition->pb_Flags &= ~(PED_CPU_TO_BE32(PBFF_RAID));
                  return 1;
            case PED_PARTITION_LVM:
                  if (state) partition->pb_Flags |= PED_CPU_TO_BE32(PBFF_LVM);
                  else partition->pb_Flags &= ~(PED_CPU_TO_BE32(PBFF_LVM));
                  return 1;
            default:
                  return 0;
      }
}

static int
amiga_partition_get_flag (const PedPartition* part, PedPartitionFlag flag)
{
      struct PartitionBlock *partition;

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

      partition = PART(part->disk_specific);

      switch (flag) {
            case PED_PARTITION_BOOT:
                  return (partition->pb_Flags & PED_CPU_TO_BE32(PBFF_BOOTABLE));
            case PED_PARTITION_HIDDEN:
                  return (partition->pb_Flags & PED_CPU_TO_BE32(PBFF_NOMOUNT));
            case PED_PARTITION_RAID:
                  return (partition->pb_Flags & PED_CPU_TO_BE32(PBFF_RAID));
            case PED_PARTITION_LVM:
                  return (partition->pb_Flags & PED_CPU_TO_BE32(PBFF_LVM));
            default:
                  return 0;
      }
}

static int
amiga_partition_is_flag_available (const PedPartition* part,
                         PedPartitionFlag flag)
{
      switch (flag) {
      case PED_PARTITION_BOOT:
      case PED_PARTITION_HIDDEN:
      case PED_PARTITION_RAID:
      case PED_PARTITION_LVM:
            return 1;
      default:
            return 0;
      }
}

static void             
amiga_partition_set_name (PedPartition* part, const char* name)
{               
      struct PartitionBlock *partition;

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

      partition = PART(part->disk_specific);
      _amiga_set_bstr(name, partition->pb_DriveName, 32);
}
static const char*
amiga_partition_get_name (const PedPartition* part)
{
      struct PartitionBlock *partition;

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

      partition = PART(part->disk_specific);

      return _amiga_get_bstr(partition->pb_DriveName);
}

static PedConstraint*
_amiga_get_constraint (const PedDisk *disk)
{
      PedDevice *dev = disk->dev;
      PedAlignment start_align, end_align;
      PedGeometry max_geom;
      struct RigidDiskBlock *rdb = RDSK(disk->disk_specific);
      PedSector cyl_size = dev->hw_geom.sectors * dev->hw_geom.heads;

      if (!ped_alignment_init(&start_align, 0, cyl_size))
            return NULL;
      if (!ped_alignment_init(&end_align, -1, cyl_size))
            return NULL;
      if (!ped_geometry_init(&max_geom, dev, MAX_RDB_BLOCK + 1,
                         dev->length - MAX_RDB_BLOCK - 1))
            return NULL;

      return ped_constraint_new (&start_align, &end_align,
            &max_geom, &max_geom, 1, dev->length);
}

static int
amiga_partition_align (PedPartition* part, const PedConstraint* constraint)
{
      PED_ASSERT (part != NULL, return 0);
      PED_ASSERT (part->disk != NULL, return 0);
      
      if (_ped_partition_attempt_align (part, constraint,
                                _amiga_get_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
amiga_partition_enumerate (PedPartition* part)
{
      int i;
      PedPartition* p;
      
      PED_ASSERT (part != NULL, return 0);
      PED_ASSERT (part->disk != NULL, return 0);

      /* never change the partition numbers */
      if (part->num != -1)
            return 1;
      for (i = 1; i <= AMIGA_MAX_PARTITIONS; i++) {
            p = ped_disk_get_partition (part->disk, i);
            if (!p) {
                  part->num = i;
                  return 1;
            }
      }

      /* failed to allocate a number */
#ifndef DISCOVER_ONLY
      ped_exception_throw (PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
            _("%s : Unable to allocate a partition"), __func__);
#endif
      return 0;
}

static int
amiga_alloc_metadata (PedDisk* disk)
{
      PedPartition*           new_part;
      PedConstraint*          constraint_any = NULL;
      PedSector         highest_block;

      PED_ASSERT (disk != NULL, goto error);
      PED_ASSERT (disk->dev != NULL, goto error);

      constraint_any = ped_constraint_any (disk->dev);

      /* Allocate space for the RDB */
      new_part = ped_partition_new (disk, PED_PARTITION_METADATA, NULL, 0, MAX_RDB_BLOCK);
      if (!new_part)
            goto error;

      if (!ped_disk_add_partition (disk, new_part, constraint_any)) {
            ped_partition_destroy (new_part);
            goto error;
      }

      ped_constraint_destroy (constraint_any);
      return 1;
error:
      ped_constraint_destroy (constraint_any);
      return 0;
}

static int
amiga_get_max_primary_partition_count (const PedDisk* disk)
{
      return AMIGA_MAX_PARTITIONS;
}

static PedDiskOps amiga_disk_ops = {
      probe:                  amiga_probe,
#ifndef DISCOVER_ONLY
      clobber:          amiga_clobber,
#else
      clobber:          NULL,
#endif
      alloc:                  amiga_alloc,
      duplicate:        amiga_duplicate,
      free:             amiga_free,
      read:             amiga_read,
#ifndef DISCOVER_ONLY
      write:                  amiga_write,
#else
      write:                  NULL,
#endif

      partition_new:          amiga_partition_new,
      partition_duplicate:    amiga_partition_duplicate,
      partition_destroy:      amiga_partition_destroy,
      partition_set_system:   amiga_partition_set_system,
      partition_set_flag:     amiga_partition_set_flag,
      partition_get_flag:     amiga_partition_get_flag,
      partition_is_flag_available:
                        amiga_partition_is_flag_available,
      partition_set_name:     amiga_partition_set_name,
      partition_get_name:     amiga_partition_get_name,
      partition_align:  amiga_partition_align,
      partition_enumerate:    amiga_partition_enumerate,


      alloc_metadata:         amiga_alloc_metadata,
      get_max_primary_partition_count:
                        amiga_get_max_primary_partition_count
};

static PedDiskType amiga_disk_type = {
      next:       NULL,
      name:       "amiga",
      ops:        &amiga_disk_ops,
      features:   PED_DISK_TYPE_PARTITION_NAME
};

void
ped_disk_amiga_init ()
{
      PED_ASSERT(sizeof(struct AmigaBlock) != 3, return);
      PED_ASSERT(sizeof(struct RigidDiskBlock) != 64, return);
      PED_ASSERT(sizeof(struct PartitionBlock) != 64, return);
      PED_ASSERT(sizeof(struct LinkedBlock) != 5, return);
      PED_ASSERT(sizeof(struct Linked2Block) != 18, return);

      ped_register_disk_type (&amiga_disk_type);
}

void
ped_disk_amiga_done ()
{
      ped_unregister_disk_type (&amiga_disk_type);
}

Generated by  Doxygen 1.6.0   Back to index