3 * Copyright (C) 1998-2003 Brian Palmer <brianp@sginet.com>
4 * Copyright (C) 2009 Hervé Poussineau
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.
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.
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.
25 DBG_DEFAULT_CHANNEL(FILESYSTEM
);
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
);
39 #define TAG_FAT_CHAIN 'CtaT'
40 #define TAG_FAT_FILE 'FtaF'
41 #define TAG_FAT_VOLUME 'VtaF'
42 #define TAG_FAT_BUFFER 'BtaF'
44 typedef struct _FAT_VOLUME_INFO
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 */
60 PFAT_VOLUME_INFO FatVolumes
[MAX_FDS
];
62 VOID
FatSwapFatBootSector(PFAT_BOOTSECTOR Obj
)
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
);
77 VOID
FatSwapFat32BootSector(PFAT32_BOOTSECTOR Obj
)
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
);
92 SW(Obj
, BackupBootSector
);
93 SD(Obj
, VolumeSerialNumber
);
94 SW(Obj
, BootSectorMagic
);
97 VOID
FatSwapFatXBootSector(PFATX_BOOTSECTOR Obj
)
99 SD(Obj
, VolumeSerialNumber
);
100 SD(Obj
, SectorsPerCluster
);
101 SW(Obj
, NumberOfFats
);
104 VOID
FatSwapDirEntry(PDIRENTRY Obj
)
108 SW(Obj
, LastAccessDate
);
109 SW(Obj
, ClusterHigh
);
116 VOID
FatSwapLFNDirEntry(PLFN_DIRENTRY Obj
)
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
]);
128 VOID
FatSwapFatXDirEntry(PFATX_DIRENTRY Obj
)
130 SD(Obj
, StartCluster
);
136 SW(Obj
, LastAccessTime
);
137 SW(Obj
, LastAccessDate
);
140 BOOLEAN
FatOpenVolume(PFAT_VOLUME_INFO Volume
, PFAT_BOOTSECTOR BootSector
, ULONGLONG PartitionSectorCount
)
144 PFAT_BOOTSECTOR FatVolumeBootSector
;
145 PFAT32_BOOTSECTOR Fat32VolumeBootSector
;
146 PFATX_BOOTSECTOR FatXVolumeBootSector
;
148 TRACE("FatOpenVolume() DeviceId = %d\n", Volume
->DeviceId
);
151 // Allocate the memory to hold the boot sector
153 FatVolumeBootSector
= (PFAT_BOOTSECTOR
)BootSector
;
154 Fat32VolumeBootSector
= (PFAT32_BOOTSECTOR
)BootSector
;
155 FatXVolumeBootSector
= (PFATX_BOOTSECTOR
)BootSector
;
158 Volume
->FatType
= FatDetermineFatType(FatVolumeBootSector
, PartitionSectorCount
);
160 // Dump boot sector (and swap it for big endian systems)
161 TRACE("Dumping boot sector:\n");
162 if (ISFATX(Volume
->FatType
))
164 FatSwapFatXBootSector(FatXVolumeBootSector
);
165 TRACE("sizeof(FATX_BOOTSECTOR) = 0x%x.\n", sizeof(FATX_BOOTSECTOR
));
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
);
173 TRACE("FatType %s\n", Volume
->FatType
== FATX16
? "FATX16" : "FATX32");
176 else if (Volume
->FatType
== FAT32
)
178 FatSwapFat32BootSector(Fat32VolumeBootSector
);
179 TRACE("sizeof(FAT32_BOOTSECTOR) = 0x%x.\n", sizeof(FAT32_BOOTSECTOR
));
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
);
212 FatSwapFatBootSector(FatVolumeBootSector
);
213 TRACE("sizeof(FAT_BOOTSECTOR) = 0x%x.\n", sizeof(FAT_BOOTSECTOR
));
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
);
239 // Check the boot sector magic
241 if (! ISFATX(Volume
->FatType
) && FatVolumeBootSector
->BootSectorMagic
!= 0xaa55)
243 sprintf(ErrMsg
, "Invalid boot sector magic (expected 0xaa55 found 0x%x)",
244 FatVolumeBootSector
->BootSectorMagic
);
245 FileSystemError(ErrMsg
);
250 // Check the FAT cluster size
251 // We do not support clusters bigger than 64k
253 if ((ISFATX(Volume
->FatType
) && 64 * 1024 < FatXVolumeBootSector
->SectorsPerCluster
* 512) ||
254 (! ISFATX(Volume
->FatType
) && 64 * 1024 < FatVolumeBootSector
->SectorsPerCluster
* FatVolumeBootSector
->BytesPerSector
))
256 FileSystemError("This file system has cluster sizes bigger than 64k.\nFreeLoader does not support this.");
261 // Get the sectors per FAT,
262 // root directory starting sector,
263 // and data sector start
265 if (ISFATX(Volume
->FatType
))
267 Volume
->BytesPerSector
= 512;
268 Volume
->SectorsPerCluster
= SWAPD(FatXVolumeBootSector
->SectorsPerCluster
);
269 Volume
->FatSectorStart
= (0x1000 / 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
= ROUND_UP(FatSize
, 0x1000) / Volume
->BytesPerSector
;
276 Volume
->RootDirSectorStart
= Volume
->FatSectorStart
+ Volume
->NumberOfFats
* Volume
->SectorsPerFat
;
277 Volume
->RootDirSectors
= FatXVolumeBootSector
->SectorsPerCluster
;
279 Volume
->DataSectorStart
= Volume
->RootDirSectorStart
+ Volume
->RootDirSectors
;
281 else if (Volume
->FatType
!= FAT32
)
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
;
290 Volume
->RootDirSectorStart
= Volume
->FatSectorStart
+ Volume
->NumberOfFats
* Volume
->SectorsPerFat
;
291 Volume
->RootDirSectors
= ((FatVolumeBootSector
->RootDirEntries
* 32) + (Volume
->BytesPerSector
- 1)) / Volume
->BytesPerSector
;
293 Volume
->DataSectorStart
= Volume
->RootDirSectorStart
+ Volume
->RootDirSectors
;
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
;
305 Volume
->RootDirStartCluster
= Fat32VolumeBootSector
->RootDirStartCluster
;
306 Volume
->DataSectorStart
= Volume
->FatSectorStart
+ Volume
->NumberOfFats
* Volume
->SectorsPerFat
;
310 // we only work with version 0
312 if (Fat32VolumeBootSector
->FileSystemVersion
!= 0)
314 FileSystemError("FreeLoader is too old to work with this FAT32 filesystem.\nPlease update FreeLoader.");
322 ULONG
FatDetermineFatType(PFAT_BOOTSECTOR FatBootSector
, ULONGLONG PartitionSectorCount
)
324 ULONG RootDirSectors
;
325 ULONG DataSectorCount
;
328 ULONG CountOfClusters
;
329 PFAT32_BOOTSECTOR Fat32BootSector
= (PFAT32_BOOTSECTOR
)FatBootSector
;
330 PFATX_BOOTSECTOR FatXBootSector
= (PFATX_BOOTSECTOR
)FatBootSector
;
332 if (0 == strncmp(FatXBootSector
->FileSystemType
, "FATX", 4))
334 CountOfClusters
= (ULONG
)(PartitionSectorCount
/ FatXBootSector
->SectorsPerCluster
);
335 if (CountOfClusters
< 65525)
337 /* Volume is FATX16 */
342 /* Volume is FAT32 */
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
);
354 if (FatBootSector
->SectorsPerCluster
== 0)
357 CountOfClusters
= DataSectorCount
/ FatBootSector
->SectorsPerCluster
;
359 if (CountOfClusters
< 4085)
361 /* Volume is FAT12 */
364 else if (CountOfClusters
< 65525)
366 /* Volume is FAT16 */
371 /* Volume is FAT32 */
377 typedef struct _DIRECTORY_BUFFER
381 ULONG DirectoryStartCluster
;
384 } DIRECTORY_BUFFER
, *PDIRECTORY_BUFFER
;
386 LIST_ENTRY DirectoryBufferListHead
= {&DirectoryBufferListHead
, &DirectoryBufferListHead
};
388 PVOID
FatBufferDirectory(PFAT_VOLUME_INFO Volume
, ULONG DirectoryStartCluster
, ULONG
*DirectorySize
, BOOLEAN RootDirectory
)
390 PDIRECTORY_BUFFER DirectoryBuffer
;
393 TRACE("FatBufferDirectory() DirectoryStartCluster = %d RootDirectory = %s\n", DirectoryStartCluster
, (RootDirectory
? "TRUE" : "FALSE"));
396 * For FAT32, the root directory is nothing special. We can treat it the same
399 if (RootDirectory
&& Volume
->FatType
== FAT32
)
401 DirectoryStartCluster
= Volume
->RootDirStartCluster
;
402 RootDirectory
= FALSE
;
405 /* Search the list for a match */
406 for (Entry
= DirectoryBufferListHead
.Flink
;
407 Entry
!= &DirectoryBufferListHead
;
408 Entry
= Entry
->Flink
)
410 DirectoryBuffer
= CONTAINING_RECORD(Entry
, DIRECTORY_BUFFER
, Link
);
412 /* Check if it matches */
413 if ((DirectoryBuffer
->Volume
== Volume
) &&
414 (DirectoryBuffer
->DirectoryStartCluster
== DirectoryStartCluster
))
416 TRACE("Found cached buffer\n");
417 *DirectorySize
= DirectoryBuffer
->DirectorySize
;
418 return DirectoryBuffer
->Data
;
423 // Calculate the size of the directory
427 *DirectorySize
= Volume
->RootDirSectors
* Volume
->BytesPerSector
;
431 *DirectorySize
= FatCountClustersInChain(Volume
, DirectoryStartCluster
) * Volume
->SectorsPerCluster
* Volume
->BytesPerSector
;
435 // Attempt to allocate memory for directory buffer
437 TRACE("Trying to allocate (DirectorySize) %d bytes.\n", *DirectorySize
);
438 DirectoryBuffer
= FrLdrTempAlloc(*DirectorySize
+ sizeof(DIRECTORY_BUFFER
),
441 if (DirectoryBuffer
== NULL
)
447 // Now read directory contents into DirectoryBuffer
451 if (!FatReadVolumeSectors(Volume
, Volume
->RootDirSectorStart
, Volume
->RootDirSectors
, DirectoryBuffer
->Data
))
453 FrLdrTempFree(DirectoryBuffer
, TAG_FAT_BUFFER
);
459 if (!FatReadClusterChain(Volume
, DirectoryStartCluster
, 0xFFFFFFFF, DirectoryBuffer
->Data
))
461 FrLdrTempFree(DirectoryBuffer
, TAG_FAT_BUFFER
);
466 /* Enqueue it in the list */
467 DirectoryBuffer
->Volume
= Volume
;
468 DirectoryBuffer
->DirectoryStartCluster
= DirectoryStartCluster
;
469 DirectoryBuffer
->DirectorySize
= *DirectorySize
;
470 InsertTailList(&DirectoryBufferListHead
, &DirectoryBuffer
->Link
);
472 return DirectoryBuffer
->Data
;
475 BOOLEAN
FatSearchDirectoryBufferForFile(PFAT_VOLUME_INFO Volume
, PVOID DirectoryBuffer
, ULONG DirectorySize
, PCHAR FileName
, PFAT_FILE_INFO FatFileInfoPointer
)
479 CHAR LfnNameBuffer
[265];
480 CHAR ShortNameBuffer
[20];
482 DIRENTRY OurDirEntry
;
483 LFN_DIRENTRY OurLfnDirEntry
;
484 PDIRENTRY DirEntry
= &OurDirEntry
;
485 PLFN_DIRENTRY LfnDirEntry
= &OurLfnDirEntry
;
487 EntryCount
= DirectorySize
/ sizeof(DIRENTRY
);
489 TRACE("FatSearchDirectoryBufferForFile() DirectoryBuffer = 0x%x EntryCount = %d FileName = %s\n", DirectoryBuffer
, EntryCount
, FileName
);
491 memset(ShortNameBuffer
, 0, 13 * sizeof(CHAR
));
492 memset(LfnNameBuffer
, 0, 261 * sizeof(CHAR
));
494 for (CurrentEntry
=0; CurrentEntry
<EntryCount
; CurrentEntry
++, DirectoryBuffer
= ((PDIRENTRY
)DirectoryBuffer
)+1)
496 OurLfnDirEntry
= *((PLFN_DIRENTRY
) DirectoryBuffer
);
497 FatSwapLFNDirEntry(LfnDirEntry
);
498 OurDirEntry
= *((PDIRENTRY
) DirectoryBuffer
);
499 FatSwapDirEntry(DirEntry
);
501 //TRACE("Dumping directory entry %d:\n", CurrentEntry);
502 //DbgDumpBuffer(DPRINT_FILESYSTEM, DirEntry, sizeof(DIRENTRY));
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.
510 if (DirEntry
->FileName
[0] == '\0')
516 // Check if this is a deleted entry or not
518 if (DirEntry
->FileName
[0] == '\xE5')
520 memset(ShortNameBuffer
, 0, 13 * sizeof(CHAR
));
521 memset(LfnNameBuffer
, 0, 261 * sizeof(CHAR
));
526 // Check if this is a LFN entry
527 // If so it needs special handling
529 if (DirEntry
->Attr
== ATTR_LONG_NAME
)
532 // Check to see if this is a deleted LFN entry, if so continue
534 if (LfnDirEntry
->SequenceNumber
& 0x80)
540 // Mask off high two bits of sequence number
541 // and make the sequence number zero-based
543 LfnDirEntry
->SequenceNumber
&= 0x3F;
544 LfnDirEntry
->SequenceNumber
--;
547 // Get all 13 LFN entry characters
549 if (LfnDirEntry
->Name0_4
[0] != 0xFFFF)
551 LfnNameBuffer
[0 + (LfnDirEntry
->SequenceNumber
* 13)] = (UCHAR
)LfnDirEntry
->Name0_4
[0];
553 if (LfnDirEntry
->Name0_4
[1] != 0xFFFF)
555 LfnNameBuffer
[1 + (LfnDirEntry
->SequenceNumber
* 13)] = (UCHAR
)LfnDirEntry
->Name0_4
[1];
557 if (LfnDirEntry
->Name0_4
[2] != 0xFFFF)
559 LfnNameBuffer
[2 + (LfnDirEntry
->SequenceNumber
* 13)] = (UCHAR
)LfnDirEntry
->Name0_4
[2];
561 if (LfnDirEntry
->Name0_4
[3] != 0xFFFF)
563 LfnNameBuffer
[3 + (LfnDirEntry
->SequenceNumber
* 13)] = (UCHAR
)LfnDirEntry
->Name0_4
[3];
565 if (LfnDirEntry
->Name0_4
[4] != 0xFFFF)
567 LfnNameBuffer
[4 + (LfnDirEntry
->SequenceNumber
* 13)] = (UCHAR
)LfnDirEntry
->Name0_4
[4];
569 if (LfnDirEntry
->Name5_10
[0] != 0xFFFF)
571 LfnNameBuffer
[5 + (LfnDirEntry
->SequenceNumber
* 13)] = (UCHAR
)LfnDirEntry
->Name5_10
[0];
573 if (LfnDirEntry
->Name5_10
[1] != 0xFFFF)
575 LfnNameBuffer
[6 + (LfnDirEntry
->SequenceNumber
* 13)] = (UCHAR
)LfnDirEntry
->Name5_10
[1];
577 if (LfnDirEntry
->Name5_10
[2] != 0xFFFF)
579 LfnNameBuffer
[7 + (LfnDirEntry
->SequenceNumber
* 13)] = (UCHAR
)LfnDirEntry
->Name5_10
[2];
581 if (LfnDirEntry
->Name5_10
[3] != 0xFFFF)
583 LfnNameBuffer
[8 + (LfnDirEntry
->SequenceNumber
* 13)] = (UCHAR
)LfnDirEntry
->Name5_10
[3];
585 if (LfnDirEntry
->Name5_10
[4] != 0xFFFF)
587 LfnNameBuffer
[9 + (LfnDirEntry
->SequenceNumber
* 13)] = (UCHAR
)LfnDirEntry
->Name5_10
[4];
589 if (LfnDirEntry
->Name5_10
[5] != 0xFFFF)
591 LfnNameBuffer
[10 + (LfnDirEntry
->SequenceNumber
* 13)] = (UCHAR
)LfnDirEntry
->Name5_10
[5];
593 if (LfnDirEntry
->Name11_12
[0] != 0xFFFF)
595 LfnNameBuffer
[11 + (LfnDirEntry
->SequenceNumber
* 13)] = (UCHAR
)LfnDirEntry
->Name11_12
[0];
597 if (LfnDirEntry
->Name11_12
[1] != 0xFFFF)
599 LfnNameBuffer
[12 + (LfnDirEntry
->SequenceNumber
* 13)] = (UCHAR
)LfnDirEntry
->Name11_12
[1];
602 //TRACE("Dumping long name buffer:\n");
603 //DbgDumpBuffer(DPRINT_FILESYSTEM, LfnNameBuffer, 260);
609 // Check for the volume label attribute
610 // and skip over this entry if found
612 if (DirEntry
->Attr
& ATTR_VOLUMENAME
)
614 memset(ShortNameBuffer
, 0, 13 * sizeof(UCHAR
));
615 memset(LfnNameBuffer
, 0, 261 * sizeof(UCHAR
));
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.
629 // Get short file name
631 FatParseShortFileName(ShortNameBuffer
, DirEntry
);
633 //TRACE("Entry: %d LFN = %s\n", CurrentEntry, LfnNameBuffer);
634 //TRACE("Entry: %d DOS name = %s\n", CurrentEntry, ShortNameBuffer);
637 // See if the file name matches either the short or long name
639 if (((strlen(FileName
) == strlen(LfnNameBuffer
)) && (_stricmp(FileName
, LfnNameBuffer
) == 0)) ||
640 ((strlen(FileName
) == strlen(ShortNameBuffer
)) && (_stricmp(FileName
, ShortNameBuffer
) == 0))) {
642 // We found the entry, now fill in the FAT_FILE_INFO struct
644 FatFileInfoPointer
->Attributes
= DirEntry
->Attr
;
645 FatFileInfoPointer
->FileSize
= DirEntry
->Size
;
646 FatFileInfoPointer
->FilePointer
= 0;
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
);
663 // Get the cluster chain
665 StartCluster
= ((ULONG
)DirEntry
->ClusterHigh
<< 16) + DirEntry
->ClusterLow
;
666 TRACE("StartCluster = 0x%x\n", StartCluster
);
667 FatFileInfoPointer
->FileFatChain
= FatGetClusterChainArray(Volume
, StartCluster
);
670 // See if memory allocation failed
672 if (FatFileInfoPointer
->FileFatChain
== NULL
)
681 // Nope, no match - zero buffers and continue looking
683 memset(ShortNameBuffer
, 0, 13 * sizeof(UCHAR
));
684 memset(LfnNameBuffer
, 0, 261 * sizeof(UCHAR
));
691 static BOOLEAN
FatXSearchDirectoryBufferForFile(PFAT_VOLUME_INFO Volume
, PVOID DirectoryBuffer
, ULONG DirectorySize
, PCHAR FileName
, PFAT_FILE_INFO FatFileInfoPointer
)
696 FATX_DIRENTRY OurDirEntry
;
697 PFATX_DIRENTRY DirEntry
= &OurDirEntry
;
699 EntryCount
= DirectorySize
/ sizeof(FATX_DIRENTRY
);
701 TRACE("FatXSearchDirectoryBufferForFile() DirectoryBuffer = 0x%x EntryCount = %d FileName = %s\n", DirectoryBuffer
, EntryCount
, FileName
);
703 FileNameLen
= strlen(FileName
);
705 for (CurrentEntry
= 0; CurrentEntry
< EntryCount
; CurrentEntry
++, DirectoryBuffer
= ((PFATX_DIRENTRY
)DirectoryBuffer
)+1)
707 OurDirEntry
= *(PFATX_DIRENTRY
) DirectoryBuffer
;
708 FatSwapFatXDirEntry(&OurDirEntry
);
709 if (0xff == DirEntry
->FileNameSize
)
713 if (0xe5 == DirEntry
->FileNameSize
)
717 if (FileNameLen
== DirEntry
->FileNameSize
&&
718 0 == _strnicmp(FileName
, DirEntry
->FileName
, FileNameLen
))
721 * We found the entry, now fill in the FAT_FILE_INFO struct
723 FatFileInfoPointer
->Attributes
= DirEntry
->Attr
;
724 FatFileInfoPointer
->FileSize
= DirEntry
->Size
;
725 FatFileInfoPointer
->FilePointer
= 0;
727 TRACE("FATX Directory Entry:\n");
728 TRACE("FileNameSize = %d\n", DirEntry
->FileNameSize
);
729 TRACE("Attr = 0x%x\n", DirEntry
->Attr
);
730 TRACE("StartCluster = 0x%x\n", DirEntry
->StartCluster
);
731 TRACE("Size = %d\n", DirEntry
->Size
);
732 TRACE("Time = %d\n", DirEntry
->Time
);
733 TRACE("Date = %d\n", DirEntry
->Date
);
734 TRACE("CreateTime = %d\n", DirEntry
->CreateTime
);
735 TRACE("CreateDate = %d\n", DirEntry
->CreateDate
);
736 TRACE("LastAccessTime = %d\n", DirEntry
->LastAccessTime
);
737 TRACE("LastAccessDate = %d\n", DirEntry
->LastAccessDate
);
740 * Get the cluster chain
742 FatFileInfoPointer
->FileFatChain
= FatGetClusterChainArray(Volume
, DirEntry
->StartCluster
);
745 * See if memory allocation failed
747 if (NULL
== FatFileInfoPointer
->FileFatChain
)
761 * This function searches the file system for the
762 * specified filename and fills in an FAT_FILE_INFO structure
763 * with info describing the file, etc. returns ARC error code
765 ARC_STATUS
FatLookupFile(PFAT_VOLUME_INFO Volume
, PCSTR FileName
, ULONG DeviceId
, PFAT_FILE_INFO FatFileInfoPointer
)
768 ULONG NumberOfPathParts
;
770 PVOID DirectoryBuffer
;
771 ULONG DirectoryStartCluster
= 0;
773 FAT_FILE_INFO FatFileInfo
;
775 TRACE("FatLookupFile() FileName = %s\n", FileName
);
777 memset(FatFileInfoPointer
, 0, sizeof(FAT_FILE_INFO
));
780 // Figure out how many sub-directories we are nested in
782 NumberOfPathParts
= FsGetNumPathParts(FileName
);
785 // Loop once for each part
787 for (i
=0; i
<NumberOfPathParts
; i
++)
790 // Get first path part
792 FsGetFirstNameFromPath(PathPart
, FileName
);
795 // Advance to the next part of the path
797 for (; (*FileName
!= '\\') && (*FileName
!= '/') && (*FileName
!= '\0'); FileName
++)
803 // Buffer the directory contents
805 DirectoryBuffer
= FatBufferDirectory(Volume
, DirectoryStartCluster
, &DirectorySize
, (i
== 0) );
806 if (DirectoryBuffer
== NULL
)
812 // Search for file name in directory
814 if (ISFATX(Volume
->FatType
))
816 if (!FatXSearchDirectoryBufferForFile(Volume
, DirectoryBuffer
, DirectorySize
, PathPart
, &FatFileInfo
))
823 if (!FatSearchDirectoryBufferForFile(Volume
, DirectoryBuffer
, DirectorySize
, PathPart
, &FatFileInfo
))
830 // If we have another sub-directory to go then
831 // grab the start cluster and free the fat chain array
833 if ((i
+1) < NumberOfPathParts
)
836 // Check if current entry is a directory
838 if (!(FatFileInfo
.Attributes
& ATTR_DIRECTORY
))
840 FrLdrTempFree(FatFileInfo
.FileFatChain
, TAG_FAT_CHAIN
);
843 DirectoryStartCluster
= FatFileInfo
.FileFatChain
[0];
844 FrLdrTempFree(FatFileInfo
.FileFatChain
, TAG_FAT_CHAIN
);
845 FatFileInfo
.FileFatChain
= NULL
;
849 memcpy(FatFileInfoPointer
, &FatFileInfo
, sizeof(FAT_FILE_INFO
));
856 * This function parses a directory entry name which
857 * is in the form of "FILE EXT" and puts it in Buffer
858 * in the form of "file.ext"
860 void FatParseShortFileName(PCHAR Buffer
, PDIRENTRY DirEntry
)
865 RtlZeroMemory(Buffer
, 13);
868 // Fixup first character
870 if (DirEntry
->FileName
[0] == 0x05)
872 DirEntry
->FileName
[0] = 0xE5;
880 if (DirEntry
->FileName
[Idx
] == ' ')
885 Buffer
[Idx
] = DirEntry
->FileName
[Idx
];
892 if ((DirEntry
->FileName
[8] != ' '))
895 Buffer
[Idx
++] = (DirEntry
->FileName
[8] == ' ') ? '\0' : DirEntry
->FileName
[8];
896 Buffer
[Idx
++] = (DirEntry
->FileName
[9] == ' ') ? '\0' : DirEntry
->FileName
[9];
897 Buffer
[Idx
++] = (DirEntry
->FileName
[10] == ' ') ? '\0' : DirEntry
->FileName
[10];
900 //TRACE("FatParseShortFileName() ShortName = %s\n", Buffer);
905 * returns the Fat entry for a given cluster number
907 BOOLEAN
FatGetFatEntry(PFAT_VOLUME_INFO Volume
, ULONG Cluster
, ULONG
* ClusterPointer
)
911 UINT32 ThisFatSecNum
;
912 UINT32 ThisFatEntOffset
;
915 BOOLEAN Success
= TRUE
;
917 //TRACE("FatGetFatEntry() Retrieving FAT entry for cluster %d.\n", Cluster);
919 // We need a buffer for 2 sectors
920 ReadBuffer
= FrLdrTempAlloc(2 * Volume
->BytesPerSector
, TAG_FAT_BUFFER
);
926 switch(Volume
->FatType
)
930 FatOffset
= Cluster
+ (Cluster
/ 2);
931 ThisFatSecNum
= Volume
->ActiveFatSectorStart
+ (FatOffset
/ Volume
->BytesPerSector
);
932 ThisFatEntOffset
= (FatOffset
% Volume
->BytesPerSector
);
934 TRACE("FatOffset: %d\n", FatOffset
);
935 TRACE("ThisFatSecNum: %d\n", ThisFatSecNum
);
936 TRACE("ThisFatEntOffset: %d\n", ThisFatEntOffset
);
938 if (ThisFatEntOffset
== (Volume
->BytesPerSector
- 1))
947 if (!FatReadVolumeSectors(Volume
, ThisFatSecNum
, SectorCount
, ReadBuffer
))
953 fat
= *((USHORT
*) (ReadBuffer
+ ThisFatEntOffset
));
955 if (Cluster
& 0x0001)
956 fat
= fat
>> 4; /* Cluster number is ODD */
958 fat
= fat
& 0x0FFF; /* Cluster number is EVEN */
965 FatOffset
= (Cluster
* 2);
966 ThisFatSecNum
= Volume
->ActiveFatSectorStart
+ (FatOffset
/ Volume
->BytesPerSector
);
967 ThisFatEntOffset
= (FatOffset
% Volume
->BytesPerSector
);
969 if (!FatReadVolumeSectors(Volume
, ThisFatSecNum
, 1, ReadBuffer
))
975 fat
= *((USHORT
*) (ReadBuffer
+ ThisFatEntOffset
));
983 FatOffset
= (Cluster
* 4);
984 ThisFatSecNum
= Volume
->ActiveFatSectorStart
+ (FatOffset
/ Volume
->BytesPerSector
);
985 ThisFatEntOffset
= (FatOffset
% Volume
->BytesPerSector
);
987 if (!FatReadVolumeSectors(Volume
, ThisFatSecNum
, 1, ReadBuffer
))
993 fat
= (*((ULONG
*) (ReadBuffer
+ ThisFatEntOffset
))) & 0x0FFFFFFF;
999 ERR("Unknown FAT type %d\n", Volume
->FatType
);
1004 //TRACE("FAT entry is 0x%x.\n", fat);
1006 FrLdrTempFree(ReadBuffer
, TAG_FAT_BUFFER
);
1008 *ClusterPointer
= fat
;
1013 ULONG
FatCountClustersInChain(PFAT_VOLUME_INFO Volume
, ULONG StartCluster
)
1015 ULONG ClusterCount
= 0;
1017 TRACE("FatCountClustersInChain() StartCluster = %d\n", StartCluster
);
1022 // If end of chain then break out of our cluster counting loop
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)))
1039 if (!FatGetFatEntry(Volume
, StartCluster
, &StartCluster
))
1045 TRACE("FatCountClustersInChain() ClusterCount = %d\n", ClusterCount
);
1047 return ClusterCount
;
1050 ULONG
* FatGetClusterChainArray(PFAT_VOLUME_INFO Volume
, ULONG StartCluster
)
1054 ULONG
* ArrayPointer
;
1057 TRACE("FatGetClusterChainArray() StartCluster = %d\n", StartCluster
);
1059 ClusterCount
= FatCountClustersInChain(Volume
, StartCluster
) + 1; // Lets get the 0x0ffffff8 on the end of the array
1060 ArraySize
= ClusterCount
* sizeof(ULONG
);
1063 // Allocate array memory
1065 ArrayPointer
= FrLdrTempAlloc(ArraySize
, TAG_FAT_CHAIN
);
1067 if (ArrayPointer
== NULL
)
1073 // Loop through and set array values
1075 for (Idx
=0; Idx
<ClusterCount
; Idx
++)
1078 // Set current cluster
1080 ArrayPointer
[Idx
] = StartCluster
;
1083 // Don't try to get next cluster for last cluster
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)))
1096 if (!FatGetFatEntry(Volume
, StartCluster
, &StartCluster
))
1098 FrLdrTempFree(ArrayPointer
, TAG_FAT_CHAIN
);
1103 return ArrayPointer
;
1107 * FatReadClusterChain()
1108 * Reads the specified clusters into memory
1110 BOOLEAN
FatReadClusterChain(PFAT_VOLUME_INFO Volume
, ULONG StartClusterNumber
, ULONG NumberOfClusters
, PVOID Buffer
)
1112 ULONG ClusterStartSector
;
1114 TRACE("FatReadClusterChain() StartClusterNumber = %d NumberOfClusters = %d Buffer = 0x%x\n", StartClusterNumber
, NumberOfClusters
, Buffer
);
1116 while (NumberOfClusters
> 0)
1119 //TRACE("FatReadClusterChain() StartClusterNumber = %d NumberOfClusters = %d Buffer = 0x%x\n", StartClusterNumber, NumberOfClusters, Buffer);
1121 // Calculate starting sector for cluster
1123 ClusterStartSector
= ((StartClusterNumber
- 2) * Volume
->SectorsPerCluster
) + Volume
->DataSectorStart
;
1126 // Read cluster into memory
1128 if (!FatReadVolumeSectors(Volume
, ClusterStartSector
, Volume
->SectorsPerCluster
, Buffer
))
1134 // Decrement count of clusters left to read
1139 // Increment buffer address by cluster size
1141 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ (Volume
->SectorsPerCluster
* Volume
->BytesPerSector
));
1146 if (!FatGetFatEntry(Volume
, StartClusterNumber
, &StartClusterNumber
))
1152 // If end of chain then break out of our cluster reading loop
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)))
1166 * FatReadPartialCluster()
1167 * Reads part of a cluster into memory
1169 BOOLEAN
FatReadPartialCluster(PFAT_VOLUME_INFO Volume
, ULONG ClusterNumber
, ULONG StartingOffset
, ULONG Length
, PVOID Buffer
)
1171 ULONG ClusterStartSector
;
1172 ULONG SectorOffset
, ReadSize
, SectorCount
;
1174 BOOLEAN Success
= FALSE
;
1176 //TRACE("FatReadPartialCluster() ClusterNumber = %d StartingOffset = %d Length = %d Buffer = 0x%x\n", ClusterNumber, StartingOffset, Length, Buffer);
1178 ClusterStartSector
= ((ClusterNumber
- 2) * Volume
->SectorsPerCluster
) + Volume
->DataSectorStart
;
1180 // This is the offset of the data in sectors
1181 SectorOffset
= (StartingOffset
/ Volume
->BytesPerSector
);
1182 StartingOffset
%= Volume
->BytesPerSector
;
1184 // Calculate how many sectors we need to read
1185 SectorCount
= (StartingOffset
+ Length
+ Volume
->BytesPerSector
- 1) / Volume
->BytesPerSector
;
1187 // Calculate rounded up read size
1188 ReadSize
= SectorCount
* Volume
->BytesPerSector
;
1190 ReadBuffer
= FrLdrTempAlloc(ReadSize
, TAG_FAT_BUFFER
);
1196 if (FatReadVolumeSectors(Volume
, ClusterStartSector
+ SectorOffset
, SectorCount
, ReadBuffer
))
1198 memcpy(Buffer
, ReadBuffer
+ StartingOffset
, Length
);
1202 FrLdrTempFree(ReadBuffer
, TAG_FAT_BUFFER
);
1209 * Reads BytesToRead from open file and
1210 * returns the number of bytes read in BytesRead
1212 BOOLEAN
FatReadFile(PFAT_FILE_INFO FatFileInfo
, ULONG BytesToRead
, ULONG
* BytesRead
, PVOID Buffer
)
1214 PFAT_VOLUME_INFO Volume
= FatFileInfo
->Volume
;
1215 ULONG ClusterNumber
;
1216 ULONG OffsetInCluster
;
1217 ULONG LengthInCluster
;
1218 ULONG NumberOfClusters
;
1219 ULONG BytesPerCluster
;
1221 TRACE("FatReadFile() BytesToRead = %d Buffer = 0x%x\n", BytesToRead
, Buffer
);
1223 if (BytesRead
!= NULL
)
1229 // If they are trying to read past the
1230 // end of the file then return success
1231 // with BytesRead == 0
1233 if (FatFileInfo
->FilePointer
>= FatFileInfo
->FileSize
)
1239 // If they are trying to read more than there is to read
1240 // then adjust the amount to read
1242 if ((FatFileInfo
->FilePointer
+ BytesToRead
) > FatFileInfo
->FileSize
)
1244 BytesToRead
= (FatFileInfo
->FileSize
- FatFileInfo
->FilePointer
);
1248 // Ok, now we have to perform at most 3 calculations
1249 // I'll draw you a picture (using nifty ASCII art):
1251 // CurrentFilePointer -+
1253 // +----------------+
1255 // +-----------+-----------+-----------+-----------+
1256 // | Cluster 1 | Cluster 2 | Cluster 3 | Cluster 4 |
1257 // +-----------+-----------+-----------+-----------+
1259 // +---------------+--------------------+
1261 // BytesToRead -------+
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
1270 // 3 - The last calculation (and read) would read
1271 // in the remainder of the data requested out of
1272 // the last cluster.
1275 BytesPerCluster
= Volume
->SectorsPerCluster
* Volume
->BytesPerSector
;
1278 // Only do the first read if we
1279 // aren't aligned on a cluster boundary
1281 if (FatFileInfo
->FilePointer
% BytesPerCluster
)
1284 // Do the math for our first read
1286 ClusterNumber
= (FatFileInfo
->FilePointer
/ BytesPerCluster
);
1287 ClusterNumber
= FatFileInfo
->FileFatChain
[ClusterNumber
];
1288 OffsetInCluster
= (FatFileInfo
->FilePointer
% BytesPerCluster
);
1289 LengthInCluster
= (BytesToRead
> (BytesPerCluster
- OffsetInCluster
)) ? (BytesPerCluster
- OffsetInCluster
) : BytesToRead
;
1292 // Now do the read and update BytesRead, BytesToRead, FilePointer, & Buffer
1294 if (!FatReadPartialCluster(Volume
, ClusterNumber
, OffsetInCluster
, LengthInCluster
, Buffer
))
1298 if (BytesRead
!= NULL
)
1300 *BytesRead
+= LengthInCluster
;
1302 BytesToRead
-= LengthInCluster
;
1303 FatFileInfo
->FilePointer
+= LengthInCluster
;
1304 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ LengthInCluster
);
1308 // Do the math for our second read (if any data left)
1310 if (BytesToRead
> 0)
1313 // Determine how many full clusters we need to read
1315 NumberOfClusters
= (BytesToRead
/ BytesPerCluster
);
1317 if (NumberOfClusters
> 0)
1319 ClusterNumber
= (FatFileInfo
->FilePointer
/ BytesPerCluster
);
1320 ClusterNumber
= FatFileInfo
->FileFatChain
[ClusterNumber
];
1323 // Now do the read and update BytesRead, BytesToRead, FilePointer, & Buffer
1325 if (!FatReadClusterChain(Volume
, ClusterNumber
, NumberOfClusters
, Buffer
))
1329 if (BytesRead
!= NULL
)
1331 *BytesRead
+= (NumberOfClusters
* BytesPerCluster
);
1333 BytesToRead
-= (NumberOfClusters
* BytesPerCluster
);
1334 FatFileInfo
->FilePointer
+= (NumberOfClusters
* BytesPerCluster
);
1335 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ (NumberOfClusters
* BytesPerCluster
));
1340 // Do the math for our third read (if any data left)
1342 if (BytesToRead
> 0)
1344 ClusterNumber
= (FatFileInfo
->FilePointer
/ BytesPerCluster
);
1345 ClusterNumber
= FatFileInfo
->FileFatChain
[ClusterNumber
];
1348 // Now do the read and update BytesRead, BytesToRead, FilePointer, & Buffer
1350 if (!FatReadPartialCluster(Volume
, ClusterNumber
, 0, BytesToRead
, Buffer
))
1354 if (BytesRead
!= NULL
)
1356 *BytesRead
+= BytesToRead
;
1358 FatFileInfo
->FilePointer
+= BytesToRead
;
1359 BytesToRead
-= BytesToRead
;
1360 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ BytesToRead
);
1366 BOOLEAN
FatReadVolumeSectors(PFAT_VOLUME_INFO Volume
, ULONG SectorNumber
, ULONG SectorCount
, PVOID Buffer
)
1368 LARGE_INTEGER Position
;
1372 //TRACE("FatReadVolumeSectors(): SectorNumber %d, SectorCount %d, Buffer %p\n",
1373 // SectorNumber, SectorCount, Buffer);
1376 // Seek to right position
1378 Position
.QuadPart
= (ULONGLONG
)SectorNumber
* 512;
1379 Status
= ArcSeek(Volume
->DeviceId
, &Position
, SeekAbsolute
);
1380 if (Status
!= ESUCCESS
)
1382 TRACE("FatReadVolumeSectors() Failed to seek\n");
1389 Status
= ArcRead(Volume
->DeviceId
, Buffer
, SectorCount
* 512, &Count
);
1390 if (Status
!= ESUCCESS
|| Count
!= SectorCount
* 512)
1392 TRACE("FatReadVolumeSectors() Failed to read\n");
1400 ARC_STATUS
FatClose(ULONG FileId
)
1402 PFAT_FILE_INFO FileHandle
= FsGetDeviceSpecific(FileId
);
1404 if (FileHandle
->FileFatChain
) FrLdrTempFree(FileHandle
->FileFatChain
, TAG_FAT_CHAIN
);
1405 FrLdrTempFree(FileHandle
, TAG_FAT_FILE
);
1410 ARC_STATUS
FatGetFileInformation(ULONG FileId
, FILEINFORMATION
* Information
)
1412 PFAT_FILE_INFO FileHandle
= FsGetDeviceSpecific(FileId
);
1414 RtlZeroMemory(Information
, sizeof(*Information
));
1415 Information
->EndingAddress
.LowPart
= FileHandle
->FileSize
;
1416 Information
->CurrentAddress
.LowPart
= FileHandle
->FilePointer
;
1418 TRACE("FatGetFileInformation(%lu) -> FileSize = %lu, FilePointer = 0x%lx\n",
1419 FileId
, Information
->EndingAddress
.LowPart
, Information
->CurrentAddress
.LowPart
);
1424 ARC_STATUS
FatOpen(CHAR
* Path
, OPENMODE OpenMode
, ULONG
* FileId
)
1426 PFAT_VOLUME_INFO FatVolume
;
1427 FAT_FILE_INFO TempFileInfo
;
1428 PFAT_FILE_INFO FileHandle
;
1430 BOOLEAN IsDirectory
;
1433 if (OpenMode
!= OpenReadOnly
&& OpenMode
!= OpenDirectory
)
1436 DeviceId
= FsGetDeviceId(*FileId
);
1437 FatVolume
= FatVolumes
[DeviceId
];
1439 TRACE("FatOpen() FileName = %s\n", Path
);
1441 RtlZeroMemory(&TempFileInfo
, sizeof(TempFileInfo
));
1442 Status
= FatLookupFile(FatVolume
, Path
, DeviceId
, &TempFileInfo
);
1443 if (Status
!= ESUCCESS
)
1447 // Check if caller opened what he expected (dir vs file)
1449 IsDirectory
= (TempFileInfo
.Attributes
& ATTR_DIRECTORY
) != 0;
1450 if (IsDirectory
&& OpenMode
!= OpenDirectory
)
1452 else if (!IsDirectory
&& OpenMode
!= OpenReadOnly
)
1455 FileHandle
= FrLdrTempAlloc(sizeof(FAT_FILE_INFO
), TAG_FAT_FILE
);
1459 RtlCopyMemory(FileHandle
, &TempFileInfo
, sizeof(FAT_FILE_INFO
));
1460 FileHandle
->Volume
= FatVolume
;
1462 FsSetDeviceSpecific(*FileId
, FileHandle
);
1466 ARC_STATUS
FatRead(ULONG FileId
, VOID
* Buffer
, ULONG N
, ULONG
* Count
)
1468 PFAT_FILE_INFO FileHandle
= FsGetDeviceSpecific(FileId
);
1472 // Call old read method
1474 Success
= FatReadFile(FileHandle
, N
, Count
, Buffer
);
1477 // Check for success
1485 ARC_STATUS
FatSeek(ULONG FileId
, LARGE_INTEGER
* Position
, SEEKMODE SeekMode
)
1487 PFAT_FILE_INFO FileHandle
= FsGetDeviceSpecific(FileId
);
1489 TRACE("FatSeek() NewFilePointer = %lu\n", Position
->LowPart
);
1491 if (SeekMode
!= SeekAbsolute
)
1493 if (Position
->HighPart
!= 0)
1495 if (Position
->LowPart
>= FileHandle
->FileSize
)
1498 FileHandle
->FilePointer
= Position
->LowPart
;
1502 const DEVVTBL FatFuncTable
=
1505 FatGetFileInformation
,
1512 const DEVVTBL
* FatMount(ULONG DeviceId
)
1514 PFAT_VOLUME_INFO Volume
;
1516 PFAT_BOOTSECTOR BootSector
= (PFAT_BOOTSECTOR
)Buffer
;
1517 PFAT32_BOOTSECTOR BootSector32
= (PFAT32_BOOTSECTOR
)Buffer
;
1518 PFATX_BOOTSECTOR BootSectorX
= (PFATX_BOOTSECTOR
)Buffer
;
1519 FILEINFORMATION FileInformation
;
1520 LARGE_INTEGER Position
;
1522 ULARGE_INTEGER SectorCount
;
1526 // Allocate data for volume information
1528 Volume
= FrLdrTempAlloc(sizeof(FAT_VOLUME_INFO
), TAG_FAT_VOLUME
);
1531 RtlZeroMemory(Volume
, sizeof(FAT_VOLUME_INFO
));
1534 // Read the BootSector
1536 Position
.HighPart
= 0;
1537 Position
.LowPart
= 0;
1538 Status
= ArcSeek(DeviceId
, &Position
, SeekAbsolute
);
1539 if (Status
!= ESUCCESS
)
1541 FrLdrTempFree(Volume
, TAG_FAT_VOLUME
);
1544 Status
= ArcRead(DeviceId
, Buffer
, sizeof(Buffer
), &Count
);
1545 if (Status
!= ESUCCESS
|| Count
!= sizeof(Buffer
))
1547 FrLdrTempFree(Volume
, TAG_FAT_VOLUME
);
1552 // Check if BootSector is valid. If no, return early
1554 if (!RtlEqualMemory(BootSector
->FileSystemType
, "FAT12 ", 8) &&
1555 !RtlEqualMemory(BootSector
->FileSystemType
, "FAT16 ", 8) &&
1556 !RtlEqualMemory(BootSector32
->FileSystemType
, "FAT32 ", 8) &&
1557 !RtlEqualMemory(BootSectorX
->FileSystemType
, "FATX", 4))
1559 FrLdrTempFree(Volume
, TAG_FAT_VOLUME
);
1564 // Determine sector count
1566 Status
= ArcGetFileInformation(DeviceId
, &FileInformation
);
1567 if (Status
!= ESUCCESS
)
1569 FrLdrTempFree(Volume
, TAG_FAT_VOLUME
);
1572 SectorCount
.HighPart
= FileInformation
.EndingAddress
.HighPart
;
1573 SectorCount
.LowPart
= FileInformation
.EndingAddress
.LowPart
;
1574 SectorCount
.QuadPart
/= SECTOR_SIZE
;
1579 Volume
->DeviceId
= DeviceId
;
1582 // Really open the volume
1584 if (!FatOpenVolume(Volume
, BootSector
, SectorCount
.QuadPart
))
1586 FrLdrTempFree(Volume
, TAG_FAT_VOLUME
);
1591 // Remember FAT volume information
1593 FatVolumes
[DeviceId
] = Volume
;
1598 return &FatFuncTable
;