diff options
Diffstat (limited to 'src/Core/FatFormatter.cpp')
-rw-r--r-- | src/Core/FatFormatter.cpp | 384 |
1 files changed, 384 insertions, 0 deletions
diff --git a/src/Core/FatFormatter.cpp b/src/Core/FatFormatter.cpp new file mode 100644 index 00000000..ba69df61 --- /dev/null +++ b/src/Core/FatFormatter.cpp @@ -0,0 +1,384 @@ +/* + Legal Notice: Some portions of the source code contained in this file were + derived from the source code of Encryption for the Masses 2.02a, which is + Copyright (c) 1998-2000 Paul Le Roux and which is governed by the 'License + Agreement for Encryption for the Masses'. Modifications and additions to + the original source code (contained in this file) and all other portions + of this file are Copyright (c) 2003-2010 TrueCrypt Developers Association + and are governed by the TrueCrypt License 3.0 the full text of which is + contained in the file License.txt included in TrueCrypt binary and source + code distribution packages. */ + +#include "Common/Tcdefs.h" +#include "Platform/Platform.h" +#include "Volume/VolumeHeader.h" +#include "FatFormatter.h" +#include "RandomNumberGenerator.h" + +namespace TrueCrypt +{ + struct fatparams + { + char volume_name[11]; + uint32 num_sectors; /* total number of sectors */ + uint32 cluster_count; /* number of clusters */ + uint32 size_root_dir; /* size of the root directory in bytes */ + uint32 size_fat; /* size of FAT */ + uint32 fats; + uint32 media; + uint32 cluster_size; + uint32 fat_length; + uint16 dir_entries; + uint16 sector_size; + uint32 hidden; + uint16 reserved; + uint16 sectors; + uint32 total_sect; + + uint16 heads; + uint16 secs_track; + + }; + + static void GetFatParams (fatparams * ft) + { + uint64 volumeSize = (uint64) ft->num_sectors * ft->sector_size; + unsigned int fatsecs; + + if(ft->cluster_size == 0) // 'Default' cluster size + { + uint32 clusterSize; + + // Determine optimal cluster size to minimize FAT size (mounting delay), maximize number of files, keep 4 KB alignment, etc. + if (volumeSize >= 2 * BYTES_PER_TB) + clusterSize = 256 * BYTES_PER_KB; + else if (volumeSize >= 512 * BYTES_PER_GB) + clusterSize = 128 * BYTES_PER_KB; + else if (volumeSize >= 128 * BYTES_PER_GB) + clusterSize = 64 * BYTES_PER_KB; + else if (volumeSize >= 64 * BYTES_PER_GB) + clusterSize = 32 * BYTES_PER_KB; + else if (volumeSize >= 32 * BYTES_PER_GB) + clusterSize = 16 * BYTES_PER_KB; + else if (volumeSize >= 16 * BYTES_PER_GB) + clusterSize = 8 * BYTES_PER_KB; + else if (volumeSize >= 512 * BYTES_PER_MB) + clusterSize = 4 * BYTES_PER_KB; + else if (volumeSize >= 256 * BYTES_PER_MB) + clusterSize = 2 * BYTES_PER_KB; + else if (volumeSize >= 1 * BYTES_PER_MB) + clusterSize = 1 * BYTES_PER_KB; + else + clusterSize = 512; + + ft->cluster_size = clusterSize / ft->sector_size; + + if (ft->cluster_size == 0) + ft->cluster_size = 1; + + if (ft->cluster_size * ft->sector_size > TC_MAX_FAT_CLUSTER_SIZE) + ft->cluster_size = TC_MAX_FAT_CLUSTER_SIZE / ft->sector_size; + + if (ft->cluster_size > 128) + ft->cluster_size = 128; + } + + if (volumeSize <= TC_MAX_FAT_CLUSTER_SIZE * 4) + ft->cluster_size = 1; + + // Geometry always set to SECTORS/1/1 + ft->secs_track = 1; + ft->heads = 1; + + ft->dir_entries = 512; + ft->fats = 2; + ft->media = 0xf8; + ft->hidden = 0; + + ft->size_root_dir = ft->dir_entries * 32; + + // FAT12 + ft->size_fat = 12; + ft->reserved = 2; + fatsecs = ft->num_sectors - (ft->size_root_dir + ft->sector_size - 1) / ft->sector_size - ft->reserved; + ft->cluster_count = (int) (((int64) fatsecs * ft->sector_size) / (ft->cluster_size * ft->sector_size)); + ft->fat_length = (((ft->cluster_count * 3 + 1) >> 1) + ft->sector_size - 1) / ft->sector_size; + + if (ft->cluster_count >= 4085) // FAT16 + { + ft->size_fat = 16; + ft->reserved = 2; + fatsecs = ft->num_sectors - (ft->size_root_dir + ft->sector_size - 1) / ft->sector_size - ft->reserved; + ft->cluster_count = (int) (((int64) fatsecs * ft->sector_size) / (ft->cluster_size * ft->sector_size)); + ft->fat_length = (ft->cluster_count * 2 + ft->sector_size - 1) / ft->sector_size; + } + + if(ft->cluster_count >= 65525) // FAT32 + { + ft->size_fat = 32; + ft->reserved = 32 - 1; + + do + { + ft->reserved++; + + fatsecs = ft->num_sectors - ft->reserved; + ft->size_root_dir = ft->cluster_size * ft->sector_size; + ft->cluster_count = (int) (((int64) fatsecs * ft->sector_size) / (ft->cluster_size * ft->sector_size)); + ft->fat_length = (ft->cluster_count * 4 + ft->sector_size - 1) / ft->sector_size; + + // Align data area on TC_MAX_VOLUME_SECTOR_SIZE + + } while (ft->sector_size == TC_SECTOR_SIZE_LEGACY + && (ft->reserved * ft->sector_size + ft->fat_length * ft->fats * ft->sector_size) % TC_MAX_VOLUME_SECTOR_SIZE != 0); + } + + ft->cluster_count -= ft->fat_length * ft->fats / ft->cluster_size; + + if (ft->num_sectors >= 65536 || ft->size_fat == 32) + { + ft->sectors = 0; + ft->total_sect = ft->num_sectors; + } + else + { + ft->sectors = (uint16) ft->num_sectors; + ft->total_sect = 0; + } + } + + static void PutBoot (fatparams * ft, byte *boot, uint32 volumeId) + { + int cnt = 0; + + boot[cnt++] = 0xeb; /* boot jump */ + boot[cnt++] = 0x3c; + boot[cnt++] = 0x90; + memcpy (boot + cnt, "MSDOS5.0", 8); /* system id */ + cnt += 8; + *(int16 *)(boot + cnt) = Endian::Little (ft->sector_size); /* bytes per sector */ + cnt += 2; + boot[cnt++] = (int8) ft->cluster_size; /* sectors per cluster */ + *(int16 *)(boot + cnt) = Endian::Little (ft->reserved); /* reserved sectors */ + cnt += 2; + boot[cnt++] = (int8) ft->fats; /* 2 fats */ + + if(ft->size_fat == 32) + { + boot[cnt++] = 0x00; + boot[cnt++] = 0x00; + } + else + { + *(int16 *)(boot + cnt) = Endian::Little (ft->dir_entries); /* 512 root entries */ + cnt += 2; + } + + *(int16 *)(boot + cnt) = Endian::Little (ft->sectors); /* # sectors */ + cnt += 2; + boot[cnt++] = (int8) ft->media; /* media byte */ + + if(ft->size_fat == 32) + { + boot[cnt++] = 0x00; + boot[cnt++] = 0x00; + } + else + { + *(uint16 *)(boot + cnt) = Endian::Little ((uint16) ft->fat_length); /* fat size */ + cnt += 2; + } + + *(int16 *)(boot + cnt) = Endian::Little (ft->secs_track); /* # sectors per track */ + cnt += 2; + *(int16 *)(boot + cnt) = Endian::Little (ft->heads); /* # heads */ + cnt += 2; + *(int32 *)(boot + cnt) = Endian::Little (ft->hidden); /* # hidden sectors */ + cnt += 4; + *(int32 *)(boot + cnt) = Endian::Little (ft->total_sect); /* # huge sectors */ + cnt += 4; + + if(ft->size_fat == 32) + { + *(int32 *)(boot + cnt) = Endian::Little (ft->fat_length); cnt += 4; /* fat size 32 */ + boot[cnt++] = 0x00; /* ExtFlags */ + boot[cnt++] = 0x00; + boot[cnt++] = 0x00; /* FSVer */ + boot[cnt++] = 0x00; + boot[cnt++] = 0x02; /* RootClus */ + boot[cnt++] = 0x00; + boot[cnt++] = 0x00; + boot[cnt++] = 0x00; + boot[cnt++] = 0x01; /* FSInfo */ + boot[cnt++] = 0x00; + boot[cnt++] = 0x06; /* BkBootSec */ + boot[cnt++] = 0x00; + memset(boot+cnt, 0, 12); cnt+=12; /* Reserved */ + } + + boot[cnt++] = 0x00; /* drive number */ // FIXED 80 > 00 + boot[cnt++] = 0x00; /* reserved */ + boot[cnt++] = 0x29; /* boot sig */ + + *(int32 *)(boot + cnt) = volumeId; + cnt += 4; + + memcpy (boot + cnt, ft->volume_name, 11); /* vol title */ + cnt += 11; + + switch(ft->size_fat) /* filesystem type */ + { + case 12: memcpy (boot + cnt, "FAT12 ", 8); break; + case 16: memcpy (boot + cnt, "FAT16 ", 8); break; + case 32: memcpy (boot + cnt, "FAT32 ", 8); break; + } + cnt += 8; + + memset (boot + cnt, 0, ft->size_fat==32 ? 420:448); /* boot code */ + cnt += ft->size_fat==32 ? 420:448; + boot[cnt++] = 0x55; + boot[cnt++] = 0xaa; /* boot sig */ + } + + + /* FAT32 FSInfo */ + static void PutFSInfo (byte *sector, fatparams *ft) + { + memset (sector, 0, ft->sector_size); + sector[3] = 0x41; /* LeadSig */ + sector[2] = 0x61; + sector[1] = 0x52; + sector[0] = 0x52; + sector[484+3] = 0x61; /* StrucSig */ + sector[484+2] = 0x41; + sector[484+1] = 0x72; + sector[484+0] = 0x72; + + // Free cluster count + *(uint32 *)(sector + 488) = Endian::Little (ft->cluster_count - ft->size_root_dir / ft->sector_size / ft->cluster_size); + + // Next free cluster + *(uint32 *)(sector + 492) = Endian::Little ((uint32) 2); + + sector[508+3] = 0xaa; /* TrailSig */ + sector[508+2] = 0x55; + sector[508+1] = 0x00; + sector[508+0] = 0x00; + } + + void FatFormatter::Format (WriteSectorCallback &writeSector, uint64 deviceSize, uint32 clusterSize, uint32 sectorSize) + { + fatparams fatParams; + +#if TC_MAX_VOLUME_SECTOR_SIZE > 0xFFFF +#error TC_MAX_VOLUME_SECTOR_SIZE > 0xFFFF +#endif + fatParams.sector_size = (uint16) sectorSize; + + if (deviceSize / fatParams.sector_size > 0xffffFFFF) + throw ParameterIncorrect (SRC_POS); + + fatParams.num_sectors = (uint32) (deviceSize / fatParams.sector_size); + fatParams.cluster_size = clusterSize / fatParams.sector_size; + memcpy (fatParams.volume_name, "NO NAME ", 11); + GetFatParams (&fatParams); + fatparams *ft = &fatParams; + + SecureBuffer sector (ft->sector_size); + uint32 sectorNumber = 0; + + /* Write the data area */ + + sector.Zero(); + + uint32 volumeId; + RandomNumberGenerator::GetDataFast (BufferPtr ((byte *) &volumeId, sizeof (volumeId))); + + PutBoot (ft, (byte *) sector, volumeId); + writeSector (sector); ++sectorNumber; + + /* fat32 boot area */ + if (ft->size_fat == 32) + { + /* fsinfo */ + PutFSInfo((byte *) sector, ft); + writeSector (sector); ++sectorNumber; + + /* reserved */ + while (sectorNumber < 6) + { + sector.Zero(); + sector[508+3] = 0xaa; /* TrailSig */ + sector[508+2] = 0x55; + writeSector (sector); ++sectorNumber; + } + + /* bootsector backup */ + sector.Zero(); + PutBoot (ft, (byte *) sector, volumeId); + writeSector (sector); ++sectorNumber; + + PutFSInfo((byte *) sector, ft); + writeSector (sector); ++sectorNumber; + } + + /* reserved */ + while (sectorNumber < (uint32)ft->reserved) + { + sector.Zero(); + writeSector (sector); ++sectorNumber; + } + + /* write fat */ + for (uint32 x = 1; x <= ft->fats; x++) + { + for (uint32 n = 0; n < ft->fat_length; n++) + { + sector.Zero(); + + if (n == 0) + { + byte fat_sig[12]; + if (ft->size_fat == 32) + { + fat_sig[0] = (byte) ft->media; + fat_sig[1] = fat_sig[2] = 0xff; + fat_sig[3] = 0x0f; + fat_sig[4] = fat_sig[5] = fat_sig[6] = 0xff; + fat_sig[7] = 0x0f; + fat_sig[8] = fat_sig[9] = fat_sig[10] = 0xff; + fat_sig[11] = 0x0f; + memcpy (sector, fat_sig, 12); + } + else if (ft->size_fat == 16) + { + fat_sig[0] = (byte) ft->media; + fat_sig[1] = 0xff; + fat_sig[2] = 0xff; + fat_sig[3] = 0xff; + memcpy (sector, fat_sig, 4); + } + else if (ft->size_fat == 12) + { + fat_sig[0] = (byte) ft->media; + fat_sig[1] = 0xff; + fat_sig[2] = 0xff; + fat_sig[3] = 0x00; + memcpy (sector, fat_sig, 4); + } + } + + if (!writeSector (sector)) + return; + } + } + + /* write rootdir */ + for (uint32 x = 0; x < ft->size_root_dir / ft->sector_size; x++) + { + sector.Zero(); + if (!writeSector (sector)) + return; + } + } +} |