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"
32 #include "file_allocation_table.h"
33 #include "directory.h"
34 #include "mem_allocate.h"
39 #include <sys/iosupport.h>
42 This device name, as known by devkitPro toolchains
44 const char* DEVICE_NAME
= "fat";
50 // BIOS Parameter Block offsets
54 // BIOS Parameter Block
55 BPB_bytesPerSector
= 0x0B,
56 BPB_sectorsPerCluster
= 0x0D,
57 BPB_reservedSectors
= 0x0E,
59 BPB_rootEntries
= 0x11,
60 BPB_numSectorsSmall
= 0x13,
62 BPB_sectorsPerFAT
= 0x16,
63 BPB_sectorsPerTrk
= 0x18,
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,
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,
91 BPB_FAT32_bootCode
= 0x5A,
92 BPB_bootSig_55
= 0x1FE,
93 BPB_bootSig_AA
= 0x1FF
96 // File system information block offsets
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];
118 uint8_t sectorBuffer
[BYTES_PER_READ
] = {0};
120 // Read first sector of disc
121 if (!_FAT_disc_readSectors (disc
, 0, 1, sectorBuffer
)) {
125 memcpy(part_table
,sectorBuffer
+0x1BE,16*4);
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
))) {
136 if(ptr
[4]==0) continue;
139 sec_t part_lba2
=part_lba
;
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
)))
158 if(next_lba2
==0) break;
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
))) {
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
)) {
180 // Make sure it is a valid MBR or boot sector
181 if ( (sectorBuffer
[BPB_bootSig_55
] != 0x55) || (sectorBuffer
[BPB_bootSig_AA
] != 0xAA)) {
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
190 } else if (!memcmp(sectorBuffer
+ BPB_FAT32_fileSysType
, FAT_SIG
, sizeof(FAT_SIG
))) {
194 startSector
= FindFirstValidPartition(disc
);
195 if (!_FAT_disc_readSectors (disc
, startSector
, 1, sectorBuffer
)) {
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
)))
207 partition
= (PARTITION
*) _FAT_mem_allocate (sizeof(PARTITION
));
208 if (partition
== 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);
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
;
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
261 partition
->filesysType
= FS_FAT32
; // FAT32 volume
264 if (partition
->filesysType
!= FS_FAT32
) {
265 partition
->rootDirCluster
= FAT16_ROOT_DIR_CLUSTER
;
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
);
294 void _FAT_partition_destructor (PARTITION
* partition
) {
295 FILE_STRUCT
* nextFile
;
297 _FAT_lock(&partition
->lock
);
299 // Synchronize open files
300 nextFile
= partition
->firstOpenFile
;
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
);
329 return (PARTITION
*)devops
->deviceData
;
332 void _FAT_partition_createFSinfo(PARTITION
* partition
)
334 if(partition
->readOnly
|| partition
->filesysType
!= FS_FAT32
)
337 uint8_t sectorBuffer
[BYTES_PER_READ
];
338 memset(sectorBuffer
, 0, sizeof(sectorBuffer
));
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
)
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
)) {
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
);
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
)
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
)) {
394 if(memcmp(sectorBuffer
+FSIB_SIG1
, FS_INFO_SIG1
, 4) || memcmp(sectorBuffer
+FSIB_SIG2
, FS_INFO_SIG2
, 4))
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
)) {