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