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