[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
This commit is contained in:
Pierre Schweitzer 2010-07-11 21:31:14 +00:00
parent 604a7a51ed
commit 17458060a5
2 changed files with 409 additions and 105 deletions

View file

@ -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;
}

View file

@ -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 <debug.h>
/* 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'*')
Status = RtlUpcaseUnicodeString(&IntName, Name, TRUE);
if (Status != STATUS_SUCCESS)
{
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
{
/* 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;
}
else
{
IntName.Buffer = NULL;
}
/* Handle matching of "f0_*.*" expression to "f0_000" file name. */
if (ExpressionPosition < (Expression->Length / sizeof(WCHAR)) &&
Expression->Buffer[ExpressionPosition] == L'.')
Result = FsRtlIsNameInExpressionPrivate(Expression, Name, IgnoreCase, UpcaseTable);
if (IntName.Buffer != NULL)
{
while (ExpressionPosition < (Expression->Length / sizeof(WCHAR)) &&
(Expression->Buffer[ExpressionPosition] == L'.' ||
Expression->Buffer[ExpressionPosition] == L'*' ||
Expression->Buffer[ExpressionPosition] == L'?'))
{
ExpressionPosition++;
}
RtlFreeUnicodeString(&IntName);
}
if (ExpressionPosition == (Expression->Length / sizeof(WCHAR)) &&
NamePosition == (Name->Length / sizeof(WCHAR)))
{
return TRUE;
}
return FALSE;
return Result;
}