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