- Acquire the temporary buffer from non paged pool in NtQueryDirectoryObject, because...
[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 *
8 * PROGRAMMERS: David Welch (welch@mcmail.com)
9 */
10
11 /* INCLUDES ***************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <internal/debug.h>
16
17
18 /* FUNCTIONS **************************************************************/
19
20
21 /**********************************************************************
22 * NAME EXPORTED
23 * NtOpenDirectoryObject
24 *
25 * DESCRIPTION
26 * Opens a namespace directory object.
27 *
28 * ARGUMENTS
29 * DirectoryHandle (OUT)
30 * Variable which receives the directory handle.
31 *
32 * DesiredAccess
33 * Desired access to the directory.
34 *
35 * ObjectAttributes
36 * Structure describing the directory.
37 *
38 * RETURN VALUE
39 * Status.
40 *
41 * NOTES
42 * Undocumented.
43 */
44 NTSTATUS STDCALL
45 NtOpenDirectoryObject (OUT PHANDLE DirectoryHandle,
46 IN ACCESS_MASK DesiredAccess,
47 IN POBJECT_ATTRIBUTES ObjectAttributes)
48 {
49 HANDLE hDirectory;
50 KPROCESSOR_MODE PreviousMode;
51 NTSTATUS Status = STATUS_SUCCESS;
52
53 PAGED_CODE();
54
55 PreviousMode = ExGetPreviousMode();
56
57 if(PreviousMode != KernelMode)
58 {
59 _SEH_TRY
60 {
61 ProbeForWriteHandle(DirectoryHandle);
62 }
63 _SEH_HANDLE
64 {
65 Status = _SEH_GetExceptionCode();
66 }
67 _SEH_END;
68
69 if(!NT_SUCCESS(Status))
70 {
71 DPRINT1("NtOpenDirectoryObject failed, Status: 0x%x\n", Status);
72 return Status;
73 }
74 }
75
76 Status = ObOpenObjectByName(ObjectAttributes,
77 ObDirectoryType,
78 NULL,
79 PreviousMode,
80 DesiredAccess,
81 NULL,
82 &hDirectory);
83 if(NT_SUCCESS(Status))
84 {
85 _SEH_TRY
86 {
87 *DirectoryHandle = hDirectory;
88 }
89 _SEH_HANDLE
90 {
91 Status = _SEH_GetExceptionCode();
92 }
93 _SEH_END;
94 }
95
96 return Status;
97 }
98
99
100 /**********************************************************************
101 * NAME EXPORTED
102 * NtQueryDirectoryObject
103 *
104 * DESCRIPTION
105 * Reads information from a directory in the system namespace.
106 *
107 * ARGUMENTS
108 * DirectoryHandle
109 * Handle, obtained with NtOpenDirectoryObject(), which
110 * must grant DIRECTORY_QUERY access to the directory
111 * object.
112 *
113 * Buffer (OUT)
114 * Buffer to hold the data read.
115 *
116 * BufferLength
117 * Size of the buffer in bytes.
118 *
119 * ReturnSingleEntry
120 * When TRUE, only 1 entry is written in DirObjInformation;
121 * otherwise as many as will fit in the buffer.
122 *
123 * RestartScan
124 * If TRUE start reading at index 0.
125 * If FALSE start reading at the index specified
126 * by object index *ObjectIndex.
127 *
128 * Context
129 * Zero based index into the directory, interpretation
130 * depends on RestartScan.
131 *
132 * ReturnLength (OUT)
133 * Caller supplied storage for the number of bytes
134 * written (or NULL).
135 *
136 * RETURN VALUE
137 * STATUS_SUCCESS - At least one (possibly more, depending on
138 * parameters and buffer size) dir entry is
139 * returned.
140 * STATUS_NO_MORE_ENTRIES - Directory is exhausted
141 * STATUS_BUFFER_TOO_SMALL - There isn't enough room in the
142 * buffer to return even 1 entry.
143 * ReturnLength will hold the required
144 * buffer size to return all remaining
145 * dir entries
146 * Other - Status code
147 *
148 *
149 * NOTES
150 * Although you can iterate over the directory by calling this
151 * function multiple times, the directory is unlocked between
152 * calls. This means that another thread can change the directory
153 * and so iterating doesn't guarantee a consistent picture of the
154 * directory. Best thing is to retrieve all directory entries in
155 * one call.
156 */
157 NTSTATUS STDCALL
158 NtQueryDirectoryObject (IN HANDLE DirectoryHandle,
159 OUT PVOID Buffer,
160 IN ULONG BufferLength,
161 IN BOOLEAN ReturnSingleEntry,
162 IN BOOLEAN RestartScan,
163 IN OUT PULONG Context,
164 OUT PULONG ReturnLength OPTIONAL)
165 {
166 PDIRECTORY_OBJECT Directory;
167 KPROCESSOR_MODE PreviousMode;
168 ULONG SkipEntries = 0;
169 ULONG NextEntry = 0;
170 ULONG CopyBytes = 0;
171 NTSTATUS Status = STATUS_SUCCESS;
172
173 PAGED_CODE();
174
175 PreviousMode = ExGetPreviousMode();
176
177 if(PreviousMode != KernelMode)
178 {
179 _SEH_TRY
180 {
181 /* a test showed that the Buffer pointer just has to be 16 bit aligned,
182 propably due to the fact that most information that needs to be copied
183 is unicode strings */
184 ProbeForWrite(Buffer,
185 BufferLength,
186 sizeof(WCHAR));
187 ProbeForWriteUlong(Context);
188 if(!RestartScan)
189 {
190 SkipEntries = *Context;
191 }
192 if(ReturnLength != NULL)
193 {
194 ProbeForWriteUlong(ReturnLength);
195 }
196 }
197 _SEH_HANDLE
198 {
199 Status = _SEH_GetExceptionCode();
200 }
201 _SEH_END;
202
203 if(!NT_SUCCESS(Status))
204 {
205 DPRINT1("NtQueryDirectoryObject failed, Status: 0x%x\n", Status);
206 return Status;
207 }
208 }
209 else if(!RestartScan)
210 {
211 SkipEntries = *Context;
212 }
213
214 Status = ObReferenceObjectByHandle(DirectoryHandle,
215 DIRECTORY_QUERY,
216 ObDirectoryType,
217 PreviousMode,
218 (PVOID*)&Directory,
219 NULL);
220 if(NT_SUCCESS(Status))
221 {
222 PVOID TemporaryBuffer = ExAllocatePool(NonPagedPool,
223 BufferLength);
224 if(TemporaryBuffer != NULL)
225 {
226 POBJECT_HEADER EntryHeader;
227 PLIST_ENTRY ListEntry;
228 KIRQL OldLevel;
229 ULONG RequiredSize = sizeof(OBJECT_DIRECTORY_INFORMATION);
230 ULONG nDirectories = 0;
231 POBJECT_DIRECTORY_INFORMATION DirInfo = (POBJECT_DIRECTORY_INFORMATION)TemporaryBuffer;
232
233 Status = STATUS_NO_MORE_ENTRIES;
234
235 KeAcquireSpinLock(&Directory->Lock, &OldLevel);
236
237 for(ListEntry = Directory->head.Flink;
238 ListEntry != &Directory->head;
239 ListEntry = ListEntry->Flink)
240 {
241 NextEntry++;
242 if(SkipEntries == 0)
243 {
244 PUNICODE_STRING Name, Type;
245 ULONG EntrySize;
246
247 EntryHeader = CONTAINING_RECORD(ListEntry, OBJECT_HEADER, Entry);
248
249 /* calculate the size of the required buffer space for this entry */
250 Name = (HEADER_TO_OBJECT_NAME(EntryHeader)->Name.Length != 0 ? &HEADER_TO_OBJECT_NAME(EntryHeader)->Name : NULL);
251 Type = &EntryHeader->Type->Name;
252 EntrySize = sizeof(OBJECT_DIRECTORY_INFORMATION) +
253 ((Name != NULL) ? ((ULONG)Name->Length + sizeof(WCHAR)) : 0) +
254 (ULONG)EntryHeader->Type->Name.Length + sizeof(WCHAR);
255
256 if(RequiredSize + EntrySize <= BufferLength)
257 {
258 /* the buffer is large enough to receive this entry. It would've
259 been much easier if the strings were directly appended to the
260 OBJECT_DIRECTORY_INFORMATION structured written into the buffer */
261 if(Name != NULL)
262 DirInfo->ObjectName = *Name;
263 else
264 {
265 DirInfo->ObjectName.Length = DirInfo->ObjectName.MaximumLength = 0;
266 DirInfo->ObjectName.Buffer = NULL;
267 }
268 DirInfo->ObjectTypeName = *Type;
269
270 nDirectories++;
271 RequiredSize += EntrySize;
272
273 Status = STATUS_SUCCESS;
274
275 if(ReturnSingleEntry)
276 {
277 /* we're only supposed to query one entry, so bail and copy the
278 strings to the buffer */
279 break;
280 }
281 DirInfo++;
282 }
283 else
284 {
285 if(ReturnSingleEntry)
286 {
287 /* the buffer is too small, so return the number of bytes that
288 would've been required for this query */
289 RequiredSize += EntrySize;
290 Status = STATUS_BUFFER_TOO_SMALL;
291 }
292
293 /* we couldn't query this entry, so leave the index that will be stored
294 in Context to this entry so the caller can query it the next time
295 he queries (hopefully with a buffer that is large enough then...) */
296 NextEntry--;
297
298 /* just copy the entries that fit into the buffer */
299 break;
300 }
301 }
302 else
303 {
304 /* skip the entry */
305 SkipEntries--;
306 }
307 }
308
309 if(!ReturnSingleEntry && ListEntry != &Directory->head)
310 {
311 /* there are more entries to enumerate but the buffer is already full.
312 only tell this to the user if he queries multiple entries */
313 Status = STATUS_MORE_ENTRIES;
314 }
315
316 if(NT_SUCCESS(Status) && nDirectories > 0)
317 {
318 PWSTR strbuf = (PWSTR)((POBJECT_DIRECTORY_INFORMATION)TemporaryBuffer + nDirectories + 1);
319 PWSTR deststrbuf = (PWSTR)((POBJECT_DIRECTORY_INFORMATION)Buffer + nDirectories + 1);
320 memset((POBJECT_DIRECTORY_INFORMATION)TemporaryBuffer + nDirectories, 0, sizeof(OBJECT_DIRECTORY_INFORMATION));
321
322 CopyBytes = (nDirectories + 1) * sizeof(OBJECT_DIRECTORY_INFORMATION);
323
324 /* copy the names from the objects and append them to the list of the
325 objects. copy to the temporary buffer only because the directory
326 lock can't be released and the buffer might be pagable memory! */
327 for(DirInfo = (POBJECT_DIRECTORY_INFORMATION)TemporaryBuffer;
328 nDirectories > 0;
329 nDirectories--, DirInfo++)
330 {
331 ULONG NameLength;
332
333 if(DirInfo->ObjectName.Length > 0)
334 {
335 RtlCopyMemory(strbuf,
336 DirInfo->ObjectName.Buffer,
337 DirInfo->ObjectName.Length);
338 /* change the buffer pointer to the buffer */
339 DirInfo->ObjectName.Buffer = deststrbuf;
340 NameLength = DirInfo->ObjectName.Length / sizeof(WCHAR);
341 /* NULL-terminate the string */
342 strbuf[NameLength] = L'\0';
343 strbuf += NameLength + 1;
344 deststrbuf += NameLength + 1;
345
346 CopyBytes += (NameLength + 1) * sizeof(WCHAR);
347 }
348
349 RtlCopyMemory(strbuf,
350 DirInfo->ObjectTypeName.Buffer,
351 DirInfo->ObjectTypeName.Length);
352 /* change the buffer pointer to the buffer */
353 DirInfo->ObjectTypeName.Buffer = deststrbuf;
354 NameLength = DirInfo->ObjectTypeName.Length / sizeof(WCHAR);
355 /* NULL-terminate the string */
356 strbuf[NameLength] = L'\0';
357 strbuf += NameLength + 1;
358 deststrbuf += NameLength + 1;
359
360 CopyBytes += (NameLength + 1) * sizeof(WCHAR);
361 }
362 }
363
364 KeReleaseSpinLock(&Directory->Lock, OldLevel);
365 ObDereferenceObject(Directory);
366
367 if(NT_SUCCESS(Status) || ReturnSingleEntry)
368 {
369 _SEH_TRY
370 {
371 if(CopyBytes != 0)
372 {
373 RtlCopyMemory(Buffer,
374 TemporaryBuffer,
375 CopyBytes);
376 }
377 *Context = NextEntry;
378 if(ReturnLength != NULL)
379 {
380 *ReturnLength = RequiredSize;
381 }
382 }
383 _SEH_HANDLE
384 {
385 Status = _SEH_GetExceptionCode();
386 }
387 _SEH_END;
388 }
389
390 ExFreePool(TemporaryBuffer);
391 }
392 else
393 {
394 Status = STATUS_INSUFFICIENT_RESOURCES;
395 }
396 }
397
398 return Status;
399 }
400
401
402 /**********************************************************************
403 * NAME (EXPORTED as Zw)
404 * NtCreateDirectoryObject
405 *
406 * DESCRIPTION
407 * Creates or opens a directory object (a container for other
408 * objects).
409 *
410 * ARGUMENTS
411 * DirectoryHandle (OUT)
412 * Caller supplied storage for the handle of the
413 * directory.
414 *
415 * DesiredAccess
416 * Access desired to the directory.
417 *
418 * ObjectAttributes
419 * Object attributes initialized with
420 * InitializeObjectAttributes.
421 *
422 * RETURN VALUE
423 * Status.
424 */
425 NTSTATUS STDCALL
426 NtCreateDirectoryObject (OUT PHANDLE DirectoryHandle,
427 IN ACCESS_MASK DesiredAccess,
428 IN POBJECT_ATTRIBUTES ObjectAttributes)
429 {
430 PDIRECTORY_OBJECT Directory;
431 HANDLE hDirectory;
432 KPROCESSOR_MODE PreviousMode;
433 NTSTATUS Status = STATUS_SUCCESS;
434
435 PAGED_CODE();
436
437 DPRINT("NtCreateDirectoryObject(DirectoryHandle %x, "
438 "DesiredAccess %x, ObjectAttributes %x\n",
439 DirectoryHandle, DesiredAccess, ObjectAttributes);
440
441 PreviousMode = ExGetPreviousMode();
442
443 if(PreviousMode != KernelMode)
444 {
445 _SEH_TRY
446 {
447 ProbeForWriteHandle(DirectoryHandle);
448 }
449 _SEH_HANDLE
450 {
451 Status = _SEH_GetExceptionCode();
452 }
453 _SEH_END;
454
455 if(!NT_SUCCESS(Status))
456 {
457 DPRINT1("NtCreateDirectoryObject failed, Status: 0x%x\n", Status);
458 return Status;
459 }
460 }
461
462 Status = ObCreateObject(PreviousMode,
463 ObDirectoryType,
464 ObjectAttributes,
465 PreviousMode,
466 NULL,
467 sizeof(DIRECTORY_OBJECT),
468 0,
469 0,
470 (PVOID*)&Directory);
471
472 if(NT_SUCCESS(Status))
473 {
474 Status = ObInsertObject((PVOID)Directory,
475 NULL,
476 DesiredAccess,
477 0,
478 NULL,
479 &hDirectory);
480 if (!NT_SUCCESS(Status))
481 {
482 ObMakeTemporaryObject(Directory);
483 }
484 ObDereferenceObject(Directory);
485
486 if(NT_SUCCESS(Status))
487 {
488 _SEH_TRY
489 {
490 *DirectoryHandle = hDirectory;
491 }
492 _SEH_HANDLE
493 {
494 Status = _SEH_GetExceptionCode();
495 }
496 _SEH_END;
497 }
498 }
499
500 return Status;
501 }
502
503 /* EOF */