* Sync up to trunk head (r64894).
[reactos.git] / drivers / filters / mountmgr / symlink.c
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 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");
41
42 /*
43 * @implemented
44 */
45 NTSTATUS
46 CreateStringWithGlobal(IN PUNICODE_STRING DosName,
47 OUT PUNICODE_STRING GlobalString)
48 {
49 UNICODE_STRING IntGlobal;
50
51 if (RtlPrefixUnicodeString(&DosDevices, DosName, TRUE))
52 {
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)
58 {
59 return STATUS_INSUFFICIENT_RESOURCES;
60 }
61
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;
67 }
68 else if (RtlPrefixUnicodeString(&Global, DosName, TRUE))
69 {
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)
75 {
76 return STATUS_INSUFFICIENT_RESOURCES;
77 }
78
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;
84 }
85 else
86 {
87 /* Simply duplicate string */
88 IntGlobal.Length = DosName->Length;
89 IntGlobal.MaximumLength = DosName->MaximumLength;
90 IntGlobal.Buffer = AllocatePool(IntGlobal.MaximumLength);
91 if (!IntGlobal.Buffer)
92 {
93 return STATUS_INSUFFICIENT_RESOURCES;
94 }
95
96 RtlCopyMemory(IntGlobal.Buffer, DosName->Buffer, IntGlobal.MaximumLength);
97 }
98
99 /* Return string */
100 GlobalString->Length = IntGlobal.Length;
101 GlobalString->MaximumLength = IntGlobal.MaximumLength;
102 GlobalString->Buffer = IntGlobal.Buffer;
103
104 return STATUS_SUCCESS;
105 }
106
107 /*
108 * @implemented
109 */
110 NTSTATUS
111 GlobalCreateSymbolicLink(IN PUNICODE_STRING DosName,
112 IN PUNICODE_STRING DeviceName)
113 {
114 NTSTATUS Status;
115 UNICODE_STRING GlobalName;
116
117 UNREFERENCED_PARAMETER(DeviceName);
118
119 /* First create the global string */
120 Status = CreateStringWithGlobal(DosName, &GlobalName);
121 if (!NT_SUCCESS(Status))
122 {
123 return Status;
124 }
125
126 /* Then, create the symlink */
127 Status = IoCreateSymbolicLink(&GlobalName, DosName);
128
129 FreePool(GlobalName.Buffer);
130
131 return Status;
132 }
133
134 /*
135 * @implemented
136 */
137 NTSTATUS
138 GlobalDeleteSymbolicLink(IN PUNICODE_STRING DosName)
139 {
140 NTSTATUS Status;
141 UNICODE_STRING GlobalName;
142
143 /* Recreate the string (to find the link) */
144 Status = CreateStringWithGlobal(DosName, &GlobalName);
145 if (!NT_SUCCESS(Status))
146 {
147 return Status;
148 }
149
150 /* And delete the link */
151 Status = IoDeleteSymbolicLink(&GlobalName);
152
153 FreePool(GlobalName.Buffer);
154
155 return Status;
156 }
157
158 /*
159 * @implemented
160 */
161 VOID
162 SendLinkCreated(IN PUNICODE_STRING SymbolicName)
163 {
164 PIRP Irp;
165 KEVENT Event;
166 ULONG NameSize;
167 NTSTATUS Status;
168 PFILE_OBJECT FileObject;
169 PIO_STACK_LOCATION Stack;
170 PMOUNTDEV_NAME Name = NULL;
171 PDEVICE_OBJECT DeviceObject;
172 IO_STATUS_BLOCK IoStatusBlock;
173
174 /* Get the device associated with the name */
175 Status = IoGetDeviceObjectPointer(SymbolicName,
176 FILE_READ_ATTRIBUTES,
177 &FileObject,
178 &DeviceObject);
179 if (!NT_SUCCESS(Status))
180 {
181 return;
182 }
183
184 /* Get attached device (will notify it) */
185 DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
186
187 /* NameSize is the size of the whole MOUNTDEV_NAME struct */
188 NameSize = sizeof(USHORT) + SymbolicName->Length;
189 Name = AllocatePool(NameSize);
190 if (!Name)
191 {
192 goto Cleanup;
193 }
194
195 /* Initialize struct */
196 Name->NameLength = SymbolicName->Length;
197 RtlCopyMemory(Name->Name, SymbolicName->Buffer, SymbolicName->Length);
198
199 KeInitializeEvent(&Event, NotificationEvent, FALSE);
200 /* Microsoft does it twice... Once with limited access, second with any
201 * So, first one here
202 */
203 Irp = IoBuildDeviceIoControlRequest(CTL_CODE(MOUNTDEVCONTROLTYPE, 4, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
204 DeviceObject,
205 Name,
206 NameSize,
207 NULL,
208 0,
209 FALSE,
210 &Event,
211 &IoStatusBlock);
212 /* This one can fail, no one matters */
213 if (Irp)
214 {
215 Stack = IoGetNextIrpStackLocation(Irp);
216 Stack->FileObject = FileObject;
217
218 Status = IoCallDriver(DeviceObject, Irp);
219 if (Status == STATUS_PENDING)
220 {
221 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
222 }
223 }
224
225 /* Then, second one */
226 KeInitializeEvent(&Event, NotificationEvent, FALSE);
227 Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_LINK_CREATED,
228 DeviceObject,
229 Name,
230 NameSize,
231 NULL,
232 0,
233 FALSE,
234 &Event,
235 &IoStatusBlock);
236 if (!Irp)
237 {
238 goto Cleanup;
239 }
240
241 Stack = IoGetNextIrpStackLocation(Irp);
242 Stack->FileObject = FileObject;
243
244 /* Really notify */
245 Status = IoCallDriver(DeviceObject, Irp);
246 if (Status == STATUS_PENDING)
247 {
248 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
249 }
250
251 Cleanup:
252 if (Name)
253 {
254 FreePool(Name);
255 }
256
257 ObDereferenceObject(DeviceObject);
258 ObDereferenceObject(FileObject);
259
260 return;
261 }
262
263 /*
264 * @implemented
265 */
266 VOID
267 SendLinkDeleted(IN PUNICODE_STRING DeviceName,
268 IN PUNICODE_STRING SymbolicName)
269 {
270 PIRP Irp;
271 KEVENT Event;
272 ULONG NameSize;
273 NTSTATUS Status;
274 PFILE_OBJECT FileObject;
275 PIO_STACK_LOCATION Stack;
276 PMOUNTDEV_NAME Name = NULL;
277 PDEVICE_OBJECT DeviceObject;
278 IO_STATUS_BLOCK IoStatusBlock;
279
280 /* Get the device associated with the name */
281 Status = IoGetDeviceObjectPointer(DeviceName,
282 FILE_READ_ATTRIBUTES,
283 &FileObject,
284 &DeviceObject);
285 if (!NT_SUCCESS(Status))
286 {
287 return;
288 }
289
290 /* Get attached device (will notify it) */
291 DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
292
293 /* NameSize is the size of the whole MOUNTDEV_NAME struct */
294 NameSize = sizeof(USHORT) + SymbolicName->Length;
295 Name = AllocatePool(NameSize);
296 if (!Name)
297 {
298 goto Cleanup;
299 }
300
301 /* Initialize struct */
302 Name->NameLength = SymbolicName->Length;
303 RtlCopyMemory(Name->Name, SymbolicName->Buffer, SymbolicName->Length);
304
305 KeInitializeEvent(&Event, NotificationEvent, FALSE);
306 /* Cf: SendLinkCreated comment */
307 Irp = IoBuildDeviceIoControlRequest(CTL_CODE(MOUNTDEVCONTROLTYPE, 5, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS),
308 DeviceObject,
309 Name,
310 NameSize,
311 NULL,
312 0,
313 FALSE,
314 &Event,
315 &IoStatusBlock);
316 /* This one can fail, no one matters */
317 if (Irp)
318 {
319 Stack = IoGetNextIrpStackLocation(Irp);
320 Stack->FileObject = FileObject;
321
322 Status = IoCallDriver(DeviceObject, Irp);
323 if (Status == STATUS_PENDING)
324 {
325 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
326 }
327 }
328
329 /* Then, second one */
330 KeInitializeEvent(&Event, NotificationEvent, FALSE);
331 Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_LINK_DELETED,
332 DeviceObject,
333 Name,
334 NameSize,
335 NULL,
336 0,
337 FALSE,
338 &Event,
339 &IoStatusBlock);
340 if (!Irp)
341 {
342 goto Cleanup;
343 }
344
345 Stack = IoGetNextIrpStackLocation(Irp);
346 Stack->FileObject = FileObject;
347
348 /* Really notify */
349 Status = IoCallDriver(DeviceObject, Irp);
350 if (Status == STATUS_PENDING)
351 {
352 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
353 }
354
355 Cleanup:
356 if (Name)
357 {
358 FreePool(Name);
359 }
360
361 ObDereferenceObject(DeviceObject);
362 ObDereferenceObject(FileObject);
363
364 return;
365 }
366
367 /*
368 * @implemented
369 */
370 NTSTATUS
371 NTAPI
372 SymbolicLinkNamesFromUniqueIdCount(IN PWSTR ValueName,
373 IN ULONG ValueType,
374 IN PVOID ValueData,
375 IN ULONG ValueLength,
376 IN PVOID Context,
377 IN PVOID EntryContext)
378 {
379 UNICODE_STRING ValueNameString;
380 PMOUNTDEV_UNIQUE_ID UniqueId = Context;
381
382 if (ValueName[0] != L'#' || ValueType != REG_BINARY ||
383 (UniqueId->UniqueIdLength != ValueLength))
384 {
385 return STATUS_SUCCESS;
386 }
387
388 if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength)
389 {
390 return STATUS_SUCCESS;
391 }
392
393 /* That one matched, increase count */
394 RtlInitUnicodeString(&ValueNameString, ValueName);
395 if (ValueNameString.Length)
396 {
397 (*((PULONG)EntryContext))++;
398 }
399
400 return STATUS_SUCCESS;
401 }
402
403 /*
404 * @implemented
405 */
406 NTSTATUS
407 NTAPI
408 SymbolicLinkNamesFromUniqueIdQuery(IN PWSTR ValueName,
409 IN ULONG ValueType,
410 IN PVOID ValueData,
411 IN ULONG ValueLength,
412 IN PVOID Context,
413 IN PVOID EntryContext)
414 {
415 UNICODE_STRING ValueNameString;
416 PMOUNTDEV_UNIQUE_ID UniqueId = Context;
417 /* Unicode strings table */
418 PUNICODE_STRING ReturnString = EntryContext;
419
420 if (ValueName[0] != L'#' || ValueType != REG_BINARY ||
421 (UniqueId->UniqueIdLength != ValueLength))
422 {
423 return STATUS_SUCCESS;
424 }
425
426 if (RtlCompareMemory(UniqueId->UniqueId, ValueData, ValueLength) != ValueLength)
427 {
428 return STATUS_SUCCESS;
429 }
430
431 /* Unique ID matches, let's put the symlink */
432 RtlInitUnicodeString(&ValueNameString, ValueName);
433 if (!ValueNameString.Length)
434 {
435 return STATUS_SUCCESS;
436 }
437
438 /* Allocate string to copy */
439 ValueNameString.Buffer = AllocatePool(ValueNameString.MaximumLength);
440 if (!ValueNameString.Buffer)
441 {
442 return STATUS_SUCCESS;
443 }
444
445 /* Copy */
446 RtlCopyMemory(ValueNameString.Buffer, ValueName, ValueNameString.Length);
447 ValueNameString.Buffer[ValueNameString.Length / sizeof(WCHAR)] = UNICODE_NULL;
448
449 while (ReturnString->Length)
450 {
451 ReturnString++;
452 }
453
454 /* And return that string */
455 *ReturnString = ValueNameString;
456
457 return STATUS_SUCCESS;
458 }
459
460 /*
461 * @implemented
462 */
463 NTSTATUS
464 CreateNewVolumeName(OUT PUNICODE_STRING VolumeName,
465 IN PGUID VolumeGuid OPTIONAL)
466 {
467 GUID Guid;
468 NTSTATUS Status;
469 UNICODE_STRING GuidString;
470
471 /* If no GUID was provided, then create one */
472 if (!VolumeGuid)
473 {
474 Status = ExUuidCreate(&Guid);
475 if (!NT_SUCCESS(Status))
476 {
477 return Status;
478 }
479 }
480 else
481 {
482 RtlCopyMemory(&Guid, VolumeGuid, sizeof(GUID));
483 }
484
485 /* Convert GUID to string */
486 Status = RtlStringFromGUID(&Guid, &GuidString);
487 if (!NT_SUCCESS(Status))
488 {
489 return Status;
490 }
491
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)
496 {
497 Status = STATUS_INSUFFICIENT_RESOURCES;
498 }
499 else
500 {
501 RtlCopyUnicodeString(VolumeName, &Volume);
502 RtlAppendUnicodeStringToString(VolumeName, &GuidString);
503 VolumeName->Buffer[VolumeName->Length / sizeof(WCHAR)] = UNICODE_NULL;
504 Status = STATUS_SUCCESS;
505 }
506
507 ExFreePoolWithTag(GuidString.Buffer, 0);
508
509 return Status;
510 }
511
512 /*
513 * @implemented
514 */
515 NTSTATUS
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,
522 IN BOOLEAN HasGuid,
523 IN LPGUID Guid)
524 {
525 NTSTATUS Status;
526 BOOLEAN WriteNew;
527 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
528
529 UNREFERENCED_PARAMETER(DeviceExtension);
530
531 /* First of all, count links */
532 RtlZeroMemory(QueryTable, sizeof(QueryTable));
533 QueryTable[0].QueryRoutine = SymbolicLinkNamesFromUniqueIdCount;
534 QueryTable[0].EntryContext = SymLinkCount;
535 *SymLinkCount = 0;
536
537 Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
538 DatabasePath,
539 QueryTable,
540 DeviceInformation->UniqueId,
541 NULL);
542 if (!NT_SUCCESS(Status))
543 {
544 *SymLinkCount = 0;
545 }
546
547 /* Check if we have to write a new one first */
548 if (SuggestedLinkName && !IsDriveLetter(SuggestedLinkName) &&
549 UseOnlyIfThereAreNoOtherLinks && *SymLinkCount == 0)
550 {
551 WriteNew = TRUE;
552 }
553 else
554 {
555 WriteNew = FALSE;
556 }
557
558 /* If has GUID, it makes one more link */
559 if (HasGuid)
560 {
561 (*SymLinkCount)++;
562 }
563
564 if (WriteNew)
565 {
566 /* Write link */
567 RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
568 DatabasePath,
569 SuggestedLinkName->Buffer,
570 REG_BINARY,
571 DeviceInformation->UniqueId->UniqueId,
572 DeviceInformation->UniqueId->UniqueIdLength);
573
574 /* And recount all the needed links */
575 RtlZeroMemory(QueryTable, sizeof(QueryTable));
576 QueryTable[0].QueryRoutine = SymbolicLinkNamesFromUniqueIdCount;
577 QueryTable[0].EntryContext = SymLinkCount;
578 *SymLinkCount = 0;
579
580 Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
581 DatabasePath,
582 QueryTable,
583 DeviceInformation->UniqueId,
584 NULL);
585 if (!NT_SUCCESS(Status))
586 {
587 return STATUS_NOT_FOUND;
588 }
589 }
590
591 /* Not links found? */
592 if (!*SymLinkCount)
593 {
594 return STATUS_NOT_FOUND;
595 }
596
597 /* Allocate a buffer big enough to hold symlinks (table of unicode strings) */
598 *SymLinks = AllocatePool(*SymLinkCount * sizeof(UNICODE_STRING));
599 if (!*SymLinks)
600 {
601 return STATUS_INSUFFICIENT_RESOURCES;
602 }
603
604 /* Prepare to query links */
605 RtlZeroMemory(*SymLinks, *SymLinkCount * sizeof(UNICODE_STRING));
606 RtlZeroMemory(QueryTable, sizeof(QueryTable));
607 QueryTable[0].QueryRoutine = SymbolicLinkNamesFromUniqueIdQuery;
608
609 /* No GUID? Keep it that way */
610 if (!HasGuid)
611 {
612 QueryTable[0].EntryContext = *SymLinks;
613 }
614 /* Otherwise, first create volume name */
615 else
616 {
617 Status = CreateNewVolumeName(SymLinks[0], Guid);
618 if (!NT_SUCCESS(Status))
619 {
620 FreePool(*SymLinks);
621 return Status;
622 }
623
624 /* Skip first link (ours) */
625 QueryTable[0].EntryContext = *SymLinks + 1;
626 }
627
628 /* Now, query */
629 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
630 DatabasePath,
631 QueryTable,
632 DeviceInformation->UniqueId,
633 NULL);
634
635 return STATUS_SUCCESS;
636 }
637
638 /*
639 * @implemented
640 */
641 PSAVED_LINK_INFORMATION
642 RemoveSavedLinks(IN PDEVICE_EXTENSION DeviceExtension,
643 IN PMOUNTDEV_UNIQUE_ID UniqueId)
644 {
645 PLIST_ENTRY NextEntry;
646 PSAVED_LINK_INFORMATION SavedLinkInformation;
647
648 /* No saved links? Easy! */
649 if (IsListEmpty(&(DeviceExtension->SavedLinksListHead)))
650 {
651 return NULL;
652 }
653
654 /* Now, browse saved links */
655 for (NextEntry = DeviceExtension->SavedLinksListHead.Flink;
656 NextEntry != &(DeviceExtension->SavedLinksListHead);
657 NextEntry = NextEntry->Flink)
658 {
659 SavedLinkInformation = CONTAINING_RECORD(NextEntry,
660 SAVED_LINK_INFORMATION,
661 SavedLinksListEntry);
662
663 /* Find the one that matches */
664 if (SavedLinkInformation->UniqueId->UniqueIdLength == UniqueId->UniqueIdLength)
665 {
666 if (RtlCompareMemory(SavedLinkInformation->UniqueId->UniqueId,
667 UniqueId->UniqueId,
668 UniqueId->UniqueIdLength) ==
669 UniqueId->UniqueIdLength)
670 {
671 /* Remove it and return it */
672 RemoveEntryList(&(SavedLinkInformation->SavedLinksListEntry));
673 return SavedLinkInformation;
674 }
675 }
676 }
677
678 /* None found (none removed) */
679 return NULL;
680 }
681
682 /*
683 * @implemented
684 */
685 NTSTATUS
686 QuerySuggestedLinkName(IN PUNICODE_STRING SymbolicName,
687 OUT PUNICODE_STRING SuggestedLinkName,
688 OUT PBOOLEAN UseOnlyIfThereAreNoOtherLinks)
689 {
690 PIRP Irp;
691 KEVENT Event;
692 NTSTATUS Status;
693 USHORT NameLength;
694 PFILE_OBJECT FileObject;
695 PDEVICE_OBJECT DeviceObject;
696 IO_STATUS_BLOCK IoStatusBlock;
697 PIO_STACK_LOCATION IoStackLocation;
698 PMOUNTDEV_SUGGESTED_LINK_NAME IoCtlSuggested;
699
700 /* First, get device */
701 Status = IoGetDeviceObjectPointer(SymbolicName,
702 FILE_READ_ATTRIBUTES,
703 &FileObject,
704 &DeviceObject);
705 if (!NT_SUCCESS(Status))
706 {
707 return Status;
708 }
709
710 /* Then, get attached device */
711 DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
712
713 /* Then, prepare buffer to query suggested name */
714 IoCtlSuggested = AllocatePool(sizeof(MOUNTDEV_SUGGESTED_LINK_NAME));
715 if (!IoCtlSuggested)
716 {
717 Status = STATUS_INSUFFICIENT_RESOURCES;
718 goto Dereference;
719 }
720
721 /* Prepare request */
722 KeInitializeEvent(&Event, NotificationEvent, FALSE);
723 Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME,
724 DeviceObject,
725 NULL,
726 0,
727 IoCtlSuggested,
728 sizeof(MOUNTDEV_SUGGESTED_LINK_NAME),
729 FALSE,
730 &Event,
731 &IoStatusBlock);
732 if (!Irp)
733 {
734 Status = STATUS_INSUFFICIENT_RESOURCES;
735 goto Release;
736 }
737
738 IoStackLocation = IoGetNextIrpStackLocation(Irp);
739 IoStackLocation->FileObject = FileObject;
740
741 /* And ask */
742 Status = IoCallDriver(DeviceObject, Irp);
743 if (Status == STATUS_PENDING)
744 {
745 KeWaitForSingleObject(&Event, Executive, KernelMode,
746 FALSE, NULL);
747 Status = IoStatusBlock.Status;
748 }
749
750 /* Overflow? Normal */
751 if (Status == STATUS_BUFFER_OVERFLOW)
752 {
753 /* Reallocate big enough buffer */
754 NameLength = IoCtlSuggested->NameLength + sizeof(MOUNTDEV_SUGGESTED_LINK_NAME);
755 FreePool(IoCtlSuggested);
756
757 IoCtlSuggested = AllocatePool(NameLength);
758 if (!IoCtlSuggested)
759 {
760 Status = STATUS_INSUFFICIENT_RESOURCES;
761 goto Dereference;
762 }
763
764 /* And reask */
765 KeInitializeEvent(&Event, NotificationEvent, FALSE);
766 Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME,
767 DeviceObject,
768 NULL,
769 0,
770 IoCtlSuggested,
771 NameLength,
772 FALSE,
773 &Event,
774 &IoStatusBlock);
775 if (!Irp)
776 {
777 Status = STATUS_INSUFFICIENT_RESOURCES;
778 goto Release;
779 }
780
781 IoStackLocation = IoGetNextIrpStackLocation(Irp);
782 IoStackLocation->FileObject = FileObject;
783
784 Status = IoCallDriver(DeviceObject, Irp);
785 if (Status == STATUS_PENDING)
786 {
787 KeWaitForSingleObject(&Event, Executive, KernelMode,
788 FALSE, NULL);
789 Status = IoStatusBlock.Status;
790 }
791 }
792
793 if (!NT_SUCCESS(Status))
794 {
795 goto Release;
796 }
797
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)
803 {
804 Status = STATUS_INSUFFICIENT_RESOURCES;
805 }
806 else
807 {
808 RtlCopyMemory(SuggestedLinkName->Buffer, IoCtlSuggested->Name, IoCtlSuggested->NameLength);
809 SuggestedLinkName->Buffer[SuggestedLinkName->Length / sizeof(WCHAR)] = UNICODE_NULL;
810 }
811
812 /* Also return its priority */
813 *UseOnlyIfThereAreNoOtherLinks = IoCtlSuggested->UseOnlyIfThereAreNoOtherLinks;
814
815 Release:
816 FreePool(IoCtlSuggested);
817
818 Dereference:
819 ObDereferenceObject(DeviceObject);
820 ObDereferenceObject(FileObject);
821
822 return Status;
823 }
824
825 /*
826 * @implemented
827 */
828 BOOLEAN
829 RedirectSavedLink(IN PSAVED_LINK_INFORMATION SavedLinkInformation,
830 IN PUNICODE_STRING DosName,
831 IN PUNICODE_STRING NewLink)
832 {
833 PLIST_ENTRY NextEntry;
834 PSYMLINK_INFORMATION SymlinkInformation;
835
836 /* Find the link */
837 for (NextEntry = SavedLinkInformation->SymbolicLinksListHead.Flink;
838 NextEntry != &(SavedLinkInformation->SymbolicLinksListHead);
839 NextEntry = NextEntry->Flink)
840 {
841 SymlinkInformation = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
842
843 if (!RtlEqualUnicodeString(DosName, &(SymlinkInformation->Name), TRUE))
844 {
845 /* Delete old link */
846 GlobalDeleteSymbolicLink(DosName);
847 /* Set its new location */
848 GlobalCreateSymbolicLink(DosName, NewLink);
849
850 /* And remove it from the list (not valid any more) */
851 RemoveEntryList(&(SymlinkInformation->SymbolicLinksListEntry));
852 FreePool(SymlinkInformation->Name.Buffer);
853 FreePool(SymlinkInformation);
854
855 return TRUE;
856 }
857 }
858
859 return FALSE;
860 }
861
862 /*
863 * @implemented
864 */
865 VOID
866 DeleteSymbolicLinkNameFromMemory(IN PDEVICE_EXTENSION DeviceExtension,
867 IN PUNICODE_STRING SymbolicLink,
868 IN BOOLEAN MarkOffline)
869 {
870 PLIST_ENTRY DeviceEntry, SymbolEntry;
871 PDEVICE_INFORMATION DeviceInformation;
872 PSYMLINK_INFORMATION SymlinkInformation;
873
874 /* First of all, ensure we have devices */
875 if (IsListEmpty(&(DeviceExtension->DeviceListHead)))
876 {
877 return;
878 }
879
880 /* Then, look for the symbolic name */
881 for (DeviceEntry = DeviceExtension->DeviceListHead.Flink;
882 DeviceEntry != &(DeviceExtension->DeviceListHead);
883 DeviceEntry = DeviceEntry->Flink)
884 {
885 DeviceInformation = CONTAINING_RECORD(DeviceEntry, DEVICE_INFORMATION, DeviceListEntry);
886
887 for (SymbolEntry = DeviceInformation->SymbolicLinksListHead.Flink;
888 SymbolEntry != &(DeviceInformation->SymbolicLinksListHead);
889 SymbolEntry = SymbolEntry->Flink)
890 {
891 SymlinkInformation = CONTAINING_RECORD(SymbolEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
892
893 /* One we have found it */
894 if (RtlCompareUnicodeString(SymbolicLink, &(SymlinkInformation->Name), TRUE) == 0)
895 {
896 /* Check if caller just want it to be offline */
897 if (MarkOffline)
898 {
899 SymlinkInformation->Online = FALSE;
900 }
901 else
902 {
903 /* If not, delete it & notify */
904 SendLinkDeleted(&(DeviceInformation->SymbolicName), SymbolicLink);
905 RemoveEntryList(&(SymlinkInformation->SymbolicLinksListEntry));
906
907 FreePool(SymlinkInformation->Name.Buffer);
908 FreePool(SymlinkInformation);
909 }
910
911 /* No need to go farther */
912 return;
913 }
914 }
915 }
916
917 return;
918 }
919
920 /*
921 * @implemented
922 */
923 BOOLEAN
924 IsDriveLetter(PUNICODE_STRING SymbolicName)
925 {
926 WCHAR Letter, Colon;
927
928 /* We must have a precise length */
929 if (SymbolicName->Length != DosDevices.Length + 2 * sizeof(WCHAR))
930 {
931 return FALSE;
932 }
933
934 /* Must start with the DosDevices prefix */
935 if (!RtlPrefixUnicodeString(&DosDevices, SymbolicName, TRUE))
936 {
937 return FALSE;
938 }
939
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)
943 {
944 return FALSE;
945 }
946
947 /* And finally it must end with a colon */
948 Colon = SymbolicName->Buffer[DosDevices.Length / sizeof(WCHAR) + 1];
949 if (Colon != L':')
950 {
951 return FALSE;
952 }
953
954 return TRUE;
955 }
956
957 /*
958 * @implemented
959 */
960 NTSTATUS
961 MountMgrQuerySymbolicLink(IN PUNICODE_STRING SymbolicName,
962 IN OUT PUNICODE_STRING LinkTarget)
963 {
964 NTSTATUS Status;
965 HANDLE LinkHandle;
966 OBJECT_ATTRIBUTES ObjectAttributes;
967
968 /* Open the symbolic link */
969 InitializeObjectAttributes(&ObjectAttributes,
970 SymbolicName,
971 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
972 NULL,
973 NULL);
974
975 Status = ZwOpenSymbolicLinkObject(&LinkHandle,
976 GENERIC_READ,
977 &ObjectAttributes);
978 if (!NT_SUCCESS(Status))
979 {
980 return Status;
981 }
982
983 /* Query its target */
984 Status = ZwQuerySymbolicLinkObject(LinkHandle,
985 LinkTarget,
986 NULL);
987
988 ZwClose(LinkHandle);
989
990 if (!NT_SUCCESS(Status))
991 {
992 return Status;
993 }
994
995 if (LinkTarget->Length <= sizeof(WCHAR))
996 {
997 return Status;
998 }
999
1000 /* If it's not finished by \, just return */
1001 if (LinkTarget->Buffer[LinkTarget->Length / sizeof(WCHAR) - 1] != L'\\')
1002 {
1003 return Status;
1004 }
1005
1006 /* Otherwise, ensure to drop the tailing \ */
1007 LinkTarget->Length -= sizeof(WCHAR);
1008 LinkTarget->Buffer[LinkTarget->Length / sizeof(WCHAR)] = UNICODE_NULL;
1009
1010 return Status;
1011 }