xref: /reactos/drivers/storage/mountmgr/symlink.c (revision ab0e04c8)
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 2011-2012 ReactOS Team
4  *
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.
9  *
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.
14  *
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.
18  *
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)
24  */
25 
26 #include "mntmgr.h"
27 
28 #define NDEBUG
29 #include <debug.h>
30 
31 /* Deprecated Windows 2000/XP versions of IOCTL_MOUNTDEV_LINK_[CREATED|DELETED]
32  * without access protection, that were updated in Windows 2003.
33  * They are sent to MountMgr clients if they do not recognize the new IOCTLs
34  * (e.g. they are for an older NT version). */
35 #if (NTDDI_VERSION >= NTDDI_WS03)
36 #define IOCTL_MOUNTDEV_LINK_CREATED_UNSECURE_DEPRECATED     CTL_CODE(MOUNTDEVCONTROLTYPE, 4, METHOD_BUFFERED, FILE_ANY_ACCESS)
37 #define IOCTL_MOUNTDEV_LINK_DELETED_UNSECURE_DEPRECATED     CTL_CODE(MOUNTDEVCONTROLTYPE, 5, METHOD_BUFFERED, FILE_ANY_ACCESS)
38 #endif
39 
40 UNICODE_STRING DeviceMount = RTL_CONSTANT_STRING(MOUNTMGR_DEVICE_NAME);
41 UNICODE_STRING DosDevicesMount = RTL_CONSTANT_STRING(L"\\DosDevices\\MountPointManager");
42 UNICODE_STRING DosDevices = RTL_CONSTANT_STRING(L"\\DosDevices\\");
43 UNICODE_STRING DeviceFloppy = RTL_CONSTANT_STRING(L"\\Device\\Floppy");
44 UNICODE_STRING DeviceCdRom = RTL_CONSTANT_STRING(L"\\Device\\CdRom");
45 UNICODE_STRING DosGlobal = RTL_CONSTANT_STRING(L"\\GLOBAL??\\");
46 UNICODE_STRING Global = RTL_CONSTANT_STRING(L"\\??\\");
47 UNICODE_STRING SafeVolumes = RTL_CONSTANT_STRING(L"\\Device\\VolumesSafeForWriteAccess");
48 UNICODE_STRING Volume = RTL_CONSTANT_STRING(L"\\??\\Volume");
49 UNICODE_STRING ReparseIndex = RTL_CONSTANT_STRING(L"\\$Extend\\$Reparse:$R:$INDEX_ALLOCATION");
50 
51 /*
52  * @implemented
53  */
54 NTSTATUS
CreateStringWithGlobal(IN PUNICODE_STRING DosName,OUT PUNICODE_STRING GlobalString)55 CreateStringWithGlobal(IN PUNICODE_STRING DosName,
56                        OUT PUNICODE_STRING GlobalString)
57 {
58     UNICODE_STRING IntGlobal;
59 
60     if (RtlPrefixUnicodeString(&DosDevices, DosName, TRUE))
61     {
62         /* DOS device - use DOS global */
63         IntGlobal.Length = DosName->Length - DosDevices.Length + DosGlobal.Length;
64         IntGlobal.MaximumLength = IntGlobal.Length + sizeof(WCHAR);
65         IntGlobal.Buffer = AllocatePool(IntGlobal.MaximumLength);
66         if (!IntGlobal.Buffer)
67         {
68             return STATUS_INSUFFICIENT_RESOURCES;
69         }
70 
71         RtlCopyMemory(IntGlobal.Buffer, DosGlobal.Buffer, DosGlobal.Length);
72         RtlCopyMemory(IntGlobal.Buffer + (DosGlobal.Length / sizeof(WCHAR)),
73                       DosName->Buffer + (DosDevices.Length / sizeof(WCHAR)),
74                       DosName->Length - DosDevices.Length);
75         IntGlobal.Buffer[IntGlobal.Length / sizeof(WCHAR)] = UNICODE_NULL;
76     }
77     else if (RtlPrefixUnicodeString(&Global, DosName, TRUE))
78     {
79         /* Switch to DOS global */
80         IntGlobal.Length = DosName->Length - Global.Length + DosGlobal.Length;
81         IntGlobal.MaximumLength = IntGlobal.Length + sizeof(WCHAR);
82         IntGlobal.Buffer = AllocatePool(IntGlobal.MaximumLength);
83         if (!IntGlobal.Buffer)
84         {
85             return STATUS_INSUFFICIENT_RESOURCES;
86         }
87 
88         RtlCopyMemory(IntGlobal.Buffer, DosGlobal.Buffer, DosGlobal.Length);
89         RtlCopyMemory(IntGlobal.Buffer + (DosGlobal.Length / sizeof(WCHAR)),
90                       DosName->Buffer + (Global.Length / sizeof(WCHAR)),
91                       DosName->Length - Global.Length);
92         IntGlobal.Buffer[IntGlobal.Length / sizeof(WCHAR)] = UNICODE_NULL;
93     }
94     else
95     {
96         /* Simply duplicate string */
97         IntGlobal.Length = DosName->Length;
98         IntGlobal.MaximumLength = DosName->MaximumLength;
99         IntGlobal.Buffer = AllocatePool(IntGlobal.MaximumLength);
100         if (!IntGlobal.Buffer)
101         {
102             return STATUS_INSUFFICIENT_RESOURCES;
103         }
104 
105         RtlCopyMemory(IntGlobal.Buffer, DosName->Buffer, IntGlobal.MaximumLength);
106     }
107 
108     /* Return string */
109     GlobalString->Length = IntGlobal.Length;
110     GlobalString->MaximumLength = IntGlobal.MaximumLength;
111     GlobalString->Buffer = IntGlobal.Buffer;
112 
113     return STATUS_SUCCESS;
114 }
115 
116 /*
117  * @implemented
118  */
119 NTSTATUS
GlobalCreateSymbolicLink(IN PUNICODE_STRING DosName,IN PUNICODE_STRING DeviceName)120 GlobalCreateSymbolicLink(IN PUNICODE_STRING DosName,
121                          IN PUNICODE_STRING DeviceName)
122 {
123     NTSTATUS Status;
124     UNICODE_STRING GlobalName;
125 
126     /* First create the global string */
127     Status = CreateStringWithGlobal(DosName, &GlobalName);
128     if (!NT_SUCCESS(Status))
129     {
130         return Status;
131     }
132 
133     /* Then, create the symlink */
134     Status = IoCreateSymbolicLink(&GlobalName, DeviceName);
135 
136     FreePool(GlobalName.Buffer);
137 
138     return Status;
139 }
140 
141 /*
142  * @implemented
143  */
144 NTSTATUS
GlobalDeleteSymbolicLink(IN PUNICODE_STRING DosName)145 GlobalDeleteSymbolicLink(IN PUNICODE_STRING DosName)
146 {
147     NTSTATUS Status;
148     UNICODE_STRING GlobalName;
149 
150     /* Recreate the string (to find the link) */
151     Status = CreateStringWithGlobal(DosName, &GlobalName);
152     if (!NT_SUCCESS(Status))
153     {
154         return Status;
155     }
156 
157     /* And delete the link */
158     Status = IoDeleteSymbolicLink(&GlobalName);
159 
160     FreePool(GlobalName.Buffer);
161 
162     return Status;
163 }
164 
165 /*
166  * @implemented
167  */
168 VOID
SendLinkCreated(IN PUNICODE_STRING SymbolicName)169 SendLinkCreated(IN PUNICODE_STRING SymbolicName)
170 {
171     NTSTATUS Status;
172     ULONG NameSize;
173     PFILE_OBJECT FileObject;
174     PMOUNTDEV_NAME Name = NULL;
175     PDEVICE_OBJECT DeviceObject;
176 
177     /* Get the device associated with the name */
178     Status = IoGetDeviceObjectPointer(SymbolicName,
179                                       FILE_READ_ATTRIBUTES,
180                                       &FileObject,
181                                       &DeviceObject);
182     if (!NT_SUCCESS(Status))
183         return;
184 
185     /* Get attached device (will notify it) */
186     DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
187 
188     /* NameSize is the size of the whole MOUNTDEV_NAME structure */
189     NameSize = sizeof(USHORT) + SymbolicName->Length;
190     Name = AllocatePool(NameSize);
191     if (!Name)
192         goto Cleanup;
193 
194     /* Initialize structure */
195     Name->NameLength = SymbolicName->Length;
196     RtlCopyMemory(Name->Name, SymbolicName->Buffer, SymbolicName->Length);
197 
198     /* Send the notification, using the new (Windows 2003+) IOCTL definition
199      * (with limited access) first. If this fails, the called driver may be
200      * for an older NT version, and so, we send again the notification using
201      * the old (Windows 2000) IOCTL definition (with any access). */
202     Status = MountMgrSendSyncDeviceIoCtl(IOCTL_MOUNTDEV_LINK_CREATED,
203                                          DeviceObject,
204                                          Name,
205                                          NameSize,
206                                          NULL,
207                                          0,
208                                          FileObject);
209     /* This one can fail, no one matters */
210     UNREFERENCED_PARAMETER(Status);
211 #if (NTDDI_VERSION >= NTDDI_WS03)
212     /* Then, second one */
213     Status = MountMgrSendSyncDeviceIoCtl(IOCTL_MOUNTDEV_LINK_CREATED_UNSECURE_DEPRECATED,
214                                          DeviceObject,
215                                          Name,
216                                          NameSize,
217                                          NULL,
218                                          0,
219                                          FileObject);
220     UNREFERENCED_PARAMETER(Status);
221 #endif // (NTDDI_VERSION >= NTDDI_WS03)
222 
223 Cleanup:
224     if (Name)
225         FreePool(Name);
226 
227     ObDereferenceObject(DeviceObject);
228     ObDereferenceObject(FileObject);
229 
230     return;
231 }
232 
233 /*
234  * @implemented
235  */
236 VOID
SendLinkDeleted(IN PUNICODE_STRING DeviceName,IN PUNICODE_STRING SymbolicName)237 SendLinkDeleted(IN PUNICODE_STRING DeviceName,
238                 IN PUNICODE_STRING SymbolicName)
239 {
240     NTSTATUS Status;
241     ULONG NameSize;
242     PFILE_OBJECT FileObject;
243     PMOUNTDEV_NAME Name = NULL;
244     PDEVICE_OBJECT DeviceObject;
245 
246     /* Get the device associated with the name */
247     Status = IoGetDeviceObjectPointer(DeviceName,
248                                       FILE_READ_ATTRIBUTES,
249                                       &FileObject,
250                                       &DeviceObject);
251     if (!NT_SUCCESS(Status))
252         return;
253 
254     /* Get attached device (will notify it) */
255     DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
256 
257     /* NameSize is the size of the whole MOUNTDEV_NAME structure */
258     NameSize = sizeof(USHORT) + SymbolicName->Length;
259     Name = AllocatePool(NameSize);
260     if (!Name)
261         goto Cleanup;
262 
263     /* Initialize structure */
264     Name->NameLength = SymbolicName->Length;
265     RtlCopyMemory(Name->Name, SymbolicName->Buffer, SymbolicName->Length);
266 
267     /* Send the notification, using the new (Windows 2003+) IOCTL definition
268      * (with limited access) first. If this fails, the called driver may be
269      * for an older NT version, and so, we send again the notification using
270      * the old (Windows 2000) IOCTL definition (with any access). */
271     Status = MountMgrSendSyncDeviceIoCtl(IOCTL_MOUNTDEV_LINK_DELETED,
272                                          DeviceObject,
273                                          Name,
274                                          NameSize,
275                                          NULL,
276                                          0,
277                                          FileObject);
278     /* This one can fail, no one matters */
279     UNREFERENCED_PARAMETER(Status);
280 #if (NTDDI_VERSION >= NTDDI_WS03)
281     /* Then, second one */
282     Status = MountMgrSendSyncDeviceIoCtl(IOCTL_MOUNTDEV_LINK_DELETED_UNSECURE_DEPRECATED,
283                                          DeviceObject,
284                                          Name,
285                                          NameSize,
286                                          NULL,
287                                          0,
288                                          FileObject);
289     UNREFERENCED_PARAMETER(Status);
290 #endif // (NTDDI_VERSION >= NTDDI_WS03)
291 
292 Cleanup:
293     if (Name)
294         FreePool(Name);
295 
296     ObDereferenceObject(DeviceObject);
297     ObDereferenceObject(FileObject);
298 
299     return;
300 }
301 
302 /*
303  * @implemented
304  */
305 NTSTATUS
306 NTAPI
SymbolicLinkNamesFromUniqueIdCount(IN PWSTR ValueName,IN ULONG ValueType,IN PVOID ValueData,IN ULONG ValueLength,IN PVOID Context,IN PVOID EntryContext)307 SymbolicLinkNamesFromUniqueIdCount(IN PWSTR ValueName,
308                                    IN ULONG ValueType,
309                                    IN PVOID ValueData,
310                                    IN ULONG ValueLength,
311                                    IN PVOID Context,
312                                    IN PVOID EntryContext)
313 {
314     UNICODE_STRING ValueNameString;
315     PMOUNTDEV_UNIQUE_ID UniqueId = Context;
316 
317     if (ValueName[0] != L'#' || ValueType != REG_BINARY ||
318         (UniqueId->UniqueIdLength != ValueLength))
319     {
320         return STATUS_SUCCESS;
321     }
322 
323     if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength)
324     {
325         return STATUS_SUCCESS;
326     }
327 
328     /* That one matched, increase count */
329     RtlInitUnicodeString(&ValueNameString, ValueName);
330     if (ValueNameString.Length)
331     {
332         (*((PULONG)EntryContext))++;
333     }
334 
335     return STATUS_SUCCESS;
336 }
337 
338 /*
339  * @implemented
340  */
341 NTSTATUS
342 NTAPI
SymbolicLinkNamesFromUniqueIdQuery(IN PWSTR ValueName,IN ULONG ValueType,IN PVOID ValueData,IN ULONG ValueLength,IN PVOID Context,IN PVOID EntryContext)343 SymbolicLinkNamesFromUniqueIdQuery(IN PWSTR ValueName,
344                                    IN ULONG ValueType,
345                                    IN PVOID ValueData,
346                                    IN ULONG ValueLength,
347                                    IN PVOID Context,
348                                    IN PVOID EntryContext)
349 {
350     UNICODE_STRING ValueNameString;
351     PMOUNTDEV_UNIQUE_ID UniqueId = Context;
352     /* Unicode strings table */
353     PUNICODE_STRING ReturnString = EntryContext;
354 
355     if (ValueName[0] != L'#' || ValueType != REG_BINARY ||
356         (UniqueId->UniqueIdLength != ValueLength))
357     {
358         return STATUS_SUCCESS;
359     }
360 
361     if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength)
362     {
363         return STATUS_SUCCESS;
364     }
365 
366     /* Unique ID matches, let's put the symlink */
367     RtlInitUnicodeString(&ValueNameString, ValueName);
368     if (!ValueNameString.Length)
369     {
370         return STATUS_SUCCESS;
371     }
372 
373     /* Allocate string to copy */
374     ValueNameString.Buffer = AllocatePool(ValueNameString.MaximumLength);
375     if (!ValueNameString.Buffer)
376     {
377         return STATUS_SUCCESS;
378     }
379 
380     /* Copy */
381     RtlCopyMemory(ValueNameString.Buffer, ValueName, ValueNameString.Length);
382     ValueNameString.Buffer[ValueNameString.Length / sizeof(WCHAR)] = UNICODE_NULL;
383 
384     while (ReturnString->Length)
385     {
386         ReturnString++;
387     }
388 
389     /* And return that string */
390     *ReturnString = ValueNameString;
391 
392     return STATUS_SUCCESS;
393 }
394 
395 /*
396  * @implemented
397  */
398 NTSTATUS
CreateNewVolumeName(OUT PUNICODE_STRING VolumeName,IN PGUID VolumeGuid OPTIONAL)399 CreateNewVolumeName(OUT PUNICODE_STRING VolumeName,
400                     IN PGUID VolumeGuid OPTIONAL)
401 {
402     GUID Guid;
403     NTSTATUS Status;
404     UNICODE_STRING GuidString;
405 
406     /* If no GUID was provided, then create one */
407     if (!VolumeGuid)
408     {
409         Status = ExUuidCreate(&Guid);
410         if (!NT_SUCCESS(Status))
411         {
412             return Status;
413         }
414     }
415     else
416     {
417         RtlCopyMemory(&Guid, VolumeGuid, sizeof(GUID));
418     }
419 
420     /* Convert GUID to string */
421     Status = RtlStringFromGUID(&Guid, &GuidString);
422     if (!NT_SUCCESS(Status))
423     {
424         return Status;
425     }
426 
427     /* Size for volume namespace, literal GUID, and null char */
428     VolumeName->MaximumLength = 0x14 + 0x4C + sizeof(UNICODE_NULL);
429     VolumeName->Buffer = AllocatePool(0x14 + 0x4C + sizeof(UNICODE_NULL));
430     if (!VolumeName->Buffer)
431     {
432         Status = STATUS_INSUFFICIENT_RESOURCES;
433     }
434     else
435     {
436         RtlCopyUnicodeString(VolumeName, &Volume);
437         RtlAppendUnicodeStringToString(VolumeName, &GuidString);
438         VolumeName->Buffer[VolumeName->Length / sizeof(WCHAR)] = UNICODE_NULL;
439         Status = STATUS_SUCCESS;
440     }
441 
442     ExFreePoolWithTag(GuidString.Buffer, 0);
443 
444     return Status;
445 }
446 
447 /*
448  * @implemented
449  */
450 NTSTATUS
QuerySymbolicLinkNamesFromStorage(IN PDEVICE_EXTENSION DeviceExtension,IN PDEVICE_INFORMATION DeviceInformation,IN PUNICODE_STRING SuggestedLinkName,IN BOOLEAN UseOnlyIfThereAreNoOtherLinks,OUT PUNICODE_STRING * SymLinks,OUT PULONG SymLinkCount,IN BOOLEAN HasGuid,IN LPGUID Guid)451 QuerySymbolicLinkNamesFromStorage(IN PDEVICE_EXTENSION DeviceExtension,
452                                   IN PDEVICE_INFORMATION DeviceInformation,
453                                   IN PUNICODE_STRING SuggestedLinkName,
454                                   IN BOOLEAN UseOnlyIfThereAreNoOtherLinks,
455                                   OUT PUNICODE_STRING * SymLinks,
456                                   OUT PULONG SymLinkCount,
457                                   IN BOOLEAN HasGuid,
458                                   IN LPGUID Guid)
459 {
460     NTSTATUS Status;
461     BOOLEAN WriteNew;
462     RTL_QUERY_REGISTRY_TABLE QueryTable[2];
463 
464     UNREFERENCED_PARAMETER(DeviceExtension);
465 
466     /* First of all, count links */
467     RtlZeroMemory(QueryTable, sizeof(QueryTable));
468     QueryTable[0].QueryRoutine = SymbolicLinkNamesFromUniqueIdCount;
469     QueryTable[0].EntryContext = SymLinkCount;
470     *SymLinkCount = 0;
471 
472     Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
473                                     DatabasePath,
474                                     QueryTable,
475                                     DeviceInformation->UniqueId,
476                                     NULL);
477     if (!NT_SUCCESS(Status))
478     {
479         *SymLinkCount = 0;
480     }
481 
482     /* Check if we have to write a new one first */
483     if (SuggestedLinkName && !IsDriveLetter(SuggestedLinkName) &&
484         UseOnlyIfThereAreNoOtherLinks && *SymLinkCount == 0)
485     {
486         WriteNew = TRUE;
487     }
488     else
489     {
490         WriteNew = FALSE;
491     }
492 
493     /* If has GUID, it makes one more link */
494     if (HasGuid)
495     {
496         (*SymLinkCount)++;
497     }
498 
499     if (WriteNew)
500     {
501         /* Write link */
502         RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
503                               DatabasePath,
504                               SuggestedLinkName->Buffer,
505                               REG_BINARY,
506                               DeviceInformation->UniqueId->UniqueId,
507                               DeviceInformation->UniqueId->UniqueIdLength);
508 
509         /* And recount all the needed links */
510         RtlZeroMemory(QueryTable, sizeof(QueryTable));
511         QueryTable[0].QueryRoutine = SymbolicLinkNamesFromUniqueIdCount;
512         QueryTable[0].EntryContext = SymLinkCount;
513         *SymLinkCount = 0;
514 
515         Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
516                                         DatabasePath,
517                                         QueryTable,
518                                         DeviceInformation->UniqueId,
519                                         NULL);
520         if (!NT_SUCCESS(Status))
521         {
522             return STATUS_NOT_FOUND;
523         }
524     }
525 
526     /* Not links found? */
527     if (!*SymLinkCount)
528     {
529         return STATUS_NOT_FOUND;
530     }
531 
532     /* Allocate a buffer big enough to hold symlinks (table of unicode strings) */
533     *SymLinks = AllocatePool(*SymLinkCount * sizeof(UNICODE_STRING));
534     if (!*SymLinks)
535     {
536         return STATUS_INSUFFICIENT_RESOURCES;
537     }
538 
539     /* Prepare to query links */
540     RtlZeroMemory(*SymLinks, *SymLinkCount * sizeof(UNICODE_STRING));
541     RtlZeroMemory(QueryTable, sizeof(QueryTable));
542     QueryTable[0].QueryRoutine = SymbolicLinkNamesFromUniqueIdQuery;
543 
544     /* No GUID? Keep it that way */
545     if (!HasGuid)
546     {
547         QueryTable[0].EntryContext = *SymLinks;
548     }
549     /* Otherwise, first create volume name */
550     else
551     {
552         Status = CreateNewVolumeName(SymLinks[0], Guid);
553         if (!NT_SUCCESS(Status))
554         {
555             FreePool(*SymLinks);
556             return Status;
557         }
558 
559         /* Skip first link (ours) */
560         QueryTable[0].EntryContext = *SymLinks + 1;
561     }
562 
563     /* Now, query */
564     RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
565                            DatabasePath,
566                            QueryTable,
567                            DeviceInformation->UniqueId,
568                            NULL);
569 
570     return STATUS_SUCCESS;
571 }
572 
573 /*
574  * @implemented
575  */
576 PSAVED_LINK_INFORMATION
RemoveSavedLinks(IN PDEVICE_EXTENSION DeviceExtension,IN PMOUNTDEV_UNIQUE_ID UniqueId)577 RemoveSavedLinks(IN PDEVICE_EXTENSION DeviceExtension,
578                  IN PMOUNTDEV_UNIQUE_ID UniqueId)
579 {
580     PLIST_ENTRY NextEntry;
581     PSAVED_LINK_INFORMATION SavedLinkInformation;
582 
583     /* No saved links? Easy! */
584     if (IsListEmpty(&(DeviceExtension->SavedLinksListHead)))
585     {
586         return NULL;
587     }
588 
589     /* Now, browse saved links */
590     for (NextEntry = DeviceExtension->SavedLinksListHead.Flink;
591          NextEntry != &(DeviceExtension->SavedLinksListHead);
592          NextEntry = NextEntry->Flink)
593     {
594         SavedLinkInformation = CONTAINING_RECORD(NextEntry,
595                                                  SAVED_LINK_INFORMATION,
596                                                  SavedLinksListEntry);
597 
598         /* Find the one that matches */
599         if (SavedLinkInformation->UniqueId->UniqueIdLength == UniqueId->UniqueIdLength)
600         {
601             if (RtlCompareMemory(SavedLinkInformation->UniqueId->UniqueId,
602                                  UniqueId->UniqueId,
603                                  UniqueId->UniqueIdLength) ==
604                 UniqueId->UniqueIdLength)
605             {
606                 /* Remove it and return it */
607                 RemoveEntryList(&(SavedLinkInformation->SavedLinksListEntry));
608                 return SavedLinkInformation;
609             }
610         }
611     }
612 
613     /* None found (none removed) */
614     return NULL;
615 }
616 
617 /*
618  * @implemented
619  */
620 NTSTATUS
QuerySuggestedLinkName(IN PUNICODE_STRING SymbolicName,OUT PUNICODE_STRING SuggestedLinkName,OUT PBOOLEAN UseOnlyIfThereAreNoOtherLinks)621 QuerySuggestedLinkName(IN PUNICODE_STRING SymbolicName,
622                        OUT PUNICODE_STRING SuggestedLinkName,
623                        OUT PBOOLEAN UseOnlyIfThereAreNoOtherLinks)
624 {
625     NTSTATUS Status;
626     USHORT NameLength;
627     PFILE_OBJECT FileObject;
628     PDEVICE_OBJECT DeviceObject;
629     PMOUNTDEV_SUGGESTED_LINK_NAME IoCtlSuggested;
630 
631     /* First, get device */
632     Status = IoGetDeviceObjectPointer(SymbolicName,
633                                       FILE_READ_ATTRIBUTES,
634                                       &FileObject,
635                                       &DeviceObject);
636     if (!NT_SUCCESS(Status))
637     {
638         return Status;
639     }
640 
641     /* Then, get attached device */
642     DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
643 
644     /* Then, prepare buffer to query suggested name */
645     IoCtlSuggested = AllocatePool(sizeof(MOUNTDEV_SUGGESTED_LINK_NAME));
646     if (!IoCtlSuggested)
647     {
648         Status = STATUS_INSUFFICIENT_RESOURCES;
649         goto Dereference;
650     }
651 
652     /* Prepare request */
653     Status = MountMgrSendSyncDeviceIoCtl(IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME,
654                                          DeviceObject,
655                                          NULL,
656                                          0,
657                                          IoCtlSuggested,
658                                          sizeof(MOUNTDEV_SUGGESTED_LINK_NAME),
659                                          FileObject);
660     /* Retry with appropriate length */
661     if (Status == STATUS_BUFFER_OVERFLOW)
662     {
663         /* Reallocate big enough buffer */
664         NameLength = IoCtlSuggested->NameLength + sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
665         FreePool(IoCtlSuggested);
666 
667         IoCtlSuggested = AllocatePool(NameLength);
668         if (!IoCtlSuggested)
669         {
670             Status = STATUS_INSUFFICIENT_RESOURCES;
671             goto Dereference;
672         }
673 
674         /* And re-ask */
675         Status = MountMgrSendSyncDeviceIoCtl(IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME,
676                                              DeviceObject,
677                                              NULL,
678                                              0,
679                                              IoCtlSuggested,
680                                              NameLength,
681                                              FileObject);
682     }
683     if (!NT_SUCCESS(Status))
684         goto Release;
685 
686     /* Now we have suggested name, copy it */
687     SuggestedLinkName->Length = IoCtlSuggested->NameLength;
688     SuggestedLinkName->MaximumLength = IoCtlSuggested->NameLength + sizeof(UNICODE_NULL);
689     SuggestedLinkName->Buffer = AllocatePool(IoCtlSuggested->NameLength + sizeof(UNICODE_NULL));
690     if (!SuggestedLinkName->Buffer)
691     {
692         Status = STATUS_INSUFFICIENT_RESOURCES;
693     }
694     else
695     {
696         RtlCopyMemory(SuggestedLinkName->Buffer, IoCtlSuggested->Name, IoCtlSuggested->NameLength);
697         SuggestedLinkName->Buffer[SuggestedLinkName->Length / sizeof(WCHAR)] = UNICODE_NULL;
698     }
699 
700     /* Also return its priority */
701     *UseOnlyIfThereAreNoOtherLinks = IoCtlSuggested->UseOnlyIfThereAreNoOtherLinks;
702 
703 Release:
704     FreePool(IoCtlSuggested);
705 
706 Dereference:
707     ObDereferenceObject(DeviceObject);
708     ObDereferenceObject(FileObject);
709 
710     return Status;
711 }
712 
713 /*
714  * @implemented
715  */
716 BOOLEAN
RedirectSavedLink(IN PSAVED_LINK_INFORMATION SavedLinkInformation,IN PUNICODE_STRING DosName,IN PUNICODE_STRING NewLink)717 RedirectSavedLink(IN PSAVED_LINK_INFORMATION SavedLinkInformation,
718                   IN PUNICODE_STRING DosName,
719                   IN PUNICODE_STRING NewLink)
720 {
721     PLIST_ENTRY NextEntry;
722     PSYMLINK_INFORMATION SymlinkInformation;
723 
724     /* Find the link */
725     for (NextEntry = SavedLinkInformation->SymbolicLinksListHead.Flink;
726          NextEntry != &(SavedLinkInformation->SymbolicLinksListHead);
727          NextEntry = NextEntry->Flink)
728     {
729         SymlinkInformation = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
730 
731         if (!RtlEqualUnicodeString(DosName, &(SymlinkInformation->Name), TRUE))
732         {
733             /* Delete old link */
734             GlobalDeleteSymbolicLink(DosName);
735             /* Set its new location */
736             GlobalCreateSymbolicLink(DosName, NewLink);
737 
738             /* And remove it from the list (not valid any more) */
739             RemoveEntryList(&(SymlinkInformation->SymbolicLinksListEntry));
740             FreePool(SymlinkInformation->Name.Buffer);
741             FreePool(SymlinkInformation);
742 
743             return TRUE;
744         }
745     }
746 
747     return FALSE;
748 }
749 
750 /*
751  * @implemented
752  */
753 VOID
DeleteSymbolicLinkNameFromMemory(IN PDEVICE_EXTENSION DeviceExtension,IN PUNICODE_STRING SymbolicLink,IN BOOLEAN MarkOffline)754 DeleteSymbolicLinkNameFromMemory(IN PDEVICE_EXTENSION DeviceExtension,
755                                  IN PUNICODE_STRING SymbolicLink,
756                                  IN BOOLEAN MarkOffline)
757 {
758     PLIST_ENTRY DeviceEntry, SymbolEntry;
759     PDEVICE_INFORMATION DeviceInformation;
760     PSYMLINK_INFORMATION SymlinkInformation;
761 
762     /* First of all, ensure we have devices */
763     if (IsListEmpty(&(DeviceExtension->DeviceListHead)))
764     {
765         return;
766     }
767 
768     /* Then, look for the symbolic name */
769     for (DeviceEntry = DeviceExtension->DeviceListHead.Flink;
770          DeviceEntry != &(DeviceExtension->DeviceListHead);
771          DeviceEntry = DeviceEntry->Flink)
772     {
773         DeviceInformation = CONTAINING_RECORD(DeviceEntry, DEVICE_INFORMATION, DeviceListEntry);
774 
775         for (SymbolEntry = DeviceInformation->SymbolicLinksListHead.Flink;
776              SymbolEntry != &(DeviceInformation->SymbolicLinksListHead);
777              SymbolEntry = SymbolEntry->Flink)
778         {
779             SymlinkInformation = CONTAINING_RECORD(SymbolEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
780 
781             /* One we have found it */
782             if (RtlCompareUnicodeString(SymbolicLink, &(SymlinkInformation->Name), TRUE) == 0)
783             {
784                 /* Check if caller just want it to be offline */
785                 if (MarkOffline)
786                 {
787                     SymlinkInformation->Online = FALSE;
788                 }
789                 else
790                 {
791                     /* If not, delete it & notify */
792                     SendLinkDeleted(&(DeviceInformation->SymbolicName), SymbolicLink);
793                     RemoveEntryList(&(SymlinkInformation->SymbolicLinksListEntry));
794 
795                     FreePool(SymlinkInformation->Name.Buffer);
796                     FreePool(SymlinkInformation);
797                 }
798 
799                 /* No need to go farther */
800                 return;
801             }
802         }
803     }
804 
805     return;
806 }
807 
808 /*
809  * @implemented
810  */
811 BOOLEAN
IsDriveLetter(PUNICODE_STRING SymbolicName)812 IsDriveLetter(PUNICODE_STRING SymbolicName)
813 {
814     WCHAR Letter, Colon;
815 
816     /* We must have a precise length */
817     if (SymbolicName->Length != DosDevices.Length + 2 * sizeof(WCHAR))
818     {
819         return FALSE;
820     }
821 
822     /* Must start with the DosDevices prefix */
823     if (!RtlPrefixUnicodeString(&DosDevices, SymbolicName, TRUE))
824     {
825         return FALSE;
826     }
827 
828     /* Check if letter is correct */
829     Letter = SymbolicName->Buffer[DosDevices.Length / sizeof(WCHAR)];
830     if ((Letter < L'A' || Letter > L'Z') && Letter != (WCHAR)-1)
831     {
832         return FALSE;
833     }
834 
835     /* And finally it must end with a colon */
836     Colon = SymbolicName->Buffer[DosDevices.Length / sizeof(WCHAR) + 1];
837     if (Colon != L':')
838     {
839         return FALSE;
840     }
841 
842     return TRUE;
843 }
844 
845 /*
846  * @implemented
847  */
848 NTSTATUS
MountMgrQuerySymbolicLink(IN PUNICODE_STRING SymbolicName,IN OUT PUNICODE_STRING LinkTarget)849 MountMgrQuerySymbolicLink(IN PUNICODE_STRING SymbolicName,
850                           IN OUT PUNICODE_STRING LinkTarget)
851 {
852     NTSTATUS Status;
853     HANDLE LinkHandle;
854     OBJECT_ATTRIBUTES ObjectAttributes;
855 
856     /* Open the symbolic link */
857     InitializeObjectAttributes(&ObjectAttributes,
858                                SymbolicName,
859                                OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
860                                NULL,
861                                NULL);
862 
863     Status = ZwOpenSymbolicLinkObject(&LinkHandle,
864                                       GENERIC_READ,
865                                       &ObjectAttributes);
866     if (!NT_SUCCESS(Status))
867     {
868         return Status;
869     }
870 
871     /* Query its target */
872     Status = ZwQuerySymbolicLinkObject(LinkHandle,
873                                        LinkTarget,
874                                        NULL);
875 
876     ZwClose(LinkHandle);
877 
878     if (!NT_SUCCESS(Status))
879     {
880         return Status;
881     }
882 
883     if (LinkTarget->Length <= sizeof(WCHAR))
884     {
885         return Status;
886     }
887 
888     /* If it's not finished by \, just return */
889     if (LinkTarget->Buffer[LinkTarget->Length / sizeof(WCHAR) - 1] != L'\\')
890     {
891         return Status;
892     }
893 
894     /* Otherwise, ensure to drop the tailing \ */
895     LinkTarget->Length -= sizeof(WCHAR);
896     LinkTarget->Buffer[LinkTarget->Length / sizeof(WCHAR)] = UNICODE_NULL;
897 
898     return Status;
899 }
900