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 * Aleksey Bragin (aleksey@reactos.org) 10 */ 11 12 /* INCLUDES ******************************************************************/ 13 14 #include <ntoskrnl.h> 15 #define NDEBUG 16 #include <debug.h> 17 18 /* PRIVATE FUNCTIONS *********************************************************/ 19 BOOLEAN 20 NTAPI 21 FsRtlIsNameInExpressionPrivate(IN PUNICODE_STRING Expression, 22 IN PUNICODE_STRING Name, 23 IN BOOLEAN IgnoreCase, 24 IN PWCHAR UpcaseTable OPTIONAL) 25 { 26 USHORT Offset, Position, BackTrackingPosition, OldBackTrackingPosition; 27 USHORT BackTrackingBuffer[16], OldBackTrackingBuffer[16] = {0}; 28 PUSHORT BackTrackingSwap, BackTracking = BackTrackingBuffer, OldBackTracking = OldBackTrackingBuffer; 29 UNICODE_STRING IntExpression; 30 USHORT ExpressionPosition, NamePosition = 0, MatchingChars = 1; 31 BOOLEAN EndOfName = FALSE; 32 BOOLEAN Result; 33 BOOLEAN DontSkipDot; 34 WCHAR CompareChar; 35 PAGED_CODE(); 36 37 /* Check if we were given strings at all */ 38 if (!Name->Length || !Expression->Length) 39 { 40 /* Return TRUE if both strings are empty, otherwise FALSE */ 41 if (!Name->Length && !Expression->Length) 42 return TRUE; 43 else 44 return FALSE; 45 } 46 47 /* Check for a shortcut: just one wildcard */ 48 if (Expression->Length == sizeof(WCHAR)) 49 { 50 if (Expression->Buffer[0] == L'*') 51 return TRUE; 52 } 53 54 ASSERT(!IgnoreCase || UpcaseTable); 55 56 /* Another shortcut, wildcard followed by some string */ 57 if (Expression->Buffer[0] == L'*') 58 { 59 /* Copy Expression to our local variable */ 60 IntExpression = *Expression; 61 62 /* Skip the first char */ 63 IntExpression.Buffer++; 64 IntExpression.Length -= sizeof(WCHAR); 65 66 /* Continue only if the rest of the expression does NOT contain 67 any more wildcards */ 68 if (!FsRtlDoesNameContainWildCards(&IntExpression)) 69 { 70 /* Check for a degenerate case */ 71 if (Name->Length < (Expression->Length - sizeof(WCHAR))) 72 return FALSE; 73 74 /* Calculate position */ 75 NamePosition = (Name->Length - IntExpression.Length) / sizeof(WCHAR); 76 77 /* Compare */ 78 if (!IgnoreCase) 79 { 80 /* We can just do a byte compare */ 81 return RtlEqualMemory(IntExpression.Buffer, 82 Name->Buffer + NamePosition, 83 IntExpression.Length); 84 } 85 else 86 { 87 /* Not so easy, need to upcase and check char by char */ 88 for (ExpressionPosition = 0; ExpressionPosition < (IntExpression.Length / sizeof(WCHAR)); ExpressionPosition++) 89 { 90 /* Assert that expression is already upcased! */ 91 ASSERT(IntExpression.Buffer[ExpressionPosition] == UpcaseTable[IntExpression.Buffer[ExpressionPosition]]); 92 93 /* Now compare upcased name char with expression */ 94 if (UpcaseTable[Name->Buffer[NamePosition + ExpressionPosition]] != 95 IntExpression.Buffer[ExpressionPosition]) 96 { 97 return FALSE; 98 } 99 } 100 101 /* It matches */ 102 return TRUE; 103 } 104 } 105 } 106 107 /* Name parsing loop */ 108 for (; !EndOfName; MatchingChars = BackTrackingPosition, NamePosition++) 109 { 110 /* Reset positions */ 111 OldBackTrackingPosition = BackTrackingPosition = 0; 112 113 if (NamePosition >= Name->Length / sizeof(WCHAR)) 114 { 115 EndOfName = TRUE; 116 if (MatchingChars && (OldBackTracking[MatchingChars - 1] == Expression->Length * 2)) 117 break; 118 } 119 120 while (MatchingChars > OldBackTrackingPosition) 121 { 122 ExpressionPosition = (OldBackTracking[OldBackTrackingPosition++] + 1) / 2; 123 124 /* Expression parsing loop */ 125 for (Offset = 0; ExpressionPosition < Expression->Length; Offset = sizeof(WCHAR)) 126 { 127 ExpressionPosition += Offset; 128 129 if (ExpressionPosition == Expression->Length) 130 { 131 BackTracking[BackTrackingPosition++] = Expression->Length * 2; 132 break; 133 } 134 135 /* If buffer too small */ 136 if (BackTrackingPosition > RTL_NUMBER_OF(BackTrackingBuffer) - 1) 137 { 138 /* Allocate memory for BackTracking */ 139 BackTracking = ExAllocatePoolWithTag(PagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE, 140 (Expression->Length + sizeof(WCHAR)) * sizeof(USHORT), 141 'nrSF'); 142 /* Copy old buffer content */ 143 RtlCopyMemory(BackTracking, 144 BackTrackingBuffer, 145 RTL_NUMBER_OF(BackTrackingBuffer) * sizeof(USHORT)); 146 147 /* Allocate memory for OldBackTracking */ 148 OldBackTracking = ExAllocatePoolWithTag(PagedPool | POOL_RAISE_IF_ALLOCATION_FAILURE, 149 (Expression->Length + sizeof(WCHAR)) * sizeof(USHORT), 150 'nrSF'); 151 /* Copy old buffer content */ 152 RtlCopyMemory(OldBackTracking, 153 OldBackTrackingBuffer, 154 RTL_NUMBER_OF(OldBackTrackingBuffer) * sizeof(USHORT)); 155 } 156 157 /* Basic check to test if chars are equal */ 158 CompareChar = (NamePosition >= Name->Length / sizeof(WCHAR)) ? UNICODE_NULL : (IgnoreCase ? UpcaseTable[Name->Buffer[NamePosition]] : 159 Name->Buffer[NamePosition]); 160 if (Expression->Buffer[ExpressionPosition / sizeof(WCHAR)] == CompareChar && !EndOfName) 161 { 162 BackTracking[BackTrackingPosition++] = (ExpressionPosition + sizeof(WCHAR)) * 2; 163 } 164 /* Check cases that eat one char */ 165 else if (Expression->Buffer[ExpressionPosition / sizeof(WCHAR)] == L'?' && !EndOfName) 166 { 167 BackTracking[BackTrackingPosition++] = (ExpressionPosition + sizeof(WCHAR)) * 2; 168 } 169 /* Test star */ 170 else if (Expression->Buffer[ExpressionPosition / sizeof(WCHAR)] == L'*') 171 { 172 BackTracking[BackTrackingPosition++] = ExpressionPosition * 2; 173 BackTracking[BackTrackingPosition++] = (ExpressionPosition * 2) + 3; 174 continue; 175 } 176 /* Check DOS_STAR */ 177 else if (Expression->Buffer[ExpressionPosition / sizeof(WCHAR)] == DOS_STAR) 178 { 179 /* Look for last dot */ 180 DontSkipDot = TRUE; 181 if (!EndOfName && Name->Buffer[NamePosition] == '.') 182 { 183 for (Position = NamePosition - 1; Position < Name->Length; Position++) 184 { 185 if (Name->Buffer[Position] == L'.') 186 { 187 DontSkipDot = FALSE; 188 break; 189 } 190 } 191 } 192 193 if (EndOfName || Name->Buffer[NamePosition] != L'.' || !DontSkipDot) 194 BackTracking[BackTrackingPosition++] = ExpressionPosition * 2; 195 196 BackTracking[BackTrackingPosition++] = (ExpressionPosition * 2) + 3; 197 continue; 198 } 199 /* Check DOS_DOT */ 200 else if (Expression->Buffer[ExpressionPosition / sizeof(WCHAR)] == DOS_DOT) 201 { 202 if (EndOfName) continue; 203 204 if (Name->Buffer[NamePosition] == L'.') 205 BackTracking[BackTrackingPosition++] = (ExpressionPosition + sizeof(WCHAR)) * 2; 206 } 207 /* Check DOS_QM */ 208 else if (Expression->Buffer[ExpressionPosition / sizeof(WCHAR)] == DOS_QM) 209 { 210 if (EndOfName || Name->Buffer[NamePosition] == L'.') continue; 211 212 BackTracking[BackTrackingPosition++] = (ExpressionPosition + sizeof(WCHAR)) * 2; 213 } 214 215 /* Leave from loop */ 216 break; 217 } 218 219 for (Position = 0; MatchingChars > OldBackTrackingPosition && Position < BackTrackingPosition; Position++) 220 { 221 while (MatchingChars > OldBackTrackingPosition && 222 BackTracking[Position] > OldBackTracking[OldBackTrackingPosition]) 223 { 224 ++OldBackTrackingPosition; 225 } 226 } 227 } 228 229 /* Swap pointers */ 230 BackTrackingSwap = BackTracking; 231 BackTracking = OldBackTracking; 232 OldBackTracking = BackTrackingSwap; 233 } 234 235 /* Store result value */ 236 Result = MatchingChars > 0 && (OldBackTracking[MatchingChars - 1] == (Expression->Length * 2)); 237 238 /* Frees the memory if necessary */ 239 if (BackTracking != BackTrackingBuffer && BackTracking != OldBackTrackingBuffer) 240 ExFreePoolWithTag(BackTracking, 'nrSF'); 241 if (OldBackTracking != BackTrackingBuffer && OldBackTracking != OldBackTrackingBuffer) 242 ExFreePoolWithTag(OldBackTracking, 'nrSF'); 243 244 return Result; 245 } 246 247 /* PUBLIC FUNCTIONS **********************************************************/ 248 249 /*++ 250 * @name FsRtlAreNamesEqual 251 * @implemented 252 * 253 * Compare two strings to check if they match 254 * 255 * @param Name1 256 * First unicode string to compare 257 * 258 * @param Name2 259 * Second unicode string to compare 260 * 261 * @param IgnoreCase 262 * If TRUE, Case will be ignored when comparing strings 263 * 264 * @param UpcaseTable 265 * Table for upcase letters. If NULL is given, system one will be used 266 * 267 * @return TRUE if the strings are equal 268 * 269 * @remarks From Bo Branten's ntifs.h v25. 270 * 271 *--*/ 272 BOOLEAN 273 NTAPI 274 FsRtlAreNamesEqual(IN PCUNICODE_STRING Name1, 275 IN PCUNICODE_STRING Name2, 276 IN BOOLEAN IgnoreCase, 277 IN PCWCH UpcaseTable OPTIONAL) 278 { 279 UNICODE_STRING UpcaseName1; 280 UNICODE_STRING UpcaseName2; 281 BOOLEAN StringsAreEqual, MemoryAllocated = FALSE; 282 USHORT i; 283 NTSTATUS Status; 284 PAGED_CODE(); 285 286 /* Well, first check their size */ 287 if (Name1->Length != Name2->Length) return FALSE; 288 289 /* Check if the caller didn't give an upcase table */ 290 if ((IgnoreCase) && !(UpcaseTable)) 291 { 292 /* Upcase the string ourselves */ 293 Status = RtlUpcaseUnicodeString(&UpcaseName1, Name1, TRUE); 294 if (!NT_SUCCESS(Status)) RtlRaiseStatus(Status); 295 296 /* Upcase the second string too */ 297 Status = RtlUpcaseUnicodeString(&UpcaseName2, Name2, TRUE); 298 if (!NT_SUCCESS(Status)) 299 { 300 RtlFreeUnicodeString(&UpcaseName1); 301 RtlRaiseStatus(Status); 302 } 303 304 Name1 = &UpcaseName1; 305 Name2 = &UpcaseName2; 306 307 /* Make sure we go through the path below, but free the strings */ 308 IgnoreCase = FALSE; 309 MemoryAllocated = TRUE; 310 } 311 312 /* Do a case-sensitive search */ 313 if (!IgnoreCase) 314 { 315 /* Use a raw memory compare */ 316 StringsAreEqual = RtlEqualMemory(Name1->Buffer, 317 Name2->Buffer, 318 Name1->Length); 319 320 /* Check if we allocated strings */ 321 if (MemoryAllocated) 322 { 323 /* Free them */ 324 RtlFreeUnicodeString(&UpcaseName1); 325 RtlFreeUnicodeString(&UpcaseName2); 326 } 327 328 /* Return the equality */ 329 return StringsAreEqual; 330 } 331 else 332 { 333 /* Case in-sensitive search */ 334 for (i = 0; i < Name1->Length / sizeof(WCHAR); i++) 335 { 336 /* Check if the character matches */ 337 if (UpcaseTable[Name1->Buffer[i]] != UpcaseTable[Name2->Buffer[i]]) 338 { 339 /* Non-match found! */ 340 return FALSE; 341 } 342 } 343 344 /* We finished the loop so we are equal */ 345 return TRUE; 346 } 347 } 348 349 /*++ 350 * @name FsRtlDissectName 351 * @implemented 352 * 353 * Dissects a given path name into first and remaining part. 354 * 355 * @param Name 356 * Unicode string to dissect. 357 * 358 * @param FirstPart 359 * Pointer to user supplied UNICODE_STRING, that will later point 360 * to the first part of the original name. 361 * 362 * @param RemainingPart 363 * Pointer to user supplied UNICODE_STRING, that will later point 364 * to the remaining part of the original name. 365 * 366 * @return None 367 * 368 * @remarks Example: 369 * Name: \test1\test2\test3 370 * FirstPart: test1 371 * RemainingPart: test2\test3 372 * 373 *--*/ 374 VOID 375 NTAPI 376 FsRtlDissectName(IN UNICODE_STRING Name, 377 OUT PUNICODE_STRING FirstPart, 378 OUT PUNICODE_STRING RemainingPart) 379 { 380 USHORT FirstPosition, i; 381 USHORT SkipFirstSlash = 0; 382 PAGED_CODE(); 383 384 /* Zero the strings before continuing */ 385 RtlZeroMemory(FirstPart, sizeof(UNICODE_STRING)); 386 RtlZeroMemory(RemainingPart, sizeof(UNICODE_STRING)); 387 388 /* Just quit if the string is empty */ 389 if (!Name.Length) return; 390 391 /* Find first backslash */ 392 FirstPosition = Name.Length / sizeof(WCHAR) ; 393 for (i = 0; i < Name.Length / sizeof(WCHAR); i++) 394 { 395 /* If we found one... */ 396 if (Name.Buffer[i] == L'\\') 397 { 398 /* If it begins string, just notice it and continue */ 399 if (i == 0) 400 { 401 SkipFirstSlash = 1; 402 } 403 else 404 { 405 /* Else, save its position and break out of the loop */ 406 FirstPosition = i; 407 break; 408 } 409 } 410 } 411 412 /* Set up the first result string */ 413 FirstPart->Buffer = Name.Buffer + SkipFirstSlash; 414 FirstPart->Length = (FirstPosition - SkipFirstSlash) * sizeof(WCHAR); 415 FirstPart->MaximumLength = FirstPart->Length; 416 417 /* And second one, if necessary */ 418 if (FirstPosition < (Name.Length / sizeof(WCHAR))) 419 { 420 RemainingPart->Buffer = Name.Buffer + FirstPosition + 1; 421 RemainingPart->Length = Name.Length - (FirstPosition + 1) * sizeof(WCHAR); 422 RemainingPart->MaximumLength = RemainingPart->Length; 423 } 424 } 425 426 /*++ 427 * @name FsRtlDoesNameContainWildCards 428 * @implemented 429 * 430 * Checks if the given string contains WildCards 431 * 432 * @param Name 433 * Pointer to a UNICODE_STRING containing Name to examine 434 * 435 * @return TRUE if Name contains wildcards, FALSE otherwise 436 * 437 * @remarks From Bo Branten's ntifs.h v12. 438 * 439 *--*/ 440 BOOLEAN 441 NTAPI 442 FsRtlDoesNameContainWildCards(IN PUNICODE_STRING Name) 443 { 444 PWCHAR Ptr; 445 PAGED_CODE(); 446 447 /* Loop through every character */ 448 if (Name->Length) 449 { 450 Ptr = Name->Buffer + (Name->Length / sizeof(WCHAR)) - 1; 451 while ((Ptr >= Name->Buffer) && (*Ptr != L'\\')) 452 { 453 /* Check for Wildcard */ 454 if (FsRtlIsUnicodeCharacterWild(*Ptr)) return TRUE; 455 Ptr--; 456 } 457 } 458 459 /* Nothing Found */ 460 return FALSE; 461 } 462 463 /*++ 464 * @name FsRtlIsNameInExpression 465 * @implemented 466 * 467 * Check if the Name string is in the Expression string. 468 * 469 * @param Expression 470 * The string in which we've to find Name. It can contain wildcards. 471 * If IgnoreCase is set to TRUE, this string MUST BE uppercase. 472 * 473 * @param Name 474 * The string to find. It cannot contain wildcards 475 * 476 * @param IgnoreCase 477 * If set to TRUE, case will be ignore with upcasing both strings 478 * 479 * @param UpcaseTable 480 * If not NULL, and if IgnoreCase is set to TRUE, it will be used to 481 * upcase the both strings 482 * 483 * @return TRUE if Name is in Expression, FALSE otherwise 484 * 485 * @remarks From Bo Branten's ntifs.h v12. This function should be 486 * rewritten to avoid recursion and better wildcard handling 487 * should be implemented (see FsRtlDoesNameContainWildCards). 488 * 489 *--*/ 490 BOOLEAN 491 NTAPI 492 FsRtlIsNameInExpression(IN PUNICODE_STRING Expression, 493 IN PUNICODE_STRING Name, 494 IN BOOLEAN IgnoreCase, 495 IN PWCHAR UpcaseTable OPTIONAL) 496 { 497 BOOLEAN Result; 498 NTSTATUS Status; 499 UNICODE_STRING IntName; 500 501 if (IgnoreCase && !UpcaseTable) 502 { 503 Status = RtlUpcaseUnicodeString(&IntName, Name, TRUE); 504 if (!NT_SUCCESS(Status)) 505 { 506 ExRaiseStatus(Status); 507 } 508 Name = &IntName; 509 IgnoreCase = FALSE; 510 } 511 else 512 { 513 IntName.Buffer = NULL; 514 } 515 516 Result = FsRtlIsNameInExpressionPrivate(Expression, Name, IgnoreCase, UpcaseTable); 517 518 if (IntName.Buffer != NULL) 519 { 520 RtlFreeUnicodeString(&IntName); 521 } 522 523 return Result; 524 } 525