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 (6 valid characters max) */ 106 for (Index = 0; Index < DotPos && Context->NameLength < 6; Index++) 107 { 108 Char = Name->Buffer[Index]; 109 110 if ((Char > L' ') && (Char != L'.') && 111 ((Char < 127) || (AllowExtendedCharacters && RtlIsValidOemCharacter(&Char)))) 112 { 113 if (RtlpIsShortIllegal(Char)) 114 Char = L'_'; 115 else if (Char >= L'a' && Char <= L'z') 116 Char = RtlpUpcaseUnicodeChar(Char); 117 118 Context->NameBuffer[Context->NameLength] = Char; 119 ++Context->NameLength; 120 } 121 } 122 123 /* Copy extension (4 valid characters max) */ 124 Context->ExtensionLength = 0; 125 if (DotPos < Length) 126 { 127 Context->ExtensionBuffer[0] = L'.'; 128 Context->ExtensionLength = 1; 129 130 while (DotPos < Length && Context->ExtensionLength < 4) 131 { 132 Char = Name->Buffer[DotPos]; 133 134 if ((Char > L' ') && (Char != L'.') && 135 ((Char < 127) || (AllowExtendedCharacters && RtlIsValidOemCharacter(&Char)))) 136 { 137 if (RtlpIsShortIllegal(Char)) 138 Char = L'_'; 139 else if (Char >= L'a' && Char <= L'z') 140 Char = RtlpUpcaseUnicodeChar(Char); 141 142 Context->ExtensionBuffer[Context->ExtensionLength++] = Char; 143 } 144 145 Char = UNICODE_NULL; 146 ++DotPos; 147 } 148 149 if (Char != UNICODE_NULL) 150 Context->ExtensionBuffer[Context->ExtensionLength - 1] = L'~'; 151 } 152 153 if (Context->NameLength <= 2) 154 { 155 Checksum = Context->Checksum = RtlpGetCheckSum(Name); 156 157 for (Index = 0; Index < 4; Index++) 158 { 159 Context->NameBuffer[Context->NameLength + Index] = 160 (Checksum & 0xF) > 9 ? (Checksum & 0xF) + L'A' - 10 : (Checksum & 0xF) + L'0'; 161 Checksum >>= 4; 162 } 163 164 Context->CheckSumInserted = TRUE; 165 Context->NameLength += 4; 166 } 167 } 168 169 ++Context->LastIndexValue; 170 171 if (Context->LastIndexValue > 4 && !Context->CheckSumInserted) 172 { 173 Checksum = Context->Checksum = RtlpGetCheckSum(Name); 174 175 for (Index = 2; Index < 6; Index++) 176 { 177 Context->NameBuffer[Index] = 178 (Checksum & 0xF) > 9 ? (Checksum & 0xF) + L'A' - 10 : (Checksum & 0xF) + L'0'; 179 Checksum >>= 4; 180 } 181 182 Context->CheckSumInserted = TRUE; 183 Context->NameLength = 6; 184 Context->LastIndexValue = 1; 185 } 186 187 /* Calculate index length and index buffer */ 188 Index = Context->LastIndexValue; 189 for (IndexLength = 1; IndexLength <= 7 && Index > 0; IndexLength++) 190 { 191 IndexBuffer[8 - IndexLength] = L'0' + (Index % 10); 192 Index /= 10; 193 } 194 195 IndexBuffer[8 - IndexLength] = L'~'; 196 197 /* Reset name length */ 198 Name8dot3->Length = 0; 199 200 /* If name present */ 201 if (Context->NameLength) 202 { 203 /* Copy name buffer */ 204 Length = Context->NameLength * sizeof(WCHAR); 205 RtlCopyMemory(Name8dot3->Buffer, Context->NameBuffer, Length); 206 Name8dot3->Length = Length; 207 } 208 209 /* Copy index buffer */ 210 Length = IndexLength * sizeof(WCHAR); 211 RtlCopyMemory(Name8dot3->Buffer + (Name8dot3->Length / sizeof(WCHAR)), 212 IndexBuffer + (8 - IndexLength), 213 Length); 214 Name8dot3->Length += Length; 215 216 /* If extension present */ 217 if (Context->ExtensionLength) 218 { 219 /* Copy extension buffer */ 220 Length = Context->ExtensionLength * sizeof(WCHAR); 221 RtlCopyMemory(Name8dot3->Buffer + (Name8dot3->Length / sizeof(WCHAR)), 222 Context->ExtensionBuffer, 223 Length); 224 Name8dot3->Length += Length; 225 } 226 } 227 228 229 /* 230 * @implemented 231 * Note: the function does not conform to the annotations. 232 * SpacesFound is not always set! 233 */ 234 _IRQL_requires_max_(PASSIVE_LEVEL) 235 _Must_inspect_result_ 236 NTSYSAPI 237 BOOLEAN 238 NTAPI 239 RtlIsNameLegalDOS8Dot3(IN PCUNICODE_STRING Name, 240 IN OUT POEM_STRING OemName, 241 OUT PBOOLEAN NameContainsSpaces OPTIONAL) 242 { 243 static const char Illegal[] = "*?<>|\"+=,;[]:/\\\345"; 244 int Dot = -1; 245 int i; 246 char Buffer[12]; 247 OEM_STRING OemString; 248 BOOLEAN GotSpace = FALSE; 249 NTSTATUS Status; 250 251 if (!OemName) 252 { 253 OemString.Length = sizeof(Buffer); 254 OemString.MaximumLength = sizeof(Buffer); 255 OemString.Buffer = Buffer; 256 OemName = &OemString; 257 } 258 259 Status = RtlUpcaseUnicodeStringToCountedOemString(OemName, Name, FALSE); 260 if (!NT_SUCCESS(Status)) 261 return FALSE; 262 263 if ((OemName->Length > 12) || (OemName->Buffer == NULL)) return FALSE; 264 265 /* a starting . is invalid, except for . and .. */ 266 if (OemName->Buffer[0] == '.') 267 { 268 if (OemName->Length != 1 && (OemName->Length != 2 || OemName->Buffer[1] != '.')) return FALSE; 269 if (NameContainsSpaces) *NameContainsSpaces = FALSE; 270 271 return TRUE; 272 } 273 274 for (i = 0; i < OemName->Length; i++) 275 { 276 switch (OemName->Buffer[i]) 277 { 278 case ' ': 279 /* leading/trailing spaces not allowed */ 280 if (!i || i == OemName->Length-1 || OemName->Buffer[i+1] == '.') return FALSE; 281 GotSpace = TRUE; 282 break; 283 284 case '.': 285 if (Dot != -1) return FALSE; 286 Dot = i; 287 break; 288 289 default: 290 if (strchr(Illegal, OemName->Buffer[i])) return FALSE; 291 break; 292 } 293 } 294 /* check file part is shorter than 8, extension shorter than 3 295 * dot cannot be last in string 296 */ 297 if (Dot == -1) 298 { 299 if (OemName->Length > 8) return FALSE; 300 } 301 else 302 { 303 if (Dot > 8 || (OemName->Length - Dot > 4) || Dot == OemName->Length - 1) return FALSE; 304 } 305 306 if (NameContainsSpaces) *NameContainsSpaces = GotSpace; 307 308 return TRUE; 309 } 310 311 /* EOF */ 312