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