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