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