remove trailing spaces
[libfat.git] / source / partition.c
blob7351da9418fc7bba245b3e5493d731f9217d65c1
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.
31 #include "partition.h"
32 #include "bit_ops.h"
33 #include "file_allocation_table.h"
34 #include "directory.h"
35 #include "mem_allocate.h"
36 #include "fatfile.h"
38 #include <string.h>
39 #include <ctype.h>
40 #include <sys/iosupport.h>
42 /*
43 This device name, as known by devkitPro toolchains
45 const char* DEVICE_NAME = "fat";
48 Data offsets
51 // BIOS Parameter Block offsets
52 enum BPB {
53 BPB_jmpBoot = 0x00,
54 BPB_OEMName = 0x03,
55 // BIOS Parameter Block
56 BPB_bytesPerSector = 0x0B,
57 BPB_sectorsPerCluster = 0x0D,
58 BPB_reservedSectors = 0x0E,
59 BPB_numFATs = 0x10,
60 BPB_rootEntries = 0x11,
61 BPB_numSectorsSmall = 0x13,
62 BPB_mediaDesc = 0x15,
63 BPB_sectorsPerFAT = 0x16,
64 BPB_sectorsPerTrk = 0x18,
65 BPB_numHeads = 0x1A,
66 BPB_numHiddenSectors = 0x1C,
67 BPB_numSectors = 0x20,
68 // Ext BIOS Parameter Block for FAT16
69 BPB_FAT16_driveNumber = 0x24,
70 BPB_FAT16_reserved1 = 0x25,
71 BPB_FAT16_extBootSig = 0x26,
72 BPB_FAT16_volumeID = 0x27,
73 BPB_FAT16_volumeLabel = 0x2B,
74 BPB_FAT16_fileSysType = 0x36,
75 // Bootcode
76 BPB_FAT16_bootCode = 0x3E,
77 // FAT32 extended block
78 BPB_FAT32_sectorsPerFAT32 = 0x24,
79 BPB_FAT32_extFlags = 0x28,
80 BPB_FAT32_fsVer = 0x2A,
81 BPB_FAT32_rootClus = 0x2C,
82 BPB_FAT32_fsInfo = 0x30,
83 BPB_FAT32_bkBootSec = 0x32,
84 // Ext BIOS Parameter Block for FAT32
85 BPB_FAT32_driveNumber = 0x40,
86 BPB_FAT32_reserved1 = 0x41,
87 BPB_FAT32_extBootSig = 0x42,
88 BPB_FAT32_volumeID = 0x43,
89 BPB_FAT32_volumeLabel = 0x47,
90 BPB_FAT32_fileSysType = 0x52,
91 // Bootcode
92 BPB_FAT32_bootCode = 0x5A,
93 BPB_bootSig_55 = 0x1FE,
94 BPB_bootSig_AA = 0x1FF
97 static const char FAT_SIG[3] = {'F', 'A', 'T'};
100 PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cacheSize, sec_t startSector) {
101 PARTITION* partition;
102 int i;
103 uint8_t sectorBuffer[BYTES_PER_READ] = {0};
105 // Read first sector of disc
106 if (!_FAT_disc_readSectors (disc, startSector, 1, sectorBuffer)) {
107 return NULL;
110 // Make sure it is a valid MBR or boot sector
111 if ( (sectorBuffer[BPB_bootSig_55] != 0x55) || (sectorBuffer[BPB_bootSig_AA] != 0xAA)) {
112 return NULL;
115 if (startSector != 0) {
116 // We're told where to start the partition, so just accept it
117 } else if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG))) {
118 // Check if there is a FAT string, which indicates this is a boot sector
119 startSector = 0;
120 } else if (!memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) {
121 // Check for FAT32
122 startSector = 0;
123 } else {
124 // This is an MBR
125 // Find first valid partition from MBR
126 // First check for an active partition
127 for (i=0x1BE; (i < 0x1FE) && (sectorBuffer[i] != 0x80); i+= 0x10);
128 // If it didn't find an active partition, search for any valid partition
129 if (i == 0x1FE) {
130 for (i=0x1BE; (i < 0x1FE) && (sectorBuffer[i+0x04] == 0x00); i+= 0x10);
133 if ( i != 0x1FE) {
134 // Go to first valid partition
135 startSector = u8array_to_u32(sectorBuffer, 0x8 + i);
136 // Load the BPB
137 if (!_FAT_disc_readSectors (disc, startSector, 1, sectorBuffer)) {
138 return NULL;
140 // Make sure it is a valid BPB
141 if ( (sectorBuffer[BPB_bootSig_55] != 0x55) || (sectorBuffer[BPB_bootSig_AA] != 0xAA)) {
142 return NULL;
144 } else {
145 // No partition found, assume this is a MBR free disk
146 startSector = 0;
150 // Now verify that this is indeed a FAT partition
151 if (memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) &&
152 memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG)))
154 return NULL;
157 // check again for the last two cases to make sure that we really have a FAT filesystem here
158 // and won't corrupt any data
159 if(memcmp(sectorBuffer + BPB_FAT16_fileSysType, "FAT", 3) != 0 && memcmp(sectorBuffer + BPB_FAT32_fileSysType, "FAT32", 5) != 0)
161 return NULL;
164 partition = (PARTITION*) _FAT_mem_allocate (sizeof(PARTITION));
165 if (partition == NULL) {
166 return NULL;
169 // Init the partition lock
170 _FAT_lock_init(&partition->lock);
172 // Set partition's disc interface
173 partition->disc = disc;
175 // Store required information about the file system
176 partition->fat.sectorsPerFat = u8array_to_u16(sectorBuffer, BPB_sectorsPerFAT);
177 if (partition->fat.sectorsPerFat == 0) {
178 partition->fat.sectorsPerFat = u8array_to_u32( sectorBuffer, BPB_FAT32_sectorsPerFAT32);
181 partition->numberOfSectors = u8array_to_u16( sectorBuffer, BPB_numSectorsSmall);
182 if (partition->numberOfSectors == 0) {
183 partition->numberOfSectors = u8array_to_u32( sectorBuffer, BPB_numSectors);
186 partition->bytesPerSector = BYTES_PER_READ; // Sector size is redefined to be 512 bytes
187 partition->sectorsPerCluster = sectorBuffer[BPB_sectorsPerCluster] * u8array_to_u16(sectorBuffer, BPB_bytesPerSector) / BYTES_PER_READ;
188 partition->bytesPerCluster = partition->bytesPerSector * partition->sectorsPerCluster;
189 partition->fat.fatStart = startSector + u8array_to_u16(sectorBuffer, BPB_reservedSectors);
191 partition->rootDirStart = partition->fat.fatStart + (sectorBuffer[BPB_numFATs] * partition->fat.sectorsPerFat);
192 partition->dataStart = partition->rootDirStart +
193 (( u8array_to_u16(sectorBuffer, BPB_rootEntries) * DIR_ENTRY_DATA_SIZE) / partition->bytesPerSector);
195 partition->totalSize = ((uint64_t)partition->numberOfSectors - (partition->dataStart - startSector)) * (uint64_t)partition->bytesPerSector;
197 // Store info about FAT
198 uint32_t clusterCount = (partition->numberOfSectors - (uint32_t)(partition->dataStart - startSector)) / partition->sectorsPerCluster;
199 partition->fat.lastCluster = clusterCount + CLUSTER_FIRST - 1;
200 partition->fat.firstFree = CLUSTER_FIRST;
202 if (clusterCount < CLUSTERS_PER_FAT12) {
203 partition->filesysType = FS_FAT12; // FAT12 volume
204 } else if (clusterCount < CLUSTERS_PER_FAT16) {
205 partition->filesysType = FS_FAT16; // FAT16 volume
206 } else {
207 partition->filesysType = FS_FAT32; // FAT32 volume
210 if (partition->filesysType != FS_FAT32) {
211 partition->rootDirCluster = FAT16_ROOT_DIR_CLUSTER;
212 } else {
213 // Set up for the FAT32 way
214 partition->rootDirCluster = u8array_to_u32(sectorBuffer, BPB_FAT32_rootClus);
215 // Check if FAT mirroring is enabled
216 if (!(sectorBuffer[BPB_FAT32_extFlags] & 0x80)) {
217 // Use the active FAT
218 partition->fat.fatStart = partition->fat.fatStart + ( partition->fat.sectorsPerFat * (sectorBuffer[BPB_FAT32_extFlags] & 0x0F));
222 // Create a cache to use
223 partition->cache = _FAT_cache_constructor (cacheSize, partition->disc);
225 // Set current directory to the root
226 partition->cwdCluster = partition->rootDirCluster;
228 // Check if this disc is writable, and set the readOnly property appropriately
229 partition->readOnly = !(_FAT_disc_features(disc) & FEATURE_MEDIUM_CANWRITE);
231 // There are currently no open files on this partition
232 partition->openFileCount = 0;
233 partition->firstOpenFile = NULL;
235 return partition;
238 void _FAT_partition_destructor (PARTITION* partition) {
239 FILE_STRUCT* nextFile;
241 _FAT_lock(&partition->lock);
243 // Synchronize open files
244 nextFile = partition->firstOpenFile;
245 while (nextFile) {
246 _FAT_syncToDisc (nextFile);
247 nextFile = nextFile->nextOpenFile;
250 // Free memory used by the cache, writing it to disc at the same time
251 _FAT_cache_destructor (partition->cache);
253 // Unlock the partition and destroy the lock
254 _FAT_unlock(&partition->lock);
255 _FAT_lock_deinit(&partition->lock);
257 // Free memory used by the partition
258 _FAT_mem_free (partition);
262 PARTITION* _FAT_partition_getPartitionFromPath (const char* path) {
263 const devoptab_t *devops;
265 devops = GetDeviceOpTab (path);
267 if (!devops) {
268 return NULL;
271 return (PARTITION*)devops->deviceData;