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