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