- Update to trunk
[reactos.git] / ntoskrnl / ob / obname.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ob/obname.c
5 * PURPOSE: Manages all functions related to the Object Manager name-
6 * space, such as finding objects or querying their names.
7 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
8 * Eric Kohl
9 * Thomas Weidenmueller (w3seek@reactos.org)
10 */
11
12 /* INCLUDES ******************************************************************/
13
14 #include <ntoskrnl.h>
15 #define NDEBUG
16 #include <debug.h>
17
18 BOOLEAN ObpCaseInsensitive = TRUE;
19 POBJECT_DIRECTORY ObpRootDirectoryObject;
20 POBJECT_DIRECTORY ObpTypeDirectoryObject;
21
22 /* DOS Device Prefix \??\ and \?? */
23 ALIGNEDNAME ObpDosDevicesShortNamePrefix = {{L'\\',L'?',L'?',L'\\'}};
24 ALIGNEDNAME ObpDosDevicesShortNameRoot = {{L'\\',L'?',L'?',L'\0'}};
25 UNICODE_STRING ObpDosDevicesShortName =
26 {
27 sizeof(ObpDosDevicesShortNamePrefix),
28 sizeof(ObpDosDevicesShortNamePrefix),
29 (PWSTR)&ObpDosDevicesShortNamePrefix
30 };
31
32 /* PRIVATE FUNCTIONS *********************************************************/
33
34 NTSTATUS
35 NTAPI
36 INIT_FUNCTION
37 ObpCreateDosDevicesDirectory(VOID)
38 {
39 OBJECT_ATTRIBUTES ObjectAttributes;
40 UNICODE_STRING Name, LinkName;
41 HANDLE Handle, SymHandle;
42 NTSTATUS Status;
43
44 /* Create the '\??' directory */
45 RtlInitUnicodeString(&Name, L"\\??");
46 InitializeObjectAttributes(&ObjectAttributes,
47 &Name,
48 OBJ_PERMANENT,
49 NULL,
50 NULL);
51 Status = NtCreateDirectoryObject(&Handle,
52 DIRECTORY_ALL_ACCESS,
53 &ObjectAttributes);
54 if (!NT_SUCCESS(Status)) return FALSE;
55
56 /* Initialize the GLOBALROOT path */
57 RtlInitUnicodeString(&LinkName, L"GLOBALROOT");
58 RtlInitUnicodeString(&Name, L"");
59 InitializeObjectAttributes(&ObjectAttributes,
60 &LinkName,
61 OBJ_PERMANENT,
62 Handle,
63 NULL);
64 Status = NtCreateSymbolicLinkObject(&SymHandle,
65 SYMBOLIC_LINK_ALL_ACCESS,
66 &ObjectAttributes,
67 &Name);
68 if (NT_SUCCESS(Status)) NtClose(SymHandle);
69
70 /* Link \??\Global to \?? */
71 RtlInitUnicodeString(&LinkName, L"Global");
72 RtlInitUnicodeString(&Name, L"\\??");
73 InitializeObjectAttributes(&ObjectAttributes,
74 &LinkName,
75 OBJ_PERMANENT,
76 Handle,
77 NULL);
78 Status = NtCreateSymbolicLinkObject(&SymHandle,
79 SYMBOLIC_LINK_ALL_ACCESS,
80 &ObjectAttributes,
81 &Name);
82 if (NT_SUCCESS(Status)) NtClose(SymHandle);
83
84 /* Close the directory handle */
85 NtClose(Handle);
86 if (!NT_SUCCESS(Status)) return Status;
87
88 /* Create link from '\DosDevices' to '\??' directory */
89 RtlCreateUnicodeString(&LinkName, L"\\DosDevices");
90 InitializeObjectAttributes(&ObjectAttributes,
91 &LinkName,
92 OBJ_PERMANENT,
93 NULL,
94 NULL);
95 Status = NtCreateSymbolicLinkObject(&SymHandle,
96 SYMBOLIC_LINK_ALL_ACCESS,
97 &ObjectAttributes,
98 &Name);
99 if (NT_SUCCESS(Status)) NtClose(SymHandle);
100
101 /* FIXME: Hack Hack! */
102 ObSystemDeviceMap = ExAllocatePoolWithTag(NonPagedPool,
103 sizeof(*ObSystemDeviceMap),
104 'mDbO');
105 if (!ObSystemDeviceMap) return STATUS_INSUFFICIENT_RESOURCES;
106 RtlZeroMemory(ObSystemDeviceMap, sizeof(*ObSystemDeviceMap));
107
108 /* Return status */
109 return Status;
110 }
111
112 VOID
113 NTAPI
114 ObDereferenceDeviceMap(IN PEPROCESS Process)
115 {
116 //KIRQL OldIrql;
117 PDEVICE_MAP DeviceMap = Process->DeviceMap;
118
119 /* FIXME: We don't use Process Devicemaps yet */
120 if (DeviceMap)
121 {
122 /* FIXME: Acquire the DeviceMap Spinlock */
123 // KeAcquireSpinLock(DeviceMap->Lock, &OldIrql);
124
125 /* Delete the device map link and dereference it */
126 Process->DeviceMap = NULL;
127 if (--DeviceMap->ReferenceCount)
128 {
129 /* Nobody is referencing it anymore, unlink the DOS directory */
130 DeviceMap->DosDevicesDirectory->DeviceMap = NULL;
131
132 /* FIXME: Release the DeviceMap Spinlock */
133 // KeReleasepinLock(DeviceMap->Lock, OldIrql);
134
135 /* Dereference the DOS Devices Directory and free the Device Map */
136 ObDereferenceObject(DeviceMap->DosDevicesDirectory);
137 ExFreePool(DeviceMap);
138 }
139 else
140 {
141 /* FIXME: Release the DeviceMap Spinlock */
142 // KeReleasepinLock(DeviceMap->Lock, OldIrql);
143 }
144 }
145 }
146
147 VOID
148 NTAPI
149 ObInheritDeviceMap(IN PEPROCESS Parent,
150 IN PEPROCESS Process)
151 {
152 /* FIXME: Devicemap Support */
153 }
154
155 /*++
156 * @name ObpDeleteNameCheck
157 *
158 * The ObpDeleteNameCheck routine checks if a named object should be
159 * removed from the object directory namespace.
160 *
161 * @param Object
162 * Pointer to the object to check for possible removal.
163 *
164 * @return None.
165 *
166 * @remarks An object is removed if the following 4 criteria are met:
167 * 1) The object has 0 handles open
168 * 2) The object is in the directory namespace and has a name
169 * 3) The object is not permanent
170 *
171 *--*/
172 VOID
173 NTAPI
174 ObpDeleteNameCheck(IN PVOID Object)
175 {
176 POBJECT_HEADER ObjectHeader;
177 OBP_LOOKUP_CONTEXT Context;
178 POBJECT_HEADER_NAME_INFO ObjectNameInfo;
179 POBJECT_TYPE ObjectType;
180 PVOID Directory = NULL;
181
182 /* Get object structures */
183 ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
184 ObjectNameInfo = ObpReferenceNameInfo(ObjectHeader);
185 ObjectType = ObjectHeader->Type;
186
187 /*
188 * Check if the handle count is 0, if the object is named,
189 * and if the object isn't a permanent object.
190 */
191 if (!(ObjectHeader->HandleCount) &&
192 (ObjectNameInfo) &&
193 (ObjectNameInfo->Name.Length) &&
194 (ObjectNameInfo->Directory) &&
195 !(ObjectHeader->Flags & OB_FLAG_PERMANENT))
196 {
197 /* Setup a lookup context */
198 ObpInitializeLookupContext(&Context);
199
200 /* Lock the directory */
201 ObpAcquireDirectoryLockExclusive(ObjectNameInfo->Directory, &Context);
202
203 /* Do the lookup */
204 Object = ObpLookupEntryDirectory(ObjectNameInfo->Directory,
205 &ObjectNameInfo->Name,
206 0,
207 FALSE,
208 &Context);
209 if (Object)
210 {
211 /* Lock the object */
212 ObpAcquireObjectLock(ObjectHeader);
213
214 /* Make sure we can still delete the object */
215 if (!(ObjectHeader->HandleCount) &&
216 !(ObjectHeader->Flags & OB_FLAG_PERMANENT))
217 {
218 /* First delete it from the directory */
219 ObpDeleteEntryDirectory(&Context);
220
221 /* Check if this is a symbolic link */
222 if (ObjectType == ObSymbolicLinkType)
223 {
224 /* Remove internal name */
225 ObpDeleteSymbolicLinkName(Object);
226 }
227
228 /* Check if the magic protection flag is set */
229 ObjectNameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
230 if ((ObjectNameInfo) &&
231 (ObjectNameInfo->QueryReferences & 0x40000000))
232 {
233 /* Remove protection flag */
234 InterlockedExchangeAdd((PLONG)&ObjectNameInfo->QueryReferences,
235 -0x40000000);
236 }
237
238 /* Get the directory */
239 Directory = ObjectNameInfo->Directory;
240 }
241
242 /* Release the lock */
243 ObpReleaseObjectLock(ObjectHeader);
244 }
245
246 /* Cleanup after lookup */
247 ObpReleaseLookupContext(&Context);
248
249 /* Remove another query reference since we added one on top */
250 ObpDereferenceNameInfo(ObjectNameInfo);
251
252 /* Check if we were inserted in a directory */
253 if (Directory)
254 {
255 /* We were, so first remove the extra reference we had added */
256 ObpDereferenceNameInfo(ObjectNameInfo);
257
258 /* Now dereference the object as well */
259 ObDereferenceObject(Object);
260 }
261 }
262 else
263 {
264 /* Remove the reference we added */
265 ObpDereferenceNameInfo(ObjectNameInfo);
266 }
267 }
268
269 NTSTATUS
270 NTAPI
271 ObpLookupObjectName(IN HANDLE RootHandle OPTIONAL,
272 IN PUNICODE_STRING ObjectName,
273 IN ULONG Attributes,
274 IN POBJECT_TYPE ObjectType,
275 IN KPROCESSOR_MODE AccessMode,
276 IN OUT PVOID ParseContext,
277 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
278 IN PVOID InsertObject OPTIONAL,
279 IN OUT PACCESS_STATE AccessState,
280 OUT POBP_LOOKUP_CONTEXT LookupContext,
281 OUT PVOID *FoundObject)
282 {
283 PVOID Object;
284 POBJECT_HEADER ObjectHeader;
285 UNICODE_STRING ComponentName, RemainingName;
286 BOOLEAN Reparse = FALSE, SymLink = FALSE;
287 POBJECT_DIRECTORY Directory = NULL, ParentDirectory = NULL, RootDirectory;
288 POBJECT_DIRECTORY ReferencedDirectory = NULL, ReferencedParentDirectory = NULL;
289 KIRQL CalloutIrql;
290 OB_PARSE_METHOD ParseRoutine;
291 NTSTATUS Status;
292 KPROCESSOR_MODE AccessCheckMode;
293 PWCHAR NewName;
294 POBJECT_HEADER_NAME_INFO ObjectNameInfo;
295 ULONG MaxReparse = 30;
296 PAGED_CODE();
297 OBTRACE(OB_NAMESPACE_DEBUG,
298 "%s - Finding Object: %wZ. Expecting: %p\n",
299 __FUNCTION__,
300 ObjectName,
301 InsertObject);
302
303 /* Initialize starting state */
304 ObpInitializeLookupContext(LookupContext);
305 *FoundObject = NULL;
306 Status = STATUS_SUCCESS;
307 Object = NULL;
308
309 /* Check if case-insensitivity is checked */
310 if (ObpCaseInsensitive)
311 {
312 /* Check if the object type requests this */
313 if (!(ObjectType) || (ObjectType->TypeInfo.CaseInsensitive))
314 {
315 /* Add the flag to disable case sensitivity */
316 Attributes |= OBJ_CASE_INSENSITIVE;
317 }
318 }
319
320 /* Check if this is a access checks are being forced */
321 AccessCheckMode = (Attributes & OBJ_FORCE_ACCESS_CHECK) ?
322 UserMode : AccessMode;
323
324 /* Check if we got a Root Directory */
325 if (RootHandle)
326 {
327 /* We did. Reference it */
328 Status = ObReferenceObjectByHandle(RootHandle,
329 0,
330 NULL,
331 AccessMode,
332 (PVOID*)&RootDirectory,
333 NULL);
334 if (!NT_SUCCESS(Status)) return Status;
335
336 /* Get the header */
337 ObjectHeader = OBJECT_TO_OBJECT_HEADER(RootDirectory);
338
339 /* The name cannot start with a separator, unless this is a file */
340 if ((ObjectName->Buffer) &&
341 (ObjectName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR) &&
342 (ObjectHeader->Type != IoFileObjectType))
343 {
344 /* The syntax is bad, so fail this request */
345 ObDereferenceObject(RootDirectory);
346 return STATUS_OBJECT_PATH_SYNTAX_BAD;
347 }
348
349 /* Don't parse a Directory */
350 if (ObjectHeader->Type != ObDirectoryType)
351 {
352 /* Make sure the Object Type has a parse routine */
353 ParseRoutine = ObjectHeader->Type->TypeInfo.ParseProcedure;
354 if (!ParseRoutine)
355 {
356 /* We can't parse a name if we don't have a parse routine */
357 ObDereferenceObject(RootDirectory);
358 return STATUS_INVALID_HANDLE;
359 }
360
361 /* Set default parse count */
362 MaxReparse = 30;
363
364 /* Now parse */
365 while (TRUE)
366 {
367 /* Start with the full name */
368 RemainingName = *ObjectName;
369
370 /* Call the Parse Procedure */
371 ObpCalloutStart(&CalloutIrql);
372 Status = ParseRoutine(RootDirectory,
373 ObjectType,
374 AccessState,
375 AccessCheckMode,
376 Attributes,
377 ObjectName,
378 &RemainingName,
379 ParseContext,
380 SecurityQos,
381 &Object);
382 ObpCalloutEnd(CalloutIrql, "Parse", ObjectHeader->Type, Object);
383
384 /* Check for success or failure, so not reparse */
385 if ((Status != STATUS_REPARSE) &&
386 (Status != STATUS_REPARSE_OBJECT))
387 {
388 /* Check for failure */
389 if (!NT_SUCCESS(Status))
390 {
391 /* Parse routine might not have cleared this, do it */
392 Object = NULL;
393 }
394 else if (!Object)
395 {
396 /* Modify status to reflect failure inside Ob */
397 Status = STATUS_OBJECT_NAME_NOT_FOUND;
398 }
399
400 /* We're done, return the status and object */
401 *FoundObject = Object;
402 ObDereferenceObject(RootDirectory);
403 return Status;
404 }
405 else if ((!ObjectName->Length) ||
406 (!ObjectName->Buffer) ||
407 (ObjectName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR))
408 {
409 /* Reparsed to the root directory, so start over */
410 ObDereferenceObject(RootDirectory);
411 RootDirectory = ObpRootDirectoryObject;
412
413 /* Don't use this anymore, since we're starting at root */
414 RootHandle = NULL;
415 goto ParseFromRoot;
416 }
417 else if (--MaxReparse)
418 {
419 /* Try reparsing again */
420 continue;
421 }
422 else
423 {
424 /* Reparsed too many times */
425 ObDereferenceObject(RootDirectory);
426
427 /* Return the object and normalized status */
428 *FoundObject = Object;
429 if (!Object) Status = STATUS_OBJECT_NAME_NOT_FOUND;
430 return Status;
431 }
432 }
433 }
434 else if (!(ObjectName->Length) || !(ObjectName->Buffer))
435 {
436 /* Just return the Root Directory if we didn't get a name*/
437 Status = ObReferenceObjectByPointer(RootDirectory,
438 0,
439 ObjectType,
440 AccessMode);
441 if (NT_SUCCESS(Status)) Object = RootDirectory;
442
443 /* Remove the first reference we added and return the object */
444 ObDereferenceObject(RootDirectory);
445 *FoundObject = Object;
446 return Status;
447 }
448 }
449 else
450 {
451 /* We did not get a Root Directory, so use the root */
452 RootDirectory = ObpRootDirectoryObject;
453
454 /* It must start with a path separator */
455 if (!(ObjectName->Length) ||
456 !(ObjectName->Buffer) ||
457 (ObjectName->Buffer[0] != OBJ_NAME_PATH_SEPARATOR))
458 {
459 /* This name is invalid, so fail */
460 return STATUS_OBJECT_PATH_SYNTAX_BAD;
461 }
462
463 /* Check if the name is only the path separator */
464 if (ObjectName->Length == sizeof(OBJ_NAME_PATH_SEPARATOR))
465 {
466 /* So the caller only wants the root directory; do we have one? */
467 if (!RootDirectory)
468 {
469 /* This must be the first time we're creating it... right? */
470 if (InsertObject)
471 {
472 /* Yes, so return it to ObInsert so that it can create it */
473 Status = ObReferenceObjectByPointer(InsertObject,
474 0,
475 ObjectType,
476 AccessMode);
477 if (NT_SUCCESS(Status)) *FoundObject = InsertObject;
478 return Status;
479 }
480 else
481 {
482 /* This should never really happen */
483 ASSERT(FALSE);
484 return STATUS_INVALID_PARAMETER;
485 }
486 }
487 else
488 {
489 /* We do have the root directory, so just return it */
490 Status = ObReferenceObjectByPointer(RootDirectory,
491 0,
492 ObjectType,
493 AccessMode);
494 if (NT_SUCCESS(Status)) *FoundObject = RootDirectory;
495 return Status;
496 }
497 }
498 else
499 {
500 ParseFromRoot:
501 /* FIXME: Check if we have a device map */
502
503 /* Check if this is a possible DOS name */
504 if (!((ULONG_PTR)(ObjectName->Buffer) & 7))
505 {
506 /*
507 * This could be one. Does it match the prefix?
508 * Note that as an optimization, the match is done as 64-bit
509 * compare since the prefix is "\??\" which is exactly 8 bytes.
510 *
511 * In the second branch, we test for "\??" which is also valid.
512 * This time, we use a 32-bit compare followed by a Unicode
513 * character compare (16-bit), since the sum is 6 bytes.
514 */
515 if ((ObjectName->Length >= ObpDosDevicesShortName.Length) &&
516 (*(PULONGLONG)(ObjectName->Buffer) ==
517 ObpDosDevicesShortNamePrefix.Alignment.QuadPart))
518 {
519 /* FIXME! */
520 }
521 else if ((ObjectName->Length == ObpDosDevicesShortName.Length -
522 sizeof(WCHAR)) &&
523 (*(PULONG)(ObjectName->Buffer) ==
524 ObpDosDevicesShortNameRoot.Alignment.LowPart) &&
525 (*((PWCHAR)(ObjectName->Buffer) + 2) ==
526 (WCHAR)(ObpDosDevicesShortNameRoot.Alignment.HighPart)))
527 {
528 /* FIXME! */
529 }
530 }
531 }
532 }
533
534 /* Check if we were reparsing a symbolic link */
535 if (!SymLink)
536 {
537 /* Allow reparse */
538 Reparse = TRUE;
539 MaxReparse = 30;
540 }
541
542 /* Reparse */
543 while (Reparse)
544 {
545 /* Get the name */
546 RemainingName = *ObjectName;
547
548 /* Disable reparsing again */
549 Reparse = FALSE;
550
551 /* Start parse loop */
552 while (TRUE)
553 {
554 /* Clear object */
555 Object = NULL;
556
557 /* Check if the name starts with a path separator */
558 if ((RemainingName.Length) &&
559 (RemainingName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR))
560 {
561 /* Skip the path separator */
562 RemainingName.Buffer++;
563 RemainingName.Length -= sizeof(OBJ_NAME_PATH_SEPARATOR);
564 }
565
566 /* Find the next Part Name */
567 ComponentName = RemainingName;
568 while (RemainingName.Length)
569 {
570 /* Break if we found the \ ending */
571 if (RemainingName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR) break;
572
573 /* Move on */
574 RemainingName.Buffer++;
575 RemainingName.Length -= sizeof(OBJ_NAME_PATH_SEPARATOR);
576 }
577
578 /* Get its size and make sure it's valid */
579 ComponentName.Length -= RemainingName.Length;
580 if (!ComponentName.Length)
581 {
582 /* Invalid size, fail */
583 Status = STATUS_OBJECT_NAME_INVALID;
584 break;
585 }
586
587 /* Check if we're in the root */
588 if (!Directory) Directory = RootDirectory;
589
590 /* Check if this is a user-mode call that needs to traverse */
591 if ((AccessCheckMode != KernelMode) &&
592 !(AccessState->Flags & TOKEN_HAS_TRAVERSE_PRIVILEGE))
593 {
594 /* We shouldn't have referenced a directory yet */
595 ASSERT(ReferencedDirectory == NULL);
596
597 /* Reference the directory */
598 ObReferenceObject(Directory);
599 ReferencedDirectory = Directory;
600
601 /* Check if we have a parent directory */
602 if (ParentDirectory)
603 {
604 /* Check for traverse access */
605 if (!ObpCheckTraverseAccess(ParentDirectory,
606 DIRECTORY_TRAVERSE,
607 AccessState,
608 FALSE,
609 AccessCheckMode,
610 &Status))
611 {
612 /* We don't have it, fail */
613 break;
614 }
615 }
616 }
617
618 /* Check if we don't have a remaining name yet */
619 if (!RemainingName.Length)
620 {
621 /* Check if we don't have a referenced directory yet */
622 if (!ReferencedDirectory)
623 {
624 /* Reference it */
625 ObReferenceObject(Directory);
626 ReferencedDirectory = Directory;
627 }
628
629 /* Check if we are inserting an object */
630 if (InsertObject)
631 {
632 /* Lock the directory */
633 ObpAcquireDirectoryLockExclusive(Directory, LookupContext);
634 }
635 }
636
637 /* Do the lookup */
638 Object = ObpLookupEntryDirectory(Directory,
639 &ComponentName,
640 Attributes,
641 InsertObject ? FALSE : TRUE,
642 LookupContext);
643 if (!Object)
644 {
645 /* We didn't find it... do we still have a path? */
646 if (RemainingName.Length)
647 {
648 /* Then tell the caller the path wasn't found */
649 Status = STATUS_OBJECT_PATH_NOT_FOUND;
650 break;
651 }
652 else if (!InsertObject)
653 {
654 /* Otherwise, we have a path, but the name isn't valid */
655 Status = STATUS_OBJECT_NAME_NOT_FOUND;
656 break;
657 }
658
659 /* Check create access for the object */
660 if (!ObCheckCreateObjectAccess(Directory,
661 ObjectType == ObDirectoryType ?
662 DIRECTORY_CREATE_SUBDIRECTORY :
663 DIRECTORY_CREATE_OBJECT,
664 AccessState,
665 &ComponentName,
666 FALSE,
667 AccessCheckMode,
668 &Status))
669 {
670 /* We don't have create access, fail */
671 break;
672 }
673
674 /* Get the object header */
675 ObjectHeader = OBJECT_TO_OBJECT_HEADER(InsertObject);
676
677 /* FIXME: Check if this is a Section Object or Sym Link */
678 /* FIXME: If it is, then check if this isn't session 0 */
679 /* FIXME: If it isn't, check for SeCreateGlobalPrivilege */
680 /* FIXME: If privilege isn't there, check for unsecure name */
681 /* FIXME: If it isn't a known unsecure name, then fail */
682
683 /* Create Object Name */
684 NewName = ExAllocatePoolWithTag(PagedPool,
685 ComponentName.Length,
686 OB_NAME_TAG);
687 if (!(NewName) ||
688 !(ObpInsertEntryDirectory(Directory,
689 LookupContext,
690 ObjectHeader)))
691 {
692 /* Either couldn't allocate the name, or insert failed */
693 if (NewName) ExFreePool(NewName);
694
695 /* Fail due to memory reasons */
696 Status = STATUS_INSUFFICIENT_RESOURCES;
697 break;
698 }
699
700 /* Reference newly to be inserted object */
701 ObReferenceObject(InsertObject);
702
703 /* Get the name information */
704 ObjectNameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
705
706 /* Reference the directory */
707 ObReferenceObject(Directory);
708
709 /* Copy the Name */
710 RtlCopyMemory(NewName,
711 ComponentName.Buffer,
712 ComponentName.Length);
713
714 /* Check if we had an old name */
715 if (ObjectNameInfo->Name.Buffer)
716 {
717 /* Free it */
718 ExFreePoolWithTag(ObjectNameInfo->Name.Buffer, OB_NAME_TAG );
719 }
720
721 /* Write new one */
722 ObjectNameInfo->Name.Buffer = NewName;
723 ObjectNameInfo->Name.Length = ComponentName.Length;
724 ObjectNameInfo->Name.MaximumLength = ComponentName.Length;
725
726 /* Return Status and the Expected Object */
727 Status = STATUS_SUCCESS;
728 Object = InsertObject;
729
730 /* Get out of here */
731 break;
732 }
733
734 ReparseObject:
735 /* We found it, so now get its header */
736 ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
737
738 /*
739 * Check for a parse Procedure, but don't bother to parse for an insert
740 * unless it's a Symbolic Link, in which case we MUST parse
741 */
742 ParseRoutine = ObjectHeader->Type->TypeInfo.ParseProcedure;
743 if ((ParseRoutine) &&
744 (!(InsertObject) || (ParseRoutine == ObpParseSymbolicLink)))
745 {
746 /* Use the Root Directory next time */
747 Directory = NULL;
748
749 /* Increment the pointer count */
750 InterlockedExchangeAdd(&ObjectHeader->PointerCount, 1);
751
752 /* Cleanup from the first lookup */
753 ObpReleaseLookupContext(LookupContext);
754
755 /* Check if we have a referenced directory */
756 if (ReferencedDirectory)
757 {
758 /* We do, dereference it */
759 ObDereferenceObject(ReferencedDirectory);
760 ReferencedDirectory = NULL;
761 }
762
763 /* Check if we have a referenced parent directory */
764 if (ReferencedParentDirectory)
765 {
766 /* We do, dereference it */
767 ObDereferenceObject(ReferencedParentDirectory);
768 ReferencedParentDirectory = NULL;
769 }
770
771 /* Call the Parse Procedure */
772 ObpCalloutStart(&CalloutIrql);
773 Status = ParseRoutine(Object,
774 ObjectType,
775 AccessState,
776 AccessCheckMode,
777 Attributes,
778 ObjectName,
779 &RemainingName,
780 ParseContext,
781 SecurityQos,
782 &Object);
783 ObpCalloutEnd(CalloutIrql, "Parse", ObjectHeader->Type, Object);
784
785 /* Remove our extra reference */
786 ObDereferenceObject(&ObjectHeader->Body);
787
788 /* Check if we have to reparse */
789 if ((Status == STATUS_REPARSE) ||
790 (Status == STATUS_REPARSE_OBJECT))
791 {
792 /* Reparse again */
793 Reparse = TRUE;
794
795 /* Start over from root if we got sent back there */
796 if ((Status == STATUS_REPARSE_OBJECT) ||
797 (ObjectName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR))
798 {
799 /* Check if we got a root directory */
800 if (RootHandle)
801 {
802 /* Stop using it, because we have a new directory now */
803 ObDereferenceObject(RootDirectory);
804 RootHandle = NULL;
805 }
806
807 /* Start at Root */
808 ParentDirectory = NULL;
809 RootDirectory = ObpRootDirectoryObject;
810
811 /* Check for reparse status */
812 if (Status == STATUS_REPARSE_OBJECT)
813 {
814 /* Don't reparse again */
815 Reparse = FALSE;
816
817 /* Did we actually get an object to which to reparse? */
818 if (!Object)
819 {
820 /* We didn't, so set a failure status */
821 Status = STATUS_OBJECT_NAME_NOT_FOUND;
822 }
823 else
824 {
825 /* We did, so we're free to parse the new object */
826 goto ReparseObject;
827 }
828 }
829 else
830 {
831 /* This is a symbolic link */
832 SymLink = TRUE;
833 goto ParseFromRoot;
834 }
835 }
836 else if (RootDirectory == ObpRootDirectoryObject)
837 {
838 /* We got STATUS_REPARSE but are at the Root Directory */
839 Object = NULL;
840 Status = STATUS_OBJECT_NAME_NOT_FOUND;
841 Reparse = FALSE;
842 }
843 }
844 else if (!NT_SUCCESS(Status))
845 {
846 /* Total failure */
847 Object = NULL;
848 }
849 else if (!Object)
850 {
851 /* We didn't reparse but we didn't find the Object Either */
852 Status = STATUS_OBJECT_NAME_NOT_FOUND;
853 }
854
855 /* Break out of the loop */
856 break;
857 }
858 else
859 {
860 /* No parse routine...do we still have a remaining name? */
861 if (!RemainingName.Length)
862 {
863 /* Are we creating an object? */
864 if (!InsertObject)
865 {
866 /* Check if this is a user-mode call that needs to traverse */
867 if ((AccessCheckMode != KernelMode) &&
868 !(AccessState->Flags & TOKEN_HAS_TRAVERSE_PRIVILEGE))
869 {
870 /* Check if we can get it */
871 if (!ObpCheckTraverseAccess(Directory,
872 DIRECTORY_TRAVERSE,
873 AccessState,
874 FALSE,
875 AccessCheckMode,
876 &Status))
877 {
878 /* We don't have access, fail */
879 Object = NULL;
880 break;
881 }
882 }
883
884 /* Reference the Object */
885 Status = ObReferenceObjectByPointer(Object,
886 0,
887 ObjectType,
888 AccessMode);
889 if (!NT_SUCCESS(Status)) Object = NULL;
890 }
891
892 /* And get out of the reparse loop */
893 break;
894 }
895 else
896 {
897 /* We still have a name; check if this is a directory object */
898 if (ObjectHeader->Type == ObDirectoryType)
899 {
900 /* Check if we have a referenced parent directory */
901 if (ReferencedParentDirectory)
902 {
903 /* Dereference it */
904 ObDereferenceObject(ReferencedParentDirectory);
905 }
906
907 /* Restart the lookup from this directory */
908 ReferencedParentDirectory = ReferencedDirectory;
909 ParentDirectory = Directory;
910 Directory = Object;
911 ReferencedDirectory = NULL;
912 }
913 else
914 {
915 /* We still have a name, but no parse routine for it */
916 Status = STATUS_OBJECT_TYPE_MISMATCH;
917 Object = NULL;
918 break;
919 }
920 }
921 }
922 }
923 }
924
925 /* Check if we failed */
926 if (!NT_SUCCESS(Status))
927 {
928 /* Cleanup after lookup */
929 ObpReleaseLookupContext(LookupContext);
930 }
931
932 /* Check if we have a device map and dereference it if so */
933 //if (DeviceMap) ObfDereferenceDeviceMap(DeviceMap);
934
935 /* Check if we have a referenced directory and dereference it if so */
936 if (ReferencedDirectory) ObDereferenceObject(ReferencedDirectory);
937
938 /* Check if we have a referenced parent directory */
939 if (ReferencedParentDirectory)
940 {
941 /* We do, dereference it */
942 ObDereferenceObject(ReferencedParentDirectory);
943 }
944
945 /* Set the found object and check if we got one */
946 *FoundObject = Object;
947 if (!Object)
948 {
949 /* Nothing was found. Did we reparse or get success? */
950 if ((Status == STATUS_REPARSE) || (NT_SUCCESS(Status)))
951 {
952 /* Set correct failure */
953 Status = STATUS_OBJECT_NAME_NOT_FOUND;
954 }
955 }
956
957 /* Check if we had a root directory */
958 if (RootHandle) ObDereferenceObject(RootDirectory);
959
960 /* Return status to caller */
961 OBTRACE(OB_NAMESPACE_DEBUG,
962 "%s - Found Object: %p. Expected: %p\n",
963 __FUNCTION__,
964 *FoundObject,
965 InsertObject);
966 return Status;
967 }
968
969 /* PUBLIC FUNCTIONS *********************************************************/
970
971 NTSTATUS
972 NTAPI
973 ObQueryNameString(IN PVOID Object,
974 OUT POBJECT_NAME_INFORMATION ObjectNameInfo,
975 IN ULONG Length,
976 OUT PULONG ReturnLength)
977 {
978 POBJECT_HEADER_NAME_INFO LocalInfo;
979 POBJECT_HEADER ObjectHeader;
980 POBJECT_DIRECTORY ParentDirectory;
981 ULONG NameSize;
982 PWCH ObjectName;
983 BOOLEAN ObjectIsNamed;
984
985 /* Get the Kernel Meta-Structures */
986 ObjectHeader = OBJECT_TO_OBJECT_HEADER(Object);
987 LocalInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
988
989 /* Check if a Query Name Procedure is available */
990 if (ObjectHeader->Type->TypeInfo.QueryNameProcedure)
991 {
992 /* Call the procedure */
993 ObjectIsNamed = ((LocalInfo) && (LocalInfo->Name.Length > 0));
994 return ObjectHeader->Type->TypeInfo.QueryNameProcedure(Object,
995 ObjectIsNamed,
996 ObjectNameInfo,
997 Length,
998 ReturnLength,
999 KernelMode);
1000 }
1001
1002 /* Check if the object doesn't even have a name */
1003 if (!(LocalInfo) || !(LocalInfo->Name.Buffer))
1004 {
1005 /* We're returning the name structure */
1006 *ReturnLength = sizeof(OBJECT_NAME_INFORMATION);
1007
1008 /* Check if we were given enough space */
1009 if (*ReturnLength > Length) return STATUS_INFO_LENGTH_MISMATCH;
1010
1011 /* Return an empty buffer */
1012 RtlInitEmptyUnicodeString(&ObjectNameInfo->Name, NULL, 0);
1013 return STATUS_SUCCESS;
1014 }
1015
1016 /*
1017 * Find the size needed for the name. We won't do
1018 * this during the Name Creation loop because we want
1019 * to let the caller know that the buffer isn't big
1020 * enough right at the beginning, not work our way through
1021 * and find out at the end
1022 */
1023 if (Object == ObpRootDirectoryObject)
1024 {
1025 /* Size of the '\' string */
1026 NameSize = sizeof(OBJ_NAME_PATH_SEPARATOR);
1027 }
1028 else
1029 {
1030 /* Get the Object Directory and add name of Object */
1031 ParentDirectory = LocalInfo->Directory;
1032 NameSize = sizeof(OBJ_NAME_PATH_SEPARATOR) + LocalInfo->Name.Length;
1033
1034 /* Loop inside the directory to get the top-most one (meaning root) */
1035 while ((ParentDirectory != ObpRootDirectoryObject) && (ParentDirectory))
1036 {
1037 /* Get the Name Information */
1038 LocalInfo = OBJECT_HEADER_TO_NAME_INFO(
1039 OBJECT_TO_OBJECT_HEADER(ParentDirectory));
1040
1041 /* Add the size of the Directory Name */
1042 if (LocalInfo && LocalInfo->Directory)
1043 {
1044 /* Size of the '\' string + Directory Name */
1045 NameSize += sizeof(OBJ_NAME_PATH_SEPARATOR) +
1046 LocalInfo->Name.Length;
1047
1048 /* Move to next parent Directory */
1049 ParentDirectory = LocalInfo->Directory;
1050 }
1051 else
1052 {
1053 /* Directory with no name. We append "...\" */
1054 NameSize += sizeof(L"...") + sizeof(OBJ_NAME_PATH_SEPARATOR);
1055 break;
1056 }
1057 }
1058 }
1059
1060 /* Finally, add the name of the structure and the null char */
1061 *ReturnLength = NameSize +
1062 sizeof(OBJECT_NAME_INFORMATION) +
1063 sizeof(UNICODE_NULL);
1064
1065 /* Check if we were given enough space */
1066 if (*ReturnLength > Length) return STATUS_INFO_LENGTH_MISMATCH;
1067
1068 /*
1069 * Now we will actually create the name. We work backwards because
1070 * it's easier to start off from the Name we have and walk up the
1071 * parent directories. We use the same logic as Name Length calculation.
1072 */
1073 LocalInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
1074 ObjectName = (PWCH)((ULONG_PTR)ObjectNameInfo + *ReturnLength);
1075 *--ObjectName = UNICODE_NULL;
1076
1077 /* Check if the object is actually the Root directory */
1078 if (Object == ObpRootDirectoryObject)
1079 {
1080 /* This is already the Root Directory, return "\\" */
1081 *--ObjectName = OBJ_NAME_PATH_SEPARATOR;
1082 ObjectNameInfo->Name.Length = (USHORT)NameSize;
1083 ObjectNameInfo->Name.MaximumLength = (USHORT)(NameSize +
1084 sizeof(UNICODE_NULL));
1085 ObjectNameInfo->Name.Buffer = ObjectName;
1086 return STATUS_SUCCESS;
1087 }
1088 else
1089 {
1090 /* Start by adding the Object's Name */
1091 ObjectName = (PWCH)((ULONG_PTR)ObjectName -
1092 LocalInfo->Name.Length);
1093 RtlCopyMemory(ObjectName,
1094 LocalInfo->Name.Buffer,
1095 LocalInfo->Name.Length);
1096
1097 /* Now parse the Parent directories until we reach the top */
1098 ParentDirectory = LocalInfo->Directory;
1099 while ((ParentDirectory != ObpRootDirectoryObject) && (ParentDirectory))
1100 {
1101 /* Get the name information */
1102 LocalInfo = OBJECT_HEADER_TO_NAME_INFO(
1103 OBJECT_TO_OBJECT_HEADER(ParentDirectory));
1104
1105 /* Add the "\" */
1106 *(--ObjectName) = OBJ_NAME_PATH_SEPARATOR;
1107
1108 /* Add the Parent Directory's Name */
1109 if (LocalInfo && LocalInfo->Name.Buffer)
1110 {
1111 /* Add the name */
1112 ObjectName = (PWCH)((ULONG_PTR)ObjectName -
1113 LocalInfo->Name.Length);
1114 RtlCopyMemory(ObjectName,
1115 LocalInfo->Name.Buffer,
1116 LocalInfo->Name.Length);
1117
1118 /* Move to next parent */
1119 ParentDirectory = LocalInfo->Directory;
1120 }
1121 else
1122 {
1123 /* Directory without a name, we add "..." */
1124 ObjectName -= sizeof(L"...");
1125 ObjectName = L"...";
1126 break;
1127 }
1128 }
1129
1130 /* Add Root Directory Name */
1131 *(--ObjectName) = OBJ_NAME_PATH_SEPARATOR;
1132 ObjectNameInfo->Name.Length = (USHORT)NameSize;
1133 ObjectNameInfo->Name.MaximumLength = (USHORT)(NameSize +
1134 sizeof(UNICODE_NULL));
1135 ObjectNameInfo->Name.Buffer = ObjectName;
1136 }
1137
1138 /* Return success */
1139 return STATUS_SUCCESS;
1140 }
1141
1142 VOID
1143 NTAPI
1144 ObQueryDeviceMapInformation(IN PEPROCESS Process,
1145 IN PPROCESS_DEVICEMAP_INFORMATION DeviceMapInfo)
1146 {
1147 //KIRQL OldIrql ;
1148
1149 /*
1150 * FIXME: This is an ugly hack for now, to always return the System Device Map
1151 * instead of returning the Process Device Map. Not important yet since we don't use it
1152 */
1153
1154 /* FIXME: Acquire the DeviceMap Spinlock */
1155 // KeAcquireSpinLock(DeviceMap->Lock, &OldIrql);
1156
1157 /* Make a copy */
1158 DeviceMapInfo->Query.DriveMap = ObSystemDeviceMap->DriveMap;
1159 RtlCopyMemory(DeviceMapInfo->Query.DriveType,
1160 ObSystemDeviceMap->DriveType,
1161 sizeof(ObSystemDeviceMap->DriveType));
1162
1163 /* FIXME: Release the DeviceMap Spinlock */
1164 // KeReleasepinLock(DeviceMap->Lock, OldIrql);
1165 }
1166
1167 /* EOF */