[CDFS] Properly check for legal names in CdIsLegalName()
[reactos.git] / drivers / filesystems / cdfs / namesup.c
1 /*++
2
3 Copyright (c) 1991-2000 Microsoft Corporation
4
5 Module Name:
6
7 NameSup.c
8
9 Abstract:
10
11 This module implements the Cdfs Name support routines
12
13
14 --*/
15
16 #include "cdprocs.h"
17
18 //
19 // The Bug check file id for this module
20 //
21
22 #define BugCheckFileId (CDFS_BUG_CHECK_NAMESUP)
23
24 #ifdef ALLOC_PRAGMA
25 #pragma alloc_text(PAGE, CdConvertBigToLittleEndian)
26 #pragma alloc_text(PAGE, CdConvertNameToCdName)
27 #pragma alloc_text(PAGE, CdDissectName)
28 #pragma alloc_text(PAGE, CdGenerate8dot3Name)
29 #pragma alloc_text(PAGE, CdFullCompareNames)
30 #pragma alloc_text(PAGE, CdIsLegalName)
31 #pragma alloc_text(PAGE, CdIs8dot3Name)
32 #pragma alloc_text(PAGE, CdIsNameInExpression)
33 #pragma alloc_text(PAGE, CdShortNameDirentOffset)
34 #pragma alloc_text(PAGE, CdUpcaseName)
35 #endif
36
37 \f
38 _Post_satisfies_(_Old_(CdName->FileName.Length) >=
39 CdName->FileName.Length + CdName->VersionString.Length)
40 VOID
41 CdConvertNameToCdName (
42 _In_ PIRP_CONTEXT IrpContext,
43 _Inout_ PCD_NAME CdName
44 )
45
46 /*++
47
48 Routine Description:
49
50 This routine is called to convert a string of bytes into a CdName.
51
52 The full name is already in the CdName structure in the FileName field.
53 We split this into the filename and version strings.
54
55 Arguments:
56
57 CdName - Pointer to CdName structure to update.
58
59 Return Value:
60
61 None.
62
63 --*/
64
65 {
66 ULONG NameLength = 0;
67 PWCHAR CurrentCharacter = CdName->FileName.Buffer;
68
69 PAGED_CODE();
70
71 UNREFERENCED_PARAMETER( IrpContext );
72
73 //
74 // Look for a separator character.
75 //
76
77 while ((NameLength < CdName->FileName.Length) &&
78 (*CurrentCharacter != L';')) {
79
80 CurrentCharacter += 1;
81 NameLength += 2;
82 }
83
84 //
85 // If there is at least one more character after a possible separator then it
86 // and all following characters are part of the version string.
87 //
88
89 CdName->VersionString.Length = 0;
90 if (NameLength + sizeof( WCHAR ) < CdName->FileName.Length) {
91
92 CdName->VersionString.MaximumLength =
93 CdName->VersionString.Length = (USHORT) (CdName->FileName.Length - NameLength - sizeof( WCHAR ));
94 CdName->VersionString.Buffer = Add2Ptr( CdName->FileName.Buffer,
95 NameLength + sizeof( WCHAR ),
96 PWCHAR );
97 }
98
99 //
100 // Now update the filename portion of the name.
101 //
102
103 CdName->FileName.Length = (USHORT) NameLength;
104
105 return;
106 }
107
108 \f
109 VOID
110 CdConvertBigToLittleEndian (
111 _In_ PIRP_CONTEXT IrpContext,
112 _In_reads_bytes_(ByteCount) PCHAR BigEndian,
113 _In_ ULONG ByteCount,
114 _Out_writes_bytes_(ByteCount) PCHAR LittleEndian
115 )
116
117 /*++
118
119 Routine Description:
120
121 This routine is called to convert a unicode string in big endian to
122 little endian. We start by copying all of the source bytes except
123 the first. This will put the low order bytes in the correct position.
124 We then copy each high order byte in its correct position.
125
126 Arguments:
127
128 BigEndian - Pointer to the string of big endian characters.
129
130 ByteCount - Number of unicode characters in this string.
131
132 LittleEndian - Pointer to array to store the little endian characters.
133
134 Return Value:
135
136 None.
137
138 --*/
139
140 {
141 ULONG RemainingByteCount = ByteCount;
142
143 PCHAR Source = BigEndian;
144 PCHAR Destination = LittleEndian;
145
146 PAGED_CODE();
147
148 //
149 // If the byte count isn't an even number then the disk is corrupt.
150 //
151
152 if (FlagOn( ByteCount, 1 )) {
153
154 CdRaiseStatus( IrpContext, STATUS_DISK_CORRUPT_ERROR );
155 }
156
157 //
158 // Start by copy the low-order bytes into the correct position. Do
159 // this by skipping the first byte in the BigEndian string.
160 //
161
162 RtlCopyMemory( Destination,
163 Source + 1,
164 RemainingByteCount - 1 );
165
166 //
167 // Now move the high-order bytes into position.
168 //
169
170 Destination += 1;
171
172 while (RemainingByteCount != 0) {
173
174 #ifdef _MSC_VER
175 #pragma prefast(push)
176 #pragma prefast(suppress:26014, "RemainingByteCount is even")
177 #endif
178 *Destination = *Source;
179 #ifdef _MSC_VER
180 #pragma prefast(pop)
181 #endif
182
183 Source += 2;
184 Destination += 2;
185
186 RemainingByteCount -= 2;
187 }
188
189 return;
190 }
191
192 \f
193 VOID
194 CdUpcaseName (
195 _In_ PIRP_CONTEXT IrpContext,
196 _In_ PCD_NAME Name,
197 _Inout_ PCD_NAME UpcaseName
198 )
199
200 /*++
201
202 Routine Description:
203
204 This routine is called to upcase a CdName structure. We will do both
205 the filename and version strings.
206
207 Arguments:
208
209 Name - This is the mixed case version of the name.
210
211 UpcaseName - This is the destination for the upcase operation.
212
213 Return Value:
214
215 None. This routine will raise all errors.
216
217 --*/
218
219 {
220 NTSTATUS Status;
221
222 PAGED_CODE();
223
224 UNREFERENCED_PARAMETER( IrpContext );
225
226 //
227 // If the name structures are different then initialize the different components.
228 //
229
230 if (Name != UpcaseName) {
231
232 //
233 // Initialize the version string portion of the name.
234 //
235
236 UpcaseName->VersionString.Length = 0;
237
238 if (Name->VersionString.Length != 0) {
239
240 UpcaseName->VersionString.MaximumLength =
241 UpcaseName->VersionString.Length = Name->VersionString.Length;
242
243 //
244 // Initially set the buffer to point to where we need to insert
245 // the separator.
246 //
247
248 UpcaseName->VersionString.Buffer = Add2Ptr( UpcaseName->FileName.Buffer,
249 Name->FileName.Length,
250 PWCHAR );
251
252 //
253 // We are currently pointing to the location to store the separator.
254 // Store the ';' and then move to the next character to
255 // copy the data.
256 //
257
258 #ifdef _MSC_VER
259 #pragma prefast( suppress:26015, "CD_NAME structures have two UNICODE_STRING structures pointing to the same allocation. there is no way to tell prefast this is the case and that the allocation is always big enough.");
260 #endif
261 *(UpcaseName->VersionString.Buffer) = L';';
262
263 UpcaseName->VersionString.Buffer += 1;
264 }
265 }
266
267 //
268 // Upcase the string using the correct upcase routine.
269 //
270
271 Status = RtlUpcaseUnicodeString( &UpcaseName->FileName,
272 &Name->FileName,
273 FALSE );
274
275 //
276 // This should never fail.
277 //
278
279 NT_ASSERT( Status == STATUS_SUCCESS );
280 __analysis_assert( Status == STATUS_SUCCESS );
281
282 if (Name->VersionString.Length != 0) {
283
284 Status = RtlUpcaseUnicodeString( &UpcaseName->VersionString,
285 &Name->VersionString,
286 FALSE );
287
288 //
289 // This should never fail.
290 //
291
292 NT_ASSERT( Status == STATUS_SUCCESS );
293 __analysis_assert( Status == STATUS_SUCCESS );
294 }
295
296 return;
297 }
298
299 \f
300 VOID
301 CdDissectName (
302 _In_ PIRP_CONTEXT IrpContext,
303 _Inout_ PUNICODE_STRING RemainingName,
304 _Out_ PUNICODE_STRING FinalName
305 )
306
307 /*++
308
309 Routine Description:
310
311 This routine is called to strip off leading components of the name strings. We search
312 for either the end of the string or separating characters. The input remaining
313 name strings should have neither a trailing or leading backslash.
314
315 Arguments:
316
317 RemainingName - Remaining name.
318
319 FinalName - Location to store next component of name.
320
321 Return Value:
322
323 None.
324
325 --*/
326
327 {
328 ULONG NameLength;
329 PWCHAR NextWchar;
330
331 PAGED_CODE();
332
333 UNREFERENCED_PARAMETER( IrpContext );
334
335 //
336 // Find the offset of the next component separators.
337 //
338
339 for (NameLength = 0, NextWchar = RemainingName->Buffer;
340 (NameLength < RemainingName->Length) && (*NextWchar != L'\\');
341 NameLength += sizeof( WCHAR) , NextWchar += 1);
342
343 //
344 // Adjust all the strings by this amount.
345 //
346
347 FinalName->Buffer = RemainingName->Buffer;
348
349 FinalName->MaximumLength = FinalName->Length = (USHORT) NameLength;
350
351 //
352 // If this is the last component then set the RemainingName lengths to zero.
353 //
354
355 if (NameLength == RemainingName->Length) {
356
357 RemainingName->Length = 0;
358
359 //
360 // Otherwise we adjust the string by this amount plus the separating character.
361 //
362
363 } else {
364
365 RemainingName->MaximumLength -= (USHORT) (NameLength + sizeof( WCHAR ));
366 RemainingName->Length -= (USHORT) (NameLength + sizeof( WCHAR ));
367 RemainingName->Buffer = Add2Ptr( RemainingName->Buffer,
368 NameLength + sizeof( WCHAR ),
369 PWCHAR );
370 }
371
372 return;
373 }
374
375 \f
376 BOOLEAN
377 CdIsLegalName (
378 _In_ PIRP_CONTEXT IrpContext,
379 _In_ PUNICODE_STRING FileName
380 )
381
382 /*++
383
384 Routine Description:
385
386 This routine checks if the name is a legal ISO 9660 name.
387
388 Arguments:
389
390 FileName - String of bytes containing the name.
391
392 Return Value:
393
394 BOOLEAN - TRUE if this name is a legal, FALSE otherwise.
395
396 --*/
397
398 {
399 PWCHAR Wchar;
400
401 PAGED_CODE();
402
403 UNREFERENCED_PARAMETER( IrpContext );
404
405 //
406 // Check if name corresponds to a legal file name.
407 //
408
409 for (Wchar = FileName->Buffer;
410 Wchar < Add2Ptr( FileName->Buffer, FileName->Length, PWCHAR );
411 Wchar++) {
412
413 #ifndef __REACTOS__
414 if ((*Wchar < 0xff) &&
415 #else
416 //
417 // Check whether ASCII characters are legal.
418 // We will consider the rest of the characters
419 // (extended ASCII and unicode) as legal.
420 //
421
422 if ((*Wchar < 0x80) &&
423 #endif
424 !FsRtlIsAnsiCharacterLegalHpfs( *Wchar, FALSE ) &&
425 (*Wchar != L'"') &&
426 (*Wchar != L'<') &&
427 (*Wchar != L'>') &&
428 (*Wchar != L'|')) {
429
430 return FALSE;
431 }
432 }
433
434 return TRUE;
435 }
436
437 \f
438 BOOLEAN
439 CdIs8dot3Name (
440 _In_ PIRP_CONTEXT IrpContext,
441 _In_ UNICODE_STRING FileName
442 )
443
444 /*++
445
446 Routine Description:
447
448 This routine checks if the name follows the 8.3 name conventions. We check for
449 the name length and whether the characters are valid.
450
451 Arguments:
452
453 FileName - String of bytes containing the name.
454
455 Return Value:
456
457 BOOLEAN - TRUE if this name is a legal 8.3 name, FALSE otherwise.
458
459 --*/
460
461 {
462 CHAR DbcsNameBuffer[ BYTE_COUNT_8_DOT_3 ];
463 STRING DbcsName = {0};
464
465 PWCHAR NextWchar;
466 ULONG Count;
467
468 ULONG DotCount = 0;
469 BOOLEAN LastCharDot = FALSE;
470
471 PAGED_CODE();
472
473 UNREFERENCED_PARAMETER( IrpContext );
474
475 //
476 // The length must be less than 24 bytes.
477 //
478
479 NT_ASSERT( FileName.Length != 0 );
480 if (FileName.Length > BYTE_COUNT_8_DOT_3) {
481
482 return FALSE;
483 }
484
485 //
486 // Walk though and check for a space character.
487 //
488
489 NextWchar = FileName.Buffer;
490 Count = 0;
491
492 do {
493
494 //
495 // No spaces allowed.
496 //
497
498 if (*NextWchar == L' ') { return FALSE; }
499
500 if (*NextWchar == L'.') {
501
502 //
503 // Not an 8.3 name if more than 1 dot or more than 8 characters
504 // remaining. (It is legal for the dot to be in the ninth
505 // position)
506 //
507
508 if ((DotCount > 0) ||
509 (Count > 8 * sizeof( WCHAR ))) {
510
511 return FALSE;
512 }
513
514 DotCount += 1;
515 LastCharDot = TRUE;
516
517 } else {
518
519 LastCharDot = FALSE;
520 }
521
522 Count += 2;
523 NextWchar += 1;
524
525 } while (Count < FileName.Length);
526
527 //
528 // Go ahead and truncate the dot if at the end.
529 //
530
531 if (LastCharDot) {
532
533 FileName.Length -= sizeof( WCHAR );
534 }
535
536 //
537 // Create an Oem name to use to check for a valid short name.
538 //
539
540 DbcsName.MaximumLength = BYTE_COUNT_8_DOT_3;
541 DbcsName.Buffer = DbcsNameBuffer;
542
543 if (!NT_SUCCESS( RtlUnicodeStringToCountedOemString( &DbcsName,
544 &FileName,
545 FALSE ))) {
546
547 return FALSE;
548 }
549
550 //
551 // We have now initialized the Oem string. Call the FsRtl package to check for a
552 // valid FAT name.
553 //
554
555 return FsRtlIsFatDbcsLegal( DbcsName, FALSE, FALSE, FALSE );
556 }
557
558 \f
559 VOID
560 CdGenerate8dot3Name (
561 _In_ PIRP_CONTEXT IrpContext,
562 _In_ PUNICODE_STRING FileName,
563 _In_ ULONG DirentOffset,
564 _Out_writes_bytes_to_(BYTE_COUNT_8_DOT_3, *ShortByteCount) PWCHAR ShortFileName,
565 _Out_ PUSHORT ShortByteCount
566 )
567
568 /*++
569
570 Routine Description:
571
572 This routine is called to generate a short name from the given long name. We will
573 generate a short name from the given long name.
574
575 We go through the following steps to make this conversion.
576
577 1 - Generate the generic short name. This will also be in unicode format.
578
579 2 - Build the string representation of the dirent offset.
580
581 3 - Build the biased short name string by combining the generic short name with
582 the dirent offset string.
583
584 4 - Copy the final unicode string back to our caller's buffer.
585
586 Arguments:
587
588 FileName - String of bytes containing the name.
589
590 DirentOffset - Offset in the directory for this filename. We incorporate the offset into
591 the short name by dividing this by 32 and prepending a tilde character to the
592 digit character. We then append this to the base of the generated short name.
593
594 ShortFileName - Pointer to the buffer to store the short name into.
595
596 ShortByteCount - Address to store the number of bytes in the short name.
597
598 Return Value:
599
600 None.
601
602 --*/
603
604 {
605 NTSTATUS Status;
606
607 UNICODE_STRING ShortName;
608 UNICODE_STRING BiasedShortName;
609 WCHAR ShortNameBuffer[ BYTE_COUNT_8_DOT_3 / sizeof( WCHAR ) ] = {0};
610 WCHAR BiasedShortNameBuffer[ BYTE_COUNT_8_DOT_3 / sizeof( WCHAR ) ];
611
612 GENERATE_NAME_CONTEXT NameContext;
613
614 ULONG BiasedDirentOffset;
615
616 ULONG MaximumBaseBytes;
617 ULONG BaseNameOffset;
618
619 PWCHAR NextWchar;
620 WCHAR ThisWchar;
621 USHORT Length;
622
623 BOOLEAN FoundTilde = FALSE;
624
625 OEM_STRING OemName = {0};
626 USHORT OemNameOffset = 0;
627 BOOLEAN OverflowBuffer = FALSE;
628
629 PAGED_CODE();
630
631 //
632 // Initialize the short string to use the input buffer.
633 //
634
635 ShortName.Buffer = ShortNameBuffer;
636 ShortName.MaximumLength = BYTE_COUNT_8_DOT_3;
637
638 //
639 // Initialize the name context.
640 //
641
642 RtlZeroMemory( &NameContext, sizeof( GENERATE_NAME_CONTEXT ));
643
644 //
645 // We now have the unicode name for the input string. Go ahead and generate
646 // the short name.
647 //
648
649 RtlGenerate8dot3Name( FileName, TRUE, &NameContext, &ShortName );
650
651 //
652 // We now have the generic short name. We want incorporate the dirent offset
653 // into the name in order to reduce the chance of name conflicts. We will use
654 // a tilde character followed by a character representation of the dirent offset.
655 // This will be the hexadecimal representation of the dirent offset in the directory.
656 // It is actuall this offset divided by 32 since we don't need the full
657 // granularity.
658 //
659
660 BiasedDirentOffset = DirentOffset >> SHORT_NAME_SHIFT;
661
662 //
663 // Point to a local buffer to store the offset string. We start
664 // at the end of the buffer and work backwards.
665 //
666
667 NextWchar = Add2Ptr( BiasedShortNameBuffer,
668 BYTE_COUNT_8_DOT_3,
669 PWCHAR );
670
671 BiasedShortName.MaximumLength = BYTE_COUNT_8_DOT_3;
672
673 //
674 // Generate an OEM version of the string so that we can check for double
675 // byte characters.
676 //
677
678 Status = RtlUnicodeStringToOemString(&OemName, &ShortName, TRUE);
679
680 //
681 // If this failed, bail out. Don't expect any problems other than no mem.
682 //
683
684 if (!NT_SUCCESS( Status)) {
685
686 NT_ASSERT( STATUS_INSUFFICIENT_RESOURCES == Status);
687 CdRaiseStatus( IrpContext, Status);
688 }
689
690 Length = 0;
691
692 //
693 // Now add the characters for the dirent offset. We need to start
694 // from the least significant digit and work backwards.
695 //
696
697 do {
698
699 NextWchar -= 1;
700
701 ThisWchar = (WCHAR) (BiasedDirentOffset & 0x0000000f);
702
703 //
704 // Store in the next character. Bias against either '0' or 'A'
705 //
706
707 if (ThisWchar <= 9) {
708
709 *NextWchar = ThisWchar + L'0';
710
711 } else {
712
713 *NextWchar = ThisWchar + L'A' - 0xA;
714 }
715
716 Length += sizeof( WCHAR );
717
718 //
719 // Shift out the low 4 bits of the offset.
720 //
721
722 BiasedDirentOffset >>= 4;
723
724 } while (BiasedDirentOffset != 0);
725
726 //
727 // Now store in the tilde character.
728 //
729
730 NextWchar -= 1;
731 *NextWchar = L'~';
732 Length += sizeof( WCHAR );
733
734 //
735 // Set the length of this string.
736 //
737
738 BiasedShortName.Length = Length;
739 BiasedShortName.Buffer = NextWchar;
740
741 //
742 // Figure out the maximum number of characters we can copy of the base
743 // name. We subract the number of characters in the dirent string from 8.
744 // We will copy this many characters or stop when we reach a '.' character
745 // or a '~' character in the name.
746 //
747
748 MaximumBaseBytes = 16 - Length;
749
750 BaseNameOffset = 0;
751
752 //
753 // Keep copying from the base name until we hit a '.', '~' or the end of
754 // the short name.
755 //
756
757 NextWchar = ShortFileName;
758 Length = 0;
759
760 while ((BaseNameOffset < ShortName.Length) &&
761 (ShortName.Buffer[BaseNameOffset / 2] != L'.')) {
762
763 //
764 // Remember if we found a tilde character in the short name,
765 // so we don't copy it or anything following it.
766 //
767
768 if (ShortName.Buffer[BaseNameOffset / 2] == L'~') {
769
770 FoundTilde = TRUE;
771 }
772
773 //
774 // We need to consider the DBCS code page, because Unicode characters
775 // may use 2 bytes as DBCS characters.
776 //
777
778 #ifdef _MSC_VER
779 #pragma prefast(push)
780 #pragma prefast(suppress:26014, "OemNameOffset <= BaseNameOffset throughout this loop; OemName buffer previously allocated based on ShortName's length.")
781 #endif
782 if (FsRtlIsLeadDbcsCharacter(OemName.Buffer[OemNameOffset])) {
783 #ifdef _MSC_VER
784 #pragma prefast(pop)
785 #endif
786
787 OemNameOffset += 2;
788
789 if ((OemNameOffset + (BiasedShortName.Length / sizeof(WCHAR))) > 8) {
790
791 OverflowBuffer = TRUE;
792 }
793 }
794 else {
795
796 OemNameOffset++;
797 }
798
799 //
800 // Only copy the bytes if we still have space for the dirent string.
801 //
802
803 if (!FoundTilde && !OverflowBuffer && (BaseNameOffset < MaximumBaseBytes)) {
804
805 *NextWchar = ShortName.Buffer[BaseNameOffset / 2];
806 Length += sizeof( WCHAR );
807 NextWchar += 1;
808 }
809
810 BaseNameOffset += 2;
811 }
812
813 RtlFreeOemString(&OemName);
814
815 //
816 // Now copy the dirent string into the biased name buffer.
817 //
818
819 #ifdef _MSC_VER
820 #pragma prefast(push)
821 #endif
822 RtlCopyMemory( NextWchar,
823 BiasedShortName.Buffer,
824 BiasedShortName.Length );
825 #ifdef _MSC_VER
826 #pragma prefast(pop)
827 #endif
828
829 Length += BiasedShortName.Length;
830 NextWchar += (BiasedShortName.Length / sizeof( WCHAR ));
831
832 //
833 // Now copy any remaining bytes over to the biased short name.
834 //
835
836 if (BaseNameOffset != ShortName.Length) {
837
838 RtlCopyMemory( NextWchar,
839 &ShortName.Buffer[BaseNameOffset / 2],
840 ShortName.Length - BaseNameOffset );
841
842 Length += (ShortName.Length - (USHORT) BaseNameOffset);
843 }
844
845 //
846 // The final short name is stored in the user's buffer.
847 //
848
849 *ShortByteCount = Length;
850 }
851
852 \f
853 BOOLEAN
854 CdIsNameInExpression (
855 _In_ PIRP_CONTEXT IrpContext,
856 _In_ PCD_NAME CurrentName,
857 _In_ PCD_NAME SearchExpression,
858 _In_ ULONG WildcardFlags,
859 _In_ BOOLEAN CheckVersion
860 )
861
862 /*++
863
864 Routine Description:
865
866 This routine will compare two CdName strings. We assume that if this
867 is to be a case-insensitive search then they are already upcased.
868
869 We compare the filename portions of the name and if they match we
870 compare the version strings if requested.
871
872 Arguments:
873
874 CurrentName - Filename from the disk.
875
876 SearchExpression - Filename expression to use for match.
877
878 WildcardFlags - Flags field which indicates which parts of the
879 search expression might have wildcards. These flags are the
880 same as in the Ccb flags field.
881
882 CheckVersion - Indicates whether we should check both the name and the
883 version strings or just the name.
884
885 Return Value:
886
887 BOOLEAN - TRUE if the expressions match, FALSE otherwise.
888
889 --*/
890
891 {
892 BOOLEAN Match = TRUE;
893 PAGED_CODE();
894
895 UNREFERENCED_PARAMETER( IrpContext );
896
897 //
898 // If there are wildcards in the expression then we call the
899 // appropriate FsRtlRoutine.
900 //
901
902 if (FlagOn( WildcardFlags, CCB_FLAG_ENUM_NAME_EXP_HAS_WILD )) {
903
904 Match = FsRtlIsNameInExpression( &SearchExpression->FileName,
905 &CurrentName->FileName,
906 FALSE,
907 NULL );
908
909 //
910 // Otherwise do a direct memory comparison for the name string.
911 //
912
913 } else {
914
915 if ((CurrentName->FileName.Length != SearchExpression->FileName.Length) ||
916 (!RtlEqualMemory( CurrentName->FileName.Buffer,
917 SearchExpression->FileName.Buffer,
918 CurrentName->FileName.Length ))) {
919
920 Match = FALSE;
921 }
922 }
923
924 //
925 // Check the version numbers if requested by the user and we have a
926 // match on the name and the version number is present.
927 //
928
929 if (Match && CheckVersion && SearchExpression->VersionString.Length &&
930 !FlagOn( WildcardFlags, CCB_FLAG_ENUM_VERSION_MATCH_ALL )) {
931
932 //
933 // If there are wildcards in the expression then call the
934 // appropriate search expression.
935 //
936
937 if (FlagOn( WildcardFlags, CCB_FLAG_ENUM_VERSION_EXP_HAS_WILD )) {
938
939 Match = FsRtlIsNameInExpression( &SearchExpression->VersionString,
940 &CurrentName->VersionString,
941 FALSE,
942 NULL );
943
944 //
945 // Otherwise do a direct memory comparison for the name string.
946 //
947
948 } else {
949
950 if ((CurrentName->VersionString.Length != SearchExpression->VersionString.Length) ||
951 (!RtlEqualMemory( CurrentName->VersionString.Buffer,
952 SearchExpression->VersionString.Buffer,
953 CurrentName->VersionString.Length ))) {
954
955 Match = FALSE;
956 }
957 }
958 }
959
960 return Match;
961 }
962
963 \f
964 ULONG
965 CdShortNameDirentOffset (
966 _In_ PIRP_CONTEXT IrpContext,
967 _In_ PUNICODE_STRING Name
968 )
969
970 /*++
971
972 Routine Description:
973
974 This routine is called to examine a name to see if the dirent offset string is contained.
975 This consists of a tilde character followed by the offset represented as a hexadecimal
976 characters. We don't do any other checks to see if this is a short name. We
977 catch that later outside this routine.
978
979 Arguments:
980
981 Name - This is the CdName to examine.
982
983 Return Value:
984
985 ULONG - MAXULONG if there is no valid dirent offset string embedded, otherwise the
986 convert the value to numeric form.
987
988 --*/
989
990 {
991 ULONG ResultOffset = MAXULONG;
992 ULONG RemainingByteCount = Name->Length;
993
994 BOOLEAN FoundTilde = FALSE;
995
996 PWCHAR NextWchar;
997
998 PAGED_CODE();
999
1000 UNREFERENCED_PARAMETER( IrpContext );
1001
1002 //
1003 // Walk through the name until we either reach the end of the name
1004 // or find a tilde character.
1005 //
1006
1007 for (NextWchar = Name->Buffer;
1008 RemainingByteCount != 0;
1009 NextWchar += 1, RemainingByteCount -= sizeof( WCHAR )) {
1010
1011 //
1012 // Check if this is a dot. Stop constructing any string if
1013 // we found a dot.
1014 //
1015
1016 if (*NextWchar == L'.') {
1017
1018 break;
1019 }
1020
1021 //
1022 // If we already found a tilde then check this character as a
1023 // valid character. It must be a digit or A to F.
1024 //
1025
1026 if (FoundTilde) {
1027
1028 if ((*NextWchar < L'0') ||
1029 (*NextWchar > L'F') ||
1030 ((*NextWchar > L'9') && (*NextWchar < 'A'))) {
1031
1032 ResultOffset = MAXULONG;
1033 break;
1034 }
1035
1036 //
1037 // Shift the result by 4 bits and add in this new character.
1038 //
1039
1040 ResultOffset <<= 4;
1041
1042 if (*NextWchar < L'A') {
1043
1044 ResultOffset += *NextWchar - L'0';
1045
1046 } else {
1047
1048 ResultOffset += (*NextWchar - L'A') + 10;
1049 }
1050
1051 continue;
1052 }
1053
1054 //
1055 // If this is a tilde then start building the dirent string.
1056 //
1057
1058 if (*NextWchar == L'~') {
1059
1060 FoundTilde = TRUE;
1061 ResultOffset = 0;
1062 }
1063 }
1064
1065 return ResultOffset;
1066 }
1067
1068 \f
1069 //
1070 // Local support routine
1071 //
1072
1073 FSRTL_COMPARISON_RESULT
1074 CdFullCompareNames (
1075 _In_ PIRP_CONTEXT IrpContext,
1076 _In_ PUNICODE_STRING NameA,
1077 _In_ PUNICODE_STRING NameB
1078 )
1079
1080 /*++
1081
1082 Routine Description:
1083
1084 This function compares two names as fast as possible. Note that since
1085 this comparison is case sensitive we can do a direct memory comparison.
1086
1087 Arguments:
1088
1089 NameA & NameB - The names to compare.
1090
1091 Return Value:
1092
1093 COMPARISON - returns
1094
1095 LessThan if NameA < NameB lexicalgraphically,
1096 GreaterThan if NameA > NameB lexicalgraphically,
1097 EqualTo if NameA is equal to NameB
1098
1099 --*/
1100
1101 {
1102 SIZE_T i;
1103 ULONG MinLength = NameA->Length;
1104 FSRTL_COMPARISON_RESULT Result = LessThan;
1105
1106 PAGED_CODE();
1107
1108 UNREFERENCED_PARAMETER( IrpContext );
1109
1110 //
1111 // Figure out the minimum of the two lengths
1112 //
1113
1114 if (NameA->Length > NameB->Length) {
1115
1116 MinLength = NameB->Length;
1117 Result = GreaterThan;
1118
1119 } else if (NameA->Length == NameB->Length) {
1120
1121 Result = EqualTo;
1122 }
1123
1124 //
1125 // Loop through looking at all of the characters in both strings
1126 // testing for equalilty, less than, and greater than
1127 //
1128
1129 i = RtlCompareMemory( NameA->Buffer, NameB->Buffer, MinLength );
1130
1131 if (i < MinLength) {
1132
1133 //
1134 // We know the offset of the first character which is different.
1135 //
1136
1137 return ((NameA->Buffer[ i / 2 ] < NameB->Buffer[ i / 2 ]) ?
1138 LessThan :
1139 GreaterThan);
1140 }
1141
1142 //
1143 // The names match up to the length of the shorter string.
1144 // The shorter string lexically appears first.
1145 //
1146
1147 return Result;
1148 }
1149
1150
1151