Merge r55012 adding Wine3D control panel as per Amine's request.
[reactos.git] / ntoskrnl / fsrtl / dbcsname.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/fsrtl/name.c
5 * PURPOSE: Provides DBCS parsing and other support routines for FSDs
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Pierre Schweitzer (pierre.schweitzer@reactos.org)
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 /* PUBLIC FUNCTIONS **********************************************************/
17
18 /*++
19 * @name FsRtlDissectDbcs
20 * @implemented
21 *
22 * Dissects a given path name into first and remaining part.
23 *
24 * @param Name
25 * ANSI string to dissect.
26 *
27 * @param FirstPart
28 * Pointer to user supplied ANSI_STRING, that will later point
29 * to the first part of the original name.
30 *
31 * @param RemainingPart
32 * Pointer to user supplied ANSI_STRING, that will later point
33 * to the remaining part of the original name.
34 *
35 * @return None
36 *
37 * @remarks Example:
38 * Name: \test1\test2\test3
39 * FirstPart: test1
40 * RemainingPart: test2\test3
41 *
42 *--*/
43 VOID
44 NTAPI
45 FsRtlDissectDbcs(IN ANSI_STRING Name,
46 OUT PANSI_STRING FirstPart,
47 OUT PANSI_STRING RemainingPart)
48 {
49 USHORT FirstPosition, i;
50 USHORT SkipFirstSlash = 0;
51 PAGED_CODE();
52
53 /* Zero the strings before continuing */
54 RtlZeroMemory(FirstPart, sizeof(ANSI_STRING));
55 RtlZeroMemory(RemainingPart, sizeof(ANSI_STRING));
56
57 /* Just quit if the string is empty */
58 if (!Name.Length) return;
59
60 /* Find first backslash */
61 FirstPosition = Name.Length;
62 for (i = 0; i < Name.Length; i++)
63 {
64 /* First make sure the character it's not the Lead DBCS */
65 if (FsRtlIsLeadDbcsCharacter(Name.Buffer[i]))
66 {
67 i++;
68 }
69 /* If we found one... */
70 else if (Name.Buffer[i] == '\\')
71 {
72 /* If it begins string, just notice it and continue */
73 if (i == 0)
74 {
75 SkipFirstSlash = 1;
76 }
77 else
78 {
79 /* Else, save its position and break out of the loop */
80 FirstPosition = i;
81 break;
82 }
83 }
84 }
85
86 /* Set up the first result string */
87 FirstPart->Buffer = Name.Buffer + SkipFirstSlash;
88 FirstPart->Length = (FirstPosition - SkipFirstSlash);
89 FirstPart->MaximumLength = FirstPart->Length;
90
91 /* And second one, if necessary */
92 if (FirstPosition < (Name.Length))
93 {
94 RemainingPart->Buffer = Name.Buffer + FirstPosition + 1;
95 RemainingPart->Length = Name.Length - (FirstPosition + 1);
96 RemainingPart->MaximumLength = RemainingPart->Length;
97 }
98 }
99
100 /*++
101 * @name FsRtlDoesDbcsContainWildCards
102 * @implemented
103 *
104 * Returns TRUE if the given DbcsName contains wildcards such as *, ?,
105 * ANSI_DOS_STAR, ANSI_DOS_DOT, and ANSI_DOS_QM
106 *
107 * @param Name
108 * The Name to check
109 *
110 * @return TRUE if there are wildcards, FALSE otherwise
111 *
112 * @remarks None
113 *
114 *--*/
115 BOOLEAN
116 NTAPI
117 FsRtlDoesDbcsContainWildCards(IN PANSI_STRING Name)
118 {
119 USHORT i;
120 PAGED_CODE();
121
122 /* Check every character */
123 for (i = 0; i < Name->Length; i++)
124 {
125 /* First make sure it's not the Lead DBCS */
126 if (FsRtlIsLeadDbcsCharacter(Name->Buffer[i]))
127 {
128 i++;
129 }
130 else if (FsRtlIsAnsiCharacterWild(Name->Buffer[i]))
131 {
132 /* Now return if it has a wildcard */
133 return TRUE;
134 }
135 }
136
137 /* We didn't return above...so none found */
138 return FALSE;
139 }
140
141 /*++
142 * @name FsRtlIsDbcsInExpression
143 * @implemented
144 *
145 * Check if the Name string is in the Expression string.
146 *
147 * @param Expression
148 * The string in which we've to find Name. It can contains wildcards
149 *
150 * @param Name
151 * The string to find. It cannot contain wildcards.
152 *
153 * @return TRUE if Name is found in Expression, FALSE otherwise
154 *
155 * @remarks
156 *
157 *--*/
158 BOOLEAN
159 NTAPI
160 FsRtlIsDbcsInExpression(IN PANSI_STRING Expression,
161 IN PANSI_STRING Name)
162 {
163 SHORT StarFound = -1;
164 PUSHORT BackTracking = NULL;
165 USHORT ExpressionPosition = 0, NamePosition = 0, MatchingChars;
166 PAGED_CODE();
167
168 ASSERT(Name->Length);
169 ASSERT(Expression->Length);
170 ASSERT(!FsRtlDoesDbcsContainWildCards(Name));
171
172 while (NamePosition < Name->Length && ExpressionPosition < Expression->Length)
173 {
174 /* Basic check to test if chars are equal */
175 if ((Expression->Buffer[ExpressionPosition] == Name->Buffer[NamePosition]))
176 {
177 NamePosition++;
178 ExpressionPosition++;
179 }
180 /* Check cases that eat one char */
181 else if ((Expression->Buffer[ExpressionPosition] == '?') || (Expression->Buffer[ExpressionPosition] == ANSI_DOS_QM) ||
182 (Expression->Buffer[ExpressionPosition] == ANSI_DOS_DOT && Name->Buffer[NamePosition] == '.'))
183 {
184 NamePosition++;
185 ExpressionPosition++;
186 }
187 /* Test star */
188 else if (Expression->Buffer[ExpressionPosition] == '*')
189 {
190 /* Save star position */
191 if (!BackTracking)
192 {
193 BackTracking = ExAllocatePoolWithTag(PagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE,
194 Expression->Length * sizeof(USHORT), 'nrSF');
195 }
196 BackTracking[++StarFound] = ExpressionPosition++;
197
198 /* If star is at the end, then eat all rest and leave */
199 if (ExpressionPosition == Expression->Length)
200 {
201 NamePosition = Name->Length;
202 break;
203 }
204 else if (Expression->Buffer[ExpressionPosition] != '?')
205 {
206 NamePosition++;
207 }
208 }
209 /* Check DOS_STAR */
210 else if (Expression->Buffer[ExpressionPosition] == ANSI_DOS_STAR)
211 {
212 MatchingChars = NamePosition;
213 while (MatchingChars < Name->Length)
214 {
215 if (Name->Buffer[MatchingChars] == '.')
216 {
217 NamePosition = MatchingChars;
218 }
219 MatchingChars++;
220 }
221 ExpressionPosition++;
222 }
223 /* If nothing match, try to backtrack */
224 else if (StarFound >= 0)
225 {
226 ExpressionPosition = BackTracking[StarFound--];
227 }
228 /* Otherwise, fail */
229 else
230 {
231 break;
232 }
233
234 /* Under certain circumstances, expression is over, but name isn't
235 * and we can backtrack, then, backtrack */
236 if (ExpressionPosition == Expression->Length &&
237 NamePosition != Name->Length && StarFound >= 0)
238 {
239 ExpressionPosition = BackTracking[StarFound--];
240 }
241 }
242 if (ExpressionPosition + 1 == Expression->Length && NamePosition == Name->Length &&
243 Expression->Buffer[ExpressionPosition] == ANSI_DOS_DOT)
244 {
245 ExpressionPosition++;
246 }
247
248 return (ExpressionPosition == Expression->Length && NamePosition == Name->Length);
249 }
250
251 /*++
252 * @name FsRtlIsFatDbcsLegal
253 * @implemented
254 *
255 * Returns TRUE if the given DbcsName is a valid FAT filename (in 8.3)
256 *
257 * @param DbcsName
258 * The filename to check. It can also contains pathname.
259 *
260 * @param WildCardsPermissible
261 * If this is set to FALSE and if filename contains wildcard, the function
262 * will fail
263 *
264 * @param PathNamePermissible
265 * If this is set to FALSE and if the filename comes with a pathname, the
266 * function will fail
267 *
268 * @param LeadingBackslashPermissible
269 * If this is set to FALSE and if the filename starts with a backslash, the
270 * function will fail
271 *
272 * @return TRUE if the DbcsName is legal, FALSE otherwise
273 *
274 * @remarks None
275 *
276 *--*/
277 BOOLEAN
278 NTAPI
279 FsRtlIsFatDbcsLegal(IN ANSI_STRING DbcsName,
280 IN BOOLEAN WildCardsPermissible,
281 IN BOOLEAN PathNamePermissible,
282 IN BOOLEAN LeadingBackslashPermissible)
283 {
284 ANSI_STRING FirstPart, RemainingPart, Name;
285 BOOLEAN LastDot;
286 USHORT i;
287 PAGED_CODE();
288
289 /* Just quit if the string is empty */
290 if (!DbcsName.Length)
291 return FALSE;
292
293 /* DbcsName wasn't supposed to be started with \ */
294 if (!LeadingBackslashPermissible && DbcsName.Buffer[0] == '\\')
295 return FALSE;
296 /* DbcsName was allowed to be started with \, but now, remove it */
297 else if (LeadingBackslashPermissible && DbcsName.Buffer[0] == '\\')
298 {
299 DbcsName.Buffer = DbcsName.Buffer + 1;
300 DbcsName.Length = DbcsName.Length - 1;
301 DbcsName.MaximumLength = DbcsName.MaximumLength - 1;
302 }
303
304 /* Extract first part of the DbcsName to work on */
305 FsRtlDissectDbcs(DbcsName, &FirstPart, &RemainingPart);
306 while (FirstPart.Length > 0)
307 {
308 /* Reset dots count */
309 LastDot = FALSE;
310
311 /* Accept special filename if wildcards are allowed */
312 if (WildCardsPermissible && (FirstPart.Length == 1 || FirstPart.Length == 2) && FirstPart.Buffer[0] == '.')
313 {
314 if (FirstPart.Length == 2)
315 {
316 if (FirstPart.Buffer[1] == '.')
317 {
318 goto EndLoop;
319 }
320 }
321 else
322 {
323 goto EndLoop;
324 }
325 }
326
327 /* Filename must be 8.3 filename */
328 if (FirstPart.Length < 3 || FirstPart.Length > 12)
329 return FALSE;
330
331 /* Now, we will parse the filename to find everything bad in */
332 for (i = 0; i < FirstPart.Length; i++)
333 {
334 /* First make sure the character it's not the Lead DBCS */
335 if (FsRtlIsLeadDbcsCharacter(FirstPart.Buffer[i]))
336 {
337 if (i == (FirstPart.Length) - 1)
338 return FALSE;
339 i++;
340 }
341 /* Then check for bad characters */
342 else if (!FsRtlIsAnsiCharacterLegalFat(FirstPart.Buffer[i], WildCardsPermissible))
343 {
344 return FALSE;
345 }
346 else if (FirstPart.Buffer[i] == '.')
347 {
348 /* Filename can only contain one dot */
349 if (LastDot)
350 return FALSE;
351
352 LastDot = TRUE;
353
354 /* We mustn't have spaces before dot or at the end of the filename
355 * and no dot at the beginning of the filename */
356 if ((i == (FirstPart.Length) - 1) || i == 0)
357 return FALSE;
358
359 if (i > 0)
360 if (FirstPart.Buffer[i - 1] == ' ')
361 return FALSE;
362
363 /* Filename must be 8.3 filename and not 3.8 filename */
364 if ((FirstPart.Length - 1) - i > 3)
365 return FALSE;
366 }
367 }
368
369 /* Filename mustn't finish with a space */
370 if (FirstPart.Buffer[FirstPart.Length - 1] == ' ')
371 return FALSE;
372
373 EndLoop:
374 /* Preparing next loop */
375 Name.Buffer = RemainingPart.Buffer;
376 Name.Length = RemainingPart.Length;
377 Name.MaximumLength = RemainingPart.MaximumLength;
378
379 /* Call once again our dissect function */
380 FsRtlDissectDbcs(Name, &FirstPart, &RemainingPart);
381
382 /* We found a pathname, it wasn't allowed */
383 if (FirstPart.Length > 0 && !PathNamePermissible)
384 return FALSE;
385 }
386 return TRUE;
387 }
388
389 /*++
390 * @name FsRtlIsHpfsDbcsLegal
391 * @implemented
392 *
393 * Returns TRUE if the given DbcsName is a valid HPFS filename
394 *
395 * @param DbcsName
396 * The filename to check. It can also contains pathname.
397 *
398 * @param WildCardsPermissible
399 * If this is set to FALSE and if filename contains wildcard, the function
400 * will fail
401 *
402 * @param PathNamePermissible
403 * If this is set to FALSE and if the filename comes with a pathname, the
404 * function will fail
405 *
406 * @param LeadingBackslashPermissible
407 * If this is set to FALSE and if the filename starts with a backslash, the
408 * function will fail
409 *
410 * @return TRUE if the DbcsName is legal, FALSE otherwise
411 *
412 * @remarks None
413 *
414 *--*/
415 BOOLEAN
416 NTAPI
417 FsRtlIsHpfsDbcsLegal(IN ANSI_STRING DbcsName,
418 IN BOOLEAN WildCardsPermissible,
419 IN BOOLEAN PathNamePermissible,
420 IN BOOLEAN LeadingBackslashPermissible)
421 {
422 ANSI_STRING FirstPart, RemainingPart, Name;
423 USHORT i;
424 PAGED_CODE();
425
426 /* Just quit if the string is empty */
427 if (!DbcsName.Length)
428 return FALSE;
429
430 /* DbcsName wasn't supposed to be started with \ */
431 if (!LeadingBackslashPermissible && DbcsName.Buffer[0] == '\\')
432 return FALSE;
433 /* DbcsName was allowed to be started with \, but now, remove it */
434 else if (LeadingBackslashPermissible && DbcsName.Buffer[0] == '\\')
435 {
436 DbcsName.Buffer = DbcsName.Buffer + 1;
437 DbcsName.Length = DbcsName.Length - 1;
438 DbcsName.MaximumLength = DbcsName.MaximumLength - 1;
439 }
440
441 /* Extract first part of the DbcsName to work on */
442 FsRtlDissectDbcs(DbcsName, &FirstPart, &RemainingPart);
443 while (FirstPart.Length > 0)
444 {
445 /* Accept special filename if wildcards are allowed */
446 if (WildCardsPermissible && (FirstPart.Length == 1 || FirstPart.Length == 2) && FirstPart.Buffer[0] == '.')
447 {
448 if (FirstPart.Length == 2)
449 {
450 if (FirstPart.Buffer[1] == '.')
451 {
452 goto EndLoop;
453 }
454 }
455 else
456 {
457 goto EndLoop;
458 }
459 }
460
461 /* Filename must be 255 bytes maximum */
462 if (FirstPart.Length > 255)
463 return FALSE;
464
465 /* Now, we will parse the filename to find everything bad in */
466 for (i = 0; i < FirstPart.Length; i++)
467 {
468 /* First make sure the character it's not the Lead DBCS */
469 if (FsRtlIsLeadDbcsCharacter(FirstPart.Buffer[i]))
470 {
471 if (i == (FirstPart.Length) - 1)
472 return FALSE;
473 i++;
474 }
475 /* Then check for bad characters */
476 else if (!FsRtlIsAnsiCharacterLegalHpfs(FirstPart.Buffer[i], WildCardsPermissible))
477 {
478 return FALSE;
479 }
480 }
481
482 /* Filename mustn't finish with a space or a dot */
483 if ((FirstPart.Buffer[FirstPart.Length - 1] == ' ') ||
484 (FirstPart.Buffer[FirstPart.Length - 1] == '.'))
485 return FALSE;
486
487 EndLoop:
488 /* Preparing next loop */
489 Name.Buffer = RemainingPart.Buffer;
490 Name.Length = RemainingPart.Length;
491 Name.MaximumLength = RemainingPart.MaximumLength;
492
493 /* Call once again our dissect function */
494 FsRtlDissectDbcs(Name, &FirstPart, &RemainingPart);
495
496 /* We found a pathname, it wasn't allowed */
497 if (FirstPart.Length > 0 && !PathNamePermissible)
498 return FALSE;
499 }
500 return TRUE;
501 }