1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS system libraries 4 * FILE: lib/rtl/dos8dot3.c 5 * PURPOSE: Short name (8.3 name) functions 6 * PROGRAMMER: Eric Kohl 7 */ 8 9 /* INCLUDES ******************************************************************/ 10 11 #include <rtl.h> 12 13 #define NDEBUG 14 #include <debug.h> 15 16 extern PUSHORT NlsUnicodeToMbOemTable; 17 18 /* CONSTANTS *****************************************************************/ 19 20 const ULONG RtlpShortIllegals[] = { 0xFFFFFFFF, 0xFC009C04, 0x38000000, 0x10000000 }; 21 22 /* FUNCTIONS *****************************************************************/ 23 24 BOOLEAN 25 NTAPI 26 RtlIsValidOemCharacter(IN PWCHAR Char); 27 28 static BOOLEAN 29 RtlpIsShortIllegal(const WCHAR Char) 30 { 31 return (Char < 128 && (RtlpShortIllegals[Char / 32] & (1 << (Char % 32)))); 32 } 33 34 static USHORT 35 RtlpGetCheckSum(PUNICODE_STRING Name) 36 { 37 PWCHAR CurrentChar; 38 USHORT Hash; 39 USHORT Saved; 40 USHORT Length; 41 42 if (!Name->Length) 43 return 0; 44 45 if (Name->Length == sizeof(WCHAR)) 46 return Name->Buffer[0]; 47 48 CurrentChar = Name->Buffer; 49 Hash = (*CurrentChar << 8) + *(CurrentChar + 1); 50 51 if (Name->Length == 2 * sizeof(WCHAR)) 52 return Hash; 53 54 Saved = Hash; 55 Length = 2; 56 57 do 58 { 59 CurrentChar += 2; 60 Hash = (Hash << 7) + *CurrentChar; 61 Hash = (Saved >> 1) + (Hash << 8); 62 63 if (Length + 1 < Name->Length / sizeof(WCHAR)) 64 { 65 Hash += *(CurrentChar + 1); 66 } 67 68 Saved = Hash; 69 Length += 2; 70 } 71 while (Length < Name->Length / sizeof(WCHAR)); 72 73 return Hash; 74 } 75 76 /* 77 * @implemented 78 */ 79 VOID 80 NTAPI 81 RtlGenerate8dot3Name(IN PUNICODE_STRING Name, 82 IN BOOLEAN AllowExtendedCharacters, 83 IN OUT PGENERATE_NAME_CONTEXT Context, 84 OUT PUNICODE_STRING Name8dot3) 85 { 86 ULONG Length = Name->Length / sizeof(WCHAR); 87 ULONG IndexLength; 88 ULONG Index; 89 ULONG DotPos; 90 WCHAR IndexBuffer[8]; 91 WCHAR Char; 92 USHORT Checksum; 93 94 if (!Context->NameLength) 95 { 96 DotPos = Length; 97 98 /* Find last dot in Name */ 99 for (Index = 0; Index < Length; Index++) 100 { 101 if (Name->Buffer[Index] == L'.') 102 DotPos = Index; 103 } 104 105 /* Copy name. OEM string length can't exceed 6. */ 106 UCHAR OemSizeLeft = 6; 107 for (Index = 0; (Index < DotPos) && OemSizeLeft; Index++) 108 { 109 Char = Name->Buffer[Index]; 110 111 if ((Char > L' ') && (Char != L'.') && 112 ((Char < 127) || (AllowExtendedCharacters && RtlIsValidOemCharacter(&Char)))) 113 { 114 if (RtlpIsShortIllegal(Char)) 115 Char = L'_'; 116 else if (Char >= L'a' && Char <= L'z') 117 Char = RtlpUpcaseUnicodeChar(Char); 118 119 /* Beware of MB OEM codepage */ 120 if (NlsMbOemCodePageTag && HIBYTE(NlsUnicodeToMbOemTable[Char])) 121 { 122 if (OemSizeLeft < 2) 123 break; 124 OemSizeLeft--; 125 } 126 127 Context->NameBuffer[Context->NameLength] = Char; 128 Context->NameLength++; 129 OemSizeLeft--; 130 } 131 } 132 133 /* Copy extension (4 valid characters max) */ 134 Context->ExtensionLength = 0; 135 if (DotPos < Length) 136 { 137 Context->ExtensionBuffer[0] = L'.'; 138 Context->ExtensionLength = 1; 139 140 while (DotPos < Length && Context->ExtensionLength < 4) 141 { 142 Char = Name->Buffer[DotPos]; 143 144 if ((Char > L' ') && (Char != L'.') && 145 ((Char < 127) || (AllowExtendedCharacters && RtlIsValidOemCharacter(&Char)))) 146 { 147 if (RtlpIsShortIllegal(Char)) 148 Char = L'_'; 149 else if (Char >= L'a' && Char <= L'z') 150 Char = RtlpUpcaseUnicodeChar(Char); 151 152 Context->ExtensionBuffer[Context->ExtensionLength++] = Char; 153 } 154 155 Char = UNICODE_NULL; 156 ++DotPos; 157 } 158 159 if (Char != UNICODE_NULL) 160 Context->ExtensionBuffer[Context->ExtensionLength - 1] = L'~'; 161 } 162 163 if (Context->NameLength <= 2) 164 { 165 Checksum = Context->Checksum = RtlpGetCheckSum(Name); 166 167 for (Index = 0; Index < 4; Index++) 168 { 169 Context->NameBuffer[Context->NameLength + Index] = 170 (Checksum & 0xF) > 9 ? (Checksum & 0xF) + L'A' - 10 : (Checksum & 0xF) + L'0'; 171 Checksum >>= 4; 172 } 173 174 Context->CheckSumInserted = TRUE; 175 Context->NameLength += 4; 176 } 177 } 178 179 ++Context->LastIndexValue; 180 181 if (Context->LastIndexValue > 4 && !Context->CheckSumInserted) 182 { 183 Checksum = Context->Checksum = RtlpGetCheckSum(Name); 184 185 for (Index = 2; Index < 6; Index++) 186 { 187 Context->NameBuffer[Index] = 188 (Checksum & 0xF) > 9 ? (Checksum & 0xF) + L'A' - 10 : (Checksum & 0xF) + L'0'; 189 Checksum >>= 4; 190 } 191 192 Context->CheckSumInserted = TRUE; 193 Context->NameLength = 6; 194 Context->LastIndexValue = 1; 195 } 196 197 /* Calculate index length and index buffer */ 198 Index = Context->LastIndexValue; 199 for (IndexLength = 1; IndexLength <= 7 && Index > 0; IndexLength++) 200 { 201 IndexBuffer[8 - IndexLength] = L'0' + (Index % 10); 202 Index /= 10; 203 } 204 205 IndexBuffer[8 - IndexLength] = L'~'; 206 207 /* Reset name length */ 208 Name8dot3->Length = 0; 209 210 /* If name present */ 211 if (Context->NameLength) 212 { 213 /* Copy name buffer */ 214 Length = Context->NameLength * sizeof(WCHAR); 215 RtlCopyMemory(Name8dot3->Buffer, Context->NameBuffer, Length); 216 Name8dot3->Length = Length; 217 } 218 219 /* Copy index buffer */ 220 Length = IndexLength * sizeof(WCHAR); 221 RtlCopyMemory(Name8dot3->Buffer + (Name8dot3->Length / sizeof(WCHAR)), 222 IndexBuffer + (8 - IndexLength), 223 Length); 224 Name8dot3->Length += Length; 225 226 /* If extension present */ 227 if (Context->ExtensionLength) 228 { 229 /* Copy extension buffer */ 230 Length = Context->ExtensionLength * sizeof(WCHAR); 231 RtlCopyMemory(Name8dot3->Buffer + (Name8dot3->Length / sizeof(WCHAR)), 232 Context->ExtensionBuffer, 233 Length); 234 Name8dot3->Length += Length; 235 } 236 } 237 238 239 /* 240 * @implemented 241 * Note: the function does not conform to the annotations. 242 * SpacesFound is not always set! 243 */ 244 _IRQL_requires_max_(PASSIVE_LEVEL) 245 _Must_inspect_result_ 246 NTSYSAPI 247 BOOLEAN 248 NTAPI 249 RtlIsNameLegalDOS8Dot3(IN PCUNICODE_STRING Name, 250 IN OUT POEM_STRING OemName, 251 OUT PBOOLEAN NameContainsSpaces OPTIONAL) 252 { 253 static const char Illegal[] = "*?<>|\"+=,;[]:/\\\345"; 254 int Dot = -1; 255 int i; 256 char Buffer[12]; 257 OEM_STRING OemString; 258 BOOLEAN GotSpace = FALSE; 259 NTSTATUS Status; 260 261 if (!OemName) 262 { 263 OemString.Length = sizeof(Buffer); 264 OemString.MaximumLength = sizeof(Buffer); 265 OemString.Buffer = Buffer; 266 OemName = &OemString; 267 } 268 269 Status = RtlUpcaseUnicodeStringToCountedOemString(OemName, Name, FALSE); 270 if (!NT_SUCCESS(Status)) 271 return FALSE; 272 273 if ((OemName->Length > 12) || (OemName->Buffer == NULL)) return FALSE; 274 275 /* a starting . is invalid, except for . and .. */ 276 if (OemName->Buffer[0] == '.') 277 { 278 if (OemName->Length != 1 && (OemName->Length != 2 || OemName->Buffer[1] != '.')) return FALSE; 279 if (NameContainsSpaces) *NameContainsSpaces = FALSE; 280 281 return TRUE; 282 } 283 284 for (i = 0; i < OemName->Length; i++) 285 { 286 switch (OemName->Buffer[i]) 287 { 288 case ' ': 289 /* leading/trailing spaces not allowed */ 290 if (!i || i == OemName->Length-1 || OemName->Buffer[i+1] == '.') return FALSE; 291 GotSpace = TRUE; 292 break; 293 294 case '.': 295 if (Dot != -1) return FALSE; 296 Dot = i; 297 break; 298 299 default: 300 if (strchr(Illegal, OemName->Buffer[i])) return FALSE; 301 break; 302 } 303 } 304 /* check file part is shorter than 8, extension shorter than 3 305 * dot cannot be last in string 306 */ 307 if (Dot == -1) 308 { 309 if (OemName->Length > 8) return FALSE; 310 } 311 else 312 { 313 if (Dot > 8 || (OemName->Length - Dot > 4) || Dot == OemName->Length - 1) return FALSE; 314 } 315 316 if (NameContainsSpaces) *NameContainsSpaces = GotSpace; 317 318 return TRUE; 319 } 320 321 /* EOF */ 322