[FASTFAT] Implement delayed close
[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 = 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 automounted 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(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
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, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
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 /*
1165 * @implemented
1166 */
1167 NTSTATUS
1168 MountMgrQueryVolumePaths(IN PDEVICE_EXTENSION DeviceExtension,
1169 IN PDEVICE_INFORMATION DeviceInformation,
1170 IN PLIST_ENTRY DeviceInfoList,
1171 OUT PMOUNTMGR_VOLUME_PATHS * VolumePaths,
1172 OUT PDEVICE_INFORMATION *FailedDevice)
1173 {
1174 ULONG Written;
1175 NTSTATUS Status;
1176 PLIST_ENTRY Entry;
1177 PSYMLINK_INFORMATION SymlinkInformation;
1178 PDEVICE_INFORMATION_ENTRY DeviceInfoEntry;
1179 PASSOCIATED_DEVICE_ENTRY AssociatedDeviceEntry;
1180 PMOUNTMGR_VOLUME_PATHS * Paths = NULL, * CurrentPath;
1181 ULONG OutputPathLength, NumberOfPaths, ReturnedPaths;
1182
1183 /* We return at least null char */
1184 OutputPathLength = sizeof(UNICODE_NULL);
1185
1186 for (Entry = DeviceInformation->SymbolicLinksListHead.Flink;
1187 Entry != &(DeviceInformation->SymbolicLinksListHead);
1188 Entry = Entry->Flink)
1189 {
1190 SymlinkInformation = CONTAINING_RECORD(Entry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
1191
1192 /* Try to find the drive letter (ie, DOS device) */
1193 if (MOUNTMGR_IS_DRIVE_LETTER(&SymlinkInformation->Name) && SymlinkInformation->Online)
1194 {
1195 /* We'll return the letter */
1196 OutputPathLength = 4 * sizeof(WCHAR);
1197 break;
1198 }
1199 }
1200
1201 /* We didn't find any */
1202 if (Entry == &(DeviceInformation->SymbolicLinksListHead))
1203 {
1204 SymlinkInformation = NULL;
1205 }
1206
1207 /* Do we have any device info to return? */
1208 for (Entry = DeviceInfoList->Flink; Entry != DeviceInfoList; Entry = Entry->Flink)
1209 {
1210 DeviceInfoEntry = CONTAINING_RECORD(Entry, DEVICE_INFORMATION_ENTRY, DeviceInformationEntry);
1211
1212 /* Matching current device */
1213 if (DeviceInfoEntry->DeviceInformation == DeviceInformation)
1214 {
1215 /* Allocate the output buffer */
1216 *VolumePaths = AllocatePool(sizeof(ULONG) + OutputPathLength);
1217 if (*VolumePaths == NULL)
1218 {
1219 return STATUS_INSUFFICIENT_RESOURCES;
1220 }
1221
1222 /* Set size */
1223 (*VolumePaths)->MultiSzLength = OutputPathLength;
1224 /* If we have a drive letter, return it */
1225 if (SymlinkInformation != NULL)
1226 {
1227 (*VolumePaths)->MultiSz[0] = SymlinkInformation->Name.Buffer[LETTER_POSITION];
1228 (*VolumePaths)->MultiSz[1] = L':';
1229 (*VolumePaths)->MultiSz[2] = UNICODE_NULL;
1230 (*VolumePaths)->MultiSz[3] = UNICODE_NULL;
1231 }
1232 else
1233 {
1234 (*VolumePaths)->MultiSz[0] = UNICODE_NULL;
1235 }
1236
1237 return STATUS_SUCCESS;
1238 }
1239 }
1240
1241 /* Allocate a new device entry */
1242 DeviceInfoEntry = AllocatePool(sizeof(DEVICE_INFORMATION_ENTRY));
1243 if (DeviceInfoEntry == NULL)
1244 {
1245 return STATUS_INSUFFICIENT_RESOURCES;
1246 }
1247
1248 /* Add it to the list */
1249 DeviceInfoEntry->DeviceInformation = DeviceInformation;
1250 InsertTailList(DeviceInfoList, &DeviceInfoEntry->DeviceInformationEntry);
1251
1252 NumberOfPaths = 0;
1253 /* Count the amount of devices we will have to handle */
1254 if (!IsListEmpty(&DeviceInformation->AssociatedDevicesHead))
1255 {
1256 for (Entry = DeviceInformation->AssociatedDevicesHead.Flink;
1257 Entry != &DeviceInformation->AssociatedDevicesHead;
1258 Entry = Entry->Flink)
1259 {
1260 ++NumberOfPaths;
1261 }
1262
1263 ASSERT(NumberOfPaths != 0);
1264 /* And allocate a big enough buffer */
1265 Paths = AllocatePool(NumberOfPaths * sizeof(PMOUNTMGR_VOLUME_PATHS));
1266 if (Paths == NULL)
1267 {
1268 RemoveEntryList(&DeviceInfoEntry->DeviceInformationEntry);
1269 FreePool(DeviceInfoEntry);
1270 return STATUS_INSUFFICIENT_RESOURCES;
1271 }
1272 }
1273
1274 /* Start the hot loop to gather all the paths and be able to compute total output length! */
1275 ReturnedPaths = 0;
1276 CurrentPath = Paths;
1277 for (Entry = DeviceInformation->AssociatedDevicesHead.Flink;
1278 Entry != &DeviceInformation->AssociatedDevicesHead;
1279 Entry = Entry->Flink)
1280 {
1281 USHORT InnerStrings;
1282 BOOLEAN Invalid = FALSE;
1283
1284 AssociatedDeviceEntry = CONTAINING_RECORD(Entry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
1285
1286 /* Validate the fact its a mount point by query reparse data */
1287 Status = MountMgrValidateBackPointer(AssociatedDeviceEntry, DeviceInformation, &Invalid);
1288
1289 /* If we found an invalid device, that's a failure */
1290 if (Invalid)
1291 {
1292 *FailedDevice = AssociatedDeviceEntry->DeviceInformation;
1293 Status = STATUS_UNSUCCESSFUL;
1294 }
1295
1296 /* Check whether we failed, if so, bail out */
1297 if (!NT_SUCCESS(Status))
1298 {
1299 ULONG i;
1300
1301 for (i = 0; i < ReturnedPaths; ++i)
1302 {
1303 FreePool(Paths[i]);
1304 }
1305
1306 if (Paths != NULL)
1307 {
1308 FreePool(Paths);
1309 }
1310 RemoveEntryList(&DeviceInfoEntry->DeviceInformationEntry);
1311 FreePool(DeviceInfoEntry);
1312 return Status;
1313 }
1314
1315 /* Query associated paths (hello ourselves :-)) */
1316 Status = MountMgrQueryVolumePaths(DeviceExtension,
1317 AssociatedDeviceEntry->DeviceInformation,
1318 DeviceInfoList,
1319 CurrentPath,
1320 FailedDevice);
1321 if (!NT_SUCCESS(Status))
1322 {
1323 ULONG i;
1324
1325 for (i = 0; i < ReturnedPaths; ++i)
1326 {
1327 FreePool(Paths[i]);
1328 }
1329
1330 if (Paths != NULL)
1331 {
1332 FreePool(Paths);
1333 }
1334 RemoveEntryList(&DeviceInfoEntry->DeviceInformationEntry);
1335 FreePool(DeviceInfoEntry);
1336 return Status;
1337 }
1338
1339 /* Count the number of strings we have in the multi string buffer */
1340 InnerStrings = 0;
1341 if ((*CurrentPath)->MultiSzLength != sizeof(UNICODE_NULL))
1342 {
1343 ULONG i;
1344 PWSTR MultiSz = (*CurrentPath)->MultiSz;
1345
1346 for (i = 0; i < (*CurrentPath)->MultiSzLength / sizeof(WCHAR); ++i, ++MultiSz)
1347 {
1348 if (*MultiSz == UNICODE_NULL)
1349 {
1350 ++InnerStrings;
1351 }
1352 }
1353 }
1354
1355 /* We returned one more path (ie, one more allocated buffer) */
1356 ++ReturnedPaths;
1357 /* Move the next pointer to use in the array */
1358 ++CurrentPath;
1359 /* Multiply String.Length by the number of found paths, we always add it after a path */
1360 OutputPathLength += (*CurrentPath)->MultiSzLength + InnerStrings * AssociatedDeviceEntry->String.Length - sizeof(UNICODE_NULL);
1361 }
1362
1363 /* Allocate the output buffer */
1364 *VolumePaths = AllocatePool(sizeof(ULONG) + OutputPathLength);
1365 if (*VolumePaths == NULL)
1366 {
1367 ULONG i;
1368
1369 for (i = 0; i < ReturnedPaths; ++i)
1370 {
1371 FreePool(Paths[i]);
1372 }
1373
1374 if (Paths != NULL)
1375 {
1376 FreePool(Paths);
1377 }
1378 RemoveEntryList(&DeviceInfoEntry->DeviceInformationEntry);
1379 FreePool(DeviceInfoEntry);
1380 return STATUS_INSUFFICIENT_RESOURCES;
1381 }
1382
1383 Written = 0;
1384 /* If we had found a DOS letter, that's the first thing we return */
1385 (*VolumePaths)->MultiSzLength = OutputPathLength;
1386 if (SymlinkInformation != NULL)
1387 {
1388 (*VolumePaths)->MultiSz[0] = SymlinkInformation->Name.Buffer[LETTER_POSITION];
1389 (*VolumePaths)->MultiSz[1] = L':';
1390 (*VolumePaths)->MultiSz[2] = UNICODE_NULL;
1391 Written = 3;
1392 }
1393
1394 /* Now, browse again all our paths to return them */
1395 CurrentPath = Paths;
1396 for (Entry = DeviceInformation->AssociatedDevicesHead.Flink;
1397 Entry != &DeviceInformation->AssociatedDevicesHead;
1398 Entry = Entry->Flink)
1399 {
1400 AssociatedDeviceEntry = CONTAINING_RECORD(Entry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
1401
1402 /* If we had a path... */
1403 if ((*CurrentPath)->MultiSzLength != sizeof(UNICODE_NULL))
1404 {
1405 ULONG i, Offset;
1406 PWSTR MultiSz;
1407
1408 /* This offset is used to "jump" into MultiSz, so, start with the string begin (ie, skip MultiSzLength) */
1409 Offset = sizeof(ULONG);
1410 /* Browse every single letter, and skip last UNICODE_NULL */
1411 for (i = 0; i < (*CurrentPath)->MultiSzLength / sizeof(WCHAR) - 1; ++i)
1412 {
1413 /* Get the letter */
1414 MultiSz = (PWSTR)((ULONG_PTR)(*CurrentPath) + Offset);
1415 /* If it was part of the path, just return it */
1416 if (*MultiSz != UNICODE_NULL)
1417 {
1418 (*VolumePaths)->MultiSz[Written] = *MultiSz;
1419 }
1420 else
1421 {
1422 /* Otherwise, as planed, return our whole associated device name */
1423 RtlCopyMemory(&(*VolumePaths)->MultiSz[Written],
1424 AssociatedDeviceEntry->String.Buffer,
1425 AssociatedDeviceEntry->String.Length);
1426 Written += AssociatedDeviceEntry->String.Length / sizeof(WCHAR);
1427 /* And don't forget to nullify */
1428 (*VolumePaths)->MultiSz[Written] = UNICODE_NULL;
1429 }
1430
1431 /* We at least return a letter or a null char */
1432 ++Written;
1433 /* Move to the next letter */
1434 Offset += sizeof(WCHAR);
1435 }
1436 }
1437
1438 FreePool(*CurrentPath);
1439 ++CurrentPath;
1440 }
1441
1442 /* MultiSz: don't forget last null char */
1443 (*VolumePaths)->MultiSz[Written] = UNICODE_NULL;
1444 /* Cleanup everything and return success! */
1445 if (Paths != NULL)
1446 {
1447 FreePool(Paths);
1448 }
1449 RemoveEntryList(&DeviceInfoEntry->DeviceInformationEntry);
1450 FreePool(DeviceInfoEntry);
1451 return STATUS_SUCCESS;
1452 }
1453
1454 /*
1455 * @implemented
1456 */
1457 NTSTATUS
1458 MountMgrQueryDosVolumePaths(IN PDEVICE_EXTENSION DeviceExtension,
1459 IN PIRP Irp)
1460 {
1461 NTSTATUS Status;
1462 PLIST_ENTRY Entry;
1463 LIST_ENTRY Devices;
1464 BOOLEAN NeedNotification;
1465 PIO_STACK_LOCATION Stack;
1466 UNICODE_STRING SymbolicName;
1467 ULONG Attempts, OutputLength;
1468 PMOUNTMGR_TARGET_NAME Target;
1469 PMOUNTMGR_VOLUME_PATHS Paths, Output;
1470 RECONCILE_WORK_ITEM_CONTEXT ReconcileContext;
1471 PDEVICE_INFORMATION DeviceInformation, ListDeviceInfo, FailedDevice;
1472
1473 Stack = IoGetCurrentIrpStackLocation(Irp);
1474
1475 /* Validate input size */
1476 if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_TARGET_NAME))
1477 {
1478 return STATUS_INVALID_PARAMETER;
1479 }
1480
1481 /* Ensure we have received UNICODE_STRING */
1482 Target = (PMOUNTMGR_TARGET_NAME)Irp->AssociatedIrp.SystemBuffer;
1483 if (Target->DeviceNameLength & 1)
1484 {
1485 return STATUS_INVALID_PARAMETER;
1486 }
1487
1488 /* Validate the entry structure size */
1489 if (Target->DeviceNameLength + FIELD_OFFSET(MOUNTMGR_TARGET_NAME, DeviceName) > Stack->Parameters.DeviceIoControl.InputBufferLength)
1490 {
1491 return STATUS_INVALID_PARAMETER;
1492 }
1493
1494 /* Ensure we can at least return needed size */
1495 if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))
1496 {
1497 return STATUS_INVALID_PARAMETER;
1498 }
1499
1500 /* Construct string for query */
1501 SymbolicName.Length = Target->DeviceNameLength;
1502 SymbolicName.MaximumLength = Target->DeviceNameLength + sizeof(UNICODE_NULL);
1503 SymbolicName.Buffer = Target->DeviceName;
1504
1505 /* Find device with our info */
1506 Status = FindDeviceInfo(DeviceExtension, &SymbolicName, FALSE, &DeviceInformation);
1507 if (!NT_SUCCESS(Status))
1508 {
1509 return Status;
1510 }
1511
1512 NeedNotification = FALSE;
1513 Attempts = 0;
1514 for (;;)
1515 {
1516 FailedDevice = NULL;
1517 InitializeListHead(&Devices);
1518
1519 /* Query paths */
1520 Status = MountMgrQueryVolumePaths(DeviceExtension, DeviceInformation, &Devices, &Paths, &FailedDevice);
1521 if (NT_SUCCESS(Status))
1522 {
1523 break;
1524 }
1525
1526 /* If it failed for generic reason (memory, whatever), bail out (ie, FailedDevice not set) */
1527 if (FailedDevice == NULL)
1528 {
1529 return Status;
1530 }
1531
1532 /* If PnP, let's notify in case of success */
1533 if (!DeviceInformation->ManuallyRegistered)
1534 {
1535 NeedNotification = TRUE;
1536 }
1537
1538 /* Reconcile database */
1539 ReconcileContext.DeviceExtension = DeviceExtension;
1540 ReconcileContext.DeviceInformation = FailedDevice;
1541 KeReleaseSemaphore(&DeviceExtension->DeviceLock, IO_NO_INCREMENT, 1, FALSE);
1542 ReconcileThisDatabaseWithMasterWorker(&ReconcileContext);
1543 KeWaitForSingleObject(&DeviceExtension->DeviceLock, Executive, KernelMode, FALSE, NULL);
1544
1545 /* Look for our device, to check it's online */
1546 for (Entry = DeviceExtension->DeviceListHead.Flink;
1547 Entry != &DeviceExtension->DeviceListHead;
1548 Entry = Entry->Flink)
1549 {
1550 ListDeviceInfo = CONTAINING_RECORD(Entry, DEVICE_INFORMATION, DeviceListEntry);
1551 /* It's online, it's OK! */
1552 if (ListDeviceInfo == DeviceInformation)
1553 {
1554 break;
1555 }
1556 }
1557
1558 /* It's not online, it's not good */
1559 if (Entry == &DeviceExtension->DeviceListHead)
1560 {
1561 return STATUS_OBJECT_NAME_NOT_FOUND;
1562 }
1563
1564 /* Increase attempts count */
1565 ++Attempts;
1566 /* Don't look forever and fail if we get out of attempts */
1567 if (Attempts >= 1000)
1568 {
1569 return Status;
1570 }
1571 }
1572
1573 /* We need to notify? Go ahead */
1574 if (NeedNotification)
1575 {
1576 MountMgrNotifyNameChange(DeviceExtension, &SymbolicName, FALSE);
1577 }
1578
1579 /* Get output buffer */
1580 Output = (PMOUNTMGR_VOLUME_PATHS)Irp->AssociatedIrp.SystemBuffer;
1581
1582 /* Set required size */
1583 Output->MultiSzLength = Paths->MultiSzLength;
1584
1585 /* Compute total length */
1586 OutputLength = Output->MultiSzLength + sizeof(ULONG);
1587
1588 /* If it cannot fit, just return need size and quit */
1589 if (OutputLength > Stack->Parameters.DeviceIoControl.OutputBufferLength)
1590 {
1591 Irp->IoStatus.Information = sizeof(ULONG);
1592 FreePool(Paths);
1593 return STATUS_BUFFER_OVERFLOW;
1594 }
1595
1596 /* Copy data and quit */
1597 Irp->IoStatus.Information = OutputLength;
1598 RtlCopyMemory(Output->MultiSz, Paths->MultiSz, Output->MultiSzLength);
1599 FreePool(Paths);
1600 return STATUS_SUCCESS;
1601 }
1602
1603 /*
1604 * @implemented
1605 */
1606 NTSTATUS
1607 MountMgrKeepLinksWhenOffline(IN PDEVICE_EXTENSION DeviceExtension,
1608 IN PIRP Irp)
1609 {
1610 NTSTATUS Status;
1611 PIO_STACK_LOCATION Stack;
1612 UNICODE_STRING SymbolicName;
1613 PMOUNTMGR_TARGET_NAME Target;
1614 PDEVICE_INFORMATION DeviceInformation;
1615
1616 Stack = IoGetCurrentIrpStackLocation(Irp);
1617
1618 /* Validate input */
1619 if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_TARGET_NAME))
1620 {
1621 return STATUS_INVALID_PARAMETER;
1622 }
1623
1624 Target = (PMOUNTMGR_TARGET_NAME)Irp->AssociatedIrp.SystemBuffer;
1625 if (Target->DeviceNameLength + sizeof(USHORT) > Stack->Parameters.DeviceIoControl.InputBufferLength)
1626 {
1627 return STATUS_INVALID_PARAMETER;
1628 }
1629
1630 SymbolicName.Length =
1631 SymbolicName.MaximumLength = Target->DeviceNameLength;
1632 SymbolicName.Buffer = Target->DeviceName;
1633
1634 /* Find the associated device */
1635 Status = FindDeviceInfo(DeviceExtension, &SymbolicName, FALSE, &DeviceInformation);
1636 if (!NT_SUCCESS(Status))
1637 {
1638 return Status;
1639 }
1640
1641 /* Mark we want to keep links */
1642 DeviceInformation->KeepLinks = TRUE;
1643
1644 return STATUS_SUCCESS;
1645 }
1646
1647 /*
1648 * @implemented
1649 */
1650 NTSTATUS
1651 MountMgrVolumeArrivalNotification(IN PDEVICE_EXTENSION DeviceExtension,
1652 IN PIRP Irp)
1653 {
1654 NTSTATUS Status;
1655 BOOLEAN OldState;
1656 PIO_STACK_LOCATION Stack;
1657 UNICODE_STRING SymbolicName;
1658 PMOUNTMGR_TARGET_NAME Target;
1659
1660 Stack = IoGetCurrentIrpStackLocation(Irp);
1661
1662 /* Validate input */
1663 if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_TARGET_NAME))
1664 {
1665 return STATUS_INVALID_PARAMETER;
1666 }
1667
1668 Target = (PMOUNTMGR_TARGET_NAME)Irp->AssociatedIrp.SystemBuffer;
1669 if (Target->DeviceNameLength + sizeof(USHORT) > Stack->Parameters.DeviceIoControl.InputBufferLength)
1670 {
1671 return STATUS_INVALID_PARAMETER;
1672 }
1673
1674 SymbolicName.Length =
1675 SymbolicName.MaximumLength = Target->DeviceNameLength;
1676 SymbolicName.Buffer = Target->DeviceName;
1677
1678 /* Disable hard errors */
1679 OldState = PsGetThreadHardErrorsAreDisabled(PsGetCurrentThread());
1680 PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), TRUE);
1681
1682 /* Call real worker */
1683 Status = MountMgrMountedDeviceArrival(DeviceExtension, &SymbolicName, TRUE);
1684
1685 PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), OldState);
1686
1687 return Status;
1688 }
1689
1690 /*
1691 * @implemented
1692 */
1693 NTSTATUS
1694 MountMgrQueryPoints(IN PDEVICE_EXTENSION DeviceExtension,
1695 IN PIRP Irp)
1696 {
1697 NTSTATUS Status;
1698 PIO_STACK_LOCATION Stack;
1699 PMOUNTDEV_UNIQUE_ID UniqueId;
1700 PMOUNTMGR_MOUNT_POINT MountPoint;
1701 UNICODE_STRING SymbolicName, DeviceName;
1702
1703 Stack = IoGetCurrentIrpStackLocation(Irp);
1704
1705 /* Validate input... */
1706 if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_MOUNT_POINT))
1707 {
1708 return STATUS_INVALID_PARAMETER;
1709 }
1710
1711 MountPoint = (PMOUNTMGR_MOUNT_POINT)Irp->AssociatedIrp.SystemBuffer;
1712 if (!MountPoint->SymbolicLinkNameLength)
1713 {
1714 MountPoint->SymbolicLinkNameOffset = 0;
1715 }
1716
1717 if (!MountPoint->UniqueIdLength)
1718 {
1719 MountPoint->UniqueIdOffset = 0;
1720 }
1721
1722 if (!MountPoint->DeviceNameLength)
1723 {
1724 MountPoint->DeviceNameOffset = 0;
1725 }
1726
1727 /* Addresses can't be odd */
1728 if ((MountPoint->SymbolicLinkNameOffset & 1) ||
1729 (MountPoint->SymbolicLinkNameLength & 1))
1730 {
1731 return STATUS_INVALID_PARAMETER;
1732 }
1733
1734 if ((MountPoint->UniqueIdOffset & 1) ||
1735 (MountPoint->UniqueIdLength & 1))
1736 {
1737 return STATUS_INVALID_PARAMETER;
1738 }
1739
1740 if ((MountPoint->DeviceNameOffset & 1) ||
1741 (MountPoint->DeviceNameLength & 1))
1742 {
1743 return STATUS_INVALID_PARAMETER;
1744 }
1745
1746 /* We can't go beyond */
1747 if (((ULONG)MountPoint->SymbolicLinkNameLength + MountPoint->UniqueIdLength +
1748 MountPoint->DeviceNameLength) < Stack->Parameters.DeviceIoControl.InputBufferLength)
1749 {
1750 return STATUS_INVALID_PARAMETER;
1751 }
1752
1753 if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTMGR_MOUNT_POINTS))
1754 {
1755 return STATUS_INVALID_PARAMETER;
1756 }
1757
1758 /* If caller provided a Symlink, use it */
1759 if (MountPoint->SymbolicLinkNameLength != 0)
1760 {
1761 if (MountPoint->SymbolicLinkNameLength > MAXSHORT)
1762 {
1763 return STATUS_INVALID_PARAMETER;
1764 }
1765
1766 SymbolicName.Length = MountPoint->SymbolicLinkNameLength;
1767 SymbolicName.MaximumLength = MountPoint->SymbolicLinkNameLength + sizeof(WCHAR);
1768 SymbolicName.Buffer = AllocatePool(SymbolicName.MaximumLength);
1769 if (!SymbolicName.Buffer)
1770 {
1771 return STATUS_INSUFFICIENT_RESOURCES;
1772 }
1773
1774 RtlCopyMemory(SymbolicName.Buffer,
1775 (PWSTR)((ULONG_PTR)MountPoint + MountPoint->SymbolicLinkNameOffset),
1776 SymbolicName.Length);
1777 SymbolicName.Buffer[SymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1778
1779 /* Query links using it */
1780 Status = QueryPointsFromSymbolicLinkName(DeviceExtension, &SymbolicName, Irp);
1781 FreePool(SymbolicName.Buffer);
1782 }
1783 /* If user provided an unique ID */
1784 else if (MountPoint->UniqueIdLength != 0)
1785 {
1786 UniqueId = AllocatePool(MountPoint->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
1787 if (!UniqueId)
1788 {
1789 return STATUS_INSUFFICIENT_RESOURCES;
1790 }
1791
1792 UniqueId->UniqueIdLength = MountPoint->UniqueIdLength;
1793 RtlCopyMemory(UniqueId->UniqueId,
1794 (PVOID)((ULONG_PTR)MountPoint + MountPoint->UniqueIdOffset),
1795 MountPoint->UniqueIdLength);
1796
1797 /* Query links using it */
1798 Status = QueryPointsFromMemory(DeviceExtension, Irp, UniqueId, NULL);
1799 FreePool(UniqueId);
1800 }
1801 /* If caller provided a device name */
1802 else if (MountPoint->DeviceNameLength != 0)
1803 {
1804 if (MountPoint->DeviceNameLength > MAXSHORT)
1805 {
1806 return STATUS_INVALID_PARAMETER;
1807 }
1808
1809 DeviceName.Length = MountPoint->DeviceNameLength;
1810 DeviceName.MaximumLength = MountPoint->DeviceNameLength + sizeof(WCHAR);
1811 DeviceName.Buffer = AllocatePool(DeviceName.MaximumLength);
1812 if (!DeviceName.Buffer)
1813 {
1814 return STATUS_INSUFFICIENT_RESOURCES;
1815 }
1816
1817 RtlCopyMemory(DeviceName.Buffer,
1818 (PWSTR)((ULONG_PTR)MountPoint + MountPoint->DeviceNameOffset),
1819 DeviceName.Length);
1820 DeviceName.Buffer[DeviceName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1821
1822 /* Query links using it */
1823 Status = QueryPointsFromMemory(DeviceExtension, Irp, NULL, &DeviceName);
1824 FreePool(DeviceName.Buffer);
1825 }
1826 else
1827 {
1828 /* Otherwise, query all links */
1829 Status = QueryPointsFromMemory(DeviceExtension, Irp, NULL, NULL);
1830 }
1831
1832 return Status;
1833 }
1834
1835 /*
1836 * @implemented
1837 */
1838 NTSTATUS
1839 MountMgrDeletePoints(IN PDEVICE_EXTENSION DeviceExtension,
1840 IN PIRP Irp)
1841 {
1842 ULONG Link;
1843 NTSTATUS Status;
1844 BOOLEAN CreateNoDrive;
1845 PIO_STACK_LOCATION Stack;
1846 PMOUNTDEV_UNIQUE_ID UniqueId;
1847 PMOUNTMGR_MOUNT_POINT MountPoint;
1848 PMOUNTMGR_MOUNT_POINTS MountPoints;
1849 UNICODE_STRING SymbolicName, DeviceName;
1850
1851 Stack = IoGetCurrentIrpStackLocation(Irp);
1852
1853 /* Validate input */
1854 if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_MOUNT_POINT))
1855 {
1856 return STATUS_INVALID_PARAMETER;
1857 }
1858
1859 /* Query points */
1860 MountPoint = (PMOUNTMGR_MOUNT_POINT)Irp->AssociatedIrp.SystemBuffer;
1861 CreateNoDrive = (MountPoint->SymbolicLinkNameOffset && MountPoint->SymbolicLinkNameLength);
1862
1863 Status = MountMgrQueryPoints(DeviceExtension, Irp);
1864 if (!NT_SUCCESS(Status))
1865 {
1866 return Status;
1867 }
1868
1869 /* For all the points matching the request */
1870 MountPoints = (PMOUNTMGR_MOUNT_POINTS)Irp->AssociatedIrp.SystemBuffer;
1871 for (Link = 0; Link < MountPoints->NumberOfMountPoints; Link++)
1872 {
1873 SymbolicName.Length = MountPoints->MountPoints[Link].SymbolicLinkNameLength;
1874 SymbolicName.MaximumLength = SymbolicName.Length + sizeof(WCHAR);
1875 SymbolicName.Buffer = AllocatePool(SymbolicName.MaximumLength);
1876 if (!SymbolicName.Buffer)
1877 {
1878 return STATUS_INSUFFICIENT_RESOURCES;
1879 }
1880
1881 RtlCopyMemory(SymbolicName.Buffer,
1882 (PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].SymbolicLinkNameOffset),
1883 SymbolicName.Length);
1884 SymbolicName.Buffer[SymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1885
1886 /* Create a no drive entry for the drive letters */
1887 if (CreateNoDrive && IsDriveLetter(&SymbolicName))
1888 {
1889 UniqueId = AllocatePool(MountPoints->MountPoints[Link].UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
1890 if (UniqueId)
1891 {
1892 UniqueId->UniqueIdLength = MountPoints->MountPoints[Link].UniqueIdLength;
1893 RtlCopyMemory(UniqueId->UniqueId,
1894 (PMOUNTDEV_UNIQUE_ID)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].UniqueIdOffset),
1895 MountPoints->MountPoints[Link].UniqueIdLength);
1896
1897 CreateNoDriveLetterEntry(UniqueId);
1898 FreePool(UniqueId);
1899 }
1900 }
1901
1902 /* If there are no link any more, and no need to create a no drive entry */
1903 if (Link == 0 && !CreateNoDrive)
1904 {
1905 /* Then, delete everything */
1906 UniqueId = AllocatePool(MountPoints->MountPoints[Link].UniqueIdLength);
1907 if (UniqueId)
1908 {
1909 RtlCopyMemory(UniqueId,
1910 (PMOUNTDEV_UNIQUE_ID)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].UniqueIdOffset),
1911 MountPoints->MountPoints[Link].UniqueIdLength);
1912
1913 DeleteNoDriveLetterEntry(UniqueId);
1914 FreePool(UniqueId);
1915 }
1916 }
1917
1918 /* Delete all the information about the mount point */
1919 GlobalDeleteSymbolicLink(&SymbolicName);
1920 DeleteSymbolicLinkNameFromMemory(DeviceExtension, &SymbolicName, FALSE);
1921 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, DatabasePath, SymbolicName.Buffer);
1922 FreePool(SymbolicName.Buffer);
1923
1924 /* Notify the change */
1925 DeviceName.Length = DeviceName.MaximumLength =
1926 MountPoints->MountPoints[Link].DeviceNameLength;
1927 DeviceName.Buffer = (PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].DeviceNameOffset);
1928 MountMgrNotifyNameChange(DeviceExtension, &DeviceName, TRUE);
1929 }
1930
1931 MountMgrNotify(DeviceExtension);
1932
1933 return Status;
1934 }
1935
1936 /*
1937 * @implemented
1938 */
1939 NTSTATUS
1940 MountMgrDeletePointsDbOnly(IN PDEVICE_EXTENSION DeviceExtension,
1941 IN PIRP Irp)
1942 {
1943 ULONG Link;
1944 NTSTATUS Status;
1945 UNICODE_STRING SymbolicName;
1946 PMOUNTDEV_UNIQUE_ID UniqueId;
1947 PMOUNTMGR_MOUNT_POINTS MountPoints;
1948
1949 /* Query points */
1950 Status = MountMgrQueryPoints(DeviceExtension, Irp);
1951 if (!NT_SUCCESS(Status))
1952 {
1953 return Status;
1954 }
1955
1956 MountPoints = (PMOUNTMGR_MOUNT_POINTS)Irp->AssociatedIrp.SystemBuffer;
1957 if (MountPoints->NumberOfMountPoints == 0)
1958 {
1959 return Status;
1960 }
1961
1962 /* For all the mount points */
1963 for (Link = 0; Link < MountPoints->NumberOfMountPoints; Link++)
1964 {
1965 SymbolicName.Length = MountPoints->MountPoints[Link].SymbolicLinkNameLength;
1966 SymbolicName.MaximumLength = SymbolicName.Length + sizeof(WCHAR);
1967 SymbolicName.Buffer = AllocatePool(SymbolicName.MaximumLength);
1968 if (!SymbolicName.Buffer)
1969 {
1970 return STATUS_INSUFFICIENT_RESOURCES;
1971 }
1972
1973 RtlCopyMemory(SymbolicName.Buffer,
1974 (PWSTR)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].SymbolicLinkNameOffset),
1975 SymbolicName.Length);
1976 SymbolicName.Buffer[SymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1977
1978 /* If the only mount point is a drive letter, then create a no letter drive entry */
1979 if (MountPoints->NumberOfMountPoints == 1 && IsDriveLetter(&SymbolicName))
1980 {
1981 UniqueId = AllocatePool(MountPoints->MountPoints[Link].UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
1982 if (UniqueId)
1983 {
1984 UniqueId->UniqueIdLength = MountPoints->MountPoints[Link].UniqueIdLength;
1985 RtlCopyMemory(UniqueId->UniqueId,
1986 (PMOUNTDEV_UNIQUE_ID)((ULONG_PTR)MountPoints + MountPoints->MountPoints[Link].UniqueIdOffset),
1987 MountPoints->MountPoints[Link].UniqueIdLength);
1988
1989 CreateNoDriveLetterEntry(UniqueId);
1990 FreePool(UniqueId);
1991 }
1992 }
1993
1994 /* Simply delete mount point from DB */
1995 DeleteSymbolicLinkNameFromMemory(DeviceExtension, &SymbolicName, TRUE);
1996 RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE, DatabasePath, SymbolicName.Buffer);
1997 FreePool(SymbolicName.Buffer);
1998 }
1999
2000 return Status;
2001 }
2002
2003 /*
2004 * @implemented
2005 */
2006 NTSTATUS
2007 MountMgrVolumeMountPointChanged(IN PDEVICE_EXTENSION DeviceExtension,
2008 IN PIRP Irp,
2009 IN NTSTATUS LockStatus,
2010 OUT PUNICODE_STRING SourceDeviceName,
2011 OUT PUNICODE_STRING SourceSymbolicName,
2012 OUT PUNICODE_STRING TargetVolumeName)
2013 {
2014 HANDLE Handle;
2015 NTSTATUS Status;
2016 PFILE_OBJECT FileObject;
2017 PIO_STACK_LOCATION Stack;
2018 ULONG Length, SavedLength;
2019 BOOLEAN FOReferenced = FALSE;
2020 IO_STATUS_BLOCK IoStatusBlock;
2021 OBJECT_ATTRIBUTES ObjectAttributes;
2022 PDEVICE_INFORMATION DeviceInformation;
2023 OBJECT_NAME_INFORMATION ObjectNameInfo;
2024 FILE_FS_DEVICE_INFORMATION FsDeviceInfo;
2025 PFILE_NAME_INFORMATION FileNameInfo = NULL;
2026 PMOUNTMGR_VOLUME_MOUNT_POINT VolumeMountPoint;
2027 POBJECT_NAME_INFORMATION ObjectNameInfoPtr = NULL;
2028 UNICODE_STRING SourceVolumeName, TargetDeviceName;
2029
2030 Stack = IoGetCurrentIrpStackLocation(Irp);
2031
2032 /* Validate input */
2033 if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(MOUNTMGR_VOLUME_MOUNT_POINT))
2034 {
2035 return STATUS_INVALID_PARAMETER;
2036 }
2037
2038 VolumeMountPoint = (PMOUNTMGR_VOLUME_MOUNT_POINT)Irp->AssociatedIrp.SystemBuffer;
2039
2040 if (((ULONG)VolumeMountPoint->SourceVolumeNameLength + VolumeMountPoint->TargetVolumeNameLength) <
2041 Stack->Parameters.DeviceIoControl.InputBufferLength)
2042 {
2043 return STATUS_INVALID_PARAMETER;
2044 }
2045
2046 /* Get source volume name */
2047 SourceVolumeName.Length =
2048 SourceVolumeName.MaximumLength = VolumeMountPoint->SourceVolumeNameLength;
2049 SourceVolumeName.Buffer = (PWSTR)((ULONG_PTR)VolumeMountPoint + VolumeMountPoint->SourceVolumeNameOffset);
2050
2051 InitializeObjectAttributes(&ObjectAttributes,
2052 &SourceVolumeName,
2053 OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
2054 NULL,
2055 NULL);
2056
2057 /* Open it */
2058 Status = ZwOpenFile(&Handle,
2059 SYNCHRONIZE | FILE_READ_ATTRIBUTES,
2060 &ObjectAttributes,
2061 &IoStatusBlock,
2062 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2063 FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_REPARSE_POINT);
2064 if (!NT_SUCCESS(Status))
2065 {
2066 return Status;
2067 }
2068
2069 TargetDeviceName.Buffer = NULL;
2070
2071 /* Query its attributes */
2072 Status = ZwQueryVolumeInformationFile(Handle,
2073 &IoStatusBlock,
2074 &FsDeviceInfo,
2075 sizeof(FsDeviceInfo),
2076 FileFsDeviceInformation);
2077 if (!NT_SUCCESS(Status))
2078 {
2079 goto Cleanup;
2080 }
2081
2082 if (FsDeviceInfo.DeviceType != FILE_DEVICE_DISK && FsDeviceInfo.DeviceType != FILE_DEVICE_VIRTUAL_DISK)
2083 {
2084 goto Cleanup;
2085 }
2086
2087 if (FsDeviceInfo.Characteristics != (FILE_REMOTE_DEVICE | FILE_REMOVABLE_MEDIA))
2088 {
2089 goto Cleanup;
2090 }
2091
2092 /* Reference it */
2093 Status = ObReferenceObjectByHandle(Handle, 0, *IoFileObjectType, KernelMode, (PVOID *)&FileObject, NULL);
2094 if (!NT_SUCCESS(Status))
2095 {
2096 goto Cleanup;
2097 }
2098 FOReferenced = TRUE;
2099
2100 /* Get file name */
2101 FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION));
2102 if (!FileNameInfo)
2103 {
2104 Status = STATUS_INSUFFICIENT_RESOURCES;
2105 goto Cleanup;
2106 }
2107
2108 Status = ZwQueryInformationFile(Handle, &IoStatusBlock, FileNameInfo,
2109 sizeof(FILE_NAME_INFORMATION),
2110 FileNameInformation);
2111 if (Status == STATUS_BUFFER_OVERFLOW)
2112 {
2113 /* Now we have real length, use it */
2114 Length = FileNameInfo->FileNameLength;
2115 FreePool(FileNameInfo);
2116
2117 FileNameInfo = AllocatePool(sizeof(FILE_NAME_INFORMATION) + Length);
2118 if (!FileNameInfo)
2119 {
2120 Status = STATUS_INSUFFICIENT_RESOURCES;
2121 goto Cleanup;
2122 }
2123
2124 /* Really query file name */
2125 Status = ZwQueryInformationFile(Handle, &IoStatusBlock, FileNameInfo,
2126 sizeof(FILE_NAME_INFORMATION) + Length,
2127 FileNameInformation);
2128 }
2129
2130 if (!NT_SUCCESS(Status))
2131 {
2132 goto Cleanup;
2133 }
2134
2135 /* Get symbolic name */
2136 ObjectNameInfoPtr = &ObjectNameInfo;
2137 SavedLength = sizeof(OBJECT_NAME_INFORMATION);
2138 Status = ObQueryNameString(FileObject->DeviceObject, ObjectNameInfoPtr, sizeof(OBJECT_NAME_INFORMATION), &Length);
2139 if (Status == STATUS_INFO_LENGTH_MISMATCH)
2140 {
2141 /* Once again, with proper size, it works better */
2142 ObjectNameInfoPtr = AllocatePool(Length);
2143 if (!ObjectNameInfoPtr)
2144 {
2145 Status = STATUS_INSUFFICIENT_RESOURCES;
2146 goto Cleanup;
2147 }
2148
2149 SavedLength = Length;
2150 Status = ObQueryNameString(FileObject->DeviceObject, ObjectNameInfoPtr, SavedLength, &Length);
2151 }
2152
2153 if (!NT_SUCCESS(Status))
2154 {
2155 goto Cleanup;
2156 }
2157
2158 /* Now, query the device name */
2159 Status = QueryDeviceInformation(&ObjectNameInfoPtr->Name, SourceDeviceName,
2160 NULL, NULL, NULL, NULL, NULL, NULL);
2161 if (!NT_SUCCESS(Status))
2162 {
2163 goto Cleanup;
2164 }
2165
2166 /* For target volume name, use input */
2167 TargetVolumeName->Length =
2168 TargetVolumeName->MaximumLength = VolumeMountPoint->TargetVolumeNameLength;
2169 TargetVolumeName->Buffer = (PWSTR)((ULONG_PTR)VolumeMountPoint + VolumeMountPoint->TargetVolumeNameOffset);
2170
2171 /* Query its device name */
2172 Status = QueryDeviceInformation(TargetVolumeName, &TargetDeviceName,
2173 NULL, NULL, NULL, NULL, NULL, NULL);
2174 if (!NT_SUCCESS(Status))
2175 {
2176 goto Cleanup;
2177 }
2178
2179 /* Return symbolic name */
2180 SourceSymbolicName->Length =
2181 SourceSymbolicName->MaximumLength = (USHORT)FileNameInfo->FileNameLength;
2182 SourceSymbolicName->Buffer = (PWSTR)FileNameInfo;
2183 /* memmove allows memory overlap */
2184 RtlMoveMemory(SourceSymbolicName->Buffer, FileNameInfo->FileName, SourceSymbolicName->Length);
2185 FileNameInfo = NULL;
2186
2187 /* Notify the change */
2188 MountMgrNotify(DeviceExtension);
2189 MountMgrNotifyNameChange(DeviceExtension, &TargetDeviceName, TRUE);
2190
2191 /* If we are locked, sync databases if possible */
2192 if (NT_SUCCESS(LockStatus))
2193 {
2194 Status = FindDeviceInfo(DeviceExtension, SourceDeviceName, FALSE, &DeviceInformation);
2195 if (NT_SUCCESS(Status))
2196 {
2197 ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation);
2198 }
2199 else
2200 {
2201 Status = STATUS_PENDING;
2202 }
2203 }
2204
2205 Cleanup:
2206 if (TargetDeviceName.Buffer)
2207 {
2208 FreePool(TargetDeviceName.Buffer);
2209 }
2210
2211 if (ObjectNameInfoPtr && ObjectNameInfoPtr != &ObjectNameInfo)
2212 {
2213 FreePool(ObjectNameInfoPtr);
2214 }
2215
2216 if (FileNameInfo)
2217 {
2218 FreePool(FileNameInfo);
2219 }
2220
2221 if (FOReferenced)
2222 {
2223 ObDereferenceObject(FileObject);
2224 }
2225
2226 return Status;
2227 }
2228
2229 /*
2230 * @implemented
2231 */
2232 NTSTATUS
2233 MountMgrVolumeMountPointCreated(IN PDEVICE_EXTENSION DeviceExtension,
2234 IN PIRP Irp,
2235 IN NTSTATUS LockStatus)
2236 {
2237 LONG Offset;
2238 BOOLEAN Found;
2239 NTSTATUS Status;
2240 HANDLE RemoteDatabase;
2241 PMOUNTDEV_UNIQUE_ID UniqueId;
2242 PDATABASE_ENTRY DatabaseEntry;
2243 PASSOCIATED_DEVICE_ENTRY AssociatedEntry;
2244 PDEVICE_INFORMATION DeviceInformation, TargetDeviceInformation;
2245 UNICODE_STRING LinkTarget, SourceDeviceName, SourceSymbolicName, TargetVolumeName, VolumeName, DbName;
2246
2247 /* Initialize string */
2248 LinkTarget.Length = 0;
2249 LinkTarget.MaximumLength = 0xC8;
2250 LinkTarget.Buffer = AllocatePool(LinkTarget.MaximumLength);
2251 if (LinkTarget.Buffer == NULL)
2252 {
2253 return STATUS_INSUFFICIENT_RESOURCES;
2254 }
2255
2256 /* If the mount point was created, then, it changed!
2257 * Also use it to query some information
2258 */
2259 Status = MountMgrVolumeMountPointChanged(DeviceExtension, Irp, LockStatus, &SourceDeviceName, &SourceSymbolicName, &TargetVolumeName);
2260 /* Pending means DB are under synchronization, bail out */
2261 if (Status == STATUS_PENDING)
2262 {
2263 FreePool(LinkTarget.Buffer);
2264 FreePool(SourceDeviceName.Buffer);
2265 FreePool(SourceSymbolicName.Buffer);
2266 return STATUS_SUCCESS;
2267 }
2268 else if (!NT_SUCCESS(Status))
2269 {
2270 FreePool(LinkTarget.Buffer);
2271 return Status;
2272 }
2273
2274 /* Query the device information */
2275 Status = FindDeviceInfo(DeviceExtension, &SourceDeviceName, FALSE, &DeviceInformation);
2276 if (!NT_SUCCESS(Status))
2277 {
2278 /* If it failed, first try to get volume name */
2279 Status = QueryVolumeName(0, NULL, &SourceDeviceName, &LinkTarget, &VolumeName);
2280 if (!NT_SUCCESS(Status))
2281 {
2282 /* Then, try to read the symlink */
2283 Status = MountMgrQuerySymbolicLink(&SourceDeviceName, &LinkTarget);
2284 if (!NT_SUCCESS(Status))
2285 {
2286 FreePool(LinkTarget.Buffer);
2287 FreePool(SourceDeviceName.Buffer);
2288 FreePool(SourceSymbolicName.Buffer);
2289 return Status;
2290 }
2291 }
2292 else
2293 {
2294 FreePool(VolumeName.Buffer);
2295 }
2296
2297 FreePool(SourceDeviceName.Buffer);
2298
2299 SourceDeviceName.Length = LinkTarget.Length;
2300 SourceDeviceName.MaximumLength = LinkTarget.MaximumLength;
2301 SourceDeviceName.Buffer = LinkTarget.Buffer;
2302
2303 /* Now that we have the correct source, reattempt to query information */
2304 Status = FindDeviceInfo(DeviceExtension, &SourceDeviceName, FALSE, &DeviceInformation);
2305 if (!NT_SUCCESS(Status))
2306 {
2307 FreePool(SourceDeviceName.Buffer);
2308 FreePool(SourceSymbolicName.Buffer);
2309 return Status;
2310 }
2311 }
2312
2313 FreePool(SourceDeviceName.Buffer);
2314
2315 /* Get information about target device */
2316 Status = FindDeviceInfo(DeviceExtension, &TargetVolumeName, FALSE, &TargetDeviceInformation);
2317 if (!NT_SUCCESS(Status))
2318 {
2319 FreePool(SourceSymbolicName.Buffer);
2320 return Status;
2321 }
2322
2323 /* Notify if not disabled */
2324 if (!TargetDeviceInformation->SkipNotifications)
2325 {
2326 PostOnlineNotification(DeviceExtension, &TargetDeviceInformation->SymbolicName);
2327 }
2328
2329 /* Open the remote database */
2330 RemoteDatabase = OpenRemoteDatabase(DeviceInformation, TRUE);
2331 if (RemoteDatabase == 0)
2332 {
2333 FreePool(SourceSymbolicName.Buffer);
2334 return STATUS_INSUFFICIENT_RESOURCES;
2335 }
2336
2337 /* Browse all the entries */
2338 Offset = 0;
2339 Found = FALSE;
2340 for (;;)
2341 {
2342 DatabaseEntry = GetRemoteDatabaseEntry(RemoteDatabase, Offset);
2343 if (DatabaseEntry == NULL)
2344 {
2345 break;
2346 }
2347
2348 /* Try to find ourselves */
2349 DbName.MaximumLength = DatabaseEntry->SymbolicNameLength;
2350 DbName.Length = DbName.MaximumLength;
2351 DbName.Buffer = (PWSTR)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset);
2352 if (RtlEqualUnicodeString(&TargetVolumeName, &DbName, TRUE))
2353 {
2354 /* Reference ourselves and update the entry */
2355 ++DatabaseEntry->EntryReferences;
2356 Status = WriteRemoteDatabaseEntry(RemoteDatabase, Offset, DatabaseEntry);
2357 FreePool(DatabaseEntry);
2358 Found = TRUE;
2359 break;
2360 }
2361
2362 Offset += DatabaseEntry->EntrySize;
2363 FreePool(DatabaseEntry);
2364 }
2365
2366 /* We couldn't find ourselves, we'll have to add ourselves */
2367 if (!Found)
2368 {
2369 ULONG EntrySize;
2370 PUNIQUE_ID_REPLICATE UniqueIdReplicate;
2371
2372 /* Query the device unique ID */
2373 Status = QueryDeviceInformation(&TargetVolumeName, NULL, &UniqueId, NULL, NULL, NULL, NULL, NULL);
2374 if (!NT_SUCCESS(Status))
2375 {
2376 FreePool(SourceSymbolicName.Buffer);
2377 CloseRemoteDatabase(RemoteDatabase);
2378 return Status;
2379 }
2380
2381 /* Allocate a database entry */
2382 EntrySize = UniqueId->UniqueIdLength + TargetVolumeName.Length + sizeof(DATABASE_ENTRY);
2383 DatabaseEntry = AllocatePool(EntrySize);
2384 if (DatabaseEntry == NULL)
2385 {
2386 FreePool(UniqueId);
2387 FreePool(SourceSymbolicName.Buffer);
2388 CloseRemoteDatabase(RemoteDatabase);
2389 return STATUS_INSUFFICIENT_RESOURCES;
2390 }
2391
2392 /* Fill it in */
2393 DatabaseEntry->EntrySize = EntrySize;
2394 DatabaseEntry->EntryReferences = 1;
2395 DatabaseEntry->SymbolicNameOffset = sizeof(DATABASE_ENTRY);
2396 DatabaseEntry->SymbolicNameLength = TargetVolumeName.Length;
2397 DatabaseEntry->UniqueIdOffset = TargetVolumeName.Length + sizeof(DATABASE_ENTRY);
2398 DatabaseEntry->UniqueIdLength = UniqueId->UniqueIdLength;
2399 RtlCopyMemory((PVOID)((ULONG_PTR)DatabaseEntry + sizeof(DATABASE_ENTRY)), TargetVolumeName.Buffer, DatabaseEntry->SymbolicNameLength);
2400 RtlCopyMemory((PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset), UniqueId->UniqueId, UniqueId->UniqueIdLength);
2401
2402 /* And write it down */
2403 Status = AddRemoteDatabaseEntry(RemoteDatabase, DatabaseEntry);
2404 FreePool(DatabaseEntry);
2405 if (!NT_SUCCESS(Status))
2406 {
2407 FreePool(UniqueId);
2408 FreePool(SourceSymbolicName.Buffer);
2409 CloseRemoteDatabase(RemoteDatabase);
2410 return Status;
2411 }
2412
2413 /* And now, allocate an Unique ID item */
2414 UniqueIdReplicate = AllocatePool(sizeof(UNIQUE_ID_REPLICATE));
2415 if (UniqueIdReplicate == NULL)
2416 {
2417 FreePool(UniqueId);
2418 FreePool(SourceSymbolicName.Buffer);
2419 CloseRemoteDatabase(RemoteDatabase);
2420 return Status;
2421 }
2422
2423 /* To associate it with the device */
2424 UniqueIdReplicate->UniqueId = UniqueId;
2425 InsertTailList(&DeviceInformation->ReplicatedUniqueIdsListHead, &UniqueIdReplicate->ReplicatedUniqueIdsListEntry);
2426 }
2427
2428 /* We're done with the remote database */
2429 CloseRemoteDatabase(RemoteDatabase);
2430
2431 /* Check we were find writing the entry */
2432 if (!NT_SUCCESS(Status))
2433 {
2434 FreePool(SourceSymbolicName.Buffer);
2435 return Status;
2436 }
2437
2438 /* This is the end, allocate an associated entry */
2439 AssociatedEntry = AllocatePool(sizeof(ASSOCIATED_DEVICE_ENTRY));
2440 if (AssociatedEntry == NULL)
2441 {
2442 FreePool(SourceSymbolicName.Buffer);
2443 return STATUS_INSUFFICIENT_RESOURCES;
2444 }
2445
2446 /* Initialize its source name string */
2447 AssociatedEntry->String.Length = SourceSymbolicName.Length;
2448 AssociatedEntry->String.MaximumLength = AssociatedEntry->String.Length + sizeof(UNICODE_NULL);
2449 AssociatedEntry->String.Buffer = AllocatePool(AssociatedEntry->String.MaximumLength);
2450 if (AssociatedEntry->String.Buffer == NULL)
2451 {
2452 FreePool(AssociatedEntry);
2453 FreePool(SourceSymbolicName.Buffer);
2454 return STATUS_INSUFFICIENT_RESOURCES;
2455 }
2456
2457 /* Copy data & insert in list */
2458 RtlCopyMemory(AssociatedEntry->String.Buffer, SourceSymbolicName.Buffer, SourceSymbolicName.Length);
2459 AssociatedEntry->String.Buffer[SourceSymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL;
2460 AssociatedEntry->DeviceInformation = DeviceInformation;
2461 InsertTailList(&TargetDeviceInformation->AssociatedDevicesHead, &AssociatedEntry->AssociatedDevicesEntry);
2462
2463 /* We're done! */
2464 FreePool(SourceSymbolicName.Buffer);
2465 return STATUS_SUCCESS;
2466 }
2467
2468 /*
2469 * @implemented
2470 */
2471 NTSTATUS
2472 MountMgrVolumeMountPointDeleted(IN PDEVICE_EXTENSION DeviceExtension,
2473 IN PIRP Irp,
2474 IN NTSTATUS LockStatus)
2475 {
2476 LONG Offset;
2477 NTSTATUS Status;
2478 PLIST_ENTRY Entry;
2479 HANDLE RemoteDatabase;
2480 PDATABASE_ENTRY DatabaseEntry;
2481 PUNIQUE_ID_REPLICATE UniqueIdReplicate;
2482 PASSOCIATED_DEVICE_ENTRY AssociatedEntry;
2483 PDEVICE_INFORMATION DeviceInformation, TargetDeviceInformation;
2484 UNICODE_STRING LinkTarget, SourceDeviceName, SourceSymbolicName, TargetVolumeName, VolumeName, DbName;
2485
2486 /* Initialize string */
2487 LinkTarget.Length = 0;
2488 LinkTarget.MaximumLength = 0xC8;
2489 LinkTarget.Buffer = AllocatePool(LinkTarget.MaximumLength);
2490 if (LinkTarget.Buffer == NULL)
2491 {
2492 return STATUS_INSUFFICIENT_RESOURCES;
2493 }
2494
2495 /* If the mount point was deleted, then, it changed!
2496 * Also use it to query some information
2497 */
2498 Status = MountMgrVolumeMountPointChanged(DeviceExtension, Irp, LockStatus, &SourceDeviceName, &SourceSymbolicName, &TargetVolumeName);
2499 /* Pending means DB are under synchronization, bail out */
2500 if (Status == STATUS_PENDING)
2501 {
2502 FreePool(LinkTarget.Buffer);
2503 FreePool(SourceDeviceName.Buffer);
2504 FreePool(SourceSymbolicName.Buffer);
2505 return STATUS_SUCCESS;
2506 }
2507 else if (!NT_SUCCESS(Status))
2508 {
2509 FreePool(LinkTarget.Buffer);
2510 return Status;
2511 }
2512
2513 /* Query the device information */
2514 Status = FindDeviceInfo(DeviceExtension, &SourceDeviceName, FALSE, &DeviceInformation);
2515 if (!NT_SUCCESS(Status))
2516 {
2517 /* If it failed, first try to get volume name */
2518 Status = QueryVolumeName(0, NULL, &SourceDeviceName, &LinkTarget, &VolumeName);
2519 if (!NT_SUCCESS(Status))
2520 {
2521 /* Then, try to read the symlink */
2522 Status = MountMgrQuerySymbolicLink(&SourceDeviceName, &LinkTarget);
2523 if (!NT_SUCCESS(Status))
2524 {
2525 FreePool(LinkTarget.Buffer);
2526 FreePool(SourceDeviceName.Buffer);
2527 FreePool(SourceSymbolicName.Buffer);
2528 return Status;
2529 }
2530 }
2531 else
2532 {
2533 FreePool(VolumeName.Buffer);
2534 }
2535
2536 FreePool(SourceDeviceName.Buffer);
2537
2538 SourceDeviceName.Length = LinkTarget.Length;
2539 SourceDeviceName.MaximumLength = LinkTarget.MaximumLength;
2540 SourceDeviceName.Buffer = LinkTarget.Buffer;
2541
2542 /* Now that we have the correct source, reattempt to query information */
2543 Status = FindDeviceInfo(DeviceExtension, &SourceDeviceName, FALSE, &DeviceInformation);
2544 if (!NT_SUCCESS(Status))
2545 {
2546 FreePool(SourceDeviceName.Buffer);
2547 FreePool(SourceSymbolicName.Buffer);
2548 return Status;
2549 }
2550 }
2551
2552 FreePool(SourceDeviceName.Buffer);
2553
2554 /* Get information about target device */
2555 Status = FindDeviceInfo(DeviceExtension, &TargetVolumeName, FALSE, &TargetDeviceInformation);
2556 if (!NT_SUCCESS(Status))
2557 {
2558 FreePool(SourceSymbolicName.Buffer);
2559 return Status;
2560 }
2561
2562 /* Open the remote database */
2563 RemoteDatabase = OpenRemoteDatabase(DeviceInformation, TRUE);
2564 if (RemoteDatabase == 0)
2565 {
2566 FreePool(SourceSymbolicName.Buffer);
2567 return STATUS_INSUFFICIENT_RESOURCES;
2568 }
2569
2570 /* Browse all the entries */
2571 Offset = 0;
2572 for (;;)
2573 {
2574 DatabaseEntry = GetRemoteDatabaseEntry(RemoteDatabase, Offset);
2575 if (DatabaseEntry == NULL)
2576 {
2577 /* We didn't find ourselves, that's infortunate! */
2578 FreePool(SourceSymbolicName.Buffer);
2579 CloseRemoteDatabase(RemoteDatabase);
2580 return STATUS_INVALID_PARAMETER;
2581 }
2582
2583 /* Try to find ourselves */
2584 DbName.MaximumLength = DatabaseEntry->SymbolicNameLength;
2585 DbName.Length = DbName.MaximumLength;
2586 DbName.Buffer = (PWSTR)((ULONG_PTR)DatabaseEntry + DatabaseEntry->SymbolicNameOffset);
2587 if (RtlEqualUnicodeString(&TargetVolumeName, &DbName, TRUE))
2588 {
2589 break;
2590 }
2591
2592 Offset += DatabaseEntry->EntrySize;
2593 FreePool(DatabaseEntry);
2594 }
2595
2596 /* Dereference ourselves */
2597 DatabaseEntry->EntryReferences--;
2598 if (DatabaseEntry->EntryReferences == 0)
2599 {
2600 /* If we're still referenced, just update the entry */
2601 Status = WriteRemoteDatabaseEntry(RemoteDatabase, Offset, DatabaseEntry);
2602 }
2603 else
2604 {
2605 /* Otherwise, delete the entry */
2606 Status = DeleteRemoteDatabaseEntry(RemoteDatabase, Offset);
2607 if (!NT_SUCCESS(Status))
2608 {
2609 FreePool(DatabaseEntry);
2610 FreePool(SourceSymbolicName.Buffer);
2611 CloseRemoteDatabase(RemoteDatabase);
2612 return Status;
2613 }
2614
2615 /* Also, delete our unique ID replicated record */
2616 for (Entry = DeviceInformation->ReplicatedUniqueIdsListHead.Flink;
2617 Entry != &DeviceInformation->ReplicatedUniqueIdsListHead;
2618 Entry = Entry->Flink)
2619 {
2620 UniqueIdReplicate = CONTAINING_RECORD(Entry, UNIQUE_ID_REPLICATE, ReplicatedUniqueIdsListEntry);
2621
2622 if (UniqueIdReplicate->UniqueId->UniqueIdLength == DatabaseEntry->UniqueIdLength &&
2623 RtlCompareMemory(UniqueIdReplicate->UniqueId->UniqueId,
2624 (PVOID)((ULONG_PTR)DatabaseEntry + DatabaseEntry->UniqueIdOffset),
2625 DatabaseEntry->UniqueIdLength) == DatabaseEntry->UniqueIdLength)
2626 {
2627 break;
2628 }
2629 }
2630
2631 /* It has to exist! */
2632 if (Entry == &DeviceInformation->ReplicatedUniqueIdsListHead)
2633 {
2634 FreePool(DatabaseEntry);
2635 FreePool(SourceSymbolicName.Buffer);
2636 CloseRemoteDatabase(RemoteDatabase);
2637 return STATUS_UNSUCCESSFUL;
2638 }
2639
2640 /* Remove it and free it */
2641 RemoveEntryList(&UniqueIdReplicate->ReplicatedUniqueIdsListEntry);
2642 FreePool(UniqueIdReplicate->UniqueId);
2643 FreePool(UniqueIdReplicate);
2644 }
2645
2646 /* We're done with the remote database */
2647 FreePool(DatabaseEntry);
2648 CloseRemoteDatabase(RemoteDatabase);
2649
2650 /* Check write operation succeed */
2651 if (!NT_SUCCESS(Status))
2652 {
2653 FreePool(SourceSymbolicName.Buffer);
2654 return Status;
2655 }
2656
2657 /* Try to find our associated device entry */
2658 for (Entry = TargetDeviceInformation->AssociatedDevicesHead.Flink;
2659 Entry != &TargetDeviceInformation->AssociatedDevicesHead;
2660 Entry = Entry->Flink)
2661 {
2662 AssociatedEntry = CONTAINING_RECORD(Entry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
2663
2664 /* If found, delete it */
2665 if (AssociatedEntry->DeviceInformation == DeviceInformation &&
2666 RtlEqualUnicodeString(&AssociatedEntry->String, &SourceSymbolicName, TRUE))
2667 {
2668 RemoveEntryList(&AssociatedEntry->AssociatedDevicesEntry);
2669 FreePool(AssociatedEntry->String.Buffer);
2670 FreePool(AssociatedEntry);
2671 break;
2672 }
2673 }
2674
2675 /* We're done! */
2676 FreePool(SourceSymbolicName.Buffer);
2677 return STATUS_SUCCESS;
2678 }
2679
2680 /*
2681 * @implemented
2682 */
2683 NTSTATUS
2684 NTAPI
2685 MountMgrDeviceControl(IN PDEVICE_OBJECT DeviceObject,
2686 IN PIRP Irp)
2687 {
2688 PIO_STACK_LOCATION Stack;
2689 NTSTATUS Status, LockStatus;
2690 PDEVICE_EXTENSION DeviceExtension;
2691
2692 Stack = IoGetCurrentIrpStackLocation(Irp);
2693 DeviceExtension = DeviceObject->DeviceExtension;
2694
2695 KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
2696
2697 switch (Stack->Parameters.DeviceIoControl.IoControlCode)
2698 {
2699 case IOCTL_MOUNTMGR_CREATE_POINT:
2700 Status = MountMgrCreatePoint(DeviceExtension, Irp);
2701 break;
2702
2703 case IOCTL_MOUNTMGR_DELETE_POINTS:
2704 Status = MountMgrDeletePoints(DeviceExtension, Irp);
2705 break;
2706
2707 case IOCTL_MOUNTMGR_QUERY_POINTS:
2708 Status = MountMgrQueryPoints(DeviceExtension, Irp);
2709 break;
2710
2711 case IOCTL_MOUNTMGR_DELETE_POINTS_DBONLY:
2712 Status = MountMgrDeletePointsDbOnly(DeviceExtension, Irp);
2713 break;
2714
2715 case IOCTL_MOUNTMGR_NEXT_DRIVE_LETTER:
2716 Status = MountMgrNextDriveLetter(DeviceExtension, Irp);
2717 break;
2718
2719 case IOCTL_MOUNTMGR_AUTO_DL_ASSIGNMENTS:
2720 DeviceExtension->AutomaticDriveLetter = TRUE;
2721 Status = STATUS_SUCCESS;
2722
2723 MountMgrAssignDriveLetters(DeviceExtension);
2724 ReconcileAllDatabasesWithMaster(DeviceExtension);
2725 WaitForOnlinesToComplete(DeviceExtension);
2726 break;
2727
2728 case IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_CREATED:
2729 KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
2730
2731 LockStatus = WaitForRemoteDatabaseSemaphore(DeviceExtension);
2732 KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
2733 Status = MountMgrVolumeMountPointCreated(DeviceExtension, Irp, LockStatus);
2734 if (NT_SUCCESS(LockStatus))
2735 {
2736 ReleaseRemoteDatabaseSemaphore(DeviceExtension);
2737 }
2738
2739 break;
2740
2741 case IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_DELETED:
2742 KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
2743
2744 LockStatus = WaitForRemoteDatabaseSemaphore(DeviceExtension);
2745 KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
2746 Status = MountMgrVolumeMountPointDeleted(DeviceExtension, Irp, LockStatus);
2747 if (NT_SUCCESS(LockStatus))
2748 {
2749 ReleaseRemoteDatabaseSemaphore(DeviceExtension);
2750 }
2751
2752 break;
2753
2754 case IOCTL_MOUNTMGR_CHANGE_NOTIFY:
2755 Status = MountMgrChangeNotify(DeviceExtension, Irp);
2756 break;
2757
2758 case IOCTL_MOUNTMGR_KEEP_LINKS_WHEN_OFFLINE:
2759 Status = MountMgrKeepLinksWhenOffline(DeviceExtension, Irp);
2760 break;
2761
2762 case IOCTL_MOUNTMGR_CHECK_UNPROCESSED_VOLUMES:
2763 Status = MountMgrCheckUnprocessedVolumes(DeviceExtension, Irp);
2764 goto Complete;
2765
2766 case IOCTL_MOUNTMGR_VOLUME_ARRIVAL_NOTIFICATION:
2767 KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
2768 Status = MountMgrVolumeArrivalNotification(DeviceExtension, Irp);
2769 goto Complete;
2770
2771 case IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH:
2772 Status = MountMgrQueryDosVolumePath(DeviceExtension, Irp);
2773 break;
2774
2775 case IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS:
2776 Status = MountMgrQueryDosVolumePaths(DeviceExtension, Irp);
2777 break;
2778
2779 case IOCTL_MOUNTMGR_SCRUB_REGISTRY:
2780 Status = MountMgrScrubRegistry(DeviceExtension);
2781 break;
2782
2783 case IOCTL_MOUNTMGR_QUERY_AUTO_MOUNT:
2784 Status = MountMgrQueryAutoMount(DeviceExtension, Irp);
2785 break;
2786
2787 case IOCTL_MOUNTMGR_SET_AUTO_MOUNT:
2788 Status = MountMgrSetAutoMount(DeviceExtension, Irp);
2789 break;
2790
2791 case IOCTL_MOUNTMGR_DEFINE_UNIX_DRIVE:
2792 case IOCTL_MOUNTMGR_QUERY_UNIX_DRIVE:
2793 DPRINT1("Winism! Rewrite the caller!\n");
2794 default:
2795 Status = STATUS_INVALID_DEVICE_REQUEST;
2796 }
2797
2798 KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
2799
2800 if (Status != STATUS_PENDING)
2801 {
2802 goto Complete;
2803 }
2804
2805 return Status;
2806
2807 Complete:
2808 Irp->IoStatus.Status = Status;
2809 IoCompleteRequest(Irp, IO_NO_INCREMENT);
2810
2811 return Status;
2812 }