[NTOSKRNL] Rework ObpDeleteSymbolicLinkName and ObpCreateSymbolicLinkName
[reactos.git] / ntoskrnl / ob / oblink.c
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 */