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