xref: /haiku/src/system/boot/loader/file_systems/fat/Volume.cpp (revision 4bd0c1066b227cec4b79883bdef697c7a27f2e90)
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 using std::nothrow;
28 
29 
30 Volume::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 
159 err1:
160 	TRACE(("fatfs: cannot mount (bad superblock ?)\n"));
161 	// XXX !? this triple-faults in QEMU ..
162 	//delete fCachedBlock;
163 }
164 
165 
166 Volume::~Volume()
167 {
168 	delete fRoot;
169 	delete fCachedBlock;
170 	close(fDevice);
171 }
172 
173 
174 status_t
175 Volume::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 
186 status_t
187 Volume::GetName(char *name, size_t size) const
188 {
189 	//TODO: WRITEME
190 	return strlcpy(name, "UNKNOWN", size);
191 }
192 
193 
194 off_t
195 Volume::ClusterToOffset(uint32 cluster) const
196 {
197 	return (fDataStart << SectorShift()) + ((cluster - 2) << ClusterShift());
198 }
199 
200 uint32
201 Volume::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 
219 again:
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 
252 bool
253 Volume::IsValidCluster(uint32 cluster) const
254 {
255 	if (cluster > 1 && cluster < fTotalClusters)
256 		return true;
257 	return false;
258 }
259 
260 
261 bool
262 Volume::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 */
274 status_t
275 Volume::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 
319 status_t
320 Volume::_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 
367 status_t
368 Volume::_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 
405 float
406 dosfs_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 
415 static status_t
416 dosfs_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 
433 file_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