xref: /reactos/ntoskrnl/ex/atom.c (revision 3bc2d590)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * COPYRIGHT:       GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/ex/atom.c
5  * PURPOSE:         Executive Atom Functions
6  * PROGRAMMERS:     Alex Ionescu (alex@relsoft.net)
7  *                  Gunnar Dalsnes
8  */
9 
10 /* INCLUDES *****************************************************************/
11 
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 /* GLOBALS ****************************************************************/
17 
18 /*
19  * FIXME: this is WRONG! The global atom table should live in the WinSta struct
20  * and accessed through a win32k callout (received in PsEstablishWin32Callouts)
21  * NOTE: There is a session/win32k global atom table also, but its private to
22  * win32k. Its used for RegisterWindowMessage() and for window classes.
23  * -Gunnar
24  */
25 PRTL_ATOM_TABLE GlobalAtomTable;
26 
27 /* PRIVATE FUNCTIONS *********************************************************/
28 
29 /*++
30  * @name ExpGetGlobalAtomTable
31  *
32  * Gets pointer to a global atom table, creates it if not already created
33  *
34  * @return Pointer to the RTL_ATOM_TABLE, or NULL if it's impossible
35  *         to create atom table
36  *
37  * @remarks Internal function
38  *
39  *--*/
40 PRTL_ATOM_TABLE
41 NTAPI
ExpGetGlobalAtomTable(VOID)42 ExpGetGlobalAtomTable(VOID)
43 {
44     NTSTATUS Status;
45 
46     /* Return it if we have one */
47     if (GlobalAtomTable) return GlobalAtomTable;
48 
49     /* Create it */
50     Status = RtlCreateAtomTable(37, &GlobalAtomTable);
51 
52     /* If we couldn't create it, return NULL */
53     if (!NT_SUCCESS(Status)) return NULL;
54 
55     /* Return the newly created one */
56     return GlobalAtomTable;
57 }
58 
59 /* FUNCTIONS ****************************************************************/
60 
61 /*++
62  * @name NtAddAtom
63  * @implemented
64  *
65  * Function NtAddAtom creates new Atom in Global Atom Table. If Atom
66  * with the same name already exist, internal Atom counter is incremented.
67  * See: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Atoms/NtAddAtom.html
68  *
69  * @param AtomName
70  *        Atom name in Unicode
71  *
72  * @param AtomNameLength
73  *        Length of the atom name
74  *
75  * @param Atom
76  *        Pointer to RTL_ATOM
77  *
78  * @return STATUS_SUCCESS in case of success, proper error code
79  *         otherwise.
80  *
81  * @remarks None
82  *
83  *--*/
84 NTSTATUS
85 NTAPI
NtAddAtom(IN PWSTR AtomName,IN ULONG AtomNameLength,OUT PRTL_ATOM Atom)86 NtAddAtom(IN PWSTR AtomName,
87           IN ULONG AtomNameLength,
88           OUT PRTL_ATOM Atom)
89 {
90     PRTL_ATOM_TABLE AtomTable = ExpGetGlobalAtomTable();
91     NTSTATUS Status;
92     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
93     LPWSTR CapturedName;
94     ULONG CapturedSize;
95     RTL_ATOM SafeAtom;
96     PAGED_CODE();
97 
98     /* Check for the table */
99     if (AtomTable == NULL) return STATUS_ACCESS_DENIED;
100 
101     /* Check for valid name */
102     if (AtomNameLength > (RTL_MAXIMUM_ATOM_LENGTH * sizeof(WCHAR)))
103     {
104         /* Fail */
105         DPRINT1("Atom name too long\n");
106         return STATUS_INVALID_PARAMETER;
107     }
108 
109     /* Check if we're called from user-mode or kernel-mode */
110     if (PreviousMode == KernelMode)
111     {
112         /* Re-use the given name if kernel mode */
113         CapturedName = AtomName;
114     }
115     else
116     {
117         /* Check if we have a name */
118         if (AtomName)
119         {
120             /* Allocate an aligned buffer + the null char */
121             CapturedSize = ((AtomNameLength + sizeof(WCHAR)) &
122                             ~(sizeof(WCHAR) -1));
123             CapturedName = ExAllocatePoolWithTag(PagedPool,
124                                                  CapturedSize,
125                                                  TAG_ATOM);
126 
127             if (!CapturedName)
128             {
129                 /* Fail the call */
130                 return STATUS_INSUFFICIENT_RESOURCES;
131             }
132 
133             /* Enter SEH */
134             _SEH2_TRY
135             {
136                 /* Probe the atom */
137                 ProbeForRead(AtomName, AtomNameLength, sizeof(WCHAR));
138 
139                 /* Copy the name and null-terminate it */
140                 RtlCopyMemory(CapturedName, AtomName, AtomNameLength);
141                 CapturedName[AtomNameLength / sizeof(WCHAR)] = UNICODE_NULL;
142 
143                 /* Probe the atom too */
144                 if (Atom) ProbeForWriteUshort(Atom);
145             }
146             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
147             {
148                 /* Return the exception code */
149                 _SEH2_YIELD(return _SEH2_GetExceptionCode());
150             }
151             _SEH2_END;
152         }
153         else
154         {
155             /* No name */
156             CapturedName = NULL;
157         }
158     }
159 
160     /* Call the runtime function */
161     Status = RtlAddAtomToAtomTable(AtomTable, CapturedName, &SafeAtom);
162     if (NT_SUCCESS(Status) && (Atom))
163     {
164         /* Success and caller wants the atom back.. .enter SEH */
165         _SEH2_TRY
166         {
167             /* Return the atom */
168             *Atom = SafeAtom;
169         }
170         _SEH2_EXCEPT(ExSystemExceptionFilter())
171         {
172             /* Get the exception code */
173             Status = _SEH2_GetExceptionCode();
174         }
175         _SEH2_END;
176     }
177 
178     /* If we captured anything, free it */
179     if ((CapturedName != NULL) && (CapturedName != AtomName))
180         ExFreePoolWithTag(CapturedName, TAG_ATOM);
181 
182     /* Return to caller */
183     return Status;
184 }
185 
186 /*++
187  * @name NtDeleteAtom
188  * @implemented
189  *
190  * Removes Atom from Global Atom Table. If Atom's reference counter
191  * is greater then 1, function decrements this counter, but Atom
192  * stayed in Global Atom Table.
193  * See: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Atoms/NtDeleteAtom.html
194  *
195  * @param Atom
196  *        Atom identifier
197  *
198  * @return STATUS_SUCCESS in case of success, proper error code
199  *         otherwise.
200  *
201  * @remarks None
202  *
203  *--*/
204 NTSTATUS
205 NTAPI
NtDeleteAtom(IN RTL_ATOM Atom)206 NtDeleteAtom(IN RTL_ATOM Atom)
207 {
208     PRTL_ATOM_TABLE AtomTable = ExpGetGlobalAtomTable();
209     PAGED_CODE();
210 
211     /* Check for valid table */
212     if (AtomTable == NULL) return STATUS_ACCESS_DENIED;
213 
214     /* Call worker function */
215     return RtlDeleteAtomFromAtomTable(AtomTable, Atom);
216 }
217 
218 /*++
219  * @name NtFindAtom
220  * @implemented
221  *
222  * Retrieves existing Atom's identifier without incrementing Atom's
223  * internal counter
224  * See: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Atoms/NtFindAtom.html
225  *
226  * @param AtomName
227  *        Atom name in Unicode
228  *
229  * @param AtomNameLength
230  *        Length of the atom name
231  *
232  * @param Atom
233  *        Pointer to RTL_ATOM
234  *
235  * @return STATUS_SUCCESS in case of success, proper error code
236  *         otherwise.
237  *
238  * @remarks None
239  *
240  *--*/
241 NTSTATUS
242 NTAPI
NtFindAtom(IN PWSTR AtomName,IN ULONG AtomNameLength,OUT PRTL_ATOM Atom)243 NtFindAtom(IN PWSTR AtomName,
244            IN ULONG AtomNameLength,
245            OUT PRTL_ATOM Atom)
246 {
247     PRTL_ATOM_TABLE AtomTable = ExpGetGlobalAtomTable();
248     NTSTATUS Status;
249     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
250     _SEH2_VOLATILE LPWSTR CapturedName;
251     ULONG CapturedSize;
252     RTL_ATOM SafeAtom;
253     PAGED_CODE();
254 
255     /* Check for the table */
256     if (AtomTable == NULL) return STATUS_ACCESS_DENIED;
257 
258     /* Check for valid name */
259     if (AtomNameLength > (RTL_MAXIMUM_ATOM_LENGTH * sizeof(WCHAR)))
260     {
261         /* Fail */
262         DPRINT1("Atom name too long\n");
263         return STATUS_INVALID_PARAMETER;
264     }
265 
266     /* Re-use the given name if kernel mode or no atom name */
267     CapturedName = AtomName;
268 
269     /* Check if we're called from user-mode*/
270     if (PreviousMode != KernelMode)
271     {
272         /* Enter SEH */
273         _SEH2_TRY
274         {
275             /* Check if we have a name */
276             if (AtomName)
277             {
278                 /* Probe the atom */
279                 ProbeForRead(AtomName, AtomNameLength, sizeof(WCHAR));
280 
281                 /* Allocate an aligned buffer + the null char */
282                 CapturedSize = ((AtomNameLength + sizeof(WCHAR)) &~
283                                 (sizeof(WCHAR) -1));
284                 CapturedName = ExAllocatePoolWithQuotaTag(PagedPool,
285                                                           CapturedSize,
286                                                           TAG_ATOM);
287                 /* Copy the name and null-terminate it */
288                 RtlCopyMemory(CapturedName, AtomName, AtomNameLength);
289                 CapturedName[AtomNameLength / sizeof(WCHAR)] = UNICODE_NULL;
290 
291                 /* Probe the atom too */
292                 if (Atom) ProbeForWriteUshort(Atom);
293             }
294         }
295         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
296         {
297             if (CapturedName != AtomName)
298             {
299                 ExFreePoolWithTag(CapturedName, TAG_ATOM);
300             }
301 
302             /* Return the exception code */
303             _SEH2_YIELD(return _SEH2_GetExceptionCode());
304         }
305         _SEH2_END;
306     }
307 
308     /* Call the runtime function */
309     Status = RtlLookupAtomInAtomTable(AtomTable, CapturedName, &SafeAtom);
310     if (NT_SUCCESS(Status) && (Atom))
311     {
312         /* Success and caller wants the atom back... enter SEH */
313         _SEH2_TRY
314         {
315             /* Return the atom */
316             *Atom = SafeAtom;
317         }
318         _SEH2_EXCEPT(ExSystemExceptionFilter())
319         {
320             Status = _SEH2_GetExceptionCode();
321         }
322         _SEH2_END;
323     }
324 
325     /* If we captured anything, free it */
326     if ((CapturedName) && (CapturedName != AtomName))
327         ExFreePoolWithTag(CapturedName, TAG_ATOM);
328 
329     /* Return to caller */
330     return Status;
331 }
332 
333 /*++
334  * @name NtQueryInformationAtom
335  * @implemented
336  *
337  * Gets single Atom properties or reads Global Atom Table
338  * See: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Atoms/NtQueryInformationAtom.html
339  *
340  * @param Atom
341  *        Atom to query. If AtomInformationClass parameter is
342  *        AtomTableInformation, Atom parameter is not used.
343  *
344  * @param AtomInformationClass
345  *        See ATOM_INFORMATION_CLASS enumeration type for details
346  *
347  * @param AtomInformation
348  *        Result of call - pointer to user's allocated buffer for data
349  *
350  * @param AtomInformationLength
351  *        Size of AtomInformation buffer, in bytes
352  *
353  * @param ReturnLength
354  *        Pointer to ULONG value containing required AtomInformation
355  *        buffer size
356  *
357  * @return STATUS_SUCCESS in case of success, proper error code
358  *         otherwise.
359  *
360  * @remarks None
361  *
362  *--*/
363 NTSTATUS
364 NTAPI
NtQueryInformationAtom(RTL_ATOM Atom,ATOM_INFORMATION_CLASS AtomInformationClass,PVOID AtomInformation,ULONG AtomInformationLength,PULONG ReturnLength)365 NtQueryInformationAtom(RTL_ATOM Atom,
366                        ATOM_INFORMATION_CLASS AtomInformationClass,
367                        PVOID AtomInformation,
368                        ULONG AtomInformationLength,
369                        PULONG ReturnLength)
370 {
371     PRTL_ATOM_TABLE AtomTable = ExpGetGlobalAtomTable();
372     PATOM_BASIC_INFORMATION BasicInformation = AtomInformation;
373     PATOM_TABLE_INFORMATION TableInformation = AtomInformation;
374     NTSTATUS Status = STATUS_SUCCESS;
375     ULONG Flags, UsageCount, NameLength, RequiredLength = 0;
376     KPROCESSOR_MODE PreviousMode;
377 
378     PAGED_CODE();
379 
380     /* Check for valid table */
381     if (AtomTable == NULL) return STATUS_ACCESS_DENIED;
382 
383     PreviousMode = ExGetPreviousMode();
384 
385     _SEH2_TRY
386     {
387         /* Probe the parameters */
388         if (PreviousMode != KernelMode)
389         {
390             ProbeForWrite(AtomInformation,
391                           AtomInformationLength,
392                           sizeof(ULONG));
393 
394             if (ReturnLength != NULL)
395             {
396                 ProbeForWriteUlong(ReturnLength);
397             }
398         }
399 
400         /* Choose class */
401         switch (AtomInformationClass)
402         {
403             /* Caller requested info about an atom */
404             case AtomBasicInformation:
405 
406                 /* Size check */
407                 RequiredLength = FIELD_OFFSET(ATOM_BASIC_INFORMATION, Name);
408                 if (RequiredLength > AtomInformationLength)
409                 {
410                     /* Fail */
411                     DPRINT1("Buffer too small\n");
412                     Status = STATUS_INFO_LENGTH_MISMATCH;
413                     _SEH2_LEAVE;
414                 }
415 
416                 /* Prepare query */
417                 UsageCount = 0;
418                 NameLength = AtomInformationLength - RequiredLength;
419                 BasicInformation->Name[0] = UNICODE_NULL;
420 
421                 /* Query the data */
422                 Status = RtlQueryAtomInAtomTable(AtomTable,
423                                                  Atom,
424                                                  &UsageCount,
425                                                  &Flags,
426                                                  BasicInformation->Name,
427                                                  &NameLength);
428                 if (NT_SUCCESS(Status))
429                 {
430                     /* Return data */
431                     BasicInformation->UsageCount = (USHORT)UsageCount;
432                     BasicInformation->Flags = (USHORT)Flags;
433                     BasicInformation->NameLength = (USHORT)NameLength;
434                     RequiredLength += NameLength + sizeof(WCHAR);
435                 }
436                 break;
437 
438             /* Caller requested info about an Atom Table */
439             case AtomTableInformation:
440 
441                 /* Size check */
442                 RequiredLength = FIELD_OFFSET(ATOM_TABLE_INFORMATION, Atoms);
443                 if (RequiredLength > AtomInformationLength)
444                 {
445                     /* Fail */
446                     DPRINT1("Buffer too small\n");
447                     Status = STATUS_INFO_LENGTH_MISMATCH;
448                     _SEH2_LEAVE;
449                 }
450 
451                 /* Query the data */
452                 Status = RtlQueryAtomListInAtomTable(AtomTable,
453                                                      (AtomInformationLength - RequiredLength) /
454                                                      sizeof(RTL_ATOM),
455                                                      &TableInformation->NumberOfAtoms,
456                                                      TableInformation->Atoms);
457                 if (NT_SUCCESS(Status))
458                 {
459                     /* Update the return length */
460                     RequiredLength += TableInformation->NumberOfAtoms * sizeof(RTL_ATOM);
461                 }
462                 break;
463 
464             /* Caller was on crack */
465             default:
466 
467                 /* Unrecognized class */
468                 Status = STATUS_INVALID_INFO_CLASS;
469                 break;
470         }
471 
472         /* Return the required size */
473         if (ReturnLength != NULL)
474         {
475             *ReturnLength = RequiredLength;
476         }
477     }
478     _SEH2_EXCEPT(ExSystemExceptionFilter())
479     {
480         Status = _SEH2_GetExceptionCode();
481     }
482     _SEH2_END;
483 
484     /* Return to caller */
485     return Status;
486 }
487 
488 /* EOF */
489