36ca10faa322115682379306f5749c82aa272ebd
[reactos.git] / reactos / 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 = STATUS_SUCCESS;
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 /* Re-use the given name if kernel mode or no atom name */
112 CapturedName = AtomName;
113
114 /* Check if we're called from user-mode*/
115 if (PreviousMode != KernelMode)
116 {
117 /* Enter SEH */
118 _SEH2_TRY
119 {
120 /* Check if we have a name */
121 if (AtomName)
122 {
123 /* Probe the atom */
124 ProbeForRead(AtomName, AtomNameLength, sizeof(WCHAR));
125
126 /* Allocate an aligned buffer + the null char */
127 CapturedSize = ((AtomNameLength + sizeof(WCHAR)) &~
128 (sizeof(WCHAR) -1));
129 CapturedName = ExAllocatePoolWithTag(PagedPool,
130 CapturedSize,
131 TAG_ATOM);
132 if (!CapturedName)
133 {
134 /* Fail the call */
135 Status = STATUS_INSUFFICIENT_RESOURCES;
136 }
137 else
138 {
139 /* Copy the name and null-terminate it */
140 RtlCopyMemory(CapturedName, AtomName, AtomNameLength);
141 CapturedName[AtomNameLength / sizeof(WCHAR)] = UNICODE_NULL;
142 }
143
144 /* Probe the atom too */
145 if (Atom) ProbeForWriteUshort(Atom);
146 }
147 }
148 _SEH2_EXCEPT(ExSystemExceptionFilter())
149 {
150 Status = _SEH2_GetExceptionCode();
151 }
152 _SEH2_END;
153 }
154
155 /* Make sure probe worked */
156 if (NT_SUCCESS(Status))
157 {
158 /* Call the runtime function */
159 Status = RtlAddAtomToAtomTable(AtomTable, CapturedName, &SafeAtom);
160 if (NT_SUCCESS(Status) && (Atom))
161 {
162 /* Success and caller wants the atom back.. .enter SEH */
163 _SEH2_TRY
164 {
165 /* Return the atom */
166 *Atom = SafeAtom;
167 }
168 _SEH2_EXCEPT(ExSystemExceptionFilter())
169 {
170 Status = _SEH2_GetExceptionCode();
171 }
172 _SEH2_END;
173 }
174 }
175
176 /* If we captured anything, free it */
177 if ((CapturedName) && (CapturedName != AtomName)) ExFreePool(CapturedName);
178
179 /* Return to caller */
180 return Status;
181 }
182
183 /*++
184 * @name NtDeleteAtom
185 * @implemented
186 *
187 * Removes Atom from Global Atom Table. If Atom's reference counter
188 * is greater then 1, function decrements this counter, but Atom
189 * stayed in Global Atom Table.
190 * See: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Atoms/NtDeleteAtom.html
191 *
192 * @param Atom
193 * Atom identifier
194 *
195 * @return STATUS_SUCCESS in case of success, proper error code
196 * othwerwise.
197 *
198 * @remarks None
199 *
200 *--*/
201 NTSTATUS
202 NTAPI
203 NtDeleteAtom(IN RTL_ATOM Atom)
204 {
205 PRTL_ATOM_TABLE AtomTable = ExpGetGlobalAtomTable();
206 PAGED_CODE();
207
208 /* Check for valid table */
209 if (AtomTable == NULL) return STATUS_ACCESS_DENIED;
210
211 /* Call worker function */
212 return RtlDeleteAtomFromAtomTable(AtomTable, Atom);
213 }
214
215 /*++
216 * @name NtFindAtom
217 * @implemented
218 *
219 * Retrieves existing Atom's identifier without incrementing Atom's
220 * internal counter
221 * See: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Atoms/NtFindAtom.html
222 *
223 * @param AtomName
224 * Atom name in Unicode
225 *
226 * @param AtomNameLength
227 * Length of the atom name
228 *
229 * @param Atom
230 * Pointer to RTL_ATOM
231 *
232 * @return STATUS_SUCCESS in case of success, proper error code
233 * othwerwise.
234 *
235 * @remarks None
236 *
237 *--*/
238 NTSTATUS
239 NTAPI
240 NtFindAtom(IN PWSTR AtomName,
241 IN ULONG AtomNameLength,
242 OUT PRTL_ATOM Atom)
243 {
244 PRTL_ATOM_TABLE AtomTable = ExpGetGlobalAtomTable();
245 NTSTATUS Status = STATUS_SUCCESS;
246 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
247 LPWSTR CapturedName = NULL;
248 ULONG CapturedSize;
249 RTL_ATOM SafeAtom;
250 PAGED_CODE();
251
252 /* Check for the table */
253 if (AtomTable == NULL) return STATUS_ACCESS_DENIED;
254
255 /* Check for valid name */
256 if (AtomNameLength > (RTL_MAXIMUM_ATOM_LENGTH * sizeof(WCHAR)))
257 {
258 /* Fail */
259 DPRINT1("Atom name too long\n");
260 return STATUS_INVALID_PARAMETER;
261 }
262
263 /* Re-use the given name if kernel mode or no atom name */
264 CapturedName = AtomName;
265
266 /* Check if we're called from user-mode*/
267 if (PreviousMode != KernelMode)
268 {
269 /* Enter SEH */
270 _SEH2_TRY
271 {
272 /* Check if we have a name */
273 if (AtomName)
274 {
275 /* Probe the atom */
276 ProbeForRead(AtomName, AtomNameLength, sizeof(WCHAR));
277
278 /* Allocate an aligned buffer + the null char */
279 CapturedSize = ((AtomNameLength + sizeof(WCHAR)) &~
280 (sizeof(WCHAR) -1));
281 CapturedName = ExAllocatePoolWithTag(PagedPool,
282 CapturedSize,
283 TAG_ATOM);
284 if (!CapturedName)
285 {
286 /* Fail the call */
287 Status = STATUS_INSUFFICIENT_RESOURCES;
288 }
289 else
290 {
291 /* Copy the name and null-terminate it */
292 RtlCopyMemory(CapturedName, AtomName, AtomNameLength);
293 CapturedName[AtomNameLength / sizeof(WCHAR)] = UNICODE_NULL;
294 }
295
296 /* Probe the atom too */
297 if (Atom) ProbeForWriteUshort(Atom);
298 }
299 }
300 _SEH2_EXCEPT(ExSystemExceptionFilter())
301 {
302 Status = _SEH2_GetExceptionCode();
303 }
304 _SEH2_END;
305 }
306
307 /* Make sure probe worked */
308 if (NT_SUCCESS(Status))
309 {
310 /* Call the runtime function */
311 Status = RtlLookupAtomInAtomTable(AtomTable, CapturedName, &SafeAtom);
312 if (NT_SUCCESS(Status) && (Atom))
313 {
314 /* Success and caller wants the atom back.. .enter SEH */
315 _SEH2_TRY
316 {
317 /* Return the atom */
318 *Atom = SafeAtom;
319 }
320 _SEH2_EXCEPT(ExSystemExceptionFilter())
321 {
322 Status = _SEH2_GetExceptionCode();
323 }
324 _SEH2_END;
325 }
326 }
327
328 /* If we captured anything, free it */
329 if ((CapturedName) && (CapturedName != AtomName)) ExFreePool(CapturedName);
330
331 /* Return to caller */
332 return Status;
333 }
334
335 /*++
336 * @name NtQueryInformationAtom
337 * @implemented
338 *
339 * Gets single Atom properties or reads Global Atom Table
340 * See: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Atoms/NtQueryInformationAtom.html
341 *
342 * @param Atom
343 * Atom to query. If AtomInformationClass parameter is
344 * AtomTableInformation, Atom parameter is not used.
345 *
346 * @param AtomInformationClass
347 * See ATOM_INFORMATION_CLASS enumeration type for details
348 *
349 * @param AtomInformation
350 * Result of call - pointer to user's allocated buffer for data
351 *
352 * @param AtomInformationLength
353 * Size of AtomInformation buffer, in bytes
354 *
355 * @param ReturnLength
356 * Pointer to ULONG value containing required AtomInformation
357 * buffer size
358 *
359 * @return STATUS_SUCCESS in case of success, proper error code
360 * othwerwise.
361 *
362 * @remarks None
363 *
364 *--*/
365 NTSTATUS
366 NTAPI
367 NtQueryInformationAtom(RTL_ATOM Atom,
368 ATOM_INFORMATION_CLASS AtomInformationClass,
369 PVOID AtomInformation,
370 ULONG AtomInformationLength,
371 PULONG ReturnLength)
372 {
373 PRTL_ATOM_TABLE AtomTable = ExpGetGlobalAtomTable();
374 PATOM_BASIC_INFORMATION BasicInformation = AtomInformation;
375 PATOM_TABLE_INFORMATION TableInformation = AtomInformation;
376 NTSTATUS Status = STATUS_SUCCESS;
377 ULONG Flags, UsageCount, NameLength, RequiredLength = 0;
378 KPROCESSOR_MODE PreviousMode;
379
380 PAGED_CODE();
381
382 /* Check for valid table */
383 if (AtomTable == NULL) return STATUS_ACCESS_DENIED;
384
385 PreviousMode = ExGetPreviousMode();
386
387 _SEH2_TRY
388 {
389 /* Probe the parameters */
390 if (PreviousMode != KernelMode)
391 {
392 ProbeForWrite(AtomInformation,
393 AtomInformationLength,
394 sizeof(ULONG));
395
396 if (ReturnLength != NULL)
397 {
398 ProbeForWriteUlong(ReturnLength);
399 }
400 }
401
402 /* Choose class */
403 switch (AtomInformationClass)
404 {
405 /* Caller requested info about an atom */
406 case AtomBasicInformation:
407
408 /* Size check */
409 RequiredLength = FIELD_OFFSET(ATOM_BASIC_INFORMATION, Name);
410 if (RequiredLength > AtomInformationLength)
411 {
412 /* Fail */
413 DPRINT1("Buffer too small\n");
414 Status = STATUS_INFO_LENGTH_MISMATCH;
415 _SEH2_LEAVE;
416 }
417
418 /* Prepare query */
419 UsageCount = 0;
420 NameLength = AtomInformationLength - RequiredLength;
421 BasicInformation->Name[0] = UNICODE_NULL;
422
423 /* Query the data */
424 Status = RtlQueryAtomInAtomTable(AtomTable,
425 Atom,
426 &UsageCount,
427 &Flags,
428 BasicInformation->Name,
429 &NameLength);
430 if (NT_SUCCESS(Status))
431 {
432 /* Return data */
433 BasicInformation->UsageCount = (USHORT)UsageCount;
434 BasicInformation->Flags = (USHORT)Flags;
435 BasicInformation->NameLength = (USHORT)NameLength;
436 RequiredLength += NameLength + sizeof(WCHAR);
437 }
438 break;
439
440 /* Caller requested info about an Atom Table */
441 case AtomTableInformation:
442
443 /* Size check */
444 RequiredLength = FIELD_OFFSET(ATOM_TABLE_INFORMATION, Atoms);
445 if (RequiredLength > AtomInformationLength)
446 {
447 /* Fail */
448 DPRINT1("Buffer too small\n");
449 Status = STATUS_INFO_LENGTH_MISMATCH;
450 _SEH2_LEAVE;
451 }
452
453 /* Query the data */
454 Status = RtlQueryAtomListInAtomTable(AtomTable,
455 (AtomInformationLength - RequiredLength) /
456 sizeof(RTL_ATOM),
457 &TableInformation->NumberOfAtoms,
458 TableInformation->Atoms);
459 if (NT_SUCCESS(Status))
460 {
461 /* Update the return length */
462 RequiredLength += TableInformation->NumberOfAtoms * sizeof(RTL_ATOM);
463 }
464 break;
465
466 /* Caller was on crack */
467 default:
468
469 /* Unrecognized class */
470 Status = STATUS_INVALID_INFO_CLASS;
471 break;
472 }
473
474 /* Return the required size */
475 if (ReturnLength != NULL)
476 {
477 *ReturnLength = RequiredLength;
478 }
479 }
480 _SEH2_EXCEPT(ExSystemExceptionFilter())
481 {
482 Status = _SEH2_GetExceptionCode();
483 }
484 _SEH2_END;
485
486 /* Return to caller */
487 return Status;
488 }
489
490 /* EOF */