/* 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 #define NDEBUG #include /* CONSTANTS *****************************************************************/ const PCHAR RtlpShortIllegals = ";+=[],\"*\\<>/?:|"; /* FUNCTIONS *****************************************************************/ static BOOLEAN RtlpIsShortIllegal(CHAR Char) { return strchr(RtlpShortIllegals, Char) ? TRUE : FALSE; } static USHORT RtlpGetCheckSum(PUNICODE_STRING Name) { USHORT Hash = 0; ULONG Length; PWCHAR c; Length = Name->Length / sizeof(WCHAR); c = Name->Buffer; while(Length--) { Hash = (Hash + (*c << 4) + (*c >> 4)) * 11; c++; } return Hash; } static ULONG RtlpGetIndexLength(ULONG Index) { ULONG Length = 0; while (Index) { Index /= 10; Length++; } return Length ? Length : 1; } /* * @implemented */ VOID NTAPI RtlGenerate8dot3Name(IN PUNICODE_STRING Name, IN BOOLEAN AllowExtendedCharacters, IN OUT PGENERATE_NAME_CONTEXT Context, OUT PUNICODE_STRING Name8dot3) { ULONG Count; WCHAR NameBuffer[8]; WCHAR ExtBuffer[4]; ULONG StrLength; ULONG NameLength; ULONG ExtLength; ULONG CopyLength; ULONG DotPos; ULONG i, j; ULONG IndexLength; ULONG CurrentIndex; USHORT Checksum; CHAR c; StrLength = Name->Length / sizeof(WCHAR); DPRINT("StrLength: %lu\n", StrLength); /* Find last dot in Name */ DotPos = StrLength; for (i = 0; i < StrLength; i++) { if (Name->Buffer[i] == L'.') { DotPos = i; } } DPRINT("DotPos: %lu\n", DotPos); /* Copy name (6 valid characters max) */ for (i = 0, NameLength = 0; NameLength < 6 && i < DotPos; i++) { c = 0; RtlUpcaseUnicodeToOemN(&c, sizeof(CHAR), &Count, &Name->Buffer[i], sizeof(WCHAR)); if (Count != 1 || c == 0 || RtlpIsShortIllegal(c)) { NameBuffer[NameLength++] = L'_'; } else if (c != '.' && c != ' ') { NameBuffer[NameLength++] = (WCHAR)c; } } DPRINT("NameBuffer: '%.08S'\n", NameBuffer); DPRINT("NameLength: %lu\n", NameLength); /* Copy extension (4 valid characters max) */ if (DotPos < StrLength) { for (i = DotPos, ExtLength = 0; ExtLength < 4 && i < StrLength; i++) { c = 0; RtlUpcaseUnicodeToOemN(&c, sizeof(CHAR), &Count, &Name->Buffer[i], sizeof(WCHAR)); if (Count != 1 || c == 0 || RtlpIsShortIllegal(c)) { ExtBuffer[ExtLength++] = L'_'; } else if (c != ' ') { ExtBuffer[ExtLength++] = c; } } } else { ExtLength = 0; } DPRINT("ExtBuffer: '%.04S'\n", ExtBuffer); DPRINT("ExtLength: %lu\n", ExtLength); /* Determine next index */ IndexLength = RtlpGetIndexLength(Context->LastIndexValue); if (Context->CheckSumInserted) { CopyLength = min(NameLength, 8 - 4 - 1 - IndexLength); Checksum = RtlpGetCheckSum(Name); } else { CopyLength = min(NameLength, 8 - 1 - IndexLength); Checksum = 0; } DPRINT("CopyLength: %lu\n", CopyLength); if ((Context->NameLength == CopyLength) && (wcsncmp(Context->NameBuffer, NameBuffer, CopyLength) == 0) && (Context->ExtensionLength == ExtLength) && (wcsncmp(Context->ExtensionBuffer, ExtBuffer, ExtLength) == 0) && (Checksum == Context->Checksum) && (Context->LastIndexValue < 999)) { Context->LastIndexValue++; if (Context->CheckSumInserted == FALSE && Context->LastIndexValue > 9) { Context->CheckSumInserted = TRUE; Context->LastIndexValue = 1; Context->Checksum = RtlpGetCheckSum(Name); } } else { Context->LastIndexValue = 1; Context->CheckSumInserted = FALSE; } IndexLength = RtlpGetIndexLength(Context->LastIndexValue); DPRINT("CurrentIndex: %lu, IndexLength %lu\n", Context->LastIndexValue, IndexLength); if (Context->CheckSumInserted) { CopyLength = min(NameLength, 8 - 4 - 1 - IndexLength); } else { CopyLength = min(NameLength, 8 - 1 - IndexLength); } /* Build the short name */ memcpy(Name8dot3->Buffer, NameBuffer, CopyLength * sizeof(WCHAR)); j = CopyLength; if (Context->CheckSumInserted) { j += 3; Checksum = Context->Checksum; for (i = 0; i < 4; i++) { Name8dot3->Buffer[j--] = (Checksum % 16) > 9 ? (Checksum % 16) + L'A' - 10 : (Checksum % 16) + L'0'; Checksum /= 16; } j = CopyLength + 4; } Name8dot3->Buffer[j++] = L'~'; j += IndexLength - 1; CurrentIndex = Context->LastIndexValue; for (i = 0; i < IndexLength; i++) { Name8dot3->Buffer[j--] = (CurrentIndex % 10) + L'0'; CurrentIndex /= 10; } j += IndexLength + 1; memcpy(Name8dot3->Buffer + j, ExtBuffer, ExtLength * sizeof(WCHAR)); Name8dot3->Length = (USHORT)(j + ExtLength) * sizeof(WCHAR); DPRINT("Name8dot3: '%wZ'\n", Name8dot3); /* Update context */ Context->NameLength = (UCHAR)CopyLength; Context->ExtensionLength = ExtLength; memcpy(Context->NameBuffer, NameBuffer, CopyLength * sizeof(WCHAR)); memcpy(Context->ExtensionBuffer, ExtBuffer, ExtLength * sizeof(WCHAR)); } /* * @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, _Inout_opt_ POEM_STRING OemName, _Out_opt_ PBOOLEAN NameContainsSpaces) { 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 */