amiga.c revision 9663:ace9a2ac3683
1/* 2 libparted/fs_amiga - amiga file system support. 3 Copyright (C) 2000, 2001, 2007 Free Software Foundation, Inc. 4 5 This program is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 3 of the License, or 8 (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 Contributor: Sven Luther <luther@debian.org> 19*/ 20 21#include <config.h> 22#include <parted/parted.h> 23#include <parted/debug.h> 24#include <parted/endian.h> 25 26#include "amiga.h" 27 28#if ENABLE_NLS 29# include <libintl.h> 30# define _(String) dgettext (PACKAGE, String) 31#else 32# define _(String) (String) 33#endif /* ENABLE_NLS */ 34 35#define IDNAME_RIGIDDISK (uint32_t)0x5244534B /* 'RDSK' */ 36#define IDNAME_BADBLOCK (uint32_t)0x42414442 /* 'BADB' */ 37#define IDNAME_PARTITION (uint32_t)0x50415254 /* 'PART' */ 38#define IDNAME_FILESYSHEADER (uint32_t)0x46534844 /* 'FSHD' */ 39#define IDNAME_LOADSEG (uint32_t)0x4C534547 /* 'LSEG' */ 40#define IDNAME_BOOT (uint32_t)0x424f4f54 /* 'BOOT' */ 41#define IDNAME_FREE (uint32_t)0xffffffff 42 43static const char * 44_amiga_block_id (uint32_t id) { 45 switch (id) { 46 case IDNAME_RIGIDDISK : 47 return "RDSK"; 48 case IDNAME_BADBLOCK : 49 return "BADB"; 50 case IDNAME_PARTITION : 51 return "PART"; 52 case IDNAME_FILESYSHEADER : 53 return "FSHD"; 54 case IDNAME_LOADSEG : 55 return "LSEG"; 56 case IDNAME_BOOT : 57 return "BOOT"; 58 case IDNAME_FREE : 59 return "<free>"; 60 default : 61 return "<unknown>"; 62 } 63} 64 65struct AmigaIds * 66_amiga_add_id (uint32_t id, struct AmigaIds *ids) { 67 struct AmigaIds *newid; 68 69 if ((newid=ped_malloc(sizeof (struct AmigaIds)))==NULL) { 70 ped_exception_throw(PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL, 71 _("%s : Failed to allocate id list element\n"), __func__); 72 return 0; 73 } 74 newid->ID = id; 75 newid->next = ids; 76 return newid; 77} 78 79void 80_amiga_free_ids (struct AmigaIds *ids) { 81 struct AmigaIds *current, *next; 82 83 for (current = ids; current != NULL; current = next) { 84 next = current->next; 85 ped_free (current); 86 } 87} 88int 89_amiga_id_in_list (uint32_t id, struct AmigaIds *ids) { 90 struct AmigaIds *current; 91 92 for (current = ids; current != NULL; current = current->next) { 93 if (id == current->ID) 94 return 1; 95 } 96 return 0; 97} 98 99#define AMIGA_RDB_NOT_FOUND ((uint32_t)0xffffffff) 100 101struct AmigaBlock { 102 uint32_t amiga_ID; /* Identifier 32 bit word */ 103 uint32_t amiga_SummedLongss; /* Size of the structure for checksums */ 104 int32_t amiga_ChkSum; /* Checksum of the structure */ 105}; 106#define AMIGA(pos) ((struct AmigaBlock *)(pos)) 107 108struct RigidDiskBlock { 109 uint32_t rdb_ID; /* Identifier 32 bit word : 'RDSK' */ 110 uint32_t rdb_SummedLongs; /* Size of the structure for checksums */ 111 int32_t rdb_ChkSum; /* Checksum of the structure */ 112 uint32_t rdb_HostID; /* SCSI Target ID of host, not really used */ 113 uint32_t rdb_BlockBytes; /* Size of disk blocks */ 114 uint32_t rdb_Flags; /* RDB Flags */ 115 /* block list heads */ 116 uint32_t rdb_BadBlockList; /* Bad block list */ 117 uint32_t rdb_PartitionList; /* Partition list */ 118 uint32_t rdb_FileSysHeaderList; /* File system header list */ 119 uint32_t rdb_DriveInit; /* Drive specific init code */ 120 uint32_t rdb_BootBlockList; /* Amiga OS 4 Boot Blocks */ 121 uint32_t rdb_Reserved1[5]; /* Unused word, need to be set to $ffffffff */ 122 /* physical drive characteristics */ 123 uint32_t rdb_Cylinders; /* Number of the cylinders of the drive */ 124 uint32_t rdb_Sectors; /* Number of sectors of the drive */ 125 uint32_t rdb_Heads; /* Number of heads of the drive */ 126 uint32_t rdb_Interleave; /* Interleave */ 127 uint32_t rdb_Park; /* Head parking cylinder */ 128 uint32_t rdb_Reserved2[3]; /* Unused word, need to be set to $ffffffff */ 129 uint32_t rdb_WritePreComp; /* Starting cylinder of write precompensation */ 130 uint32_t rdb_ReducedWrite; /* Starting cylinder of reduced write current */ 131 uint32_t rdb_StepRate; /* Step rate of the drive */ 132 uint32_t rdb_Reserved3[5]; /* Unused word, need to be set to $ffffffff */ 133 /* logical drive characteristics */ 134 uint32_t rdb_RDBBlocksLo; /* low block of range reserved for hardblocks */ 135 uint32_t rdb_RDBBlocksHi; /* high block of range for these hardblocks */ 136 uint32_t rdb_LoCylinder; /* low cylinder of partitionable disk area */ 137 uint32_t rdb_HiCylinder; /* high cylinder of partitionable data area */ 138 uint32_t rdb_CylBlocks; /* number of blocks available per cylinder */ 139 uint32_t rdb_AutoParkSeconds; /* zero for no auto park */ 140 uint32_t rdb_HighRDSKBlock; /* highest block used by RDSK */ 141 /* (not including replacement bad blocks) */ 142 uint32_t rdb_Reserved4; 143 /* drive identification */ 144 char rdb_DiskVendor[8]; 145 char rdb_DiskProduct[16]; 146 char rdb_DiskRevision[4]; 147 char rdb_ControllerVendor[8]; 148 char rdb_ControllerProduct[16]; 149 char rdb_ControllerRevision[4]; 150 uint32_t rdb_Reserved5[10]; 151}; 152 153#define AMIGA_MAX_PARTITIONS 128 154#define RDB_LOCATION_LIMIT 16 155#define RDSK(pos) ((struct RigidDiskBlock *)(pos)) 156 157static int 158_amiga_checksum (struct AmigaBlock *blk) { 159 uint32_t *rdb = (uint32_t *) blk; 160 uint32_t sum; 161 int i, end; 162 163 sum = PED_BE32_TO_CPU (rdb[0]); 164 end = PED_BE32_TO_CPU (rdb[1]); 165 166 if (end > PED_SECTOR_SIZE_DEFAULT) end = PED_SECTOR_SIZE_DEFAULT; 167 168 for (i = 1; i < end; i++) sum += PED_BE32_TO_CPU (rdb[i]); 169 170 return sum; 171} 172 173static void 174_amiga_calculate_checksum (struct AmigaBlock *blk) { 175 176 blk->amiga_ChkSum = PED_CPU_TO_BE32( 177 PED_BE32_TO_CPU(blk->amiga_ChkSum) - 178 _amiga_checksum((struct AmigaBlock *) blk)); 179 return; 180} 181 182 183static struct AmigaBlock * 184_amiga_read_block (PedDevice *dev, struct AmigaBlock *blk, PedSector block, struct AmigaIds *ids) { 185 if (!ped_device_read (dev, blk, block, 1)) { 186 switch (ped_exception_throw(PED_EXCEPTION_ERROR, 187 PED_EXCEPTION_CANCEL, 188 _("%s : Couldn't read block %llu\n"), __func__, block)) 189 { 190 case PED_EXCEPTION_CANCEL : 191 case PED_EXCEPTION_UNHANDLED : 192 default : 193 return NULL; 194 } 195 } 196 if (ids && !_amiga_id_in_list(PED_BE32_TO_CPU(blk->amiga_ID), ids)) 197 return NULL; 198 if (_amiga_checksum (blk) != 0) { 199 switch (ped_exception_throw(PED_EXCEPTION_ERROR, 200 PED_EXCEPTION_FIX | PED_EXCEPTION_IGNORE | PED_EXCEPTION_CANCEL, 201 _("%s : Bad checksum on block %llu of type %s\n"), 202 __func__, block, _amiga_block_id(PED_BE32_TO_CPU(blk->amiga_ID)))) 203 { 204 case PED_EXCEPTION_CANCEL : 205 return NULL; 206 case PED_EXCEPTION_FIX : 207 _amiga_calculate_checksum(AMIGA(blk)); 208 if (!ped_device_write (dev, blk, block, 1)) { 209 switch (ped_exception_throw(PED_EXCEPTION_FATAL, 210 PED_EXCEPTION_CANCEL, 211 _("%s : Couldn't write block %d\n"), __func__, block)) 212 { 213 case PED_EXCEPTION_CANCEL : 214 case PED_EXCEPTION_UNHANDLED : 215 default : 216 return NULL; 217 } 218 } 219 case PED_EXCEPTION_IGNORE : 220 case PED_EXCEPTION_UNHANDLED : 221 default : 222 return blk; 223 } 224 } 225 return blk; 226} 227 228static uint32_t 229_amiga_find_rdb (PedDevice *dev, struct RigidDiskBlock *rdb) { 230 int i; 231 struct AmigaIds *ids; 232 233 ids = _amiga_add_id (IDNAME_RIGIDDISK, NULL); 234 235 for (i = 0; i<RDB_LOCATION_LIMIT; i++) { 236 if (!_amiga_read_block (dev, AMIGA(rdb), i, ids)) { 237 continue; 238 } 239 if (PED_BE32_TO_CPU (rdb->rdb_ID) == IDNAME_RIGIDDISK) { 240 _amiga_free_ids (ids); 241 return i; 242 } 243 } 244 _amiga_free_ids (ids); 245 return AMIGA_RDB_NOT_FOUND; 246} 247 248static int 249_amiga_loop_check (uint32_t block, uint32_t * blocklist, uint32_t max) 250{ 251 uint32_t i; 252 253 for (i = 0; i < max; i++) 254 if (block == blocklist[i]) { 255 /* We are looping, let's stop. */ 256 return 1; 257 } 258 blocklist[max] = block; 259 return 0; 260} 261 262/* We have already allocated a rdb, we are now reading it from the disk */ 263struct PartitionBlock * 264amiga_find_part (PedGeometry *geom, struct PartitionBlock *part) 265{ 266 struct RigidDiskBlock *rdb; 267 uint32_t partblock; 268 uint32_t partlist[AMIGA_MAX_PARTITIONS]; 269 int i; 270 271 PED_ASSERT(geom!= NULL, return NULL); 272 PED_ASSERT(geom->dev!= NULL, return NULL); 273 274 if (!(rdb = ped_malloc (PED_SECTOR_SIZE_DEFAULT))) { 275 switch (ped_exception_throw(PED_EXCEPTION_ERROR, 276 PED_EXCEPTION_CANCEL, 277 _("%s : Failed to allocate disk_specific rdb block\n"), __func__)) 278 { 279 case PED_EXCEPTION_CANCEL : 280 case PED_EXCEPTION_UNHANDLED : 281 default : 282 return NULL; 283 } 284 } 285 if (_amiga_find_rdb (geom->dev, rdb) == AMIGA_RDB_NOT_FOUND) { 286 switch (ped_exception_throw(PED_EXCEPTION_ERROR, 287 PED_EXCEPTION_CANCEL, 288 _("%s : Didn't find rdb block, should never happen\n"), __func__)) 289 { 290 case PED_EXCEPTION_CANCEL : 291 case PED_EXCEPTION_UNHANDLED : 292 default : 293 ped_free(rdb); 294 return NULL; 295 } 296 } 297 298 /* We initialize the hardblock free list to detect loops */ 299 for (i = 0; i < AMIGA_MAX_PARTITIONS; i++) partlist[i] = IDNAME_FREE; 300 301 for (i = 1, partblock = PED_BE32_TO_CPU(rdb->rdb_PartitionList); 302 i < AMIGA_MAX_PARTITIONS && partblock != IDNAME_FREE; 303 i++, partblock = PED_BE32_TO_CPU(part->pb_Next)) 304 { 305 PedSector start, end; 306 PedSector cylblocks; 307 308 /* Let's look for loops in the partition table */ 309 if (_amiga_loop_check(partblock, partlist, i)) { 310 ped_free (rdb); 311 return NULL; 312 } 313 /* Let's read a partition block to get its geometry*/ 314 if (!ped_device_read (geom->dev, part, (PedSector)partblock, 1)) { 315 switch (ped_exception_throw(PED_EXCEPTION_ERROR, 316 PED_EXCEPTION_CANCEL, 317 _("%s : Failed to read partition block %llu\n"), 318 __func__, (PedSector)partblock)) 319 { 320 case PED_EXCEPTION_CANCEL : 321 case PED_EXCEPTION_UNHANDLED : 322 default : 323 ped_free(rdb); 324 return NULL; 325 } 326 } 327 328 /* Current block is not a Partition Block */ 329 if (part->pb_ID != IDNAME_PARTITION) { 330 ped_free (rdb); 331 return NULL; 332 } 333 334 /* Calculate the geometry of the partition */ 335 cylblocks = ((PedSector) PED_BE32_TO_CPU (part->de_Surfaces)) * 336 ((PedSector) PED_BE32_TO_CPU (part->de_BlocksPerTrack)); 337 start = ((PedSector) PED_BE32_TO_CPU (part->de_LowCyl)) * cylblocks; 338 end = ((((PedSector) PED_BE32_TO_CPU (part->de_HighCyl))+1) * (cylblocks))-1; 339 340 /* And check if it is the one we are searching for */ 341 if (start == geom->start && end == geom->end) { 342 ped_free (rdb); 343 return part; 344 } 345 } 346 347 ped_free (rdb); 348 return NULL; 349} 350