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