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