1 /*++
2 
3 Copyright (C) Microsoft Corporation, 1991 - 1999
4 
5 Module Name:
6 
7     class.c
8 
9 Abstract:
10 
11     SCSI class driver routines
12 
13 Environment:
14 
15     kernel mode only
16 
17 Notes:
18 
19 
20 Revision History:
21 
22 --*/
23 
24 #include "classp.h"
25 
26 ULONG BreakOnClose = 0;
27 
28 PCSTR LockTypeStrings[] = {
29     "Simple",
30     "Secure",
31     "Internal"
32 };
33 
34 
35 PFILE_OBJECT_EXTENSION
36 NTAPI
37 ClasspGetFsContext(
38     IN PCOMMON_DEVICE_EXTENSION CommonExtension,
39     IN PFILE_OBJECT FileObject
40     );
41 
42 VOID
43 NTAPI
44 ClasspCleanupDisableMcn(
45     IN PFILE_OBJECT_EXTENSION FsContext
46     );
47 
48 #ifdef ALLOC_PRAGMA
49 #pragma alloc_text(PAGE, ClassCreateClose)
50 #pragma alloc_text(PAGE, ClasspCreateClose)
51 #pragma alloc_text(PAGE, ClasspCleanupProtectedLocks)
52 #pragma alloc_text(PAGE, ClasspEjectionControl)
53 #pragma alloc_text(PAGE, ClasspCleanupDisableMcn)
54 #pragma alloc_text(PAGE, ClasspGetFsContext)
55 #endif
56 
57 NTSTATUS
58 NTAPI
59 ClassCreateClose(
60     IN PDEVICE_OBJECT DeviceObject,
61     IN PIRP Irp
62     )
63 
64 /*++
65 
66 Routine Description:
67 
68     SCSI class driver create and close routine.  This is called by the I/O system
69     when the device is opened or closed.
70 
71 Arguments:
72 
73     DriverObject - Pointer to driver object created by system.
74 
75     Irp - IRP involved.
76 
77 Return Value:
78 
79     Device-specific drivers return value or STATUS_SUCCESS.
80 
81 --*/
82 
83 {
84     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
85     ULONG removeState;
86     NTSTATUS status;
87 
88     PAGED_CODE();
89 
90     //
91     // If we're getting a close request then we know the device object hasn't
92     // been completely destroyed.  Let the driver cleanup if necessary.
93     //
94 
95     removeState = ClassAcquireRemoveLock(DeviceObject, Irp);
96 
97     //
98     // Invoke the device-specific routine, if one exists. Otherwise complete
99     // with SUCCESS
100     //
101 
102     if((removeState == NO_REMOVE) ||
103        IS_CLEANUP_REQUEST(IoGetCurrentIrpStackLocation(Irp)->MajorFunction)) {
104 
105         status = ClasspCreateClose(DeviceObject, Irp);
106 
107         if((NT_SUCCESS(status)) &&
108            (commonExtension->DevInfo->ClassCreateClose)) {
109 
110             return commonExtension->DevInfo->ClassCreateClose(DeviceObject, Irp);
111         }
112 
113     } else {
114         status = STATUS_DEVICE_DOES_NOT_EXIST;
115     }
116 
117     Irp->IoStatus.Status = status;
118     ClassReleaseRemoveLock(DeviceObject, Irp);
119     ClassCompleteRequest(DeviceObject, Irp, IO_NO_INCREMENT);
120     return status;
121 }
122 
123 NTSTATUS
124 NTAPI
125 ClasspCreateClose(
126     IN PDEVICE_OBJECT DeviceObject,
127     IN PIRP Irp
128     )
129 /*++
130 
131 Routine Description:
132 
133     This routine will handle create/close operations for a given classpnp
134     device if the class driver doesn't supply it's own handler.  If there
135     is a file object supplied for our driver (if it's a FO_DIRECT_DEVICE_OPEN
136     file object) then it will initialize a file extension on create or destroy
137     the extension on a close.
138 
139 Arguments:
140 
141     DeviceObject - the device object being opened or closed.
142 
143     Irp - the create/close irp
144 
145 Return Value:
146 
147     status
148 
149 --*/
150 {
151     PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
152     PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
153 
154     PFILE_OBJECT fileObject = irpStack->FileObject;
155 
156     NTSTATUS status = STATUS_SUCCESS;
157 
158     PAGED_CODE();
159 
160 
161     //
162     // ISSUE-2000/3/28-henrygab - if lower stack fails create/close, we end up
163     // in an inconsistent state.  re-write to verify all args and allocate all
164     // required resources, then pass the irp down, then complete the
165     // transaction.  this is because we also cannot forward the irp, then fail
166     // it after it has succeeded a lower-level driver.
167     //
168 
169     if(irpStack->MajorFunction == IRP_MJ_CREATE) {
170 
171         PIO_SECURITY_CONTEXT securityContext =
172             irpStack->Parameters.Create.SecurityContext;
173         DebugPrint((2,
174                     "ClasspCREATEClose: create received for device %p\n",
175                     DeviceObject));
176         DebugPrint((2,
177                     "ClasspCREATEClose: desired access %lx\n",
178                     securityContext->DesiredAccess));
179         DebugPrint((2,
180                     "ClasspCREATEClose: file object %lx\n",
181                     irpStack->FileObject));
182 
183         ASSERT(BreakOnClose == FALSE);
184 
185         if(irpStack->FileObject != NULL) {
186 
187             PFILE_OBJECT_EXTENSION fsContext;
188 
189             //
190             // Allocate our own file object extension for this device object.
191             //
192 
193             status = AllocateDictionaryEntry(
194                         &commonExtension->FileObjectDictionary,
195                         (ULONG_PTR)irpStack->FileObject,
196                         sizeof(FILE_OBJECT_EXTENSION),
197                         CLASS_TAG_FILE_OBJECT_EXTENSION,
198                         (PVOID *)&fsContext);
199 
200             if(NT_SUCCESS(status)) {
201 
202                 RtlZeroMemory(fsContext,
203                               sizeof(FILE_OBJECT_EXTENSION));
204 
205                 fsContext->FileObject = irpStack->FileObject;
206                 fsContext->DeviceObject = DeviceObject;
207             } else if (status == STATUS_OBJECT_NAME_COLLISION) {
208                 status = STATUS_SUCCESS;
209             }
210         }
211 
212     } else {
213 
214         DebugPrint((2,
215                     "ClasspCreateCLOSE: close received for device %p\n",
216                     DeviceObject));
217         DebugPrint((2,
218                     "ClasspCreateCLOSE: file object %p\n",
219                     fileObject));
220 
221         if(irpStack->FileObject != NULL) {
222 
223             PFILE_OBJECT_EXTENSION fsContext =
224                 ClasspGetFsContext(commonExtension, irpStack->FileObject);
225 
226             DebugPrint((2,
227                         "ClasspCreateCLOSE: file extension %p\n",
228                         fsContext));
229 
230             if(fsContext != NULL) {
231 
232                 DebugPrint((2,
233                             "ClasspCreateCLOSE: extension is ours - "
234                             "freeing\n"));
235                 ASSERT(BreakOnClose == FALSE);
236 
237                 ClasspCleanupProtectedLocks(fsContext);
238 
239                 ClasspCleanupDisableMcn(fsContext);
240 
241                 FreeDictionaryEntry(&(commonExtension->FileObjectDictionary),
242                                     fsContext);
243             }
244         }
245     }
246 
247     //
248     // Notify the lower levels about the create or close operation - give them
249     // a chance to cleanup too.
250     //
251 
252     DebugPrint((2,
253                 "ClasspCreateClose: %s for devobj %p\n",
254                 (NT_SUCCESS(status) ? "Success" : "FAILED"),
255                 DeviceObject));
256 
257 
258     if(NT_SUCCESS(status)) {
259 
260         KEVENT event;
261 
262         //
263         // Set up the event to wait on
264         //
265 
266         KeInitializeEvent(&event, SynchronizationEvent, FALSE);
267 
268         IoCopyCurrentIrpStackLocationToNext(Irp);
269         IoSetCompletionRoutine( Irp, ClassSignalCompletion, &event,
270                                 TRUE, TRUE, TRUE);
271 
272         status = IoCallDriver(commonExtension->LowerDeviceObject, Irp);
273 
274         if(status == STATUS_PENDING) {
275             KeWaitForSingleObject(&event,
276                                   Executive,
277                                   KernelMode,
278                                   FALSE,
279                                   NULL);
280             status = Irp->IoStatus.Status;
281         }
282 
283         if (!NT_SUCCESS(status)) {
284             DebugPrint((ClassDebugError,
285                         "ClasspCreateClose: Lower driver failed, but we "
286                         "succeeded.  This is a problem, lock counts will be "
287                         "out of sync between levels.\n"));
288         }
289 
290     }
291 
292 
293     return status;
294 }
295 
296 VOID
297 NTAPI
298 ClasspCleanupProtectedLocks(
299     IN PFILE_OBJECT_EXTENSION FsContext
300     )
301 {
302     PCOMMON_DEVICE_EXTENSION commonExtension =
303         FsContext->DeviceObject->DeviceExtension;
304 
305     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
306         commonExtension->PartitionZeroExtension;
307 
308     ULONG newDeviceLockCount = 1;
309 
310     PAGED_CODE();
311 
312     DebugPrint((2,
313                 "ClasspCleanupProtectedLocks called for %p\n",
314                 FsContext->DeviceObject));
315     DebugPrint((2,
316                 "ClasspCleanupProtectedLocks - FsContext %p is locked "
317                 "%d times\n", FsContext, FsContext->LockCount));
318 
319     ASSERT(BreakOnClose == FALSE);
320 
321     //
322     // Synchronize with ejection and ejection control requests.
323     //
324 
325     KeEnterCriticalRegion();
326     KeWaitForSingleObject(&(fdoExtension->EjectSynchronizationEvent),
327                           UserRequest,
328                           UserMode,
329                           FALSE,
330                           NULL);
331 
332     //
333     // For each secure lock on this handle decrement the secured lock count
334     // for the FDO.  Keep track of the new value.
335     //
336 
337     if(FsContext->LockCount != 0) {
338 
339         do {
340 
341             InterlockedDecrement((PLONG)&FsContext->LockCount);
342 
343             newDeviceLockCount =
344                 InterlockedDecrement(&fdoExtension->ProtectedLockCount);
345 
346         } while(FsContext->LockCount != 0);
347 
348         //
349         // If the new lock count has been dropped to zero then issue a lock
350         // command to the device.
351         //
352 
353         DebugPrint((2,
354                     "ClasspCleanupProtectedLocks: FDO secured lock count = %d "
355                     "lock count = %d\n",
356                     fdoExtension->ProtectedLockCount,
357                     fdoExtension->LockCount));
358 
359         if((newDeviceLockCount == 0) && (fdoExtension->LockCount == 0)) {
360 
361             SCSI_REQUEST_BLOCK srb;
362             PCDB cdb;
363             NTSTATUS status;
364 
365             DebugPrint((2,
366                         "ClasspCleanupProtectedLocks: FDO lock count dropped "
367                         "to zero\n"));
368 
369             RtlZeroMemory(&srb, sizeof(SCSI_REQUEST_BLOCK));
370             cdb = (PCDB) &(srb.Cdb);
371 
372             srb.CdbLength = 6;
373 
374             cdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL;
375 
376             //
377             // TRUE - prevent media removal.
378             // FALSE - allow media removal.
379             //
380 
381             cdb->MEDIA_REMOVAL.Prevent = FALSE;
382 
383             //
384             // Set timeout value.
385             //
386 
387             srb.TimeOutValue = fdoExtension->TimeOutValue;
388             status = ClassSendSrbSynchronous(fdoExtension->DeviceObject,
389                                              &srb,
390                                              NULL,
391                                              0,
392                                              FALSE);
393 
394             DebugPrint((2,
395                         "ClasspCleanupProtectedLocks: unlock request to drive "
396                         "returned status %lx\n", status));
397         }
398     }
399 
400     KeSetEvent(&fdoExtension->EjectSynchronizationEvent,
401                IO_NO_INCREMENT,
402                FALSE);
403     KeLeaveCriticalRegion();
404     return;
405 }
406 
407 VOID
408 NTAPI
409 ClasspCleanupDisableMcn(
410     IN PFILE_OBJECT_EXTENSION FsContext
411     )
412 {
413     PCOMMON_DEVICE_EXTENSION commonExtension =
414         FsContext->DeviceObject->DeviceExtension;
415 
416     PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
417         commonExtension->PartitionZeroExtension;
418 
419     PAGED_CODE();
420 
421     DebugPrint((ClassDebugTrace,
422                 "ClasspCleanupDisableMcn called for %p\n",
423                 FsContext->DeviceObject));
424     DebugPrint((ClassDebugTrace,
425                 "ClasspCleanupDisableMcn - FsContext %p is disabled "
426                 "%d times\n", FsContext, FsContext->McnDisableCount));
427 
428     //
429     // For each secure lock on this handle decrement the secured lock count
430     // for the FDO.  Keep track of the new value.
431     //
432 
433     while(FsContext->McnDisableCount != 0) {
434         FsContext->McnDisableCount--;
435         ClassEnableMediaChangeDetection(fdoExtension);
436     }
437 
438     return;
439 }
440 
441 #if 1
442 /*
443  *  BUGBUG REMOVE this old function implementation as soon as the
444  *                  boottime pagefile problems with the new one (below)
445  *                  are resolved.
446  */
447 NTSTATUS
448 NTAPI
449 ClasspEjectionControl(
450     IN PDEVICE_OBJECT Fdo,
451     IN PIRP Irp,
452     IN MEDIA_LOCK_TYPE LockType,
453     IN BOOLEAN Lock
454     )
455 {
456     PFUNCTIONAL_DEVICE_EXTENSION FdoExtension = Fdo->DeviceExtension;
457     PCOMMON_DEVICE_EXTENSION commonExtension =
458         (PCOMMON_DEVICE_EXTENSION) FdoExtension;
459 
460     PFILE_OBJECT_EXTENSION fsContext = NULL;
461     NTSTATUS status;
462     volatile PSCSI_REQUEST_BLOCK srb = NULL;
463     BOOLEAN countChanged = FALSE;
464 
465     PAGED_CODE();
466 
467     //
468     // Interlock with ejection and secure lock cleanup code.  This is a
469     // user request so we can allow the stack to get swapped out while we
470     // wait for synchronization.
471     //
472 
473     status = KeWaitForSingleObject(
474                 &(FdoExtension->EjectSynchronizationEvent),
475                 UserRequest,
476                 UserMode,
477                 FALSE,
478                 NULL);
479 
480     ASSERT(status == STATUS_SUCCESS);
481 
482     DebugPrint((2,
483                 "ClasspEjectionControl: "
484                 "Received request for %s lock type\n",
485                 LockTypeStrings[LockType]
486                 ));
487 
488     _SEH2_TRY {
489         PCDB cdb;
490 
491         srb = ClasspAllocateSrb(FdoExtension);
492 
493         if(srb == NULL) {
494             status = STATUS_INSUFFICIENT_RESOURCES;
495             _SEH2_LEAVE;
496         }
497 
498         RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
499 
500         cdb = (PCDB) srb->Cdb;
501 
502         //
503         // Determine if this is a "secured" request.
504         //
505 
506         if(LockType == SecureMediaLock) {
507 
508             PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
509             PFILE_OBJECT fileObject = irpStack->FileObject;
510 
511             //
512             // Make sure that the file object we are supplied has a
513             // proper FsContext before we try doing a secured lock.
514             //
515 
516             if(fileObject != NULL) {
517                 fsContext = ClasspGetFsContext(commonExtension, fileObject);
518             }
519 
520             if (fsContext == NULL) {
521 
522                 //
523                 // This handle isn't setup correctly.  We can't let the
524                 // operation go.
525                 //
526 
527                 status = STATUS_INVALID_PARAMETER;
528                 _SEH2_LEAVE;
529             }
530         }
531 
532         if(Lock) {
533 
534             //
535             // This is a lock command.  Reissue the command in case bus or
536             // device was reset and the lock was cleared.
537             // note: may need to decrement count if actual lock operation
538             //       failed....
539             //
540 
541             switch(LockType) {
542 
543                 case SimpleMediaLock: {
544                     FdoExtension->LockCount++;
545                     countChanged = TRUE;
546                     break;
547                 }
548 
549                 case SecureMediaLock: {
550                     fsContext->LockCount++;
551                     FdoExtension->ProtectedLockCount++;
552                     countChanged = TRUE;
553                     break;
554                 }
555 
556                 case InternalMediaLock: {
557                     FdoExtension->InternalLockCount++;
558                     countChanged = TRUE;
559                     break;
560                 }
561             }
562 
563         } else {
564 
565             //
566             // This is an unlock command.  If it's a secured one then make sure
567             // the caller has a lock outstanding or return an error.
568             // note: may need to re-increment the count if actual unlock
569             //       operation fails....
570             //
571 
572             switch(LockType) {
573 
574                 case SimpleMediaLock: {
575                     if(FdoExtension->LockCount != 0) {
576                         FdoExtension->LockCount--;
577                         countChanged = TRUE;
578                     }
579                     break;
580                 }
581 
582                 case SecureMediaLock: {
583                     if(fsContext->LockCount == 0) {
584                         status = STATUS_INVALID_DEVICE_STATE;
585                         _SEH2_LEAVE;
586                     }
587                     fsContext->LockCount--;
588                     FdoExtension->ProtectedLockCount--;
589                     countChanged = TRUE;
590                     break;
591                 }
592 
593                 case InternalMediaLock: {
594                     ASSERT(FdoExtension->InternalLockCount != 0);
595                     FdoExtension->InternalLockCount--;
596                     countChanged = TRUE;
597                     break;
598                 }
599             }
600 
601             //
602             // We only send an unlock command to the drive if both the
603             // secured and unsecured lock counts have dropped to zero.
604             //
605 
606             if((FdoExtension->ProtectedLockCount != 0) ||
607                (FdoExtension->InternalLockCount != 0) ||
608                (FdoExtension->LockCount != 0)) {
609 
610                 status = STATUS_SUCCESS;
611                 _SEH2_LEAVE;
612             }
613         }
614 
615         status = STATUS_SUCCESS;
616         if (TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
617 
618             srb->CdbLength = 6;
619             cdb->MEDIA_REMOVAL.OperationCode = SCSIOP_MEDIUM_REMOVAL;
620 
621             //
622             // TRUE - prevent media removal.
623             // FALSE - allow media removal.
624             //
625 
626             cdb->MEDIA_REMOVAL.Prevent = Lock;
627 
628             //
629             // Set timeout value.
630             //
631 
632             srb->TimeOutValue = FdoExtension->TimeOutValue;
633 
634             //
635             // The actual lock operation on the device isn't so important
636             // as the internal lock counts.  Ignore failures.
637             //
638 
639             status = ClassSendSrbSynchronous(FdoExtension->DeviceObject,
640                                              srb,
641                                              NULL,
642                                              0,
643                                              FALSE);
644         }
645 
646     } _SEH2_FINALLY {
647 
648         if (!NT_SUCCESS(status)) {
649             DebugPrint((2,
650                         "ClasspEjectionControl: FAILED status %x -- "
651                         "reverting lock counts\n", status));
652 
653             if (countChanged) {
654 
655                 //
656                 // have to revert to previous counts if the
657                 // lock/unlock operation actually failed.
658                 //
659 
660                 if(Lock) {
661 
662                     switch(LockType) {
663 
664                         case SimpleMediaLock: {
665                             FdoExtension->LockCount--;
666                             break;
667                         }
668 
669                         case SecureMediaLock: {
670                             fsContext->LockCount--;
671                             FdoExtension->ProtectedLockCount--;
672                             break;
673                         }
674 
675                         case InternalMediaLock: {
676                             FdoExtension->InternalLockCount--;
677                             break;
678                         }
679                     }
680 
681                 } else {
682 
683                     switch(LockType) {
684 
685                         case SimpleMediaLock: {
686                             FdoExtension->LockCount++;
687                             break;
688                         }
689 
690                         case SecureMediaLock: {
691                             fsContext->LockCount++;
692                             FdoExtension->ProtectedLockCount++;
693                             break;
694                         }
695 
696                         case InternalMediaLock: {
697                             FdoExtension->InternalLockCount++;
698                             break;
699                         }
700                     }
701                 }
702 
703             }
704 
705         } else {
706 
707             DebugPrint((2,
708                         "ClasspEjectionControl: Succeeded\n"));
709 
710         }
711 
712         DebugPrint((2,
713                     "ClasspEjectionControl: "
714                     "Current Counts: Internal: %x  Secure: %x  Simple: %x\n",
715                     FdoExtension->InternalLockCount,
716                     FdoExtension->ProtectedLockCount,
717                     FdoExtension->LockCount
718                     ));
719 
720         KeSetEvent(&(FdoExtension->EjectSynchronizationEvent),
721                    IO_NO_INCREMENT,
722                    FALSE);
723         if (srb) {
724             ClassFreeOrReuseSrb(FdoExtension, srb);
725         }
726 
727     } _SEH2_END;
728     return status;
729 }
730 
731 #else
732 
733 /*
734  *  BUGBUG RESTORE
735  *      This is a new implementation of the function that doesn't thrash memory
736  *      or depend on the srbLookasideList.
737  *      HOWEVER, it seems to cause pagefile initialization to fail during boot
738  *      for some reason.  Need to resolve this before switching to this function.
739  */
740 NTSTATUS
741 NTAPI
742 ClasspEjectionControl(
743     IN PDEVICE_OBJECT Fdo,
744     IN PIRP Irp,
745     IN MEDIA_LOCK_TYPE LockType,
746     IN BOOLEAN Lock
747     )
748 {
749     PFUNCTIONAL_DEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
750     PFILE_OBJECT_EXTENSION fsContext;
751     BOOLEAN fileHandleOk = TRUE;
752     BOOLEAN countChanged = FALSE;
753     NTSTATUS status;
754 
755     PAGED_CODE();
756 
757     status = KeWaitForSingleObject(
758                 &fdoExt->EjectSynchronizationEvent,
759                 UserRequest,
760                 UserMode,
761                 FALSE,
762                 NULL);
763     ASSERT(status == STATUS_SUCCESS);
764 
765     /*
766      *  If this is a "secured" request, we have to make sure
767      *  that the file handle is valid.
768      */
769     if (LockType == SecureMediaLock){
770         PIO_STACK_LOCATION thisSp = IoGetCurrentIrpStackLocation(Irp);
771 
772         /*
773          *  Make sure that the file object we are supplied has a
774          *  proper FsContext before we try doing a secured lock.
775          */
776         if (thisSp->FileObject){
777             PCOMMON_DEVICE_EXTENSION commonExt = (PCOMMON_DEVICE_EXTENSION)fdoExt;
778             fsContext = ClasspGetFsContext(commonExt, thisSp->FileObject);
779         }
780         else {
781             fsContext = NULL;
782         }
783 
784         if (!fsContext){
785             ASSERT(fsContext);
786             fileHandleOk = FALSE;
787         }
788     }
789 
790     if (fileHandleOk){
791 
792         /*
793          *  Adjust the lock counts and make sure they make sense.
794          */
795         status = STATUS_SUCCESS;
796         if (Lock){
797             switch(LockType) {
798                 case SimpleMediaLock:
799                     fdoExt->LockCount++;
800                     countChanged = TRUE;
801                     break;
802                 case SecureMediaLock:
803                     fsContext->LockCount++;
804                     fdoExt->ProtectedLockCount++;
805                     countChanged = TRUE;
806                     break;
807                 case InternalMediaLock:
808                     fdoExt->InternalLockCount++;
809                     countChanged = TRUE;
810                     break;
811             }
812         }
813         else {
814             /*
815              *  This is an unlock command.  If it's a secured one then make sure
816              *  the caller has a lock outstanding or return an error.
817              */
818             switch (LockType){
819                 case SimpleMediaLock:
820                     if (fdoExt->LockCount > 0){
821                         fdoExt->LockCount--;
822                         countChanged = TRUE;
823                     }
824                     else {
825                         ASSERT(fdoExt->LockCount > 0);
826                         status = STATUS_INTERNAL_ERROR;
827                     }
828                     break;
829                 case SecureMediaLock:
830                     if (fsContext->LockCount > 0){
831                         ASSERT(fdoExt->ProtectedLockCount > 0);
832                         fsContext->LockCount--;
833                         fdoExt->ProtectedLockCount--;
834                         countChanged = TRUE;
835                     }
836                     else {
837                         ASSERT(fsContext->LockCount > 0);
838                         status = STATUS_INVALID_DEVICE_STATE;
839                     }
840                     break;
841                 case InternalMediaLock:
842                     ASSERT(fdoExt->InternalLockCount > 0);
843                     fdoExt->InternalLockCount--;
844                     countChanged = TRUE;
845                     break;
846             }
847         }
848 
849         if (NT_SUCCESS(status)){
850             /*
851              *  We only send an unlock command to the drive if
852              *  all the lock counts have dropped to zero.
853              */
854             if (!Lock &&
855                (fdoExt->ProtectedLockCount ||
856                 fdoExt->InternalLockCount ||
857                 fdoExt->LockCount)){
858 
859                 /*
860                  *  The lock count is still positive, so don't unlock yet.
861                  */
862                 status = STATUS_SUCCESS;
863             }
864             else if (!TEST_FLAG(Fdo->Characteristics, FILE_REMOVABLE_MEDIA)) {
865                 /*
866                  *  The device isn't removable media.  don't send a cmd.
867                  */
868                 status  = STATUS_SUCCESS;
869             }
870             else {
871                 TRANSFER_PACKET *pkt;
872 
873                 pkt = DequeueFreeTransferPacket(Fdo, TRUE);
874                 if (pkt){
875                     KEVENT event;
876 
877                     /*
878                      *  Store the number of packets servicing the irp (one)
879                      *  inside the original IRP.  It will be used to counted down
880                      *  to zero when the packet completes.
881                      *  Initialize the original IRP's status to success.
882                      *  If the packet fails, we will set it to the error status.
883                      */
884                     Irp->Tail.Overlay.DriverContext[0] = LongToPtr(1);
885                     Irp->IoStatus.Status = STATUS_SUCCESS;
886 
887                     /*
888                      *  Set this up as a SYNCHRONOUS transfer, submit it,
889                      *  and wait for the packet to complete.  The result
890                      *  status will be written to the original irp.
891                      */
892                     KeInitializeEvent(&event, SynchronizationEvent, FALSE);
893                     SetupEjectionTransferPacket(pkt, Lock, &event, Irp);
894                     SubmitTransferPacket(pkt);
895                     KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
896                     status = Irp->IoStatus.Status;
897                 }
898                 else {
899                     status = STATUS_INSUFFICIENT_RESOURCES;
900                 }
901             }
902         }
903     }
904     else {
905         status = STATUS_INVALID_PARAMETER;
906     }
907 
908     if (!NT_SUCCESS(status) && countChanged) {
909 
910         //
911         // have to revert to previous counts if the
912         // lock/unlock operation actually failed.
913         //
914 
915         if(Lock) {
916 
917             switch(LockType) {
918 
919                 case SimpleMediaLock: {
920                     FdoExtension->LockCount--;
921                     break;
922                 }
923 
924                 case SecureMediaLock: {
925                     fsContext->LockCount--;
926                     FdoExtension->ProtectedLockCount--;
927                     break;
928                 }
929 
930                 case InternalMediaLock: {
931                     FdoExtension->InternalLockCount--;
932                     break;
933                 }
934             }
935 
936         } else {
937 
938             switch(LockType) {
939 
940                 case SimpleMediaLock: {
941                     FdoExtension->LockCount++;
942                     break;
943                 }
944 
945                 case SecureMediaLock: {
946                     fsContext->LockCount++;
947                     FdoExtension->ProtectedLockCount++;
948                     break;
949                 }
950 
951                 case InternalMediaLock: {
952                     FdoExtension->InternalLockCount++;
953                     break;
954                 }
955             }
956         }
957     }
958 
959 
960 
961     KeSetEvent(&fdoExt->EjectSynchronizationEvent, IO_NO_INCREMENT, FALSE);
962 
963     return status;
964 }
965 #endif
966 
967 PFILE_OBJECT_EXTENSION
968 NTAPI
969 ClasspGetFsContext(
970     IN PCOMMON_DEVICE_EXTENSION CommonExtension,
971     IN PFILE_OBJECT FileObject
972     )
973 {
974     PAGED_CODE();
975     return GetDictionaryEntry(&(CommonExtension->FileObjectDictionary),
976                               (ULONG_PTR)FileObject);
977 }
978