reactos/sdk/lib/rtl/dos8dot3.c

312 lines
8.3 KiB
C
Raw Normal View History

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS system libraries
* FILE: lib/rtl/dos8dot3.c
* PURPOSE: Short name (8.3 name) functions
* PROGRAMMER: Eric Kohl
*/
/* INCLUDES ******************************************************************/
#include <rtl.h>
#define NDEBUG
#include <debug.h>
extern PUSHORT NlsUnicodeToMbOemTable;
/* CONSTANTS *****************************************************************/
const ULONG RtlpShortIllegals[] = { 0xFFFFFFFF, 0xFC009C04, 0x38000000, 0x10000000 };
/* FUNCTIONS *****************************************************************/
BOOLEAN
NTAPI
RtlIsValidOemCharacter(IN PWCHAR Char);
static BOOLEAN
RtlpIsShortIllegal(const WCHAR Char)
{
return (Char < 128 && (RtlpShortIllegals[Char / 32] & (1 << (Char % 32))));
}
static USHORT
RtlpGetCheckSum(PUNICODE_STRING Name)
{
PWCHAR CurrentChar;
USHORT Hash;
USHORT Saved;
USHORT Length;
if (!Name->Length)
return 0;
if (Name->Length == sizeof(WCHAR))
return Name->Buffer[0];
CurrentChar = Name->Buffer;
Hash = (*CurrentChar << 8) + *(CurrentChar + 1);
if (Name->Length == 2 * sizeof(WCHAR))
return Hash;
Saved = Hash;
Length = 2;
do
{
CurrentChar += 2;
Hash = (Hash << 7) + *CurrentChar;
Hash = (Saved >> 1) + (Hash << 8);
if (Length + 1 < Name->Length / sizeof(WCHAR))
{
Hash += *(CurrentChar + 1);
}
Saved = Hash;
Length += 2;
}
while (Length < Name->Length / sizeof(WCHAR));
return Hash;
}
/*
* @implemented
*/
VOID
NTAPI
RtlGenerate8dot3Name(IN PUNICODE_STRING Name,
IN BOOLEAN AllowExtendedCharacters,
IN OUT PGENERATE_NAME_CONTEXT Context,
OUT PUNICODE_STRING Name8dot3)
{
ULONG Length = Name->Length / sizeof(WCHAR);
ULONG IndexLength;
ULONG Index;
ULONG DotPos;
WCHAR IndexBuffer[8];
WCHAR Char;
USHORT Checksum;
if (!Context->NameLength)
{
DotPos = Length;
/* Find last dot in Name */
for (Index = 0; Index < Length; Index++)
{
if (Name->Buffer[Index] == L'.')
DotPos = Index;
}
/* Copy name (6 valid characters max) */
for (Index = 0; Index < DotPos && Context->NameLength < 6; Index++)
{
Char = Name->Buffer[Index];
if ((Char > L' ') && (Char != L'.') &&
((Char < 127) || (AllowExtendedCharacters && RtlIsValidOemCharacter(&Char))))
[RTL] Major bugfixing session for RtlGenerate8dot3Name()... Up to know, AllowExtendedCharacters was totally ignored and any upcased char, as long as it was not in the forbidden list was accepted. This was bringing numerous problems (cf: apitest :-)). So, now, the following fixes were brought: - AllowExtendedCharacters isn't ignored any longer. If it's given, the unicode char is upcased, if it's not, as previously, the ansi char is upcased - Be more strict in the allowed chars in name: only allow "graph" chars. This avoids generating broken names that aren't allowed in FAT - In case no char could be added in the name, then, for the generation of the name checksum and use it as shortname - When writing the checksum, don't use reversed order, but direct order, to match Windows behavior (as exposed with tests) - When writing the checksum, use when possible bit operations instead of numerical operations for performances reasons (NFC) - Rewrite RtlpGetCheckSum() with an algorithm that produces the exact same checksums than Windows 2003. This whole commit fixes all apitests related (direct visible effect). As a bonus, it also fixes short names generation in FAT: we no longer produce broken volumes with international file names. This also fixes less visible issues: we were producing unicode strings with null char in their middle (as exposed in tests), not sure about how all functions could handle this: likely not that good, this could have lead to various memory issues & corruptions. CORE-10223 #resolve #comment Fixed with r69389 svn path=/trunk/; revision=69389
2015-09-27 20:54:20 +00:00
{
if (RtlpIsShortIllegal(Char))
Char = L'_';
else if (Char >= L'a' && Char <= L'z')
Char = RtlpUpcaseUnicodeChar(Char);
Context->NameBuffer[Context->NameLength] = Char;
++Context->NameLength;
[RTL] Major bugfixing session for RtlGenerate8dot3Name()... Up to know, AllowExtendedCharacters was totally ignored and any upcased char, as long as it was not in the forbidden list was accepted. This was bringing numerous problems (cf: apitest :-)). So, now, the following fixes were brought: - AllowExtendedCharacters isn't ignored any longer. If it's given, the unicode char is upcased, if it's not, as previously, the ansi char is upcased - Be more strict in the allowed chars in name: only allow "graph" chars. This avoids generating broken names that aren't allowed in FAT - In case no char could be added in the name, then, for the generation of the name checksum and use it as shortname - When writing the checksum, don't use reversed order, but direct order, to match Windows behavior (as exposed with tests) - When writing the checksum, use when possible bit operations instead of numerical operations for performances reasons (NFC) - Rewrite RtlpGetCheckSum() with an algorithm that produces the exact same checksums than Windows 2003. This whole commit fixes all apitests related (direct visible effect). As a bonus, it also fixes short names generation in FAT: we no longer produce broken volumes with international file names. This also fixes less visible issues: we were producing unicode strings with null char in their middle (as exposed in tests), not sure about how all functions could handle this: likely not that good, this could have lead to various memory issues & corruptions. CORE-10223 #resolve #comment Fixed with r69389 svn path=/trunk/; revision=69389
2015-09-27 20:54:20 +00:00
}
}
/* Copy extension (4 valid characters max) */
Context->ExtensionLength = 0;
if (DotPos < Length)
{
Context->ExtensionBuffer[0] = L'.';
Context->ExtensionLength = 1;
while (DotPos < Length && Context->ExtensionLength < 4)
{
Char = Name->Buffer[DotPos];
if ((Char > L' ') && (Char != L'.') &&
((Char < 127) || (AllowExtendedCharacters && RtlIsValidOemCharacter(&Char))))
{
if (RtlpIsShortIllegal(Char))
Char = L'_';
else if (Char >= L'a' && Char <= L'z')
Char = RtlpUpcaseUnicodeChar(Char);
Context->ExtensionBuffer[Context->ExtensionLength++] = Char;
}
Char = UNICODE_NULL;
++DotPos;
}
if (Char != UNICODE_NULL)
Context->ExtensionBuffer[Context->ExtensionLength - 1] = L'~';
}
if (Context->NameLength <= 2)
{
Checksum = Context->Checksum = RtlpGetCheckSum(Name);
for (Index = 0; Index < 4; Index++)
{
Context->NameBuffer[Context->NameLength + Index] =
(Checksum & 0xF) > 9 ? (Checksum & 0xF) + L'A' - 10 : (Checksum & 0xF) + L'0';
Checksum >>= 4;
}
Context->CheckSumInserted = TRUE;
Context->NameLength += 4;
}
}
++Context->LastIndexValue;
if (Context->LastIndexValue > 4 && !Context->CheckSumInserted)
{
Checksum = Context->Checksum = RtlpGetCheckSum(Name);
for (Index = 2; Index < 6; Index++)
{
Context->NameBuffer[Index] =
(Checksum & 0xF) > 9 ? (Checksum & 0xF) + L'A' - 10 : (Checksum & 0xF) + L'0';
Checksum >>= 4;
}
Context->CheckSumInserted = TRUE;
Context->NameLength = 6;
Context->LastIndexValue = 1;
}
/* Calculate index length and index buffer */
Index = Context->LastIndexValue;
for (IndexLength = 1; IndexLength <= 7 && Index > 0; IndexLength++)
{
IndexBuffer[8 - IndexLength] = L'0' + (Index % 10);
Index /= 10;
}
IndexBuffer[8 - IndexLength] = L'~';
/* Reset name length */
Name8dot3->Length = 0;
/* If name present */
if (Context->NameLength)
{
/* Copy name buffer */
Length = Context->NameLength * sizeof(WCHAR);
RtlCopyMemory(Name8dot3->Buffer, Context->NameBuffer, Length);
Name8dot3->Length = Length;
}
/* Copy index buffer */
Length = IndexLength * sizeof(WCHAR);
RtlCopyMemory(Name8dot3->Buffer + (Name8dot3->Length / sizeof(WCHAR)),
IndexBuffer + (8 - IndexLength),
Length);
Name8dot3->Length += Length;
/* If extension present */
if (Context->ExtensionLength)
{
/* Copy extension buffer */
Length = Context->ExtensionLength * sizeof(WCHAR);
RtlCopyMemory(Name8dot3->Buffer + (Name8dot3->Length / sizeof(WCHAR)),
Context->ExtensionBuffer,
Length);
Name8dot3->Length += Length;
}
}
/*
* @implemented
* Note: the function does not conform to the annotations.
* SpacesFound is not always set!
*/
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSYSAPI
BOOLEAN
NTAPI
RtlIsNameLegalDOS8Dot3(IN PCUNICODE_STRING Name,
IN OUT POEM_STRING OemName,
OUT PBOOLEAN NameContainsSpaces OPTIONAL)
{
static const char Illegal[] = "*?<>|\"+=,;[]:/\\\345";
int Dot = -1;
int i;
char Buffer[12];
OEM_STRING OemString;
BOOLEAN GotSpace = FALSE;
NTSTATUS Status;
if (!OemName)
{
OemString.Length = sizeof(Buffer);
OemString.MaximumLength = sizeof(Buffer);
OemString.Buffer = Buffer;
OemName = &OemString;
}
Status = RtlUpcaseUnicodeStringToCountedOemString(OemName, Name, FALSE);
if (!NT_SUCCESS(Status))
return FALSE;
if ((OemName->Length > 12) || (OemName->Buffer == NULL)) return FALSE;
/* a starting . is invalid, except for . and .. */
if (OemName->Buffer[0] == '.')
{
if (OemName->Length != 1 && (OemName->Length != 2 || OemName->Buffer[1] != '.')) return FALSE;
if (NameContainsSpaces) *NameContainsSpaces = FALSE;
return TRUE;
}
for (i = 0; i < OemName->Length; i++)
{
switch (OemName->Buffer[i])
{
case ' ':
/* leading/trailing spaces not allowed */
if (!i || i == OemName->Length-1 || OemName->Buffer[i+1] == '.') return FALSE;
GotSpace = TRUE;
break;
case '.':
if (Dot != -1) return FALSE;
Dot = i;
break;
default:
if (strchr(Illegal, OemName->Buffer[i])) return FALSE;
break;
}
}
/* check file part is shorter than 8, extension shorter than 3
* dot cannot be last in string
*/
if (Dot == -1)
{
if (OemName->Length > 8) return FALSE;
}
else
{
if (Dot > 8 || (OemName->Length - Dot > 4) || Dot == OemName->Length - 1) return FALSE;
}
if (NameContainsSpaces) *NameContainsSpaces = GotSpace;
return TRUE;
}
/* EOF */