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.
10 #include "CachedBlock.h"
12 #include <boot/partitions.h>
13 #include <boot/platform.h>
22 //#define TRACE(x) dprintf x
23 #define TRACE(x) do {} while (0)
26 using namespace FATFS
;
30 Volume::Volume(boot::Partition
*partition
)
35 TRACE(("%s()\n", __FUNCTION__
));
36 if ((fDevice
= open_node(partition
, O_RDONLY
)) < B_OK
)
39 fCachedBlock
= new(nothrow
) CachedBlock(*this);
40 if (fCachedBlock
== NULL
)
44 /* = (char *)malloc(4096);
48 fBlockSize
= partition
->block_size
;
62 TRACE(("%s: reading bootsector\n", __FUNCTION__
));
64 buf
= fCachedBlock
->SetTo(0);
68 TRACE(("%s: checking signature\n", __FUNCTION__
));
69 // check the signature
70 if (((buf
[0x1fe] != 0x55) || (buf
[0x1ff] != 0xaa)) && (buf
[0x15] == 0xf8))
73 if (!memcmp(buf
+3, "NTFS ", 8) || !memcmp(buf
+3, "HPFS ", 8))
76 TRACE(("%s: signature ok\n", __FUNCTION__
));
77 fBytesPerSector
= read16(buf
,0xb);
78 switch (fBytesPerSector
) {
91 TRACE(("%s: block shift %d\n", __FUNCTION__
, fBlockShift
));
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:
101 TRACE(("%s: sect/cluster %d\n", __FUNCTION__
, fSectorsPerCluster
));
102 fClusterShift
= fSectorShift
;
103 for (uint32 spc
= fSectorsPerCluster
; !(spc
& 0x01); ) {
107 TRACE(("%s: cluster shift %d\n", __FUNCTION__
, fClusterShift
));
109 fReservedSectors
= read16(buf
,0xe);
110 fFatCount
= buf
[0x10];
111 if ((fFatCount
== 0) || (fFatCount
> 8))
114 fMediaDesc
= buf
[0x15];
115 if ((fMediaDesc
!= 0xf0) && (fMediaDesc
< 0xf8))
118 fSectorsPerFat
= read16(buf
,0x16);
119 if (fSectorsPerFat
== 0) {
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
)
132 fFSInfoSector
= read16(buf
, 0x30);
133 if (fFSInfoSector
< 1 || fFSInfoSector
> fTotalSectors
)
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
,
149 //if (fTotalSectors > partition->sectors_per_track * partition->cylinder_count * partition->head_count)
150 if ((off_t
)fTotalSectors
* fBytesPerSector
> partition
->size
)
153 TRACE(("%s: found fat%d filesystem, root dir at cluster %d\n", __FUNCTION__
,
154 fFatBits
, fRootDirCluster
));
156 fRoot
= new(nothrow
) Directory(*this, 0, fRootDirCluster
, "/");
160 TRACE(("fatfs: cannot mount (bad superblock ?)\n"));
161 // XXX !? this triple-faults in QEMU ..
162 //delete fCachedBlock;
177 if (fCachedBlock
== NULL
)
187 Volume::GetName(char *name
, size_t size
) const
190 return strlcpy(name
, "UNKNOWN", size
);
195 Volume::ClusterToOffset(uint32 cluster
) const
197 return (fDataStart
<< SectorShift()) + ((cluster
- 2) << ClusterShift());
201 Volume::NextCluster(uint32 cluster
, uint32 skip
)
203 //TRACE(("%s(%d, %d)\n", __FUNCTION__, cluster, skip));
204 // lookup the FAT for next cluster in chain
208 int fatBytes
= (FatBits() + 7) / 8;
216 return InvalidClusterID();
220 offset
= fBytesPerSector
* fReservedSectors
;
221 //offset += fActiveFat * fTotalClusters * fFatBits / 8;
222 offset
+= cluster
* fatBytes
;
224 if (!IsValidCluster(cluster
))
225 return InvalidClusterID();
227 buf
= fCachedBlock
->SetTo(ToBlock(offset
));
229 return InvalidClusterID();
231 offset
%= BlockSize();
235 next
= read32(buf
, offset
);
239 next
= read16(buf
, offset
);
242 return InvalidClusterID();
253 Volume::IsValidCluster(uint32 cluster
) const
255 if (cluster
> 1 && cluster
< fTotalClusters
)
262 Volume::IsLastCluster(uint32 cluster
) const
264 if (cluster
>= fTotalClusters
&& ((cluster
& 0xff8) == 0xff8))
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.
275 Volume::AllocateCluster(uint32 previousCluster
, uint32
& _newCluster
)
278 return B_UNSUPPORTED
;
279 // TODO: Support FAT16 and FAT12.
281 const int fatBytes
= FatBits() / 8;
282 const uint32 blockOffsetMask
= (uint32
)BlockSize() - 1;
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
));
292 uint32 value
= read32(buffer
, offset
& blockOffsetMask
);
294 // found one -- mark it used (end of file)
295 status_t error
= _UpdateCluster(i
, 0x0ffffff8);
299 // If a previous cluster was given, update its list link.
300 if (IsValidCluster(previousCluster
)) {
301 error
= _UpdateCluster(previousCluster
, i
);
303 _UpdateCluster(i
, 0);
308 _ClusterAllocated(i
);
315 return B_DEVICE_FULL
;
320 Volume::_UpdateCluster(uint32 cluster
, uint32 value
)
322 if (fFatBits
!= 32 && fFatBits
!= 16)
323 return B_UNSUPPORTED
;
324 // TODO: Support FAT12.
326 if (!IsValidCluster(cluster
))
327 return InvalidClusterID();
329 // get the buffer we need to change
330 const int fatBytes
= FatBits() / 8;
332 for (uint8 i
= 0; i
< fFatCount
; i
++) {
334 = fBytesPerSector
* (fReservedSectors
+ i
* fSectorsPerFat
);
335 offset
+= cluster
* fatBytes
;
337 uint8
* buffer
= fCachedBlock
->SetTo(ToBlock(offset
));
339 return InvalidClusterID();
341 offset
%= BlockSize();
346 *(uint32
*)(buffer
+ offset
) = B_HOST_TO_LENDIAN_INT32(value
);
349 *(uint16
*)(buffer
+ offset
) = B_HOST_TO_LENDIAN_INT16(value
);
352 return InvalidClusterID();
355 // write the block back to disk
356 status_t error
= fCachedBlock
->Flush();
358 fCachedBlock
->Unset();
368 Volume::_ClusterAllocated(uint32 cluster
)
370 // update the FS info
372 // exists only for FAT32
376 off_t offset
= fBytesPerSector
* fFSInfoSector
;
378 status_t error
= fCachedBlock
->SetTo(offset
/ BlockSize(),
383 uint8
* buffer
= fCachedBlock
->Block() + offset
% BlockSize();
385 // update number of free cluster
386 int32 freeClusters
= read32(buffer
, 0x1e8);
387 if (freeClusters
!= -1)
388 write32(buffer
, 0x1e8, freeClusters
- 1);
390 // update number of most recently allocated cluster
391 write32(buffer
, 0x1ec, cluster
);
393 // write the block back
394 error
= fCachedBlock
->Flush();
396 fCachedBlock
->Unset();
406 dosfs_identify_file_system(boot::Partition
*partition
)
408 TRACE(("%s()\n", __FUNCTION__
));
409 Volume
volume(partition
);
411 return volume
.InitCheck() < B_OK
? 0 : 0.8;
416 dosfs_get_file_system(boot::Partition
*partition
, ::Directory
**_root
)
418 TRACE(("%s()\n", __FUNCTION__
));
419 Volume
*volume
= new(nothrow
) Volume(partition
);
423 if (volume
->InitCheck() < B_OK
) {
428 *_root
= volume
->Root();
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