[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 typedef struct _DIRECTORY_BUFFER
374 {
375 LIST_ENTRY Link;
376 PVOID Volume;
377 ULONG DirectoryStartCluster;
378 ULONG DirectorySize;
379 UCHAR Data[];
380 } DIRECTORY_BUFFER, *PDIRECTORY_BUFFER;
381
382 LIST_ENTRY DirectoryBufferListHead = {&DirectoryBufferListHead, &DirectoryBufferListHead};
383
384 PVOID FatBufferDirectory(PFAT_VOLUME_INFO Volume, ULONG DirectoryStartCluster, ULONG *DirectorySize, BOOLEAN RootDirectory)
385 {
386 PDIRECTORY_BUFFER DirectoryBuffer;
387 PLIST_ENTRY Entry;
388
389 TRACE("FatBufferDirectory() DirectoryStartCluster = %d RootDirectory = %s\n", DirectoryStartCluster, (RootDirectory ? "TRUE" : "FALSE"));
390
391 /*
392 * For FAT32, the root directory is nothing special. We can treat it the same
393 * as a subdirectory.
394 */
395 if (RootDirectory && Volume->FatType == FAT32)
396 {
397 DirectoryStartCluster = Volume->RootDirStartCluster;
398 RootDirectory = FALSE;
399 }
400
401 /* Search the list for a match */
402 for (Entry = DirectoryBufferListHead.Flink;
403 Entry != &DirectoryBufferListHead;
404 Entry = Entry->Flink)
405 {
406 DirectoryBuffer = CONTAINING_RECORD(Entry, DIRECTORY_BUFFER, Link);
407
408 /* Check if it matches */
409 if ((DirectoryBuffer->Volume == Volume) &&
410 (DirectoryBuffer->DirectoryStartCluster == DirectoryStartCluster))
411 {
412 TRACE("Found cached buffer\n");
413 *DirectorySize = DirectoryBuffer->DirectorySize;
414 return DirectoryBuffer->Data;
415 }
416 }
417
418 //
419 // Calculate the size of the directory
420 //
421 if (RootDirectory)
422 {
423 *DirectorySize = Volume->RootDirSectors * Volume->BytesPerSector;
424 }
425 else
426 {
427 *DirectorySize = FatCountClustersInChain(Volume, DirectoryStartCluster) * Volume->SectorsPerCluster * Volume->BytesPerSector;
428 }
429
430 //
431 // Attempt to allocate memory for directory buffer
432 //
433 TRACE("Trying to allocate (DirectorySize) %d bytes.\n", *DirectorySize);
434 DirectoryBuffer = MmHeapAlloc(*DirectorySize + sizeof(DIRECTORY_BUFFER));
435
436 if (DirectoryBuffer == NULL)
437 {
438 return NULL;
439 }
440
441 //
442 // Now read directory contents into DirectoryBuffer
443 //
444 if (RootDirectory)
445 {
446 if (!FatReadVolumeSectors(Volume, Volume->RootDirSectorStart, Volume->RootDirSectors, DirectoryBuffer->Data))
447 {
448 MmHeapFree(DirectoryBuffer);
449 return NULL;
450 }
451 }
452 else
453 {
454 if (!FatReadClusterChain(Volume, DirectoryStartCluster, 0xFFFFFFFF, DirectoryBuffer->Data))
455 {
456 MmHeapFree(DirectoryBuffer);
457 return NULL;
458 }
459 }
460
461 /* Enqueue it in the list */
462 DirectoryBuffer->Volume = Volume;
463 DirectoryBuffer->DirectoryStartCluster = DirectoryStartCluster;
464 DirectoryBuffer->DirectorySize = *DirectorySize;
465 InsertTailList(&DirectoryBufferListHead, &DirectoryBuffer->Link);
466
467 return DirectoryBuffer->Data;
468 }
469
470 BOOLEAN FatSearchDirectoryBufferForFile(PFAT_VOLUME_INFO Volume, PVOID DirectoryBuffer, ULONG DirectorySize, PCHAR FileName, PFAT_FILE_INFO FatFileInfoPointer)
471 {
472 ULONG EntryCount;
473 ULONG CurrentEntry;
474 CHAR LfnNameBuffer[265];
475 CHAR ShortNameBuffer[20];
476 ULONG StartCluster;
477 DIRENTRY OurDirEntry;
478 LFN_DIRENTRY OurLfnDirEntry;
479 PDIRENTRY DirEntry = &OurDirEntry;
480 PLFN_DIRENTRY LfnDirEntry = &OurLfnDirEntry;
481
482 EntryCount = DirectorySize / sizeof(DIRENTRY);
483
484 TRACE("FatSearchDirectoryBufferForFile() DirectoryBuffer = 0x%x EntryCount = %d FileName = %s\n", DirectoryBuffer, EntryCount, FileName);
485
486 memset(ShortNameBuffer, 0, 13 * sizeof(CHAR));
487 memset(LfnNameBuffer, 0, 261 * sizeof(CHAR));
488
489 for (CurrentEntry=0; CurrentEntry<EntryCount; CurrentEntry++, DirectoryBuffer = ((PDIRENTRY)DirectoryBuffer)+1)
490 {
491 OurLfnDirEntry = *((PLFN_DIRENTRY) DirectoryBuffer);
492 FatSwapLFNDirEntry(LfnDirEntry);
493 OurDirEntry = *((PDIRENTRY) DirectoryBuffer);
494 FatSwapDirEntry(DirEntry);
495
496 //TRACE("Dumping directory entry %d:\n", CurrentEntry);
497 //DbgDumpBuffer(DPRINT_FILESYSTEM, DirEntry, sizeof(DIRENTRY));
498
499 //
500 // Check if this is the last file in the directory
501 // If DirEntry[0] == 0x00 then that means all the
502 // entries after this one are unused. If this is the
503 // last entry then we didn't find the file in this directory.
504 //
505 if (DirEntry->FileName[0] == '\0')
506 {
507 return FALSE;
508 }
509
510 //
511 // Check if this is a deleted entry or not
512 //
513 if (DirEntry->FileName[0] == '\xE5')
514 {
515 memset(ShortNameBuffer, 0, 13 * sizeof(CHAR));
516 memset(LfnNameBuffer, 0, 261 * sizeof(CHAR));
517 continue;
518 }
519
520 //
521 // Check if this is a LFN entry
522 // If so it needs special handling
523 //
524 if (DirEntry->Attr == ATTR_LONG_NAME)
525 {
526 //
527 // Check to see if this is a deleted LFN entry, if so continue
528 //
529 if (LfnDirEntry->SequenceNumber & 0x80)
530 {
531 continue;
532 }
533
534 //
535 // Mask off high two bits of sequence number
536 // and make the sequence number zero-based
537 //
538 LfnDirEntry->SequenceNumber &= 0x3F;
539 LfnDirEntry->SequenceNumber--;
540
541 //
542 // Get all 13 LFN entry characters
543 //
544 if (LfnDirEntry->Name0_4[0] != 0xFFFF)
545 {
546 LfnNameBuffer[0 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name0_4[0];
547 }
548 if (LfnDirEntry->Name0_4[1] != 0xFFFF)
549 {
550 LfnNameBuffer[1 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name0_4[1];
551 }
552 if (LfnDirEntry->Name0_4[2] != 0xFFFF)
553 {
554 LfnNameBuffer[2 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name0_4[2];
555 }
556 if (LfnDirEntry->Name0_4[3] != 0xFFFF)
557 {
558 LfnNameBuffer[3 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name0_4[3];
559 }
560 if (LfnDirEntry->Name0_4[4] != 0xFFFF)
561 {
562 LfnNameBuffer[4 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name0_4[4];
563 }
564 if (LfnDirEntry->Name5_10[0] != 0xFFFF)
565 {
566 LfnNameBuffer[5 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name5_10[0];
567 }
568 if (LfnDirEntry->Name5_10[1] != 0xFFFF)
569 {
570 LfnNameBuffer[6 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name5_10[1];
571 }
572 if (LfnDirEntry->Name5_10[2] != 0xFFFF)
573 {
574 LfnNameBuffer[7 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name5_10[2];
575 }
576 if (LfnDirEntry->Name5_10[3] != 0xFFFF)
577 {
578 LfnNameBuffer[8 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name5_10[3];
579 }
580 if (LfnDirEntry->Name5_10[4] != 0xFFFF)
581 {
582 LfnNameBuffer[9 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name5_10[4];
583 }
584 if (LfnDirEntry->Name5_10[5] != 0xFFFF)
585 {
586 LfnNameBuffer[10 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name5_10[5];
587 }
588 if (LfnDirEntry->Name11_12[0] != 0xFFFF)
589 {
590 LfnNameBuffer[11 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name11_12[0];
591 }
592 if (LfnDirEntry->Name11_12[1] != 0xFFFF)
593 {
594 LfnNameBuffer[12 + (LfnDirEntry->SequenceNumber * 13)] = (UCHAR)LfnDirEntry->Name11_12[1];
595 }
596
597 //TRACE("Dumping long name buffer:\n");
598 //DbgDumpBuffer(DPRINT_FILESYSTEM, LfnNameBuffer, 260);
599
600 continue;
601 }
602
603 //
604 // Check for the volume label attribute
605 // and skip over this entry if found
606 //
607 if (DirEntry->Attr & ATTR_VOLUMENAME)
608 {
609 memset(ShortNameBuffer, 0, 13 * sizeof(UCHAR));
610 memset(LfnNameBuffer, 0, 261 * sizeof(UCHAR));
611 continue;
612 }
613
614 //
615 // If we get here then we've found a short file name
616 // entry and LfnNameBuffer contains the long file
617 // name or zeroes. All we have to do now is see if the
618 // file name matches either the short or long file name
619 // and fill in the FAT_FILE_INFO structure if it does
620 // or zero our buffers and continue looking.
621 //
622
623 //
624 // Get short file name
625 //
626 FatParseShortFileName(ShortNameBuffer, DirEntry);
627
628 //TRACE("Entry: %d LFN = %s\n", CurrentEntry, LfnNameBuffer);
629 //TRACE("Entry: %d DOS name = %s\n", CurrentEntry, ShortNameBuffer);
630
631 //
632 // See if the file name matches either the short or long name
633 //
634 if (((strlen(FileName) == strlen(LfnNameBuffer)) && (_stricmp(FileName, LfnNameBuffer) == 0)) ||
635 ((strlen(FileName) == strlen(ShortNameBuffer)) && (_stricmp(FileName, ShortNameBuffer) == 0))) {
636 //
637 // We found the entry, now fill in the FAT_FILE_INFO struct
638 //
639 FatFileInfoPointer->Attributes = DirEntry->Attr;
640 FatFileInfoPointer->FileSize = DirEntry->Size;
641 FatFileInfoPointer->FilePointer = 0;
642
643 TRACE("MSDOS Directory Entry:\n");
644 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]);
645 TRACE("Attr = 0x%x\n", DirEntry->Attr);
646 TRACE("ReservedNT = 0x%x\n", DirEntry->ReservedNT);
647 TRACE("TimeInTenths = %d\n", DirEntry->TimeInTenths);
648 TRACE("CreateTime = %d\n", DirEntry->CreateTime);
649 TRACE("CreateDate = %d\n", DirEntry->CreateDate);
650 TRACE("LastAccessDate = %d\n", DirEntry->LastAccessDate);
651 TRACE("ClusterHigh = 0x%x\n", DirEntry->ClusterHigh);
652 TRACE("Time = %d\n", DirEntry->Time);
653 TRACE("Date = %d\n", DirEntry->Date);
654 TRACE("ClusterLow = 0x%x\n", DirEntry->ClusterLow);
655 TRACE("Size = %d\n", DirEntry->Size);
656
657 //
658 // Get the cluster chain
659 //
660 StartCluster = ((ULONG)DirEntry->ClusterHigh << 16) + DirEntry->ClusterLow;
661 TRACE("StartCluster = 0x%x\n", StartCluster);
662 FatFileInfoPointer->FileFatChain = FatGetClusterChainArray(Volume, StartCluster);
663
664 //
665 // See if memory allocation failed
666 //
667 if (FatFileInfoPointer->FileFatChain == NULL)
668 {
669 return FALSE;
670 }
671
672 return TRUE;
673 }
674
675 //
676 // Nope, no match - zero buffers and continue looking
677 //
678 memset(ShortNameBuffer, 0, 13 * sizeof(UCHAR));
679 memset(LfnNameBuffer, 0, 261 * sizeof(UCHAR));
680 continue;
681 }
682
683 return FALSE;
684 }
685
686 static BOOLEAN FatXSearchDirectoryBufferForFile(PFAT_VOLUME_INFO Volume, PVOID DirectoryBuffer, ULONG DirectorySize, PCHAR FileName, PFAT_FILE_INFO FatFileInfoPointer)
687 {
688 ULONG EntryCount;
689 ULONG CurrentEntry;
690 SIZE_T FileNameLen;
691 FATX_DIRENTRY OurDirEntry;
692 PFATX_DIRENTRY DirEntry = &OurDirEntry;
693
694 EntryCount = DirectorySize / sizeof(FATX_DIRENTRY);
695
696 TRACE("FatXSearchDirectoryBufferForFile() DirectoryBuffer = 0x%x EntryCount = %d FileName = %s\n", DirectoryBuffer, EntryCount, FileName);
697
698 FileNameLen = strlen(FileName);
699
700 for (CurrentEntry = 0; CurrentEntry < EntryCount; CurrentEntry++, DirectoryBuffer = ((PFATX_DIRENTRY)DirectoryBuffer)+1)
701 {
702 OurDirEntry = *(PFATX_DIRENTRY) DirectoryBuffer;
703 FatSwapFatXDirEntry(&OurDirEntry);
704 if (0xff == DirEntry->FileNameSize)
705 {
706 break;
707 }
708 if (0xe5 == DirEntry->FileNameSize)
709 {
710 continue;
711 }
712 if (FileNameLen == DirEntry->FileNameSize &&
713 0 == _strnicmp(FileName, DirEntry->FileName, FileNameLen))
714 {
715 /*
716 * We found the entry, now fill in the FAT_FILE_INFO struct
717 */
718 FatFileInfoPointer->FileSize = DirEntry->Size;
719 FatFileInfoPointer->FilePointer = 0;
720
721 TRACE("FATX Directory Entry:\n");
722 TRACE("FileNameSize = %d\n", DirEntry->FileNameSize);
723 TRACE("Attr = 0x%x\n", DirEntry->Attr);
724 TRACE("StartCluster = 0x%x\n", DirEntry->StartCluster);
725 TRACE("Size = %d\n", DirEntry->Size);
726 TRACE("Time = %d\n", DirEntry->Time);
727 TRACE("Date = %d\n", DirEntry->Date);
728 TRACE("CreateTime = %d\n", DirEntry->CreateTime);
729 TRACE("CreateDate = %d\n", DirEntry->CreateDate);
730 TRACE("LastAccessTime = %d\n", DirEntry->LastAccessTime);
731 TRACE("LastAccessDate = %d\n", DirEntry->LastAccessDate);
732
733 /*
734 * Get the cluster chain
735 */
736 FatFileInfoPointer->FileFatChain = FatGetClusterChainArray(Volume, DirEntry->StartCluster);
737
738 /*
739 * See if memory allocation failed
740 */
741 if (NULL == FatFileInfoPointer->FileFatChain)
742 {
743 return FALSE;
744 }
745
746 return TRUE;
747 }
748 }
749
750 return FALSE;
751 }
752
753 /*
754 * FatLookupFile()
755 * This function searches the file system for the
756 * specified filename and fills in an FAT_FILE_INFO structure
757 * with info describing the file, etc. returns ARC error code
758 */
759 LONG FatLookupFile(PFAT_VOLUME_INFO Volume, PCSTR FileName, ULONG DeviceId, PFAT_FILE_INFO FatFileInfoPointer)
760 {
761 UINT32 i;
762 ULONG NumberOfPathParts;
763 CHAR PathPart[261];
764 PVOID DirectoryBuffer;
765 ULONG DirectoryStartCluster = 0;
766 ULONG DirectorySize;
767 FAT_FILE_INFO FatFileInfo;
768
769 TRACE("FatLookupFile() FileName = %s\n", FileName);
770
771 memset(FatFileInfoPointer, 0, sizeof(FAT_FILE_INFO));
772
773 //
774 // Figure out how many sub-directories we are nested in
775 //
776 NumberOfPathParts = FsGetNumPathParts(FileName);
777
778 //
779 // Loop once for each part
780 //
781 for (i=0; i<NumberOfPathParts; i++)
782 {
783 //
784 // Get first path part
785 //
786 FsGetFirstNameFromPath(PathPart, FileName);
787
788 //
789 // Advance to the next part of the path
790 //
791 for (; (*FileName != '\\') && (*FileName != '/') && (*FileName != '\0'); FileName++)
792 {
793 }
794 FileName++;
795
796 //
797 // Buffer the directory contents
798 //
799 DirectoryBuffer = FatBufferDirectory(Volume, DirectoryStartCluster, &DirectorySize, (i == 0) );
800 if (DirectoryBuffer == NULL)
801 {
802 return ENOMEM;
803 }
804
805 //
806 // Search for file name in directory
807 //
808 if (ISFATX(Volume->FatType))
809 {
810 if (!FatXSearchDirectoryBufferForFile(Volume, DirectoryBuffer, DirectorySize, PathPart, &FatFileInfo))
811 {
812 return ENOENT;
813 }
814 }
815 else
816 {
817 if (!FatSearchDirectoryBufferForFile(Volume, DirectoryBuffer, DirectorySize, PathPart, &FatFileInfo))
818 {
819 return ENOENT;
820 }
821 }
822
823 //
824 // If we have another sub-directory to go then
825 // grab the start cluster and free the fat chain array
826 //
827 if ((i+1) < NumberOfPathParts)
828 {
829 //
830 // Check if current entry is a directory
831 //
832 if (!(FatFileInfo.Attributes & ATTR_DIRECTORY))
833 {
834 MmHeapFree(FatFileInfo.FileFatChain);
835 return ENOTDIR;
836 }
837 DirectoryStartCluster = FatFileInfo.FileFatChain[0];
838 MmHeapFree(FatFileInfo.FileFatChain);
839 FatFileInfo.FileFatChain = NULL;
840 }
841 }
842
843 memcpy(FatFileInfoPointer, &FatFileInfo, sizeof(FAT_FILE_INFO));
844
845 return ESUCCESS;
846 }
847
848 /*
849 * FatParseFileName()
850 * This function parses a directory entry name which
851 * is in the form of "FILE EXT" and puts it in Buffer
852 * in the form of "file.ext"
853 */
854 void FatParseShortFileName(PCHAR Buffer, PDIRENTRY DirEntry)
855 {
856 ULONG Idx;
857
858 Idx = 0;
859 RtlZeroMemory(Buffer, 13);
860
861 //
862 // Fixup first character
863 //
864 if (DirEntry->FileName[0] == 0x05)
865 {
866 DirEntry->FileName[0] = 0xE5;
867 }
868
869 //
870 // Get the file name
871 //
872 while (Idx < 8)
873 {
874 if (DirEntry->FileName[Idx] == ' ')
875 {
876 break;
877 }
878
879 Buffer[Idx] = DirEntry->FileName[Idx];
880 Idx++;
881 }
882
883 //
884 // Get extension
885 //
886 if ((DirEntry->FileName[8] != ' '))
887 {
888 Buffer[Idx++] = '.';
889 Buffer[Idx++] = (DirEntry->FileName[8] == ' ') ? '\0' : DirEntry->FileName[8];
890 Buffer[Idx++] = (DirEntry->FileName[9] == ' ') ? '\0' : DirEntry->FileName[9];
891 Buffer[Idx++] = (DirEntry->FileName[10] == ' ') ? '\0' : DirEntry->FileName[10];
892 }
893
894 //TRACE("FatParseShortFileName() ShortName = %s\n", Buffer);
895 }
896
897 /*
898 * FatGetFatEntry()
899 * returns the Fat entry for a given cluster number
900 */
901 BOOLEAN FatGetFatEntry(PFAT_VOLUME_INFO Volume, ULONG Cluster, ULONG* ClusterPointer)
902 {
903 ULONG fat = 0;
904 UINT32 FatOffset;
905 UINT32 ThisFatSecNum;
906 UINT32 ThisFatEntOffset;
907 ULONG SectorCount;
908 PUCHAR ReadBuffer;
909 BOOLEAN status = TRUE;;
910
911 //TRACE("FatGetFatEntry() Retrieving FAT entry for cluster %d.\n", Cluster);
912
913 // We need a buffer for 2 secors
914 ReadBuffer = HeapAllocate(FrLdrTempHeap, 2 * Volume->BytesPerSector, 'xTAF');
915 if (!ReadBuffer)
916 {
917 return FALSE;
918 }
919
920 switch(Volume->FatType)
921 {
922 case FAT12:
923
924 FatOffset = Cluster + (Cluster / 2);
925 ThisFatSecNum = Volume->ActiveFatSectorStart + (FatOffset / Volume->BytesPerSector);
926 ThisFatEntOffset = (FatOffset % Volume->BytesPerSector);
927
928 TRACE("FatOffset: %d\n", FatOffset);
929 TRACE("ThisFatSecNum: %d\n", ThisFatSecNum);
930 TRACE("ThisFatEntOffset: %d\n", ThisFatEntOffset);
931
932
933 if (ThisFatEntOffset == (Volume->BytesPerSector - 1))
934 {
935 SectorCount = 2;
936 }
937 else
938 {
939 SectorCount = 1;
940 }
941
942 if (!FatReadVolumeSectors(Volume, ThisFatSecNum, SectorCount, ReadBuffer))
943 {
944 status = FALSE;
945 break;
946 }
947
948 fat = *((USHORT *) (ReadBuffer + ThisFatEntOffset));
949 fat = SWAPW(fat);
950 if (Cluster & 0x0001)
951 fat = fat >> 4; /* Cluster number is ODD */
952 else
953 fat = fat & 0x0FFF; /* Cluster number is EVEN */
954
955 break;
956
957 case FAT16:
958 case FATX16:
959
960 FatOffset = (Cluster * 2);
961 ThisFatSecNum = Volume->ActiveFatSectorStart + (FatOffset / Volume->BytesPerSector);
962 ThisFatEntOffset = (FatOffset % Volume->BytesPerSector);
963
964 if (!FatReadVolumeSectors(Volume, ThisFatSecNum, 1, ReadBuffer))
965 {
966 status = FALSE;
967 break;
968 }
969
970 fat = *((USHORT *) (ReadBuffer + ThisFatEntOffset));
971 fat = SWAPW(fat);
972
973 break;
974
975 case FAT32:
976 case FATX32:
977
978 FatOffset = (Cluster * 4);
979 ThisFatSecNum = Volume->ActiveFatSectorStart + (FatOffset / Volume->BytesPerSector);
980 ThisFatEntOffset = (FatOffset % Volume->BytesPerSector);
981
982 if (!FatReadVolumeSectors(Volume, ThisFatSecNum, 1, ReadBuffer))
983 {
984 return FALSE;
985 }
986
987 // Get the fat entry
988 fat = (*((ULONG *) (ReadBuffer + ThisFatEntOffset))) & 0x0FFFFFFF;
989 fat = SWAPD(fat);
990
991 break;
992
993 default:
994 ERR("Unknown FAT type %d\n", Volume->FatType);
995 status = FALSE;
996 break;
997 }
998
999 //TRACE("FAT entry is 0x%x.\n", fat);
1000
1001 HeapFree(FrLdrTempHeap, ReadBuffer, 'xTAF');
1002
1003 *ClusterPointer = fat;
1004
1005 return TRUE;
1006 }
1007
1008 ULONG FatCountClustersInChain(PFAT_VOLUME_INFO Volume, ULONG StartCluster)
1009 {
1010 ULONG ClusterCount = 0;
1011
1012 TRACE("FatCountClustersInChain() StartCluster = %d\n", StartCluster);
1013
1014 while (1)
1015 {
1016 //
1017 // If end of chain then break out of our cluster counting loop
1018 //
1019 if (((Volume->FatType == FAT12) && (StartCluster >= 0xff8)) ||
1020 ((Volume->FatType == FAT16 || Volume->FatType == FATX16) && (StartCluster >= 0xfff8)) ||
1021 ((Volume->FatType == FAT32 || Volume->FatType == FATX32) && (StartCluster >= 0x0ffffff8)))
1022 {
1023 break;
1024 }
1025
1026 //
1027 // Increment count
1028 //
1029 ClusterCount++;
1030
1031 //
1032 // Get next cluster
1033 //
1034 if (!FatGetFatEntry(Volume, StartCluster, &StartCluster))
1035 {
1036 return 0;
1037 }
1038 }
1039
1040 TRACE("FatCountClustersInChain() ClusterCount = %d\n", ClusterCount);
1041
1042 return ClusterCount;
1043 }
1044
1045 ULONG* FatGetClusterChainArray(PFAT_VOLUME_INFO Volume, ULONG StartCluster)
1046 {
1047 ULONG ClusterCount;
1048 ULONG ArraySize;
1049 ULONG* ArrayPointer;
1050 ULONG Idx;
1051
1052 TRACE("FatGetClusterChainArray() StartCluster = %d\n", StartCluster);
1053
1054 ClusterCount = FatCountClustersInChain(Volume, StartCluster) + 1; // Lets get the 0x0ffffff8 on the end of the array
1055 ArraySize = ClusterCount * sizeof(ULONG);
1056
1057 //
1058 // Allocate array memory
1059 //
1060 ArrayPointer = MmHeapAlloc(ArraySize);
1061
1062 if (ArrayPointer == NULL)
1063 {
1064 return NULL;
1065 }
1066
1067 //
1068 // Loop through and set array values
1069 //
1070 for (Idx=0; Idx<ClusterCount; Idx++)
1071 {
1072 //
1073 // Set current cluster
1074 //
1075 ArrayPointer[Idx] = StartCluster;
1076
1077 //
1078 // Don't try to get next cluster for last cluster
1079 //
1080 if (((Volume->FatType == FAT12) && (StartCluster >= 0xff8)) ||
1081 ((Volume->FatType == FAT16 || Volume->FatType == FATX16) && (StartCluster >= 0xfff8)) ||
1082 ((Volume->FatType == FAT32 || Volume->FatType == FATX32) && (StartCluster >= 0x0ffffff8)))
1083 {
1084 Idx++;
1085 break;
1086 }
1087
1088 //
1089 // Get next cluster
1090 //
1091 if (!FatGetFatEntry(Volume, StartCluster, &StartCluster))
1092 {
1093 MmHeapFree(ArrayPointer);
1094 return NULL;
1095 }
1096 }
1097
1098 return ArrayPointer;
1099 }
1100
1101 /*
1102 * FatReadClusterChain()
1103 * Reads the specified clusters into memory
1104 */
1105 BOOLEAN FatReadClusterChain(PFAT_VOLUME_INFO Volume, ULONG StartClusterNumber, ULONG NumberOfClusters, PVOID Buffer)
1106 {
1107 ULONG ClusterStartSector;
1108
1109 TRACE("FatReadClusterChain() StartClusterNumber = %d NumberOfClusters = %d Buffer = 0x%x\n", StartClusterNumber, NumberOfClusters, Buffer);
1110
1111 while (NumberOfClusters > 0)
1112 {
1113
1114 //TRACE("FatReadClusterChain() StartClusterNumber = %d NumberOfClusters = %d Buffer = 0x%x\n", StartClusterNumber, NumberOfClusters, Buffer);
1115 //
1116 // Calculate starting sector for cluster
1117 //
1118 ClusterStartSector = ((StartClusterNumber - 2) * Volume->SectorsPerCluster) + Volume->DataSectorStart;
1119
1120 //
1121 // Read cluster into memory
1122 //
1123 if (!FatReadVolumeSectors(Volume, ClusterStartSector, Volume->SectorsPerCluster, Buffer))
1124 {
1125 return FALSE;
1126 }
1127
1128 //
1129 // Decrement count of clusters left to read
1130 //
1131 NumberOfClusters--;
1132
1133 //
1134 // Increment buffer address by cluster size
1135 //
1136 Buffer = (PVOID)((ULONG_PTR)Buffer + (Volume->SectorsPerCluster * Volume->BytesPerSector));
1137
1138 //
1139 // Get next cluster
1140 //
1141 if (!FatGetFatEntry(Volume, StartClusterNumber, &StartClusterNumber))
1142 {
1143 return FALSE;
1144 }
1145
1146 //
1147 // If end of chain then break out of our cluster reading loop
1148 //
1149 if (((Volume->FatType == FAT12) && (StartClusterNumber >= 0xff8)) ||
1150 ((Volume->FatType == FAT16 || Volume->FatType == FATX16) && (StartClusterNumber >= 0xfff8)) ||
1151 ((Volume->FatType == FAT32 || Volume->FatType == FATX32) && (StartClusterNumber >= 0x0ffffff8)))
1152 {
1153 break;
1154 }
1155 }
1156
1157 return TRUE;
1158 }
1159
1160 /*
1161 * FatReadPartialCluster()
1162 * Reads part of a cluster into memory
1163 */
1164 BOOLEAN FatReadPartialCluster(PFAT_VOLUME_INFO Volume, ULONG ClusterNumber, ULONG StartingOffset, ULONG Length, PVOID Buffer)
1165 {
1166 ULONG ClusterStartSector;
1167 ULONG SectorOffset, ReadSize, SectorCount;
1168 PUCHAR ReadBuffer;
1169 BOOLEAN status = FALSE;
1170
1171 //TRACE("FatReadPartialCluster() ClusterNumber = %d StartingOffset = %d Length = %d Buffer = 0x%x\n", ClusterNumber, StartingOffset, Length, Buffer);
1172
1173 ClusterStartSector = ((ClusterNumber - 2) * Volume->SectorsPerCluster) + Volume->DataSectorStart;
1174
1175 // This is the offset of the data in sectors
1176 SectorOffset = (StartingOffset / Volume->BytesPerSector);
1177 StartingOffset %= Volume->BytesPerSector;
1178
1179 // Calculate how many sectors we need to read
1180 SectorCount = (StartingOffset + Length + Volume->BytesPerSector - 1) / Volume->BytesPerSector;
1181
1182 // Calculate rounded up read size
1183 ReadSize = SectorCount * Volume->BytesPerSector;
1184
1185 ReadBuffer = HeapAllocate(FrLdrTempHeap, ReadSize, 'xTAF');
1186 if (!ReadBuffer)
1187 {
1188 return FALSE;
1189 }
1190
1191 if (FatReadVolumeSectors(Volume, ClusterStartSector + SectorOffset, SectorCount, ReadBuffer))
1192 {
1193 memcpy(Buffer, ReadBuffer + StartingOffset, Length);
1194 status = TRUE;
1195 }
1196
1197 HeapFree(FrLdrTempHeap, ReadBuffer, 'xTAF');
1198
1199 return status;
1200 }
1201
1202 /*
1203 * FatReadFile()
1204 * Reads BytesToRead from open file and
1205 * returns the number of bytes read in BytesRead
1206 */
1207 BOOLEAN FatReadFile(PFAT_FILE_INFO FatFileInfo, ULONG BytesToRead, ULONG* BytesRead, PVOID Buffer)
1208 {
1209 PFAT_VOLUME_INFO Volume = FatFileInfo->Volume;
1210 ULONG ClusterNumber;
1211 ULONG OffsetInCluster;
1212 ULONG LengthInCluster;
1213 ULONG NumberOfClusters;
1214 ULONG BytesPerCluster;
1215
1216 TRACE("FatReadFile() BytesToRead = %d Buffer = 0x%x\n", BytesToRead, Buffer);
1217
1218 if (BytesRead != NULL)
1219 {
1220 *BytesRead = 0;
1221 }
1222
1223 //
1224 // If they are trying to read past the
1225 // end of the file then return success
1226 // with BytesRead == 0
1227 //
1228 if (FatFileInfo->FilePointer >= FatFileInfo->FileSize)
1229 {
1230 return TRUE;
1231 }
1232
1233 //
1234 // If they are trying to read more than there is to read
1235 // then adjust the amount to read
1236 //
1237 if ((FatFileInfo->FilePointer + BytesToRead) > FatFileInfo->FileSize)
1238 {
1239 BytesToRead = (FatFileInfo->FileSize - FatFileInfo->FilePointer);
1240 }
1241
1242 //
1243 // Ok, now we have to perform at most 3 calculations
1244 // I'll draw you a picture (using nifty ASCII art):
1245 //
1246 // CurrentFilePointer -+
1247 // |
1248 // +----------------+
1249 // |
1250 // +-----------+-----------+-----------+-----------+
1251 // | Cluster 1 | Cluster 2 | Cluster 3 | Cluster 4 |
1252 // +-----------+-----------+-----------+-----------+
1253 // | |
1254 // +---------------+--------------------+
1255 // |
1256 // BytesToRead -------+
1257 //
1258 // 1 - The first calculation (and read) will align
1259 // the file pointer with the next cluster.
1260 // boundary (if we are supposed to read that much)
1261 // 2 - The next calculation (and read) will read
1262 // in all the full clusters that the requested
1263 // amount of data would cover (in this case
1264 // clusters 2 & 3).
1265 // 3 - The last calculation (and read) would read
1266 // in the remainder of the data requested out of
1267 // the last cluster.
1268 //
1269
1270 BytesPerCluster = Volume->SectorsPerCluster * Volume->BytesPerSector;
1271
1272 //
1273 // Only do the first read if we
1274 // aren't aligned on a cluster boundary
1275 //
1276 if (FatFileInfo->FilePointer % BytesPerCluster)
1277 {
1278 //
1279 // Do the math for our first read
1280 //
1281 ClusterNumber = (FatFileInfo->FilePointer / BytesPerCluster);
1282 ClusterNumber = FatFileInfo->FileFatChain[ClusterNumber];
1283 OffsetInCluster = (FatFileInfo->FilePointer % BytesPerCluster);
1284 LengthInCluster = (BytesToRead > (BytesPerCluster - OffsetInCluster)) ? (BytesPerCluster - OffsetInCluster) : BytesToRead;
1285
1286 //
1287 // Now do the read and update BytesRead, BytesToRead, FilePointer, & Buffer
1288 //
1289 if (!FatReadPartialCluster(Volume, ClusterNumber, OffsetInCluster, LengthInCluster, Buffer))
1290 {
1291 return FALSE;
1292 }
1293 if (BytesRead != NULL)
1294 {
1295 *BytesRead += LengthInCluster;
1296 }
1297 BytesToRead -= LengthInCluster;
1298 FatFileInfo->FilePointer += LengthInCluster;
1299 Buffer = (PVOID)((ULONG_PTR)Buffer + LengthInCluster);
1300 }
1301
1302 //
1303 // Do the math for our second read (if any data left)
1304 //
1305 if (BytesToRead > 0)
1306 {
1307 //
1308 // Determine how many full clusters we need to read
1309 //
1310 NumberOfClusters = (BytesToRead / BytesPerCluster);
1311
1312 if (NumberOfClusters > 0)
1313 {
1314 ClusterNumber = (FatFileInfo->FilePointer / BytesPerCluster);
1315 ClusterNumber = FatFileInfo->FileFatChain[ClusterNumber];
1316
1317 //
1318 // Now do the read and update BytesRead, BytesToRead, FilePointer, & Buffer
1319 //
1320 if (!FatReadClusterChain(Volume, ClusterNumber, NumberOfClusters, Buffer))
1321 {
1322 return FALSE;
1323 }
1324 if (BytesRead != NULL)
1325 {
1326 *BytesRead += (NumberOfClusters * BytesPerCluster);
1327 }
1328 BytesToRead -= (NumberOfClusters * BytesPerCluster);
1329 FatFileInfo->FilePointer += (NumberOfClusters * BytesPerCluster);
1330 Buffer = (PVOID)((ULONG_PTR)Buffer + (NumberOfClusters * BytesPerCluster));
1331 }
1332 }
1333
1334 //
1335 // Do the math for our third read (if any data left)
1336 //
1337 if (BytesToRead > 0)
1338 {
1339 ClusterNumber = (FatFileInfo->FilePointer / BytesPerCluster);
1340 ClusterNumber = FatFileInfo->FileFatChain[ClusterNumber];
1341
1342 //
1343 // Now do the read and update BytesRead, BytesToRead, FilePointer, & Buffer
1344 //
1345 if (!FatReadPartialCluster(Volume, ClusterNumber, 0, BytesToRead, Buffer))
1346 {
1347 return FALSE;
1348 }
1349 if (BytesRead != NULL)
1350 {
1351 *BytesRead += BytesToRead;
1352 }
1353 FatFileInfo->FilePointer += BytesToRead;
1354 BytesToRead -= BytesToRead;
1355 Buffer = (PVOID)((ULONG_PTR)Buffer + BytesToRead);
1356 }
1357
1358 return TRUE;
1359 }
1360
1361 BOOLEAN FatReadVolumeSectors(PFAT_VOLUME_INFO Volume, ULONG SectorNumber, ULONG SectorCount, PVOID Buffer)
1362 {
1363 LARGE_INTEGER Position;
1364 ULONG Count;
1365 LONG ret;
1366
1367 //TRACE("FatReadVolumeSectors(): SectorNumber %d, SectorCount %d, Buffer %p\n",
1368 // SectorNumber, SectorCount, Buffer);
1369
1370 //
1371 // Seek to right position
1372 //
1373 Position.QuadPart = (ULONGLONG)SectorNumber * 512;
1374 ret = ArcSeek(Volume->DeviceId, &Position, SeekAbsolute);
1375 if (ret != ESUCCESS)
1376 {
1377 TRACE("FatReadVolumeSectors() Failed to seek\n");
1378 return FALSE;
1379 }
1380
1381 //
1382 // Read data
1383 //
1384 ret = ArcRead(Volume->DeviceId, Buffer, SectorCount * 512, &Count);
1385 if (ret != ESUCCESS || Count != SectorCount * 512)
1386 {
1387 TRACE("FatReadVolumeSectors() Failed to read\n");
1388 return FALSE;
1389 }
1390
1391 // Return success
1392 return TRUE;
1393 }
1394
1395 LONG FatClose(ULONG FileId)
1396 {
1397 PFAT_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId);
1398
1399 if (FileHandle->FileFatChain) MmHeapFree(FileHandle->FileFatChain);
1400 MmHeapFree(FileHandle);
1401
1402 return ESUCCESS;
1403 }
1404
1405 LONG FatGetFileInformation(ULONG FileId, FILEINFORMATION* Information)
1406 {
1407 PFAT_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId);
1408
1409 RtlZeroMemory(Information, sizeof(FILEINFORMATION));
1410 Information->EndingAddress.LowPart = FileHandle->FileSize;
1411 Information->CurrentAddress.LowPart = FileHandle->FilePointer;
1412
1413 TRACE("FatGetFileInformation() FileSize = %d\n",
1414 Information->EndingAddress.LowPart);
1415 TRACE("FatGetFileInformation() FilePointer = %d\n",
1416 Information->CurrentAddress.LowPart);
1417
1418 return ESUCCESS;
1419 }
1420
1421 LONG FatOpen(CHAR* Path, OPENMODE OpenMode, ULONG* FileId)
1422 {
1423 PFAT_VOLUME_INFO FatVolume;
1424 FAT_FILE_INFO TempFileInfo;
1425 PFAT_FILE_INFO FileHandle;
1426 ULONG DeviceId;
1427 BOOLEAN IsDirectory;
1428 LONG ret;
1429
1430 if (OpenMode != OpenReadOnly && OpenMode != OpenDirectory)
1431 return EACCES;
1432
1433 DeviceId = FsGetDeviceId(*FileId);
1434 FatVolume = FatVolumes[DeviceId];
1435
1436 TRACE("FatOpen() FileName = %s\n", Path);
1437
1438 RtlZeroMemory(&TempFileInfo, sizeof(TempFileInfo));
1439 ret = FatLookupFile(FatVolume, Path, DeviceId, &TempFileInfo);
1440 if (ret != ESUCCESS)
1441 return ENOENT;
1442
1443 //
1444 // Check if caller opened what he expected (dir vs file)
1445 //
1446 IsDirectory = (TempFileInfo.Attributes & ATTR_DIRECTORY) != 0;
1447 if (IsDirectory && OpenMode != OpenDirectory)
1448 return EISDIR;
1449 else if (!IsDirectory && OpenMode != OpenReadOnly)
1450 return ENOTDIR;
1451
1452 FileHandle = MmHeapAlloc(sizeof(FAT_FILE_INFO));
1453 if (!FileHandle)
1454 return ENOMEM;
1455
1456 RtlCopyMemory(FileHandle, &TempFileInfo, sizeof(FAT_FILE_INFO));
1457 FileHandle->Volume = FatVolume;
1458
1459 FsSetDeviceSpecific(*FileId, FileHandle);
1460 return ESUCCESS;
1461 }
1462
1463 LONG FatRead(ULONG FileId, VOID* Buffer, ULONG N, ULONG* Count)
1464 {
1465 PFAT_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId);
1466 BOOLEAN ret;
1467
1468 //
1469 // Call old read method
1470 //
1471 ret = FatReadFile(FileHandle, N, Count, Buffer);
1472
1473 //
1474 // Check for success
1475 //
1476 if (ret)
1477 return ESUCCESS;
1478 else
1479 return EIO;
1480 }
1481
1482 LONG FatSeek(ULONG FileId, LARGE_INTEGER* Position, SEEKMODE SeekMode)
1483 {
1484 PFAT_FILE_INFO FileHandle = FsGetDeviceSpecific(FileId);
1485
1486 TRACE("FatSeek() NewFilePointer = %lu\n", Position->LowPart);
1487
1488 if (SeekMode != SeekAbsolute)
1489 return EINVAL;
1490 if (Position->HighPart != 0)
1491 return EINVAL;
1492 if (Position->LowPart >= FileHandle->FileSize)
1493 return EINVAL;
1494
1495 FileHandle->FilePointer = Position->LowPart;
1496 return ESUCCESS;
1497 }
1498
1499 const DEVVTBL FatFuncTable =
1500 {
1501 FatClose,
1502 FatGetFileInformation,
1503 FatOpen,
1504 FatRead,
1505 FatSeek,
1506 L"fastfat",
1507 };
1508
1509 const DEVVTBL* FatMount(ULONG DeviceId)
1510 {
1511 PFAT_VOLUME_INFO Volume;
1512 UCHAR Buffer[512];
1513 PFAT_BOOTSECTOR BootSector = (PFAT_BOOTSECTOR)Buffer;
1514 PFAT32_BOOTSECTOR BootSector32 = (PFAT32_BOOTSECTOR)Buffer;
1515 PFATX_BOOTSECTOR BootSectorX = (PFATX_BOOTSECTOR)Buffer;
1516 FILEINFORMATION FileInformation;
1517 LARGE_INTEGER Position;
1518 ULONG Count;
1519 ULARGE_INTEGER SectorCount;
1520 LONG ret;
1521
1522 //
1523 // Allocate data for volume information
1524 //
1525 Volume = MmHeapAlloc(sizeof(FAT_VOLUME_INFO));
1526 if (!Volume)
1527 return NULL;
1528 RtlZeroMemory(Volume, sizeof(FAT_VOLUME_INFO));
1529
1530 //
1531 // Read the BootSector
1532 //
1533 Position.HighPart = 0;
1534 Position.LowPart = 0;
1535 ret = ArcSeek(DeviceId, &Position, SeekAbsolute);
1536 if (ret != ESUCCESS)
1537 {
1538 MmHeapFree(Volume);
1539 return NULL;
1540 }
1541 ret = ArcRead(DeviceId, Buffer, sizeof(Buffer), &Count);
1542 if (ret != ESUCCESS || Count != sizeof(Buffer))
1543 {
1544 MmHeapFree(Volume);
1545 return NULL;
1546 }
1547
1548 //
1549 // Check if BootSector is valid. If no, return early
1550 //
1551 if (!RtlEqualMemory(BootSector->FileSystemType, "FAT12 ", 8) &&
1552 !RtlEqualMemory(BootSector->FileSystemType, "FAT16 ", 8) &&
1553 !RtlEqualMemory(BootSector32->FileSystemType, "FAT32 ", 8) &&
1554 !RtlEqualMemory(BootSectorX->FileSystemType, "FATX", 4))
1555 {
1556 MmHeapFree(Volume);
1557 return NULL;
1558 }
1559
1560 //
1561 // Determine sector count
1562 //
1563 ret = ArcGetFileInformation(DeviceId, &FileInformation);
1564 if (ret != ESUCCESS)
1565 {
1566 MmHeapFree(Volume);
1567 return NULL;
1568 }
1569 SectorCount.HighPart = FileInformation.EndingAddress.HighPart;
1570 SectorCount.LowPart = FileInformation.EndingAddress.LowPart;
1571 SectorCount.QuadPart /= SECTOR_SIZE;
1572
1573 //
1574 // Keep device id
1575 //
1576 Volume->DeviceId = DeviceId;
1577
1578 //
1579 // Really open the volume
1580 //
1581 if (!FatOpenVolume(Volume, BootSector, SectorCount.QuadPart))
1582 {
1583 MmHeapFree(Volume);
1584 return NULL;
1585 }
1586
1587 //
1588 // Remember FAT volume information
1589 //
1590 FatVolumes[DeviceId] = Volume;
1591
1592 //
1593 // Return success
1594 //
1595 return &FatFuncTable;
1596 }