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
)
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
;
39 /* Check if we were given strings at all */
40 if (!Name
->Length
|| !Expression
->Length
)
42 /* Return TRUE if both strings are empty, otherwise FALSE */
43 if (!Name
->Length
&& !Expression
->Length
)
49 /* Check for a shortcut: just one wildcard */
50 if (Expression
->Length
== sizeof(WCHAR
))
52 if (Expression
->Buffer
[0] == L
'*')
56 ASSERT(!IgnoreCase
|| UpcaseTable
);
58 /* Another shortcut, wildcard followed by some string */
59 if (Expression
->Buffer
[0] == L
'*')
61 /* Copy Expression to our local variable */
62 IntExpression
= *Expression
;
64 /* Skip the first char */
65 IntExpression
.Buffer
++;
66 IntExpression
.Length
-= sizeof(WCHAR
);
68 /* Continue only if the rest of the expression does NOT contain
70 if (!FsRtlDoesNameContainWildCards(&IntExpression
))
72 /* Check for a degenerate case */
73 if (Name
->Length
< (Expression
->Length
- sizeof(WCHAR
)))
76 /* Calculate position */
77 NamePosition
= (Name
->Length
- IntExpression
.Length
) / sizeof(WCHAR
);
82 /* We can just do a byte compare */
83 return RtlEqualMemory(IntExpression
.Buffer
,
84 Name
->Buffer
+ NamePosition
,
85 IntExpression
.Length
);
89 /* Not so easy, need to upcase and check char by char */
90 for (ExpressionPosition
= 0; ExpressionPosition
< (IntExpression
.Length
/ sizeof(WCHAR
)); ExpressionPosition
++)
92 /* Assert that expression is already upcased! */
93 ASSERT(IntExpression
.Buffer
[ExpressionPosition
] == UpcaseTable
[IntExpression
.Buffer
[ExpressionPosition
]]);
95 /* Now compare upcased name char with expression */
96 if (UpcaseTable
[Name
->Buffer
[NamePosition
+ ExpressionPosition
]] !=
97 IntExpression
.Buffer
[ExpressionPosition
])
109 /* Name parsing loop */
110 for (; !EndOfName
; MatchingChars
= BackTrackingPosition
, NamePosition
++)
112 /* Reset positions */
113 OldBackTrackingPosition
= BackTrackingPosition
= 0;
115 if (NamePosition
>= Name
->Length
/ sizeof(WCHAR
))
118 if (MatchingChars
&& (OldBackTracking
[MatchingChars
- 1] == Expression
->Length
* 2))
122 while (MatchingChars
> OldBackTrackingPosition
)
124 ExpressionPosition
= (OldBackTracking
[OldBackTrackingPosition
++] + 1) / 2;
126 /* Expression parsing loop */
127 for (Offset
= 0; ExpressionPosition
< Expression
->Length
; Offset
= sizeof(WCHAR
))
129 ExpressionPosition
+= Offset
;
131 if (ExpressionPosition
== Expression
->Length
)
133 BackTracking
[BackTrackingPosition
++] = Expression
->Length
* 2;
137 /* If buffer too small */
138 if (BackTrackingPosition
> BackTrackingBufferSize
- 2)
140 /* We should only ever get here once! */
141 ASSERT(AllocatedBuffer
== NULL
);
142 ASSERT((BackTracking
== BackTrackingBuffer
) || (BackTracking
== OldBackTrackingBuffer
));
143 ASSERT((OldBackTracking
== BackTrackingBuffer
) || (OldBackTracking
== OldBackTrackingBuffer
));
145 /* Calculate buffer size */
146 BackTrackingBufferSize
= (Expression
->Length
+ 1) * 2;
148 /* Allocate memory for both back-tracking buffers */
149 AllocatedBuffer
= ExAllocatePoolWithTag(PagedPool
| POOL_RAISE_IF_ALLOCATION_FAILURE
,
150 2 * BackTrackingBufferSize
* sizeof(USHORT
),
152 if (AllocatedBuffer
== NULL
)
154 DPRINT1("Failed to allocate BackTracking buffer. BackTrackingBufferSize = =x%lx\n",
155 BackTrackingBufferSize
);
160 /* Backtracking is at the start of the buffer */
161 BackTracking
= AllocatedBuffer
;
163 /* Copy BackTrackingBuffer content */
164 RtlCopyMemory(BackTracking
,
166 RTL_NUMBER_OF(BackTrackingBuffer
) * sizeof(USHORT
));
168 /* OldBackTracking is after BackTracking */
169 OldBackTracking
= &BackTracking
[BackTrackingBufferSize
];
171 /* Copy OldBackTrackingBuffer content */
172 RtlCopyMemory(OldBackTracking
,
173 OldBackTrackingBuffer
,
174 RTL_NUMBER_OF(OldBackTrackingBuffer
) * sizeof(USHORT
));
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
)
182 BackTracking
[BackTrackingPosition
++] = (ExpressionPosition
+ sizeof(WCHAR
)) * 2;
184 /* Check cases that eat one char */
185 else if (Expression
->Buffer
[ExpressionPosition
/ sizeof(WCHAR
)] == L
'?' && !EndOfName
)
187 BackTracking
[BackTrackingPosition
++] = (ExpressionPosition
+ sizeof(WCHAR
)) * 2;
190 else if (Expression
->Buffer
[ExpressionPosition
/ sizeof(WCHAR
)] == L
'*')
192 BackTracking
[BackTrackingPosition
++] = ExpressionPosition
* 2;
193 BackTracking
[BackTrackingPosition
++] = (ExpressionPosition
* 2) + 3;
197 else if (Expression
->Buffer
[ExpressionPosition
/ sizeof(WCHAR
)] == DOS_STAR
)
199 /* Look for last dot */
201 if (!EndOfName
&& Name
->Buffer
[NamePosition
] == '.')
203 for (Position
= NamePosition
- 1; Position
< Name
->Length
; Position
++)
205 if (Name
->Buffer
[Position
] == L
'.')
213 if (EndOfName
|| Name
->Buffer
[NamePosition
] != L
'.' || !DontSkipDot
)
214 BackTracking
[BackTrackingPosition
++] = ExpressionPosition
* 2;
216 BackTracking
[BackTrackingPosition
++] = (ExpressionPosition
* 2) + 3;
220 else if (Expression
->Buffer
[ExpressionPosition
/ sizeof(WCHAR
)] == DOS_DOT
)
222 if (EndOfName
) continue;
224 if (Name
->Buffer
[NamePosition
] == L
'.')
225 BackTracking
[BackTrackingPosition
++] = (ExpressionPosition
+ sizeof(WCHAR
)) * 2;
228 else if (Expression
->Buffer
[ExpressionPosition
/ sizeof(WCHAR
)] == DOS_QM
)
230 if (EndOfName
|| Name
->Buffer
[NamePosition
] == L
'.') continue;
232 BackTracking
[BackTrackingPosition
++] = (ExpressionPosition
+ sizeof(WCHAR
)) * 2;
235 /* Leave from loop */
239 for (Position
= 0; MatchingChars
> OldBackTrackingPosition
&& Position
< BackTrackingPosition
; Position
++)
241 while (MatchingChars
> OldBackTrackingPosition
&&
242 BackTracking
[Position
] > OldBackTracking
[OldBackTrackingPosition
])
244 ++OldBackTrackingPosition
;
250 BackTrackingSwap
= BackTracking
;
251 BackTracking
= OldBackTracking
;
252 OldBackTracking
= BackTrackingSwap
;
255 /* Store result value */
256 Result
= MatchingChars
> 0 && (OldBackTracking
[MatchingChars
- 1] == (Expression
->Length
* 2));
260 /* Frees the memory if necessary */
261 if (AllocatedBuffer
!= NULL
)
263 ExFreePoolWithTag(AllocatedBuffer
, 'nrSF');
269 /* PUBLIC FUNCTIONS **********************************************************/
272 * @name FsRtlAreNamesEqual
275 * Compare two strings to check if they match
278 * First unicode string to compare
281 * Second unicode string to compare
284 * If TRUE, Case will be ignored when comparing strings
287 * Table for upcase letters. If NULL is given, system one will be used
289 * @return TRUE if the strings are equal
291 * @remarks From Bo Branten's ntifs.h v25.
296 FsRtlAreNamesEqual(IN PCUNICODE_STRING Name1
,
297 IN PCUNICODE_STRING Name2
,
298 IN BOOLEAN IgnoreCase
,
299 IN PCWCH UpcaseTable OPTIONAL
)
301 UNICODE_STRING UpcaseName1
;
302 UNICODE_STRING UpcaseName2
;
303 BOOLEAN StringsAreEqual
, MemoryAllocated
= FALSE
;
308 /* Well, first check their size */
309 if (Name1
->Length
!= Name2
->Length
) return FALSE
;
311 /* Check if the caller didn't give an upcase table */
312 if ((IgnoreCase
) && !(UpcaseTable
))
314 /* Upcase the string ourselves */
315 Status
= RtlUpcaseUnicodeString(&UpcaseName1
, Name1
, TRUE
);
316 if (!NT_SUCCESS(Status
)) RtlRaiseStatus(Status
);
318 /* Upcase the second string too */
319 Status
= RtlUpcaseUnicodeString(&UpcaseName2
, Name2
, TRUE
);
320 if (!NT_SUCCESS(Status
))
322 RtlFreeUnicodeString(&UpcaseName1
);
323 RtlRaiseStatus(Status
);
326 Name1
= &UpcaseName1
;
327 Name2
= &UpcaseName2
;
329 /* Make sure we go through the path below, but free the strings */
331 MemoryAllocated
= TRUE
;
334 /* Do a case-sensitive search */
337 /* Use a raw memory compare */
338 StringsAreEqual
= RtlEqualMemory(Name1
->Buffer
,
342 /* Check if we allocated strings */
346 RtlFreeUnicodeString(&UpcaseName1
);
347 RtlFreeUnicodeString(&UpcaseName2
);
350 /* Return the equality */
351 return StringsAreEqual
;
355 /* Case in-sensitive search */
356 for (i
= 0; i
< Name1
->Length
/ sizeof(WCHAR
); i
++)
358 /* Check if the character matches */
359 if (UpcaseTable
[Name1
->Buffer
[i
]] != UpcaseTable
[Name2
->Buffer
[i
]])
361 /* Non-match found! */
366 /* We finished the loop so we are equal */
372 * @name FsRtlDissectName
375 * Dissects a given path name into first and remaining part.
378 * Unicode string to dissect.
381 * Pointer to user supplied UNICODE_STRING, that will later point
382 * to the first part of the original name.
384 * @param RemainingPart
385 * Pointer to user supplied UNICODE_STRING, that will later point
386 * to the remaining part of the original name.
391 * Name: \test1\test2\test3
393 * RemainingPart: test2\test3
398 FsRtlDissectName(IN UNICODE_STRING Name
,
399 OUT PUNICODE_STRING FirstPart
,
400 OUT PUNICODE_STRING RemainingPart
)
402 USHORT FirstPosition
, i
;
403 USHORT SkipFirstSlash
= 0;
406 /* Zero the strings before continuing */
407 RtlZeroMemory(FirstPart
, sizeof(UNICODE_STRING
));
408 RtlZeroMemory(RemainingPart
, sizeof(UNICODE_STRING
));
410 /* Just quit if the string is empty */
411 if (!Name
.Length
) return;
413 /* Find first backslash */
414 FirstPosition
= Name
.Length
/ sizeof(WCHAR
) ;
415 for (i
= 0; i
< Name
.Length
/ sizeof(WCHAR
); i
++)
417 /* If we found one... */
418 if (Name
.Buffer
[i
] == L
'\\')
420 /* If it begins string, just notice it and continue */
427 /* Else, save its position and break out of the loop */
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
;
439 /* And second one, if necessary */
440 if (FirstPosition
< (Name
.Length
/ sizeof(WCHAR
)))
442 RemainingPart
->Buffer
= Name
.Buffer
+ FirstPosition
+ 1;
443 RemainingPart
->Length
= Name
.Length
- (FirstPosition
+ 1) * sizeof(WCHAR
);
444 RemainingPart
->MaximumLength
= RemainingPart
->Length
;
449 * @name FsRtlDoesNameContainWildCards
452 * Checks if the given string contains WildCards
455 * Pointer to a UNICODE_STRING containing Name to examine
457 * @return TRUE if Name contains wildcards, FALSE otherwise
459 * @remarks From Bo Branten's ntifs.h v12.
464 FsRtlDoesNameContainWildCards(IN PUNICODE_STRING Name
)
469 /* Loop through every character */
472 Ptr
= Name
->Buffer
+ (Name
->Length
/ sizeof(WCHAR
)) - 1;
473 while ((Ptr
>= Name
->Buffer
) && (*Ptr
!= L
'\\'))
475 /* Check for Wildcard */
476 if (FsRtlIsUnicodeCharacterWild(*Ptr
)) return TRUE
;
486 * @name FsRtlIsNameInExpression
489 * Check if the Name string is in the Expression string.
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.
496 * The string to find. It cannot contain wildcards
499 * If set to TRUE, case will be ignore with upcasing both strings
502 * If not NULL, and if IgnoreCase is set to TRUE, it will be used to
503 * upcase the both strings
505 * @return TRUE if Name is in Expression, FALSE otherwise
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).
514 FsRtlIsNameInExpression(IN PUNICODE_STRING Expression
,
515 IN PUNICODE_STRING Name
,
516 IN BOOLEAN IgnoreCase
,
517 IN PWCHAR UpcaseTable OPTIONAL
)
521 UNICODE_STRING IntName
;
523 if (IgnoreCase
&& !UpcaseTable
)
525 Status
= RtlUpcaseUnicodeString(&IntName
, Name
, TRUE
);
526 if (!NT_SUCCESS(Status
))
528 ExRaiseStatus(Status
);
535 IntName
.Buffer
= NULL
;
538 Result
= FsRtlIsNameInExpressionPrivate(Expression
, Name
, IgnoreCase
, UpcaseTable
);
540 if (IntName
.Buffer
!= NULL
)
542 RtlFreeUnicodeString(&IntName
);