3 Copyright (c) 1991-2000 Microsoft Corporation
11 This module implements the Cdfs Name support routines
19 // The Bug check file id for this module
22 #define BugCheckFileId (CDFS_BUG_CHECK_NAMESUP)
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)
38 CdConvertNameToCdName (
39 IN PIRP_CONTEXT IrpContext
,
40 IN OUT PCD_NAME CdName
47 This routine is called to convert a string of bytes into a CdName.
49 The full name is already in the CdName structure in the FileName field.
50 We split this into the filename and version strings.
54 CdName - Pointer to CdName structure to update.
64 PWCHAR CurrentCharacter
= CdName
->FileName
.Buffer
;
69 // Look for a separator character.
72 while ((NameLength
< CdName
->FileName
.Length
) &&
73 (*CurrentCharacter
!= L
';')) {
75 CurrentCharacter
+= 1;
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.
84 CdName
->VersionString
.Length
= 0;
85 if (NameLength
+ sizeof( WCHAR
) < CdName
->FileName
.Length
) {
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
),
95 // Now update the filename portion of the name.
98 CdName
->FileName
.Length
= (USHORT
) NameLength
;
105 CdConvertBigToLittleEndian (
106 IN PIRP_CONTEXT IrpContext
,
109 OUT PCHAR LittleEndian
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.
123 BigEndian - Pointer to the string of big endian characters.
125 ByteCount - Number of unicode characters in this string.
127 LittleEndian - Pointer to array to store the little endian characters.
136 ULONG RemainingByteCount
= ByteCount
;
138 PCHAR Source
= BigEndian
;
139 PCHAR Destination
= LittleEndian
;
144 // If the byte count isn't an even number then the disk is corrupt.
147 if (FlagOn( ByteCount
, 1 )) {
149 CdRaiseStatus( IrpContext
, STATUS_DISK_CORRUPT_ERROR
);
153 // Start by copy the low-order bytes into the correct position. Do
154 // this by skipping the first byte in the BigEndian string.
157 RtlCopyMemory( Destination
,
159 RemainingByteCount
- 1 );
162 // Now move the high-order bytes into position.
167 while (RemainingByteCount
!= 0) {
169 *Destination
= *Source
;
174 RemainingByteCount
-= 2;
183 IN PIRP_CONTEXT IrpContext
,
185 IN OUT PCD_NAME UpcaseName
192 This routine is called to upcase a CdName structure. We will do both
193 the filename and version strings.
197 Name - This is the mixed case version of the name.
199 UpcaseName - This is the destination for the upcase operation.
203 None. This routine will raise all errors.
209 //PVOID NewBuffer; /* ReactOS Change: GCC Uninitialized variable */
214 // If the name structures are different then initialize the different components.
217 if (Name
!= UpcaseName
) {
220 // Initialize the version string portion of the name.
223 UpcaseName
->VersionString
.Length
= 0;
225 if (Name
->VersionString
.Length
!= 0) {
227 UpcaseName
->VersionString
.MaximumLength
=
228 UpcaseName
->VersionString
.Length
= Name
->VersionString
.Length
;
231 // Initially set the buffer to point to where we need to insert
235 UpcaseName
->VersionString
.Buffer
= Add2Ptr( UpcaseName
->FileName
.Buffer
,
236 Name
->FileName
.Length
,
240 // We are currently pointing to the location to store the separator.
241 // Store the ';' and then move to the next character to
245 *(UpcaseName
->VersionString
.Buffer
) = L
';';
247 UpcaseName
->VersionString
.Buffer
+= 1;
252 // Upcase the string using the correct upcase routine.
255 Status
= RtlUpcaseUnicodeString( &UpcaseName
->FileName
,
260 // This should never fail.
263 ASSERT( Status
== STATUS_SUCCESS
);
265 if (Name
->VersionString
.Length
!= 0) {
267 Status
= RtlUpcaseUnicodeString( &UpcaseName
->VersionString
,
268 &Name
->VersionString
,
272 // This should never fail.
275 ASSERT( Status
== STATUS_SUCCESS
);
284 IN PIRP_CONTEXT IrpContext
,
285 IN OUT PUNICODE_STRING RemainingName
,
286 OUT PUNICODE_STRING FinalName
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.
299 RemainingName - Remaining name.
301 FinalName - Location to store next component of name.
316 // Find the offset of the next component separators.
319 for (NameLength
= 0, NextWchar
= RemainingName
->Buffer
;
320 (NameLength
< RemainingName
->Length
) && (*NextWchar
!= L
'\\');
321 NameLength
+= sizeof( WCHAR
) , NextWchar
+= 1);
324 // Adjust all the strings by this amount.
327 FinalName
->Buffer
= RemainingName
->Buffer
;
329 FinalName
->MaximumLength
= FinalName
->Length
= (USHORT
) NameLength
;
332 // If this is the last component then set the RemainingName lengths to zero.
335 if (NameLength
== RemainingName
->Length
) {
337 RemainingName
->Length
= 0;
340 // Otherwise we adjust the string by this amount plus the separating character.
345 RemainingName
->MaximumLength
-= (USHORT
) (NameLength
+ sizeof( WCHAR
));
346 RemainingName
->Length
-= (USHORT
) (NameLength
+ sizeof( WCHAR
));
347 RemainingName
->Buffer
= Add2Ptr( RemainingName
->Buffer
,
348 NameLength
+ sizeof( WCHAR
),
358 IN PIRP_CONTEXT IrpContext
,
359 IN UNICODE_STRING FileName
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.
371 FileName - String of bytes containing the name.
375 BOOLEAN - TRUE if this name is a legal 8.3 name, FALSE otherwise.
380 CHAR DbcsNameBuffer
[ BYTE_COUNT_8_DOT_3
];
387 BOOLEAN LastCharDot
= FALSE
;
392 // The length must be less than 24 bytes.
395 ASSERT( FileName
.Length
!= 0 );
396 if (FileName
.Length
> BYTE_COUNT_8_DOT_3
) {
402 // Walk though and check for a space character.
405 NextWchar
= FileName
.Buffer
;
411 // No spaces allowed.
414 if (*NextWchar
== L
' ') { return FALSE
; }
416 if (*NextWchar
== L
'.') {
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
424 if ((DotCount
> 0) ||
425 (Count
> 8 * sizeof( WCHAR
))) {
441 } while (Count
< FileName
.Length
);
444 // Go ahead and truncate the dot if at the end.
449 FileName
.Length
-= sizeof( WCHAR
);
453 // Create an Oem name to use to check for a valid short name.
456 DbcsName
.MaximumLength
= BYTE_COUNT_8_DOT_3
;
457 DbcsName
.Buffer
= DbcsNameBuffer
;
459 if (!NT_SUCCESS( RtlUnicodeStringToCountedOemString( &DbcsName
,
467 // We have now initialized the Oem string. Call the FsRtl package to check for a
471 return FsRtlIsFatDbcsLegal( DbcsName
, FALSE
, FALSE
, FALSE
);
476 CdGenerate8dot3Name (
477 IN PIRP_CONTEXT IrpContext
,
478 IN PUNICODE_STRING FileName
,
479 IN ULONG DirentOffset
,
480 OUT PWCHAR ShortFileName
,
481 OUT PUSHORT ShortByteCount
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.
491 We go through the following steps to make this conversion.
493 1 - Generate the generic short name. This will also be in unicode format.
495 2 - Build the string representation of the dirent offset.
497 3 - Build the biased short name string by combining the generic short name with
498 the dirent offset string.
500 4 - Copy the final unicode string back to our caller's buffer.
504 FileName - String of bytes containing the name.
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.
510 ShortFileName - Pointer to the buffer to store the short name into.
512 ShortByteCount - Address to store the number of bytes in the short name.
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
) ];
527 GENERATE_NAME_CONTEXT NameContext
;
529 ULONG BiasedDirentOffset
;
531 ULONG MaximumBaseBytes
;
532 ULONG BaseNameOffset
;
538 BOOLEAN FoundTilde
= FALSE
;
541 USHORT OemNameOffset
= 0;
542 BOOLEAN OverflowBuffer
= FALSE
;
547 // Initialize the short string to use the input buffer.
550 ShortName
.Buffer
= ShortNameBuffer
;
551 ShortName
.MaximumLength
= BYTE_COUNT_8_DOT_3
;
554 // Initialize the name context.
557 RtlZeroMemory( &NameContext
, sizeof( GENERATE_NAME_CONTEXT
));
560 // We now have the unicode name for the input string. Go ahead and generate
564 RtlGenerate8dot3Name( FileName
, TRUE
, &NameContext
, &ShortName
);
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
575 BiasedDirentOffset
= DirentOffset
>> SHORT_NAME_SHIFT
;
578 // Point to a local buffer to store the offset string. We start
579 // at the end of the buffer and work backwards.
582 NextWchar
= Add2Ptr( BiasedShortNameBuffer
,
586 BiasedShortName
.MaximumLength
= BYTE_COUNT_8_DOT_3
;
589 // Generate an OEM version of the string so that we can check for double
593 Status
= RtlUnicodeStringToOemString(&OemName
, &ShortName
, TRUE
);
595 if (!NT_SUCCESS( Status
)) {
597 CdRaiseStatus( IrpContext
, Status
);
603 // Now add the characters for the dirent offset. We need to start
604 // from the least significant digit and work backwards.
611 ThisWchar
= (WCHAR
) (BiasedDirentOffset
& 0x0000000f);
614 // Store in the next character. Bias against either '0' or 'A'
617 if (ThisWchar
<= 9) {
619 *NextWchar
= ThisWchar
+ L
'0';
623 *NextWchar
= ThisWchar
+ L
'A' - 0xA;
626 Length
+= sizeof( WCHAR
);
629 // Shift out the low 4 bits of the offset.
632 BiasedDirentOffset
>>= 4;
634 } while (BiasedDirentOffset
!= 0);
637 // Now store in the tilde character.
642 Length
+= sizeof( WCHAR
);
645 // Set the length of this string.
648 BiasedShortName
.Length
= Length
;
649 BiasedShortName
.Buffer
= NextWchar
;
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.
658 MaximumBaseBytes
= 16 - Length
;
663 // Keep copying from the base name until we hit a '.', '~' or the end of
667 NextWchar
= ShortFileName
;
670 while ((BaseNameOffset
< ShortName
.Length
) &&
671 (ShortName
.Buffer
[BaseNameOffset
/ 2] != L
'.')) {
674 // Remember if we found a tilde character in the short name,
675 // so we don't copy it or anything following it.
678 if (ShortName
.Buffer
[BaseNameOffset
/ 2] == L
'~') {
684 // We need to consider the DBCS code page, because Unicode characters
685 // may use 2 bytes as DBCS characters.
688 if (FsRtlIsLeadDbcsCharacter(OemName
.Buffer
[OemNameOffset
])) {
697 if ((OemNameOffset
+ (BiasedShortName
.Length
/ sizeof(WCHAR
))) > 8) {
699 OverflowBuffer
= TRUE
;
703 // Only copy the bytes if we still have space for the dirent string.
706 if (!FoundTilde
&& !OverflowBuffer
&& (BaseNameOffset
< MaximumBaseBytes
)) {
708 *NextWchar
= ShortName
.Buffer
[BaseNameOffset
/ 2];
709 Length
+= sizeof( WCHAR
);
716 RtlFreeOemString(&OemName
);
719 // Now copy the dirent string into the biased name buffer.
722 RtlCopyMemory( NextWchar
,
723 BiasedShortName
.Buffer
,
724 BiasedShortName
.Length
);
726 Length
+= BiasedShortName
.Length
;
727 NextWchar
+= (BiasedShortName
.Length
/ sizeof( WCHAR
));
730 // Now copy any remaining bytes over to the biased short name.
733 if (BaseNameOffset
!= ShortName
.Length
) {
735 RtlCopyMemory( NextWchar
,
736 &ShortName
.Buffer
[BaseNameOffset
/ 2],
737 ShortName
.Length
- BaseNameOffset
);
739 Length
+= (ShortName
.Length
- (USHORT
) BaseNameOffset
);
743 // The final short name is stored in the user's buffer.
746 *ShortByteCount
= Length
;
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
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.
768 We compare the filename portions of the name and if they match we
769 compare the version strings if requested.
773 CurrentName - Filename from the disk.
775 SearchExpression - Filename expression to use for match.
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.
781 CheckVersion - Indicates whether we should check both the name and the
782 version strings or just the name.
786 BOOLEAN - TRUE if the expressions match, FALSE otherwise.
791 BOOLEAN Match
= TRUE
;
795 // If there are wildcards in the expression then we call the
796 // appropriate FsRtlRoutine.
799 if (FlagOn( WildcardFlags
, CCB_FLAG_ENUM_NAME_EXP_HAS_WILD
)) {
801 Match
= FsRtlIsNameInExpression( &SearchExpression
->FileName
,
802 &CurrentName
->FileName
,
807 // Otherwise do a direct memory comparison for the name string.
812 if ((CurrentName
->FileName
.Length
!= SearchExpression
->FileName
.Length
) ||
813 (!RtlEqualMemory( CurrentName
->FileName
.Buffer
,
814 SearchExpression
->FileName
.Buffer
,
815 CurrentName
->FileName
.Length
))) {
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.
826 if (Match
&& CheckVersion
&& SearchExpression
->VersionString
.Length
&&
827 !FlagOn( WildcardFlags
, CCB_FLAG_ENUM_VERSION_MATCH_ALL
)) {
830 // If there are wildcards in the expression then call the
831 // appropriate search expression.
834 if (FlagOn( WildcardFlags
, CCB_FLAG_ENUM_VERSION_EXP_HAS_WILD
)) {
836 Match
= FsRtlIsNameInExpression( &SearchExpression
->VersionString
,
837 &CurrentName
->VersionString
,
842 // Otherwise do a direct memory comparison for the name string.
847 if ((CurrentName
->VersionString
.Length
!= SearchExpression
->VersionString
.Length
) ||
848 (!RtlEqualMemory( CurrentName
->VersionString
.Buffer
,
849 SearchExpression
->VersionString
.Buffer
,
850 CurrentName
->VersionString
.Length
))) {
862 CdShortNameDirentOffset (
863 IN PIRP_CONTEXT IrpContext
,
864 IN PUNICODE_STRING Name
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.
878 Name - This is the CdName to examine.
882 ULONG - MAXULONG if there is no valid dirent offset string embedded, otherwise the
883 convert the value to numeric form.
888 ULONG ResultOffset
= MAXULONG
;
889 ULONG RemainingByteCount
= Name
->Length
;
891 BOOLEAN FoundTilde
= FALSE
;
898 // Walk through the name until we either reach the end of the name
899 // or find a tilde character.
902 for (NextWchar
= Name
->Buffer
;
903 RemainingByteCount
!= 0;
904 NextWchar
+= 1, RemainingByteCount
-= sizeof( WCHAR
)) {
907 // Check if this is a dot. Stop constructing any string if
911 if (*NextWchar
== L
'.') {
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.
923 if ((*NextWchar
< L
'0') ||
924 (*NextWchar
> L
'F') ||
925 ((*NextWchar
> L
'9') && (*NextWchar
< 'A'))) {
927 ResultOffset
= MAXULONG
;
932 // Shift the result by 4 bits and add in this new character.
937 if (*NextWchar
< L
'A') {
939 ResultOffset
+= *NextWchar
- L
'0';
943 ResultOffset
+= (*NextWchar
- L
'A') + 10;
950 // If this is a tilde then start building the dirent string.
953 if (*NextWchar
== L
'~') {
965 // Local support routine
968 FSRTL_COMPARISON_RESULT
970 IN PIRP_CONTEXT IrpContext
,
971 IN PUNICODE_STRING NameA
,
972 IN PUNICODE_STRING NameB
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.
984 NameA & NameB - The names to compare.
990 LessThan if NameA < NameB lexicographically,
991 GreaterThan if NameA > NameB lexicographically,
992 EqualTo if NameA is equal to NameB
998 ULONG MinLength
= NameA
->Length
;
999 FSRTL_COMPARISON_RESULT Result
= LessThan
;
1004 // Figure out the minimum of the two lengths
1007 if (NameA
->Length
> NameB
->Length
) {
1009 MinLength
= NameB
->Length
;
1010 Result
= GreaterThan
;
1012 } else if (NameA
->Length
== NameB
->Length
) {
1018 // Loop through looking at all of the characters in both strings
1019 // testing for equality, less than, and greater than
1022 i
= RtlCompareMemory( NameA
->Buffer
, NameB
->Buffer
, MinLength
);
1024 if (i
< MinLength
) {
1027 // We know the offset of the first character which is different.
1030 return ((NameA
->Buffer
[ i
/ 2 ] < NameB
->Buffer
[ i
/ 2 ]) ?
1036 // The names match up to the length of the shorter string.
1037 // The shorter string lexically appears first.