| /* |
| libparted/fs_amiga - amiga file system support. |
| Copyright (C) 2000, 2001, 2007 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| |
| Contributor: Sven Luther <luther@debian.org> |
| */ |
| |
| #include <config.h> |
| #include <parted/parted.h> |
| #include <parted/debug.h> |
| #include <parted/endian.h> |
| |
| #include "amiga.h" |
| |
| #if ENABLE_NLS |
| # include <libintl.h> |
| # define _(String) dgettext (PACKAGE, String) |
| #else |
| # define _(String) (String) |
| #endif /* ENABLE_NLS */ |
| |
| #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>"; |
| } |
| } |
| |
| struct AmigaIds * |
| _amiga_add_id (uint32_t id, struct AmigaIds *ids) { |
| struct AmigaIds *newid; |
| |
| if ((newid=ped_malloc(sizeof (struct AmigaIds)))==NULL) { |
| ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, |
| _("%s : Failed to allocate id list element\n"), __func__); |
| return 0; |
| } |
| newid->ID = id; |
| newid->next = ids; |
| return newid; |
| } |
| |
| void |
| _amiga_free_ids (struct AmigaIds *ids) { |
| struct AmigaIds *current, *next; |
| |
| for (current = ids; current != NULL; current = next) { |
| next = current->next; |
| ped_free (current); |
| } |
| } |
| 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; |
| } |
| |
| #define AMIGA_RDB_NOT_FOUND ((uint32_t)0xffffffff) |
| |
| 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)) |
| |
| 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 AMIGA_MAX_PARTITIONS 128 |
| #define RDB_LOCATION_LIMIT 16 |
| #define RDSK(pos) ((struct RigidDiskBlock *)(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_DEFAULT) end = PED_SECTOR_SIZE_DEFAULT; |
| |
| 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)) { |
| switch (ped_exception_throw(PED_EXCEPTION_ERROR, |
| PED_EXCEPTION_CANCEL, |
| _("%s : Couldn't read block %llu\n"), __func__, block)) |
| { |
| case PED_EXCEPTION_CANCEL : |
| case PED_EXCEPTION_UNHANDLED : |
| default : |
| 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\n"), |
| __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)) { |
| switch (ped_exception_throw(PED_EXCEPTION_FATAL, |
| PED_EXCEPTION_CANCEL, |
| _("%s : Couldn't write block %d\n"), __func__, block)) |
| { |
| case PED_EXCEPTION_CANCEL : |
| case PED_EXCEPTION_UNHANDLED : |
| default : |
| return NULL; |
| } |
| } |
| case PED_EXCEPTION_IGNORE : |
| case PED_EXCEPTION_UNHANDLED : |
| default : |
| return blk; |
| } |
| } |
| return blk; |
| } |
| |
| 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; |
| } |
| |
| 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 */ |
| struct PartitionBlock * |
| amiga_find_part (PedGeometry *geom, struct PartitionBlock *part) |
| { |
| struct RigidDiskBlock *rdb; |
| uint32_t partblock; |
| uint32_t partlist[AMIGA_MAX_PARTITIONS]; |
| int i; |
| |
| PED_ASSERT(geom!= NULL, return NULL); |
| PED_ASSERT(geom->dev!= NULL, return NULL); |
| |
| if (!(rdb = ped_malloc (PED_SECTOR_SIZE_DEFAULT))) { |
| switch (ped_exception_throw(PED_EXCEPTION_ERROR, |
| PED_EXCEPTION_CANCEL, |
| _("%s : Failed to allocate disk_specific rdb block\n"), __func__)) |
| { |
| case PED_EXCEPTION_CANCEL : |
| case PED_EXCEPTION_UNHANDLED : |
| default : |
| return NULL; |
| } |
| } |
| if (_amiga_find_rdb (geom->dev, rdb) == AMIGA_RDB_NOT_FOUND) { |
| switch (ped_exception_throw(PED_EXCEPTION_ERROR, |
| PED_EXCEPTION_CANCEL, |
| _("%s : Didn't find rdb block, should never happen\n"), __func__)) |
| { |
| case PED_EXCEPTION_CANCEL : |
| case PED_EXCEPTION_UNHANDLED : |
| default : |
| ped_free(rdb); |
| return NULL; |
| } |
| } |
| |
| /* We initialize the hardblock free list to detect loops */ |
| for (i = 0; i < AMIGA_MAX_PARTITIONS; i++) partlist[i] = IDNAME_FREE; |
| |
| for (i = 1, partblock = PED_BE32_TO_CPU(rdb->rdb_PartitionList); |
| i < AMIGA_MAX_PARTITIONS && partblock != IDNAME_FREE; |
| i++, partblock = PED_BE32_TO_CPU(part->pb_Next)) |
| { |
| PedSector start, end; |
| PedSector cylblocks; |
| |
| /* Let's look for loops in the partition table */ |
| if (_amiga_loop_check(partblock, partlist, i)) { |
| ped_free (rdb); |
| return NULL; |
| } |
| /* Let's read a partition block to get its geometry*/ |
| if (!ped_device_read (geom->dev, part, (PedSector)partblock, 1)) { |
| switch (ped_exception_throw(PED_EXCEPTION_ERROR, |
| PED_EXCEPTION_CANCEL, |
| _("%s : Failed to read partition block %llu\n"), |
| __func__, (PedSector)partblock)) |
| { |
| case PED_EXCEPTION_CANCEL : |
| case PED_EXCEPTION_UNHANDLED : |
| default : |
| ped_free(rdb); |
| return NULL; |
| } |
| } |
| |
| /* Current block is not a Partition Block */ |
| if (part->pb_ID != IDNAME_PARTITION) { |
| ped_free (rdb); |
| return NULL; |
| } |
| |
| /* Calculate the geometry of the partition */ |
| cylblocks = ((PedSector) PED_BE32_TO_CPU (part->de_Surfaces)) * |
| ((PedSector) PED_BE32_TO_CPU (part->de_BlocksPerTrack)); |
| start = ((PedSector) PED_BE32_TO_CPU (part->de_LowCyl)) * cylblocks; |
| end = ((((PedSector) PED_BE32_TO_CPU (part->de_HighCyl))+1) * (cylblocks))-1; |
| |
| /* And check if it is the one we are searching for */ |
| if (start == geom->start && end == geom->end) { |
| ped_free (rdb); |
| return part; |
| } |
| } |
| |
| ped_free (rdb); |
| return NULL; |
| } |