xref: /reactos/sdk/lib/rtl/atom.c (revision 6db0d24f)
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
RtlpHashAtomName(IN PRTL_ATOM_TABLE AtomTable,IN PWSTR AtomName,OUT PRTL_ATOM_TABLE_ENTRY ** HashLink)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
RtlpCheckIntegerAtom(PWSTR AtomName,PUSHORT AtomValue)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
RtlCreateAtomTable(IN ULONG TableSize,IN OUT PRTL_ATOM_TABLE * AtomTable)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
RtlDestroyAtomTable(IN PRTL_ATOM_TABLE AtomTable)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
RtlEmptyAtomTable(PRTL_ATOM_TABLE AtomTable,BOOLEAN DeletePinned)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
RtlAddAtomToAtomTable(IN PRTL_ATOM_TABLE AtomTable,IN PWSTR AtomName,OUT PRTL_ATOM Atom)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
RtlDeleteAtomFromAtomTable(IN PRTL_ATOM_TABLE AtomTable,IN RTL_ATOM Atom)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
RtlLookupAtomInAtomTable(IN PRTL_ATOM_TABLE AtomTable,IN PWSTR AtomName,OUT PRTL_ATOM Atom)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
RtlPinAtomInAtomTable(IN PRTL_ATOM_TABLE AtomTable,IN RTL_ATOM Atom)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
RtlQueryAtomInAtomTable(PRTL_ATOM_TABLE AtomTable,RTL_ATOM Atom,PULONG RefCount,PULONG PinCount,PWSTR AtomName,PULONG NameLength)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
RtlQueryAtomListInAtomTable(IN PRTL_ATOM_TABLE AtomTable,IN ULONG MaxAtomCount,OUT ULONG * AtomCount,OUT RTL_ATOM * AtomList)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