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