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