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