xref: /reactos/drivers/filesystems/cdfs/allocsup.c (revision cc439606)
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 
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 
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 
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 
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 
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 
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 
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 
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 
936