xref: /haiku/src/system/boot/loader/file_systems/amiga_ffs/amiga_ffs.cpp (revision 9642f7705b27e5c270c15fa526d14e1848c2c27d)
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 
19 using namespace FFS;
20 
21 
22 class 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 
33 int32
34 BCPLString::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 
48 status_t
49 BaseBlock::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 
58 status_t
59 BaseBlock::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 
76 char
77 DirectoryBlock::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 
89 int32
90 DirectoryBlock::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 
103 int32
104 DirectoryBlock::HashValueAt(int32 index) const
105 {
106 	return index >= HashSize() ? -1 : (int32)B_BENDIAN_TO_HOST_INT32(HashTable()[index]);
107 }
108 
109 
110 int32
111 DirectoryBlock::FirstHashValue(int32 &index) const
112 {
113 	index = -1;
114 	return NextHashValue(index);
115 }
116 
117 
118 int32
119 DirectoryBlock::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 
136 HashIterator::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 
148 HashIterator::~HashIterator()
149 {
150 	free(fData);
151 }
152 
153 
154 status_t
155 HashIterator::InitCheck()
156 {
157 	return fData != NULL ? B_OK : B_NO_MEMORY;
158 }
159 
160 
161 void
162 HashIterator::Goto(int32 index)
163 {
164 	fCurrent = index;
165 	fBlock = fDirectory.HashValueAt(index);
166 }
167 
168 
169 NodeBlock *
170 HashIterator::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 
199 void
200 HashIterator::Rewind()
201 {
202 	fCurrent = 0;
203 	fBlock = -1;
204 }
205 
206 
207 //	#pragma mark -
208 
209 
210 status_t
211 FFS::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