be1eb348082ac374e7831c6d9ca7966c59025990
[reactos.git] / reactos / 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 /* Allow null matching */
147 else if (Expression->Buffer[ExpressionPosition] != L'?' &&
148 Expression->Buffer[ExpressionPosition] != Name->Buffer[NamePosition])
149 {
150 NamePosition++;
151 }
152 }
153 /* Check DOS_STAR */
154 else if (Expression->Buffer[ExpressionPosition] == DOS_STAR)
155 {
156 MatchingChars = NamePosition;
157 while (MatchingChars < Name->Length / sizeof(WCHAR))
158 {
159 if (Name->Buffer[MatchingChars] == L'.')
160 {
161 NamePosition = MatchingChars + 1;
162 }
163 MatchingChars++;
164 }
165 ExpressionPosition++;
166 }
167 /* If nothing match, try to backtrack */
168 else if (StarFound >= 0)
169 {
170 ExpressionPosition = BackTracking[StarFound--];
171 }
172 /* Otherwise, fail */
173 else
174 {
175 break;
176 }
177
178 /* Under certain circumstances, expression is over, but name isn't
179 * and we can backtrack, then, backtrack */
180 if (ExpressionPosition == Expression->Length / sizeof(WCHAR) &&
181 NamePosition != Name->Length / sizeof(WCHAR) &&
182 StarFound >= 0)
183 {
184 ExpressionPosition = BackTracking[StarFound--];
185 }
186 }
187 if (ExpressionPosition + 1 == Expression->Length / sizeof(WCHAR) && NamePosition == Name->Length / sizeof(WCHAR) &&
188 (Expression->Buffer[ExpressionPosition] == DOS_DOT || Expression->Buffer[ExpressionPosition] == L'*'))
189 {
190 ExpressionPosition++;
191 }
192
193 if (BackTracking)
194 {
195 ExFreePoolWithTag(BackTracking, 'nrSF');
196 }
197
198 return (ExpressionPosition == Expression->Length / sizeof(WCHAR) && NamePosition == Name->Length / sizeof(WCHAR));
199 }
200
201 /* PUBLIC FUNCTIONS **********************************************************/
202
203 /*++
204 * @name FsRtlAreNamesEqual
205 * @implemented
206 *
207 * Compare two strings to check if they match
208 *
209 * @param Name1
210 * First unicode string to compare
211 *
212 * @param Name2
213 * Second unicode string to compare
214 *
215 * @param IgnoreCase
216 * If TRUE, Case will be ignored when comparing strings
217 *
218 * @param UpcaseTable
219 * Table for upcase letters. If NULL is given, system one will be used
220 *
221 * @return TRUE if the strings are equal
222 *
223 * @remarks From Bo Branten's ntifs.h v25.
224 *
225 *--*/
226 BOOLEAN
227 NTAPI
228 FsRtlAreNamesEqual(IN PCUNICODE_STRING Name1,
229 IN PCUNICODE_STRING Name2,
230 IN BOOLEAN IgnoreCase,
231 IN PCWCH UpcaseTable OPTIONAL)
232 {
233 UNICODE_STRING UpcaseName1;
234 UNICODE_STRING UpcaseName2;
235 BOOLEAN StringsAreEqual, MemoryAllocated = FALSE;
236 USHORT i;
237 NTSTATUS Status;
238 PAGED_CODE();
239
240 /* Well, first check their size */
241 if (Name1->Length != Name2->Length) return FALSE;
242
243 /* Check if the caller didn't give an upcase table */
244 if ((IgnoreCase) && !(UpcaseTable))
245 {
246 /* Upcase the string ourselves */
247 Status = RtlUpcaseUnicodeString(&UpcaseName1, Name1, TRUE);
248 if (!NT_SUCCESS(Status)) RtlRaiseStatus(Status);
249
250 /* Upcase the second string too */
251 RtlUpcaseUnicodeString(&UpcaseName2, Name2, TRUE);
252 Name1 = &UpcaseName1;
253 Name2 = &UpcaseName2;
254
255 /* Make sure we go through the path below, but free the strings */
256 IgnoreCase = FALSE;
257 MemoryAllocated = TRUE;
258 }
259
260 /* Do a case-sensitive search */
261 if (!IgnoreCase)
262 {
263 /* Use a raw memory compare */
264 StringsAreEqual = RtlEqualMemory(Name1->Buffer,
265 Name2->Buffer,
266 Name1->Length);
267
268 /* Check if we allocated strings */
269 if (MemoryAllocated)
270 {
271 /* Free them */
272 RtlFreeUnicodeString(&UpcaseName1);
273 RtlFreeUnicodeString(&UpcaseName2);
274 }
275
276 /* Return the equality */
277 return StringsAreEqual;
278 }
279 else
280 {
281 /* Case in-sensitive search */
282 for (i = 0; i < Name1->Length / sizeof(WCHAR); i++)
283 {
284 /* Check if the character matches */
285 if (UpcaseTable[Name1->Buffer[i]] != UpcaseTable[Name2->Buffer[i]])
286 {
287 /* Non-match found! */
288 return FALSE;
289 }
290 }
291
292 /* We finished the loop so we are equal */
293 return TRUE;
294 }
295 }
296
297 /*++
298 * @name FsRtlDissectName
299 * @implemented
300 *
301 * Dissects a given path name into first and remaining part.
302 *
303 * @param Name
304 * Unicode string to dissect.
305 *
306 * @param FirstPart
307 * Pointer to user supplied UNICODE_STRING, that will later point
308 * to the first part of the original name.
309 *
310 * @param RemainingPart
311 * Pointer to user supplied UNICODE_STRING, that will later point
312 * to the remaining part of the original name.
313 *
314 * @return None
315 *
316 * @remarks Example:
317 * Name: \test1\test2\test3
318 * FirstPart: test1
319 * RemainingPart: test2\test3
320 *
321 *--*/
322 VOID
323 NTAPI
324 FsRtlDissectName(IN UNICODE_STRING Name,
325 OUT PUNICODE_STRING FirstPart,
326 OUT PUNICODE_STRING RemainingPart)
327 {
328 USHORT FirstPosition, i;
329 USHORT SkipFirstSlash = 0;
330 PAGED_CODE();
331
332 /* Zero the strings before continuing */
333 RtlZeroMemory(FirstPart, sizeof(UNICODE_STRING));
334 RtlZeroMemory(RemainingPart, sizeof(UNICODE_STRING));
335
336 /* Just quit if the string is empty */
337 if (!Name.Length) return;
338
339 /* Find first backslash */
340 FirstPosition = Name.Length / sizeof(WCHAR) ;
341 for (i = 0; i < Name.Length / sizeof(WCHAR); i++)
342 {
343 /* If we found one... */
344 if (Name.Buffer[i] == L'\\')
345 {
346 /* If it begins string, just notice it and continue */
347 if (i == 0)
348 {
349 SkipFirstSlash = 1;
350 }
351 else
352 {
353 /* Else, save its position and break out of the loop */
354 FirstPosition = i;
355 break;
356 }
357 }
358 }
359
360 /* Set up the first result string */
361 FirstPart->Buffer = Name.Buffer + SkipFirstSlash;
362 FirstPart->Length = (FirstPosition - SkipFirstSlash) * sizeof(WCHAR);
363 FirstPart->MaximumLength = FirstPart->Length;
364
365 /* And second one, if necessary */
366 if (FirstPosition < (Name.Length / sizeof(WCHAR)))
367 {
368 RemainingPart->Buffer = Name.Buffer + FirstPosition + 1;
369 RemainingPart->Length = Name.Length - (FirstPosition + 1) * sizeof(WCHAR);
370 RemainingPart->MaximumLength = RemainingPart->Length;
371 }
372 }
373
374 /*++
375 * @name FsRtlDoesNameContainWildCards
376 * @implemented
377 *
378 * Checks if the given string contains WildCards
379 *
380 * @param Name
381 * Pointer to a UNICODE_STRING containing Name to examine
382 *
383 * @return TRUE if Name contains wildcards, FALSE otherwise
384 *
385 * @remarks From Bo Branten's ntifs.h v12.
386 *
387 *--*/
388 BOOLEAN
389 NTAPI
390 FsRtlDoesNameContainWildCards(IN PUNICODE_STRING Name)
391 {
392 PWCHAR Ptr;
393 PAGED_CODE();
394
395 /* Loop through every character */
396 if (Name->Length)
397 {
398 Ptr = Name->Buffer + (Name->Length / sizeof(WCHAR)) - 1;
399 while ((Ptr >= Name->Buffer) && (*Ptr != L'\\'))
400 {
401 /* Check for Wildcard */
402 if (FsRtlIsUnicodeCharacterWild(*Ptr)) return TRUE;
403 Ptr--;
404 }
405 }
406
407 /* Nothing Found */
408 return FALSE;
409 }
410
411 /*++
412 * @name FsRtlIsNameInExpression
413 * @implemented
414 *
415 * Check if the Name string is in the Expression string.
416 *
417 * @param Expression
418 * The string in which we've to find Name. It can contain wildcards.
419 * If IgnoreCase is set to TRUE, this string MUST BE uppercase.
420 *
421 * @param Name
422 * The string to find. It cannot contain wildcards
423 *
424 * @param IgnoreCase
425 * If set to TRUE, case will be ignore with upcasing both strings
426 *
427 * @param UpcaseTable
428 * If not NULL, and if IgnoreCase is set to TRUE, it will be used to
429 * upcase the both strings
430 *
431 * @return TRUE if Name is in Expression, FALSE otherwise
432 *
433 * @remarks From Bo Branten's ntifs.h v12. This function should be
434 * rewritten to avoid recursion and better wildcard handling
435 * should be implemented (see FsRtlDoesNameContainWildCards).
436 *
437 *--*/
438 BOOLEAN
439 NTAPI
440 FsRtlIsNameInExpression(IN PUNICODE_STRING Expression,
441 IN PUNICODE_STRING Name,
442 IN BOOLEAN IgnoreCase,
443 IN PWCHAR UpcaseTable OPTIONAL)
444 {
445 BOOLEAN Result;
446 NTSTATUS Status;
447 UNICODE_STRING IntName;
448
449 if (IgnoreCase && !UpcaseTable)
450 {
451 Status = RtlUpcaseUnicodeString(&IntName, Name, TRUE);
452 if (!NT_SUCCESS(Status))
453 {
454 ExRaiseStatus(Status);
455 }
456 Name = &IntName;
457 IgnoreCase = FALSE;
458 }
459 else
460 {
461 IntName.Buffer = NULL;
462 }
463
464 Result = FsRtlIsNameInExpressionPrivate(Expression, Name, IgnoreCase, UpcaseTable);
465
466 if (IntName.Buffer != NULL)
467 {
468 RtlFreeUnicodeString(&IntName);
469 }
470
471 return Result;
472 }