/*
* Copyright 2003-2013, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2008, François Revol <revol@free.fr>
* Distributed under the terms of the MIT License.
*/
#include "Volume.h"
#include "Directory.h"
#include "CachedBlock.h"
#include <boot/partitions.h>
#include <boot/platform.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
//#define TRACE(x) dprintf x
#define TRACE(x) do {} while (0)
using namespace FATFS;
using std::nothrow;
Volume::Volume(boot::Partition *partition)
:
fCachedBlock(NULL),
fRoot(NULL)
{
TRACE(("%s()\n", __FUNCTION__));
if ((fDevice = open_node(partition, O_RDONLY)) < B_OK)
return;
fCachedBlock = new(nothrow) CachedBlock(*this);
if (fCachedBlock == NULL)
return;
uint8 *buf;
/* = (char *)malloc(4096);
if (buf == NULL)
return;*/
fBlockSize = partition->block_size;
switch (fBlockSize) {
case 0x200:
fBlockShift = 9;
break;
case 0x400:
fBlockShift = 10;
break;
case 0x800:
fBlockShift = 11;
break;
default:
goto err1;
}
TRACE(("%s: reading bootsector\n", __FUNCTION__));
// read boot sector
buf = fCachedBlock->SetTo(0);
if (buf == NULL)
goto err1;
TRACE(("%s: checking signature\n", __FUNCTION__));
// check the signature
if (((buf[0x1fe] != 0x55) || (buf[0x1ff] != 0xaa)) && (buf[0x15] == 0xf8))
goto err1;
if (!memcmp(buf+3, "NTFS ", 8) || !memcmp(buf+3, "HPFS ", 8))
goto err1;
TRACE(("%s: signature ok\n", __FUNCTION__));
fBytesPerSector = read16(buf,0xb);
switch (fBytesPerSector) {
case 0x200:
fSectorShift = 9;
break;
case 0x400:
fSectorShift = 10;
break;
case 0x800:
fSectorShift = 11;
break;
default:
goto err1;
}
TRACE(("%s: block shift %d\n", __FUNCTION__, fBlockShift));
fSectorsPerCluster = buf[0xd];
switch (fSectorsPerCluster) {
case 1: case 2: case 4: case 8:
case 0x10: case 0x20: case 0x40: case 0x80:
break;
default:
goto err1;
}
TRACE(("%s: sect/cluster %d\n", __FUNCTION__, fSectorsPerCluster));
fClusterShift = fSectorShift;
for (uint32 spc = fSectorsPerCluster; !(spc & 0x01); ) {
spc >>= 1;
fClusterShift += 1;
}
TRACE(("%s: cluster shift %d\n", __FUNCTION__, fClusterShift));
fReservedSectors = read16(buf,0xe);
fFatCount = buf[0x10];
if ((fFatCount == 0) || (fFatCount > 8))
goto err1;
fMediaDesc = buf[0x15];
if ((fMediaDesc != 0xf0) && (fMediaDesc < 0xf8))
goto err1;
fSectorsPerFat = read16(buf,0x16);
if (fSectorsPerFat == 0) {
// FAT32
fFatBits = 32;
fSectorsPerFat = read32(buf,0x24);
fTotalSectors = read32(buf,0x20);
bool lFatMirrored = !(buf[0x28] & 0x80);
fActiveFat = (lFatMirrored) ? (buf[0x28] & 0xf) : 0;
fDataStart = fReservedSectors + fFatCount * fSectorsPerFat;
fTotalClusters = (fTotalSectors - fDataStart) / fSectorsPerCluster;
fRootDirCluster = read32(buf,0x2c);
if (fRootDirCluster >= fTotalClusters)
goto err1;
fFSInfoSector = read16(buf, 0x30);
if (fFSInfoSector < 1 || fFSInfoSector > fTotalSectors)
goto err1;
} else {
// FAT12/16
// XXX:FIXME
fFatBits = 16;
goto err1;
}
TRACE(("%s: block size %d, sector size %d, sectors/cluster %d\n",
__FUNCTION__, fBlockSize, fBytesPerSector, fSectorsPerCluster));
TRACE(("%s: block shift %d, sector shift %d, cluster shift %d\n",
__FUNCTION__, fBlockShift, fSectorShift, fClusterShift));
TRACE(("%s: reserved %d, max root entries %d, media %02x, sectors/fat %d\n",
__FUNCTION__, fReservedSectors, fMaxRootEntries, fMediaDesc,
fSectorsPerFat));
//if (fTotalSectors > partition->sectors_per_track * partition->cylinder_count * partition->head_count)
if ((off_t)fTotalSectors * fBytesPerSector > partition->size)
goto err1;
TRACE(("%s: found fat%d filesystem, root dir at cluster %d\n", __FUNCTION__,
fFatBits, fRootDirCluster));
fRoot = new(nothrow) Directory(*this, 0, fRootDirCluster, "/");
return;
err1:
TRACE(("fatfs: cannot mount (bad superblock ?)\n"));
// XXX !? this triple-faults in QEMU ..
//delete fCachedBlock;
}
Volume::~Volume()
{
delete fRoot;
delete fCachedBlock;
close(fDevice);
}
status_t
Volume::InitCheck()
{
if (fCachedBlock == NULL)
return B_ERROR;
if (fRoot == NULL)
return B_ERROR;
return B_OK;
}
status_t
Volume::GetName(char *name, size_t size) const
{
//TODO: WRITEME
return strlcpy(name, "UNKNOWN", size);
}
off_t
Volume::ClusterToOffset(uint32 cluster) const
{
return (fDataStart << SectorShift()) + ((cluster - 2) << ClusterShift());
}
uint32
Volume::NextCluster(uint32 cluster, uint32 skip)
{
//TRACE(("%s(%d, %d)\n", __FUNCTION__, cluster, skip));
// lookup the FAT for next cluster in chain
off_t offset;
uint8 *buf;
int32 next;
int fatBytes = (FatBits() + 7) / 8;
switch (fFatBits) {
case 32:
case 16:
break;
//XXX handle FAT12
default:
return InvalidClusterID();
}
again:
offset = fBytesPerSector * fReservedSectors;
//offset += fActiveFat * fTotalClusters * fFatBits / 8;
offset += cluster * fatBytes;
if (!IsValidCluster(cluster))
return InvalidClusterID();
buf = fCachedBlock->SetTo(ToBlock(offset));
if (!buf)
return InvalidClusterID();
offset %= BlockSize();
switch (fFatBits) {
case 32:
next = read32(buf, offset);
next &= 0x0fffffff;
break;
case 16:
next = read16(buf, offset);
break;
default:
return InvalidClusterID();
}
if (skip--) {
cluster = next;
goto again;
}
return next;
}
bool
Volume::IsValidCluster(uint32 cluster) const
{
if (cluster > 1 && cluster < fTotalClusters)
return true;
return false;
}
bool
Volume::IsLastCluster(uint32 cluster) const
{
if (cluster >= fTotalClusters && ((cluster & 0xff8) == 0xff8))
return true;
return false;
}
/*! Allocates a free cluster.
If \a previousCluster is a valid cluster idnex, its chain pointer is
changed to point to the newly allocated cluster.
*/
status_t
Volume::AllocateCluster(uint32 previousCluster, uint32& _newCluster)
{
if (fFatBits != 32)
return B_UNSUPPORTED;
// TODO: Support FAT16 and FAT12.
const int fatBytes = FatBits() / 8;
const uint32 blockOffsetMask = (uint32)BlockSize() - 1;
// Iterate through the FAT to find a free cluster.
off_t offset = fBytesPerSector * fReservedSectors;
offset += 2 * fatBytes;
for (uint32 i = 2; i < fTotalClusters; i++, offset += fatBytes) {
uint8* buffer = fCachedBlock->SetTo(ToBlock(offset));
if (buffer == NULL)
return B_ERROR;
uint32 value = read32(buffer, offset & blockOffsetMask);
if (value == 0) {
// found one -- mark it used (end of file)
status_t error = _UpdateCluster(i, 0x0ffffff8);
if (error != B_OK)
return error;
// If a previous cluster was given, update its list link.
if (IsValidCluster(previousCluster)) {
error = _UpdateCluster(previousCluster, i);
if (error != B_OK) {
_UpdateCluster(i, 0);
return error;
}
}
_ClusterAllocated(i);
_newCluster = i;
return B_OK;
}
}
return B_DEVICE_FULL;
}
status_t
Volume::_UpdateCluster(uint32 cluster, uint32 value)
{
if (fFatBits != 32 && fFatBits != 16)
return B_UNSUPPORTED;
// TODO: Support FAT12.
if (!IsValidCluster(cluster))
return InvalidClusterID();
// get the buffer we need to change
const int fatBytes = FatBits() / 8;
for (uint8 i = 0; i < fFatCount; i++) {
off_t offset
= fBytesPerSector * (fReservedSectors + i * fSectorsPerFat);
offset += cluster * fatBytes;
uint8* buffer = fCachedBlock->SetTo(ToBlock(offset));
if (buffer == NULL)
return InvalidClusterID();
offset %= BlockSize();
// set the value
switch (fFatBits) {
case 32:
*(uint32*)(buffer + offset) = B_HOST_TO_LENDIAN_INT32(value);
break;
case 16:
*(uint16*)(buffer + offset) = B_HOST_TO_LENDIAN_INT16(value);
break;
default:
return InvalidClusterID();
}
// write the block back to disk
status_t error = fCachedBlock->Flush();
if (error != B_OK) {
fCachedBlock->Unset();
return error;
}
}
return B_OK;
}
status_t
Volume::_ClusterAllocated(uint32 cluster)
{
// update the FS info
// exists only for FAT32
if (fFatBits != 32)
return B_OK;
off_t offset = fBytesPerSector * fFSInfoSector;
status_t error = fCachedBlock->SetTo(offset / BlockSize(),
CachedBlock::READ);
if (error != B_OK)
return error;
uint8* buffer = fCachedBlock->Block() + offset % BlockSize();
// update number of free cluster
int32 freeClusters = read32(buffer, 0x1e8);
if (freeClusters != -1)
write32(buffer, 0x1e8, freeClusters - 1);
// update number of most recently allocated cluster
write32(buffer, 0x1ec, cluster);
// write the block back
error = fCachedBlock->Flush();
if (error != B_OK)
fCachedBlock->Unset();
return error;
}
// #pragma mark -
float
dosfs_identify_file_system(boot::Partition *partition)
{
TRACE(("%s()\n", __FUNCTION__));
Volume volume(partition);
return volume.InitCheck() < B_OK ? 0 : 0.8;
}
static status_t
dosfs_get_file_system(boot::Partition *partition, ::Directory **_root)
{
TRACE(("%s()\n", __FUNCTION__));
Volume *volume = new(nothrow) Volume(partition);
if (volume == NULL)
return B_NO_MEMORY;
if (volume->InitCheck() < B_OK) {
delete volume;
return B_ERROR;
}
*_root = volume->Root();
return B_OK;
}
file_system_module_info gFATFileSystemModule = {
"file_systems/dosfs/v1",
kPartitionTypeFAT32, // XXX:FIXME: FAT16 too ?
dosfs_identify_file_system,
dosfs_get_file_system
};
↑ V629 Consider inspecting the 'fDataStart << SectorShift()' expression. Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fMaxRootEntries.