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