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