[CDFS_NEW] Fix broken cast.
[reactos.git] / drivers / filesystems / cdfs_new / allocsup.c
1 /*++
2
3 Copyright (c) 1990-2000 Microsoft Corporation
4
5 Module Name:
6
7 AllocSup.c
8
9 Abstract:
10
11 This module implements the Allocation support routines for Cdfs.
12
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.
21
22 We store the following information into an Mcb entry for an extent.
23
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
29
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.
36
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.
43
44
45 --*/
46
47 #include "cdprocs.h"
48
49 //
50 // The Bug check file id for this module
51 //
52
53 #define BugCheckFileId (CDFS_BUG_CHECK_ALLOCSUP)
54
55 //
56 // Local support routines
57 //
58
59 ULONG
60 CdFindMcbEntry (
61 _In_ PIRP_CONTEXT IrpContext,
62 _In_ PFCB Fcb,
63 _In_ LONGLONG FileOffset
64 );
65
66 VOID
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
73 );
74
75 #ifdef ALLOC_PRAGMA
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)
84 #endif
85
86 \f
87 _Requires_lock_held_(_Global_critical_region_)
88 VOID
89 #ifdef _MSC_VER
90 // PREFast currently has no way to express the Fcb==Fcb->Vcb->VolumeDasdFcb early return
91 #pragma warning(suppress: 6001 6101)
92 #endif
93 CdLookupAllocation (
94 _In_ PIRP_CONTEXT IrpContext,
95 _In_ PFCB Fcb,
96 _In_ LONGLONG FileOffset,
97 _Out_ PLONGLONG DiskOffset,
98 _Out_ PULONG ByteCount
99 )
100
101 /*++
102
103 Routine Description:
104
105 This routine looks through the mapping information for the file
106 to find the logical diskoffset and number of bytes at that offset.
107 We only deal with logical 2048 byte sectors here.
108
109 If the mapping isn't present we will look it up on disk now.
110 This routine assumes we are looking up a valid range in the file. This
111 routine raises if it can't find mapping for the file offset.
112
113 The Fcb may not be locked prior to calling this routine. We will always
114 acquire it here.
115
116 Arguments:
117
118 Fcb - Fcb representing this stream.
119
120 FileOffset - Lookup the allocation beginning at this point.
121
122 DiskOffset - Address to store the logical disk offset.
123
124 ByteCount - Address to store the number of contiguous bytes beginning
125 at DiskOffset above.
126
127 Return Value:
128
129 None.
130
131 --*/
132
133 {
134 BOOLEAN FirstPass = TRUE;
135 ULONG McbEntryOffset;
136 PFCB ParentFcb = NULL;
137 BOOLEAN CleanupParent = FALSE;
138
139 BOOLEAN UnlockFcb = FALSE;
140
141 LONGLONG CurrentFileOffset;
142 ULONG CurrentMcbOffset;
143 PCD_MCB_ENTRY CurrentMcbEntry;
144
145 DIRENT_ENUM_CONTEXT DirContext = {0};
146 DIRENT Dirent = {0};
147
148 PAGED_CODE();
149
150 ASSERT_IRP_CONTEXT( IrpContext );
151 ASSERT_FCB( Fcb );
152
153 //
154 // For DASD IO we already have clamped the read to the volume limits.
155 // We'll allow reading beyond those limits for extended DASD IO, so
156 // no MCB lookup here.
157 //
158
159 if (Fcb == Fcb->Vcb->VolumeDasdFcb) {
160
161 *DiskOffset = FileOffset;
162 return;
163 }
164
165 //
166 // Use a try finally to facilitate cleanup.
167 //
168
169 _SEH2_TRY {
170
171 //
172 // We use a loop to perform the lookup. If we don't find the mapping in the
173 // first pass then we look up all of the allocation and then look again.
174
175 while (TRUE) {
176
177 //
178 //
179 // Lookup the entry containing this file offset.
180 //
181
182 CdLockFcb( IrpContext, Fcb );
183 UnlockFcb = TRUE;
184
185 McbEntryOffset = CdFindMcbEntry( IrpContext, Fcb, FileOffset );
186
187 //
188 // If within the Mcb then we use the data out of this entry and are
189 // done.
190 //
191
192 if (McbEntryOffset < Fcb->Mcb.CurrentEntryCount) {
193
194 CdDiskOffsetFromMcbEntry( IrpContext,
195 Fcb->Mcb.McbArray + McbEntryOffset,
196 FileOffset,
197 DiskOffset,
198 ByteCount );
199
200 break;
201
202 //
203 // If this is not the first pass then the disk is corrupt.
204 //
205
206 } else if (!FirstPass) {
207
208 CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR );
209 }
210
211 CdUnlockFcb( IrpContext, Fcb );
212 UnlockFcb = FALSE;
213
214 //
215 // Initialize the search dirent structures.
216 //
217
218 CdInitializeDirContext( IrpContext, &DirContext );
219 CdInitializeDirent( IrpContext, &Dirent );
220
221 //
222 // Otherwise we need to walk the dirents for this file until we find
223 // the one containing this entry. The parent Fcb should always be
224 // present.
225 //
226
227 ParentFcb = Fcb->ParentFcb;
228 CdAcquireFileShared( IrpContext, ParentFcb );
229 CleanupParent = TRUE;
230
231 //
232 // Do an unsafe test to see if we need to create a file object.
233 //
234
235 CdVerifyOrCreateDirStreamFile( IrpContext, ParentFcb);
236
237 //
238 // Initialize the local variables to indicate the first dirent
239 // and lookup the first dirent.
240 //
241
242 CurrentFileOffset = 0;
243 CurrentMcbOffset = 0;
244
245 CdLookupDirent( IrpContext,
246 ParentFcb,
247 CdQueryFidDirentOffset( Fcb->FileId ),
248 &DirContext );
249
250 //
251 // If we are adding allocation to the Mcb then add all of it.
252 //
253
254 while (TRUE ) {
255
256 //
257 // Update the dirent from the on-disk dirent.
258 //
259
260 CdUpdateDirentFromRawDirent( IrpContext, ParentFcb, &DirContext, &Dirent );
261
262 //
263 // Add this dirent to the Mcb if not already present.
264 //
265
266 CdLockFcb( IrpContext, Fcb );
267 UnlockFcb = TRUE;
268
269 if (CurrentMcbOffset >= Fcb->Mcb.CurrentEntryCount) {
270
271 CdAddAllocationFromDirent( IrpContext, Fcb, CurrentMcbOffset, CurrentFileOffset, &Dirent );
272 }
273
274 CdUnlockFcb( IrpContext, Fcb );
275 UnlockFcb = FALSE;
276
277 //
278 // If this is the last dirent for the file then exit.
279 //
280
281 if (!FlagOn( Dirent.DirentFlags, CD_ATTRIBUTE_MULTI )) {
282
283 break;
284 }
285
286 //
287 // If we couldn't find another entry then the directory is corrupt because
288 // the last dirent for a file doesn't exist.
289 //
290
291 if (!CdLookupNextDirent( IrpContext, ParentFcb, &DirContext, &DirContext )) {
292
293 CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR );
294 }
295
296 //
297 // Update our loop variables.
298 //
299
300 CurrentMcbEntry = Fcb->Mcb.McbArray + CurrentMcbOffset;
301 CurrentFileOffset += CurrentMcbEntry->ByteCount;
302 CurrentMcbOffset += 1;
303 }
304
305 //
306 // All of the allocation is loaded. Go back and look up the mapping again.
307 // It better be there this time.
308 //
309
310 FirstPass = FALSE;
311 }
312
313 } _SEH2_FINALLY {
314
315 if (CleanupParent) {
316
317 //
318 // Release the parent and cleanup the dirent structures.
319 //
320
321 CdReleaseFile( IrpContext, ParentFcb );
322
323 CdCleanupDirContext( IrpContext, &DirContext );
324 CdCleanupDirent( IrpContext, &Dirent );
325 }
326
327 if (UnlockFcb) { CdUnlockFcb( IrpContext, Fcb ); }
328 } _SEH2_END;
329
330 return;
331 }
332
333 \f
334 VOID
335 CdAddAllocationFromDirent (
336 _In_ PIRP_CONTEXT IrpContext,
337 _Inout_ PFCB Fcb,
338 _In_ ULONG McbEntryOffset,
339 _In_ LONGLONG StartingFileOffset,
340 _In_ PDIRENT Dirent
341 )
342
343 /*++
344
345 Routine Description:
346
347 This routine is called to add an entry into the Cd Mcb. We grow the Mcb
348 as necessary and update the new entry.
349
350 NOTE - The Fcb has already been locked prior to makeing this call.
351
352 Arguments:
353
354 Fcb - Fcb containing the Mcb to update.
355
356 McbEntryOffset - Offset into the Mcb array to add this data.
357
358 StartingFileOffset - Offset in bytes from the start of the file.
359
360 Dirent - Dirent containing the on-disk data for this entry.
361
362 Return Value:
363
364 None
365
366 --*/
367
368 {
369 ULONG NewArraySize;
370 PVOID NewMcbArray;
371 PCD_MCB_ENTRY McbEntry;
372
373 PAGED_CODE();
374
375 UNREFERENCED_PARAMETER( IrpContext );
376
377 ASSERT_IRP_CONTEXT( IrpContext );
378 ASSERT_FCB( Fcb );
379 ASSERT_LOCKED_FCB( Fcb );
380
381 //
382 // If we need to grow the Mcb then do it now.
383 //
384
385 if (McbEntryOffset >= Fcb->Mcb.MaximumEntryCount) {
386
387 //
388 // Allocate a new buffer and copy the old data over.
389 //
390
391 NewArraySize = Fcb->Mcb.MaximumEntryCount * 2 * sizeof( CD_MCB_ENTRY );
392
393 NewMcbArray = FsRtlAllocatePoolWithTag( CdPagedPool,
394 NewArraySize,
395 TAG_MCB_ARRAY );
396
397 RtlZeroMemory( NewMcbArray, NewArraySize );
398 RtlCopyMemory( NewMcbArray,
399 Fcb->Mcb.McbArray,
400 Fcb->Mcb.MaximumEntryCount * sizeof( CD_MCB_ENTRY ));
401
402 //
403 // Deallocate the current array unless it is embedded in the Fcb.
404 //
405
406 if (Fcb->Mcb.MaximumEntryCount != 1) {
407
408 CdFreePool( &Fcb->Mcb.McbArray );
409 }
410
411 //
412 // Now update the Mcb with the new array.
413 //
414
415 Fcb->Mcb.MaximumEntryCount *= 2;
416 Fcb->Mcb.McbArray = NewMcbArray;
417 }
418
419 //
420 // Update the new entry with the input data.
421 //
422
423 McbEntry = Fcb->Mcb.McbArray + McbEntryOffset;
424
425 //
426 // Start with the location and length on disk.
427 //
428
429 McbEntry->DiskOffset = LlBytesFromBlocks( Fcb->Vcb, Dirent->StartingOffset );
430 McbEntry->ByteCount = Dirent->DataLength;
431
432 //
433 // Round the byte count up to a logical block boundary if this is
434 // the last extent.
435 //
436
437 if (!FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_MULTI )) {
438
439 McbEntry->ByteCount = BlockAlign( Fcb->Vcb, McbEntry->ByteCount );
440 }
441
442 //
443 // The file offset is the logical position within this file.
444 // We know this is correct regardless of whether we bias the
445 // file size or disk offset.
446 //
447
448 McbEntry->FileOffset = StartingFileOffset;
449
450 //
451 // Convert the interleave information from logical blocks to
452 // bytes.
453 //
454
455 if (Dirent->FileUnitSize != 0) {
456
457 McbEntry->DataBlockByteCount = LlBytesFromBlocks( Fcb->Vcb, Dirent->FileUnitSize );
458 McbEntry->TotalBlockByteCount = McbEntry->DataBlockByteCount +
459 LlBytesFromBlocks( Fcb->Vcb, Dirent->InterleaveGapSize );
460
461 //
462 // If the file is not interleaved then the size of the data block
463 // and total block are the same as the byte count.
464 //
465
466 } else {
467
468 McbEntry->DataBlockByteCount =
469 McbEntry->TotalBlockByteCount = McbEntry->ByteCount;
470 }
471
472 //
473 // Update the number of entries in the Mcb. The Mcb is never sparse
474 // so whenever we add an entry it becomes the last entry in the Mcb.
475 //
476
477 Fcb->Mcb.CurrentEntryCount = McbEntryOffset + 1;
478
479 return;
480 }
481
482 \f
483 VOID
484 CdAddInitialAllocation (
485 _In_ PIRP_CONTEXT IrpContext,
486 _Inout_ PFCB Fcb,
487 _In_ ULONG StartingBlock,
488 _In_ LONGLONG DataLength
489 )
490
491 /*++
492
493 Routine Description:
494
495 This routine is called to set up the initial entry in an Mcb.
496
497 This routine handles the single initial entry for a directory file. We will
498 round the start block down to a sector boundary. Our caller has already
499 biased the DataLength with any adjustments. This is used for the case
500 where there is a single entry and we want to align the data on a sector
501 boundary.
502
503 Arguments:
504
505 Fcb - Fcb containing the Mcb to update.
506
507 StartingBlock - Starting logical block for this directory. This is
508 the start of the actual data. We will bias this by the sector
509 offset of the data.
510
511 DataLength - Length of the data.
512
513 Return Value:
514
515 None
516
517 --*/
518
519 {
520 PCD_MCB_ENTRY McbEntry;
521
522 PAGED_CODE();
523
524 UNREFERENCED_PARAMETER( IrpContext );
525
526 ASSERT_IRP_CONTEXT( IrpContext );
527 ASSERT_FCB( Fcb );
528 ASSERT_LOCKED_FCB( Fcb );
529 NT_ASSERT( 0 == Fcb->Mcb.CurrentEntryCount);
530 NT_ASSERT( CDFS_NTC_FCB_DATA != Fcb->NodeTypeCode);
531
532 //
533 // Update the new entry with the input data.
534 //
535
536 McbEntry = Fcb->Mcb.McbArray;
537
538 //
539 // Start with the location and length on disk.
540 //
541
542 McbEntry->DiskOffset = LlBytesFromBlocks( Fcb->Vcb, StartingBlock );
543 McbEntry->DiskOffset -= Fcb->StreamOffset;
544
545 McbEntry->ByteCount = DataLength;
546
547 //
548 // The file offset is the logical position within this file.
549 // We know this is correct regardless of whether we bias the
550 // file size or disk offset.
551 //
552
553 McbEntry->FileOffset = 0;
554
555 //
556 // If the file is not interleaved then the size of the data block
557 // and total block are the same as the byte count.
558 //
559
560 McbEntry->DataBlockByteCount =
561 McbEntry->TotalBlockByteCount = McbEntry->ByteCount;
562
563 //
564 // Update the number of entries in the Mcb. The Mcb is never sparse
565 // so whenever we add an entry it becomes the last entry in the Mcb.
566 //
567
568 Fcb->Mcb.CurrentEntryCount = 1;
569
570 return;
571 }
572
573 \f
574 VOID
575 CdTruncateAllocation (
576 _In_ PIRP_CONTEXT IrpContext,
577 _Inout_ PFCB Fcb,
578 _In_ LONGLONG StartingFileOffset
579 )
580
581 /*++
582
583 Routine Description:
584
585 This routine truncates the Mcb for a file by eliminating all of the Mcb
586 entries from the entry which contains the given offset.
587
588 The Fcb should be locked when this routine is called.
589
590 Arguments:
591
592 Fcb - Fcb containing the Mcb to truncate.
593
594 StartingFileOffset - Offset in the file to truncate the Mcb from.
595
596 Return Value:
597
598 None
599
600 --*/
601
602 {
603 ULONG McbEntryOffset;
604
605 PAGED_CODE();
606
607 ASSERT_IRP_CONTEXT( IrpContext );
608 ASSERT_FCB( Fcb );
609 ASSERT_LOCKED_FCB( Fcb );
610
611 //
612 // Find the entry containg this starting offset.
613 //
614
615 McbEntryOffset = CdFindMcbEntry( IrpContext, Fcb, StartingFileOffset );
616
617 //
618 // Now set the current size of the mcb to this point.
619 //
620
621 Fcb->Mcb.CurrentEntryCount = McbEntryOffset;
622
623 return;
624 }
625
626 \f
627 _At_(Fcb->NodeByteSize, _In_range_(>=, FIELD_OFFSET( FCB, FcbType )))
628 VOID
629 CdInitializeMcb (
630 _In_ PIRP_CONTEXT IrpContext,
631 _Inout_updates_bytes_(Fcb->NodeByteSize) PFCB Fcb
632 )
633
634 /*++
635
636 Routine Description:
637
638 This routine is called to initialize the Mcb in an Fcb. We initialize
639 this with an entry count of one and point to the entry in the Fcb
640 itself.
641
642 Fcb should be acquired exclusively when this is called.
643
644 Arguments:
645
646 Fcb - Fcb containing the Mcb to initialize.
647
648 Return Value:
649
650 None
651
652 --*/
653
654 {
655 PAGED_CODE();
656
657 UNREFERENCED_PARAMETER( IrpContext );
658
659 ASSERT_IRP_CONTEXT( IrpContext );
660 ASSERT_FCB( Fcb );
661
662 //
663 // Set the entry counts to show there is one entry in the array and
664 // it is unused.
665 //
666
667 Fcb->Mcb.MaximumEntryCount = 1;
668 Fcb->Mcb.CurrentEntryCount = 0;
669
670 Fcb->Mcb.McbArray = &Fcb->McbEntry;
671
672 return;
673 }
674
675 \f
676 _At_(Fcb->NodeByteSize, _In_range_(>=, FIELD_OFFSET( FCB, FcbType )))
677 _When_(Fcb->NodeTypeCode == CDFS_NTC_FCB_PATH_TABLE, _At_(Fcb->NodeByteSize, _In_range_(==, SIZEOF_FCB_INDEX)))
678 _When_(Fcb->NodeTypeCode == CDFS_NTC_FCB_INDEX, _At_(Fcb->NodeByteSize, _In_range_(==, SIZEOF_FCB_INDEX)))
679 _When_(Fcb->NodeTypeCode == CDFS_NTC_FCB_DATA, _At_(Fcb->NodeByteSize, _In_range_(==, SIZEOF_FCB_DATA)))
680 VOID
681 CdUninitializeMcb (
682 _In_ PIRP_CONTEXT IrpContext,
683 _Inout_updates_bytes_(Fcb->NodeByteSize) PFCB Fcb
684 )
685
686 /*++
687
688 Routine Description:
689
690 This routine is called to cleanup an Mcb in an Fcb. We look at the
691 maximum run count in the Fcb and if greater than one we will deallocate
692 the buffer.
693
694 Fcb should be acquired exclusively when this is called.
695
696 Arguments:
697
698 Fcb - Fcb containing the Mcb to uninitialize.
699
700 Return Value:
701
702 None
703
704 --*/
705
706 {
707 PAGED_CODE();
708
709 UNREFERENCED_PARAMETER( IrpContext );
710
711 ASSERT_IRP_CONTEXT( IrpContext );
712 ASSERT_FCB( Fcb );
713
714 //
715 // If the count is greater than one then this is an allocated buffer.
716 //
717
718 if (Fcb->Mcb.MaximumEntryCount > 1) {
719
720 CdFreePool( &Fcb->Mcb.McbArray );
721 }
722
723 return;
724 }
725
726 \f
727 //
728 // Local suupport routine
729 //
730
731 ULONG
732 CdFindMcbEntry (
733 _In_ PIRP_CONTEXT IrpContext,
734 _In_ PFCB Fcb,
735 _In_ LONGLONG FileOffset
736 )
737
738 /*++
739
740 Routine Description:
741
742 This routine is called to find the Mcb entry which contains the file
743 offset at the given point. If the file offset is not currently in the
744 Mcb then we return the offset of the entry to add.
745
746 Fcb should be locked when this is called.
747
748 Arguments:
749
750 Fcb - Fcb containing the Mcb to uninitialize.
751
752 FileOffset - Return the Mcb entry which contains this file offset.
753
754 Return Value:
755
756 ULONG - Offset in the Mcb of the entry for this offset.
757
758 --*/
759
760 {
761 ULONG CurrentMcbOffset;
762 PCD_MCB_ENTRY CurrentMcbEntry;
763
764 PAGED_CODE();
765
766 UNREFERENCED_PARAMETER( IrpContext );
767
768 ASSERT_IRP_CONTEXT( IrpContext );
769 ASSERT_FCB( Fcb );
770 ASSERT_LOCKED_FCB( Fcb );
771
772 //
773 // We expect a linear search will be sufficient here.
774 //
775
776 CurrentMcbOffset = 0;
777 CurrentMcbEntry = Fcb->Mcb.McbArray;
778
779 while (CurrentMcbOffset < Fcb->Mcb.CurrentEntryCount) {
780
781 //
782 // Check if the offset lies within the current Mcb position.
783 //
784
785 if (FileOffset < CurrentMcbEntry->FileOffset + CurrentMcbEntry->ByteCount) {
786
787 break;
788 }
789
790 //
791 // Move to the next entry.
792 //
793
794 CurrentMcbOffset += 1;
795 CurrentMcbEntry += 1;
796 }
797
798 //
799 // This is the offset containing this file offset (or the point
800 // where an entry should be added).
801 //
802
803 return CurrentMcbOffset;
804 }
805
806 \f
807 //
808 // Local support routine
809 //
810
811 VOID
812 CdDiskOffsetFromMcbEntry (
813 _In_ PIRP_CONTEXT IrpContext,
814 _In_ PCD_MCB_ENTRY McbEntry,
815 _In_ LONGLONG FileOffset,
816 _Out_ PLONGLONG DiskOffset,
817 _Out_ PULONG ByteCount
818 )
819
820 /*++
821
822 Routine Description:
823
824 This routine is called to return the diskoffset and length of the file
825 data which begins at offset 'FileOffset'. We have the Mcb entry which
826 contains the mapping and interleave information.
827
828 NOTE - This routine deals with data in 2048 byte logical sectors. If
829 this is an XA file then our caller has already converted from
830 'raw' file bytes to 'cooked' file bytes.
831
832 Arguments:
833
834 McbEntry - Entry in the Mcb containing the allocation information.
835
836 FileOffset - Starting Offset in the file to find the matching disk
837 offsets.
838
839 DiskOffset - Address to store the starting disk offset for this operation.
840
841 ByteCount - Address to store number of contiguous bytes starting at this
842 disk offset.
843
844 Return Value:
845
846 None
847
848 --*/
849
850 {
851 LONGLONG ExtentOffset;
852
853 LONGLONG CurrentDiskOffset;
854 LONGLONG CurrentExtentOffset;
855
856 LONGLONG LocalByteCount;
857
858 PAGED_CODE();
859
860 UNREFERENCED_PARAMETER( IrpContext );
861
862 ASSERT_IRP_CONTEXT( IrpContext );
863
864 //
865 // Extent offset is the difference between the file offset and the start
866 // of the extent.
867 //
868
869 ExtentOffset = FileOffset - McbEntry->FileOffset;
870
871 //
872 // Optimize the non-interleave case.
873 //
874
875 if (McbEntry->ByteCount == McbEntry->DataBlockByteCount) {
876
877 *DiskOffset = McbEntry->DiskOffset + ExtentOffset;
878
879 LocalByteCount = McbEntry->ByteCount - ExtentOffset;
880
881 } else {
882
883 //
884 // Walk though any interleave until we reach the current offset in
885 // this extent.
886 //
887
888 CurrentExtentOffset = McbEntry->DataBlockByteCount;
889 CurrentDiskOffset = McbEntry->DiskOffset;
890
891 while (CurrentExtentOffset <= ExtentOffset) {
892
893 CurrentDiskOffset += McbEntry->TotalBlockByteCount;
894 CurrentExtentOffset += McbEntry->DataBlockByteCount;
895 }
896
897 //
898 // We are now positioned at the data block containing the starting
899 // file offset we were given. The disk offset is the offset of
900 // the start of this block plus the extent offset into this block.
901 // The byte count is the data block byte count minus our offset into
902 // this block.
903 //
904
905 *DiskOffset = CurrentDiskOffset + (ExtentOffset + McbEntry->DataBlockByteCount - CurrentExtentOffset);
906
907 //
908 // Make sure we aren't past the end of the data length. This is possible
909 // if we only use part of the last data block on an interleaved file.
910 //
911
912 if (CurrentExtentOffset > McbEntry->ByteCount) {
913
914 CurrentExtentOffset = McbEntry->ByteCount;
915 }
916
917 LocalByteCount = CurrentExtentOffset - ExtentOffset;
918 }
919
920 //
921 // If the byte count exceeds our limit then cut it to fit in 32 bits.
922 //
923
924 if (LocalByteCount > MAXULONG) {
925
926 *ByteCount = MAXULONG;
927
928 } else {
929
930 *ByteCount = (ULONG) LocalByteCount;
931 }
932
933 return;
934 }
935