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