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