2bf4af91b37691d96a56d7873cc52943420dc30c
[reactos.git] / reactos / 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 USHORT ExpressionPosition = 0, NamePosition = 0, MatchingChars, StarFound = MAXUSHORT;
164 PAGED_CODE();
165
166 ASSERT(Name->Length);
167 ASSERT(Expression->Length);
168 ASSERT(!FsRtlDoesDbcsContainWildCards(Name));
169
170 while (NamePosition < Name->Length && ExpressionPosition < Expression->Length)
171 {
172 if ((Expression->Buffer[ExpressionPosition] == Name->Buffer[NamePosition]))
173 {
174 NamePosition++;
175 ExpressionPosition++;
176 }
177 else if (StarFound != MAXUSHORT && (Expression->Buffer[StarFound + 1] == '*' ||
178 Expression->Buffer[StarFound + 1] == '?' || Expression->Buffer[StarFound + 1] == ANSI_DOS_DOT))
179 {
180 ExpressionPosition = StarFound + 1;
181 switch (Expression->Buffer[ExpressionPosition])
182 {
183 case '*':
184 StarFound = MAXUSHORT;
185 break;
186
187 case '?':
188 if (++ExpressionPosition == Expression->Length)
189 {
190 NamePosition = Name->Length;
191 break;
192 }
193
194 MatchingChars = NamePosition;
195 while (NamePosition < Name->Length &&
196 Name->Buffer[NamePosition] != Expression->Buffer[ExpressionPosition])
197 {
198 NamePosition++;
199 }
200
201 if (NamePosition - MatchingChars > 0)
202 {
203 StarFound = MAXUSHORT;
204 }
205 break;
206
207 case ANSI_DOS_DOT:
208 while (NamePosition < Name->Length && Name->Buffer[NamePosition] != '.')
209 {
210 NamePosition++;
211 }
212 ExpressionPosition++;
213 StarFound = MAXUSHORT;
214 break;
215
216 default:
217 /* Should never happen */
218 ASSERT(FALSE);
219 }
220 }
221 else if ((Expression->Buffer[ExpressionPosition] == '?') || (Expression->Buffer[ExpressionPosition] == ANSI_DOS_QM) ||
222 (Expression->Buffer[ExpressionPosition] == ANSI_DOS_DOT && Name->Buffer[NamePosition] == '.'))
223 {
224 NamePosition++;
225 ExpressionPosition++;
226 StarFound = MAXUSHORT;
227 }
228 else if (Expression->Buffer[ExpressionPosition] == '*')
229 {
230 StarFound = ExpressionPosition++;
231 if (ExpressionPosition == Expression->Length)
232 {
233 NamePosition = Name->Length;
234 break;
235 }
236 }
237 else if (Expression->Buffer[ExpressionPosition] == ANSI_DOS_STAR)
238 {
239 StarFound = MAXUSHORT;
240 MatchingChars = NamePosition;
241 while (MatchingChars < Name->Length)
242 {
243 if (Name->Buffer[MatchingChars] == '.')
244 {
245 NamePosition = MatchingChars;
246 }
247 MatchingChars++;
248 }
249 ExpressionPosition++;
250 }
251 else if (StarFound != MAXUSHORT)
252 {
253 ExpressionPosition = StarFound + 1;
254 while (NamePosition < Name->Length &&
255 Name->Buffer[NamePosition] != Expression->Buffer[ExpressionPosition])
256 {
257 NamePosition++;
258 }
259 }
260 else
261 {
262 break;
263 }
264 }
265 if (ExpressionPosition + 1 == Expression->Length && NamePosition == Name->Length &&
266 Expression->Buffer[ExpressionPosition] == ANSI_DOS_DOT)
267 {
268 ExpressionPosition++;
269 }
270
271 return (ExpressionPosition == Expression->Length && NamePosition == Name->Length);
272 }
273
274 /*++
275 * @name FsRtlIsFatDbcsLegal
276 * @implemented
277 *
278 * Returns TRUE if the given DbcsName is a valid FAT filename (in 8.3)
279 *
280 * @param DbcsName
281 * The filename to check. It can also contains pathname.
282 *
283 * @param WildCardsPermissible
284 * If this is set to FALSE and if filename contains wildcard, the function
285 * will fail
286 *
287 * @param PathNamePermissible
288 * If this is set to FALSE and if the filename comes with a pathname, the
289 * function will fail
290 *
291 * @param LeadingBackslashPermissible
292 * If this is set to FALSE and if the filename starts with a backslash, the
293 * function will fail
294 *
295 * @return TRUE if the DbcsName is legal, FALSE otherwise
296 *
297 * @remarks None
298 *
299 *--*/
300 BOOLEAN
301 NTAPI
302 FsRtlIsFatDbcsLegal(IN ANSI_STRING DbcsName,
303 IN BOOLEAN WildCardsPermissible,
304 IN BOOLEAN PathNamePermissible,
305 IN BOOLEAN LeadingBackslashPermissible)
306 {
307 ANSI_STRING FirstPart, RemainingPart, Name;
308 BOOLEAN LastDot;
309 USHORT i;
310 PAGED_CODE();
311
312 /* Just quit if the string is empty */
313 if (!DbcsName.Length)
314 return FALSE;
315
316 /* DbcsName wasn't supposed to be started with \ */
317 if (!LeadingBackslashPermissible && DbcsName.Buffer[0] == '\\')
318 return FALSE;
319 /* DbcsName was allowed to be started with \, but now, remove it */
320 else if (LeadingBackslashPermissible && DbcsName.Buffer[0] == '\\')
321 {
322 DbcsName.Buffer = DbcsName.Buffer + 1;
323 DbcsName.Length = DbcsName.Length - 1;
324 DbcsName.MaximumLength = DbcsName.MaximumLength - 1;
325 }
326
327 /* Extract first part of the DbcsName to work on */
328 FsRtlDissectDbcs(DbcsName, &FirstPart, &RemainingPart);
329 while (FirstPart.Length > 0)
330 {
331 /* Reset dots count */
332 LastDot = FALSE;
333
334 /* Accept special filename if wildcards are allowed */
335 if (WildCardsPermissible && (FirstPart.Length == 1 || FirstPart.Length == 2) && FirstPart.Buffer[0] == '.')
336 {
337 if (FirstPart.Length == 2)
338 {
339 if (FirstPart.Buffer[1] == '.')
340 {
341 goto EndLoop;
342 }
343 }
344 else
345 {
346 goto EndLoop;
347 }
348 }
349
350 /* Filename must be 8.3 filename */
351 if (FirstPart.Length < 3 || FirstPart.Length > 12)
352 return FALSE;
353
354 /* Now, we will parse the filename to find everything bad in */
355 for (i = 0; i < FirstPart.Length; i++)
356 {
357 /* First make sure the character it's not the Lead DBCS */
358 if (FsRtlIsLeadDbcsCharacter(FirstPart.Buffer[i]))
359 {
360 if (i == (FirstPart.Length) - 1)
361 return FALSE;
362 i++;
363 }
364 /* Then check for bad characters */
365 else if (!FsRtlIsAnsiCharacterLegalFat(FirstPart.Buffer[i], WildCardsPermissible))
366 {
367 return FALSE;
368 }
369 else if (FirstPart.Buffer[i] == '.')
370 {
371 /* Filename can only contain one dot */
372 if (LastDot)
373 return FALSE;
374
375 LastDot = TRUE;
376
377 /* We mustn't have spaces before dot or at the end of the filename
378 * and no dot at the beginning of the filename */
379 if ((i == (FirstPart.Length) - 1) || i == 0)
380 return FALSE;
381
382 if (i > 0)
383 if (FirstPart.Buffer[i - 1] == ' ')
384 return FALSE;
385
386 /* Filename must be 8.3 filename and not 3.8 filename */
387 if ((FirstPart.Length - 1) - i > 3)
388 return FALSE;
389 }
390 }
391
392 /* Filename mustn't finish with a space */
393 if (FirstPart.Buffer[FirstPart.Length - 1] == ' ')
394 return FALSE;
395
396 EndLoop:
397 /* Preparing next loop */
398 Name.Buffer = RemainingPart.Buffer;
399 Name.Length = RemainingPart.Length;
400 Name.MaximumLength = RemainingPart.MaximumLength;
401
402 /* Call once again our dissect function */
403 FsRtlDissectDbcs(Name, &FirstPart, &RemainingPart);
404
405 /* We found a pathname, it wasn't allowed */
406 if (FirstPart.Length > 0 && !PathNamePermissible)
407 return FALSE;
408 }
409 return TRUE;
410 }
411
412 /*++
413 * @name FsRtlIsHpfsDbcsLegal
414 * @implemented
415 *
416 * Returns TRUE if the given DbcsName is a valid HPFS filename
417 *
418 * @param DbcsName
419 * The filename to check. It can also contains pathname.
420 *
421 * @param WildCardsPermissible
422 * If this is set to FALSE and if filename contains wildcard, the function
423 * will fail
424 *
425 * @param PathNamePermissible
426 * If this is set to FALSE and if the filename comes with a pathname, the
427 * function will fail
428 *
429 * @param LeadingBackslashPermissible
430 * If this is set to FALSE and if the filename starts with a backslash, the
431 * function will fail
432 *
433 * @return TRUE if the DbcsName is legal, FALSE otherwise
434 *
435 * @remarks None
436 *
437 *--*/
438 BOOLEAN
439 NTAPI
440 FsRtlIsHpfsDbcsLegal(IN ANSI_STRING DbcsName,
441 IN BOOLEAN WildCardsPermissible,
442 IN BOOLEAN PathNamePermissible,
443 IN BOOLEAN LeadingBackslashPermissible)
444 {
445 ANSI_STRING FirstPart, RemainingPart, Name;
446 USHORT i;
447 PAGED_CODE();
448
449 /* Just quit if the string is empty */
450 if (!DbcsName.Length)
451 return FALSE;
452
453 /* DbcsName wasn't supposed to be started with \ */
454 if (!LeadingBackslashPermissible && DbcsName.Buffer[0] == '\\')
455 return FALSE;
456 /* DbcsName was allowed to be started with \, but now, remove it */
457 else if (LeadingBackslashPermissible && DbcsName.Buffer[0] == '\\')
458 {
459 DbcsName.Buffer = DbcsName.Buffer + 1;
460 DbcsName.Length = DbcsName.Length - 1;
461 DbcsName.MaximumLength = DbcsName.MaximumLength - 1;
462 }
463
464 /* Extract first part of the DbcsName to work on */
465 FsRtlDissectDbcs(DbcsName, &FirstPart, &RemainingPart);
466 while (FirstPart.Length > 0)
467 {
468 /* Accept special filename if wildcards are allowed */
469 if (WildCardsPermissible && (FirstPart.Length == 1 || FirstPart.Length == 2) && FirstPart.Buffer[0] == '.')
470 {
471 if (FirstPart.Length == 2)
472 {
473 if (FirstPart.Buffer[1] == '.')
474 {
475 goto EndLoop;
476 }
477 }
478 else
479 {
480 goto EndLoop;
481 }
482 }
483
484 /* Filename must be 255 bytes maximum */
485 if (FirstPart.Length > 255)
486 return FALSE;
487
488 /* Now, we will parse the filename to find everything bad in */
489 for (i = 0; i < FirstPart.Length; i++)
490 {
491 /* First make sure the character it's not the Lead DBCS */
492 if (FsRtlIsLeadDbcsCharacter(FirstPart.Buffer[i]))
493 {
494 if (i == (FirstPart.Length) - 1)
495 return FALSE;
496 i++;
497 }
498 /* Then check for bad characters */
499 else if (!FsRtlIsAnsiCharacterLegalHpfs(FirstPart.Buffer[i], WildCardsPermissible))
500 {
501 return FALSE;
502 }
503 }
504
505 /* Filename mustn't finish with a space or a dot */
506 if ((FirstPart.Buffer[FirstPart.Length - 1] == ' ') ||
507 (FirstPart.Buffer[FirstPart.Length - 1] == '.'))
508 return FALSE;
509
510 EndLoop:
511 /* Preparing next loop */
512 Name.Buffer = RemainingPart.Buffer;
513 Name.Length = RemainingPart.Length;
514 Name.MaximumLength = RemainingPart.MaximumLength;
515
516 /* Call once again our dissect function */
517 FsRtlDissectDbcs(Name, &FirstPart, &RemainingPart);
518
519 /* We found a pathname, it wasn't allowed */
520 if (FirstPart.Length > 0 && !PathNamePermissible)
521 return FALSE;
522 }
523 return TRUE;
524 }