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