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