2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/fsrtl/name.c
5 * PURPOSE: Provides name parsing and other support routines for FSDs
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Filip Navara (navaraf@reactos.org)
8 * Pierre Schweitzer (pierre.schweitzer@reactos.org)
9 * Aleksey Bragin (aleksey@reactos.org)
12 /* INCLUDES ******************************************************************/
18 /* PRIVATE FUNCTIONS *********************************************************/
21 FsRtlIsNameInExpressionPrivate(IN PUNICODE_STRING Expression
,
22 IN PUNICODE_STRING Name
,
23 IN BOOLEAN IgnoreCase
,
24 IN PWCHAR UpcaseTable OPTIONAL
)
27 PUSHORT BackTracking
= NULL
;
28 UNICODE_STRING IntExpression
;
29 USHORT ExpressionPosition
= 0, NamePosition
= 0, MatchingChars
;
34 /* Check if we were given strings at all */
35 if (!Name
->Length
|| !Expression
->Length
)
37 /* Return TRUE if both strings are empty, otherwise FALSE */
38 if (Name
->Length
== 0 && Expression
->Length
== 0)
44 /* Check for a shortcut: just one wildcard */
45 if (Expression
->Length
== sizeof(WCHAR
))
47 if (Expression
->Buffer
[0] == L
'*')
51 ASSERT(!IgnoreCase
|| UpcaseTable
);
53 /* Another shortcut, wildcard followed by some string */
54 if (Expression
->Buffer
[0] == L
'*')
56 /* Copy Expression to our local variable */
57 IntExpression
= *Expression
;
59 /* Skip the first char */
60 IntExpression
.Buffer
++;
61 IntExpression
.Length
-= sizeof(WCHAR
);
63 /* Continue only if the rest of the expression does NOT contain
65 if (!FsRtlDoesNameContainWildCards(&IntExpression
))
67 /* Check for a degenerate case */
68 if (Name
->Length
< (Expression
->Length
- sizeof(WCHAR
)))
71 /* Calculate position */
72 NamePosition
= (Name
->Length
- IntExpression
.Length
) / sizeof(WCHAR
);
77 /* We can just do a byte compare */
78 return RtlEqualMemory(IntExpression
.Buffer
,
79 Name
->Buffer
+ NamePosition
,
80 IntExpression
.Length
);
84 /* Not so easy, need to upcase and check char by char */
85 for (ExpressionPosition
= 0; ExpressionPosition
< (IntExpression
.Length
/ sizeof(WCHAR
)); ExpressionPosition
++)
87 /* Assert that expression is already upcased! */
88 ASSERT(IntExpression
.Buffer
[ExpressionPosition
] == UpcaseTable
[IntExpression
.Buffer
[ExpressionPosition
]]);
90 /* Now compare upcased name char with expression */
91 if (UpcaseTable
[Name
->Buffer
[NamePosition
+ ExpressionPosition
]] !=
92 IntExpression
.Buffer
[ExpressionPosition
])
104 while ((NamePosition
< Name
->Length
/ sizeof(WCHAR
)) &&
105 (ExpressionPosition
< Expression
->Length
/ sizeof(WCHAR
)))
107 /* Basic check to test if chars are equal */
108 CompareChar
= IgnoreCase
? UpcaseTable
[Name
->Buffer
[NamePosition
]] :
109 Name
->Buffer
[NamePosition
];
110 if (Expression
->Buffer
[ExpressionPosition
] == CompareChar
)
113 ExpressionPosition
++;
115 /* Check cases that eat one char */
116 else if (Expression
->Buffer
[ExpressionPosition
] == L
'?' || (Expression
->Buffer
[ExpressionPosition
] == DOS_QM
))
119 ExpressionPosition
++;
122 else if (Expression
->Buffer
[ExpressionPosition
] == L
'*')
124 /* Skip contigous stars */
125 while ((ExpressionPosition
+ 1 < (USHORT
)(Expression
->Length
/ sizeof(WCHAR
))) &&
126 (Expression
->Buffer
[ExpressionPosition
+ 1] == L
'*'))
128 ExpressionPosition
++;
131 /* Save star position */
134 BackTracking
= ExAllocatePoolWithTag(PagedPool
| POOL_RAISE_IF_ALLOCATION_FAILURE
,
135 (Expression
->Length
/ sizeof(WCHAR
)) * sizeof(USHORT
),
138 BackTracking
[++StarFound
] = ExpressionPosition
++;
140 /* If star is at the end, then eat all rest and leave */
141 if (ExpressionPosition
== Expression
->Length
/ sizeof(WCHAR
))
143 NamePosition
= Name
->Length
/ sizeof(WCHAR
);
146 /* Allow null matching */
147 else if (Expression
->Buffer
[ExpressionPosition
] != L
'?' &&
148 Expression
->Buffer
[ExpressionPosition
] != Name
->Buffer
[NamePosition
])
154 else if (Expression
->Buffer
[ExpressionPosition
] == DOS_STAR
)
156 /* We can only consume dot if that's not the last one
157 * Otherwise, we null match
159 if (Name
->Buffer
[NamePosition
] == L
'.')
161 MatchingChars
= NamePosition
+ 1;
162 while (MatchingChars
< Name
->Length
/ sizeof(WCHAR
))
164 if (Name
->Buffer
[MatchingChars
] == L
'.')
174 /* XXX: Eat everything till the end */
175 if (ExpressionPosition
+ 1 == Expression
->Length
/ sizeof(WCHAR
))
177 NamePosition
= Name
->Length
;
180 /* Try to eat till the next matching char or . */
181 MatchingChars
= NamePosition
;
182 while (MatchingChars
< Name
->Length
/ sizeof(WCHAR
))
184 if (ExpressionPosition
+ 1 < Expression
->Length
/ sizeof(WCHAR
) &&
185 Name
->Buffer
[MatchingChars
] == Expression
->Buffer
[ExpressionPosition
+ 1])
187 NamePosition
= MatchingChars
;
190 else if (Name
->Buffer
[MatchingChars
] == L
'.')
192 NamePosition
= MatchingChars
+ 1;
198 ExpressionPosition
++;
201 else if (Expression
->Buffer
[ExpressionPosition
] == DOS_DOT
)
203 /* First try to find whether we are beyond last dot (beyond name) */
205 MatchingChars
= NamePosition
+ 1;
206 while (MatchingChars
< Name
->Length
/ sizeof(WCHAR
))
208 if (Name
->Buffer
[MatchingChars
] == L
'.')
216 /* If we are beyond name, we null match */
219 if (Name
->Buffer
[NamePosition
] == L
'.')
223 ExpressionPosition
++;
226 /* If not, we only match a dot */
227 else if (Name
->Buffer
[NamePosition
] == L
'.')
230 ExpressionPosition
++;
233 /* Otherwise, fail */
239 /* If nothing match, try to backtrack */
240 else if (StarFound
>= 0)
242 ExpressionPosition
= BackTracking
[StarFound
--];
244 /* Otherwise, fail */
250 /* Under certain circumstances, expression is over, but name isn't
251 * and we can backtrack, then, backtrack */
252 if (ExpressionPosition
== Expression
->Length
/ sizeof(WCHAR
) &&
253 NamePosition
!= Name
->Length
/ sizeof(WCHAR
) &&
256 ExpressionPosition
= BackTracking
[StarFound
--];
259 /* If we have nullable matching wc at the end of the string, eat them */
260 if (ExpressionPosition
!= Expression
->Length
/ sizeof(WCHAR
) && NamePosition
== Name
->Length
/ sizeof(WCHAR
))
262 while (ExpressionPosition
< Expression
->Length
/ sizeof(WCHAR
))
264 if (Expression
->Buffer
[ExpressionPosition
] != DOS_DOT
&&
265 Expression
->Buffer
[ExpressionPosition
] != L
'*' &&
266 Expression
->Buffer
[ExpressionPosition
] != DOS_STAR
)
270 ExpressionPosition
++;
276 ExFreePoolWithTag(BackTracking
, 'nrSF');
279 return (ExpressionPosition
== Expression
->Length
/ sizeof(WCHAR
) && NamePosition
== Name
->Length
/ sizeof(WCHAR
));
282 /* PUBLIC FUNCTIONS **********************************************************/
285 * @name FsRtlAreNamesEqual
288 * Compare two strings to check if they match
291 * First unicode string to compare
294 * Second unicode string to compare
297 * If TRUE, Case will be ignored when comparing strings
300 * Table for upcase letters. If NULL is given, system one will be used
302 * @return TRUE if the strings are equal
304 * @remarks From Bo Branten's ntifs.h v25.
309 FsRtlAreNamesEqual(IN PCUNICODE_STRING Name1
,
310 IN PCUNICODE_STRING Name2
,
311 IN BOOLEAN IgnoreCase
,
312 IN PCWCH UpcaseTable OPTIONAL
)
314 UNICODE_STRING UpcaseName1
;
315 UNICODE_STRING UpcaseName2
;
316 BOOLEAN StringsAreEqual
, MemoryAllocated
= FALSE
;
321 /* Well, first check their size */
322 if (Name1
->Length
!= Name2
->Length
) return FALSE
;
324 /* Check if the caller didn't give an upcase table */
325 if ((IgnoreCase
) && !(UpcaseTable
))
327 /* Upcase the string ourselves */
328 Status
= RtlUpcaseUnicodeString(&UpcaseName1
, Name1
, TRUE
);
329 if (!NT_SUCCESS(Status
)) RtlRaiseStatus(Status
);
331 /* Upcase the second string too */
332 RtlUpcaseUnicodeString(&UpcaseName2
, Name2
, TRUE
);
333 Name1
= &UpcaseName1
;
334 Name2
= &UpcaseName2
;
336 /* Make sure we go through the path below, but free the strings */
338 MemoryAllocated
= TRUE
;
341 /* Do a case-sensitive search */
344 /* Use a raw memory compare */
345 StringsAreEqual
= RtlEqualMemory(Name1
->Buffer
,
349 /* Check if we allocated strings */
353 RtlFreeUnicodeString(&UpcaseName1
);
354 RtlFreeUnicodeString(&UpcaseName2
);
357 /* Return the equality */
358 return StringsAreEqual
;
362 /* Case in-sensitive search */
363 for (i
= 0; i
< Name1
->Length
/ sizeof(WCHAR
); i
++)
365 /* Check if the character matches */
366 if (UpcaseTable
[Name1
->Buffer
[i
]] != UpcaseTable
[Name2
->Buffer
[i
]])
368 /* Non-match found! */
373 /* We finished the loop so we are equal */
379 * @name FsRtlDissectName
382 * Dissects a given path name into first and remaining part.
385 * Unicode string to dissect.
388 * Pointer to user supplied UNICODE_STRING, that will later point
389 * to the first part of the original name.
391 * @param RemainingPart
392 * Pointer to user supplied UNICODE_STRING, that will later point
393 * to the remaining part of the original name.
398 * Name: \test1\test2\test3
400 * RemainingPart: test2\test3
405 FsRtlDissectName(IN UNICODE_STRING Name
,
406 OUT PUNICODE_STRING FirstPart
,
407 OUT PUNICODE_STRING RemainingPart
)
409 USHORT FirstPosition
, i
;
410 USHORT SkipFirstSlash
= 0;
413 /* Zero the strings before continuing */
414 RtlZeroMemory(FirstPart
, sizeof(UNICODE_STRING
));
415 RtlZeroMemory(RemainingPart
, sizeof(UNICODE_STRING
));
417 /* Just quit if the string is empty */
418 if (!Name
.Length
) return;
420 /* Find first backslash */
421 FirstPosition
= Name
.Length
/ sizeof(WCHAR
) ;
422 for (i
= 0; i
< Name
.Length
/ sizeof(WCHAR
); i
++)
424 /* If we found one... */
425 if (Name
.Buffer
[i
] == L
'\\')
427 /* If it begins string, just notice it and continue */
434 /* Else, save its position and break out of the loop */
441 /* Set up the first result string */
442 FirstPart
->Buffer
= Name
.Buffer
+ SkipFirstSlash
;
443 FirstPart
->Length
= (FirstPosition
- SkipFirstSlash
) * sizeof(WCHAR
);
444 FirstPart
->MaximumLength
= FirstPart
->Length
;
446 /* And second one, if necessary */
447 if (FirstPosition
< (Name
.Length
/ sizeof(WCHAR
)))
449 RemainingPart
->Buffer
= Name
.Buffer
+ FirstPosition
+ 1;
450 RemainingPart
->Length
= Name
.Length
- (FirstPosition
+ 1) * sizeof(WCHAR
);
451 RemainingPart
->MaximumLength
= RemainingPart
->Length
;
456 * @name FsRtlDoesNameContainWildCards
459 * Checks if the given string contains WildCards
462 * Pointer to a UNICODE_STRING containing Name to examine
464 * @return TRUE if Name contains wildcards, FALSE otherwise
466 * @remarks From Bo Branten's ntifs.h v12.
471 FsRtlDoesNameContainWildCards(IN PUNICODE_STRING Name
)
476 /* Loop through every character */
479 Ptr
= Name
->Buffer
+ (Name
->Length
/ sizeof(WCHAR
)) - 1;
480 while ((Ptr
>= Name
->Buffer
) && (*Ptr
!= L
'\\'))
482 /* Check for Wildcard */
483 if (FsRtlIsUnicodeCharacterWild(*Ptr
)) return TRUE
;
493 * @name FsRtlIsNameInExpression
496 * Check if the Name string is in the Expression string.
499 * The string in which we've to find Name. It can contain wildcards.
500 * If IgnoreCase is set to TRUE, this string MUST BE uppercase.
503 * The string to find. It cannot contain wildcards
506 * If set to TRUE, case will be ignore with upcasing both strings
509 * If not NULL, and if IgnoreCase is set to TRUE, it will be used to
510 * upcase the both strings
512 * @return TRUE if Name is in Expression, FALSE otherwise
514 * @remarks From Bo Branten's ntifs.h v12. This function should be
515 * rewritten to avoid recursion and better wildcard handling
516 * should be implemented (see FsRtlDoesNameContainWildCards).
521 FsRtlIsNameInExpression(IN PUNICODE_STRING Expression
,
522 IN PUNICODE_STRING Name
,
523 IN BOOLEAN IgnoreCase
,
524 IN PWCHAR UpcaseTable OPTIONAL
)
528 UNICODE_STRING IntName
;
530 if (IgnoreCase
&& !UpcaseTable
)
532 Status
= RtlUpcaseUnicodeString(&IntName
, Name
, TRUE
);
533 if (!NT_SUCCESS(Status
))
535 ExRaiseStatus(Status
);
542 IntName
.Buffer
= NULL
;
545 Result
= FsRtlIsNameInExpressionPrivate(Expression
, Name
, IgnoreCase
, UpcaseTable
);
547 if (IntName
.Buffer
!= NULL
)
549 RtlFreeUnicodeString(&IntName
);