3 * Copyright (C) 2011-2012 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
19 * COPYRIGHT: See COPYING in the top level directory
20 * PROJECT: ReactOS kernel
21 * FILE: drivers/filesystem/mountmgr/symlink.c
22 * PURPOSE: Mount Manager - Symbolic links functions
23 * PROGRAMMER: Pierre Schweitzer (pierre.schweitzer@reactos.org)
26 /* INCLUDES *****************************************************************/
33 UNICODE_STRING DeviceMount
= RTL_CONSTANT_STRING(MOUNTMGR_DEVICE_NAME
);
34 UNICODE_STRING DosDevicesMount
= RTL_CONSTANT_STRING(L
"\\DosDevices\\MountPointManager");
35 UNICODE_STRING DosDevices
= RTL_CONSTANT_STRING(L
"\\DosDevices\\");
36 UNICODE_STRING DeviceFloppy
= RTL_CONSTANT_STRING(L
"\\Device\\Floppy");
37 UNICODE_STRING DeviceCdRom
= RTL_CONSTANT_STRING(L
"\\Device\\CdRom");
38 UNICODE_STRING DosGlobal
= RTL_CONSTANT_STRING(L
"\\GLOBAL??\\");
39 UNICODE_STRING Global
= RTL_CONSTANT_STRING(L
"\\??\\");
40 UNICODE_STRING SafeVolumes
= RTL_CONSTANT_STRING(L
"\\Device\\VolumesSafeForWriteAccess");
41 UNICODE_STRING Volume
= RTL_CONSTANT_STRING(L
"\\??\\Volume");
42 UNICODE_STRING ReparseIndex
= RTL_CONSTANT_STRING(L
"\\$Extend\\$Reparse:$R:$INDEX_ALLOCATION");
48 CreateStringWithGlobal(IN PUNICODE_STRING DosName
,
49 OUT PUNICODE_STRING GlobalString
)
51 UNICODE_STRING IntGlobal
;
53 if (RtlPrefixUnicodeString(&DosDevices
, DosName
, TRUE
))
55 /* DOS device - use DOS global */
56 IntGlobal
.Length
= DosName
->Length
- DosDevices
.Length
+ DosGlobal
.Length
;
57 IntGlobal
.MaximumLength
= IntGlobal
.Length
+ sizeof(WCHAR
);
58 IntGlobal
.Buffer
= AllocatePool(IntGlobal
.MaximumLength
);
59 if (!IntGlobal
.Buffer
)
61 return STATUS_INSUFFICIENT_RESOURCES
;
64 RtlCopyMemory(IntGlobal
.Buffer
, DosGlobal
.Buffer
, DosGlobal
.Length
);
65 RtlCopyMemory(IntGlobal
.Buffer
+ (DosGlobal
.Length
/ sizeof(WCHAR
)),
66 DosName
->Buffer
+ (DosDevices
.Length
/ sizeof(WCHAR
)),
67 DosName
->Length
- DosDevices
.Length
);
68 IntGlobal
.Buffer
[IntGlobal
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
72 if (RtlPrefixUnicodeString(&Global
, DosName
, TRUE
))
74 /* Switch to DOS global */
75 IntGlobal
.Length
= DosName
->Length
- Global
.Length
+ DosGlobal
.Length
;
76 IntGlobal
.MaximumLength
= IntGlobal
.Length
+ sizeof(WCHAR
);
77 IntGlobal
.Buffer
= AllocatePool(IntGlobal
.MaximumLength
);
78 if (!IntGlobal
.Buffer
)
80 return STATUS_INSUFFICIENT_RESOURCES
;
83 RtlCopyMemory(IntGlobal
.Buffer
, DosGlobal
.Buffer
, DosGlobal
.Length
);
84 RtlCopyMemory(IntGlobal
.Buffer
+ (DosGlobal
.Length
/ sizeof(WCHAR
)),
85 DosName
->Buffer
+ (Global
.Length
/ sizeof(WCHAR
)),
86 DosName
->Length
- Global
.Length
);
87 IntGlobal
.Buffer
[IntGlobal
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
91 /* Simply duplicate string */
92 IntGlobal
.Length
= DosName
->Length
;
93 IntGlobal
.MaximumLength
= DosName
->MaximumLength
;
94 IntGlobal
.Buffer
= AllocatePool(IntGlobal
.MaximumLength
);
95 if (!IntGlobal
.Buffer
)
97 return STATUS_INSUFFICIENT_RESOURCES
;
100 RtlCopyMemory(IntGlobal
.Buffer
, DosName
->Buffer
, IntGlobal
.MaximumLength
);
105 GlobalString
->Length
= IntGlobal
.Length
;
106 GlobalString
->MaximumLength
= IntGlobal
.MaximumLength
;
107 GlobalString
->Buffer
= IntGlobal
.Buffer
;
109 return STATUS_SUCCESS
;
116 GlobalCreateSymbolicLink(IN PUNICODE_STRING DosName
,
117 IN PUNICODE_STRING DeviceName
)
120 UNICODE_STRING GlobalName
;
122 UNREFERENCED_PARAMETER(DeviceName
);
124 /* First create the global string */
125 Status
= CreateStringWithGlobal(DosName
, &GlobalName
);
126 if (!NT_SUCCESS(Status
))
131 /* Then, create the symlink */
132 Status
= IoCreateSymbolicLink(&GlobalName
, DosName
);
134 FreePool(GlobalName
.Buffer
);
143 GlobalDeleteSymbolicLink(IN PUNICODE_STRING DosName
)
146 UNICODE_STRING GlobalName
;
148 /* Recreate the string (to find the link) */
149 Status
= CreateStringWithGlobal(DosName
, &GlobalName
);
150 if (!NT_SUCCESS(Status
))
155 /* And delete the link */
156 Status
= IoDeleteSymbolicLink(&GlobalName
);
158 FreePool(GlobalName
.Buffer
);
167 SendLinkCreated(IN PUNICODE_STRING SymbolicName
)
173 PFILE_OBJECT FileObject
;
174 PIO_STACK_LOCATION Stack
;
175 PMOUNTDEV_NAME Name
= NULL
;
176 PDEVICE_OBJECT DeviceObject
;
177 IO_STATUS_BLOCK IoStatusBlock
;
179 /* Get the device associated with the name */
180 Status
= IoGetDeviceObjectPointer(SymbolicName
,
181 FILE_READ_ATTRIBUTES
,
184 if (!NT_SUCCESS(Status
))
189 /* Get attached device (will notify it) */
190 DeviceObject
= IoGetAttachedDeviceReference(FileObject
->DeviceObject
);
192 /* NameSize is the size of the whole MOUNTDEV_NAME struct */
193 NameSize
= sizeof(USHORT
) + SymbolicName
->Length
;
194 Name
= AllocatePool(NameSize
);
200 /* Initialize struct */
201 Name
->NameLength
= SymbolicName
->Length
;
202 RtlCopyMemory(Name
->Name
, SymbolicName
->Buffer
, SymbolicName
->Length
);
204 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
205 /* Microsoft does it twice... Once with limited access, second with any
208 Irp
= IoBuildDeviceIoControlRequest(CTL_CODE(MOUNTDEVCONTROLTYPE
, 4, METHOD_BUFFERED
, FILE_READ_ACCESS
| FILE_WRITE_ACCESS
),
217 /* This one can fail, no one matters */
220 Stack
= IoGetNextIrpStackLocation(Irp
);
221 Stack
->FileObject
= FileObject
;
223 Status
= IoCallDriver(DeviceObject
, Irp
);
224 if (Status
== STATUS_PENDING
)
226 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
230 /* Then, second one */
231 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
232 Irp
= IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_LINK_CREATED
,
246 Stack
= IoGetNextIrpStackLocation(Irp
);
247 Stack
->FileObject
= FileObject
;
250 Status
= IoCallDriver(DeviceObject
, Irp
);
251 if (Status
== STATUS_PENDING
)
253 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
262 ObDereferenceObject(DeviceObject
);
263 ObDereferenceObject(FileObject
);
272 SendLinkDeleted(IN PUNICODE_STRING DeviceName
,
273 IN PUNICODE_STRING SymbolicName
)
279 PFILE_OBJECT FileObject
;
280 PIO_STACK_LOCATION Stack
;
281 PMOUNTDEV_NAME Name
= NULL
;
282 PDEVICE_OBJECT DeviceObject
;
283 IO_STATUS_BLOCK IoStatusBlock
;
285 /* Get the device associated with the name */
286 Status
= IoGetDeviceObjectPointer(DeviceName
,
287 FILE_READ_ATTRIBUTES
,
290 if (!NT_SUCCESS(Status
))
295 /* Get attached device (will notify it) */
296 DeviceObject
= IoGetAttachedDeviceReference(FileObject
->DeviceObject
);
298 /* NameSize is the size of the whole MOUNTDEV_NAME struct */
299 NameSize
= sizeof(USHORT
) + SymbolicName
->Length
;
300 Name
= AllocatePool(NameSize
);
306 /* Initialize struct */
307 Name
->NameLength
= SymbolicName
->Length
;
308 RtlCopyMemory(Name
->Name
, SymbolicName
->Buffer
, SymbolicName
->Length
);
310 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
311 /* Cf: SendLinkCreated comment */
312 Irp
= IoBuildDeviceIoControlRequest(CTL_CODE(MOUNTDEVCONTROLTYPE
, 5, METHOD_BUFFERED
, FILE_READ_ACCESS
| FILE_WRITE_ACCESS
),
321 /* This one can fail, no one matters */
324 Stack
= IoGetNextIrpStackLocation(Irp
);
325 Stack
->FileObject
= FileObject
;
327 Status
= IoCallDriver(DeviceObject
, Irp
);
328 if (Status
== STATUS_PENDING
)
330 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
334 /* Then, second one */
335 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
336 Irp
= IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_LINK_DELETED
,
350 Stack
= IoGetNextIrpStackLocation(Irp
);
351 Stack
->FileObject
= FileObject
;
354 Status
= IoCallDriver(DeviceObject
, Irp
);
355 if (Status
== STATUS_PENDING
)
357 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
366 ObDereferenceObject(DeviceObject
);
367 ObDereferenceObject(FileObject
);
377 SymbolicLinkNamesFromUniqueIdCount(IN PWSTR ValueName
,
380 IN ULONG ValueLength
,
382 IN PVOID EntryContext
)
384 UNICODE_STRING ValueNameString
;
385 PMOUNTDEV_UNIQUE_ID UniqueId
= Context
;
387 if (ValueName
[0] != L
'#' || ValueType
!= REG_BINARY
||
388 (UniqueId
->UniqueIdLength
!= ValueLength
))
390 return STATUS_SUCCESS
;
393 if (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) != ValueLength
)
395 return STATUS_SUCCESS
;
398 /* That one matched, increase count */
399 RtlInitUnicodeString(&ValueNameString
, ValueName
);
400 if (ValueNameString
.Length
)
402 (*((PULONG
)EntryContext
))++;
405 return STATUS_SUCCESS
;
413 SymbolicLinkNamesFromUniqueIdQuery(IN PWSTR ValueName
,
416 IN ULONG ValueLength
,
418 IN PVOID EntryContext
)
420 UNICODE_STRING ValueNameString
;
421 PMOUNTDEV_UNIQUE_ID UniqueId
= Context
;
422 /* Unicode strings table */
423 PUNICODE_STRING ReturnString
= EntryContext
;
425 if (ValueName
[0] != L
'#' || ValueType
!= REG_BINARY
||
426 (UniqueId
->UniqueIdLength
!= ValueLength
))
428 return STATUS_SUCCESS
;
431 if (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) != ValueLength
)
433 return STATUS_SUCCESS
;
436 /* Unique ID matches, let's put the symlink */
437 RtlInitUnicodeString(&ValueNameString
, ValueName
);
438 if (!ValueNameString
.Length
)
440 return STATUS_SUCCESS
;
443 /* Allocate string to copy */
444 ValueNameString
.Buffer
= AllocatePool(ValueNameString
.MaximumLength
);
445 if (!ValueNameString
.Buffer
)
447 return STATUS_SUCCESS
;
451 RtlCopyMemory(ValueNameString
.Buffer
, ValueName
, ValueNameString
.Length
);
452 ValueNameString
.Buffer
[ValueNameString
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
454 while (ReturnString
->Length
)
459 /* And return that string */
460 *ReturnString
= ValueNameString
;
462 return STATUS_SUCCESS
;
469 CreateNewVolumeName(OUT PUNICODE_STRING VolumeName
,
470 IN PGUID VolumeGuid OPTIONAL
)
474 UNICODE_STRING GuidString
;
476 /* If no GUID was provided, then create one */
479 Status
= ExUuidCreate(&Guid
);
480 if (!NT_SUCCESS(Status
))
487 RtlCopyMemory(&Guid
, VolumeGuid
, sizeof(GUID
));
490 /* Convert GUID to string */
491 Status
= RtlStringFromGUID(&Guid
, &GuidString
);
492 if (!NT_SUCCESS(Status
))
497 /* Size for volume namespace, litteral GUID, and null char */
498 VolumeName
->MaximumLength
= 0x14 + 0x4C + sizeof(UNICODE_NULL
);
499 VolumeName
->Buffer
= AllocatePool(0x14 + 0x4C + sizeof(UNICODE_NULL
));
500 if (!VolumeName
->Buffer
)
502 Status
= STATUS_INSUFFICIENT_RESOURCES
;
506 RtlCopyUnicodeString(VolumeName
, &Volume
);
507 RtlAppendUnicodeStringToString(VolumeName
, &GuidString
);
508 VolumeName
->Buffer
[VolumeName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
509 Status
= STATUS_SUCCESS
;
512 ExFreePoolWithTag(GuidString
.Buffer
, 0);
521 QuerySymbolicLinkNamesFromStorage(IN PDEVICE_EXTENSION DeviceExtension
,
522 IN PDEVICE_INFORMATION DeviceInformation
,
523 IN PUNICODE_STRING SuggestedLinkName
,
524 IN BOOLEAN UseOnlyIfThereAreNoOtherLinks
,
525 OUT PUNICODE_STRING
* SymLinks
,
526 OUT PULONG SymLinkCount
,
532 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
534 UNREFERENCED_PARAMETER(DeviceExtension
);
536 /* First of all, count links */
537 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
538 QueryTable
[0].QueryRoutine
= SymbolicLinkNamesFromUniqueIdCount
;
539 QueryTable
[0].EntryContext
= SymLinkCount
;
542 Status
= RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
545 DeviceInformation
->UniqueId
,
547 if (!NT_SUCCESS(Status
))
552 /* Check if we have to write a new one first */
553 if (SuggestedLinkName
&& !IsDriveLetter(SuggestedLinkName
) &&
554 UseOnlyIfThereAreNoOtherLinks
&& *SymLinkCount
== 0)
563 /* If has GUID, it makes one more link */
572 RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
574 SuggestedLinkName
->Buffer
,
576 DeviceInformation
->UniqueId
->UniqueId
,
577 DeviceInformation
->UniqueId
->UniqueIdLength
);
579 /* And recount all the needed links */
580 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
581 QueryTable
[0].QueryRoutine
= SymbolicLinkNamesFromUniqueIdCount
;
582 QueryTable
[0].EntryContext
= SymLinkCount
;
585 Status
= RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
588 DeviceInformation
->UniqueId
,
590 if (!NT_SUCCESS(Status
))
592 return STATUS_NOT_FOUND
;
596 /* Not links found? */
599 return STATUS_NOT_FOUND
;
602 /* Allocate a buffer big enough to hold symlinks (table of unicode strings) */
603 *SymLinks
= AllocatePool(*SymLinkCount
* sizeof(UNICODE_STRING
));
606 return STATUS_INSUFFICIENT_RESOURCES
;
609 /* Prepare to query links */
610 RtlZeroMemory(*SymLinks
, *SymLinkCount
* sizeof(UNICODE_STRING
));
611 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
612 QueryTable
[0].QueryRoutine
= SymbolicLinkNamesFromUniqueIdQuery
;
614 /* No GUID? Keep it that way */
617 QueryTable
[0].EntryContext
= *SymLinks
;
619 /* Otherwise, first create volume name */
622 Status
= CreateNewVolumeName(SymLinks
[0], Guid
);
623 if (!NT_SUCCESS(Status
))
629 /* Skip first link (ours) */
630 QueryTable
[0].EntryContext
= *SymLinks
+ 1;
634 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
637 DeviceInformation
->UniqueId
,
640 return STATUS_SUCCESS
;
646 PSAVED_LINK_INFORMATION
647 RemoveSavedLinks(IN PDEVICE_EXTENSION DeviceExtension
,
648 IN PMOUNTDEV_UNIQUE_ID UniqueId
)
650 PLIST_ENTRY NextEntry
;
651 PSAVED_LINK_INFORMATION SavedLinkInformation
;
653 /* No saved links? Easy! */
654 if (IsListEmpty(&(DeviceExtension
->SavedLinksListHead
)))
659 /* Now, browse saved links */
660 for (NextEntry
= DeviceExtension
->SavedLinksListHead
.Flink
;
661 NextEntry
!= &(DeviceExtension
->SavedLinksListHead
);
662 NextEntry
= NextEntry
->Flink
)
664 SavedLinkInformation
= CONTAINING_RECORD(NextEntry
,
665 SAVED_LINK_INFORMATION
,
666 SavedLinksListEntry
);
668 /* Find the one that matches */
669 if (SavedLinkInformation
->UniqueId
->UniqueIdLength
== UniqueId
->UniqueIdLength
)
671 if (RtlCompareMemory(SavedLinkInformation
->UniqueId
->UniqueId
,
673 UniqueId
->UniqueIdLength
) ==
674 UniqueId
->UniqueIdLength
)
676 /* Remove it and return it */
677 RemoveEntryList(&(SavedLinkInformation
->SavedLinksListEntry
));
678 return SavedLinkInformation
;
683 /* None found (none removed) */
691 QuerySuggestedLinkName(IN PUNICODE_STRING SymbolicName
,
692 OUT PUNICODE_STRING SuggestedLinkName
,
693 OUT PBOOLEAN UseOnlyIfThereAreNoOtherLinks
)
699 PFILE_OBJECT FileObject
;
700 PDEVICE_OBJECT DeviceObject
;
701 IO_STATUS_BLOCK IoStatusBlock
;
702 PIO_STACK_LOCATION IoStackLocation
;
703 PMOUNTDEV_SUGGESTED_LINK_NAME IoCtlSuggested
;
705 /* First, get device */
706 Status
= IoGetDeviceObjectPointer(SymbolicName
,
707 FILE_READ_ATTRIBUTES
,
710 if (!NT_SUCCESS(Status
))
715 /* Then, get attached device */
716 DeviceObject
= IoGetAttachedDeviceReference(FileObject
->DeviceObject
);
718 /* Then, prepare buffer to query suggested name */
719 IoCtlSuggested
= AllocatePool(sizeof(MOUNTDEV_SUGGESTED_LINK_NAME
));
722 Status
= STATUS_INSUFFICIENT_RESOURCES
;
726 /* Prepare request */
727 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
728 Irp
= IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME
,
733 sizeof(MOUNTDEV_SUGGESTED_LINK_NAME
),
739 Status
= STATUS_INSUFFICIENT_RESOURCES
;
743 IoStackLocation
= IoGetNextIrpStackLocation(Irp
);
744 IoStackLocation
->FileObject
= FileObject
;
747 Status
= IoCallDriver(DeviceObject
, Irp
);
748 if (Status
== STATUS_PENDING
)
750 KeWaitForSingleObject(&Event
, Executive
, KernelMode
,
752 Status
= IoStatusBlock
.Status
;
755 /* Overflow? Normal */
756 if (Status
== STATUS_BUFFER_OVERFLOW
)
758 /* Reallocate big enough buffer */
759 NameLength
= IoCtlSuggested
->NameLength
+ sizeof(MOUNTDEV_SUGGESTED_LINK_NAME
);
760 FreePool(IoCtlSuggested
);
762 IoCtlSuggested
= AllocatePool(NameLength
);
765 Status
= STATUS_INSUFFICIENT_RESOURCES
;
770 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
771 Irp
= IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME
,
782 Status
= STATUS_INSUFFICIENT_RESOURCES
;
786 IoStackLocation
= IoGetNextIrpStackLocation(Irp
);
787 IoStackLocation
->FileObject
= FileObject
;
789 Status
= IoCallDriver(DeviceObject
, Irp
);
790 if (Status
== STATUS_PENDING
)
792 KeWaitForSingleObject(&Event
, Executive
, KernelMode
,
794 Status
= IoStatusBlock
.Status
;
798 if (!NT_SUCCESS(Status
))
803 /* Now we have suggested name, copy it */
804 SuggestedLinkName
->Length
= IoCtlSuggested
->NameLength
;
805 SuggestedLinkName
->MaximumLength
= IoCtlSuggested
->NameLength
+ sizeof(UNICODE_NULL
);
806 SuggestedLinkName
->Buffer
= AllocatePool(IoCtlSuggested
->NameLength
+ sizeof(UNICODE_NULL
));
807 if (!SuggestedLinkName
->Buffer
)
809 Status
= STATUS_INSUFFICIENT_RESOURCES
;
813 RtlCopyMemory(SuggestedLinkName
->Buffer
, IoCtlSuggested
->Name
, IoCtlSuggested
->NameLength
);
814 SuggestedLinkName
->Buffer
[SuggestedLinkName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
817 /* Also return its priority */
818 *UseOnlyIfThereAreNoOtherLinks
= IoCtlSuggested
->UseOnlyIfThereAreNoOtherLinks
;
821 FreePool(IoCtlSuggested
);
824 ObDereferenceObject(DeviceObject
);
825 ObDereferenceObject(FileObject
);
834 RedirectSavedLink(IN PSAVED_LINK_INFORMATION SavedLinkInformation
,
835 IN PUNICODE_STRING DosName
,
836 IN PUNICODE_STRING NewLink
)
838 PLIST_ENTRY NextEntry
;
839 PSYMLINK_INFORMATION SymlinkInformation
;
842 for (NextEntry
= SavedLinkInformation
->SymbolicLinksListHead
.Flink
;
843 NextEntry
!= &(SavedLinkInformation
->SymbolicLinksListHead
);
844 NextEntry
= NextEntry
->Flink
)
846 SymlinkInformation
= CONTAINING_RECORD(NextEntry
, SYMLINK_INFORMATION
, SymbolicLinksListEntry
);
848 if (!RtlEqualUnicodeString(DosName
, &(SymlinkInformation
->Name
), TRUE
))
850 /* Delete old link */
851 GlobalDeleteSymbolicLink(DosName
);
852 /* Set its new location */
853 GlobalCreateSymbolicLink(DosName
, NewLink
);
855 /* And remove it from the list (not valid any more) */
856 RemoveEntryList(&(SymlinkInformation
->SymbolicLinksListEntry
));
857 FreePool(SymlinkInformation
->Name
.Buffer
);
858 FreePool(SymlinkInformation
);
871 DeleteSymbolicLinkNameFromMemory(IN PDEVICE_EXTENSION DeviceExtension
,
872 IN PUNICODE_STRING SymbolicLink
,
873 IN BOOLEAN MarkOffline
)
875 PLIST_ENTRY DeviceEntry
, SymbolEntry
;
876 PDEVICE_INFORMATION DeviceInformation
;
877 PSYMLINK_INFORMATION SymlinkInformation
;
879 /* First of all, ensure we have devices */
880 if (IsListEmpty(&(DeviceExtension
->DeviceListHead
)))
885 /* Then, look for the symbolic name */
886 for (DeviceEntry
= DeviceExtension
->DeviceListHead
.Flink
;
887 DeviceEntry
!= &(DeviceExtension
->DeviceListHead
);
888 DeviceEntry
= DeviceEntry
->Flink
)
890 DeviceInformation
= CONTAINING_RECORD(DeviceEntry
, DEVICE_INFORMATION
, DeviceListEntry
);
892 for (SymbolEntry
= DeviceInformation
->SymbolicLinksListHead
.Flink
;
893 SymbolEntry
!= &(DeviceInformation
->SymbolicLinksListHead
);
894 SymbolEntry
= SymbolEntry
->Flink
)
896 SymlinkInformation
= CONTAINING_RECORD(SymbolEntry
, SYMLINK_INFORMATION
, SymbolicLinksListEntry
);
898 /* One we have found it */
899 if (RtlCompareUnicodeString(SymbolicLink
, &(SymlinkInformation
->Name
), TRUE
) == 0)
901 /* Check if caller just want it to be offline */
904 SymlinkInformation
->Online
= FALSE
;
908 /* If not, delete it & notify */
909 SendLinkDeleted(&(DeviceInformation
->SymbolicName
), SymbolicLink
);
910 RemoveEntryList(&(SymlinkInformation
->SymbolicLinksListEntry
));
912 FreePool(SymlinkInformation
->Name
.Buffer
);
913 FreePool(SymlinkInformation
);
916 /* No need to go farther */
929 IsDriveLetter(PUNICODE_STRING SymbolicName
)
932 BOOLEAN Result
= FALSE
;
934 /* We must have a precise length */
935 if (SymbolicName
->Length
!= sizeof(DosDevices
.Buffer
) + 2 * sizeof(WCHAR
))
940 /* Check if len is correct */
941 Letter
= SymbolicName
->Buffer
[sizeof(DosDevices
.Buffer
) / sizeof(WCHAR
)];
942 if (((Letter
>= L
'A' && Letter
<= L
'Z') || Letter
== (WCHAR
)-1) &&
943 SymbolicName
->Buffer
[(sizeof(DosDevices
.Buffer
) + sizeof(WCHAR
)) / sizeof (WCHAR
)] == L
':')
945 /* In case it's not a normal drive letter, check differently */
946 SymbolicName
->Length
= sizeof(DosDevices
.Buffer
);
947 Result
= RtlEqualUnicodeString(SymbolicName
, &DosDevices
, TRUE
);
948 SymbolicName
->Length
= sizeof(DosDevices
.Buffer
) + 2 * sizeof(WCHAR
);
958 MountMgrQuerySymbolicLink(IN PUNICODE_STRING SymbolicName
,
959 IN OUT PUNICODE_STRING LinkTarget
)
963 OBJECT_ATTRIBUTES ObjectAttributes
;
965 /* Open the symbolic link */
966 InitializeObjectAttributes(&ObjectAttributes
,
968 OBJ_KERNEL_HANDLE
| OBJ_CASE_INSENSITIVE
,
972 Status
= ZwOpenSymbolicLinkObject(&LinkHandle
,
975 if (!NT_SUCCESS(Status
))
980 /* Query its target */
981 Status
= ZwQuerySymbolicLinkObject(LinkHandle
,
987 if (!NT_SUCCESS(Status
))
992 if (LinkTarget
->Length
<= sizeof(WCHAR
))
997 /* If it's not finished by \, just return */
998 if (LinkTarget
->Buffer
[LinkTarget
->Length
/ sizeof(WCHAR
) - 1] != L
'\\')
1003 /* Otherwise, ensure to drop the tailing \ */
1004 LinkTarget
->Length
-= sizeof(WCHAR
);
1005 LinkTarget
->Buffer
[LinkTarget
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;