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