mirror of
https://github.com/reactos/reactos.git
synced 2024-08-26 13:18:51 +00:00
![Pierre Schweitzer](/assets/img/avatar_default.png)
This implies that a sample for W10. It has been backported to NT5.2; not sure how it would work on a W2K3 (feel free to test!)
1115 lines
26 KiB
C
1115 lines
26 KiB
C
/*++
|
||
|
||
Copyright (c) 1989-2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
NameSup.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the Fat Name support routines
|
||
|
||
|
||
--*/
|
||
|
||
#include "fatprocs.h"
|
||
|
||
#define Dbg (DEBUG_TRACE_NAMESUP)
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, Fat8dot3ToString)
|
||
#pragma alloc_text(PAGE, FatIsNameInExpression)
|
||
#pragma alloc_text(PAGE, FatStringTo8dot3)
|
||
#pragma alloc_text(PAGE, FatSetFullFileNameInFcb)
|
||
#pragma alloc_text(PAGE, FatGetUnicodeNameFromFcb)
|
||
#pragma alloc_text(PAGE, FatUnicodeToUpcaseOem)
|
||
#pragma alloc_text(PAGE, FatSelectNames)
|
||
#pragma alloc_text(PAGE, FatEvaluateNameCase)
|
||
#pragma alloc_text(PAGE, FatSpaceInName)
|
||
#pragma alloc_text(PAGE, FatUnicodeRestoreShortNameCase)
|
||
#endif
|
||
|
||
|
||
BOOLEAN
|
||
FatIsNameInExpression (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN OEM_STRING Expression,
|
||
IN OEM_STRING Name
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine compare a name and an expression and tells the caller if
|
||
the name is equal to or not equal to the expression. The input name
|
||
cannot contain wildcards, while the expression may contain wildcards.
|
||
|
||
Arguments:
|
||
|
||
Expression - Supplies the input expression to check against
|
||
The caller must have already upcased the Expression.
|
||
|
||
Name - Supplies the input name to check for. The caller must have
|
||
already upcased the name.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if Name is an element in the set of strings denoted
|
||
by the input Expression and FALSE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Call the appropriate FsRtl routine do to the real work
|
||
//
|
||
|
||
return FsRtlIsDbcsInExpression( &Expression,
|
||
&Name );
|
||
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
}
|
||
|
||
|
||
VOID
|
||
FatStringTo8dot3 (
|
||
_In_ PIRP_CONTEXT IrpContext,
|
||
_In_ OEM_STRING InputString,
|
||
_Out_writes_bytes_(11) PFAT8DOT3 Output8dot3
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Convert a string into fat 8.3 format. The string must not contain
|
||
any wildcards.
|
||
|
||
Arguments:
|
||
|
||
InputString - Supplies the input string to convert
|
||
|
||
Output8dot3 - Receives the converted string, the memory must be supplied
|
||
by the caller.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i;
|
||
ULONG j;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace(+1, Dbg, "FatStringTo8dot3\n", 0);
|
||
DebugTrace( 0, Dbg, "InputString = %Z\n", &InputString);
|
||
|
||
NT_ASSERT( InputString.Length <= 12 );
|
||
|
||
//
|
||
// Make the output name all blanks
|
||
//
|
||
|
||
RtlFillMemory( Output8dot3, 11, UCHAR_SP );
|
||
|
||
//
|
||
// Copy over the first part of the file name. Stop when we get to
|
||
// the end of the input string or a dot.
|
||
//
|
||
|
||
for (i = 0;
|
||
(i < (ULONG)InputString.Length) && (InputString.Buffer[i] != '.') && (i < 11);
|
||
i += 1) {
|
||
|
||
(*Output8dot3)[i] = InputString.Buffer[i];
|
||
}
|
||
|
||
//
|
||
// Check if we need to process an extension
|
||
//
|
||
|
||
if (i < (ULONG)InputString.Length) {
|
||
|
||
//
|
||
// Make sure we have a dot and then skip over it.
|
||
//
|
||
|
||
NT_ASSERT( (InputString.Length - i) <= 4 );
|
||
NT_ASSERT( InputString.Buffer[i] == '.' );
|
||
|
||
i += 1;
|
||
|
||
//
|
||
// Copy over the extension. Stop when we get to the
|
||
// end of the input string.
|
||
//
|
||
|
||
for (j = 8; (i < (ULONG)InputString.Length); j += 1, i += 1) {
|
||
|
||
(*Output8dot3)[j] = InputString.Buffer[i];
|
||
}
|
||
}
|
||
|
||
//
|
||
// Before we return check if we should translate the first character
|
||
// from 0xe5 to 0x5.
|
||
//
|
||
|
||
if ((*Output8dot3)[0] == 0xe5) {
|
||
|
||
(*Output8dot3)[0] = FAT_DIRENT_REALLY_0E5;
|
||
}
|
||
|
||
DebugTrace(-1, Dbg, "FatStringTo8dot3 -> (VOID)\n", 0);
|
||
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
Fat8dot3ToString (
|
||
_In_ PIRP_CONTEXT IrpContext,
|
||
_In_ PDIRENT Dirent,
|
||
_In_ BOOLEAN RestoreCase,
|
||
_Out_ POEM_STRING OutputString
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Convert fat 8.3 format into a string. The 8.3 name must be well formed.
|
||
|
||
Arguments:
|
||
|
||
Dirent - Supplies the input 8.3 name to convert
|
||
|
||
RestoreCase - If TRUE, then the magic reserved bits are used to restore
|
||
the original case.
|
||
|
||
OutputString - Receives the converted name, the memory must be supplied
|
||
by the caller.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG StringIndex;
|
||
ULONG BaseLength, ExtensionLength;
|
||
|
||
PAGED_CODE();
|
||
|
||
DebugTrace(+1, Dbg, "Fat8dot3ToString\n", 0);
|
||
|
||
//
|
||
// First, find the length of the base component.
|
||
//
|
||
|
||
for (BaseLength = 8; BaseLength > 0; BaseLength -= 1) {
|
||
|
||
if (Dirent->FileName[BaseLength - 1] != UCHAR_SP) {
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now find the length of the extension.
|
||
//
|
||
|
||
for (ExtensionLength = 3; ExtensionLength > 0; ExtensionLength -= 1) {
|
||
|
||
if (Dirent->FileName[8 + ExtensionLength - 1] != UCHAR_SP) {
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If there was a base part, copy it and check the case. Don't forget
|
||
// if the first character needs to be changed from 0x05 to 0xe5.
|
||
//
|
||
|
||
if (BaseLength != 0) {
|
||
|
||
RtlCopyMemory( OutputString->Buffer, Dirent->FileName, BaseLength );
|
||
|
||
if (OutputString->Buffer[0] == FAT_DIRENT_REALLY_0E5) {
|
||
|
||
OutputString->Buffer[0] = 0xe5;
|
||
}
|
||
|
||
//
|
||
// Now if we are to restore case, look for A-Z
|
||
//
|
||
|
||
if (FatData.ChicagoMode &&
|
||
RestoreCase &&
|
||
FlagOn(Dirent->NtByte, FAT_DIRENT_NT_BYTE_8_LOWER_CASE)) {
|
||
|
||
for (StringIndex = 0; StringIndex < BaseLength; StringIndex += 1) {
|
||
|
||
//
|
||
// Depending on whether the media was built in a system that was
|
||
// running with "code page invariance" (see FatEvaluateNameCase),
|
||
// there could be double-byte OEM characters lying in wait here.
|
||
// Gotta skip them.
|
||
//
|
||
|
||
if (FsRtlIsLeadDbcsCharacter(OutputString->Buffer[StringIndex])) {
|
||
|
||
StringIndex += 1;
|
||
continue;
|
||
}
|
||
|
||
if ((OutputString->Buffer[StringIndex] >= 'A') &&
|
||
(OutputString->Buffer[StringIndex] <= 'Z')) {
|
||
|
||
OutputString->Buffer[StringIndex] += 'a' - 'A';
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// If there was an extension, copy that over. Else we now know the
|
||
// size of the string.
|
||
//
|
||
|
||
if (ExtensionLength != 0) {
|
||
|
||
PUCHAR o, d;
|
||
|
||
//
|
||
// Now add the dot
|
||
//
|
||
|
||
OutputString->Buffer[BaseLength++] = '.';
|
||
|
||
//
|
||
// Copy over the extension into the output buffer.
|
||
//
|
||
|
||
o = (PUCHAR)&OutputString->Buffer[BaseLength];
|
||
d = &Dirent->FileName[8];
|
||
|
||
switch (ExtensionLength) {
|
||
case 3:
|
||
*o++ = *d++;
|
||
case 2:
|
||
*o++ = *d++;
|
||
case 1:
|
||
*o++ = *d++;
|
||
}
|
||
|
||
//
|
||
// Set the output string length
|
||
//
|
||
|
||
OutputString->Length = (USHORT)(BaseLength + ExtensionLength);
|
||
|
||
//
|
||
// Now if we are to restore case, look for A-Z
|
||
//
|
||
|
||
if (FatData.ChicagoMode &&
|
||
RestoreCase &&
|
||
FlagOn(Dirent->NtByte, FAT_DIRENT_NT_BYTE_3_LOWER_CASE)) {
|
||
|
||
for (StringIndex = BaseLength;
|
||
StringIndex < OutputString->Length;
|
||
StringIndex++ ) {
|
||
|
||
//
|
||
// Depending on whether the media was built in a system that was
|
||
// running with "code page invariance" (see FatEvaluateNameCase),
|
||
// there could be double-byte OEM characters lying in wait here.
|
||
// Gotta skip them.
|
||
//
|
||
|
||
if (FsRtlIsLeadDbcsCharacter(OutputString->Buffer[StringIndex])) {
|
||
|
||
StringIndex += 1;
|
||
continue;
|
||
}
|
||
|
||
if ((OutputString->Buffer[StringIndex] >= 'A') &&
|
||
(OutputString->Buffer[StringIndex] <= 'Z')) {
|
||
|
||
OutputString->Buffer[StringIndex] += 'a' - 'A';
|
||
}
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Set the output string length
|
||
//
|
||
|
||
OutputString->Length = (USHORT)BaseLength;
|
||
}
|
||
|
||
//
|
||
// And return to our caller
|
||
//
|
||
|
||
DebugTrace(-1, Dbg, "Fat8dot3ToString, OutputString = \"%Z\" -> VOID\n", OutputString);
|
||
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
|
||
return;
|
||
}
|
||
|
||
_Requires_lock_held_(_Global_critical_region_)
|
||
VOID
|
||
FatGetUnicodeNameFromFcb (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb,
|
||
IN OUT PUNICODE_STRING Lfn
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will return the unicode name for a given Fcb. If the
|
||
file has an LFN, it will return this. Otherwise it will return
|
||
the UNICODE conversion of the Oem name, properly cased.
|
||
|
||
Arguments:
|
||
|
||
Fcb - Supplies the Fcb to query.
|
||
|
||
Lfn - Supplies a string that already has enough storage for the
|
||
full unicode name.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PDIRENT Dirent;
|
||
PBCB DirentBcb = NULL;
|
||
ULONG DirentByteOffset;
|
||
|
||
CCB LocalCcb;
|
||
|
||
PAGED_CODE();
|
||
|
||
NT_ASSERT((MAX_LFN_CHARACTERS * sizeof( WCHAR)) == Lfn->MaximumLength);
|
||
|
||
//
|
||
// We'll start by locating the dirent for the name.
|
||
//
|
||
|
||
FatStringTo8dot3( IrpContext,
|
||
Fcb->ShortName.Name.Oem,
|
||
&LocalCcb.OemQueryTemplate.Constant );
|
||
|
||
LocalCcb.Flags = 0;
|
||
LocalCcb.UnicodeQueryTemplate.Length = 0;
|
||
LocalCcb.ContainsWildCards = FALSE;
|
||
|
||
FatLocateDirent( IrpContext,
|
||
Fcb->ParentDcb,
|
||
&LocalCcb,
|
||
Fcb->LfnOffsetWithinDirectory,
|
||
NULL,
|
||
&Dirent,
|
||
&DirentBcb,
|
||
(PVBO)&DirentByteOffset,
|
||
NULL,
|
||
Lfn,
|
||
NULL );
|
||
_SEH2_TRY {
|
||
|
||
//
|
||
// If we didn't find the Dirent, something is terribly wrong.
|
||
//
|
||
|
||
if ((DirentBcb == NULL) ||
|
||
(DirentByteOffset != Fcb->DirentOffsetWithinDirectory)) {
|
||
|
||
FatRaiseStatus( IrpContext, STATUS_FILE_INVALID );
|
||
}
|
||
|
||
//
|
||
// Check for the easy case.
|
||
//
|
||
|
||
if (Lfn->Length == 0) {
|
||
|
||
NTSTATUS Status;
|
||
OEM_STRING ShortName;
|
||
UCHAR ShortNameBuffer[12];
|
||
|
||
//
|
||
// If we thought that there was an LFN here and didn't find one,
|
||
// we're as dead. This shouldn't happen in normal operation, but
|
||
// if someone scrambles a directory by hand ...
|
||
//
|
||
|
||
NT_ASSERT( Fcb->LfnOffsetWithinDirectory == Fcb->DirentOffsetWithinDirectory );
|
||
|
||
if (Fcb->LfnOffsetWithinDirectory != Fcb->DirentOffsetWithinDirectory) {
|
||
|
||
FatRaiseStatus( IrpContext, STATUS_FILE_INVALID );
|
||
}
|
||
|
||
//
|
||
// There is no LFN, so manufacture a UNICODE name.
|
||
//
|
||
|
||
ShortName.Length = 0;
|
||
ShortName.MaximumLength = 12;
|
||
ShortName.Buffer = (PCHAR)ShortNameBuffer;
|
||
|
||
Fat8dot3ToString( IrpContext, Dirent, TRUE, &ShortName );
|
||
|
||
//
|
||
// OK, now convert this string to UNICODE
|
||
//
|
||
|
||
#ifdef _MSC_VER
|
||
#pragma prefast( suppress:28931, "needed for debug build" )
|
||
#endif
|
||
Status = RtlOemStringToCountedUnicodeString( Lfn,
|
||
&ShortName,
|
||
FALSE );
|
||
|
||
NT_ASSERT( Status == STATUS_SUCCESS );
|
||
}
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
FatUnpinBcb( IrpContext, DirentBcb );
|
||
} _SEH2_END;
|
||
}
|
||
|
||
_Requires_lock_held_(_Global_critical_region_)
|
||
VOID
|
||
FatSetFullFileNameInFcb (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PFCB Fcb
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
If the FullFileName field in the Fcb has not yet been filled in, we
|
||
proceed to do so.
|
||
|
||
Arguments:
|
||
|
||
Fcb - Supplies the file.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
if (Fcb->FullFileName.Buffer == NULL) {
|
||
|
||
UNICODE_STRING Lfn;
|
||
PFCB TmpFcb = Fcb;
|
||
PFCB StopFcb;
|
||
PWCHAR TmpBuffer;
|
||
ULONG PathLength = 0;
|
||
|
||
//
|
||
// We will assume we do this infrequently enough, that it's OK to
|
||
// to a pool allocation here.
|
||
//
|
||
|
||
Lfn.Length = 0;
|
||
Lfn.MaximumLength = MAX_LFN_CHARACTERS * sizeof(WCHAR);
|
||
Lfn.Buffer = FsRtlAllocatePoolWithTag( PagedPool,
|
||
MAX_LFN_CHARACTERS * sizeof(WCHAR),
|
||
TAG_FILENAME_BUFFER );
|
||
|
||
_SEH2_TRY {
|
||
|
||
//
|
||
// First determine how big the name will be. If we find an
|
||
// ancestor with a FullFileName, our work is easier.
|
||
//
|
||
|
||
while (TmpFcb != Fcb->Vcb->RootDcb) {
|
||
|
||
if ((TmpFcb != Fcb) && (TmpFcb->FullFileName.Buffer != NULL)) {
|
||
|
||
PathLength += TmpFcb->FullFileName.Length;
|
||
|
||
Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag( PagedPool,
|
||
PathLength,
|
||
TAG_FILENAME_BUFFER );
|
||
|
||
RtlCopyMemory( Fcb->FullFileName.Buffer,
|
||
TmpFcb->FullFileName.Buffer,
|
||
TmpFcb->FullFileName.Length );
|
||
|
||
break;
|
||
}
|
||
|
||
PathLength += TmpFcb->FinalNameLength + sizeof(WCHAR);
|
||
|
||
TmpFcb = TmpFcb->ParentDcb;
|
||
}
|
||
|
||
//
|
||
// If FullFileName.Buffer is still NULL, allocate it.
|
||
//
|
||
|
||
if (Fcb->FullFileName.Buffer == NULL) {
|
||
|
||
Fcb->FullFileName.Buffer = FsRtlAllocatePoolWithTag( PagedPool,
|
||
PathLength,
|
||
TAG_FILENAME_BUFFER );
|
||
}
|
||
|
||
StopFcb = TmpFcb;
|
||
|
||
TmpFcb = Fcb;
|
||
TmpBuffer = Fcb->FullFileName.Buffer + PathLength / sizeof(WCHAR);
|
||
|
||
Fcb->FullFileName.Length =
|
||
Fcb->FullFileName.MaximumLength = (USHORT)PathLength;
|
||
|
||
while (TmpFcb != StopFcb) {
|
||
|
||
FatGetUnicodeNameFromFcb( IrpContext,
|
||
TmpFcb,
|
||
&Lfn );
|
||
|
||
TmpBuffer -= Lfn.Length / sizeof(WCHAR);
|
||
|
||
RtlCopyMemory( TmpBuffer, Lfn.Buffer, Lfn.Length );
|
||
|
||
TmpBuffer -= 1;
|
||
|
||
*TmpBuffer = L'\\';
|
||
|
||
TmpFcb = TmpFcb->ParentDcb;
|
||
}
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
if (_SEH2_AbnormalTermination()) {
|
||
|
||
if (Fcb->FullFileName.Buffer) {
|
||
|
||
ExFreePool( Fcb->FullFileName.Buffer );
|
||
Fcb->FullFileName.Buffer = NULL;
|
||
}
|
||
}
|
||
|
||
ExFreePool( Lfn.Buffer );
|
||
} _SEH2_END;
|
||
}
|
||
}
|
||
|
||
VOID
|
||
FatUnicodeToUpcaseOem (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN POEM_STRING OemString,
|
||
IN PUNICODE_STRING UnicodeString
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is our standard routine for trying to use stack space
|
||
if possible when calling RtlUpcaseUnicodeStringToCountedOemString().
|
||
|
||
If an unmappable character is encountered, we set the destination
|
||
length to 0.
|
||
|
||
Arguments:
|
||
|
||
OemString - Specifies the destination string. Space is already assumed to
|
||
be allocated. If there is not enough, then we allocate enough
|
||
space.
|
||
|
||
UnicodeString - Specifies the source string.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
Status = RtlUpcaseUnicodeStringToCountedOemString( OemString,
|
||
UnicodeString,
|
||
FALSE );
|
||
|
||
if (Status == STATUS_BUFFER_OVERFLOW) {
|
||
|
||
OemString->Buffer = NULL;
|
||
OemString->Length = 0;
|
||
OemString->MaximumLength = 0;
|
||
|
||
Status = RtlUpcaseUnicodeStringToCountedOemString( OemString,
|
||
UnicodeString,
|
||
TRUE );
|
||
}
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
if (Status == STATUS_UNMAPPABLE_CHARACTER) {
|
||
|
||
OemString->Length = 0;
|
||
|
||
} else {
|
||
|
||
FatNormalizeAndRaiseStatus( IrpContext, Status );
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
_Requires_lock_held_(_Global_critical_region_)
|
||
VOID
|
||
FatSelectNames (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PDCB Parent,
|
||
IN POEM_STRING OemName,
|
||
IN PUNICODE_STRING UnicodeName,
|
||
IN OUT POEM_STRING ShortName,
|
||
IN PUNICODE_STRING SuggestedShortName OPTIONAL,
|
||
IN OUT BOOLEAN *AllLowerComponent,
|
||
IN OUT BOOLEAN *AllLowerExtension,
|
||
IN OUT BOOLEAN *CreateLfn
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes the original UNICODE string that the user specified,
|
||
and the upcased Oem equivalent. This routine then decides if the OemName
|
||
is acceptable for dirent, or whether a short name has to be manufactured.
|
||
|
||
Two values are returned to the caller. One tells the caller if the name
|
||
happens to be all lower case < 0x80. In this special case we don't
|
||
have to create an Lfn. Also we tell the caller if it must create an LFN.
|
||
|
||
Arguments:
|
||
|
||
OemName - Supplies the proposed short Oem name.
|
||
|
||
ShortName - If this OemName is OK for storeage in a dirent it is copied to
|
||
this string, otherwise this string is filled with a name that is OK.
|
||
If OemName and ShortName are the same string, no copy is done.
|
||
|
||
UnicodeName - Provides the original final name.
|
||
|
||
SuggestedShortName - a first-try short name to try before auto-generation
|
||
is used
|
||
|
||
AllLowerComponent - Returns whether this component was all lower case.
|
||
|
||
AllLowerExtension - Returns wheather the extension was all lower case.
|
||
|
||
CreateLfn - Tells the caller if we must create an LFN for the UnicodeName or
|
||
SuggestedLongName
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN GenerateShortName;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// First see if we must generate a short name.
|
||
//
|
||
|
||
if ((OemName->Length == 0) ||
|
||
!FatIsNameShortOemValid( IrpContext, *OemName, FALSE, FALSE, FALSE ) ||
|
||
FatSpaceInName( IrpContext, UnicodeName )) {
|
||
|
||
WCHAR ShortNameBuffer[12];
|
||
UNICODE_STRING ShortUnicodeName;
|
||
GENERATE_NAME_CONTEXT Context;
|
||
BOOLEAN TrySuggestedShortName;
|
||
|
||
PDIRENT Dirent;
|
||
PBCB Bcb = NULL;
|
||
ULONG ByteOffset;
|
||
NTSTATUS Status;
|
||
|
||
GenerateShortName = TRUE;
|
||
|
||
TrySuggestedShortName = (SuggestedShortName != NULL);
|
||
|
||
//
|
||
// Now generate a short name.
|
||
//
|
||
|
||
ShortUnicodeName.Length = 0;
|
||
ShortUnicodeName.MaximumLength = 12 * sizeof(WCHAR);
|
||
ShortUnicodeName.Buffer = ShortNameBuffer;
|
||
|
||
RtlZeroMemory( &Context, sizeof( GENERATE_NAME_CONTEXT ) );
|
||
|
||
_SEH2_TRY {
|
||
|
||
while ( TRUE ) {
|
||
|
||
FatUnpinBcb( IrpContext, Bcb );
|
||
|
||
if (TrySuggestedShortName) {
|
||
|
||
//
|
||
// Try our caller's candidate first. Note that this must have
|
||
// been uppercased previously.
|
||
//
|
||
|
||
ShortUnicodeName.Length = SuggestedShortName->Length;
|
||
ShortUnicodeName.MaximumLength = SuggestedShortName->MaximumLength;
|
||
ShortUnicodeName.Buffer = SuggestedShortName->Buffer;
|
||
|
||
TrySuggestedShortName = FALSE;
|
||
|
||
} else {
|
||
|
||
RtlGenerate8dot3Name( UnicodeName, TRUE, &Context, &ShortUnicodeName );
|
||
}
|
||
|
||
//
|
||
// We have a candidate, make sure it doesn't exist.
|
||
//
|
||
|
||
#ifdef _MSC_VER
|
||
#pragma prefast( suppress:28931, "needed for debug build" )
|
||
#endif
|
||
Status = RtlUnicodeStringToCountedOemString( ShortName,
|
||
&ShortUnicodeName,
|
||
FALSE );
|
||
|
||
NT_ASSERT( Status == STATUS_SUCCESS );
|
||
|
||
FatLocateSimpleOemDirent( IrpContext,
|
||
Parent,
|
||
ShortName,
|
||
&Dirent,
|
||
&Bcb,
|
||
(PVBO)&ByteOffset );
|
||
|
||
if (Bcb == NULL) {
|
||
|
||
_SEH2_LEAVE;
|
||
|
||
}
|
||
}
|
||
|
||
} _SEH2_FINALLY {
|
||
|
||
FatUnpinBcb( IrpContext, Bcb );
|
||
} _SEH2_END;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Only do this copy if the two string are indeed different.
|
||
//
|
||
|
||
if (ShortName != OemName) {
|
||
ShortName->Length = OemName->Length;
|
||
|
||
//
|
||
// If FsRtlIsFatDbcsLegal() on OemName fails, we will not
|
||
// be in this code path, so we infer that ShortName's
|
||
// buffer is big enough to hold the full FAT file name in
|
||
// OemName.
|
||
//
|
||
|
||
_Analysis_assume_(ShortName->MaximumLength <= OemName->Length);
|
||
|
||
RtlCopyMemory( ShortName->Buffer, OemName->Buffer, OemName->Length );
|
||
}
|
||
|
||
GenerateShortName = FALSE;
|
||
}
|
||
|
||
//
|
||
// Now see if the caller will have to use unicode string as an LFN
|
||
//
|
||
|
||
if (GenerateShortName) {
|
||
|
||
*CreateLfn = TRUE;
|
||
*AllLowerComponent = FALSE;
|
||
*AllLowerExtension = FALSE;
|
||
|
||
} else {
|
||
|
||
FatEvaluateNameCase( IrpContext,
|
||
UnicodeName,
|
||
AllLowerComponent,
|
||
AllLowerExtension,
|
||
CreateLfn );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
FatEvaluateNameCase (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PUNICODE_STRING UnicodeName,
|
||
IN OUT BOOLEAN *AllLowerComponent,
|
||
IN OUT BOOLEAN *AllLowerExtension,
|
||
IN OUT BOOLEAN *CreateLfn
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes a UNICODE string and sees if it is eligible for
|
||
the special case optimization.
|
||
|
||
Arguments:
|
||
|
||
UnicodeName - Provides the original final name.
|
||
|
||
AllLowerComponent - Returns whether this compoent was all lower case.
|
||
|
||
AllLowerExtension - Returns wheather the extension was all lower case.
|
||
|
||
CreateLfn - Tells the call if we must create an LFN for the UnicodeName.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i;
|
||
UCHAR Uppers = 0;
|
||
UCHAR Lowers = 0;
|
||
|
||
BOOLEAN ExtensionPresent = FALSE;
|
||
|
||
PAGED_CODE();
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
|
||
*CreateLfn = FALSE;
|
||
|
||
for (i = 0; i < UnicodeName->Length / sizeof(WCHAR); i++) {
|
||
|
||
WCHAR c;
|
||
|
||
c = UnicodeName->Buffer[i];
|
||
|
||
if ((c >= 'A') && (c <= 'Z')) {
|
||
|
||
Uppers += 1;
|
||
|
||
} else if ((c >= 'a') && (c <= 'z')) {
|
||
|
||
Lowers += 1;
|
||
|
||
} else if ((c >= 0x0080) && FatData.CodePageInvariant) {
|
||
|
||
break;
|
||
}
|
||
|
||
//
|
||
// If we come to a period, figure out if the extension was
|
||
// all one case.
|
||
//
|
||
|
||
if (c == L'.') {
|
||
|
||
*CreateLfn = (Lowers != 0) && (Uppers != 0);
|
||
|
||
*AllLowerComponent = !(*CreateLfn) && (Lowers != 0);
|
||
|
||
ExtensionPresent = TRUE;
|
||
|
||
//
|
||
// Now reset the uppers and lowers count.
|
||
//
|
||
|
||
Uppers = Lowers = 0;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now check again for creating an LFN.
|
||
//
|
||
|
||
*CreateLfn = (*CreateLfn ||
|
||
(i != UnicodeName->Length / sizeof(WCHAR)) ||
|
||
((Lowers != 0) && (Uppers != 0)));
|
||
|
||
//
|
||
// Now we know the final state of CreateLfn, update the two
|
||
// "AllLower" booleans.
|
||
//
|
||
|
||
if (ExtensionPresent) {
|
||
|
||
*AllLowerComponent = !(*CreateLfn) && *AllLowerComponent;
|
||
*AllLowerExtension = !(*CreateLfn) && (Lowers != 0);
|
||
|
||
} else {
|
||
|
||
*AllLowerComponent = !(*CreateLfn) && (Lowers != 0);
|
||
*AllLowerExtension = FALSE;
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
FatSpaceInName (
|
||
IN PIRP_CONTEXT IrpContext,
|
||
IN PUNICODE_STRING UnicodeName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine takes a UNICODE string and sees if it contains any spaces.
|
||
|
||
Arguments:
|
||
|
||
UnicodeName - Provides the final name.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - TRUE if it does, FALSE if it doesn't.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i;
|
||
|
||
PAGED_CODE();
|
||
UNREFERENCED_PARAMETER( IrpContext );
|
||
|
||
for (i=0; i < UnicodeName->Length/sizeof(WCHAR); i++) {
|
||
|
||
if (UnicodeName->Buffer[i] == L' ') {
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
VOID
|
||
FatUnicodeRestoreShortNameCase(
|
||
IN PUNICODE_STRING ShortNameWithCase,
|
||
IN BOOLEAN LowerCase8,
|
||
IN BOOLEAN LowerCase3
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Given an 8.3 filename in a UNICODE_STRING, fix the case of the
|
||
name given the two 8do3 case flags.
|
||
|
||
Arguments:
|
||
|
||
ShortNameWithCase - the UNICODE_STRING containing the short name.
|
||
LowerCase8, LowerCase3 - the flag indicating whether to downcase the 8dot3 name component.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
USHORT i;
|
||
UNICODE_STRING DownCaseSeg;
|
||
|
||
PAGED_CODE();
|
||
|
||
NT_ASSERT( ShortNameWithCase->Length <= 24 );
|
||
|
||
//
|
||
// Have to repair the case of the short name
|
||
//
|
||
|
||
for (i = 0; i < (ShortNameWithCase->Length/sizeof(WCHAR)) &&
|
||
ShortNameWithCase->Buffer[i] != L'.'; i++);
|
||
|
||
//
|
||
// Now pointing at the '.', or otherwise the end of name component
|
||
//
|
||
|
||
if (LowerCase8) {
|
||
|
||
DownCaseSeg.Buffer = ShortNameWithCase->Buffer;
|
||
DownCaseSeg.MaximumLength = DownCaseSeg.Length = i*sizeof(WCHAR);
|
||
|
||
RtlDowncaseUnicodeString(&DownCaseSeg, &DownCaseSeg, FALSE);
|
||
}
|
||
|
||
i++;
|
||
|
||
//
|
||
// Now pointing at first wchar of the extension.
|
||
//
|
||
|
||
if (LowerCase3) {
|
||
|
||
//
|
||
// It is not neccesarily the case that we can rely on the flag
|
||
// indicating that we really have an extension.
|
||
//
|
||
|
||
if ((i*sizeof(WCHAR)) < ShortNameWithCase->Length) {
|
||
DownCaseSeg.Buffer = &ShortNameWithCase->Buffer[i];
|
||
DownCaseSeg.MaximumLength = DownCaseSeg.Length = ShortNameWithCase->Length - i*sizeof(WCHAR);
|
||
|
||
RtlDowncaseUnicodeString(&DownCaseSeg, &DownCaseSeg, FALSE);
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
|