Fix KeRaiseUserException and rename Object Information Structure
[reactos.git] / reactos / ntoskrnl / ob / dirobj.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ob/dirobj.c
6 * PURPOSE: Interface functions to directory object
7 * PROGRAMMER: David Welch (welch@mcmail.com)
8 * UPDATE HISTORY:
9 * 22/05/98: Created
10 */
11
12 /* INCLUDES ***************************************************************/
13
14 #include <ntoskrnl.h>
15 #define NDEBUG
16 #include <internal/debug.h>
17
18
19 /* FUNCTIONS **************************************************************/
20
21
22 /**********************************************************************
23 * NAME EXPORTED
24 * NtOpenDirectoryObject
25 *
26 * DESCRIPTION
27 * Opens a namespace directory object.
28 *
29 * ARGUMENTS
30 * DirectoryHandle (OUT)
31 * Variable which receives the directory handle.
32 *
33 * DesiredAccess
34 * Desired access to the directory.
35 *
36 * ObjectAttributes
37 * Structure describing the directory.
38 *
39 * RETURN VALUE
40 * Status.
41 *
42 * NOTES
43 * Undocumented.
44 */
45 NTSTATUS STDCALL
46 NtOpenDirectoryObject (OUT PHANDLE DirectoryHandle,
47 IN ACCESS_MASK DesiredAccess,
48 IN POBJECT_ATTRIBUTES ObjectAttributes)
49 {
50 PVOID Object;
51 NTSTATUS Status;
52
53 *DirectoryHandle = 0;
54
55 Status = ObReferenceObjectByName(ObjectAttributes->ObjectName,
56 ObjectAttributes->Attributes,
57 NULL,
58 DesiredAccess,
59 ObDirectoryType,
60 UserMode,
61 NULL,
62 &Object);
63 if (!NT_SUCCESS(Status))
64 {
65 return Status;
66 }
67
68 Status = ObCreateHandle(PsGetCurrentProcess(),
69 Object,
70 DesiredAccess,
71 FALSE,
72 DirectoryHandle);
73 return STATUS_SUCCESS;
74 }
75
76 static NTSTATUS
77 CopyDirectoryString(PUNICODE_STRING UnsafeTarget, PUNICODE_STRING Source, PUCHAR *Buffer)
78 {
79 UNICODE_STRING Target;
80 NTSTATUS Status;
81 WCHAR NullWchar;
82
83 Target.Length = Source->Length;
84 Target.MaximumLength = (Source->Length + sizeof (WCHAR));
85 Target.Buffer = (PWCHAR) *Buffer;
86 Status = MmCopyToCaller(UnsafeTarget, &Target, sizeof(UNICODE_STRING));
87 if (! NT_SUCCESS(Status))
88 {
89 return Status;
90 }
91 Status = MmCopyToCaller(*Buffer, Source->Buffer, Source->Length);
92 if (! NT_SUCCESS(Status))
93 {
94 return Status;
95 }
96 *Buffer += Source->Length;
97 NullWchar = L'\0';
98 Status = MmCopyToCaller(*Buffer, &NullWchar, sizeof(WCHAR));
99 if (! NT_SUCCESS(Status))
100 {
101 return Status;
102 }
103 *Buffer += sizeof(WCHAR);
104
105 return STATUS_SUCCESS;
106 }
107
108
109 /**********************************************************************
110 * NAME EXPORTED
111 * NtQueryDirectoryObject
112 *
113 * DESCRIPTION
114 * Reads information from a directory in the system namespace.
115 *
116 * ARGUMENTS
117 * DirectoryHandle
118 * Handle, obtained with NtOpenDirectoryObject(), which
119 * must grant DIRECTORY_QUERY access to the directory
120 * object.
121 *
122 * Buffer (OUT)
123 * Buffer to hold the data read.
124 *
125 * BufferLength
126 * Size of the buffer in bytes.
127 *
128 * ReturnSingleEntry
129 * When TRUE, only 1 entry is written in DirObjInformation;
130 * otherwise as many as will fit in the buffer.
131 *
132 * RestartScan
133 * If TRUE start reading at index 0.
134 * If FALSE start reading at the index specified
135 * by object index *ObjectIndex.
136 *
137 * Context
138 * Zero based index into the directory, interpretation
139 * depends on RestartScan.
140 *
141 * ReturnLength (OUT)
142 * Caller supplied storage for the number of bytes
143 * written (or NULL).
144 *
145 * RETURN VALUE
146 * STATUS_SUCCESS - At least one (possibly more, depending on
147 * parameters and buffer size) dir entry is
148 * returned.
149 * STATUS_NO_MORE_ENTRIES - Directory is exhausted
150 * STATUS_BUFFER_TOO_SMALL - There isn't enough room in the
151 * buffer to return even 1 entry.
152 * ReturnLength will hold the required
153 * buffer size to return all remaining
154 * dir entries
155 * Other - Status code
156 *
157 *
158 * NOTES
159 * Although you can iterate over the directory by calling this
160 * function multiple times, the directory is unlocked between
161 * calls. This means that another thread can change the directory
162 * and so iterating doesn't guarantee a consistent picture of the
163 * directory. Best thing is to retrieve all directory entries in
164 * one call.
165 */
166 NTSTATUS STDCALL
167 NtQueryDirectoryObject (IN HANDLE DirectoryHandle,
168 OUT PVOID Buffer,
169 IN ULONG BufferLength,
170 IN BOOLEAN ReturnSingleEntry,
171 IN BOOLEAN RestartScan,
172 IN OUT PULONG UnsafeContext,
173 OUT PULONG UnsafeReturnLength OPTIONAL)
174 {
175 PDIRECTORY_OBJECT dir = NULL;
176 PLIST_ENTRY current_entry = NULL;
177 PLIST_ENTRY start_entry;
178 POBJECT_HEADER current = NULL;
179 NTSTATUS Status = STATUS_SUCCESS;
180 ULONG DirectoryCount = 0;
181 ULONG DirectoryIndex = 0;
182 POBJECT_DIRECTORY_INFORMATION current_odi = (POBJECT_DIRECTORY_INFORMATION) Buffer;
183 OBJECT_DIRECTORY_INFORMATION ZeroOdi;
184 PUCHAR FirstFree = (PUCHAR) Buffer;
185 ULONG Context;
186 ULONG RequiredSize;
187 ULONG NewValue;
188 KIRQL OldLevel;
189
190 DPRINT("NtQueryDirectoryObject(DirectoryHandle %x)\n", DirectoryHandle);
191
192 /* Check Context is not NULL */
193 if (NULL == UnsafeContext)
194 {
195 return STATUS_INVALID_PARAMETER;
196 }
197
198 /* Reference the DIRECTORY_OBJECT */
199 Status = ObReferenceObjectByHandle(DirectoryHandle,
200 DIRECTORY_QUERY,
201 ObDirectoryType,
202 UserMode,
203 (PVOID*)&dir,
204 NULL);
205 if (!NT_SUCCESS(Status))
206 {
207 return Status;
208 }
209
210 KeAcquireSpinLock(&dir->Lock, &OldLevel);
211
212 /*
213 * Optionally, skip over some entries at the start of the directory
214 * (use *ObjectIndex value)
215 */
216 start_entry = dir->head.Flink;
217 if (! RestartScan)
218 {
219 register ULONG EntriesToSkip;
220
221 Status = MmCopyFromCaller(&Context, UnsafeContext, sizeof(ULONG));
222 if (! NT_SUCCESS(Status))
223 {
224 KeReleaseSpinLock(&dir->Lock, OldLevel);
225 ObDereferenceObject(dir);
226 return Status;
227 }
228 EntriesToSkip = Context;
229
230 CHECKPOINT;
231
232 for (; 0 != EntriesToSkip-- && start_entry != &dir->head;
233 start_entry = start_entry->Flink)
234 {
235 ;
236 }
237 if ((0 != EntriesToSkip) && (start_entry == &dir->head))
238 {
239 KeReleaseSpinLock(&dir->Lock, OldLevel);
240 ObDereferenceObject(dir);
241 return STATUS_NO_MORE_ENTRIES;
242 }
243 }
244
245 /*
246 * Compute number of entries that we will copy into the buffer and
247 * the total size of all entries (even if larger than the buffer size)
248 */
249 DirectoryCount = 0;
250 /* For the end sentenil */
251 RequiredSize = sizeof(OBJECT_DIRECTORY_INFORMATION);
252 for (current_entry = start_entry;
253 current_entry != &dir->head;
254 current_entry = current_entry->Flink)
255 {
256 current = CONTAINING_RECORD(current_entry, OBJECT_HEADER, Entry);
257
258 RequiredSize += sizeof(OBJECT_DIRECTORY_INFORMATION) +
259 current->Name.Length + sizeof(WCHAR) +
260 current->ObjectType->TypeName.Length + sizeof(WCHAR);
261 if (RequiredSize <= BufferLength &&
262 (! ReturnSingleEntry || DirectoryCount < 1))
263 {
264 DirectoryCount++;
265 }
266 }
267
268 /*
269 * If there's no room to even copy a single entry then return error
270 * status.
271 */
272 if (0 == DirectoryCount &&
273 !(IsListEmpty(&dir->head) && BufferLength >= RequiredSize))
274 {
275 KeReleaseSpinLock(&dir->Lock, OldLevel);
276 ObDereferenceObject(dir);
277 if (NULL != UnsafeReturnLength)
278 {
279 Status = MmCopyToCaller(UnsafeReturnLength, &RequiredSize, sizeof(ULONG));
280 }
281 return NT_SUCCESS(Status) ? STATUS_BUFFER_TOO_SMALL : Status;
282 }
283
284 /*
285 * Move FirstFree to point to the Unicode strings area
286 */
287 FirstFree += (DirectoryCount + 1) * sizeof(OBJECT_DIRECTORY_INFORMATION);
288
289 /* Scan the directory */
290 current_entry = start_entry;
291 for (DirectoryIndex = 0; DirectoryIndex < DirectoryCount; DirectoryIndex++)
292 {
293 current = CONTAINING_RECORD(current_entry, OBJECT_HEADER, Entry);
294
295 /*
296 * Copy the current directory entry's data into the buffer
297 * and update the OBJDIR_INFORMATION entry in the array.
298 */
299 /* --- Object's name --- */
300 Status = CopyDirectoryString(&current_odi->ObjectName, &current->Name, &FirstFree);
301 if (! NT_SUCCESS(Status))
302 {
303 KeReleaseSpinLock(&dir->Lock, OldLevel);
304 ObDereferenceObject(dir);
305 return Status;
306 }
307 /* --- Object type's name --- */
308 Status = CopyDirectoryString(&current_odi->ObjectTypeName, &current->ObjectType->TypeName, &FirstFree);
309 if (! NT_SUCCESS(Status))
310 {
311 KeReleaseSpinLock(&dir->Lock, OldLevel);
312 ObDereferenceObject(dir);
313 return Status;
314 }
315
316 /* Next entry in the array */
317 current_odi++;
318 /* Next object in the directory */
319 current_entry = current_entry->Flink;
320 }
321
322 /*
323 * Don't need dir object anymore
324 */
325 KeReleaseSpinLock(&dir->Lock, OldLevel);
326 ObDereferenceObject(dir);
327
328 /* Terminate with all zero entry */
329 memset(&ZeroOdi, '\0', sizeof(OBJECT_DIRECTORY_INFORMATION));
330 Status = MmCopyToCaller(current_odi, &ZeroOdi, sizeof(OBJECT_DIRECTORY_INFORMATION));
331 if (! NT_SUCCESS(Status))
332 {
333 return Status;
334 }
335
336 /*
337 * Store current index in Context
338 */
339 if (RestartScan)
340 {
341 Context = DirectoryCount;
342 }
343 else
344 {
345 Context += DirectoryCount;
346 }
347 Status = MmCopyToCaller(UnsafeContext, &Context, sizeof(ULONG));
348 if (! NT_SUCCESS(Status))
349 {
350 return Status;
351 }
352
353 /*
354 * Report to the caller how much bytes
355 * we wrote in the user buffer.
356 */
357 if (NULL != UnsafeReturnLength)
358 {
359 NewValue = FirstFree - (PUCHAR) Buffer;
360 Status = MmCopyToCaller(UnsafeReturnLength, &NewValue, sizeof(ULONG));
361 if (! NT_SUCCESS(Status))
362 {
363 return Status;
364 }
365 }
366
367 return Status;
368 }
369
370
371 /**********************************************************************
372 * NAME (EXPORTED as Zw)
373 * NtCreateDirectoryObject
374 *
375 * DESCRIPTION
376 * Creates or opens a directory object (a container for other
377 * objects).
378 *
379 * ARGUMENTS
380 * DirectoryHandle (OUT)
381 * Caller supplied storage for the handle of the
382 * directory.
383 *
384 * DesiredAccess
385 * Access desired to the directory.
386 *
387 * ObjectAttributes
388 * Object attributes initialized with
389 * InitializeObjectAttributes.
390 *
391 * RETURN VALUE
392 * Status.
393 */
394 NTSTATUS STDCALL
395 NtCreateDirectoryObject (OUT PHANDLE DirectoryHandle,
396 IN ACCESS_MASK DesiredAccess,
397 IN POBJECT_ATTRIBUTES ObjectAttributes)
398 {
399 PDIRECTORY_OBJECT DirectoryObject;
400 NTSTATUS Status;
401
402 DPRINT("NtCreateDirectoryObject(DirectoryHandle %x, "
403 "DesiredAccess %x, ObjectAttributes %x, "
404 "ObjectAttributes->ObjectName %wZ)\n",
405 DirectoryHandle, DesiredAccess, ObjectAttributes,
406 ObjectAttributes->ObjectName);
407
408 Status = NtOpenDirectoryObject (DirectoryHandle,
409 DesiredAccess,
410 ObjectAttributes);
411
412 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
413 {
414 Status = ObCreateObject (ExGetPreviousMode(),
415 ObDirectoryType,
416 ObjectAttributes,
417 ExGetPreviousMode(),
418 NULL,
419 sizeof(DIRECTORY_OBJECT),
420 0,
421 0,
422 (PVOID*)&DirectoryObject);
423 if (!NT_SUCCESS(Status))
424 {
425 return Status;
426 }
427
428 Status = ObInsertObject ((PVOID)DirectoryObject,
429 NULL,
430 DesiredAccess,
431 0,
432 NULL,
433 DirectoryHandle);
434
435 ObDereferenceObject(DirectoryObject);
436 }
437
438 return Status;
439 }
440
441 /* EOF */