[CDFS_NEW] Replace old driver with a Ms-PL licensed version straight out of the drive...
[reactos.git] / drivers / filesystems / cdfs_new / dirsup.c
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 \f
124 VOID
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 \f
207 BOOLEAN
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_)\f
395 VOID
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 \f
533 VOID
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 \f
823 _Success_(return != FALSE) BOOLEAN
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 \f
996 BOOLEAN
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 \f
1106 _At_(FileContext->ShortName.FileName.MaximumLength, _In_range_(>=, BYTE_COUNT_8_DOT_3))
1107 BOOLEAN
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 \f
1274 BOOLEAN
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 \f
1425 VOID
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 \f
1635 VOID
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 \f
1683 //
1684 // Local support routine
1685 //
1686
1687 ULONG
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 \f
1785 //
1786 // Local support routine
1787 //
1788
1789 XA_EXTENT_TYPE
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