1/*
2 * Copyright 2003-2009, Axel D��rfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "amiga_rdb.h"
8
9#include <ByteOrder.h>
10#include <KernelExport.h>
11#include <disk_device_manager/ddm_modules.h>
12#include <disk_device_types.h>
13#ifdef _BOOT_MODE
14#	include <boot/partitions.h>
15#else
16#	include <DiskDeviceTypes.h>
17#endif
18#include <util/kernel_cpp.h>
19
20#include <unistd.h>
21#include <string.h>
22
23
24//#define TRACE_AMIGA_RDB
25#ifdef TRACE_AMIGA_RDB
26#	define TRACE(x) dprintf x
27#else
28#	define TRACE(x) ;
29#endif
30
31
32#define AMIGA_PARTITION_MODULE_NAME "partitioning_systems/amiga_rdb/v1"
33
34
35template<typename Type> bool
36validate_check_sum(Type *type)
37{
38	if (type->SummedLongs() != sizeof(*type) / sizeof(uint32))
39		return false;
40
41	// check checksum
42	uint32 *longs = (uint32 *)type;
43	uint32 sum = 0;
44	for (uint32 i = 0; i < type->SummedLongs(); i++)
45		sum += B_BENDIAN_TO_HOST_INT32(longs[i]);
46
47#ifdef TRACE_AMIGA_RDB
48	if (sum != 0)
49		TRACE(("search_rdb: check sum is incorrect!\n"));
50#endif
51
52	return sum == 0;
53}
54
55
56#ifdef TRACE_AMIGA_RDB
57static char *
58get_tupel(uint32 id)
59{
60	static unsigned char tupel[5];
61
62	tupel[0] = 0xff & (id >> 24);
63	tupel[1] = 0xff & (id >> 16);
64	tupel[2] = 0xff & (id >> 8);
65	tupel[3] = 0xff & (id);
66	tupel[4] = 0;
67	for (int16 i = 0;i < 4;i++) {
68		if (tupel[i] < ' ' || tupel[i] > 128)
69			tupel[i] = '.';
70	}
71
72	return (char *)tupel;
73}
74#endif
75
76
77static status_t
78get_next_partition(int fd, rigid_disk_block &rdb, uint32 &cookie,
79	partition_block &partition)
80{
81	if (cookie == 0) {
82		// first entry
83		cookie = rdb.FirstPartition();
84	} else if (cookie == 0xffffffff) {
85		// last entry
86		return B_ENTRY_NOT_FOUND;
87	}
88
89	ssize_t bytesRead = read_pos(fd, (off_t)cookie * rdb.BlockSize(),
90		(void *)&partition, sizeof(partition_block));
91	if (bytesRead < (ssize_t)sizeof(partition_block))
92		return B_ERROR;
93
94	// TODO: Should we retry with the next block if the following test fails, as
95	// long as this we find partition_blocks within a reasonable range?
96
97	if (partition.ID() != RDB_PARTITION_ID
98		|| !validate_check_sum<partition_block>(&partition))
99		return B_BAD_DATA;
100
101	cookie = partition.Next();
102	return B_OK;
103}
104
105
106static bool
107search_rdb(int fd, rigid_disk_block **_rdb)
108{
109	for (int32 sector = 0; sector < RDB_LOCATION_LIMIT; sector++) {
110		uint8 buffer[512];
111		ssize_t bytesRead = read_pos(fd, sector * 512, buffer, sizeof(buffer));
112		if (bytesRead < (ssize_t)sizeof(buffer)) {
113			TRACE(("search_rdb: read error: %ld\n", bytesRead));
114			return false;
115		}
116
117		rigid_disk_block *rdb = (rigid_disk_block *)buffer;
118		if (rdb->ID() == RDB_DISK_ID
119			&& validate_check_sum<rigid_disk_block>(rdb)) {
120			// copy the RDB to a new piece of memory
121			rdb = new rigid_disk_block();
122			memcpy(rdb, buffer, sizeof(rigid_disk_block));
123
124			*_rdb = rdb;
125			return true;
126		}
127	}
128
129	return false;
130}
131
132
133// #pragma mark - public module interface
134
135
136static status_t
137amiga_rdb_std_ops(int32 op, ...)
138{
139	switch (op) {
140		case B_MODULE_INIT:
141		case B_MODULE_UNINIT:
142			return B_OK;
143	}
144
145	return B_ERROR;
146}
147
148
149static float
150amiga_rdb_identify_partition(int fd, partition_data *partition, void **_cookie)
151{
152	rigid_disk_block *rdb;
153	if (!search_rdb(fd, &rdb))
154		return B_ERROR;
155
156	*_cookie = (void *)rdb;
157	return 0.5f;
158}
159
160
161static status_t
162amiga_rdb_scan_partition(int fd, partition_data *partition, void *_cookie)
163{
164	TRACE(("amiga_rdb_scan_partition(cookie = %p)\n", _cookie));
165
166	rigid_disk_block &rdb = *(rigid_disk_block *)_cookie;
167
168	partition->status = B_PARTITION_VALID;
169	partition->flags |= B_PARTITION_PARTITIONING_SYSTEM
170						| B_PARTITION_READ_ONLY;
171	partition->content_size = partition->size;
172
173	// scan all children
174
175	partition_block partitionBlock;
176	uint32 index = 0, cookie = 0;
177	status_t status;
178
179	while ((status = get_next_partition(fd, rdb, cookie, partitionBlock))
180			== B_OK) {
181		disk_environment &environment
182			= *(disk_environment *)&partitionBlock.environment[0];
183		TRACE(("amiga_rdb: file system: %s\n",
184			get_tupel(B_BENDIAN_TO_HOST_INT32(environment.dos_type))));
185
186		if (environment.Start() + environment.Size()
187				> (uint64)partition->size) {
188			TRACE(("amiga_rdb: child partition exceeds existing space (%lld "
189				"bytes)\n", environment.Size()));
190			continue;
191		}
192
193		partition_data *child = create_child_partition(partition->id, index++,
194			partition->offset + environment.Start(), environment.Size(), -1);
195		if (child == NULL) {
196			TRACE(("amiga_rdb: Creating child at index %ld failed\n",
197				index - 1));
198			return B_ERROR;
199		}
200
201		child->block_size = environment.BlockSize();
202	}
203
204	if (status == B_ENTRY_NOT_FOUND)
205		return B_OK;
206
207	return status;
208}
209
210
211static void
212amiga_rdb_free_identify_partition_cookie(partition_data *partition,
213	void *_cookie)
214{
215	delete (rigid_disk_block *)_cookie;
216}
217
218
219#ifndef _BOOT_MODE
220static partition_module_info sAmigaPartitionModule = {
221#else
222partition_module_info gAmigaPartitionModule = {
223#endif
224	{
225		AMIGA_PARTITION_MODULE_NAME,
226		0,
227		amiga_rdb_std_ops
228	},
229	"amiga",							// short_name
230	AMIGA_PARTITION_NAME,				// pretty_name
231	0,									// flags
232
233	// scanning
234	amiga_rdb_identify_partition,
235	amiga_rdb_scan_partition,
236	amiga_rdb_free_identify_partition_cookie,
237	NULL,
238};
239
240#ifndef _BOOT_MODE
241partition_module_info *modules[] = {
242	&sAmigaPartitionModule,
243	NULL
244};
245#endif
246
247