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 /* First create the global string */
123 Status
= CreateStringWithGlobal(DosName
, &GlobalName
);
124 if (!NT_SUCCESS(Status
))
129 /* Then, create the symlink */
130 Status
= IoCreateSymbolicLink(&GlobalName
, DosName
);
132 FreePool(GlobalName
.Buffer
);
141 GlobalDeleteSymbolicLink(IN PUNICODE_STRING DosName
)
144 UNICODE_STRING GlobalName
;
146 /* Recreate the string (to find the link) */
147 Status
= CreateStringWithGlobal(DosName
, &GlobalName
);
148 if (!NT_SUCCESS(Status
))
153 /* And delete the link */
154 Status
= IoDeleteSymbolicLink(&GlobalName
);
156 FreePool(GlobalName
.Buffer
);
165 SendLinkCreated(IN PUNICODE_STRING SymbolicName
)
171 PFILE_OBJECT FileObject
;
172 PIO_STACK_LOCATION Stack
;
173 PMOUNTDEV_NAME Name
= NULL
;
174 PDEVICE_OBJECT DeviceObject
;
175 IO_STATUS_BLOCK IoStatusBlock
;
177 /* Get the device associated with the name */
178 Status
= IoGetDeviceObjectPointer(SymbolicName
,
179 FILE_READ_ATTRIBUTES
,
182 if (!NT_SUCCESS(Status
))
187 /* Get attached device (will notify it) */
188 DeviceObject
= IoGetAttachedDeviceReference(FileObject
->DeviceObject
);
190 /* NameSize is the size of the whole MOUNTDEV_NAME struct */
191 NameSize
= sizeof(USHORT
) + SymbolicName
->Length
;
192 Name
= AllocatePool(NameSize
);
198 /* Initialize struct */
199 Name
->NameLength
= SymbolicName
->Length
;
200 RtlCopyMemory(Name
->Name
, SymbolicName
->Buffer
, SymbolicName
->Length
);
202 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
203 /* Microsoft does it twice... Once with limited access, second with any
206 Irp
= IoBuildDeviceIoControlRequest(CTL_CODE(MOUNTDEVCONTROLTYPE
, 4, METHOD_BUFFERED
, FILE_READ_ACCESS
| FILE_WRITE_ACCESS
),
215 /* This one can fail, no one matters */
218 Stack
= IoGetNextIrpStackLocation(Irp
);
219 Stack
->FileObject
= FileObject
;
221 Status
= IoCallDriver(DeviceObject
, Irp
);
222 if (Status
== STATUS_PENDING
)
224 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
228 /* Then, second one */
229 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
230 Irp
= IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_LINK_CREATED
,
244 Stack
= IoGetNextIrpStackLocation(Irp
);
245 Stack
->FileObject
= FileObject
;
248 Status
= IoCallDriver(DeviceObject
, Irp
);
249 if (Status
== STATUS_PENDING
)
251 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
260 ObDereferenceObject(DeviceObject
);
261 ObDereferenceObject(FileObject
);
270 SendLinkDeleted(IN PUNICODE_STRING DeviceName
,
271 IN PUNICODE_STRING SymbolicName
)
277 PFILE_OBJECT FileObject
;
278 PIO_STACK_LOCATION Stack
;
279 PMOUNTDEV_NAME Name
= NULL
;
280 PDEVICE_OBJECT DeviceObject
;
281 IO_STATUS_BLOCK IoStatusBlock
;
283 /* Get the device associated with the name */
284 Status
= IoGetDeviceObjectPointer(DeviceName
,
285 FILE_READ_ATTRIBUTES
,
288 if (!NT_SUCCESS(Status
))
293 /* Get attached device (will notify it) */
294 DeviceObject
= IoGetAttachedDeviceReference(FileObject
->DeviceObject
);
296 /* NameSize is the size of the whole MOUNTDEV_NAME struct */
297 NameSize
= sizeof(USHORT
) + SymbolicName
->Length
;
298 Name
= AllocatePool(NameSize
);
304 /* Initialize struct */
305 Name
->NameLength
= SymbolicName
->Length
;
306 RtlCopyMemory(Name
->Name
, SymbolicName
->Buffer
, SymbolicName
->Length
);
308 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
309 /* Cf: SendLinkCreated comment */
310 Irp
= IoBuildDeviceIoControlRequest(CTL_CODE(MOUNTDEVCONTROLTYPE
, 5, METHOD_BUFFERED
, FILE_READ_ACCESS
| FILE_WRITE_ACCESS
),
319 /* This one can fail, no one matters */
322 Stack
= IoGetNextIrpStackLocation(Irp
);
323 Stack
->FileObject
= FileObject
;
325 Status
= IoCallDriver(DeviceObject
, Irp
);
326 if (Status
== STATUS_PENDING
)
328 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
332 /* Then, second one */
333 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
334 Irp
= IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_LINK_DELETED
,
348 Stack
= IoGetNextIrpStackLocation(Irp
);
349 Stack
->FileObject
= FileObject
;
352 Status
= IoCallDriver(DeviceObject
, Irp
);
353 if (Status
== STATUS_PENDING
)
355 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
364 ObDereferenceObject(DeviceObject
);
365 ObDereferenceObject(FileObject
);
375 SymbolicLinkNamesFromUniqueIdCount(IN PWSTR ValueName
,
378 IN ULONG ValueLength
,
380 IN PVOID EntryContext
)
382 UNICODE_STRING ValueNameString
;
383 PMOUNTDEV_UNIQUE_ID UniqueId
= Context
;
385 if (ValueName
[0] != L
'#' || ValueType
!= REG_BINARY
||
386 (UniqueId
->UniqueIdLength
!= ValueLength
))
388 return STATUS_SUCCESS
;
391 if (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) != ValueLength
)
393 return STATUS_SUCCESS
;
396 /* That one matched, increase count */
397 RtlInitUnicodeString(&ValueNameString
, ValueName
);
398 if (ValueNameString
.Length
)
400 (*((PULONG
)EntryContext
))++;
403 return STATUS_SUCCESS
;
411 SymbolicLinkNamesFromUniqueIdQuery(IN PWSTR ValueName
,
414 IN ULONG ValueLength
,
416 IN PVOID EntryContext
)
418 UNICODE_STRING ValueNameString
;
419 PMOUNTDEV_UNIQUE_ID UniqueId
= Context
;
420 /* Unicode strings table */
421 PUNICODE_STRING ReturnString
= EntryContext
;
423 if (ValueName
[0] != L
'#' || ValueType
!= REG_BINARY
||
424 (UniqueId
->UniqueIdLength
!= ValueLength
))
426 return STATUS_SUCCESS
;
429 if (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) != ValueLength
)
431 return STATUS_SUCCESS
;
434 /* Unique ID matches, let's put the symlink */
435 RtlInitUnicodeString(&ValueNameString
, ValueName
);
436 if (!ValueNameString
.Length
)
438 return STATUS_SUCCESS
;
441 /* Allocate string to copy */
442 ValueNameString
.Buffer
= AllocatePool(ValueNameString
.MaximumLength
);
443 if (!ValueNameString
.Buffer
)
445 return STATUS_SUCCESS
;
449 RtlCopyMemory(ValueNameString
.Buffer
, ValueName
, ValueNameString
.Length
);
450 ValueNameString
.Buffer
[ValueNameString
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
452 while (ReturnString
->Length
)
457 /* And return that string */
458 *ReturnString
= ValueNameString
;
460 return STATUS_SUCCESS
;
467 CreateNewVolumeName(OUT PUNICODE_STRING VolumeName
,
468 IN PGUID VolumeGuid OPTIONAL
)
472 UNICODE_STRING GuidString
;
474 /* If no GUID was provided, then create one */
477 Status
= ExUuidCreate(&Guid
);
478 if (!NT_SUCCESS(Status
))
485 RtlCopyMemory(&Guid
, VolumeGuid
, sizeof(GUID
));
488 /* Convert GUID to string */
489 Status
= RtlStringFromGUID(&Guid
, &GuidString
);
490 if (!NT_SUCCESS(Status
))
495 /* Size for volume namespace, litteral GUID, and null char */
496 VolumeName
->MaximumLength
= 0x14 + 0x4C + sizeof(UNICODE_NULL
);
497 VolumeName
->Buffer
= AllocatePool(0x14 + 0x4C + sizeof(UNICODE_NULL
));
498 if (!VolumeName
->Buffer
)
500 Status
= STATUS_INSUFFICIENT_RESOURCES
;
504 RtlCopyUnicodeString(VolumeName
, &Volume
);
505 RtlAppendUnicodeStringToString(VolumeName
, &GuidString
);
506 VolumeName
->Buffer
[VolumeName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
507 Status
= STATUS_SUCCESS
;
510 ExFreePoolWithTag(GuidString
.Buffer
, 0);
519 QuerySymbolicLinkNamesFromStorage(IN PDEVICE_EXTENSION DeviceExtension
,
520 IN PDEVICE_INFORMATION DeviceInformation
,
521 IN PUNICODE_STRING SuggestedLinkName
,
522 IN BOOLEAN UseOnlyIfThereAreNoOtherLinks
,
523 OUT PUNICODE_STRING
* SymLinks
,
524 OUT PULONG SymLinkCount
,
530 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
532 /* First of all, count links */
533 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
534 QueryTable
[0].QueryRoutine
= SymbolicLinkNamesFromUniqueIdCount
;
535 QueryTable
[0].EntryContext
= SymLinkCount
;
538 Status
= RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
541 DeviceInformation
->UniqueId
,
543 if (!NT_SUCCESS(Status
))
548 /* Check if we have to write a new one first */
549 if (SuggestedLinkName
&& !IsDriveLetter(SuggestedLinkName
) &&
550 UseOnlyIfThereAreNoOtherLinks
&& *SymLinkCount
== 0)
559 /* If has GUID, it makes one more link */
568 RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
570 SuggestedLinkName
->Buffer
,
572 DeviceInformation
->UniqueId
->UniqueId
,
573 DeviceInformation
->UniqueId
->UniqueIdLength
);
575 /* And recount all the needed links */
576 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
577 QueryTable
[0].QueryRoutine
= SymbolicLinkNamesFromUniqueIdCount
;
578 QueryTable
[0].EntryContext
= SymLinkCount
;
581 Status
= RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
584 DeviceInformation
->UniqueId
,
586 if (!NT_SUCCESS(Status
))
588 return STATUS_NOT_FOUND
;
592 /* Not links found? */
595 return STATUS_NOT_FOUND
;
598 /* Allocate a buffer big enough to hold symlinks (table of unicode strings) */
599 *SymLinks
= AllocatePool(*SymLinkCount
* sizeof(UNICODE_STRING
));
602 return STATUS_INSUFFICIENT_RESOURCES
;
605 /* Prepare to query links */
606 RtlZeroMemory(*SymLinks
, *SymLinkCount
* sizeof(UNICODE_STRING
));
607 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
608 QueryTable
[0].QueryRoutine
= SymbolicLinkNamesFromUniqueIdQuery
;
610 /* No GUID? Keep it that way */
613 QueryTable
[0].EntryContext
= *SymLinks
;
615 /* Otherwise, first create volume name */
618 Status
= CreateNewVolumeName(SymLinks
[0], Guid
);
619 if (!NT_SUCCESS(Status
))
625 /* Skip first link (ours) */
626 QueryTable
[0].EntryContext
= *SymLinks
+ 1;
630 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
633 DeviceInformation
->UniqueId
,
636 return STATUS_SUCCESS
;
642 PSAVED_LINK_INFORMATION
643 RemoveSavedLinks(IN PDEVICE_EXTENSION DeviceExtension
,
644 IN PMOUNTDEV_UNIQUE_ID UniqueId
)
646 PLIST_ENTRY NextEntry
;
647 PSAVED_LINK_INFORMATION SavedLinkInformation
;
649 /* No saved links? Easy! */
650 if (IsListEmpty(&(DeviceExtension
->SavedLinksListHead
)))
655 /* Now, browse saved links */
656 for (NextEntry
= DeviceExtension
->SavedLinksListHead
.Flink
;
657 NextEntry
!= &(DeviceExtension
->SavedLinksListHead
);
658 NextEntry
= NextEntry
->Flink
)
660 SavedLinkInformation
= CONTAINING_RECORD(NextEntry
,
661 SAVED_LINK_INFORMATION
,
662 SavedLinksListEntry
);
664 /* Find the one that matches */
665 if (SavedLinkInformation
->UniqueId
->UniqueIdLength
== UniqueId
->UniqueIdLength
)
667 if (RtlCompareMemory(SavedLinkInformation
->UniqueId
->UniqueId
,
669 UniqueId
->UniqueIdLength
) ==
670 UniqueId
->UniqueIdLength
)
672 /* Remove it and return it */
673 RemoveEntryList(&(SavedLinkInformation
->SavedLinksListEntry
));
674 return SavedLinkInformation
;
679 /* None found (none removed) */
687 QuerySuggestedLinkName(IN PUNICODE_STRING SymbolicName
,
688 OUT PUNICODE_STRING SuggestedLinkName
,
689 OUT PBOOLEAN UseOnlyIfThereAreNoOtherLinks
)
695 PFILE_OBJECT FileObject
;
696 PDEVICE_OBJECT DeviceObject
;
697 IO_STATUS_BLOCK IoStatusBlock
;
698 PIO_STACK_LOCATION IoStackLocation
;
699 PMOUNTDEV_SUGGESTED_LINK_NAME IoCtlSuggested
;
701 /* First, get device */
702 Status
= IoGetDeviceObjectPointer(SymbolicName
,
703 FILE_READ_ATTRIBUTES
,
706 if (!NT_SUCCESS(Status
))
711 /* Then, get attached device */
712 DeviceObject
= IoGetAttachedDeviceReference(FileObject
->DeviceObject
);
714 /* Then, prepare buffer to query suggested name */
715 IoCtlSuggested
= AllocatePool(sizeof(MOUNTDEV_SUGGESTED_LINK_NAME
));
718 Status
= STATUS_INSUFFICIENT_RESOURCES
;
722 /* Prepare request */
723 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
724 Irp
= IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME
,
729 sizeof(MOUNTDEV_SUGGESTED_LINK_NAME
),
735 Status
= STATUS_INSUFFICIENT_RESOURCES
;
739 IoStackLocation
= IoGetNextIrpStackLocation(Irp
);
740 IoStackLocation
->FileObject
= FileObject
;
743 Status
= IoCallDriver(DeviceObject
, Irp
);
744 if (Status
== STATUS_PENDING
)
746 KeWaitForSingleObject(&Event
, Executive
, KernelMode
,
748 Status
= IoStatusBlock
.Status
;
751 /* Overflow? Normal */
752 if (Status
== STATUS_BUFFER_OVERFLOW
)
754 /* Reallocate big enough buffer */
755 NameLength
= IoCtlSuggested
->NameLength
+ sizeof(MOUNTDEV_SUGGESTED_LINK_NAME
);
756 FreePool(IoCtlSuggested
);
758 IoCtlSuggested
= AllocatePool(NameLength
);
761 Status
= STATUS_INSUFFICIENT_RESOURCES
;
766 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
767 Irp
= IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME
,
778 Status
= STATUS_INSUFFICIENT_RESOURCES
;
782 IoStackLocation
= IoGetNextIrpStackLocation(Irp
);
783 IoStackLocation
->FileObject
= FileObject
;
785 Status
= IoCallDriver(DeviceObject
, Irp
);
786 if (Status
== STATUS_PENDING
)
788 KeWaitForSingleObject(&Event
, Executive
, KernelMode
,
790 Status
= IoStatusBlock
.Status
;
794 if (!NT_SUCCESS(Status
))
799 /* Now we have suggested name, copy it */
800 SuggestedLinkName
->Length
= IoCtlSuggested
->NameLength
;
801 SuggestedLinkName
->MaximumLength
= IoCtlSuggested
->NameLength
+ sizeof(UNICODE_NULL
);
802 SuggestedLinkName
->Buffer
= AllocatePool(IoCtlSuggested
->NameLength
+ sizeof(UNICODE_NULL
));
803 if (!SuggestedLinkName
->Buffer
)
805 Status
= STATUS_INSUFFICIENT_RESOURCES
;
809 RtlCopyMemory(SuggestedLinkName
->Buffer
, IoCtlSuggested
->Name
, IoCtlSuggested
->NameLength
);
810 SuggestedLinkName
->Buffer
[SuggestedLinkName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
813 /* Also return its priority */
814 *UseOnlyIfThereAreNoOtherLinks
= IoCtlSuggested
->UseOnlyIfThereAreNoOtherLinks
;
817 FreePool(IoCtlSuggested
);
820 ObDereferenceObject(DeviceObject
);
821 ObDereferenceObject(FileObject
);
830 RedirectSavedLink(IN PSAVED_LINK_INFORMATION SavedLinkInformation
,
831 IN PUNICODE_STRING DosName
,
832 IN PUNICODE_STRING NewLink
)
834 PLIST_ENTRY NextEntry
;
835 PSYMLINK_INFORMATION SymlinkInformation
;
838 for (NextEntry
= SavedLinkInformation
->SymbolicLinksListHead
.Flink
;
839 NextEntry
!= &(SavedLinkInformation
->SymbolicLinksListHead
);
840 NextEntry
= NextEntry
->Flink
)
842 SymlinkInformation
= CONTAINING_RECORD(NextEntry
, SYMLINK_INFORMATION
, SymbolicLinksListEntry
);
844 if (!RtlEqualUnicodeString(DosName
, &(SymlinkInformation
->Name
), TRUE
))
846 /* Delete old link */
847 GlobalDeleteSymbolicLink(DosName
);
848 /* Set its new location */
849 GlobalCreateSymbolicLink(DosName
, NewLink
);
851 /* And remove it from the list (not valid any more) */
852 RemoveEntryList(&(SymlinkInformation
->SymbolicLinksListEntry
));
853 FreePool(SymlinkInformation
->Name
.Buffer
);
854 FreePool(SymlinkInformation
);
867 DeleteSymbolicLinkNameFromMemory(IN PDEVICE_EXTENSION DeviceExtension
,
868 IN PUNICODE_STRING SymbolicLink
,
869 IN BOOLEAN MarkOffline
)
871 PLIST_ENTRY DeviceEntry
, SymbolEntry
;
872 PDEVICE_INFORMATION DeviceInformation
;
873 PSYMLINK_INFORMATION SymlinkInformation
;
875 /* First of all, ensure we have devices */
876 if (IsListEmpty(&(DeviceExtension
->DeviceListHead
)))
881 /* Then, look for the symbolic name */
882 for (DeviceEntry
= DeviceExtension
->DeviceListHead
.Flink
;
883 DeviceEntry
!= &(DeviceExtension
->DeviceListHead
);
884 DeviceEntry
= DeviceEntry
->Flink
)
886 DeviceInformation
= CONTAINING_RECORD(DeviceEntry
, DEVICE_INFORMATION
, DeviceListEntry
);
888 for (SymbolEntry
= DeviceInformation
->SymbolicLinksListHead
.Flink
;
889 SymbolEntry
!= &(DeviceInformation
->SymbolicLinksListHead
);
890 SymbolEntry
= SymbolEntry
->Flink
)
892 SymlinkInformation
= CONTAINING_RECORD(SymbolEntry
, SYMLINK_INFORMATION
, SymbolicLinksListEntry
);
894 /* One we have found it */
895 if (RtlCompareUnicodeString(SymbolicLink
, &(SymlinkInformation
->Name
), TRUE
) == 0)
897 /* Check if caller just want it to be offline */
900 SymlinkInformation
->Online
= FALSE
;
904 /* If not, delete it & notify */
905 SendLinkDeleted(&(DeviceInformation
->SymbolicName
), SymbolicLink
);
906 RemoveEntryList(&(SymlinkInformation
->SymbolicLinksListEntry
));
908 FreePool(SymlinkInformation
->Name
.Buffer
);
909 FreePool(SymlinkInformation
);
912 /* No need to go farther */
925 IsDriveLetter(PUNICODE_STRING SymbolicName
)
928 BOOLEAN Result
= FALSE
;
930 /* We must have a precise length */
931 if (SymbolicName
->Length
!= sizeof(DosDevices
.Buffer
) + 2 * sizeof(WCHAR
))
936 /* Check if len is correct */
937 Letter
= SymbolicName
->Buffer
[sizeof(DosDevices
.Buffer
) / sizeof(WCHAR
)];
938 if (((Letter
>= L
'A' && Letter
<= L
'Z') || Letter
== (WCHAR
)-1) &&
939 SymbolicName
->Buffer
[(sizeof(DosDevices
.Buffer
) + sizeof(WCHAR
)) / sizeof (WCHAR
)] == L
':')
941 /* In case it's not a normal drive letter, check differently */
942 SymbolicName
->Length
= sizeof(DosDevices
.Buffer
);
943 Result
= RtlEqualUnicodeString(SymbolicName
, &DosDevices
, TRUE
);
944 SymbolicName
->Length
= sizeof(DosDevices
.Buffer
) + 2 * sizeof(WCHAR
);
954 MountMgrQuerySymbolicLink(IN PUNICODE_STRING SymbolicName
,
955 IN OUT PUNICODE_STRING LinkTarget
)
959 OBJECT_ATTRIBUTES ObjectAttributes
;
961 /* Open the symbolic link */
962 InitializeObjectAttributes(&ObjectAttributes
,
964 OBJ_KERNEL_HANDLE
| OBJ_CASE_INSENSITIVE
,
968 Status
= ZwOpenSymbolicLinkObject(&LinkHandle
,
971 if (!NT_SUCCESS(Status
))
976 /* Query its target */
977 Status
= ZwQuerySymbolicLinkObject(LinkHandle
,
983 if (!NT_SUCCESS(Status
))
988 if (LinkTarget
->Length
<= sizeof(WCHAR
))
993 /* If it's not finished by \, just return */
994 if (LinkTarget
->Buffer
[LinkTarget
->Length
/ sizeof(WCHAR
) - 1] != L
'\\')
999 /* Otherwise, ensure to drop the tailing \ */
1000 LinkTarget
->Length
-= sizeof(WCHAR
);
1001 LinkTarget
->Buffer
[LinkTarget
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;