1024 lines
26 KiB
C
1024 lines
26 KiB
C
|
/*++
|
|||
|
|
|||
|
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;
|
|||
|
}
|
|||
|
|