xref: /reactos/drivers/filesystems/cdfs/dirsup.c (revision 34593d93)
1 /*++
2 
3 Copyright (c) 1989-2000 Microsoft Corporation
4 
5 Module Name:
6 
7     DirSup.c
8 
9 Abstract:
10 
11     This module implements the dirent support routines for Cdfs.
12 
13     Directories on a CD consist of a number of contiguous sectors on
14     the disk.  File descriptors consist of one or more directory entries
15     (dirents) within a directory.  Files may contain version numbers.  If
16     present all like-named files will be ordered contiguously in the
17     directory by decreasing version numbers.  We will only return the
18     first of these on a directory query unless the user explicitly
19     asks for version numbers.  Finally dirents will not span sector
20     boundaries.  Unused bytes at the end of a sector will be zero
21     filled.
22 
23     Directory sector:                                                   Offset
24                                                                         2048
25         +---------------------------------------------------------------+
26         |            |          |          |           |          |     |
27         | foo;4      | foo;4    | foo;3    |  hat      |  zebra   | Zero|
28         |            |          |          |           |          | Fill|
29         |            |  final   |  single  |           |          |     |
30         |            |  extent  |   extent |           |          |     |
31         +---------------------------------------------------------------+
32 
33     Dirent operations:
34 
35         - Position scan at known offset in directory.  Dirent at this
36             offset must exist and is valid.  Used when scanning a directory
37             from the beginning when the self entry is known to be valid.
38             Used when positioning at the first dirent for an open
39             file to scan the allocation information.  Used when resuming
40             a directory enumeration from a valid directory entry.
41 
42         - Position scan at known offset in directory.  Dirent is known to
43             start at this position but must be checked for validity.
44             Used to read the self-directory entry.
45 
46         - Move to the next dirent within a directory.
47 
48         - Given a known starting dirent, collect all the dirents for
49             that file.  Scan will finish positioned at the last dirent
50             for the file.  We will accumulate the extent lengths to
51             find the size of the file.
52 
53         - Given a known starting dirent, position the scan for the first
54             dirent of the following file.  Used when not interested in
55             all of the details for the current file and are looking for
56             the next file.
57 
58         - Update a common dirent structure with the details of the on-disk
59             structure.  This is used to smooth out the differences
60 
61         - Build the filename (name and version strings) out of the stream
62             of bytes in the file name on disk.  For Joliet disks we will have
63             to convert to little endian.
64 
65 
66 --*/
67 
68 #include "cdprocs.h"
69 
70 //
71 //  The Bug check file id for this module
72 //
73 
74 #define BugCheckFileId                   (CDFS_BUG_CHECK_DIRSUP)
75 
76 //
77 //  Local macros
78 //
79 
80 //
81 //  PRAW_DIRENT
82 //  CdRawDirent (
83 //      _In_ PIRP_CONTEXT IrpContext,
84 //      _In_ PDIR_ENUM_CONTEXT DirContext
85 //      );
86 //
87 
88 #define CdRawDirent(IC,DC)                                      \
89     Add2Ptr( (DC)->Sector, (DC)->SectorOffset, PRAW_DIRENT )
90 
91 //
92 //  Local support routines
93 //
94 
95 ULONG
96 CdCheckRawDirentBounds (
97     _In_ PIRP_CONTEXT IrpContext,
98     _In_ PDIRENT_ENUM_CONTEXT DirContext
99     );
100 
101 XA_EXTENT_TYPE
102 CdCheckForXAExtent (
103     _In_ PIRP_CONTEXT IrpContext,
104     _In_ PRAW_DIRENT RawDirent,
105     _Inout_ PDIRENT Dirent
106     );
107 
108 #ifdef ALLOC_PRAGMA
109 #pragma alloc_text(PAGE, CdCheckForXAExtent)
110 #pragma alloc_text(PAGE, CdCheckRawDirentBounds)
111 #pragma alloc_text(PAGE, CdCleanupFileContext)
112 #pragma alloc_text(PAGE, CdFindFile)
113 #pragma alloc_text(PAGE, CdFindDirectory)
114 #pragma alloc_text(PAGE, CdFindFileByShortName)
115 #pragma alloc_text(PAGE, CdLookupDirent)
116 #pragma alloc_text(PAGE, CdLookupLastFileDirent)
117 #pragma alloc_text(PAGE, CdLookupNextDirent)
118 #pragma alloc_text(PAGE, CdLookupNextInitialFileDirent)
119 #pragma alloc_text(PAGE, CdUpdateDirentFromRawDirent)
120 #pragma alloc_text(PAGE, CdUpdateDirentName)
121 #endif
122 
123 
124 VOID
CdLookupDirent(_In_ PIRP_CONTEXT IrpContext,_In_ PFCB Fcb,_In_ ULONG DirentOffset,_Out_ PDIRENT_ENUM_CONTEXT DirContext)125 CdLookupDirent (
126     _In_ PIRP_CONTEXT IrpContext,
127     _In_ PFCB Fcb,
128     _In_ ULONG DirentOffset,
129     _Out_ PDIRENT_ENUM_CONTEXT DirContext
130     )
131 
132 /*++
133 
134 Routine Description:
135 
136     This routine is called to initiate a walk through a directory.  We will
137     position ourselves in the directory at offset DirentOffset.  We know that
138     a dirent begins at this boundary but may have to verify the dirent bounds.
139     We will call this routine when looking up the first entry of a known
140     file or verifying the self entry of a directory.
141 
142 Arguments:
143 
144     Fcb - Fcb for the directory being traversed.
145 
146     DirentOffset - This is our target point in the directory.  We will map the
147         page containing this entry and possibly verify the dirent bounds at
148         this location.
149 
150     DirContext - This is the dirent context for this scan.  We update it with
151         the location of the dirent we found.  This structure has been initialized
152         outside of this call.
153 
154 Return Value:
155 
156     None.
157 
158 --*/
159 
160 {
161     LONGLONG BaseOffset;
162 
163     PAGED_CODE();
164 
165     //
166     //  Initialize the offset of the first dirent we want to map.
167     //
168 
169     DirContext->BaseOffset = SectorTruncate( DirentOffset );
170     BaseOffset = DirContext->BaseOffset;
171 
172     DirContext->DataLength = SECTOR_SIZE;
173 
174     DirContext->SectorOffset = SectorOffset( DirentOffset );
175 
176     //
177     //  Truncate the data length if we are at the end of the file.
178     //
179 
180     if (DirContext->DataLength > (Fcb->FileSize.QuadPart - BaseOffset)) {
181 
182         DirContext->DataLength = (ULONG) (Fcb->FileSize.QuadPart - BaseOffset);
183     }
184 
185     //
186     //  Now map the data at this offset.
187     //
188 
189     CcMapData( Fcb->FileObject,
190                (PLARGE_INTEGER) &BaseOffset,
191                DirContext->DataLength,
192                TRUE,
193                &DirContext->Bcb,
194                &DirContext->Sector );
195 
196     //
197     //  Verify the dirent bounds.
198     //
199 
200     DirContext->NextDirentOffset = CdCheckRawDirentBounds( IrpContext,
201                                                            DirContext );
202 
203     return;
204 }
205 
206 
207 BOOLEAN
CdLookupNextDirent(_In_ PIRP_CONTEXT IrpContext,_In_ PFCB Fcb,_In_ PDIRENT_ENUM_CONTEXT CurrentDirContext,_Inout_ PDIRENT_ENUM_CONTEXT NextDirContext)208 CdLookupNextDirent (
209     _In_ PIRP_CONTEXT IrpContext,
210     _In_ PFCB Fcb,
211     _In_ PDIRENT_ENUM_CONTEXT CurrentDirContext,
212     _Inout_ PDIRENT_ENUM_CONTEXT NextDirContext
213     )
214 
215 /*++
216 
217 Routine Description:
218 
219     This routine is called to find the next dirent in the directory.  The
220     current position is given and we look for the next.  We leave the context
221     for the starting position untouched and update the context for the
222     dirent we found.  The target context may already be initialized so we
223     may already have the sector in memory.
224 
225     This routine will position the enumeration context for the next dirent and
226     verify the dirent bounds.
227 
228     NOTE - This routine can be called with CurrentDirContext and NextDirContext
229         pointing to the same enumeration context.
230 
231 Arguments:
232 
233     Fcb - Fcb for the directory being traversed.
234 
235     CurrentDirContext - This is the dirent context for this scan.  We update
236         it with the location of the dirent we found.  This is currently
237         pointing to a dirent location.  The dirent bounds at this location
238         have already been verified.
239 
240     NextDirContext - This is the dirent context to update with the dirent we
241         find.  This may already point to a dirent so we need to check if
242         we are in the same sector and unmap any buffer as necessary.
243 
244         This dirent is left in an indeterminant state if we don't find a dirent.
245 
246 Return Value:
247 
248     BOOLEAN - TRUE if we find a location for the next dirent, FALSE otherwise.
249         This routine can cause a raise if the directory is corrupt.
250 
251 --*/
252 
253 {
254     LONGLONG CurrentBaseOffset = CurrentDirContext->BaseOffset;
255     ULONG TempUlong;
256 
257     BOOLEAN FoundDirent = FALSE;
258 
259     PAGED_CODE();
260 
261     //
262     //  Check if a different sector is mapped.  If so then move our target
263     //  enumeration context to the same sector.
264     //
265 
266     if ((CurrentDirContext->BaseOffset != NextDirContext->BaseOffset) ||
267         (NextDirContext->Bcb == NULL)) {
268 
269         //
270         //  Unpin the current target Bcb and map the next sector.
271         //
272 
273         CdUnpinData( IrpContext, &NextDirContext->Bcb );
274 
275         CcMapData( Fcb->FileObject,
276                    (PLARGE_INTEGER) &CurrentBaseOffset,
277                    CurrentDirContext->DataLength,
278                    TRUE,
279                    &NextDirContext->Bcb,
280                    &NextDirContext->Sector );
281 
282         //
283         //  Copy the data length and sector offset.
284         //
285 
286         NextDirContext->DataLength = CurrentDirContext->DataLength;
287         NextDirContext->BaseOffset = CurrentDirContext->BaseOffset;
288     }
289 
290     //
291     //  Now move to the same offset in the sector.
292     //
293 
294     NextDirContext->SectorOffset = CurrentDirContext->SectorOffset;
295 
296     //
297     //  If the value is zero then unmap the current sector and set up
298     //  the base offset to the beginning of the next sector.
299     //
300 
301     if (CurrentDirContext->NextDirentOffset == 0) {
302 
303         CurrentBaseOffset = NextDirContext->BaseOffset + NextDirContext->DataLength;
304 
305         //
306         //  Unmap the current sector.  We test the value of the Bcb in the
307         //  loop below to see if we need to read in another sector.
308         //
309 
310         CdUnpinData( IrpContext, &NextDirContext->Bcb );
311 
312     //
313     //  There is another possible dirent in the current sector.  Update the
314     //  enumeration context to reflect this.
315     //
316 
317     } else {
318 
319         NextDirContext->SectorOffset += CurrentDirContext->NextDirentOffset;
320     }
321 
322     //
323     //  Now loop until we find the next possible dirent or walk off the directory.
324     //
325 
326     while (TRUE) {
327 
328         //
329         //  If we don't currently have a sector mapped then map the
330         //  directory at the current offset.
331         //
332 
333         if (NextDirContext->Bcb == NULL) {
334 
335             TempUlong = SECTOR_SIZE;
336 
337             if (TempUlong > (ULONG) (Fcb->FileSize.QuadPart - CurrentBaseOffset)) {
338 
339                 TempUlong = (ULONG) (Fcb->FileSize.QuadPart - CurrentBaseOffset);
340 
341                 //
342                 //  If the length is zero then there is no dirent.
343                 //
344 
345                 if (TempUlong == 0) {
346 
347                     break;
348                 }
349             }
350 
351             CcMapData( Fcb->FileObject,
352                        (PLARGE_INTEGER) &CurrentBaseOffset,
353                        TempUlong,
354                        TRUE,
355                        &NextDirContext->Bcb,
356                        &NextDirContext->Sector );
357 
358             NextDirContext->BaseOffset = (ULONG) CurrentBaseOffset;
359             NextDirContext->SectorOffset = 0;
360             NextDirContext->DataLength = TempUlong;
361         }
362 
363         //
364         //  The CDFS spec allows for sectors in a directory to contain all zeroes.
365         //  In this case we need to move to the next sector.  So look at the
366         //  current potential dirent for a zero length.  Move to the next
367         //  dirent if length is zero.
368         //
369 
370         if (*((PCHAR) CdRawDirent( IrpContext, NextDirContext )) != 0) {
371 
372             FoundDirent = TRUE;
373             break;
374         }
375 
376         CurrentBaseOffset = NextDirContext->BaseOffset + NextDirContext->DataLength;
377         CdUnpinData( IrpContext, &NextDirContext->Bcb );
378     }
379 
380     //
381     //  Check the dirent bounds if we found a dirent.
382     //
383 
384     if (FoundDirent) {
385 
386         NextDirContext->NextDirentOffset = CdCheckRawDirentBounds( IrpContext,
387                                                                    NextDirContext );
388     }
389 
390     return FoundDirent;
391 }
392 
393 
394 _At_(Dirent->CdTime, _Post_notnull_)
395 VOID
CdUpdateDirentFromRawDirent(_In_ PIRP_CONTEXT IrpContext,_In_ PFCB Fcb,_In_ PDIRENT_ENUM_CONTEXT DirContext,_Inout_ PDIRENT Dirent)396 CdUpdateDirentFromRawDirent (
397     _In_ PIRP_CONTEXT IrpContext,
398     _In_ PFCB Fcb,
399     _In_ PDIRENT_ENUM_CONTEXT DirContext,
400     _Inout_ PDIRENT Dirent
401     )
402 
403 /*++
404 
405 Routine Description:
406 
407     This routine is called to safely copy the data from the dirent on disk
408     to the in-memory dirent.  The fields on disk are unaligned so we
409     need to safely copy them to our structure.
410 
411 Arguments:
412 
413     Fcb - Fcb for the directory being scanned.
414 
415     DirContext - Enumeration context for the raw disk dirent.
416 
417     Dirent - In-memory dirent to update.
418 
419 Return Value:
420 
421     None.
422 
423 --*/
424 
425 {
426     PRAW_DIRENT RawDirent = CdRawDirent( IrpContext, DirContext );
427 
428     PAGED_CODE();
429 
430     UNREFERENCED_PARAMETER( Fcb );
431 
432     //
433     //  Clear all of the current state flags except the flag indicating that
434     //  we allocated a name string.
435     //
436 
437     ClearFlag( Dirent->Flags, DIRENT_FLAG_NOT_PERSISTENT );
438 
439     //
440     //  The dirent offset is the sum of the start of the sector and the
441     //  sector offset.
442     //
443 
444     Dirent->DirentOffset = DirContext->BaseOffset + DirContext->SectorOffset;
445 
446     //
447     //  Copy the dirent length from the raw dirent.
448     //
449 
450     Dirent->DirentLength = RawDirent->DirLen;
451 
452     //
453     //  The starting offset on disk is computed by finding the starting
454     //  logical block and stepping over the Xar block.
455     //
456 
457     CopyUchar4( &Dirent->StartingOffset, RawDirent->FileLoc );
458 
459     Dirent->StartingOffset += RawDirent->XarLen;
460 
461     //
462     //  Do a safe copy to get the data length.
463     //
464 
465     CopyUchar4( &Dirent->DataLength, RawDirent->DataLen );
466 
467     //
468     //  Save a pointer to the time stamps.
469     //
470 
471     Dirent->CdTime = (PCHAR)RawDirent->RecordTime;
472 
473     //
474     //  Copy the dirent flags.
475     //
476 
477     Dirent->DirentFlags = CdRawDirentFlags( IrpContext, RawDirent );
478 
479     //
480     //  For both the file unit and interleave skip we want to take the
481     //  logical block count.
482     //
483 
484     Dirent->FileUnitSize =
485     Dirent->InterleaveGapSize = 0;
486 
487     if (RawDirent->IntLeaveSize != 0) {
488 
489         Dirent->FileUnitSize = RawDirent->IntLeaveSize;
490         Dirent->InterleaveGapSize = RawDirent->IntLeaveSkip;
491     }
492 
493     //
494     //  Get the name length and remember a pointer to the start of the
495     //  name string.  We don't do any processing on the name at this
496     //  point.
497     //
498     //  Check that the name length is non-zero.
499     //
500 
501     if (RawDirent->FileIdLen == 0) {
502 
503         CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
504     }
505 
506     Dirent->FileNameLen = RawDirent->FileIdLen;
507     Dirent->FileName = (PCHAR)RawDirent->FileId;
508 
509     //
510     //  If there are any remaining bytes at the end of the dirent then
511     //  there may be a system use area.  We protect ourselves from
512     //  disks which don't pad the dirent entries correctly by using
513     //  a fudge factor of one.  All system use areas must have a length
514     //  greater than one.  Don't bother with the system use area
515     //  if this is a directory.
516     //
517 
518     Dirent->XAAttributes = 0;
519     Dirent->XAFileNumber = 0;
520     Dirent->ExtentType = Form1Data;
521     Dirent->SystemUseOffset = 0;
522 
523     if (!FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY ) &&
524         (Dirent->DirentLength > ((FIELD_OFFSET( RAW_DIRENT, FileId ) + Dirent->FileNameLen) + 1))) {
525 
526         Dirent->SystemUseOffset = WordAlign( FIELD_OFFSET( RAW_DIRENT, FileId ) + Dirent->FileNameLen );
527     }
528 
529     return;
530 }
531 
532 
533 VOID
CdUpdateDirentName(_In_ PIRP_CONTEXT IrpContext,_Inout_ PDIRENT Dirent,_In_ ULONG IgnoreCase)534 CdUpdateDirentName (
535     _In_ PIRP_CONTEXT IrpContext,
536     _Inout_ PDIRENT Dirent,
537     _In_ ULONG IgnoreCase
538     )
539 
540 /*++
541 
542 Routine Description:
543 
544     This routine is called to update the name in the dirent with the name
545     from the disk.  We will look for the special case of the self and
546     parent entries and also construct the Unicode name for the Joliet disk
547     in order to work around the BigEndian on-disk structure.
548 
549 Arguments:
550 
551     Dirent - Pointer to the in-memory dirent structure.
552 
553     IgnoreCase - TRUE if we should build the upcased version.  Otherwise we
554         use the exact case name.
555 
556 Return Value:
557 
558     None.
559 
560 --*/
561 
562 {
563     UCHAR DirectoryValue;
564     ULONG Length;
565 
566     NTSTATUS Status;
567 
568     PAGED_CODE();
569 
570     //
571     //  Check if this is a self or parent entry.  There is no version number
572     //  in these cases.  We use a fixed string for these.
573     //
574     //      Self-Entry - Length is 1, value is 0.
575     //      Parent-Entry - Length is 1, value is 1.
576     //
577 
578     if ((Dirent->FileNameLen == 1) &&
579         FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY )) {
580 
581         DirectoryValue = *((PCHAR) Dirent->FileName);
582 
583         if ((DirectoryValue == 0) || (DirectoryValue == 1)) {
584 
585             //
586             //  We should not have allocated a name by the time we see these cases.
587             //  If we have, this means that the image is in violation of ISO 9660 7.6.2,
588             //  which states that the ./.. entries must be the first two in the directory.
589             //
590 
591             if (FlagOn( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER )) {
592 
593                 CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
594             }
595 
596             //
597             //  Now use one of the hard coded directory names.
598             //
599 
600             Dirent->CdFileName.FileName = CdUnicodeDirectoryNames[DirectoryValue];
601 
602             //
603             //  Show that there is no version number.
604             //
605 
606             Dirent->CdFileName.VersionString.Length = 0;
607 
608             //
609             //  The case name is the same as the exact name.
610             //
611 
612             Dirent->CdCaseFileName = Dirent->CdFileName;
613 
614             //
615             //  Mark this as a constant value entry.
616             //
617 
618             SetFlag( Dirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY );
619 
620             //
621             //  Return now.
622             //
623 
624             return;
625         }
626     }
627 
628     //
629     //  Mark this as a non-constant value entry.
630     //
631 
632     ClearFlag( Dirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY );
633 
634     //
635     //  Compute how large a buffer we will need.  If this is an ignore
636     //  case operation then we will want a double size buffer.  If the disk is not
637     //  a Joliet disk then we might need two bytes for each byte in the name.
638     //
639 
640     Length = Dirent->FileNameLen;
641 
642     if (IgnoreCase) {
643 
644         Length *= 2;
645     }
646 
647     if (!FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_JOLIET )) {
648 
649         Length *= sizeof( WCHAR );
650     }
651 
652     //
653     //  Now decide if we need to allocate a new buffer.  We will if
654     //  this name won't fit in the embedded name buffer and it is
655     //  larger than the current allocated buffer.  We always use the
656     //  allocated buffer if present.
657     //
658     //  If we haven't allocated a buffer then use the embedded buffer if the data
659     //  will fit.  This is the typical case.
660     //
661 
662     if (!FlagOn( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER ) &&
663         (Length <= sizeof( Dirent->NameBuffer ))) {
664 
665         Dirent->CdFileName.FileName.MaximumLength = sizeof( Dirent->NameBuffer );
666         Dirent->CdFileName.FileName.Buffer = Dirent->NameBuffer;
667 
668     } else {
669 
670         //
671         //  We need to use an allocated buffer.  Check if the current buffer
672         //  is large enough.
673         //
674 
675         if (Length > Dirent->CdFileName.FileName.MaximumLength) {
676 
677             //
678             //  Free any allocated buffer.
679             //
680 
681             if (FlagOn( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER )) {
682 
683                 CdFreePool( &Dirent->CdFileName.FileName.Buffer );
684                 ClearFlag( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER );
685             }
686 
687             Dirent->CdFileName.FileName.Buffer = FsRtlAllocatePoolWithTag( CdPagedPool,
688                                                                             Length,
689                                                                             TAG_DIRENT_NAME );
690 
691             SetFlag( Dirent->Flags, DIRENT_FLAG_ALLOC_BUFFER );
692 
693             Dirent->CdFileName.FileName.MaximumLength = (USHORT) Length;
694         }
695     }
696 
697     //
698     //  We now have a buffer for the name.  We need to either convert the on-disk bigendian
699     //  to little endian or covert the name to Unicode.
700     //
701 
702     if (!FlagOn( IrpContext->Vcb->VcbState, VCB_STATE_JOLIET )) {
703 
704         Status = RtlOemToUnicodeN( Dirent->CdFileName.FileName.Buffer,
705                                    Dirent->CdFileName.FileName.MaximumLength,
706                                    &Length,
707                                    Dirent->FileName,
708                                    Dirent->FileNameLen );
709 
710         __analysis_assert( Status == STATUS_SUCCESS );
711         NT_ASSERT( Status == STATUS_SUCCESS );
712         Dirent->CdFileName.FileName.Length = (USHORT) Length;
713 
714     } else {
715 
716         //
717         //  Convert this string to little endian.
718         //
719 
720         CdConvertBigToLittleEndian( IrpContext,
721                                     Dirent->FileName,
722                                     Dirent->FileNameLen,
723                                     (PCHAR) Dirent->CdFileName.FileName.Buffer );
724 
725         Dirent->CdFileName.FileName.Length = (USHORT) Dirent->FileNameLen;
726     }
727 
728     //
729     //  Split the name into name and version strings.
730     //
731 
732     CdConvertNameToCdName( IrpContext,
733                            &Dirent->CdFileName );
734 
735     //
736     //  The name length better be non-zero.
737     //
738 
739     if (Dirent->CdFileName.FileName.Length == 0) {
740 
741         CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
742     }
743 
744     //
745     //  If the filename ends with a period then back up one character.
746     //
747 
748     if (Dirent->CdFileName.FileName.Buffer[(Dirent->CdFileName.FileName.Length - sizeof( WCHAR )) / 2] == L'.') {
749 
750         //
751         //  Slide the version string down.
752         //
753 
754         if (Dirent->CdFileName.VersionString.Length != 0) {
755 
756             PWCHAR NewVersion;
757 
758             //
759             //  Start from the position currently containing the separator.
760             //
761 
762             NewVersion = Add2Ptr( Dirent->CdFileName.FileName.Buffer,
763                                   Dirent->CdFileName.FileName.Length,
764                                   PWCHAR );
765 
766             //
767             //  Now overwrite the period.
768             //
769 
770             RtlMoveMemory( NewVersion - 1,
771                            NewVersion,
772                            Dirent->CdFileName.VersionString.Length + sizeof( WCHAR ));
773 
774             //
775             //  Now point to the new version string.
776             //
777 
778             Dirent->CdFileName.VersionString.Buffer = NewVersion;
779         }
780 
781         //
782         //  Shrink the filename length.
783         //
784 
785         Dirent->CdFileName.FileName.Length -= sizeof( WCHAR );
786     }
787 
788     if (!CdIsLegalName( IrpContext, &Dirent->CdFileName.FileName )) {
789 
790         CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
791     }
792 
793     //
794     //  If this an exact case operation then use the filename exactly.
795     //
796 
797     if (!IgnoreCase) {
798 
799         Dirent->CdCaseFileName = Dirent->CdFileName;
800 
801     //
802     //  Otherwise perform our upcase operation.  We already have guaranteed the buffers are
803     //  there.
804     //
805 
806     } else {
807 
808         Dirent->CdCaseFileName.FileName.Buffer = Add2Ptr( Dirent->CdFileName.FileName.Buffer,
809                                                           Dirent->CdFileName.FileName.MaximumLength / 2,
810                                                           PWCHAR);
811 
812         Dirent->CdCaseFileName.FileName.MaximumLength = Dirent->CdFileName.FileName.MaximumLength / 2;
813 
814         CdUpcaseName( IrpContext,
815                       &Dirent->CdFileName,
816                       &Dirent->CdCaseFileName );
817     }
818 
819     return;
820 }
821 
822 
823 _Success_(return != FALSE) BOOLEAN
CdFindFile(_In_ PIRP_CONTEXT IrpContext,_In_ PFCB Fcb,_In_ PCD_NAME Name,_In_ BOOLEAN IgnoreCase,_Inout_ PFILE_ENUM_CONTEXT FileContext,_Out_ PCD_NAME * MatchingName)824 CdFindFile (
825     _In_ PIRP_CONTEXT IrpContext,
826     _In_ PFCB Fcb,
827     _In_ PCD_NAME Name,
828     _In_ BOOLEAN IgnoreCase,
829     _Inout_ PFILE_ENUM_CONTEXT FileContext,
830     _Out_ PCD_NAME *MatchingName
831     )
832 
833 /*++
834 
835 Routine Description:
836 
837     This routine is called to search a dirctory for a file matching the input
838     name.  This name has been upcased at this point if this a case-insensitive
839     search.  The name has been separated into separate name and version strings.
840     We look for an exact match in the name and only consider the version if
841     there is a version specified in the search name.
842 
843 Arguments:
844 
845     Fcb - Fcb for the directory being scanned.
846 
847     Name - Name to search for.
848 
849     IgnoreCase - Indicates the case of the search.
850 
851     FileContext - File context to use for the search.  This has already been
852         initialized.
853 
854     MatchingName - Pointer to buffer containing matching name.  We need this
855         in case we don't match the name in the directory but match the
856         short name instead.
857 
858 Return Value:
859 
860     BOOLEAN - TRUE if matching entry is found, FALSE otherwise.
861 
862 --*/
863 
864 {
865     PDIRENT Dirent;
866     ULONG ShortNameDirentOffset;
867 
868     BOOLEAN Found = FALSE;
869 
870     PAGED_CODE();
871 
872     //
873     //  Make sure there is a stream file for this Fcb.
874     //
875 
876     CdVerifyOrCreateDirStreamFile( IrpContext, Fcb);
877 
878     //
879     //  Check to see whether we need to check for a possible short name.
880     //
881 
882     ShortNameDirentOffset = CdShortNameDirentOffset( IrpContext, &Name->FileName );
883 
884     //
885     //  Position ourselves at the first entry.
886     //
887 
888     CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, Fcb->StreamOffset );
889 
890     //
891     //  Loop while there are more entries in this directory.
892     //
893 
894     do {
895 
896         Dirent = &FileContext->InitialDirent->Dirent;
897 
898         //
899         //  We only consider files which don't have the associated bit set.
900         //  We also only look for files.  All directories would already
901         //  have been found.
902         //
903 
904         if (!FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_ASSOC | CD_ATTRIBUTE_DIRECTORY )) {
905 
906             //
907             //  Update the name in the current dirent.
908             //
909 
910             CdUpdateDirentName( IrpContext, Dirent, IgnoreCase );
911 
912             //
913             //  Don't bother with constant entries.
914             //
915 
916             if (FlagOn( Dirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY )) {
917 
918                 continue;
919             }
920 
921             //
922             //  Now check whether we have a name match.
923             //  We exit the loop if we have a match.
924             //
925 
926             if (CdIsNameInExpression( IrpContext,
927                                       &Dirent->CdCaseFileName,
928                                       Name,
929                                       0,
930                                       TRUE )) {
931 
932                 *MatchingName = &Dirent->CdCaseFileName;
933                 Found = TRUE;
934                 break;
935             }
936 
937             //
938             //  The names didn't match.  If the input name is a possible short
939             //  name and we are at the correct offset in the directory then
940             //  check if the short names match.
941             //
942 
943             if (((Dirent->DirentOffset >> SHORT_NAME_SHIFT) == ShortNameDirentOffset) &&
944                 (Name->VersionString.Length == 0) &&
945                 !CdIs8dot3Name( IrpContext,
946                                 Dirent->CdFileName.FileName )) {
947 
948                 //
949                 //  Create the short name and check for a match.
950                 //
951 
952                 CdGenerate8dot3Name( IrpContext,
953                                      &Dirent->CdCaseFileName.FileName,
954                                      Dirent->DirentOffset,
955                                      FileContext->ShortName.FileName.Buffer,
956                                      &FileContext->ShortName.FileName.Length );
957 
958                 //
959                 //  Now check whether we have a name match.
960                 //  We exit the loop if we have a match.
961                 //
962 
963                 if (CdIsNameInExpression( IrpContext,
964                                           &FileContext->ShortName,
965                                           Name,
966                                           0,
967                                           FALSE )) {
968 
969                     *MatchingName = &FileContext->ShortName,
970                     Found = TRUE;
971                     break;
972                 }
973             }
974         }
975 
976         //
977         //  Go to the next initial dirent for a file.
978         //
979 
980     } while (CdLookupNextInitialFileDirent( IrpContext, Fcb, FileContext ));
981 
982     //
983     //  If we find the file then collect all of the dirents.
984     //
985 
986     if (Found) {
987 
988         CdLookupLastFileDirent( IrpContext, Fcb, FileContext );
989 
990     }
991 
992     return Found;
993 }
994 
995 
996 BOOLEAN
CdFindDirectory(_In_ PIRP_CONTEXT IrpContext,_In_ PFCB Fcb,_In_ PCD_NAME Name,_In_ BOOLEAN IgnoreCase,_Inout_ PFILE_ENUM_CONTEXT FileContext)997 CdFindDirectory (
998     _In_ PIRP_CONTEXT IrpContext,
999     _In_ PFCB Fcb,
1000     _In_ PCD_NAME Name,
1001     _In_ BOOLEAN IgnoreCase,
1002     _Inout_ PFILE_ENUM_CONTEXT FileContext
1003     )
1004 
1005 /*++
1006 
1007 Routine Description:
1008 
1009     This routine is called to search a dirctory for a directory matching the input
1010     name.  This name has been upcased at this point if this a case-insensitive
1011     search.  We look for an exact match in the name and do not look for shortname
1012     equivalents.
1013 
1014 Arguments:
1015 
1016     Fcb - Fcb for the directory being scanned.
1017 
1018     Name - Name to search for.
1019 
1020     IgnoreCase - Indicates the case of the search.
1021 
1022     FileContext - File context to use for the search.  This has already been
1023         initialized.
1024 
1025 Return Value:
1026 
1027     BOOLEAN - TRUE if matching entry is found, FALSE otherwise.
1028 
1029 --*/
1030 
1031 {
1032     PDIRENT Dirent;
1033 
1034     BOOLEAN Found = FALSE;
1035 
1036     PAGED_CODE();
1037 
1038     //
1039     //  Make sure there is a stream file for this Fcb.
1040     //
1041 
1042     CdVerifyOrCreateDirStreamFile( IrpContext, Fcb);
1043 
1044     //
1045     //  Position ourselves at the first entry.
1046     //
1047 
1048     CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, Fcb->StreamOffset );
1049 
1050     //
1051     //  Loop while there are more entries in this directory.
1052     //
1053 
1054     do {
1055 
1056         Dirent = &FileContext->InitialDirent->Dirent;
1057 
1058         //
1059         //  We only look for directories.  Directories cannot have the
1060         //  associated bit set.
1061         //
1062 
1063         if (FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_DIRECTORY )) {
1064 
1065             //
1066             //  Update the name in the current dirent.
1067             //
1068 
1069             CdUpdateDirentName( IrpContext, Dirent, IgnoreCase );
1070 
1071             //
1072             //  Don't bother with constant entries.
1073             //
1074 
1075             if (FlagOn( Dirent->Flags, DIRENT_FLAG_CONSTANT_ENTRY )) {
1076 
1077                 continue;
1078             }
1079 
1080             //
1081             //  Now check whether we have a name match.
1082             //  We exit the loop if we have a match.
1083             //
1084 
1085             if (CdIsNameInExpression( IrpContext,
1086                                       &Dirent->CdCaseFileName,
1087                                       Name,
1088                                       0,
1089                                       TRUE )) {
1090 
1091                 Found = TRUE;
1092                 break;
1093             }
1094         }
1095 
1096         //
1097         //  Go to the next initial dirent.
1098         //
1099 
1100     } while (CdLookupNextInitialFileDirent( IrpContext, Fcb, FileContext ));
1101 
1102     return Found;
1103 }
1104 
1105 
1106 _At_(FileContext->ShortName.FileName.MaximumLength, _In_range_(>=, BYTE_COUNT_8_DOT_3))
1107 BOOLEAN
CdFindFileByShortName(_In_ PIRP_CONTEXT IrpContext,_In_ PFCB Fcb,_In_ PCD_NAME Name,_In_ BOOLEAN IgnoreCase,_In_ ULONG ShortNameDirentOffset,_Inout_ PFILE_ENUM_CONTEXT FileContext)1108 CdFindFileByShortName (
1109     _In_ PIRP_CONTEXT IrpContext,
1110     _In_ PFCB Fcb,
1111     _In_ PCD_NAME Name,
1112     _In_ BOOLEAN IgnoreCase,
1113     _In_ ULONG ShortNameDirentOffset,
1114     _Inout_ PFILE_ENUM_CONTEXT FileContext
1115     )
1116 
1117 /*++
1118 
1119 Routine Description:
1120 
1121     This routine is called to find the file name entry whose short name
1122     is defined by the input DirentOffset.  The dirent offset here is
1123     multiplied by 32 and we look for the dirent begins in this 32 byte offset in
1124     directory.  The minimum dirent length is 34 so we are guaranteed that only
1125     one dirent can begin in each 32 byte block in the directory.
1126 
1127 Arguments:
1128 
1129     Fcb - Fcb for the directory being scanned.
1130 
1131     Name - Name we are trying to match.  We know this contains the tilde
1132         character followed by decimal characters.
1133 
1134     IgnoreCase - Indicates whether we need to upcase the long name and
1135         generated short name.
1136 
1137     ShortNameDirentOffset - This is the shifted value for the offset of the
1138         name in the directory.
1139 
1140     FileContext - This is the initialized file context to use for the search.
1141 
1142 Return Value:
1143 
1144     BOOLEAN - TRUE if a matching name was found, FALSE otherwise.
1145 
1146 --*/
1147 
1148 {
1149     BOOLEAN Found = FALSE;
1150     PDIRENT Dirent;
1151 
1152     ULONG ThisShortNameDirentOffset;
1153 
1154     PAGED_CODE();
1155 
1156     //
1157     //  Make sure there is a stream file for this Fcb.
1158     //
1159 
1160     CdVerifyOrCreateDirStreamFile( IrpContext, Fcb);
1161 
1162     //
1163     //  Position ourselves at the start of the directory and update
1164     //
1165     //
1166 
1167     CdLookupInitialFileDirent( IrpContext, Fcb, FileContext, Fcb->StreamOffset );
1168 
1169     //
1170     //  Loop until we have found the entry or are beyond this dirent.
1171     //
1172 
1173     do {
1174 
1175         //
1176         //  Compute the short name dirent offset for the current dirent.
1177         //
1178 
1179         Dirent = &FileContext->InitialDirent->Dirent;
1180         ThisShortNameDirentOffset = Dirent->DirentOffset >> SHORT_NAME_SHIFT;
1181 
1182         //
1183         //  If beyond the target then exit.
1184         //
1185 
1186         if (ThisShortNameDirentOffset > ShortNameDirentOffset) {
1187 
1188             break;
1189         }
1190 
1191         //
1192         //  If equal to the target then check if we have a name match.
1193         //  We will either match or fail here.
1194         //
1195 
1196         if (ThisShortNameDirentOffset == ShortNameDirentOffset) {
1197 
1198             //
1199             //  If this is an associated file then get out.
1200             //
1201 
1202             if (FlagOn( Dirent->DirentFlags, CD_ATTRIBUTE_ASSOC )) {
1203 
1204                 break;
1205             }
1206 
1207             //
1208             //  Update the name in the dirent and check if it is not
1209             //  an 8.3 name.
1210             //
1211 
1212             CdUpdateDirentName( IrpContext, Dirent, IgnoreCase );
1213 
1214             if (CdIs8dot3Name( IrpContext,
1215                                Dirent->CdFileName.FileName )) {
1216 
1217                 break;
1218             }
1219 
1220             //
1221             //  Generate the 8.3 name see if it matches our input name.
1222             //
1223 
1224             CdGenerate8dot3Name( IrpContext,
1225                                  &Dirent->CdCaseFileName.FileName,
1226                                  Dirent->DirentOffset,
1227                                  FileContext->ShortName.FileName.Buffer,
1228                                  &FileContext->ShortName.FileName.Length );
1229 
1230             //
1231             //  Check if this name matches.
1232             //
1233 
1234             if (CdIsNameInExpression( IrpContext,
1235                                       Name,
1236                                       &FileContext->ShortName,
1237                                       0,
1238                                       FALSE )) {
1239 
1240                 //
1241                 //  Let our caller know we found an entry.
1242                 //
1243 
1244                 Found = TRUE;
1245             }
1246 
1247             //
1248             //  Break out of the loop.
1249             //
1250 
1251             break;
1252         }
1253 
1254         //
1255         //  Continue until there are no more entries.
1256         //
1257 
1258     } while (CdLookupNextInitialFileDirent( IrpContext, Fcb, FileContext ));
1259 
1260     //
1261     //  If we find the file then collect all of the dirents.
1262     //
1263 
1264     if (Found) {
1265 
1266         CdLookupLastFileDirent( IrpContext, Fcb, FileContext );
1267 
1268     }
1269 
1270     return Found;
1271 }
1272 
1273 
1274 BOOLEAN
CdLookupNextInitialFileDirent(_In_ PIRP_CONTEXT IrpContext,_In_ PFCB Fcb,_Inout_ PFILE_ENUM_CONTEXT FileContext)1275 CdLookupNextInitialFileDirent (
1276     _In_ PIRP_CONTEXT IrpContext,
1277     _In_ PFCB Fcb,
1278     _Inout_ PFILE_ENUM_CONTEXT FileContext
1279     )
1280 
1281 /*++
1282 
1283 Routine Description:
1284 
1285     This routine is called to walk through the directory until we find the
1286     first possible dirent for file.  We are positioned at some point described
1287     by the FileContext.  We will walk through any remaing dirents for the
1288     current file until we find the first dirent for some subsequent file.
1289 
1290     We can be called when we have found just one dirent for a file or all
1291     of them.  We first check the CurrentDirContext.  In the typical
1292     single-extent case this is unused.  Then we look to the InitialDirContext
1293     which must be initialized.
1294 
1295     This routine will save the initial DirContext to the PriorDirContext and
1296     clean up any existing DirContext for the Prior or Current positions in
1297     the enumeration context.
1298 
1299 Arguments:
1300 
1301     Fcb - This is the directory to scan.
1302 
1303     FileContext - This is the file enumeration context.  It is currently pointing
1304         at some file in the directory.
1305 
1306 Return Value:
1307 
1308 --*/
1309 
1310 {
1311     PRAW_DIRENT RawDirent;
1312 
1313     PDIRENT_ENUM_CONTEXT CurrentDirContext;
1314     PDIRENT_ENUM_CONTEXT TargetDirContext;
1315     PCOMPOUND_DIRENT TempDirent;
1316 
1317     BOOLEAN FoundDirent = FALSE;
1318     BOOLEAN FoundLastDirent;
1319 
1320     PAGED_CODE();
1321 
1322     //
1323     //  Start by saving the initial dirent of the current file as the
1324     //  previous file.
1325     //
1326 
1327     TempDirent = FileContext->PriorDirent;
1328     FileContext->PriorDirent = FileContext->InitialDirent;
1329     FileContext->InitialDirent = TempDirent;
1330 
1331     //
1332     //  We will use the initial dirent of the prior file unless the
1333     //  previous search returned multiple extents.
1334     //
1335 
1336     CurrentDirContext = &FileContext->PriorDirent->DirContext;
1337 
1338     if (FlagOn( FileContext->Flags, FILE_CONTEXT_MULTIPLE_DIRENTS )) {
1339 
1340         CurrentDirContext = &FileContext->CurrentDirent->DirContext;
1341     }
1342 
1343     //
1344     //  Clear all of the flags and file size for the next file.
1345     //
1346 
1347     FileContext->Flags = 0;
1348     FileContext->FileSize = 0;
1349 
1350     FileContext->ShortName.FileName.Length = 0;
1351 
1352     //
1353     //  We always want to store the result into the updated initial dirent
1354     //  context.
1355     //
1356 
1357     TargetDirContext = &FileContext->InitialDirent->DirContext;
1358 
1359     //
1360     //  Loop until we find the first dirent after the last dirent of the
1361     //  current file.  We may not be at the last dirent for the current file yet
1362     //  so we may walk forward looking for the last and then find the
1363     //  initial dirent for the next file after that.
1364     //
1365 
1366     while (TRUE) {
1367 
1368         //
1369         //  Remember if the last dirent we visited was the last dirent for
1370         //  a file.
1371         //
1372 
1373         RawDirent = CdRawDirent( IrpContext, CurrentDirContext );
1374 
1375         FoundLastDirent = !FlagOn( CdRawDirentFlags( IrpContext, RawDirent ), CD_ATTRIBUTE_MULTI );
1376 
1377         //
1378         //  Try to find another dirent.
1379         //
1380 
1381         FoundDirent = CdLookupNextDirent( IrpContext,
1382                                           Fcb,
1383                                           CurrentDirContext,
1384                                           TargetDirContext );
1385 
1386         //
1387         //  Exit the loop if no entry found.
1388         //
1389 
1390         if (!FoundDirent) {
1391 
1392             break;
1393 
1394         }
1395 
1396         //
1397         //  Update the in-memory dirent.
1398         //
1399 
1400         CdUpdateDirentFromRawDirent( IrpContext,
1401                                      Fcb,
1402                                      TargetDirContext,
1403                                      &FileContext->InitialDirent->Dirent );
1404 
1405         //
1406         //  Exit the loop if we had the end for the previous file.
1407         //
1408 
1409         if (FoundLastDirent) {
1410 
1411             break;
1412         }
1413 
1414         //
1415         //  Always use a single dirent from this point on.
1416         //
1417 
1418         CurrentDirContext = TargetDirContext;
1419     }
1420 
1421     return FoundDirent;
1422 }
1423 
1424 
1425 VOID
CdLookupLastFileDirent(_In_ PIRP_CONTEXT IrpContext,_In_ PFCB Fcb,_In_ PFILE_ENUM_CONTEXT FileContext)1426 CdLookupLastFileDirent (
1427     _In_ PIRP_CONTEXT IrpContext,
1428     _In_ PFCB Fcb,
1429     _In_ PFILE_ENUM_CONTEXT FileContext
1430     )
1431 
1432 /*++
1433 
1434 Routine Description:
1435 
1436     This routine is called when we've found the matching initial dirent for
1437     a file.  Now we want to find all of the dirents for a file as well as
1438     compute the running total for the file size.
1439 
1440     We also go out to the system use area and check whether this is an
1441     XA sector.  In that case we will compute the real file size.
1442 
1443     The dirent in the initial compound dirent has been updated from the
1444     raw dirent when this routine is called.
1445 
1446 Arguments:
1447 
1448     Fcb - Directory containing the entries for the file.
1449 
1450     FileContext - Enumeration context for this search.  It currently points
1451         to the first dirent of the file and the in-memory dirent has been
1452         updated.
1453 
1454 Return Value:
1455 
1456     None.  This routine may raise STATUS_FILE_CORRUPT.
1457 
1458 --*/
1459 
1460 {
1461     XA_EXTENT_TYPE ExtentType = Form1Data;
1462     PCOMPOUND_DIRENT CurrentCompoundDirent;
1463     PDIRENT CurrentDirent = NULL;
1464 
1465     BOOLEAN FirstPass = TRUE;
1466     BOOLEAN FoundDirent;
1467 
1468     PAGED_CODE();
1469 
1470     //
1471     //  The current dirent to look at is the initial dirent for the file.
1472     //
1473 
1474     CurrentCompoundDirent = FileContext->InitialDirent;
1475 
1476     //
1477     //  Loop until we reach the last dirent for the file.
1478     //
1479 
1480     while (TRUE) {
1481 
1482         CurrentDirent = &CurrentCompoundDirent->Dirent;
1483 
1484         //
1485         //  Check if this extent has XA sectors.
1486         //
1487 
1488         if ((CurrentDirent->SystemUseOffset != 0) &&
1489             FlagOn( Fcb->Vcb->VcbState, VCB_STATE_CDXA ) &&
1490             CdCheckForXAExtent( IrpContext,
1491                                 CdRawDirent( IrpContext, &CurrentCompoundDirent->DirContext ),
1492                                 CurrentDirent )) {
1493 
1494             //
1495             //  Any previous dirent must describe XA sectors as well.
1496             //
1497 
1498             if (!FirstPass && (ExtentType != CurrentDirent->ExtentType)) {
1499 
1500                 CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
1501             }
1502 
1503             //
1504             //  If there are XA sectors then the data on the disk must
1505             //  be correctly aligned on sectors and be an integral number of
1506             //  sectors.  Only an issue if the logical block size is not
1507             //  2048.
1508             //
1509 
1510             if (Fcb->Vcb->BlockSize != SECTOR_SIZE) {
1511 
1512                 //
1513                 //  We will do the following checks.
1514                 //
1515                 //      Data must start on a sector boundary.
1516                 //      Data length must be integral number of sectors.
1517                 //
1518 
1519                 if ((SectorBlockOffset( Fcb->Vcb, CurrentDirent->StartingOffset ) != 0) ||
1520                     (SectorBlockOffset( Fcb->Vcb, CurrentDirent->DataLength ) != 0)) {
1521 
1522                     CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
1523                 }
1524 
1525                 //
1526                 //  If interleaved then both the file unit and interleave
1527                 //  gap must be integral number of sectors.
1528                 //
1529 
1530                 if ((CurrentDirent->FileUnitSize != 0) &&
1531                     ((SectorBlockOffset( Fcb->Vcb, CurrentDirent->FileUnitSize ) != 0) ||
1532                      (SectorBlockOffset( Fcb->Vcb, CurrentDirent->InterleaveGapSize ) != 0))) {
1533 
1534                     CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
1535                 }
1536             }
1537 
1538             //
1539             //  If this is the first dirent then add the bytes for the RIFF
1540             //  header.
1541             //
1542 
1543             if (FirstPass) {
1544 
1545                 FileContext->FileSize = sizeof( RIFF_HEADER );
1546             }
1547 
1548             //
1549             //  Add the size of the mode2-form2 sector for each sector
1550             //  we have here.
1551             //
1552 
1553             FileContext->FileSize += Int32x32To64( CurrentDirent->DataLength >> SECTOR_SHIFT,
1554                                                    XA_SECTOR_SIZE);
1555 
1556         } else {
1557 
1558             //
1559             //  This extent does not have XA sectors.  Any previous dirent
1560             //  better not have XA sectors.
1561             //
1562 
1563             if (!FirstPass && (ExtentType != CurrentDirent->ExtentType)) {
1564 
1565                 CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
1566             }
1567 
1568             //
1569             //  Add these bytes to the file size.
1570             //
1571 
1572             FileContext->FileSize += CurrentDirent->DataLength;
1573         }
1574 
1575         //
1576         //  If we are at the last dirent then exit.
1577         //
1578 
1579         if (!FlagOn( CurrentDirent->DirentFlags, CD_ATTRIBUTE_MULTI )) {
1580 
1581             break;
1582         }
1583 
1584         //
1585         //  Remember the extent type of the current extent.
1586         //
1587 
1588         ExtentType = CurrentDirent->ExtentType;
1589 
1590         //
1591         //  Look for the next dirent of the file.
1592         //
1593 
1594         FoundDirent = CdLookupNextDirent( IrpContext,
1595                                           Fcb,
1596                                           &CurrentCompoundDirent->DirContext,
1597                                           &FileContext->CurrentDirent->DirContext );
1598 
1599         //
1600         //  If we didn't find the entry then this is a corrupt directory.
1601         //
1602 
1603         if (!FoundDirent) {
1604 
1605             CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
1606         }
1607 
1608         //
1609         //  Remember the dirent we just found.
1610         //
1611 
1612         CurrentCompoundDirent = FileContext->CurrentDirent;
1613         FirstPass = FALSE;
1614 
1615         //
1616         //  Look up all of the dirent information for the given dirent.
1617         //
1618 
1619         CdUpdateDirentFromRawDirent( IrpContext,
1620                                      Fcb,
1621                                      &CurrentCompoundDirent->DirContext,
1622                                      &CurrentCompoundDirent->Dirent );
1623 
1624         //
1625         //  Set flag to show there were multiple extents.
1626         //
1627 
1628         SetFlag( FileContext->Flags, FILE_CONTEXT_MULTIPLE_DIRENTS );
1629     }
1630 
1631     return;
1632 }
1633 
1634 
1635 VOID
CdCleanupFileContext(_In_ PIRP_CONTEXT IrpContext,_In_ PFILE_ENUM_CONTEXT FileContext)1636 CdCleanupFileContext (
1637     _In_ PIRP_CONTEXT IrpContext,
1638     _In_ PFILE_ENUM_CONTEXT FileContext
1639     )
1640 
1641 /*++
1642 
1643 Routine Description:
1644 
1645     This routine is called to cleanup the enumeration context for a file
1646     search in a directory.  We will unpin any remaining Bcbs and free
1647     any allocated buffers.
1648 
1649 Arguments:
1650 
1651     FileContext - Enumeration context for the file search.
1652 
1653 Return Value:
1654 
1655     None.
1656 
1657 --*/
1658 
1659 {
1660     PCOMPOUND_DIRENT CurrentCompoundDirent;
1661     ULONG Count = 2;
1662 
1663     PAGED_CODE();
1664 
1665     UNREFERENCED_PARAMETER( IrpContext );
1666 
1667     //
1668     //  Cleanup the individual compound dirents.
1669     //
1670 
1671     do {
1672 
1673         CurrentCompoundDirent = &FileContext->Dirents[ Count ];
1674         CdCleanupDirContext( IrpContext, &CurrentCompoundDirent->DirContext );
1675         CdCleanupDirent( IrpContext, &CurrentCompoundDirent->Dirent );
1676 
1677     } while (Count--);
1678 
1679     return;
1680 }
1681 
1682 
1683 //
1684 //  Local support routine
1685 //
1686 
1687 ULONG
CdCheckRawDirentBounds(_In_ PIRP_CONTEXT IrpContext,_In_ PDIRENT_ENUM_CONTEXT DirContext)1688 CdCheckRawDirentBounds (
1689     _In_ PIRP_CONTEXT IrpContext,
1690     _In_ PDIRENT_ENUM_CONTEXT DirContext
1691     )
1692 
1693 /*++
1694 
1695 Routine Description:
1696 
1697     This routine takes a Dirent enumeration context and computes the offset
1698     to the next dirent.  A non-zero value indicates the offset within this
1699     sector.  A zero value indicates to move to the next sector.  If the
1700     current dirent does not fit within the sector then we will raise
1701     STATUS_CORRUPT.
1702 
1703 Arguments:
1704 
1705     DirContext - Enumeration context indicating the current position in
1706         the sector.
1707 
1708 Return Value:
1709 
1710     ULONG - Offset to the next dirent in this sector or zero if the
1711         next dirent is in the next sector.
1712 
1713     This routine will raise on a dirent which does not fit into the
1714     described data buffer.
1715 
1716 --*/
1717 
1718 {
1719     ULONG NextDirentOffset;
1720     PRAW_DIRENT RawDirent;
1721 
1722     PAGED_CODE();
1723 
1724     UNREFERENCED_PARAMETER( IrpContext );
1725 
1726     //
1727     //  We should always have at least a byte still available in the
1728     //  current buffer.
1729     //
1730 
1731     NT_ASSERT( (DirContext->DataLength - DirContext->SectorOffset) >= 1 );
1732 
1733     //
1734     //  Get a pointer to the current dirent.
1735     //
1736 
1737     RawDirent = CdRawDirent( IrpContext, DirContext );
1738 
1739     //
1740     //  If the dirent length is non-zero then look at the current dirent.
1741     //
1742 
1743     if (RawDirent->DirLen != 0) {
1744 
1745         //
1746         //  Check the following bound for the dirent length.
1747         //
1748         //      - Fits in the available bytes in the sector.
1749         //      - Is at least the minimal dirent size.
1750         //      - Is large enough to hold the file name.
1751         //
1752 
1753         if ((RawDirent->DirLen > (DirContext->DataLength - DirContext->SectorOffset)) ||
1754             (RawDirent->DirLen < MIN_RAW_DIRENT_LEN) ||
1755             (RawDirent->DirLen < (MIN_RAW_DIRENT_LEN - 1 + RawDirent->FileIdLen))) {
1756 
1757             CdRaiseStatus( IrpContext, STATUS_FILE_CORRUPT_ERROR );
1758         }
1759 
1760         //
1761         //  Copy the dirent length field.
1762         //
1763 
1764         NextDirentOffset = RawDirent->DirLen;
1765 
1766         //
1767         //  If we are exactly at the next sector then tell our caller by
1768         //  returning zero.
1769         //
1770 
1771         if (NextDirentOffset == (DirContext->DataLength - DirContext->SectorOffset)) {
1772 
1773             NextDirentOffset = 0;
1774         }
1775 
1776     } else {
1777 
1778         NextDirentOffset = 0;
1779     }
1780 
1781     return NextDirentOffset;
1782 }
1783 
1784 
1785 //
1786 //  Local support routine
1787 //
1788 
1789 XA_EXTENT_TYPE
CdCheckForXAExtent(_In_ PIRP_CONTEXT IrpContext,_In_ PRAW_DIRENT RawDirent,_Inout_ PDIRENT Dirent)1790 CdCheckForXAExtent (
1791     _In_ PIRP_CONTEXT IrpContext,
1792     _In_ PRAW_DIRENT RawDirent,
1793     _Inout_ PDIRENT Dirent
1794     )
1795 
1796 /*++
1797 
1798 Routine Description:
1799 
1800     This routine is called to scan through the system use area to test if
1801     the current dirent has the XA bit set.  The bit in the in-memory
1802     dirent will be set as appropriate.
1803 
1804 Arguments:
1805 
1806     RawDirent - Pointer to the on-disk dirent.
1807 
1808     Dirent - Pointer to the in-memory dirent.  We will update this with the
1809         appropriate XA flag.
1810 
1811 Return Value:
1812 
1813     XA_EXTENT_TYPE - Type of physical extent for this on disk dirent.
1814 
1815 --*/
1816 
1817 {
1818     XA_EXTENT_TYPE ExtentType = Form1Data;
1819     PSYSTEM_USE_XA SystemUseArea;
1820 
1821     PAGED_CODE();
1822 
1823     UNREFERENCED_PARAMETER( IrpContext );
1824 
1825     //
1826     //  Check if there is enough space for the XA system use area.
1827     //
1828 
1829     if (Dirent->DirentLength - Dirent->SystemUseOffset >= sizeof( SYSTEM_USE_XA )) {
1830 
1831         SystemUseArea = Add2Ptr( RawDirent, Dirent->SystemUseOffset, PSYSTEM_USE_XA );
1832 
1833         //
1834         //  Check for a valid signature.
1835         //
1836 
1837         if (SystemUseArea->Signature == SYSTEM_XA_SIGNATURE) {
1838 
1839             //
1840             //  Check for an audio track.
1841             //
1842 
1843             if (FlagOn( SystemUseArea->Attributes, SYSTEM_USE_XA_DA )) {
1844 
1845                 ExtentType = CDAudio;
1846 
1847             } else if (FlagOn( SystemUseArea->Attributes, SYSTEM_USE_XA_FORM2 )) {
1848 
1849                 //
1850                 //  Check for XA data.  Note that a number of discs (video CDs)
1851                 //  have files marked as type XA Mode 2 Form 1 (2048 bytes of
1852                 //  user data),  but actually record these sectors as Mode2 Form 2
1853                 //  (2352). We will fail to read these files,  since for M2F1,
1854                 //  a normal read CD command is issued (as per SCSI specs).
1855                 //
1856 
1857                 ExtentType = Mode2Form2Data;
1858             }
1859 
1860             Dirent->XAAttributes = SystemUseArea->Attributes;
1861             Dirent->XAFileNumber = SystemUseArea->FileNumber;
1862         }
1863     }
1864 
1865     Dirent->ExtentType = ExtentType;
1866     return ExtentType;
1867 }
1868 
1869 
1870 
1871