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