Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1023 lines
27 KiB

/*++
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 <empty>
\nt\pri\os\ \nt\pri\os <empty>
nt\pri\os \nt\pri\os <empty>
\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 <empty>
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;
//
// Clear the fieldspresent flag in the name descriptor.
//
Name->FieldsPresent = 0;
//
// 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) {
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 const 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_NTFS_STREAM_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 PCWCH UpcaseTable,
IN ULONG UpcaseTableSize,
IN PCUNICODE_STRING Expression,
IN PCUNICODE_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") );
//
// It better be a valid unicode string.
//
if ((FileName->Length == 0) || FlagOn( FileName->Length, 1 )) {
IsNameValid = FALSE;
} else {
//
// 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 / sizeof( WCHAR ); i += 1) {
wc = FileName->Buffer[i];
if ((wc <= 0x0020) || (wc == 0x007c)) { return FALSE; }
}
} else {
for (i = 0; i < FileName->Length / sizeof( WCHAR ); 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;
}