Handle .msi files
[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(PagedPool,
223 BufferLength);
224 if(TemporaryBuffer != NULL)
225 {
226 POBJECT_HEADER EntryHeader;
227 PLIST_ENTRY ListEntry;
228 KIRQL OldLevel;
229 ULONG RequiredSize = 0;
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);
319 PWSTR deststrbuf = (PWSTR)((POBJECT_DIRECTORY_INFORMATION)Buffer + nDirectories);
320
321 CopyBytes = nDirectories * sizeof(OBJECT_DIRECTORY_INFORMATION);
322
323 /* copy the names from the objects and append them to the list of the
324 objects. copy to the temporary buffer only because the directory
325 lock can't be released and the buffer might be pagable memory! */
326 for(DirInfo = (POBJECT_DIRECTORY_INFORMATION)TemporaryBuffer;
327 nDirectories > 0;
328 nDirectories--, DirInfo++)
329 {
330 ULONG NameLength;
331
332 if(DirInfo->ObjectName.Length > 0)
333 {
334 RtlCopyMemory(strbuf,
335 DirInfo->ObjectName.Buffer,
336 DirInfo->ObjectName.Length);
337 /* change the buffer pointer to the buffer */
338 DirInfo->ObjectName.Buffer = deststrbuf;
339 NameLength = DirInfo->ObjectName.Length / sizeof(WCHAR);
340 /* NULL-terminate the string */
341 strbuf[NameLength] = L'\0';
342 strbuf += NameLength + 1;
343 deststrbuf += NameLength + 1;
344
345 CopyBytes += (NameLength + 1) * sizeof(WCHAR);
346 }
347
348 RtlCopyMemory(strbuf,
349 DirInfo->ObjectTypeName.Buffer,
350 DirInfo->ObjectTypeName.Length);
351 /* change the buffer pointer to the buffer */
352 DirInfo->ObjectTypeName.Buffer = deststrbuf;
353 NameLength = DirInfo->ObjectTypeName.Length / sizeof(WCHAR);
354 /* NULL-terminate the string */
355 strbuf[NameLength] = L'\0';
356 strbuf += NameLength + 1;
357 deststrbuf += NameLength + 1;
358
359 CopyBytes += (NameLength + 1) * sizeof(WCHAR);
360 }
361 }
362
363 KeReleaseSpinLock(&Directory->Lock, OldLevel);
364 ObDereferenceObject(Directory);
365
366 if(NT_SUCCESS(Status) || ReturnSingleEntry)
367 {
368 _SEH_TRY
369 {
370 if(CopyBytes != 0)
371 {
372 RtlCopyMemory(Buffer,
373 TemporaryBuffer,
374 CopyBytes);
375 }
376 *Context = NextEntry;
377 if(ReturnLength != NULL)
378 {
379 *ReturnLength = RequiredSize;
380 }
381 }
382 _SEH_HANDLE
383 {
384 Status = _SEH_GetExceptionCode();
385 }
386 _SEH_END;
387 }
388
389 ExFreePool(TemporaryBuffer);
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 ProbeForWriteHandle(DirectoryHandle);
447 }
448 _SEH_HANDLE
449 {
450 Status = _SEH_GetExceptionCode();
451 }
452 _SEH_END;
453
454 if(!NT_SUCCESS(Status))
455 {
456 DPRINT1("NtCreateDirectoryObject failed, Status: 0x%x\n", Status);
457 return Status;
458 }
459 }
460
461 Status = ObCreateObject(PreviousMode,
462 ObDirectoryType,
463 ObjectAttributes,
464 PreviousMode,
465 NULL,
466 sizeof(DIRECTORY_OBJECT),
467 0,
468 0,
469 (PVOID*)&Directory);
470
471 if(NT_SUCCESS(Status))
472 {
473 Status = ObInsertObject((PVOID)Directory,
474 NULL,
475 DesiredAccess,
476 0,
477 NULL,
478 &hDirectory);
479 if (!NT_SUCCESS(Status))
480 {
481 ObMakeTemporaryObject(Directory);
482 }
483 ObDereferenceObject(Directory);
484
485 if(NT_SUCCESS(Status))
486 {
487 _SEH_TRY
488 {
489 *DirectoryHandle = hDirectory;
490 }
491 _SEH_HANDLE
492 {
493 Status = _SEH_GetExceptionCode();
494 }
495 _SEH_END;
496 }
497 }
498
499 return Status;
500 }
501
502 /* EOF */