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
Volume(boot::Partition * partition)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
~Volume()166 Volume::~Volume()
167 {
168 delete fRoot;
169 delete fCachedBlock;
170 close(fDevice);
171 }
172
173
174 status_t
InitCheck()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
GetName(char * name,size_t size) const187 Volume::GetName(char *name, size_t size) const
188 {
189 //TODO: WRITEME
190 return strlcpy(name, "UNKNOWN", size);
191 }
192
193
194 off_t
ClusterToOffset(uint32 cluster) const195 Volume::ClusterToOffset(uint32 cluster) const
196 {
197 return (fDataStart << SectorShift()) + ((cluster - 2) << ClusterShift());
198 }
199
200 uint32
NextCluster(uint32 cluster,uint32 skip)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
IsValidCluster(uint32 cluster) const253 Volume::IsValidCluster(uint32 cluster) const
254 {
255 if (cluster > 1 && cluster < fTotalClusters)
256 return true;
257 return false;
258 }
259
260
261 bool
IsLastCluster(uint32 cluster) const262 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
AllocateCluster(uint32 previousCluster,uint32 & _newCluster)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
_UpdateCluster(uint32 cluster,uint32 value)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
_ClusterAllocated(uint32 cluster)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
dosfs_identify_file_system(boot::Partition * partition)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
dosfs_get_file_system(boot::Partition * partition,::Directory ** _root)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