[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / fsrtl / dbcsname.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 DBCS parsing and other support routines for FSDs
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Pierre Schweitzer (pierre.schweitzer@reactos.org)
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 /* PUBLIC FUNCTIONS **********************************************************/
17
18 /*++
19 * @name FsRtlDissectDbcs
20 * @implemented
21 *
22 * Dissects a given path name into first and remaining part.
23 *
24 * @param Name
25 * ANSI string to dissect.
26 *
27 * @param FirstPart
28 * Pointer to user supplied ANSI_STRING, that will later point
29 * to the first part of the original name.
30 *
31 * @param RemainingPart
32 * Pointer to user supplied ANSI_STRING, that will later point
33 * to the remaining part of the original name.
34 *
35 * @return None
36 *
37 * @remarks Example:
38 * Name: \test1\test2\test3
39 * FirstPart: test1
40 * RemainingPart: test2\test3
41 *
42 *--*/
43 VOID
44 NTAPI
45 FsRtlDissectDbcs(IN ANSI_STRING Name,
46 OUT PANSI_STRING FirstPart,
47 OUT PANSI_STRING RemainingPart)
48 {
49 ULONG FirstPosition, i;
50 ULONG SkipFirstSlash = 0;
51 PAGED_CODE();
52
53 /* Zero the strings before continuing */
54 RtlZeroMemory(FirstPart, sizeof(ANSI_STRING));
55 RtlZeroMemory(RemainingPart, sizeof(ANSI_STRING));
56
57 /* Just quit if the string is empty */
58 if (!Name.Length) return;
59
60 /* Find first backslash */
61 FirstPosition = Name.Length;
62 for (i = 0; i < Name.Length; i++)
63 {
64 /* First make sure the character it's not the Lead DBCS */
65 if (FsRtlIsLeadDbcsCharacter(Name.Buffer[i]))
66 {
67 i++;
68 }
69 /* If we found one... */
70 else if (Name.Buffer[i] == '\\')
71 {
72 /* If it begins string, just notice it and continue */
73 if (i == 0)
74 {
75 SkipFirstSlash = 1;
76 }
77 else
78 {
79 /* Else, save its position and break out of the loop */
80 FirstPosition = i;
81 break;
82 }
83 }
84 }
85
86 /* Set up the first result string */
87 FirstPart->Buffer = Name.Buffer + SkipFirstSlash;
88 FirstPart->Length = (FirstPosition - SkipFirstSlash);
89 FirstPart->MaximumLength = FirstPart->Length;
90
91 /* And second one, if necessary */
92 if (FirstPosition < (Name.Length))
93 {
94 RemainingPart->Buffer = Name.Buffer + FirstPosition + 1;
95 RemainingPart->Length = Name.Length - (FirstPosition + 1);
96 RemainingPart->MaximumLength = RemainingPart->Length;
97 }
98 }
99
100 /*++
101 * @name FsRtlDoesDbcsContainWildCards
102 * @implemented
103 *
104 * Returns TRUE if the given DbcsName contains wildcards such as *, ?,
105 * ANSI_DOS_STAR, ANSI_DOS_DOT, and ANSI_DOS_QM
106 *
107 * @param Name
108 * The Name to check
109 *
110 * @return TRUE if there are wildcards, FALSE otherwise
111 *
112 * @remarks None
113 *
114 *--*/
115 BOOLEAN
116 NTAPI
117 FsRtlDoesDbcsContainWildCards(IN PANSI_STRING Name)
118 {
119 ULONG i;
120 PAGED_CODE();
121
122 /* Check every character */
123 for (i = 0; i < Name->Length; i++)
124 {
125 /* First make sure it's not the Lead DBCS */
126 if (FsRtlIsLeadDbcsCharacter(Name->Buffer[i]))
127 {
128 i++;
129 }
130 else if (FsRtlIsAnsiCharacterWild(Name->Buffer[i]))
131 {
132 /* Now return if it has a wildcard */
133 return TRUE;
134 }
135 }
136
137 /* We didn't return above...so none found */
138 return FALSE;
139 }
140
141 /*++
142 * @name FsRtlIsDbcsInExpression
143 * @halfplemented
144 *
145 * Check if the Name string is in the Expression string.
146 *
147 * @param Expression
148 * The string in which we've to find Name. It can contains wildcards
149 *
150 * @param Name
151 * The string to find. It cannot contain wildcards.
152 *
153 * @return TRUE if Name is found in Expression, FALSE otherwise
154 *
155 * @remarks Implementation should be fixed to handle wildcards
156 *
157 *--*/
158 BOOLEAN
159 NTAPI
160 FsRtlIsDbcsInExpression(IN PANSI_STRING Expression,
161 IN PANSI_STRING Name)
162 {
163 ULONG ExpressionPosition, NamePosition, MatchingChars = 0;
164 PAGED_CODE();
165
166 ASSERT(Name->Length);
167 ASSERT(Expression->Length);
168 ASSERT(!FsRtlDoesDbcsContainWildCards(Name));
169
170 /* One can't be null, both can be */
171 if (!Expression->Length || !Name->Length)
172 {
173 return !(Expression->Length ^ Name->Length);
174 }
175
176 for (ExpressionPosition = 0; ExpressionPosition < Expression->Length; ExpressionPosition++)
177 {
178 if ((Expression->Buffer[ExpressionPosition] == Name->Buffer[MatchingChars]) ||
179 (Expression->Buffer[ExpressionPosition] == '?') ||
180 (Expression->Buffer[ExpressionPosition] == ANSI_DOS_QM) ||
181 (Expression->Buffer[ExpressionPosition] == ANSI_DOS_DOT &&
182 (Name->Buffer[MatchingChars] == '.' || Name->Buffer[MatchingChars] == '0')))
183 {
184 MatchingChars++;
185 }
186 else if (Expression->Buffer[ExpressionPosition] == '*')
187 {
188 MatchingChars = Name->Length;
189 }
190 else if (Expression->Buffer[ExpressionPosition] == ANSI_DOS_STAR)
191 {
192 for (NamePosition = MatchingChars; NamePosition < Name->Length; NamePosition++)
193 {
194 if (Name->Buffer[NamePosition] == '.')
195 {
196 MatchingChars = NamePosition;
197 break;
198 }
199 }
200 }
201 else
202 {
203 MatchingChars = 0;
204 }
205 if (MatchingChars == Name->Length)
206 {
207 return TRUE;
208 }
209 }
210
211 return FALSE;
212 }
213
214 /*++
215 * @name FsRtlIsFatDbcsLegal
216 * @implemented
217 *
218 * Returns TRUE if the given DbcsName is a valid FAT filename (in 8.3)
219 *
220 * @param DbcsName
221 * The filename to check. It can also contains pathname.
222 *
223 * @param WildCardsPermissible
224 * If this is set to FALSE and if filename contains wildcard, the function
225 * will fail
226 *
227 * @param PathNamePermissible
228 * If this is set to FALSE and if the filename comes with a pathname, the
229 * function will fail
230 *
231 * @param LeadingBackslashPermissible
232 * If this is set to FALSE and if the filename starts with a backslash, the
233 * function will fail
234 *
235 * @return TRUE if the DbcsName is legal, FALSE otherwise
236 *
237 * @remarks None
238 *
239 *--*/
240 BOOLEAN
241 NTAPI
242 FsRtlIsFatDbcsLegal(IN ANSI_STRING DbcsName,
243 IN BOOLEAN WildCardsPermissible,
244 IN BOOLEAN PathNamePermissible,
245 IN BOOLEAN LeadingBackslashPermissible)
246 {
247 ANSI_STRING FirstPart, RemainingPart, Name;
248 BOOLEAN LastDot;
249 ULONG i;
250 PAGED_CODE();
251
252 /* Just quit if the string is empty */
253 if (!DbcsName.Length)
254 return FALSE;
255
256 /* DbcsName wasn't supposed to be started with \ */
257 if (!LeadingBackslashPermissible && DbcsName.Buffer[0] == '\\')
258 return FALSE;
259 /* DbcsName was allowed to be started with \, but now, remove it */
260 else if (LeadingBackslashPermissible && DbcsName.Buffer[0] == '\\')
261 {
262 DbcsName.Buffer = DbcsName.Buffer + 1;
263 DbcsName.Length = DbcsName.Length - 1;
264 DbcsName.MaximumLength = DbcsName.MaximumLength - 1;
265 }
266
267 /* Extract first part of the DbcsName to work on */
268 FsRtlDissectDbcs(DbcsName, &FirstPart, &RemainingPart);
269 while (FirstPart.Length > 0)
270 {
271 /* Reset dots count */
272 LastDot = FALSE;
273
274 /* Accept special filename if wildcards are allowed */
275 if (WildCardsPermissible && (FirstPart.Length == 1 || FirstPart.Length == 2) && FirstPart.Buffer[0] == '.')
276 {
277 if (FirstPart.Length == 2)
278 {
279 if (FirstPart.Buffer[1] == '.')
280 {
281 goto EndLoop;
282 }
283 }
284 else
285 {
286 goto EndLoop;
287 }
288 }
289
290 /* Filename must be 8.3 filename */
291 if (FirstPart.Length < 3 || FirstPart.Length > 12)
292 return FALSE;
293
294 /* Now, we will parse the filename to find everything bad in */
295 for (i = 0; i < FirstPart.Length; i++)
296 {
297 /* First make sure the character it's not the Lead DBCS */
298 if (FsRtlIsLeadDbcsCharacter(FirstPart.Buffer[i]))
299 {
300 if (i == (FirstPart.Length) - 1)
301 return FALSE;
302 i++;
303 }
304 /* Then check for bad characters */
305 else if (!FsRtlIsAnsiCharacterLegalFat(FirstPart.Buffer[i], WildCardsPermissible))
306 {
307 return FALSE;
308 }
309 else if (FirstPart.Buffer[i] == '.')
310 {
311 /* Filename can only contain one dot */
312 if (LastDot)
313 return FALSE;
314
315 LastDot = TRUE;
316
317 /* We mustn't have spaces before dot or at the end of the filename
318 * and no dot at the beginning of the filename */
319 if ((i == (FirstPart.Length) - 1) || i == 0)
320 return FALSE;
321
322 if (i > 0)
323 if (FirstPart.Buffer[i - 1] == ' ')
324 return FALSE;
325
326 /* Filename must be 8.3 filename and not 3.8 filename */
327 if ((FirstPart.Length - 1) - i > 3)
328 return FALSE;
329 }
330 }
331
332 /* Filename mustn't finish with a space */
333 if (FirstPart.Buffer[FirstPart.Length - 1] == ' ')
334 return FALSE;
335
336 EndLoop:
337 /* Preparing next loop */
338 Name.Buffer = RemainingPart.Buffer;
339 Name.Length = RemainingPart.Length;
340 Name.MaximumLength = RemainingPart.MaximumLength;
341
342 /* Call once again our dissect function */
343 FsRtlDissectDbcs(Name, &FirstPart, &RemainingPart);
344
345 /* We found a pathname, it wasn't allowed */
346 if (FirstPart.Length > 0 && !PathNamePermissible)
347 return FALSE;
348 }
349 return TRUE;
350 }
351
352 /*++
353 * @name FsRtlIsHpfsDbcsLegal
354 * @implemented
355 *
356 * Returns TRUE if the given DbcsName is a valid HPFS filename
357 *
358 * @param DbcsName
359 * The filename to check. It can also contains pathname.
360 *
361 * @param WildCardsPermissible
362 * If this is set to FALSE and if filename contains wildcard, the function
363 * will fail
364 *
365 * @param PathNamePermissible
366 * If this is set to FALSE and if the filename comes with a pathname, the
367 * function will fail
368 *
369 * @param LeadingBackslashPermissible
370 * If this is set to FALSE and if the filename starts with a backslash, the
371 * function will fail
372 *
373 * @return TRUE if the DbcsName is legal, FALSE otherwise
374 *
375 * @remarks None
376 *
377 *--*/
378 BOOLEAN
379 NTAPI
380 FsRtlIsHpfsDbcsLegal(IN ANSI_STRING DbcsName,
381 IN BOOLEAN WildCardsPermissible,
382 IN BOOLEAN PathNamePermissible,
383 IN BOOLEAN LeadingBackslashPermissible)
384 {
385 ANSI_STRING FirstPart, RemainingPart, Name;
386 ULONG i;
387 PAGED_CODE();
388
389 /* Just quit if the string is empty */
390 if (!DbcsName.Length)
391 return FALSE;
392
393 /* DbcsName wasn't supposed to be started with \ */
394 if (!LeadingBackslashPermissible && DbcsName.Buffer[0] == '\\')
395 return FALSE;
396 /* DbcsName was allowed to be started with \, but now, remove it */
397 else if (LeadingBackslashPermissible && DbcsName.Buffer[0] == '\\')
398 {
399 DbcsName.Buffer = DbcsName.Buffer + 1;
400 DbcsName.Length = DbcsName.Length - 1;
401 DbcsName.MaximumLength = DbcsName.MaximumLength - 1;
402 }
403
404 /* Extract first part of the DbcsName to work on */
405 FsRtlDissectDbcs(DbcsName, &FirstPart, &RemainingPart);
406 while (FirstPart.Length > 0)
407 {
408 /* Accept special filename if wildcards are allowed */
409 if (WildCardsPermissible && (FirstPart.Length == 1 || FirstPart.Length == 2) && FirstPart.Buffer[0] == '.')
410 {
411 if (FirstPart.Length == 2)
412 {
413 if (FirstPart.Buffer[1] == '.')
414 {
415 goto EndLoop;
416 }
417 }
418 else
419 {
420 goto EndLoop;
421 }
422 }
423
424 /* Filename must be 255 bytes maximum */
425 if (FirstPart.Length > 255)
426 return FALSE;
427
428 /* Now, we will parse the filename to find everything bad in */
429 for (i = 0; i < FirstPart.Length; i++)
430 {
431 /* First make sure the character it's not the Lead DBCS */
432 if (FsRtlIsLeadDbcsCharacter(FirstPart.Buffer[i]))
433 {
434 if (i == (FirstPart.Length) - 1)
435 return FALSE;
436 i++;
437 }
438 /* Then check for bad characters */
439 else if (!!FsRtlIsAnsiCharacterLegalHpfs(FirstPart.Buffer[i], WildCardsPermissible))
440 {
441 return FALSE;
442 }
443 }
444
445 /* Filename mustn't finish with a space or a dot */
446 if ((FirstPart.Buffer[FirstPart.Length - 1] == ' ') ||
447 (FirstPart.Buffer[FirstPart.Length - 1] == '.'))
448 return FALSE;
449
450 EndLoop:
451 /* Preparing next loop */
452 Name.Buffer = RemainingPart.Buffer;
453 Name.Length = RemainingPart.Length;
454 Name.MaximumLength = RemainingPart.MaximumLength;
455
456 /* Call once again our dissect function */
457 FsRtlDissectDbcs(Name, &FirstPart, &RemainingPart);
458
459 /* We found a pathname, it wasn't allowed */
460 if (FirstPart.Length > 0 && !PathNamePermissible)
461 return FALSE;
462 }
463 return TRUE;
464 }