Fix kernel-mode executive atom implementation (mostly add SEH and tidy up the code...
[reactos.git] / reactos / ntoskrnl / ex / atom.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
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 <internal/debug.h>
15
16 #define TAG_ATOM TAG('A', 't', 'o', 'm')
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 PRTL_ATOM_TABLE
32 NTAPI
33 ExpGetGlobalAtomTable(VOID)
34 {
35 NTSTATUS Status;
36
37 /* Return it if we have one */
38 if (GlobalAtomTable) return GlobalAtomTable;
39
40 /* Create it */
41 Status = RtlCreateAtomTable(37, &GlobalAtomTable);
42
43 /* If we couldn't create it, return NULL */
44 if (!NT_SUCCESS(Status)) return NULL;
45
46 /* Return the newly created one */
47 return GlobalAtomTable;
48 }
49
50 /* FUNCTIONS ****************************************************************/
51
52 /*
53 * @implemented
54 */
55 NTSTATUS
56 NTAPI
57 NtAddAtom(IN PWSTR AtomName,
58 IN ULONG AtomNameLength,
59 OUT PRTL_ATOM Atom)
60 {
61 PRTL_ATOM_TABLE AtomTable = ExpGetGlobalAtomTable();
62 NTSTATUS Status = STATUS_SUCCESS;
63 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
64 LPWSTR CapturedName = NULL;
65 ULONG CapturedSize;
66 RTL_ATOM SafeAtom;
67 PAGED_CODE();
68
69 /* Check for the table */
70 if (AtomTable == NULL) return STATUS_ACCESS_DENIED;
71
72 /* Check for valid name */
73 if (AtomNameLength > (RTL_MAXIMUM_ATOM_LENGTH * sizeof(WCHAR)))
74 {
75 /* Fail */
76 DPRINT1("Atom name too long\n");
77 return STATUS_INVALID_PARAMETER;
78 }
79
80 /* Check if we're called from user-mode*/
81 if (PreviousMode != KernelMode)
82 {
83 /* Enter SEH */
84 _SEH_TRY
85 {
86 /* Check if we have a name */
87 if (AtomName)
88 {
89 /* Probe the atom */
90 ProbeForRead(AtomName, AtomNameLength, sizeof(WCHAR));
91
92 /* Allocate an aligned buffer + the null char */
93 CapturedSize = ((AtomNameLength + sizeof(WCHAR)) &~
94 (sizeof(WCHAR) -1));
95 CapturedName = ExAllocatePoolWithTag(PagedPool,
96 CapturedSize,
97 TAG_ATOM);
98 if (!CapturedName)
99 {
100 /* Fail the call */
101 Status = STATUS_INSUFFICIENT_RESOURCES;
102 }
103 else
104 {
105 /* Copy the name and null-terminate it */
106 RtlMoveMemory(CapturedName, AtomName, AtomNameLength);
107 CapturedName[AtomNameLength / sizeof(WCHAR)] = UNICODE_NULL;
108 }
109
110 /* Probe the atom too */
111 if (Atom) ProbeForWriteUshort(Atom);
112 }
113 }
114 _SEH_EXCEPT(_SEH_ExSystemExceptionFilter)
115 {
116 Status = _SEH_GetExceptionCode();
117 }
118 _SEH_END;
119 }
120 else
121 {
122 /* Simplify code and re-use one variable */
123 if (AtomName) CapturedName = AtomName;
124 }
125
126 /* Make sure probe worked */
127 if (NT_SUCCESS(Status))
128 {
129 /* Call the runtime function */
130 Status = RtlAddAtomToAtomTable(AtomTable, CapturedName, &SafeAtom);
131 if (NT_SUCCESS(Status) && (Atom))
132 {
133 /* Success and caller wants the atom back.. .enter SEH */
134 _SEH_TRY
135 {
136 /* Return the atom */
137 *Atom = SafeAtom;
138 }
139 _SEH_EXCEPT(_SEH_ExSystemExceptionFilter)
140 {
141 Status = _SEH_GetExceptionCode();
142 }
143 _SEH_END;
144 }
145 }
146
147 /* If we captured anything, free it */
148 if ((CapturedName) && (CapturedName != AtomName)) ExFreePool(CapturedName);
149
150 /* Return to caller */
151 return Status;
152 }
153
154 /*
155 * @implemented
156 */
157 NTSTATUS
158 NTAPI
159 NtDeleteAtom(IN RTL_ATOM Atom)
160 {
161 PRTL_ATOM_TABLE AtomTable = ExpGetGlobalAtomTable();
162 PAGED_CODE();
163
164 /* Check for valid table */
165 if (AtomTable == NULL) return STATUS_ACCESS_DENIED;
166
167 /* Call worker function */
168 return RtlDeleteAtomFromAtomTable(AtomTable, Atom);
169 }
170
171 /*
172 * @implemented
173 */
174 NTSTATUS
175 NTAPI
176 NtFindAtom(IN PWSTR AtomName,
177 IN ULONG AtomNameLength,
178 OUT PRTL_ATOM Atom)
179 {
180 PRTL_ATOM_TABLE AtomTable = ExpGetGlobalAtomTable();
181 NTSTATUS Status = STATUS_SUCCESS;
182 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
183 LPWSTR CapturedName = NULL;
184 ULONG CapturedSize;
185 RTL_ATOM SafeAtom;
186 PAGED_CODE();
187
188 /* Check for the table */
189 if (AtomTable == NULL) return STATUS_ACCESS_DENIED;
190
191 /* Check for valid name */
192 if (AtomNameLength > (RTL_MAXIMUM_ATOM_LENGTH * sizeof(WCHAR)))
193 {
194 /* Fail */
195 DPRINT1("Atom name too long\n");
196 return STATUS_INVALID_PARAMETER;
197 }
198
199 /* Check if we're called from user-mode*/
200 if (PreviousMode != KernelMode)
201 {
202 /* Enter SEH */
203 _SEH_TRY
204 {
205 /* Check if we have a name */
206 if (AtomName)
207 {
208 /* Probe the atom */
209 ProbeForRead(AtomName, AtomNameLength, sizeof(WCHAR));
210
211 /* Allocate an aligned buffer + the null char */
212 CapturedSize = ((AtomNameLength + sizeof(WCHAR)) &~
213 (sizeof(WCHAR) -1));
214 CapturedName = ExAllocatePoolWithTag(PagedPool,
215 CapturedSize,
216 TAG_ATOM);
217 if (!CapturedName)
218 {
219 /* Fail the call */
220 Status = STATUS_INSUFFICIENT_RESOURCES;
221 }
222 else
223 {
224 /* Copy the name and null-terminate it */
225 RtlMoveMemory(CapturedName, AtomName, AtomNameLength);
226 CapturedName[AtomNameLength / sizeof(WCHAR)] = UNICODE_NULL;
227 }
228
229 /* Probe the atom too */
230 if (Atom) ProbeForWriteUshort(Atom);
231 }
232 }
233 _SEH_EXCEPT(_SEH_ExSystemExceptionFilter)
234 {
235 Status = _SEH_GetExceptionCode();
236 }
237 _SEH_END;
238 }
239 else
240 {
241 /* Simplify code and re-use one variable */
242 if (AtomName) CapturedName = AtomName;
243 }
244
245 /* Make sure probe worked */
246 if (NT_SUCCESS(Status))
247 {
248 /* Call the runtime function */
249 Status = RtlLookupAtomInAtomTable(AtomTable, CapturedName, &SafeAtom);
250 if (NT_SUCCESS(Status) && (Atom))
251 {
252 /* Success and caller wants the atom back.. .enter SEH */
253 _SEH_TRY
254 {
255 /* Return the atom */
256 *Atom = SafeAtom;
257 }
258 _SEH_EXCEPT(_SEH_ExSystemExceptionFilter)
259 {
260 Status = _SEH_GetExceptionCode();
261 }
262 _SEH_END;
263 }
264 }
265
266 /* If we captured anything, free it */
267 if ((CapturedName) && (CapturedName != AtomName)) ExFreePool(CapturedName);
268
269 /* Return to caller */
270 return Status;
271 }
272
273 /*
274 * @implemented
275 */
276 NTSTATUS
277 NTAPI
278 NtQueryInformationAtom(RTL_ATOM Atom,
279 ATOM_INFORMATION_CLASS AtomInformationClass,
280 PVOID AtomInformation,
281 ULONG AtomInformationLength,
282 PULONG ReturnLength)
283 {
284 PRTL_ATOM_TABLE AtomTable = ExpGetGlobalAtomTable();
285 PATOM_BASIC_INFORMATION BasicInformation = AtomInformation;
286 PATOM_TABLE_INFORMATION TableInformation = AtomInformation;
287 NTSTATUS Status;
288 ULONG Flags, UsageCount, NameLength;
289
290 /* Check for valid table */
291 if (AtomTable == NULL) return STATUS_ACCESS_DENIED;
292
293 /* FIXME: SEH! */
294
295 /* Choose class */
296 switch (AtomInformationClass)
297 {
298 /* Caller requested info about an atom */
299 case AtomBasicInformation:
300
301 /* Size check */
302 *ReturnLength = FIELD_OFFSET(ATOM_BASIC_INFORMATION, Name);
303 if (*ReturnLength > AtomInformationLength)
304 {
305 /* Fail */
306 DPRINT1("Buffer too small\n");
307 return STATUS_INFO_LENGTH_MISMATCH;
308 }
309
310 /* Prepare query */
311 UsageCount = 0;
312 NameLength = AtomInformationLength - *ReturnLength;
313 BasicInformation->Name[0] = UNICODE_NULL;
314
315 /* Query the data */
316 Status = RtlQueryAtomInAtomTable(AtomTable,
317 Atom,
318 &UsageCount,
319 &Flags,
320 BasicInformation->Name,
321 &NameLength);
322 if (NT_SUCCESS(Status))
323 {
324 /* Return data */
325 BasicInformation->UsageCount = (USHORT)UsageCount;
326 BasicInformation->Flags = (USHORT)Flags;
327 BasicInformation->NameLength = (USHORT)NameLength;
328 *ReturnLength += NameLength + sizeof(WCHAR);
329 }
330 break;
331
332 /* Caller requested info about an Atom Table */
333 case AtomTableInformation:
334
335 /* Size check */
336 *ReturnLength = FIELD_OFFSET(ATOM_TABLE_INFORMATION, Atoms);
337 if (*ReturnLength > AtomInformationLength)
338 {
339 /* Fail */
340 DPRINT1("Buffer too small\n");
341 return STATUS_INFO_LENGTH_MISMATCH;
342 }
343
344 /* Query the data */
345 Status = RtlQueryAtomListInAtomTable(AtomTable,
346 (AtomInformationLength - *ReturnLength) /
347 sizeof(RTL_ATOM),
348 &TableInformation->NumberOfAtoms,
349 TableInformation->Atoms);
350 if (NT_SUCCESS(Status))
351 {
352 /* Update the return length */
353 *ReturnLength += TableInformation->NumberOfAtoms *
354 sizeof(RTL_ATOM);
355 }
356 break;
357
358 /* Caller was on crack */
359 default:
360
361 /* Unrecognized class */
362 Status = STATUS_INVALID_INFO_CLASS;
363 }
364
365 /* Return to caller */
366 return Status;
367 }
368
369 /* EOF */