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