From 17458060a5b5595ae4eadce54a2d346d80187bb5 Mon Sep 17 00:00:00 2001 From: Pierre Schweitzer Date: Sun, 11 Jul 2010 21:31:14 +0000 Subject: [PATCH] [NTOSKRNL] Merging some stuff from pierre-fsd/arty-newcc branches: - Implemented FsRtlDissectDbcs(), FsRtlIsDbcsInExpression(), FsRtlIsFatDbcsLegal(), FsRtlIsHpfsDbcsLegal() - Implemented FsRtlIsNameInExpressionPrivate() - Rewritten FsRtlIsNameInExpression() - Updated comments About FsRtlIsNameInExpressionPrivate(), it comes with a fix there isn't in branch, which avoids reading string more than its own length, and this helps getting rid of a workaround later in code. svn path=/trunk/; revision=48002 --- reactos/ntoskrnl/fsrtl/dbcsname.c | 327 +++++++++++++++++++++++++++--- reactos/ntoskrnl/fsrtl/name.c | 183 ++++++++++------- 2 files changed, 407 insertions(+), 103 deletions(-) diff --git a/reactos/ntoskrnl/fsrtl/dbcsname.c b/reactos/ntoskrnl/fsrtl/dbcsname.c index 2e8af663325..64359a50c01 100644 --- a/reactos/ntoskrnl/fsrtl/dbcsname.c +++ b/reactos/ntoskrnl/fsrtl/dbcsname.c @@ -4,6 +4,7 @@ * FILE: ntoskrnl/fsrtl/name.c * PURPOSE: Provides DBCS parsing and other support routines for FSDs * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) + * Pierre Schweitzer (pierre.schweitzer@reactos.org) */ /* INCLUDES ******************************************************************/ @@ -45,19 +46,67 @@ FsRtlDissectDbcs(IN ANSI_STRING Name, OUT PANSI_STRING FirstPart, OUT PANSI_STRING RemainingPart) { - KeBugCheck(FILE_SYSTEM); + ULONG FirstPosition, i; + ULONG SkipFirstSlash = 0; + + /* Zero the strings before continuing */ + RtlZeroMemory(FirstPart, sizeof(ANSI_STRING)); + RtlZeroMemory(RemainingPart, sizeof(ANSI_STRING)); + + /* Just quit if the string is empty */ + if (!Name.Length) return; + + /* Find first backslash */ + FirstPosition = Name.Length; + for (i = 0; i < Name.Length; i++) + { + /* First make sure the character it's not the Lead DBCS */ + if (FsRtlIsLeadDbcsCharacter(Name.Buffer[i])) + { + i++; + } + /* If we found one... */ + else if (Name.Buffer[i] == '\\') + { + /* If it begins string, just notice it and continue */ + if (i == 0) + { + SkipFirstSlash = 1; + } + else + { + /* Else, save its position and break out of the loop */ + FirstPosition = i; + break; + } + } + } + + /* Set up the first result string */ + FirstPart->Buffer = Name.Buffer + SkipFirstSlash; + FirstPart->Length = (FirstPosition - SkipFirstSlash); + FirstPart->MaximumLength = FirstPart->Length; + + /* And second one, if necessary */ + if (FirstPosition < (Name.Length)) + { + RemainingPart->Buffer = Name.Buffer + FirstPosition + 1; + RemainingPart->Length = Name.Length - (FirstPosition + 1); + RemainingPart->MaximumLength = RemainingPart->Length; + } } /*++ * @name FsRtlDoesDbcsContainWildCards * @implemented * - * FILLME + * Returns TRUE if the given DbcsName contains wildcards such as *, ?, + * ANSI_DOS_STAR, ANSI_DOS_DOT, and ANSI_DOS_QM * * @param Name - * FILLME + * The Name to check * - * @return None + * @return TRUE if there are wildcards, FALSE otherwise * * @remarks None * @@ -89,17 +138,17 @@ FsRtlDoesDbcsContainWildCards(IN PANSI_STRING Name) /*++ * @name FsRtlIsDbcsInExpression - * @unimplemented + * @implemented * - * FILLME + * Check if the Name string is in the Expression string. * * @param Expression - * FILLME + * The string in which we've to find Name. It can contains wildcards * * @param Name - * FILLME + * The string to find. It cannot contain wildcards. * - * @return None + * @return TRUE if Name is found in Expression, FALSE otherwise * * @remarks None * @@ -109,27 +158,74 @@ NTAPI FsRtlIsDbcsInExpression(IN PANSI_STRING Expression, IN PANSI_STRING Name) { - KeBugCheck(FILE_SYSTEM); + ULONG ExpressionPosition, NamePosition, MatchingChars = 0; + + ASSERT(!FsRtlDoesDbcsContainWildCards(Name)); + + /* One can't be null, both can be */ + if (!Expression->Length || !Name->Length) + { + return !(Expression->Length ^ Name->Length); + } + + for (ExpressionPosition = 0; ExpressionPosition < Expression->Length; ExpressionPosition++) + { + if ((Expression->Buffer[ExpressionPosition] == Name->Buffer[MatchingChars]) || + (Expression->Buffer[ExpressionPosition] == '?') || + (Expression->Buffer[ExpressionPosition] == ANSI_DOS_QM) || + (Expression->Buffer[ExpressionPosition] == ANSI_DOS_DOT && + (Name->Buffer[MatchingChars] == '.' || Name->Buffer[MatchingChars] == '0'))) + { + MatchingChars++; + } + else if (Expression->Buffer[ExpressionPosition] == '*') + { + MatchingChars = Name->Length; + } + else if (Expression->Buffer[ExpressionPosition] == ANSI_DOS_STAR) + { + for (NamePosition = MatchingChars; NamePosition < Name->Length; NamePosition++) + { + if (Name->Buffer[NamePosition] == '.') + { + MatchingChars = NamePosition; + break; + } + } + } + else + { + MatchingChars = 0; + } + if (MatchingChars == Name->Length) + { + return TRUE; + } + } + return FALSE; } /*++ * @name FsRtlIsFatDbcsLegal - * @unimplemented + * @implemented * - * FILLME + * Returns TRUE if the given DbcsName is a valid FAT filename (in 8.3) * * @param DbcsName - * FILLME + * The filename to check. It can also contains pathname. * * @param WildCardsPermissible - * FILLME + * If this is set to FALSE and if filename contains wildcard, the function + * will fail * * @param PathNamePermissible - * FILLME + * If this is set to FALSE and if the filename comes with a pathname, the + * function will fail * * @param LeadingBackslashPermissible - * FILLME + * If this is set to FALSE and if the filename starts with a backslash, the + * function will fail * * @return TRUE if the DbcsName is legal, FALSE otherwise * @@ -143,27 +239,130 @@ FsRtlIsFatDbcsLegal(IN ANSI_STRING DbcsName, IN BOOLEAN PathNamePermissible, IN BOOLEAN LeadingBackslashPermissible) { - KeBugCheck(FILE_SYSTEM); - return FALSE; + ANSI_STRING FirstPart, RemainingPart, Name; + BOOLEAN LastDot; + ULONG i; + + /* Just quit if the string is empty */ + if (!DbcsName.Length) + return FALSE; + + /* DbcsName wasn't supposed to be started with \ */ + if (!LeadingBackslashPermissible && DbcsName.Buffer[0] == '\\') + return FALSE; + /* DbcsName was allowed to be started with \, but now, remove it */ + else if (LeadingBackslashPermissible && DbcsName.Buffer[0] == '\\') + { + DbcsName.Buffer = DbcsName.Buffer + 1; + DbcsName.Length = DbcsName.Length - 1; + DbcsName.MaximumLength = DbcsName.MaximumLength - 1; + } + + /* Extract first part of the DbcsName to work on */ + FsRtlDissectDbcs(DbcsName, &FirstPart, &RemainingPart); + while (FirstPart.Length > 0) + { + /* Reset dots count */ + LastDot = FALSE; + + /* Accept special filename if wildcards are allowed */ + if (WildCardsPermissible && (FirstPart.Length == 1 || FirstPart.Length == 2) && FirstPart.Buffer[0] == '.') + { + if (FirstPart.Length == 2) + { + if (FirstPart.Buffer[1] == '.') + { + goto EndLoop; + } + } + else + { + goto EndLoop; + } + } + + /* Filename must be 8.3 filename */ + if (FirstPart.Length < 3 || FirstPart.Length > 12) + return FALSE; + + /* Now, we will parse the filename to find everything bad in */ + for (i = 0; i < FirstPart.Length; i++) + { + /* First make sure the character it's not the Lead DBCS */ + if (FsRtlIsLeadDbcsCharacter(FirstPart.Buffer[i])) + { + if (i == (FirstPart.Length) - 1) + return FALSE; + i++; + } + /* Then check for bad characters */ + else if (!FsRtlIsAnsiCharacterLegalFat(FirstPart.Buffer[i], WildCardsPermissible)) + { + return FALSE; + } + else if (FirstPart.Buffer[i] == '.') + { + /* Filename can only contain one dot */ + if (LastDot) + return FALSE; + + LastDot = TRUE; + + /* We mustn't have spaces before dot or at the end of the filename + * and no dot at the beginning of the filename */ + if ((i == (FirstPart.Length) - 1) || i == 0) + return FALSE; + + if (i > 0) + if (FirstPart.Buffer[i - 1] == ' ') + return FALSE; + + /* Filename must be 8.3 filename and not 3.8 filename */ + if ((FirstPart.Length - 1) - i > 3) + return FALSE; + } + } + + /* Filename mustn't finish with a space */ + if (FirstPart.Buffer[FirstPart.Length - 1] == ' ') + return FALSE; + + EndLoop: + /* Preparing next loop */ + Name.Buffer = RemainingPart.Buffer; + Name.Length = RemainingPart.Length; + Name.MaximumLength = RemainingPart.MaximumLength; + + /* Call once again our dissect function */ + FsRtlDissectDbcs(Name, &FirstPart, &RemainingPart); + + /* We found a pathname, it wasn't allowed */ + if (FirstPart.Length > 0 && !PathNamePermissible) + return FALSE; + } + return TRUE; } /*++ * @name FsRtlIsHpfsDbcsLegal - * @unimplemented + * @implemented * - * FILLME + * Returns TRUE if the given DbcsName is a valid HPFS filename * * @param DbcsName - * FILLME + * The filename to check. It can also contains pathname. * * @param WildCardsPermissible - * FILLME + * If this is set to FALSE and if filename contains wildcard, the function + * will fail * * @param PathNamePermissible - * FILLME + * If this is set to FALSE and if the filename comes with a pathname, the + * function will fail * * @param LeadingBackslashPermissible - * FILLME + * If this is set to FALSE and if the filename starts with a backslash, the + * function will fail * * @return TRUE if the DbcsName is legal, FALSE otherwise * @@ -177,6 +376,82 @@ FsRtlIsHpfsDbcsLegal(IN ANSI_STRING DbcsName, IN BOOLEAN PathNamePermissible, IN BOOLEAN LeadingBackslashPermissible) { - KeBugCheck(FILE_SYSTEM); - return FALSE; + ANSI_STRING FirstPart, RemainingPart, Name; + ULONG i; + + /* Just quit if the string is empty */ + if (!DbcsName.Length) + return FALSE; + + /* DbcsName wasn't supposed to be started with \ */ + if (!LeadingBackslashPermissible && DbcsName.Buffer[0] == '\\') + return FALSE; + /* DbcsName was allowed to be started with \, but now, remove it */ + else if (LeadingBackslashPermissible && DbcsName.Buffer[0] == '\\') + { + DbcsName.Buffer = DbcsName.Buffer + 1; + DbcsName.Length = DbcsName.Length - 1; + DbcsName.MaximumLength = DbcsName.MaximumLength - 1; + } + + /* Extract first part of the DbcsName to work on */ + FsRtlDissectDbcs(DbcsName, &FirstPart, &RemainingPart); + while (FirstPart.Length > 0) + { + /* Accept special filename if wildcards are allowed */ + if (WildCardsPermissible && (FirstPart.Length == 1 || FirstPart.Length == 2) && FirstPart.Buffer[0] == '.') + { + if (FirstPart.Length == 2) + { + if (FirstPart.Buffer[1] == '.') + { + goto EndLoop; + } + } + else + { + goto EndLoop; + } + } + + /* Filename must be 255 bytes maximum */ + if (FirstPart.Length > 255) + return FALSE; + + /* Now, we will parse the filename to find everything bad in */ + for (i = 0; i < FirstPart.Length; i++) + { + /* First make sure the character it's not the Lead DBCS */ + if (FsRtlIsLeadDbcsCharacter(FirstPart.Buffer[i])) + { + if (i == (FirstPart.Length) - 1) + return FALSE; + i++; + } + /* Then check for bad characters */ + else if (!!FsRtlIsAnsiCharacterLegalHpfs(FirstPart.Buffer[i], WildCardsPermissible)) + { + return FALSE; + } + } + + /* Filename mustn't finish with a space or a dot */ + if ((FirstPart.Buffer[FirstPart.Length - 1] == ' ') || + (FirstPart.Buffer[FirstPart.Length - 1] == '.')) + return FALSE; + + EndLoop: + /* Preparing next loop */ + Name.Buffer = RemainingPart.Buffer; + Name.Length = RemainingPart.Length; + Name.MaximumLength = RemainingPart.MaximumLength; + + /* Call once again our dissect function */ + FsRtlDissectDbcs(Name, &FirstPart, &RemainingPart); + + /* We found a pathname, it wasn't allowed */ + if (FirstPart.Length > 0 && !PathNamePermissible) + return FALSE; + } + return TRUE; } diff --git a/reactos/ntoskrnl/fsrtl/name.c b/reactos/ntoskrnl/fsrtl/name.c index 17f5f884521..881a66799bb 100644 --- a/reactos/ntoskrnl/fsrtl/name.c +++ b/reactos/ntoskrnl/fsrtl/name.c @@ -5,6 +5,7 @@ * PURPOSE: Provides name parsing and other support routines for FSDs * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) * Filip Navara (navaraf@reactos.org) + * Pierre Schweitzer (pierre.schweitzer@reactos.org) */ /* INCLUDES ******************************************************************/ @@ -13,27 +14,96 @@ #define NDEBUG #include +/* PRIVATE FUNCTIONS *********************************************************/ +BOOLEAN +NTAPI +FsRtlIsNameInExpressionPrivate(IN PUNICODE_STRING Expression, + IN PUNICODE_STRING Name, + IN BOOLEAN IgnoreCase, + IN PWCHAR UpcaseTable OPTIONAL) +{ + ULONG i = 0, j, k = 0; + + ASSERT(!FsRtlDoesNameContainWildCards(Name)); + + while (i < Name->Length / sizeof(WCHAR) && k < Expression->Length / sizeof(WCHAR)) + { + if ((Expression->Buffer[k] == (IgnoreCase ? UpcaseTable[Name->Buffer[i]] : Name->Buffer[i])) || + (Expression->Buffer[k] == L'?') || (Expression->Buffer[k] == DOS_QM) || + (Expression->Buffer[k] == DOS_DOT && (Name->Buffer[i] == L'.' || Name->Buffer[i] == L'0'))) + { + i++; + k++; + } + else if (Expression->Buffer[k] == L'*') + { + if (k < Expression->Length / sizeof(WCHAR)) + { + if (Expression->Buffer[k+1] != L'*' && Expression->Buffer[k+1] != L'?' && + Expression->Buffer[k+1] != DOS_DOT && Expression->Buffer[k+1] != DOS_QM && + Expression->Buffer[k+1] != DOS_STAR) + { + while ((IgnoreCase ? UpcaseTable[Name->Buffer[i]] : Name->Buffer[i]) != Expression->Buffer[k+1] && + i <= Name->Length / sizeof(WCHAR)) i++; + } + else + { + if (!(Expression->Buffer[k+1] != DOS_DOT && (Name->Buffer[i] == L'.' || Name->Buffer[i] == L'0'))) + { + i++; + } + } + } + else + { + i = Name->Length / sizeof(WCHAR); + } + k++; + } + else if (Expression->Buffer[k] == DOS_STAR) + { + j = i; + while (j <= Name->Length / sizeof(WCHAR)) + { + if (Name->Buffer[j] == L'.') + { + i = j; + } + j++; + } + k++; + } + else + { + i++; + k = 0; + } + } + + return (k == Expression->Length / sizeof(WCHAR)); +} + /* PUBLIC FUNCTIONS **********************************************************/ /*++ * @name FsRtlAreNamesEqual * @implemented * - * FILLME + * Compare two strings to check if they match * * @param Name1 - * FILLME + * First unicode string to compare * * @param Name2 - * FILLME + * Second unicode string to compare * * @param IgnoreCase - * FILLME + * If TRUE, Case will be ignored when comparing strings * * @param UpcaseTable - * FILLME + * Table for upcase letters. If NULL is given, system one will be used * - * @return None + * @return TRUE if the strings are equal * * @remarks From Bo Branten's ntifs.h v25. * @@ -188,7 +258,7 @@ FsRtlDissectName(IN UNICODE_STRING Name, * @name FsRtlDoesNameContainWildCards * @implemented * - * FILLME + * Checks if the given string contains WildCards * * @param Name * Pointer to a UNICODE_STRING containing Name to examine @@ -224,13 +294,21 @@ FsRtlDoesNameContainWildCards(IN PUNICODE_STRING Name) * @name FsRtlIsNameInExpression * @implemented * - * FILLME + * Check if the Name string is in the Expression string. * - * @param DeviceObject - * FILLME + * @param Expression + * The string in which we've to find Name. It can contain wildcards. + * If IgnoreCase is set to TRUE, this string MUST BE uppercase. * - * @param Irp - * FILLME + * @param Name + * The string to find. It cannot contain wildcards + * + * @param IgnoreCase + * If set to TRUE, case will be ignore with upcasing both strings + * + * @param UpcaseTable + * If not NULL, and if IgnoreCase is set to TRUE, it will be used to + * upcase the both strings * * @return TRUE if Name is in Expression, FALSE otherwise * @@ -246,80 +324,31 @@ FsRtlIsNameInExpression(IN PUNICODE_STRING Expression, IN BOOLEAN IgnoreCase, IN PWCHAR UpcaseTable OPTIONAL) { - USHORT ExpressionPosition, NamePosition; - UNICODE_STRING TempExpression, TempName; + BOOLEAN Result; + NTSTATUS Status; + UNICODE_STRING IntName; - ExpressionPosition = 0; - NamePosition = 0; - while (ExpressionPosition < (Expression->Length / sizeof(WCHAR)) && - NamePosition < (Name->Length / sizeof(WCHAR))) + if (IgnoreCase && !UpcaseTable) { - if (Expression->Buffer[ExpressionPosition] == L'*') - { - ExpressionPosition++; - if (ExpressionPosition == (Expression->Length / sizeof(WCHAR))) - { - return TRUE; - } - while (NamePosition < (Name->Length / sizeof(WCHAR))) - { - TempExpression.Length = - TempExpression.MaximumLength = - Expression->Length - (ExpressionPosition * sizeof(WCHAR)); - TempExpression.Buffer = Expression->Buffer + ExpressionPosition; - TempName.Length = - TempName.MaximumLength = - Name->Length - (NamePosition * sizeof(WCHAR)); - TempName.Buffer = Name->Buffer + NamePosition; - /* FIXME: Rewrite to get rid of recursion */ - if (FsRtlIsNameInExpression(&TempExpression, &TempName, - IgnoreCase, UpcaseTable)) - { - return TRUE; - } - NamePosition++; - } - } - else + Status = RtlUpcaseUnicodeString(&IntName, Name, TRUE); + if (Status != STATUS_SUCCESS) { - /* FIXME: Take UpcaseTable into account! */ - if (Expression->Buffer[ExpressionPosition] == L'?' || - (IgnoreCase && - RtlUpcaseUnicodeChar(Expression->Buffer[ExpressionPosition]) == - RtlUpcaseUnicodeChar(Name->Buffer[NamePosition])) || - (!IgnoreCase && - Expression->Buffer[ExpressionPosition] == - Name->Buffer[NamePosition])) - { - NamePosition++; - ExpressionPosition++; - } - else - { - return FALSE; - } + ExRaiseStatus(Status); } + Name = &IntName; + IgnoreCase = FALSE; } - - /* Handle matching of "f0_*.*" expression to "f0_000" file name. */ - if (ExpressionPosition < (Expression->Length / sizeof(WCHAR)) && - Expression->Buffer[ExpressionPosition] == L'.') + else { - while (ExpressionPosition < (Expression->Length / sizeof(WCHAR)) && - (Expression->Buffer[ExpressionPosition] == L'.' || - Expression->Buffer[ExpressionPosition] == L'*' || - Expression->Buffer[ExpressionPosition] == L'?')) - { - ExpressionPosition++; - } + IntName.Buffer = NULL; } - if (ExpressionPosition == (Expression->Length / sizeof(WCHAR)) && - NamePosition == (Name->Length / sizeof(WCHAR))) + Result = FsRtlIsNameInExpressionPrivate(Expression, Name, IgnoreCase, UpcaseTable); + + if (IntName.Buffer != NULL) { - return TRUE; + RtlFreeUnicodeString(&IntName); } - return FALSE; + return Result; } - -- 2.17.1