xref: /reactos/drivers/storage/class/disk/pnp.c (revision 84344399)
1 /*++
2 
3 Copyright (C) Microsoft Corporation, 1991 - 1999
4 
5 Module Name:
6 
7     pnp.c
8 
9 Abstract:
10 
11     SCSI disk class driver
12 
13 Environment:
14 
15     kernel mode only
16 
17 Notes:
18 
19 Revision History:
20 
21 --*/
22 
23 #include "disk.h"
24 
25 
26 #ifdef DEBUG_USE_WPP
27 #include "pnp.tmh"
28 #endif
29 
30 #ifndef __REACTOS__
31 extern PULONG InitSafeBootMode;
32 #else
33 extern NTSYSAPI ULONG InitSafeBootMode;
34 #endif
35 ULONG diskDeviceSequenceNumber = 0;
36 extern BOOLEAN DiskIsPastReinit;
37 
38 
39 #ifdef ALLOC_PRAGMA
40 
41 #pragma alloc_text(PAGE, DiskAddDevice)
42 #pragma alloc_text(PAGE, DiskInitFdo)
43 #pragma alloc_text(PAGE, DiskStartFdo)
44 #pragma alloc_text(PAGE, DiskGenerateDeviceName)
45 #pragma alloc_text(PAGE, DiskCreateSymbolicLinks)
46 #pragma alloc_text(PAGE, DiskDeleteSymbolicLinks)
47 #pragma alloc_text(PAGE, DiskRemoveDevice)
48 #endif
49 
50 
51 NTSTATUS
52 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
53 DiskAddDevice(
54     IN PDRIVER_OBJECT DriverObject,
55     IN PDEVICE_OBJECT PhysicalDeviceObject
56     )
57 
58 /*++
59 
60 Routine Description:
61 
62     This routine gets a port drivers capabilities, obtains the
63     inquiry data, searches the SCSI bus for the port driver and creates
64     the device objects for the disks found.
65 
66 Arguments:
67 
68     DriverObject - Pointer to driver object created by system.
69 
70     Pdo - Device object use to send requests to port driver.
71 
72 Return Value:
73 
74     True is returned if one disk was found and successfully created.
75 
76 --*/
77 
78 {
79     ULONG rootPartitionMountable = FALSE;
80 
81     PCONFIGURATION_INFORMATION configurationInformation;
82     ULONG diskCount;
83 
84     NTSTATUS status;
85 
86     PAGED_CODE();
87 
88     //
89     // See if we should be allowing file systems to mount on partition zero.
90     //
91 
92     TRY {
93         HANDLE deviceKey = NULL;
94 
95         UNICODE_STRING diskKeyName;
96         OBJECT_ATTRIBUTES objectAttributes = {0};
97         HANDLE diskKey;
98 
99         RTL_QUERY_REGISTRY_TABLE queryTable[2] = { 0 };
100 
101         status = IoOpenDeviceRegistryKey(PhysicalDeviceObject,
102                                          PLUGPLAY_REGKEY_DEVICE,
103                                          KEY_READ,
104                                          &deviceKey);
105 
106         if(!NT_SUCCESS(status)) {
107             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "DiskAddDevice: Error %#08lx opening device key "
108                            "for pdo %p\n",
109                         status, PhysicalDeviceObject));
110             LEAVE;
111         }
112 
113         RtlInitUnicodeString(&diskKeyName, L"Disk");
114         InitializeObjectAttributes(&objectAttributes,
115                                    &diskKeyName,
116                                    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
117                                    deviceKey,
118                                    NULL);
119 
120         status = ZwOpenKey(&diskKey, KEY_READ, &objectAttributes);
121         ZwClose(deviceKey);
122 
123         if(!NT_SUCCESS(status)) {
124             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "DiskAddDevice: Error %#08lx opening disk key "
125                            "for pdo %p device key %p\n",
126                         status, PhysicalDeviceObject, deviceKey));
127             LEAVE;
128         }
129 
130         queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_TYPECHECK;
131         queryTable[0].Name = L"RootPartitionMountable";
132         queryTable[0].EntryContext = &(rootPartitionMountable);
133         queryTable[0].DefaultType = (REG_DWORD << RTL_QUERY_REGISTRY_TYPECHECK_SHIFT) | REG_NONE;
134 
135 #ifdef _MSC_VER
136 #pragma prefast(suppress:6309, "We don't have QueryRoutine so Context doesn't make any sense")
137 #endif
138         status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
139                                         diskKey,
140                                         queryTable,
141                                         NULL,
142                                         NULL);
143 
144         if(!NT_SUCCESS(status)) {
145             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "DiskAddDevice: Error %#08lx reading value from "
146                            "disk key %p for pdo %p\n",
147                         status, diskKey, PhysicalDeviceObject));
148         }
149 
150         ZwClose(diskKey);
151 
152     } FINALLY {
153 
154         //
155         // Do nothing.
156         //
157 
158         if(!NT_SUCCESS(status)) {
159             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "DiskAddDevice: Will %sallow file system to mount on "
160                            "partition zero of disk %p\n",
161                         (rootPartitionMountable ? "" : "not "),
162                         PhysicalDeviceObject));
163         }
164     }
165 
166     //
167     // Create device objects for disk
168     //
169 
170     diskCount = 0;
171 
172     status = DiskCreateFdo(
173                  DriverObject,
174                  PhysicalDeviceObject,
175                  &diskCount,
176                  (BOOLEAN) !rootPartitionMountable
177                  );
178 
179     //
180     // Get the number of disks already initialized.
181     //
182 
183     configurationInformation = IoGetConfigurationInformation();
184 
185     if (NT_SUCCESS(status)) {
186 
187         //
188         // Increment system disk device count.
189         //
190 
191         configurationInformation->DiskCount++;
192 
193     }
194 
195     return status;
196 
197 } // end DiskAddDevice()
198 
199 
200 NTSTATUS
201 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
202 DiskInitFdo(
203     IN PDEVICE_OBJECT Fdo
204     )
205 
206 /*++
207 
208 Routine Description:
209 
210     This routine is called to do one-time initialization of new device objects
211 
212 
213 Arguments:
214 
215     Fdo - a pointer to the functional device object for this device
216 
217 Return Value:
218 
219     status
220 
221 --*/
222 
223 {
224     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
225     PDISK_DATA diskData = (PDISK_DATA) fdoExtension->CommonExtension.DriverData;
226 
227     ULONG srbFlags = 0;
228     ULONG timeOut = 0;
229     ULONG bytesPerSector;
230 
231     PULONG dmSkew;
232 
233     NTSTATUS status = STATUS_SUCCESS;
234 
235     PAGED_CODE();
236 
237     //
238     // Build the lookaside list for srb's for the physical disk. Should only
239     // need a couple.  If this fails then we don't have an emergency SRB so
240     // fail the call to initialize.
241     //
242 
243     ClassInitializeSrbLookasideList((PCOMMON_DEVICE_EXTENSION) fdoExtension,
244                                     PARTITION0_LIST_SIZE);
245 
246     if (fdoExtension->DeviceDescriptor->RemovableMedia)
247     {
248         SET_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA);
249     }
250 
251     //
252     // Initialize the srb flags.
253     //
254 
255     //
256     // Because all requests share a common sense buffer, it is possible
257     // for the buffer to be overwritten if the port driver completes
258     // multiple failed requests that require a request sense before the
259     // class driver's completion routine can consume the data in the buffer.
260     // To prevent this, we allow the port driver to allocate a unique sense
261     // buffer each time it needs one.  We are responsible for freeing this
262     // buffer.  This also allows the adapter to be configured to support
263     // additional sense data beyond the minimum 18 bytes.
264     //
265 
266     SET_FLAG(fdoExtension->SrbFlags, SRB_FLAGS_PORT_DRIVER_ALLOCSENSE);
267 
268     if (fdoExtension->DeviceDescriptor->CommandQueueing &&
269         fdoExtension->AdapterDescriptor->CommandQueueing) {
270 
271         SET_FLAG(fdoExtension->SrbFlags, SRB_FLAGS_QUEUE_ACTION_ENABLE);
272 
273     }
274 
275     //
276     // Look for controllers that require special flags.
277     //
278 
279     ClassScanForSpecial(fdoExtension, DiskBadControllers, DiskSetSpecialHacks);
280 
281     //
282     // Clear buffer for drive geometry.
283     //
284 
285     RtlZeroMemory(&(fdoExtension->DiskGeometry),
286                   sizeof(DISK_GEOMETRY));
287 
288     //
289     // Allocate request sense buffer.
290     //
291 
292     fdoExtension->SenseData = ExAllocatePoolWithTag(NonPagedPoolNxCacheAligned,
293                                                     SENSE_BUFFER_SIZE_EX,
294                                                     DISK_TAG_START);
295 
296     if (fdoExtension->SenseData == NULL) {
297 
298         //
299         // The buffer can not be allocated.
300         //
301 
302         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "DiskInitFdo: Can not allocate request sense buffer\n"));
303 
304         status = STATUS_INSUFFICIENT_RESOURCES;
305         return status;
306     }
307 
308     //
309     // Set the buffer size of SenseData
310     //
311 
312     fdoExtension->SenseDataLength = SENSE_BUFFER_SIZE_EX;
313 
314     //
315     // Physical device object will describe the entire
316     // device, starting at byte offset 0.
317     //
318 
319     fdoExtension->CommonExtension.StartingOffset.QuadPart = (LONGLONG)(0);
320 
321     //
322     // Set timeout value in seconds.
323     //
324     if ( (fdoExtension->MiniportDescriptor != NULL) &&
325          (fdoExtension->MiniportDescriptor->IoTimeoutValue > 0) ) {
326         //
327         // use the value set by Storport miniport driver
328         //
329         fdoExtension->TimeOutValue = fdoExtension->MiniportDescriptor->IoTimeoutValue;
330     } else {
331         //
332         // get timeout value from registry
333         //
334         timeOut = ClassQueryTimeOutRegistryValue(Fdo);
335 
336         if (timeOut) {
337             fdoExtension->TimeOutValue = timeOut;
338         } else {
339             fdoExtension->TimeOutValue = SCSI_DISK_TIMEOUT;
340         }
341     }
342     //
343     // If this is a removable drive, build an entry in devicemap\scsi
344     // indicating it's physicaldriveN name, set up the appropriate
345     // update partitions routine and set the flags correctly.
346     // note: only do this after the timeout value is set, above.
347     //
348 
349     if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
350 
351         ClassUpdateInformationInRegistry( Fdo,
352                                           "PhysicalDrive",
353                                           fdoExtension->DeviceNumber,
354                                           NULL,
355                                           0);
356         //
357         // Enable media change notification for removable disks
358         //
359         ClassInitializeMediaChangeDetection(fdoExtension,
360                                             (PUCHAR)"Disk");
361 
362     } else {
363 
364         SET_FLAG(fdoExtension->DeviceFlags, DEV_SAFE_START_UNIT);
365         SET_FLAG(fdoExtension->SrbFlags, SRB_FLAGS_NO_QUEUE_FREEZE);
366 
367     }
368 
369     //
370     // The commands we send during the init could cause the flags to change
371     // in case of any error.  Save the SRB flags locally and restore it at
372     // the end of this function, so that the class driver can get it.
373     //
374 
375     srbFlags = fdoExtension->SrbFlags;
376 
377 
378     //
379     // Read the drive capacity.  Don't use the disk version of the routine here
380     // since we don't know the disk signature yet - the disk version will
381     // attempt to determine the BIOS reported geometry.
382     //
383 
384     (VOID)ClassReadDriveCapacity(Fdo);
385 
386     //
387     // Set up sector size fields.
388     //
389     // Stack variables will be used to update
390     // the partition device extensions.
391     //
392     // The device extension field SectorShift is
393     // used to calculate sectors in I/O transfers.
394     //
395     // The DiskGeometry structure is used to service
396     // IOCTls used by the format utility.
397     //
398 
399     bytesPerSector = fdoExtension->DiskGeometry.BytesPerSector;
400 
401     //
402     // Make sure sector size is not zero.
403     //
404 
405     if (bytesPerSector == 0) {
406 
407         //
408         // Default sector size for disk is 512.
409         //
410 
411         bytesPerSector = fdoExtension->DiskGeometry.BytesPerSector = 512;
412         fdoExtension->SectorShift = 9;
413     }
414 
415     //
416     // Determine is DM Driver is loaded on an IDE drive that is
417     // under control of Atapi - this could be either a crashdump or
418     // an Atapi device is sharing the controller with an IDE disk.
419     //
420 
421     HalExamineMBR(fdoExtension->CommonExtension.DeviceObject,
422                   fdoExtension->DiskGeometry.BytesPerSector,
423                   (ULONG)0x54,
424                   (PVOID *)&dmSkew);
425 
426     if (dmSkew) {
427 
428         //
429         // Update the device extension, so that the call to IoReadPartitionTable
430         // will get the correct information. Any I/O to this disk will have
431         // to be skewed by *dmSkew sectors aka DMByteSkew.
432         //
433 
434         fdoExtension->DMSkew     = *dmSkew;
435         fdoExtension->DMActive   = TRUE;
436         fdoExtension->DMByteSkew = fdoExtension->DMSkew * bytesPerSector;
437 
438         FREE_POOL(dmSkew);
439     }
440 
441 #if defined(_X86_) || defined(_AMD64_)
442 
443     //
444     // Try to read the signature off the disk and determine the correct drive
445     // geometry based on that.  This requires rereading the disk size to get
446     // the cylinder count updated correctly.
447     //
448 
449     if(!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
450 
451         DiskReadSignature(Fdo);
452         DiskReadDriveCapacity(Fdo);
453 
454         if (diskData->GeometrySource == DiskGeometryUnknown)
455         {
456             //
457             // Neither the  BIOS  nor the port driver could provide us with a  reliable
458             // geometry.  Before we use the default,  look to see if it was partitioned
459             // under Windows NT4 [or earlier] and apply the one that was used back then
460             //
461 
462             if (DiskIsNT4Geometry(fdoExtension))
463             {
464                 diskData->RealGeometry = fdoExtension->DiskGeometry;
465                 diskData->RealGeometry.SectorsPerTrack   = 0x20;
466                 diskData->RealGeometry.TracksPerCylinder = 0x40;
467                 fdoExtension->DiskGeometry = diskData->RealGeometry;
468 
469                 diskData->GeometrySource = DiskGeometryFromNT4;
470             }
471         }
472     }
473 
474 #endif
475 
476     DiskCreateSymbolicLinks(Fdo);
477 
478     //
479     // Get the SCSI address if it's available for use with SMART ioctls.
480     // SMART ioctls are used for failure prediction, so we need to get
481     // the SCSI address before initializing failure prediction.
482     //
483 
484     {
485         PIRP irp;
486         KEVENT event;
487         IO_STATUS_BLOCK statusBlock = { 0 };
488 
489         KeInitializeEvent(&event, SynchronizationEvent, FALSE);
490 
491         irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_GET_ADDRESS,
492                                             fdoExtension->CommonExtension.LowerDeviceObject,
493                                             NULL,
494                                             0L,
495                                             &(diskData->ScsiAddress),
496                                             sizeof(SCSI_ADDRESS),
497                                             FALSE,
498                                             &event,
499                                             &statusBlock);
500 
501         status = STATUS_UNSUCCESSFUL;
502 
503         if(irp != NULL) {
504 
505             status = IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
506 
507             if(status == STATUS_PENDING) {
508                 KeWaitForSingleObject(&event,
509                                       Executive,
510                                       KernelMode,
511                                       FALSE,
512                                       NULL);
513                 status = statusBlock.Status;
514             }
515         }
516     }
517 
518     //
519     // Determine the type of disk and enable failure prediction in the hardware
520     // and enable failure prediction polling.
521     //
522 
523     if (InitSafeBootMode == 0) // __REACTOS__
524     {
525         DiskDetectFailurePrediction(fdoExtension,
526                                     &diskData->FailurePredictionCapability,
527                                     NT_SUCCESS(status));
528 
529         if (diskData->FailurePredictionCapability != FailurePredictionNone)
530         {
531             //
532             // Cool, we've got some sort of failure prediction, enable it
533             // at the hardware and then enable polling for it
534             //
535 
536             //
537             // By default we allow performance to be degradeded if failure
538             // prediction is enabled.
539             //
540 
541             diskData->AllowFPPerfHit = TRUE;
542 
543             //
544             // Enable polling only after Atapi and SBP2 add support for the new
545             // SRB flag that indicates that the request should not reset the
546             // drive spin down idle timer.
547             //
548 
549             status = DiskEnableDisableFailurePredictPolling(fdoExtension,
550                                           TRUE,
551                                           DISK_DEFAULT_FAILURE_POLLING_PERIOD);
552 
553             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "DiskInitFdo: Failure Prediction Poll enabled as "
554                            "%d for device %p, Status = %lx\n",
555                      diskData->FailurePredictionCapability,
556                      Fdo,
557                      status));
558         }
559     } else {
560 
561         //
562         // In safe boot mode we do not enable failure prediction, as perhaps
563         // it is the reason why normal boot does not work
564         //
565 
566         diskData->FailurePredictionCapability = FailurePredictionNone;
567 
568     }
569 
570     //
571     // Initialize the verify mutex
572     //
573 
574     KeInitializeMutex(&diskData->VerifyMutex, MAX_SECTORS_PER_VERIFY);
575 
576     //
577     // Initialize the flush group context
578     //
579 
580     RtlZeroMemory(&diskData->FlushContext, sizeof(DISK_GROUP_CONTEXT));
581 
582     InitializeListHead(&diskData->FlushContext.CurrList);
583     InitializeListHead(&diskData->FlushContext.NextList);
584 
585     KeInitializeSpinLock(&diskData->FlushContext.Spinlock);
586     KeInitializeEvent(&diskData->FlushContext.Event, SynchronizationEvent, FALSE);
587 
588 
589     //
590     // Restore the saved value
591     //
592     fdoExtension->SrbFlags = srbFlags;
593 
594     return STATUS_SUCCESS;
595 
596 } // end DiskInitFdo()
597 
598 NTSTATUS
599 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
600 DiskStopDevice(
601     IN PDEVICE_OBJECT DeviceObject,
602     IN UCHAR Type
603     )
604 
605 {
606     UNREFERENCED_PARAMETER(DeviceObject);
607     UNREFERENCED_PARAMETER(Type);
608     return STATUS_SUCCESS;
609 }
610 
611 NTSTATUS
612 DiskGenerateDeviceName(
613     IN ULONG DeviceNumber,
614     OUT PCCHAR *RawName
615     )
616 
617 /*++
618 
619 Routine Description:
620 
621     This routine will allocate a unicode string buffer and then fill it in
622     with a generated name for the specified device object.
623 
624     It is the responsibility of the user to allocate a UNICODE_STRING structure
625     to pass in and to free UnicodeName->Buffer when done with it.
626 
627 Arguments:
628 
629     DeviceObject - a pointer to the device object
630 
631     UnicodeName - a unicode string to put the name buffer into
632 
633 Return Value:
634 
635     status
636 
637 --*/
638 
639 #define FDO_NAME_FORMAT "\\Device\\Harddisk%d\\DR%d"
640 
641 {
642     CHAR rawName[64] = { 0 };
643     NTSTATUS status;
644 
645     PAGED_CODE();
646 
647         status = RtlStringCchPrintfA(rawName, sizeof(rawName) - 1, FDO_NAME_FORMAT, DeviceNumber,
648                                     diskDeviceSequenceNumber++);
649         if (!NT_SUCCESS(status)) {
650             TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "DiskGenerateDeviceName: Format FDO name failed with error: 0x%X\n", status));
651             return status;
652         }
653 
654     *RawName = ExAllocatePoolWithTag(PagedPool,
655                                      strlen(rawName) + 1,
656                                      DISK_TAG_NAME);
657 
658     if(*RawName == NULL) {
659         return STATUS_INSUFFICIENT_RESOURCES;
660     }
661 
662     status = RtlStringCchCopyA(*RawName, strlen(rawName) + 1, rawName);
663     if (!NT_SUCCESS(status)) {
664         TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_PNP, "DiskGenerateDeviceName: Device name copy failed with error: 0x%X\n", status));
665         FREE_POOL(*RawName);
666         return status;
667     }
668 
669     TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "DiskGenerateDeviceName: generated \"%s\"\n", rawName));
670 
671     return STATUS_SUCCESS;
672 }
673 
674 
675 VOID
676 DiskCreateSymbolicLinks(
677     IN PDEVICE_OBJECT DeviceObject
678     )
679 
680 /*++
681 
682 Routine Description:
683 
684     This routine will generate a symbolic link for the specified device object
685     using the well known form \\Device\HarddiskX\PartitionY, where X and Y is
686     always 0 which represents the entire disk object.
687 
688     This routine will not try to delete any previous symbolic link for the
689     same generated name - the caller must make sure the symbolic link has
690     been broken before calling this routine.
691 
692 Arguments:
693 
694     DeviceObject - the device object to make a well known name for
695 
696 Return Value:
697 
698     STATUS
699 
700 --*/
701 
702 {
703     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
704     PDISK_DATA diskData = commonExtension->DriverData;
705 
706     WCHAR wideSourceName[64] = { 0 };
707     UNICODE_STRING unicodeSourceName;
708 
709     NTSTATUS status;
710 
711     PAGED_CODE();
712 
713     //
714     // Build the destination for the link first using the device name
715     // stored in the device object
716     //
717 
718     NT_ASSERT(commonExtension->DeviceName.Buffer);
719 
720     if(!diskData->LinkStatus.WellKnownNameCreated) {
721         //
722         // Put together the source name using the partition and device number
723         // in the device extension and disk data segment
724         //
725 
726         status = RtlStringCchPrintfW(wideSourceName, sizeof(wideSourceName) / sizeof(wideSourceName[0]) - 1,
727                                      L"\\Device\\Harddisk%d\\Partition0",
728                                      commonExtension->PartitionZeroExtension->DeviceNumber);
729 
730         if (NT_SUCCESS(status)) {
731 
732             RtlInitUnicodeString(&unicodeSourceName, wideSourceName);
733 
734             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "DiskCreateSymbolicLink: Linking %wZ to %wZ\n",
735                        &unicodeSourceName,
736                        &commonExtension->DeviceName));
737 
738             status = IoCreateSymbolicLink(&unicodeSourceName,
739                                           &commonExtension->DeviceName);
740 
741             if(NT_SUCCESS(status)){
742                 diskData->LinkStatus.WellKnownNameCreated = TRUE;
743             }
744         }
745     }
746 
747     if (!diskData->LinkStatus.PhysicalDriveLinkCreated) {
748 
749         //
750         // Create a physical drive N link using the device number we saved
751         // away during AddDevice.
752         //
753 
754         status = RtlStringCchPrintfW(wideSourceName, sizeof(wideSourceName) / sizeof(wideSourceName[0]) - 1,
755                                      L"\\DosDevices\\PhysicalDrive%d",
756                                      commonExtension->PartitionZeroExtension->DeviceNumber);
757         if (NT_SUCCESS(status)) {
758 
759             RtlInitUnicodeString(&unicodeSourceName, wideSourceName);
760 
761             TracePrint((TRACE_LEVEL_INFORMATION, TRACE_FLAG_PNP, "DiskCreateSymbolicLink: Linking %wZ to %wZ\n",
762                         &unicodeSourceName,
763                         &(commonExtension->DeviceName)));
764 
765             status = IoCreateSymbolicLink(&unicodeSourceName,
766                                           &(commonExtension->DeviceName));
767 
768             if(NT_SUCCESS(status)) {
769                 diskData->LinkStatus.PhysicalDriveLinkCreated = TRUE;
770             }
771         }
772     }
773 
774 
775     return;
776 }
777 
778 
779 VOID
780 DiskDeleteSymbolicLinks(
781     IN PDEVICE_OBJECT DeviceObject
782     )
783 
784 /*++
785 
786 Routine Description:
787 
788     This routine will delete the well known name (symlink) for the specified
789     device.  It generates the link name using information stored in the
790     device extension
791 
792 Arguments:
793 
794     DeviceObject - the device object we are unlinking
795 
796 Return Value:
797 
798     status
799 
800 --*/
801 
802 {
803     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
804     PDISK_DATA diskData = commonExtension->DriverData;
805 
806     WCHAR wideLinkName[64] = { 0 };
807     UNICODE_STRING unicodeLinkName;
808     NTSTATUS status;
809 
810     PAGED_CODE();
811 
812     if(diskData->LinkStatus.WellKnownNameCreated) {
813 
814         status = RtlStringCchPrintfW(wideLinkName, sizeof(wideLinkName) / sizeof(wideLinkName[0]) - 1,
815                                     L"\\Device\\Harddisk%d\\Partition0",
816                                     commonExtension->PartitionZeroExtension->DeviceNumber);
817         if (NT_SUCCESS(status)) {
818             RtlInitUnicodeString(&unicodeLinkName, wideLinkName);
819             IoDeleteSymbolicLink(&unicodeLinkName);
820         }
821         diskData->LinkStatus.WellKnownNameCreated = FALSE;
822     }
823 
824     if(diskData->LinkStatus.PhysicalDriveLinkCreated) {
825 
826         status = RtlStringCchPrintfW(wideLinkName, sizeof(wideLinkName) / sizeof(wideLinkName[0]) - 1,
827                                     L"\\DosDevices\\PhysicalDrive%d",
828                                     commonExtension->PartitionZeroExtension->DeviceNumber);
829         if (NT_SUCCESS(status)) {
830             RtlInitUnicodeString(&unicodeLinkName, wideLinkName);
831             IoDeleteSymbolicLink(&unicodeLinkName);
832         }
833         diskData->LinkStatus.PhysicalDriveLinkCreated = FALSE;
834     }
835 
836 
837     return;
838 }
839 
840 
841 NTSTATUS
842 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
843 DiskRemoveDevice(
844     IN PDEVICE_OBJECT DeviceObject,
845     IN UCHAR Type
846     )
847 
848 /*++
849 
850 Routine Description:
851 
852     This routine will release any resources the device may have allocated for
853     this device object and return.
854 
855 Arguments:
856 
857     DeviceObject - the device object being removed
858 
859 Return Value:
860 
861     status
862 
863 --*/
864 
865 {
866     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
867     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
868 
869     PAGED_CODE();
870 
871     //
872     // Handle query and cancel
873     //
874 
875     if((Type == IRP_MN_QUERY_REMOVE_DEVICE) ||
876        (Type == IRP_MN_CANCEL_REMOVE_DEVICE)) {
877         return STATUS_SUCCESS;
878     }
879 
880     //
881     // Delete our object directory.
882     //
883 
884     if(fdoExtension->DeviceDirectory != NULL) {
885         ZwMakeTemporaryObject(fdoExtension->DeviceDirectory);
886         ZwClose(fdoExtension->DeviceDirectory);
887         fdoExtension->DeviceDirectory = NULL;
888     }
889 
890     if(Type == IRP_MN_REMOVE_DEVICE) {
891 
892         FREE_POOL(fdoExtension->SenseData);
893 
894         IoGetConfigurationInformation()->DiskCount--;
895 
896     }
897 
898     DiskDeleteSymbolicLinks(DeviceObject);
899 
900     if (Type == IRP_MN_REMOVE_DEVICE)
901     {
902         ClassDeleteSrbLookasideList(commonExtension);
903     }
904 
905     return STATUS_SUCCESS;
906 }
907 
908 
909 NTSTATUS
910 NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
911 DiskStartFdo(
912     IN PDEVICE_OBJECT Fdo
913     )
914 
915 /*++
916 
917 Routine Description:
918 
919     This routine will query the underlying device for any information necessary
920     to complete initialization of the device.  This will include physical
921     disk geometry, mode sense information and such.
922 
923     This routine does not perform partition enumeration - that is left to the
924     re-enumeration routine
925 
926     If this routine fails it will return an error value.  It does not clean up
927     any resources - that is left for the Stop/Remove routine.
928 
929 Arguments:
930 
931     Fdo - a pointer to the functional device object for this device
932 
933 Return Value:
934 
935     status
936 
937 --*/
938 
939 {
940     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
941     PCOMMON_DEVICE_EXTENSION commonExtension = &(fdoExtension->CommonExtension);
942     PDISK_DATA diskData = commonExtension->DriverData;
943     STORAGE_HOTPLUG_INFO hotplugInfo = { 0 };
944     DISK_CACHE_INFORMATION cacheInfo = { 0 };
945     ULONG isPowerProtected = 0;
946     NTSTATUS status;
947 
948     PAGED_CODE();
949 
950     //
951     // Get the hotplug information, so we can turn off write cache if needed
952     //
953     // NOTE: Capabilities info is not good enough to determine hotplugedness
954     //       as  we cannot determine device relations  information and other
955     //       dependencies. Get the hotplug info instead
956     //
957 
958     {
959         PIRP irp;
960         KEVENT event;
961         IO_STATUS_BLOCK statusBlock = { 0 };
962 
963         KeInitializeEvent(&event, SynchronizationEvent, FALSE);
964 
965         irp = IoBuildDeviceIoControlRequest(IOCTL_STORAGE_GET_HOTPLUG_INFO,
966                                             Fdo,
967                                             NULL,
968                                             0L,
969                                             &hotplugInfo,
970                                             sizeof(STORAGE_HOTPLUG_INFO),
971                                             FALSE,
972                                             &event,
973                                             &statusBlock);
974 
975         if (irp != NULL) {
976 
977             // send to self -- classpnp handles this
978             status = IoCallDriver(Fdo, irp);
979             if (status == STATUS_PENDING) {
980                 KeWaitForSingleObject(&event,
981                                       Executive,
982                                       KernelMode,
983                                       FALSE,
984                                       NULL);
985 
986                 status = statusBlock.Status;
987             }
988             NT_ASSERT(NT_SUCCESS(status));
989         }
990     }
991 
992     //
993     // Clear the DEV_WRITE_CACHE flag now  and set
994     // it below only if we read that from the disk
995     //
996     CLEAR_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE);
997     ADJUST_FUA_FLAG(fdoExtension);
998 
999     diskData->WriteCacheOverride = DiskWriteCacheDefault;
1000 
1001     //
1002     // Look into the registry to  see if the user
1003     // has chosen to override the default setting
1004     //
1005     ClassGetDeviceParameter(fdoExtension,
1006                             DiskDeviceParameterSubkey,
1007                             DiskDeviceUserWriteCacheSetting,
1008                             (PULONG)&diskData->WriteCacheOverride);
1009 
1010     if (diskData->WriteCacheOverride == DiskWriteCacheDefault)
1011     {
1012         //
1013         // The user has not overridden the default settings
1014         //
1015         if (TEST_FLAG(fdoExtension->ScanForSpecialFlags, CLASS_SPECIAL_DISABLE_WRITE_CACHE))
1016         {
1017             //
1018             // This flag indicates that we have faulty firmware and this
1019             // may cause the filesystem to refuse to mount on this media
1020             //
1021             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "DiskStartFdo: Turning off write cache for %p due to a firmware issue\n", Fdo));
1022 
1023             diskData->WriteCacheOverride = DiskWriteCacheDisable;
1024         }
1025         else if (hotplugInfo.DeviceHotplug && !hotplugInfo.WriteCacheEnableOverride)
1026         {
1027             //
1028             // This flag indicates that the device is hotpluggable making it unsafe to enable caching
1029             //
1030             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "DiskStartFdo: Turning off write cache for %p due to hotpluggable device\n", Fdo));
1031 
1032             diskData->WriteCacheOverride = DiskWriteCacheDisable;
1033         }
1034         else if (hotplugInfo.MediaHotplug)
1035         {
1036             //
1037             // This flag indicates that the media in the device cannot be reliably locked
1038             //
1039             TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_PNP, "DiskStartFdo: Turning off write cache for %p due to unlockable media\n", Fdo));
1040 
1041             diskData->WriteCacheOverride = DiskWriteCacheDisable;
1042         }
1043         else
1044         {
1045             //
1046             // Even though the device does  not seem to have any obvious problems
1047             // we leave it to the user to modify the previous write cache setting
1048             //
1049         }
1050     }
1051 
1052     //
1053     // Query the disk to see if write cache is enabled
1054     // and  set the DEV_WRITE_CACHE flag appropriately
1055     //
1056 
1057     status = DiskGetCacheInformation(fdoExtension, &cacheInfo);
1058 
1059     if (NT_SUCCESS(status))
1060     {
1061         if (cacheInfo.WriteCacheEnabled == TRUE)
1062         {
1063             SET_FLAG(fdoExtension->DeviceFlags, DEV_WRITE_CACHE);
1064             ADJUST_FUA_FLAG(fdoExtension);
1065 
1066             if (diskData->WriteCacheOverride == DiskWriteCacheDisable)
1067             {
1068                 //
1069                 // Write cache is currently enabled on this
1070                 // device, but we would like to turn it off
1071                 //
1072                 cacheInfo.WriteCacheEnabled = FALSE;
1073 
1074                 DiskSetCacheInformation(fdoExtension, &cacheInfo);
1075             }
1076         }
1077         else
1078         {
1079             if (diskData->WriteCacheOverride == DiskWriteCacheEnable)
1080             {
1081                 //
1082                 // Write cache is currently disabled on this
1083                 // device, but we  would  like to turn it on
1084                 //
1085                 cacheInfo.WriteCacheEnabled = TRUE;
1086 
1087                 DiskSetCacheInformation(fdoExtension, &cacheInfo);
1088             }
1089         }
1090     }
1091 
1092     //
1093     // Query the registry to see if this disk is power-protected or not
1094     //
1095 
1096     CLEAR_FLAG(fdoExtension->DeviceFlags, DEV_POWER_PROTECTED);
1097 
1098     ClassGetDeviceParameter(fdoExtension, DiskDeviceParameterSubkey, DiskDeviceCacheIsPowerProtected, &isPowerProtected);
1099 
1100     if (isPowerProtected == 1)
1101     {
1102         SET_FLAG(fdoExtension->DeviceFlags, DEV_POWER_PROTECTED);
1103     }
1104 
1105     ADJUST_FUA_FLAG(fdoExtension);
1106 
1107     return STATUS_SUCCESS;
1108 
1109 } // end DiskStartFdo()
1110 
1111