support to read/write FS info sector for FAT32 partitions. speeds up statvfs. patch...
[libfat.git] / source / partition.c
blobd4072cc3ceebd109f2f320967732e5d0a1c945cb
1 /*
2 partition.c
3 Functions for mounting and dismounting partitions
4 on various block devices.
6 Copyright (c) 2006 Michael "Chishm" Chisholm
8 Redistribution and use in source and binary forms, with or without modification,
9 are permitted provided that the following conditions are met:
11 1. Redistributions of source code must retain the above copyright notice,
12 this list of conditions and the following disclaimer.
13 2. Redistributions in binary form must reproduce the above copyright notice,
14 this list of conditions and the following disclaimer in the documentation and/or
15 other materials provided with the distribution.
16 3. The name of the author may not be used to endorse or promote products derived
17 from this software without specific prior written permission.
19 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
20 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
21 AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
22 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #include "partition.h"
31 #include "bit_ops.h"
32 #include "file_allocation_table.h"
33 #include "directory.h"
34 #include "mem_allocate.h"
35 #include "fatfile.h"
37 #include <string.h>
38 #include <ctype.h>
39 #include <sys/iosupport.h>
42 This device name, as known by devkitPro toolchains
44 const char* DEVICE_NAME = "fat";
47 Data offsets
50 // BIOS Parameter Block offsets
51 enum BPB {
52 BPB_jmpBoot = 0x00,
53 BPB_OEMName = 0x03,
54 // BIOS Parameter Block
55 BPB_bytesPerSector = 0x0B,
56 BPB_sectorsPerCluster = 0x0D,
57 BPB_reservedSectors = 0x0E,
58 BPB_numFATs = 0x10,
59 BPB_rootEntries = 0x11,
60 BPB_numSectorsSmall = 0x13,
61 BPB_mediaDesc = 0x15,
62 BPB_sectorsPerFAT = 0x16,
63 BPB_sectorsPerTrk = 0x18,
64 BPB_numHeads = 0x1A,
65 BPB_numHiddenSectors = 0x1C,
66 BPB_numSectors = 0x20,
67 // Ext BIOS Parameter Block for FAT16
68 BPB_FAT16_driveNumber = 0x24,
69 BPB_FAT16_reserved1 = 0x25,
70 BPB_FAT16_extBootSig = 0x26,
71 BPB_FAT16_volumeID = 0x27,
72 BPB_FAT16_volumeLabel = 0x2B,
73 BPB_FAT16_fileSysType = 0x36,
74 // Bootcode
75 BPB_FAT16_bootCode = 0x3E,
76 // FAT32 extended block
77 BPB_FAT32_sectorsPerFAT32 = 0x24,
78 BPB_FAT32_extFlags = 0x28,
79 BPB_FAT32_fsVer = 0x2A,
80 BPB_FAT32_rootClus = 0x2C,
81 BPB_FAT32_fsInfo = 0x30,
82 BPB_FAT32_bkBootSec = 0x32,
83 // Ext BIOS Parameter Block for FAT32
84 BPB_FAT32_driveNumber = 0x40,
85 BPB_FAT32_reserved1 = 0x41,
86 BPB_FAT32_extBootSig = 0x42,
87 BPB_FAT32_volumeID = 0x43,
88 BPB_FAT32_volumeLabel = 0x47,
89 BPB_FAT32_fileSysType = 0x52,
90 // Bootcode
91 BPB_FAT32_bootCode = 0x5A,
92 BPB_bootSig_55 = 0x1FE,
93 BPB_bootSig_AA = 0x1FF
96 // File system information block offsets
97 enum FSIB
99 FSIB_SIG1 = 0x00,
100 FSIB_SIG2 = 0x1e4,
101 FSIB_numberOfFreeCluster = 0x1e8,
102 FSIB_numberLastAllocCluster = 0x1ec,
103 FSIB_bootSig_55 = 0x1FE,
104 FSIB_bootSig_AA = 0x1FF
107 static const char FAT_SIG[3] = {'F', 'A', 'T'};
108 static const char FS_INFO_SIG1[4] = {'R', 'R', 'a', 'A'};
109 static const char FS_INFO_SIG2[4] = {'r', 'r', 'A', 'a'};
112 sec_t FindFirstValidPartition(const DISC_INTERFACE* disc)
114 uint8_t part_table[16*4];
115 uint8_t *ptr;
116 int i;
118 uint8_t sectorBuffer[BYTES_PER_READ] = {0};
120 // Read first sector of disc
121 if (!_FAT_disc_readSectors (disc, 0, 1, sectorBuffer)) {
122 return 0;
125 memcpy(part_table,sectorBuffer+0x1BE,16*4);
126 ptr = part_table;
128 for(i=0;i<4;i++,ptr+=16) {
129 sec_t part_lba = u8array_to_u32(ptr, 0x8);
131 if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) ||
132 !memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) {
133 return part_lba;
136 if(ptr[4]==0) continue;
138 if(ptr[4]==0x0F) {
139 sec_t part_lba2=part_lba;
140 sec_t next_lba2=0;
141 int n;
143 for(n=0;n<8;n++) // max 8 logic partitions
145 if(!_FAT_disc_readSectors (disc, part_lba+next_lba2, 1, sectorBuffer)) return 0;
147 part_lba2 = part_lba + next_lba2 + u8array_to_u32(sectorBuffer, 0x1C6) ;
148 next_lba2 = u8array_to_u32(sectorBuffer, 0x1D6);
150 if(!_FAT_disc_readSectors (disc, part_lba2, 1, sectorBuffer)) return 0;
152 if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) ||
153 !memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG)))
155 return part_lba2;
158 if(next_lba2==0) break;
160 } else {
161 if(!_FAT_disc_readSectors (disc, part_lba, 1, sectorBuffer)) return 0;
162 if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) ||
163 !memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) {
164 return part_lba;
168 return 0;
171 PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cacheSize, uint32_t sectorsPerPage, sec_t startSector) {
172 PARTITION* partition;
173 uint8_t sectorBuffer[BYTES_PER_READ] = {0};
175 // Read first sector of disc
176 if (!_FAT_disc_readSectors (disc, startSector, 1, sectorBuffer)) {
177 return NULL;
180 // Make sure it is a valid MBR or boot sector
181 if ( (sectorBuffer[BPB_bootSig_55] != 0x55) || (sectorBuffer[BPB_bootSig_AA] != 0xAA)) {
182 return NULL;
185 if (startSector != 0) {
186 // We're told where to start the partition, so just accept it
187 } else if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG))) {
188 // Check if there is a FAT string, which indicates this is a boot sector
189 startSector = 0;
190 } else if (!memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) {
191 // Check for FAT32
192 startSector = 0;
193 } else {
194 startSector = FindFirstValidPartition(disc);
195 if (!_FAT_disc_readSectors (disc, startSector, 1, sectorBuffer)) {
196 return NULL;
200 // Now verify that this is indeed a FAT partition
201 if (memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) &&
202 memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG)))
204 return NULL;
207 partition = (PARTITION*) _FAT_mem_allocate (sizeof(PARTITION));
208 if (partition == NULL) {
209 return NULL;
212 // Init the partition lock
213 _FAT_lock_init(&partition->lock);
215 if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)))
216 strncpy(partition->label, (char*)(sectorBuffer + BPB_FAT16_volumeLabel), 11);
217 else
218 strncpy(partition->label, (char*)(sectorBuffer + BPB_FAT32_volumeLabel), 11);
219 partition->label[11] = '\0';
221 // Set partition's disc interface
222 partition->disc = disc;
224 // Store required information about the file system
225 partition->fat.sectorsPerFat = u8array_to_u16(sectorBuffer, BPB_sectorsPerFAT);
226 if (partition->fat.sectorsPerFat == 0) {
227 partition->fat.sectorsPerFat = u8array_to_u32( sectorBuffer, BPB_FAT32_sectorsPerFAT32);
230 partition->numberOfSectors = u8array_to_u16( sectorBuffer, BPB_numSectorsSmall);
231 if (partition->numberOfSectors == 0) {
232 partition->numberOfSectors = u8array_to_u32( sectorBuffer, BPB_numSectors);
235 partition->bytesPerSector = BYTES_PER_READ; // Sector size is redefined to be 512 bytes
236 partition->sectorsPerCluster = sectorBuffer[BPB_sectorsPerCluster] * u8array_to_u16(sectorBuffer, BPB_bytesPerSector) / BYTES_PER_READ;
237 partition->bytesPerCluster = partition->bytesPerSector * partition->sectorsPerCluster;
238 partition->fat.fatStart = startSector + u8array_to_u16(sectorBuffer, BPB_reservedSectors);
240 partition->rootDirStart = partition->fat.fatStart + (sectorBuffer[BPB_numFATs] * partition->fat.sectorsPerFat);
241 partition->dataStart = partition->rootDirStart +
242 (( u8array_to_u16(sectorBuffer, BPB_rootEntries) * DIR_ENTRY_DATA_SIZE) / partition->bytesPerSector);
244 partition->totalSize = ((uint64_t)partition->numberOfSectors - (partition->dataStart - startSector)) * (uint64_t)partition->bytesPerSector;
246 //FS info sector
247 partition->fsInfoSector = startSector + (u8array_to_u16(sectorBuffer, BPB_FAT32_fsInfo) ? u8array_to_u16(sectorBuffer, BPB_FAT32_fsInfo) : 1);
249 // Store info about FAT
250 uint32_t clusterCount = (partition->numberOfSectors - (uint32_t)(partition->dataStart - startSector)) / partition->sectorsPerCluster;
251 partition->fat.lastCluster = clusterCount + CLUSTER_FIRST - 1;
252 partition->fat.firstFree = CLUSTER_FIRST;
253 partition->fat.numberFreeCluster = 0;
254 partition->fat.numberLastAllocCluster = 0;
256 if (clusterCount < CLUSTERS_PER_FAT12) {
257 partition->filesysType = FS_FAT12; // FAT12 volume
258 } else if (clusterCount < CLUSTERS_PER_FAT16) {
259 partition->filesysType = FS_FAT16; // FAT16 volume
260 } else {
261 partition->filesysType = FS_FAT32; // FAT32 volume
264 if (partition->filesysType != FS_FAT32) {
265 partition->rootDirCluster = FAT16_ROOT_DIR_CLUSTER;
266 } else {
267 // Set up for the FAT32 way
268 partition->rootDirCluster = u8array_to_u32(sectorBuffer, BPB_FAT32_rootClus);
269 // Check if FAT mirroring is enabled
270 if (!(sectorBuffer[BPB_FAT32_extFlags] & 0x80)) {
271 // Use the active FAT
272 partition->fat.fatStart = partition->fat.fatStart + ( partition->fat.sectorsPerFat * (sectorBuffer[BPB_FAT32_extFlags] & 0x0F));
276 // Create a cache to use
277 partition->cache = _FAT_cache_constructor (cacheSize, sectorsPerPage, partition->disc, startSector+partition->numberOfSectors);
279 // Set current directory to the root
280 partition->cwdCluster = partition->rootDirCluster;
282 // Check if this disc is writable, and set the readOnly property appropriately
283 partition->readOnly = !(_FAT_disc_features(disc) & FEATURE_MEDIUM_CANWRITE);
285 // There are currently no open files on this partition
286 partition->openFileCount = 0;
287 partition->firstOpenFile = NULL;
289 _FAT_partition_readFSinfo(partition);
291 return partition;
294 void _FAT_partition_destructor (PARTITION* partition) {
295 FILE_STRUCT* nextFile;
297 _FAT_lock(&partition->lock);
299 // Synchronize open files
300 nextFile = partition->firstOpenFile;
301 while (nextFile) {
302 _FAT_syncToDisc (nextFile);
303 nextFile = nextFile->nextOpenFile;
306 // Write out the fs info sector
307 _FAT_partition_writeFSinfo(partition);
309 // Free memory used by the cache, writing it to disc at the same time
310 _FAT_cache_destructor (partition->cache);
312 // Unlock the partition and destroy the lock
313 _FAT_unlock(&partition->lock);
314 _FAT_lock_deinit(&partition->lock);
316 // Free memory used by the partition
317 _FAT_mem_free (partition);
320 PARTITION* _FAT_partition_getPartitionFromPath (const char* path) {
321 const devoptab_t *devops;
323 devops = GetDeviceOpTab (path);
325 if (!devops) {
326 return NULL;
329 return (PARTITION*)devops->deviceData;
332 void _FAT_partition_createFSinfo(PARTITION * partition)
334 if(partition->readOnly || partition->filesysType != FS_FAT32)
335 return;
337 uint8_t sectorBuffer[BYTES_PER_READ];
338 memset(sectorBuffer, 0, sizeof(sectorBuffer));
340 int i;
341 for(i = 0; i < 4; ++i)
343 sectorBuffer[FSIB_SIG1+i] = FS_INFO_SIG1[i];
344 sectorBuffer[FSIB_SIG2+i] = FS_INFO_SIG2[i];
347 partition->fat.numberFreeCluster = _FAT_fat_freeClusterCount(partition);
348 u32_to_u8array(sectorBuffer, FSIB_numberOfFreeCluster, partition->fat.numberFreeCluster);
349 u32_to_u8array(sectorBuffer, FSIB_numberLastAllocCluster, partition->fat.numberLastAllocCluster);
351 sectorBuffer[FSIB_bootSig_55] = 0x55;
352 sectorBuffer[FSIB_bootSig_AA] = 0xAA;
354 _FAT_disc_writeSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer);
357 void _FAT_partition_readFSinfo(PARTITION * partition)
359 if(partition->filesysType != FS_FAT32)
360 return;
362 uint8_t sectorBuffer[BYTES_PER_READ] = {0};
364 // Read first sector of disc
365 if (!_FAT_disc_readSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer)) {
366 return;
369 if(memcmp(sectorBuffer+FSIB_SIG1, FS_INFO_SIG1, 4) != 0 ||
370 memcmp(sectorBuffer+FSIB_SIG2, FS_INFO_SIG2, 4) != 0 ||
371 u8array_to_u32(sectorBuffer, FSIB_numberOfFreeCluster) == 0)
373 //sector does not yet exist, create one!
374 _FAT_partition_createFSinfo(partition);
375 return;
378 partition->fat.numberFreeCluster = u8array_to_u32(sectorBuffer, FSIB_numberOfFreeCluster);
379 partition->fat.numberLastAllocCluster = u8array_to_u32(sectorBuffer, FSIB_numberLastAllocCluster);
382 void _FAT_partition_writeFSinfo(PARTITION * partition)
384 if(partition->filesysType != FS_FAT32)
385 return;
387 uint8_t sectorBuffer[BYTES_PER_READ] = {0};
389 // Read first sector of disc
390 if (!_FAT_disc_readSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer)) {
391 return;
394 if(memcmp(sectorBuffer+FSIB_SIG1, FS_INFO_SIG1, 4) || memcmp(sectorBuffer+FSIB_SIG2, FS_INFO_SIG2, 4))
395 return;
397 u32_to_u8array(sectorBuffer, FSIB_numberOfFreeCluster, partition->fat.numberFreeCluster);
398 u32_to_u8array(sectorBuffer, FSIB_numberLastAllocCluster, partition->fat.numberLastAllocCluster);
400 // Read first sector of disc
401 if (!_FAT_disc_writeSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer)) {
402 return;