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 UNREFERENCED_PARAMETER(DeviceName
);
119 /* First create the global string */
120 Status
= CreateStringWithGlobal(DosName
, &GlobalName
);
121 if (!NT_SUCCESS(Status
))
126 /* Then, create the symlink */
127 Status
= IoCreateSymbolicLink(&GlobalName
, DosName
);
129 FreePool(GlobalName
.Buffer
);
138 GlobalDeleteSymbolicLink(IN PUNICODE_STRING DosName
)
141 UNICODE_STRING GlobalName
;
143 /* Recreate the string (to find the link) */
144 Status
= CreateStringWithGlobal(DosName
, &GlobalName
);
145 if (!NT_SUCCESS(Status
))
150 /* And delete the link */
151 Status
= IoDeleteSymbolicLink(&GlobalName
);
153 FreePool(GlobalName
.Buffer
);
162 SendLinkCreated(IN PUNICODE_STRING SymbolicName
)
168 PFILE_OBJECT FileObject
;
169 PIO_STACK_LOCATION Stack
;
170 PMOUNTDEV_NAME Name
= NULL
;
171 PDEVICE_OBJECT DeviceObject
;
172 IO_STATUS_BLOCK IoStatusBlock
;
174 /* Get the device associated with the name */
175 Status
= IoGetDeviceObjectPointer(SymbolicName
,
176 FILE_READ_ATTRIBUTES
,
179 if (!NT_SUCCESS(Status
))
184 /* Get attached device (will notify it) */
185 DeviceObject
= IoGetAttachedDeviceReference(FileObject
->DeviceObject
);
187 /* NameSize is the size of the whole MOUNTDEV_NAME struct */
188 NameSize
= sizeof(USHORT
) + SymbolicName
->Length
;
189 Name
= AllocatePool(NameSize
);
195 /* Initialize struct */
196 Name
->NameLength
= SymbolicName
->Length
;
197 RtlCopyMemory(Name
->Name
, SymbolicName
->Buffer
, SymbolicName
->Length
);
199 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
200 /* Microsoft does it twice... Once with limited access, second with any
203 Irp
= IoBuildDeviceIoControlRequest(CTL_CODE(MOUNTDEVCONTROLTYPE
, 4, METHOD_BUFFERED
, FILE_READ_ACCESS
| FILE_WRITE_ACCESS
),
212 /* This one can fail, no one matters */
215 Stack
= IoGetNextIrpStackLocation(Irp
);
216 Stack
->FileObject
= FileObject
;
218 Status
= IoCallDriver(DeviceObject
, Irp
);
219 if (Status
== STATUS_PENDING
)
221 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
225 /* Then, second one */
226 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
227 Irp
= IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_LINK_CREATED
,
241 Stack
= IoGetNextIrpStackLocation(Irp
);
242 Stack
->FileObject
= FileObject
;
245 Status
= IoCallDriver(DeviceObject
, Irp
);
246 if (Status
== STATUS_PENDING
)
248 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
257 ObDereferenceObject(DeviceObject
);
258 ObDereferenceObject(FileObject
);
267 SendLinkDeleted(IN PUNICODE_STRING DeviceName
,
268 IN PUNICODE_STRING SymbolicName
)
274 PFILE_OBJECT FileObject
;
275 PIO_STACK_LOCATION Stack
;
276 PMOUNTDEV_NAME Name
= NULL
;
277 PDEVICE_OBJECT DeviceObject
;
278 IO_STATUS_BLOCK IoStatusBlock
;
280 /* Get the device associated with the name */
281 Status
= IoGetDeviceObjectPointer(DeviceName
,
282 FILE_READ_ATTRIBUTES
,
285 if (!NT_SUCCESS(Status
))
290 /* Get attached device (will notify it) */
291 DeviceObject
= IoGetAttachedDeviceReference(FileObject
->DeviceObject
);
293 /* NameSize is the size of the whole MOUNTDEV_NAME struct */
294 NameSize
= sizeof(USHORT
) + SymbolicName
->Length
;
295 Name
= AllocatePool(NameSize
);
301 /* Initialize struct */
302 Name
->NameLength
= SymbolicName
->Length
;
303 RtlCopyMemory(Name
->Name
, SymbolicName
->Buffer
, SymbolicName
->Length
);
305 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
306 /* Cf: SendLinkCreated comment */
307 Irp
= IoBuildDeviceIoControlRequest(CTL_CODE(MOUNTDEVCONTROLTYPE
, 5, METHOD_BUFFERED
, FILE_READ_ACCESS
| FILE_WRITE_ACCESS
),
316 /* This one can fail, no one matters */
319 Stack
= IoGetNextIrpStackLocation(Irp
);
320 Stack
->FileObject
= FileObject
;
322 Status
= IoCallDriver(DeviceObject
, Irp
);
323 if (Status
== STATUS_PENDING
)
325 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
329 /* Then, second one */
330 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
331 Irp
= IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_LINK_DELETED
,
345 Stack
= IoGetNextIrpStackLocation(Irp
);
346 Stack
->FileObject
= FileObject
;
349 Status
= IoCallDriver(DeviceObject
, Irp
);
350 if (Status
== STATUS_PENDING
)
352 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
361 ObDereferenceObject(DeviceObject
);
362 ObDereferenceObject(FileObject
);
372 SymbolicLinkNamesFromUniqueIdCount(IN PWSTR ValueName
,
375 IN ULONG ValueLength
,
377 IN PVOID EntryContext
)
379 UNICODE_STRING ValueNameString
;
380 PMOUNTDEV_UNIQUE_ID UniqueId
= Context
;
382 if (ValueName
[0] != L
'#' || ValueType
!= REG_BINARY
||
383 (UniqueId
->UniqueIdLength
!= ValueLength
))
385 return STATUS_SUCCESS
;
388 if (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) != ValueLength
)
390 return STATUS_SUCCESS
;
393 /* That one matched, increase count */
394 RtlInitUnicodeString(&ValueNameString
, ValueName
);
395 if (ValueNameString
.Length
)
397 (*((PULONG
)EntryContext
))++;
400 return STATUS_SUCCESS
;
408 SymbolicLinkNamesFromUniqueIdQuery(IN PWSTR ValueName
,
411 IN ULONG ValueLength
,
413 IN PVOID EntryContext
)
415 UNICODE_STRING ValueNameString
;
416 PMOUNTDEV_UNIQUE_ID UniqueId
= Context
;
417 /* Unicode strings table */
418 PUNICODE_STRING ReturnString
= EntryContext
;
420 if (ValueName
[0] != L
'#' || ValueType
!= REG_BINARY
||
421 (UniqueId
->UniqueIdLength
!= ValueLength
))
423 return STATUS_SUCCESS
;
426 if (RtlCompareMemory(UniqueId
->UniqueId
, ValueData
, ValueLength
) != ValueLength
)
428 return STATUS_SUCCESS
;
431 /* Unique ID matches, let's put the symlink */
432 RtlInitUnicodeString(&ValueNameString
, ValueName
);
433 if (!ValueNameString
.Length
)
435 return STATUS_SUCCESS
;
438 /* Allocate string to copy */
439 ValueNameString
.Buffer
= AllocatePool(ValueNameString
.MaximumLength
);
440 if (!ValueNameString
.Buffer
)
442 return STATUS_SUCCESS
;
446 RtlCopyMemory(ValueNameString
.Buffer
, ValueName
, ValueNameString
.Length
);
447 ValueNameString
.Buffer
[ValueNameString
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
449 while (ReturnString
->Length
)
454 /* And return that string */
455 *ReturnString
= ValueNameString
;
457 return STATUS_SUCCESS
;
464 CreateNewVolumeName(OUT PUNICODE_STRING VolumeName
,
465 IN PGUID VolumeGuid OPTIONAL
)
469 UNICODE_STRING GuidString
;
471 /* If no GUID was provided, then create one */
474 Status
= ExUuidCreate(&Guid
);
475 if (!NT_SUCCESS(Status
))
482 RtlCopyMemory(&Guid
, VolumeGuid
, sizeof(GUID
));
485 /* Convert GUID to string */
486 Status
= RtlStringFromGUID(&Guid
, &GuidString
);
487 if (!NT_SUCCESS(Status
))
492 /* Size for volume namespace, litteral GUID, and null char */
493 VolumeName
->MaximumLength
= 0x14 + 0x4C + sizeof(UNICODE_NULL
);
494 VolumeName
->Buffer
= AllocatePool(0x14 + 0x4C + sizeof(UNICODE_NULL
));
495 if (!VolumeName
->Buffer
)
497 Status
= STATUS_INSUFFICIENT_RESOURCES
;
501 RtlCopyUnicodeString(VolumeName
, &Volume
);
502 RtlAppendUnicodeStringToString(VolumeName
, &GuidString
);
503 VolumeName
->Buffer
[VolumeName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
504 Status
= STATUS_SUCCESS
;
507 ExFreePoolWithTag(GuidString
.Buffer
, 0);
516 QuerySymbolicLinkNamesFromStorage(IN PDEVICE_EXTENSION DeviceExtension
,
517 IN PDEVICE_INFORMATION DeviceInformation
,
518 IN PUNICODE_STRING SuggestedLinkName
,
519 IN BOOLEAN UseOnlyIfThereAreNoOtherLinks
,
520 OUT PUNICODE_STRING
* SymLinks
,
521 OUT PULONG SymLinkCount
,
527 RTL_QUERY_REGISTRY_TABLE QueryTable
[2];
529 UNREFERENCED_PARAMETER(DeviceExtension
);
531 /* First of all, count links */
532 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
533 QueryTable
[0].QueryRoutine
= SymbolicLinkNamesFromUniqueIdCount
;
534 QueryTable
[0].EntryContext
= SymLinkCount
;
537 Status
= RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
540 DeviceInformation
->UniqueId
,
542 if (!NT_SUCCESS(Status
))
547 /* Check if we have to write a new one first */
548 if (SuggestedLinkName
&& !IsDriveLetter(SuggestedLinkName
) &&
549 UseOnlyIfThereAreNoOtherLinks
&& *SymLinkCount
== 0)
558 /* If has GUID, it makes one more link */
567 RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE
,
569 SuggestedLinkName
->Buffer
,
571 DeviceInformation
->UniqueId
->UniqueId
,
572 DeviceInformation
->UniqueId
->UniqueIdLength
);
574 /* And recount all the needed links */
575 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
576 QueryTable
[0].QueryRoutine
= SymbolicLinkNamesFromUniqueIdCount
;
577 QueryTable
[0].EntryContext
= SymLinkCount
;
580 Status
= RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
583 DeviceInformation
->UniqueId
,
585 if (!NT_SUCCESS(Status
))
587 return STATUS_NOT_FOUND
;
591 /* Not links found? */
594 return STATUS_NOT_FOUND
;
597 /* Allocate a buffer big enough to hold symlinks (table of unicode strings) */
598 *SymLinks
= AllocatePool(*SymLinkCount
* sizeof(UNICODE_STRING
));
601 return STATUS_INSUFFICIENT_RESOURCES
;
604 /* Prepare to query links */
605 RtlZeroMemory(*SymLinks
, *SymLinkCount
* sizeof(UNICODE_STRING
));
606 RtlZeroMemory(QueryTable
, sizeof(QueryTable
));
607 QueryTable
[0].QueryRoutine
= SymbolicLinkNamesFromUniqueIdQuery
;
609 /* No GUID? Keep it that way */
612 QueryTable
[0].EntryContext
= *SymLinks
;
614 /* Otherwise, first create volume name */
617 Status
= CreateNewVolumeName(SymLinks
[0], Guid
);
618 if (!NT_SUCCESS(Status
))
624 /* Skip first link (ours) */
625 QueryTable
[0].EntryContext
= *SymLinks
+ 1;
629 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE
,
632 DeviceInformation
->UniqueId
,
635 return STATUS_SUCCESS
;
641 PSAVED_LINK_INFORMATION
642 RemoveSavedLinks(IN PDEVICE_EXTENSION DeviceExtension
,
643 IN PMOUNTDEV_UNIQUE_ID UniqueId
)
645 PLIST_ENTRY NextEntry
;
646 PSAVED_LINK_INFORMATION SavedLinkInformation
;
648 /* No saved links? Easy! */
649 if (IsListEmpty(&(DeviceExtension
->SavedLinksListHead
)))
654 /* Now, browse saved links */
655 for (NextEntry
= DeviceExtension
->SavedLinksListHead
.Flink
;
656 NextEntry
!= &(DeviceExtension
->SavedLinksListHead
);
657 NextEntry
= NextEntry
->Flink
)
659 SavedLinkInformation
= CONTAINING_RECORD(NextEntry
,
660 SAVED_LINK_INFORMATION
,
661 SavedLinksListEntry
);
663 /* Find the one that matches */
664 if (SavedLinkInformation
->UniqueId
->UniqueIdLength
== UniqueId
->UniqueIdLength
)
666 if (RtlCompareMemory(SavedLinkInformation
->UniqueId
->UniqueId
,
668 UniqueId
->UniqueIdLength
) ==
669 UniqueId
->UniqueIdLength
)
671 /* Remove it and return it */
672 RemoveEntryList(&(SavedLinkInformation
->SavedLinksListEntry
));
673 return SavedLinkInformation
;
678 /* None found (none removed) */
686 QuerySuggestedLinkName(IN PUNICODE_STRING SymbolicName
,
687 OUT PUNICODE_STRING SuggestedLinkName
,
688 OUT PBOOLEAN UseOnlyIfThereAreNoOtherLinks
)
694 PFILE_OBJECT FileObject
;
695 PDEVICE_OBJECT DeviceObject
;
696 IO_STATUS_BLOCK IoStatusBlock
;
697 PIO_STACK_LOCATION IoStackLocation
;
698 PMOUNTDEV_SUGGESTED_LINK_NAME IoCtlSuggested
;
700 /* First, get device */
701 Status
= IoGetDeviceObjectPointer(SymbolicName
,
702 FILE_READ_ATTRIBUTES
,
705 if (!NT_SUCCESS(Status
))
710 /* Then, get attached device */
711 DeviceObject
= IoGetAttachedDeviceReference(FileObject
->DeviceObject
);
713 /* Then, prepare buffer to query suggested name */
714 IoCtlSuggested
= AllocatePool(sizeof(MOUNTDEV_SUGGESTED_LINK_NAME
));
717 Status
= STATUS_INSUFFICIENT_RESOURCES
;
721 /* Prepare request */
722 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
723 Irp
= IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME
,
728 sizeof(MOUNTDEV_SUGGESTED_LINK_NAME
),
734 Status
= STATUS_INSUFFICIENT_RESOURCES
;
738 IoStackLocation
= IoGetNextIrpStackLocation(Irp
);
739 IoStackLocation
->FileObject
= FileObject
;
742 Status
= IoCallDriver(DeviceObject
, Irp
);
743 if (Status
== STATUS_PENDING
)
745 KeWaitForSingleObject(&Event
, Executive
, KernelMode
,
747 Status
= IoStatusBlock
.Status
;
750 /* Overflow? Normal */
751 if (Status
== STATUS_BUFFER_OVERFLOW
)
753 /* Reallocate big enough buffer */
754 NameLength
= IoCtlSuggested
->NameLength
+ sizeof(MOUNTDEV_SUGGESTED_LINK_NAME
);
755 FreePool(IoCtlSuggested
);
757 IoCtlSuggested
= AllocatePool(NameLength
);
760 Status
= STATUS_INSUFFICIENT_RESOURCES
;
765 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
766 Irp
= IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME
,
777 Status
= STATUS_INSUFFICIENT_RESOURCES
;
781 IoStackLocation
= IoGetNextIrpStackLocation(Irp
);
782 IoStackLocation
->FileObject
= FileObject
;
784 Status
= IoCallDriver(DeviceObject
, Irp
);
785 if (Status
== STATUS_PENDING
)
787 KeWaitForSingleObject(&Event
, Executive
, KernelMode
,
789 Status
= IoStatusBlock
.Status
;
793 if (!NT_SUCCESS(Status
))
798 /* Now we have suggested name, copy it */
799 SuggestedLinkName
->Length
= IoCtlSuggested
->NameLength
;
800 SuggestedLinkName
->MaximumLength
= IoCtlSuggested
->NameLength
+ sizeof(UNICODE_NULL
);
801 SuggestedLinkName
->Buffer
= AllocatePool(IoCtlSuggested
->NameLength
+ sizeof(UNICODE_NULL
));
802 if (!SuggestedLinkName
->Buffer
)
804 Status
= STATUS_INSUFFICIENT_RESOURCES
;
808 RtlCopyMemory(SuggestedLinkName
->Buffer
, IoCtlSuggested
->Name
, IoCtlSuggested
->NameLength
);
809 SuggestedLinkName
->Buffer
[SuggestedLinkName
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
812 /* Also return its priority */
813 *UseOnlyIfThereAreNoOtherLinks
= IoCtlSuggested
->UseOnlyIfThereAreNoOtherLinks
;
816 FreePool(IoCtlSuggested
);
819 ObDereferenceObject(DeviceObject
);
820 ObDereferenceObject(FileObject
);
829 RedirectSavedLink(IN PSAVED_LINK_INFORMATION SavedLinkInformation
,
830 IN PUNICODE_STRING DosName
,
831 IN PUNICODE_STRING NewLink
)
833 PLIST_ENTRY NextEntry
;
834 PSYMLINK_INFORMATION SymlinkInformation
;
837 for (NextEntry
= SavedLinkInformation
->SymbolicLinksListHead
.Flink
;
838 NextEntry
!= &(SavedLinkInformation
->SymbolicLinksListHead
);
839 NextEntry
= NextEntry
->Flink
)
841 SymlinkInformation
= CONTAINING_RECORD(NextEntry
, SYMLINK_INFORMATION
, SymbolicLinksListEntry
);
843 if (!RtlEqualUnicodeString(DosName
, &(SymlinkInformation
->Name
), TRUE
))
845 /* Delete old link */
846 GlobalDeleteSymbolicLink(DosName
);
847 /* Set its new location */
848 GlobalCreateSymbolicLink(DosName
, NewLink
);
850 /* And remove it from the list (not valid any more) */
851 RemoveEntryList(&(SymlinkInformation
->SymbolicLinksListEntry
));
852 FreePool(SymlinkInformation
->Name
.Buffer
);
853 FreePool(SymlinkInformation
);
866 DeleteSymbolicLinkNameFromMemory(IN PDEVICE_EXTENSION DeviceExtension
,
867 IN PUNICODE_STRING SymbolicLink
,
868 IN BOOLEAN MarkOffline
)
870 PLIST_ENTRY DeviceEntry
, SymbolEntry
;
871 PDEVICE_INFORMATION DeviceInformation
;
872 PSYMLINK_INFORMATION SymlinkInformation
;
874 /* First of all, ensure we have devices */
875 if (IsListEmpty(&(DeviceExtension
->DeviceListHead
)))
880 /* Then, look for the symbolic name */
881 for (DeviceEntry
= DeviceExtension
->DeviceListHead
.Flink
;
882 DeviceEntry
!= &(DeviceExtension
->DeviceListHead
);
883 DeviceEntry
= DeviceEntry
->Flink
)
885 DeviceInformation
= CONTAINING_RECORD(DeviceEntry
, DEVICE_INFORMATION
, DeviceListEntry
);
887 for (SymbolEntry
= DeviceInformation
->SymbolicLinksListHead
.Flink
;
888 SymbolEntry
!= &(DeviceInformation
->SymbolicLinksListHead
);
889 SymbolEntry
= SymbolEntry
->Flink
)
891 SymlinkInformation
= CONTAINING_RECORD(SymbolEntry
, SYMLINK_INFORMATION
, SymbolicLinksListEntry
);
893 /* One we have found it */
894 if (RtlCompareUnicodeString(SymbolicLink
, &(SymlinkInformation
->Name
), TRUE
) == 0)
896 /* Check if caller just want it to be offline */
899 SymlinkInformation
->Online
= FALSE
;
903 /* If not, delete it & notify */
904 SendLinkDeleted(&(DeviceInformation
->SymbolicName
), SymbolicLink
);
905 RemoveEntryList(&(SymlinkInformation
->SymbolicLinksListEntry
));
907 FreePool(SymlinkInformation
->Name
.Buffer
);
908 FreePool(SymlinkInformation
);
911 /* No need to go farther */
924 IsDriveLetter(PUNICODE_STRING SymbolicName
)
928 /* We must have a precise length */
929 if (SymbolicName
->Length
!= DosDevices
.Length
+ 2 * sizeof(WCHAR
))
934 /* Must start with the DosDevices prefix */
935 if (!RtlPrefixUnicodeString(&DosDevices
, SymbolicName
, TRUE
))
940 /* Check if letter is correct */
941 Letter
= SymbolicName
->Buffer
[DosDevices
.Length
/ sizeof(WCHAR
)];
942 if ((Letter
< L
'A' || Letter
> L
'Z') && Letter
!= (WCHAR
)-1)
947 /* And finally it must end with a colon */
948 Colon
= SymbolicName
->Buffer
[DosDevices
.Length
/ sizeof(WCHAR
) + 1];
961 MountMgrQuerySymbolicLink(IN PUNICODE_STRING SymbolicName
,
962 IN OUT PUNICODE_STRING LinkTarget
)
966 OBJECT_ATTRIBUTES ObjectAttributes
;
968 /* Open the symbolic link */
969 InitializeObjectAttributes(&ObjectAttributes
,
971 OBJ_KERNEL_HANDLE
| OBJ_CASE_INSENSITIVE
,
975 Status
= ZwOpenSymbolicLinkObject(&LinkHandle
,
978 if (!NT_SUCCESS(Status
))
983 /* Query its target */
984 Status
= ZwQuerySymbolicLinkObject(LinkHandle
,
990 if (!NT_SUCCESS(Status
))
995 if (LinkTarget
->Length
<= sizeof(WCHAR
))
1000 /* If it's not finished by \, just return */
1001 if (LinkTarget
->Buffer
[LinkTarget
->Length
/ sizeof(WCHAR
) - 1] != L
'\\')
1006 /* Otherwise, ensure to drop the tailing \ */
1007 LinkTarget
->Length
-= sizeof(WCHAR
);
1008 LinkTarget
->Buffer
[LinkTarget
->Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;