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