1/*
2 * Copyright 2003-2013, Axel D��rfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "amiga_ffs.h"
8
9#include <boot/partitions.h>
10#include <boot/platform.h>
11
12#include <string.h>
13#include <unistd.h>
14#include <fcntl.h>
15#include <stdio.h>
16#include <stdlib.h>
17
18
19using namespace FFS;
20
21
22class BCPLString {
23	public:
24		uint8 Length() { return fLength; }
25		const char *String() { return (const char *)(&fLength + 1); }
26		int32 CopyTo(char *name, size_t size);
27
28	private:
29		uint8	fLength;
30};
31
32
33int32
34BCPLString::CopyTo(char *name, size_t size)
35{
36	int32 length = size - 1 > Length() ? Length() : size - 1;
37
38	memcpy(name, String(), length);
39	name[length] = '\0';
40
41	return length;
42}
43
44
45//	#pragma mark -
46
47
48status_t
49BaseBlock::GetNameBackOffset(int32 offset, char *name, size_t size) const
50{
51	BCPLString *string = (BCPLString *)&fData[fSize - offset];
52	string->CopyTo(name, size);
53
54	return B_OK;
55}
56
57
58status_t
59BaseBlock::ValidateCheckSum() const
60{
61	if (fData == NULL)
62		return B_NO_INIT;
63
64	int32 sum = 0;
65	for (int32 index = 0; index < fSize; index++) {
66		sum += Offset(index);
67	}
68
69	return sum == 0 ? B_OK : B_BAD_DATA;
70}
71
72
73//	#pragma mark -
74
75
76char
77DirectoryBlock::ToUpperChar(int32 type, char c) const
78{
79	// Taken from Ralph Babel's "The Amiga Guru Book" (1993), section 15.3.4.3
80
81	if (type == DT_AMIGA_OFS || type == DT_AMIGA_FFS)
82		return c >= 'a' && c <= 'z' ? c + ('A' - 'a') : c;
83
84	return (c >= '\340' && c <= '\376' && c != '\367')
85		|| (c >= 'a' && c <= 'z') ? c + ('A' - 'a') : c;
86}
87
88
89int32
90DirectoryBlock::HashIndexFor(int32 type, const char *name) const
91{
92	int32 hash = strlen(name);
93
94	while (name[0]) {
95		hash = (hash * 13 + ToUpperChar(type, name[0])) & 0x7ff;
96		name++;
97	}
98
99	return hash % HashSize();
100}
101
102
103int32
104DirectoryBlock::HashValueAt(int32 index) const
105{
106	return index >= HashSize() ? -1 : (int32)B_BENDIAN_TO_HOST_INT32(HashTable()[index]);
107}
108
109
110int32
111DirectoryBlock::FirstHashValue(int32 &index) const
112{
113	index = -1;
114	return NextHashValue(index);
115}
116
117
118int32
119DirectoryBlock::NextHashValue(int32 &index) const
120{
121	index++;
122
123	int32 value;
124	while ((value = HashValueAt(index)) == 0) {
125		if (++index >= HashSize())
126			return -1;
127	}
128
129	return value;
130}
131
132
133//	#pragma mark -
134
135
136HashIterator::HashIterator(int32 device, DirectoryBlock &directory)
137	:
138	fDirectory(directory),
139	fDevice(device),
140	fCurrent(0),
141	fBlock(-1)
142{
143	fData = (int32 *)malloc(directory.BlockSize());
144	fNode.SetTo(directory.BlockData(), directory.BlockSize());
145}
146
147
148HashIterator::~HashIterator()
149{
150	free(fData);
151}
152
153
154status_t
155HashIterator::InitCheck()
156{
157	return fData != NULL ? B_OK : B_NO_MEMORY;
158}
159
160
161void
162HashIterator::Goto(int32 index)
163{
164	fCurrent = index;
165	fBlock = fDirectory.HashValueAt(index);
166}
167
168
169NodeBlock *
170HashIterator::GetNext(int32 &block)
171{
172	if (fBlock == -1) {
173		// first entry
174		fBlock = fDirectory.FirstHashValue(fCurrent);
175	} else if (fBlock == 0) {
176		fBlock = fDirectory.NextHashValue(fCurrent);
177	}
178
179	if (fBlock == -1)
180		return NULL;
181
182	block = fBlock;
183
184	if (read_pos(fDevice, fBlock * fNode.BlockSize(), fData, fNode.BlockSize()) < B_OK)
185		return NULL;
186
187	fNode.SetTo(fData);
188	if (fNode.ValidateCheckSum() != B_OK) {
189		dprintf("block at %" B_PRId32 " bad checksum.\n", fBlock);
190		return NULL;
191	}
192
193	fBlock = fNode.HashChain();
194
195	return &fNode;
196}
197
198
199void
200HashIterator::Rewind()
201{
202	fCurrent = 0;
203	fBlock = -1;
204}
205
206
207//	#pragma mark -
208
209
210status_t
211FFS::get_root_block(int fDevice, char *buffer, int32 blockSize, off_t partitionSize)
212{
213	// calculate root block position (it depends on the block size)
214
215	// ToDo: get the number of reserved blocks out of the disk_environment structure??
216	//		(from the amiga_rdb module)
217	int32 reservedBlocks = 2;
218	off_t offset = (((partitionSize / blockSize) - 1 - reservedBlocks) / 2) + reservedBlocks;
219		// ToDo: this calculation might be incorrect for certain cases.
220
221	if (read_pos(fDevice, offset * blockSize, buffer, blockSize) < B_OK)
222		return B_ERROR;
223
224	RootBlock root(buffer, blockSize);
225	if (root.ValidateCheckSum() < B_OK)
226		return B_BAD_DATA;
227
228	//printf("primary = %ld, secondary = %ld\n", root.PrimaryType(), root.SecondaryType());
229	if (!root.IsRootBlock())
230		return B_BAD_TYPE;
231
232	return B_OK;
233}
234
235