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