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