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