2006-07-14 04:42:37 +02:00
|
|
|
/*
|
|
|
|
partition.c
|
|
|
|
Functions for mounting and dismounting partitions
|
|
|
|
on various block devices.
|
|
|
|
|
|
|
|
Copyright (c) 2006 Michael "Chishm" Chisholm
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without modification,
|
|
|
|
are permitted provided that the following conditions are met:
|
|
|
|
|
|
|
|
1. Redistributions of source code must retain the above copyright notice,
|
|
|
|
this list of conditions and the following disclaimer.
|
|
|
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
this list of conditions and the following disclaimer in the documentation and/or
|
|
|
|
other materials provided with the distribution.
|
|
|
|
3. The name of the author may not be used to endorse or promote products derived
|
|
|
|
from this software without specific prior written permission.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
|
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
|
|
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
|
|
|
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
|
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include "partition.h"
|
|
|
|
#include "bit_ops.h"
|
|
|
|
#include "file_allocation_table.h"
|
|
|
|
#include "directory.h"
|
2008-11-20 06:22:28 +01:00
|
|
|
#include "mem_allocate.h"
|
|
|
|
#include "fatfile.h"
|
2006-07-14 04:42:37 +02:00
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <ctype.h>
|
2008-11-20 06:22:28 +01:00
|
|
|
#include <sys/iosupport.h>
|
2006-07-14 04:42:37 +02:00
|
|
|
|
|
|
|
/*
|
2008-05-10 21:35:18 +02:00
|
|
|
This device name, as known by devkitPro toolchains
|
2006-07-14 04:42:37 +02:00
|
|
|
*/
|
|
|
|
const char* DEVICE_NAME = "fat";
|
|
|
|
|
|
|
|
/*
|
|
|
|
Data offsets
|
|
|
|
*/
|
|
|
|
|
|
|
|
// BIOS Parameter Block offsets
|
|
|
|
enum BPB {
|
|
|
|
BPB_jmpBoot = 0x00,
|
|
|
|
BPB_OEMName = 0x03,
|
|
|
|
// BIOS Parameter Block
|
|
|
|
BPB_bytesPerSector = 0x0B,
|
|
|
|
BPB_sectorsPerCluster = 0x0D,
|
|
|
|
BPB_reservedSectors = 0x0E,
|
|
|
|
BPB_numFATs = 0x10,
|
|
|
|
BPB_rootEntries = 0x11,
|
|
|
|
BPB_numSectorsSmall = 0x13,
|
|
|
|
BPB_mediaDesc = 0x15,
|
|
|
|
BPB_sectorsPerFAT = 0x16,
|
|
|
|
BPB_sectorsPerTrk = 0x18,
|
|
|
|
BPB_numHeads = 0x1A,
|
|
|
|
BPB_numHiddenSectors = 0x1C,
|
|
|
|
BPB_numSectors = 0x20,
|
|
|
|
// Ext BIOS Parameter Block for FAT16
|
|
|
|
BPB_FAT16_driveNumber = 0x24,
|
|
|
|
BPB_FAT16_reserved1 = 0x25,
|
|
|
|
BPB_FAT16_extBootSig = 0x26,
|
|
|
|
BPB_FAT16_volumeID = 0x27,
|
|
|
|
BPB_FAT16_volumeLabel = 0x2B,
|
|
|
|
BPB_FAT16_fileSysType = 0x36,
|
|
|
|
// Bootcode
|
|
|
|
BPB_FAT16_bootCode = 0x3E,
|
|
|
|
// FAT32 extended block
|
|
|
|
BPB_FAT32_sectorsPerFAT32 = 0x24,
|
|
|
|
BPB_FAT32_extFlags = 0x28,
|
|
|
|
BPB_FAT32_fsVer = 0x2A,
|
|
|
|
BPB_FAT32_rootClus = 0x2C,
|
|
|
|
BPB_FAT32_fsInfo = 0x30,
|
|
|
|
BPB_FAT32_bkBootSec = 0x32,
|
|
|
|
// Ext BIOS Parameter Block for FAT32
|
|
|
|
BPB_FAT32_driveNumber = 0x40,
|
|
|
|
BPB_FAT32_reserved1 = 0x41,
|
|
|
|
BPB_FAT32_extBootSig = 0x42,
|
|
|
|
BPB_FAT32_volumeID = 0x43,
|
|
|
|
BPB_FAT32_volumeLabel = 0x47,
|
|
|
|
BPB_FAT32_fileSysType = 0x52,
|
|
|
|
// Bootcode
|
|
|
|
BPB_FAT32_bootCode = 0x5A,
|
|
|
|
BPB_bootSig_55 = 0x1FE,
|
|
|
|
BPB_bootSig_AA = 0x1FF
|
|
|
|
};
|
|
|
|
|
2008-11-20 06:22:28 +01:00
|
|
|
static const char FAT_SIG[3] = {'F', 'A', 'T'};
|
2006-07-14 04:42:37 +02:00
|
|
|
|
|
|
|
|
2008-11-20 06:22:28 +01:00
|
|
|
PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cacheSize, sec_t startSector) {
|
2006-07-14 04:42:37 +02:00
|
|
|
PARTITION* partition;
|
|
|
|
int i;
|
2008-11-20 06:22:28 +01:00
|
|
|
uint8_t sectorBuffer[BYTES_PER_READ] = {0};
|
2006-07-14 04:42:37 +02:00
|
|
|
|
|
|
|
// Read first sector of disc
|
2008-11-30 08:12:08 +01:00
|
|
|
if (!_FAT_disc_readSectors (disc, startSector, 1, sectorBuffer)) {
|
2006-07-14 04:42:37 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure it is a valid MBR or boot sector
|
|
|
|
if ( (sectorBuffer[BPB_bootSig_55] != 0x55) || (sectorBuffer[BPB_bootSig_AA] != 0xAA)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-11-20 06:22:28 +01:00
|
|
|
if (startSector != 0) {
|
|
|
|
// We're told where to start the partition, so just accept it
|
|
|
|
} else if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG))) {
|
|
|
|
// Check if there is a FAT string, which indicates this is a boot sector
|
|
|
|
startSector = 0;
|
|
|
|
} else if (!memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) {
|
2006-07-14 04:42:37 +02:00
|
|
|
// Check for FAT32
|
2008-11-20 06:22:28 +01:00
|
|
|
startSector = 0;
|
2006-07-14 04:42:37 +02:00
|
|
|
} else {
|
|
|
|
// This is an MBR
|
|
|
|
// Find first valid partition from MBR
|
|
|
|
// First check for an active partition
|
|
|
|
for (i=0x1BE; (i < 0x1FE) && (sectorBuffer[i] != 0x80); i+= 0x10);
|
|
|
|
// If it didn't find an active partition, search for any valid partition
|
|
|
|
if (i == 0x1FE) {
|
|
|
|
for (i=0x1BE; (i < 0x1FE) && (sectorBuffer[i+0x04] == 0x00); i+= 0x10);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( i != 0x1FE) {
|
2008-11-30 08:12:08 +01:00
|
|
|
// Go to first valid partition
|
2008-11-20 06:22:28 +01:00
|
|
|
startSector = u8array_to_u32(sectorBuffer, 0x8 + i);
|
2008-11-30 08:12:08 +01:00
|
|
|
// Load the BPB
|
|
|
|
if (!_FAT_disc_readSectors (disc, startSector, 1, sectorBuffer)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
// Make sure it is a valid BPB
|
|
|
|
if ( (sectorBuffer[BPB_bootSig_55] != 0x55) || (sectorBuffer[BPB_bootSig_AA] != 0xAA)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2006-07-14 04:42:37 +02:00
|
|
|
} else {
|
2008-11-30 08:12:08 +01:00
|
|
|
// No partition found, assume this is a MBR free disk
|
|
|
|
startSector = 0;
|
2006-07-14 04:42:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-20 06:22:28 +01:00
|
|
|
// Now verify that this is indeed a FAT partition
|
|
|
|
if (memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) &&
|
|
|
|
memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG)))
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-03-13 09:34:32 +01:00
|
|
|
// check again for the last two cases to make sure that we really have a FAT filesystem here
|
|
|
|
// and won't corrupt any data
|
|
|
|
if(memcmp(sectorBuffer + BPB_FAT16_fileSysType, "FAT", 3) != 0 && memcmp(sectorBuffer + BPB_FAT32_fileSysType, "FAT32", 5) != 0)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2006-07-14 04:42:37 +02:00
|
|
|
partition = (PARTITION*) _FAT_mem_allocate (sizeof(PARTITION));
|
|
|
|
if (partition == NULL) {
|
2006-07-22 14:16:40 +02:00
|
|
|
return NULL;
|
2006-07-14 04:42:37 +02:00
|
|
|
}
|
|
|
|
|
2008-11-20 06:22:28 +01:00
|
|
|
// Init the partition lock
|
|
|
|
_FAT_lock_init(&partition->lock);
|
|
|
|
|
2006-07-14 04:42:37 +02:00
|
|
|
// Set partition's disc interface
|
|
|
|
partition->disc = disc;
|
|
|
|
|
|
|
|
// Store required information about the file system
|
|
|
|
partition->fat.sectorsPerFat = u8array_to_u16(sectorBuffer, BPB_sectorsPerFAT);
|
|
|
|
if (partition->fat.sectorsPerFat == 0) {
|
|
|
|
partition->fat.sectorsPerFat = u8array_to_u32( sectorBuffer, BPB_FAT32_sectorsPerFAT32);
|
|
|
|
}
|
|
|
|
|
|
|
|
partition->numberOfSectors = u8array_to_u16( sectorBuffer, BPB_numSectorsSmall);
|
|
|
|
if (partition->numberOfSectors == 0) {
|
|
|
|
partition->numberOfSectors = u8array_to_u32( sectorBuffer, BPB_numSectors);
|
|
|
|
}
|
|
|
|
|
|
|
|
partition->bytesPerSector = BYTES_PER_READ; // Sector size is redefined to be 512 bytes
|
|
|
|
partition->sectorsPerCluster = sectorBuffer[BPB_sectorsPerCluster] * u8array_to_u16(sectorBuffer, BPB_bytesPerSector) / BYTES_PER_READ;
|
|
|
|
partition->bytesPerCluster = partition->bytesPerSector * partition->sectorsPerCluster;
|
2008-11-20 06:22:28 +01:00
|
|
|
partition->fat.fatStart = startSector + u8array_to_u16(sectorBuffer, BPB_reservedSectors);
|
2006-07-14 04:42:37 +02:00
|
|
|
|
|
|
|
partition->rootDirStart = partition->fat.fatStart + (sectorBuffer[BPB_numFATs] * partition->fat.sectorsPerFat);
|
2008-11-20 06:22:28 +01:00
|
|
|
partition->dataStart = partition->rootDirStart +
|
|
|
|
(( u8array_to_u16(sectorBuffer, BPB_rootEntries) * DIR_ENTRY_DATA_SIZE) / partition->bytesPerSector);
|
2006-07-14 04:42:37 +02:00
|
|
|
|
2008-11-20 06:22:28 +01:00
|
|
|
partition->totalSize = ((uint64_t)partition->numberOfSectors - (partition->dataStart - startSector)) * (uint64_t)partition->bytesPerSector;
|
2006-07-14 04:42:37 +02:00
|
|
|
|
|
|
|
// Store info about FAT
|
2008-11-20 06:22:28 +01:00
|
|
|
partition->fat.lastCluster = (partition->numberOfSectors - (uint32_t)(partition->dataStart - startSector)) / partition->sectorsPerCluster;
|
2006-07-14 04:42:37 +02:00
|
|
|
partition->fat.firstFree = CLUSTER_FIRST;
|
|
|
|
|
|
|
|
if (partition->fat.lastCluster < CLUSTERS_PER_FAT12) {
|
|
|
|
partition->filesysType = FS_FAT12; // FAT12 volume
|
|
|
|
} else if (partition->fat.lastCluster < CLUSTERS_PER_FAT16) {
|
|
|
|
partition->filesysType = FS_FAT16; // FAT16 volume
|
|
|
|
} else {
|
|
|
|
partition->filesysType = FS_FAT32; // FAT32 volume
|
|
|
|
}
|
|
|
|
|
|
|
|
if (partition->filesysType != FS_FAT32) {
|
|
|
|
partition->rootDirCluster = FAT16_ROOT_DIR_CLUSTER;
|
|
|
|
} else {
|
|
|
|
// Set up for the FAT32 way
|
|
|
|
partition->rootDirCluster = u8array_to_u32(sectorBuffer, BPB_FAT32_rootClus);
|
|
|
|
// Check if FAT mirroring is enabled
|
|
|
|
if (!(sectorBuffer[BPB_FAT32_extFlags] & 0x80)) {
|
|
|
|
// Use the active FAT
|
|
|
|
partition->fat.fatStart = partition->fat.fatStart + ( partition->fat.sectorsPerFat * (sectorBuffer[BPB_FAT32_extFlags] & 0x0F));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a cache to use
|
|
|
|
partition->cache = _FAT_cache_constructor (cacheSize, partition->disc);
|
|
|
|
|
|
|
|
// Set current directory to the root
|
|
|
|
partition->cwdCluster = partition->rootDirCluster;
|
|
|
|
|
|
|
|
// Check if this disc is writable, and set the readOnly property appropriately
|
|
|
|
partition->readOnly = !(_FAT_disc_features(disc) & FEATURE_MEDIUM_CANWRITE);
|
|
|
|
|
|
|
|
// There are currently no open files on this partition
|
|
|
|
partition->openFileCount = 0;
|
2008-12-10 15:26:58 +01:00
|
|
|
partition->firstOpenFile = NULL;
|
2006-07-14 04:42:37 +02:00
|
|
|
|
|
|
|
return partition;
|
|
|
|
}
|
|
|
|
|
2008-11-20 06:22:28 +01:00
|
|
|
void _FAT_partition_destructor (PARTITION* partition) {
|
|
|
|
FILE_STRUCT* nextFile;
|
2006-07-14 04:42:37 +02:00
|
|
|
|
2008-11-20 06:22:28 +01:00
|
|
|
_FAT_lock(&partition->lock);
|
2006-07-14 04:42:37 +02:00
|
|
|
|
2008-11-20 06:22:28 +01:00
|
|
|
// Synchronize open files
|
|
|
|
nextFile = partition->firstOpenFile;
|
|
|
|
while (nextFile) {
|
|
|
|
_FAT_syncToDisc (nextFile);
|
|
|
|
nextFile = nextFile->nextOpenFile;
|
2006-07-14 04:42:37 +02:00
|
|
|
}
|
|
|
|
|
2008-11-20 06:22:28 +01:00
|
|
|
// Free memory used by the cache, writing it to disc at the same time
|
|
|
|
_FAT_cache_destructor (partition->cache);
|
2006-07-14 04:42:37 +02:00
|
|
|
|
2008-11-20 06:22:28 +01:00
|
|
|
// Unlock the partition and destroy the lock
|
|
|
|
_FAT_unlock(&partition->lock);
|
|
|
|
_FAT_lock_deinit(&partition->lock);
|
2006-07-14 04:42:37 +02:00
|
|
|
|
2008-11-20 06:22:28 +01:00
|
|
|
// Free memory used by the partition
|
|
|
|
_FAT_mem_free (partition);
|
2006-07-14 04:42:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PARTITION* _FAT_partition_getPartitionFromPath (const char* path) {
|
2008-11-20 06:22:28 +01:00
|
|
|
const devoptab_t *devops;
|
2006-07-14 04:42:37 +02:00
|
|
|
|
2008-11-20 06:22:28 +01:00
|
|
|
devops = GetDeviceOpTab (path);
|
2006-07-14 04:42:37 +02:00
|
|
|
|
2008-11-20 06:22:28 +01:00
|
|
|
if (!devops) {
|
2006-07-14 04:42:37 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-11-20 06:22:28 +01:00
|
|
|
return (PARTITION*)devops->deviceData;
|
2006-07-14 04:42:37 +02:00
|
|
|
}
|