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
8 * PROGRAMMERS: David Welch (welch@mcmail.com)
11 /* INCLUDES ***************************************************************/
15 #include <internal/debug.h>
18 /* FUNCTIONS **************************************************************/
21 /**********************************************************************
23 * NtOpenDirectoryObject
26 * Opens a namespace directory object.
29 * DirectoryHandle (OUT)
30 * Variable which receives the directory handle.
33 * Desired access to the directory.
36 * Structure describing the directory.
45 NtOpenDirectoryObject (OUT PHANDLE DirectoryHandle
,
46 IN ACCESS_MASK DesiredAccess
,
47 IN POBJECT_ATTRIBUTES ObjectAttributes
)
50 KPROCESSOR_MODE PreviousMode
;
51 NTSTATUS Status
= STATUS_SUCCESS
;
55 PreviousMode
= ExGetPreviousMode();
57 if(PreviousMode
!= KernelMode
)
61 ProbeForWriteHandle(DirectoryHandle
);
65 Status
= _SEH_GetExceptionCode();
69 if(!NT_SUCCESS(Status
))
71 DPRINT1("NtOpenDirectoryObject failed, Status: 0x%x\n", Status
);
76 Status
= ObOpenObjectByName(ObjectAttributes
,
83 if(NT_SUCCESS(Status
))
87 *DirectoryHandle
= hDirectory
;
91 Status
= _SEH_GetExceptionCode();
100 /**********************************************************************
102 * NtQueryDirectoryObject
105 * Reads information from a directory in the system namespace.
109 * Handle, obtained with NtOpenDirectoryObject(), which
110 * must grant DIRECTORY_QUERY access to the directory
114 * Buffer to hold the data read.
117 * Size of the buffer in bytes.
120 * When TRUE, only 1 entry is written in DirObjInformation;
121 * otherwise as many as will fit in the buffer.
124 * If TRUE start reading at index 0.
125 * If FALSE start reading at the index specified
126 * by object index *ObjectIndex.
129 * Zero based index into the directory, interpretation
130 * depends on RestartScan.
133 * Caller supplied storage for the number of bytes
137 * STATUS_SUCCESS - At least one (possibly more, depending on
138 * parameters and buffer size) dir entry is
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
146 * Other - Status code
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
158 NtQueryDirectoryObject (IN HANDLE DirectoryHandle
,
160 IN ULONG BufferLength
,
161 IN BOOLEAN ReturnSingleEntry
,
162 IN BOOLEAN RestartScan
,
163 IN OUT PULONG Context
,
164 OUT PULONG ReturnLength OPTIONAL
)
166 PDIRECTORY_OBJECT Directory
;
167 KPROCESSOR_MODE PreviousMode
;
168 ULONG SkipEntries
= 0;
171 NTSTATUS Status
= STATUS_SUCCESS
;
175 PreviousMode
= ExGetPreviousMode();
177 if(PreviousMode
!= KernelMode
)
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
,
187 ProbeForWriteUlong(Context
);
190 SkipEntries
= *Context
;
192 if(ReturnLength
!= NULL
)
194 ProbeForWriteUlong(ReturnLength
);
199 Status
= _SEH_GetExceptionCode();
203 if(!NT_SUCCESS(Status
))
205 DPRINT1("NtQueryDirectoryObject failed, Status: 0x%x\n", Status
);
209 else if(!RestartScan
)
211 SkipEntries
= *Context
;
214 Status
= ObReferenceObjectByHandle(DirectoryHandle
,
220 if(NT_SUCCESS(Status
))
222 PVOID TemporaryBuffer
= ExAllocatePool(NonPagedPool
,
224 if(TemporaryBuffer
!= NULL
)
226 POBJECT_HEADER EntryHeader
;
227 PLIST_ENTRY ListEntry
;
229 ULONG RequiredSize
= sizeof(OBJECT_DIRECTORY_INFORMATION
);
230 ULONG nDirectories
= 0;
231 POBJECT_DIRECTORY_INFORMATION DirInfo
= (POBJECT_DIRECTORY_INFORMATION
)TemporaryBuffer
;
233 Status
= STATUS_NO_MORE_ENTRIES
;
235 KeAcquireSpinLock(&Directory
->Lock
, &OldLevel
);
237 for(ListEntry
= Directory
->head
.Flink
;
238 ListEntry
!= &Directory
->head
;
239 ListEntry
= ListEntry
->Flink
)
244 PUNICODE_STRING Name
, Type
;
247 EntryHeader
= CONTAINING_RECORD(ListEntry
, OBJECT_HEADER
, Entry
);
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
);
256 if(RequiredSize
+ EntrySize
<= BufferLength
)
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 */
262 DirInfo
->ObjectName
= *Name
;
265 DirInfo
->ObjectName
.Length
= DirInfo
->ObjectName
.MaximumLength
= 0;
266 DirInfo
->ObjectName
.Buffer
= NULL
;
268 DirInfo
->ObjectTypeName
= *Type
;
271 RequiredSize
+= EntrySize
;
273 Status
= STATUS_SUCCESS
;
275 if(ReturnSingleEntry
)
277 /* we're only supposed to query one entry, so bail and copy the
278 strings to the buffer */
285 if(ReturnSingleEntry
)
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
;
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...) */
298 /* just copy the entries that fit into the buffer */
309 if(!ReturnSingleEntry
&& ListEntry
!= &Directory
->head
)
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
;
316 if(NT_SUCCESS(Status
) && nDirectories
> 0)
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
));
322 CopyBytes
= (nDirectories
+ 1) * sizeof(OBJECT_DIRECTORY_INFORMATION
);
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
;
329 nDirectories
--, DirInfo
++)
333 if(DirInfo
->ObjectName
.Length
> 0)
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;
346 CopyBytes
+= (NameLength
+ 1) * sizeof(WCHAR
);
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;
360 CopyBytes
+= (NameLength
+ 1) * sizeof(WCHAR
);
364 KeReleaseSpinLock(&Directory
->Lock
, OldLevel
);
365 ObDereferenceObject(Directory
);
367 if(NT_SUCCESS(Status
) || ReturnSingleEntry
)
373 RtlCopyMemory(Buffer
,
377 *Context
= NextEntry
;
378 if(ReturnLength
!= NULL
)
380 *ReturnLength
= RequiredSize
;
385 Status
= _SEH_GetExceptionCode();
390 ExFreePool(TemporaryBuffer
);
394 Status
= STATUS_INSUFFICIENT_RESOURCES
;
402 /**********************************************************************
403 * NAME (EXPORTED as Zw)
404 * NtCreateDirectoryObject
407 * Creates or opens a directory object (a container for other
411 * DirectoryHandle (OUT)
412 * Caller supplied storage for the handle of the
416 * Access desired to the directory.
419 * Object attributes initialized with
420 * InitializeObjectAttributes.
426 NtCreateDirectoryObject (OUT PHANDLE DirectoryHandle
,
427 IN ACCESS_MASK DesiredAccess
,
428 IN POBJECT_ATTRIBUTES ObjectAttributes
)
430 PDIRECTORY_OBJECT Directory
;
432 KPROCESSOR_MODE PreviousMode
;
433 NTSTATUS Status
= STATUS_SUCCESS
;
437 DPRINT("NtCreateDirectoryObject(DirectoryHandle %x, "
438 "DesiredAccess %x, ObjectAttributes %x\n",
439 DirectoryHandle
, DesiredAccess
, ObjectAttributes
);
441 PreviousMode
= ExGetPreviousMode();
443 if(PreviousMode
!= KernelMode
)
447 ProbeForWriteHandle(DirectoryHandle
);
451 Status
= _SEH_GetExceptionCode();
455 if(!NT_SUCCESS(Status
))
457 DPRINT1("NtCreateDirectoryObject failed, Status: 0x%x\n", Status
);
462 Status
= ObCreateObject(PreviousMode
,
467 sizeof(DIRECTORY_OBJECT
),
472 if(NT_SUCCESS(Status
))
474 Status
= ObInsertObject((PVOID
)Directory
,
480 if (!NT_SUCCESS(Status
))
482 ObMakeTemporaryObject(Directory
);
484 ObDereferenceObject(Directory
);
486 if(NT_SUCCESS(Status
))
490 *DirectoryHandle
= hDirectory
;
494 Status
= _SEH_GetExceptionCode();