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)
10 /* INCLUDES ******************************************************************/
16 /* PUBLIC FUNCTIONS **********************************************************/
19 * @name FsRtlDissectDbcs
22 * Dissects a given path name into first and remaining part.
25 * ANSI string to dissect.
28 * Pointer to user supplied ANSI_STRING, that will later point
29 * to the first part of the original name.
31 * @param RemainingPart
32 * Pointer to user supplied ANSI_STRING, that will later point
33 * to the remaining part of the original name.
38 * Name: \test1\test2\test3
40 * RemainingPart: test2\test3
45 FsRtlDissectDbcs(IN ANSI_STRING Name
,
46 OUT PANSI_STRING FirstPart
,
47 OUT PANSI_STRING RemainingPart
)
49 USHORT FirstPosition
, i
;
50 USHORT SkipFirstSlash
= 0;
53 /* Zero the strings before continuing */
54 RtlZeroMemory(FirstPart
, sizeof(ANSI_STRING
));
55 RtlZeroMemory(RemainingPart
, sizeof(ANSI_STRING
));
57 /* Just quit if the string is empty */
58 if (!Name
.Length
) return;
60 /* Find first backslash */
61 FirstPosition
= Name
.Length
;
62 for (i
= 0; i
< Name
.Length
; i
++)
64 /* First make sure the character it's not the Lead DBCS */
65 if (FsRtlIsLeadDbcsCharacter(Name
.Buffer
[i
]))
69 /* If we found one... */
70 else if (Name
.Buffer
[i
] == '\\')
72 /* If it begins string, just notice it and continue */
79 /* Else, save its position and break out of the loop */
86 /* Set up the first result string */
87 FirstPart
->Buffer
= Name
.Buffer
+ SkipFirstSlash
;
88 FirstPart
->Length
= (FirstPosition
- SkipFirstSlash
);
89 FirstPart
->MaximumLength
= FirstPart
->Length
;
91 /* And second one, if necessary */
92 if (FirstPosition
< (Name
.Length
))
94 RemainingPart
->Buffer
= Name
.Buffer
+ FirstPosition
+ 1;
95 RemainingPart
->Length
= Name
.Length
- (FirstPosition
+ 1);
96 RemainingPart
->MaximumLength
= RemainingPart
->Length
;
101 * @name FsRtlDoesDbcsContainWildCards
104 * Returns TRUE if the given DbcsName contains wildcards such as *, ?,
105 * ANSI_DOS_STAR, ANSI_DOS_DOT, and ANSI_DOS_QM
110 * @return TRUE if there are wildcards, FALSE otherwise
117 FsRtlDoesDbcsContainWildCards(IN PANSI_STRING Name
)
122 /* Check every character */
123 for (i
= 0; i
< Name
->Length
; i
++)
125 /* First make sure it's not the Lead DBCS */
126 if (FsRtlIsLeadDbcsCharacter(Name
->Buffer
[i
]))
130 else if (FsRtlIsAnsiCharacterWild(Name
->Buffer
[i
]))
132 /* Now return if it has a wildcard */
137 /* We didn't return above...so none found */
142 * @name FsRtlIsDbcsInExpression
145 * Check if the Name string is in the Expression string.
148 * The string in which we've to find Name. It can contains wildcards
151 * The string to find. It cannot contain wildcards.
153 * @return TRUE if Name is found in Expression, FALSE otherwise
160 FsRtlIsDbcsInExpression(IN PANSI_STRING Expression
,
161 IN PANSI_STRING Name
)
163 SHORT StarFound
= -1, DosStarFound
= -1;
164 PUSHORT BackTracking
= NULL
, DosBackTracking
= NULL
;
165 USHORT ExpressionPosition
= 0, NamePosition
= 0, MatchingChars
, LastDot
;
168 ASSERT(Name
->Length
);
169 ASSERT(Expression
->Length
);
170 ASSERT(!FsRtlDoesDbcsContainWildCards(Name
));
172 if (Name
->Length
== 0)
174 return (Expression
->Length
== 0);
177 while (NamePosition
< Name
->Length
&& ExpressionPosition
< Expression
->Length
)
179 /* Basic check to test if chars are equal */
180 if ((Expression
->Buffer
[ExpressionPosition
] == Name
->Buffer
[NamePosition
]))
183 ExpressionPosition
++;
185 /* Check cases that eat one char */
186 else if (Expression
->Buffer
[ExpressionPosition
] == '?')
189 ExpressionPosition
++;
192 else if (Expression
->Buffer
[ExpressionPosition
] == '*')
194 /* Skip contigous stars */
195 while (ExpressionPosition
+ 1 < Expression
->Length
&& Expression
->Buffer
[ExpressionPosition
+ 1] == '*')
197 ExpressionPosition
++;
200 /* Save star position */
203 BackTracking
= ExAllocatePoolWithTag(PagedPool
| POOL_RAISE_IF_ALLOCATION_FAILURE
,
204 Expression
->Length
* sizeof(USHORT
), 'nrSF');
206 BackTracking
[++StarFound
] = ExpressionPosition
++;
208 /* If star is at the end, then eat all rest and leave */
209 if (ExpressionPosition
== Expression
->Length
)
211 NamePosition
= Name
->Length
;
214 /* Allow null matching */
215 else if (Expression
->Buffer
[ExpressionPosition
] != '?' &&
216 Expression
->Buffer
[ExpressionPosition
] != Name
->Buffer
[NamePosition
])
222 else if (Expression
->Buffer
[ExpressionPosition
] == ANSI_DOS_STAR
)
224 /* Skip contigous stars */
225 while (ExpressionPosition
+ 1 < Expression
->Length
&& Expression
->Buffer
[ExpressionPosition
+ 1] == ANSI_DOS_STAR
)
227 ExpressionPosition
++;
230 /* Look for last dot */
232 LastDot
= (USHORT
)-1;
233 while (MatchingChars
< Name
->Length
)
235 if (Name
->Buffer
[MatchingChars
] == '.')
237 LastDot
= MatchingChars
;
238 if (LastDot
> NamePosition
)
245 /* If we don't have dots or we didn't find last yet
246 * start eating everything
248 if (MatchingChars
!= Name
->Length
|| LastDot
== (USHORT
)-1)
250 if (!DosBackTracking
) DosBackTracking
= ExAllocatePoolWithTag(PagedPool
| POOL_RAISE_IF_ALLOCATION_FAILURE
,
251 Expression
->Length
* sizeof(USHORT
), 'nrSF');
252 DosBackTracking
[++DosStarFound
] = ExpressionPosition
++;
254 /* Not the same char, start exploring */
255 if (Expression
->Buffer
[ExpressionPosition
] != Name
->Buffer
[NamePosition
])
260 /* Else, if we are at last dot, eat it - otherwise, null match */
261 if (Name
->Buffer
[NamePosition
] == '.')
264 ExpressionPosition
++;
268 else if (Expression
->Buffer
[ExpressionPosition
] == ANSI_DOS_DOT
)
270 /* We only match dots */
271 if (Name
->Buffer
[NamePosition
] == '.')
275 /* Try to explore later on for null matching */
276 else if (ExpressionPosition
+ 1 < Expression
->Length
&&
277 Name
->Buffer
[NamePosition
] == Expression
->Buffer
[ExpressionPosition
+ 1])
281 ExpressionPosition
++;
284 else if (Expression
->Buffer
[ExpressionPosition
] == ANSI_DOS_QM
)
286 /* We match everything except dots */
287 if (Name
->Buffer
[NamePosition
] != '.')
291 ExpressionPosition
++;
293 /* If nothing match, try to backtrack */
294 else if (StarFound
>= 0)
296 ExpressionPosition
= BackTracking
[StarFound
--];
298 else if (DosStarFound
>= 0)
300 ExpressionPosition
= DosBackTracking
[DosStarFound
--];
302 /* Otherwise, fail */
308 /* Under certain circumstances, expression is over, but name isn't
309 * and we can backtrack, then, backtrack */
310 if (ExpressionPosition
== Expression
->Length
&&
311 NamePosition
!= Name
->Length
&& StarFound
>= 0)
313 ExpressionPosition
= BackTracking
[StarFound
--];
316 /* If we have nullable matching wc at the end of the string, eat them */
317 if (ExpressionPosition
!= Expression
->Length
&& NamePosition
== Name
->Length
)
319 while (ExpressionPosition
< Expression
->Length
)
321 if (Expression
->Buffer
[ExpressionPosition
] != ANSI_DOS_DOT
&&
322 Expression
->Buffer
[ExpressionPosition
] != '*' &&
323 Expression
->Buffer
[ExpressionPosition
] != ANSI_DOS_STAR
)
327 ExpressionPosition
++;
333 ExFreePoolWithTag(BackTracking
, 'nrSF');
337 ExFreePoolWithTag(DosBackTracking
, 'nrSF');
340 return (ExpressionPosition
== Expression
->Length
&& NamePosition
== Name
->Length
);
344 * @name FsRtlIsFatDbcsLegal
347 * Returns TRUE if the given DbcsName is a valid FAT filename (in 8.3)
350 * The filename to check. It can also contains pathname.
352 * @param WildCardsPermissible
353 * If this is set to FALSE and if filename contains wildcard, the function
356 * @param PathNamePermissible
357 * If this is set to FALSE and if the filename comes with a pathname, the
360 * @param LeadingBackslashPermissible
361 * If this is set to FALSE and if the filename starts with a backslash, the
364 * @return TRUE if the DbcsName is legal, FALSE otherwise
371 FsRtlIsFatDbcsLegal(IN ANSI_STRING DbcsName
,
372 IN BOOLEAN WildCardsPermissible
,
373 IN BOOLEAN PathNamePermissible
,
374 IN BOOLEAN LeadingBackslashPermissible
)
376 ANSI_STRING FirstPart
, RemainingPart
, Name
;
381 /* Just quit if the string is empty */
382 if (!DbcsName
.Length
)
385 /* DbcsName wasn't supposed to be started with \ */
386 if (!LeadingBackslashPermissible
&& DbcsName
.Buffer
[0] == '\\')
388 /* DbcsName was allowed to be started with \, but now, remove it */
389 else if (LeadingBackslashPermissible
&& DbcsName
.Buffer
[0] == '\\')
391 DbcsName
.Buffer
= DbcsName
.Buffer
+ 1;
392 DbcsName
.Length
= DbcsName
.Length
- 1;
393 DbcsName
.MaximumLength
= DbcsName
.MaximumLength
- 1;
396 /* Extract first part of the DbcsName to work on */
397 FsRtlDissectDbcs(DbcsName
, &FirstPart
, &RemainingPart
);
398 while (FirstPart
.Length
> 0)
400 /* Reset dots count */
403 /* Accept special filename if wildcards are allowed */
404 if (WildCardsPermissible
&& (FirstPart
.Length
== 1 || FirstPart
.Length
== 2) && FirstPart
.Buffer
[0] == '.')
406 if (FirstPart
.Length
== 2)
408 if (FirstPart
.Buffer
[1] == '.')
419 /* Filename must be 8.3 filename */
420 if (FirstPart
.Length
< 3 || FirstPart
.Length
> 12)
423 /* Now, we will parse the filename to find everything bad in */
424 for (i
= 0; i
< FirstPart
.Length
; i
++)
426 /* First make sure the character it's not the Lead DBCS */
427 if (FsRtlIsLeadDbcsCharacter(FirstPart
.Buffer
[i
]))
429 if (i
== (FirstPart
.Length
) - 1)
433 /* Then check for bad characters */
434 else if (!FsRtlIsAnsiCharacterLegalFat(FirstPart
.Buffer
[i
], WildCardsPermissible
))
438 else if (FirstPart
.Buffer
[i
] == '.')
440 /* Filename can only contain one dot */
446 /* We mustn't have spaces before dot or at the end of the filename
447 * and no dot at the beginning of the filename */
448 if ((i
== (FirstPart
.Length
) - 1) || i
== 0)
452 if (FirstPart
.Buffer
[i
- 1] == ' ')
455 /* Filename must be 8.3 filename and not 3.8 filename */
456 if ((FirstPart
.Length
- 1) - i
> 3)
461 /* Filename mustn't finish with a space */
462 if (FirstPart
.Buffer
[FirstPart
.Length
- 1] == ' ')
466 /* Preparing next loop */
467 Name
.Buffer
= RemainingPart
.Buffer
;
468 Name
.Length
= RemainingPart
.Length
;
469 Name
.MaximumLength
= RemainingPart
.MaximumLength
;
471 /* Call once again our dissect function */
472 FsRtlDissectDbcs(Name
, &FirstPart
, &RemainingPart
);
474 /* We found a pathname, it wasn't allowed */
475 if (FirstPart
.Length
> 0 && !PathNamePermissible
)
482 * @name FsRtlIsHpfsDbcsLegal
485 * Returns TRUE if the given DbcsName is a valid HPFS filename
488 * The filename to check. It can also contains pathname.
490 * @param WildCardsPermissible
491 * If this is set to FALSE and if filename contains wildcard, the function
494 * @param PathNamePermissible
495 * If this is set to FALSE and if the filename comes with a pathname, the
498 * @param LeadingBackslashPermissible
499 * If this is set to FALSE and if the filename starts with a backslash, the
502 * @return TRUE if the DbcsName is legal, FALSE otherwise
509 FsRtlIsHpfsDbcsLegal(IN ANSI_STRING DbcsName
,
510 IN BOOLEAN WildCardsPermissible
,
511 IN BOOLEAN PathNamePermissible
,
512 IN BOOLEAN LeadingBackslashPermissible
)
514 ANSI_STRING FirstPart
, RemainingPart
, Name
;
518 /* Just quit if the string is empty */
519 if (!DbcsName
.Length
)
522 /* DbcsName wasn't supposed to be started with \ */
523 if (!LeadingBackslashPermissible
&& DbcsName
.Buffer
[0] == '\\')
525 /* DbcsName was allowed to be started with \, but now, remove it */
526 else if (LeadingBackslashPermissible
&& DbcsName
.Buffer
[0] == '\\')
528 DbcsName
.Buffer
= DbcsName
.Buffer
+ 1;
529 DbcsName
.Length
= DbcsName
.Length
- 1;
530 DbcsName
.MaximumLength
= DbcsName
.MaximumLength
- 1;
533 /* Extract first part of the DbcsName to work on */
534 FsRtlDissectDbcs(DbcsName
, &FirstPart
, &RemainingPart
);
535 while (FirstPart
.Length
> 0)
537 /* Accept special filename if wildcards are allowed */
538 if (WildCardsPermissible
&& (FirstPart
.Length
== 1 || FirstPart
.Length
== 2) && FirstPart
.Buffer
[0] == '.')
540 if (FirstPart
.Length
== 2)
542 if (FirstPart
.Buffer
[1] == '.')
553 /* Filename must be 255 bytes maximum */
554 if (FirstPart
.Length
> 255)
557 /* Now, we will parse the filename to find everything bad in */
558 for (i
= 0; i
< FirstPart
.Length
; i
++)
560 /* First make sure the character it's not the Lead DBCS */
561 if (FsRtlIsLeadDbcsCharacter(FirstPart
.Buffer
[i
]))
563 if (i
== (FirstPart
.Length
) - 1)
567 /* Then check for bad characters */
568 else if (!FsRtlIsAnsiCharacterLegalHpfs(FirstPart
.Buffer
[i
], WildCardsPermissible
))
574 /* Filename mustn't finish with a space or a dot */
575 if ((FirstPart
.Buffer
[FirstPart
.Length
- 1] == ' ') ||
576 (FirstPart
.Buffer
[FirstPart
.Length
- 1] == '.'))
580 /* Preparing next loop */
581 Name
.Buffer
= RemainingPart
.Buffer
;
582 Name
.Length
= RemainingPart
.Length
;
583 Name
.MaximumLength
= RemainingPart
.MaximumLength
;
585 /* Call once again our dissect function */
586 FsRtlDissectDbcs(Name
, &FirstPart
, &RemainingPart
);
588 /* We found a pathname, it wasn't allowed */
589 if (FirstPart
.Length
> 0 && !PathNamePermissible
)