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
;
32 /* Check if we were given strings at all */
33 if (!Name
->Length
|| !Expression
->Length
)
35 /* Return TRUE if both strings are empty, otherwise FALSE */
36 if (Name
->Length
== 0 && Expression
->Length
== 0)
42 /* Check for a shortcut: just one wildcard */
43 if (Expression
->Length
== sizeof(WCHAR
))
45 if (Expression
->Buffer
[0] == L
'*')
49 ASSERT(!IgnoreCase
|| UpcaseTable
);
51 /* Another shortcut, wildcard followed by some string */
52 if (Expression
->Buffer
[0] == L
'*')
54 /* Copy Expression to our local variable */
55 IntExpression
= *Expression
;
57 /* Skip the first char */
58 IntExpression
.Buffer
++;
59 IntExpression
.Length
-= sizeof(WCHAR
);
61 /* Continue only if the rest of the expression does NOT contain
63 if (!FsRtlDoesNameContainWildCards(&IntExpression
))
65 /* Check for a degenerate case */
66 if (Name
->Length
< (Expression
->Length
- sizeof(WCHAR
)))
69 /* Calculate position */
70 NamePosition
= (Name
->Length
- IntExpression
.Length
) / sizeof(WCHAR
);
75 /* We can just do a byte compare */
76 return RtlEqualMemory(IntExpression
.Buffer
,
77 Name
->Buffer
+ NamePosition
,
78 IntExpression
.Length
);
82 /* Not so easy, need to upcase and check char by char */
83 for (ExpressionPosition
= 0; ExpressionPosition
< (IntExpression
.Length
/ sizeof(WCHAR
)); ExpressionPosition
++)
85 /* Assert that expression is already upcased! */
86 ASSERT(IntExpression
.Buffer
[ExpressionPosition
] == UpcaseTable
[IntExpression
.Buffer
[ExpressionPosition
]]);
88 /* Now compare upcased name char with expression */
89 if (UpcaseTable
[Name
->Buffer
[NamePosition
+ ExpressionPosition
]] !=
90 IntExpression
.Buffer
[ExpressionPosition
])
102 while (NamePosition
< Name
->Length
/ sizeof(WCHAR
) && ExpressionPosition
< Expression
->Length
/ sizeof(WCHAR
))
104 /* Basic check to test if chars are equal */
105 if ((Expression
->Buffer
[ExpressionPosition
] == (IgnoreCase
? UpcaseTable
[Name
->Buffer
[NamePosition
]] : Name
->Buffer
[NamePosition
])))
108 ExpressionPosition
++;
110 /* Check cases that eat one char */
111 else if (Expression
->Buffer
[ExpressionPosition
] == L
'?' || (Expression
->Buffer
[ExpressionPosition
] == DOS_QM
) ||
112 (Expression
->Buffer
[ExpressionPosition
] == DOS_DOT
&& Name
->Buffer
[NamePosition
] == L
'.'))
115 ExpressionPosition
++;
118 else if (Expression
->Buffer
[ExpressionPosition
] == L
'*')
120 /* Save star position */
123 BackTracking
= ExAllocatePoolWithTag(PagedPool
| POOL_RAISE_IF_ALLOCATION_FAILURE
,
124 (Expression
->Length
/ sizeof(WCHAR
)) * sizeof(USHORT
),
127 BackTracking
[++StarFound
] = ExpressionPosition
++;
129 /* If star is at the end, then eat all rest and leave */
130 if (ExpressionPosition
== Expression
->Length
/ sizeof(WCHAR
))
132 NamePosition
= Name
->Length
/ sizeof(WCHAR
);
135 else if (Expression
->Buffer
[ExpressionPosition
] != L
'?')
141 else if (Expression
->Buffer
[ExpressionPosition
] == DOS_STAR
)
143 MatchingChars
= NamePosition
;
144 while (MatchingChars
< Name
->Length
/ sizeof(WCHAR
))
146 if (Name
->Buffer
[MatchingChars
] == L
'.')
148 NamePosition
= MatchingChars
;
152 ExpressionPosition
++;
154 /* If nothing match, try to backtrack */
155 else if (StarFound
>= 0)
157 ExpressionPosition
= BackTracking
[StarFound
--];
159 /* Otherwise, fail */
165 /* Under certain circumstances, expression is over, but name isn't
166 * and we can backtrack, then, backtrack */
167 if (ExpressionPosition
== Expression
->Length
/ sizeof(WCHAR
) &&
168 NamePosition
!= Name
->Length
/ sizeof(WCHAR
) &&
171 ExpressionPosition
= BackTracking
[StarFound
--];
174 if (ExpressionPosition
+ 1 == Expression
->Length
/ sizeof(WCHAR
) && NamePosition
== Name
->Length
/ sizeof(WCHAR
) &&
175 Expression
->Buffer
[ExpressionPosition
] == DOS_DOT
)
177 ExpressionPosition
++;
182 ExFreePoolWithTag(BackTracking
, 'nrSF');
185 return (ExpressionPosition
== Expression
->Length
/ sizeof(WCHAR
) && NamePosition
== Name
->Length
/ sizeof(WCHAR
));
188 /* PUBLIC FUNCTIONS **********************************************************/
191 * @name FsRtlAreNamesEqual
194 * Compare two strings to check if they match
197 * First unicode string to compare
200 * Second unicode string to compare
203 * If TRUE, Case will be ignored when comparing strings
206 * Table for upcase letters. If NULL is given, system one will be used
208 * @return TRUE if the strings are equal
210 * @remarks From Bo Branten's ntifs.h v25.
215 FsRtlAreNamesEqual(IN PCUNICODE_STRING Name1
,
216 IN PCUNICODE_STRING Name2
,
217 IN BOOLEAN IgnoreCase
,
218 IN PCWCH UpcaseTable OPTIONAL
)
220 UNICODE_STRING UpcaseName1
;
221 UNICODE_STRING UpcaseName2
;
222 BOOLEAN StringsAreEqual
, MemoryAllocated
= FALSE
;
227 /* Well, first check their size */
228 if (Name1
->Length
!= Name2
->Length
) return FALSE
;
230 /* Check if the caller didn't give an upcase table */
231 if ((IgnoreCase
) && !(UpcaseTable
))
233 /* Upcase the string ourselves */
234 Status
= RtlUpcaseUnicodeString(&UpcaseName1
, Name1
, TRUE
);
235 if (!NT_SUCCESS(Status
)) RtlRaiseStatus(Status
);
237 /* Upcase the second string too */
238 RtlUpcaseUnicodeString(&UpcaseName2
, Name2
, TRUE
);
239 Name1
= &UpcaseName1
;
240 Name2
= &UpcaseName2
;
242 /* Make sure we go through the path below, but free the strings */
244 MemoryAllocated
= TRUE
;
247 /* Do a case-sensitive search */
250 /* Use a raw memory compare */
251 StringsAreEqual
= RtlEqualMemory(Name1
->Buffer
,
255 /* Check if we allocated strings */
259 RtlFreeUnicodeString(&UpcaseName1
);
260 RtlFreeUnicodeString(&UpcaseName2
);
263 /* Return the equality */
264 return StringsAreEqual
;
268 /* Case in-sensitive search */
269 for (i
= 0; i
< Name1
->Length
/ sizeof(WCHAR
); i
++)
271 /* Check if the character matches */
272 if (UpcaseTable
[Name1
->Buffer
[i
]] != UpcaseTable
[Name2
->Buffer
[i
]])
274 /* Non-match found! */
279 /* We finished the loop so we are equal */
285 * @name FsRtlDissectName
288 * Dissects a given path name into first and remaining part.
291 * Unicode string to dissect.
294 * Pointer to user supplied UNICODE_STRING, that will later point
295 * to the first part of the original name.
297 * @param RemainingPart
298 * Pointer to user supplied UNICODE_STRING, that will later point
299 * to the remaining part of the original name.
304 * Name: \test1\test2\test3
306 * RemainingPart: test2\test3
311 FsRtlDissectName(IN UNICODE_STRING Name
,
312 OUT PUNICODE_STRING FirstPart
,
313 OUT PUNICODE_STRING RemainingPart
)
315 USHORT FirstPosition
, i
;
316 USHORT SkipFirstSlash
= 0;
319 /* Zero the strings before continuing */
320 RtlZeroMemory(FirstPart
, sizeof(UNICODE_STRING
));
321 RtlZeroMemory(RemainingPart
, sizeof(UNICODE_STRING
));
323 /* Just quit if the string is empty */
324 if (!Name
.Length
) return;
326 /* Find first backslash */
327 FirstPosition
= Name
.Length
/ sizeof(WCHAR
) ;
328 for (i
= 0; i
< Name
.Length
/ sizeof(WCHAR
); i
++)
330 /* If we found one... */
331 if (Name
.Buffer
[i
] == L
'\\')
333 /* If it begins string, just notice it and continue */
340 /* Else, save its position and break out of the loop */
347 /* Set up the first result string */
348 FirstPart
->Buffer
= Name
.Buffer
+ SkipFirstSlash
;
349 FirstPart
->Length
= (FirstPosition
- SkipFirstSlash
) * sizeof(WCHAR
);
350 FirstPart
->MaximumLength
= FirstPart
->Length
;
352 /* And second one, if necessary */
353 if (FirstPosition
< (Name
.Length
/ sizeof(WCHAR
)))
355 RemainingPart
->Buffer
= Name
.Buffer
+ FirstPosition
+ 1;
356 RemainingPart
->Length
= Name
.Length
- (FirstPosition
+ 1) * sizeof(WCHAR
);
357 RemainingPart
->MaximumLength
= RemainingPart
->Length
;
362 * @name FsRtlDoesNameContainWildCards
365 * Checks if the given string contains WildCards
368 * Pointer to a UNICODE_STRING containing Name to examine
370 * @return TRUE if Name contains wildcards, FALSE otherwise
372 * @remarks From Bo Branten's ntifs.h v12.
377 FsRtlDoesNameContainWildCards(IN PUNICODE_STRING Name
)
382 /* Loop through every character */
385 Ptr
= Name
->Buffer
+ (Name
->Length
/ sizeof(WCHAR
)) - 1;
386 while ((Ptr
>= Name
->Buffer
) && (*Ptr
!= L
'\\'))
388 /* Check for Wildcard */
389 if (FsRtlIsUnicodeCharacterWild(*Ptr
)) return TRUE
;
399 * @name FsRtlIsNameInExpression
402 * Check if the Name string is in the Expression string.
405 * The string in which we've to find Name. It can contain wildcards.
406 * If IgnoreCase is set to TRUE, this string MUST BE uppercase.
409 * The string to find. It cannot contain wildcards
412 * If set to TRUE, case will be ignore with upcasing both strings
415 * If not NULL, and if IgnoreCase is set to TRUE, it will be used to
416 * upcase the both strings
418 * @return TRUE if Name is in Expression, FALSE otherwise
420 * @remarks From Bo Branten's ntifs.h v12. This function should be
421 * rewritten to avoid recursion and better wildcard handling
422 * should be implemented (see FsRtlDoesNameContainWildCards).
427 FsRtlIsNameInExpression(IN PUNICODE_STRING Expression
,
428 IN PUNICODE_STRING Name
,
429 IN BOOLEAN IgnoreCase
,
430 IN PWCHAR UpcaseTable OPTIONAL
)
434 UNICODE_STRING IntName
;
436 if (IgnoreCase
&& !UpcaseTable
)
438 Status
= RtlUpcaseUnicodeString(&IntName
, Name
, TRUE
);
439 if (!NT_SUCCESS(Status
))
441 ExRaiseStatus(Status
);
448 IntName
.Buffer
= NULL
;
451 Result
= FsRtlIsNameInExpressionPrivate(Expression
, Name
, IgnoreCase
, UpcaseTable
);
453 if (IntName
.Buffer
!= NULL
)
455 RtlFreeUnicodeString(&IntName
);