/* -*- 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, 2007-2014, 2019-2022 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Contributor: Sven Luther */ #include #include #include #include #include "pt-tools.h" #ifndef MAX # define MAX(a,b) ((a) < (b) ? (b) : (a)) #endif #if ENABLE_NLS # include # define _(String) dgettext (PACKAGE, String) #else # define _(String) (String) #endif /* ENABLE_NLS */ #include "misc.h" /* 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"; default : return ""; } } 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; free (current); } } static int _GL_ATTRIBUTE_PURE _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_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 (const 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 ((PedDevice*)dev, blk, block, 1)) return NULL; /* FALLTHROUGH */ 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 (const PedDevice *dev, struct RigidDiskBlock *rdb) { int i; struct AmigaIds *ids; ids = _amiga_add_id (IDNAME_RIGIDDISK, NULL); for (i = 0; irdb_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 */ char pb_DriveName[32]; /* Preferred DOS device name: BSTR form */ uint32_t pb_Reserved2[15]; uint32_t de_TableSize; /* Size of Environment vector */ /* Size of the blocks in 32 bit words, usually 128 */ uint32_t de_SizeBlock; uint32_t de_SecOrg; /* Not used; must be 0 */ uint32_t de_Surfaces; /* Number of heads (surfaces) */ /* Disk sectors per block, used with SizeBlock, usually 1 */ uint32_t de_SectorPerBlock; 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 file system */ 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 (const PedDevice *dev) { struct RigidDiskBlock *rdb; uint32_t found; PED_ASSERT(dev != NULL); if ((rdb=RDSK(ped_malloc(dev->sector_size)))==NULL) return 0; found = _amiga_find_rdb (dev, rdb); free (rdb); return (found == AMIGA_RDB_NOT_FOUND ? 0 : 1); } static PedDisk* amiga_alloc (const PedDevice* dev) { PedDisk *disk; struct RigidDiskBlock *rdb; PedSector cyl_size; int highest_cylinder, highest_block; PED_ASSERT(dev != 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 (disk->dev->sector_size))) { free (disk); return NULL; } rdb = disk->disk_specific; /* Upon failed assertion this does leak. That's fine, because if the assertion fails, you have bigger problems than this leak. */ PED_ASSERT(sizeof(*rdb) <= disk->dev->sector_size); memset(rdb, 0, disk->dev->sector_size); 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 (disk->dev->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); PED_ASSERT(disk->dev != NULL); PED_ASSERT(disk->disk_specific != 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); PED_ASSERT(disk->disk_specific != NULL); free (disk->disk_specific); _ped_disk_free (disk); } 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); PED_ASSERT(disk->dev != NULL); PED_ASSERT(disk->dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0); PED_ASSERT(disk->disk_specific != NULL); 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 (disk->dev->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; /* 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)) { 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, PED_PARTITION_NORMAL, NULL, start, end))) { 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 file system is present on the disk */ part->fs_type = ped_file_system_probe (&part->geom); PedConstraint *constraint_exact = ped_constraint_exact (&part->geom); if (constraint_exact == NULL) return 0; bool ok = ped_disk_add_partition (disk, part, constraint_exact); ped_constraint_destroy (constraint_exact); if (!ok) { ped_partition_destroy(part); free(partition); return 0; } } free(partition); return 1; } static int _amiga_find_free_blocks(const PedDisk *disk, uint32_t *table, struct LinkedBlock *block, uint32_t first, uint32_t type) { PedSector next; PED_ASSERT(disk != NULL); PED_ASSERT(disk->dev != NULL); 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 _GL_ATTRIBUTE_PURE _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 * _GL_ATTRIBUTE_PURE _amiga_next_real_partition(const 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 (const PedDisk* disk) { struct RigidDiskBlock *rdb; struct LinkedBlock *block; struct PartitionBlock *partition; PedPartition *part, *next_part; PedSector cylblocks, first_hb, last_hb; uint32_t * table; uint32_t i; uint32_t rdb_num, part_num, block_num, next_num; PED_ASSERT (disk != NULL); PED_ASSERT (disk->dev != NULL); PED_ASSERT (disk->disk_specific != NULL); if (!(rdb = ped_malloc (disk->dev->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; size_t pb_size = sizeof (struct PartitionBlock); /* Initialize only the part that won't be copied over with a partition block in amiga_read. */ memset ((char *)(RDSK(disk->disk_specific)) + pb_size, 0, PED_SECTOR_SIZE_DEFAULT - pb_size); } else { memcpy (RDSK(disk->disk_specific), rdb, disk->dev->sector_size); } 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); /* Allocate a free block table and initialize it. There must be room for at least RDB_NUM + 2 entries, since the first RDB_NUM+1 entries get IDNAME_RIGIDDISK, and the following one must have LINK_END to serve as sentinel. */ size_t tab_size = 2 + MAX (last_hb - first_hb, rdb_num); if (!(table = ped_malloc (tab_size * sizeof *table))) return 0; for (i = 0; i <= rdb_num; i++) table[i] = IDNAME_RIGIDDISK; for ( ; i < tab_size; i++) table[i] = LINK_END; /* Let's allocate a partition block */ if (!(block = ped_malloc (disk->dev->sector_size))) { 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 bad blocks."), __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 partition blocks."), __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 file system blocks."), __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 boot blocks."), __func__); goto error_free_table; } block_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); PED_ASSERT(part->geom.start % cylblocks == 0); PED_ASSERT((part->geom.end + 1) % cylblocks == 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); if (next_part == NULL) partition->pb_Next = PED_CPU_TO_BE32(LINK_END); else partition->pb_Next = 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; free (table); free (block); return ped_device_sync (disk->dev); error_free_table: free (table); free (block); 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); PED_ASSERT(disk->dev != NULL); PED_ASSERT(disk->disk_specific != 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 (disk->dev->sector_size))) { 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); PED_ASSERT(part->disk != NULL); PED_ASSERT(part->disk_specific != 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); if (ped_partition_is_active (part)) { PED_ASSERT (part->disk_specific != NULL); 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); PED_ASSERT (part->disk_specific != NULL); 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 (is_linux_swap (fs_type->name)) 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); PED_ASSERT (part->disk_specific != NULL); 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 _GL_ATTRIBUTE_PURE amiga_partition_get_flag (const PedPartition* part, PedPartitionFlag flag) { struct PartitionBlock *partition; PED_ASSERT (part != NULL); PED_ASSERT (part->disk_specific != NULL); 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); PED_ASSERT (part->disk_specific != NULL); 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); PED_ASSERT (part->disk_specific != NULL); partition = PART(part->disk_specific); return _amiga_get_bstr(partition->pb_DriveName); } static PedAlignment* amiga_get_partition_alignment(const PedDisk *disk) { PedSector cylinder_size = disk->dev->hw_geom.sectors * disk->dev->hw_geom.heads; return ped_alignment_new(0, cylinder_size); } static PedConstraint* _amiga_get_constraint (const PedDisk *disk) { PedDevice *dev = disk->dev; PedAlignment start_align, end_align; PedGeometry max_geom; 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); PED_ASSERT (part->disk != NULL); 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); PED_ASSERT (part->disk != NULL); /* 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, _("Unable to allocate a partition number.")); #endif return 0; } static int amiga_alloc_metadata (PedDisk* disk) { PedPartition* new_part; PedConstraint* constraint_any = NULL; PED_ASSERT (disk != NULL); PED_ASSERT (disk->dev != NULL); 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 bool amiga_get_max_supported_partition_count (const PedDisk* disk, int *max_n) { *max_n = AMIGA_MAX_PARTITIONS; return true; } #include "pt-common.h" PT_define_limit_functions (amiga) static PedDiskOps amiga_disk_ops = { clobber: NULL, write: NULL_IF_DISCOVER_ONLY (amiga_write), partition_set_name: amiga_partition_set_name, partition_get_name: amiga_partition_get_name, get_partition_alignment: amiga_get_partition_alignment, PT_op_function_initializers (amiga) }; 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); PED_ASSERT (sizeof (struct RigidDiskBlock) != 64); PED_ASSERT (sizeof (struct PartitionBlock) != 64); PED_ASSERT (sizeof (struct LinkedBlock) != 5); PED_ASSERT (sizeof (struct Linked2Block) != 18); ped_disk_type_register (&amiga_disk_type); } void ped_disk_amiga_done () { ped_disk_type_unregister (&amiga_disk_type); }