From e611b132f9b8abe35b362e5870b74bce94a1e58e Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 16 May 2020 20:51:50 -0700 Subject: initial commit --- private/ntos/cntfs/namesup.c | 1114 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1114 insertions(+) create mode 100644 private/ntos/cntfs/namesup.c (limited to 'private/ntos/cntfs/namesup.c') diff --git a/private/ntos/cntfs/namesup.c b/private/ntos/cntfs/namesup.c new file mode 100644 index 000000000..466f1b283 --- /dev/null +++ b/private/ntos/cntfs/namesup.c @@ -0,0 +1,1114 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + NameSup.c + +Abstract: + + This module implements the Ntfs Name support routines + +Author: + + Gary Kimura [GaryKi] & Tom Miller [TomM] 20-Feb-1990 + +Revision History: + +--*/ + +#include "NtfsProc.h" + +#define Dbg (DEBUG_TRACE_NAMESUP) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, NtfsCollateNames) +#pragma alloc_text(PAGE, NtfsIsFatNameValid) +#pragma alloc_text(PAGE, NtfsIsFileNameValid) +#pragma alloc_text(PAGE, NtfsParseName) +#pragma alloc_text(PAGE, NtfsParsePath) +#pragma alloc_text(PAGE, NtfsUpcaseName) +#endif + +#define MAX_CHARS_IN_8_DOT_3 (12) + + +PARSE_TERMINATION_REASON +NtfsParsePath ( + IN UNICODE_STRING Path, + IN BOOLEAN WildCardsPermissible, + OUT PUNICODE_STRING FirstPart, + OUT PNTFS_NAME_DESCRIPTOR Name, + OUT PUNICODE_STRING RemainingPart + ) + +/*++ + +Routine Description: + + This routine takes as input a path. Each component of the path is + checked until either: + + - The end of the path has been reached, or + + - A well formed complex name is excountered, or + + - An illegal character is encountered, or + + - A complex name component is malformed + + At this point the return value is set to one of the three reasons + above, and the arguments are set as follows: + + FirstPart: All the components up to one containing an illegal + character or colon character. May be the whole path. + + Name: The "pieces" of a component containing an illegal + character or colon character. This name is actually + a struncture containing the four pieces of a name, + "file name, attribute type, attribute name, version + number." In the example below, they are shown + separated by plus signs. + + RemainingPart: All the remaining components. + + A preceding or trailing backslash is ignored during processing and + stripped in either FirstPart or RemainingPart. Following are some + examples of this routine's actions. + + Path FirstPart Name Remaining + ================ ========= ============ ========= + + \nt\pri\os \nt\pri\os + + \nt\pri\os\ \nt\pri\os + + nt\pri\os \nt\pri\os + + \nt\pr"\os \nt pr" os + + \nt\pri\os:contr::3\ntfs \nt\pri os + contr + + 3 ntfs + + \nt\pri\os\circle:pict:circ \nt\pri\os circle + pict + circ + +Arguments: + + Path - This unicode string descibes the path to parse. Note that path + here may only describe a single component. + + WildCardsPermissible - This parameter tells us if wild card characters + should be considered legal. + + FirstPart - This unicode string will receive portion of the path, up to + a component boundry, successfully parsed before the parse terminated. + Note that store for this string comes from the Path parameter. + + Name - This is the name we were parsing when we reached our termination + condition. It is a srtucture of strings that receive the file name, + attribute type, attribute name, and version number respectively. + It wil be filled in only to the extent that the parse succeeded. For + example, in the case we encounter an illegal character in the + attribute type field, only the file name field will be filled in. + This may signal a special control file, and this possibility must be + investigated by the file system. + + RemainingPart - This string will receive any portion of the path, starting + at the first component boundry after the termination name, not parsed. + It will often be an empty string. + +ReturnValue: + + An enumerated type with one of the following values: + + EndOfPathReached - The path was fully parsed. Only first part + is filled in. + NonSimpleName - A component of the path containing a legal, + well formed non-simple name was encountered. + IllegalCharacterInName - An illegal character was encountered. Parsing + stops immediately. + MalFormedName - A non-simple name did not conform to the + correct format. This may be a result of too + many fields, or a malformed version number. + AttributeOnly - A component of the path containing a legal + well formed non-simple name was encountered + which does not have a file name. + VersionNumberPresent - A component of the path containing a legal + well formed non-simple name was encountered + which contains a version number. + +--*/ + +{ + UNICODE_STRING FirstName; + + BOOLEAN WellFormed; + BOOLEAN MoreNamesInPath; + BOOLEAN FirstIteration; + BOOLEAN FoundIllegalCharacter; + + PARSE_TERMINATION_REASON TerminationReason; + + PAGED_CODE(); + + // + // Initialize some loacal variables and OUT parameters. + // + + FirstIteration = TRUE; + MoreNamesInPath = TRUE; + + // + // By default, set the returned first part to start at the beginning of + // the input buffer and include a leading backslash. + // + + FirstPart->Buffer = Path.Buffer; + + if (Path.Buffer[0] == L'\\') { + + FirstPart->Length = 2; + FirstPart->MaximumLength = 2; + + } else { + + FirstPart->Length = 0; + FirstPart->MaximumLength = 0; + } + + // + // Do the first check outside the loop in case we are given a backslash + // by itself. + // + + if (FirstPart->Length == Path.Length) { + + RemainingPart->Length = 0; + RemainingPart->Buffer = &Path.Buffer[Path.Length >> 1]; + + return EndOfPathReached; + } + + // + // Crack the path, checking each componant + // + + while (MoreNamesInPath) { + + // + // Clear the flags field in the name descriptor. + // + + Name->FieldsPresent = 0; + + FsRtlDissectName( Path, &FirstName, RemainingPart ); + + MoreNamesInPath = (BOOLEAN)(RemainingPart->Length != 0); + + // + // If this is not the last name in the path, then attributes + // and version numbers are not allowed. If this is the last + // name then propagate the callers arguments. + // + + WellFormed = NtfsParseName( FirstName, + WildCardsPermissible, + &FoundIllegalCharacter, + Name ); + + // + // Check the cases when we will break out of this loop, ie. if the + // the name was not well formed or it was non-simple. + // + + if ( !WellFormed || + (Name->FieldsPresent != FILE_NAME_PRESENT_FLAG) + + // + // TEMPCODE TRAILING_DOT + // + + || (Name->FileName.Length != Name->FileName.MaximumLength) + + ) { + + break; + } + + // + // We will continue parsing this string, so consider the current + // FirstName to be parsed and add it to FirstPart. Also reset + // the Name->FieldsPresent variable. + // + + if ( FirstIteration ) { + + FirstPart->Length += FirstName.Length; + FirstIteration = FALSE; + + } else { + + FirstPart->Length += (sizeof(WCHAR) + FirstName.Length); + } + + FirstPart->MaximumLength = FirstPart->Length; + + Path = *RemainingPart; + } + + // + // At this point FirstPart, Name, and RemainingPart should all be set + // correctly. It remains, only to generate the correct return value. + // + + if ( !WellFormed ) { + + if ( FoundIllegalCharacter ) { + + TerminationReason = IllegalCharacterInName; + + } else { + + TerminationReason = MalFormedName; + } + + } else { + + if ( Name->FieldsPresent == FILE_NAME_PRESENT_FLAG ) { + + // + // TEMPCODE TRAILING_DOT + // + + if (Name->FileName.Length != Name->FileName.MaximumLength) { + + TerminationReason = NonSimpleName; + + } else { + + TerminationReason = EndOfPathReached; + } + + } else if (FlagOn( Name->FieldsPresent, VERSION_NUMBER_PRESENT_FLAG )) { + + TerminationReason = VersionNumberPresent; + + } else if (!FlagOn( Name->FieldsPresent, FILE_NAME_PRESENT_FLAG )) { + + TerminationReason = AttributeOnly; + + } else { + + TerminationReason = NonSimpleName; + } + + } + + return TerminationReason; +} + + +BOOLEAN +NtfsParseName ( + IN UNICODE_STRING Name, + IN BOOLEAN WildCardsPermissible, + OUT PBOOLEAN FoundIllegalCharacter, + OUT PNTFS_NAME_DESCRIPTOR ParsedName + ) + +/*++ + +Routine Description: + + This routine takes as input a single name component. It is processed into + file name, attribute type, attribute name, and version number fields. + + If the name is well formed according to the following rules: + + A. An NTFS name may not contain any of the following characters: + + 0x0000-0x001F " / < > ? | * + + B. An Ntfs name can take any of the following forms: + + ::T + :A + :A:T + N + N:::V + N::T + N::T:V + N:A + N:A::V + N:A:T + N:A:T:V + + If a version number is present, there must be a file name. + We specifically note the legal names without a filename + component (AttributeOnly) and any name with a version number + (VersionNumberPresent). + + Incidently, N corresponds to file name, T to attribute type, A to + attribute name, and V to version number. + + TRUE is returned. If FALSE is returned, then the OUT parameter + FoundIllegalCharacter will be set appropriatly. Note that the buffer + space for ParsedName comes from Name. + +Arguments: + + Name - This is the single path element input name. + + WildCardsPermissible - This determines if wild cards characters should be + considered legal + + FoundIllegalCharacter - This parameter will receive a TRUE if the the + function returns FALSE because of encountering an illegal character. + + ParsedName - Recieves the pieces of the processed name. Note that the + storage for all the string from the input Name. + +ReturnValue: + + TRUE if the Name is well formed, and FALSE otherwise. + + +--*/ + +{ + ULONG Index; + ULONG NameLength; + ULONG FieldCount; + ULONG FieldIndexes[5]; + UCHAR ValidCharFlags = FSRTL_NTFS_LEGAL; + + PULONG Fields; + + BOOLEAN IsNameValid = TRUE; + + PAGED_CODE(); + + // + // Initialize some OUT parameters and local variables. + // + + *FoundIllegalCharacter = FALSE; + + Fields = &ParsedName->FieldsPresent; + + *Fields = 0; + + FieldCount = 1; + + FieldIndexes[0] = 0xFFFFFFFF; // We add on to this later... + + // + // For starters, zero length names are invalid. + // + + NameLength = Name.Length / sizeof(WCHAR); + + if ( NameLength == 0 ) { + + return FALSE; + } + + // + // Now name must correspond to a legal single Ntfs Name. + // + + for (Index = 0; Index < NameLength; Index += 1) { + + WCHAR Char; + + Char = Name.Buffer[Index]; + + // + // First check that file names are well formed in terms of colons. + // + + if ( Char == L':' ) { + + // + // A colon can't be the last character, and we can't have + // more than three colons. + // + + if ( (Index == NameLength - 1) || + (FieldCount >= 4) ) { + + IsNameValid = FALSE; + break; + } + + FieldIndexes[FieldCount] = Index; + + FieldCount += 1; + + ValidCharFlags |= FSRTL_OLE_LEGAL; + + continue; + } + + // + // Now check for wild card characters if they weren't allowed, + // and other illegal characters. + // + + if ( (Char <= 0xff) && + !FsRtlTestAnsiCharacter( Char, TRUE, WildCardsPermissible, ValidCharFlags )) { + + IsNameValid = FALSE; + *FoundIllegalCharacter = TRUE; + break; + } + } + + // + // If we ran into a problem with one of the fields, don't try to load + // up that field into the out parameter. + // + + if ( !IsNameValid ) { + + FieldCount -= 1; + + // + // Set the end of the last field to the current Index. + // + + } else { + + FieldIndexes[FieldCount] = Index; + } + + // + // Now we load up the OUT parmeters + // + + while ( FieldCount != 0 ) { + + ULONG StartIndex; + ULONG EndIndex; + USHORT Length; + + // + // Add one here since this is actually the position of the colon. + // + + StartIndex = FieldIndexes[FieldCount - 1] + 1; + + EndIndex = FieldIndexes[FieldCount]; + + Length = (USHORT)((EndIndex - StartIndex) * sizeof(WCHAR)); + + // + // If this field is empty, skip it + // + + if ( Length == 0 ) { + + FieldCount -= 1; + continue; + } + + // + // Now depending of the field, extract the appropriate information. + // + + if ( FieldCount == 1 ) { + + UNICODE_STRING TempName; + + TempName.Buffer = &Name.Buffer[StartIndex]; + TempName.Length = Length; + TempName.MaximumLength = Length; + + // + // If the resulting length is 0, forget this entry. + // + + if (TempName.Length == 0) { + + FieldCount -= 1; + continue; + } + + SetFlag(*Fields, FILE_NAME_PRESENT_FLAG); + + ParsedName->FileName = TempName; + + } else if ( FieldCount == 2) { + + SetFlag(*Fields, ATTRIBUTE_NAME_PRESENT_FLAG); + + ParsedName->AttributeName.Buffer = &Name.Buffer[StartIndex]; + ParsedName->AttributeName.Length = Length; + ParsedName->AttributeName.MaximumLength = Length; + + } else if ( FieldCount == 3) { + + SetFlag(*Fields, ATTRIBUTE_TYPE_PRESENT_FLAG); + + ParsedName->AttributeType.Buffer = &Name.Buffer[StartIndex]; + ParsedName->AttributeType.Length = Length; + ParsedName->AttributeType.MaximumLength = Length; + + } else if ( FieldCount == 4) { + + ULONG VersionNumber; + STRING VersionNumberA; + UNICODE_STRING VersionNumberU; + + NTSTATUS Status; + UCHAR *endp = NULL; + + VersionNumberU.Buffer = &Name.Buffer[StartIndex]; + VersionNumberU.Length = Length; + VersionNumberU.MaximumLength = Length; + + // + // Note that the resulting Ansi string is null terminated. + // + + Status = RtlUnicodeStringToCountedOemString( &VersionNumberA, + &VersionNumberU, + TRUE ); + + // + // If something went wrong (most likely ran out of pool), raise. + // + + if ( !NT_SUCCESS(Status) ) { + + ExRaiseStatus( Status ); + } + + VersionNumber = 0; //**** strtoul( VersionNumberA.Buffer, &endp, 0 ); + + RtlFreeOemString( &VersionNumberA ); + + if ( (VersionNumber == MAXULONG) || (endp != NULL) ) { + + IsNameValid = FALSE; + + } else { + + SetFlag( *Fields, VERSION_NUMBER_PRESENT_FLAG ); + ParsedName->VersionNumber = VersionNumber; + } + } + + FieldCount -= 1; + } + + // + // Check for special malformed cases. + // + + if (FlagOn( *Fields, VERSION_NUMBER_PRESENT_FLAG ) + && !FlagOn( *Fields, FILE_NAME_PRESENT_FLAG )) { + + IsNameValid = FALSE; + } + + return IsNameValid; +} + + +VOID +NtfsUpcaseName ( + IN PWCH UpcaseTable, + IN ULONG UpcaseTableSize, + IN OUT PUNICODE_STRING Name + ) + +/*++ + +Routine Description: + + This routine upcases a string. + +Arguments: + + UpcaseTable - Pointer to an array of Unicode upcased characters indexed by + the Unicode character to be upcased. + + UpcaseTableSize - Size of the Upcase table in unicode characters + + Name - Supplies the string to upcase + +Return Value: + + None. + +--*/ + +{ + ULONG i; + ULONG Length; + + PAGED_CODE(); + + DebugTrace( +1, Dbg, ("NtfsUpcaseName\n") ); + DebugTrace( 0, Dbg, ("Name = %Z\n", Name) ); + + Length = Name->Length / sizeof(WCHAR); + + for (i=0; i < Length; i += 1) { + + if ((ULONG)Name->Buffer[i] < UpcaseTableSize) { + Name->Buffer[i] = UpcaseTable[ (ULONG)Name->Buffer[i] ]; + } + } + + DebugTrace( 0, Dbg, ("Upcased Name = %Z\n", Name) ); + DebugTrace( -1, Dbg, ("NtfsUpcaseName -> VOID\n") ); + + return; +} + + +FSRTL_COMPARISON_RESULT +NtfsCollateNames ( + IN PWCH UpcaseTable, + IN ULONG UpcaseTableSize, + IN PUNICODE_STRING Expression, + IN PUNICODE_STRING Name, + IN FSRTL_COMPARISON_RESULT WildIs, + IN BOOLEAN IgnoreCase + ) + +/*++ + +Routine Description: + + This routine compares an expression with a name lexigraphically for + LessThan, EqualTo, or GreaterThan. If the expression does not contain + any wildcards, this procedure does a complete comparison. If the + expression does contain wild cards, then the comparison is only done up + to the first wildcard character. Name may not contain wild cards. + The wildcard character compares as less then all other characters. So + the wildcard name "*.*" will always compare less than all all strings. + +Arguments: + + UpcaseTable - Pointer to an array of Unicode upcased characters indexed by + the Unicode character to be upcased. + + UpcaseTableSize - Size of the Upcase table in unicode characters + + Expression - Supplies the first name expression to compare, optionally with + wild cards. Note that caller must have already upcased + the name (this will make lookup faster). + + Name - Supplies the second name to compare - no wild cards allowed. + The caller must have already upcased the name. + + WildIs - Determines what Result is returned if a wild card is encountered + in the Expression String. For example, to find the start of + an expression in the Btree, LessThan should be supplied; then + GreaterThan should be supplied to find the end of the expression + in the tree. + + IgnoreCase - TRUE if case should be ignored for the comparison + +Return Value: + + FSRTL_COMPARISON_RESULT - LessThan if Expression < Name + EqualTo if Expression == Name + GreaterThan if Expression > Name + +--*/ + +{ + WCHAR ConstantChar; + WCHAR ExpressionChar; + + ULONG i; + ULONG Length; + + PAGED_CODE(); + + DebugTrace( +1, Dbg, ("NtfsCollateNames\n") ); + DebugTrace( 0, Dbg, ("Expression = %Z\n", Expression) ); + DebugTrace( 0, Dbg, ("Name = %Z\n", Name) ); + DebugTrace( 0, Dbg, ("WildIs = %08lx\n", WildIs) ); + DebugTrace( 0, Dbg, ("IgnoreCase = %02lx\n", IgnoreCase) ); + + // + // Calculate the length in wchars that we need to compare. This will + // be the smallest length of the two strings. + // + + if (Expression->Length < Name->Length) { + + Length = Expression->Length / sizeof(WCHAR); + + } else { + + Length = Name->Length / sizeof(WCHAR); + } + + // + // Now we'll just compare the elements in the names until we can decide + // their lexicagrahical ordering, checking for wild cards in + // LocalExpression (from Expression). + // + // If an upcase table was specified, the compare is done case insensitive. + // + + for (i = 0; i < Length; i += 1) { + + ConstantChar = Name->Buffer[i]; + ExpressionChar = Expression->Buffer[i]; + + if ( IgnoreCase ) { + + if (ConstantChar < UpcaseTableSize) { + ConstantChar = UpcaseTable[(ULONG)ConstantChar]; + } + if (ExpressionChar < UpcaseTableSize) { + ExpressionChar = UpcaseTable[(ULONG)ExpressionChar]; + } + } + + if ( FsRtlIsUnicodeCharacterWild(ExpressionChar) ) { + + DebugTrace( -1, Dbg, ("NtfsCollateNames -> %08lx (Wild)\n", WildIs) ); + return WildIs; + } + + if ( ExpressionChar < ConstantChar ) { + + DebugTrace( -1, Dbg, ("NtfsCollateNames -> LessThan\n") ); + return LessThan; + } + + if ( ExpressionChar > ConstantChar ) { + + DebugTrace( -1, Dbg, ("NtfsCollateNames -> GreaterThan\n") ); + return GreaterThan; + } + } + + // + // We've gone through the entire short match and they're equal + // so we need to now check which one is shorter, or, if + // LocalExpression is longer, we need to see if the next character is + // wild! (For example, an enumeration of "ABC*", must return + // "ABC". + // + + if (Expression->Length < Name->Length) { + + DebugTrace( -1, Dbg, ("NtfsCollateNames -> LessThan (length)\n") ); + return LessThan; + } + + if (Expression->Length > Name->Length) { + + if (FsRtlIsUnicodeCharacterWild(Expression->Buffer[i])) { + + DebugTrace( -1, Dbg, ("NtfsCollateNames -> %08lx (trailing wild)\n", WildIs) ); + return WildIs; + } + + DebugTrace( -1, Dbg, ("NtfsCollateNames -> GreaterThan (length)\n") ); + return GreaterThan; + } + + DebugTrace( -1, Dbg, ("NtfsCollateNames -> EqualTo\n") ); + return EqualTo; +} + +BOOLEAN +NtfsIsFileNameValid ( + IN PUNICODE_STRING FileName, + IN BOOLEAN WildCardsPermissible + ) + +/*++ + +Routine Description: + + This routine checks if the specified file name is valid. Note that + only the file name part of the name is allowed, ie. no colons are + permitted. + +Arguments: + + FileName - Supplies the name to check. + + WildCardsPermissible - Tells us if wild card characters are ok. + +Return Value: + + BOOLEAN - TRUE if the name is valid, FALSE otherwise. + +--*/ + +{ + ULONG Index; + ULONG NameLength; + BOOLEAN AllDots = TRUE; + BOOLEAN IsNameValid = TRUE; + + PAGED_CODE(); + + DebugTrace( +1, Dbg, ("NtfsIsFileNameValid\n") ); + DebugTrace( 0, Dbg, ("FileName = %Z\n", FileName) ); + DebugTrace( 0, Dbg, ("WildCardsPermissible = %s\n", + WildCardsPermissible ? "TRUE" : "FALSE") ); + + // + // Check if corresponds to a legal single Ntfs Name. + // + + NameLength = FileName->Length / sizeof(WCHAR); + + for (Index = 0; Index < NameLength; Index += 1) { + + WCHAR Char; + + Char = FileName->Buffer[Index]; + + // + // Check for wild card characters if they weren't allowed, and + // check for the other illegal characters including the colon and + // backslash characters since this can only be a single component. + // + + if ( ((Char <= 0xff) && + !FsRtlIsAnsiCharacterLegalNtfs(Char, WildCardsPermissible)) || + (Char == L':') || + (Char == L'\\') ) { + + IsNameValid = FALSE; + break; + } + + // + // Remember if this is not a '.' character. + // + + if (Char != L'.') { + + AllDots = FALSE; + } + } + + // + // The names '.' and '..' are also invalid. + // + + if (AllDots + && (NameLength == 1 + || NameLength == 2)) { + + IsNameValid = FALSE; + } + + DebugTrace( -1, Dbg, ("NtfsIsFileNameValid -> %s\n", IsNameValid ? "TRUE" : "FALSE") ); + + return IsNameValid; +} + + +BOOLEAN +NtfsIsFatNameValid ( + IN PUNICODE_STRING FileName, + IN BOOLEAN WildCardsPermissible + ) + +/*++ + +Routine Description: + + This routine checks if the specified file name is conformant to the + Fat 8.3 file naming rules. + +Arguments: + + FileName - Supplies the name to check. + + WildCardsPermissible - Tells us if wild card characters are ok. + +Return Value: + + BOOLEAN - TRUE if the name is valid, FALSE otherwise. + +--*/ + +{ + BOOLEAN Results; + STRING DbcsName; + USHORT i; + CHAR Buffer[24]; + WCHAR wc; + + PAGED_CODE(); + + // + // If the name is more than 24 bytes then it can't be a valid Fat name. + // + + if (FileName->Length > 24) { + + return FALSE; + } + + // + // We will do some extra checking ourselves because we really want to be + // fairly restrictive of what an 8.3 name contains. That way + // we will then generate an 8.3 name for some nomially valid 8.3 + // names (e.g., names that contain DBCS characters). The extra characters + // we'll filter off are those characters less than and equal to the space + // character and those beyond lowercase z. + // + + if (FlagOn(NtfsData.Flags,NTFS_FLAGS_ALLOW_EXTENDED_CHAR)) { + + for (i = 0; i < FileName->Length / 2; i += 1) { + + wc = FileName->Buffer[i]; + + if ((wc <= 0x0020) || (wc == 0x007c)) { return FALSE; } + } + + } else { + + for (i = 0; i < FileName->Length / 2; i += 1) { + + wc = FileName->Buffer[i]; + + if ((wc <= 0x0020) || (wc >= 0x007f) || (wc == 0x007c)) { return FALSE; } + } + } + + // + // The characters match up okay so now build up the dbcs string to call + // the fsrtl routine to check for legal 8.3 formation + // + + Results = FALSE; + + DbcsName.MaximumLength = 24; + DbcsName.Buffer = Buffer; + + if (NT_SUCCESS(RtlUnicodeStringToCountedOemString( &DbcsName, FileName, FALSE))) { + + if (FsRtlIsFatDbcsLegal( DbcsName, WildCardsPermissible, FALSE, FALSE )) { + + Results = TRUE; + } + } + + // + // And return to our caller + // + + return Results; +} + + +BOOLEAN +NtfsIsDosNameInCurrentCodePage( + IN PUNICODE_STRING FileName + ) + +/*++ + +Routine Description: + + This routine checks that the given file name is composed only of OEM + characters and that it conforms to the DOS 8.3 standard. In other + words it answers the question: "Can I copy this file to a FAT + partition and then back again and have the same name that I + started out with?" + +Arguments: + + FileName - Supplies the file name to check. + +Return Value: + + FALSE - The supplied file name is not a DOS file name. + TRUE - The supplied file name is a DOS file name. + +--*/ +{ + WCHAR upcase_buffer[MAX_CHARS_IN_8_DOT_3 + 1]; + UNICODE_STRING upcase_file_name; + CHAR oem_buffer[MAX_CHARS_IN_8_DOT_3 + 1]; + STRING oem_string; + WCHAR unicode_buffer[MAX_CHARS_IN_8_DOT_3 + 1]; + UNICODE_STRING unicode_string; + ULONG i; + USHORT Length; + + // + // This ain't 8.3 if unicode version has more than 12 characters. + // + + if (FileName->Length > MAX_CHARS_IN_8_DOT_3*sizeof(WCHAR)) { + return FALSE; + } + + upcase_file_name.Buffer = upcase_buffer; + upcase_file_name.Length = 0; + upcase_file_name.MaximumLength = (MAX_CHARS_IN_8_DOT_3 + 1) * sizeof(WCHAR); + + oem_string.Buffer = oem_buffer; + oem_string.Length = 0; + oem_string.MaximumLength = MAX_CHARS_IN_8_DOT_3 + 1; + + unicode_string.Buffer = unicode_buffer; + unicode_string.Length = 0; + unicode_string.MaximumLength = (MAX_CHARS_IN_8_DOT_3 + 1) * sizeof(WCHAR); + + // + // Return false if there is a space in the name. + // + + for (i = 0, Length = FileName->Length / 2; + i < Length; + i += 1) { + + WCHAR wc; + + wc = FileName->Buffer[i]; + if (wc == 0x0020) { + + return FALSE; + } + } + + // + // Upcase the original file name. + // + + if (!NT_SUCCESS(RtlUpcaseUnicodeString(&upcase_file_name, FileName, FALSE)) || + + // + // Convert the upcased unicode string to OEM and check out the OEM string + // to see if it's 8.3. + // + + !NT_SUCCESS(RtlUnicodeStringToOemString(&oem_string, &upcase_file_name, FALSE)) || + !FsRtlIsFatDbcsLegal(oem_string, FALSE, FALSE, FALSE) || + + // + // Convert the OEM back to UNICODE and make sure that it's the same + // as the original upcased version of the file name. + // + + !NT_SUCCESS(RtlOemStringToUnicodeString(&unicode_string, &oem_string, FALSE)) || + !RtlEqualUnicodeString(&upcase_file_name, &unicode_string, FALSE)) { + + return FALSE; + } + + return TRUE; +} + -- cgit v1.2.3