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