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