[MOUNTMGR]
[reactos.git] / reactos / drivers / filters / mountmgr / device.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/device.c
22 * PURPOSE: Mount Manager - Device Control
23 * PROGRAMMER: Pierre Schweitzer (pierre.schweitzer@reactos.org)
24 */
25
26 #include "mntmgr.h"
27
28 #define MAX_DEVICES 0x3E8 /* Matches 1000 devices */
29
30 #define NDEBUG
31 #include <debug.h>
32
33 /*
34 * @implemented
35 */
36 NTSTATUS
37 MountMgrChangeNotify(IN PDEVICE_EXTENSION DeviceExtension,
38 IN PIRP Irp)
39 {
40 KIRQL OldIrql;
41 NTSTATUS Status;
42 PIO_STACK_LOCATION Stack;
43 PMOUNTMGR_CHANGE_NOTIFY_INFO ChangeNotify;
44
45 /* Get the I/O buffer */
46 Stack = IoGetCurrentIrpStackLocation(Irp);
47 ChangeNotify = (PMOUNTMGR_CHANGE_NOTIFY_INFO)Irp->AssociatedIrp.SystemBuffer;
48
49 /* Validate it */
50 if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTMGR_CHANGE_NOTIFY_INFO) ||
51 Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_CHANGE_NOTIFY_INFO))
52 {
53 return STATUS_INVALID_PARAMETER;
54 }
55
56 /* If epic number doesn't match, just return now one */
57 if (DeviceExtension->EpicNumber != ChangeNotify->EpicNumber)
58 {
59 ChangeNotify->EpicNumber = DeviceExtension->EpicNumber;
60 Irp->IoStatus.Information = 0;
61 return STATUS_SUCCESS;
62 }
63
64 /* If IRP is to be canceled, forget about that */
65 IoAcquireCancelSpinLock(&OldIrql);
66 if (Irp->Cancel)
67 {
68 Status = STATUS_CANCELLED;
69 }
70 /* Otherwise queue the IRP to be notified with the next epic number change */
71 else
72 {
73 InsertTailList(&(DeviceExtension->IrpListHead), &(Irp->Tail.Overlay.ListEntry));
74 IoMarkIrpPending(Irp);
75 IoSetCancelRoutine(Irp, MountMgrCancel);
76 Status = STATUS_PENDING;
77 }
78 IoReleaseCancelSpinLock(OldIrql);
79
80 return Status;
81 }
82
83 /*
84 * @implemented
85 */
86 NTSTATUS
87 MountmgrWriteNoAutoMount(IN PDEVICE_EXTENSION DeviceExtension)
88 {
89 ULONG Value = DeviceExtension->NoAutoMount;
90
91 return RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
92 DeviceExtension->RegistryPath.Buffer,
93 L"NoAutoMount",
94 REG_DWORD,
95 &Value,
96 sizeof(Value));
97
98 }
99
100 /*
101 * @implemented
102 */
103 NTSTATUS
104 MountMgrSetAutoMount(IN PDEVICE_EXTENSION DeviceExtension,
105 IN PIRP Irp)
106 {
107 PIO_STACK_LOCATION Stack;
108 PMOUNTMGR_SET_AUTO_MOUNT SetState;
109
110 Stack = IoGetCurrentIrpStackLocation(Irp);
111
112 if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_SET_AUTO_MOUNT))
113 {
114 Irp->IoStatus.Information = 0;
115 return STATUS_INVALID_PARAMETER;
116 }
117
118 /* Only change if there's a real difference */
119 SetState = (PMOUNTMGR_SET_AUTO_MOUNT)Irp->AssociatedIrp.SystemBuffer;
120 if (SetState->NewState == !DeviceExtension->NoAutoMount)
121 {
122 Irp->IoStatus.Information = 0;
123 return STATUS_SUCCESS;
124 }
125
126 /* Set new state; ! on purpose */
127 DeviceExtension->NoAutoMount = !SetState->NewState;
128 Irp->IoStatus.Information = 0;
129 return MountmgrWriteNoAutoMount(DeviceExtension);
130 }
131
132 /*
133 * @implemented
134 */
135 NTSTATUS
136 MountMgrQueryAutoMount(IN PDEVICE_EXTENSION DeviceExtension,
137 IN PIRP Irp)
138 {
139 PIO_STACK_LOCATION Stack;
140 PMOUNTMGR_QUERY_AUTO_MOUNT QueryState;
141
142 Stack = IoGetCurrentIrpStackLocation(Irp);
143
144 if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTMGR_QUERY_AUTO_MOUNT))
145 {
146 Irp->IoStatus.Information = 0;
147 return STATUS_INVALID_PARAMETER;
148 }
149
150 QueryState = (PMOUNTMGR_QUERY_AUTO_MOUNT)Irp->AssociatedIrp.SystemBuffer;
151 QueryState->CurrentState = !DeviceExtension->NoAutoMount;
152 Irp->IoStatus.Information = sizeof(MOUNTMGR_QUERY_AUTO_MOUNT);
153
154 return STATUS_SUCCESS;
155 }
156
157 /*
158 * @implemented
159 */
160 NTSTATUS
161 NTAPI
162 ScrubRegistryRoutine(IN PWSTR ValueName,
163 IN ULONG ValueType,
164 IN PVOID ValueData,
165 IN ULONG ValueLength,
166 IN PVOID Context,
167 IN PVOID EntryContext)
168 {
169 NTSTATUS Status;
170 PLIST_ENTRY NextEntry;
171 PDEVICE_INFORMATION DeviceInfo;
172 PBOOLEAN Continue = EntryContext;
173 PDEVICE_EXTENSION DeviceExtension = Context;
174
175 if (ValueType != REG_BINARY)
176 {
177 return STATUS_SUCCESS;
178 }
179
180 /* Delete values for devices that don't have the matching unique ID */
181 if (!IsListEmpty(&(DeviceExtension->DeviceListHead)))
182 {
183 for (NextEntry = DeviceExtension->DeviceListHead.Flink;
184 NextEntry != &(DeviceExtension->DeviceListHead);
185 NextEntry = NextEntry->Flink)
186 {
187 DeviceInfo = CONTAINING_RECORD(NextEntry,
188 DEVICE_INFORMATION,
189 DeviceListEntry);
190
191 if (!DeviceInfo->UniqueId || DeviceInfo->UniqueId->UniqueIdLength != ValueLength)
192 {
193 continue;
194 }
195
196 if (RtlCompareMemory(DeviceInfo->UniqueId->UniqueId, ValueData, ValueLength) == ValueLength)
197 {
198 return STATUS_SUCCESS;
199 }
200 }
201 }
202
203 /* Wrong unique ID, scrub it */
204 Status = RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
205 DatabasePath,
206 ValueName);
207 if (!NT_SUCCESS(Status))
208 {
209 *Continue = TRUE;
210 return STATUS_UNSUCCESSFUL;
211 }
212
213 *Continue = FALSE;
214 return Status;
215 }
216
217 /*
218 * @implemented
219 */
220 NTSTATUS
221 MountMgrScrubRegistry(IN PDEVICE_EXTENSION DeviceExtension)
222 {
223 NTSTATUS Status;
224 BOOLEAN Continue;
225 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
226
227 do
228 {
229 RtlZeroMemory(QueryTable, sizeof(QueryTable));
230 QueryTable[0].QueryRoutine = ScrubRegistryRoutine;
231 QueryTable[0].EntryContext = &Continue;
232 Continue = FALSE;
233
234 Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
235 DatabasePath,
236 QueryTable,
237 DeviceExtension,
238 NULL);
239 }
240 while (Continue);
241
242 return Status;
243 }
244
245 /*
246 * @implemented
247 */
248 NTSTATUS
249 MountMgrCreatePoint(IN PDEVICE_EXTENSION DeviceExtension,
250 IN PIRP Irp)
251 {
252 ULONG MaxLength;
253 PIO_STACK_LOCATION Stack;
254 PMOUNTMGR_CREATE_POINT_INPUT Point;
255 UNICODE_STRING DeviceName, SymbolicName;
256
257 Stack = IoGetCurrentIrpStackLocation(Irp);
258
259 if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_CREATE_POINT_INPUT))
260 {
261 return STATUS_INVALID_PARAMETER;
262 }
263
264 Point = (PMOUNTMGR_CREATE_POINT_INPUT)Irp->AssociatedIrp.SystemBuffer;
265
266 MaxLength = MAX((Point->DeviceNameOffset + Point->DeviceNameLength),
267 (Point->SymbolicLinkNameLength + Point->SymbolicLinkNameOffset));
268 if (MaxLength >= Stack->Parameters.DeviceIoControl.InputBufferLength)
269 {
270 return STATUS_INVALID_PARAMETER;
271 }
272
273 /* Get all the strings and call the worker */
274 SymbolicName.Length = Point->SymbolicLinkNameLength;
275 SymbolicName.MaximumLength = Point->SymbolicLinkNameLength;
276 DeviceName.Length = Point->DeviceNameLength;
277 DeviceName.MaximumLength = Point->DeviceNameLength;
278 SymbolicName.Buffer = (PVOID)((ULONG_PTR)Point + Point->SymbolicLinkNameOffset);
279 DeviceName.Buffer = (PVOID)((ULONG_PTR)Point + Point->DeviceNameOffset);
280
281 return MountMgrCreatePointWorker(DeviceExtension, &SymbolicName, &DeviceName);
282 }
283
284 /*
285 * @implemented
286 */
287 NTSTATUS
288 MountMgrCheckUnprocessedVolumes(IN PDEVICE_EXTENSION DeviceExtension,
289 IN PIRP Irp)
290 {
291 PLIST_ENTRY NextEntry;
292 PDEVICE_INFORMATION DeviceInformation;
293 NTSTATUS ArrivalStatus, Status = STATUS_SUCCESS;
294
295 UNREFERENCED_PARAMETER(Irp);
296
297 /* No offline volumes, nothing more to do */
298 if (IsListEmpty(&(DeviceExtension->OfflineDeviceListHead)))
299 {
300 KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
301 return STATUS_SUCCESS;
302 }
303
304 KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
305
306 /* Reactivate all the offline volumes */
307 while (!IsListEmpty(&(DeviceExtension->OfflineDeviceListHead)))
308 {
309 NextEntry = RemoveHeadList(&(DeviceExtension->OfflineDeviceListHead));
310 DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
311
312 ArrivalStatus = MountMgrMountedDeviceArrival(DeviceExtension,
313 &(DeviceInformation->SymbolicName),
314 DeviceInformation->ManuallyRegistered);
315 /* Then, remove them dead information */
316 MountMgrFreeDeadDeviceInfo(DeviceInformation);
317
318 if (NT_SUCCESS(Status))
319 {
320 Status = ArrivalStatus;
321 }
322 }
323
324 return Status;
325 }
326
327 /*
328 * @implemented
329 */
330 BOOLEAN
331 IsFtVolume(IN PUNICODE_STRING SymbolicName)
332 {
333 PIRP Irp;
334 KEVENT Event;
335 NTSTATUS Status;
336 PFILE_OBJECT FileObject;
337 IO_STATUS_BLOCK IoStatusBlock;
338 PARTITION_INFORMATION PartitionInfo;
339 PDEVICE_OBJECT DeviceObject, FileDeviceObject;
340
341 /* Get device object */
342 Status = IoGetDeviceObjectPointer(SymbolicName,
343 FILE_READ_ATTRIBUTES,
344 &FileObject,
345 &DeviceObject);
346 if (!NT_SUCCESS(Status))
347 {
348 return FALSE;
349 }
350
351 /* Get attached device */
352 FileDeviceObject = FileObject->DeviceObject;
353 DeviceObject = IoGetAttachedDeviceReference(FileDeviceObject);
354
355 /* FT volume can't be removable */
356 if (FileDeviceObject->Characteristics & FILE_REMOVABLE_MEDIA)
357 {
358 ObDereferenceObject(DeviceObject);
359 ObDereferenceObject(FileObject);
360 return FALSE;
361 }
362
363 ObDereferenceObject(FileObject);
364
365 /* Get partition information */
366 KeInitializeEvent(&Event, NotificationEvent, FALSE);
367 Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO,
368 DeviceObject,
369 NULL,
370 0,
371 &PartitionInfo,
372 sizeof(PartitionInfo),
373 FALSE,
374 &Event,
375 &IoStatusBlock);
376 if (!Irp)
377 {
378 ObDereferenceObject(DeviceObject);
379 return FALSE;
380 }
381
382 Status = IoCallDriver(DeviceObject, Irp);
383 if (Status == STATUS_PENDING)
384 {
385 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
386 Status = IoStatusBlock.Status;
387 }
388
389 ObDereferenceObject(DeviceObject);
390 if (!NT_SUCCESS(Status))
391 {
392 return FALSE;
393 }
394
395 /* Check if this is a FT volume */
396 return IsRecognizedPartition(PartitionInfo.PartitionType);
397 }
398
399 /*
400 * @implemented
401 */
402 VOID
403 ProcessSuggestedDriveLetters(IN PDEVICE_EXTENSION DeviceExtension)
404 {
405 WCHAR NameBuffer[DRIVE_LETTER_LENGTH / sizeof(WCHAR)];
406 PLIST_ENTRY NextEntry;
407 UNICODE_STRING SymbolicName;
408 PDEVICE_INFORMATION DeviceInformation;
409
410 /* No devices? Nothing to do! */
411 if (IsListEmpty(&(DeviceExtension->DeviceListHead)))
412 {
413 return;
414 }
415
416 /* For all the devices */
417 for (NextEntry = DeviceExtension->DeviceListHead.Flink;
418 NextEntry != &(DeviceExtension->DeviceListHead);
419 NextEntry = NextEntry->Flink)
420 {
421 DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
422
423 /* If no drive letter */
424 if (DeviceInformation->SuggestedDriveLetter == (UCHAR)-1)
425 {
426 /* Ensure it has no entry yet */
427 if (!HasDriveLetter(DeviceInformation) &&
428 !HasNoDriveLetterEntry(DeviceInformation->UniqueId))
429 {
430 /* And create one */
431 CreateNoDriveLetterEntry(DeviceInformation->UniqueId);
432 }
433
434 DeviceInformation->SuggestedDriveLetter = 0;
435 }
436 /* Suggested letter & no entry */
437 else if (DeviceInformation->SuggestedDriveLetter &&
438 !HasNoDriveLetterEntry(DeviceInformation->UniqueId))
439 {
440 /* Just create a mount point */
441 SymbolicName.Buffer = NameBuffer;
442 RtlCopyMemory(NameBuffer, DosDevices.Buffer, DosDevices.Length);
443 NameBuffer[LETTER_POSITION] = DeviceInformation->SuggestedDriveLetter;
444 NameBuffer[COLON_POSITION] = L':';
445 SymbolicName.Length =
446 SymbolicName.MaximumLength = DRIVE_LETTER_LENGTH;
447
448 MountMgrCreatePointWorker(DeviceExtension, &SymbolicName, &(DeviceInformation->DeviceName));
449 }
450 }
451 }
452
453 /*
454 * @implemented
455 */
456 NTSTATUS
457 MountMgrNextDriveLetterWorker(IN PDEVICE_EXTENSION DeviceExtension,
458 IN PUNICODE_STRING DeviceName,
459 OUT PMOUNTMGR_DRIVE_LETTER_INFORMATION DriveLetterInfo)
460 {
461 NTSTATUS Status;
462 UCHAR DriveLetter;
463 PLIST_ENTRY NextEntry;
464 PMOUNTDEV_UNIQUE_ID UniqueId;
465 BOOLEAN Removable, GptDriveLetter;
466 PDEVICE_INFORMATION DeviceInformation;
467 WCHAR NameBuffer[DRIVE_LETTER_LENGTH];
468 PSYMLINK_INFORMATION SymlinkInformation;
469 UNICODE_STRING TargetDeviceName, SymbolicName;
470
471 /* First, process suggested letters */
472 if (!DeviceExtension->ProcessedSuggestions)
473 {
474 ProcessSuggestedDriveLetters(DeviceExtension);
475 DeviceExtension->ProcessedSuggestions = TRUE;
476 }
477
478 /* Then, get information about the device */
479 Status = QueryDeviceInformation(DeviceName, &TargetDeviceName, NULL, &Removable, &GptDriveLetter, NULL, NULL, NULL);
480 if (!NT_SUCCESS(Status))
481 {
482 return Status;
483 }
484
485 /* Ensure we have such device */
486 NextEntry = DeviceExtension->DeviceListHead.Flink;
487 while (NextEntry != &(DeviceExtension->DeviceListHead))
488 {
489 DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
490
491 if (RtlCompareUnicodeString(&(DeviceInformation->DeviceName), &TargetDeviceName, TRUE) == 0)
492 {
493 break;
494 }
495
496 NextEntry = NextEntry->Flink;
497 }
498
499 if (NextEntry == &(DeviceExtension->DeviceListHead))
500 {
501 FreePool(TargetDeviceName.Buffer);
502 return STATUS_OBJECT_NAME_NOT_FOUND;
503 }
504
505 /* Now, mark we have assigned a letter (assumption) */
506 DeviceInformation->LetterAssigned =
507 DriveLetterInfo->DriveLetterWasAssigned = TRUE;
508
509 /* Browse all the symlink to see if there's already a drive letter */
510 NextEntry = DeviceInformation->SymbolicLinksListHead.Flink;
511 while (NextEntry != &(DeviceInformation->SymbolicLinksListHead))
512 {
513 SymlinkInformation = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
514
515 /* This is a driver letter & online one, forget about new drive eltter */
516 if (IsDriveLetter(&(SymlinkInformation->Name)) && SymlinkInformation->Online)
517 {
518 DriveLetterInfo->DriveLetterWasAssigned = FALSE;
519 DriveLetterInfo->CurrentDriveLetter = (CHAR)SymlinkInformation->Name.Buffer[LETTER_POSITION];
520 break;
521 }
522
523 NextEntry = NextEntry->Flink;
524 }
525
526 /* If we didn't find a drive letter online
527 * ensure there's no GPT drive letter nor no drive entry
528 */
529 if (NextEntry == &(DeviceInformation->SymbolicLinksListHead))
530 {
531 if (GptDriveLetter || HasNoDriveLetterEntry(DeviceInformation->UniqueId))
532 {
533 DriveLetterInfo->DriveLetterWasAssigned = FALSE;
534 DriveLetterInfo->CurrentDriveLetter = 0;
535
536 goto Release;
537 }
538 }
539
540 /* No, ensure that the device is not automonted nor removable */
541 if (!DeviceExtension->NoAutoMount && !Removable)
542 {
543 if (DriveLetterInfo->DriveLetterWasAssigned)
544 {
545 DriveLetterInfo->DriveLetterWasAssigned = FALSE;
546 DriveLetterInfo->CurrentDriveLetter = 0;
547
548 goto Release;
549 }
550 }
551
552 if (!DriveLetterInfo->DriveLetterWasAssigned)
553 {
554 goto Release;
555 }
556
557 /* Now everything is fine, start processing */
558
559 if (RtlPrefixUnicodeString(&DeviceFloppy, &TargetDeviceName, TRUE))
560 {
561 /* If the device is a floppy, start with letter A */
562 DriveLetter = 'A';
563 }
564 else if (RtlPrefixUnicodeString(&DeviceCdRom, &TargetDeviceName, TRUE))
565 {
566 /* If the device is a CD-ROM, start with letter D */
567 DriveLetter = 'D';
568 }
569 else
570 {
571 /* Finally, if it's a disk, use C */
572 DriveLetter = 'C';
573 }
574
575 /* We cannot set NO drive letter */
576 ASSERT(DeviceInformation->SuggestedDriveLetter != (UCHAR)-1);
577
578 /* If we don't have suggested letter but it's a FT volume, fail */
579 if (!DeviceInformation->SuggestedDriveLetter && IsFtVolume(&(DeviceInformation->DeviceName)))
580 {
581 DriveLetterInfo->DriveLetterWasAssigned = FALSE;
582 DriveLetterInfo->CurrentDriveLetter = 0;
583
584 goto Release;
585 }
586
587 /* Prepare buffer */
588 RtlCopyMemory(NameBuffer, DosDevices.Buffer, DosDevices.Length);
589 NameBuffer[COLON_POSITION] = L':';
590 SymbolicName.Buffer = NameBuffer;
591 SymbolicName.Length =
592 SymbolicName.MaximumLength = DRIVE_LETTER_LENGTH;
593
594 /* It's all prepared, create mount point */
595 if (DeviceInformation->SuggestedDriveLetter)
596 {
597 DriveLetterInfo->CurrentDriveLetter = DeviceInformation->SuggestedDriveLetter;
598 NameBuffer[LETTER_POSITION] = DeviceInformation->SuggestedDriveLetter;
599
600 Status = MountMgrCreatePointWorker(DeviceExtension, &SymbolicName, &TargetDeviceName);
601 if (NT_SUCCESS(Status))
602 {
603 goto Release;
604 }
605 }
606
607 /* It failed with this letter... Try another one! */
608 for (DriveLetterInfo->CurrentDriveLetter = DriveLetter;
609 DriveLetterInfo->CurrentDriveLetter <= L'Z';
610 DriveLetterInfo->CurrentDriveLetter++)
611 {
612 NameBuffer[LETTER_POSITION] = DeviceInformation->SuggestedDriveLetter;
613
614 Status = MountMgrCreatePointWorker(DeviceExtension, &SymbolicName, &TargetDeviceName);
615 if (NT_SUCCESS(Status))
616 {
617 break;
618 }
619 }
620
621 /* We failed setting a letter */
622 if (DriveLetterInfo->CurrentDriveLetter > L'Z')
623 {
624 DriveLetterInfo->DriveLetterWasAssigned = FALSE;
625 DriveLetterInfo->CurrentDriveLetter = 0;
626
627 /* Try at least to add a no drive letter entry */
628 Status = QueryDeviceInformation(&TargetDeviceName, NULL, &UniqueId, NULL, NULL, NULL, NULL, NULL);
629 if (NT_SUCCESS(Status))
630 {
631 CreateNoDriveLetterEntry(UniqueId);
632 FreePool(UniqueId);
633 }
634 }
635
636 Release:
637 FreePool(TargetDeviceName.Buffer);
638
639 return STATUS_SUCCESS;
640 }
641
642
643 /*
644 * @implemented
645 */
646 NTSTATUS
647 MountMgrNextDriveLetter(IN PDEVICE_EXTENSION DeviceExtension,
648 IN PIRP Irp)
649 {
650 NTSTATUS Status;
651 PIO_STACK_LOCATION Stack;
652 UNICODE_STRING DeviceName;
653 PMOUNTMGR_DRIVE_LETTER_TARGET DriveLetterTarget;
654 MOUNTMGR_DRIVE_LETTER_INFORMATION DriveLetterInformation;
655
656 Stack = IoGetCurrentIrpStackLocation(Irp);
657
658 /* Validate input */
659 if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_DRIVE_LETTER_TARGET) ||
660 Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION))
661 {
662 return STATUS_INVALID_PARAMETER;
663 }
664
665 DriveLetterTarget = (PMOUNTMGR_DRIVE_LETTER_TARGET)Irp->AssociatedIrp.SystemBuffer;
666 if (DriveLetterTarget->DeviceNameLength + sizeof(USHORT) > Stack->Parameters.DeviceIoControl.InputBufferLength)
667 {
668 return STATUS_INVALID_PARAMETER;
669 }
670
671 /* Call the worker */
672 DeviceName.Buffer = DriveLetterTarget->DeviceName;
673 DeviceName.Length =
674 DeviceName.MaximumLength = DriveLetterTarget->DeviceNameLength;
675
676 Status = MountMgrNextDriveLetterWorker(DeviceExtension, &DeviceName,
677 &DriveLetterInformation);
678 if (NT_SUCCESS(Status))
679 {
680 *(PMOUNTMGR_DRIVE_LETTER_INFORMATION)Irp->AssociatedIrp.SystemBuffer =
681 DriveLetterInformation;
682 Irp->IoStatus.Information = sizeof(MOUNTMGR_DRIVE_LETTER_INFORMATION);
683 }
684
685 return Status;
686 }
687
688 /*
689 * @implemented
690 */
691 NTSTATUS
692 NTAPI
693 MountMgrQuerySystemVolumeNameQueryRoutine(IN PWSTR ValueName,
694 IN ULONG ValueType,
695 IN PVOID ValueData,
696 IN ULONG ValueLength,
697 IN PVOID Context,
698 IN PVOID EntryContext)
699 {
700 UNICODE_STRING ValueString;
701 PUNICODE_STRING SystemVolumeName;
702
703 UNREFERENCED_PARAMETER(ValueName);
704 UNREFERENCED_PARAMETER(ValueLength);
705 UNREFERENCED_PARAMETER(EntryContext);
706
707 if (ValueType != REG_SZ)
708 {
709 return STATUS_SUCCESS;
710 }
711
712 RtlInitUnicodeString(&ValueString, ValueData);
713 SystemVolumeName = Context;
714
715 /* Return a string containing system volume name */
716 SystemVolumeName->Length = ValueString.Length;
717 SystemVolumeName->MaximumLength = ValueString.Length + sizeof(WCHAR);
718 SystemVolumeName->Buffer = AllocatePool(SystemVolumeName->MaximumLength);
719 if (SystemVolumeName->Buffer)
720 {
721 RtlCopyMemory(SystemVolumeName->Buffer, ValueData, ValueString.Length);
722 SystemVolumeName->Buffer[ValueString.Length / sizeof(WCHAR)] = UNICODE_NULL;
723 }
724
725 return STATUS_SUCCESS;
726
727 }
728
729 /*
730 * @implemented
731 */
732 NTSTATUS
733 MountMgrQuerySystemVolumeName(OUT PUNICODE_STRING SystemVolumeName)
734 {
735 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
736
737 RtlZeroMemory(QueryTable, sizeof(QueryTable));
738 QueryTable[0].QueryRoutine = MountMgrQuerySystemVolumeNameQueryRoutine;
739 QueryTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED;
740 QueryTable[0].Name = L"SystemPartition";
741
742 SystemVolumeName->Buffer = NULL;
743
744 RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
745 L"\\Registry\\Machine\\System\\Setup",
746 QueryTable,
747 SystemVolumeName,
748 NULL);
749
750 if (SystemVolumeName->Buffer)
751 {
752 return STATUS_SUCCESS;
753 }
754
755 return STATUS_UNSUCCESSFUL;
756 }
757
758 /*
759 * @implemented
760 */
761 VOID
762 MountMgrAssignDriveLetters(IN PDEVICE_EXTENSION DeviceExtension)
763 {
764 NTSTATUS Status;
765 PLIST_ENTRY NextEntry;
766 UNICODE_STRING SystemVolumeName;
767 PDEVICE_INFORMATION DeviceInformation;
768 MOUNTMGR_DRIVE_LETTER_INFORMATION DriveLetterInformation;
769
770 /* First, get system volume name */
771 Status = MountMgrQuerySystemVolumeName(&SystemVolumeName);
772
773 /* If there are no device, it's all done */
774 if (IsListEmpty(&(DeviceExtension->DeviceListHead)))
775 {
776 if (NT_SUCCESS(Status))
777 {
778 FreePool(SystemVolumeName.Buffer);
779 }
780
781 return;
782 }
783
784 /* Now, for all the devices... */
785 for (NextEntry = DeviceExtension->DeviceListHead.Flink;
786 NextEntry != &(DeviceExtension->DeviceListHead);
787 NextEntry = NextEntry->Flink)
788 {
789 DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
790
791 /* If the device doesn't have a letter assigned, do it! */
792 if (!DeviceInformation->LetterAssigned)
793 {
794 MountMgrNextDriveLetterWorker(DeviceExtension,
795 &(DeviceInformation->DeviceName),
796 &DriveLetterInformation);
797 }
798
799 /* If it was the system volume */
800 if (NT_SUCCESS(Status) && RtlEqualUnicodeString(&SystemVolumeName, &(DeviceInformation->DeviceName), TRUE))
801 {
802 /* Keep track of it */
803 DeviceExtension->DriveLetterData = AllocatePool(DeviceInformation->UniqueId->UniqueIdLength +
804 sizeof(MOUNTDEV_UNIQUE_ID));
805 if (DeviceExtension->DriveLetterData)
806 {
807 RtlCopyMemory(DeviceExtension->DriveLetterData,
808 DeviceInformation->UniqueId,
809 DeviceInformation->UniqueId->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
810 }
811
812 /* If it was not automount, ensure it gets mounted */
813 if (!DeviceExtension->NoAutoMount)
814 {
815 DeviceExtension->NoAutoMount = TRUE;
816
817 MountMgrNextDriveLetterWorker(DeviceExtension,
818 &(DeviceInformation->DeviceName),
819 &DriveLetterInformation);
820
821 DeviceExtension->NoAutoMount = FALSE;
822 }
823 }
824 }
825
826 if (NT_SUCCESS(Status))
827 {
828 FreePool(SystemVolumeName.Buffer);
829 }
830 }
831
832 /*
833 * @implemented
834 */
835 NTSTATUS
836 MountMgrQueryDosVolumePath(IN PDEVICE_EXTENSION DeviceExtension,
837 IN PIRP Irp)
838 {
839 NTSTATUS Status;
840 ULONG DevicesFound;
841 PIO_STACK_LOCATION Stack;
842 PLIST_ENTRY SymlinksEntry;
843 UNICODE_STRING SymbolicName;
844 PMOUNTMGR_TARGET_NAME Target;
845 PWSTR DeviceString, OldBuffer;
846 USHORT DeviceLength, OldLength;
847 PDEVICE_INFORMATION DeviceInformation;
848 PSYMLINK_INFORMATION SymlinkInformation;
849 PASSOCIATED_DEVICE_ENTRY AssociatedDevice;
850
851 Stack = IoGetCurrentIrpStackLocation(Irp);
852
853 /* Validate input size */
854 if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_TARGET_NAME))
855 {
856 return STATUS_INVALID_PARAMETER;
857 }
858
859 /* Ensure we have received UNICODE_STRING */
860 Target = (PMOUNTMGR_TARGET_NAME)Irp->AssociatedIrp.SystemBuffer;
861 if (Target->DeviceNameLength & 1)
862 {
863 return STATUS_INVALID_PARAMETER;
864 }
865
866 /* Validate the entry structure size */
867 if (Target->DeviceNameLength + sizeof(UNICODE_NULL) > Stack->Parameters.DeviceIoControl.InputBufferLength)
868 {
869 return STATUS_INVALID_PARAMETER;
870 }
871
872 /* Ensure we can at least return needed size */
873 if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))
874 {
875 return STATUS_INVALID_PARAMETER;
876 }
877
878 /* Construct string for query */
879 SymbolicName.Length = Target->DeviceNameLength;
880 SymbolicName.MaximumLength = Target->DeviceNameLength + sizeof(UNICODE_NULL);
881 SymbolicName.Buffer = Target->DeviceName;
882
883 /* Find device with our info */
884 Status = FindDeviceInfo(DeviceExtension, &SymbolicName, FALSE, &DeviceInformation);
885 if (!NT_SUCCESS(Status))
886 {
887 return Status;
888 }
889
890 DeviceLength = 0;
891 DeviceString = NULL;
892 DevicesFound = 0;
893
894 /* Try to find associated device info */
895 while (TRUE)
896 {
897 for (SymlinksEntry = DeviceInformation->SymbolicLinksListHead.Flink;
898 SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead);
899 SymlinksEntry = SymlinksEntry->Flink)
900 {
901 SymlinkInformation = CONTAINING_RECORD(SymlinksEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
902
903 /* Try to find with drive letter */
904 if (MOUNTMGR_IS_DRIVE_LETTER(&SymlinkInformation->Name) && SymlinkInformation->Online)
905 {
906 break;
907 }
908 }
909
910 /* We didn't find, break */
911 if (SymlinksEntry == &(DeviceInformation->SymbolicLinksListHead))
912 {
913 break;
914 }
915
916 /* It doesn't have associated device, go to fallback method */
917 if (IsListEmpty(&DeviceInformation->AssociatedDevicesHead))
918 {
919 goto TryWithVolumeName;
920 }
921
922 /* Create a string with the information about the device */
923 AssociatedDevice = CONTAINING_RECORD(&(DeviceInformation->SymbolicLinksListHead), ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
924 OldLength = DeviceLength;
925 OldBuffer = DeviceString;
926 DeviceLength += AssociatedDevice->String.Length;
927 DeviceString = AllocatePool(DeviceLength);
928 if (!DeviceString)
929 {
930 if (OldBuffer)
931 {
932 FreePool(OldBuffer);
933 }
934
935 return STATUS_INSUFFICIENT_RESOURCES;
936 }
937
938 /* Store our info and previous if any */
939 RtlCopyMemory(DeviceString, AssociatedDevice->String.Buffer, AssociatedDevice->String.Length);
940 if (OldBuffer)
941 {
942 RtlCopyMemory(&DeviceString[AssociatedDevice->String.Length / sizeof(WCHAR)], OldBuffer, OldLength);
943 FreePool(OldBuffer);
944 }
945
946 /* Count and continue looking */
947 ++DevicesFound;
948 DeviceInformation = AssociatedDevice->DeviceInformation;
949
950 /* If too many devices, try another way */
951 if (DevicesFound > MAX_DEVICES) /* 1000 */
952 {
953 goto TryWithVolumeName;
954 }
955 }
956
957 /* Reallocate our string, so that we can prepend disk letter */
958 OldBuffer = DeviceString;
959 OldLength = DeviceLength;
960 DeviceLength += 2 * sizeof(WCHAR);
961 DeviceString = AllocatePool(DeviceLength);
962 if (!DeviceString)
963 {
964 if (OldBuffer)
965 {
966 FreePool(OldBuffer);
967 }
968
969 return STATUS_INSUFFICIENT_RESOURCES;
970 }
971
972 /* Get the letter */
973 DeviceString[0] = SymlinkInformation->Name.Buffer[LETTER_POSITION];
974 DeviceString[1] = L':';
975
976 /* And copy the rest */
977 if (OldBuffer)
978 {
979 RtlCopyMemory(&DeviceString[2], OldBuffer, OldLength);
980 FreePool(OldBuffer);
981 }
982
983 TryWithVolumeName:
984 /* If we didn't find anything, try differently */
985 if (DeviceLength < 2 * sizeof(WCHAR) || DeviceString[1] != L':')
986 {
987 if (DeviceString)
988 {
989 FreePool(DeviceString);
990 DeviceLength = 0;
991 }
992
993 /* Try to find a volume name matching */
994 for (SymlinksEntry = DeviceInformation->SymbolicLinksListHead.Flink;
995 SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead);
996 SymlinksEntry = SymlinksEntry->Flink)
997 {
998 SymlinkInformation = CONTAINING_RECORD(SymlinksEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
999
1000 if (MOUNTMGR_IS_VOLUME_NAME(&SymlinkInformation->Name))
1001 {
1002 break;
1003 }
1004 }
1005
1006 /* If found copy */
1007 if (SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead))
1008 {
1009 DeviceLength = SymlinkInformation->Name.Length;
1010 DeviceString = AllocatePool(DeviceLength);
1011 if (!DeviceString)
1012 {
1013 return STATUS_INSUFFICIENT_RESOURCES;
1014 }
1015
1016 RtlCopyMemory(DeviceString, SymlinkInformation->Name.Buffer, DeviceLength);
1017 /* Ensure we are in the right namespace; [1] can be ? */
1018 DeviceString[1] = L'\\';
1019 }
1020 }
1021
1022 /* If we found something */
1023 if (DeviceString)
1024 {
1025 /* At least, we will return our length */
1026 ((PMOUNTMGR_VOLUME_PATHS)Irp->AssociatedIrp.SystemBuffer)->MultiSzLength = DeviceLength;
1027 /* MOUNTMGR_VOLUME_PATHS is a string + a ULONG */
1028 Irp->IoStatus.Information = DeviceLength + sizeof(ULONG);
1029
1030 /* If we have enough room for copying the string */
1031 if (sizeof(ULONG) + DeviceLength <= Stack->Parameters.DeviceIoControl.OutputBufferLength)
1032 {
1033 /* Copy it */
1034 if (DeviceLength)
1035 {
1036 RtlCopyMemory(((PMOUNTMGR_VOLUME_PATHS)Irp->AssociatedIrp.SystemBuffer)->MultiSz, DeviceString, DeviceLength);
1037 }
1038
1039 /* And double zero at its end - this is needed in case of multiple paths which are separated by a single 0 */
1040 FreePool(DeviceString);
1041 ((PMOUNTMGR_VOLUME_PATHS)Irp->AssociatedIrp.SystemBuffer)->MultiSz[DeviceLength / sizeof(WCHAR)] = 0;
1042 ((PMOUNTMGR_VOLUME_PATHS)Irp->AssociatedIrp.SystemBuffer)->MultiSz[DeviceLength / sizeof(WCHAR) + 1] = 0;
1043
1044 return STATUS_SUCCESS;
1045 }
1046 else
1047 {
1048 /* Just return appropriate size and leave */
1049 FreePool(DeviceString);
1050 Irp->IoStatus.Information = sizeof(ULONG);
1051 return STATUS_BUFFER_OVERFLOW;
1052 }
1053 }
1054
1055 /* Fail */
1056 return STATUS_NOT_FOUND;
1057 }
1058
1059 /*
1060 * @implemented
1061 */
1062 NTSTATUS
1063 MountMgrValidateBackPointer(IN PASSOCIATED_DEVICE_ENTRY AssociatedDeviceEntry,
1064 IN PDEVICE_INFORMATION DeviceInformation,
1065 OUT PBOOLEAN Invalid)
1066 {
1067 HANDLE Handle;
1068 NTSTATUS Status;
1069 PLIST_ENTRY SymlinksEntry;
1070 IO_STATUS_BLOCK IoStatusBlock;
1071 PREPARSE_DATA_BUFFER ReparseData;
1072 OBJECT_ATTRIBUTES ObjectAttributes;
1073 UNICODE_STRING FullName, SubstituteName;
1074 PSYMLINK_INFORMATION SymlinkInformation;
1075
1076 /* Initialize & allocate a string big enough to contain our complete mount point name */
1077 FullName.Length = AssociatedDeviceEntry->String.Length + AssociatedDeviceEntry->DeviceInformation->DeviceName.Length + sizeof(WCHAR);
1078 FullName.MaximumLength = FullName.Length + sizeof(UNICODE_NULL);
1079 FullName.Buffer = AllocatePool(FullName.MaximumLength);
1080 if (!FullName.Buffer)
1081 {
1082 return STATUS_INSUFFICIENT_RESOURCES;
1083 }
1084
1085 /* Create the path */
1086 RtlCopyMemory(FullName.Buffer, AssociatedDeviceEntry->DeviceInformation->DeviceName.Buffer, AssociatedDeviceEntry->DeviceInformation->DeviceName.Length);
1087 FullName.Buffer[AssociatedDeviceEntry->DeviceInformation->DeviceName.Length / sizeof(WCHAR)] = L'\\';
1088 RtlCopyMemory(&FullName.Buffer[AssociatedDeviceEntry->DeviceInformation->DeviceName.Length / sizeof(WCHAR) + 1], AssociatedDeviceEntry->String.Buffer, AssociatedDeviceEntry->String.Length);
1089 FullName.Buffer[FullName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1090
1091 /* Open it to query the reparse point */
1092 InitializeObjectAttributes(&ObjectAttributes,
1093 &FullName,
1094 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1095 NULL,
1096 NULL);
1097 Status = ZwOpenFile(&Handle,
1098 SYNCHRONIZE | FILE_READ_ATTRIBUTES,
1099 &ObjectAttributes, &IoStatusBlock,
1100 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1101 FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT);
1102 FreePool(FullName.Buffer);
1103
1104 if (!NT_SUCCESS(Status))
1105 {
1106 *Invalid = TRUE;
1107 return STATUS_SUCCESS;
1108 }
1109
1110 /* Allocate a buffer big enough to read reparse data */
1111 ReparseData = AllocatePool(0x4000);
1112 if (ReparseData == NULL)
1113 {
1114 ZwClose(Handle);
1115 return STATUS_INSUFFICIENT_RESOURCES;
1116 }
1117
1118 /* Query reparse data */
1119 Status = ZwFsControlFile(Handle,
1120 NULL, NULL, NULL,
1121 &IoStatusBlock,
1122 FSCTL_GET_REPARSE_POINT,
1123 NULL, 0,
1124 ReparseData, 0x4000);
1125 ZwClose(Handle);
1126
1127 if (!NT_SUCCESS(Status))
1128 {
1129 FreePool(ReparseData);
1130 *Invalid = TRUE;
1131 return STATUS_SUCCESS;
1132 }
1133
1134 /* Create a string with the substitute name */
1135 SubstituteName.Length = ReparseData->SymbolicLinkReparseBuffer.SubstituteNameLength;
1136 SubstituteName.MaximumLength = SubstituteName.Length;
1137 SubstituteName.Buffer = (PWSTR)((ULONG_PTR)ReparseData->SymbolicLinkReparseBuffer.PathBuffer + ReparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset);
1138
1139 /* If that's a volume name that matches our associated device, that's a success! */
1140 if (MOUNTMGR_IS_VOLUME_NAME(&SubstituteName))
1141 {
1142 if (SubstituteName.Length == 98 && SubstituteName.Buffer[1] == L'?')
1143 {
1144 for (SymlinksEntry = DeviceInformation->SymbolicLinksListHead.Flink;
1145 SymlinksEntry != &(DeviceInformation->SymbolicLinksListHead);
1146 SymlinksEntry = SymlinksEntry->Flink)
1147 {
1148 SymlinkInformation = CONTAINING_RECORD(SymlinksEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
1149
1150 if (RtlEqualUnicodeString(&SubstituteName, &SymlinkInformation->Name, TRUE))
1151 {
1152 FreePool(ReparseData);
1153 return STATUS_SUCCESS;
1154 }
1155 }
1156 }
1157 }
1158
1159 FreePool(ReparseData);
1160 *Invalid = TRUE;
1161 return STATUS_SUCCESS;
1162 }
1163
1164 NTSTATUS
1165 MountMgrQueryVolumePaths(IN PDEVICE_EXTENSION DeviceExtension,
1166 IN PDEVICE_INFORMATION DeviceInformation,
1167 IN PLIST_ENTRY DeviceInfoList,
1168 OUT PMOUNTMGR_VOLUME_PATHS * VolumePaths,
1169 OUT PDEVICE_INFORMATION *FailedDevice)
1170 {
1171 ULONG Written;
1172 NTSTATUS Status;
1173 PLIST_ENTRY Entry;
1174 PSYMLINK_INFORMATION SymlinkInformation;
1175 PDEVICE_INFORMATION_ENTRY DeviceInfoEntry;
1176 PASSOCIATED_DEVICE_ENTRY AssociatedDeviceEntry;
1177 PMOUNTMGR_VOLUME_PATHS * Paths = NULL, * CurrentPath;
1178 ULONG OutputPathLength, NumberOfPaths, ReturnedPaths;
1179
1180 /* We return at least null char */
1181 OutputPathLength = sizeof(UNICODE_NULL);
1182
1183 for (Entry = DeviceInformation->SymbolicLinksListHead.Flink;
1184 Entry != &(DeviceInformation->SymbolicLinksListHead);
1185 Entry = Entry->Flink)
1186 {
1187 SymlinkInformation = CONTAINING_RECORD(Entry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
1188
1189 /* Try to find the drive letter (ie, DOS device) */
1190 if (MOUNTMGR_IS_DRIVE_LETTER(&SymlinkInformation->Name) && SymlinkInformation->Online)
1191 {
1192 /* We'll return the letter */
1193 OutputPathLength = 4 * sizeof(WCHAR);
1194 break;
1195 }
1196 }
1197
1198 /* We didn't find any */
1199 if (Entry == &(DeviceInformation->SymbolicLinksListHead))
1200 {
1201 SymlinkInformation = NULL;
1202 }
1203
1204 /* Do we have any device info to return? */
1205 for (Entry = DeviceInfoList->Flink; Entry != DeviceInfoList; Entry = Entry->Flink)
1206 {
1207 DeviceInfoEntry = CONTAINING_RECORD(Entry, DEVICE_INFORMATION_ENTRY, DeviceInformationEntry);
1208
1209 /* Matching current device */
1210 if (DeviceInfoEntry->DeviceInformation == DeviceInformation)
1211 {
1212 /* Allocate the output buffer */
1213 *VolumePaths = AllocatePool(sizeof(ULONG) + OutputPathLength);
1214 if (*VolumePaths == NULL)
1215 {
1216 return STATUS_INSUFFICIENT_RESOURCES;
1217 }
1218
1219 /* Set size */
1220 (*VolumePaths)->MultiSzLength = OutputPathLength;
1221 /* If we have a drive letter, return it */
1222 if (SymlinkInformation != NULL)
1223 {
1224 (*VolumePaths)->MultiSz[0] = SymlinkInformation->Name.Buffer[LETTER_POSITION];
1225 (*VolumePaths)->MultiSz[1] = L':';
1226 (*VolumePaths)->MultiSz[2] = UNICODE_NULL;
1227 (*VolumePaths)->MultiSz[3] = UNICODE_NULL;
1228 }
1229 else
1230 {
1231 (*VolumePaths)->MultiSz[0] = UNICODE_NULL;
1232 }
1233
1234 return STATUS_SUCCESS;
1235 }
1236 }
1237
1238 /* Allocate a new device entry */
1239 DeviceInfoEntry = AllocatePool(sizeof(DEVICE_INFORMATION_ENTRY));
1240 if (DeviceInfoEntry == NULL)
1241 {
1242 return STATUS_INSUFFICIENT_RESOURCES;
1243 }
1244
1245 /* Add it to the list */
1246 DeviceInfoEntry->DeviceInformation = DeviceInformation;
1247 InsertTailList(DeviceInfoList, &DeviceInfoEntry->DeviceInformationEntry);
1248
1249 NumberOfPaths = 0;
1250 /* Count the amount of devices we will have to handle */
1251 if (!IsListEmpty(&DeviceInformation->AssociatedDevicesHead))
1252 {
1253 for (Entry = DeviceInformation->AssociatedDevicesHead.Flink;
1254 Entry != &DeviceInformation->AssociatedDevicesHead;
1255 Entry = Entry->Flink)
1256 {
1257 ++NumberOfPaths;
1258 }
1259
1260 ASSERT(NumberOfPaths != 0);
1261 /* And allocate a big enough buffer */
1262 Paths = AllocatePool(NumberOfPaths * sizeof(PMOUNTMGR_VOLUME_PATHS));
1263 if (Paths == NULL)
1264 {
1265 RemoveEntryList(&DeviceInfoEntry->DeviceInformationEntry);
1266 FreePool(DeviceInfoEntry);
1267 return STATUS_INSUFFICIENT_RESOURCES;
1268 }
1269 }
1270
1271 /* Start the hot loop to gather all the paths and be able to compute total output length! */
1272 ReturnedPaths = 0;
1273 CurrentPath = Paths;
1274 for (Entry = DeviceInformation->AssociatedDevicesHead.Flink;
1275 Entry != &DeviceInformation->AssociatedDevicesHead;
1276 Entry = Entry->Flink)
1277 {
1278 USHORT InnerStrings;
1279 BOOLEAN Invalid = FALSE;
1280
1281 AssociatedDeviceEntry = CONTAINING_RECORD(Entry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
1282
1283 /* Validate the fact its a mount point by query reparse data */
1284 Status = MountMgrValidateBackPointer(AssociatedDeviceEntry, DeviceInformation, &Invalid);
1285
1286 /* If we found an invalid device, that's a failure */
1287 if (Invalid)
1288 {
1289 *FailedDevice = AssociatedDeviceEntry->DeviceInformation;
1290 Status = STATUS_UNSUCCESSFUL;
1291 }
1292
1293 /* Check whether we failed, if so, bail out */
1294 if (!NT_SUCCESS(Status))
1295 {
1296 ULONG i;
1297
1298 for (i = 0; i < ReturnedPaths; ++i)
1299 {
1300 FreePool(Paths[i]);
1301 }
1302
1303 if (Paths != NULL)
1304 {
1305 FreePool(Paths);
1306 }
1307 RemoveEntryList(&DeviceInfoEntry->DeviceInformationEntry);
1308 FreePool(DeviceInfoEntry);
1309 return Status;
1310 }
1311
1312 /* Query associated paths (hello ourselves :-)) */
1313 Status = MountMgrQueryVolumePaths(DeviceExtension,
1314 AssociatedDeviceEntry->DeviceInformation,
1315 DeviceInfoList,
1316 CurrentPath,
1317 FailedDevice);
1318 if (!NT_SUCCESS(Status))
1319 {
1320 ULONG i;
1321
1322 for (i = 0; i < ReturnedPaths; ++i)
1323 {
1324 FreePool(Paths[i]);
1325 }
1326
1327 if (Paths != NULL)
1328 {
1329 FreePool(Paths);
1330 }
1331 RemoveEntryList(&DeviceInfoEntry->DeviceInformationEntry);
1332 FreePool(DeviceInfoEntry);
1333 return Status;
1334 }
1335
1336 /* Count the number of strings we have in the multi string buffer */
1337 InnerStrings = 0;
1338 if ((*CurrentPath)->MultiSzLength != sizeof(UNICODE_NULL))
1339 {
1340 ULONG i;
1341 PWSTR MultiSz = (*CurrentPath)->MultiSz;
1342
1343 for (i = 0; i < (*CurrentPath)->MultiSzLength / sizeof(WCHAR); ++i, ++MultiSz)
1344 {
1345 if (*MultiSz == UNICODE_NULL)
1346 {
1347 ++InnerStrings;
1348 }
1349 }
1350 }
1351
1352 /* We returned one more path (ie, one more allocated buffer) */
1353 ++ReturnedPaths;
1354 /* Move the next pointer to use in the array */
1355 ++CurrentPath;
1356 /* Multiply String.Length by the number of found paths, we always add it after a path */
1357 OutputPathLength += (*CurrentPath)->MultiSzLength + InnerStrings * AssociatedDeviceEntry->String.Length - sizeof(UNICODE_NULL);
1358 }
1359
1360 /* Allocate the output buffer */
1361 *VolumePaths = AllocatePool(sizeof(ULONG) + OutputPathLength);
1362 if (*VolumePaths == NULL)
1363 {
1364 ULONG i;
1365
1366 for (i = 0; i < ReturnedPaths; ++i)
1367 {
1368 FreePool(Paths[i]);
1369 }
1370
1371 if (Paths != NULL)
1372 {
1373 FreePool(Paths);
1374 }
1375 RemoveEntryList(&DeviceInfoEntry->DeviceInformationEntry);
1376 FreePool(DeviceInfoEntry);
1377 return STATUS_INSUFFICIENT_RESOURCES;
1378 }
1379
1380 Written = 0;
1381 /* If we had found a DOS letter, that's the first thing we return */
1382 (*VolumePaths)->MultiSzLength = OutputPathLength;
1383 if (SymlinkInformation != NULL)
1384 {
1385 (*VolumePaths)->MultiSz[0] = SymlinkInformation->Name.Buffer[LETTER_POSITION];
1386 (*VolumePaths)->MultiSz[1] = L':';
1387 (*VolumePaths)->MultiSz[2] = UNICODE_NULL;
1388 Written = 3;
1389 }
1390
1391 /* Now, browse again all our paths to return them */
1392 CurrentPath = Paths;
1393 for (Entry = DeviceInformation->AssociatedDevicesHead.Flink;
1394 Entry != &DeviceInformation->AssociatedDevicesHead;
1395 Entry = Entry->Flink)
1396 {
1397 AssociatedDeviceEntry = CONTAINING_RECORD(Entry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
1398
1399 /* If we had a path... */
1400 if ((*CurrentPath)->MultiSzLength != sizeof(UNICODE_NULL))
1401 {
1402 ULONG i, Offset;
1403 PWSTR MultiSz;
1404
1405 /* This offset is used to "jump" into MultiSz, so, start with the string begin (ie, skip MultiSzLength) */
1406 Offset = sizeof(ULONG);
1407 /* Browse every single letter, and skip last UNICODE_NULL */
1408 for (i = 0; i < (*CurrentPath)->MultiSzLength / sizeof(WCHAR) - 1; ++i)
1409 {
1410 /* Get the letter */
1411 MultiSz = (PWSTR)((ULONG_PTR)(*CurrentPath) + Offset);
1412 /* If it was part of the path, just return it */
1413 if (*MultiSz != UNICODE_NULL)
1414 {
1415 (*VolumePaths)->MultiSz[Written] = *MultiSz;
1416 }
1417 else
1418 {
1419 /* Otherwise, as planed, return our whole associated device name */
1420 RtlCopyMemory(&(*VolumePaths)->MultiSz[Written],
1421 AssociatedDeviceEntry->String.Buffer,
1422 AssociatedDeviceEntry->String.Length);
1423 Written += AssociatedDeviceEntry->String.Length / sizeof(WCHAR);
1424 /* And don't forget to nullify */
1425 (*VolumePaths)->MultiSz[Written] = UNICODE_NULL;
1426 }
1427
1428 /* We at least return a letter or a null char */
1429 ++Written;
1430 /* Move to the next letter */
1431 Offset += sizeof(WCHAR);
1432 }
1433 }
1434
1435 FreePool(*CurrentPath);
1436 ++CurrentPath;
1437 }
1438
1439 /* MultiSz: don't forget last null char */
1440 (*VolumePaths)->MultiSz[Written] = UNICODE_NULL;
1441 /* Cleanup everything and return success! */
1442 if (Paths != NULL)
1443 {
1444 FreePool(Paths);
1445 }
1446 RemoveEntryList(&DeviceInfoEntry->DeviceInformationEntry);
1447 FreePool(DeviceInfoEntry);
1448 return STATUS_SUCCESS;
1449 }
1450
1451 NTSTATUS
1452 MountMgrQueryDosVolumePaths(IN PDEVICE_EXTENSION DeviceExtension,
1453 IN PIRP Irp)
1454 {
1455 NTSTATUS Status;
1456 PLIST_ENTRY Entry;
1457 LIST_ENTRY Devices;
1458 BOOLEAN NeedNotification;
1459 PIO_STACK_LOCATION Stack;
1460 UNICODE_STRING SymbolicName;
1461 ULONG Attempts, OutputLength;
1462 PMOUNTMGR_TARGET_NAME Target;
1463 PMOUNTMGR_VOLUME_PATHS Paths, Output;
1464 PDEVICE_INFORMATION DeviceInformation, ListDeviceInfo, FailedDevice;
1465
1466 Stack = IoGetCurrentIrpStackLocation(Irp);
1467
1468 /* Validate input size */
1469 if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_TARGET_NAME))
1470 {
1471 return STATUS_INVALID_PARAMETER;
1472 }
1473
1474 /* Ensure we have received UNICODE_STRING */
1475 Target = (PMOUNTMGR_TARGET_NAME)Irp->AssociatedIrp.SystemBuffer;
1476 if (Target->DeviceNameLength & 1)
1477 {
1478 return STATUS_INVALID_PARAMETER;
1479 }
1480
1481 /* Validate the entry structure size */
1482 if (Target->DeviceNameLength + FIELD_OFFSET(MOUNTMGR_TARGET_NAME, DeviceName) > Stack->Parameters.DeviceIoControl.InputBufferLength)
1483 {
1484 return STATUS_INVALID_PARAMETER;
1485 }
1486
1487 /* Ensure we can at least return needed size */
1488 if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))
1489 {
1490 return STATUS_INVALID_PARAMETER;
1491 }
1492
1493 /* Construct string for query */
1494 SymbolicName.Length = Target->DeviceNameLength;
1495 SymbolicName.MaximumLength = Target->DeviceNameLength + sizeof(UNICODE_NULL);
1496 SymbolicName.Buffer = Target->DeviceName;
1497
1498 /* Find device with our info */
1499 Status = FindDeviceInfo(DeviceExtension, &SymbolicName, FALSE, &DeviceInformation);
1500 if (!NT_SUCCESS(Status))
1501 {
1502 return Status;
1503 }
1504
1505 NeedNotification = FALSE;
1506 Attempts = 0;
1507 for (;;)
1508 {
1509 FailedDevice = NULL;
1510 InitializeListHead(&Devices);
1511
1512 /* Query paths */
1513 Status = MountMgrQueryVolumePaths(DeviceExtension, DeviceInformation, &Devices, &Paths, &FailedDevice);
1514 if (NT_SUCCESS(Status))
1515 {
1516 break;
1517 }
1518
1519 /* If it failed for generic reason (memory, whatever), bail out (ie, FailedDevice not set) */
1520 if (FailedDevice == NULL)
1521 {
1522 return Status;
1523 }
1524
1525 /* If PnP, let's notify in case of success */
1526 if (!DeviceInformation->ManuallyRegistered)
1527 {
1528 NeedNotification = TRUE;
1529 }
1530
1531 /* Reconcile database */
1532 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
1533 ReconcileThisDatabaseWithMasterWorker(&DeviceExtension);
1534 KeWaitForSingleObject(&DeviceExtension->DeviceLock, Executive, KernelMode, FALSE, NULL);
1535
1536 /* Look for our device, to check it's online */
1537 for (Entry = DeviceExtension->DeviceListHead.Flink;
1538 Entry != &DeviceExtension->DeviceListHead;
1539 Entry = Entry->Flink)
1540 {
1541 ListDeviceInfo = CONTAINING_RECORD(Entry, DEVICE_INFORMATION, DeviceListEntry);
1542 /* It's online, it's OK! */
1543 if (ListDeviceInfo == DeviceInformation)
1544 {
1545 break;
1546 }
1547 }
1548
1549 /* It's not online, it's not good */
1550 if (Entry == &DeviceExtension->DeviceListHead)
1551 {
1552 return STATUS_OBJECT_NAME_NOT_FOUND;
1553 }
1554
1555 /* Increase attempts count */
1556 ++Attempts;
1557 /* Don't look forever and fail if we get out of attempts */
1558 if (Attempts >= 1000)
1559 {
1560 return Status;
1561 }
1562 }
1563
1564 /* We need to notify? Go ahead */
1565 if (NeedNotification)
1566 {
1567 MountMgrNotifyNameChange(DeviceExtension, &SymbolicName, FALSE);
1568 }
1569
1570 /* Get output buffer */
1571 Output = (PMOUNTMGR_VOLUME_PATHS)Irp->AssociatedIrp.SystemBuffer;
1572
1573 /* Set required size */
1574 Output->MultiSzLength = Paths->MultiSzLength;
1575
1576 /* Compute total length */
1577 OutputLength = Output->MultiSzLength + sizeof(ULONG);
1578
1579 /* If it cannot fit, just return need size and quit */
1580 if (OutputLength > Stack->Parameters.DeviceIoControl.OutputBufferLength)
1581 {
1582 Irp->IoStatus.Information = sizeof(ULONG);
1583 FreePool(Paths);
1584 return STATUS_BUFFER_OVERFLOW;
1585 }
1586
1587 /* Copy data and quit */
1588 Irp->IoStatus.Information = OutputLength;
1589 RtlCopyMemory(Output->MultiSz, Paths->MultiSz, Output->MultiSzLength);
1590 FreePool(Paths);
1591 return STATUS_SUCCESS;
1592 }
1593
1594 /*
1595 * @implemented
1596 */
1597 NTSTATUS
1598 MountMgrKeepLinksWhenOffline(IN PDEVICE_EXTENSION DeviceExtension,
1599 IN PIRP Irp)
1600 {
1601 NTSTATUS Status;
1602 PIO_STACK_LOCATION Stack;
1603 UNICODE_STRING SymbolicName;
1604 PMOUNTMGR_TARGET_NAME Target;
1605 PDEVICE_INFORMATION DeviceInformation;
1606
1607 Stack = IoGetCurrentIrpStackLocation(Irp);
1608
1609 /* Validate input */
1610 if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_TARGET_NAME))
1611 {
1612 return STATUS_INVALID_PARAMETER;
1613 }
1614
1615 Target = (PMOUNTMGR_TARGET_NAME)Irp->AssociatedIrp.SystemBuffer;
1616 if (Target->DeviceNameLength + sizeof(USHORT) > Stack->Parameters.DeviceIoControl.InputBufferLength)
1617 {
1618 return STATUS_INVALID_PARAMETER;
1619 }
1620
1621 SymbolicName.Length =
1622 SymbolicName.MaximumLength = Target->DeviceNameLength;
1623 SymbolicName.Buffer = Target->DeviceName;
1624
1625 /* Find the associated device */
1626 Status = FindDeviceInfo(DeviceExtension, &SymbolicName, FALSE, &DeviceInformation);
1627 if (!NT_SUCCESS(Status))
1628 {
1629 return Status;
1630 }
1631
1632 /* Mark we want to keep links */
1633 DeviceInformation->KeepLinks = TRUE;
1634
1635 return STATUS_SUCCESS;
1636 }
1637
1638 /*
1639 * @implemented
1640 */
1641 NTSTATUS
1642 MountMgrVolumeArrivalNotification(IN PDEVICE_EXTENSION DeviceExtension,
1643 IN PIRP Irp)
1644 {
1645 NTSTATUS Status;
1646 BOOLEAN OldState;
1647 PIO_STACK_LOCATION Stack;
1648 UNICODE_STRING SymbolicName;
1649 PMOUNTMGR_TARGET_NAME Target;
1650
1651 Stack = IoGetCurrentIrpStackLocation(Irp);
1652
1653 /* Validate input */
1654 if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_TARGET_NAME))
1655 {
1656 return STATUS_INVALID_PARAMETER;
1657 }
1658
1659 Target = (PMOUNTMGR_TARGET_NAME)Irp->AssociatedIrp.SystemBuffer;
1660 if (Target->DeviceNameLength + sizeof(USHORT) > Stack->Parameters.DeviceIoControl.InputBufferLength)
1661 {
1662 return STATUS_INVALID_PARAMETER;
1663 }
1664
1665 SymbolicName.Length =
1666 SymbolicName.MaximumLength = Target->DeviceNameLength;
1667 SymbolicName.Buffer = Target->DeviceName;
1668
1669 /* Disable hard errors */
1670 OldState = PsGetThreadHardErrorsAreDisabled(PsGetCurrentThread());
1671 PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), TRUE);
1672
1673 /* Call real worker */
1674 Status = MountMgrMountedDeviceArrival(DeviceExtension, &SymbolicName, TRUE);
1675
1676 PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), OldState);
1677
1678 return Status;
1679 }
1680
1681 /*
1682 * @implemented
1683 */
1684 NTSTATUS
1685 MountMgrQueryPoints(IN PDEVICE_EXTENSION DeviceExtension,
1686 IN PIRP Irp)
1687 {
1688 NTSTATUS Status;
1689 PIO_STACK_LOCATION Stack;
1690 PMOUNTDEV_UNIQUE_ID UniqueId;
1691 PMOUNTMGR_MOUNT_POINT MountPoint;
1692 UNICODE_STRING SymbolicName, DeviceName;
1693
1694 Stack = IoGetCurrentIrpStackLocation(Irp);
1695
1696 /* Validate input... */
1697 if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_MOUNT_POINT))
1698 {
1699 return STATUS_INVALID_PARAMETER;
1700 }
1701
1702 MountPoint = (PMOUNTMGR_MOUNT_POINT)Irp->AssociatedIrp.SystemBuffer;
1703 if (!MountPoint->SymbolicLinkNameLength)
1704 {
1705 MountPoint->SymbolicLinkNameOffset = 0;
1706 }
1707
1708 if (!MountPoint->UniqueIdLength)
1709 {
1710 MountPoint->UniqueIdOffset = 0;
1711 }
1712
1713 if (!MountPoint->DeviceNameLength)
1714 {
1715 MountPoint->DeviceNameOffset = 0;
1716 }
1717
1718 /* Addresses can't be odd */
1719 if ((MountPoint->SymbolicLinkNameOffset & 1) ||
1720 (MountPoint->SymbolicLinkNameLength & 1))
1721 {
1722 return STATUS_INVALID_PARAMETER;
1723 }
1724
1725 if ((MountPoint->UniqueIdOffset & 1) ||
1726 (MountPoint->UniqueIdLength & 1))
1727 {
1728 return STATUS_INVALID_PARAMETER;
1729 }
1730
1731 if ((MountPoint->DeviceNameOffset & 1) ||
1732 (MountPoint->DeviceNameLength & 1))
1733 {
1734 return STATUS_INVALID_PARAMETER;
1735 }
1736
1737 /* We can't go beyond */
1738 if (((ULONG)MountPoint->SymbolicLinkNameLength + MountPoint->UniqueIdLength +
1739 MountPoint->DeviceNameLength) < Stack->Parameters.DeviceIoControl.InputBufferLength)
1740 {
1741 return STATUS_INVALID_PARAMETER;
1742 }
1743
1744 if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTMGR_MOUNT_POINTS))
1745 {
1746 return STATUS_INVALID_PARAMETER;
1747 }
1748
1749 /* If caller provided a Symlink, use it */
1750 if (MountPoint->SymbolicLinkNameLength != 0)
1751 {
1752 if (MountPoint->SymbolicLinkNameLength > MAXSHORT)
1753 {
1754 return STATUS_INVALID_PARAMETER;
1755 }
1756
1757 SymbolicName.Length = MountPoint->SymbolicLinkNameLength;
1758 SymbolicName.MaximumLength = MountPoint->SymbolicLinkNameLength + sizeof(WCHAR);
1759 SymbolicName.Buffer = AllocatePool(SymbolicName.MaximumLength);
1760 if (!SymbolicName.Buffer)
1761 {
1762 return STATUS_INSUFFICIENT_RESOURCES;
1763 }
1764
1765 RtlCopyMemory(SymbolicName.Buffer,
1766 (PWSTR)((ULONG_PTR)MountPoint + MountPoint->SymbolicLinkNameOffset),
1767 SymbolicName.Length);
1768 SymbolicName.Buffer[SymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1769
1770 /* Query links using it */
1771 Status = QueryPointsFromSymbolicLinkName(DeviceExtension, &SymbolicName, Irp);
1772 FreePool(SymbolicName.Buffer);
1773 }
1774 /* If user provided an unique ID */
1775 else if (MountPoint->UniqueIdLength != 0)
1776 {
1777 UniqueId = AllocatePool(MountPoint->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
1778 if (!UniqueId)
1779 {
1780 return STATUS_INSUFFICIENT_RESOURCES;
1781 }
1782
1783 UniqueId->UniqueIdLength = MountPoint->UniqueIdLength;
1784 RtlCopyMemory(UniqueId->UniqueId,
1785 (PVOID)((ULONG_PTR)MountPoint + MountPoint->UniqueIdOffset),
1786 MountPoint->UniqueIdLength);
1787
1788 /* Query links using it */
1789 Status = QueryPointsFromMemory(DeviceExtension, Irp, UniqueId, NULL);
1790 FreePool(UniqueId);
1791 }
1792 /* If caller provided a device name */
1793 else if (MountPoint->DeviceNameLength != 0)
1794 {
1795 if (MountPoint->DeviceNameLength > MAXSHORT)
1796 {
1797 return STATUS_INVALID_PARAMETER;
1798 }
1799
1800 DeviceName.Length = MountPoint->DeviceNameLength;
1801 DeviceName.MaximumLength = MountPoint->DeviceNameLength + sizeof(WCHAR);
1802 DeviceName.Buffer = AllocatePool(DeviceName.MaximumLength);
1803 if (!DeviceName.Buffer)
1804 {
1805 return STATUS_INSUFFICIENT_RESOURCES;
1806 }
1807
1808 RtlCopyMemory(DeviceName.Buffer,
1809 (PWSTR)((ULONG_PTR)MountPoint + MountPoint->DeviceNameOffset),
1810 DeviceName.Length);
1811 DeviceName.Buffer[DeviceName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1812
1813 /* Query links using it */
1814 Status = QueryPointsFromMemory(DeviceExtension, Irp, NULL, &DeviceName);
1815 FreePool(DeviceName.Buffer);
1816 }
1817 else
1818 {
1819 /* Otherwise, query all links */
1820 Status = QueryPointsFromMemory(DeviceExtension, Irp, NULL, NULL);
1821 }
1822
1823 return Status;
1824 }
1825
1826 /*
1827 * @implemented
1828 */
1829 NTSTATUS
1830 MountMgrDeletePoints(IN PDEVICE_EXTENSION DeviceExtension,
1831 IN PIRP Irp)
1832 {
1833 ULONG Link;
1834 NTSTATUS Status;
1835 BOOLEAN CreateNoDrive;
1836 PIO_STACK_LOCATION Stack;
1837 PMOUNTDEV_UNIQUE_ID UniqueId;
1838 PMOUNTMGR_MOUNT_POINT MountPoint;
1839 PMOUNTMGR_MOUNT_POINTS MountPoints;
1840 UNICODE_STRING SymbolicName, DeviceName;
1841
1842 Stack = IoGetCurrentIrpStackLocation(Irp);
1843
1844 /* Validate input */
1845 if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_MOUNT_POINT))
1846 {
1847 return STATUS_INVALID_PARAMETER;
1848 }
1849
1850 /* Query points */
1851 MountPoint = (PMOUNTMGR_MOUNT_POINT)Irp->AssociatedIrp.SystemBuffer;
1852 CreateNoDrive = (MountPoint->SymbolicLinkNameOffset && MountPoint->SymbolicLinkNameLength);
1853
1854 Status = MountMgrQueryPoints(DeviceExtension, Irp);
1855 if (!NT_SUCCESS(Status))
1856 {
1857 return Status;
1858 }
1859
1860 /* For all the points matching the request */
1861 MountPoints = (PMOUNTMGR_MOUNT_POINTS)Irp->AssociatedIrp.SystemBuffer;
1862 for (Link = 0; Link < MountPoints->NumberOfMountPoints; Link++)
1863 {
1864 SymbolicName.Length = MountPoints->MountPoints[Link].SymbolicLinkNameLength;
1865 SymbolicName.MaximumLength = SymbolicName.Length + sizeof(WCHAR);
1866 SymbolicName.Buffer = AllocatePool(SymbolicName.MaximumLength);
1867 if (!SymbolicName.Buffer)
1868 {
1869 return STATUS_INSUFFICIENT_RESOURCES;
1870 }
1871
1872 RtlCopyMemory(SymbolicName.Buffer,
1873 (PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].SymbolicLinkNameOffset),
1874 SymbolicName.Length);
1875 SymbolicName.Buffer[SymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1876
1877 /* Create a no drive entry for the drive letters */
1878 if (CreateNoDrive && IsDriveLetter(&SymbolicName))
1879 {
1880 UniqueId = AllocatePool(MountPoints->MountPoints[Link].UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
1881 if (UniqueId)
1882 {
1883 UniqueId->UniqueIdLength = MountPoints->MountPoints[Link].UniqueIdLength;
1884 RtlCopyMemory(UniqueId->UniqueId,
1885 (PMOUNTDEV_UNIQUE_ID)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].UniqueIdOffset),
1886 MountPoints->MountPoints[Link].UniqueIdLength);
1887
1888 CreateNoDriveLetterEntry(UniqueId);
1889 FreePool(UniqueId);
1890 }
1891 }
1892
1893 /* If there are no link any more, and no need to create a no drive entry */
1894 if (Link == 0 && !CreateNoDrive)
1895 {
1896 /* Then, delete everything */
1897 UniqueId = AllocatePool(MountPoints->MountPoints[Link].UniqueIdLength);
1898 if (UniqueId)
1899 {
1900 RtlCopyMemory(UniqueId,
1901 (PMOUNTDEV_UNIQUE_ID)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].UniqueIdOffset),
1902 MountPoints->MountPoints[Link].UniqueIdLength);
1903
1904 DeleteNoDriveLetterEntry(UniqueId);
1905 FreePool(UniqueId);
1906 }
1907 }
1908
1909 /* Delete all the information about the mount point */
1910 GlobalDeleteSymbolicLink(&SymbolicName);
1911 DeleteSymbolicLinkNameFromMemory(DeviceExtension, &SymbolicName, FALSE);
1912 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, DatabasePath, SymbolicName.Buffer);
1913 FreePool(SymbolicName.Buffer);
1914
1915 /* Notify the change */
1916 DeviceName.Length = DeviceName.MaximumLength =
1917 MountPoints->MountPoints[Link].DeviceNameLength;
1918 DeviceName.Buffer = (PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].DeviceNameOffset);
1919 MountMgrNotifyNameChange(DeviceExtension, &DeviceName, TRUE);
1920 }
1921
1922 MountMgrNotify(DeviceExtension);
1923
1924 return Status;
1925 }
1926
1927 /*
1928 * @implemented
1929 */
1930 NTSTATUS
1931 MountMgrDeletePointsDbOnly(IN PDEVICE_EXTENSION DeviceExtension,
1932 IN PIRP Irp)
1933 {
1934 ULONG Link;
1935 NTSTATUS Status;
1936 UNICODE_STRING SymbolicName;
1937 PMOUNTDEV_UNIQUE_ID UniqueId;
1938 PMOUNTMGR_MOUNT_POINTS MountPoints;
1939
1940 /* Query points */
1941 Status = MountMgrQueryPoints(DeviceExtension, Irp);
1942 if (!NT_SUCCESS(Status))
1943 {
1944 return Status;
1945 }
1946
1947 MountPoints = (PMOUNTMGR_MOUNT_POINTS)Irp->AssociatedIrp.SystemBuffer;
1948 if (MountPoints->NumberOfMountPoints == 0)
1949 {
1950 return Status;
1951 }
1952
1953 /* For all the mount points */
1954 for (Link = 0; Link < MountPoints->NumberOfMountPoints; Link++)
1955 {
1956 SymbolicName.Length = MountPoints->MountPoints[Link].SymbolicLinkNameLength;
1957 SymbolicName.MaximumLength = SymbolicName.Length + sizeof(WCHAR);
1958 SymbolicName.Buffer = AllocatePool(SymbolicName.MaximumLength);
1959 if (!SymbolicName.Buffer)
1960 {
1961 return STATUS_INSUFFICIENT_RESOURCES;
1962 }
1963
1964 RtlCopyMemory(SymbolicName.Buffer,
1965 (PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].SymbolicLinkNameOffset),
1966 SymbolicName.Length);
1967 SymbolicName.Buffer[SymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1968
1969 /* If the only mount point is a drive letter, then create a no letter drive entry */
1970 if (MountPoints->NumberOfMountPoints == 1 && IsDriveLetter(&SymbolicName))
1971 {
1972 UniqueId = AllocatePool(MountPoints->MountPoints[Link].UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
1973 if (UniqueId)
1974 {
1975 UniqueId->UniqueIdLength = MountPoints->MountPoints[Link].UniqueIdLength;
1976 RtlCopyMemory(UniqueId->UniqueId,
1977 (PMOUNTDEV_UNIQUE_ID)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].UniqueIdOffset),
1978 MountPoints->MountPoints[Link].UniqueIdLength);
1979
1980 CreateNoDriveLetterEntry(UniqueId);
1981 FreePool(UniqueId);
1982 }
1983 }
1984
1985 /* Simply delete mount point from DB */
1986 DeleteSymbolicLinkNameFromMemory(DeviceExtension, &SymbolicName, TRUE);
1987 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, DatabasePath, SymbolicName.Buffer);
1988 FreePool(SymbolicName.Buffer);
1989 }
1990
1991 return Status;
1992 }
1993
1994 /*
1995 * @implemented
1996 */
1997 NTSTATUS
1998 MountMgrVolumeMountPointChanged(IN PDEVICE_EXTENSION DeviceExtension,
1999 IN PIRP Irp,
2000 IN NTSTATUS LockStatus,
2001 OUT PUNICODE_STRING SourceDeviceName,
2002 OUT PUNICODE_STRING SourceSymbolicName,
2003 OUT PUNICODE_STRING TargetVolumeName)
2004 {
2005 HANDLE Handle;
2006 NTSTATUS Status;
2007 PFILE_OBJECT FileObject;
2008 PIO_STACK_LOCATION Stack;
2009 ULONG Length, SavedLength;
2010 BOOLEAN FOReferenced = FALSE;
2011 IO_STATUS_BLOCK IoStatusBlock;
2012 OBJECT_ATTRIBUTES ObjectAttributes;
2013 PDEVICE_INFORMATION DeviceInformation;
2014 OBJECT_NAME_INFORMATION ObjectNameInfo;
2015 FILE_FS_DEVICE_INFORMATION FsDeviceInfo;
2016 PFILE_NAME_INFORMATION FileNameInfo = NULL;
2017 PMOUNTMGR_VOLUME_MOUNT_POINT VolumeMountPoint;
2018 POBJECT_NAME_INFORMATION ObjectNameInfoPtr = NULL;
2019 UNICODE_STRING SourceVolumeName, TargetDeviceName;
2020
2021 Stack = IoGetCurrentIrpStackLocation(Irp);
2022
2023 /* Validate input */
2024 if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_VOLUME_MOUNT_POINT))
2025 {
2026 return STATUS_INVALID_PARAMETER;
2027 }
2028
2029 VolumeMountPoint = (PMOUNTMGR_VOLUME_MOUNT_POINT)Irp->AssociatedIrp.SystemBuffer;
2030
2031 if (((ULONG)VolumeMountPoint->SourceVolumeNameLength + VolumeMountPoint->TargetVolumeNameLength) <
2032 Stack->Parameters.DeviceIoControl.InputBufferLength)
2033 {
2034 return STATUS_INVALID_PARAMETER;
2035 }
2036
2037 /* Get source volume name */
2038 SourceVolumeName.Length =
2039 SourceVolumeName.MaximumLength = VolumeMountPoint->SourceVolumeNameLength;
2040 SourceVolumeName.Buffer = (PWSTR)((ULONG_PTR)VolumeMountPoint + VolumeMountPoint->SourceVolumeNameOffset);
2041
2042 InitializeObjectAttributes(&ObjectAttributes,
2043 &SourceVolumeName,
2044 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
2045 NULL,
2046 NULL);
2047
2048 /* Open it */
2049 Status = ZwOpenFile(&Handle,
2050 SYNCHRONIZE | FILE_READ_ATTRIBUTES,
2051 &ObjectAttributes,
2052 &IoStatusBlock,
2053 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2054 FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT);
2055 if (!NT_SUCCESS(Status))
2056 {
2057 return Status;
2058 }
2059
2060 TargetDeviceName.Buffer = NULL;
2061
2062 /* Query its attributes */
2063 Status = ZwQueryVolumeInformationFile(Handle,
2064 &IoStatusBlock,
2065 &FsDeviceInfo,
2066 sizeof(FsDeviceInfo),
2067 FileFsDeviceInformation);
2068 if (!NT_SUCCESS(Status))
2069 {
2070 goto Cleanup;
2071 }
2072
2073 if (FsDeviceInfo.DeviceType != FILE_DEVICE_DISK && FsDeviceInfo.DeviceType != FILE_DEVICE_VIRTUAL_DISK)
2074 {
2075 goto Cleanup;
2076 }
2077
2078 if (FsDeviceInfo.Characteristics != (FILE_REMOTE_DEVICE | FILE_REMOVABLE_MEDIA))
2079 {
2080 goto Cleanup;
2081 }
2082
2083 /* Reference it */
2084 Status = ObReferenceObjectByHandle(Handle, 0, *IoFileObjectType, KernelMode, (PVOID *)&FileObject, NULL);
2085 if (!NT_SUCCESS(Status))
2086 {
2087 goto Cleanup;
2088 }
2089 FOReferenced = TRUE;
2090
2091 /* Get file name */
2092 FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION));
2093 if (!FileNameInfo)
2094 {
2095 Status = STATUS_INSUFFICIENT_RESOURCES;
2096 goto Cleanup;
2097 }
2098
2099 Status = ZwQueryInformationFile(Handle, &IoStatusBlock, FileNameInfo,
2100 sizeof(FILE_NAME_INFORMATION),
2101 FileNameInformation);
2102 if (Status == STATUS_BUFFER_OVERFLOW)
2103 {
2104 /* Now we have real length, use it */
2105 Length = FileNameInfo->FileNameLength;
2106 FreePool(FileNameInfo);
2107
2108 FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION) + Length);
2109 if (!FileNameInfo)
2110 {
2111 Status = STATUS_INSUFFICIENT_RESOURCES;
2112 goto Cleanup;
2113 }
2114
2115 /* Really query file name */
2116 Status = ZwQueryInformationFile(Handle, &IoStatusBlock, FileNameInfo,
2117 sizeof(FILE_NAME_INFORMATION) + Length,
2118 FileNameInformation);
2119 }
2120
2121 if (!NT_SUCCESS(Status))
2122 {
2123 goto Cleanup;
2124 }
2125
2126 /* Get symbolic name */
2127 ObjectNameInfoPtr = &ObjectNameInfo;
2128 SavedLength = sizeof(OBJECT_NAME_INFORMATION);
2129 Status = ObQueryNameString(FileObject->DeviceObject, ObjectNameInfoPtr, sizeof(OBJECT_NAME_INFORMATION), &Length);
2130 if (Status == STATUS_INFO_LENGTH_MISMATCH)
2131 {
2132 /* Once again, with proper size, it works better */
2133 ObjectNameInfoPtr = AllocatePool(Length);
2134 if (!ObjectNameInfoPtr)
2135 {
2136 Status = STATUS_INSUFFICIENT_RESOURCES;
2137 goto Cleanup;
2138 }
2139
2140 SavedLength = Length;
2141 Status = ObQueryNameString(FileObject->DeviceObject, ObjectNameInfoPtr, SavedLength, &Length);
2142 }
2143
2144 if (!NT_SUCCESS(Status))
2145 {
2146 goto Cleanup;
2147 }
2148
2149 /* Now, query the device name */
2150 Status = QueryDeviceInformation(&ObjectNameInfoPtr->Name, SourceDeviceName,
2151 NULL, NULL, NULL, NULL, NULL, NULL);
2152 if (!NT_SUCCESS(Status))
2153 {
2154 goto Cleanup;
2155 }
2156
2157 /* For target volume name, use input */
2158 TargetVolumeName->Length =
2159 TargetVolumeName->MaximumLength = VolumeMountPoint->TargetVolumeNameLength;
2160 TargetVolumeName->Buffer = (PWSTR)((ULONG_PTR)VolumeMountPoint + VolumeMountPoint->TargetVolumeNameOffset);
2161
2162 /* Query its device name */
2163 Status = QueryDeviceInformation(TargetVolumeName, &TargetDeviceName,
2164 NULL, NULL, NULL, NULL, NULL, NULL);
2165 if (!NT_SUCCESS(Status))
2166 {
2167 goto Cleanup;
2168 }
2169
2170 /* Return symbolic name */
2171 SourceSymbolicName->Length =
2172 SourceSymbolicName->MaximumLength = (USHORT)FileNameInfo->FileNameLength;
2173 SourceSymbolicName->Buffer = (PWSTR)FileNameInfo;
2174 /* memmove allows memory overlap */
2175 RtlMoveMemory(SourceSymbolicName->Buffer, FileNameInfo->FileName, SourceSymbolicName->Length);
2176 FileNameInfo = NULL;
2177
2178 /* Notify the change */
2179 MountMgrNotify(DeviceExtension);
2180 MountMgrNotifyNameChange(DeviceExtension, &TargetDeviceName, TRUE);
2181
2182 /* If we are locked, sync databases if possible */
2183 if (NT_SUCCESS(LockStatus))
2184 {
2185 Status = FindDeviceInfo(DeviceExtension, SourceDeviceName, FALSE, &DeviceInformation);
2186 if (NT_SUCCESS(Status))
2187 {
2188 ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation);
2189 }
2190 else
2191 {
2192 Status = STATUS_PENDING;
2193 }
2194 }
2195
2196 Cleanup:
2197 if (TargetDeviceName.Buffer)
2198 {
2199 FreePool(TargetDeviceName.Buffer);
2200 }
2201
2202 if (ObjectNameInfoPtr && ObjectNameInfoPtr != &ObjectNameInfo)
2203 {
2204 FreePool(ObjectNameInfoPtr);
2205 }
2206
2207 if (FileNameInfo)
2208 {
2209 FreePool(FileNameInfo);
2210 }
2211
2212 if (FOReferenced)
2213 {
2214 ObDereferenceObject(FileObject);
2215 }
2216
2217 return Status;
2218 }
2219
2220 /*
2221 * @implemented
2222 */
2223 NTSTATUS
2224 MountMgrVolumeMountPointCreated(IN PDEVICE_EXTENSION DeviceExtension,
2225 IN PIRP Irp,
2226 IN NTSTATUS LockStatus)
2227 {
2228 LONG Offset;
2229 BOOLEAN Found;
2230 NTSTATUS Status;
2231 HANDLE RemoteDatabase;
2232 PMOUNTDEV_UNIQUE_ID UniqueId;
2233 PDATABASE_ENTRY DatabaseEntry;
2234 PASSOCIATED_DEVICE_ENTRY AssociatedEntry;
2235 PDEVICE_INFORMATION DeviceInformation, TargetDeviceInformation;
2236 UNICODE_STRING LinkTarget, SourceDeviceName, SourceSymbolicName, TargetVolumeName, VolumeName, DbName;
2237
2238 /* Initialize string */
2239 LinkTarget.Length = 0;
2240 LinkTarget.MaximumLength = 0xC8;
2241 LinkTarget.Buffer = AllocatePool(LinkTarget.MaximumLength);
2242 if (LinkTarget.Buffer == NULL)
2243 {
2244 return STATUS_INSUFFICIENT_RESOURCES;
2245 }
2246
2247 /* If the mount point was created, then, it changed!
2248 * Also use it to query some information
2249 */
2250 Status = MountMgrVolumeMountPointChanged(DeviceExtension, Irp, LockStatus, &SourceDeviceName, &SourceSymbolicName, &TargetVolumeName);
2251 /* Pending means DB are under synchronization, bail out */
2252 if (Status == STATUS_PENDING)
2253 {
2254 FreePool(LinkTarget.Buffer);
2255 FreePool(SourceDeviceName.Buffer);
2256 FreePool(SourceSymbolicName.Buffer);
2257 return STATUS_SUCCESS;
2258 }
2259 else if (!NT_SUCCESS(Status))
2260 {
2261 FreePool(LinkTarget.Buffer);
2262 return Status;
2263 }
2264
2265 /* Query the device information */
2266 Status = FindDeviceInfo(DeviceExtension, &SourceDeviceName, FALSE, &DeviceInformation);
2267 if (!NT_SUCCESS(Status))
2268 {
2269 /* If it failed, first try to get volume name */
2270 Status = QueryVolumeName(0, NULL, &SourceDeviceName, &LinkTarget, &VolumeName);
2271 if (!NT_SUCCESS(Status))
2272 {
2273 /* Then, try to read the symlink */
2274 Status = MountMgrQuerySymbolicLink(&SourceDeviceName, &LinkTarget);
2275 if (!NT_SUCCESS(Status))
2276 {
2277 FreePool(LinkTarget.Buffer);
2278 FreePool(SourceDeviceName.Buffer);
2279 FreePool(SourceSymbolicName.Buffer);
2280 return Status;
2281 }
2282 }
2283 else
2284 {
2285 FreePool(VolumeName.Buffer);
2286 }
2287
2288 FreePool(SourceDeviceName.Buffer);
2289
2290 SourceDeviceName.Length = LinkTarget.Length;
2291 SourceDeviceName.MaximumLength = LinkTarget.MaximumLength;
2292 SourceDeviceName.Buffer = LinkTarget.Buffer;
2293
2294 /* Now that we have the correct source, reattempt to query information */
2295 Status = FindDeviceInfo(DeviceExtension, &SourceDeviceName, FALSE, &DeviceInformation);
2296 if (!NT_SUCCESS(Status))
2297 {
2298 FreePool(SourceDeviceName.Buffer);
2299 FreePool(SourceSymbolicName.Buffer);
2300 return Status;
2301 }
2302 }
2303
2304 FreePool(SourceDeviceName.Buffer);
2305
2306 /* Get information about target device */
2307 Status = FindDeviceInfo(DeviceExtension, &TargetVolumeName, FALSE, &TargetDeviceInformation);
2308 if (!NT_SUCCESS(Status))
2309 {
2310 FreePool(SourceSymbolicName.Buffer);
2311 return Status;
2312 }
2313
2314 /* Notify if not disabled */
2315 if (!TargetDeviceInformation->SkipNotifications)
2316 {
2317 PostOnlineNotification(DeviceExtension, &TargetDeviceInformation->SymbolicName);
2318 }
2319
2320 /* Open the remote database */
2321 RemoteDatabase = OpenRemoteDatabase(DeviceInformation, TRUE);
2322 if (RemoteDatabase == 0)
2323 {
2324 FreePool(SourceSymbolicName.Buffer);
2325 return STATUS_INSUFFICIENT_RESOURCES;
2326 }
2327
2328 /* Browse all the entries */
2329 Offset = 0;
2330 Found = FALSE;
2331 for (;;)
2332 {
2333 DatabaseEntry = GetRemoteDatabaseEntry(RemoteDatabase, Offset);
2334 if (DatabaseEntry == NULL)
2335 {
2336 break;
2337 }
2338
2339 /* Try to find ourselves */
2340 DbName.MaximumLength = DatabaseEntry->SymbolicNameLength;
2341 DbName.Length = DbName.MaximumLength;
2342 DbName.Buffer = (PWSTR)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset);
2343 if (RtlEqualUnicodeString(&TargetVolumeName, &DbName, TRUE))
2344 {
2345 /* Reference ourselves and update the entry */
2346 ++DatabaseEntry->EntryReferences;
2347 Status = WriteRemoteDatabaseEntry(RemoteDatabase, Offset, DatabaseEntry);
2348 FreePool(DatabaseEntry);
2349 Found = TRUE;
2350 break;
2351 }
2352
2353 Offset += DatabaseEntry->EntrySize;
2354 FreePool(DatabaseEntry);
2355 }
2356
2357 /* We couldn't find ourselves, we'll have to add ourselves */
2358 if (!Found)
2359 {
2360 ULONG EntrySize;
2361 PUNIQUE_ID_REPLICATE UniqueIdReplicate;
2362
2363 /* Query the device unique ID */
2364 Status = QueryDeviceInformation(&TargetVolumeName, NULL, &UniqueId, NULL, NULL, NULL, NULL, NULL);
2365 if (!NT_SUCCESS(Status))
2366 {
2367 FreePool(SourceSymbolicName.Buffer);
2368 CloseRemoteDatabase(RemoteDatabase);
2369 return Status;
2370 }
2371
2372 /* Allocate a database entry */
2373 EntrySize = UniqueId->UniqueIdLength + TargetVolumeName.Length + sizeof(DATABASE_ENTRY);
2374 DatabaseEntry = AllocatePool(EntrySize);
2375 if (DatabaseEntry == NULL)
2376 {
2377 FreePool(UniqueId);
2378 FreePool(SourceSymbolicName.Buffer);
2379 CloseRemoteDatabase(RemoteDatabase);
2380 return STATUS_INSUFFICIENT_RESOURCES;
2381 }
2382
2383 /* Fill it in */
2384 DatabaseEntry->EntrySize = EntrySize;
2385 DatabaseEntry->EntryReferences = 1;
2386 DatabaseEntry->SymbolicNameOffset = sizeof(DATABASE_ENTRY);
2387 DatabaseEntry->SymbolicNameLength = TargetVolumeName.Length;
2388 DatabaseEntry->UniqueIdOffset = TargetVolumeName.Length + sizeof(DATABASE_ENTRY);
2389 DatabaseEntry->UniqueIdLength = UniqueId->UniqueIdLength;
2390 RtlCopyMemory((PVOID)((ULONG_PTR)DatabaseEntry + sizeof(DATABASE_ENTRY)), TargetVolumeName.Buffer, DatabaseEntry->SymbolicNameLength);
2391 RtlCopyMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset), UniqueId->UniqueId, UniqueId->UniqueIdLength);
2392
2393 /* And write it down */
2394 Status = AddRemoteDatabaseEntry(RemoteDatabase, DatabaseEntry);
2395 FreePool(DatabaseEntry);
2396 if (!NT_SUCCESS(Status))
2397 {
2398 FreePool(UniqueId);
2399 FreePool(SourceSymbolicName.Buffer);
2400 CloseRemoteDatabase(RemoteDatabase);
2401 return Status;
2402 }
2403
2404 /* And now, allocate an Unique ID item */
2405 UniqueIdReplicate = AllocatePool(sizeof(UNIQUE_ID_REPLICATE));
2406 if (UniqueIdReplicate == NULL)
2407 {
2408 FreePool(UniqueId);
2409 FreePool(SourceSymbolicName.Buffer);
2410 CloseRemoteDatabase(RemoteDatabase);
2411 return Status;
2412 }
2413
2414 /* To associate it with the device */
2415 UniqueIdReplicate->UniqueId = UniqueId;
2416 InsertTailList(&DeviceInformation->ReplicatedUniqueIdsListHead, &UniqueIdReplicate->ReplicatedUniqueIdsListEntry);
2417 }
2418
2419 /* We're done with the remote database */
2420 CloseRemoteDatabase(RemoteDatabase);
2421
2422 /* Check we were find writing the entry */
2423 if (!NT_SUCCESS(Status))
2424 {
2425 FreePool(SourceSymbolicName.Buffer);
2426 return Status;
2427 }
2428
2429 /* This is the end, allocate an associated entry */
2430 AssociatedEntry = AllocatePool(sizeof(ASSOCIATED_DEVICE_ENTRY));
2431 if (AssociatedEntry == NULL)
2432 {
2433 FreePool(SourceSymbolicName.Buffer);
2434 return STATUS_INSUFFICIENT_RESOURCES;
2435 }
2436
2437 /* Initialize its source name string */
2438 AssociatedEntry->String.Length = SourceSymbolicName.Length;
2439 AssociatedEntry->String.MaximumLength = AssociatedEntry->String.Length + sizeof(UNICODE_NULL);
2440 AssociatedEntry->String.Buffer = AllocatePool(AssociatedEntry->String.MaximumLength);
2441 if (AssociatedEntry->String.Buffer == NULL)
2442 {
2443 FreePool(AssociatedEntry);
2444 FreePool(SourceSymbolicName.Buffer);
2445 return STATUS_INSUFFICIENT_RESOURCES;
2446 }
2447
2448 /* Copy data & insert in list */
2449 RtlCopyMemory(AssociatedEntry->String.Buffer, SourceSymbolicName.Buffer, SourceSymbolicName.Length);
2450 AssociatedEntry->String.Buffer[SourceSymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL;
2451 AssociatedEntry->DeviceInformation = DeviceInformation;
2452 InsertTailList(&TargetDeviceInformation->AssociatedDevicesHead, &AssociatedEntry->AssociatedDevicesEntry);
2453
2454 /* We're done! */
2455 FreePool(SourceSymbolicName.Buffer);
2456 return STATUS_SUCCESS;
2457 }
2458
2459 /*
2460 * @implemented
2461 */
2462 NTSTATUS
2463 MountMgrVolumeMountPointDeleted(IN PDEVICE_EXTENSION DeviceExtension,
2464 IN PIRP Irp,
2465 IN NTSTATUS LockStatus)
2466 {
2467 LONG Offset;
2468 NTSTATUS Status;
2469 PLIST_ENTRY Entry;
2470 HANDLE RemoteDatabase;
2471 PDATABASE_ENTRY DatabaseEntry;
2472 PUNIQUE_ID_REPLICATE UniqueIdReplicate;
2473 PASSOCIATED_DEVICE_ENTRY AssociatedEntry;
2474 PDEVICE_INFORMATION DeviceInformation, TargetDeviceInformation;
2475 UNICODE_STRING LinkTarget, SourceDeviceName, SourceSymbolicName, TargetVolumeName, VolumeName, DbName;
2476
2477 /* Initialize string */
2478 LinkTarget.Length = 0;
2479 LinkTarget.MaximumLength = 0xC8;
2480 LinkTarget.Buffer = AllocatePool(LinkTarget.MaximumLength);
2481 if (LinkTarget.Buffer == NULL)
2482 {
2483 return STATUS_INSUFFICIENT_RESOURCES;
2484 }
2485
2486 /* If the mount point was deleted, then, it changed!
2487 * Also use it to query some information
2488 */
2489 Status = MountMgrVolumeMountPointChanged(DeviceExtension, Irp, LockStatus, &SourceDeviceName, &SourceSymbolicName, &TargetVolumeName);
2490 /* Pending means DB are under synchronization, bail out */
2491 if (Status == STATUS_PENDING)
2492 {
2493 FreePool(LinkTarget.Buffer);
2494 FreePool(SourceDeviceName.Buffer);
2495 FreePool(SourceSymbolicName.Buffer);
2496 return STATUS_SUCCESS;
2497 }
2498 else if (!NT_SUCCESS(Status))
2499 {
2500 FreePool(LinkTarget.Buffer);
2501 return Status;
2502 }
2503
2504 /* Query the device information */
2505 Status = FindDeviceInfo(DeviceExtension, &SourceDeviceName, FALSE, &DeviceInformation);
2506 if (!NT_SUCCESS(Status))
2507 {
2508 /* If it failed, first try to get volume name */
2509 Status = QueryVolumeName(0, NULL, &SourceDeviceName, &LinkTarget, &VolumeName);
2510 if (!NT_SUCCESS(Status))
2511 {
2512 /* Then, try to read the symlink */
2513 Status = MountMgrQuerySymbolicLink(&SourceDeviceName, &LinkTarget);
2514 if (!NT_SUCCESS(Status))
2515 {
2516 FreePool(LinkTarget.Buffer);
2517 FreePool(SourceDeviceName.Buffer);
2518 FreePool(SourceSymbolicName.Buffer);
2519 return Status;
2520 }
2521 }
2522 else
2523 {
2524 FreePool(VolumeName.Buffer);
2525 }
2526
2527 FreePool(SourceDeviceName.Buffer);
2528
2529 SourceDeviceName.Length = LinkTarget.Length;
2530 SourceDeviceName.MaximumLength = LinkTarget.MaximumLength;
2531 SourceDeviceName.Buffer = LinkTarget.Buffer;
2532
2533 /* Now that we have the correct source, reattempt to query information */
2534 Status = FindDeviceInfo(DeviceExtension, &SourceDeviceName, FALSE, &DeviceInformation);
2535 if (!NT_SUCCESS(Status))
2536 {
2537 FreePool(SourceDeviceName.Buffer);
2538 FreePool(SourceSymbolicName.Buffer);
2539 return Status;
2540 }
2541 }
2542
2543 FreePool(SourceDeviceName.Buffer);
2544
2545 /* Get information about target device */
2546 Status = FindDeviceInfo(DeviceExtension, &TargetVolumeName, FALSE, &TargetDeviceInformation);
2547 if (!NT_SUCCESS(Status))
2548 {
2549 FreePool(SourceSymbolicName.Buffer);
2550 return Status;
2551 }
2552
2553 /* Open the remote database */
2554 RemoteDatabase = OpenRemoteDatabase(DeviceInformation, TRUE);
2555 if (RemoteDatabase == 0)
2556 {
2557 FreePool(SourceSymbolicName.Buffer);
2558 return STATUS_INSUFFICIENT_RESOURCES;
2559 }
2560
2561 /* Browse all the entries */
2562 Offset = 0;
2563 for (;;)
2564 {
2565 DatabaseEntry = GetRemoteDatabaseEntry(RemoteDatabase, Offset);
2566 if (DatabaseEntry == NULL)
2567 {
2568 /* We didn't find ourselves, that's infortunate! */
2569 FreePool(SourceSymbolicName.Buffer);
2570 CloseRemoteDatabase(RemoteDatabase);
2571 return STATUS_INVALID_PARAMETER;
2572 }
2573
2574 /* Try to find ourselves */
2575 DbName.MaximumLength = DatabaseEntry->SymbolicNameLength;
2576 DbName.Length = DbName.MaximumLength;
2577 DbName.Buffer = (PWSTR)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset);
2578 if (RtlEqualUnicodeString(&TargetVolumeName, &DbName, TRUE))
2579 {
2580 break;
2581 }
2582
2583 Offset += DatabaseEntry->EntrySize;
2584 FreePool(DatabaseEntry);
2585 }
2586
2587 /* Dereference ourselves */
2588 DatabaseEntry->EntryReferences--;
2589 if (DatabaseEntry->EntryReferences == 0)
2590 {
2591 /* If we're still referenced, just update the entry */
2592 Status = WriteRemoteDatabaseEntry(RemoteDatabase, Offset, DatabaseEntry);
2593 }
2594 else
2595 {
2596 /* Otherwise, delete the entry */
2597 Status = DeleteRemoteDatabaseEntry(RemoteDatabase, Offset);
2598 if (!NT_SUCCESS(Status))
2599 {
2600 FreePool(DatabaseEntry);
2601 FreePool(SourceSymbolicName.Buffer);
2602 CloseRemoteDatabase(RemoteDatabase);
2603 return Status;
2604 }
2605
2606 /* Also, delete our unique ID replicated record */
2607 for (Entry = DeviceInformation->ReplicatedUniqueIdsListHead.Flink;
2608 Entry != &DeviceInformation->ReplicatedUniqueIdsListHead;
2609 Entry = Entry->Flink)
2610 {
2611 UniqueIdReplicate = CONTAINING_RECORD(Entry, UNIQUE_ID_REPLICATE, ReplicatedUniqueIdsListEntry);
2612
2613 if (UniqueIdReplicate->UniqueId->UniqueIdLength == DatabaseEntry->UniqueIdLength &&
2614 RtlCompareMemory(UniqueIdReplicate->UniqueId->UniqueId,
2615 (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset),
2616 DatabaseEntry->UniqueIdLength) == DatabaseEntry->UniqueIdLength)
2617 {
2618 break;
2619 }
2620 }
2621
2622 /* It has to exist! */
2623 if (Entry == &DeviceInformation->ReplicatedUniqueIdsListHead)
2624 {
2625 FreePool(DatabaseEntry);
2626 FreePool(SourceSymbolicName.Buffer);
2627 CloseRemoteDatabase(RemoteDatabase);
2628 return STATUS_UNSUCCESSFUL;
2629 }
2630
2631 /* Remove it and free it */
2632 RemoveEntryList(&UniqueIdReplicate->ReplicatedUniqueIdsListEntry);
2633 FreePool(UniqueIdReplicate->UniqueId);
2634 FreePool(UniqueIdReplicate);
2635 }
2636
2637 /* We're done with the remote database */
2638 FreePool(DatabaseEntry);
2639 CloseRemoteDatabase(RemoteDatabase);
2640
2641 /* Check write operation succeed */
2642 if (!NT_SUCCESS(Status))
2643 {
2644 FreePool(SourceSymbolicName.Buffer);
2645 return Status;
2646 }
2647
2648 /* Try to find our associated device entry */
2649 for (Entry = TargetDeviceInformation->AssociatedDevicesHead.Flink;
2650 Entry != &TargetDeviceInformation->AssociatedDevicesHead;
2651 Entry = Entry->Flink)
2652 {
2653 AssociatedEntry = CONTAINING_RECORD(Entry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
2654
2655 /* If found, delete it */
2656 if (AssociatedEntry->DeviceInformation == DeviceInformation &&
2657 RtlEqualUnicodeString(&AssociatedEntry->String, &SourceSymbolicName, TRUE))
2658 {
2659 RemoveEntryList(&AssociatedEntry->AssociatedDevicesEntry);
2660 FreePool(AssociatedEntry->String.Buffer);
2661 FreePool(AssociatedEntry);
2662 break;
2663 }
2664 }
2665
2666 /* We're done! */
2667 FreePool(SourceSymbolicName.Buffer);
2668 return STATUS_SUCCESS;
2669 }
2670
2671 /*
2672 * @implemented
2673 */
2674 NTSTATUS
2675 NTAPI
2676 MountMgrDeviceControl(IN PDEVICE_OBJECT DeviceObject,
2677 IN PIRP Irp)
2678 {
2679 PIO_STACK_LOCATION Stack;
2680 NTSTATUS Status, LockStatus;
2681 PDEVICE_EXTENSION DeviceExtension;
2682
2683 Stack = IoGetCurrentIrpStackLocation(Irp);
2684 DeviceExtension = DeviceObject->DeviceExtension;
2685
2686 KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
2687
2688 switch (Stack->Parameters.DeviceIoControl.IoControlCode)
2689 {
2690 case IOCTL_MOUNTMGR_CREATE_POINT:
2691 Status = MountMgrCreatePoint(DeviceExtension, Irp);
2692 break;
2693
2694 case IOCTL_MOUNTMGR_DELETE_POINTS:
2695 Status = MountMgrDeletePoints(DeviceExtension, Irp);
2696 break;
2697
2698 case IOCTL_MOUNTMGR_QUERY_POINTS:
2699 Status = MountMgrQueryPoints(DeviceExtension, Irp);
2700 break;
2701
2702 case IOCTL_MOUNTMGR_DELETE_POINTS_DBONLY:
2703 Status = MountMgrDeletePointsDbOnly(DeviceExtension, Irp);
2704 break;
2705
2706 case IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER:
2707 Status = MountMgrNextDriveLetter(DeviceExtension, Irp);
2708 break;
2709
2710 case IOCTL_MOUNTMGR_AUTO_DL_ASSIGNMENTS:
2711 DeviceExtension->AutomaticDriveLetter = TRUE;
2712 Status = STATUS_SUCCESS;
2713
2714 MountMgrAssignDriveLetters(DeviceExtension);
2715 ReconcileAllDatabasesWithMaster(DeviceExtension);
2716 WaitForOnlinesToComplete(DeviceExtension);
2717 break;
2718
2719 case IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_CREATED:
2720 KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
2721
2722 LockStatus = WaitForRemoteDatabaseSemaphore(DeviceExtension);
2723 KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
2724 Status = MountMgrVolumeMountPointCreated(DeviceExtension, Irp, LockStatus);
2725 if (NT_SUCCESS(LockStatus))
2726 {
2727 ReleaseRemoteDatabaseSemaphore(DeviceExtension);
2728 }
2729
2730 break;
2731
2732 case IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_DELETED:
2733 KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
2734
2735 LockStatus = WaitForRemoteDatabaseSemaphore(DeviceExtension);
2736 KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
2737 Status = MountMgrVolumeMountPointDeleted(DeviceExtension, Irp, LockStatus);
2738 if (NT_SUCCESS(LockStatus))
2739 {
2740 ReleaseRemoteDatabaseSemaphore(DeviceExtension);
2741 }
2742
2743 break;
2744
2745 case IOCTL_MOUNTMGR_CHANGE_NOTIFY:
2746 Status = MountMgrChangeNotify(DeviceExtension, Irp);
2747 break;
2748
2749 case IOCTL_MOUNTMGR_KEEP_LINKS_WHEN_OFFLINE:
2750 Status = MountMgrKeepLinksWhenOffline(DeviceExtension, Irp);
2751 break;
2752
2753 case IOCTL_MOUNTMGR_CHECK_UNPROCESSED_VOLUMES:
2754 Status = MountMgrCheckUnprocessedVolumes(DeviceExtension, Irp);
2755 goto Complete;
2756
2757 case IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION:
2758 KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
2759 Status = MountMgrVolumeArrivalNotification(DeviceExtension, Irp);
2760 goto Complete;
2761
2762 case IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH:
2763 Status = MountMgrQueryDosVolumePath(DeviceExtension, Irp);
2764 break;
2765
2766 case IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS:
2767 Status = MountMgrQueryDosVolumePaths(DeviceExtension, Irp);
2768 break;
2769
2770 case IOCTL_MOUNTMGR_SCRUB_REGISTRY:
2771 Status = MountMgrScrubRegistry(DeviceExtension);
2772 break;
2773
2774 case IOCTL_MOUNTMGR_QUERY_AUTO_MOUNT:
2775 Status = MountMgrQueryAutoMount(DeviceExtension, Irp);
2776 break;
2777
2778 case IOCTL_MOUNTMGR_SET_AUTO_MOUNT:
2779 Status = MountMgrSetAutoMount(DeviceExtension, Irp);
2780 break;
2781
2782 default:
2783 Status = STATUS_INVALID_DEVICE_REQUEST;
2784 }
2785
2786 KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
2787
2788 if (Status != STATUS_PENDING)
2789 {
2790 goto Complete;
2791 }
2792
2793 return Status;
2794
2795 Complete:
2796 Irp->IoStatus.Status = Status;
2797 IoCompleteRequest(Irp, IO_NO_INCREMENT);
2798
2799 return Status;
2800 }