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