2 * PROJECT: ReactOS FAT file system driver
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: drivers/filesystems/fastfat/fat.c
5 * PURPOSE: FAT support routines
6 * PROGRAMMERS: Alexey Vlasov
9 /* INCLUDES *****************************************************************/
14 /* PROTOTYPES ***************************************************************/
15 typedef struct _FAT_SCAN_CONTEXT
17 PFILE_OBJECT FileObject
;
18 LARGE_INTEGER PageOffset
;
19 LONGLONG BeyondLastEntryOffset
;
24 #define FatEntryToDataOffset(xEntry, xVcb) \
25 ((xVcb)->DataArea + (((LONGLONG) ((xEntry) - 0x02)) << (xVcb)->BytesPerClusterLog))
27 #define FatDataOffsetToEntry(xOffset, xVcb) \
28 ((ULONG) ((xOffset - (xVcb)->DataArea) >> (xVcb)->BytesPerClusterLog) + 0x02)
31 FatScanFat32ForContinousRun(IN OUT PFAT_PAGE_CONTEXT Context
,
37 FatValidBpb(IN PBIOS_PARAMETER_BLOCK Bpb
);
39 /* VARIABLES ****************************************************************/
40 FAT_METHODS Fat12Methods
= {
47 FAT_METHODS Fat16Methods
= {
54 FAT_METHODS Fat32Methods
= {
55 FatScanFat32ForContinousRun
,
61 /* FUNCTIONS ****************************************************************/
64 * Pins the page containing ByteOffset byte.
67 * Keeps current BCB, Buffer pointer
68 * and maintains current and next page offset.
71 * Offset from the beginning of the data stream to be pinned.
74 * Pointer to the buffer starting with the specified ByteOffset.
78 PFAT_PAGE_CONTEXT Context
,
81 SIZE_T OffsetWithinPage
;
83 OffsetWithinPage
= (SIZE_T
) (ByteOffset
& (PAGE_SIZE
- 1));
84 ByteOffset
-= OffsetWithinPage
;
85 if (ByteOffset
!= Context
->Offset
.QuadPart
)
87 Context
->Offset
.QuadPart
= ByteOffset
;
88 if (Context
->Bcb
!= NULL
)
90 CcUnpinData(Context
->Bcb
);
93 if (!CcMapData(Context
->FileObject
,
100 Context
->Offset
.QuadPart
= 0LL;
101 ExRaiseStatus(STATUS_CANT_WAIT
);
104 Context
->EndOfPage
.QuadPart
=
105 Context
->Offset
.QuadPart
+ PAGE_SIZE
;
106 if (Context
->EndOfPage
.QuadPart
107 > Context
->EndOfData
.QuadPart
)
109 Context
->ValidLength
= (SIZE_T
)
110 (Context
->EndOfData
.QuadPart
111 - Context
->Offset
.QuadPart
);
115 Context
->ValidLength
= PAGE_SIZE
;
117 return Add2Ptr(Context
->Buffer
, OffsetWithinPage
, PVOID
);
121 * Pins the next page of data stream.
124 * Keeps current BCB, Buffer pointer
125 * and maintains current and next page offset.
128 * Pointer to the buffer starting with the beginning of the next page.
132 PFAT_PAGE_CONTEXT Context
)
134 ASSERT ((Context
->Offset
.QuadPart
% PAGE_SIZE
)
135 != (Context
->EndOfPage
.QuadPart
% PAGE_SIZE
)
136 && Context
->Bcb
!= NULL
);
138 ASSERT (Context
->ValidLength
== PAGE_SIZE
);
140 Context
->Offset
= Context
->EndOfPage
;
141 CcUnpinData(Context
->Bcb
);
142 if (!CcMapData(Context
->FileObject
,
150 Context
->Offset
.QuadPart
= 0LL;
151 ExRaiseStatus(STATUS_CANT_WAIT
);
153 Context
->EndOfPage
.QuadPart
=
154 Context
->Offset
.QuadPart
+ PAGE_SIZE
;
155 return Context
->Buffer
;
159 * Determines the index of the set bit.
162 * Number having a single bit set.
165 * Index of the set bit.
174 - ((Number
>> 1) & 033333333333)
175 - ((Number
>> 2) & 011111111111);
176 return (((Temp
+ (Temp
>> 3)) & 030707070707) % 63);
180 * Scans FAT32 for continous chain of clusters
183 * Pointer to FAT_PAGE_CONTEXT.
186 * Supplies the Index of the first cluster
187 * and receves the last index after the last
188 * cluster in the chain.
191 * Indicates if the context allows blocking.
194 * Value of the last claster terminated the scan.
197 * Raises STATUS_CANT_WAIT race condition.
200 FatScanFat32ForContinousRun(IN OUT PFAT_PAGE_CONTEXT Context
,
204 PULONG Entry
, EndOfPage
;
206 Entry
= FatPinPage(Context
, ((LONGLONG
) *Index
) << 0x2);
207 EndOfPage
= FatPinEndOfPage(Context
, PULONG
);
212 if ((*Entry
& FAT_CLUSTER_LAST
) != ++(*Index
))
213 return (*Entry
& FAT_CLUSTER_LAST
);
214 } while (++Entry
< EndOfPage
);
215 /* Check if this is the last available entry */
216 if (FatPinIsLastPage(Context
))
218 Entry
= (PULONG
) FatPinNextPage(Context
);
219 EndOfPage
= FatPinEndOfPage(Context
, PULONG
);
225 FatSetFat32ContinousRun(IN OUT PFAT_SCAN_CONTEXT Context
,
230 ExRaiseStatus(STATUS_NOT_IMPLEMENTED
);
234 FatScanFat32ForValueRun(IN OUT PFAT_SCAN_CONTEXT Context
,
239 ExRaiseStatus(STATUS_NOT_IMPLEMENTED
);
243 FatSetFat32ValueRun(IN OUT PFAT_SCAN_CONTEXT Context
,
249 ExRaiseStatus(STATUS_NOT_IMPLEMENTED
);
253 * Queries file MCB for the specified region [Vbo, Vbo + Length],
254 * returns the number of runs in the region as well as the first
255 * run of the range itself.
256 * If the specified region is not fully cached in MCB the routine
257 * scans FAT for the file and fills the MCB until the file offset
258 * (defined as Vbo + Length) is reached.
261 * Pointer to FCB structure for the file.
264 * Virtual Byte Offset in the file.
267 * Receives the Value of Logical Byte offset corresponding
268 * to supplied Vbo Value.
271 * Supplies file range length to be examined and receives
272 * the length of first run.
275 * Receives the index (in MCB cache) of first run.
278 * Incremented index of the last run (+1).
281 * Should be called by I/O routines to split the I/O operation
282 * into sequential or parallel I/O operations.
285 FatScanFat(IN PFCB Fcb
,
288 IN OUT PLONGLONG Length
,
292 LONGLONG CurrentLbo
, CurrentVbo
, BeyondLastVbo
, CurrentLength
;
293 ULONG Entry
, NextEntry
, NumberOfEntries
, CurrentIndex
;
294 FAT_PAGE_CONTEXT Context
;
297 /* Some often used values */
300 BeyondLastVbo
= Vbo
+ *Length
;
301 CurrentLength
= ((LONGLONG
) Vcb
->Clusters
) << Vcb
->BytesPerClusterLog
;
302 if (BeyondLastVbo
> CurrentLength
)
303 BeyondLastVbo
= CurrentLength
;
304 /* Try to locate first run */
305 if (FsRtlLookupLargeMcbEntry(&Fcb
->Mcb
, Vbo
, Lbo
, Length
, NULL
, NULL
, Index
))
307 /* Check if we have a single mapped run */
308 if (Vbo
>= BeyondLastVbo
)
309 goto FatScanFcbFatExit
;
313 /* Get the first scan startup values */
314 if (FsRtlLookupLastLargeMcbEntryAndIndex(
315 &Fcb
->Mcb
, &CurrentVbo
, &CurrentLbo
, &CurrentIndex
))
317 Entry
= FatDataOffsetToEntry(CurrentLbo
, Vcb
);
321 /* Map is empty, set up initial values */
322 Entry
= Fcb
->FirstCluster
;
324 ExRaiseStatus(STATUS_FILE_CORRUPT_ERROR
);
325 if (Entry
>= Vcb
->Clusters
)
327 if (Entry
< FAT_CLUSTER_LAST
)
328 ExRaiseStatus(STATUS_FILE_CORRUPT_ERROR
);
334 /* Initialize Context */
335 RtlZeroMemory(&Context
, sizeof(Context
));
336 Context
.FileObject
= Vcb
->StreamFileObject
;
337 Context
.EndOfData
.QuadPart
= Vcb
->BeyondLastClusterInFat
;
339 while (CurrentVbo
< BeyondLastVbo
)
341 /* Locate Continous run starting with the current entry */
342 NumberOfEntries
= Entry
;
343 NextEntry
= Vcb
->Methods
.ScanContinousRun(
344 &Context
, &NumberOfEntries
, CanWait
);
345 NumberOfEntries
-= Entry
;
346 /* Check value that terminated the for being valid for FAT */
347 if (NextEntry
<= 0x2)
348 ExRaiseStatus(STATUS_FILE_CORRUPT_ERROR
);
349 if (NextEntry
>= Vcb
->Clusters
)
351 if (NextEntry
< FAT_CLUSTER_LAST
)
352 ExRaiseStatus(STATUS_FILE_CORRUPT_ERROR
);
356 CurrentLength
= ((LONGLONG
) NumberOfEntries
)
357 << Vcb
->BytesPerClusterLog
;
358 FsRtlAddLargeMcbEntry(&Fcb
->Mcb
,
360 FatEntryToDataOffset(Entry
, Vcb
),
362 /* Setup next iteration */
364 CurrentVbo
+= CurrentLength
;
367 if (*Length
== 0LL && CurrentIndex
> 0)
369 if (!FsRtlLookupLargeMcbEntry(&Fcb
->Mcb
,
370 Vbo
, Lbo
, Length
, NULL
, NULL
, Index
))
382 FatValidBpb(IN PBIOS_PARAMETER_BLOCK Bpb
)
384 return (FatValidBytesPerSector(Bpb
->BytesPerSector
)
385 && FatValidSectorsPerCluster(Bpb
->SectorsPerCluster
)
386 && Bpb
->ReservedSectors
> 0
388 && (Bpb
->Sectors
> 0 || Bpb
->LargeSectors
> 0)
389 && (Bpb
->SectorsPerFat
> 0
390 || (Bpb
->LargeSectorsPerFat
> 0 && Bpb
->FsVersion
== 0))
391 && (Bpb
->Media
== 0xf0
392 || Bpb
->Media
== 0xf8
393 || Bpb
->Media
== 0xf9
394 || Bpb
->Media
== 0xfb
395 || Bpb
->Media
== 0xfc
396 || Bpb
->Media
== 0xfd
397 || Bpb
->Media
== 0xfe
398 || Bpb
->Media
== 0xff)
399 && (Bpb
->SectorsPerFat
== 0 || Bpb
->RootEntries
> 0)
400 && (Bpb
->SectorsPerFat
> 0 || !Bpb
->MirrorDisabled
));
404 FatiInitializeVcb(PVCB Vcb
)
406 ULONG ClustersCapacity
;
408 /* Various characteristics needed for navigation in FAT */
409 if ((Vcb
->Sectors
= Vcb
->Bpb
.Sectors
) == 0)
410 Vcb
->Sectors
= Vcb
->Bpb
.LargeSectors
;
411 if ((Vcb
->SectorsPerFat
= Vcb
->Bpb
.SectorsPerFat
) == 0)
412 Vcb
->SectorsPerFat
= Vcb
->Bpb
.LargeSectorsPerFat
;
413 Vcb
->RootDirent
= Vcb
->Bpb
.ReservedSectors
+ Vcb
->Bpb
.Fats
* Vcb
->SectorsPerFat
;
414 Vcb
->RootDirentSectors
= BytesToSectors(Vcb
,
415 Vcb
->Bpb
.RootEntries
* sizeof(DIR_ENTRY
));
416 Vcb
->DataArea
= Vcb
->RootDirent
+ Vcb
->RootDirentSectors
;
417 Vcb
->Clusters
= (Vcb
->Sectors
- Vcb
->Bpb
.ReservedSectors
418 - Vcb
->Bpb
.Fats
* Vcb
->SectorsPerFat
419 - Vcb
->RootDirentSectors
) / Vcb
->Bpb
.SectorsPerCluster
;
420 if (Vcb
->BytesPerClusterLog
< 4087)
422 Vcb
->IndexDepth
= 0x0c;
423 Vcb
->Methods
= Fat12Methods
;
427 Vcb
->IndexDepth
= 0x10;
428 Vcb
->Methods
= Fat16Methods
;
430 /* Large Sectors are used for FAT32 */
431 if (Vcb
->Bpb
.Sectors
== 0) {
432 Vcb
->IndexDepth
= 0x20;
433 Vcb
->Methods
= Fat32Methods
;
435 ClustersCapacity
= (SectorsToBytes(Vcb
, Vcb
->Sectors
) * 0x8 / Vcb
->IndexDepth
) - 1;
436 if (Vcb
->Clusters
> ClustersCapacity
)
437 Vcb
->Clusters
= ClustersCapacity
;
438 Vcb
->BytesPerCluster
= SectorsToBytes(Vcb
, Vcb
->Bpb
.SectorsPerCluster
);
439 Vcb
->BytesPerClusterLog
= FatPowerOfTwo(Vcb
->BytesPerCluster
);
440 Vcb
->BeyondLastClusterInFat
= ((LONGLONG
) Vcb
->Clusters
) * Vcb
->IndexDepth
/ 0x8;
442 /* Update real volume size with the real value. */
443 Vcb
->Header
.FileSize
.QuadPart
=
444 Vcb
->Header
.AllocationSize
.QuadPart
= SectorsToBytes(Vcb
, Vcb
->Sectors
);
448 FatInitializeVcb(IN PVCB Vcb
,
449 IN PDEVICE_OBJECT TargetDeviceObject
,
455 LARGE_INTEGER Offset
;
457 RtlZeroMemory(Vcb
, sizeof(*Vcb
));
459 /* Initialize list head, so that it will
460 * not fail in cleanup.
462 InitializeListHead(&Vcb
->VcbLinks
);
464 /* Setup FCB Header */
465 Vcb
->Header
.NodeTypeCode
= FAT_NTC_VCB
;
466 Vcb
->Header
.NodeByteSize
= sizeof(*Vcb
);
468 /* Setup Vcb fields */
469 Vcb
->TargetDeviceObject
= TargetDeviceObject
;
470 ObReferenceObject(TargetDeviceObject
);
473 /* Setup FCB Header */
474 ExInitializeFastMutex(&Vcb
->HeaderMutex
);
475 FsRtlSetupAdvancedHeader(&Vcb
->Header
, &Vcb
->HeaderMutex
);
477 /* Create Volume File Object */
478 Vcb
->StreamFileObject
= IoCreateStreamFileObject(NULL
,
479 Vcb
->TargetDeviceObject
);
481 /* We have to setup all FCB fields needed for CC */
482 Vcb
->StreamFileObject
->FsContext
= Vcb
;
483 Vcb
->StreamFileObject
->SectionObjectPointer
= &Vcb
->SectionObjectPointers
;
485 /* At least full boot sector should be available */
486 Vcb
->Header
.FileSize
.QuadPart
= sizeof(PACKED_BOOT_SECTOR
);
487 Vcb
->Header
.AllocationSize
.QuadPart
= sizeof(PACKED_BOOT_SECTOR
);
488 Vcb
->Header
.ValidDataLength
.HighPart
= MAXLONG
;
489 Vcb
->Header
.ValidDataLength
.LowPart
= MAXULONG
;
492 CcInitializeCacheMap(Vcb
->StreamFileObject
,
493 (PCC_FILE_SIZES
)&Vcb
->Header
.AllocationSize
,
495 &FatGlobalData
.CacheMgrNoopCallbacks
,
498 /* Read boot sector */
502 /* Note: Volume Read path does not require
503 * any of the parameters set further
506 if (CcMapData(Vcb
->StreamFileObject
,
508 sizeof(PACKED_BOOT_SECTOR
),
513 PPACKED_BOOT_SECTOR BootSector
= (PPACKED_BOOT_SECTOR
) Buffer
;
514 FatUnpackBios(&Vcb
->Bpb
, &BootSector
->PackedBpb
);
515 if (!(FatBootSectorJumpValid(BootSector
->Jump
) &&
516 FatValidBpb(&Vcb
->Bpb
)))
518 Status
= STATUS_UNRECOGNIZED_VOLUME
;
520 CopyUchar4(&Vcb
->Vpb
->SerialNumber
, BootSector
->Id
);
525 Status
= STATUS_UNRECOGNIZED_VOLUME
;
526 goto FatInitializeVcbCleanup
;
529 /* Set up notifications */
530 FsRtlNotifyInitializeSync(&Vcb
->NotifySync
);
531 InitializeListHead(&Vcb
->NotifyList
);
533 /* Call helper function */
534 FatiInitializeVcb(Vcb
);
536 /* Add this Vcb to grobal Vcb list. */
537 InsertTailList(&FatGlobalData
.VcbListHead
, &Vcb
->VcbLinks
);
538 return STATUS_SUCCESS
;
540 FatInitializeVcbCleanup
:
542 /* Unwind the routine actions */
543 FatUninitializeVcb(Vcb
);
548 FatUninitializeVcb(IN PVCB Vcb
)
550 LARGE_INTEGER ZeroSize
;
552 ZeroSize
.QuadPart
= 0LL;
554 /* Close volume file */
555 if (Vcb
->StreamFileObject
!= NULL
)
557 /* Uninitialize CC. */
558 CcUninitializeCacheMap(Vcb
->StreamFileObject
, &ZeroSize
, NULL
);
559 ObDereferenceObject(Vcb
->StreamFileObject
);
560 Vcb
->StreamFileObject
= NULL
;
563 /* Free notifications stuff */
564 FsRtlNotifyUninitializeSync(&Vcb
->NotifySync
);
566 /* Unlink from global Vcb list. */
567 RemoveEntryList(&Vcb
->VcbLinks
);
569 /* Release Target Device */
570 ObDereferenceObject(Vcb
->TargetDeviceObject
);
571 Vcb
->TargetDeviceObject
= NULL
;