[FREELDR] Add FAT caching to FAT filesystem driver.
[reactos.git] / boot / freeldr / freeldr / lib / fs / fat.c
1 /*
2 * FreeLoader
3 * Copyright (C) 1998-2003 Brian Palmer <brianp@sginet.com>
4 * Copyright (C) 2009 Hervé Poussineau
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include <freeldr.h>
22
23 #include <debug.h>
24 DBG_DEFAULT_CHANNEL(FILESYSTEM);
25
26 ULONG FatDetermineFatType(PFAT_BOOTSECTOR FatBootSector, ULONGLONG PartitionSectorCount);
27 PVOID FatBufferDirectory(PFAT_VOLUME_INFO Volume, ULONG DirectoryStartCluster, ULONG* EntryCountPointer, BOOLEAN RootDirectory);
28 BOOLEAN FatSearchDirectoryBufferForFile(PFAT_VOLUME_INFO Volume, PVOID DirectoryBuffer, ULONG EntryCount, PCHAR FileName, PFAT_FILE_INFO FatFileInfoPointer);
29 ARC_STATUS FatLookupFile(PFAT_VOLUME_INFO Volume, PCSTR FileName, PFAT_FILE_INFO FatFileInfoPointer);
30 void FatParseShortFileName(PCHAR Buffer, PDIRENTRY DirEntry);
31 BOOLEAN FatGetFatEntry(PFAT_VOLUME_INFO Volume, ULONG Cluster, ULONG* ClusterPointer);
32 ULONG FatCountClustersInChain(PFAT_VOLUME_INFO Volume, ULONG StartCluster);
33 ULONG* FatGetClusterChainArray(PFAT_VOLUME_INFO Volume, ULONG StartCluster);
34 BOOLEAN FatReadClusterChain(PFAT_VOLUME_INFO Volume, ULONG StartClusterNumber, ULONG NumberOfClusters, PVOID Buffer);
35 BOOLEAN FatReadPartialCluster(PFAT_VOLUME_INFO Volume, ULONG ClusterNumber, ULONG StartingOffset, ULONG Length, PVOID Buffer);
36 BOOLEAN FatReadVolumeSectors(PFAT_VOLUME_INFO Volume, ULONG SectorNumber, ULONG SectorCount, PVOID Buffer);
37
38 #define TAG_FAT_CHAIN 'CtaT'
39 #define TAG_FAT_FILE 'FtaF'
40 #define TAG_FAT_VOLUME 'VtaF'
41 #define TAG_FAT_BUFFER 'BtaF'
42 #define TAG_FAT_CACHE 'HtaF'
43
44 #define FAT_MAX_CACHE_SIZE (256 * 1024) // 256 KiB, note: it should fit maximum FAT12 FAT size (6144 bytes)
45
46 typedef struct _FAT_VOLUME_INFO
47 {
48 PUCHAR FatCache; /* A part of 1st FAT cached in memory */
49 PULONG FatCacheIndex; /* Cached sector's indexes */
50 ULONG FatCacheSize; /* Size of the cache in sectors */
51 ULONG FatSectorStart; /* Starting sector of 1st FAT table */
52 ULONG ActiveFatSectorStart; /* Starting sector of active FAT table */
53 ULONG SectorsPerFat; /* Sectors per FAT table */
54 ULONG RootDirSectorStart; /* Starting sector of the root directory (non-fat32) */
55 ULONG RootDirSectors; /* Number of sectors of the root directory (non-fat32) */
56 ULONG RootDirStartCluster; /* Starting cluster number of the root directory (fat32 only) */
57 ULONG DataSectorStart; /* Starting sector of the data area */
58 ULONG DeviceId;
59 UINT16 BytesPerSector; /* Number of bytes per sector */
60 UINT8 FatType; /* FAT12, FAT16, FAT32, FATX16 or FATX32 */
61 UINT8 NumberOfFats; /* Number of FAT tables */
62 UINT8 SectorsPerCluster; /* Number of sectors per cluster */
63 } FAT_VOLUME_INFO;
64
65 PFAT_VOLUME_INFO FatVolumes[MAX_FDS];
66
67 VOID FatSwapFatBootSector(PFAT_BOOTSECTOR Obj)
68 {
69 SW(Obj, BytesPerSector);
70 SW(Obj, ReservedSectors);
71 SW(Obj, RootDirEntries);
72 SW(Obj, TotalSectors);
73 SW(Obj, SectorsPerFat);
74 SW(Obj, SectorsPerTrack);
75 SW(Obj, NumberOfHeads);
76 SD(Obj, HiddenSectors);
77 SD(Obj, TotalSectorsBig);
78 SD(Obj, VolumeSerialNumber);
79 SW(Obj, BootSectorMagic);
80 }
81
82 VOID FatSwapFat32BootSector(PFAT32_BOOTSECTOR Obj)
83 {
84 SW(Obj, BytesPerSector);
85 SW(Obj, ReservedSectors);
86 SW(Obj, RootDirEntries);
87 SW(Obj, TotalSectors);
88 SW(Obj, SectorsPerFat);
89 SW(Obj, NumberOfHeads);
90 SD(Obj, HiddenSectors);
91 SD(Obj, TotalSectorsBig);
92 SD(Obj, SectorsPerFatBig);
93 SW(Obj, ExtendedFlags);
94 SW(Obj, FileSystemVersion);
95 SD(Obj, RootDirStartCluster);
96 SW(Obj, FsInfo);
97 SW(Obj, BackupBootSector);
98 SD(Obj, VolumeSerialNumber);
99 SW(Obj, BootSectorMagic);
100 }
101
102 VOID FatSwapFatXBootSector(PFATX_BOOTSECTOR Obj)
103 {
104 SD(Obj, VolumeSerialNumber);
105 SD(Obj, SectorsPerCluster);
106 SW(Obj, NumberOfFats);
107 }
108
109 VOID FatSwapDirEntry(PDIRENTRY Obj)
110 {
111 SW(Obj, CreateTime);
112 SW(Obj, CreateDate);
113 SW(Obj, LastAccessDate);
114 SW(Obj, ClusterHigh);
115 SW(Obj, Time);
116 SW(Obj, Date);
117 SW(Obj, ClusterLow);
118 SD(Obj, Size);
119 }
120
121 VOID FatSwapLFNDirEntry(PLFN_DIRENTRY Obj)
122 {
123 int i;
124 SW(Obj, StartCluster);
125 for(i = 0; i < 5; i++)
126 Obj->Name0_4[i] = SWAPW(Obj->Name0_4[i]);
127 for(i = 0; i < 6; i++)
128 Obj->Name5_10[i] = SWAPW(Obj->Name5_10[i]);
129 for(i = 0; i < 2; i++)
130 Obj->Name11_12[i] = SWAPW(Obj->Name11_12[i]);
131 }
132
133 VOID FatSwapFatXDirEntry(PFATX_DIRENTRY Obj)
134 {
135 SD(Obj, StartCluster);
136 SD(Obj, Size);
137 SW(Obj, Time);
138 SW(Obj, Date);
139 SW(Obj, CreateTime);
140 SW(Obj, CreateDate);
141 SW(Obj, LastAccessTime);
142 SW(Obj, LastAccessDate);
143 }
144
145 BOOLEAN FatOpenVolume(PFAT_VOLUME_INFO Volume, PFAT_BOOTSECTOR BootSector, ULONGLONG PartitionSectorCount)
146 {
147 char ErrMsg[80];
148 ULONG FatSize, i;
149 PFAT_BOOTSECTOR FatVolumeBootSector;
150 PFAT32_BOOTSECTOR Fat32VolumeBootSector;
151 PFATX_BOOTSECTOR FatXVolumeBootSector;
152
153 TRACE("FatOpenVolume() DeviceId = %d\n", Volume->DeviceId);
154
155 //
156 // Allocate the memory to hold the boot sector
157 //
158 FatVolumeBootSector = (PFAT_BOOTSECTOR)BootSector;
159 Fat32VolumeBootSector = (PFAT32_BOOTSECTOR)BootSector;
160 FatXVolumeBootSector = (PFATX_BOOTSECTOR)BootSector;
161
162 // Get the FAT type
163 Volume->FatType = FatDetermineFatType(FatVolumeBootSector, PartitionSectorCount);
164
165 // Dump boot sector (and swap it for big endian systems)
166 TRACE("Dumping boot sector:\n");
167 if (ISFATX(Volume->FatType))
168 {
169 FatSwapFatXBootSector(FatXVolumeBootSector);
170 TRACE("sizeof(FATX_BOOTSECTOR) = 0x%x.\n", sizeof(FATX_BOOTSECTOR));
171
172 TRACE("FileSystemType: %c%c%c%c.\n", FatXVolumeBootSector->FileSystemType[0], FatXVolumeBootSector->FileSystemType[1], FatXVolumeBootSector->FileSystemType[2], FatXVolumeBootSector->FileSystemType[3]);
173 TRACE("VolumeSerialNumber: 0x%x\n", FatXVolumeBootSector->VolumeSerialNumber);
174 TRACE("SectorsPerCluster: %d\n", FatXVolumeBootSector->SectorsPerCluster);
175 TRACE("NumberOfFats: %d\n", FatXVolumeBootSector->NumberOfFats);
176 TRACE("Unknown: 0x%x\n", FatXVolumeBootSector->Unknown);
177
178 TRACE("FatType %s\n", Volume->FatType == FATX16 ? "FATX16" : "FATX32");
179
180 }
181 else if (Volume->FatType == FAT32)
182 {
183 FatSwapFat32BootSector(Fat32VolumeBootSector);
184 TRACE("sizeof(FAT32_BOOTSECTOR) = 0x%x.\n", sizeof(FAT32_BOOTSECTOR));
185
186 TRACE("JumpBoot: 0x%x 0x%x 0x%x\n", Fat32VolumeBootSector->JumpBoot[0], Fat32VolumeBootSector->JumpBoot[1], Fat32VolumeBootSector->JumpBoot[2]);
187 TRACE("OemName: %c%c%c%c%c%c%c%c\n", Fat32VolumeBootSector->OemName[0], Fat32VolumeBootSector->OemName[1], Fat32VolumeBootSector->OemName[2], Fat32VolumeBootSector->OemName[3], Fat32VolumeBootSector->OemName[4], Fat32VolumeBootSector->OemName[5], Fat32VolumeBootSector->OemName[6], Fat32VolumeBootSector->OemName[7]);
188 TRACE("BytesPerSector: %d\n", Fat32VolumeBootSector->BytesPerSector);
189 TRACE("SectorsPerCluster: %d\n", Fat32VolumeBootSector->SectorsPerCluster);
190 TRACE("ReservedSectors: %d\n", Fat32VolumeBootSector->ReservedSectors);
191 TRACE("NumberOfFats: %d\n", Fat32VolumeBootSector->NumberOfFats);
192 TRACE("RootDirEntries: %d\n", Fat32VolumeBootSector->RootDirEntries);
193 TRACE("TotalSectors: %d\n", Fat32VolumeBootSector->TotalSectors);
194 TRACE("MediaDescriptor: 0x%x\n", Fat32VolumeBootSector->MediaDescriptor);
195 TRACE("SectorsPerFat: %d\n", Fat32VolumeBootSector->SectorsPerFat);
196 TRACE("SectorsPerTrack: %d\n", Fat32VolumeBootSector->SectorsPerTrack);
197 TRACE("NumberOfHeads: %d\n", Fat32VolumeBootSector->NumberOfHeads);
198 TRACE("HiddenSectors: %d\n", Fat32VolumeBootSector->HiddenSectors);
199 TRACE("TotalSectorsBig: %d\n", Fat32VolumeBootSector->TotalSectorsBig);
200 TRACE("SectorsPerFatBig: %d\n", Fat32VolumeBootSector->SectorsPerFatBig);
201 TRACE("ExtendedFlags: 0x%x\n", Fat32VolumeBootSector->ExtendedFlags);
202 TRACE("FileSystemVersion: 0x%x\n", Fat32VolumeBootSector->FileSystemVersion);
203 TRACE("RootDirStartCluster: %d\n", Fat32VolumeBootSector->RootDirStartCluster);
204 TRACE("FsInfo: %d\n", Fat32VolumeBootSector->FsInfo);
205 TRACE("BackupBootSector: %d\n", Fat32VolumeBootSector->BackupBootSector);
206 TRACE("Reserved: 0x%x\n", Fat32VolumeBootSector->Reserved);
207 TRACE("DriveNumber: 0x%x\n", Fat32VolumeBootSector->DriveNumber);
208 TRACE("Reserved1: 0x%x\n", Fat32VolumeBootSector->Reserved1);
209 TRACE("BootSignature: 0x%x\n", Fat32VolumeBootSector->BootSignature);
210 TRACE("VolumeSerialNumber: 0x%x\n", Fat32VolumeBootSector->VolumeSerialNumber);
211 TRACE("VolumeLabel: %c%c%c%c%c%c%c%c%c%c%c\n", Fat32VolumeBootSector->VolumeLabel[0], Fat32VolumeBootSector->VolumeLabel[1], Fat32VolumeBootSector->VolumeLabel[2], Fat32VolumeBootSector->VolumeLabel[3], Fat32VolumeBootSector->VolumeLabel[4], Fat32VolumeBootSector->VolumeLabel[5], Fat32VolumeBootSector->VolumeLabel[6], Fat32VolumeBootSector->VolumeLabel[7], Fat32VolumeBootSector->VolumeLabel[8], Fat32VolumeBootSector->VolumeLabel[9], Fat32VolumeBootSector->VolumeLabel[10]);
212 TRACE("FileSystemType: %c%c%c%c%c%c%c%c\n", Fat32VolumeBootSector->FileSystemType[0], Fat32VolumeBootSector->FileSystemType[1], Fat32VolumeBootSector->FileSystemType[2], Fat32VolumeBootSector->FileSystemType[3], Fat32VolumeBootSector->FileSystemType[4], Fat32VolumeBootSector->FileSystemType[5], Fat32VolumeBootSector->FileSystemType[6], Fat32VolumeBootSector->FileSystemType[7]);
213 TRACE("BootSectorMagic: 0x%x\n", Fat32VolumeBootSector->BootSectorMagic);
214 }
215 else
216 {
217 FatSwapFatBootSector(FatVolumeBootSector);
218 TRACE("sizeof(FAT_BOOTSECTOR) = 0x%x.\n", sizeof(FAT_BOOTSECTOR));
219
220 TRACE("JumpBoot: 0x%x 0x%x 0x%x\n", FatVolumeBootSector->JumpBoot[0], FatVolumeBootSector->JumpBoot[1], FatVolumeBootSector->JumpBoot[2]);
221 TRACE("OemName: %c%c%c%c%c%c%c%c\n", FatVolumeBootSector->OemName[0], FatVolumeBootSector->OemName[1], FatVolumeBootSector->OemName[2], FatVolumeBootSector->OemName[3], FatVolumeBootSector->OemName[4], FatVolumeBootSector->OemName[5], FatVolumeBootSector->OemName[6], FatVolumeBootSector->OemName[7]);
222 TRACE("BytesPerSector: %d\n", FatVolumeBootSector->BytesPerSector);
223 TRACE("SectorsPerCluster: %d\n", FatVolumeBootSector->SectorsPerCluster);
224 TRACE("ReservedSectors: %d\n", FatVolumeBootSector->ReservedSectors);
225 TRACE("NumberOfFats: %d\n", FatVolumeBootSector->NumberOfFats);
226 TRACE("RootDirEntries: %d\n", FatVolumeBootSector->RootDirEntries);
227 TRACE("TotalSectors: %d\n", FatVolumeBootSector->TotalSectors);
228 TRACE("MediaDescriptor: 0x%x\n", FatVolumeBootSector->MediaDescriptor);
229 TRACE("SectorsPerFat: %d\n", FatVolumeBootSector->SectorsPerFat);
230 TRACE("SectorsPerTrack: %d\n", FatVolumeBootSector->SectorsPerTrack);
231 TRACE("NumberOfHeads: %d\n", FatVolumeBootSector->NumberOfHeads);
232 TRACE("HiddenSectors: %d\n", FatVolumeBootSector->HiddenSectors);
233 TRACE("TotalSectorsBig: %d\n", FatVolumeBootSector->TotalSectorsBig);
234 TRACE("DriveNumber: 0x%x\n", FatVolumeBootSector->DriveNumber);
235 TRACE("Reserved1: 0x%x\n", FatVolumeBootSector->Reserved1);
236 TRACE("BootSignature: 0x%x\n", FatVolumeBootSector->BootSignature);
237 TRACE("VolumeSerialNumber: 0x%x\n", FatVolumeBootSector->VolumeSerialNumber);
238 TRACE("VolumeLabel: %c%c%c%c%c%c%c%c%c%c%c\n", FatVolumeBootSector->VolumeLabel[0], FatVolumeBootSector->VolumeLabel[1], FatVolumeBootSector->VolumeLabel[2], FatVolumeBootSector->VolumeLabel[3], FatVolumeBootSector->VolumeLabel[4], FatVolumeBootSector->VolumeLabel[5], FatVolumeBootSector->VolumeLabel[6], FatVolumeBootSector->VolumeLabel[7], FatVolumeBootSector->VolumeLabel[8], FatVolumeBootSector->VolumeLabel[9], FatVolumeBootSector->VolumeLabel[10]);
239 TRACE("FileSystemType: %c%c%c%c%c%c%c%c\n", FatVolumeBootSector->FileSystemType[0], FatVolumeBootSector->FileSystemType[1], FatVolumeBootSector->FileSystemType[2], FatVolumeBootSector->FileSystemType[3], FatVolumeBootSector->FileSystemType[4], FatVolumeBootSector->FileSystemType[5], FatVolumeBootSector->FileSystemType[6], FatVolumeBootSector->FileSystemType[7]);
240 TRACE("BootSectorMagic: 0x%x\n", FatVolumeBootSector->BootSectorMagic);
241 }
242
243 //
244 // Check the boot sector magic
245 //
246 if (! ISFATX(Volume->FatType) && FatVolumeBootSector->BootSectorMagic != 0xaa55)
247 {
248 sprintf(ErrMsg, "Invalid boot sector magic (expected 0xaa55 found 0x%x)",
249 FatVolumeBootSector->BootSectorMagic);
250 FileSystemError(ErrMsg);
251 return FALSE;
252 }
253
254 //
255 // Check the FAT cluster size
256 // We do not support clusters bigger than 64k
257 //
258 if ((ISFATX(Volume->FatType) && 64 * 1024 < FatXVolumeBootSector->SectorsPerCluster * 512) ||
259 (! ISFATX(Volume->FatType) && 64 * 1024 < FatVolumeBootSector->SectorsPerCluster * FatVolumeBootSector->BytesPerSector))
260 {
261 FileSystemError("This file system has cluster sizes bigger than 64k.\nFreeLoader does not support this.");
262 return FALSE;
263 }
264
265 //
266 // Get the sectors per FAT,
267 // root directory starting sector,
268 // and data sector start
269 //
270 if (ISFATX(Volume->FatType))
271 {
272 Volume->BytesPerSector = 512;
273 Volume->SectorsPerCluster = SWAPD(FatXVolumeBootSector->SectorsPerCluster);
274 Volume->FatSectorStart = (0x1000 / Volume->BytesPerSector);
275 Volume->ActiveFatSectorStart = Volume->FatSectorStart;
276 Volume->NumberOfFats = 1;
277 FatSize = (ULONG)(PartitionSectorCount / Volume->SectorsPerCluster *
278 (Volume->FatType == FATX16 ? 2 : 4));
279 Volume->SectorsPerFat = ROUND_UP(FatSize, 0x1000) / Volume->BytesPerSector;
280
281 Volume->RootDirSectorStart = Volume->FatSectorStart + Volume->NumberOfFats * Volume->SectorsPerFat;
282 Volume->RootDirSectors = FatXVolumeBootSector->SectorsPerCluster;
283
284 Volume->DataSectorStart = Volume->RootDirSectorStart + Volume->RootDirSectors;
285 }
286 else if (Volume->FatType != FAT32)
287 {
288 Volume->BytesPerSector = FatVolumeBootSector->BytesPerSector;
289 Volume->SectorsPerCluster = FatVolumeBootSector->SectorsPerCluster;
290 Volume->FatSectorStart = FatVolumeBootSector->ReservedSectors;
291 Volume->ActiveFatSectorStart = Volume->FatSectorStart;
292 Volume->NumberOfFats = FatVolumeBootSector->NumberOfFats;
293 Volume->SectorsPerFat = FatVolumeBootSector->SectorsPerFat;
294
295 Volume->RootDirSectorStart = Volume->FatSectorStart + Volume->NumberOfFats * Volume->SectorsPerFat;
296 Volume->RootDirSectors = ((FatVolumeBootSector->RootDirEntries * 32) + (Volume->BytesPerSector - 1)) / Volume->BytesPerSector;
297
298 Volume->DataSectorStart = Volume->RootDirSectorStart + Volume->RootDirSectors;
299 }
300 else
301 {
302 Volume->BytesPerSector = Fat32VolumeBootSector->BytesPerSector;
303 Volume->SectorsPerCluster = Fat32VolumeBootSector->SectorsPerCluster;
304 Volume->FatSectorStart = Fat32VolumeBootSector->ReservedSectors;
305 Volume->ActiveFatSectorStart = Volume->FatSectorStart +
306 ((Fat32VolumeBootSector->ExtendedFlags & 0x80) ? ((Fat32VolumeBootSector->ExtendedFlags & 0x0f) * Fat32VolumeBootSector->SectorsPerFatBig) : 0);
307 Volume->NumberOfFats = Fat32VolumeBootSector->NumberOfFats;
308 Volume->SectorsPerFat = Fat32VolumeBootSector->SectorsPerFatBig;
309
310 Volume->RootDirStartCluster = Fat32VolumeBootSector->RootDirStartCluster;
311 Volume->DataSectorStart = Volume->FatSectorStart + Volume->NumberOfFats * Volume->SectorsPerFat;
312
313 //
314 // Check version
315 // we only work with version 0
316 //
317 if (Fat32VolumeBootSector->FileSystemVersion != 0)
318 {
319 FileSystemError("FreeLoader is too old to work with this FAT32 filesystem.\nPlease update FreeLoader.");
320 return FALSE;
321 }
322 }
323
324 Volume->FatCacheSize = min(Volume->SectorsPerFat, FAT_MAX_CACHE_SIZE / Volume->BytesPerSector);
325 TRACE("FAT cache is %d sectors, %d bytes\n", Volume->FatCacheSize, Volume->FatCacheSize * Volume->BytesPerSector);
326
327 Volume->FatCache = FrLdrTempAlloc(Volume->FatCacheSize * Volume->BytesPerSector, TAG_FAT_CACHE);
328 if (!Volume->FatCache)
329 {
330 FileSystemError("Cannot allocate memory for FAT cache");
331 return FALSE;
332 }
333
334 Volume->FatCacheIndex = FrLdrTempAlloc(Volume->FatCacheSize * sizeof(*Volume->FatCacheIndex), TAG_FAT_VOLUME);
335 if (!Volume->FatCacheIndex)
336 {
337 FileSystemError("Cannot allocate memory for FAT cache index");
338 FrLdrTempFree(Volume->FatCache, TAG_FAT_CACHE);
339 return FALSE;
340 }
341
342 // read the beginning of the FAT (or the whole one) to cache
343 if (!FatReadVolumeSectors(Volume, Volume->ActiveFatSectorStart, Volume->FatCacheSize, Volume->FatCache))
344 {
345 FileSystemError("Error when reading FAT cache");
346 FrLdrTempFree(Volume->FatCache, TAG_FAT_CACHE);
347 FrLdrTempFree(Volume->FatCacheIndex, TAG_FAT_VOLUME);
348 return FALSE;
349 }
350
351 // fill the index with sector numbers
352 for (i = 0; i < Volume->FatCacheSize; i++)
353 {
354 Volume->FatCacheIndex[i] = Volume->ActiveFatSectorStart + i;
355 }
356
357 return TRUE;
358 }
359
360 ULONG FatDetermineFatType(PFAT_BOOTSECTOR FatBootSector, ULONGLONG PartitionSectorCount)
361 {
362 ULONG RootDirSectors;
363 ULONG DataSectorCount;
364 ULONG SectorsPerFat;
365 ULONG TotalSectors;
366 ULONG CountOfClusters;
367 PFAT32_BOOTSECTOR Fat32BootSector = (PFAT32_BOOTSECTOR)FatBootSector;
368 PFATX_BOOTSECTOR FatXBootSector = (PFATX_BOOTSECTOR)FatBootSector;
369
370 if (0 == strncmp(FatXBootSector->FileSystemType, "FATX", 4))
371 {
372 CountOfClusters = (ULONG)(PartitionSectorCount / FatXBootSector->SectorsPerCluster);
373 if (CountOfClusters < 65525)
374 {
375 /* Volume is FATX16 */
376 return FATX16;
377 }
378 else
379 {
380 /* Volume is FAT32 */
381 return FATX32;
382 }
383 }
384 else
385 {
386 RootDirSectors = ((SWAPW(FatBootSector->RootDirEntries) * 32) + (SWAPW(FatBootSector->BytesPerSector) - 1)) / SWAPW(FatBootSector->BytesPerSector);
387 SectorsPerFat = SWAPW(FatBootSector->SectorsPerFat) ? SWAPW(FatBootSector->SectorsPerFat) : SWAPD(Fat32BootSector->SectorsPerFatBig);
388 TotalSectors = SWAPW(FatBootSector->TotalSectors) ? SWAPW(FatBootSector->TotalSectors) : SWAPD(FatBootSector->TotalSectorsBig);
389 DataSectorCount = TotalSectors - (SWAPW(FatBootSector->ReservedSectors) + (FatBootSector->NumberOfFats * SectorsPerFat) + RootDirSectors);
390
391 //mjl
392 if (FatBootSector->SectorsPerCluster == 0)
393 CountOfClusters = 0;
394 else
395 CountOfClusters = DataSectorCount / FatBootSector->SectorsPerCluster;
396
397 if (CountOfClusters < 4085)
398 {
399 /* Volume is FAT12 */
400 return FAT12;
401 }
402 else if (CountOfClusters < 65525)
403 {
404 /* Volume is FAT16 */
405 return FAT16;
406 }
407 else
408 {
409 /* Volume is FAT32 */
410 return FAT32;
411 }
412 }
413 }
414
415 typedef struct _DIRECTORY_BUFFER
416 {
417 LIST_ENTRY Link;
418 PVOID Volume;
419 ULONG DirectoryStartCluster;
420 ULONG DirectorySize;
421 UCHAR Data[];
422 } DIRECTORY_BUFFER, *PDIRECTORY_BUFFER;
423
424 LIST_ENTRY DirectoryBufferListHead = {&DirectoryBufferListHead, &DirectoryBufferListHead};
425
426 PVOID FatBufferDirectory(PFAT_VOLUME_INFO Volume, ULONG DirectoryStartCluster, ULONG *DirectorySize, BOOLEAN RootDirectory)
427 {
428 PDIRECTORY_BUFFER DirectoryBuffer;
429 PLIST_ENTRY Entry;
430
431 TRACE("FatBufferDirectory() DirectoryStartCluster = %d RootDirectory = %s\n", DirectoryStartCluster, (RootDirectory ? "TRUE" : "FALSE"));
432
433 /*
434 * For FAT32, the root directory is nothing special. We can treat it the same
435 * as a subdirectory.
436 */
437 if (RootDirectory && Volume->FatType == FAT32)
438 {
439 DirectoryStartCluster = Volume->RootDirStartCluster;
440 RootDirectory = FALSE;
441 }
442
443 /* Search the list for a match */
444 for (Entry = DirectoryBufferListHead.Flink;
445 Entry != &DirectoryBufferListHead;
446 Entry = Entry->Flink)
447 {
448 DirectoryBuffer = CONTAINING_RECORD(Entry, DIRECTORY_BUFFER, Link);
449
450 /* Check if it matches */
451 if ((DirectoryBuffer->Volume == Volume) &&
452 (DirectoryBuffer->DirectoryStartCluster == DirectoryStartCluster))
453 {
454 TRACE("Found cached buffer\n");
455 *DirectorySize = DirectoryBuffer->DirectorySize;
456 return DirectoryBuffer->Data;
457 }
458 }
459
460 //
461 // Calculate the size of the directory
462 //
463 if (RootDirectory)
464 {
465 *DirectorySize = Volume->RootDirSectors * Volume->BytesPerSector;
466 }
467 else
468 {
469 *DirectorySize = FatCountClustersInChain(Volume, DirectoryStartCluster) * Volume->SectorsPerCluster * Volume->BytesPerSector;
470 }
471
472 //
473 // Attempt to allocate memory for directory buffer
474 //
475 TRACE("Trying to allocate (DirectorySize) %d bytes.\n", *DirectorySize);
476 DirectoryBuffer = FrLdrTempAlloc(*DirectorySize + sizeof(DIRECTORY_BUFFER),
477 TAG_FAT_BUFFER);
478
479 if (DirectoryBuffer == NULL)
480 {
481 return NULL;
482 }
483
484 //
485 // Now read directory contents into DirectoryBuffer
486 //
487 if (RootDirectory)
488 {
489 if (!FatReadVolumeSectors(Volume, Volume->RootDirSectorStart, Volume->RootDirSectors, DirectoryBuffer->Data))
490 {
491 FrLdrTempFree(DirectoryBuffer, TAG_FAT_BUFFER);
492 return NULL;
493 }
494 }
495 else
496 {
497 if (!FatReadClusterChain(Volume, DirectoryStartCluster, 0xFFFFFFFF, DirectoryBuffer->Data))
498 {
499 FrLdrTempFree(DirectoryBuffer, TAG_FAT_BUFFER);
500 return NULL;
501 }
502 }
503
504 /* Enqueue it in the list */
505 DirectoryBuffer->Volume = Volume;
506 DirectoryBuffer->DirectoryStartCluster = DirectoryStartCluster;
507 DirectoryBuffer->DirectorySize = *DirectorySize;
508 InsertTailList(&DirectoryBufferListHead, &DirectoryBuffer->Link);
509
510 return DirectoryBuffer->Data;
511 }
512
513 BOOLEAN FatSearchDirectoryBufferForFile(PFAT_VOLUME_INFO Volume, PVOID DirectoryBuffer, ULONG DirectorySize, PCHAR FileName, PFAT_FILE_INFO FatFileInfoPointer)
514 {
515 ULONG EntryCount;
516 ULONG CurrentEntry;
517 CHAR LfnNameBuffer[265];
518 CHAR ShortNameBuffer[20];
519 ULONG StartCluster;
520 DIRENTRY OurDirEntry;
521 LFN_DIRENTRY OurLfnDirEntry;
522 PDIRENTRY DirEntry = &OurDirEntry;
523 PLFN_DIRENTRY LfnDirEntry = &OurLfnDirEntry;
524
525 EntryCount = DirectorySize / sizeof(DIRENTRY);
526
527 TRACE("FatSearchDirectoryBufferForFile() DirectoryBuffer = 0x%x EntryCount = %d FileName = %s\n", DirectoryBuffer, EntryCount, FileName);
528
529 memset(ShortNameBuffer, 0, 13 * sizeof(CHAR));
530 memset(LfnNameBuffer, 0, 261 * sizeof(CHAR));
531
532 for (CurrentEntry=0; CurrentEntry<EntryCount; CurrentEntry++, DirectoryBuffer = ((PDIRENTRY)DirectoryBuffer)+1)
533 {
534 OurLfnDirEntry = *((PLFN_DIRENTRY) DirectoryBuffer);
535 FatSwapLFNDirEntry(LfnDirEntry);
536 OurDirEntry = *((PDIRENTRY) DirectoryBuffer);
537 FatSwapDirEntry(DirEntry);
538
539 //TRACE("Dumping directory entry %d:\n", CurrentEntry);
540 //DbgDumpBuffer(DPRINT_FILESYSTEM, DirEntry, sizeof(DIRENTRY));
541
542 //
543 // Check if this is the last file in the directory
544 // If DirEntry[0] == 0x00 then that means all the
545 // entries after this one are unused. If this is the
546 // last entry then we didn't find the file in this directory.
547 //
548 if (DirEntry->FileName[0] == '\0')
549 {
550 return FALSE;
551 }
552
553 //
554 // Check if this is a deleted entry or not
555 //
556 if (DirEntry->FileName[0] == '\xE5')
557 {
558 memset(ShortNameBuffer, 0, 13 * sizeof(CHAR));
559 memset(LfnNameBuffer, 0, 261 * sizeof(CHAR));
560 continue;
561 }
562
563 //
564 // Check if this is a LFN entry
565 // If so it needs special handling
566 //
567 if (DirEntry->Attr == ATTR_LONG_NAME)
568 {
569 //
570 // Check to see if this is a deleted LFN entry, if so continue
571 //
572 if (LfnDirEntry->SequenceNumber & 0x80)
573 {
574 continue;
575 }
576
577 //
578 // Mask off high two bits of sequence number
579 // and make the sequence number zero-based
580 //
581 LfnDirEntry->SequenceNumber &= 0x3F;
582 LfnDirEntry->SequenceNumber--;
583
584 //
585 // Get all 13 LFN entry characters
586 //
587 if (LfnDirEntry->Name0_4[0] != 0xFFFF)
588 {
589 LfnNameBuffer[0 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name0_4[0];
590 }
591 if (LfnDirEntry->Name0_4[1] != 0xFFFF)
592 {
593 LfnNameBuffer[1 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name0_4[1];
594 }
595 if (LfnDirEntry->Name0_4[2] != 0xFFFF)
596 {
597 LfnNameBuffer[2 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name0_4[2];
598 }
599 if (LfnDirEntry->Name0_4[3] != 0xFFFF)
600 {
601 LfnNameBuffer[3 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name0_4[3];
602 }
603 if (LfnDirEntry->Name0_4[4] != 0xFFFF)
604 {
605 LfnNameBuffer[4 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name0_4[4];
606 }
607 if (LfnDirEntry->Name5_10[0] != 0xFFFF)
608 {
609 LfnNameBuffer[5 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name5_10[0];
610 }
611 if (LfnDirEntry->Name5_10[1] != 0xFFFF)
612 {
613 LfnNameBuffer[6 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name5_10[1];
614 }
615 if (LfnDirEntry->Name5_10[2] != 0xFFFF)
616 {
617 LfnNameBuffer[7 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name5_10[2];
618 }
619 if (LfnDirEntry->Name5_10[3] != 0xFFFF)
620 {
621 LfnNameBuffer[8 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name5_10[3];
622 }
623 if (LfnDirEntry->Name5_10[4] != 0xFFFF)
624 {
625 LfnNameBuffer[9 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name5_10[4];
626 }
627 if (LfnDirEntry->Name5_10[5] != 0xFFFF)
628 {
629 LfnNameBuffer[10 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name5_10[5];
630 }
631 if (LfnDirEntry->Name11_12[0] != 0xFFFF)
632 {
633 LfnNameBuffer[11 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name11_12[0];
634 }
635 if (LfnDirEntry->Name11_12[1] != 0xFFFF)
636 {
637 LfnNameBuffer[12 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name11_12[1];
638 }
639
640 //TRACE("Dumping long name buffer:\n");
641 //DbgDumpBuffer(DPRINT_FILESYSTEM, LfnNameBuffer, 260);
642
643 continue;
644 }
645
646 //
647 // Check for the volume label attribute
648 // and skip over this entry if found
649 //
650 if (DirEntry->Attr & ATTR_VOLUMENAME)
651 {
652 memset(ShortNameBuffer, 0, 13 * sizeof(UCHAR));
653 memset(LfnNameBuffer, 0, 261 * sizeof(UCHAR));
654 continue;
655 }
656
657 //
658 // If we get here then we've found a short file name
659 // entry and LfnNameBuffer contains the long file
660 // name or zeroes. All we have to do now is see if the
661 // file name matches either the short or long file name
662 // and fill in the FAT_FILE_INFO structure if it does
663 // or zero our buffers and continue looking.
664 //
665
666 //
667 // Get short file name
668 //
669 FatParseShortFileName(ShortNameBuffer, DirEntry);
670
671 //TRACE("Entry: %d LFN = %s\n", CurrentEntry, LfnNameBuffer);
672 //TRACE("Entry: %d DOS name = %s\n", CurrentEntry, ShortNameBuffer);
673
674 //
675 // See if the file name matches either the short or long name
676 //
677 if (((strlen(FileName) == strlen(LfnNameBuffer)) && (_stricmp(FileName, LfnNameBuffer) == 0)) ||
678 ((strlen(FileName) == strlen(ShortNameBuffer)) && (_stricmp(FileName, ShortNameBuffer) == 0))) {
679 //
680 // We found the entry, now fill in the FAT_FILE_INFO struct
681 //
682 FatFileInfoPointer->Attributes = DirEntry->Attr;
683 FatFileInfoPointer->FileSize = DirEntry->Size;
684 FatFileInfoPointer->FilePointer = 0;
685
686 TRACE("MSDOS Directory Entry:\n");
687 TRACE("FileName[11] = %c%c%c%c%c%c%c%c%c%c%c\n", DirEntry->FileName[0], DirEntry->FileName[1], DirEntry->FileName[2], DirEntry->FileName[3], DirEntry->FileName[4], DirEntry->FileName[5], DirEntry->FileName[6], DirEntry->FileName[7], DirEntry->FileName[8], DirEntry->FileName[9], DirEntry->FileName[10]);
688 TRACE("Attr = 0x%x\n", DirEntry->Attr);
689 TRACE("ReservedNT = 0x%x\n", DirEntry->ReservedNT);
690 TRACE("TimeInTenths = %d\n", DirEntry->TimeInTenths);
691 TRACE("CreateTime = %d\n", DirEntry->CreateTime);
692 TRACE("CreateDate = %d\n", DirEntry->CreateDate);
693 TRACE("LastAccessDate = %d\n", DirEntry->LastAccessDate);
694 TRACE("ClusterHigh = 0x%x\n", DirEntry->ClusterHigh);
695 TRACE("Time = %d\n", DirEntry->Time);
696 TRACE("Date = %d\n", DirEntry->Date);
697 TRACE("ClusterLow = 0x%x\n", DirEntry->ClusterLow);
698 TRACE("Size = %d\n", DirEntry->Size);
699
700 //
701 // Get the cluster chain
702 //
703 StartCluster = ((ULONG)DirEntry->ClusterHigh << 16) + DirEntry->ClusterLow;
704 TRACE("StartCluster = 0x%x\n", StartCluster);
705 FatFileInfoPointer->FileFatChain = FatGetClusterChainArray(Volume, StartCluster);
706
707 //
708 // See if memory allocation failed
709 //
710 if (FatFileInfoPointer->FileFatChain == NULL)
711 {
712 return FALSE;
713 }
714
715 return TRUE;
716 }
717
718 //
719 // Nope, no match - zero buffers and continue looking
720 //
721 memset(ShortNameBuffer, 0, 13 * sizeof(UCHAR));
722 memset(LfnNameBuffer, 0, 261 * sizeof(UCHAR));
723 continue;
724 }
725
726 return FALSE;
727 }
728
729 static BOOLEAN FatXSearchDirectoryBufferForFile(PFAT_VOLUME_INFO Volume, PVOID DirectoryBuffer, ULONG DirectorySize, PCHAR FileName, PFAT_FILE_INFO FatFileInfoPointer)
730 {
731 ULONG EntryCount;
732 ULONG CurrentEntry;
733 SIZE_T FileNameLen;
734 FATX_DIRENTRY OurDirEntry;
735 PFATX_DIRENTRY DirEntry = &OurDirEntry;
736
737 EntryCount = DirectorySize / sizeof(FATX_DIRENTRY);
738
739 TRACE("FatXSearchDirectoryBufferForFile() DirectoryBuffer = 0x%x EntryCount = %d FileName = %s\n", DirectoryBuffer, EntryCount, FileName);
740
741 FileNameLen = strlen(FileName);
742
743 for (CurrentEntry = 0; CurrentEntry < EntryCount; CurrentEntry++, DirectoryBuffer = ((PFATX_DIRENTRY)DirectoryBuffer)+1)
744 {
745 OurDirEntry = *(PFATX_DIRENTRY) DirectoryBuffer;
746 FatSwapFatXDirEntry(&OurDirEntry);
747 if (0xff == DirEntry->FileNameSize)
748 {
749 break;
750 }
751 if (0xe5 == DirEntry->FileNameSize)
752 {
753 continue;
754 }
755 if (FileNameLen == DirEntry->FileNameSize &&
756 0 == _strnicmp(FileName, DirEntry->FileName, FileNameLen))
757 {
758 /*
759 * We found the entry, now fill in the FAT_FILE_INFO struct
760 */
761 FatFileInfoPointer->Attributes = DirEntry->Attr;
762 FatFileInfoPointer->FileSize = DirEntry->Size;
763 FatFileInfoPointer->FilePointer = 0;
764
765 TRACE("FATX Directory Entry:\n");
766 TRACE("FileNameSize = %d\n", DirEntry->FileNameSize);
767 TRACE("Attr = 0x%x\n", DirEntry->Attr);
768 TRACE("StartCluster = 0x%x\n", DirEntry->StartCluster);
769 TRACE("Size = %d\n", DirEntry->Size);
770 TRACE("Time = %d\n", DirEntry->Time);
771 TRACE("Date = %d\n", DirEntry->Date);
772 TRACE("CreateTime = %d\n", DirEntry->CreateTime);
773 TRACE("CreateDate = %d\n", DirEntry->CreateDate);
774 TRACE("LastAccessTime = %d\n", DirEntry->LastAccessTime);
775 TRACE("LastAccessDate = %d\n", DirEntry->LastAccessDate);
776
777 /*
778 * Get the cluster chain
779 */
780 FatFileInfoPointer->FileFatChain = FatGetClusterChainArray(Volume, DirEntry->StartCluster);
781
782 /*
783 * See if memory allocation failed
784 */
785 if (NULL == FatFileInfoPointer->FileFatChain)
786 {
787 return FALSE;
788 }
789
790 return TRUE;
791 }
792 }
793
794 return FALSE;
795 }
796
797 /*
798 * FatLookupFile()
799 * This function searches the file system for the
800 * specified filename and fills in an FAT_FILE_INFO structure
801 * with info describing the file, etc. returns ARC error code
802 */
803 ARC_STATUS FatLookupFile(PFAT_VOLUME_INFO Volume, PCSTR FileName, PFAT_FILE_INFO FatFileInfoPointer)
804 {
805 UINT32 i;
806 ULONG NumberOfPathParts;
807 CHAR PathPart[261];
808 PVOID DirectoryBuffer;
809 ULONG DirectoryStartCluster = 0;
810 ULONG DirectorySize;
811 FAT_FILE_INFO FatFileInfo;
812
813 TRACE("FatLookupFile() FileName = %s\n", FileName);
814
815 memset(FatFileInfoPointer, 0, sizeof(FAT_FILE_INFO));
816
817 //
818 // Figure out how many sub-directories we are nested in
819 //
820 NumberOfPathParts = FsGetNumPathParts(FileName);
821
822 //
823 // Loop once for each part
824 //
825 for (i=0; i<NumberOfPathParts; i++)
826 {
827 //
828 // Get first path part
829 //
830 FsGetFirstNameFromPath(PathPart, FileName);
831
832 //
833 // Advance to the next part of the path
834 //
835 for (; (*FileName != '\\') && (*FileName != '/') && (*FileName != '\0'); FileName++)
836 {
837 }
838 FileName++;
839
840 //
841 // Buffer the directory contents
842 //
843 DirectoryBuffer = FatBufferDirectory(Volume, DirectoryStartCluster, &DirectorySize, (i == 0) );
844 if (DirectoryBuffer == NULL)
845 {
846 return ENOMEM;
847 }
848
849 //
850 // Search for file name in directory
851 //
852 if (ISFATX(Volume->FatType))
853 {
854 if (!FatXSearchDirectoryBufferForFile(Volume, DirectoryBuffer, DirectorySize, PathPart, &FatFileInfo))
855 {
856 return ENOENT;
857 }
858 }
859 else
860 {
861 if (!FatSearchDirectoryBufferForFile(Volume, DirectoryBuffer, DirectorySize, PathPart, &FatFileInfo))
862 {
863 return ENOENT;
864 }
865 }
866
867 //
868 // If we have another sub-directory to go then
869 // grab the start cluster and free the fat chain array
870 //
871 if ((i+1) < NumberOfPathParts)
872 {
873 //
874 // Check if current entry is a directory
875 //
876 if (!(FatFileInfo.Attributes & ATTR_DIRECTORY))
877 {
878 FrLdrTempFree(FatFileInfo.FileFatChain, TAG_FAT_CHAIN);
879 return ENOTDIR;
880 }
881 DirectoryStartCluster = FatFileInfo.FileFatChain[0];
882 FrLdrTempFree(FatFileInfo.FileFatChain, TAG_FAT_CHAIN);
883 FatFileInfo.FileFatChain = NULL;
884 }
885 }
886
887 memcpy(FatFileInfoPointer, &FatFileInfo, sizeof(FAT_FILE_INFO));
888
889 return ESUCCESS;
890 }
891
892 /*
893 * FatParseFileName()
894 * This function parses a directory entry name which
895 * is in the form of "FILE EXT" and puts it in Buffer
896 * in the form of "file.ext"
897 */
898 void FatParseShortFileName(PCHAR Buffer, PDIRENTRY DirEntry)
899 {
900 ULONG Idx;
901
902 Idx = 0;
903 RtlZeroMemory(Buffer, 13);
904
905 //
906 // Fixup first character
907 //
908 if (DirEntry->FileName[0] == 0x05)
909 {
910 DirEntry->FileName[0] = 0xE5;
911 }
912
913 //
914 // Get the file name
915 //
916 while (Idx < 8)
917 {
918 if (DirEntry->FileName[Idx] == ' ')
919 {
920 break;
921 }
922
923 Buffer[Idx] = DirEntry->FileName[Idx];
924 Idx++;
925 }
926
927 //
928 // Get extension
929 //
930 if ((DirEntry->FileName[8] != ' '))
931 {
932 Buffer[Idx++] = '.';
933 Buffer[Idx++] = (DirEntry->FileName[8] == ' ') ? '\0' : DirEntry->FileName[8];
934 Buffer[Idx++] = (DirEntry->FileName[9] == ' ') ? '\0' : DirEntry->FileName[9];
935 Buffer[Idx++] = (DirEntry->FileName[10] == ' ') ? '\0' : DirEntry->FileName[10];
936 }
937
938 //TRACE("FatParseShortFileName() ShortName = %s\n", Buffer);
939 }
940
941 /**
942 * @brief Reads 1-4 sectors from FAT using the cache
943 */
944 static
945 PUCHAR FatGetFatSector(PFAT_VOLUME_INFO Volume, UINT32 FatSectorNumber)
946 {
947 UINT32 SectorNumAbsolute = Volume->ActiveFatSectorStart + FatSectorNumber;
948 UINT32 CacheIndex = FatSectorNumber % Volume->FatCacheSize;
949
950 ASSERT(FatSectorNumber < Volume->SectorsPerFat);
951
952 // cache miss
953 if (Volume->FatCacheIndex[CacheIndex] != SectorNumAbsolute)
954 {
955 UINT32 SectorsToRead = min(Volume->FatCacheSize - CacheIndex, min(Volume->SectorsPerFat - SectorNumAbsolute, 4));
956 UINT8 i;
957
958 if (!FatReadVolumeSectors(Volume, SectorNumAbsolute, SectorsToRead, &Volume->FatCache[CacheIndex * Volume->BytesPerSector]))
959 {
960 return NULL;
961 }
962
963 for (i = 0; i < SectorsToRead; i++)
964 {
965 Volume->FatCacheIndex[CacheIndex + i] = SectorNumAbsolute + i;
966 }
967
968 TRACE("FAT cache miss: read sector 0x%x from disk\n", SectorNumAbsolute);
969 }
970 else
971 {
972 TRACE("FAT cache hit: sector 0x%x present\n", SectorNumAbsolute);
973 }
974
975 return &Volume->FatCache[CacheIndex * Volume->BytesPerSector];
976 }
977
978 /*
979 * FatGetFatEntry()
980 * returns the Fat entry for a given cluster number
981 */
982 BOOLEAN FatGetFatEntry(PFAT_VOLUME_INFO Volume, ULONG Cluster, ULONG* ClusterPointer)
983 {
984 UINT32 FatOffset, ThisFatSecNum, ThisFatEntOffset, fat;
985 PUCHAR ReadBuffer;
986
987 TRACE("FatGetFatEntry() Retrieving FAT entry for cluster %d.\n", Cluster);
988
989 switch(Volume->FatType)
990 {
991 case FAT12:
992
993 FatOffset = Cluster + (Cluster / 2);
994 ThisFatSecNum = FatOffset / Volume->BytesPerSector;
995 ThisFatEntOffset = (FatOffset % Volume->BytesPerSector);
996
997 TRACE("FatOffset: %d\n", FatOffset);
998 TRACE("ThisFatSecNum: %d\n", ThisFatSecNum);
999 TRACE("ThisFatEntOffset: %d\n", ThisFatEntOffset);
1000
1001 // The cluster pointer can span within two sectors, but the FatGetFatSector function
1002 // reads 4 sectors most times, except when we are at the edge of FAT cache
1003 // and/or FAT region on the disk. For FAT12 the whole FAT would be cached so
1004 // there will be no situation when the first sector is at the end of the cache
1005 // and the next one is in the beginning
1006
1007 ReadBuffer = FatGetFatSector(Volume, ThisFatSecNum);
1008 if (!ReadBuffer)
1009 {
1010 return FALSE;
1011 }
1012
1013 fat = *((USHORT *) (ReadBuffer + ThisFatEntOffset));
1014 fat = SWAPW(fat);
1015 if (Cluster & 0x0001)
1016 fat = fat >> 4; /* Cluster number is ODD */
1017 else
1018 fat = fat & 0x0FFF; /* Cluster number is EVEN */
1019
1020 break;
1021
1022 case FAT16:
1023 case FATX16:
1024
1025 FatOffset = (Cluster * 2);
1026 ThisFatSecNum = FatOffset / Volume->BytesPerSector;
1027 ThisFatEntOffset = (FatOffset % Volume->BytesPerSector);
1028
1029 ReadBuffer = FatGetFatSector(Volume, ThisFatSecNum);
1030 if (!ReadBuffer)
1031 {
1032 return FALSE;
1033 }
1034
1035 fat = *((USHORT *) (ReadBuffer + ThisFatEntOffset));
1036 fat = SWAPW(fat);
1037
1038 break;
1039
1040 case FAT32:
1041 case FATX32:
1042
1043 FatOffset = (Cluster * 4);
1044 ThisFatSecNum = FatOffset / Volume->BytesPerSector;
1045 ThisFatEntOffset = (FatOffset % Volume->BytesPerSector);
1046
1047 ReadBuffer = FatGetFatSector(Volume, ThisFatSecNum);
1048 if (!ReadBuffer)
1049 {
1050 Success = FALSE;
1051 break;
1052 }
1053
1054 // Get the fat entry
1055 fat = (*((ULONG *) (ReadBuffer + ThisFatEntOffset))) & 0x0FFFFFFF;
1056 fat = SWAPD(fat);
1057
1058 break;
1059
1060 default:
1061 ERR("Unknown FAT type %d\n", Volume->FatType);
1062 return FALSE;
1063 }
1064
1065 TRACE("FAT entry is 0x%x.\n", fat);
1066
1067 *ClusterPointer = fat;
1068
1069 return TRUE;
1070 }
1071
1072 ULONG FatCountClustersInChain(PFAT_VOLUME_INFO Volume, ULONG StartCluster)
1073 {
1074 ULONG ClusterCount = 0;
1075
1076 TRACE("FatCountClustersInChain() StartCluster = %d\n", StartCluster);
1077
1078 while (1)
1079 {
1080 //
1081 // If end of chain then break out of our cluster counting loop
1082 //
1083 if (((Volume->FatType == FAT12) && (StartCluster >= 0xff8)) ||
1084 ((Volume->FatType == FAT16 || Volume->FatType == FATX16) && (StartCluster >= 0xfff8)) ||
1085 ((Volume->FatType == FAT32 || Volume->FatType == FATX32) && (StartCluster >= 0x0ffffff8)))
1086 {
1087 break;
1088 }
1089
1090 //
1091 // Increment count
1092 //
1093 ClusterCount++;
1094
1095 //
1096 // Get next cluster
1097 //
1098 if (!FatGetFatEntry(Volume, StartCluster, &StartCluster))
1099 {
1100 return 0;
1101 }
1102 }
1103
1104 TRACE("FatCountClustersInChain() ClusterCount = %d\n", ClusterCount);
1105
1106 return ClusterCount;
1107 }
1108
1109 ULONG* FatGetClusterChainArray(PFAT_VOLUME_INFO Volume, ULONG StartCluster)
1110 {
1111 ULONG ClusterCount;
1112 ULONG ArraySize;
1113 ULONG* ArrayPointer;
1114 ULONG Idx;
1115
1116 TRACE("FatGetClusterChainArray() StartCluster = %d\n", StartCluster);
1117
1118 ClusterCount = FatCountClustersInChain(Volume, StartCluster) + 1; // Lets get the 0x0ffffff8 on the end of the array
1119 ArraySize = ClusterCount * sizeof(ULONG);
1120
1121 //
1122 // Allocate array memory
1123 //
1124 ArrayPointer = FrLdrTempAlloc(ArraySize, TAG_FAT_CHAIN);
1125
1126 if (ArrayPointer == NULL)
1127 {
1128 return NULL;
1129 }
1130
1131 //
1132 // Loop through and set array values
1133 //
1134 for (Idx=0; Idx<ClusterCount; Idx++)
1135 {
1136 //
1137 // Set current cluster
1138 //
1139 ArrayPointer[Idx] = StartCluster;
1140
1141 //
1142 // Don't try to get next cluster for last cluster
1143 //
1144 if (((Volume->FatType == FAT12) && (StartCluster >= 0xff8)) ||
1145 ((Volume->FatType == FAT16 || Volume->FatType == FATX16) && (StartCluster >= 0xfff8)) ||
1146 ((Volume->FatType == FAT32 || Volume->FatType == FATX32) && (StartCluster >= 0x0ffffff8)))
1147 {
1148 Idx++;
1149 break;
1150 }
1151
1152 //
1153 // Get next cluster
1154 //
1155 if (!FatGetFatEntry(Volume, StartCluster, &StartCluster))
1156 {
1157 FrLdrTempFree(ArrayPointer, TAG_FAT_CHAIN);
1158 return NULL;
1159 }
1160 }
1161
1162 return ArrayPointer;
1163 }
1164
1165 /*
1166 * FatReadClusterChain()
1167 * Reads the specified clusters into memory
1168 */
1169 BOOLEAN FatReadClusterChain(PFAT_VOLUME_INFO Volume, ULONG StartClusterNumber, ULONG NumberOfClusters, PVOID Buffer)
1170 {
1171 ULONG ClusterStartSector;
1172
1173 TRACE("FatReadClusterChain() StartClusterNumber = %d NumberOfClusters = %d Buffer = 0x%x\n", StartClusterNumber, NumberOfClusters, Buffer);
1174
1175 while (NumberOfClusters > 0)
1176 {
1177
1178 //TRACE("FatReadClusterChain() StartClusterNumber = %d NumberOfClusters = %d Buffer = 0x%x\n", StartClusterNumber, NumberOfClusters, Buffer);
1179 //
1180 // Calculate starting sector for cluster
1181 //
1182 ClusterStartSector = ((StartClusterNumber - 2) * Volume->SectorsPerCluster) + Volume->DataSectorStart;
1183
1184 //
1185 // Read cluster into memory
1186 //
1187 if (!FatReadVolumeSectors(Volume, ClusterStartSector, Volume->SectorsPerCluster, Buffer))
1188 {
1189 return FALSE;
1190 }
1191
1192 //
1193 // Decrement count of clusters left to read
1194 //
1195 NumberOfClusters--;
1196
1197 //
1198 // Increment buffer address by cluster size
1199 //
1200 Buffer = (PVOID)((ULONG_PTR)Buffer + (Volume->SectorsPerCluster * Volume->BytesPerSector));
1201
1202 //
1203 // Get next cluster
1204 //
1205 if (!FatGetFatEntry(Volume, StartClusterNumber, &StartClusterNumber))
1206 {
1207 return FALSE;
1208 }
1209
1210 //
1211 // If end of chain then break out of our cluster reading loop
1212 //
1213 if (((Volume->FatType == FAT12) && (StartClusterNumber >= 0xff8)) ||
1214 ((Volume->FatType == FAT16 || Volume->FatType == FATX16) && (StartClusterNumber >= 0xfff8)) ||
1215 ((Volume->FatType == FAT32 || Volume->FatType == FATX32) && (StartClusterNumber >= 0x0ffffff8)))
1216 {
1217 break;
1218 }
1219 }
1220
1221 return TRUE;
1222 }
1223
1224 /*
1225 * FatReadPartialCluster()
1226 * Reads part of a cluster into memory
1227 */
1228 BOOLEAN FatReadPartialCluster(PFAT_VOLUME_INFO Volume, ULONG ClusterNumber, ULONG StartingOffset, ULONG Length, PVOID Buffer)
1229 {
1230 ULONG ClusterStartSector;
1231 ULONG SectorOffset, ReadSize, SectorCount;
1232 PUCHAR ReadBuffer;
1233 BOOLEAN Success = FALSE;
1234
1235 //TRACE("FatReadPartialCluster() ClusterNumber = %d StartingOffset = %d Length = %d Buffer = 0x%x\n", ClusterNumber, StartingOffset, Length, Buffer);
1236
1237 ClusterStartSector = ((ClusterNumber - 2) * Volume->SectorsPerCluster) + Volume->DataSectorStart;
1238
1239 // This is the offset of the data in sectors
1240 SectorOffset = (StartingOffset / Volume->BytesPerSector);
1241 StartingOffset %= Volume->BytesPerSector;
1242
1243 // Calculate how many sectors we need to read
1244 SectorCount = (StartingOffset + Length + Volume->BytesPerSector - 1) / Volume->BytesPerSector;
1245
1246 // Calculate rounded up read size
1247 ReadSize = SectorCount * Volume->BytesPerSector;
1248
1249 ReadBuffer = FrLdrTempAlloc(ReadSize, TAG_FAT_BUFFER);
1250 if (!ReadBuffer)
1251 {
1252 return FALSE;
1253 }
1254
1255 if (FatReadVolumeSectors(Volume, ClusterStartSector + SectorOffset, SectorCount, ReadBuffer))
1256 {
1257 memcpy(Buffer, ReadBuffer + StartingOffset, Length);
1258 Success = TRUE;
1259 }
1260
1261 FrLdrTempFree(ReadBuffer, TAG_FAT_BUFFER);
1262
1263 return Success;
1264 }
1265
1266 /*
1267 * FatReadFile()
1268 * Reads BytesToRead from open file and
1269 * returns the number of bytes read in BytesRead
1270 */
1271 BOOLEAN FatReadFile(PFAT_FILE_INFO FatFileInfo, ULONG BytesToRead, ULONG* BytesRead, PVOID Buffer)
1272 {
1273 PFAT_VOLUME_INFO Volume = FatFileInfo->Volume;
1274 ULONG ClusterNumber;
1275 ULONG OffsetInCluster;
1276 ULONG LengthInCluster;
1277 ULONG NumberOfClusters;
1278 ULONG BytesPerCluster;
1279
1280 TRACE("FatReadFile() BytesToRead = %d Buffer = 0x%x\n", BytesToRead, Buffer);
1281
1282 if (BytesRead != NULL)
1283 {
1284 *BytesRead = 0;
1285 }
1286
1287 //
1288 // If they are trying to read past the
1289 // end of the file then return success
1290 // with BytesRead == 0
1291 //
1292 if (FatFileInfo->FilePointer >= FatFileInfo->FileSize)
1293 {
1294 return TRUE;
1295 }
1296
1297 //
1298 // If they are trying to read more than there is to read
1299 // then adjust the amount to read
1300 //
1301 if ((FatFileInfo->FilePointer + BytesToRead) > FatFileInfo->FileSize)
1302 {
1303 BytesToRead = (FatFileInfo->FileSize - FatFileInfo->FilePointer);
1304 }
1305
1306 //
1307 // Ok, now we have to perform at most 3 calculations
1308 // I'll draw you a picture (using nifty ASCII art):
1309 //
1310 // CurrentFilePointer -+
1311 // |
1312 // +----------------+
1313 // |
1314 // +-----------+-----------+-----------+-----------+
1315 // | Cluster 1 | Cluster 2 | Cluster 3 | Cluster 4 |
1316 // +-----------+-----------+-----------+-----------+
1317 // | |
1318 // +---------------+--------------------+
1319 // |
1320 // BytesToRead -------+
1321 //
1322 // 1 - The first calculation (and read) will align
1323 // the file pointer with the next cluster.
1324 // boundary (if we are supposed to read that much)
1325 // 2 - The next calculation (and read) will read
1326 // in all the full clusters that the requested
1327 // amount of data would cover (in this case
1328 // clusters 2 & 3).
1329 // 3 - The last calculation (and read) would read
1330 // in the remainder of the data requested out of
1331 // the last cluster.
1332 //
1333
1334 BytesPerCluster = Volume->SectorsPerCluster * Volume->BytesPerSector;
1335
1336 //
1337 // Only do the first read if we
1338 // aren't aligned on a cluster boundary
1339 //
1340 if (FatFileInfo->FilePointer % BytesPerCluster)
1341 {
1342 //
1343 // Do the math for our first read
1344 //
1345 ClusterNumber = (FatFileInfo->FilePointer / BytesPerCluster);
1346 ClusterNumber = FatFileInfo->FileFatChain[ClusterNumber];
1347 OffsetInCluster = (FatFileInfo->FilePointer % BytesPerCluster);
1348 LengthInCluster = (BytesToRead > (BytesPerCluster - OffsetInCluster)) ? (BytesPerCluster - OffsetInCluster) : BytesToRead;
1349
1350 //
1351 // Now do the read and update BytesRead, BytesToRead, FilePointer, & Buffer
1352 //
1353 if (!FatReadPartialCluster(Volume, ClusterNumber, OffsetInCluster, LengthInCluster, Buffer))
1354 {
1355 return FALSE;
1356 }
1357 if (BytesRead != NULL)
1358 {
1359 *BytesRead += LengthInCluster;
1360 }
1361 BytesToRead -= LengthInCluster;
1362 FatFileInfo->FilePointer += LengthInCluster;
1363 Buffer = (PVOID)((ULONG_PTR)Buffer + LengthInCluster);
1364 }
1365
1366 //
1367 // Do the math for our second read (if any data left)
1368 //
1369 if (BytesToRead > 0)
1370 {
1371 //
1372 // Determine how many full clusters we need to read
1373 //
1374 NumberOfClusters = (BytesToRead / BytesPerCluster);
1375
1376 if (NumberOfClusters > 0)
1377 {
1378 ClusterNumber = (FatFileInfo->FilePointer / BytesPerCluster);
1379 ClusterNumber = FatFileInfo->FileFatChain[ClusterNumber];
1380
1381 //
1382 // Now do the read and update BytesRead, BytesToRead, FilePointer, & Buffer
1383 //
1384 if (!FatReadClusterChain(Volume, ClusterNumber, NumberOfClusters, Buffer))
1385 {
1386 return FALSE;
1387 }
1388 if (BytesRead != NULL)
1389 {
1390 *BytesRead += (NumberOfClusters * BytesPerCluster);
1391 }
1392 BytesToRead -= (NumberOfClusters * BytesPerCluster);
1393 FatFileInfo->FilePointer += (NumberOfClusters * BytesPerCluster);
1394 Buffer = (PVOID)((ULONG_PTR)Buffer + (NumberOfClusters * BytesPerCluster));
1395 }
1396 }
1397
1398 //
1399 // Do the math for our third read (if any data left)
1400 //
1401 if (BytesToRead > 0)
1402 {
1403 ClusterNumber = (FatFileInfo->FilePointer / BytesPerCluster);
1404 ClusterNumber = FatFileInfo->FileFatChain[ClusterNumber];
1405
1406 //
1407 // Now do the read and update BytesRead, BytesToRead, FilePointer, & Buffer
1408 //
1409 if (!FatReadPartialCluster(Volume, ClusterNumber, 0, BytesToRead, Buffer))
1410 {
1411 return FALSE;
1412 }
1413 if (BytesRead != NULL)
1414 {
1415 *BytesRead += BytesToRead;
1416 }
1417 FatFileInfo->FilePointer += BytesToRead;
1418 BytesToRead -= BytesToRead;
1419 Buffer = (PVOID)((ULONG_PTR)Buffer + BytesToRead);
1420 }
1421
1422 return TRUE;
1423 }
1424
1425 BOOLEAN FatReadVolumeSectors(PFAT_VOLUME_INFO Volume, ULONG SectorNumber, ULONG SectorCount, PVOID Buffer)
1426 {
1427 LARGE_INTEGER Position;
1428 ULONG Count;
1429 ARC_STATUS Status;
1430
1431 //TRACE("FatReadVolumeSectors(): SectorNumber %d, SectorCount %d, Buffer %p\n",
1432 // SectorNumber, SectorCount, Buffer);
1433
1434 //
1435 // Seek to right position
1436 //
1437 Position.QuadPart = (ULONGLONG)SectorNumber * 512;
1438 Status = ArcSeek(Volume->DeviceId, &Position, SeekAbsolute);
1439 if (Status != ESUCCESS)
1440 {
1441 TRACE("FatReadVolumeSectors() Failed to seek\n");
1442 return FALSE;
1443 }
1444
1445 //
1446 // Read data
1447 //
1448 Status = ArcRead(Volume->DeviceId, Buffer, SectorCount * 512, &Count);
1449 if (Status != ESUCCESS || Count != SectorCount * 512)
1450 {
1451 TRACE("FatReadVolumeSectors() Failed to read\n");
1452 return FALSE;
1453 }
1454
1455 // Return success
1456 return TRUE;
1457 }
1458
1459 ARC_STATUS FatClose(ULONG FileId)
1460 {
1461 PFAT_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId);
1462
1463 if (FileHandle->FileFatChain) FrLdrTempFree(FileHandle->FileFatChain, TAG_FAT_CHAIN);
1464 FrLdrTempFree(FileHandle, TAG_FAT_FILE);
1465
1466 return ESUCCESS;
1467 }
1468
1469 ARC_STATUS FatGetFileInformation(ULONG FileId, FILEINFORMATION* Information)
1470 {
1471 PFAT_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId);
1472
1473 RtlZeroMemory(Information, sizeof(*Information));
1474 Information->EndingAddress.LowPart = FileHandle->FileSize;
1475 Information->CurrentAddress.LowPart = FileHandle->FilePointer;
1476
1477 TRACE("FatGetFileInformation(%lu) -> FileSize = %lu, FilePointer = 0x%lx\n",
1478 FileId, Information->EndingAddress.LowPart, Information->CurrentAddress.LowPart);
1479
1480 return ESUCCESS;
1481 }
1482
1483 ARC_STATUS FatOpen(CHAR* Path, OPENMODE OpenMode, ULONG* FileId)
1484 {
1485 PFAT_VOLUME_INFO FatVolume;
1486 FAT_FILE_INFO TempFileInfo;
1487 PFAT_FILE_INFO FileHandle;
1488 ULONG DeviceId;
1489 BOOLEAN IsDirectory;
1490 ARC_STATUS Status;
1491
1492 if (OpenMode != OpenReadOnly && OpenMode != OpenDirectory)
1493 return EACCES;
1494
1495 DeviceId = FsGetDeviceId(*FileId);
1496 FatVolume = FatVolumes[DeviceId];
1497
1498 TRACE("FatOpen() FileName = %s\n", Path);
1499
1500 RtlZeroMemory(&TempFileInfo, sizeof(TempFileInfo));
1501 Status = FatLookupFile(FatVolume, Path, &TempFileInfo);
1502 if (Status != ESUCCESS)
1503 return ENOENT;
1504
1505 //
1506 // Check if caller opened what he expected (dir vs file)
1507 //
1508 IsDirectory = (TempFileInfo.Attributes & ATTR_DIRECTORY) != 0;
1509 if (IsDirectory && OpenMode != OpenDirectory)
1510 return EISDIR;
1511 else if (!IsDirectory && OpenMode != OpenReadOnly)
1512 return ENOTDIR;
1513
1514 FileHandle = FrLdrTempAlloc(sizeof(FAT_FILE_INFO), TAG_FAT_FILE);
1515 if (!FileHandle)
1516 return ENOMEM;
1517
1518 RtlCopyMemory(FileHandle, &TempFileInfo, sizeof(FAT_FILE_INFO));
1519 FileHandle->Volume = FatVolume;
1520
1521 FsSetDeviceSpecific(*FileId, FileHandle);
1522 return ESUCCESS;
1523 }
1524
1525 ARC_STATUS FatRead(ULONG FileId, VOID* Buffer, ULONG N, ULONG* Count)
1526 {
1527 PFAT_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId);
1528 BOOLEAN Success;
1529
1530 //
1531 // Call old read method
1532 //
1533 Success = FatReadFile(FileHandle, N, Count, Buffer);
1534
1535 //
1536 // Check for success
1537 //
1538 if (Success)
1539 return ESUCCESS;
1540 else
1541 return EIO;
1542 }
1543
1544 ARC_STATUS FatSeek(ULONG FileId, LARGE_INTEGER* Position, SEEKMODE SeekMode)
1545 {
1546 PFAT_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId);
1547 LARGE_INTEGER NewPosition = *Position;
1548
1549 switch (SeekMode)
1550 {
1551 case SeekAbsolute:
1552 break;
1553 case SeekRelative:
1554 NewPosition.QuadPart += (ULONGLONG)FileHandle->FilePointer;
1555 break;
1556 default:
1557 ASSERT(FALSE);
1558 return EINVAL;
1559 }
1560
1561 if (NewPosition.HighPart != 0)
1562 return EINVAL;
1563 if (NewPosition.LowPart >= FileHandle->FileSize)
1564 return EINVAL;
1565
1566 FileHandle->FilePointer = NewPosition.LowPart;
1567 return ESUCCESS;
1568 }
1569
1570 const DEVVTBL FatFuncTable =
1571 {
1572 FatClose,
1573 FatGetFileInformation,
1574 FatOpen,
1575 FatRead,
1576 FatSeek,
1577 L"fastfat",
1578 };
1579
1580 const DEVVTBL* FatMount(ULONG DeviceId)
1581 {
1582 PFAT_VOLUME_INFO Volume;
1583 UCHAR Buffer[512];
1584 PFAT_BOOTSECTOR BootSector = (PFAT_BOOTSECTOR)Buffer;
1585 PFAT32_BOOTSECTOR BootSector32 = (PFAT32_BOOTSECTOR)Buffer;
1586 PFATX_BOOTSECTOR BootSectorX = (PFATX_BOOTSECTOR)Buffer;
1587 FILEINFORMATION FileInformation;
1588 LARGE_INTEGER Position;
1589 ULONG Count;
1590 ULARGE_INTEGER SectorCount;
1591 ARC_STATUS Status;
1592
1593 TRACE("Enter FatMount(%lu)\n", DeviceId);
1594
1595 //
1596 // Allocate data for volume information
1597 //
1598 Volume = FrLdrTempAlloc(sizeof(FAT_VOLUME_INFO), TAG_FAT_VOLUME);
1599 if (!Volume)
1600 return NULL;
1601 RtlZeroMemory(Volume, sizeof(FAT_VOLUME_INFO));
1602
1603 //
1604 // Read the BootSector
1605 //
1606 Position.QuadPart = 0;
1607 Status = ArcSeek(DeviceId, &Position, SeekAbsolute);
1608 if (Status != ESUCCESS)
1609 {
1610 FrLdrTempFree(Volume, TAG_FAT_VOLUME);
1611 return NULL;
1612 }
1613 Status = ArcRead(DeviceId, Buffer, sizeof(Buffer), &Count);
1614 if (Status != ESUCCESS || Count != sizeof(Buffer))
1615 {
1616 FrLdrTempFree(Volume, TAG_FAT_VOLUME);
1617 return NULL;
1618 }
1619
1620 //
1621 // Check if BootSector is valid. If no, return early
1622 //
1623 if (!RtlEqualMemory(BootSector->FileSystemType, "FAT12 ", 8) &&
1624 !RtlEqualMemory(BootSector->FileSystemType, "FAT16 ", 8) &&
1625 !RtlEqualMemory(BootSector32->FileSystemType, "FAT32 ", 8) &&
1626 !RtlEqualMemory(BootSectorX->FileSystemType, "FATX", 4))
1627 {
1628 FrLdrTempFree(Volume, TAG_FAT_VOLUME);
1629 return NULL;
1630 }
1631
1632 //
1633 // Determine sector count
1634 //
1635 Status = ArcGetFileInformation(DeviceId, &FileInformation);
1636 if (Status != ESUCCESS)
1637 {
1638 FrLdrTempFree(Volume, TAG_FAT_VOLUME);
1639 return NULL;
1640 }
1641 SectorCount.QuadPart = (FileInformation.EndingAddress.QuadPart - FileInformation.StartingAddress.QuadPart);
1642 SectorCount.QuadPart /= SECTOR_SIZE;
1643
1644 //
1645 // Keep device id
1646 //
1647 Volume->DeviceId = DeviceId;
1648
1649 //
1650 // Really open the volume
1651 //
1652 if (!FatOpenVolume(Volume, BootSector, SectorCount.QuadPart))
1653 {
1654 FrLdrTempFree(Volume, TAG_FAT_VOLUME);
1655 return NULL;
1656 }
1657
1658 //
1659 // Remember FAT volume information
1660 //
1661 FatVolumes[DeviceId] = Volume;
1662
1663 //
1664 // Return success
1665 //
1666 TRACE("FatMount(%lu) success\n", DeviceId);
1667 return &FatFuncTable;
1668 }