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)
31 UNICODE_STRING DeviceMount
= RTL_CONSTANT_STRING(MOUNTMGR_DEVICE_NAME
);
32 UNICODE_STRING DosDevicesMount
= RTL_CONSTANT_STRING(L
"\\DosDevices\\MountPointManager");
33 UNICODE_STRING DosDevices
= RTL_CONSTANT_STRING(L
"\\DosDevices\\");
34 UNICODE_STRING DeviceFloppy
= RTL_CONSTANT_STRING(L
"\\Device\\Floppy");
35 UNICODE_STRING DeviceCdRom
= RTL_CONSTANT_STRING(L
"\\Device\\CdRom");
36 UNICODE_STRING DosGlobal
= RTL_CONSTANT_STRING(L
"\\GLOBAL??\\");
37 UNICODE_STRING Global
= RTL_CONSTANT_STRING(L
"\\??\\");
38 UNICODE_STRING SafeVolumes
= RTL_CONSTANT_STRING(L
"\\Device\\VolumesSafeForWriteAccess");
39 UNICODE_STRING Volume
= RTL_CONSTANT_STRING(L
"\\??\\Volume");
40 UNICODE_STRING ReparseIndex
= RTL_CONSTANT_STRING(L
"\\$Extend\\$Reparse:$R:$INDEX_ALLOCATION");
46 CreateStringWithGlobal(IN PUNICODE_STRING DosName
,
47 OUT PUNICODE_STRING GlobalString
)
49 UNICODE_STRING IntGlobal
;
51 if (RtlPrefixUnicodeString(&DosDevices
, DosName
, TRUE
))
53 /* DOS device - use DOS global */
54 IntGlobal
.Length
= DosName
->Length
- DosDevices
.Length
+ DosGlobal
.Length
;
55 IntGlobal
.MaximumLength
= IntGlobal
.Length
+ sizeof(WCHAR
);
56 IntGlobal
.Buffer
= AllocatePool(IntGlobal
.MaximumLength
);
57 if (!IntGlobal
.Buffer
)
59 return STATUS_INSUFFICIENT_RESOURCES
;
62 RtlCopyMemory(IntGlobal
.Buffer
, DosGlobal
.Buffer
, DosGlobal
.Length
);
63 RtlCopyMemory(IntGlobal
.Buffer
+ (DosGlobal
.Length
/ sizeof(WCHAR
)),
64 DosName
->Buffer
+ (DosDevices
.Length
/ sizeof(WCHAR
)),
65 DosName
->Length
- DosDevices
.Length
);
66 IntGlobal
.Buffer
[IntGlobal
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
68 else if (RtlPrefixUnicodeString(&Global
, DosName
, TRUE
))
70 /* Switch to DOS global */
71 IntGlobal
.Length
= DosName
->Length
- Global
.Length
+ DosGlobal
.Length
;
72 IntGlobal
.MaximumLength
= IntGlobal
.Length
+ sizeof(WCHAR
);
73 IntGlobal
.Buffer
= AllocatePool(IntGlobal
.MaximumLength
);
74 if (!IntGlobal
.Buffer
)
76 return STATUS_INSUFFICIENT_RESOURCES
;
79 RtlCopyMemory(IntGlobal
.Buffer
, DosGlobal
.Buffer
, DosGlobal
.Length
);
80 RtlCopyMemory(IntGlobal
.Buffer
+ (DosGlobal
.Length
/ sizeof(WCHAR
)),
81 DosName
->Buffer
+ (Global
.Length
/ sizeof(WCHAR
)),
82 DosName
->Length
- Global
.Length
);
83 IntGlobal
.Buffer
[IntGlobal
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
87 /* Simply duplicate string */
88 IntGlobal
.Length
= DosName
->Length
;
89 IntGlobal
.MaximumLength
= DosName
->MaximumLength
;
90 IntGlobal
.Buffer
= AllocatePool(IntGlobal
.MaximumLength
);
91 if (!IntGlobal
.Buffer
)
93 return STATUS_INSUFFICIENT_RESOURCES
;
96 RtlCopyMemory(IntGlobal
.Buffer
, DosName
->Buffer
, IntGlobal
.MaximumLength
);
100 GlobalString
->Length
= IntGlobal
.Length
;
101 GlobalString
->MaximumLength
= IntGlobal
.MaximumLength
;
102 GlobalString
->Buffer
= IntGlobal
.Buffer
;
104 return STATUS_SUCCESS
;
111 GlobalCreateSymbolicLink(IN PUNICODE_STRING DosName
,
112 IN PUNICODE_STRING DeviceName
)
115 UNICODE_STRING GlobalName
;
117 /* First create the global string */
118 Status
= CreateStringWithGlobal(DosName
, &GlobalName
);
119 if (!NT_SUCCESS(Status
))
124 /* Then, create the symlink */
125 Status
= IoCreateSymbolicLink(&GlobalName
, DeviceName
);
127 FreePool(GlobalName
.Buffer
);
136 GlobalDeleteSymbolicLink(IN PUNICODE_STRING DosName
)
139 UNICODE_STRING GlobalName
;
141 /* Recreate the string (to find the link) */
142 Status
= CreateStringWithGlobal(DosName
, &GlobalName
);
143 if (!NT_SUCCESS(Status
))
148 /* And delete the link */
149 Status
= IoDeleteSymbolicLink(&GlobalName
);
151 FreePool(GlobalName
.Buffer
);
160 SendLinkCreated(IN PUNICODE_STRING SymbolicName
)
166 PFILE_OBJECT FileObject
;
167 PIO_STACK_LOCATION Stack
;
168 PMOUNTDEV_NAME Name
= NULL
;
169 PDEVICE_OBJECT DeviceObject
;
170 IO_STATUS_BLOCK IoStatusBlock
;
172 /* Get the device associated with the name */
173 Status
= IoGetDeviceObjectPointer(SymbolicName
,
174 FILE_READ_ATTRIBUTES
,
177 if (!NT_SUCCESS(Status
))
182 /* Get attached device (will notify it) */
183 DeviceObject
= IoGetAttachedDeviceReference(FileObject
->DeviceObject
);
185 /* NameSize is the size of the whole MOUNTDEV_NAME struct */
186 NameSize
= sizeof(USHORT
) + SymbolicName
->Length
;
187 Name
= AllocatePool(NameSize
);
193 /* Initialize struct */
194 Name
->NameLength
= SymbolicName
->Length
;
195 RtlCopyMemory(Name
->Name
, SymbolicName
->Buffer
, SymbolicName
->Length
);
197 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
198 /* Microsoft does it twice... Once with limited access, second with any
201 Irp
= IoBuildDeviceIoControlRequest(CTL_CODE(MOUNTDEVCONTROLTYPE
, 4, METHOD_BUFFERED
, FILE_READ_ACCESS
| FILE_WRITE_ACCESS
),
210 /* This one can fail, no one matters */
213 Stack
= IoGetNextIrpStackLocation(Irp
);
214 Stack
->FileObject
= FileObject
;
216 Status
= IoCallDriver(DeviceObject
, Irp
);
217 if (Status
== STATUS_PENDING
)
219 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
223 /* Then, second one */
224 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
225 Irp
= IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_LINK_CREATED
,
239 Stack
= IoGetNextIrpStackLocation(Irp
);
240 Stack
->FileObject
= FileObject
;
243 Status
= IoCallDriver(DeviceObject
, Irp
);
244 if (Status
== STATUS_PENDING
)
246 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
255 ObDereferenceObject(DeviceObject
);
256 ObDereferenceObject(FileObject
);
265 SendLinkDeleted(IN PUNICODE_STRING DeviceName
,
266 IN PUNICODE_STRING SymbolicName
)
272 PFILE_OBJECT FileObject
;
273 PIO_STACK_LOCATION Stack
;
274 PMOUNTDEV_NAME Name
= NULL
;
275 PDEVICE_OBJECT DeviceObject
;
276 IO_STATUS_BLOCK IoStatusBlock
;
278 /* Get the device associated with the name */
279 Status
= IoGetDeviceObjectPointer(DeviceName
,
280 FILE_READ_ATTRIBUTES
,
283 if (!NT_SUCCESS(Status
))
288 /* Get attached device (will notify it) */
289 DeviceObject
= IoGetAttachedDeviceReference(FileObject
->DeviceObject
);
291 /* NameSize is the size of the whole MOUNTDEV_NAME struct */
292 NameSize
= sizeof(USHORT
) + SymbolicName
->Length
;
293 Name
= AllocatePool(NameSize
);
299 /* Initialize struct */
300 Name
->NameLength
= SymbolicName
->Length
;
301 RtlCopyMemory(Name
->Name
, SymbolicName
->Buffer
, SymbolicName
->Length
);
303 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
304 /* Cf: SendLinkCreated comment */
305 Irp
= IoBuildDeviceIoControlRequest(CTL_CODE(MOUNTDEVCONTROLTYPE
, 5, METHOD_BUFFERED
, FILE_READ_ACCESS
| FILE_WRITE_ACCESS
),
314 /* This one can fail, no one matters */
317 Stack
= IoGetNextIrpStackLocation(Irp
);
318 Stack
->FileObject
= FileObject
;
320 Status
= IoCallDriver(DeviceObject
, Irp
);
321 if (Status
== STATUS_PENDING
)
323 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
327 /* Then, second one */
328 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
329 Irp
= IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_LINK_DELETED
,
343 Stack
= IoGetNextIrpStackLocation(Irp
);
344 Stack
->FileObject
= FileObject
;
347 Status
= IoCallDriver(DeviceObject
, Irp
);
348 if (Status
== STATUS_PENDING
)
350 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
359 ObDereferenceObject(DeviceObject
);
360 ObDereferenceObject(FileObject
);
370 SymbolicLinkNamesFromUniqueIdCount(IN PWSTR ValueName
,
373 IN ULONG ValueLength
,
375 IN PVOID EntryContext
)
377 UNICODE_STRING ValueNameString
;
378 PMOUNTDEV_UNIQUE_ID UniqueId
= Context
;
380 if (ValueName
[0] != L
'#' || ValueType
!= REG_BINARY
||
381 (UniqueId
->UniqueIdLength
!= ValueLength
))
383 return STATUS_SUCCESS
;
386 if (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) != ValueLength
)
388 return STATUS_SUCCESS
;
391 /* That one matched, increase count */
392 RtlInitUnicodeString(&ValueNameString
, ValueName
);
393 if (ValueNameString
.Length
)
395 (*((PULONG
)EntryContext
))++;
398 return STATUS_SUCCESS
;
406 SymbolicLinkNamesFromUniqueIdQuery(IN PWSTR ValueName
,
409 IN ULONG ValueLength
,
411 IN PVOID EntryContext
)
413 UNICODE_STRING ValueNameString
;
414 PMOUNTDEV_UNIQUE_ID UniqueId
= Context
;
415 /* Unicode strings table */
416 PUNICODE_STRING ReturnString
= EntryContext
;
418 if (ValueName
[0] != L
'#' || ValueType
!= REG_BINARY
||
419 (UniqueId
->UniqueIdLength
!= ValueLength
))
421 return STATUS_SUCCESS
;
424 if (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) != ValueLength
)
426 return STATUS_SUCCESS
;
429 /* Unique ID matches, let's put the symlink */
430 RtlInitUnicodeString(&ValueNameString
, ValueName
);
431 if (!ValueNameString
.Length
)
433 return STATUS_SUCCESS
;
436 /* Allocate string to copy */
437 ValueNameString
.Buffer
= AllocatePool(ValueNameString
.MaximumLength
);
438 if (!ValueNameString
.Buffer
)
440 return STATUS_SUCCESS
;
444 RtlCopyMemory(ValueNameString
.Buffer
, ValueName
, ValueNameString
.Length
);
445 ValueNameString
.Buffer
[ValueNameString
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
447 while (ReturnString
->Length
)
452 /* And return that string */
453 *ReturnString
= ValueNameString
;
455 return STATUS_SUCCESS
;
462 CreateNewVolumeName(OUT PUNICODE_STRING VolumeName
,
463 IN PGUID VolumeGuid OPTIONAL
)
467 UNICODE_STRING GuidString
;
469 /* If no GUID was provided, then create one */
472 Status
= ExUuidCreate(&Guid
);
473 if (!NT_SUCCESS(Status
))
480 RtlCopyMemory(&Guid
, VolumeGuid
, sizeof(GUID
));
483 /* Convert GUID to string */
484 Status
= RtlStringFromGUID(&Guid
, &GuidString
);
485 if (!NT_SUCCESS(Status
))
490 /* Size for volume namespace, literal GUID, and null char */
491 VolumeName
->MaximumLength
= 0x14 + 0x4C + sizeof(UNICODE_NULL
);
492 VolumeName
->Buffer
= AllocatePool(0x14 + 0x4C + sizeof(UNICODE_NULL
));
493 if (!VolumeName
->Buffer
)
495 Status
= STATUS_INSUFFICIENT_RESOURCES
;
499 RtlCopyUnicodeString(VolumeName
, &Volume
);
500 RtlAppendUnicodeStringToString(VolumeName
, &GuidString
);
501 VolumeName
->Buffer
[VolumeName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
502 Status
= STATUS_SUCCESS
;
505 ExFreePoolWithTag(GuidString
.Buffer
, 0);
514 QuerySymbolicLinkNamesFromStorage(IN PDEVICE_EXTENSION DeviceExtension
,
515 IN PDEVICE_INFORMATION DeviceInformation
,
516 IN PUNICODE_STRING SuggestedLinkName
,
517 IN BOOLEAN UseOnlyIfThereAreNoOtherLinks
,
518 OUT PUNICODE_STRING
* SymLinks
,
519 OUT PULONG SymLinkCount
,
525 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
527 UNREFERENCED_PARAMETER(DeviceExtension
);
529 /* First of all, count links */
530 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
531 QueryTable
[0].QueryRoutine
= SymbolicLinkNamesFromUniqueIdCount
;
532 QueryTable
[0].EntryContext
= SymLinkCount
;
535 Status
= RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
538 DeviceInformation
->UniqueId
,
540 if (!NT_SUCCESS(Status
))
545 /* Check if we have to write a new one first */
546 if (SuggestedLinkName
&& !IsDriveLetter(SuggestedLinkName
) &&
547 UseOnlyIfThereAreNoOtherLinks
&& *SymLinkCount
== 0)
556 /* If has GUID, it makes one more link */
565 RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
567 SuggestedLinkName
->Buffer
,
569 DeviceInformation
->UniqueId
->UniqueId
,
570 DeviceInformation
->UniqueId
->UniqueIdLength
);
572 /* And recount all the needed links */
573 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
574 QueryTable
[0].QueryRoutine
= SymbolicLinkNamesFromUniqueIdCount
;
575 QueryTable
[0].EntryContext
= SymLinkCount
;
578 Status
= RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
581 DeviceInformation
->UniqueId
,
583 if (!NT_SUCCESS(Status
))
585 return STATUS_NOT_FOUND
;
589 /* Not links found? */
592 return STATUS_NOT_FOUND
;
595 /* Allocate a buffer big enough to hold symlinks (table of unicode strings) */
596 *SymLinks
= AllocatePool(*SymLinkCount
* sizeof(UNICODE_STRING
));
599 return STATUS_INSUFFICIENT_RESOURCES
;
602 /* Prepare to query links */
603 RtlZeroMemory(*SymLinks
, *SymLinkCount
* sizeof(UNICODE_STRING
));
604 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
605 QueryTable
[0].QueryRoutine
= SymbolicLinkNamesFromUniqueIdQuery
;
607 /* No GUID? Keep it that way */
610 QueryTable
[0].EntryContext
= *SymLinks
;
612 /* Otherwise, first create volume name */
615 Status
= CreateNewVolumeName(SymLinks
[0], Guid
);
616 if (!NT_SUCCESS(Status
))
622 /* Skip first link (ours) */
623 QueryTable
[0].EntryContext
= *SymLinks
+ 1;
627 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
630 DeviceInformation
->UniqueId
,
633 return STATUS_SUCCESS
;
639 PSAVED_LINK_INFORMATION
640 RemoveSavedLinks(IN PDEVICE_EXTENSION DeviceExtension
,
641 IN PMOUNTDEV_UNIQUE_ID UniqueId
)
643 PLIST_ENTRY NextEntry
;
644 PSAVED_LINK_INFORMATION SavedLinkInformation
;
646 /* No saved links? Easy! */
647 if (IsListEmpty(&(DeviceExtension
->SavedLinksListHead
)))
652 /* Now, browse saved links */
653 for (NextEntry
= DeviceExtension
->SavedLinksListHead
.Flink
;
654 NextEntry
!= &(DeviceExtension
->SavedLinksListHead
);
655 NextEntry
= NextEntry
->Flink
)
657 SavedLinkInformation
= CONTAINING_RECORD(NextEntry
,
658 SAVED_LINK_INFORMATION
,
659 SavedLinksListEntry
);
661 /* Find the one that matches */
662 if (SavedLinkInformation
->UniqueId
->UniqueIdLength
== UniqueId
->UniqueIdLength
)
664 if (RtlCompareMemory(SavedLinkInformation
->UniqueId
->UniqueId
,
666 UniqueId
->UniqueIdLength
) ==
667 UniqueId
->UniqueIdLength
)
669 /* Remove it and return it */
670 RemoveEntryList(&(SavedLinkInformation
->SavedLinksListEntry
));
671 return SavedLinkInformation
;
676 /* None found (none removed) */
684 QuerySuggestedLinkName(IN PUNICODE_STRING SymbolicName
,
685 OUT PUNICODE_STRING SuggestedLinkName
,
686 OUT PBOOLEAN UseOnlyIfThereAreNoOtherLinks
)
692 PFILE_OBJECT FileObject
;
693 PDEVICE_OBJECT DeviceObject
;
694 IO_STATUS_BLOCK IoStatusBlock
;
695 PIO_STACK_LOCATION IoStackLocation
;
696 PMOUNTDEV_SUGGESTED_LINK_NAME IoCtlSuggested
;
698 /* First, get device */
699 Status
= IoGetDeviceObjectPointer(SymbolicName
,
700 FILE_READ_ATTRIBUTES
,
703 if (!NT_SUCCESS(Status
))
708 /* Then, get attached device */
709 DeviceObject
= IoGetAttachedDeviceReference(FileObject
->DeviceObject
);
711 /* Then, prepare buffer to query suggested name */
712 IoCtlSuggested
= AllocatePool(sizeof(MOUNTDEV_SUGGESTED_LINK_NAME
));
715 Status
= STATUS_INSUFFICIENT_RESOURCES
;
719 /* Prepare request */
720 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
721 Irp
= IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME
,
726 sizeof(MOUNTDEV_SUGGESTED_LINK_NAME
),
732 Status
= STATUS_INSUFFICIENT_RESOURCES
;
736 IoStackLocation
= IoGetNextIrpStackLocation(Irp
);
737 IoStackLocation
->FileObject
= FileObject
;
740 Status
= IoCallDriver(DeviceObject
, Irp
);
741 if (Status
== STATUS_PENDING
)
743 KeWaitForSingleObject(&Event
, Executive
, KernelMode
,
745 Status
= IoStatusBlock
.Status
;
748 /* Overflow? Normal */
749 if (Status
== STATUS_BUFFER_OVERFLOW
)
751 /* Reallocate big enough buffer */
752 NameLength
= IoCtlSuggested
->NameLength
+ sizeof(MOUNTDEV_SUGGESTED_LINK_NAME
);
753 FreePool(IoCtlSuggested
);
755 IoCtlSuggested
= AllocatePool(NameLength
);
758 Status
= STATUS_INSUFFICIENT_RESOURCES
;
763 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
764 Irp
= IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME
,
775 Status
= STATUS_INSUFFICIENT_RESOURCES
;
779 IoStackLocation
= IoGetNextIrpStackLocation(Irp
);
780 IoStackLocation
->FileObject
= FileObject
;
782 Status
= IoCallDriver(DeviceObject
, Irp
);
783 if (Status
== STATUS_PENDING
)
785 KeWaitForSingleObject(&Event
, Executive
, KernelMode
,
787 Status
= IoStatusBlock
.Status
;
791 if (!NT_SUCCESS(Status
))
796 /* Now we have suggested name, copy it */
797 SuggestedLinkName
->Length
= IoCtlSuggested
->NameLength
;
798 SuggestedLinkName
->MaximumLength
= IoCtlSuggested
->NameLength
+ sizeof(UNICODE_NULL
);
799 SuggestedLinkName
->Buffer
= AllocatePool(IoCtlSuggested
->NameLength
+ sizeof(UNICODE_NULL
));
800 if (!SuggestedLinkName
->Buffer
)
802 Status
= STATUS_INSUFFICIENT_RESOURCES
;
806 RtlCopyMemory(SuggestedLinkName
->Buffer
, IoCtlSuggested
->Name
, IoCtlSuggested
->NameLength
);
807 SuggestedLinkName
->Buffer
[SuggestedLinkName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
810 /* Also return its priority */
811 *UseOnlyIfThereAreNoOtherLinks
= IoCtlSuggested
->UseOnlyIfThereAreNoOtherLinks
;
814 FreePool(IoCtlSuggested
);
817 ObDereferenceObject(DeviceObject
);
818 ObDereferenceObject(FileObject
);
827 RedirectSavedLink(IN PSAVED_LINK_INFORMATION SavedLinkInformation
,
828 IN PUNICODE_STRING DosName
,
829 IN PUNICODE_STRING NewLink
)
831 PLIST_ENTRY NextEntry
;
832 PSYMLINK_INFORMATION SymlinkInformation
;
835 for (NextEntry
= SavedLinkInformation
->SymbolicLinksListHead
.Flink
;
836 NextEntry
!= &(SavedLinkInformation
->SymbolicLinksListHead
);
837 NextEntry
= NextEntry
->Flink
)
839 SymlinkInformation
= CONTAINING_RECORD(NextEntry
, SYMLINK_INFORMATION
, SymbolicLinksListEntry
);
841 if (!RtlEqualUnicodeString(DosName
, &(SymlinkInformation
->Name
), TRUE
))
843 /* Delete old link */
844 GlobalDeleteSymbolicLink(DosName
);
845 /* Set its new location */
846 GlobalCreateSymbolicLink(DosName
, NewLink
);
848 /* And remove it from the list (not valid any more) */
849 RemoveEntryList(&(SymlinkInformation
->SymbolicLinksListEntry
));
850 FreePool(SymlinkInformation
->Name
.Buffer
);
851 FreePool(SymlinkInformation
);
864 DeleteSymbolicLinkNameFromMemory(IN PDEVICE_EXTENSION DeviceExtension
,
865 IN PUNICODE_STRING SymbolicLink
,
866 IN BOOLEAN MarkOffline
)
868 PLIST_ENTRY DeviceEntry
, SymbolEntry
;
869 PDEVICE_INFORMATION DeviceInformation
;
870 PSYMLINK_INFORMATION SymlinkInformation
;
872 /* First of all, ensure we have devices */
873 if (IsListEmpty(&(DeviceExtension
->DeviceListHead
)))
878 /* Then, look for the symbolic name */
879 for (DeviceEntry
= DeviceExtension
->DeviceListHead
.Flink
;
880 DeviceEntry
!= &(DeviceExtension
->DeviceListHead
);
881 DeviceEntry
= DeviceEntry
->Flink
)
883 DeviceInformation
= CONTAINING_RECORD(DeviceEntry
, DEVICE_INFORMATION
, DeviceListEntry
);
885 for (SymbolEntry
= DeviceInformation
->SymbolicLinksListHead
.Flink
;
886 SymbolEntry
!= &(DeviceInformation
->SymbolicLinksListHead
);
887 SymbolEntry
= SymbolEntry
->Flink
)
889 SymlinkInformation
= CONTAINING_RECORD(SymbolEntry
, SYMLINK_INFORMATION
, SymbolicLinksListEntry
);
891 /* One we have found it */
892 if (RtlCompareUnicodeString(SymbolicLink
, &(SymlinkInformation
->Name
), TRUE
) == 0)
894 /* Check if caller just want it to be offline */
897 SymlinkInformation
->Online
= FALSE
;
901 /* If not, delete it & notify */
902 SendLinkDeleted(&(DeviceInformation
->SymbolicName
), SymbolicLink
);
903 RemoveEntryList(&(SymlinkInformation
->SymbolicLinksListEntry
));
905 FreePool(SymlinkInformation
->Name
.Buffer
);
906 FreePool(SymlinkInformation
);
909 /* No need to go farther */
922 IsDriveLetter(PUNICODE_STRING SymbolicName
)
926 /* We must have a precise length */
927 if (SymbolicName
->Length
!= DosDevices
.Length
+ 2 * sizeof(WCHAR
))
932 /* Must start with the DosDevices prefix */
933 if (!RtlPrefixUnicodeString(&DosDevices
, SymbolicName
, TRUE
))
938 /* Check if letter is correct */
939 Letter
= SymbolicName
->Buffer
[DosDevices
.Length
/ sizeof(WCHAR
)];
940 if ((Letter
< L
'A' || Letter
> L
'Z') && Letter
!= (WCHAR
)-1)
945 /* And finally it must end with a colon */
946 Colon
= SymbolicName
->Buffer
[DosDevices
.Length
/ sizeof(WCHAR
) + 1];
959 MountMgrQuerySymbolicLink(IN PUNICODE_STRING SymbolicName
,
960 IN OUT PUNICODE_STRING LinkTarget
)
964 OBJECT_ATTRIBUTES ObjectAttributes
;
966 /* Open the symbolic link */
967 InitializeObjectAttributes(&ObjectAttributes
,
969 OBJ_KERNEL_HANDLE
| OBJ_CASE_INSENSITIVE
,
973 Status
= ZwOpenSymbolicLinkObject(&LinkHandle
,
976 if (!NT_SUCCESS(Status
))
981 /* Query its target */
982 Status
= ZwQuerySymbolicLinkObject(LinkHandle
,
988 if (!NT_SUCCESS(Status
))
993 if (LinkTarget
->Length
<= sizeof(WCHAR
))
998 /* If it's not finished by \, just return */
999 if (LinkTarget
->Buffer
[LinkTarget
->Length
/ sizeof(WCHAR
) - 1] != L
'\\')
1004 /* Otherwise, ensure to drop the tailing \ */
1005 LinkTarget
->Length
-= sizeof(WCHAR
);
1006 LinkTarget
->Buffer
[LinkTarget
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;