* Sync up to trunk HEAD (r62285). Branch guys deserve the significant speedups too ;)
[reactos.git] / drivers / filters / mountmgr / mountmgr.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 2011 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/mountmgr.c
22 * PURPOSE: Mount Manager
23 * PROGRAMMER: Pierre Schweitzer (pierre.schweitzer@reactos.org)
24 * Alex Ionescu (alex.ionescu@reactos.org)
25 */
26
27 #include "mntmgr.h"
28
29 #define NDEBUG
30 #include <debug.h>
31
32 /* FIXME */
33 GUID MountedDevicesGuid = {0x53F5630D, 0xB6BF, 0x11D0, {0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B}};
34
35 PDEVICE_OBJECT gdeviceObject;
36 KEVENT UnloadEvent;
37 LONG Unloading;
38
39 static const WCHAR Cunc[] = L"\\??\\C:";
40
41 /*
42 * TODO:
43 * - MountMgrQueryDosVolumePaths
44 * - MountMgrQueryVolumePaths
45 * - MountMgrValidateBackPointer
46 * - MountMgrVolumeMountPointCreated
47 * - MountMgrVolumeMountPointDeleted
48 * - ReconcileThisDatabaseWithMasterWorker
49 */
50
51 /*
52 * @implemented
53 */
54 BOOLEAN
55 IsOffline(PUNICODE_STRING SymbolicName)
56 {
57 NTSTATUS Status;
58 ULONG IsOffline, Default;
59 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
60
61 /* Prepare to look in the registry to see if
62 * given volume is offline
63 */
64 RtlZeroMemory(QueryTable, sizeof(QueryTable));
65 QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
66 QueryTable[0].Name = SymbolicName->Buffer;
67 QueryTable[0].EntryContext = &IsOffline;
68 QueryTable[0].DefaultType = REG_DWORD;
69 QueryTable[0].DefaultLength = sizeof(ULONG);
70 QueryTable[0].DefaultData = &Default;
71
72 Default = 0;
73
74 /* Query status */
75 Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
76 OfflinePath,
77 QueryTable,
78 NULL,
79 NULL);
80 if (!NT_SUCCESS(Status))
81 {
82 IsOffline = 0;
83 }
84
85 return (IsOffline != 0);
86 }
87
88 /*
89 * @implemented
90 */
91 BOOLEAN
92 HasDriveLetter(IN PDEVICE_INFORMATION DeviceInformation)
93 {
94 PLIST_ENTRY NextEntry;
95 PSYMLINK_INFORMATION SymlinkInfo;
96
97 /* To have a drive letter, a device must have symbolic links */
98 if (IsListEmpty(&(DeviceInformation->SymbolicLinksListHead)))
99 {
100 return FALSE;
101 }
102
103 /* Browse all the links untill a drive letter is found */
104 NextEntry = &(DeviceInformation->SymbolicLinksListHead);
105 do
106 {
107 SymlinkInfo = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
108
109 if (SymlinkInfo->Online)
110 {
111 if (IsDriveLetter(&(SymlinkInfo->Name)))
112 {
113 return TRUE;
114 }
115 }
116
117 NextEntry = NextEntry->Flink;
118 } while (NextEntry != &(DeviceInformation->SymbolicLinksListHead));
119
120 return FALSE;
121 }
122
123 /*
124 * @implemented
125 */
126 NTSTATUS
127 CreateNewDriveLetterName(OUT PUNICODE_STRING DriveLetter,
128 IN PUNICODE_STRING DeviceName,
129 IN UCHAR Letter,
130 IN PMOUNTDEV_UNIQUE_ID UniqueId OPTIONAL)
131 {
132 NTSTATUS Status = STATUS_UNSUCCESSFUL;
133
134 /* Allocate a big enough buffer to contain the symbolic link */
135 DriveLetter->MaximumLength = sizeof(DosDevices.Buffer) + 3 * sizeof(WCHAR);
136 DriveLetter->Buffer = AllocatePool(sizeof(DosDevices.Buffer) + 3 * sizeof(WCHAR));
137 if (!DriveLetter->Buffer)
138 {
139 return STATUS_INSUFFICIENT_RESOURCES;
140 }
141
142 /* Copy prefix */
143 RtlCopyUnicodeString(DriveLetter, &DosDevices);
144
145 /* Update string to reflect real contents */
146 DriveLetter->Length = sizeof(DosDevices.Buffer) + 2 * sizeof(WCHAR);
147 DriveLetter->Buffer[(sizeof(DosDevices.Buffer) + 2 * sizeof(WCHAR)) / sizeof (WCHAR)] = UNICODE_NULL;
148 DriveLetter->Buffer[(sizeof(DosDevices.Buffer) + sizeof(WCHAR)) / sizeof (WCHAR)] = L':';
149
150 /* If caller wants a no drive entry */
151 if (Letter == (UCHAR)-1)
152 {
153 /* Then, create a no letter entry */
154 CreateNoDriveLetterEntry(UniqueId);
155 FreePool(DriveLetter->Buffer);
156 return STATUS_UNSUCCESSFUL;
157 }
158 else if (Letter)
159 {
160 /* Use the letter given by the caller */
161 DriveLetter->Buffer[sizeof(DosDevices.Buffer) / sizeof(WCHAR)] = (WCHAR)Letter;
162 Status = GlobalCreateSymbolicLink(DriveLetter, DeviceName);
163 if (NT_SUCCESS(Status))
164 {
165 return Status;
166 }
167 }
168
169 /* If caller didn't provide a letter, let's find one for him */
170
171 if (RtlPrefixUnicodeString(&DeviceFloppy, DeviceName, TRUE))
172 {
173 /* If the device is a floppy, start with letter A */
174 Letter = 'A';
175 }
176 else if (RtlPrefixUnicodeString(&DeviceCdRom, DeviceName, TRUE))
177 {
178 /* If the device is a CD-ROM, start with letter D */
179 Letter = 'D';
180 }
181 else
182 {
183 /* Finally, if it's a disk, use C */
184 Letter = 'C';
185 }
186
187 /* Try to affect a letter (up to Z, ofc) until it's possible */
188 for (; Letter <= 'Z'; Letter++)
189 {
190 DriveLetter->Buffer[sizeof(DosDevices.Buffer) / sizeof(WCHAR)] = (WCHAR)Letter;
191 Status = GlobalCreateSymbolicLink(DriveLetter, DeviceName);
192 if (NT_SUCCESS(Status))
193 {
194 return Status;
195 }
196 }
197
198 /* We failed to allocate a letter */
199 FreePool(DriveLetter->Buffer);
200 return Status;
201 }
202
203 /*
204 * @implemented
205 */
206 NTSTATUS
207 QueryDeviceInformation(IN PUNICODE_STRING SymbolicName,
208 OUT PUNICODE_STRING DeviceName OPTIONAL,
209 OUT PMOUNTDEV_UNIQUE_ID * UniqueId OPTIONAL,
210 OUT PBOOLEAN Removable OPTIONAL,
211 OUT PBOOLEAN GptDriveLetter OPTIONAL,
212 OUT PBOOLEAN HasGuid OPTIONAL,
213 IN OUT LPGUID StableGuid OPTIONAL,
214 OUT PBOOLEAN Valid OPTIONAL)
215 {
216 PIRP Irp;
217 USHORT Size;
218 KEVENT Event;
219 NTSTATUS Status;
220 BOOLEAN IsRemovable;
221 PMOUNTDEV_NAME Name;
222 PMOUNTDEV_UNIQUE_ID Id;
223 PFILE_OBJECT FileObject;
224 PIO_STACK_LOCATION Stack;
225 PDEVICE_OBJECT DeviceObject;
226 IO_STATUS_BLOCK IoStatusBlock;
227 PARTITION_INFORMATION_EX PartitionInfo;
228 STORAGE_DEVICE_NUMBER StorageDeviceNumber;
229 VOLUME_GET_GPT_ATTRIBUTES_INFORMATION GptAttributes;
230
231 /* Get device associated with the symbolic name */
232 Status = IoGetDeviceObjectPointer(SymbolicName,
233 FILE_READ_ATTRIBUTES,
234 &FileObject,
235 &DeviceObject);
236 if (!NT_SUCCESS(Status))
237 {
238 return Status;
239 }
240
241 /* The associate FO can't have a file name */
242 if (FileObject->FileName.Length)
243 {
244 ObDereferenceObject(FileObject);
245 return STATUS_OBJECT_NAME_NOT_FOUND;
246 }
247
248 /* Check if it's removable & return to the user (if asked to) */
249 IsRemovable = (FileObject->DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA);
250 if (Removable)
251 {
252 *Removable = IsRemovable;
253 }
254
255 /* Get the attached device */
256 DeviceObject = IoGetAttachedDeviceReference(FileObject->DeviceObject);
257
258 /* If we've been asked for a GPT drive letter */
259 if (GptDriveLetter)
260 {
261 /* Consider it has one */
262 *GptDriveLetter = TRUE;
263
264 if (!IsRemovable)
265 {
266 /* Query the GPT attributes */
267 KeInitializeEvent(&Event, NotificationEvent, FALSE);
268 Irp = IoBuildDeviceIoControlRequest(IOCTL_VOLUME_GET_GPT_ATTRIBUTES,
269 DeviceObject,
270 NULL,
271 0,
272 &GptAttributes,
273 sizeof(GptAttributes),
274 FALSE,
275 &Event,
276 &IoStatusBlock);
277 if (!Irp)
278 {
279 ObDereferenceObject(DeviceObject);
280 ObDereferenceObject(FileObject);
281 return STATUS_INSUFFICIENT_RESOURCES;
282 }
283
284 Status = IoCallDriver(DeviceObject, Irp);
285 if (Status == STATUS_PENDING)
286 {
287 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
288 Status = IoStatusBlock.Status;
289 }
290
291 /* In case of failure, don't fail, that's no vital */
292 if (!NT_SUCCESS(Status))
293 {
294 Status = STATUS_SUCCESS;
295 }
296 /* Check if it has a drive letter */
297 else if (!(GptAttributes.GptAttributes &
298 GPT_BASIC_DATA_ATTRIBUTE_NO_DRIVE_LETTER))
299 {
300 *GptDriveLetter = FALSE;
301 }
302 }
303 }
304
305 /* If caller wants to know if there's valid contents */
306 if (Valid)
307 {
308 /* Suppose it's not OK */
309 *Valid = FALSE;
310
311 if (!IsRemovable)
312 {
313 /* Query partitions information */
314 KeInitializeEvent(&Event, NotificationEvent, FALSE);
315 Irp = IoBuildDeviceIoControlRequest(IOCTL_DISK_GET_PARTITION_INFO_EX,
316 DeviceObject,
317 NULL,
318 0,
319 &PartitionInfo,
320 sizeof(PartitionInfo),
321 FALSE,
322 &Event,
323 &IoStatusBlock);
324 if (!Irp)
325 {
326 ObDereferenceObject(DeviceObject);
327 ObDereferenceObject(FileObject);
328 return STATUS_INSUFFICIENT_RESOURCES;
329 }
330
331 Status = IoCallDriver(DeviceObject, Irp);
332 if (Status == STATUS_PENDING)
333 {
334 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
335 Status = IoStatusBlock.Status;
336 }
337
338 /* Once again here, failure isn't major */
339 if (!NT_SUCCESS(Status))
340 {
341 Status = STATUS_SUCCESS;
342 }
343 /* Verify we know something in */
344 else if (PartitionInfo.PartitionStyle == PARTITION_STYLE_MBR &&
345 IsRecognizedPartition(PartitionInfo.Mbr.PartitionType))
346 {
347 *Valid = TRUE;
348 }
349
350 /* It looks correct, ensure it is & query device number */
351 if (*Valid)
352 {
353 KeInitializeEvent(&Event, NotificationEvent, FALSE);
354 Irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_GET_DEVICE_NUMBER,
355 DeviceObject,
356 NULL,
357 0,
358 &StorageDeviceNumber,
359 sizeof(StorageDeviceNumber),
360 FALSE,
361 &Event,
362 &IoStatusBlock);
363 if (!Irp)
364 {
365 ObDereferenceObject(DeviceObject);
366 ObDereferenceObject(FileObject);
367 return STATUS_INSUFFICIENT_RESOURCES;
368 }
369
370 Status = IoCallDriver(DeviceObject, Irp);
371 if (Status == STATUS_PENDING)
372 {
373 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
374 Status = IoStatusBlock.Status;
375 }
376
377 if (!NT_SUCCESS(Status))
378 {
379 Status = STATUS_SUCCESS;
380 }
381 else
382 {
383 *Valid = FALSE;
384 }
385 }
386 }
387 }
388
389 /* If caller needs device name */
390 if (DeviceName)
391 {
392 /* Allocate a buffer just to request length */
393 Name = AllocatePool(sizeof(MOUNTDEV_NAME));
394 if (!Name)
395 {
396 ObDereferenceObject(DeviceObject);
397 ObDereferenceObject(FileObject);
398 return STATUS_INSUFFICIENT_RESOURCES;
399 }
400
401 /* Query device name */
402 KeInitializeEvent(&Event, NotificationEvent, FALSE);
403 Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME,
404 DeviceObject,
405 NULL,
406 0,
407 Name,
408 sizeof(MOUNTDEV_NAME),
409 FALSE,
410 &Event,
411 &IoStatusBlock);
412 if (!Irp)
413 {
414 FreePool(Name);
415 ObDereferenceObject(DeviceObject);
416 ObDereferenceObject(FileObject);
417 return STATUS_INSUFFICIENT_RESOURCES;
418 }
419
420 Stack = IoGetNextIrpStackLocation(Irp);
421 Stack->FileObject = FileObject;
422
423 Status = IoCallDriver(DeviceObject, Irp);
424 if (Status == STATUS_PENDING)
425 {
426 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
427 Status = IoStatusBlock.Status;
428 }
429
430 /* Now, we've got the correct length */
431 if (Status == STATUS_BUFFER_OVERFLOW)
432 {
433 Size = Name->NameLength + sizeof(MOUNTDEV_NAME);
434
435 FreePool(Name);
436
437 /* Allocate proper size */
438 Name = AllocatePool(Size);
439 if (!Name)
440 {
441 ObDereferenceObject(DeviceObject);
442 ObDereferenceObject(FileObject);
443 return STATUS_INSUFFICIENT_RESOURCES;
444 }
445
446 /* And query name (for real that time) */
447 KeInitializeEvent(&Event, NotificationEvent, FALSE);
448 Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME,
449 DeviceObject,
450 NULL,
451 0,
452 Name,
453 Size,
454 FALSE,
455 &Event,
456 &IoStatusBlock);
457 if (!Irp)
458 {
459 FreePool(Name);
460 ObDereferenceObject(DeviceObject);
461 ObDereferenceObject(FileObject);
462 return STATUS_INSUFFICIENT_RESOURCES;
463 }
464
465 Stack = IoGetNextIrpStackLocation(Irp);
466 Stack->FileObject = FileObject;
467
468 Status = IoCallDriver(DeviceObject, Irp);
469 if (Status == STATUS_PENDING)
470 {
471 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
472 Status = IoStatusBlock.Status;
473 }
474 }
475
476 /* Here we can't fail and assume default value */
477 if (!NT_SUCCESS(Status))
478 {
479 FreePool(Name);
480 ObDereferenceObject(DeviceObject);
481 ObDereferenceObject(FileObject);
482 return Status;
483 }
484
485 /* Copy back found name to the caller */
486 DeviceName->Length = Name->NameLength;
487 DeviceName->MaximumLength = Name->NameLength + sizeof(WCHAR);
488 DeviceName->Buffer = AllocatePool(DeviceName->MaximumLength);
489 if (!DeviceName->Buffer)
490 {
491 FreePool(Name);
492 ObDereferenceObject(DeviceObject);
493 ObDereferenceObject(FileObject);
494 return STATUS_INSUFFICIENT_RESOURCES;
495 }
496
497 RtlCopyMemory(DeviceName->Buffer, Name->Name, Name->NameLength);
498 DeviceName->Buffer[Name->NameLength / sizeof(WCHAR)] = UNICODE_NULL;
499 FreePool(Name);
500 }
501
502 /* If caller wants device unique ID */
503 if (UniqueId)
504 {
505 /* Prepare buffer to probe length */
506 Id = AllocatePool(sizeof(MOUNTDEV_UNIQUE_ID));
507 if (!Id)
508 {
509 ObDereferenceObject(DeviceObject);
510 ObDereferenceObject(FileObject);
511 return STATUS_INSUFFICIENT_RESOURCES;
512 }
513
514 /* Query unique ID length */
515 KeInitializeEvent(&Event, NotificationEvent, FALSE);
516 Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_UNIQUE_ID,
517 DeviceObject,
518 NULL,
519 0,
520 Id,
521 sizeof(MOUNTDEV_UNIQUE_ID),
522 FALSE,
523 &Event,
524 &IoStatusBlock);
525 if (!Irp)
526 {
527 FreePool(Id);
528 ObDereferenceObject(DeviceObject);
529 ObDereferenceObject(FileObject);
530 return STATUS_INSUFFICIENT_RESOURCES;
531 }
532
533 Stack = IoGetNextIrpStackLocation(Irp);
534 Stack->FileObject = FileObject;
535
536 Status = IoCallDriver(DeviceObject, Irp);
537 if (Status == STATUS_PENDING)
538 {
539 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
540 Status = IoStatusBlock.Status;
541 }
542
543 /* Retry with appropriate length */
544 if (Status == STATUS_BUFFER_OVERFLOW)
545 {
546 Size = Id->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID);
547
548 FreePool(Id);
549
550 /* Allocate the correct buffer */
551 Id = AllocatePool(Size);
552 if (!Id)
553 {
554 ObDereferenceObject(DeviceObject);
555 ObDereferenceObject(FileObject);
556 return STATUS_INSUFFICIENT_RESOURCES;
557 }
558
559 /* Query unique ID */
560 KeInitializeEvent(&Event, NotificationEvent, FALSE);
561 Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_DEVICE_NAME,
562 DeviceObject,
563 NULL,
564 0,
565 Id,
566 Size,
567 FALSE,
568 &Event,
569 &IoStatusBlock);
570 if (!Irp)
571 {
572 FreePool(Id);
573 ObDereferenceObject(DeviceObject);
574 ObDereferenceObject(FileObject);
575 return STATUS_INSUFFICIENT_RESOURCES;
576 }
577
578 Stack = IoGetNextIrpStackLocation(Irp);
579 Stack->FileObject = FileObject;
580
581 Status = IoCallDriver(DeviceObject, Irp);
582 if (Status == STATUS_PENDING)
583 {
584 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
585 Status = IoStatusBlock.Status;
586 }
587 }
588
589 /* Hands back unique ID */
590 if (NT_SUCCESS(Status))
591 {
592 *UniqueId = Id;
593 }
594 else
595 {
596 /* In case of failure, also free the rest */
597 FreePool(Id);
598 if (DeviceName->Length)
599 {
600 FreePool(DeviceName->Buffer);
601 }
602
603 ObDereferenceObject(DeviceObject);
604 ObDereferenceObject(FileObject);
605
606 return Status;
607 }
608 }
609
610 /* If user wants to know about GUID */
611 if (HasGuid)
612 {
613 /* Query device stable GUID */
614 KeInitializeEvent(&Event, NotificationEvent, FALSE);
615 Irp = IoBuildDeviceIoControlRequest(IOCTL_MOUNTDEV_QUERY_STABLE_GUID,
616 DeviceObject,
617 NULL,
618 0,
619 StableGuid,
620 sizeof(GUID),
621 FALSE,
622 &Event,
623 &IoStatusBlock);
624 if (!Irp)
625 {
626 ObDereferenceObject(DeviceObject);
627 ObDereferenceObject(FileObject);
628 return STATUS_INSUFFICIENT_RESOURCES;
629 }
630
631 Stack = IoGetNextIrpStackLocation(Irp);
632 Stack->FileObject = FileObject;
633
634 Status = IoCallDriver(DeviceObject, Irp);
635 if (Status == STATUS_PENDING)
636 {
637 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
638 Status = IoStatusBlock.Status;
639 }
640
641 *HasGuid = NT_SUCCESS(Status);
642 }
643
644 ObDereferenceObject(DeviceObject);
645 ObDereferenceObject(FileObject);
646 return Status;
647 }
648
649 /*
650 * @implemented
651 */
652 NTSTATUS
653 FindDeviceInfo(IN PDEVICE_EXTENSION DeviceExtension,
654 IN PUNICODE_STRING SymbolicName,
655 IN BOOLEAN DeviceNameGiven,
656 OUT PDEVICE_INFORMATION * DeviceInformation)
657 {
658 NTSTATUS Status;
659 PLIST_ENTRY NextEntry;
660 UNICODE_STRING DeviceName;
661 PDEVICE_INFORMATION DeviceInfo = NULL;
662
663 /* If a device name was given, use it */
664 if (DeviceNameGiven)
665 {
666 DeviceName.Length = SymbolicName->Length;
667 DeviceName.Buffer = SymbolicName->Buffer;
668 }
669 else
670 {
671 /* Otherwise, query it */
672 Status = QueryDeviceInformation(SymbolicName,
673 &DeviceName,
674 NULL, NULL,
675 NULL, NULL,
676 NULL, NULL);
677 if (!NT_SUCCESS(Status))
678 {
679 return Status;
680 }
681 }
682
683 /* Look for device information matching devive */
684 for (NextEntry = DeviceExtension->DeviceListHead.Flink;
685 NextEntry != &(DeviceExtension->DeviceListHead);
686 NextEntry = NextEntry->Flink)
687 {
688 DeviceInfo = CONTAINING_RECORD(NextEntry,
689 DEVICE_INFORMATION,
690 DeviceListEntry);
691
692 if (RtlEqualUnicodeString(&DeviceName, &(DeviceInfo->DeviceName), TRUE))
693 {
694 break;
695 }
696 }
697
698 /* Release our buffer if required */
699 if (!DeviceNameGiven)
700 {
701 FreePool(DeviceName.Buffer);
702 }
703
704 /* Return found intormation */
705 if (NextEntry == &(DeviceExtension->DeviceListHead))
706 {
707 return STATUS_OBJECT_NAME_NOT_FOUND;
708 }
709
710 *DeviceInformation = DeviceInfo;
711 return STATUS_SUCCESS;
712 }
713
714 /*
715 * @implemented
716 */
717 VOID
718 MountMgrFreeDeadDeviceInfo(IN PDEVICE_INFORMATION DeviceInformation)
719 {
720 FreePool(DeviceInformation->SymbolicName.Buffer);
721 FreePool(DeviceInformation);
722 }
723
724 /*
725 * @implemented
726 */
727 VOID
728 MountMgrFreeMountedDeviceInfo(IN PDEVICE_INFORMATION DeviceInformation)
729 {
730 PLIST_ENTRY NextEntry;
731 PSYMLINK_INFORMATION SymLink;
732 PUNIQUE_ID_REPLICATE UniqueId;
733 PASSOCIATED_DEVICE_ENTRY AssociatedDevice;
734
735 /* Purge symbolic links list */
736 while (!IsListEmpty(&(DeviceInformation->SymbolicLinksListHead)))
737 {
738 NextEntry = RemoveHeadList(&(DeviceInformation->SymbolicLinksListHead));
739 SymLink = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
740
741 GlobalDeleteSymbolicLink(&(SymLink->Name));
742 FreePool(SymLink->Name.Buffer);
743 }
744
745 /* Purge replicated unique IDs list */
746 while (!IsListEmpty(&(DeviceInformation->ReplicatedUniqueIdsListHead)))
747 {
748 NextEntry = RemoveHeadList(&(DeviceInformation->ReplicatedUniqueIdsListHead));
749 UniqueId = CONTAINING_RECORD(NextEntry, UNIQUE_ID_REPLICATE, ReplicatedUniqueIdsListEntry);
750
751 FreePool(UniqueId->UniqueId);
752 FreePool(UniqueId);
753 }
754
755 while (!IsListEmpty(&(DeviceInformation->AssociatedDevicesHead)))
756 {
757 NextEntry = RemoveHeadList(&(DeviceInformation->AssociatedDevicesHead));
758 AssociatedDevice = CONTAINING_RECORD(NextEntry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
759
760 FreePool(AssociatedDevice->String.Buffer);
761 FreePool(AssociatedDevice);
762 }
763
764 /* Free the rest of the buffers */
765 FreePool(DeviceInformation->SymbolicName.Buffer);
766 if (DeviceInformation->KeepLinks)
767 {
768 FreePool(DeviceInformation->UniqueId);
769 }
770 FreePool(DeviceInformation->DeviceName.Buffer);
771
772 /* Finally, stop waiting for notifications for this device */
773 if (DeviceInformation->TargetDeviceNotificationEntry)
774 {
775 IoUnregisterPlugPlayNotification(DeviceInformation->TargetDeviceNotificationEntry);
776 }
777 }
778
779 /*
780 * @implemented
781 */
782 VOID
783 MountMgrFreeSavedLink(IN PSAVED_LINK_INFORMATION SavedLinkInformation)
784 {
785 PLIST_ENTRY NextEntry;
786 PSYMLINK_INFORMATION SymlinkInformation;
787
788 /* For all the saved links */
789 while (!IsListEmpty(&(SavedLinkInformation->SymbolicLinksListHead)))
790 {
791 NextEntry = RemoveHeadList(&(SavedLinkInformation->SymbolicLinksListHead));
792 SymlinkInformation = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
793
794 /* Remove from system & free */
795 GlobalDeleteSymbolicLink(&(SymlinkInformation->Name));
796 FreePool(SymlinkInformation->Name.Buffer);
797 FreePool(SymlinkInformation);
798 }
799
800 /* And free unique ID & entry */
801 FreePool(SavedLinkInformation->UniqueId);
802 FreePool(SavedLinkInformation);
803 }
804
805
806 /*
807 * @implemented
808 */
809 VOID
810 NTAPI
811 MountMgrUnload(IN struct _DRIVER_OBJECT *DriverObject)
812 {
813 PLIST_ENTRY NextEntry;
814 PUNIQUE_ID_WORK_ITEM WorkItem;
815 PDEVICE_EXTENSION DeviceExtension;
816 PDEVICE_INFORMATION DeviceInformation;
817 PSAVED_LINK_INFORMATION SavedLinkInformation;
818
819 UNREFERENCED_PARAMETER(DriverObject);
820
821 /* Don't get notification any longer */
822 IoUnregisterShutdownNotification(gdeviceObject);
823
824 /* Free registry buffer */
825 DeviceExtension = gdeviceObject->DeviceExtension;
826 if (DeviceExtension->RegistryPath.Buffer)
827 {
828 FreePool(DeviceExtension->RegistryPath.Buffer);
829 DeviceExtension->RegistryPath.Buffer = NULL;
830 }
831
832 InterlockedExchange(&Unloading, TRUE);
833
834 KeInitializeEvent(&UnloadEvent, NotificationEvent, FALSE);
835
836 /* Wait for workers to finish */
837 if (InterlockedIncrement(&DeviceExtension->WorkerReferences))
838 {
839 KeReleaseSemaphore(&(DeviceExtension->WorkerSemaphore),
840 IO_NO_INCREMENT, 1, FALSE);
841
842 KeWaitForSingleObject(&UnloadEvent, Executive, KernelMode, FALSE, NULL);
843 }
844 else
845 {
846 InterlockedDecrement(&(DeviceExtension->WorkerReferences));
847 }
848
849 /* Don't get any notification any longer² */
850 IoUnregisterPlugPlayNotification(DeviceExtension->NotificationEntry);
851
852 /* Acquire the driver exclusively */
853 KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode,
854 FALSE, NULL);
855
856 /* Clear offline devices list */
857 while (!IsListEmpty(&(DeviceExtension->OfflineDeviceListHead)))
858 {
859 NextEntry = RemoveHeadList(&(DeviceExtension->OfflineDeviceListHead));
860 DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
861 MountMgrFreeDeadDeviceInfo(DeviceInformation);
862 }
863
864 /* Clear saved links list */
865 while (!IsListEmpty(&(DeviceExtension->SavedLinksListHead)))
866 {
867 NextEntry = RemoveHeadList(&(DeviceExtension->SavedLinksListHead));
868 SavedLinkInformation = CONTAINING_RECORD(NextEntry, SAVED_LINK_INFORMATION, SavedLinksListEntry);
869 MountMgrFreeSavedLink(SavedLinkInformation);
870 }
871
872 /* Clear workers list */
873 while (!IsListEmpty(&(DeviceExtension->UniqueIdWorkerItemListHead)))
874 {
875 NextEntry = RemoveHeadList(&(DeviceExtension->UniqueIdWorkerItemListHead));
876 WorkItem = CONTAINING_RECORD(NextEntry, UNIQUE_ID_WORK_ITEM, UniqueIdWorkerItemListEntry);
877
878 KeResetEvent(&UnloadEvent);
879 WorkItem->Event = &UnloadEvent;
880
881 KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT,
882 1, FALSE);
883
884 IoCancelIrp(WorkItem->Irp);
885 KeWaitForSingleObject(&UnloadEvent, Executive, KernelMode, FALSE, NULL);
886
887 IoFreeIrp(WorkItem->Irp);
888 FreePool(WorkItem->DeviceName.Buffer);
889 FreePool(WorkItem->IrpBuffer);
890 FreePool(WorkItem);
891
892 KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode,
893 FALSE, NULL);
894 }
895
896 /* If we have drive letter data, release */
897 if (DeviceExtension->DriveLetterData)
898 {
899 FreePool(DeviceExtension->DriveLetterData);
900 DeviceExtension->DriveLetterData = NULL;
901 }
902
903 /* Release driver & quit */
904 KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
905
906 GlobalDeleteSymbolicLink(&DosDevicesMount);
907 IoDeleteDevice(gdeviceObject);
908 }
909
910 /*
911 * @implemented
912 */
913 BOOLEAN
914 MountmgrReadNoAutoMount(IN PUNICODE_STRING RegistryPath)
915 {
916 NTSTATUS Status;
917 ULONG Result, Default = 0;
918 RTL_QUERY_REGISTRY_TABLE QueryTable[2];
919
920 RtlZeroMemory(QueryTable, sizeof(QueryTable));
921
922 /* Simply read data from register */
923 QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
924 QueryTable[0].Name = L"NoAutoMount";
925 QueryTable[0].EntryContext = &Result;
926 QueryTable[0].DefaultType = REG_NONE;
927 QueryTable[0].DefaultData = &Default;
928 QueryTable[0].DefaultLength = sizeof(ULONG);
929
930 Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
931 RegistryPath->Buffer,
932 QueryTable,
933 NULL,
934 NULL);
935 if (!NT_SUCCESS(Status))
936 {
937 return (Default != 0);
938 }
939
940 return (Result != 0);
941 }
942
943 /*
944 * @implemented
945 */
946 NTSTATUS
947 MountMgrMountedDeviceArrival(IN PDEVICE_EXTENSION DeviceExtension,
948 IN PUNICODE_STRING SymbolicName,
949 IN BOOLEAN FromVolume)
950 {
951 WCHAR Letter;
952 GUID StableGuid;
953 HANDLE LinkHandle;
954 ULONG SymLinkCount, i;
955 PLIST_ENTRY NextEntry;
956 PUNICODE_STRING SymLinks;
957 NTSTATUS Status, IntStatus;
958 OBJECT_ATTRIBUTES ObjectAttributes;
959 PSYMLINK_INFORMATION SymlinkInformation;
960 PMOUNTDEV_UNIQUE_ID UniqueId, NewUniqueId;
961 PSAVED_LINK_INFORMATION SavedLinkInformation;
962 PDEVICE_INFORMATION DeviceInformation, CurrentDevice;
963 WCHAR CSymLinkBuffer[MAX_PATH], LinkTargetBuffer[MAX_PATH];
964 UNICODE_STRING TargetDeviceName, SuggestedLinkName, DeviceName, VolumeName, DriveLetter, LinkTarget, CSymLink;
965 BOOLEAN HasGuid, HasGptDriveLetter, Valid, UseOnlyIfThereAreNoOtherLinks, IsDrvLetter, IsOff, IsVolumeName, LinkError;
966
967 /* New device = new structure to represent it */
968 DeviceInformation = AllocatePool(sizeof(DEVICE_INFORMATION));
969 if (!DeviceInformation)
970 {
971 return STATUS_INSUFFICIENT_RESOURCES;
972 }
973
974 /* Initialise device structure */
975 RtlZeroMemory(DeviceInformation, sizeof(DEVICE_INFORMATION));
976 InitializeListHead(&(DeviceInformation->SymbolicLinksListHead));
977 InitializeListHead(&(DeviceInformation->ReplicatedUniqueIdsListHead));
978 InitializeListHead(&(DeviceInformation->AssociatedDevicesHead));
979 DeviceInformation->SymbolicName.Length = SymbolicName->Length;
980 DeviceInformation->SymbolicName.MaximumLength = SymbolicName->Length + sizeof(UNICODE_NULL);
981 DeviceInformation->SymbolicName.Buffer = AllocatePool(DeviceInformation->SymbolicName.MaximumLength);
982 if (!DeviceInformation->SymbolicName.Buffer)
983 {
984 FreePool(DeviceInformation);
985 return STATUS_INSUFFICIENT_RESOURCES;
986 }
987
988 /* Copy symbolic name */
989 RtlCopyMemory(DeviceInformation->SymbolicName.Buffer, SymbolicName->Buffer, SymbolicName->Length);
990 DeviceInformation->SymbolicName.Buffer[DeviceInformation->SymbolicName.Length / sizeof(WCHAR)] = UNICODE_NULL;
991 DeviceInformation->Volume = FromVolume;
992 DeviceInformation->DeviceExtension = DeviceExtension;
993
994 /* Query as much data as possible about device */
995 Status = QueryDeviceInformation(SymbolicName,
996 &TargetDeviceName,
997 &UniqueId,
998 &(DeviceInformation->Removable),
999 &HasGptDriveLetter,
1000 &HasGuid,
1001 &StableGuid,
1002 &Valid);
1003 if (!NT_SUCCESS(Status))
1004 {
1005 KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
1006
1007 for (NextEntry = DeviceExtension->OfflineDeviceListHead.Flink;
1008 NextEntry != &(DeviceExtension->OfflineDeviceListHead);
1009 NextEntry = NextEntry->Flink)
1010 {
1011 CurrentDevice = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
1012
1013 if (RtlEqualUnicodeString(&(DeviceInformation->SymbolicName), &(CurrentDevice->SymbolicName), TRUE))
1014 {
1015 break;
1016 }
1017 }
1018
1019 if (NextEntry != &(DeviceExtension->OfflineDeviceListHead))
1020 {
1021 MountMgrFreeDeadDeviceInfo(DeviceInformation);
1022 }
1023 else
1024 {
1025 InsertTailList(&(DeviceExtension->OfflineDeviceListHead), &(DeviceInformation->DeviceListEntry));
1026 }
1027
1028 KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
1029
1030 return Status;
1031 }
1032
1033 /* Save gathered data */
1034 DeviceInformation->UniqueId = UniqueId;
1035 DeviceInformation->DeviceName = TargetDeviceName;
1036 DeviceInformation->KeepLinks = FALSE;
1037
1038 /* If we found system partition, mark it */
1039 if (DeviceExtension->DriveLetterData && UniqueId->UniqueIdLength == DeviceExtension->DriveLetterData->UniqueIdLength)
1040 {
1041 if (RtlCompareMemory(UniqueId->UniqueId, DeviceExtension->DriveLetterData->UniqueId, UniqueId->UniqueIdLength)
1042 == UniqueId->UniqueIdLength)
1043 {
1044 IoSetSystemPartition(&TargetDeviceName);
1045 }
1046 }
1047
1048 /* Check suggested link name */
1049 Status = QuerySuggestedLinkName(&(DeviceInformation->SymbolicName),
1050 &SuggestedLinkName,
1051 &UseOnlyIfThereAreNoOtherLinks);
1052 if (!NT_SUCCESS(Status))
1053 {
1054 SuggestedLinkName.Buffer = NULL;
1055 }
1056
1057 /* If it's OK, set it and save its letter (if any) */
1058 if (SuggestedLinkName.Buffer && IsDriveLetter(&SuggestedLinkName))
1059 {
1060 DeviceInformation->SuggestedDriveLetter = (UCHAR)SuggestedLinkName.Buffer[LETTER_POSITION];
1061 }
1062
1063 /* Acquire driver exclusively */
1064 KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
1065
1066 /* Check if we already have device in to prevent double registration */
1067 for (NextEntry = DeviceExtension->DeviceListHead.Flink;
1068 NextEntry != &(DeviceExtension->DeviceListHead);
1069 NextEntry = NextEntry->Flink)
1070 {
1071 CurrentDevice = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
1072
1073 if (RtlEqualUnicodeString(&(DeviceInformation->DeviceName), &TargetDeviceName, TRUE))
1074 {
1075 break;
1076 }
1077 }
1078
1079 /* If we found it, clear ours, and return success, all correct */
1080 if (NextEntry != &(DeviceExtension->DeviceListHead))
1081 {
1082 if (SuggestedLinkName.Buffer)
1083 {
1084 FreePool(SuggestedLinkName.Buffer);
1085 }
1086
1087 FreePool(UniqueId);
1088 FreePool(TargetDeviceName.Buffer);
1089 FreePool(DeviceInformation->DeviceName.Buffer);
1090 FreePool(DeviceInformation);
1091
1092 KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
1093
1094 return STATUS_SUCCESS;
1095 }
1096
1097 /* Check if there are symlinks associated with our device in registry */
1098 Status = QuerySymbolicLinkNamesFromStorage(DeviceExtension,
1099 DeviceInformation,
1100 (SuggestedLinkName.Buffer) ? &SuggestedLinkName : NULL,
1101 UseOnlyIfThereAreNoOtherLinks,
1102 &SymLinks,
1103 &SymLinkCount,
1104 HasGuid,
1105 &StableGuid);
1106
1107 /* If our device is a CD-ROM */
1108 if (RtlPrefixUnicodeString(&DeviceCdRom, &TargetDeviceName, TRUE))
1109 {
1110 LinkTarget.Length = 0;
1111 LinkTarget.MaximumLength = sizeof(LinkTargetBuffer);
1112 LinkTarget.Buffer = LinkTargetBuffer;
1113
1114 RtlCopyMemory(CSymLinkBuffer, Cunc, sizeof(Cunc));
1115 RtlInitUnicodeString(&CSymLink, CSymLinkBuffer);
1116
1117 /* Start checking all letters that could have been associated */
1118 for (Letter = L'D'; Letter <= L'Z'; Letter++)
1119 {
1120 CSymLink.Buffer[LETTER_POSITION] = Letter;
1121
1122 InitializeObjectAttributes(&ObjectAttributes,
1123 &CSymLink,
1124 OBJ_CASE_INSENSITIVE,
1125 NULL,
1126 NULL);
1127
1128 /* Try to open the associated symlink */
1129 Status = ZwOpenSymbolicLinkObject(&LinkHandle, SYMBOLIC_LINK_QUERY, &ObjectAttributes);
1130 if (!NT_SUCCESS(Status))
1131 {
1132 continue;
1133 }
1134
1135 /* And query its target */
1136 Status = ZwQuerySymbolicLinkObject(LinkHandle, &LinkTarget, NULL);
1137 ZwClose(LinkHandle);
1138
1139 if (!NT_SUCCESS(Status))
1140 {
1141 continue;
1142 }
1143
1144 IntStatus = STATUS_UNSUCCESSFUL;
1145 if (!RtlEqualUnicodeString(&LinkTarget, &DeviceInformation->DeviceName, FALSE))
1146 {
1147 continue;
1148 }
1149
1150 /* This link is matching our device, whereas it's not supposed to have any
1151 * symlink associated.
1152 * Delete it
1153 */
1154 if (!SymLinkCount)
1155 {
1156 IoDeleteSymbolicLink(&CSymLink);
1157 continue;
1158 }
1159
1160 /* Now, for all the symlinks, check for ours */
1161 for (i = 0; i < SymLinkCount; i++)
1162 {
1163 if (IsDriveLetter(&(SymLinks[i])))
1164 {
1165 /* If it exists, that's correct */
1166 if (SymLinks[i].Buffer[LETTER_POSITION] == Letter)
1167 {
1168 IntStatus = STATUS_SUCCESS;
1169 }
1170 }
1171 }
1172
1173 /* Useless link, delete it */
1174 if (IntStatus == STATUS_UNSUCCESSFUL)
1175 {
1176 IoDeleteSymbolicLink(&CSymLink);
1177 }
1178 }
1179 }
1180
1181 /* Suggested name is no longer required */
1182 if (SuggestedLinkName.Buffer)
1183 {
1184 FreePool(SuggestedLinkName.Buffer);
1185 }
1186
1187 /* If if failed, ensure we don't take symlinks into account */
1188 if (!NT_SUCCESS(Status))
1189 {
1190 SymLinks = NULL;
1191 SymLinkCount = 0;
1192 }
1193
1194 /* Now we queried them, remove the symlinks */
1195 SavedLinkInformation = RemoveSavedLinks(DeviceExtension, UniqueId);
1196
1197 IsDrvLetter = FALSE;
1198 IsOff = FALSE;
1199 IsVolumeName = FALSE;
1200 /* For all the symlinks */
1201 for (i = 0; i < SymLinkCount; i++)
1202 {
1203 /* Check if our device is a volume */
1204 if (MOUNTMGR_IS_VOLUME_NAME(&(SymLinks[i])))
1205 {
1206 IsVolumeName = TRUE;
1207 }
1208 /* If it has a drive letter */
1209 else if (IsDriveLetter(&(SymLinks[i])))
1210 {
1211 if (IsDrvLetter)
1212 {
1213 DeleteFromLocalDatabase(&(SymLinks[i]), UniqueId);
1214 continue;
1215 }
1216 else
1217 {
1218 IsDrvLetter = TRUE;
1219 }
1220 }
1221
1222 /* And recreate the symlink to our device */
1223 Status = GlobalCreateSymbolicLink(&(SymLinks[i]), &TargetDeviceName);
1224 if (!NT_SUCCESS(Status))
1225 {
1226 LinkError = TRUE;
1227
1228 if ((SavedLinkInformation && !RedirectSavedLink(SavedLinkInformation, &(SymLinks[i]), &TargetDeviceName)) ||
1229 !SavedLinkInformation)
1230 {
1231 Status = QueryDeviceInformation(&(SymLinks[i]), &DeviceName, NULL, NULL, NULL, NULL, NULL, NULL);
1232 if (NT_SUCCESS(Status))
1233 {
1234 LinkError = RtlEqualUnicodeString(&TargetDeviceName, &DeviceName, TRUE);
1235 FreePool(DeviceName.Buffer);
1236 }
1237
1238 if (!LinkError)
1239 {
1240 if (IsDriveLetter(&(SymLinks[i])))
1241 {
1242 IsDrvLetter = FALSE;
1243 DeleteFromLocalDatabase(&(SymLinks[i]), UniqueId);
1244 }
1245
1246 FreePool(SymLinks[i].Buffer);
1247 continue;
1248 }
1249 }
1250 }
1251
1252 /* Check if was offline */
1253 if (IsOffline(&(SymLinks[i])))
1254 {
1255 IsOff = TRUE;
1256 }
1257
1258 /* Finally, associate this symlink with the device */
1259 SymlinkInformation = AllocatePool(sizeof(SYMLINK_INFORMATION));
1260 if (!SymlinkInformation)
1261 {
1262 GlobalDeleteSymbolicLink(&(SymLinks[i]));
1263 FreePool(SymLinks[i].Buffer);
1264 continue;
1265 }
1266
1267 SymlinkInformation->Name = SymLinks[i];
1268 SymlinkInformation->Online = TRUE;
1269
1270 InsertTailList(&(DeviceInformation->SymbolicLinksListHead),
1271 &(SymlinkInformation->SymbolicLinksListEntry));
1272 }
1273
1274 /* Now, for all the recreated symlinks, notify their recreation */
1275 for (NextEntry = DeviceInformation->SymbolicLinksListHead.Flink;
1276 NextEntry != &(DeviceInformation->SymbolicLinksListHead);
1277 NextEntry = NextEntry->Flink)
1278 {
1279 SymlinkInformation = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
1280
1281 SendLinkCreated(&(SymlinkInformation->Name));
1282 }
1283
1284 /* If we had saved links, it's time to free them */
1285 if (SavedLinkInformation)
1286 {
1287 MountMgrFreeSavedLink(SavedLinkInformation);
1288 }
1289
1290 /* If our device doesn't have a volume name */
1291 if (!IsVolumeName)
1292 {
1293 /* It's time to create one */
1294 Status = CreateNewVolumeName(&VolumeName, NULL);
1295 if (NT_SUCCESS(Status))
1296 {
1297 /* Write it to global database */
1298 RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
1299 DatabasePath,
1300 VolumeName.Buffer,
1301 REG_BINARY,
1302 UniqueId->UniqueId,
1303 UniqueId->UniqueIdLength);
1304
1305 /* And create the symlink */
1306 GlobalCreateSymbolicLink(&VolumeName, &TargetDeviceName);
1307
1308 SymlinkInformation = AllocatePool(sizeof(SYMLINK_INFORMATION));
1309 if (!SymlinkInformation)
1310 {
1311 FreePool(VolumeName.Buffer);
1312 }
1313 /* Finally, associate it with the device and notify creation */
1314 else
1315 {
1316 SymlinkInformation->Name = VolumeName;
1317 SymlinkInformation->Online = TRUE;
1318 InsertTailList(&(DeviceInformation->SymbolicLinksListHead),
1319 &(SymlinkInformation->SymbolicLinksListEntry));
1320
1321 SendLinkCreated(&VolumeName);
1322 }
1323 }
1324 }
1325
1326 /* If we found a drive letter, then, ignore the suggested one */
1327 if (IsDrvLetter)
1328 {
1329 DeviceInformation->SuggestedDriveLetter = 0;
1330 }
1331 /* Else, it's time to set up one */
1332 else if (!DeviceExtension->NoAutoMount && !DeviceInformation->Removable &&
1333 DeviceExtension->AutomaticDriveLetter && HasGptDriveLetter &&
1334 DeviceInformation->SuggestedDriveLetter &&
1335 !HasNoDriveLetterEntry(UniqueId))
1336 {
1337 /* Create a new drive letter */
1338 Status = CreateNewDriveLetterName(&DriveLetter, &TargetDeviceName,
1339 DeviceInformation->SuggestedDriveLetter,
1340 NULL);
1341 if (!NT_SUCCESS(Status))
1342 {
1343 CreateNoDriveLetterEntry(UniqueId);
1344 }
1345 else
1346 {
1347 /* Save it to global database */
1348 RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE,
1349 DatabasePath,
1350 DriveLetter.Buffer,
1351 REG_BINARY,
1352 UniqueId->UniqueId,
1353 UniqueId->UniqueIdLength);
1354
1355 /* Associate it with the device and notify creation */
1356 SymlinkInformation = AllocatePool(sizeof(SYMLINK_INFORMATION));
1357 if (!SymlinkInformation)
1358 {
1359 FreePool(DriveLetter.Buffer);
1360 }
1361 else
1362 {
1363 SymlinkInformation->Name = DriveLetter;
1364 SymlinkInformation->Online = TRUE;
1365 InsertTailList(&(DeviceInformation->SymbolicLinksListHead),
1366 &(SymlinkInformation->SymbolicLinksListEntry));
1367
1368 SendLinkCreated(&DriveLetter);
1369 }
1370 }
1371 }
1372
1373 /* If required, register for notifications about the device */
1374 if (!FromVolume)
1375 {
1376 RegisterForTargetDeviceNotification(DeviceExtension, DeviceInformation);
1377 }
1378
1379 /* Finally, insert the device into our devices list */
1380 InsertTailList(&(DeviceExtension->DeviceListHead), &(DeviceInformation->DeviceListEntry));
1381
1382 /* Copy device unique ID */
1383 NewUniqueId = AllocatePool(UniqueId->UniqueIdLength + sizeof(MOUNTDEV_UNIQUE_ID));
1384 if (NewUniqueId)
1385 {
1386 NewUniqueId->UniqueIdLength = UniqueId->UniqueIdLength;
1387 RtlCopyMemory(NewUniqueId->UniqueId, UniqueId->UniqueId, UniqueId->UniqueIdLength);
1388 }
1389
1390 /* If device's offline or valid, skip its notifications */
1391 if (IsOff || Valid)
1392 {
1393 DeviceInformation->SkipNotifications = TRUE;
1394 }
1395
1396 /* In case device is valid and is set to no automount,
1397 * set it offline.
1398 */
1399 if (DeviceExtension->NoAutoMount || IsDrvLetter)
1400 {
1401 IsOff = !DeviceInformation->SkipNotifications;
1402 }
1403 else
1404 {
1405 IsOff = FALSE;
1406 }
1407
1408 /* Finally, release the exclusive lock */
1409 KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
1410
1411 /* If device is not offline, notify its arrival */
1412 if (!IsOff)
1413 {
1414 SendOnlineNotification(SymbolicName);
1415 }
1416
1417 /* If we had symlinks (from storage), free them */
1418 if (SymLinks)
1419 {
1420 FreePool(SymLinks);
1421 }
1422
1423 /* Notify about unique id change */
1424 if (NewUniqueId)
1425 {
1426 IssueUniqueIdChangeNotify(DeviceExtension, SymbolicName, NewUniqueId);
1427 FreePool(NewUniqueId);
1428 }
1429
1430 /* If this drive was set to have a drive letter automatically
1431 * Now it's back, local databases sync will be required
1432 */
1433 if (DeviceExtension->AutomaticDriveLetter)
1434 {
1435 KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
1436
1437 ReconcileThisDatabaseWithMaster(DeviceExtension, DeviceInformation);
1438
1439 NextEntry = DeviceExtension->DeviceListHead.Flink;
1440 CurrentDevice = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
1441 while (CurrentDevice != DeviceInformation)
1442 {
1443 if (!CurrentDevice->NoDatabase)
1444 {
1445 ReconcileThisDatabaseWithMaster(DeviceExtension, CurrentDevice);
1446 }
1447
1448 NextEntry = NextEntry->Flink;
1449 CurrentDevice = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
1450 }
1451
1452 KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
1453 }
1454
1455 return STATUS_SUCCESS;
1456 }
1457
1458 /*
1459 * @implemented
1460 */
1461 VOID
1462 MountMgrMountedDeviceRemoval(IN PDEVICE_EXTENSION DeviceExtension,
1463 IN PUNICODE_STRING DeviceName)
1464 {
1465 PLIST_ENTRY NextEntry, DeviceEntry;
1466 PUNIQUE_ID_REPLICATE UniqueIdReplicate;
1467 PSYMLINK_INFORMATION SymlinkInformation;
1468 PASSOCIATED_DEVICE_ENTRY AssociatedDevice;
1469 PSAVED_LINK_INFORMATION SavedLinkInformation = NULL;
1470 PDEVICE_INFORMATION DeviceInformation, CurrentDevice;
1471
1472 /* Acquire device exclusively */
1473 KeWaitForSingleObject(&(DeviceExtension->DeviceLock), Executive, KernelMode, FALSE, NULL);
1474
1475 /* Look for the leaving device */
1476 for (NextEntry = DeviceExtension->DeviceListHead.Flink;
1477 NextEntry != &(DeviceExtension->DeviceListHead);
1478 NextEntry = NextEntry->Flink)
1479 {
1480 DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
1481
1482 if (!RtlCompareUnicodeString(&(DeviceInformation->SymbolicName), DeviceName, TRUE))
1483 {
1484 break;
1485 }
1486 }
1487
1488 /* If we found it */
1489 if (NextEntry != &(DeviceExtension->DeviceListHead))
1490 {
1491 /* If it's asked to keep links, then, prepare to save them */
1492 if (DeviceInformation->KeepLinks)
1493 {
1494 SavedLinkInformation = AllocatePool(sizeof(SAVED_LINK_INFORMATION));
1495 if (!SavedLinkInformation)
1496 {
1497 DeviceInformation->KeepLinks = FALSE;
1498 }
1499 }
1500
1501 /* If it's possible (and asked), start to save them */
1502 if (DeviceInformation->KeepLinks)
1503 {
1504 InsertTailList(&(DeviceExtension->SavedLinksListHead), &(SavedLinkInformation->SavedLinksListEntry));
1505 InitializeListHead(&(SavedLinkInformation->SymbolicLinksListHead));
1506 SavedLinkInformation->UniqueId = DeviceInformation->UniqueId;
1507 }
1508
1509 /* For all the symlinks */
1510 while (!IsListEmpty(&(DeviceInformation->SymbolicLinksListHead)))
1511 {
1512 NextEntry = RemoveHeadList(&(DeviceInformation->SymbolicLinksListHead));
1513 SymlinkInformation = CONTAINING_RECORD(NextEntry, SYMLINK_INFORMATION, SymbolicLinksListEntry);
1514
1515 /* If we have to, save the link */
1516 if (DeviceInformation->KeepLinks)
1517 {
1518 InsertTailList(&(SavedLinkInformation->SymbolicLinksListHead), &(SymlinkInformation->SymbolicLinksListEntry));
1519 }
1520 /* Otherwise, just release it */
1521 else
1522 {
1523 GlobalDeleteSymbolicLink(&(SymlinkInformation->Name));
1524 FreePool(SymlinkInformation->Name.Buffer);
1525 FreePool(SymlinkInformation);
1526 }
1527 }
1528
1529 /* Free all the replicated unique IDs */
1530 while (!IsListEmpty(&(DeviceInformation->ReplicatedUniqueIdsListHead)))
1531 {
1532 NextEntry = RemoveHeadList(&(DeviceInformation->ReplicatedUniqueIdsListHead));
1533 UniqueIdReplicate = CONTAINING_RECORD(NextEntry, UNIQUE_ID_REPLICATE, ReplicatedUniqueIdsListEntry);
1534
1535
1536 FreePool(UniqueIdReplicate->UniqueId);
1537 FreePool(UniqueIdReplicate);
1538 }
1539
1540 while (!IsListEmpty(&(DeviceInformation->AssociatedDevicesHead)))
1541 {
1542 NextEntry = RemoveHeadList(&(DeviceInformation->AssociatedDevicesHead));
1543 AssociatedDevice = CONTAINING_RECORD(NextEntry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
1544
1545 DeviceInformation->NoDatabase = TRUE;
1546 FreePool(AssociatedDevice->String.Buffer);
1547 FreePool(AssociatedDevice);
1548 }
1549
1550 /* Remove device from the device list */
1551 RemoveEntryList(&(DeviceInformation->DeviceListEntry));
1552
1553 /* If there are still devices, check if some were associated with ours */
1554 if (!IsListEmpty(&(DeviceInformation->DeviceListEntry)))
1555 {
1556 for (NextEntry = DeviceExtension->DeviceListHead.Flink;
1557 NextEntry != &(DeviceExtension->DeviceListHead);
1558 NextEntry = NextEntry->Flink)
1559 {
1560 CurrentDevice = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
1561
1562 /* And then, remove them */
1563 DeviceEntry = CurrentDevice->AssociatedDevicesHead.Flink;
1564 while (DeviceEntry != &(CurrentDevice->AssociatedDevicesHead))
1565 {
1566 AssociatedDevice = CONTAINING_RECORD(NextEntry, ASSOCIATED_DEVICE_ENTRY, AssociatedDevicesEntry);
1567 DeviceEntry = DeviceEntry->Flink;
1568
1569 if (AssociatedDevice->DeviceInformation != DeviceInformation)
1570 {
1571 continue;
1572 }
1573
1574 RemoveEntryList(&(AssociatedDevice->AssociatedDevicesEntry));
1575 FreePool(AssociatedDevice->String.Buffer);
1576 FreePool(AssociatedDevice);
1577 }
1578 }
1579 }
1580
1581 /* Finally, clean up device name, symbolic name */
1582 FreePool(DeviceInformation->SymbolicName.Buffer);
1583 if (!DeviceInformation->KeepLinks)
1584 {
1585 FreePool(DeviceInformation->UniqueId);
1586 }
1587 FreePool(DeviceInformation->DeviceName.Buffer);
1588
1589 /* Unregister notifications */
1590 if (DeviceInformation->TargetDeviceNotificationEntry)
1591 {
1592 IoUnregisterPlugPlayNotification(DeviceInformation->TargetDeviceNotificationEntry);
1593 }
1594
1595 /* And leave */
1596 FreePool(DeviceInformation);
1597 }
1598 else
1599 {
1600 /* We didn't find device, perhaps because it was offline */
1601 for (NextEntry = DeviceExtension->OfflineDeviceListHead.Flink;
1602 NextEntry != &(DeviceExtension->OfflineDeviceListHead);
1603 NextEntry = NextEntry->Flink)
1604 {
1605 DeviceInformation = CONTAINING_RECORD(NextEntry, DEVICE_INFORMATION, DeviceListEntry);
1606
1607 /* It was, remove it */
1608 if (RtlCompareUnicodeString(&(DeviceInformation->SymbolicName), DeviceName, TRUE) == 0)
1609 {
1610 RemoveEntryList(&(DeviceInformation->DeviceListEntry));
1611 MountMgrFreeDeadDeviceInfo(DeviceInformation);
1612 break;
1613 }
1614 }
1615 }
1616
1617 /* Releave driver */
1618 KeReleaseSemaphore(&(DeviceExtension->DeviceLock), IO_NO_INCREMENT, 1, FALSE);
1619 }
1620
1621 /*
1622 * @implemented
1623 */
1624 NTSTATUS
1625 NTAPI
1626 MountMgrMountedDeviceNotification(IN PVOID NotificationStructure,
1627 IN PVOID Context)
1628 {
1629 BOOLEAN OldState;
1630 PDEVICE_EXTENSION DeviceExtension;
1631 PDEVICE_INTERFACE_CHANGE_NOTIFICATION Notification;
1632
1633 /* Notification for a device arrived */
1634 /* Disable hard errors */
1635 OldState = PsGetThreadHardErrorsAreDisabled(PsGetCurrentThread());
1636 PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), TRUE);
1637
1638 DeviceExtension = Context;
1639 Notification = NotificationStructure;
1640
1641 /* Dispatch according to the event */
1642 if (IsEqualGUID(&(Notification->Event), &GUID_DEVICE_INTERFACE_ARRIVAL))
1643 {
1644 MountMgrMountedDeviceArrival(DeviceExtension, Notification->SymbolicLinkName, FALSE);
1645 }
1646 else if (IsEqualGUID(&(Notification->Event), &GUID_DEVICE_INTERFACE_REMOVAL))
1647 {
1648 MountMgrMountedDeviceRemoval(DeviceExtension, Notification->SymbolicLinkName);
1649 }
1650
1651 /* Reset hard errors */
1652 PsSetThreadHardErrorsAreDisabled(PsGetCurrentThread(), OldState);
1653
1654 return STATUS_SUCCESS;
1655 }
1656
1657 /*
1658 * @implemented
1659 */
1660 NTSTATUS
1661 NTAPI
1662 MountMgrCreateClose(IN PDEVICE_OBJECT DeviceObject,
1663 IN PIRP Irp)
1664 {
1665 PIO_STACK_LOCATION Stack;
1666 NTSTATUS Status = STATUS_SUCCESS;
1667
1668 UNREFERENCED_PARAMETER(DeviceObject);
1669
1670 Stack = IoGetCurrentIrpStackLocation(Irp);
1671
1672 /* Allow driver opening for communication
1673 * as long as it's not taken for a directory
1674 */
1675 if (Stack->MajorFunction == IRP_MJ_CREATE &&
1676 Stack->Parameters.Create.Options & FILE_DIRECTORY_FILE)
1677 {
1678 Status = STATUS_NOT_A_DIRECTORY;
1679 }
1680
1681 Irp->IoStatus.Status = Status;
1682 Irp->IoStatus.Information = 0;
1683 IoCompleteRequest(Irp, IO_NO_INCREMENT);
1684 return Status;
1685 }
1686
1687 /*
1688 * @implemented
1689 */
1690 VOID
1691 NTAPI
1692 MountMgrCancel(IN PDEVICE_OBJECT DeviceObject,
1693 IN PIRP Irp)
1694 {
1695 UNREFERENCED_PARAMETER(DeviceObject);
1696
1697 RemoveEntryList(&(Irp->Tail.Overlay.ListEntry));
1698
1699 IoReleaseCancelSpinLock(Irp->CancelIrql);
1700
1701 Irp->IoStatus.Information = 0;
1702 Irp->IoStatus.Status = STATUS_CANCELLED;
1703 IoCompleteRequest(Irp, IO_NO_INCREMENT);
1704 }
1705
1706 /*
1707 * @implemented
1708 */
1709 NTSTATUS
1710 NTAPI
1711 MountMgrCleanup(IN PDEVICE_OBJECT DeviceObject,
1712 IN PIRP Irp)
1713 {
1714 PIRP ListIrp;
1715 KIRQL OldIrql;
1716 PLIST_ENTRY NextEntry;
1717 PFILE_OBJECT FileObject;
1718 PIO_STACK_LOCATION Stack;
1719 PDEVICE_EXTENSION DeviceExtension;
1720
1721 DeviceExtension = DeviceObject->DeviceExtension;
1722 Stack = IoGetCurrentIrpStackLocation(Irp);
1723 FileObject = Stack->FileObject;
1724
1725 IoAcquireCancelSpinLock(&OldIrql);
1726
1727 /* If IRP list if empty, it's OK */
1728 if (IsListEmpty(&(DeviceExtension->IrpListHead)))
1729 {
1730 IoReleaseCancelSpinLock(OldIrql);
1731
1732 Irp->IoStatus.Status = STATUS_SUCCESS;
1733 Irp->IoStatus.Information = 0;
1734 IoCompleteRequest(Irp, IO_NO_INCREMENT);
1735
1736 return STATUS_SUCCESS;
1737 }
1738
1739 /* Otherwise, cancel all the IRPs */
1740 NextEntry = &(DeviceExtension->IrpListHead);
1741 do
1742 {
1743 ListIrp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry);
1744 if (IoGetCurrentIrpStackLocation(ListIrp)->FileObject == FileObject)
1745 {
1746 ListIrp->Cancel = TRUE;
1747 ListIrp->CancelIrql = OldIrql;
1748 ListIrp->CancelRoutine = NULL;
1749 MountMgrCancel(DeviceObject, ListIrp);
1750
1751 IoAcquireCancelSpinLock(&OldIrql);
1752 }
1753
1754 NextEntry = NextEntry->Flink;
1755 }
1756 while (NextEntry != &(DeviceExtension->IrpListHead));
1757
1758 IoReleaseCancelSpinLock(OldIrql);
1759
1760 Irp->IoStatus.Status = STATUS_SUCCESS;
1761 Irp->IoStatus.Information = 0;
1762 IoCompleteRequest(Irp, IO_NO_INCREMENT);
1763
1764 return STATUS_SUCCESS;
1765 }
1766
1767 /*
1768 * @implemented
1769 */
1770 NTSTATUS
1771 NTAPI
1772 MountMgrShutdown(IN PDEVICE_OBJECT DeviceObject,
1773 IN PIRP Irp)
1774 {
1775 PDEVICE_EXTENSION DeviceExtension;
1776
1777 DeviceExtension = DeviceObject->DeviceExtension;
1778
1779 InterlockedExchange(&Unloading, TRUE);
1780
1781 KeInitializeEvent(&UnloadEvent, NotificationEvent, FALSE);
1782
1783 /* Wait for workers */
1784 if (InterlockedIncrement(&(DeviceExtension->WorkerReferences)))
1785 {
1786 KeReleaseSemaphore(&(DeviceExtension->WorkerSemaphore),
1787 IO_NO_INCREMENT,
1788 1,
1789 FALSE);
1790 KeWaitForSingleObject(&UnloadEvent, Executive, KernelMode, FALSE, NULL);
1791 }
1792 else
1793 {
1794 InterlockedDecrement(&(DeviceExtension->WorkerReferences));
1795 }
1796
1797 Irp->IoStatus.Status = STATUS_SUCCESS;
1798 Irp->IoStatus.Information = 0;
1799 IoCompleteRequest(Irp, IO_NO_INCREMENT);
1800
1801 return STATUS_SUCCESS;
1802 }
1803
1804 /* FUNCTIONS ****************************************************************/
1805
1806 NTSTATUS
1807 NTAPI
1808 DriverEntry(IN PDRIVER_OBJECT DriverObject,
1809 IN PUNICODE_STRING RegistryPath)
1810 {
1811 NTSTATUS Status;
1812 PDEVICE_OBJECT DeviceObject;
1813 PDEVICE_EXTENSION DeviceExtension;
1814
1815 RtlCreateRegistryKey(RTL_REGISTRY_ABSOLUTE, DatabasePath);
1816
1817 Status = IoCreateDevice(DriverObject,
1818 sizeof(DEVICE_EXTENSION),
1819 &DeviceMount,
1820 FILE_DEVICE_NETWORK,
1821 FILE_DEVICE_SECURE_OPEN,
1822 FALSE,
1823 &DeviceObject);
1824 if (!NT_SUCCESS(Status))
1825 {
1826 return Status;
1827 }
1828
1829 DriverObject->DriverUnload = MountMgrUnload;
1830
1831 DeviceExtension = DeviceObject->DeviceExtension;
1832 RtlZeroMemory(DeviceExtension, sizeof(DEVICE_EXTENSION));
1833 DeviceExtension->DeviceObject = DeviceObject;
1834 DeviceExtension->DriverObject = DriverObject;
1835
1836 InitializeListHead(&(DeviceExtension->DeviceListHead));
1837 InitializeListHead(&(DeviceExtension->OfflineDeviceListHead));
1838
1839 KeInitializeSemaphore(&(DeviceExtension->DeviceLock), 1, 1);
1840 KeInitializeSemaphore(&(DeviceExtension->RemoteDatabaseLock), 1, 1);
1841
1842 InitializeListHead(&(DeviceExtension->IrpListHead));
1843 DeviceExtension->EpicNumber = 1;
1844
1845 InitializeListHead(&(DeviceExtension->SavedLinksListHead));
1846
1847 InitializeListHead(&(DeviceExtension->WorkerQueueListHead));
1848 KeInitializeSemaphore(&(DeviceExtension->WorkerSemaphore), 0, MAXLONG);
1849 DeviceExtension->WorkerReferences = -1;
1850 KeInitializeSpinLock(&(DeviceExtension->WorkerLock));
1851
1852 InitializeListHead(&(DeviceExtension->UniqueIdWorkerItemListHead));
1853 InitializeListHead(&(DeviceExtension->OnlineNotificationListHead));
1854 DeviceExtension->OnlineNotificationCount = 1;
1855
1856 DeviceExtension->RegistryPath.Length = RegistryPath->Length;
1857 DeviceExtension->RegistryPath.MaximumLength = RegistryPath->Length + sizeof(WCHAR);
1858 DeviceExtension->RegistryPath.Buffer = AllocatePool(DeviceExtension->RegistryPath.MaximumLength);
1859 if (!DeviceExtension->RegistryPath.Buffer)
1860 {
1861 IoDeleteDevice(DeviceObject);
1862 return STATUS_INSUFFICIENT_RESOURCES;
1863 }
1864
1865 RtlCopyUnicodeString(&(DeviceExtension->RegistryPath), RegistryPath);
1866
1867 DeviceExtension->NoAutoMount = MountmgrReadNoAutoMount(&(DeviceExtension->RegistryPath));
1868
1869 GlobalCreateSymbolicLink(&DosDevicesMount, &DeviceMount);
1870
1871 /* Register for device arrival & removal. Ask to be notified for already
1872 * present devices
1873 */
1874 Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange,
1875 PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
1876 &MountedDevicesGuid,
1877 DriverObject,
1878 MountMgrMountedDeviceNotification,
1879 DeviceObject,
1880 &(DeviceExtension->NotificationEntry));
1881
1882 if (!NT_SUCCESS(Status))
1883 {
1884 IoDeleteDevice(DeviceObject);
1885 return Status;
1886 }
1887
1888 DriverObject->MajorFunction[IRP_MJ_CREATE] =
1889 DriverObject->MajorFunction[IRP_MJ_CLOSE] = MountMgrCreateClose;
1890 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MountMgrDeviceControl;
1891 DriverObject->MajorFunction[IRP_MJ_CLEANUP] = MountMgrCleanup;
1892 DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = MountMgrShutdown;
1893
1894 gdeviceObject = DeviceObject;
1895
1896 Status = IoRegisterShutdownNotification(DeviceObject);
1897 if (!NT_SUCCESS(Status))
1898 {
1899 IoDeleteDevice(DeviceObject);
1900 }
1901
1902 return Status;
1903 }