Merge 13511:13830 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 NTSTATUS Status = STATUS_SUCCESS;
173
174 PAGED_CODE();
175
176 PreviousMode = ExGetPreviousMode();
177
178 if(PreviousMode != KernelMode)
179 {
180 _SEH_TRY
181 {
182 /* a test showed that the Buffer pointer just has to be 16 bit aligned,
183 propably due to the fact that most information that needs to be copied
184 is unicode strings */
185 ProbeForWrite(Buffer,
186 BufferLength,
187 sizeof(WCHAR));
188 ProbeForWrite(Context,
189 sizeof(ULONG),
190 sizeof(ULONG));
191 if(!RestartScan)
192 {
193 SkipEntries = *Context;
194 }
195 if(ReturnLength != NULL)
196 {
197 ProbeForWrite(ReturnLength,
198 sizeof(ULONG),
199 sizeof(ULONG));
200 }
201 }
202 _SEH_HANDLE
203 {
204 Status = _SEH_GetExceptionCode();
205 }
206 _SEH_END;
207
208 if(!NT_SUCCESS(Status))
209 {
210 DPRINT1("NtQueryDirectoryObject failed, Status: 0x%x\n", Status);
211 return Status;
212 }
213 }
214 else if(!RestartScan)
215 {
216 SkipEntries = *Context;
217 }
218
219 Status = ObReferenceObjectByHandle(DirectoryHandle,
220 DIRECTORY_QUERY,
221 ObDirectoryType,
222 PreviousMode,
223 (PVOID*)&Directory,
224 NULL);
225 if(NT_SUCCESS(Status))
226 {
227 PVOID TemporaryBuffer = ExAllocatePool(PagedPool,
228 BufferLength);
229 if(TemporaryBuffer != NULL)
230 {
231 POBJECT_HEADER EntryHeader;
232 PLIST_ENTRY ListEntry;
233 KIRQL OldLevel;
234 ULONG RequiredSize = 0;
235 ULONG nDirectories = 0;
236 POBJECT_DIRECTORY_INFORMATION DirInfo = (POBJECT_DIRECTORY_INFORMATION)TemporaryBuffer;
237
238 Status = STATUS_NO_MORE_ENTRIES;
239
240 KeAcquireSpinLock(&Directory->Lock, &OldLevel);
241
242 for(ListEntry = Directory->head.Flink;
243 ListEntry != &Directory->head;
244 ListEntry = ListEntry->Flink)
245 {
246 NextEntry++;
247 if(SkipEntries == 0)
248 {
249 PUNICODE_STRING Name, Type;
250 ULONG EntrySize;
251
252 EntryHeader = CONTAINING_RECORD(ListEntry, OBJECT_HEADER, Entry);
253
254 /* calculate the size of the required buffer space for this entry */
255 Name = (EntryHeader->Name.Length != 0 ? &EntryHeader->Name : NULL);
256 Type = &EntryHeader->ObjectType->TypeName;
257 EntrySize = sizeof(OBJECT_DIRECTORY_INFORMATION) +
258 ((Name != NULL) ? ((ULONG)Name->Length + sizeof(WCHAR)) : 0) +
259 (ULONG)EntryHeader->ObjectType->TypeName.Length + sizeof(WCHAR);
260
261 if(RequiredSize + EntrySize <= BufferLength)
262 {
263 /* the buffer is large enough to receive this entry. It would've
264 been much easier if the strings were directly appended to the
265 OBJECT_DIRECTORY_INFORMATION structured written into the buffer */
266 if(Name != NULL)
267 DirInfo->ObjectName = *Name;
268 else
269 {
270 DirInfo->ObjectName.Length = DirInfo->ObjectName.MaximumLength = 0;
271 DirInfo->ObjectName.Buffer = NULL;
272 }
273 DirInfo->ObjectTypeName = *Type;
274
275 nDirectories++;
276 RequiredSize += EntrySize;
277
278 Status = STATUS_SUCCESS;
279
280 if(ReturnSingleEntry)
281 {
282 /* we're only supposed to query one entry, so bail and copy the
283 strings to the buffer */
284 break;
285 }
286 DirInfo++;
287 }
288 else
289 {
290 if(ReturnSingleEntry)
291 {
292 /* the buffer is too small, so return the number of bytes that
293 would've been required for this query */
294 RequiredSize += EntrySize;
295 Status = STATUS_BUFFER_TOO_SMALL;
296 }
297
298 /* we couldn't query this entry, so leave the index that will be stored
299 in Context to this entry so the caller can query it the next time
300 he queries (hopefully with a buffer that is large enough then...) */
301 NextEntry--;
302
303 /* just copy the entries that fit into the buffer */
304 break;
305 }
306 }
307 else
308 {
309 /* skip the entry */
310 SkipEntries--;
311 }
312 }
313
314 if(!ReturnSingleEntry && ListEntry != &Directory->head)
315 {
316 /* there are more entries to enumerate but the buffer is already full.
317 only tell this to the user if he queries multiple entries */
318 Status = STATUS_MORE_ENTRIES;
319 }
320
321 if(NT_SUCCESS(Status))
322 {
323 if(nDirectories > 0)
324 {
325 _SEH_TRY
326 {
327 POBJECT_DIRECTORY_INFORMATION DestDirInfo = (POBJECT_DIRECTORY_INFORMATION)Buffer;
328 PWSTR strbuf = (PWSTR)((POBJECT_DIRECTORY_INFORMATION)Buffer + nDirectories);
329
330 /* copy all OBJECT_DIRECTORY_INFORMATION structures to the buffer and
331 just append all strings (whose pointers are stored in the buffer!)
332 and replace the pointers */
333 for(DirInfo = (POBJECT_DIRECTORY_INFORMATION)TemporaryBuffer;
334 nDirectories > 0;
335 nDirectories--, DirInfo++, DestDirInfo++)
336 {
337 if(DirInfo->ObjectName.Length > 0)
338 {
339 DestDirInfo->ObjectName.Length = DirInfo->ObjectName.Length;
340 DestDirInfo->ObjectName.MaximumLength = DirInfo->ObjectName.MaximumLength;
341 DestDirInfo->ObjectName.Buffer = strbuf;
342 RtlCopyMemory(strbuf,
343 DirInfo->ObjectName.Buffer,
344 DirInfo->ObjectName.Length);
345 /* NULL-terminate the string */
346 strbuf[DirInfo->ObjectName.Length / sizeof(WCHAR)] = L'\0';
347 strbuf += (DirInfo->ObjectName.Length / sizeof(WCHAR)) + 1;
348 }
349
350 DestDirInfo->ObjectTypeName.Length = DirInfo->ObjectTypeName.Length;
351 DestDirInfo->ObjectTypeName.MaximumLength = DirInfo->ObjectTypeName.MaximumLength;
352 DestDirInfo->ObjectTypeName.Buffer = strbuf;
353 RtlCopyMemory(strbuf,
354 DirInfo->ObjectTypeName.Buffer,
355 DirInfo->ObjectTypeName.Length);
356 /* NULL-terminate the string */
357 strbuf[DirInfo->ObjectTypeName.Length / sizeof(WCHAR)] = L'\0';
358 strbuf += (DirInfo->ObjectTypeName.Length / sizeof(WCHAR)) + 1;
359 }
360 }
361 _SEH_HANDLE
362 {
363 Status = _SEH_GetExceptionCode();
364 }
365 _SEH_END;
366 }
367 }
368
369 KeReleaseSpinLock(&Directory->Lock, OldLevel);
370 ObDereferenceObject(Directory);
371
372 ExFreePool(TemporaryBuffer);
373
374 if(NT_SUCCESS(Status) || ReturnSingleEntry)
375 {
376 _SEH_TRY
377 {
378 *Context = NextEntry;
379 if(ReturnLength != NULL)
380 {
381 *ReturnLength = RequiredSize;
382 }
383 }
384 _SEH_HANDLE
385 {
386 Status = _SEH_GetExceptionCode();
387 }
388 _SEH_END;
389 }
390 }
391 else
392 {
393 Status = STATUS_INSUFFICIENT_RESOURCES;
394 }
395 }
396
397 return Status;
398 }
399
400
401 /**********************************************************************
402 * NAME (EXPORTED as Zw)
403 * NtCreateDirectoryObject
404 *
405 * DESCRIPTION
406 * Creates or opens a directory object (a container for other
407 * objects).
408 *
409 * ARGUMENTS
410 * DirectoryHandle (OUT)
411 * Caller supplied storage for the handle of the
412 * directory.
413 *
414 * DesiredAccess
415 * Access desired to the directory.
416 *
417 * ObjectAttributes
418 * Object attributes initialized with
419 * InitializeObjectAttributes.
420 *
421 * RETURN VALUE
422 * Status.
423 */
424 NTSTATUS STDCALL
425 NtCreateDirectoryObject (OUT PHANDLE DirectoryHandle,
426 IN ACCESS_MASK DesiredAccess,
427 IN POBJECT_ATTRIBUTES ObjectAttributes)
428 {
429 PDIRECTORY_OBJECT Directory;
430 HANDLE hDirectory;
431 KPROCESSOR_MODE PreviousMode;
432 NTSTATUS Status = STATUS_SUCCESS;
433
434 PAGED_CODE();
435
436 DPRINT("NtCreateDirectoryObject(DirectoryHandle %x, "
437 "DesiredAccess %x, ObjectAttributes %x\n",
438 DirectoryHandle, DesiredAccess, ObjectAttributes);
439
440 PreviousMode = ExGetPreviousMode();
441
442 if(PreviousMode != KernelMode)
443 {
444 _SEH_TRY
445 {
446 ProbeForWrite(DirectoryHandle,
447 sizeof(HANDLE),
448 sizeof(ULONG));
449 }
450 _SEH_HANDLE
451 {
452 Status = _SEH_GetExceptionCode();
453 }
454 _SEH_END;
455
456 if(!NT_SUCCESS(Status))
457 {
458 DPRINT1("NtCreateDirectoryObject failed, Status: 0x%x\n", Status);
459 return Status;
460 }
461 }
462
463 Status = ObCreateObject(PreviousMode,
464 ObDirectoryType,
465 ObjectAttributes,
466 PreviousMode,
467 NULL,
468 sizeof(DIRECTORY_OBJECT),
469 0,
470 0,
471 (PVOID*)&Directory);
472 if(NT_SUCCESS(Status))
473 {
474 Status = ObInsertObject((PVOID)Directory,
475 NULL,
476 DesiredAccess,
477 0,
478 NULL,
479 &hDirectory);
480 ObDereferenceObject(Directory);
481
482 if(NT_SUCCESS(Status))
483 {
484 _SEH_TRY
485 {
486 *DirectoryHandle = hDirectory;
487 }
488 _SEH_HANDLE
489 {
490 Status = _SEH_GetExceptionCode();
491 }
492 _SEH_END;
493 }
494 }
495
496 return Status;
497 }
498
499 /* EOF */