xref: /haiku/src/system/boot/loader/file_systems/fat/Volume.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
1 /*
2 ** Copyright 2003, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3 ** Distributed under the terms of the OpenBeOS License.
4 */
5 
6 
7 #include "Volume.h"
8 #include "Directory.h"
9 #include "CachedBlock.h"
10 
11 #include <boot/partitions.h>
12 #include <boot/platform.h>
13 #include <util/kernel_cpp.h>
14 
15 #include <string.h>
16 #include <unistd.h>
17 #include <fcntl.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 
21 //#define TRACE(x) dprintf x
22 #define TRACE(x) do {} while (0)
23 
24 using namespace FATFS;
25 
26 
27 Volume::Volume(boot::Partition *partition)
28 	:
29 	fCachedBlock(NULL),
30 	fRoot(NULL)
31 {
32 	TRACE(("%s()\n", __FUNCTION__));
33 	if ((fDevice = open_node(partition, O_RDONLY)) < B_OK)
34 		return;
35 
36 	fCachedBlock = new CachedBlock(*this);
37 	if (fCachedBlock == NULL)
38 		return;
39 
40 	uint8 *buf;
41 	/* = (char *)malloc(4096);
42 	if (buf == NULL)
43 		return;*/
44 
45 	fBlockSize = partition->block_size;
46 	switch (fBlockSize) {
47 	case 0x200:
48 		fBlockShift = 9;
49 		break;
50 	case 0x400:
51 		fBlockShift = 10;
52 		break;
53 	case 0x800:
54 		fBlockShift = 11;
55 		break;
56 	default:
57 		goto err1;
58 	}
59 	TRACE(("%s: reading bootsector\n", __FUNCTION__));
60 	// read boot sector
61 	buf = fCachedBlock->SetTo(0);
62 	if (buf == NULL)
63 		goto err1;
64 
65 	TRACE(("%s: checking signature\n", __FUNCTION__));
66 	// check the signature
67 	if (((buf[0x1fe] != 0x55) || (buf[0x1ff] != 0xaa)) && (buf[0x15] == 0xf8))
68 		goto err1;
69 
70 	if (!memcmp(buf+3, "NTFS    ", 8) || !memcmp(buf+3, "HPFS    ", 8))
71 		goto err1;
72 
73 	TRACE(("%s: signature ok\n", __FUNCTION__));
74 	fBytesPerSector = read16(buf,0xb);
75 	switch (fBytesPerSector) {
76 	case 0x200:
77 		fSectorShift = 9;
78 		break;
79 	case 0x400:
80 		fSectorShift = 10;
81 		break;
82 	case 0x800:
83 		fSectorShift = 11;
84 		break;
85 	default:
86 		goto err1;
87 	}
88 	TRACE(("%s: block shift %d\n", __FUNCTION__, fBlockShift));
89 
90 	fSectorsPerCluster = buf[0xd];
91 	switch (fSectorsPerCluster) {
92 	case 1:	case 2:	case 4:	case 8:
93 	case 0x10:	case 0x20:	case 0x40:	case 0x80:
94 		break;
95 	default:
96 		goto err1;
97 	}
98 	TRACE(("%s: sect/cluster %d\n", __FUNCTION__, fSectorsPerCluster));
99 	fClusterShift = fSectorShift;
100 	for (uint32 spc = fSectorsPerCluster; !(spc & 0x01); ) {
101 		spc >>= 1;
102 		fClusterShift += 1;
103 	}
104 	TRACE(("%s: cluster shift %d\n", __FUNCTION__, fClusterShift));
105 
106 	fReservedSectors = read16(buf,0xe);
107 	fFatCount = buf[0x10];
108 	if ((fFatCount == 0) || (fFatCount > 8))
109 		goto err1;
110 
111 	fMediaDesc = buf[0x15];
112 	if ((fMediaDesc != 0xf0) && (fMediaDesc < 0xf8))
113 		goto err1;
114 
115 	fSectorsPerFat = read16(buf,0x16);
116 	if (fSectorsPerFat == 0) {
117 		// FAT32
118 		fFatBits = 32;
119 		fSectorsPerFat = read32(buf,0x24);
120 		fTotalSectors = read32(buf,0x20);
121 		bool lFatMirrored = !(buf[0x28] & 0x80);
122 		fActiveFat = (lFatMirrored) ? (buf[0x28] & 0xf) : 0;
123 		fDataStart = fReservedSectors + fFatCount * fSectorsPerFat;
124 		fTotalClusters = (fTotalSectors - fDataStart) / fSectorsPerCluster;
125 		fRootDirCluster = read32(buf,0x2c);
126 		if (fRootDirCluster >= fTotalClusters)
127 			goto err1;
128 
129 		fFSInfoSector = read16(buf, 0x30);
130 		if (fFSInfoSector < 1 || fFSInfoSector > fTotalSectors)
131 			goto err1;
132 	} else {
133 		// FAT12/16
134 		// XXX:FIXME
135 		fFatBits = 16;
136 		goto err1;
137 	}
138 	TRACE(("%s: block size %d, sector size %d, sectors/cluster %d\n", __FUNCTION__,
139 		fBlockSize, fBytesPerSector, fSectorsPerCluster));
140 	TRACE(("%s: block shift %d, sector shift %d, cluster shift %d\n", __FUNCTION__,
141 		fBlockShift, fSectorShift, fClusterShift));
142 	TRACE(("%s: reserved %d, max root entries %d, media %02x, sectors/fat %d\n", __FUNCTION__,
143 		fReservedSectors, fMaxRootEntries, fMediaDesc, fSectorsPerFat));
144 
145 
146 	//if (fTotalSectors > partition->sectors_per_track * partition->cylinder_count * partition->head_count)
147 	if ((off_t)fTotalSectors * fBytesPerSector > partition->size)
148 		goto err1;
149 
150 	TRACE(("%s: found fat%d filesystem, root dir at cluster %d\n", __FUNCTION__,
151 		fFatBits, fRootDirCluster));
152 
153 	fRoot = new Directory(*this, 0, fRootDirCluster, "/");
154 	return;
155 
156 err1:
157 	TRACE("fatfs: cannot mount (bad superblock ?)\n");
158 	// XXX !? this triple-faults in QEMU ..
159 	//delete fCachedBlock;
160 }
161 
162 
163 Volume::~Volume()
164 {
165 	delete fRoot;
166 	delete fCachedBlock;
167 	close(fDevice);
168 }
169 
170 
171 status_t
172 Volume::InitCheck()
173 {
174 	if (fCachedBlock == NULL)
175 		return B_ERROR;
176 	if (fRoot == NULL)
177 		return B_ERROR;
178 
179 	return B_OK;
180 }
181 
182 
183 status_t
184 Volume::GetName(char *name, size_t size) const
185 {
186 	//TODO: WRITEME
187 	return strlcpy(name, "UNKNOWN", size);
188 }
189 
190 
191 off_t
192 Volume::ClusterToOffset(uint32 cluster) const
193 {
194 	return (fDataStart << SectorShift()) + ((cluster - 2) << ClusterShift());
195 }
196 
197 uint32
198 Volume::NextCluster(uint32 cluster, uint32 skip)
199 {
200 	//TRACE(("%s(%d, %d)\n", __FUNCTION__, cluster, skip));
201 	// lookup the FAT for next cluster in chain
202 	off_t offset;
203 	uint8 *buf;
204 	int32 next;
205 	int fatBytes = (FatBits() + 7) / 8;
206 
207 	switch (fFatBits) {
208 		case 32:
209 		case 16:
210 			break;
211 		//XXX handle FAT12
212 		default:
213 			return InvalidClusterID();
214 	}
215 
216 again:
217 	offset = fBytesPerSector * fReservedSectors;
218 	//offset += fActiveFat * fTotalClusters * fFatBits / 8;
219 	offset += cluster * fatBytes;
220 
221 	if (!IsValidCluster(cluster))
222 		return InvalidClusterID();
223 
224 	buf = fCachedBlock->SetTo(ToBlock(offset));
225 	if (!buf)
226 		return InvalidClusterID();
227 
228 	offset %= BlockSize();
229 
230 	switch (fFatBits) {
231 		case 32:
232 			next = read32(buf, offset);
233 			next &= 0x0fffffff;
234 			break;
235 		case 16:
236 			next = read16(buf, offset);
237 			break;
238 		default:
239 			return InvalidClusterID();
240 	}
241 	if (skip--) {
242 		cluster = next;
243 		goto again;
244 	}
245 	return next;
246 }
247 
248 
249 bool
250 Volume::IsValidCluster(uint32 cluster) const
251 {
252 	if (cluster > 1 && cluster < fTotalClusters)
253 		return true;
254 	return false;
255 }
256 
257 
258 bool
259 Volume::IsLastCluster(uint32 cluster) const
260 {
261 	if (cluster >= fTotalClusters && ((cluster & 0xff8) == 0xff8))
262 		return true;
263 	return false;
264 }
265 
266 
267 /*!	Allocates a free cluster.
268 	If \a previousCluster is a valid cluster idnex, its chain pointer is
269 	changed to point to the newly allocated cluster.
270 */
271 status_t
272 Volume::AllocateCluster(uint32 previousCluster, uint32& _newCluster)
273 {
274 	if (fFatBits != 32)
275 		return B_UNSUPPORTED;
276 		// TODO: Support FAT16 and FAT12.
277 
278 	const int fatBytes = FatBits() / 8;
279 	const uint32 blockOffsetMask = (uint32)BlockSize() - 1;
280 
281 	// Iterate through the FAT to find a free cluster.
282 	off_t offset = fBytesPerSector * fReservedSectors;
283 	offset += 2 * fatBytes;
284 	for (uint32 i = 2; i < fTotalClusters; i++, offset += fatBytes) {
285 		uint8* buffer = fCachedBlock->SetTo(ToBlock(offset));
286 		if (buffer == NULL)
287 			return B_ERROR;
288 
289 		uint32 value = read32(buffer, offset & blockOffsetMask);
290 		if (value == 0) {
291 			// found one -- mark it used (end of file)
292 			status_t error = _UpdateCluster(i, 0x0ffffff8);
293 			if (error != B_OK)
294 				return error;
295 
296 			// If a previous cluster was given, update its list link.
297 			if (IsValidCluster(previousCluster)) {
298 				error = _UpdateCluster(previousCluster, i);
299 				if (error != B_OK) {
300 					_UpdateCluster(i, 0);
301 					return error;
302 				}
303 			}
304 
305 			_ClusterAllocated(i);
306 
307 			_newCluster = i;
308 			return B_OK;
309 		}
310 	}
311 
312 	return B_DEVICE_FULL;
313 }
314 
315 
316 status_t
317 Volume::_UpdateCluster(uint32 cluster, uint32 value)
318 {
319 	if (fFatBits != 32 && fFatBits != 16)
320 		return B_UNSUPPORTED;
321 		// TODO: Support FAT12.
322 
323 	if (!IsValidCluster(cluster))
324 		return InvalidClusterID();
325 
326 	// get the buffer we need to change
327 	const int fatBytes = FatBits() / 8;
328 
329 	for (uint8 i = 0; i < fFatCount; i++) {
330 		off_t offset
331 			= fBytesPerSector * (fReservedSectors + i * fSectorsPerFat);
332 		offset += cluster * fatBytes;
333 
334 		uint8* buffer = fCachedBlock->SetTo(ToBlock(offset));
335 		if (buffer == NULL)
336 			return InvalidClusterID();
337 
338 		offset %= BlockSize();
339 
340 		// set the value
341 		switch (fFatBits) {
342 			case 32:
343 				*(uint32*)(buffer + offset) = B_HOST_TO_LENDIAN_INT32(value);
344 				break;
345 			case 16:
346 				*(uint16*)(buffer + offset) = B_HOST_TO_LENDIAN_INT16(value);
347 				break;
348 			default:
349 				return InvalidClusterID();
350 		}
351 
352 		// write the block back to disk
353 		status_t error = fCachedBlock->Flush();
354 		if (error != B_OK) {
355 			fCachedBlock->Unset();
356 			return error;
357 		}
358 	}
359 
360 	return B_OK;
361 }
362 
363 
364 status_t
365 Volume::_ClusterAllocated(uint32 cluster)
366 {
367 	// update the FS info
368 
369 	// exists only for FAT32
370 	if (fFatBits != 32)
371 		return B_OK;
372 
373 	off_t offset = fBytesPerSector * fFSInfoSector;
374 
375 	status_t error = fCachedBlock->SetTo(offset / BlockSize(),
376 		CachedBlock::READ);
377 	if (error != B_OK)
378 		return error;
379 
380 	uint8* buffer = fCachedBlock->Block() + offset % BlockSize();
381 
382 	// update number of free cluster
383 	int32 freeClusters = read32(buffer, 0x1e8);
384 	if (freeClusters != -1)
385 		write32(buffer, 0x1e8, freeClusters - 1);
386 
387 	// update number of most recently allocated cluster
388 	write32(buffer, 0x1ec, cluster);
389 
390 	// write the block back
391 	error = fCachedBlock->Flush();
392 	if (error != B_OK)
393 		fCachedBlock->Unset();
394 
395 	return error;
396 }
397 
398 
399 //	#pragma mark -
400 
401 
402 float
403 dosfs_identify_file_system(boot::Partition *partition)
404 {
405 	TRACE(("%s()\n", __FUNCTION__));
406 	Volume volume(partition);
407 
408 	return volume.InitCheck() < B_OK ? 0 : 0.8;
409 }
410 
411 
412 static status_t
413 dosfs_get_file_system(boot::Partition *partition, ::Directory **_root)
414 {
415 	TRACE(("%s()\n", __FUNCTION__));
416 	Volume *volume = new Volume(partition);
417 	if (volume == NULL)
418 		return B_NO_MEMORY;
419 
420 	if (volume->InitCheck() < B_OK) {
421 		delete volume;
422 		return B_ERROR;
423 	}
424 
425 	*_root = volume->Root();
426 	return B_OK;
427 }
428 
429 
430 file_system_module_info gFATFileSystemModule = {
431 	"file_systems/dosfs/v1",
432 	kPartitionTypeFAT32, // XXX:FIXME: FAT16 too ?
433 	dosfs_identify_file_system,
434 	dosfs_get_file_system
435 };
436 
437