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