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