3 Copyright (c) 1990-2000 Microsoft Corporation
11 This module implements the Allocation support routines for Cdfs.
13 The data structure used here is the CD_MCB. There is an entry in
14 the Mcb for each dirent for a file. The entry will map the offset
15 within some file to a starting disk offset and number of bytes.
16 The Mcb also contains the interleave information for an extent.
17 An interleave consists of a number of blocks with data and a
18 (possibly different) number of blocks to skip. Any number of
19 data/skip pairs may exist in an extent but the data and skip sizes
20 are the same throughout the extent.
22 We store the following information into an Mcb entry for an extent.
24 FileOffset Offset in file for start of extent
25 DiskOffset Offset on disk for start of extent
26 ByteCount Number of file bytes in extent, no skip bytes
27 DataBlockByteCount Number of bytes in each data block
28 TotalBlockByteCount Number of bytes is data block and skip block
30 The disk offset in the Mcb has already been biased by the size of
31 the Xar block if present. All of the byte count fields are aligned
32 on logical block boundaries. If this is a directory or path table
33 then the file offset has been biased to round the initial disk
34 offset down to a sector boundary. The biasing is done when loading
35 the values into an Mcb entry.
37 An XA file has a header prepended to the file and each sector is 2352
38 bytes. The allocation information ignores the header and only deals
39 with 2048 byte sectors. Callers into the allocation package have
40 adjusted the starting offset value to reflect 2048 sectors. On return
41 from this package the caller will have to convert from 2048 sector values
42 into raw XA sector values.
50 // The Bug check file id for this module
53 #define BugCheckFileId (CDFS_BUG_CHECK_ALLOCSUP)
56 // Local support routines
61 _In_ PIRP_CONTEXT IrpContext
,
63 _In_ LONGLONG FileOffset
67 CdDiskOffsetFromMcbEntry (
68 _In_ PIRP_CONTEXT IrpContext
,
69 _In_ PCD_MCB_ENTRY McbEntry
,
70 _In_ LONGLONG FileOffset
,
71 _Out_ PLONGLONG DiskOffset
,
72 _Out_ PULONG ByteCount
76 #pragma alloc_text(PAGE, CdAddInitialAllocation)
77 #pragma alloc_text(PAGE, CdAddAllocationFromDirent)
78 #pragma alloc_text(PAGE, CdDiskOffsetFromMcbEntry)
79 #pragma alloc_text(PAGE, CdFindMcbEntry)
80 #pragma alloc_text(PAGE, CdInitializeMcb)
81 #pragma alloc_text(PAGE, CdLookupAllocation)
82 #pragma alloc_text(PAGE, CdTruncateAllocation)
83 #pragma alloc_text(PAGE, CdUninitializeMcb)
87 _Requires_lock_held_(_Global_critical_region_
)
89 // PREFast currently has no way to express the Fcb==Fcb->Vcb->VolumeDasdFcb early return
90 #pragma warning(suppress: 6001 6101)
92 _In_ PIRP_CONTEXT IrpContext
,
94 _In_ LONGLONG FileOffset
,
95 _Out_ PLONGLONG DiskOffset
,
96 _Out_ PULONG ByteCount
103 This routine looks through the mapping information for the file
104 to find the logical diskoffset and number of bytes at that offset.
105 We only deal with logical 2048 byte sectors here.
107 If the mapping isn't present we will look it up on disk now.
108 This routine assumes we are looking up a valid range in the file. This
109 routine raises if it can't find mapping for the file offset.
111 The Fcb may not be locked prior to calling this routine. We will always
116 Fcb - Fcb representing this stream.
118 FileOffset - Lookup the allocation beginning at this point.
120 DiskOffset - Address to store the logical disk offset.
122 ByteCount - Address to store the number of contiguous bytes beginning
132 BOOLEAN FirstPass
= TRUE
;
133 ULONG McbEntryOffset
;
134 PFCB ParentFcb
= NULL
;
135 BOOLEAN CleanupParent
= FALSE
;
137 BOOLEAN UnlockFcb
= FALSE
;
139 LONGLONG CurrentFileOffset
;
140 ULONG CurrentMcbOffset
;
141 PCD_MCB_ENTRY CurrentMcbEntry
;
143 DIRENT_ENUM_CONTEXT DirContext
= {0};
148 ASSERT_IRP_CONTEXT( IrpContext
);
152 // For DASD IO we already have clamped the read to the volume limits.
153 // We'll allow reading beyond those limits for extended DASD IO, so
154 // no MCB lookup here.
157 if (Fcb
== Fcb
->Vcb
->VolumeDasdFcb
) {
159 *DiskOffset
= FileOffset
;
164 // Use a try finally to facilitate cleanup.
170 // We use a loop to perform the lookup. If we don't find the mapping in the
171 // first pass then we look up all of the allocation and then look again.
177 // Lookup the entry containing this file offset.
180 CdLockFcb( IrpContext
, Fcb
);
183 McbEntryOffset
= CdFindMcbEntry( IrpContext
, Fcb
, FileOffset
);
186 // If within the Mcb then we use the data out of this entry and are
190 if (McbEntryOffset
< Fcb
->Mcb
.CurrentEntryCount
) {
192 CdDiskOffsetFromMcbEntry( IrpContext
,
193 Fcb
->Mcb
.McbArray
+ McbEntryOffset
,
201 // If this is not the first pass then the disk is corrupt.
204 } else if (!FirstPass
) {
206 CdRaiseStatus( IrpContext
, STATUS_DISK_CORRUPT_ERROR
);
209 CdUnlockFcb( IrpContext
, Fcb
);
213 // Initialize the search dirent structures.
216 CdInitializeDirContext( IrpContext
, &DirContext
);
217 CdInitializeDirent( IrpContext
, &Dirent
);
220 // Otherwise we need to walk the dirents for this file until we find
221 // the one containing this entry. The parent Fcb should always be
225 ParentFcb
= Fcb
->ParentFcb
;
226 CdAcquireFileShared( IrpContext
, ParentFcb
);
227 CleanupParent
= TRUE
;
230 // Do an unsafe test to see if we need to create a file object.
233 CdVerifyOrCreateDirStreamFile( IrpContext
, ParentFcb
);
236 // Initialize the local variables to indicate the first dirent
237 // and lookup the first dirent.
240 CurrentFileOffset
= 0;
241 CurrentMcbOffset
= 0;
243 CdLookupDirent( IrpContext
,
245 CdQueryFidDirentOffset( Fcb
->FileId
),
249 // If we are adding allocation to the Mcb then add all of it.
255 // Update the dirent from the on-disk dirent.
258 CdUpdateDirentFromRawDirent( IrpContext
, ParentFcb
, &DirContext
, &Dirent
);
261 // Add this dirent to the Mcb if not already present.
264 CdLockFcb( IrpContext
, Fcb
);
267 if (CurrentMcbOffset
>= Fcb
->Mcb
.CurrentEntryCount
) {
269 CdAddAllocationFromDirent( IrpContext
, Fcb
, CurrentMcbOffset
, CurrentFileOffset
, &Dirent
);
272 CdUnlockFcb( IrpContext
, Fcb
);
276 // If this is the last dirent for the file then exit.
279 if (!FlagOn( Dirent
.DirentFlags
, CD_ATTRIBUTE_MULTI
)) {
285 // If we couldn't find another entry then the directory is corrupt because
286 // the last dirent for a file doesn't exist.
289 if (!CdLookupNextDirent( IrpContext
, ParentFcb
, &DirContext
, &DirContext
)) {
291 CdRaiseStatus( IrpContext
, STATUS_DISK_CORRUPT_ERROR
);
295 // Update our loop variables.
298 CurrentMcbEntry
= Fcb
->Mcb
.McbArray
+ CurrentMcbOffset
;
299 CurrentFileOffset
+= CurrentMcbEntry
->ByteCount
;
300 CurrentMcbOffset
+= 1;
304 // All of the allocation is loaded. Go back and look up the mapping again.
305 // It better be there this time.
316 // Release the parent and cleanup the dirent structures.
319 CdReleaseFile( IrpContext
, ParentFcb
);
321 CdCleanupDirContext( IrpContext
, &DirContext
);
322 CdCleanupDirent( IrpContext
, &Dirent
);
325 if (UnlockFcb
) { CdUnlockFcb( IrpContext
, Fcb
); }
333 CdAddAllocationFromDirent (
334 _In_ PIRP_CONTEXT IrpContext
,
336 _In_ ULONG McbEntryOffset
,
337 _In_ LONGLONG StartingFileOffset
,
345 This routine is called to add an entry into the Cd Mcb. We grow the Mcb
346 as necessary and update the new entry.
348 NOTE - The Fcb has already been locked prior to makeing this call.
352 Fcb - Fcb containing the Mcb to update.
354 McbEntryOffset - Offset into the Mcb array to add this data.
356 StartingFileOffset - Offset in bytes from the start of the file.
358 Dirent - Dirent containing the on-disk data for this entry.
369 PCD_MCB_ENTRY McbEntry
;
373 UNREFERENCED_PARAMETER( IrpContext
);
375 ASSERT_IRP_CONTEXT( IrpContext
);
377 ASSERT_LOCKED_FCB( Fcb
);
380 // If we need to grow the Mcb then do it now.
383 if (McbEntryOffset
>= Fcb
->Mcb
.MaximumEntryCount
) {
386 // Allocate a new buffer and copy the old data over.
389 NewArraySize
= Fcb
->Mcb
.MaximumEntryCount
* 2 * sizeof( CD_MCB_ENTRY
);
391 NewMcbArray
= FsRtlAllocatePoolWithTag( CdPagedPool
,
395 RtlZeroMemory( NewMcbArray
, NewArraySize
);
396 RtlCopyMemory( NewMcbArray
,
398 Fcb
->Mcb
.MaximumEntryCount
* sizeof( CD_MCB_ENTRY
));
401 // Deallocate the current array unless it is embedded in the Fcb.
404 if (Fcb
->Mcb
.MaximumEntryCount
!= 1) {
406 CdFreePool( &Fcb
->Mcb
.McbArray
);
410 // Now update the Mcb with the new array.
413 Fcb
->Mcb
.MaximumEntryCount
*= 2;
414 Fcb
->Mcb
.McbArray
= NewMcbArray
;
418 // Update the new entry with the input data.
421 McbEntry
= Fcb
->Mcb
.McbArray
+ McbEntryOffset
;
424 // Start with the location and length on disk.
427 McbEntry
->DiskOffset
= LlBytesFromBlocks( Fcb
->Vcb
, Dirent
->StartingOffset
);
428 McbEntry
->ByteCount
= Dirent
->DataLength
;
431 // Round the byte count up to a logical block boundary if this is
435 if (!FlagOn( Dirent
->DirentFlags
, CD_ATTRIBUTE_MULTI
)) {
437 McbEntry
->ByteCount
= BlockAlign( Fcb
->Vcb
, McbEntry
->ByteCount
);
441 // The file offset is the logical position within this file.
442 // We know this is correct regardless of whether we bias the
443 // file size or disk offset.
446 McbEntry
->FileOffset
= StartingFileOffset
;
449 // Convert the interleave information from logical blocks to
453 if (Dirent
->FileUnitSize
!= 0) {
455 McbEntry
->DataBlockByteCount
= LlBytesFromBlocks( Fcb
->Vcb
, Dirent
->FileUnitSize
);
456 McbEntry
->TotalBlockByteCount
= McbEntry
->DataBlockByteCount
+
457 LlBytesFromBlocks( Fcb
->Vcb
, Dirent
->InterleaveGapSize
);
460 // If the file is not interleaved then the size of the data block
461 // and total block are the same as the byte count.
466 McbEntry
->DataBlockByteCount
=
467 McbEntry
->TotalBlockByteCount
= McbEntry
->ByteCount
;
471 // Update the number of entries in the Mcb. The Mcb is never sparse
472 // so whenever we add an entry it becomes the last entry in the Mcb.
475 Fcb
->Mcb
.CurrentEntryCount
= McbEntryOffset
+ 1;
482 CdAddInitialAllocation (
483 _In_ PIRP_CONTEXT IrpContext
,
485 _In_ ULONG StartingBlock
,
486 _In_ LONGLONG DataLength
493 This routine is called to set up the initial entry in an Mcb.
495 This routine handles the single initial entry for a directory file. We will
496 round the start block down to a sector boundary. Our caller has already
497 biased the DataLength with any adjustments. This is used for the case
498 where there is a single entry and we want to align the data on a sector
503 Fcb - Fcb containing the Mcb to update.
505 StartingBlock - Starting logical block for this directory. This is
506 the start of the actual data. We will bias this by the sector
509 DataLength - Length of the data.
518 PCD_MCB_ENTRY McbEntry
;
522 UNREFERENCED_PARAMETER( IrpContext
);
524 ASSERT_IRP_CONTEXT( IrpContext
);
526 ASSERT_LOCKED_FCB( Fcb
);
527 NT_ASSERT( 0 == Fcb
->Mcb
.CurrentEntryCount
);
528 NT_ASSERT( CDFS_NTC_FCB_DATA
!= Fcb
->NodeTypeCode
);
531 // Update the new entry with the input data.
534 McbEntry
= Fcb
->Mcb
.McbArray
;
537 // Start with the location and length on disk.
540 McbEntry
->DiskOffset
= LlBytesFromBlocks( Fcb
->Vcb
, StartingBlock
);
541 McbEntry
->DiskOffset
-= Fcb
->StreamOffset
;
543 McbEntry
->ByteCount
= DataLength
;
546 // The file offset is the logical position within this file.
547 // We know this is correct regardless of whether we bias the
548 // file size or disk offset.
551 McbEntry
->FileOffset
= 0;
554 // If the file is not interleaved then the size of the data block
555 // and total block are the same as the byte count.
558 McbEntry
->DataBlockByteCount
=
559 McbEntry
->TotalBlockByteCount
= McbEntry
->ByteCount
;
562 // Update the number of entries in the Mcb. The Mcb is never sparse
563 // so whenever we add an entry it becomes the last entry in the Mcb.
566 Fcb
->Mcb
.CurrentEntryCount
= 1;
573 CdTruncateAllocation (
574 _In_ PIRP_CONTEXT IrpContext
,
576 _In_ LONGLONG StartingFileOffset
583 This routine truncates the Mcb for a file by eliminating all of the Mcb
584 entries from the entry which contains the given offset.
586 The Fcb should be locked when this routine is called.
590 Fcb - Fcb containing the Mcb to truncate.
592 StartingFileOffset - Offset in the file to truncate the Mcb from.
601 ULONG McbEntryOffset
;
605 ASSERT_IRP_CONTEXT( IrpContext
);
607 ASSERT_LOCKED_FCB( Fcb
);
610 // Find the entry containg this starting offset.
613 McbEntryOffset
= CdFindMcbEntry( IrpContext
, Fcb
, StartingFileOffset
);
616 // Now set the current size of the mcb to this point.
619 Fcb
->Mcb
.CurrentEntryCount
= McbEntryOffset
;
625 _At_(Fcb
->NodeByteSize
, _In_range_(>=, FIELD_OFFSET( FCB
, FcbType
)))
628 _In_ PIRP_CONTEXT IrpContext
,
629 _Inout_updates_bytes_(Fcb
->NodeByteSize
) PFCB Fcb
636 This routine is called to initialize the Mcb in an Fcb. We initialize
637 this with an entry count of one and point to the entry in the Fcb
640 Fcb should be acquired exclusively when this is called.
644 Fcb - Fcb containing the Mcb to initialize.
655 UNREFERENCED_PARAMETER( IrpContext
);
657 ASSERT_IRP_CONTEXT( IrpContext
);
661 // Set the entry counts to show there is one entry in the array and
665 Fcb
->Mcb
.MaximumEntryCount
= 1;
666 Fcb
->Mcb
.CurrentEntryCount
= 0;
668 Fcb
->Mcb
.McbArray
= &Fcb
->McbEntry
;
674 _At_(Fcb
->NodeByteSize
, _In_range_(>=, FIELD_OFFSET( FCB
, FcbType
)))
675 _When_(Fcb
->NodeTypeCode
== CDFS_NTC_FCB_PATH_TABLE
, _At_(Fcb
->NodeByteSize
, _In_range_(==, SIZEOF_FCB_INDEX
)))
676 _When_(Fcb
->NodeTypeCode
== CDFS_NTC_FCB_INDEX
, _At_(Fcb
->NodeByteSize
, _In_range_(==, SIZEOF_FCB_INDEX
)))
677 _When_(Fcb
->NodeTypeCode
== CDFS_NTC_FCB_DATA
, _At_(Fcb
->NodeByteSize
, _In_range_(==, SIZEOF_FCB_DATA
)))
680 _In_ PIRP_CONTEXT IrpContext
,
681 _Inout_updates_bytes_(Fcb
->NodeByteSize
) PFCB Fcb
688 This routine is called to cleanup an Mcb in an Fcb. We look at the
689 maximum run count in the Fcb and if greater than one we will deallocate
692 Fcb should be acquired exclusively when this is called.
696 Fcb - Fcb containing the Mcb to uninitialize.
707 UNREFERENCED_PARAMETER( IrpContext
);
709 ASSERT_IRP_CONTEXT( IrpContext
);
713 // If the count is greater than one then this is an allocated buffer.
716 if (Fcb
->Mcb
.MaximumEntryCount
> 1) {
718 CdFreePool( &Fcb
->Mcb
.McbArray
);
726 // Local suupport routine
731 _In_ PIRP_CONTEXT IrpContext
,
733 _In_ LONGLONG FileOffset
740 This routine is called to find the Mcb entry which contains the file
741 offset at the given point. If the file offset is not currently in the
742 Mcb then we return the offset of the entry to add.
744 Fcb should be locked when this is called.
748 Fcb - Fcb containing the Mcb to uninitialize.
750 FileOffset - Return the Mcb entry which contains this file offset.
754 ULONG - Offset in the Mcb of the entry for this offset.
759 ULONG CurrentMcbOffset
;
760 PCD_MCB_ENTRY CurrentMcbEntry
;
764 UNREFERENCED_PARAMETER( IrpContext
);
766 ASSERT_IRP_CONTEXT( IrpContext
);
768 ASSERT_LOCKED_FCB( Fcb
);
771 // We expect a linear search will be sufficient here.
774 CurrentMcbOffset
= 0;
775 CurrentMcbEntry
= Fcb
->Mcb
.McbArray
;
777 while (CurrentMcbOffset
< Fcb
->Mcb
.CurrentEntryCount
) {
780 // Check if the offset lies within the current Mcb position.
783 if (FileOffset
< CurrentMcbEntry
->FileOffset
+ CurrentMcbEntry
->ByteCount
) {
789 // Move to the next entry.
792 CurrentMcbOffset
+= 1;
793 CurrentMcbEntry
+= 1;
797 // This is the offset containing this file offset (or the point
798 // where an entry should be added).
801 return CurrentMcbOffset
;
806 // Local support routine
810 CdDiskOffsetFromMcbEntry (
811 _In_ PIRP_CONTEXT IrpContext
,
812 _In_ PCD_MCB_ENTRY McbEntry
,
813 _In_ LONGLONG FileOffset
,
814 _Out_ PLONGLONG DiskOffset
,
815 _Out_ PULONG ByteCount
822 This routine is called to return the diskoffset and length of the file
823 data which begins at offset 'FileOffset'. We have the Mcb entry which
824 contains the mapping and interleave information.
826 NOTE - This routine deals with data in 2048 byte logical sectors. If
827 this is an XA file then our caller has already converted from
828 'raw' file bytes to 'cooked' file bytes.
832 McbEntry - Entry in the Mcb containing the allocation information.
834 FileOffset - Starting Offset in the file to find the matching disk
837 DiskOffset - Address to store the starting disk offset for this operation.
839 ByteCount - Address to store number of contiguous bytes starting at this
849 LONGLONG ExtentOffset
;
851 LONGLONG CurrentDiskOffset
;
852 LONGLONG CurrentExtentOffset
;
854 LONGLONG LocalByteCount
;
858 UNREFERENCED_PARAMETER( IrpContext
);
860 ASSERT_IRP_CONTEXT( IrpContext
);
863 // Extent offset is the difference between the file offset and the start
867 ExtentOffset
= FileOffset
- McbEntry
->FileOffset
;
870 // Optimize the non-interleave case.
873 if (McbEntry
->ByteCount
== McbEntry
->DataBlockByteCount
) {
875 *DiskOffset
= McbEntry
->DiskOffset
+ ExtentOffset
;
877 LocalByteCount
= McbEntry
->ByteCount
- ExtentOffset
;
882 // Walk though any interleave until we reach the current offset in
886 CurrentExtentOffset
= McbEntry
->DataBlockByteCount
;
887 CurrentDiskOffset
= McbEntry
->DiskOffset
;
889 while (CurrentExtentOffset
<= ExtentOffset
) {
891 CurrentDiskOffset
+= McbEntry
->TotalBlockByteCount
;
892 CurrentExtentOffset
+= McbEntry
->DataBlockByteCount
;
896 // We are now positioned at the data block containing the starting
897 // file offset we were given. The disk offset is the offset of
898 // the start of this block plus the extent offset into this block.
899 // The byte count is the data block byte count minus our offset into
903 *DiskOffset
= CurrentDiskOffset
+ (ExtentOffset
+ McbEntry
->DataBlockByteCount
- CurrentExtentOffset
);
906 // Make sure we aren't past the end of the data length. This is possible
907 // if we only use part of the last data block on an interleaved file.
910 if (CurrentExtentOffset
> McbEntry
->ByteCount
) {
912 CurrentExtentOffset
= McbEntry
->ByteCount
;
915 LocalByteCount
= CurrentExtentOffset
- ExtentOffset
;
919 // If the byte count exceeds our limit then cut it to fit in 32 bits.
922 if (LocalByteCount
> MAXULONG
) {
924 *ByteCount
= MAXULONG
;
928 *ByteCount
= (ULONG
) LocalByteCount
;