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