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