[CLT2012]
[reactos.git] / ntoskrnl / fsrtl / name.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 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)
10 */
11
12 /* INCLUDES ******************************************************************/
13
14 #include <ntoskrnl.h>
15 #define NDEBUG
16 #include <debug.h>
17
18 /* PRIVATE FUNCTIONS *********************************************************/
19 BOOLEAN
20 NTAPI
21 FsRtlIsNameInExpressionPrivate(IN PUNICODE_STRING Expression,
22 IN PUNICODE_STRING Name,
23 IN BOOLEAN IgnoreCase,
24 IN PWCHAR UpcaseTable OPTIONAL)
25 {
26 SHORT StarFound = -1;
27 PUSHORT BackTracking = NULL;
28 UNICODE_STRING IntExpression;
29 USHORT ExpressionPosition = 0, NamePosition = 0, MatchingChars;
30 PAGED_CODE();
31
32 /* Check if we were given strings at all */
33 if (!Name->Length || !Expression->Length)
34 {
35 /* Return TRUE if both strings are empty, otherwise FALSE */
36 if (Name->Length == 0 && Expression->Length == 0)
37 return TRUE;
38 else
39 return FALSE;
40 }
41
42 /* Check for a shortcut: just one wildcard */
43 if (Expression->Length == sizeof(WCHAR))
44 {
45 if (Expression->Buffer[0] == L'*')
46 return TRUE;
47 }
48
49 ASSERT(!IgnoreCase || UpcaseTable);
50
51 /* Another shortcut, wildcard followed by some string */
52 if (Expression->Buffer[0] == L'*')
53 {
54 /* Copy Expression to our local variable */
55 IntExpression = *Expression;
56
57 /* Skip the first char */
58 IntExpression.Buffer++;
59 IntExpression.Length -= sizeof(WCHAR);
60
61 /* Continue only if the rest of the expression does NOT contain
62 any more wildcards */
63 if (!FsRtlDoesNameContainWildCards(&IntExpression))
64 {
65 /* Check for a degenerate case */
66 if (Name->Length < (Expression->Length - sizeof(WCHAR)))
67 return FALSE;
68
69 /* Calculate position */
70 NamePosition = (Name->Length - IntExpression.Length) / sizeof(WCHAR);
71
72 /* Compare */
73 if (!IgnoreCase)
74 {
75 /* We can just do a byte compare */
76 return RtlEqualMemory(IntExpression.Buffer,
77 Name->Buffer + NamePosition,
78 IntExpression.Length);
79 }
80 else
81 {
82 /* Not so easy, need to upcase and check char by char */
83 for (ExpressionPosition = 0; ExpressionPosition < (IntExpression.Length / sizeof(WCHAR)); ExpressionPosition++)
84 {
85 /* Assert that expression is already upcased! */
86 ASSERT(IntExpression.Buffer[ExpressionPosition] == UpcaseTable[IntExpression.Buffer[ExpressionPosition]]);
87
88 /* Now compare upcased name char with expression */
89 if (UpcaseTable[Name->Buffer[NamePosition + ExpressionPosition]] !=
90 IntExpression.Buffer[ExpressionPosition])
91 {
92 return FALSE;
93 }
94 }
95
96 /* It matches */
97 return TRUE;
98 }
99 }
100 }
101
102 while (NamePosition < Name->Length / sizeof(WCHAR) && ExpressionPosition < Expression->Length / sizeof(WCHAR))
103 {
104 /* Basic check to test if chars are equal */
105 if ((Expression->Buffer[ExpressionPosition] == (IgnoreCase ? UpcaseTable[Name->Buffer[NamePosition]] : Name->Buffer[NamePosition])))
106 {
107 NamePosition++;
108 ExpressionPosition++;
109 }
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'.'))
113 {
114 NamePosition++;
115 ExpressionPosition++;
116 }
117 /* Test star */
118 else if (Expression->Buffer[ExpressionPosition] == L'*')
119 {
120 /* Save star position */
121 if (!BackTracking)
122 {
123 BackTracking = ExAllocatePoolWithTag(PagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE,
124 (Expression->Length / sizeof(WCHAR)) * sizeof(USHORT),
125 'nrSF');
126 }
127 BackTracking[++StarFound] = ExpressionPosition++;
128
129 /* If star is at the end, then eat all rest and leave */
130 if (ExpressionPosition == Expression->Length / sizeof(WCHAR))
131 {
132 NamePosition = Name->Length / sizeof(WCHAR);
133 break;
134 }
135 else if (Expression->Buffer[ExpressionPosition] != L'?')
136 {
137 NamePosition++;
138 }
139 }
140 /* Check DOS_STAR */
141 else if (Expression->Buffer[ExpressionPosition] == DOS_STAR)
142 {
143 MatchingChars = NamePosition;
144 while (MatchingChars < Name->Length / sizeof(WCHAR))
145 {
146 if (Name->Buffer[MatchingChars] == L'.')
147 {
148 NamePosition = MatchingChars;
149 }
150 MatchingChars++;
151 }
152 ExpressionPosition++;
153 }
154 /* If nothing match, try to backtrack */
155 else if (StarFound >= 0)
156 {
157 ExpressionPosition = BackTracking[StarFound--];
158 }
159 /* Otherwise, fail */
160 else
161 {
162 break;
163 }
164
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) &&
169 StarFound >= 0)
170 {
171 ExpressionPosition = BackTracking[StarFound--];
172 }
173 }
174 if (ExpressionPosition + 1 == Expression->Length / sizeof(WCHAR) && NamePosition == Name->Length / sizeof(WCHAR) &&
175 Expression->Buffer[ExpressionPosition] == DOS_DOT)
176 {
177 ExpressionPosition++;
178 }
179
180 if (BackTracking)
181 {
182 ExFreePoolWithTag(BackTracking, 'nrSF');
183 }
184
185 return (ExpressionPosition == Expression->Length / sizeof(WCHAR) && NamePosition == Name->Length / sizeof(WCHAR));
186 }
187
188 /* PUBLIC FUNCTIONS **********************************************************/
189
190 /*++
191 * @name FsRtlAreNamesEqual
192 * @implemented
193 *
194 * Compare two strings to check if they match
195 *
196 * @param Name1
197 * First unicode string to compare
198 *
199 * @param Name2
200 * Second unicode string to compare
201 *
202 * @param IgnoreCase
203 * If TRUE, Case will be ignored when comparing strings
204 *
205 * @param UpcaseTable
206 * Table for upcase letters. If NULL is given, system one will be used
207 *
208 * @return TRUE if the strings are equal
209 *
210 * @remarks From Bo Branten's ntifs.h v25.
211 *
212 *--*/
213 BOOLEAN
214 NTAPI
215 FsRtlAreNamesEqual(IN PCUNICODE_STRING Name1,
216 IN PCUNICODE_STRING Name2,
217 IN BOOLEAN IgnoreCase,
218 IN PCWCH UpcaseTable OPTIONAL)
219 {
220 UNICODE_STRING UpcaseName1;
221 UNICODE_STRING UpcaseName2;
222 BOOLEAN StringsAreEqual, MemoryAllocated = FALSE;
223 USHORT i;
224 NTSTATUS Status;
225 PAGED_CODE();
226
227 /* Well, first check their size */
228 if (Name1->Length != Name2->Length) return FALSE;
229
230 /* Check if the caller didn't give an upcase table */
231 if ((IgnoreCase) && !(UpcaseTable))
232 {
233 /* Upcase the string ourselves */
234 Status = RtlUpcaseUnicodeString(&UpcaseName1, Name1, TRUE);
235 if (!NT_SUCCESS(Status)) RtlRaiseStatus(Status);
236
237 /* Upcase the second string too */
238 RtlUpcaseUnicodeString(&UpcaseName2, Name2, TRUE);
239 Name1 = &UpcaseName1;
240 Name2 = &UpcaseName2;
241
242 /* Make sure we go through the path below, but free the strings */
243 IgnoreCase = FALSE;
244 MemoryAllocated = TRUE;
245 }
246
247 /* Do a case-sensitive search */
248 if (!IgnoreCase)
249 {
250 /* Use a raw memory compare */
251 StringsAreEqual = RtlEqualMemory(Name1->Buffer,
252 Name2->Buffer,
253 Name1->Length);
254
255 /* Check if we allocated strings */
256 if (MemoryAllocated)
257 {
258 /* Free them */
259 RtlFreeUnicodeString(&UpcaseName1);
260 RtlFreeUnicodeString(&UpcaseName2);
261 }
262
263 /* Return the equality */
264 return StringsAreEqual;
265 }
266 else
267 {
268 /* Case in-sensitive search */
269 for (i = 0; i < Name1->Length / sizeof(WCHAR); i++)
270 {
271 /* Check if the character matches */
272 if (UpcaseTable[Name1->Buffer[i]] != UpcaseTable[Name2->Buffer[i]])
273 {
274 /* Non-match found! */
275 return FALSE;
276 }
277 }
278
279 /* We finished the loop so we are equal */
280 return TRUE;
281 }
282 }
283
284 /*++
285 * @name FsRtlDissectName
286 * @implemented
287 *
288 * Dissects a given path name into first and remaining part.
289 *
290 * @param Name
291 * Unicode string to dissect.
292 *
293 * @param FirstPart
294 * Pointer to user supplied UNICODE_STRING, that will later point
295 * to the first part of the original name.
296 *
297 * @param RemainingPart
298 * Pointer to user supplied UNICODE_STRING, that will later point
299 * to the remaining part of the original name.
300 *
301 * @return None
302 *
303 * @remarks Example:
304 * Name: \test1\test2\test3
305 * FirstPart: test1
306 * RemainingPart: test2\test3
307 *
308 *--*/
309 VOID
310 NTAPI
311 FsRtlDissectName(IN UNICODE_STRING Name,
312 OUT PUNICODE_STRING FirstPart,
313 OUT PUNICODE_STRING RemainingPart)
314 {
315 USHORT FirstPosition, i;
316 USHORT SkipFirstSlash = 0;
317 PAGED_CODE();
318
319 /* Zero the strings before continuing */
320 RtlZeroMemory(FirstPart, sizeof(UNICODE_STRING));
321 RtlZeroMemory(RemainingPart, sizeof(UNICODE_STRING));
322
323 /* Just quit if the string is empty */
324 if (!Name.Length) return;
325
326 /* Find first backslash */
327 FirstPosition = Name.Length / sizeof(WCHAR) ;
328 for (i = 0; i < Name.Length / sizeof(WCHAR); i++)
329 {
330 /* If we found one... */
331 if (Name.Buffer[i] == L'\\')
332 {
333 /* If it begins string, just notice it and continue */
334 if (i == 0)
335 {
336 SkipFirstSlash = 1;
337 }
338 else
339 {
340 /* Else, save its position and break out of the loop */
341 FirstPosition = i;
342 break;
343 }
344 }
345 }
346
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;
351
352 /* And second one, if necessary */
353 if (FirstPosition < (Name.Length / sizeof(WCHAR)))
354 {
355 RemainingPart->Buffer = Name.Buffer + FirstPosition + 1;
356 RemainingPart->Length = Name.Length - (FirstPosition + 1) * sizeof(WCHAR);
357 RemainingPart->MaximumLength = RemainingPart->Length;
358 }
359 }
360
361 /*++
362 * @name FsRtlDoesNameContainWildCards
363 * @implemented
364 *
365 * Checks if the given string contains WildCards
366 *
367 * @param Name
368 * Pointer to a UNICODE_STRING containing Name to examine
369 *
370 * @return TRUE if Name contains wildcards, FALSE otherwise
371 *
372 * @remarks From Bo Branten's ntifs.h v12.
373 *
374 *--*/
375 BOOLEAN
376 NTAPI
377 FsRtlDoesNameContainWildCards(IN PUNICODE_STRING Name)
378 {
379 PWCHAR Ptr;
380 PAGED_CODE();
381
382 /* Loop through every character */
383 if (Name->Length)
384 {
385 Ptr = Name->Buffer + (Name->Length / sizeof(WCHAR)) - 1;
386 while ((Ptr >= Name->Buffer) && (*Ptr != L'\\'))
387 {
388 /* Check for Wildcard */
389 if (FsRtlIsUnicodeCharacterWild(*Ptr)) return TRUE;
390 Ptr--;
391 }
392 }
393
394 /* Nothing Found */
395 return FALSE;
396 }
397
398 /*++
399 * @name FsRtlIsNameInExpression
400 * @implemented
401 *
402 * Check if the Name string is in the Expression string.
403 *
404 * @param Expression
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.
407 *
408 * @param Name
409 * The string to find. It cannot contain wildcards
410 *
411 * @param IgnoreCase
412 * If set to TRUE, case will be ignore with upcasing both strings
413 *
414 * @param UpcaseTable
415 * If not NULL, and if IgnoreCase is set to TRUE, it will be used to
416 * upcase the both strings
417 *
418 * @return TRUE if Name is in Expression, FALSE otherwise
419 *
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).
423 *
424 *--*/
425 BOOLEAN
426 NTAPI
427 FsRtlIsNameInExpression(IN PUNICODE_STRING Expression,
428 IN PUNICODE_STRING Name,
429 IN BOOLEAN IgnoreCase,
430 IN PWCHAR UpcaseTable OPTIONAL)
431 {
432 BOOLEAN Result;
433 NTSTATUS Status;
434 UNICODE_STRING IntName;
435
436 if (IgnoreCase && !UpcaseTable)
437 {
438 Status = RtlUpcaseUnicodeString(&IntName, Name, TRUE);
439 if (!NT_SUCCESS(Status))
440 {
441 ExRaiseStatus(Status);
442 }
443 Name = &IntName;
444 IgnoreCase = FALSE;
445 }
446 else
447 {
448 IntName.Buffer = NULL;
449 }
450
451 Result = FsRtlIsNameInExpressionPrivate(Expression, Name, IgnoreCase, UpcaseTable);
452
453 if (IntName.Buffer != NULL)
454 {
455 RtlFreeUnicodeString(&IntName);
456 }
457
458 return Result;
459 }