2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/fsrtl/dbcsname.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 USHORT Offset
, Position
, BackTrackingPosition
, OldBackTrackingPosition
;
164 USHORT BackTrackingBuffer
[16], OldBackTrackingBuffer
[16] = {0};
165 PUSHORT BackTrackingSwap
, BackTracking
= BackTrackingBuffer
, OldBackTracking
= OldBackTrackingBuffer
;
166 ULONG BackTrackingBufferSize
= RTL_NUMBER_OF(BackTrackingBuffer
);
167 PVOID AllocatedBuffer
= NULL
;
168 USHORT ExpressionPosition
, NamePosition
= 0, MatchingChars
= 1;
169 USHORT NameChar
= 0, ExpressionChar
;
170 BOOLEAN EndOfName
= FALSE
;
175 ASSERT(Name
->Length
);
176 ASSERT(Expression
->Length
);
177 ASSERT(!FsRtlDoesDbcsContainWildCards(Name
));
179 /* Check if we were given strings at all */
180 if (!Name
->Length
|| !Expression
->Length
)
182 /* Return TRUE if both strings are empty, otherwise FALSE */
183 if (Name
->Length
== 0 && Expression
->Length
== 0)
189 /* Check for a shortcut: just one wildcard */
190 if (Expression
->Length
== sizeof(CHAR
))
192 if (Expression
->Buffer
[0] == '*')
196 //ASSERT(FsRtlDoesDbcsContainWildCards(Expression));
198 /* Another shortcut, wildcard followed by some string */
199 if (Expression
->Buffer
[0] == '*')
201 /* Copy Expression to our local variable */
202 ANSI_STRING IntExpression
= *Expression
;
204 /* Skip the first char */
205 IntExpression
.Buffer
++;
206 IntExpression
.Length
-= sizeof(CHAR
);
208 /* Continue only if the rest of the expression does NOT contain
209 any more wildcards */
210 if (!FsRtlDoesDbcsContainWildCards(&IntExpression
))
212 /* Check for a degenerate case */
213 if (Name
->Length
< (Expression
->Length
- sizeof(CHAR
)))
216 /* Calculate position */
217 NamePosition
= (Name
->Length
- IntExpression
.Length
) / sizeof(CHAR
);
219 /* Check whether we are breaking a two chars char (DBCS) */
220 if (NlsMbOemCodePageTag
)
224 while (MatchingChars
< NamePosition
)
226 /* Check if current char is DBCS lead char, if so, jump by two chars */
227 MatchingChars
+= FsRtlIsLeadDbcsCharacter(Name
->Buffer
[MatchingChars
]) ? 2 : 1;
231 if (MatchingChars
> NamePosition
)
236 return RtlEqualMemory(IntExpression
.Buffer
,
237 (Name
->Buffer
+ NamePosition
),
238 IntExpression
.Length
);
242 /* Name parsing loop */
243 for (; !EndOfName
; MatchingChars
= BackTrackingPosition
)
245 /* Reset positions */
246 OldBackTrackingPosition
= BackTrackingPosition
= 0;
248 if (NamePosition
>= Name
->Length
)
251 if (OldBackTracking
[MatchingChars
- 1] == Expression
->Length
* 2)
256 /* If lead byte present */
257 if (FsRtlIsLeadDbcsCharacter(Name
->Buffer
[NamePosition
]))
259 NameChar
= Name
->Buffer
[NamePosition
] +
260 (0x100 * Name
->Buffer
[NamePosition
+ 1]);
261 NamePosition
+= sizeof(USHORT
);
265 NameChar
= Name
->Buffer
[NamePosition
];
266 NamePosition
+= sizeof(UCHAR
);
270 while (MatchingChars
> OldBackTrackingPosition
)
272 ExpressionPosition
= (OldBackTracking
[OldBackTrackingPosition
++] + 1) / 2;
274 /* Expression parsing loop */
275 for (Offset
= 0; ExpressionPosition
< Expression
->Length
; )
277 ExpressionPosition
+= Offset
;
279 if (ExpressionPosition
== Expression
->Length
)
281 BackTracking
[BackTrackingPosition
++] = Expression
->Length
* 2;
285 /* If buffer too small */
286 if (BackTrackingPosition
> BackTrackingBufferSize
- 2)
288 /* We should only ever get here once! */
289 ASSERT(AllocatedBuffer
== NULL
);
290 ASSERT((BackTracking
== BackTrackingBuffer
) || (BackTracking
== OldBackTrackingBuffer
));
291 ASSERT((OldBackTracking
== BackTrackingBuffer
) || (OldBackTracking
== OldBackTrackingBuffer
));
293 /* Calculate buffer size */
294 BackTrackingBufferSize
= (Expression
->Length
+ 1) * 2;
296 /* Allocate memory for both back-tracking buffers */
297 AllocatedBuffer
= ExAllocatePoolWithTag(PagedPool
| POOL_RAISE_IF_ALLOCATION_FAILURE
,
298 2 * BackTrackingBufferSize
* sizeof(USHORT
),
300 if (AllocatedBuffer
== NULL
)
302 DPRINT1("Failed to allocate BackTracking buffer. BackTrackingBufferSize = =x%lx\n",
303 BackTrackingBufferSize
);
308 /* Backtracking is at the start of the buffer */
309 BackTracking
= AllocatedBuffer
;
311 /* Copy BackTrackingBuffer content */
312 RtlCopyMemory(BackTracking
,
314 RTL_NUMBER_OF(BackTrackingBuffer
) * sizeof(USHORT
));
316 /* OldBackTracking is after BackTracking */
317 OldBackTracking
= &BackTracking
[BackTrackingBufferSize
];
319 /* Copy OldBackTrackingBuffer content */
320 RtlCopyMemory(OldBackTracking
,
321 OldBackTrackingBuffer
,
322 RTL_NUMBER_OF(OldBackTrackingBuffer
) * sizeof(USHORT
));
325 /* If lead byte present */
326 if (FsRtlIsLeadDbcsCharacter(Expression
->Buffer
[ExpressionPosition
]))
328 ExpressionChar
= Expression
->Buffer
[ExpressionPosition
] +
329 (0x100 * Expression
->Buffer
[ExpressionPosition
+ 1]);
330 Offset
= sizeof(USHORT
);
334 ExpressionChar
= Expression
->Buffer
[ExpressionPosition
];
335 Offset
= sizeof(UCHAR
);
338 /* Basic check to test if chars are equal */
339 if (ExpressionChar
== NameChar
&& !EndOfName
)
341 BackTracking
[BackTrackingPosition
++] = (ExpressionPosition
+ Offset
) * 2;
343 /* Check cases that eat one char */
344 else if (ExpressionChar
== '?' && !EndOfName
)
346 BackTracking
[BackTrackingPosition
++] = (ExpressionPosition
+ Offset
) * 2;
349 else if (ExpressionChar
== '*')
351 BackTracking
[BackTrackingPosition
++] = ExpressionPosition
* 2;
352 BackTracking
[BackTrackingPosition
++] = (ExpressionPosition
* 2) + 1;
356 else if (ExpressionChar
== ANSI_DOS_STAR
)
358 /* Look for last dot */
360 if (!EndOfName
&& NameChar
== '.')
362 for (Position
= NamePosition
; Position
< Name
->Length
; )
364 /* If lead byte not present */
365 if (!FsRtlIsLeadDbcsCharacter(Name
->Buffer
[Position
]))
367 if (Name
->Buffer
[Position
] == '.')
373 Position
+= sizeof(UCHAR
);
377 Position
+= sizeof(USHORT
);
382 if (EndOfName
|| NameChar
!= '.' || !DontSkipDot
)
383 BackTracking
[BackTrackingPosition
++] = ExpressionPosition
* 2;
385 BackTracking
[BackTrackingPosition
++] = (ExpressionPosition
* 2) + 1;
389 else if (ExpressionChar
== DOS_DOT
)
391 if (EndOfName
) continue;
394 BackTracking
[BackTrackingPosition
++] = (ExpressionPosition
+ Offset
) * 2;
397 else if (ExpressionChar
== ANSI_DOS_QM
)
399 if (EndOfName
|| NameChar
== '.') continue;
401 BackTracking
[BackTrackingPosition
++] = (ExpressionPosition
+ Offset
) * 2;
404 /* Leave from loop */
408 for (Position
= 0; MatchingChars
> OldBackTrackingPosition
&& Position
< BackTrackingPosition
; Position
++)
410 while (MatchingChars
> OldBackTrackingPosition
&&
411 BackTracking
[Position
] > OldBackTracking
[OldBackTrackingPosition
])
413 ++OldBackTrackingPosition
;
419 BackTrackingSwap
= BackTracking
;
420 BackTracking
= OldBackTracking
;
421 OldBackTracking
= BackTrackingSwap
;
424 /* Store result value */
425 Result
= (OldBackTracking
[MatchingChars
- 1] == Expression
->Length
* 2);
429 /* Frees the memory if necessary */
430 if (AllocatedBuffer
!= NULL
)
432 ExFreePoolWithTag(AllocatedBuffer
, 'nrSF');
439 * @name FsRtlIsFatDbcsLegal
442 * Returns TRUE if the given DbcsName is a valid FAT filename (in 8.3)
445 * The filename to check. It can also contains pathname.
447 * @param WildCardsPermissible
448 * If this is set to FALSE and if filename contains wildcard, the function
451 * @param PathNamePermissible
452 * If this is set to FALSE and if the filename comes with a pathname, the
455 * @param LeadingBackslashPermissible
456 * If this is set to FALSE and if the filename starts with a backslash, the
459 * @return TRUE if the DbcsName is legal, FALSE otherwise
466 FsRtlIsFatDbcsLegal(IN ANSI_STRING DbcsName
,
467 IN BOOLEAN WildCardsPermissible
,
468 IN BOOLEAN PathNamePermissible
,
469 IN BOOLEAN LeadingBackslashPermissible
)
471 ANSI_STRING FirstPart
, RemainingPart
;
476 /* Just quit if the string is empty */
477 if (!DbcsName
.Length
)
480 /* Accept special filename if wildcards are allowed */
481 if (WildCardsPermissible
&& (DbcsName
.Length
== 1 || DbcsName
.Length
== 2) && DbcsName
.Buffer
[0] == '.')
483 if (DbcsName
.Length
== 2)
485 if (DbcsName
.Buffer
[1] == '.')
494 /* DbcsName wasn't supposed to be started with \ */
495 if (!LeadingBackslashPermissible
&& DbcsName
.Buffer
[0] == '\\')
497 /* DbcsName was allowed to be started with \, but now, remove it */
498 else if (LeadingBackslashPermissible
&& DbcsName
.Buffer
[0] == '\\')
500 DbcsName
.Buffer
= DbcsName
.Buffer
+ 1;
501 DbcsName
.Length
= DbcsName
.Length
- 1;
502 DbcsName
.MaximumLength
= DbcsName
.MaximumLength
- 1;
505 if (PathNamePermissible
)
507 /* We copy the buffer for FsRtlDissectDbcs call */
508 RemainingPart
.Buffer
= DbcsName
.Buffer
;
509 RemainingPart
.Length
= DbcsName
.Length
;
510 RemainingPart
.MaximumLength
= DbcsName
.MaximumLength
;
512 while (RemainingPart
.Length
> 0)
514 if (RemainingPart
.Buffer
[0] == '\\')
517 /* Call once again our dissect function */
518 FsRtlDissectDbcs(RemainingPart
, &FirstPart
, &RemainingPart
);
520 if (!FsRtlIsFatDbcsLegal(FirstPart
,
521 WildCardsPermissible
,
532 if (WildCardsPermissible
&& FsRtlDoesDbcsContainWildCards(&DbcsName
))
534 for (i
= 0; i
< DbcsName
.Length
; i
++)
536 /* First make sure the character it's not the Lead DBCS */
537 if (FsRtlIsLeadDbcsCharacter(DbcsName
.Buffer
[i
]))
541 /* Then check for bad characters */
542 else if (!FsRtlIsAnsiCharacterLegalFat(DbcsName
.Buffer
[i
], TRUE
))
551 /* Filename must be 8.3 filename */
552 if (DbcsName
.Length
> 12)
555 /* Reset dots count */
558 for (i
= 0; i
< DbcsName
.Length
; i
++)
560 /* First make sure the character it's not the Lead DBCS */
561 if (FsRtlIsLeadDbcsCharacter(DbcsName
.Buffer
[i
]))
563 if (!LastDot
&& (i
>= 7))
566 if (i
== (DbcsName
.Length
- 1))
572 /* Then check for bad characters */
573 else if (!FsRtlIsAnsiCharacterLegalFat(DbcsName
.Buffer
[i
], WildCardsPermissible
))
577 else if (DbcsName
.Buffer
[i
] == '.')
579 /* Filename can only contain one dot */
585 /* We mustn't have spaces before dot or at the end of the filename
586 * and no dot at the beginning of the filename */
587 if (i
== (DbcsName
.Length
- 1) || i
== 0)
590 /* Filename must be 8.3 filename and not 3.8 filename */
591 if ((DbcsName
.Length
- 1) - i
> 3)
594 if ((i
> 0) && DbcsName
.Buffer
[i
- 1] == ' ')
597 /* Filename mustn't finish with a space */
598 else if (DbcsName
.Buffer
[i
] == ' ' && i
== (DbcsName
.Length
- 1))
603 if (!LastDot
&& (i
>= 8))
611 * @name FsRtlIsHpfsDbcsLegal
614 * Returns TRUE if the given DbcsName is a valid HPFS filename
617 * The filename to check. It can also contains pathname.
619 * @param WildCardsPermissible
620 * If this is set to FALSE and if filename contains wildcard, the function
623 * @param PathNamePermissible
624 * If this is set to FALSE and if the filename comes with a pathname, the
627 * @param LeadingBackslashPermissible
628 * If this is set to FALSE and if the filename starts with a backslash, the
631 * @return TRUE if the DbcsName is legal, FALSE otherwise
638 FsRtlIsHpfsDbcsLegal(IN ANSI_STRING DbcsName
,
639 IN BOOLEAN WildCardsPermissible
,
640 IN BOOLEAN PathNamePermissible
,
641 IN BOOLEAN LeadingBackslashPermissible
)
643 ANSI_STRING FirstPart
, RemainingPart
;
647 /* Just quit if the string is empty */
648 if (!DbcsName
.Length
)
651 /* Accept special filename if wildcards are allowed */
652 if (WildCardsPermissible
&& (DbcsName
.Length
== 1 || DbcsName
.Length
== 2) && DbcsName
.Buffer
[0] == '.')
654 if (DbcsName
.Length
== 2)
656 if (DbcsName
.Buffer
[1] == '.')
665 /* DbcsName wasn't supposed to be started with \ */
666 if (!LeadingBackslashPermissible
&& DbcsName
.Buffer
[0] == '\\')
668 /* DbcsName was allowed to be started with \, but now, remove it */
669 else if (LeadingBackslashPermissible
&& DbcsName
.Buffer
[0] == '\\')
671 DbcsName
.Buffer
= DbcsName
.Buffer
+ 1;
672 DbcsName
.Length
= DbcsName
.Length
- 1;
673 DbcsName
.MaximumLength
= DbcsName
.MaximumLength
- 1;
676 if (PathNamePermissible
)
678 /* We copy the buffer for FsRtlDissectDbcs call */
679 RemainingPart
.Buffer
= DbcsName
.Buffer
;
680 RemainingPart
.Length
= DbcsName
.Length
;
681 RemainingPart
.MaximumLength
= DbcsName
.MaximumLength
;
683 while (RemainingPart
.Length
> 0)
685 if (RemainingPart
.Buffer
[0] == '\\')
688 /* Call once again our dissect function */
689 FsRtlDissectDbcs(RemainingPart
, &FirstPart
, &RemainingPart
);
691 if (!FsRtlIsHpfsDbcsLegal(FirstPart
,
692 WildCardsPermissible
,
703 if (DbcsName
.Length
> 255)
706 for (i
= 0; i
< DbcsName
.Length
; i
++)
708 /* First make sure the character it's not the Lead DBCS */
709 if (FsRtlIsLeadDbcsCharacter(DbcsName
.Buffer
[i
]))
711 if (i
== (DbcsName
.Length
- 1))
715 /* Then check for bad characters */
716 else if (!FsRtlIsAnsiCharacterLegalHpfs(DbcsName
.Buffer
[i
], WildCardsPermissible
))
720 /* Filename mustn't finish with a space or a dot */
721 else if ((DbcsName
.Buffer
[i
] == ' ' || DbcsName
.Buffer
[i
] == '.') && i
== (DbcsName
.Length
- 1))