xref: /reactos/ntoskrnl/ob/oblink.c (revision 6c5d38c2)
1 /*
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)
8 */
9 
10 /* INCLUDES *****************************************************************/
11 
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 /* GLOBALS ******************************************************************/
17 
18 POBJECT_TYPE ObpSymbolicLinkObjectType = NULL;
19 
20 /* PRIVATE FUNCTIONS *********************************************************/
21 
22 VOID
23 ObpProcessDosDeviceSymbolicLink(IN POBJECT_SYMBOLIC_LINK SymbolicLink,
24                                 IN BOOLEAN DeleteLink)
25 {
26     PDEVICE_MAP DeviceMap;
27     UNICODE_STRING TargetPath, LocalTarget;
28     POBJECT_DIRECTORY NameDirectory, DirectoryObject;
29     ULONG MaxReparse;
30     OBP_LOOKUP_CONTEXT LookupContext;
31     ULONG DriveType;
32     POBJECT_HEADER ObjectHeader;
33     POBJECT_HEADER_NAME_INFO ObjectNameInfo;
34     BOOLEAN DirectoryLocked;
35     PVOID Object;
36 
37     /*
38      * To prevent endless reparsing, setting an upper limit on the
39      * number of reparses.
40      */
41     MaxReparse = 32;
42     NameDirectory = NULL;
43 
44     /* Get header data */
45     ObjectHeader = OBJECT_TO_OBJECT_HEADER(SymbolicLink);
46     ObjectNameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
47 
48     /* Check if we have a directory in our symlink to use */
49     if (ObjectNameInfo != NULL)
50     {
51         NameDirectory = ObjectNameInfo->Directory;
52     }
53 
54     /* Initialize lookup context */
55     ObpInitializeLookupContext(&LookupContext);
56 
57     /*
58      * If we have to create the link, locate the IoDeviceObject if any
59      * this symbolic link points to.
60      */
61     if (SymbolicLink->LinkTargetObject != NULL || !DeleteLink)
62     {
63         /* Start the search from the root */
64         DirectoryObject = ObpRootDirectoryObject;
65 
66         /* Keep track of our progress while parsing the name */
67         LocalTarget = SymbolicLink->LinkTarget;
68 
69         /* If LUID mappings are enabled, use system map */
70         if (ObpLUIDDeviceMapsEnabled != 0)
71         {
72             DeviceMap = ObSystemDeviceMap;
73         }
74         /* Otherwise, use the one in the process */
75         else
76         {
77             DeviceMap = PsGetCurrentProcess()->DeviceMap;
78         }
79 
80 ReparseTargetPath:
81         /*
82          * If we have a device map active, check whether we have a drive
83          * letter prefixed with ??, if so, chomp it
84          */
85         if (DeviceMap != NULL)
86         {
87             if (!((ULONG_PTR)(LocalTarget.Buffer) & 7))
88             {
89                 if (DeviceMap->DosDevicesDirectory != NULL)
90                 {
91                     if (LocalTarget.Length >= ObpDosDevicesShortName.Length &&
92                         (*(PULONGLONG)LocalTarget.Buffer ==
93                          ObpDosDevicesShortNamePrefix.Alignment.QuadPart))
94                     {
95                         DirectoryObject = DeviceMap->DosDevicesDirectory;
96 
97                         LocalTarget.Length -= ObpDosDevicesShortName.Length;
98                         LocalTarget.Buffer += (ObpDosDevicesShortName.Length / sizeof(WCHAR));
99                     }
100                 }
101             }
102         }
103 
104         /* Try walking the target path and open each part of the path */
105         while (TRUE)
106         {
107             if (LocalTarget.Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
108             {
109                 ++LocalTarget.Buffer;
110                 LocalTarget.Length -= sizeof(WCHAR);
111             }
112 
113             /* Remember the current component of the target path */
114             TargetPath = LocalTarget;
115 
116             /* Move forward to the next component of the target path */
117             if (LocalTarget.Length != 0)
118             {
119                 do
120                 {
121                     if (LocalTarget.Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
122                     {
123                         break;
124                     }
125 
126                     ++LocalTarget.Buffer;
127                     LocalTarget.Length -= sizeof(WCHAR);
128                 }
129                 while (LocalTarget.Length != 0);
130             }
131 
132             TargetPath.Length -= LocalTarget.Length;
133 
134             /*
135              * Finished processing the entire path, stop
136              * That's a failure case, we quit here
137              */
138             if (TargetPath.Length == 0)
139             {
140                 ObpReleaseLookupContext(&LookupContext);
141                 return;
142             }
143 
144 
145             /*
146              * Make sure a deadlock does not occur as an exclusive lock on a pushlock
147              * would have already taken one in ObpLookupObjectName() on the parent
148              * directory where the symlink is being created [ObInsertObject()].
149              * Prevent recursive locking by faking lock state in the lookup context
150              * when the current directory is same as the parent directory where
151              * the symlink is being created. If the lock state is not faked,
152              * ObpLookupEntryDirectory() will try to get a recursive lock on the
153              * pushlock and hang. For e.g. this happens when a substed drive is pointed to
154              * another substed drive.
155              */
156             if (DirectoryObject == NameDirectory)
157             {
158                 DirectoryLocked = LookupContext.DirectoryLocked;
159                 LookupContext.DirectoryLocked = TRUE;
160             }
161             else
162             {
163                 DirectoryLocked = FALSE;
164             }
165 
166             Object = ObpLookupEntryDirectory(DirectoryObject,
167                                              &TargetPath,
168                                              0,
169                                              FALSE,
170                                              &LookupContext);
171 
172             /* Locking was faked, undo it now */
173             if (DirectoryObject == NameDirectory)
174             {
175                 LookupContext.DirectoryLocked = DirectoryLocked;
176             }
177 
178             /* Lookup failed, stop */
179             if (Object == NULL)
180             {
181                 break;
182             }
183 
184             /* If we don't have a directory object, we'll have to handle the object */
185             if (OBJECT_TO_OBJECT_HEADER(Object)->Type != ObpDirectoryObjectType)
186             {
187                 /* If that's not a symbolic link, stop here, nothing to do */
188                 if (OBJECT_TO_OBJECT_HEADER(Object)->Type != ObpSymbolicLinkObjectType ||
189                     (((POBJECT_SYMBOLIC_LINK)Object)->DosDeviceDriveIndex != 0))
190                 {
191                     break;
192                 }
193 
194                 /* We're out of reparse attempts */
195                 if (MaxReparse == 0)
196                 {
197                     Object = NULL;
198                     break;
199                 }
200 
201                 --MaxReparse;
202 
203                 /* Symlink points to another initialized symlink, ask caller to reparse */
204                 DirectoryObject = ObpRootDirectoryObject;
205 
206                 LocalTarget = ((POBJECT_SYMBOLIC_LINK)Object)->LinkTarget;
207 
208                 goto ReparseTargetPath;
209             }
210 
211             /* Make this current directory, and continue search */
212             DirectoryObject = Object;
213         }
214     }
215 
216     DeviceMap = NULL;
217     /* That's a drive letter, find a suitable device map */
218     if (SymbolicLink->DosDeviceDriveIndex != 0)
219     {
220         ObjectHeader = OBJECT_TO_OBJECT_HEADER(SymbolicLink);
221         ObjectNameInfo = ObpReferenceNameInfo(ObjectHeader);
222         if (ObjectNameInfo != NULL)
223         {
224             if (ObjectNameInfo->Directory != NULL)
225             {
226                 DeviceMap = ObjectNameInfo->Directory->DeviceMap;
227             }
228 
229             ObpDereferenceNameInfo(ObjectNameInfo);
230         }
231     }
232 
233     /* If we were asked to delete the symlink */
234     if (DeleteLink)
235     {
236         /* Zero its target */
237         RtlInitUnicodeString(&SymbolicLink->LinkTargetRemaining, NULL);
238 
239         /* If we had a target objected, dereference it */
240         if (SymbolicLink->LinkTargetObject != NULL)
241         {
242             ObDereferenceObject(SymbolicLink->LinkTargetObject);
243             SymbolicLink->LinkTargetObject = NULL;
244         }
245 
246         /* If it was a drive letter */
247         if (DeviceMap != NULL)
248         {
249             /* Acquire the device map lock */
250             KeAcquireGuardedMutex(&ObpDeviceMapLock);
251 
252             /* Remove the drive entry */
253             DeviceMap->DriveType[SymbolicLink->DosDeviceDriveIndex - 1] =
254                 DOSDEVICE_DRIVE_UNKNOWN;
255             DeviceMap->DriveMap &=
256                 ~(1 << (SymbolicLink->DosDeviceDriveIndex - 1));
257 
258             /* Release the device map lock */
259             KeReleaseGuardedMutex(&ObpDeviceMapLock);
260 
261             /* Reset the drive index, valid drive index starts from 1 */
262             SymbolicLink->DosDeviceDriveIndex = 0;
263         }
264     }
265     else
266     {
267         DriveType = DOSDEVICE_DRIVE_CALCULATE;
268 
269         /* If we have a drive letter and a pointer device object */
270         if (Object != NULL && SymbolicLink->DosDeviceDriveIndex != 0 &&
271             OBJECT_TO_OBJECT_HEADER(Object)->Type == IoDeviceObjectType)
272         {
273             /* Calculate the drive type */
274             switch(((PDEVICE_OBJECT)Object)->DeviceType)
275             {
276                 case FILE_DEVICE_VIRTUAL_DISK:
277                     DriveType = DOSDEVICE_DRIVE_RAMDISK;
278                     break;
279                 case FILE_DEVICE_CD_ROM:
280                 case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
281                     DriveType = DOSDEVICE_DRIVE_CDROM;
282                     break;
283                 case FILE_DEVICE_DISK:
284                 case FILE_DEVICE_DISK_FILE_SYSTEM:
285                 case FILE_DEVICE_FILE_SYSTEM:
286                     if (((PDEVICE_OBJECT)Object)->Characteristics & FILE_REMOVABLE_MEDIA)
287                         DriveType = DOSDEVICE_DRIVE_REMOVABLE;
288                     else
289                         DriveType = DOSDEVICE_DRIVE_FIXED;
290                     break;
291                 case FILE_DEVICE_NETWORK:
292                 case FILE_DEVICE_NETWORK_FILE_SYSTEM:
293                     DriveType = DOSDEVICE_DRIVE_REMOTE;
294                     break;
295                 default:
296                     DPRINT1("Device Type %lu for %wZ is not known or unhandled\n",
297                             ((PDEVICE_OBJECT)Object)->DeviceType,
298                             &SymbolicLink->LinkTarget);
299                     DriveType = DOSDEVICE_DRIVE_UNKNOWN;
300             }
301         }
302 
303         /* Add a new drive entry */
304         if (DeviceMap != NULL)
305         {
306             /* Acquire the device map lock */
307             KeAcquireGuardedMutex(&ObpDeviceMapLock);
308 
309             DeviceMap->DriveType[SymbolicLink->DosDeviceDriveIndex - 1] =
310                 (UCHAR)DriveType;
311             DeviceMap->DriveMap |=
312                 1 << (SymbolicLink->DosDeviceDriveIndex - 1);
313 
314             /* Release the device map lock */
315             KeReleaseGuardedMutex(&ObpDeviceMapLock);
316         }
317     }
318 
319     /* Cleanup */
320     ObpReleaseLookupContext(&LookupContext);
321 }
322 
323 VOID
324 NTAPI
325 ObpDeleteSymbolicLinkName(IN POBJECT_SYMBOLIC_LINK SymbolicLink)
326 {
327     /* Just call the helper */
328     ObpProcessDosDeviceSymbolicLink(SymbolicLink, TRUE);
329 }
330 
331 VOID
332 NTAPI
333 ObpCreateSymbolicLinkName(IN POBJECT_SYMBOLIC_LINK SymbolicLink)
334 {
335     WCHAR UpperDrive;
336     POBJECT_HEADER ObjectHeader;
337     POBJECT_HEADER_NAME_INFO ObjectNameInfo;
338 
339     /* Get header data */
340     ObjectHeader = OBJECT_TO_OBJECT_HEADER(SymbolicLink);
341     ObjectNameInfo = ObpReferenceNameInfo(ObjectHeader);
342 
343     /* No name info, nothing to create */
344     if (ObjectNameInfo == NULL)
345     {
346         return;
347     }
348 
349     /* If we have a device map, look for creating a letter based drive */
350     if (ObjectNameInfo->Directory != NULL &&
351         ObjectNameInfo->Directory->DeviceMap != NULL)
352     {
353         /* Is it a drive letter based name? */
354         if (ObjectNameInfo->Name.Length == 2 * sizeof(WCHAR))
355         {
356             if (ObjectNameInfo->Name.Buffer[1] == L':')
357             {
358                 UpperDrive = RtlUpcaseUnicodeChar(ObjectNameInfo->Name.Buffer[0]);
359                 if (UpperDrive >= L'A' && UpperDrive <= L'Z')
360                 {
361                     /* Compute its index (it's 1 based - 0 means no letter) */
362                     SymbolicLink->DosDeviceDriveIndex = UpperDrive - (L'A' - 1);
363                 }
364             }
365         }
366 
367         /* Call the helper */
368         ObpProcessDosDeviceSymbolicLink(SymbolicLink, FALSE);
369     }
370 
371     /* We're done */
372     ObpDereferenceNameInfo(ObjectNameInfo);
373 }
374 
375 /*++
376 * @name ObpDeleteSymbolicLink
377 *
378 *     The ObpDeleteSymbolicLink routine <FILLMEIN>
379 *
380 * @param ObjectBody
381 *        <FILLMEIN>
382 *
383 * @return None.
384 *
385 * @remarks None.
386 *
387 *--*/
388 VOID
389 NTAPI
390 ObpDeleteSymbolicLink(PVOID ObjectBody)
391 {
392     POBJECT_SYMBOLIC_LINK SymlinkObject = (POBJECT_SYMBOLIC_LINK)ObjectBody;
393 
394     /* Make sure that the symbolic link has a name */
395     if (SymlinkObject->LinkTarget.Buffer)
396     {
397         /* Free the name */
398         ExFreePool(SymlinkObject->LinkTarget.Buffer);
399         SymlinkObject->LinkTarget.Buffer = NULL;
400     }
401 }
402 
403 /*++
404 * @name ObpParseSymbolicLink
405 *
406 *     The ObpParseSymbolicLink routine <FILLMEIN>
407 *
408 * @param Object
409 *        <FILLMEIN>
410 *
411 * @param NextObject
412 *        <FILLMEIN>
413 *
414 * @param FullPath
415 *        <FILLMEIN>
416 *
417 * @param RemainingPath
418 *        <FILLMEIN>
419 *
420 * @param Attributes
421 *        <FILLMEIN>
422 *
423 * @return STATUS_SUCCESS or appropriate error value.
424 *
425 * @remarks None.
426 *
427 *--*/
428 NTSTATUS
429 NTAPI
430 ObpParseSymbolicLink(IN PVOID ParsedObject,
431                      IN PVOID ObjectType,
432                      IN OUT PACCESS_STATE AccessState,
433                      IN KPROCESSOR_MODE AccessMode,
434                      IN ULONG Attributes,
435                      IN OUT PUNICODE_STRING FullPath,
436                      IN OUT PUNICODE_STRING RemainingName,
437                      IN OUT PVOID Context OPTIONAL,
438                      IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
439                      OUT PVOID *NextObject)
440 {
441     POBJECT_SYMBOLIC_LINK SymlinkObject = (POBJECT_SYMBOLIC_LINK)ParsedObject;
442     PUNICODE_STRING TargetPath;
443     PWSTR NewTargetPath;
444     ULONG LengthUsed, MaximumLength, TempLength;
445     NTSTATUS Status;
446     PAGED_CODE();
447 
448     /* Assume failure */
449     *NextObject = NULL;
450 
451     /* Check if we're out of name to parse */
452     if (!RemainingName->Length)
453     {
454         /* Check if we got an object type */
455         if (ObjectType)
456         {
457             /* Reference the object only */
458             Status = ObReferenceObjectByPointer(ParsedObject,
459                                                 0,
460                                                 ObjectType,
461                                                 AccessMode);
462             if (NT_SUCCESS(Status))
463             {
464                 /* Return it */
465                 *NextObject = ParsedObject;
466             }
467 
468             if ((NT_SUCCESS(Status)) || (Status != STATUS_OBJECT_TYPE_MISMATCH))
469             {
470                 /* Fail */
471                 return Status;
472             }
473         }
474     }
475     else if (RemainingName->Buffer[0] != OBJ_NAME_PATH_SEPARATOR)
476     {
477         /* Symbolic links must start with a backslash */
478         return STATUS_OBJECT_TYPE_MISMATCH;
479     }
480 
481     /* Check if this symlink is bound to a specific object */
482     if (SymlinkObject->LinkTargetObject)
483     {
484         UNIMPLEMENTED;
485     }
486 
487     /* Set the target path and length */
488     TargetPath = &SymlinkObject->LinkTarget;
489     TempLength = TargetPath->Length;
490 
491     /*
492      * Strip off the extra trailing '\', if we don't do this we will end up
493      * adding a extra '\' between TargetPath and RemainingName
494      * causing caller's like ObpLookupObjectName() to fail.
495      */
496     if (TempLength && RemainingName->Length)
497     {
498         /* The target and remaining names aren't empty, so check for slashes */
499         if ((TargetPath->Buffer[TempLength / sizeof(WCHAR) - 1] ==
500             OBJ_NAME_PATH_SEPARATOR) &&
501             (RemainingName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR))
502         {
503             /* Reduce the length by one to cut off the extra '\' */
504             TempLength -= sizeof(OBJ_NAME_PATH_SEPARATOR);
505         }
506     }
507 
508     /* Calculate the new length */
509     LengthUsed = TempLength + RemainingName->Length;
510 
511     /* Check if it's not too much */
512     if (LengthUsed > 0xFFF0)
513         return STATUS_NAME_TOO_LONG;
514 
515     /* Optimization: check if the new name is shorter */
516     if (FullPath->MaximumLength <= LengthUsed)
517     {
518         /* It's not, allocate a new one */
519         MaximumLength = LengthUsed + sizeof(WCHAR);
520         NewTargetPath = ExAllocatePoolWithTag(NonPagedPool,
521                                               MaximumLength,
522                                               OB_NAME_TAG);
523         if (!NewTargetPath) return STATUS_INSUFFICIENT_RESOURCES;
524     }
525     else
526     {
527         /* It is! Reuse the name... */
528         MaximumLength = FullPath->MaximumLength;
529         NewTargetPath = FullPath->Buffer;
530     }
531 
532     /* Make sure we have a length */
533     if (RemainingName->Length)
534     {
535         /* Copy the new path */
536         RtlMoveMemory((PVOID)((ULONG_PTR)NewTargetPath + TempLength),
537                       RemainingName->Buffer,
538                       RemainingName->Length);
539     }
540 
541     /* Copy the target path and null-terminate it */
542     RtlCopyMemory(NewTargetPath, TargetPath->Buffer, TempLength);
543     NewTargetPath[LengthUsed / sizeof(WCHAR)] = UNICODE_NULL;
544 
545     /* If the optimization didn't work, free the old buffer */
546     if (NewTargetPath != FullPath->Buffer) ExFreePool(FullPath->Buffer);
547 
548     /* Update the path values */
549     FullPath->Length = (USHORT)LengthUsed;
550     FullPath->MaximumLength = (USHORT)MaximumLength;
551     FullPath->Buffer = NewTargetPath;
552 
553     /* Tell the parse routine to start reparsing */
554     return STATUS_REPARSE;
555 }
556 
557 /* PUBLIC FUNCTIONS **********************************************************/
558 
559 /*++
560 * @name NtCreateSymbolicLinkObject
561 * @implemented NT4
562 *
563 *     The NtCreateSymbolicLinkObject opens or creates a symbolic link object.
564 *
565 * @param LinkHandle
566 *        Variable which receives the symlink handle.
567 *
568 * @param DesiredAccess
569 *        Desired access to the symlink.
570 *
571 * @param ObjectAttributes
572 *        Structure describing the symlink.
573 *
574 * @param LinkTarget
575 *        Unicode string defining the symlink's target
576 *
577 * @return STATUS_SUCCESS or appropriate error value.
578 *
579 * @remarks None.
580 *
581 *--*/
582 NTSTATUS
583 NTAPI
584 NtCreateSymbolicLinkObject(OUT PHANDLE LinkHandle,
585                            IN ACCESS_MASK DesiredAccess,
586                            IN POBJECT_ATTRIBUTES ObjectAttributes,
587                            IN PUNICODE_STRING LinkTarget)
588 {
589     HANDLE hLink;
590     POBJECT_SYMBOLIC_LINK SymbolicLink;
591     UNICODE_STRING CapturedLinkTarget;
592     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
593     NTSTATUS Status;
594     PAGED_CODE();
595 
596     /* Check if we need to probe parameters */
597     if (PreviousMode != KernelMode)
598     {
599         _SEH2_TRY
600         {
601             /* Probe the target */
602             CapturedLinkTarget = ProbeForReadUnicodeString(LinkTarget);
603             ProbeForRead(CapturedLinkTarget.Buffer,
604                          CapturedLinkTarget.MaximumLength,
605                          sizeof(WCHAR));
606 
607             /* Probe the return handle */
608             ProbeForWriteHandle(LinkHandle);
609         }
610         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
611         {
612             /* Return the exception code */
613             _SEH2_YIELD(return _SEH2_GetExceptionCode());
614         }
615         _SEH2_END;
616     }
617     else
618     {
619         /* No need to capture */
620         CapturedLinkTarget = *LinkTarget;
621     }
622 
623     /* Check if the maximum length is odd */
624     if (CapturedLinkTarget.MaximumLength % sizeof(WCHAR))
625     {
626         /* Round it down */
627         CapturedLinkTarget.MaximumLength =
628             (USHORT)ALIGN_DOWN(CapturedLinkTarget.MaximumLength, WCHAR);
629     }
630 
631     /* Fail if the length is odd, or if the maximum is smaller or 0 */
632     if ((CapturedLinkTarget.Length % sizeof(WCHAR)) ||
633         (CapturedLinkTarget.MaximumLength < CapturedLinkTarget.Length) ||
634         !(CapturedLinkTarget.MaximumLength))
635     {
636         /* This message is displayed on the debugger in Windows */
637         DbgPrint("OB: Invalid symbolic link target - %wZ\n",
638                  &CapturedLinkTarget);
639         return STATUS_INVALID_PARAMETER;
640     }
641 
642     /* Create the object */
643     Status = ObCreateObject(PreviousMode,
644                             ObpSymbolicLinkObjectType,
645                             ObjectAttributes,
646                             PreviousMode,
647                             NULL,
648                             sizeof(OBJECT_SYMBOLIC_LINK),
649                             0,
650                             0,
651                             (PVOID*)&SymbolicLink);
652     if (NT_SUCCESS(Status))
653     {
654         /* Success! Fill in the creation time immediately */
655         KeQuerySystemTime(&SymbolicLink->CreationTime);
656 
657         /* Setup the target name */
658         SymbolicLink->LinkTarget.Length = CapturedLinkTarget.Length;
659         SymbolicLink->LinkTarget.MaximumLength = CapturedLinkTarget.MaximumLength;
660         SymbolicLink->LinkTarget.Buffer =
661             ExAllocatePoolWithTag(PagedPool,
662                                   CapturedLinkTarget.MaximumLength,
663                                   TAG_SYMLINK_TARGET);
664         if (!SymbolicLink->LinkTarget.Buffer)
665         {
666             /* Dereference the symbolic link object and fail */
667             ObDereferenceObject(SymbolicLink);
668             return STATUS_NO_MEMORY;
669         }
670 
671         /* Copy it */
672         _SEH2_TRY
673         {
674             RtlCopyMemory(SymbolicLink->LinkTarget.Buffer,
675                           CapturedLinkTarget.Buffer,
676                           CapturedLinkTarget.MaximumLength);
677         }
678         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
679         {
680             ObDereferenceObject(SymbolicLink);
681             _SEH2_YIELD(return _SEH2_GetExceptionCode());
682         }
683         _SEH2_END;
684 
685         /* Initialize the remaining name, dos drive index and target object */
686         SymbolicLink->LinkTargetObject = NULL;
687         SymbolicLink->DosDeviceDriveIndex = 0;
688         RtlInitUnicodeString(&SymbolicLink->LinkTargetRemaining, NULL);
689 
690         /* Insert it into the object tree */
691         Status = ObInsertObject(SymbolicLink,
692                                 NULL,
693                                 DesiredAccess,
694                                 0,
695                                 NULL,
696                                 &hLink);
697         if (NT_SUCCESS(Status))
698         {
699             _SEH2_TRY
700             {
701                 /* Return the handle to caller */
702                 *LinkHandle = hLink;
703             }
704             _SEH2_EXCEPT(ExSystemExceptionFilter())
705             {
706                 /* Get exception code */
707                 Status = _SEH2_GetExceptionCode();
708             }
709             _SEH2_END;
710         }
711     }
712 
713     /* Return status to caller */
714     return Status;
715 }
716 
717 /*++
718 * @name NtOpenSymbolicLinkObject
719 * @implemented NT4
720 *
721 *     The NtOpenSymbolicLinkObject opens a symbolic link object.
722 *
723 * @param LinkHandle
724 *        Variable which receives the symlink handle.
725 *
726 * @param DesiredAccess
727 *        Desired access to the symlink.
728 *
729 * @param ObjectAttributes
730 *        Structure describing the symlink.
731 *
732 * @return STATUS_SUCCESS or appropriate error value.
733 *
734 * @remarks None.
735 *
736 *--*/
737 NTSTATUS
738 NTAPI
739 NtOpenSymbolicLinkObject(OUT PHANDLE LinkHandle,
740                          IN ACCESS_MASK DesiredAccess,
741                          IN POBJECT_ATTRIBUTES ObjectAttributes)
742 {
743     HANDLE hLink;
744     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
745     NTSTATUS Status;
746     PAGED_CODE();
747 
748     /* Check if we need to probe parameters */
749     if (PreviousMode != KernelMode)
750     {
751         _SEH2_TRY
752         {
753             /* Probe the return handle */
754             ProbeForWriteHandle(LinkHandle);
755         }
756         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
757         {
758             /* Return the exception code */
759             _SEH2_YIELD(return _SEH2_GetExceptionCode());
760         }
761         _SEH2_END;
762     }
763 
764     /* Open the object */
765     Status = ObOpenObjectByName(ObjectAttributes,
766                                 ObpSymbolicLinkObjectType,
767                                 PreviousMode,
768                                 NULL,
769                                 DesiredAccess,
770                                 NULL,
771                                 &hLink);
772 
773     _SEH2_TRY
774     {
775         /* Return the handle to caller */
776         *LinkHandle = hLink;
777     }
778     _SEH2_EXCEPT(ExSystemExceptionFilter())
779     {
780         /* Get exception code */
781         Status = _SEH2_GetExceptionCode();
782     }
783     _SEH2_END;
784 
785     /* Return status to caller */
786     return Status;
787 }
788 
789 /*++
790 * @name NtQuerySymbolicLinkObject
791 * @implemented NT4
792 *
793 *     The NtQuerySymbolicLinkObject queries a symbolic link object.
794 *
795 * @param LinkHandle
796 *        Symlink handle to query
797 *
798 * @param LinkTarget
799 *        Unicode string defining the symlink's target
800 *
801 * @param ResultLength
802 *        Caller supplied storage for the number of bytes written (or NULL).
803 *
804 * @return STATUS_SUCCESS or appropriate error value.
805 *
806 * @remarks None.
807 *
808 *--*/
809 NTSTATUS
810 NTAPI
811 NtQuerySymbolicLinkObject(IN HANDLE LinkHandle,
812                           OUT PUNICODE_STRING LinkTarget,
813                           OUT PULONG ResultLength OPTIONAL)
814 {
815     UNICODE_STRING SafeLinkTarget = { 0, 0, NULL };
816     POBJECT_SYMBOLIC_LINK SymlinkObject;
817     KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
818     NTSTATUS Status;
819     ULONG LengthUsed;
820     PAGED_CODE();
821 
822     if (PreviousMode != KernelMode)
823     {
824         _SEH2_TRY
825         {
826             /* Probe the unicode string for read and write */
827             ProbeForWriteUnicodeString(LinkTarget);
828 
829             /* Probe the unicode string's buffer for write */
830             SafeLinkTarget = *LinkTarget;
831             ProbeForWrite(SafeLinkTarget.Buffer,
832                           SafeLinkTarget.MaximumLength,
833                           sizeof(WCHAR));
834 
835             /* Probe the return length */
836             if (ResultLength) ProbeForWriteUlong(ResultLength);
837         }
838         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
839         {
840             /* Return the exception code */
841             _SEH2_YIELD(return _SEH2_GetExceptionCode());
842         }
843         _SEH2_END;
844     }
845     else
846     {
847         /* No need to probe */
848         SafeLinkTarget = *LinkTarget;
849     }
850 
851     /* Reference the object */
852     Status = ObReferenceObjectByHandle(LinkHandle,
853                                        SYMBOLIC_LINK_QUERY,
854                                        ObpSymbolicLinkObjectType,
855                                        PreviousMode,
856                                        (PVOID *)&SymlinkObject,
857                                        NULL);
858     if (NT_SUCCESS(Status))
859     {
860         /* Lock the object */
861         ObpAcquireObjectLock(OBJECT_TO_OBJECT_HEADER(SymlinkObject));
862 
863         /*
864          * So here's the thing: If you specify a return length, then the
865          * implementation will use the maximum length. If you don't, then
866          * it will use the length.
867          */
868         LengthUsed = ResultLength ? SymlinkObject->LinkTarget.MaximumLength :
869                                     SymlinkObject->LinkTarget.Length;
870 
871         /* Enter SEH so we can safely copy */
872         _SEH2_TRY
873         {
874             /* Make sure our buffer will fit */
875             if (LengthUsed <= SafeLinkTarget.MaximumLength)
876             {
877                 /* Copy the buffer */
878                 RtlCopyMemory(SafeLinkTarget.Buffer,
879                               SymlinkObject->LinkTarget.Buffer,
880                               LengthUsed);
881 
882                 /* Copy the new length */
883                 LinkTarget->Length = SymlinkObject->LinkTarget.Length;
884             }
885             else
886             {
887                 /* Otherwise set the failure status */
888                 Status = STATUS_BUFFER_TOO_SMALL;
889             }
890 
891             /* In both cases, check if the required length was requested */
892             if (ResultLength)
893             {
894                 /* Then return it */
895                 *ResultLength = SymlinkObject->LinkTarget.MaximumLength;
896             }
897         }
898         _SEH2_EXCEPT(ExSystemExceptionFilter())
899         {
900             /* Get the error code */
901             Status = _SEH2_GetExceptionCode();
902         }
903         _SEH2_END;
904 
905         /* Unlock and dereference the object */
906         ObpReleaseObjectLock(OBJECT_TO_OBJECT_HEADER(SymlinkObject));
907         ObDereferenceObject(SymlinkObject);
908     }
909 
910     /* Return query status */
911     return Status;
912 }
913 
914 /* EOF */
915