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