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 
  43 static 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 
  65 struct 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 
  79 void
  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 }
  88 int
  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 
 101 struct 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 
 108 struct 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 
 157 static 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 
 173 static 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 
 183 static 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 
 228 static 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 
 248 static 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 */
 263 struct PartitionBlock *
 264 amiga_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 }