2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ob/oblink.c
5 * PURPOSE: Implements symbolic links
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
7 * David Welch (welch@mcmail.com)
10 /* INCLUDES *****************************************************************/
16 /* GLOBALS ******************************************************************/
18 POBJECT_TYPE ObSymbolicLinkType
= NULL
;
20 /* PRIVATE FUNCTIONS *********************************************************/
24 ObpDeleteSymbolicLinkName(IN POBJECT_SYMBOLIC_LINK SymbolicLink
)
26 POBJECT_HEADER ObjectHeader
;
27 POBJECT_HEADER_NAME_INFO ObjectNameInfo
;
29 /* FIXME: Need to support Device maps */
32 ObjectHeader
= OBJECT_TO_OBJECT_HEADER(SymbolicLink
);
33 ObjectNameInfo
= ObpReferenceNameInfo(ObjectHeader
);
35 /* Check if we are not actually in a directory with a device map */
36 if (!(ObjectNameInfo
) ||
37 !(ObjectNameInfo
->Directory
) /*||
38 !(ObjectNameInfo->Directory->DeviceMap)*/)
40 ObpDereferenceNameInfo(ObjectNameInfo
);
44 /* Check if it's a DOS drive letter, and remove the entry from drive map if needed */
45 if (SymbolicLink
->DosDeviceDriveIndex
!= 0 &&
46 ObjectNameInfo
->Name
.Length
== 2 * sizeof(WCHAR
) &&
47 ObjectNameInfo
->Name
.Buffer
[1] == L
':' &&
48 ( (ObjectNameInfo
->Name
.Buffer
[0] >= L
'A' &&
49 ObjectNameInfo
->Name
.Buffer
[0] <= L
'Z') ||
50 (ObjectNameInfo
->Name
.Buffer
[0] >= L
'a' &&
51 ObjectNameInfo
->Name
.Buffer
[0] <= L
'z') ))
53 /* Remove the drive entry */
54 KeAcquireGuardedMutex(&ObpDeviceMapLock
);
55 ObSystemDeviceMap
->DriveType
[SymbolicLink
->DosDeviceDriveIndex
-1] =
56 DOSDEVICE_DRIVE_UNKNOWN
;
57 ObSystemDeviceMap
->DriveMap
&=
58 ~(1 << (SymbolicLink
->DosDeviceDriveIndex
-1));
59 KeReleaseGuardedMutex(&ObpDeviceMapLock
);
61 /* Reset the drive index, valid drive index starts from 1 */
62 SymbolicLink
->DosDeviceDriveIndex
= 0;
65 ObpDereferenceNameInfo(ObjectNameInfo
);
70 ObpParseSymbolicLinkToIoDeviceObject(IN POBJECT_DIRECTORY SymbolicLinkDirectory
,
71 IN OUT POBJECT_DIRECTORY
*Directory
,
72 IN OUT PUNICODE_STRING TargetPath
,
73 IN OUT POBP_LOOKUP_CONTEXT Context
,
79 if (! TargetPath
|| ! Object
|| ! Context
|| ! Directory
||
80 ! SymbolicLinkDirectory
)
82 return STATUS_INVALID_PARAMETER
;
85 /* FIXME: Need to support Device maps */
87 /* Try walking the target path and open each part of the path */
88 while (TargetPath
->Length
)
90 /* Strip '\' if present at the beginning of the target path */
91 if (TargetPath
->Length
>= sizeof(OBJ_NAME_PATH_SEPARATOR
)&&
92 TargetPath
->Buffer
[0] == OBJ_NAME_PATH_SEPARATOR
)
95 TargetPath
->Length
-= sizeof(OBJ_NAME_PATH_SEPARATOR
);
98 /* Remember the current component of the target path */
101 /* Move forward to the next component of the target path */
102 while (TargetPath
->Length
>= sizeof(OBJ_NAME_PATH_SEPARATOR
))
104 if (TargetPath
->Buffer
[0] != OBJ_NAME_PATH_SEPARATOR
)
106 TargetPath
->Buffer
++;
107 TargetPath
->Length
-= sizeof(OBJ_NAME_PATH_SEPARATOR
);
113 Name
.Length
-= TargetPath
->Length
;
115 /* Finished processing the entire path, stop */
120 * Make sure a deadlock does not occur as an exclusive lock on a pushlock
121 * would have already taken one in ObpLookupObjectName() on the parent
122 * directory where the symlink is being created [ObInsertObject()].
123 * Prevent recursive locking by faking lock state in the lookup context
124 * when the current directory is same as the parent directory where
125 * the symlink is being created. If the lock state is not faked,
126 * ObpLookupEntryDirectory() will try to get a recursive lock on the
127 * pushlock and hang. For e.g. this happens when a substed drive is pointed to
128 * another substed drive.
130 if (*Directory
== SymbolicLinkDirectory
&& ! Context
->DirectoryLocked
)
132 /* Fake lock state so that ObpLookupEntryDirectory() doesn't attempt a lock */
134 Context
->DirectoryLocked
= TRUE
;
137 ManualUnlock
= FALSE
;
139 *Object
= ObpLookupEntryDirectory(*Directory
,
145 /* Locking was faked, undo it now */
146 if (*Directory
== SymbolicLinkDirectory
&& ManualUnlock
)
147 Context
->DirectoryLocked
= FALSE
;
149 /* Lookup failed, stop */
153 if (OBJECT_TO_OBJECT_HEADER(*Object
)->Type
== ObDirectoryType
)
155 /* Make this current directory, and continue search */
156 *Directory
= (POBJECT_DIRECTORY
)*Object
;
158 else if (OBJECT_TO_OBJECT_HEADER(*Object
)->Type
== ObSymbolicLinkType
&&
159 (((POBJECT_SYMBOLIC_LINK
)*Object
)->DosDeviceDriveIndex
== 0))
161 /* Symlink points to another initialized symlink, ask caller to reparse */
162 *Directory
= ObpRootDirectoryObject
;
163 TargetPath
= &((POBJECT_SYMBOLIC_LINK
)*Object
)->LinkTarget
;
164 return STATUS_REPARSE_OBJECT
;
168 /* Neither directory or symlink, stop */
173 /* Return a valid object, only if object type is IoDeviceObject */
175 OBJECT_TO_OBJECT_HEADER(*Object
)->Type
!= IoDeviceObjectType
)
179 return STATUS_SUCCESS
;
184 ObpCreateSymbolicLinkName(IN POBJECT_SYMBOLIC_LINK SymbolicLink
)
186 POBJECT_HEADER ObjectHeader
;
187 POBJECT_HEADER_NAME_INFO ObjectNameInfo
;
189 POBJECT_DIRECTORY Directory
;
190 UNICODE_STRING TargetPath
;
192 ULONG DriveType
= DOSDEVICE_DRIVE_CALCULATE
;
194 const ULONG MaxReparseAttempts
= 20;
195 OBP_LOOKUP_CONTEXT Context
;
197 /* FIXME: Need to support Device maps */
199 /* Get header data */
200 ObjectHeader
= OBJECT_TO_OBJECT_HEADER(SymbolicLink
);
201 ObjectNameInfo
= ObpReferenceNameInfo(ObjectHeader
);
203 /* Check if we are not actually in a directory with a device map */
204 if (!(ObjectNameInfo
) ||
205 !(ObjectNameInfo
->Directory
) /*||
206 !(ObjectNameInfo->Directory->DeviceMap)*/)
208 ObpDereferenceNameInfo(ObjectNameInfo
);
212 /* Check if it's a DOS drive letter, and set the drive index accordingly */
213 if (ObjectNameInfo
->Name
.Length
== 2 * sizeof(WCHAR
) &&
214 ObjectNameInfo
->Name
.Buffer
[1] == L
':' &&
215 ( (ObjectNameInfo
->Name
.Buffer
[0] >= L
'A' &&
216 ObjectNameInfo
->Name
.Buffer
[0] <= L
'Z') ||
217 (ObjectNameInfo
->Name
.Buffer
[0] >= L
'a' &&
218 ObjectNameInfo
->Name
.Buffer
[0] <= L
'z') ))
220 SymbolicLink
->DosDeviceDriveIndex
=
221 RtlUpcaseUnicodeChar(ObjectNameInfo
->Name
.Buffer
[0]) - L
'A';
222 /* The Drive index start from 1 */
223 SymbolicLink
->DosDeviceDriveIndex
++;
225 /* Initialize lookup context */
226 ObpInitializeLookupContext(&Context
);
228 /* Start the search from the root */
229 Directory
= ObpRootDirectoryObject
;
230 TargetPath
= SymbolicLink
->LinkTarget
;
233 * Locate the IoDeviceObject if any this symbolic link points to.
234 * To prevent endless reparsing, setting an upper limit on the
235 * number of reparses.
237 Status
= STATUS_REPARSE_OBJECT
;
239 while (Status
== STATUS_REPARSE_OBJECT
&&
240 ReparseCnt
< MaxReparseAttempts
)
243 ObpParseSymbolicLinkToIoDeviceObject(ObjectNameInfo
->Directory
,
248 if (Status
== STATUS_REPARSE_OBJECT
)
252 /* Cleanup lookup context */
253 ObpReleaseLookupContext(&Context
);
255 /* Error, or max resparse attemtps exceeded */
256 if (! NT_SUCCESS(Status
) || ReparseCnt
>= MaxReparseAttempts
)
259 ObpDereferenceNameInfo(ObjectNameInfo
);
265 /* Calculate the drive type */
266 switch(((PDEVICE_OBJECT
)Object
)->DeviceType
)
268 case FILE_DEVICE_VIRTUAL_DISK
:
269 DriveType
= DOSDEVICE_DRIVE_RAMDISK
;
271 case FILE_DEVICE_CD_ROM
:
272 case FILE_DEVICE_CD_ROM_FILE_SYSTEM
:
273 DriveType
= DOSDEVICE_DRIVE_CDROM
;
275 case FILE_DEVICE_DISK
:
276 case FILE_DEVICE_DISK_FILE_SYSTEM
:
277 case FILE_DEVICE_FILE_SYSTEM
:
278 if (((PDEVICE_OBJECT
)Object
)->Characteristics
& FILE_REMOVABLE_MEDIA
)
279 DriveType
= DOSDEVICE_DRIVE_REMOVABLE
;
281 DriveType
= DOSDEVICE_DRIVE_FIXED
;
283 case FILE_DEVICE_NETWORK
:
284 case FILE_DEVICE_NETWORK_FILE_SYSTEM
:
285 DriveType
= DOSDEVICE_DRIVE_REMOTE
;
288 DPRINT1("Device Type %lu for %wZ is not known or unhandled\n",
289 ((PDEVICE_OBJECT
)Object
)->DeviceType
,
290 &SymbolicLink
->LinkTarget
);
291 DriveType
= DOSDEVICE_DRIVE_UNKNOWN
;
295 /* Add a new drive entry */
296 KeAcquireGuardedMutex(&ObpDeviceMapLock
);
297 ObSystemDeviceMap
->DriveType
[SymbolicLink
->DosDeviceDriveIndex
-1] =
299 ObSystemDeviceMap
->DriveMap
|=
300 1 << (SymbolicLink
->DosDeviceDriveIndex
-1);
301 KeReleaseGuardedMutex(&ObpDeviceMapLock
);
305 ObpDereferenceNameInfo(ObjectNameInfo
);
309 * @name ObpDeleteSymbolicLink
311 * The ObpDeleteSymbolicLink routine <FILLMEIN>
323 ObpDeleteSymbolicLink(PVOID ObjectBody
)
325 POBJECT_SYMBOLIC_LINK SymlinkObject
= (POBJECT_SYMBOLIC_LINK
)ObjectBody
;
327 /* Make sure that the symbolic link has a name */
328 if (SymlinkObject
->LinkTarget
.Buffer
)
331 ExFreePool(SymlinkObject
->LinkTarget
.Buffer
);
332 SymlinkObject
->LinkTarget
.Buffer
= NULL
;
337 * @name ObpParseSymbolicLink
339 * The ObpParseSymbolicLink routine <FILLMEIN>
350 * @param RemainingPath
356 * @return STATUS_SUCCESS or appropriate error value.
363 ObpParseSymbolicLink(IN PVOID ParsedObject
,
365 IN OUT PACCESS_STATE AccessState
,
366 IN KPROCESSOR_MODE AccessMode
,
368 IN OUT PUNICODE_STRING FullPath
,
369 IN OUT PUNICODE_STRING RemainingName
,
370 IN OUT PVOID Context OPTIONAL
,
371 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL
,
372 OUT PVOID
*NextObject
)
374 POBJECT_SYMBOLIC_LINK SymlinkObject
= (POBJECT_SYMBOLIC_LINK
)ParsedObject
;
375 PUNICODE_STRING TargetPath
;
377 ULONG LengthUsed
, MaximumLength
, TempLength
;
384 /* Check if we're out of name to parse */
385 if (!RemainingName
->Length
)
387 /* Check if we got an object type */
390 /* Reference the object only */
391 Status
= ObReferenceObjectByPointer(ParsedObject
,
395 if (NT_SUCCESS(Status
))
398 *NextObject
= ParsedObject
;
401 if ((NT_SUCCESS(Status
)) || (Status
!= STATUS_OBJECT_TYPE_MISMATCH
))
408 else if (RemainingName
->Buffer
[0] != OBJ_NAME_PATH_SEPARATOR
)
410 /* Symbolic links must start with a backslash */
411 return STATUS_OBJECT_TYPE_MISMATCH
;
414 /* Check if this symlink is bound to a specific object */
415 if (SymlinkObject
->LinkTargetObject
)
420 /* Set the target path and length */
421 TargetPath
= &SymlinkObject
->LinkTarget
;
422 TempLength
= TargetPath
->Length
;
425 * Strip off the extra trailing '\', if we don't do this we will end up
426 * adding a extra '\' between TargetPath and RemainingName
427 * causing caller's like ObpLookupObjectName() to fail.
429 if (TempLength
&& RemainingName
->Length
)
431 /* The target and remaining names aren't empty, so check for slashes */
432 if ((TargetPath
->Buffer
[TempLength
/ sizeof(WCHAR
) - 1] ==
433 OBJ_NAME_PATH_SEPARATOR
) &&
434 (RemainingName
->Buffer
[0] == OBJ_NAME_PATH_SEPARATOR
))
436 /* Reduce the length by one to cut off the extra '\' */
437 TempLength
-= sizeof(OBJ_NAME_PATH_SEPARATOR
);
441 /* Calculate the new length */
442 LengthUsed
= TempLength
+ RemainingName
->Length
;
444 /* Check if it's not too much */
445 if (LengthUsed
> 0xFFF0)
446 return STATUS_NAME_TOO_LONG
;
448 /* Optimization: check if the new name is shorter */
449 if (FullPath
->MaximumLength
<= LengthUsed
)
451 /* It's not, allocate a new one */
452 MaximumLength
= LengthUsed
+ sizeof(WCHAR
);
453 NewTargetPath
= ExAllocatePoolWithTag(NonPagedPool
,
455 TAG_SYMLINK_TTARGET
);
456 if (!NewTargetPath
) return STATUS_INSUFFICIENT_RESOURCES
;
460 /* It is! Reuse the name... */
461 MaximumLength
= FullPath
->MaximumLength
;
462 NewTargetPath
= FullPath
->Buffer
;
465 /* Make sure we have a length */
466 if (RemainingName
->Length
)
468 /* Copy the new path */
469 RtlMoveMemory((PVOID
)((ULONG_PTR
)NewTargetPath
+ TempLength
),
470 RemainingName
->Buffer
,
471 RemainingName
->Length
);
474 /* Copy the target path and null-terminate it */
475 RtlCopyMemory(NewTargetPath
, TargetPath
->Buffer
, TempLength
);
476 NewTargetPath
[LengthUsed
/ sizeof(WCHAR
)] = UNICODE_NULL
;
478 /* If the optimization didn't work, free the old buffer */
479 if (NewTargetPath
!= FullPath
->Buffer
) ExFreePool(FullPath
->Buffer
);
481 /* Update the path values */
482 FullPath
->Length
= (USHORT
)LengthUsed
;
483 FullPath
->MaximumLength
= (USHORT
)MaximumLength
;
484 FullPath
->Buffer
= NewTargetPath
;
486 /* Tell the parse routine to start reparsing */
487 return STATUS_REPARSE
;
490 /* PUBLIC FUNCTIONS **********************************************************/
493 * @name NtCreateSymbolicLinkObject
496 * The NtCreateSymbolicLinkObject opens or creates a symbolic link object.
499 * Variable which receives the symlink handle.
501 * @param DesiredAccess
502 * Desired access to the symlink.
504 * @param ObjectAttributes
505 * Structure describing the symlink.
508 * Unicode string defining the symlink's target
510 * @return STATUS_SUCCESS or appropriate error value.
517 NtCreateSymbolicLinkObject(OUT PHANDLE LinkHandle
,
518 IN ACCESS_MASK DesiredAccess
,
519 IN POBJECT_ATTRIBUTES ObjectAttributes
,
520 IN PUNICODE_STRING LinkTarget
)
523 POBJECT_SYMBOLIC_LINK SymbolicLink
;
524 UNICODE_STRING CapturedLinkTarget
;
525 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
529 /* Check if we need to probe parameters */
530 if (PreviousMode
!= KernelMode
)
534 /* Probe the target */
535 CapturedLinkTarget
= ProbeForReadUnicodeString(LinkTarget
);
536 ProbeForRead(CapturedLinkTarget
.Buffer
,
537 CapturedLinkTarget
.MaximumLength
,
540 /* Probe the return handle */
541 ProbeForWriteHandle(LinkHandle
);
543 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
545 /* Return the exception code */
546 _SEH2_YIELD(return _SEH2_GetExceptionCode());
552 /* No need to capture */
553 CapturedLinkTarget
= *LinkTarget
;
556 /* Check if the maximum length is odd */
557 if (CapturedLinkTarget
.MaximumLength
% sizeof(WCHAR
))
560 CapturedLinkTarget
.MaximumLength
=
561 (USHORT
)ALIGN_DOWN(CapturedLinkTarget
.MaximumLength
, WCHAR
);
564 /* Fail if the length is odd, or if the maximum is smaller or 0 */
565 if ((CapturedLinkTarget
.Length
% sizeof(WCHAR
)) ||
566 (CapturedLinkTarget
.MaximumLength
< CapturedLinkTarget
.Length
) ||
567 !(CapturedLinkTarget
.MaximumLength
))
569 /* This message is displayed on the debugger in Windows */
570 DbgPrint("OB: Invalid symbolic link target - %wZ\n",
571 &CapturedLinkTarget
);
572 return STATUS_INVALID_PARAMETER
;
575 /* Create the object */
576 Status
= ObCreateObject(PreviousMode
,
581 sizeof(OBJECT_SYMBOLIC_LINK
),
584 (PVOID
*)&SymbolicLink
);
585 if (NT_SUCCESS(Status
))
587 /* Success! Fill in the creation time immediately */
588 KeQuerySystemTime(&SymbolicLink
->CreationTime
);
590 /* Setup the target name */
591 SymbolicLink
->LinkTarget
.Length
= CapturedLinkTarget
.Length
;
592 SymbolicLink
->LinkTarget
.MaximumLength
= CapturedLinkTarget
.Length
+
594 SymbolicLink
->LinkTarget
.Buffer
=
595 ExAllocatePoolWithTag(PagedPool
,
596 CapturedLinkTarget
.MaximumLength
,
598 if (!SymbolicLink
->LinkTarget
.Buffer
)
600 /* Dereference the symbolic link object and fail */
601 ObDereferenceObject(SymbolicLink
);
602 return STATUS_NO_MEMORY
;
606 RtlCopyMemory(SymbolicLink
->LinkTarget
.Buffer
,
607 CapturedLinkTarget
.Buffer
,
608 CapturedLinkTarget
.MaximumLength
);
610 /* Initialize the remaining name, dos drive index and target object */
611 SymbolicLink
->LinkTargetObject
= NULL
;
612 SymbolicLink
->DosDeviceDriveIndex
= 0;
613 RtlInitUnicodeString(&SymbolicLink
->LinkTargetRemaining
, NULL
);
615 /* Insert it into the object tree */
616 Status
= ObInsertObject(SymbolicLink
,
622 if (NT_SUCCESS(Status
))
626 /* Return the handle to caller */
629 _SEH2_EXCEPT(ExSystemExceptionFilter())
631 /* Get exception code */
632 Status
= _SEH2_GetExceptionCode();
638 /* Return status to caller */
643 * @name NtOpenSymbolicLinkObject
646 * The NtOpenSymbolicLinkObject opens a symbolic link object.
649 * Variable which receives the symlink handle.
651 * @param DesiredAccess
652 * Desired access to the symlink.
654 * @param ObjectAttributes
655 * Structure describing the symlink.
657 * @return STATUS_SUCCESS or appropriate error value.
664 NtOpenSymbolicLinkObject(OUT PHANDLE LinkHandle
,
665 IN ACCESS_MASK DesiredAccess
,
666 IN POBJECT_ATTRIBUTES ObjectAttributes
)
669 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
673 /* Check if we need to probe parameters */
674 if (PreviousMode
!= KernelMode
)
678 /* Probe the return handle */
679 ProbeForWriteHandle(LinkHandle
);
681 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
683 /* Return the exception code */
684 _SEH2_YIELD(return _SEH2_GetExceptionCode());
689 /* Open the object */
690 Status
= ObOpenObjectByName(ObjectAttributes
,
697 if (NT_SUCCESS(Status
))
701 /* Return the handle to caller */
704 _SEH2_EXCEPT(ExSystemExceptionFilter())
706 /* Get exception code */
707 Status
= _SEH2_GetExceptionCode();
712 /* Return status to caller */
717 * @name NtQuerySymbolicLinkObject
720 * The NtQuerySymbolicLinkObject queries a symbolic link object.
723 * Symlink handle to query
726 * Unicode string defining the symlink's target
728 * @param ResultLength
729 * Caller supplied storage for the number of bytes written (or NULL).
731 * @return STATUS_SUCCESS or appropriate error value.
738 NtQuerySymbolicLinkObject(IN HANDLE LinkHandle
,
739 OUT PUNICODE_STRING LinkTarget
,
740 OUT PULONG ResultLength OPTIONAL
)
742 UNICODE_STRING SafeLinkTarget
= { 0, 0, NULL
};
743 POBJECT_SYMBOLIC_LINK SymlinkObject
;
744 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
749 if (PreviousMode
!= KernelMode
)
753 /* Probe the unicode string for read and write */
754 ProbeForWriteUnicodeString(LinkTarget
);
756 /* Probe the unicode string's buffer for write */
757 SafeLinkTarget
= *LinkTarget
;
758 ProbeForWrite(SafeLinkTarget
.Buffer
,
759 SafeLinkTarget
.MaximumLength
,
762 /* Probe the return length */
763 if (ResultLength
) ProbeForWriteUlong(ResultLength
);
765 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
767 /* Return the exception code */
768 _SEH2_YIELD(return _SEH2_GetExceptionCode());
774 /* No need to probe */
775 SafeLinkTarget
= *LinkTarget
;
778 /* Reference the object */
779 Status
= ObReferenceObjectByHandle(LinkHandle
,
783 (PVOID
*)&SymlinkObject
,
785 if (NT_SUCCESS(Status
))
787 /* Lock the object */
788 ObpAcquireObjectLock(OBJECT_TO_OBJECT_HEADER(SymlinkObject
));
791 * So here's the thing: If you specify a return length, then the
792 * implementation will use the maximum length. If you don't, then
793 * it will use the length.
795 LengthUsed
= ResultLength
? SymlinkObject
->LinkTarget
.MaximumLength
:
796 SymlinkObject
->LinkTarget
.Length
;
798 /* Enter SEH so we can safely copy */
801 /* Make sure our buffer will fit */
802 if (LengthUsed
<= SafeLinkTarget
.MaximumLength
)
804 /* Copy the buffer */
805 RtlCopyMemory(SafeLinkTarget
.Buffer
,
806 SymlinkObject
->LinkTarget
.Buffer
,
809 /* Copy the new length */
810 LinkTarget
->Length
= SymlinkObject
->LinkTarget
.Length
;
814 /* Otherwise set the failure status */
815 Status
= STATUS_BUFFER_TOO_SMALL
;
818 /* In both cases, check if the required length was requested */
822 *ResultLength
= SymlinkObject
->LinkTarget
.MaximumLength
;
825 _SEH2_EXCEPT(ExSystemExceptionFilter())
827 /* Get the error code */
828 Status
= _SEH2_GetExceptionCode();
832 /* Unlock and dereference the object */
833 ObpReleaseObjectLock(OBJECT_TO_OBJECT_HEADER(SymlinkObject
));
834 ObDereferenceObject(SymlinkObject
);
837 /* Return query status */