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