1 /* $Id: dirobj.c,v 1.25 2004/08/24 17:07:27 navaraf Exp $
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)
12 /* INCLUDES ***************************************************************/
16 #include <internal/debug.h>
19 /* FUNCTIONS **************************************************************/
22 /**********************************************************************
24 * NtOpenDirectoryObject
27 * Opens a namespace directory object.
30 * DirectoryHandle (OUT)
31 * Variable which receives the directory handle.
34 * Desired access to the directory.
37 * Structure describing the directory.
46 NtOpenDirectoryObject (OUT PHANDLE DirectoryHandle
,
47 IN ACCESS_MASK DesiredAccess
,
48 IN POBJECT_ATTRIBUTES ObjectAttributes
)
55 Status
= ObReferenceObjectByName(ObjectAttributes
->ObjectName
,
56 ObjectAttributes
->Attributes
,
63 if (!NT_SUCCESS(Status
))
68 Status
= ObCreateHandle(PsGetCurrentProcess(),
73 return STATUS_SUCCESS
;
77 CopyDirectoryString(PUNICODE_STRING UnsafeTarget
, PUNICODE_STRING Source
, PUCHAR
*Buffer
)
79 UNICODE_STRING Target
;
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
))
91 Status
= MmCopyToCaller(*Buffer
, Source
->Buffer
, Source
->Length
);
92 if (! NT_SUCCESS(Status
))
96 *Buffer
+= Source
->Length
;
98 Status
= MmCopyToCaller(*Buffer
, &NullWchar
, sizeof(WCHAR
));
99 if (! NT_SUCCESS(Status
))
103 *Buffer
+= sizeof(WCHAR
);
105 return STATUS_SUCCESS
;
109 /**********************************************************************
111 * NtQueryDirectoryObject
114 * Reads information from a directory in the system namespace.
118 * Handle, obtained with NtOpenDirectoryObject(), which
119 * must grant DIRECTORY_QUERY access to the directory
123 * Buffer to hold the data read.
126 * Size of the buffer in bytes.
129 * When TRUE, only 1 entry is written in DirObjInformation;
130 * otherwise as many as will fit in the buffer.
133 * If TRUE start reading at index 0.
134 * If FALSE start reading at the index specified
135 * by object index *ObjectIndex.
138 * Zero based index into the directory, interpretation
139 * depends on RestartScan.
142 * Caller supplied storage for the number of bytes
146 * STATUS_SUCCESS - At least one (possibly more, depending on
147 * parameters and buffer size) dir entry is
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
155 * Other - Status code
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
167 NtQueryDirectoryObject (IN HANDLE DirectoryHandle
,
169 IN ULONG BufferLength
,
170 IN BOOLEAN ReturnSingleEntry
,
171 IN BOOLEAN RestartScan
,
172 IN OUT PULONG UnsafeContext
,
173 OUT PULONG UnsafeReturnLength OPTIONAL
)
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 PDIRECTORY_BASIC_INFORMATION current_odi
= (PDIRECTORY_BASIC_INFORMATION
) Buffer
;
183 DIRECTORY_BASIC_INFORMATION ZeroOdi
;
184 PUCHAR FirstFree
= (PUCHAR
) Buffer
;
190 DPRINT("NtQueryDirectoryObject(DirectoryHandle %x)\n", DirectoryHandle
);
192 /* Check Context is not NULL */
193 if (NULL
== UnsafeContext
)
195 return STATUS_INVALID_PARAMETER
;
198 /* Reference the DIRECTORY_OBJECT */
199 Status
= ObReferenceObjectByHandle(DirectoryHandle
,
205 if (!NT_SUCCESS(Status
))
210 KeAcquireSpinLock(&dir
->Lock
, &OldLevel
);
213 * Optionally, skip over some entries at the start of the directory
214 * (use *ObjectIndex value)
216 start_entry
= dir
->head
.Flink
;
219 register ULONG EntriesToSkip
;
221 Status
= MmCopyFromCaller(&Context
, UnsafeContext
, sizeof(ULONG
));
222 if (! NT_SUCCESS(Status
))
224 KeReleaseSpinLock(&dir
->Lock
, OldLevel
);
225 ObDereferenceObject(dir
);
228 EntriesToSkip
= Context
;
232 for (; 0 != EntriesToSkip
-- && start_entry
!= &dir
->head
;
233 start_entry
= start_entry
->Flink
)
237 if ((0 != EntriesToSkip
) && (start_entry
== &dir
->head
))
239 KeReleaseSpinLock(&dir
->Lock
, OldLevel
);
240 ObDereferenceObject(dir
);
241 return STATUS_NO_MORE_ENTRIES
;
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)
250 /* For the end sentenil */
251 RequiredSize
= sizeof(DIRECTORY_BASIC_INFORMATION
);
252 for (current_entry
= start_entry
;
253 current_entry
!= &dir
->head
;
254 current_entry
= current_entry
->Flink
)
256 current
= CONTAINING_RECORD(current_entry
, OBJECT_HEADER
, Entry
);
258 RequiredSize
+= sizeof(DIRECTORY_BASIC_INFORMATION
) +
259 current
->Name
.Length
+ sizeof(WCHAR
) +
260 current
->ObjectType
->TypeName
.Length
+ sizeof(WCHAR
);
261 if (RequiredSize
<= BufferLength
&&
262 (! ReturnSingleEntry
|| DirectoryCount
< 1))
269 * If there's no room to even copy a single entry then return error
272 if (0 == DirectoryCount
&&
273 !(IsListEmpty(&dir
->head
) && BufferLength
>= RequiredSize
))
275 KeReleaseSpinLock(&dir
->Lock
, OldLevel
);
276 ObDereferenceObject(dir
);
277 if (NULL
!= UnsafeReturnLength
)
279 Status
= MmCopyToCaller(UnsafeReturnLength
, &RequiredSize
, sizeof(ULONG
));
281 return NT_SUCCESS(Status
) ? STATUS_BUFFER_TOO_SMALL
: Status
;
285 * Move FirstFree to point to the Unicode strings area
287 FirstFree
+= (DirectoryCount
+ 1) * sizeof(DIRECTORY_BASIC_INFORMATION
);
289 /* Scan the directory */
290 current_entry
= start_entry
;
291 for (DirectoryIndex
= 0; DirectoryIndex
< DirectoryCount
; DirectoryIndex
++)
293 current
= CONTAINING_RECORD(current_entry
, OBJECT_HEADER
, Entry
);
296 * Copy the current directory entry's data into the buffer
297 * and update the OBJDIR_INFORMATION entry in the array.
299 /* --- Object's name --- */
300 Status
= CopyDirectoryString(¤t_odi
->ObjectName
, ¤t
->Name
, &FirstFree
);
301 if (! NT_SUCCESS(Status
))
303 KeReleaseSpinLock(&dir
->Lock
, OldLevel
);
304 ObDereferenceObject(dir
);
307 /* --- Object type's name --- */
308 Status
= CopyDirectoryString(¤t_odi
->ObjectTypeName
, ¤t
->ObjectType
->TypeName
, &FirstFree
);
309 if (! NT_SUCCESS(Status
))
311 KeReleaseSpinLock(&dir
->Lock
, OldLevel
);
312 ObDereferenceObject(dir
);
316 /* Next entry in the array */
318 /* Next object in the directory */
319 current_entry
= current_entry
->Flink
;
323 * Don't need dir object anymore
325 KeReleaseSpinLock(&dir
->Lock
, OldLevel
);
326 ObDereferenceObject(dir
);
328 /* Terminate with all zero entry */
329 memset(&ZeroOdi
, '\0', sizeof(DIRECTORY_BASIC_INFORMATION
));
330 Status
= MmCopyToCaller(current_odi
, &ZeroOdi
, sizeof(DIRECTORY_BASIC_INFORMATION
));
331 if (! NT_SUCCESS(Status
))
337 * Store current index in Context
341 Context
= DirectoryCount
;
345 Context
+= DirectoryCount
;
347 Status
= MmCopyToCaller(UnsafeContext
, &Context
, sizeof(ULONG
));
348 if (! NT_SUCCESS(Status
))
354 * Report to the caller how much bytes
355 * we wrote in the user buffer.
357 if (NULL
!= UnsafeReturnLength
)
359 NewValue
= FirstFree
- (PUCHAR
) Buffer
;
360 Status
= MmCopyToCaller(UnsafeReturnLength
, &NewValue
, sizeof(ULONG
));
361 if (! NT_SUCCESS(Status
))
371 /**********************************************************************
372 * NAME (EXPORTED as Zw)
373 * NtCreateDirectoryObject
376 * Creates or opens a directory object (a container for other
380 * DirectoryHandle (OUT)
381 * Caller supplied storage for the handle of the
385 * Access desired to the directory.
388 * Object attributes initialized with
389 * InitializeObjectAttributes.
395 NtCreateDirectoryObject (OUT PHANDLE DirectoryHandle
,
396 IN ACCESS_MASK DesiredAccess
,
397 IN POBJECT_ATTRIBUTES ObjectAttributes
)
399 PDIRECTORY_OBJECT DirectoryObject
;
402 DPRINT("NtCreateDirectoryObject(DirectoryHandle %x, "
403 "DesiredAccess %x, ObjectAttributes %x, "
404 "ObjectAttributes->ObjectName %wZ)\n",
405 DirectoryHandle
, DesiredAccess
, ObjectAttributes
,
406 ObjectAttributes
->ObjectName
);
408 Status
= ObCreateObject (ExGetPreviousMode(),
413 sizeof(DIRECTORY_OBJECT
),
416 (PVOID
*)&DirectoryObject
);
417 if (!NT_SUCCESS(Status
))
422 Status
= ObInsertObject ((PVOID
)DirectoryObject
,
429 ObDereferenceObject(DirectoryObject
);