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)
11 /* INCLUDES *****************************************************************/
17 /* GLOBALS ******************************************************************/
19 POBJECT_TYPE ObpSymbolicLinkObjectType
= NULL
;
21 /* PRIVATE FUNCTIONS *********************************************************/
24 ObpProcessDosDeviceSymbolicLink(IN POBJECT_SYMBOLIC_LINK SymbolicLink
,
25 IN BOOLEAN DeleteLink
)
27 PDEVICE_MAP DeviceMap
;
28 UNICODE_STRING TargetPath
, LocalTarget
;
29 POBJECT_DIRECTORY NameDirectory
, DirectoryObject
;
31 OBP_LOOKUP_CONTEXT LookupContext
;
33 POBJECT_HEADER ObjectHeader
;
34 POBJECT_HEADER_NAME_INFO ObjectNameInfo
;
35 BOOLEAN DirectoryLocked
;
39 * To prevent endless reparsing, setting an upper limit on the
46 ObjectHeader
= OBJECT_TO_OBJECT_HEADER(SymbolicLink
);
47 ObjectNameInfo
= OBJECT_HEADER_TO_NAME_INFO(ObjectHeader
);
49 /* Check if we have a directory in our symlink to use */
50 if (ObjectNameInfo
!= NULL
)
52 NameDirectory
= ObjectNameInfo
->Directory
;
55 /* Initialize lookup context */
56 ObpInitializeLookupContext(&LookupContext
);
59 * If we have to create the link, locate the IoDeviceObject if any
60 * this symbolic link points to.
62 if (SymbolicLink
->LinkTargetObject
!= NULL
|| !DeleteLink
)
64 /* Start the search from the root */
65 DirectoryObject
= ObpRootDirectoryObject
;
67 /* Keep track of our progress while parsing the name */
68 LocalTarget
= SymbolicLink
->LinkTarget
;
70 /* If LUID mappings are enabled, use system map */
71 if (ObpLUIDDeviceMapsEnabled
!= 0)
73 DeviceMap
= ObSystemDeviceMap
;
75 /* Otherwise, use the one in the process */
78 DeviceMap
= PsGetCurrentProcess()->DeviceMap
;
83 * If we have a device map active, check whether we have a drive
84 * letter prefixed with ??, if so, chomp it
86 if (DeviceMap
!= NULL
)
88 if (!((ULONG_PTR
)(LocalTarget
.Buffer
) & 7))
90 if (DeviceMap
->DosDevicesDirectory
!= NULL
)
92 if (LocalTarget
.Length
>= ObpDosDevicesShortName
.Length
&&
93 (*(PULONGLONG
)LocalTarget
.Buffer
==
94 ObpDosDevicesShortNamePrefix
.Alignment
.QuadPart
))
96 DirectoryObject
= DeviceMap
->DosDevicesDirectory
;
98 LocalTarget
.Length
-= ObpDosDevicesShortName
.Length
;
99 LocalTarget
.Buffer
+= (ObpDosDevicesShortName
.Length
/ sizeof(WCHAR
));
105 /* Try walking the target path and open each part of the path */
108 if (LocalTarget
.Buffer
[0] == OBJ_NAME_PATH_SEPARATOR
)
110 ++LocalTarget
.Buffer
;
111 LocalTarget
.Length
-= sizeof(WCHAR
);
114 /* Remember the current component of the target path */
115 TargetPath
= LocalTarget
;
117 /* Move forward to the next component of the target path */
118 if (LocalTarget
.Length
!= 0)
122 if (LocalTarget
.Buffer
[0] == OBJ_NAME_PATH_SEPARATOR
)
127 ++LocalTarget
.Buffer
;
128 LocalTarget
.Length
-= sizeof(WCHAR
);
130 while (LocalTarget
.Length
!= 0);
133 TargetPath
.Length
-= LocalTarget
.Length
;
136 * Finished processing the entire path, stop
137 * That's a failure case, we quit here
139 if (TargetPath
.Length
== 0)
141 ObpReleaseLookupContext(&LookupContext
);
147 * Make sure a deadlock does not occur as an exclusive lock on a pushlock
148 * would have already taken one in ObpLookupObjectName() on the parent
149 * directory where the symlink is being created [ObInsertObject()].
150 * Prevent recursive locking by faking lock state in the lookup context
151 * when the current directory is same as the parent directory where
152 * the symlink is being created. If the lock state is not faked,
153 * ObpLookupEntryDirectory() will try to get a recursive lock on the
154 * pushlock and hang. For e.g. this happens when a substed drive is pointed to
155 * another substed drive.
157 if (DirectoryObject
== NameDirectory
)
159 DirectoryLocked
= LookupContext
.DirectoryLocked
;
160 LookupContext
.DirectoryLocked
= TRUE
;
164 DirectoryLocked
= FALSE
;
167 Object
= ObpLookupEntryDirectory(DirectoryObject
,
173 /* Locking was faked, undo it now */
174 if (DirectoryObject
== NameDirectory
)
176 LookupContext
.DirectoryLocked
= DirectoryLocked
;
179 /* Lookup failed, stop */
185 /* If we don't have a directory object, we'll have to handle the object */
186 if (OBJECT_TO_OBJECT_HEADER(Object
)->Type
!= ObpDirectoryObjectType
)
188 /* If that's not a symbolic link, stop here, nothing to do */
189 if (OBJECT_TO_OBJECT_HEADER(Object
)->Type
!= ObpSymbolicLinkObjectType
||
190 (((POBJECT_SYMBOLIC_LINK
)Object
)->DosDeviceDriveIndex
!= 0))
195 /* We're out of reparse attempts */
204 /* Symlink points to another initialized symlink, ask caller to reparse */
205 DirectoryObject
= ObpRootDirectoryObject
;
207 LocalTarget
= ((POBJECT_SYMBOLIC_LINK
)Object
)->LinkTarget
;
209 goto ReparseTargetPath
;
212 /* Make this current directory, and continue search */
213 DirectoryObject
= Object
;
218 /* That's a drive letter, find a suitable device map */
219 if (SymbolicLink
->DosDeviceDriveIndex
!= 0)
221 ObjectHeader
= OBJECT_TO_OBJECT_HEADER(SymbolicLink
);
222 ObjectNameInfo
= ObpReferenceNameInfo(ObjectHeader
);
223 if (ObjectNameInfo
!= NULL
)
225 if (ObjectNameInfo
->Directory
!= NULL
)
227 DeviceMap
= ObjectNameInfo
->Directory
->DeviceMap
;
230 ObpDereferenceNameInfo(ObjectNameInfo
);
234 /* If we were asked to delete the symlink */
237 /* Zero its target */
238 RtlInitUnicodeString(&SymbolicLink
->LinkTargetRemaining
, NULL
);
240 /* If we had a target objected, dereference it */
241 if (SymbolicLink
->LinkTargetObject
!= NULL
)
243 ObDereferenceObject(SymbolicLink
->LinkTargetObject
);
244 SymbolicLink
->LinkTargetObject
= NULL
;
247 /* If it was a drive letter */
248 if (DeviceMap
!= NULL
)
250 /* Acquire the device map lock */
251 KeAcquireGuardedMutex(&ObpDeviceMapLock
);
253 /* Remove the drive entry */
254 DeviceMap
->DriveType
[SymbolicLink
->DosDeviceDriveIndex
- 1] =
255 DOSDEVICE_DRIVE_UNKNOWN
;
256 DeviceMap
->DriveMap
&=
257 ~(1 << (SymbolicLink
->DosDeviceDriveIndex
- 1));
259 /* Release the device map lock */
260 KeReleaseGuardedMutex(&ObpDeviceMapLock
);
262 /* Reset the drive index, valid drive index starts from 1 */
263 SymbolicLink
->DosDeviceDriveIndex
= 0;
268 DriveType
= DOSDEVICE_DRIVE_CALCULATE
;
270 /* If we have a drive letter and a pointer device object */
271 if (Object
!= NULL
&& SymbolicLink
->DosDeviceDriveIndex
!= 0 &&
272 OBJECT_TO_OBJECT_HEADER(Object
)->Type
== IoDeviceObjectType
)
274 /* Calculate the drive type */
275 switch(((PDEVICE_OBJECT
)Object
)->DeviceType
)
277 case FILE_DEVICE_VIRTUAL_DISK
:
278 DriveType
= DOSDEVICE_DRIVE_RAMDISK
;
280 case FILE_DEVICE_CD_ROM
:
281 case FILE_DEVICE_CD_ROM_FILE_SYSTEM
:
282 DriveType
= DOSDEVICE_DRIVE_CDROM
;
284 case FILE_DEVICE_DISK
:
285 case FILE_DEVICE_DISK_FILE_SYSTEM
:
286 case FILE_DEVICE_FILE_SYSTEM
:
287 if (((PDEVICE_OBJECT
)Object
)->Characteristics
& FILE_REMOVABLE_MEDIA
)
288 DriveType
= DOSDEVICE_DRIVE_REMOVABLE
;
290 DriveType
= DOSDEVICE_DRIVE_FIXED
;
292 case FILE_DEVICE_NETWORK
:
293 case FILE_DEVICE_NETWORK_FILE_SYSTEM
:
294 DriveType
= DOSDEVICE_DRIVE_REMOTE
;
297 DPRINT1("Device Type %lu for %wZ is not known or unhandled\n",
298 ((PDEVICE_OBJECT
)Object
)->DeviceType
,
299 &SymbolicLink
->LinkTarget
);
300 DriveType
= DOSDEVICE_DRIVE_UNKNOWN
;
304 /* Add a new drive entry */
305 if (DeviceMap
!= NULL
)
307 /* Acquire the device map lock */
308 KeAcquireGuardedMutex(&ObpDeviceMapLock
);
310 DeviceMap
->DriveType
[SymbolicLink
->DosDeviceDriveIndex
- 1] =
312 DeviceMap
->DriveMap
|=
313 1 << (SymbolicLink
->DosDeviceDriveIndex
- 1);
315 /* Release the device map lock */
316 KeReleaseGuardedMutex(&ObpDeviceMapLock
);
321 ObpReleaseLookupContext(&LookupContext
);
326 ObpDeleteSymbolicLinkName(IN POBJECT_SYMBOLIC_LINK SymbolicLink
)
328 /* Just call the helper */
329 ObpProcessDosDeviceSymbolicLink(SymbolicLink
, TRUE
);
334 ObpCreateSymbolicLinkName(IN POBJECT_SYMBOLIC_LINK SymbolicLink
)
337 POBJECT_HEADER ObjectHeader
;
338 POBJECT_HEADER_NAME_INFO ObjectNameInfo
;
340 /* Get header data */
341 ObjectHeader
= OBJECT_TO_OBJECT_HEADER(SymbolicLink
);
342 ObjectNameInfo
= ObpReferenceNameInfo(ObjectHeader
);
344 /* No name info, nothing to create */
345 if (ObjectNameInfo
== NULL
)
350 /* If we have a device map, look for creating a letter based drive */
351 if (ObjectNameInfo
->Directory
!= NULL
&&
352 ObjectNameInfo
->Directory
->DeviceMap
!= NULL
)
354 /* Is it a drive letter based name? */
355 if (ObjectNameInfo
->Name
.Length
== 2 * sizeof(WCHAR
))
357 if (ObjectNameInfo
->Name
.Buffer
[1] == L
':')
359 UpperDrive
= RtlUpcaseUnicodeChar(ObjectNameInfo
->Name
.Buffer
[0]);
360 if (UpperDrive
>= L
'A' && UpperDrive
<= L
'Z')
362 /* Compute its index (it's 1 based - 0 means no letter) */
363 SymbolicLink
->DosDeviceDriveIndex
= UpperDrive
- (L
'A' - 1);
368 /* Call the helper */
369 ObpProcessDosDeviceSymbolicLink(SymbolicLink
, FALSE
);
373 ObpDereferenceNameInfo(ObjectNameInfo
);
377 * @name ObpDeleteSymbolicLink
379 * The ObpDeleteSymbolicLink routine <FILLMEIN>
391 ObpDeleteSymbolicLink(PVOID ObjectBody
)
393 POBJECT_SYMBOLIC_LINK SymlinkObject
= (POBJECT_SYMBOLIC_LINK
)ObjectBody
;
395 /* Make sure that the symbolic link has a name */
396 if (SymlinkObject
->LinkTarget
.Buffer
)
399 ExFreePool(SymlinkObject
->LinkTarget
.Buffer
);
400 SymlinkObject
->LinkTarget
.Buffer
= NULL
;
405 * @name ObpParseSymbolicLink
407 * The ObpParseSymbolicLink routine <FILLMEIN>
418 * @param RemainingPath
424 * @return STATUS_SUCCESS or appropriate error value.
431 ObpParseSymbolicLink(IN PVOID ParsedObject
,
433 IN OUT PACCESS_STATE AccessState
,
434 IN KPROCESSOR_MODE AccessMode
,
436 IN OUT PUNICODE_STRING FullPath
,
437 IN OUT PUNICODE_STRING RemainingName
,
438 IN OUT PVOID Context OPTIONAL
,
439 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL
,
440 OUT PVOID
*NextObject
)
442 POBJECT_SYMBOLIC_LINK SymlinkObject
= (POBJECT_SYMBOLIC_LINK
)ParsedObject
;
443 PUNICODE_STRING TargetPath
;
445 ULONG LengthUsed
, MaximumLength
, TempLength
;
452 /* Check if we're out of name to parse */
453 if (!RemainingName
->Length
)
455 /* Check if we got an object type */
458 /* Reference the object only */
459 Status
= ObReferenceObjectByPointer(ParsedObject
,
463 if (NT_SUCCESS(Status
))
466 *NextObject
= ParsedObject
;
469 if ((NT_SUCCESS(Status
)) || (Status
!= STATUS_OBJECT_TYPE_MISMATCH
))
476 else if (RemainingName
->Buffer
[0] != OBJ_NAME_PATH_SEPARATOR
)
478 /* Symbolic links must start with a backslash */
479 return STATUS_OBJECT_TYPE_MISMATCH
;
482 /* Check if this symlink is bound to a specific object */
483 if (SymlinkObject
->LinkTargetObject
)
485 /* No name to reparse, directly reparse the object */
486 if (!SymlinkObject
->LinkTargetRemaining
.Length
)
488 *NextObject
= SymlinkObject
->LinkTargetObject
;
489 return STATUS_REPARSE_OBJECT
;
492 TempLength
= SymlinkObject
->LinkTargetRemaining
.Length
;
493 /* The target and remaining names aren't empty, so check for slashes */
494 if (SymlinkObject
->LinkTargetRemaining
.Buffer
[TempLength
/ sizeof(WCHAR
) - 1] == OBJ_NAME_PATH_SEPARATOR
&&
495 RemainingName
->Buffer
[0] == OBJ_NAME_PATH_SEPARATOR
)
497 /* Reduce the length by one to cut off the extra '\' */
498 TempLength
-= sizeof(OBJ_NAME_PATH_SEPARATOR
);
501 /* Calculate the new length */
502 LengthUsed
= TempLength
+ RemainingName
->Length
;
503 LengthUsed
+= (sizeof(WCHAR
) * (RemainingName
->Buffer
- FullPath
->Buffer
));
505 /* Check if it's not too much */
506 if (LengthUsed
> 0xFFF0)
507 return STATUS_NAME_TOO_LONG
;
509 /* If FullPath is enough, use it */
510 if (FullPath
->MaximumLength
> LengthUsed
)
512 /* Update remaining length if appropriate */
513 if (RemainingName
->Length
)
515 RtlMoveMemory((PVOID
)((ULONG_PTR
)RemainingName
->Buffer
+ TempLength
),
516 RemainingName
->Buffer
,
517 RemainingName
->Length
);
520 /* And move the target object name */
521 RtlCopyMemory(RemainingName
->Buffer
,
522 SymlinkObject
->LinkTargetRemaining
.Buffer
,
525 /* Finally update the full path with what we parsed */
526 FullPath
->Length
+= SymlinkObject
->LinkTargetRemaining
.Length
;
527 RemainingName
->Length
+= SymlinkObject
->LinkTargetRemaining
.Length
;
528 RemainingName
->MaximumLength
+= RemainingName
->Length
+ sizeof(WCHAR
);
529 FullPath
->Buffer
[FullPath
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
532 *NextObject
= SymlinkObject
->LinkTargetObject
;
533 return STATUS_REPARSE_OBJECT
;
536 /* FullPath is not enough, we'll have to reallocate */
537 MaximumLength
= LengthUsed
+ sizeof(WCHAR
);
538 NewTargetPath
= ExAllocatePoolWithTag(NonPagedPool
,
541 if (!NewTargetPath
) return STATUS_INSUFFICIENT_RESOURCES
;
543 /* Copy path begin */
544 RtlCopyMemory(NewTargetPath
,
546 sizeof(WCHAR
) * (RemainingName
->Buffer
- FullPath
->Buffer
));
548 /* Copy path end (if any) */
549 if (RemainingName
->Length
)
551 RtlCopyMemory((PVOID
)((ULONG_PTR
)&NewTargetPath
[RemainingName
->Buffer
- FullPath
->Buffer
] + TempLength
),
552 RemainingName
->Buffer
,
553 RemainingName
->Length
);
556 /* And finish path with bound object */
557 RtlCopyMemory(&NewTargetPath
[RemainingName
->Buffer
- FullPath
->Buffer
],
558 SymlinkObject
->LinkTargetRemaining
.Buffer
,
561 /* Free old buffer */
562 ExFreePool(FullPath
->Buffer
);
564 /* Set new buffer in FullPath */
565 FullPath
->Buffer
= NewTargetPath
;
566 FullPath
->MaximumLength
= MaximumLength
;
567 FullPath
->Length
= LengthUsed
;
569 /* Update remaining with what we handled */
570 RemainingName
->Length
= LengthUsed
+ (ULONG_PTR
)NewTargetPath
- (ULONG_PTR
)&NewTargetPath
[RemainingName
->Buffer
- FullPath
->Buffer
];
571 RemainingName
->Buffer
= &NewTargetPath
[RemainingName
->Buffer
- FullPath
->Buffer
];
572 RemainingName
->MaximumLength
= RemainingName
->Length
+ sizeof(WCHAR
);
575 *NextObject
= SymlinkObject
->LinkTargetObject
;
576 return STATUS_REPARSE_OBJECT
;
579 /* Set the target path and length */
580 TargetPath
= &SymlinkObject
->LinkTarget
;
581 TempLength
= TargetPath
->Length
;
584 * Strip off the extra trailing '\', if we don't do this we will end up
585 * adding a extra '\' between TargetPath and RemainingName
586 * causing caller's like ObpLookupObjectName() to fail.
588 if (TempLength
&& RemainingName
->Length
)
590 /* The target and remaining names aren't empty, so check for slashes */
591 if ((TargetPath
->Buffer
[TempLength
/ sizeof(WCHAR
) - 1] ==
592 OBJ_NAME_PATH_SEPARATOR
) &&
593 (RemainingName
->Buffer
[0] == OBJ_NAME_PATH_SEPARATOR
))
595 /* Reduce the length by one to cut off the extra '\' */
596 TempLength
-= sizeof(OBJ_NAME_PATH_SEPARATOR
);
600 /* Calculate the new length */
601 LengthUsed
= TempLength
+ RemainingName
->Length
;
603 /* Check if it's not too much */
604 if (LengthUsed
> 0xFFF0)
605 return STATUS_NAME_TOO_LONG
;
607 /* Optimization: check if the new name is shorter */
608 if (FullPath
->MaximumLength
<= LengthUsed
)
610 /* It's not, allocate a new one */
611 MaximumLength
= LengthUsed
+ sizeof(WCHAR
);
612 NewTargetPath
= ExAllocatePoolWithTag(NonPagedPool
,
615 if (!NewTargetPath
) return STATUS_INSUFFICIENT_RESOURCES
;
619 /* It is! Reuse the name... */
620 MaximumLength
= FullPath
->MaximumLength
;
621 NewTargetPath
= FullPath
->Buffer
;
624 /* Make sure we have a length */
625 if (RemainingName
->Length
)
627 /* Copy the new path */
628 RtlMoveMemory((PVOID
)((ULONG_PTR
)NewTargetPath
+ TempLength
),
629 RemainingName
->Buffer
,
630 RemainingName
->Length
);
633 /* Copy the target path and null-terminate it */
634 RtlCopyMemory(NewTargetPath
, TargetPath
->Buffer
, TempLength
);
635 NewTargetPath
[LengthUsed
/ sizeof(WCHAR
)] = UNICODE_NULL
;
637 /* If the optimization didn't work, free the old buffer */
638 if (NewTargetPath
!= FullPath
->Buffer
) ExFreePool(FullPath
->Buffer
);
640 /* Update the path values */
641 FullPath
->Length
= (USHORT
)LengthUsed
;
642 FullPath
->MaximumLength
= (USHORT
)MaximumLength
;
643 FullPath
->Buffer
= NewTargetPath
;
645 /* Tell the parse routine to start reparsing */
646 return STATUS_REPARSE
;
649 /* PUBLIC FUNCTIONS **********************************************************/
652 * @name NtCreateSymbolicLinkObject
655 * The NtCreateSymbolicLinkObject opens or creates a symbolic link object.
658 * Variable which receives the symlink handle.
660 * @param DesiredAccess
661 * Desired access to the symlink.
663 * @param ObjectAttributes
664 * Structure describing the symlink.
667 * Unicode string defining the symlink's target
669 * @return STATUS_SUCCESS or appropriate error value.
676 NtCreateSymbolicLinkObject(OUT PHANDLE LinkHandle
,
677 IN ACCESS_MASK DesiredAccess
,
678 IN POBJECT_ATTRIBUTES ObjectAttributes
,
679 IN PUNICODE_STRING LinkTarget
)
682 POBJECT_SYMBOLIC_LINK SymbolicLink
;
683 UNICODE_STRING CapturedLinkTarget
;
684 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
688 /* Check if we need to probe parameters */
689 if (PreviousMode
!= KernelMode
)
693 /* Probe the target */
694 CapturedLinkTarget
= ProbeForReadUnicodeString(LinkTarget
);
695 ProbeForRead(CapturedLinkTarget
.Buffer
,
696 CapturedLinkTarget
.MaximumLength
,
699 /* Probe the return handle */
700 ProbeForWriteHandle(LinkHandle
);
702 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
704 /* Return the exception code */
705 _SEH2_YIELD(return _SEH2_GetExceptionCode());
711 /* No need to capture */
712 CapturedLinkTarget
= *LinkTarget
;
715 /* Check if the maximum length is odd */
716 if (CapturedLinkTarget
.MaximumLength
% sizeof(WCHAR
))
719 CapturedLinkTarget
.MaximumLength
=
720 (USHORT
)ALIGN_DOWN(CapturedLinkTarget
.MaximumLength
, WCHAR
);
723 /* Fail if the length is odd, or if the maximum is smaller or 0 */
724 if ((CapturedLinkTarget
.Length
% sizeof(WCHAR
)) ||
725 (CapturedLinkTarget
.MaximumLength
< CapturedLinkTarget
.Length
) ||
726 !(CapturedLinkTarget
.MaximumLength
))
728 /* This message is displayed on the debugger in Windows */
729 DbgPrint("OB: Invalid symbolic link target - %wZ\n",
730 &CapturedLinkTarget
);
731 return STATUS_INVALID_PARAMETER
;
734 /* Create the object */
735 Status
= ObCreateObject(PreviousMode
,
736 ObpSymbolicLinkObjectType
,
740 sizeof(OBJECT_SYMBOLIC_LINK
),
743 (PVOID
*)&SymbolicLink
);
744 if (NT_SUCCESS(Status
))
746 /* Success! Fill in the creation time immediately */
747 KeQuerySystemTime(&SymbolicLink
->CreationTime
);
749 /* Setup the target name */
750 SymbolicLink
->LinkTarget
.Length
= CapturedLinkTarget
.Length
;
751 SymbolicLink
->LinkTarget
.MaximumLength
= CapturedLinkTarget
.MaximumLength
;
752 SymbolicLink
->LinkTarget
.Buffer
=
753 ExAllocatePoolWithTag(PagedPool
,
754 CapturedLinkTarget
.MaximumLength
,
756 if (!SymbolicLink
->LinkTarget
.Buffer
)
758 /* Dereference the symbolic link object and fail */
759 ObDereferenceObject(SymbolicLink
);
760 return STATUS_NO_MEMORY
;
766 RtlCopyMemory(SymbolicLink
->LinkTarget
.Buffer
,
767 CapturedLinkTarget
.Buffer
,
768 CapturedLinkTarget
.MaximumLength
);
770 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
772 ObDereferenceObject(SymbolicLink
);
773 _SEH2_YIELD(return _SEH2_GetExceptionCode());
777 /* Initialize the remaining name, dos drive index and target object */
778 SymbolicLink
->LinkTargetObject
= NULL
;
779 SymbolicLink
->DosDeviceDriveIndex
= 0;
780 RtlInitUnicodeString(&SymbolicLink
->LinkTargetRemaining
, NULL
);
782 /* Insert it into the object tree */
783 Status
= ObInsertObject(SymbolicLink
,
789 if (NT_SUCCESS(Status
))
793 /* Return the handle to caller */
796 _SEH2_EXCEPT(ExSystemExceptionFilter())
798 /* Get exception code */
799 Status
= _SEH2_GetExceptionCode();
805 /* Return status to caller */
810 * @name NtOpenSymbolicLinkObject
813 * The NtOpenSymbolicLinkObject opens a symbolic link object.
816 * Variable which receives the symlink handle.
818 * @param DesiredAccess
819 * Desired access to the symlink.
821 * @param ObjectAttributes
822 * Structure describing the symlink.
824 * @return STATUS_SUCCESS or appropriate error value.
831 NtOpenSymbolicLinkObject(OUT PHANDLE LinkHandle
,
832 IN ACCESS_MASK DesiredAccess
,
833 IN POBJECT_ATTRIBUTES ObjectAttributes
)
836 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
840 /* Check if we need to probe parameters */
841 if (PreviousMode
!= KernelMode
)
845 /* Probe the return handle */
846 ProbeForWriteHandle(LinkHandle
);
848 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
850 /* Return the exception code */
851 _SEH2_YIELD(return _SEH2_GetExceptionCode());
856 /* Open the object */
857 Status
= ObOpenObjectByName(ObjectAttributes
,
858 ObpSymbolicLinkObjectType
,
867 /* Return the handle to caller */
870 _SEH2_EXCEPT(ExSystemExceptionFilter())
872 /* Get exception code */
873 Status
= _SEH2_GetExceptionCode();
877 /* Return status to caller */
882 * @name NtQuerySymbolicLinkObject
885 * The NtQuerySymbolicLinkObject queries a symbolic link object.
888 * Symlink handle to query
891 * Unicode string defining the symlink's target
893 * @param ResultLength
894 * Caller supplied storage for the number of bytes written (or NULL).
896 * @return STATUS_SUCCESS or appropriate error value.
903 NtQuerySymbolicLinkObject(IN HANDLE LinkHandle
,
904 OUT PUNICODE_STRING LinkTarget
,
905 OUT PULONG ResultLength OPTIONAL
)
907 UNICODE_STRING SafeLinkTarget
= { 0, 0, NULL
};
908 POBJECT_SYMBOLIC_LINK SymlinkObject
;
909 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
914 if (PreviousMode
!= KernelMode
)
918 /* Probe the unicode string for read and write */
919 ProbeForWriteUnicodeString(LinkTarget
);
921 /* Probe the unicode string's buffer for write */
922 SafeLinkTarget
= *LinkTarget
;
923 ProbeForWrite(SafeLinkTarget
.Buffer
,
924 SafeLinkTarget
.MaximumLength
,
927 /* Probe the return length */
928 if (ResultLength
) ProbeForWriteUlong(ResultLength
);
930 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
932 /* Return the exception code */
933 _SEH2_YIELD(return _SEH2_GetExceptionCode());
939 /* No need to probe */
940 SafeLinkTarget
= *LinkTarget
;
943 /* Reference the object */
944 Status
= ObReferenceObjectByHandle(LinkHandle
,
946 ObpSymbolicLinkObjectType
,
948 (PVOID
*)&SymlinkObject
,
950 if (NT_SUCCESS(Status
))
952 /* Lock the object */
953 ObpAcquireObjectLock(OBJECT_TO_OBJECT_HEADER(SymlinkObject
));
956 * So here's the thing: If you specify a return length, then the
957 * implementation will use the maximum length. If you don't, then
958 * it will use the length.
960 LengthUsed
= ResultLength
? SymlinkObject
->LinkTarget
.MaximumLength
:
961 SymlinkObject
->LinkTarget
.Length
;
963 /* Enter SEH so we can safely copy */
966 /* Make sure our buffer will fit */
967 if (LengthUsed
<= SafeLinkTarget
.MaximumLength
)
969 /* Copy the buffer */
970 RtlCopyMemory(SafeLinkTarget
.Buffer
,
971 SymlinkObject
->LinkTarget
.Buffer
,
974 /* Copy the new length */
975 LinkTarget
->Length
= SymlinkObject
->LinkTarget
.Length
;
979 /* Otherwise set the failure status */
980 Status
= STATUS_BUFFER_TOO_SMALL
;
983 /* In both cases, check if the required length was requested */
987 *ResultLength
= SymlinkObject
->LinkTarget
.MaximumLength
;
990 _SEH2_EXCEPT(ExSystemExceptionFilter())
992 /* Get the error code */
993 Status
= _SEH2_GetExceptionCode();
997 /* Unlock and dereference the object */
998 ObpReleaseObjectLock(OBJECT_TO_OBJECT_HEADER(SymlinkObject
));
999 ObDereferenceObject(SymlinkObject
);
1002 /* Return query status */