3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: FAT filesystem driver for FreeLoader
5 * COPYRIGHT: Copyright 1998-2003 Brian Palmer (brianp@sginet.com)
6 * Copyright 2009 Hervé Poussineau
7 * Copyright 2019 Victor Perevertkin (victor.perevertkin@reactos.org)
13 DBG_DEFAULT_CHANNEL(FILESYSTEM
);
15 ULONG
FatDetermineFatType(PFAT_BOOTSECTOR FatBootSector
, ULONGLONG PartitionSectorCount
);
16 PVOID
FatBufferDirectory(PFAT_VOLUME_INFO Volume
, ULONG DirectoryStartCluster
, ULONG
* EntryCountPointer
, BOOLEAN RootDirectory
);
17 BOOLEAN
FatSearchDirectoryBufferForFile(PFAT_VOLUME_INFO Volume
, PVOID DirectoryBuffer
, ULONG EntryCount
, PCHAR FileName
, PFAT_FILE_INFO FatFileInfoPointer
);
18 ARC_STATUS
FatLookupFile(PFAT_VOLUME_INFO Volume
, PCSTR FileName
, PFAT_FILE_INFO FatFileInfoPointer
);
19 void FatParseShortFileName(PCHAR Buffer
, PDIRENTRY DirEntry
);
20 static BOOLEAN
FatGetFatEntry(PFAT_VOLUME_INFO Volume
, UINT32 Cluster
, PUINT32 ClusterPointer
);
21 static ULONG
FatCountClustersInChain(PFAT_VOLUME_INFO Volume
, UINT32 StartCluster
);
22 static BOOLEAN
FatReadClusterChain(PFAT_VOLUME_INFO Volume
, UINT32 StartClusterNumber
, UINT32 NumberOfClusters
, PVOID Buffer
, PUINT32 LastClusterNumber
);
23 BOOLEAN
FatReadPartialCluster(PFAT_VOLUME_INFO Volume
, ULONG ClusterNumber
, ULONG StartingOffset
, ULONG Length
, PVOID Buffer
);
24 BOOLEAN
FatReadVolumeSectors(PFAT_VOLUME_INFO Volume
, ULONG SectorNumber
, ULONG SectorCount
, PVOID Buffer
);
26 #define FAT_IS_END_CLUSTER(clnumber) \
27 (((Volume->FatType == FAT12) && (clnumber >= 0xff8)) || \
28 ((Volume->FatType == FAT16 || Volume->FatType == FATX16) && (clnumber >= 0xfff8)) || \
29 ((Volume->FatType == FAT32 || Volume->FatType == FATX32) && (clnumber >= 0x0ffffff8)))
31 #define TAG_FAT_CHAIN 'CtaT'
32 #define TAG_FAT_FILE 'FtaF'
33 #define TAG_FAT_VOLUME 'VtaF'
34 #define TAG_FAT_BUFFER 'BtaF'
35 #define TAG_FAT_CACHE 'HtaF'
37 #define FAT_MAX_CACHE_SIZE (256 * 1024) // 256 KiB, note: it should fit maximum FAT12 FAT size (6144 bytes)
39 typedef struct _FAT_VOLUME_INFO
41 PUCHAR FatCache
; /* A part of 1st FAT cached in memory */
42 PULONG FatCacheIndex
; /* Cached sector's indexes */
43 ULONG FatCacheSize
; /* Size of the cache in sectors */
44 ULONG FatSectorStart
; /* Starting sector of 1st FAT table */
45 ULONG ActiveFatSectorStart
; /* Starting sector of active FAT table */
46 ULONG SectorsPerFat
; /* Sectors per FAT table */
47 ULONG RootDirSectorStart
; /* Starting sector of the root directory (non-fat32) */
48 ULONG RootDirSectors
; /* Number of sectors of the root directory (non-fat32) */
49 ULONG RootDirStartCluster
; /* Starting cluster number of the root directory (fat32 only) */
50 ULONG DataSectorStart
; /* Starting sector of the data area */
52 UINT16 BytesPerSector
; /* Number of bytes per sector */
53 UINT8 FatType
; /* FAT12, FAT16, FAT32, FATX16 or FATX32 */
54 UINT8 NumberOfFats
; /* Number of FAT tables */
55 UINT8 SectorsPerCluster
; /* Number of sectors per cluster */
58 PFAT_VOLUME_INFO FatVolumes
[MAX_FDS
];
60 VOID
FatSwapFatBootSector(PFAT_BOOTSECTOR Obj
)
62 SW(Obj
, BytesPerSector
);
63 SW(Obj
, ReservedSectors
);
64 SW(Obj
, RootDirEntries
);
65 SW(Obj
, TotalSectors
);
66 SW(Obj
, SectorsPerFat
);
67 SW(Obj
, SectorsPerTrack
);
68 SW(Obj
, NumberOfHeads
);
69 SD(Obj
, HiddenSectors
);
70 SD(Obj
, TotalSectorsBig
);
71 SD(Obj
, VolumeSerialNumber
);
72 SW(Obj
, BootSectorMagic
);
75 VOID
FatSwapFat32BootSector(PFAT32_BOOTSECTOR Obj
)
77 SW(Obj
, BytesPerSector
);
78 SW(Obj
, ReservedSectors
);
79 SW(Obj
, RootDirEntries
);
80 SW(Obj
, TotalSectors
);
81 SW(Obj
, SectorsPerFat
);
82 SW(Obj
, NumberOfHeads
);
83 SD(Obj
, HiddenSectors
);
84 SD(Obj
, TotalSectorsBig
);
85 SD(Obj
, SectorsPerFatBig
);
86 SW(Obj
, ExtendedFlags
);
87 SW(Obj
, FileSystemVersion
);
88 SD(Obj
, RootDirStartCluster
);
90 SW(Obj
, BackupBootSector
);
91 SD(Obj
, VolumeSerialNumber
);
92 SW(Obj
, BootSectorMagic
);
95 VOID
FatSwapFatXBootSector(PFATX_BOOTSECTOR Obj
)
97 SD(Obj
, VolumeSerialNumber
);
98 SD(Obj
, SectorsPerCluster
);
99 SW(Obj
, NumberOfFats
);
102 VOID
FatSwapDirEntry(PDIRENTRY Obj
)
106 SW(Obj
, LastAccessDate
);
107 SW(Obj
, ClusterHigh
);
114 VOID
FatSwapLFNDirEntry(PLFN_DIRENTRY Obj
)
117 SW(Obj
, StartCluster
);
118 for(i
= 0; i
< 5; i
++)
119 Obj
->Name0_4
[i
] = SWAPW(Obj
->Name0_4
[i
]);
120 for(i
= 0; i
< 6; i
++)
121 Obj
->Name5_10
[i
] = SWAPW(Obj
->Name5_10
[i
]);
122 for(i
= 0; i
< 2; i
++)
123 Obj
->Name11_12
[i
] = SWAPW(Obj
->Name11_12
[i
]);
126 VOID
FatSwapFatXDirEntry(PFATX_DIRENTRY Obj
)
128 SD(Obj
, StartCluster
);
134 SW(Obj
, LastAccessTime
);
135 SW(Obj
, LastAccessDate
);
138 BOOLEAN
FatOpenVolume(PFAT_VOLUME_INFO Volume
, PFAT_BOOTSECTOR BootSector
, ULONGLONG PartitionSectorCount
)
142 PFAT_BOOTSECTOR FatVolumeBootSector
;
143 PFAT32_BOOTSECTOR Fat32VolumeBootSector
;
144 PFATX_BOOTSECTOR FatXVolumeBootSector
;
146 TRACE("FatOpenVolume() DeviceId = %d\n", Volume
->DeviceId
);
149 // Allocate the memory to hold the boot sector
151 FatVolumeBootSector
= (PFAT_BOOTSECTOR
)BootSector
;
152 Fat32VolumeBootSector
= (PFAT32_BOOTSECTOR
)BootSector
;
153 FatXVolumeBootSector
= (PFATX_BOOTSECTOR
)BootSector
;
156 Volume
->FatType
= FatDetermineFatType(FatVolumeBootSector
, PartitionSectorCount
);
158 // Dump boot sector (and swap it for big endian systems)
159 TRACE("Dumping boot sector:\n");
160 if (ISFATX(Volume
->FatType
))
162 FatSwapFatXBootSector(FatXVolumeBootSector
);
163 TRACE("sizeof(FATX_BOOTSECTOR) = 0x%x.\n", sizeof(FATX_BOOTSECTOR
));
165 TRACE("FileSystemType: %c%c%c%c.\n", FatXVolumeBootSector
->FileSystemType
[0], FatXVolumeBootSector
->FileSystemType
[1], FatXVolumeBootSector
->FileSystemType
[2], FatXVolumeBootSector
->FileSystemType
[3]);
166 TRACE("VolumeSerialNumber: 0x%x\n", FatXVolumeBootSector
->VolumeSerialNumber
);
167 TRACE("SectorsPerCluster: %d\n", FatXVolumeBootSector
->SectorsPerCluster
);
168 TRACE("NumberOfFats: %d\n", FatXVolumeBootSector
->NumberOfFats
);
169 TRACE("Unknown: 0x%x\n", FatXVolumeBootSector
->Unknown
);
171 TRACE("FatType %s\n", Volume
->FatType
== FATX16
? "FATX16" : "FATX32");
174 else if (Volume
->FatType
== FAT32
)
176 FatSwapFat32BootSector(Fat32VolumeBootSector
);
177 TRACE("sizeof(FAT32_BOOTSECTOR) = 0x%x.\n", sizeof(FAT32_BOOTSECTOR
));
179 TRACE("JumpBoot: 0x%x 0x%x 0x%x\n", Fat32VolumeBootSector
->JumpBoot
[0], Fat32VolumeBootSector
->JumpBoot
[1], Fat32VolumeBootSector
->JumpBoot
[2]);
180 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]);
181 TRACE("BytesPerSector: %d\n", Fat32VolumeBootSector
->BytesPerSector
);
182 TRACE("SectorsPerCluster: %d\n", Fat32VolumeBootSector
->SectorsPerCluster
);
183 TRACE("ReservedSectors: %d\n", Fat32VolumeBootSector
->ReservedSectors
);
184 TRACE("NumberOfFats: %d\n", Fat32VolumeBootSector
->NumberOfFats
);
185 TRACE("RootDirEntries: %d\n", Fat32VolumeBootSector
->RootDirEntries
);
186 TRACE("TotalSectors: %d\n", Fat32VolumeBootSector
->TotalSectors
);
187 TRACE("MediaDescriptor: 0x%x\n", Fat32VolumeBootSector
->MediaDescriptor
);
188 TRACE("SectorsPerFat: %d\n", Fat32VolumeBootSector
->SectorsPerFat
);
189 TRACE("SectorsPerTrack: %d\n", Fat32VolumeBootSector
->SectorsPerTrack
);
190 TRACE("NumberOfHeads: %d\n", Fat32VolumeBootSector
->NumberOfHeads
);
191 TRACE("HiddenSectors: %d\n", Fat32VolumeBootSector
->HiddenSectors
);
192 TRACE("TotalSectorsBig: %d\n", Fat32VolumeBootSector
->TotalSectorsBig
);
193 TRACE("SectorsPerFatBig: %d\n", Fat32VolumeBootSector
->SectorsPerFatBig
);
194 TRACE("ExtendedFlags: 0x%x\n", Fat32VolumeBootSector
->ExtendedFlags
);
195 TRACE("FileSystemVersion: 0x%x\n", Fat32VolumeBootSector
->FileSystemVersion
);
196 TRACE("RootDirStartCluster: %d\n", Fat32VolumeBootSector
->RootDirStartCluster
);
197 TRACE("FsInfo: %d\n", Fat32VolumeBootSector
->FsInfo
);
198 TRACE("BackupBootSector: %d\n", Fat32VolumeBootSector
->BackupBootSector
);
199 TRACE("Reserved: 0x%x\n", Fat32VolumeBootSector
->Reserved
);
200 TRACE("DriveNumber: 0x%x\n", Fat32VolumeBootSector
->DriveNumber
);
201 TRACE("Reserved1: 0x%x\n", Fat32VolumeBootSector
->Reserved1
);
202 TRACE("BootSignature: 0x%x\n", Fat32VolumeBootSector
->BootSignature
);
203 TRACE("VolumeSerialNumber: 0x%x\n", Fat32VolumeBootSector
->VolumeSerialNumber
);
204 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]);
205 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]);
206 TRACE("BootSectorMagic: 0x%x\n", Fat32VolumeBootSector
->BootSectorMagic
);
210 FatSwapFatBootSector(FatVolumeBootSector
);
211 TRACE("sizeof(FAT_BOOTSECTOR) = 0x%x.\n", sizeof(FAT_BOOTSECTOR
));
213 TRACE("JumpBoot: 0x%x 0x%x 0x%x\n", FatVolumeBootSector
->JumpBoot
[0], FatVolumeBootSector
->JumpBoot
[1], FatVolumeBootSector
->JumpBoot
[2]);
214 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]);
215 TRACE("BytesPerSector: %d\n", FatVolumeBootSector
->BytesPerSector
);
216 TRACE("SectorsPerCluster: %d\n", FatVolumeBootSector
->SectorsPerCluster
);
217 TRACE("ReservedSectors: %d\n", FatVolumeBootSector
->ReservedSectors
);
218 TRACE("NumberOfFats: %d\n", FatVolumeBootSector
->NumberOfFats
);
219 TRACE("RootDirEntries: %d\n", FatVolumeBootSector
->RootDirEntries
);
220 TRACE("TotalSectors: %d\n", FatVolumeBootSector
->TotalSectors
);
221 TRACE("MediaDescriptor: 0x%x\n", FatVolumeBootSector
->MediaDescriptor
);
222 TRACE("SectorsPerFat: %d\n", FatVolumeBootSector
->SectorsPerFat
);
223 TRACE("SectorsPerTrack: %d\n", FatVolumeBootSector
->SectorsPerTrack
);
224 TRACE("NumberOfHeads: %d\n", FatVolumeBootSector
->NumberOfHeads
);
225 TRACE("HiddenSectors: %d\n", FatVolumeBootSector
->HiddenSectors
);
226 TRACE("TotalSectorsBig: %d\n", FatVolumeBootSector
->TotalSectorsBig
);
227 TRACE("DriveNumber: 0x%x\n", FatVolumeBootSector
->DriveNumber
);
228 TRACE("Reserved1: 0x%x\n", FatVolumeBootSector
->Reserved1
);
229 TRACE("BootSignature: 0x%x\n", FatVolumeBootSector
->BootSignature
);
230 TRACE("VolumeSerialNumber: 0x%x\n", FatVolumeBootSector
->VolumeSerialNumber
);
231 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]);
232 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]);
233 TRACE("BootSectorMagic: 0x%x\n", FatVolumeBootSector
->BootSectorMagic
);
237 // Check the boot sector magic
239 if (! ISFATX(Volume
->FatType
) && FatVolumeBootSector
->BootSectorMagic
!= 0xaa55)
241 sprintf(ErrMsg
, "Invalid boot sector magic (expected 0xaa55 found 0x%x)",
242 FatVolumeBootSector
->BootSectorMagic
);
243 FileSystemError(ErrMsg
);
248 // Check the FAT cluster size
249 // We do not support clusters bigger than 64k
251 if ((ISFATX(Volume
->FatType
) && 64 * 1024 < FatXVolumeBootSector
->SectorsPerCluster
* 512) ||
252 (! ISFATX(Volume
->FatType
) && 64 * 1024 < FatVolumeBootSector
->SectorsPerCluster
* FatVolumeBootSector
->BytesPerSector
))
254 FileSystemError("This file system has cluster sizes bigger than 64k.\nFreeLoader does not support this.");
259 // Get the sectors per FAT,
260 // root directory starting sector,
261 // and data sector start
263 if (ISFATX(Volume
->FatType
))
265 Volume
->BytesPerSector
= 512;
266 Volume
->SectorsPerCluster
= SWAPD(FatXVolumeBootSector
->SectorsPerCluster
);
267 Volume
->FatSectorStart
= (0x1000 / Volume
->BytesPerSector
);
268 Volume
->ActiveFatSectorStart
= Volume
->FatSectorStart
;
269 Volume
->NumberOfFats
= 1;
270 FatSize
= (ULONG
)(PartitionSectorCount
/ Volume
->SectorsPerCluster
*
271 (Volume
->FatType
== FATX16
? 2 : 4));
272 Volume
->SectorsPerFat
= ROUND_UP(FatSize
, 0x1000) / Volume
->BytesPerSector
;
274 Volume
->RootDirSectorStart
= Volume
->FatSectorStart
+ Volume
->NumberOfFats
* Volume
->SectorsPerFat
;
275 Volume
->RootDirSectors
= FatXVolumeBootSector
->SectorsPerCluster
;
277 Volume
->DataSectorStart
= Volume
->RootDirSectorStart
+ Volume
->RootDirSectors
;
279 else if (Volume
->FatType
!= FAT32
)
281 Volume
->BytesPerSector
= FatVolumeBootSector
->BytesPerSector
;
282 Volume
->SectorsPerCluster
= FatVolumeBootSector
->SectorsPerCluster
;
283 Volume
->FatSectorStart
= FatVolumeBootSector
->ReservedSectors
;
284 Volume
->ActiveFatSectorStart
= Volume
->FatSectorStart
;
285 Volume
->NumberOfFats
= FatVolumeBootSector
->NumberOfFats
;
286 Volume
->SectorsPerFat
= FatVolumeBootSector
->SectorsPerFat
;
288 Volume
->RootDirSectorStart
= Volume
->FatSectorStart
+ Volume
->NumberOfFats
* Volume
->SectorsPerFat
;
289 Volume
->RootDirSectors
= ((FatVolumeBootSector
->RootDirEntries
* 32) + (Volume
->BytesPerSector
- 1)) / Volume
->BytesPerSector
;
291 Volume
->DataSectorStart
= Volume
->RootDirSectorStart
+ Volume
->RootDirSectors
;
295 Volume
->BytesPerSector
= Fat32VolumeBootSector
->BytesPerSector
;
296 Volume
->SectorsPerCluster
= Fat32VolumeBootSector
->SectorsPerCluster
;
297 Volume
->FatSectorStart
= Fat32VolumeBootSector
->ReservedSectors
;
298 Volume
->ActiveFatSectorStart
= Volume
->FatSectorStart
+
299 ((Fat32VolumeBootSector
->ExtendedFlags
& 0x80) ? ((Fat32VolumeBootSector
->ExtendedFlags
& 0x0f) * Fat32VolumeBootSector
->SectorsPerFatBig
) : 0);
300 Volume
->NumberOfFats
= Fat32VolumeBootSector
->NumberOfFats
;
301 Volume
->SectorsPerFat
= Fat32VolumeBootSector
->SectorsPerFatBig
;
303 Volume
->RootDirStartCluster
= Fat32VolumeBootSector
->RootDirStartCluster
;
304 Volume
->DataSectorStart
= Volume
->FatSectorStart
+ Volume
->NumberOfFats
* Volume
->SectorsPerFat
;
308 // we only work with version 0
310 if (Fat32VolumeBootSector
->FileSystemVersion
!= 0)
312 FileSystemError("FreeLoader is too old to work with this FAT32 filesystem.\nPlease update FreeLoader.");
317 Volume
->FatCacheSize
= min(Volume
->SectorsPerFat
, FAT_MAX_CACHE_SIZE
/ Volume
->BytesPerSector
);
318 TRACE("FAT cache is %d sectors, %d bytes\n", Volume
->FatCacheSize
, Volume
->FatCacheSize
* Volume
->BytesPerSector
);
320 Volume
->FatCache
= FrLdrTempAlloc(Volume
->FatCacheSize
* Volume
->BytesPerSector
, TAG_FAT_CACHE
);
321 if (!Volume
->FatCache
)
323 FileSystemError("Cannot allocate memory for FAT cache");
327 Volume
->FatCacheIndex
= FrLdrTempAlloc(Volume
->FatCacheSize
* sizeof(*Volume
->FatCacheIndex
), TAG_FAT_VOLUME
);
328 if (!Volume
->FatCacheIndex
)
330 FileSystemError("Cannot allocate memory for FAT cache index");
331 FrLdrTempFree(Volume
->FatCache
, TAG_FAT_CACHE
);
335 // read the beginning of the FAT (or the whole one) to cache
336 if (!FatReadVolumeSectors(Volume
, Volume
->ActiveFatSectorStart
, Volume
->FatCacheSize
, Volume
->FatCache
))
338 FileSystemError("Error when reading FAT cache");
339 FrLdrTempFree(Volume
->FatCache
, TAG_FAT_CACHE
);
340 FrLdrTempFree(Volume
->FatCacheIndex
, TAG_FAT_VOLUME
);
344 // fill the index with sector numbers
345 for (i
= 0; i
< Volume
->FatCacheSize
; i
++)
347 Volume
->FatCacheIndex
[i
] = Volume
->ActiveFatSectorStart
+ i
;
353 ULONG
FatDetermineFatType(PFAT_BOOTSECTOR FatBootSector
, ULONGLONG PartitionSectorCount
)
355 ULONG RootDirSectors
;
356 ULONG DataSectorCount
;
359 ULONG CountOfClusters
;
360 PFAT32_BOOTSECTOR Fat32BootSector
= (PFAT32_BOOTSECTOR
)FatBootSector
;
361 PFATX_BOOTSECTOR FatXBootSector
= (PFATX_BOOTSECTOR
)FatBootSector
;
363 if (0 == strncmp(FatXBootSector
->FileSystemType
, "FATX", 4))
365 CountOfClusters
= (ULONG
)(PartitionSectorCount
/ FatXBootSector
->SectorsPerCluster
);
366 if (CountOfClusters
< 65525)
368 /* Volume is FATX16 */
373 /* Volume is FAT32 */
379 RootDirSectors
= ((SWAPW(FatBootSector
->RootDirEntries
) * 32) + (SWAPW(FatBootSector
->BytesPerSector
) - 1)) / SWAPW(FatBootSector
->BytesPerSector
);
380 SectorsPerFat
= SWAPW(FatBootSector
->SectorsPerFat
) ? SWAPW(FatBootSector
->SectorsPerFat
) : SWAPD(Fat32BootSector
->SectorsPerFatBig
);
381 TotalSectors
= SWAPW(FatBootSector
->TotalSectors
) ? SWAPW(FatBootSector
->TotalSectors
) : SWAPD(FatBootSector
->TotalSectorsBig
);
382 DataSectorCount
= TotalSectors
- (SWAPW(FatBootSector
->ReservedSectors
) + (FatBootSector
->NumberOfFats
* SectorsPerFat
) + RootDirSectors
);
385 if (FatBootSector
->SectorsPerCluster
== 0)
388 CountOfClusters
= DataSectorCount
/ FatBootSector
->SectorsPerCluster
;
390 if (CountOfClusters
< 4085)
392 /* Volume is FAT12 */
395 else if (CountOfClusters
< 65525)
397 /* Volume is FAT16 */
402 /* Volume is FAT32 */
408 typedef struct _DIRECTORY_BUFFER
412 ULONG DirectoryStartCluster
;
415 } DIRECTORY_BUFFER
, *PDIRECTORY_BUFFER
;
417 LIST_ENTRY DirectoryBufferListHead
= {&DirectoryBufferListHead
, &DirectoryBufferListHead
};
419 PVOID
FatBufferDirectory(PFAT_VOLUME_INFO Volume
, ULONG DirectoryStartCluster
, ULONG
*DirectorySize
, BOOLEAN RootDirectory
)
421 PDIRECTORY_BUFFER DirectoryBuffer
;
424 TRACE("FatBufferDirectory() DirectoryStartCluster = %d RootDirectory = %s\n", DirectoryStartCluster
, (RootDirectory
? "TRUE" : "FALSE"));
427 * For FAT32, the root directory is nothing special. We can treat it the same
430 if (RootDirectory
&& Volume
->FatType
== FAT32
)
432 DirectoryStartCluster
= Volume
->RootDirStartCluster
;
433 RootDirectory
= FALSE
;
436 /* Search the list for a match */
437 for (Entry
= DirectoryBufferListHead
.Flink
;
438 Entry
!= &DirectoryBufferListHead
;
439 Entry
= Entry
->Flink
)
441 DirectoryBuffer
= CONTAINING_RECORD(Entry
, DIRECTORY_BUFFER
, Link
);
443 /* Check if it matches */
444 if ((DirectoryBuffer
->Volume
== Volume
) &&
445 (DirectoryBuffer
->DirectoryStartCluster
== DirectoryStartCluster
))
447 TRACE("Found cached buffer\n");
448 *DirectorySize
= DirectoryBuffer
->DirectorySize
;
449 return DirectoryBuffer
->Data
;
454 // Calculate the size of the directory
458 *DirectorySize
= Volume
->RootDirSectors
* Volume
->BytesPerSector
;
462 *DirectorySize
= FatCountClustersInChain(Volume
, DirectoryStartCluster
) * Volume
->SectorsPerCluster
* Volume
->BytesPerSector
;
466 // Attempt to allocate memory for directory buffer
468 TRACE("Trying to allocate (DirectorySize) %d bytes.\n", *DirectorySize
);
469 DirectoryBuffer
= FrLdrTempAlloc(*DirectorySize
+ sizeof(DIRECTORY_BUFFER
),
472 if (DirectoryBuffer
== NULL
)
478 // Now read directory contents into DirectoryBuffer
482 if (!FatReadVolumeSectors(Volume
, Volume
->RootDirSectorStart
, Volume
->RootDirSectors
, DirectoryBuffer
->Data
))
484 FrLdrTempFree(DirectoryBuffer
, TAG_FAT_BUFFER
);
490 if (!FatReadClusterChain(Volume
, DirectoryStartCluster
, 0xFFFFFFFF, DirectoryBuffer
->Data
, NULL
))
492 FrLdrTempFree(DirectoryBuffer
, TAG_FAT_BUFFER
);
497 /* Enqueue it in the list */
498 DirectoryBuffer
->Volume
= Volume
;
499 DirectoryBuffer
->DirectoryStartCluster
= DirectoryStartCluster
;
500 DirectoryBuffer
->DirectorySize
= *DirectorySize
;
501 InsertTailList(&DirectoryBufferListHead
, &DirectoryBuffer
->Link
);
503 return DirectoryBuffer
->Data
;
506 BOOLEAN
FatSearchDirectoryBufferForFile(PFAT_VOLUME_INFO Volume
, PVOID DirectoryBuffer
, ULONG DirectorySize
, PCHAR FileName
, PFAT_FILE_INFO FatFileInfoPointer
)
510 CHAR LfnNameBuffer
[265];
511 CHAR ShortNameBuffer
[20];
513 DIRENTRY OurDirEntry
;
514 LFN_DIRENTRY OurLfnDirEntry
;
515 PDIRENTRY DirEntry
= &OurDirEntry
;
516 PLFN_DIRENTRY LfnDirEntry
= &OurLfnDirEntry
;
518 EntryCount
= DirectorySize
/ sizeof(DIRENTRY
);
520 TRACE("FatSearchDirectoryBufferForFile() DirectoryBuffer = 0x%x EntryCount = %d FileName = %s\n", DirectoryBuffer
, EntryCount
, FileName
);
522 memset(ShortNameBuffer
, 0, 13 * sizeof(CHAR
));
523 memset(LfnNameBuffer
, 0, 261 * sizeof(CHAR
));
525 for (CurrentEntry
=0; CurrentEntry
<EntryCount
; CurrentEntry
++, DirectoryBuffer
= ((PDIRENTRY
)DirectoryBuffer
)+1)
527 OurLfnDirEntry
= *((PLFN_DIRENTRY
) DirectoryBuffer
);
528 FatSwapLFNDirEntry(LfnDirEntry
);
529 OurDirEntry
= *((PDIRENTRY
) DirectoryBuffer
);
530 FatSwapDirEntry(DirEntry
);
532 //TRACE("Dumping directory entry %d:\n", CurrentEntry);
533 //DbgDumpBuffer(DPRINT_FILESYSTEM, DirEntry, sizeof(DIRENTRY));
536 // Check if this is the last file in the directory
537 // If DirEntry[0] == 0x00 then that means all the
538 // entries after this one are unused. If this is the
539 // last entry then we didn't find the file in this directory.
541 if (DirEntry
->FileName
[0] == '\0')
547 // Check if this is a deleted entry or not
549 if (DirEntry
->FileName
[0] == '\xE5')
551 memset(ShortNameBuffer
, 0, 13 * sizeof(CHAR
));
552 memset(LfnNameBuffer
, 0, 261 * sizeof(CHAR
));
557 // Check if this is a LFN entry
558 // If so it needs special handling
560 if (DirEntry
->Attr
== ATTR_LONG_NAME
)
563 // Check to see if this is a deleted LFN entry, if so continue
565 if (LfnDirEntry
->SequenceNumber
& 0x80)
571 // Mask off high two bits of sequence number
572 // and make the sequence number zero-based
574 LfnDirEntry
->SequenceNumber
&= 0x3F;
575 LfnDirEntry
->SequenceNumber
--;
578 // Get all 13 LFN entry characters
580 if (LfnDirEntry
->Name0_4
[0] != 0xFFFF)
582 LfnNameBuffer
[0 + (LfnDirEntry
->SequenceNumber
* 13)] = (UCHAR
)LfnDirEntry
->Name0_4
[0];
584 if (LfnDirEntry
->Name0_4
[1] != 0xFFFF)
586 LfnNameBuffer
[1 + (LfnDirEntry
->SequenceNumber
* 13)] = (UCHAR
)LfnDirEntry
->Name0_4
[1];
588 if (LfnDirEntry
->Name0_4
[2] != 0xFFFF)
590 LfnNameBuffer
[2 + (LfnDirEntry
->SequenceNumber
* 13)] = (UCHAR
)LfnDirEntry
->Name0_4
[2];
592 if (LfnDirEntry
->Name0_4
[3] != 0xFFFF)
594 LfnNameBuffer
[3 + (LfnDirEntry
->SequenceNumber
* 13)] = (UCHAR
)LfnDirEntry
->Name0_4
[3];
596 if (LfnDirEntry
->Name0_4
[4] != 0xFFFF)
598 LfnNameBuffer
[4 + (LfnDirEntry
->SequenceNumber
* 13)] = (UCHAR
)LfnDirEntry
->Name0_4
[4];
600 if (LfnDirEntry
->Name5_10
[0] != 0xFFFF)
602 LfnNameBuffer
[5 + (LfnDirEntry
->SequenceNumber
* 13)] = (UCHAR
)LfnDirEntry
->Name5_10
[0];
604 if (LfnDirEntry
->Name5_10
[1] != 0xFFFF)
606 LfnNameBuffer
[6 + (LfnDirEntry
->SequenceNumber
* 13)] = (UCHAR
)LfnDirEntry
->Name5_10
[1];
608 if (LfnDirEntry
->Name5_10
[2] != 0xFFFF)
610 LfnNameBuffer
[7 + (LfnDirEntry
->SequenceNumber
* 13)] = (UCHAR
)LfnDirEntry
->Name5_10
[2];
612 if (LfnDirEntry
->Name5_10
[3] != 0xFFFF)
614 LfnNameBuffer
[8 + (LfnDirEntry
->SequenceNumber
* 13)] = (UCHAR
)LfnDirEntry
->Name5_10
[3];
616 if (LfnDirEntry
->Name5_10
[4] != 0xFFFF)
618 LfnNameBuffer
[9 + (LfnDirEntry
->SequenceNumber
* 13)] = (UCHAR
)LfnDirEntry
->Name5_10
[4];
620 if (LfnDirEntry
->Name5_10
[5] != 0xFFFF)
622 LfnNameBuffer
[10 + (LfnDirEntry
->SequenceNumber
* 13)] = (UCHAR
)LfnDirEntry
->Name5_10
[5];
624 if (LfnDirEntry
->Name11_12
[0] != 0xFFFF)
626 LfnNameBuffer
[11 + (LfnDirEntry
->SequenceNumber
* 13)] = (UCHAR
)LfnDirEntry
->Name11_12
[0];
628 if (LfnDirEntry
->Name11_12
[1] != 0xFFFF)
630 LfnNameBuffer
[12 + (LfnDirEntry
->SequenceNumber
* 13)] = (UCHAR
)LfnDirEntry
->Name11_12
[1];
633 //TRACE("Dumping long name buffer:\n");
634 //DbgDumpBuffer(DPRINT_FILESYSTEM, LfnNameBuffer, 260);
640 // Check for the volume label attribute
641 // and skip over this entry if found
643 if (DirEntry
->Attr
& ATTR_VOLUMENAME
)
645 memset(ShortNameBuffer
, 0, 13 * sizeof(UCHAR
));
646 memset(LfnNameBuffer
, 0, 261 * sizeof(UCHAR
));
651 // If we get here then we've found a short file name
652 // entry and LfnNameBuffer contains the long file
653 // name or zeroes. All we have to do now is see if the
654 // file name matches either the short or long file name
655 // and fill in the FAT_FILE_INFO structure if it does
656 // or zero our buffers and continue looking.
660 // Get short file name
662 FatParseShortFileName(ShortNameBuffer
, DirEntry
);
664 //TRACE("Entry: %d LFN = %s\n", CurrentEntry, LfnNameBuffer);
665 //TRACE("Entry: %d DOS name = %s\n", CurrentEntry, ShortNameBuffer);
668 // See if the file name matches either the short or long name
670 if (((strlen(FileName
) == strlen(LfnNameBuffer
)) && (_stricmp(FileName
, LfnNameBuffer
) == 0)) ||
671 ((strlen(FileName
) == strlen(ShortNameBuffer
)) && (_stricmp(FileName
, ShortNameBuffer
) == 0))) {
673 // We found the entry, now fill in the FAT_FILE_INFO struct
675 FatFileInfoPointer
->Attributes
= DirEntry
->Attr
;
676 FatFileInfoPointer
->FileSize
= DirEntry
->Size
;
677 FatFileInfoPointer
->FilePointer
= 0;
678 StartCluster
= ((ULONG
)DirEntry
->ClusterHigh
<< 16) + DirEntry
->ClusterLow
;
679 FatFileInfoPointer
->CurrentCluster
= StartCluster
;
680 FatFileInfoPointer
->StartCluster
= StartCluster
;
682 TRACE("MSDOS Directory Entry:\n");
683 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]);
684 TRACE("Attr = 0x%x\n", DirEntry
->Attr
);
685 TRACE("ReservedNT = 0x%x\n", DirEntry
->ReservedNT
);
686 TRACE("TimeInTenths = %d\n", DirEntry
->TimeInTenths
);
687 TRACE("CreateTime = %d\n", DirEntry
->CreateTime
);
688 TRACE("CreateDate = %d\n", DirEntry
->CreateDate
);
689 TRACE("LastAccessDate = %d\n", DirEntry
->LastAccessDate
);
690 TRACE("ClusterHigh = 0x%x\n", DirEntry
->ClusterHigh
);
691 TRACE("Time = %d\n", DirEntry
->Time
);
692 TRACE("Date = %d\n", DirEntry
->Date
);
693 TRACE("ClusterLow = 0x%x\n", DirEntry
->ClusterLow
);
694 TRACE("Size = %d\n", DirEntry
->Size
);
695 TRACE("StartCluster = 0x%x\n", StartCluster
);
701 // Nope, no match - zero buffers and continue looking
703 memset(ShortNameBuffer
, 0, 13 * sizeof(UCHAR
));
704 memset(LfnNameBuffer
, 0, 261 * sizeof(UCHAR
));
711 static BOOLEAN
FatXSearchDirectoryBufferForFile(PFAT_VOLUME_INFO Volume
, PVOID DirectoryBuffer
, ULONG DirectorySize
, PCHAR FileName
, PFAT_FILE_INFO FatFileInfoPointer
)
716 FATX_DIRENTRY OurDirEntry
;
717 PFATX_DIRENTRY DirEntry
= &OurDirEntry
;
719 EntryCount
= DirectorySize
/ sizeof(FATX_DIRENTRY
);
721 TRACE("FatXSearchDirectoryBufferForFile() DirectoryBuffer = 0x%x EntryCount = %d FileName = %s\n", DirectoryBuffer
, EntryCount
, FileName
);
723 FileNameLen
= strlen(FileName
);
725 for (CurrentEntry
= 0; CurrentEntry
< EntryCount
; CurrentEntry
++, DirectoryBuffer
= ((PFATX_DIRENTRY
)DirectoryBuffer
)+1)
727 OurDirEntry
= *(PFATX_DIRENTRY
) DirectoryBuffer
;
728 FatSwapFatXDirEntry(&OurDirEntry
);
729 if (0xff == DirEntry
->FileNameSize
)
733 if (0xe5 == DirEntry
->FileNameSize
)
737 if (FileNameLen
== DirEntry
->FileNameSize
&&
738 0 == _strnicmp(FileName
, DirEntry
->FileName
, FileNameLen
))
741 * We found the entry, now fill in the FAT_FILE_INFO struct
743 FatFileInfoPointer
->Attributes
= DirEntry
->Attr
;
744 FatFileInfoPointer
->FileSize
= DirEntry
->Size
;
745 FatFileInfoPointer
->FilePointer
= 0;
746 FatFileInfoPointer
->CurrentCluster
= DirEntry
->StartCluster
;
747 FatFileInfoPointer
->StartCluster
= DirEntry
->StartCluster
;
749 TRACE("FATX Directory Entry:\n");
750 TRACE("FileNameSize = %d\n", DirEntry
->FileNameSize
);
751 TRACE("Attr = 0x%x\n", DirEntry
->Attr
);
752 TRACE("StartCluster = 0x%x\n", DirEntry
->StartCluster
);
753 TRACE("Size = %d\n", DirEntry
->Size
);
754 TRACE("Time = %d\n", DirEntry
->Time
);
755 TRACE("Date = %d\n", DirEntry
->Date
);
756 TRACE("CreateTime = %d\n", DirEntry
->CreateTime
);
757 TRACE("CreateDate = %d\n", DirEntry
->CreateDate
);
758 TRACE("LastAccessTime = %d\n", DirEntry
->LastAccessTime
);
759 TRACE("LastAccessDate = %d\n", DirEntry
->LastAccessDate
);
770 * This function searches the file system for the
771 * specified filename and fills in an FAT_FILE_INFO structure
772 * with info describing the file, etc. returns ARC error code
774 ARC_STATUS
FatLookupFile(PFAT_VOLUME_INFO Volume
, PCSTR FileName
, PFAT_FILE_INFO FatFileInfoPointer
)
777 ULONG NumberOfPathParts
;
779 PVOID DirectoryBuffer
;
780 ULONG DirectoryStartCluster
= 0;
782 FAT_FILE_INFO FatFileInfo
;
784 TRACE("FatLookupFile() FileName = %s\n", FileName
);
786 memset(FatFileInfoPointer
, 0, sizeof(FAT_FILE_INFO
));
789 // Figure out how many sub-directories we are nested in
791 NumberOfPathParts
= FsGetNumPathParts(FileName
);
794 // Loop once for each part
796 for (i
=0; i
<NumberOfPathParts
; i
++)
799 // Get first path part
801 FsGetFirstNameFromPath(PathPart
, FileName
);
804 // Advance to the next part of the path
806 for (; (*FileName
!= '\\') && (*FileName
!= '/') && (*FileName
!= '\0'); FileName
++)
812 // Buffer the directory contents
814 DirectoryBuffer
= FatBufferDirectory(Volume
, DirectoryStartCluster
, &DirectorySize
, (i
== 0) );
815 if (DirectoryBuffer
== NULL
)
821 // Search for file name in directory
823 if (ISFATX(Volume
->FatType
))
825 if (!FatXSearchDirectoryBufferForFile(Volume
, DirectoryBuffer
, DirectorySize
, PathPart
, &FatFileInfo
))
832 if (!FatSearchDirectoryBufferForFile(Volume
, DirectoryBuffer
, DirectorySize
, PathPart
, &FatFileInfo
))
839 // If we have another sub-directory to go then
840 // grab the start cluster and free the fat chain array
842 if ((i
+1) < NumberOfPathParts
)
845 // Check if current entry is a directory
847 if (!(FatFileInfo
.Attributes
& ATTR_DIRECTORY
))
851 DirectoryStartCluster
= FatFileInfo
.StartCluster
;
855 memcpy(FatFileInfoPointer
, &FatFileInfo
, sizeof(FAT_FILE_INFO
));
862 * This function parses a directory entry name which
863 * is in the form of "FILE EXT" and puts it in Buffer
864 * in the form of "file.ext"
866 void FatParseShortFileName(PCHAR Buffer
, PDIRENTRY DirEntry
)
871 RtlZeroMemory(Buffer
, 13);
874 // Fixup first character
876 if (DirEntry
->FileName
[0] == 0x05)
878 DirEntry
->FileName
[0] = 0xE5;
886 if (DirEntry
->FileName
[Idx
] == ' ')
891 Buffer
[Idx
] = DirEntry
->FileName
[Idx
];
898 if ((DirEntry
->FileName
[8] != ' '))
901 Buffer
[Idx
++] = (DirEntry
->FileName
[8] == ' ') ? '\0' : DirEntry
->FileName
[8];
902 Buffer
[Idx
++] = (DirEntry
->FileName
[9] == ' ') ? '\0' : DirEntry
->FileName
[9];
903 Buffer
[Idx
++] = (DirEntry
->FileName
[10] == ' ') ? '\0' : DirEntry
->FileName
[10];
906 //TRACE("FatParseShortFileName() ShortName = %s\n", Buffer);
910 * @brief Reads 1-4 sectors from FAT using the cache
913 PUCHAR
FatGetFatSector(PFAT_VOLUME_INFO Volume
, UINT32 FatSectorNumber
)
915 UINT32 SectorNumAbsolute
= Volume
->ActiveFatSectorStart
+ FatSectorNumber
;
916 UINT32 CacheIndex
= FatSectorNumber
% Volume
->FatCacheSize
;
918 ASSERT(FatSectorNumber
< Volume
->SectorsPerFat
);
921 if (Volume
->FatCacheIndex
[CacheIndex
] != SectorNumAbsolute
)
923 UINT32 SectorsToRead
= min(Volume
->FatCacheSize
- CacheIndex
, min(Volume
->SectorsPerFat
- SectorNumAbsolute
, 4));
926 if (!FatReadVolumeSectors(Volume
, SectorNumAbsolute
, SectorsToRead
, &Volume
->FatCache
[CacheIndex
* Volume
->BytesPerSector
]))
931 for (i
= 0; i
< SectorsToRead
; i
++)
933 Volume
->FatCacheIndex
[CacheIndex
+ i
] = SectorNumAbsolute
+ i
;
936 TRACE("FAT cache miss: read sector 0x%x from disk\n", SectorNumAbsolute
);
940 TRACE("FAT cache hit: sector 0x%x present\n", SectorNumAbsolute
);
943 return &Volume
->FatCache
[CacheIndex
* Volume
->BytesPerSector
];
948 * returns the Fat entry for a given cluster number
951 BOOLEAN
FatGetFatEntry(PFAT_VOLUME_INFO Volume
, UINT32 Cluster
, PUINT32 ClusterPointer
)
953 UINT32 FatOffset
, ThisFatSecNum
, ThisFatEntOffset
, fat
;
956 TRACE("FatGetFatEntry() Retrieving FAT entry for cluster %d.\n", Cluster
);
958 switch(Volume
->FatType
)
962 FatOffset
= Cluster
+ (Cluster
/ 2);
963 ThisFatSecNum
= FatOffset
/ Volume
->BytesPerSector
;
964 ThisFatEntOffset
= (FatOffset
% Volume
->BytesPerSector
);
966 TRACE("FatOffset: %d\n", FatOffset
);
967 TRACE("ThisFatSecNum: %d\n", ThisFatSecNum
);
968 TRACE("ThisFatEntOffset: %d\n", ThisFatEntOffset
);
970 // The cluster pointer can span within two sectors, but the FatGetFatSector function
971 // reads 4 sectors most times, except when we are at the edge of FAT cache
972 // and/or FAT region on the disk. For FAT12 the whole FAT would be cached so
973 // there will be no situation when the first sector is at the end of the cache
974 // and the next one is in the beginning
976 ReadBuffer
= FatGetFatSector(Volume
, ThisFatSecNum
);
982 fat
= *((USHORT
*) (ReadBuffer
+ ThisFatEntOffset
));
984 if (Cluster
& 0x0001)
985 fat
= fat
>> 4; /* Cluster number is ODD */
987 fat
= fat
& 0x0FFF; /* Cluster number is EVEN */
994 FatOffset
= (Cluster
* 2);
995 ThisFatSecNum
= FatOffset
/ Volume
->BytesPerSector
;
996 ThisFatEntOffset
= (FatOffset
% Volume
->BytesPerSector
);
998 ReadBuffer
= FatGetFatSector(Volume
, ThisFatSecNum
);
1004 fat
= *((USHORT
*) (ReadBuffer
+ ThisFatEntOffset
));
1012 FatOffset
= (Cluster
* 4);
1013 ThisFatSecNum
= FatOffset
/ Volume
->BytesPerSector
;
1014 ThisFatEntOffset
= (FatOffset
% Volume
->BytesPerSector
);
1016 ReadBuffer
= FatGetFatSector(Volume
, ThisFatSecNum
);
1022 // Get the fat entry
1023 fat
= (*((ULONG
*) (ReadBuffer
+ ThisFatEntOffset
))) & 0x0FFFFFFF;
1029 ERR("Unknown FAT type %d\n", Volume
->FatType
);
1033 TRACE("FAT entry is 0x%x.\n", fat
);
1035 *ClusterPointer
= fat
;
1041 ULONG
FatCountClustersInChain(PFAT_VOLUME_INFO Volume
, UINT32 StartCluster
)
1043 ULONG ClusterCount
= 0;
1045 TRACE("FatCountClustersInChain() StartCluster = %d\n", StartCluster
);
1050 // If end of chain then break out of our cluster counting loop
1052 if (FAT_IS_END_CLUSTER(StartCluster
))
1065 if (!FatGetFatEntry(Volume
, StartCluster
, &StartCluster
))
1071 TRACE("FatCountClustersInChain() ClusterCount = %d\n", ClusterCount
);
1073 return ClusterCount
;
1077 BOOLEAN
FatReadAdjacentClusters(
1078 PFAT_VOLUME_INFO Volume
,
1079 UINT32 StartClusterNumber
,
1082 PUINT32 ClustersRead
,
1083 PUINT32 LastClusterNumber
)
1085 UINT32 NextClusterNumber
;
1086 UINT32 ClustersToRead
= 1;
1087 UINT32 PrevClusterNumber
= StartClusterNumber
;
1088 UINT32 ClusterStartSector
= ((PrevClusterNumber
- 2) * Volume
->SectorsPerCluster
) + Volume
->DataSectorStart
;
1091 *LastClusterNumber
= 0;
1093 if (!FatGetFatEntry(Volume
, StartClusterNumber
, &NextClusterNumber
))
1098 // getting the number of adjacent clusters
1099 while (!FAT_IS_END_CLUSTER(NextClusterNumber
) && ClustersToRead
< MaxClusters
&& (NextClusterNumber
== PrevClusterNumber
+ 1))
1102 PrevClusterNumber
= NextClusterNumber
;
1103 if (!FatGetFatEntry(Volume
, PrevClusterNumber
, &NextClusterNumber
))
1109 if (!FatReadVolumeSectors(Volume
, ClusterStartSector
, ClustersToRead
* Volume
->SectorsPerCluster
, Buffer
))
1114 *ClustersRead
= ClustersToRead
;
1115 *LastClusterNumber
= NextClusterNumber
;
1117 return !FAT_IS_END_CLUSTER(NextClusterNumber
) && ClustersToRead
< MaxClusters
;
1121 * FatReadClusterChain()
1122 * Reads the specified clusters into memory
1125 BOOLEAN
FatReadClusterChain(PFAT_VOLUME_INFO Volume
, UINT32 StartClusterNumber
, UINT32 NumberOfClusters
, PVOID Buffer
, PUINT32 LastClusterNumber
)
1127 UINT32 ClustersRead
, NextClusterNumber
, ClustersLeft
= NumberOfClusters
;
1129 TRACE("FatReadClusterChain() StartClusterNumber = %d NumberOfClusters = %d Buffer = 0x%x\n", StartClusterNumber
, NumberOfClusters
, Buffer
);
1131 ASSERT(NumberOfClusters
> 0);
1133 while (FatReadAdjacentClusters(Volume
, StartClusterNumber
, ClustersLeft
, Buffer
, &ClustersRead
, &NextClusterNumber
))
1135 ClustersLeft
-= ClustersRead
;
1136 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ (ClustersRead
* Volume
->SectorsPerCluster
* Volume
->BytesPerSector
));
1137 StartClusterNumber
= NextClusterNumber
;
1140 if (LastClusterNumber
)
1142 *LastClusterNumber
= NextClusterNumber
;
1145 return (ClustersRead
> 0);
1149 * FatReadPartialCluster()
1150 * Reads part of a cluster into memory
1152 BOOLEAN
FatReadPartialCluster(PFAT_VOLUME_INFO Volume
, ULONG ClusterNumber
, ULONG StartingOffset
, ULONG Length
, PVOID Buffer
)
1154 ULONG ClusterStartSector
;
1155 ULONG SectorOffset
, ReadSize
, SectorCount
;
1157 BOOLEAN Success
= FALSE
;
1159 //TRACE("FatReadPartialCluster() ClusterNumber = %d StartingOffset = %d Length = %d Buffer = 0x%x\n", ClusterNumber, StartingOffset, Length, Buffer);
1161 ClusterStartSector
= ((ClusterNumber
- 2) * Volume
->SectorsPerCluster
) + Volume
->DataSectorStart
;
1163 // This is the offset of the data in sectors
1164 SectorOffset
= (StartingOffset
/ Volume
->BytesPerSector
);
1165 StartingOffset
%= Volume
->BytesPerSector
;
1167 // Calculate how many sectors we need to read
1168 SectorCount
= (StartingOffset
+ Length
+ Volume
->BytesPerSector
- 1) / Volume
->BytesPerSector
;
1170 // Calculate rounded up read size
1171 ReadSize
= SectorCount
* Volume
->BytesPerSector
;
1173 ReadBuffer
= FrLdrTempAlloc(ReadSize
, TAG_FAT_BUFFER
);
1179 if (FatReadVolumeSectors(Volume
, ClusterStartSector
+ SectorOffset
, SectorCount
, ReadBuffer
))
1181 memcpy(Buffer
, ReadBuffer
+ StartingOffset
, Length
);
1185 FrLdrTempFree(ReadBuffer
, TAG_FAT_BUFFER
);
1192 * Reads BytesToRead from open file and
1193 * returns the number of bytes read in BytesRead
1196 BOOLEAN
FatReadFile(PFAT_FILE_INFO FatFileInfo
, ULONG BytesToRead
, ULONG
* BytesRead
, PVOID Buffer
)
1198 PFAT_VOLUME_INFO Volume
= FatFileInfo
->Volume
;
1199 UINT32 NextClusterNumber
, BytesPerCluster
;
1201 TRACE("FatReadFile() BytesToRead = %d Buffer = 0x%x\n", BytesToRead
, Buffer
);
1203 if (BytesRead
!= NULL
)
1209 // If they are trying to read past the
1210 // end of the file then return success
1211 // with BytesRead == 0
1213 if (FatFileInfo
->FilePointer
>= FatFileInfo
->FileSize
)
1219 // If they are trying to read more than there is to read
1220 // then adjust the amount to read
1222 if ((FatFileInfo
->FilePointer
+ BytesToRead
) > FatFileInfo
->FileSize
)
1224 BytesToRead
= (FatFileInfo
->FileSize
- FatFileInfo
->FilePointer
);
1228 // Ok, now we have to perform at most 3 calculations
1229 // I'll draw you a picture (using nifty ASCII art):
1231 // CurrentFilePointer -+
1233 // +----------------+
1235 // +-----------+-----------+-----------+-----------+
1236 // | Cluster 1 | Cluster 2 | Cluster 3 | Cluster 4 |
1237 // +-----------+-----------+-----------+-----------+
1239 // +---------------+--------------------+
1241 // BytesToRead -------+
1243 // 1 - The first calculation (and read) will align
1244 // the file pointer with the next cluster.
1245 // boundary (if we are supposed to read that much)
1246 // 2 - The next calculation (and read) will read
1247 // in all the full clusters that the requested
1248 // amount of data would cover (in this case
1250 // 3 - The last calculation (and read) would read
1251 // in the remainder of the data requested out of
1252 // the last cluster.
1255 BytesPerCluster
= Volume
->SectorsPerCluster
* Volume
->BytesPerSector
;
1258 // Only do the first read if we
1259 // aren't aligned on a cluster boundary
1261 if (FatFileInfo
->FilePointer
% BytesPerCluster
)
1264 // Do the math for our first read
1266 UINT32 OffsetInCluster
= FatFileInfo
->FilePointer
% BytesPerCluster
;
1267 UINT32 LengthInCluster
= min(BytesToRead
, BytesPerCluster
- OffsetInCluster
);
1269 ASSERT(LengthInCluster
<= BytesPerCluster
&& LengthInCluster
> 0);
1272 // Now do the read and update BytesRead, BytesToRead, FilePointer, & Buffer
1274 if (!FatReadPartialCluster(Volume
, FatFileInfo
->CurrentCluster
, OffsetInCluster
, LengthInCluster
, Buffer
))
1278 if (BytesRead
!= NULL
)
1280 *BytesRead
+= LengthInCluster
;
1282 BytesToRead
-= LengthInCluster
;
1283 FatFileInfo
->FilePointer
+= LengthInCluster
;
1284 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ LengthInCluster
);
1286 // get the next cluster if needed
1287 if ((LengthInCluster
+ OffsetInCluster
) == BytesPerCluster
)
1289 if (!FatGetFatEntry(Volume
, FatFileInfo
->CurrentCluster
, &NextClusterNumber
))
1294 FatFileInfo
->CurrentCluster
= NextClusterNumber
;
1295 TRACE("FatReadFile() FatFileInfo->CurrentCluster = 0x%x\n", FatFileInfo
->CurrentCluster
);
1300 // Do the math for our second read (if any data left)
1302 if (BytesToRead
> 0)
1305 // Determine how many full clusters we need to read
1307 UINT32 NumberOfClusters
= BytesToRead
/ BytesPerCluster
;
1309 TRACE("Going to read: %u clusters\n", NumberOfClusters
);
1311 if (NumberOfClusters
> 0)
1313 UINT32 BytesReadHere
= NumberOfClusters
* BytesPerCluster
;
1315 ASSERT(!FAT_IS_END_CLUSTER(FatFileInfo
->CurrentCluster
));
1317 if (!FatReadClusterChain(Volume
, FatFileInfo
->CurrentCluster
, NumberOfClusters
, Buffer
, &NextClusterNumber
))
1322 if (BytesRead
!= NULL
)
1324 *BytesRead
+= BytesReadHere
;
1326 BytesToRead
-= BytesReadHere
;
1327 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ BytesReadHere
);
1329 ASSERT(!FAT_IS_END_CLUSTER(NextClusterNumber
) || BytesToRead
== 0);
1331 FatFileInfo
->FilePointer
+= BytesReadHere
;
1332 FatFileInfo
->CurrentCluster
= NextClusterNumber
;
1333 TRACE("FatReadFile() FatFileInfo->CurrentCluster = 0x%x\n", FatFileInfo
->CurrentCluster
);
1338 // Do the math for our third read (if any data left)
1340 if (BytesToRead
> 0)
1342 ASSERT(!FAT_IS_END_CLUSTER(FatFileInfo
->CurrentCluster
));
1345 // Now do the read and update BytesRead, BytesToRead, FilePointer, & Buffer
1347 if (!FatReadPartialCluster(Volume
, FatFileInfo
->CurrentCluster
, 0, BytesToRead
, Buffer
))
1351 if (BytesRead
!= NULL
)
1353 *BytesRead
+= BytesToRead
;
1355 FatFileInfo
->FilePointer
+= BytesToRead
;
1356 BytesToRead
-= BytesToRead
;
1357 Buffer
= (PVOID
)((ULONG_PTR
)Buffer
+ BytesToRead
);
1363 BOOLEAN
FatReadVolumeSectors(PFAT_VOLUME_INFO Volume
, ULONG SectorNumber
, ULONG SectorCount
, PVOID Buffer
)
1365 LARGE_INTEGER Position
;
1369 //TRACE("FatReadVolumeSectors(): SectorNumber %d, SectorCount %d, Buffer %p\n",
1370 // SectorNumber, SectorCount, Buffer);
1373 // Seek to right position
1375 Position
.QuadPart
= (ULONGLONG
)SectorNumber
* 512;
1376 Status
= ArcSeek(Volume
->DeviceId
, &Position
, SeekAbsolute
);
1377 if (Status
!= ESUCCESS
)
1379 TRACE("FatReadVolumeSectors() Failed to seek\n");
1386 Status
= ArcRead(Volume
->DeviceId
, Buffer
, SectorCount
* 512, &Count
);
1387 if (Status
!= ESUCCESS
|| Count
!= SectorCount
* 512)
1389 TRACE("FatReadVolumeSectors() Failed to read\n");
1397 ARC_STATUS
FatClose(ULONG FileId
)
1399 PFAT_FILE_INFO FileHandle
= FsGetDeviceSpecific(FileId
);
1401 FrLdrTempFree(FileHandle
, TAG_FAT_FILE
);
1406 ARC_STATUS
FatGetFileInformation(ULONG FileId
, FILEINFORMATION
* Information
)
1408 PFAT_FILE_INFO FileHandle
= FsGetDeviceSpecific(FileId
);
1410 RtlZeroMemory(Information
, sizeof(*Information
));
1411 Information
->EndingAddress
.LowPart
= FileHandle
->FileSize
;
1412 Information
->CurrentAddress
.LowPart
= FileHandle
->FilePointer
;
1414 TRACE("FatGetFileInformation(%lu) -> FileSize = %lu, FilePointer = 0x%lx\n",
1415 FileId
, Information
->EndingAddress
.LowPart
, Information
->CurrentAddress
.LowPart
);
1420 ARC_STATUS
FatOpen(CHAR
* Path
, OPENMODE OpenMode
, ULONG
* FileId
)
1422 PFAT_VOLUME_INFO FatVolume
;
1423 FAT_FILE_INFO TempFileInfo
;
1424 PFAT_FILE_INFO FileHandle
;
1426 BOOLEAN IsDirectory
;
1429 if (OpenMode
!= OpenReadOnly
&& OpenMode
!= OpenDirectory
)
1432 DeviceId
= FsGetDeviceId(*FileId
);
1433 FatVolume
= FatVolumes
[DeviceId
];
1435 TRACE("FatOpen() FileName = %s\n", Path
);
1437 RtlZeroMemory(&TempFileInfo
, sizeof(TempFileInfo
));
1438 Status
= FatLookupFile(FatVolume
, Path
, &TempFileInfo
);
1439 if (Status
!= ESUCCESS
)
1443 // Check if caller opened what he expected (dir vs file)
1445 IsDirectory
= (TempFileInfo
.Attributes
& ATTR_DIRECTORY
) != 0;
1446 if (IsDirectory
&& OpenMode
!= OpenDirectory
)
1448 else if (!IsDirectory
&& OpenMode
!= OpenReadOnly
)
1451 FileHandle
= FrLdrTempAlloc(sizeof(FAT_FILE_INFO
), TAG_FAT_FILE
);
1455 RtlCopyMemory(FileHandle
, &TempFileInfo
, sizeof(FAT_FILE_INFO
));
1456 FileHandle
->Volume
= FatVolume
;
1458 FsSetDeviceSpecific(*FileId
, FileHandle
);
1462 ARC_STATUS
FatRead(ULONG FileId
, VOID
* Buffer
, ULONG N
, ULONG
* Count
)
1464 PFAT_FILE_INFO FileHandle
= FsGetDeviceSpecific(FileId
);
1468 // Call old read method
1470 Success
= FatReadFile(FileHandle
, N
, Count
, Buffer
);
1473 // Check for success
1481 ARC_STATUS
FatSeek(ULONG FileId
, LARGE_INTEGER
* Position
, SEEKMODE SeekMode
)
1483 PFAT_FILE_INFO FileHandle
= FsGetDeviceSpecific(FileId
);
1484 PFAT_VOLUME_INFO Volume
= FileHandle
->Volume
;
1485 LARGE_INTEGER NewPosition
= *Position
;
1492 NewPosition
.QuadPart
+= (UINT64
)FileHandle
->FilePointer
;
1499 if (NewPosition
.HighPart
!= 0)
1501 if (NewPosition
.LowPart
>= FileHandle
->FileSize
)
1504 TRACE("FatSeek() NewPosition = %u, OldPointer = %u, SeekMode = %d\n", NewPosition
.LowPart
, FileHandle
->FilePointer
, SeekMode
);
1507 UINT32 OldClusterIdx
= FileHandle
->FilePointer
/ (Volume
->SectorsPerCluster
* Volume
->BytesPerSector
);
1508 UINT32 NewClusterIdx
= NewPosition
.LowPart
/ (Volume
->SectorsPerCluster
* Volume
->BytesPerSector
);
1510 TRACE("FatSeek() OldClusterIdx: %u, NewClusterIdx: %u\n", OldClusterIdx
, NewClusterIdx
);
1512 if (NewClusterIdx
!= OldClusterIdx
)
1514 UINT32 CurrentClusterIdx
, ClusterNumber
;
1516 if (NewClusterIdx
> OldClusterIdx
)
1518 CurrentClusterIdx
= OldClusterIdx
;
1519 ClusterNumber
= FileHandle
->CurrentCluster
;
1523 CurrentClusterIdx
= 0;
1524 ClusterNumber
= FileHandle
->StartCluster
;
1527 for (; CurrentClusterIdx
< NewClusterIdx
; CurrentClusterIdx
++)
1529 if (!FatGetFatEntry(Volume
, ClusterNumber
, &ClusterNumber
))
1534 FileHandle
->CurrentCluster
= ClusterNumber
;
1538 FileHandle
->FilePointer
= NewPosition
.LowPart
;
1543 const DEVVTBL FatFuncTable
=
1546 FatGetFileInformation
,
1553 const DEVVTBL
* FatMount(ULONG DeviceId
)
1555 PFAT_VOLUME_INFO Volume
;
1557 PFAT_BOOTSECTOR BootSector
= (PFAT_BOOTSECTOR
)Buffer
;
1558 PFAT32_BOOTSECTOR BootSector32
= (PFAT32_BOOTSECTOR
)Buffer
;
1559 PFATX_BOOTSECTOR BootSectorX
= (PFATX_BOOTSECTOR
)Buffer
;
1560 FILEINFORMATION FileInformation
;
1561 LARGE_INTEGER Position
;
1563 ULARGE_INTEGER SectorCount
;
1566 TRACE("Enter FatMount(%lu)\n", DeviceId
);
1569 // Allocate data for volume information
1571 Volume
= FrLdrTempAlloc(sizeof(FAT_VOLUME_INFO
), TAG_FAT_VOLUME
);
1574 RtlZeroMemory(Volume
, sizeof(FAT_VOLUME_INFO
));
1577 // Read the BootSector
1579 Position
.QuadPart
= 0;
1580 Status
= ArcSeek(DeviceId
, &Position
, SeekAbsolute
);
1581 if (Status
!= ESUCCESS
)
1583 FrLdrTempFree(Volume
, TAG_FAT_VOLUME
);
1586 Status
= ArcRead(DeviceId
, Buffer
, sizeof(Buffer
), &Count
);
1587 if (Status
!= ESUCCESS
|| Count
!= sizeof(Buffer
))
1589 FrLdrTempFree(Volume
, TAG_FAT_VOLUME
);
1594 // Check if BootSector is valid. If no, return early
1596 if (!RtlEqualMemory(BootSector
->FileSystemType
, "FAT12 ", 8) &&
1597 !RtlEqualMemory(BootSector
->FileSystemType
, "FAT16 ", 8) &&
1598 !RtlEqualMemory(BootSector32
->FileSystemType
, "FAT32 ", 8) &&
1599 !RtlEqualMemory(BootSectorX
->FileSystemType
, "FATX", 4))
1601 FrLdrTempFree(Volume
, TAG_FAT_VOLUME
);
1606 // Determine sector count
1608 Status
= ArcGetFileInformation(DeviceId
, &FileInformation
);
1609 if (Status
!= ESUCCESS
)
1611 FrLdrTempFree(Volume
, TAG_FAT_VOLUME
);
1614 SectorCount
.QuadPart
= (FileInformation
.EndingAddress
.QuadPart
- FileInformation
.StartingAddress
.QuadPart
);
1615 SectorCount
.QuadPart
/= SECTOR_SIZE
;
1620 Volume
->DeviceId
= DeviceId
;
1623 // Really open the volume
1625 if (!FatOpenVolume(Volume
, BootSector
, SectorCount
.QuadPart
))
1627 FrLdrTempFree(Volume
, TAG_FAT_VOLUME
);
1632 // Remember FAT volume information
1634 FatVolumes
[DeviceId
] = Volume
;
1639 TRACE("FatMount(%lu) success\n", DeviceId
);
1640 return &FatFuncTable
;