1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS system libraries 4 * FILE: lib/rtl/atom.c 5 * PURPOSE: Atom management 6 * PROGRAMMER: Thomas Weidenmueller 7 */ 8 9 /* INCLUDES *****************************************************************/ 10 11 #include <rtl.h> 12 13 #define NDEBUG 14 #include <debug.h> 15 16 /* PROTOTYPES ****************************************************************/ 17 18 extern NTSTATUS RtlpInitAtomTableLock(PRTL_ATOM_TABLE AtomTable); 19 extern VOID RtlpDestroyAtomTableLock(PRTL_ATOM_TABLE AtomTable); 20 extern BOOLEAN RtlpLockAtomTable(PRTL_ATOM_TABLE AtomTable); 21 extern VOID RtlpUnlockAtomTable(PRTL_ATOM_TABLE AtomTable); 22 23 extern BOOLEAN RtlpCreateAtomHandleTable(PRTL_ATOM_TABLE AtomTable); 24 extern VOID RtlpDestroyAtomHandleTable(PRTL_ATOM_TABLE AtomTable); 25 26 extern PRTL_ATOM_TABLE RtlpAllocAtomTable(ULONG Size); 27 extern VOID RtlpFreeAtomTable(PRTL_ATOM_TABLE AtomTable); 28 extern PRTL_ATOM_TABLE_ENTRY RtlpAllocAtomTableEntry(ULONG Size); 29 extern VOID RtlpFreeAtomTableEntry(PRTL_ATOM_TABLE_ENTRY Entry); 30 31 extern BOOLEAN RtlpCreateAtomHandle(PRTL_ATOM_TABLE AtomTable, PRTL_ATOM_TABLE_ENTRY Entry); 32 extern VOID RtlpFreeAtomHandle(PRTL_ATOM_TABLE AtomTable, PRTL_ATOM_TABLE_ENTRY Entry); 33 extern PRTL_ATOM_TABLE_ENTRY RtlpGetAtomEntry(PRTL_ATOM_TABLE AtomTable, ULONG Index); 34 35 /* FUNCTIONS *****************************************************************/ 36 37 static 38 PRTL_ATOM_TABLE_ENTRY 39 RtlpHashAtomName( 40 IN PRTL_ATOM_TABLE AtomTable, 41 IN PWSTR AtomName, 42 OUT PRTL_ATOM_TABLE_ENTRY **HashLink) 43 { 44 UNICODE_STRING Name; 45 ULONG Hash; 46 47 RtlInitUnicodeString(&Name, AtomName); 48 49 if (Name.Length != 0 && 50 NT_SUCCESS(RtlHashUnicodeString(&Name, 51 TRUE, 52 HASH_STRING_ALGORITHM_X65599, 53 &Hash))) 54 { 55 PRTL_ATOM_TABLE_ENTRY Current; 56 PRTL_ATOM_TABLE_ENTRY *Link; 57 58 Link = &AtomTable->Buckets[Hash % AtomTable->NumberOfBuckets]; 59 60 /* search for an existing entry */ 61 Current = *Link; 62 while (Current != NULL) 63 { 64 if (Current->NameLength == Name.Length / sizeof(WCHAR) && 65 !_wcsicmp(Current->Name, Name.Buffer)) 66 { 67 *HashLink = Link; 68 return Current; 69 } 70 71 Link = &Current->HashLink; 72 Current = Current->HashLink; 73 } 74 75 /* no matching atom found, return the hash link */ 76 *HashLink = Link; 77 } 78 else 79 *HashLink = NULL; 80 81 return NULL; 82 } 83 84 static 85 BOOLEAN 86 RtlpCheckIntegerAtom( 87 PWSTR AtomName, 88 PUSHORT AtomValue) 89 { 90 UNICODE_STRING AtomString; 91 ULONG LongValue; 92 USHORT LoValue; 93 PWCHAR p; 94 95 DPRINT("RtlpCheckIntegerAtom(AtomName '%S' AtomValue %p)\n", 96 AtomName, AtomValue); 97 98 if (!((ULONG_PTR)AtomName & 0xFFFF0000)) 99 { 100 LoValue = (USHORT)((ULONG_PTR)AtomName & 0xFFFF); 101 102 if (LoValue == 0) 103 LoValue = 0xC000; 104 105 if (AtomValue != NULL) 106 *AtomValue = LoValue; 107 108 return TRUE; 109 } 110 111 /* 112 * AtomName cannot be NULL because this 113 * case was caught by the previous test. 114 */ 115 ASSERT(AtomName != NULL); 116 117 if (*AtomName != L'#') 118 return FALSE; 119 120 p = AtomName; 121 p++; 122 while (*p) 123 { 124 if ((*p < L'0') || (*p > L'9')) 125 return FALSE; 126 p++; 127 } 128 129 p = AtomName; 130 p++; 131 RtlInitUnicodeString(&AtomString, p); 132 133 DPRINT("AtomString: %wZ\n", &AtomString); 134 135 RtlUnicodeStringToInteger(&AtomString, 10, &LongValue); 136 137 DPRINT("LongValue: %lu\n", LongValue); 138 139 *AtomValue = (USHORT)(LongValue & 0x0000FFFF); 140 141 return TRUE; 142 } 143 144 145 /* 146 * @implemented 147 */ 148 NTSTATUS 149 NTAPI 150 RtlCreateAtomTable( 151 IN ULONG TableSize, 152 IN OUT PRTL_ATOM_TABLE *AtomTable) 153 { 154 PRTL_ATOM_TABLE Table; 155 NTSTATUS Status; 156 157 DPRINT("RtlCreateAtomTable(TableSize %lu AtomTable %p)\n", 158 TableSize, AtomTable); 159 160 if (*AtomTable != NULL) 161 { 162 return STATUS_SUCCESS; 163 } 164 165 /* Use default if size was incorrect */ 166 if (TableSize <= 1) TableSize = 37; 167 168 /* allocate atom table */ 169 Table = RtlpAllocAtomTable(((TableSize - 1) * sizeof(PRTL_ATOM_TABLE_ENTRY)) + 170 sizeof(RTL_ATOM_TABLE)); 171 if (Table == NULL) 172 { 173 return STATUS_NO_MEMORY; 174 } 175 176 /* initialize atom table */ 177 Table->NumberOfBuckets = TableSize; 178 179 Status = RtlpInitAtomTableLock(Table); 180 if (!NT_SUCCESS(Status)) 181 { 182 RtlpFreeAtomTable(Table); 183 return Status; 184 } 185 186 if (!RtlpCreateAtomHandleTable(Table)) 187 { 188 RtlpDestroyAtomTableLock(Table); 189 RtlpFreeAtomTable(Table); 190 return STATUS_NO_MEMORY; 191 } 192 193 *AtomTable = Table; 194 return STATUS_SUCCESS; 195 } 196 197 198 /* 199 * @implemented 200 */ 201 NTSTATUS 202 NTAPI 203 RtlDestroyAtomTable( 204 IN PRTL_ATOM_TABLE AtomTable) 205 { 206 PRTL_ATOM_TABLE_ENTRY *CurrentBucket, *LastBucket; 207 PRTL_ATOM_TABLE_ENTRY CurrentEntry, NextEntry; 208 209 DPRINT("RtlDestroyAtomTable (AtomTable %p)\n", AtomTable); 210 211 if (!RtlpLockAtomTable(AtomTable)) 212 { 213 return (STATUS_INVALID_PARAMETER); 214 } 215 216 /* delete all atoms */ 217 LastBucket = AtomTable->Buckets + AtomTable->NumberOfBuckets; 218 for (CurrentBucket = AtomTable->Buckets; 219 CurrentBucket != LastBucket; 220 CurrentBucket++) 221 { 222 NextEntry = *CurrentBucket; 223 *CurrentBucket = NULL; 224 225 while (NextEntry != NULL) 226 { 227 CurrentEntry = NextEntry; 228 NextEntry = NextEntry->HashLink; 229 230 /* no need to delete the atom handle, the handles will all be freed 231 up when destroying the atom handle table! */ 232 233 RtlpFreeAtomTableEntry(CurrentEntry); 234 } 235 } 236 237 RtlpDestroyAtomHandleTable(AtomTable); 238 239 RtlpUnlockAtomTable(AtomTable); 240 241 RtlpDestroyAtomTableLock(AtomTable); 242 243 RtlpFreeAtomTable(AtomTable); 244 245 return STATUS_SUCCESS; 246 } 247 248 249 /* 250 * @implemented 251 */ 252 NTSTATUS 253 NTAPI 254 RtlEmptyAtomTable( 255 PRTL_ATOM_TABLE AtomTable, 256 BOOLEAN DeletePinned) 257 { 258 PRTL_ATOM_TABLE_ENTRY *CurrentBucket, *LastBucket; 259 PRTL_ATOM_TABLE_ENTRY CurrentEntry, NextEntry, *PtrEntry; 260 261 DPRINT("RtlEmptyAtomTable (AtomTable %p DeletePinned %x)\n", 262 AtomTable, DeletePinned); 263 264 if (RtlpLockAtomTable(AtomTable) == FALSE) 265 { 266 return (STATUS_INVALID_PARAMETER); 267 } 268 269 /* delete all atoms */ 270 LastBucket = AtomTable->Buckets + AtomTable->NumberOfBuckets; 271 for (CurrentBucket = AtomTable->Buckets; 272 CurrentBucket != LastBucket; 273 CurrentBucket++) 274 { 275 NextEntry = *CurrentBucket; 276 PtrEntry = CurrentBucket; 277 278 while (NextEntry != NULL) 279 { 280 CurrentEntry = NextEntry; 281 NextEntry = NextEntry->HashLink; 282 283 if (DeletePinned || !(CurrentEntry->Flags & RTL_ATOM_IS_PINNED)) 284 { 285 *PtrEntry = NextEntry; 286 287 RtlpFreeAtomHandle(AtomTable, CurrentEntry); 288 289 RtlpFreeAtomTableEntry(CurrentEntry); 290 } 291 else 292 { 293 PtrEntry = &CurrentEntry->HashLink; 294 } 295 } 296 } 297 298 RtlpUnlockAtomTable(AtomTable); 299 300 return STATUS_SUCCESS; 301 } 302 303 304 /* 305 * @implemented 306 */ 307 NTSTATUS NTAPI 308 RtlAddAtomToAtomTable(IN PRTL_ATOM_TABLE AtomTable, 309 IN PWSTR AtomName, 310 OUT PRTL_ATOM Atom) 311 { 312 USHORT AtomValue; 313 PRTL_ATOM_TABLE_ENTRY *HashLink; 314 PRTL_ATOM_TABLE_ENTRY Entry = NULL; 315 NTSTATUS Status = STATUS_SUCCESS; 316 317 DPRINT("RtlAddAtomToAtomTable (AtomTable %p AtomName %S Atom %p)\n", 318 AtomTable, AtomName, Atom); 319 320 if (RtlpCheckIntegerAtom (AtomName, &AtomValue)) 321 { 322 /* integer atom */ 323 if (AtomValue >= 0xC000) 324 { 325 Status = STATUS_INVALID_PARAMETER; 326 } 327 else if (Atom != NULL) 328 { 329 *Atom = (RTL_ATOM)AtomValue; 330 } 331 332 return Status; 333 } 334 335 RtlpLockAtomTable(AtomTable); 336 337 /* string atom, hash it and try to find an existing atom with the same name */ 338 Entry = RtlpHashAtomName(AtomTable, AtomName, &HashLink); 339 340 if (Entry != NULL) 341 { 342 /* found another atom, increment the reference counter unless it's pinned */ 343 344 if (!(Entry->Flags & RTL_ATOM_IS_PINNED)) 345 { 346 if (++Entry->ReferenceCount == 0) 347 { 348 /* FIXME - references overflowed, pin the atom? */ 349 Entry->Flags |= RTL_ATOM_IS_PINNED; 350 } 351 } 352 353 if (Atom != NULL) 354 { 355 *Atom = (RTL_ATOM)Entry->Atom; 356 } 357 } 358 else 359 { 360 /* couldn't find an existing atom, HashLink now points to either the 361 HashLink pointer of the previous atom or to the bucket so we can 362 simply add it to the list */ 363 if (HashLink != NULL) 364 { 365 ULONG AtomNameLen = (ULONG)wcslen(AtomName); 366 367 if (AtomNameLen > RTL_MAXIMUM_ATOM_LENGTH) 368 { 369 Status = STATUS_INVALID_PARAMETER; 370 goto end; 371 } 372 373 Entry = RtlpAllocAtomTableEntry(sizeof(RTL_ATOM_TABLE_ENTRY) - 374 sizeof(Entry->Name) + 375 (AtomNameLen + 1) * sizeof(WCHAR)); 376 if (Entry != NULL) 377 { 378 Entry->HashLink = NULL; 379 Entry->ReferenceCount = 1; 380 Entry->Flags = 0x0; 381 382 Entry->NameLength = (UCHAR)AtomNameLen; 383 RtlCopyMemory(Entry->Name, 384 AtomName, 385 (AtomNameLen + 1) * sizeof(WCHAR)); 386 387 if (RtlpCreateAtomHandle(AtomTable, Entry)) 388 { 389 /* append the atom to the list */ 390 *HashLink = Entry; 391 392 if (Atom != NULL) 393 { 394 *Atom = (RTL_ATOM)Entry->Atom; 395 } 396 } 397 else 398 { 399 RtlpFreeAtomTableEntry(Entry); 400 Status = STATUS_NO_MEMORY; 401 } 402 } 403 else 404 { 405 Status = STATUS_NO_MEMORY; 406 } 407 } 408 else 409 { 410 /* The caller supplied an empty atom name! */ 411 Status = STATUS_OBJECT_NAME_INVALID; 412 } 413 } 414 end: 415 RtlpUnlockAtomTable(AtomTable); 416 417 return Status; 418 } 419 420 421 /* 422 * @implemented 423 */ 424 NTSTATUS 425 NTAPI 426 RtlDeleteAtomFromAtomTable( 427 IN PRTL_ATOM_TABLE AtomTable, 428 IN RTL_ATOM Atom) 429 { 430 PRTL_ATOM_TABLE_ENTRY Entry; 431 NTSTATUS Status = STATUS_SUCCESS; 432 433 DPRINT("RtlDeleteAtomFromAtomTable (AtomTable %p Atom %x)\n", 434 AtomTable, Atom); 435 436 if (Atom >= 0xC000) 437 { 438 RtlpLockAtomTable(AtomTable); 439 440 Entry = RtlpGetAtomEntry(AtomTable, (ULONG)((USHORT)Atom - 0xC000)); 441 442 if (Entry != NULL && Entry->Atom == (USHORT)Atom) 443 { 444 if (!(Entry->Flags & RTL_ATOM_IS_PINNED)) 445 { 446 if (--Entry->ReferenceCount == 0) 447 { 448 PRTL_ATOM_TABLE_ENTRY *HashLink; 449 450 /* it's time to delete the atom. we need to unlink it from 451 the list. The easiest way is to take the atom name and 452 hash it again, this way we get the pointer to either 453 the hash bucket or the previous atom that links to the 454 one we want to delete. This way we can easily bypass 455 this item. */ 456 if (RtlpHashAtomName(AtomTable, Entry->Name, &HashLink) != NULL) 457 { 458 /* bypass this atom */ 459 *HashLink = Entry->HashLink; 460 461 RtlpFreeAtomHandle(AtomTable, Entry); 462 463 RtlpFreeAtomTableEntry(Entry); 464 } 465 else 466 { 467 /* WTF?! This should never happen!!! */ 468 ASSERT(FALSE); 469 } 470 } 471 } 472 else 473 { 474 /* tried to delete a pinned atom, do nothing and return 475 STATUS_WAS_LOCKED, which is NOT a failure code! */ 476 Status = STATUS_WAS_LOCKED; 477 } 478 } 479 else 480 { 481 Status = STATUS_INVALID_HANDLE; 482 } 483 484 RtlpUnlockAtomTable(AtomTable); 485 } 486 487 return Status; 488 } 489 490 491 /* 492 * @implemented 493 */ 494 NTSTATUS NTAPI 495 RtlLookupAtomInAtomTable(IN PRTL_ATOM_TABLE AtomTable, 496 IN PWSTR AtomName, 497 OUT PRTL_ATOM Atom) 498 { 499 PRTL_ATOM_TABLE_ENTRY Entry, *HashLink; 500 USHORT AtomValue; 501 RTL_ATOM FoundAtom = 0; 502 NTSTATUS Status = STATUS_SUCCESS; 503 504 DPRINT("RtlLookupAtomInAtomTable (AtomTable %p AtomName %S Atom %p)\n", 505 AtomTable, AtomName, Atom); 506 507 if (RtlpCheckIntegerAtom (AtomName, &AtomValue)) 508 { 509 /* integer atom */ 510 if (AtomValue >= 0xC000) 511 { 512 Status = STATUS_INVALID_PARAMETER; 513 } 514 else if (Atom != NULL) 515 { 516 *Atom = (RTL_ATOM)AtomValue; 517 } 518 519 return Status; 520 } 521 522 RtlpLockAtomTable(AtomTable); 523 Status = STATUS_OBJECT_NAME_NOT_FOUND; 524 525 /* string atom */ 526 Entry = RtlpHashAtomName(AtomTable, AtomName, &HashLink); 527 if (Entry != NULL) 528 { 529 Status = STATUS_SUCCESS; 530 FoundAtom = (RTL_ATOM)Entry->Atom; 531 } 532 533 RtlpUnlockAtomTable(AtomTable); 534 if (NT_SUCCESS(Status) && Atom != NULL) 535 { 536 *Atom = FoundAtom; 537 } 538 return Status; 539 } 540 541 542 /* 543 * @implemented 544 */ 545 NTSTATUS NTAPI 546 RtlPinAtomInAtomTable(IN PRTL_ATOM_TABLE AtomTable, 547 IN RTL_ATOM Atom) 548 { 549 NTSTATUS Status = STATUS_SUCCESS; 550 551 DPRINT("RtlPinAtomInAtomTable (AtomTable %p Atom %x)\n", 552 AtomTable, Atom); 553 554 if (Atom >= 0xC000) 555 { 556 PRTL_ATOM_TABLE_ENTRY Entry; 557 558 RtlpLockAtomTable(AtomTable); 559 560 Entry = RtlpGetAtomEntry(AtomTable, (ULONG)((USHORT)Atom - 0xC000)); 561 562 if (Entry != NULL && Entry->Atom == (USHORT)Atom) 563 { 564 Entry->Flags |= RTL_ATOM_IS_PINNED; 565 } 566 else 567 { 568 Status = STATUS_INVALID_HANDLE; 569 } 570 571 RtlpUnlockAtomTable(AtomTable); 572 } 573 574 return Status; 575 } 576 577 578 /* 579 * @implemented 580 * 581 * This API is really messed up with regards to NameLength. If you pass in a 582 * valid buffer for AtomName, NameLength should be the size of the buffer 583 * (in bytes, not characters). So if you expect the string to be 6 char long, 584 * you need to allocate a buffer of 7 WCHARs and pass 14 for NameLength. 585 * The AtomName returned is always null terminated. If the NameLength you pass 586 * is smaller than 4 (4 would leave room for 1 character) the function will 587 * return with status STATUS_BUFFER_TOO_SMALL. If you pass more than 4, the 588 * return status will be STATUS_SUCCESS, even if the buffer is not large enough 589 * to hold the complete string. In that case, the string is silently truncated 590 * and made to fit in the provided buffer. On return NameLength is set to the 591 * number of bytes (but EXCLUDING the bytes for the null terminator) copied. 592 * So, if the string is 6 char long, you pass a buffer of 10 bytes, on return 593 * NameLength will be set to 8. 594 * If you pass in a NULL value for AtomName, the length of the string in bytes 595 * (again EXCLUDING the null terminator) is returned in NameLength, at least 596 * on Win2k, XP and ReactOS. NT4 will return 0 in that case. 597 */ 598 NTSTATUS 599 NTAPI 600 RtlQueryAtomInAtomTable( 601 PRTL_ATOM_TABLE AtomTable, 602 RTL_ATOM Atom, 603 PULONG RefCount, 604 PULONG PinCount, 605 PWSTR AtomName, 606 PULONG NameLength) 607 { 608 ULONG Length; 609 BOOL Unlock = FALSE; 610 611 union 612 { 613 /* A RTL_ATOM_TABLE_ENTRY has a "WCHAR Name[1]" entry at the end. 614 * Make sure we reserve enough room to facilitate a 12 character name */ 615 RTL_ATOM_TABLE_ENTRY AtomTableEntry; 616 WCHAR StringBuffer[sizeof(RTL_ATOM_TABLE_ENTRY) / sizeof(WCHAR) + 12]; 617 } NumberEntry; 618 PRTL_ATOM_TABLE_ENTRY Entry; 619 NTSTATUS Status = STATUS_SUCCESS; 620 621 if (Atom < 0xC000) 622 { 623 /* Synthesize an entry */ 624 NumberEntry.AtomTableEntry.Atom = Atom; 625 NumberEntry.AtomTableEntry.NameLength = swprintf(NumberEntry.AtomTableEntry.Name, 626 L"#%lu", 627 (ULONG)Atom); 628 NumberEntry.AtomTableEntry.ReferenceCount = 1; 629 NumberEntry.AtomTableEntry.Flags = RTL_ATOM_IS_PINNED; 630 Entry = &NumberEntry.AtomTableEntry; 631 } 632 else 633 { 634 RtlpLockAtomTable(AtomTable); 635 Unlock = TRUE; 636 637 Entry = RtlpGetAtomEntry(AtomTable, (ULONG)((USHORT)Atom - 0xC000)); 638 } 639 640 if (Entry != NULL && Entry->Atom == (USHORT)Atom) 641 { 642 DPRINT("Atom name: %wZ\n", &Entry->Name); 643 644 if (RefCount != NULL) 645 { 646 *RefCount = Entry->ReferenceCount; 647 } 648 649 if (PinCount != NULL) 650 { 651 *PinCount = ((Entry->Flags & RTL_ATOM_IS_PINNED) != 0); 652 } 653 654 if (NULL != NameLength) 655 { 656 Length = Entry->NameLength * sizeof(WCHAR); 657 if (NULL != AtomName) 658 { 659 if (*NameLength < Length + sizeof(WCHAR)) 660 { 661 if (*NameLength < 4) 662 { 663 *NameLength = Length; 664 Status = STATUS_BUFFER_TOO_SMALL; 665 } 666 else 667 { 668 Length = *NameLength - sizeof(WCHAR); 669 } 670 } 671 if (NT_SUCCESS(Status)) 672 { 673 RtlCopyMemory(AtomName, Entry->Name, Length); 674 AtomName[Length / sizeof(WCHAR)] = L'\0'; 675 *NameLength = Length; 676 } 677 } 678 else 679 { 680 *NameLength = Length; 681 } 682 } 683 else if (NULL != AtomName) 684 { 685 Status = STATUS_INVALID_PARAMETER; 686 } 687 } 688 else 689 { 690 Status = STATUS_INVALID_HANDLE; 691 } 692 693 if (Unlock) RtlpUnlockAtomTable(AtomTable); 694 695 return Status; 696 } 697 698 699 /* 700 * @private - only used by NtQueryInformationAtom 701 */ 702 NTSTATUS 703 NTAPI 704 RtlQueryAtomListInAtomTable( 705 IN PRTL_ATOM_TABLE AtomTable, 706 IN ULONG MaxAtomCount, 707 OUT ULONG *AtomCount, 708 OUT RTL_ATOM *AtomList) 709 { 710 PRTL_ATOM_TABLE_ENTRY *CurrentBucket, *LastBucket; 711 PRTL_ATOM_TABLE_ENTRY CurrentEntry; 712 ULONG Atoms = 0; 713 NTSTATUS Status = STATUS_SUCCESS; 714 715 RtlpLockAtomTable(AtomTable); 716 717 LastBucket = AtomTable->Buckets + AtomTable->NumberOfBuckets; 718 for (CurrentBucket = AtomTable->Buckets; 719 CurrentBucket != LastBucket; 720 CurrentBucket++) 721 { 722 CurrentEntry = *CurrentBucket; 723 724 while (CurrentEntry != NULL) 725 { 726 if (MaxAtomCount > 0) 727 { 728 *(AtomList++) = (RTL_ATOM)CurrentEntry->Atom; 729 MaxAtomCount--; 730 } 731 else 732 { 733 /* buffer too small, but don't bail. we need to determine the 734 total number of atoms in the table! */ 735 Status = STATUS_INFO_LENGTH_MISMATCH; 736 } 737 738 Atoms++; 739 CurrentEntry = CurrentEntry->HashLink; 740 } 741 } 742 743 *AtomCount = Atoms; 744 745 RtlpUnlockAtomTable(AtomTable); 746 747 return Status; 748 } 749 750