xref: /reactos/sdk/lib/rtl/dos8dot3.c (revision c2c66aff)
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