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 SHORT StarFound
= -1, DosStarFound
= -1;
27 USHORT BackTrackingBuffer
[5], DosBackTrackingBuffer
[5];
28 PUSHORT BackTracking
= BackTrackingBuffer
, DosBackTracking
= DosBackTrackingBuffer
;
29 SHORT BackTrackingSize
= RTL_NUMBER_OF(BackTrackingBuffer
);
30 SHORT DosBackTrackingSize
= RTL_NUMBER_OF(DosBackTrackingBuffer
);
31 UNICODE_STRING IntExpression
;
32 USHORT ExpressionPosition
= 0, NamePosition
= 0, MatchingChars
, LastDot
;
36 /* Check if we were given strings at all */
37 if (!Name
->Length
|| !Expression
->Length
)
39 /* Return TRUE if both strings are empty, otherwise FALSE */
40 if (Name
->Length
== 0 && Expression
->Length
== 0)
46 /* Check for a shortcut: just one wildcard */
47 if (Expression
->Length
== sizeof(WCHAR
))
49 if (Expression
->Buffer
[0] == L
'*')
53 ASSERT(!IgnoreCase
|| UpcaseTable
);
55 /* Another shortcut, wildcard followed by some string */
56 if (Expression
->Buffer
[0] == L
'*')
58 /* Copy Expression to our local variable */
59 IntExpression
= *Expression
;
61 /* Skip the first char */
62 IntExpression
.Buffer
++;
63 IntExpression
.Length
-= sizeof(WCHAR
);
65 /* Continue only if the rest of the expression does NOT contain
67 if (!FsRtlDoesNameContainWildCards(&IntExpression
))
69 /* Check for a degenerate case */
70 if (Name
->Length
< (Expression
->Length
- sizeof(WCHAR
)))
73 /* Calculate position */
74 NamePosition
= (Name
->Length
- IntExpression
.Length
) / sizeof(WCHAR
);
79 /* We can just do a byte compare */
80 return RtlEqualMemory(IntExpression
.Buffer
,
81 Name
->Buffer
+ NamePosition
,
82 IntExpression
.Length
);
86 /* Not so easy, need to upcase and check char by char */
87 for (ExpressionPosition
= 0; ExpressionPosition
< (IntExpression
.Length
/ sizeof(WCHAR
)); ExpressionPosition
++)
89 /* Assert that expression is already upcased! */
90 ASSERT(IntExpression
.Buffer
[ExpressionPosition
] == UpcaseTable
[IntExpression
.Buffer
[ExpressionPosition
]]);
92 /* Now compare upcased name char with expression */
93 if (UpcaseTable
[Name
->Buffer
[NamePosition
+ ExpressionPosition
]] !=
94 IntExpression
.Buffer
[ExpressionPosition
])
106 while ((NamePosition
< Name
->Length
/ sizeof(WCHAR
)) &&
107 (ExpressionPosition
< Expression
->Length
/ sizeof(WCHAR
)))
109 /* Basic check to test if chars are equal */
110 CompareChar
= IgnoreCase
? UpcaseTable
[Name
->Buffer
[NamePosition
]] :
111 Name
->Buffer
[NamePosition
];
112 if (Expression
->Buffer
[ExpressionPosition
] == CompareChar
)
115 ExpressionPosition
++;
117 /* Check cases that eat one char */
118 else if (Expression
->Buffer
[ExpressionPosition
] == L
'?')
121 ExpressionPosition
++;
124 else if (Expression
->Buffer
[ExpressionPosition
] == L
'*')
126 /* Skip contigous stars */
127 while ((ExpressionPosition
+ 1 < (USHORT
)(Expression
->Length
/ sizeof(WCHAR
))) &&
128 (Expression
->Buffer
[ExpressionPosition
+ 1] == L
'*'))
130 ExpressionPosition
++;
133 /* Save star position */
135 if (StarFound
>= BackTrackingSize
)
137 BackTrackingSize
= Expression
->Length
/ sizeof(WCHAR
);
138 BackTracking
= ExAllocatePoolWithTag(PagedPool
| POOL_RAISE_IF_ALLOCATION_FAILURE
,
139 BackTrackingSize
* sizeof(USHORT
),
141 RtlCopyMemory(BackTracking
, BackTrackingBuffer
, sizeof(BackTrackingBuffer
));
144 BackTracking
[StarFound
] = ExpressionPosition
++;
146 /* If star is at the end, then eat all rest and leave */
147 if (ExpressionPosition
== Expression
->Length
/ sizeof(WCHAR
))
149 NamePosition
= Name
->Length
/ sizeof(WCHAR
);
153 /* Allow null matching */
154 if (Expression
->Buffer
[ExpressionPosition
] != L
'?' &&
155 Expression
->Buffer
[ExpressionPosition
] != Name
->Buffer
[NamePosition
])
161 else if (Expression
->Buffer
[ExpressionPosition
] == DOS_STAR
)
163 /* Skip contigous stars */
164 while ((ExpressionPosition
+ 1 < (USHORT
)(Expression
->Length
/ sizeof(WCHAR
))) &&
165 (Expression
->Buffer
[ExpressionPosition
+ 1] == DOS_STAR
))
167 ExpressionPosition
++;
170 /* Look for last dot */
172 LastDot
= (USHORT
)-1;
173 while (MatchingChars
< Name
->Length
/ sizeof(WCHAR
))
175 if (Name
->Buffer
[MatchingChars
] == L
'.')
177 LastDot
= MatchingChars
;
178 if (LastDot
> NamePosition
)
185 /* If we don't have dots or we didn't find last yet
186 * start eating everything
188 if (MatchingChars
!= Name
->Length
|| LastDot
== (USHORT
)-1)
191 if (DosStarFound
>= DosBackTrackingSize
)
193 DosBackTrackingSize
= Expression
->Length
/ sizeof(WCHAR
);
194 DosBackTracking
= ExAllocatePoolWithTag(PagedPool
| POOL_RAISE_IF_ALLOCATION_FAILURE
,
195 DosBackTrackingSize
* sizeof(USHORT
),
197 RtlCopyMemory(DosBackTracking
, DosBackTrackingBuffer
, sizeof(DosBackTrackingBuffer
));
199 DosBackTracking
[DosStarFound
] = ExpressionPosition
++;
201 /* Not the same char, start exploring */
202 if (Expression
->Buffer
[ExpressionPosition
] != Name
->Buffer
[NamePosition
])
207 /* Else, if we are at last dot, eat it - otherwise, null match */
208 if (Name
->Buffer
[NamePosition
] == '.')
211 ExpressionPosition
++;
215 else if (Expression
->Buffer
[ExpressionPosition
] == DOS_DOT
)
217 /* We only match dots */
218 if (Name
->Buffer
[NamePosition
] == L
'.')
222 /* Try to explore later on for null matching */
223 else if ((ExpressionPosition
+ 1 < (USHORT
)(Expression
->Length
/ sizeof(WCHAR
))) &&
224 (Name
->Buffer
[NamePosition
] == Expression
->Buffer
[ExpressionPosition
+ 1]))
228 ExpressionPosition
++;
231 else if (Expression
->Buffer
[ExpressionPosition
] == DOS_QM
)
233 /* We match everything except dots */
234 if (Name
->Buffer
[NamePosition
] != L
'.')
238 ExpressionPosition
++;
240 /* If nothing match, try to backtrack */
241 else if (StarFound
>= 0)
243 ExpressionPosition
= BackTracking
[StarFound
--];
245 else if (DosStarFound
>= 0)
247 ExpressionPosition
= DosBackTracking
[DosStarFound
--];
249 /* Otherwise, fail */
255 /* Under certain circumstances, expression is over, but name isn't
256 * and we can backtrack, then, backtrack */
257 if (ExpressionPosition
== Expression
->Length
/ sizeof(WCHAR
) &&
258 NamePosition
!= Name
->Length
/ sizeof(WCHAR
) &&
261 ExpressionPosition
= BackTracking
[StarFound
--];
264 /* If we have nullable matching wc at the end of the string, eat them */
265 if (ExpressionPosition
!= Expression
->Length
/ sizeof(WCHAR
) && NamePosition
== Name
->Length
/ sizeof(WCHAR
))
267 while (ExpressionPosition
< Expression
->Length
/ sizeof(WCHAR
))
269 if (Expression
->Buffer
[ExpressionPosition
] != DOS_DOT
&&
270 Expression
->Buffer
[ExpressionPosition
] != L
'*' &&
271 Expression
->Buffer
[ExpressionPosition
] != DOS_STAR
)
275 ExpressionPosition
++;
279 if (BackTracking
!= BackTrackingBuffer
)
281 ExFreePoolWithTag(BackTracking
, 'nrSF');
283 if (DosBackTracking
!= DosBackTrackingBuffer
)
285 ExFreePoolWithTag(DosBackTracking
, 'nrSF');
288 return (ExpressionPosition
== Expression
->Length
/ sizeof(WCHAR
) && NamePosition
== Name
->Length
/ sizeof(WCHAR
));
291 /* PUBLIC FUNCTIONS **********************************************************/
294 * @name FsRtlAreNamesEqual
297 * Compare two strings to check if they match
300 * First unicode string to compare
303 * Second unicode string to compare
306 * If TRUE, Case will be ignored when comparing strings
309 * Table for upcase letters. If NULL is given, system one will be used
311 * @return TRUE if the strings are equal
313 * @remarks From Bo Branten's ntifs.h v25.
318 FsRtlAreNamesEqual(IN PCUNICODE_STRING Name1
,
319 IN PCUNICODE_STRING Name2
,
320 IN BOOLEAN IgnoreCase
,
321 IN PCWCH UpcaseTable OPTIONAL
)
323 UNICODE_STRING UpcaseName1
;
324 UNICODE_STRING UpcaseName2
;
325 BOOLEAN StringsAreEqual
, MemoryAllocated
= FALSE
;
330 /* Well, first check their size */
331 if (Name1
->Length
!= Name2
->Length
) return FALSE
;
333 /* Check if the caller didn't give an upcase table */
334 if ((IgnoreCase
) && !(UpcaseTable
))
336 /* Upcase the string ourselves */
337 Status
= RtlUpcaseUnicodeString(&UpcaseName1
, Name1
, TRUE
);
338 if (!NT_SUCCESS(Status
)) RtlRaiseStatus(Status
);
340 /* Upcase the second string too */
341 RtlUpcaseUnicodeString(&UpcaseName2
, Name2
, TRUE
);
342 Name1
= &UpcaseName1
;
343 Name2
= &UpcaseName2
;
345 /* Make sure we go through the path below, but free the strings */
347 MemoryAllocated
= TRUE
;
350 /* Do a case-sensitive search */
353 /* Use a raw memory compare */
354 StringsAreEqual
= RtlEqualMemory(Name1
->Buffer
,
358 /* Check if we allocated strings */
362 RtlFreeUnicodeString(&UpcaseName1
);
363 RtlFreeUnicodeString(&UpcaseName2
);
366 /* Return the equality */
367 return StringsAreEqual
;
371 /* Case in-sensitive search */
372 for (i
= 0; i
< Name1
->Length
/ sizeof(WCHAR
); i
++)
374 /* Check if the character matches */
375 if (UpcaseTable
[Name1
->Buffer
[i
]] != UpcaseTable
[Name2
->Buffer
[i
]])
377 /* Non-match found! */
382 /* We finished the loop so we are equal */
388 * @name FsRtlDissectName
391 * Dissects a given path name into first and remaining part.
394 * Unicode string to dissect.
397 * Pointer to user supplied UNICODE_STRING, that will later point
398 * to the first part of the original name.
400 * @param RemainingPart
401 * Pointer to user supplied UNICODE_STRING, that will later point
402 * to the remaining part of the original name.
407 * Name: \test1\test2\test3
409 * RemainingPart: test2\test3
414 FsRtlDissectName(IN UNICODE_STRING Name
,
415 OUT PUNICODE_STRING FirstPart
,
416 OUT PUNICODE_STRING RemainingPart
)
418 USHORT FirstPosition
, i
;
419 USHORT SkipFirstSlash
= 0;
422 /* Zero the strings before continuing */
423 RtlZeroMemory(FirstPart
, sizeof(UNICODE_STRING
));
424 RtlZeroMemory(RemainingPart
, sizeof(UNICODE_STRING
));
426 /* Just quit if the string is empty */
427 if (!Name
.Length
) return;
429 /* Find first backslash */
430 FirstPosition
= Name
.Length
/ sizeof(WCHAR
) ;
431 for (i
= 0; i
< Name
.Length
/ sizeof(WCHAR
); i
++)
433 /* If we found one... */
434 if (Name
.Buffer
[i
] == L
'\\')
436 /* If it begins string, just notice it and continue */
443 /* Else, save its position and break out of the loop */
450 /* Set up the first result string */
451 FirstPart
->Buffer
= Name
.Buffer
+ SkipFirstSlash
;
452 FirstPart
->Length
= (FirstPosition
- SkipFirstSlash
) * sizeof(WCHAR
);
453 FirstPart
->MaximumLength
= FirstPart
->Length
;
455 /* And second one, if necessary */
456 if (FirstPosition
< (Name
.Length
/ sizeof(WCHAR
)))
458 RemainingPart
->Buffer
= Name
.Buffer
+ FirstPosition
+ 1;
459 RemainingPart
->Length
= Name
.Length
- (FirstPosition
+ 1) * sizeof(WCHAR
);
460 RemainingPart
->MaximumLength
= RemainingPart
->Length
;
465 * @name FsRtlDoesNameContainWildCards
468 * Checks if the given string contains WildCards
471 * Pointer to a UNICODE_STRING containing Name to examine
473 * @return TRUE if Name contains wildcards, FALSE otherwise
475 * @remarks From Bo Branten's ntifs.h v12.
480 FsRtlDoesNameContainWildCards(IN PUNICODE_STRING Name
)
485 /* Loop through every character */
488 Ptr
= Name
->Buffer
+ (Name
->Length
/ sizeof(WCHAR
)) - 1;
489 while ((Ptr
>= Name
->Buffer
) && (*Ptr
!= L
'\\'))
491 /* Check for Wildcard */
492 if (FsRtlIsUnicodeCharacterWild(*Ptr
)) return TRUE
;
502 * @name FsRtlIsNameInExpression
505 * Check if the Name string is in the Expression string.
508 * The string in which we've to find Name. It can contain wildcards.
509 * If IgnoreCase is set to TRUE, this string MUST BE uppercase.
512 * The string to find. It cannot contain wildcards
515 * If set to TRUE, case will be ignore with upcasing both strings
518 * If not NULL, and if IgnoreCase is set to TRUE, it will be used to
519 * upcase the both strings
521 * @return TRUE if Name is in Expression, FALSE otherwise
523 * @remarks From Bo Branten's ntifs.h v12. This function should be
524 * rewritten to avoid recursion and better wildcard handling
525 * should be implemented (see FsRtlDoesNameContainWildCards).
530 FsRtlIsNameInExpression(IN PUNICODE_STRING Expression
,
531 IN PUNICODE_STRING Name
,
532 IN BOOLEAN IgnoreCase
,
533 IN PWCHAR UpcaseTable OPTIONAL
)
537 UNICODE_STRING IntName
;
539 if (IgnoreCase
&& !UpcaseTable
)
541 Status
= RtlUpcaseUnicodeString(&IntName
, Name
, TRUE
);
542 if (!NT_SUCCESS(Status
))
544 ExRaiseStatus(Status
);
551 IntName
.Buffer
= NULL
;
554 Result
= FsRtlIsNameInExpressionPrivate(Expression
, Name
, IgnoreCase
, UpcaseTable
);
556 if (IntName
.Buffer
!= NULL
)
558 RtlFreeUnicodeString(&IntName
);