1 ////////////////////////////////////////////////////////////////////
2 // Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine
3 // All rights reserved
4 // This file was released under the GPLv2 on June 2015.
5 ////////////////////////////////////////////////////////////////////
6 /*
7  Module Name: Phys_eject.cpp
8 
9  Execution: Kernel mode only
10 
11  Description:
12 
13    Contains code that implement read/write operations for physical device
14 */
15 
16 #include            "udf.h"
17 // define the file specific bug-check id
18 #define         UDF_BUG_CHECK_ID        UDF_FILE_PHYS_EJECT
19 
20 extern void
21 UDFKeyWaiter(
22     IN void* Context
23     );
24 
25 /*
26     This routine checks for User Eject request & initiates Dismount
27  */
28 void
29 NTAPI
UDFEjectReqWaiter(IN void * Context)30 UDFEjectReqWaiter(
31     IN void* Context
32     )
33 {
34     PUDFEjectWaitContext WC = (PUDFEjectWaitContext)Context;
35     PVCB Vcb;
36     OSSTATUS RC = STATUS_SUCCESS;
37     OSSTATUS WRC;
38     LARGE_INTEGER delay;
39     LARGE_INTEGER time;
40     BOOLEAN UseEvent = TRUE;
41     uint32 d;
42     BOOLEAN FlushWCache = FALSE;
43     IO_STATUS_BLOCK IoStatus;
44     BOOLEAN VcbAcquired;
45     BOOLEAN AllFlushed;
46     PDEVICE_OBJECT TargetDevObj;
47     uint32 BM_FlushPriod;
48     uint32 Tree_FlushPriod;
49     uint32 SkipCount = 0;
50     uint32 SkipEjectCount = 0;
51     uint32 flags = 0;
52     uint32 flush_stat = 0;
53     BOOLEAN UseEject = TRUE;
54     BOOLEAN MediaLoss = FALSE;
55 
56     BOOLEAN SkipEject = FALSE;
57     BOOLEAN SkipFlush = FALSE;
58 
59 //    BOOLEAN FlushAndEject = FALSE;
60 
61     UDFPrint(("    UDFEjectReqWaiter: start\n"));
62     uint8 supported_evt_classes = 0;
63     uint32 i, j;
64     uint8 evt_type;
65     BOOLEAN OldLowFreeSpace = FALSE;
66     uint32 space_check_counter = 0x7fffffff;
67     PGET_LAST_ERROR_USER_OUT Error = NULL;
68 
69     // Drain out Event Queue
70     Vcb = WC->Vcb;
71     TargetDevObj = Vcb->TargetDeviceObject;
72     UseEvent = Vcb->UseEvent;
73     if(UseEvent) {
74         supported_evt_classes = EventStat_Class_Media;
75     } else {
76         UDFPrint(("    Eject Button ignored\n"));
77     }
78     for(j=0; j<4; j++) {
79         UDFPrint(("    Reading events... (0)\n"));
80         if(supported_evt_classes) {
81             for(i=1; i<=EventRetStat_Class_Mask;i++) {
82                 evt_type = (((UCHAR)1) << i);
83                 if( !(supported_evt_classes & evt_type) )
84                     continue;
85                 ((PGET_EVENT_USER_IN)(&(WC->EjectReqBuffer)))->Immed = TRUE;
86                 ((PGET_EVENT_USER_IN)(&(WC->EjectReqBuffer)))->EventClass = evt_type;
87 
88                 RC = UDFPhSendIOCTL( IOCTL_CDRW_GET_EVENT,
89                                      TargetDevObj,
90                                      &(WC->EjectReqBuffer),sizeof(GET_EVENT_USER_IN),
91                                      &(WC->EjectReqBuffer),sizeof(GET_EVENT_USER_OUT),
92                                      FALSE,NULL);
93 
94                 if(RC == STATUS_INVALID_DEVICE_REQUEST) {
95                     UseEvent = FALSE;
96                     break;
97                 }
98                 if(RC == STATUS_NO_SUCH_DEVICE)
99                     break;
100                 if(OS_SUCCESS(RC)) {
101                     supported_evt_classes = WC->EjectReqBuffer.MediaChange.Header.SupportedClasses;
102                 }
103             }
104         }
105         if(!UseEvent)
106             break;
107         if(RC == STATUS_NO_SUCH_DEVICE)
108             break;
109     }
110     supported_evt_classes = 0;
111 
112     // Wait for events
113     while(TRUE) {
114         _SEH2_TRY {
115 
116             VcbAcquired = FALSE;
117             delay.QuadPart = -10000000; // 1.0 sec
118             WRC = KeWaitForSingleObject(&(Vcb->EjectWaiter->StopReq), Executive, KernelMode, FALSE, &delay);
119             if(WRC == STATUS_SUCCESS) {
120 stop_waiter:
121 //                if(!
122                 UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE);//) {
123 /*                    delay.QuadPart = -1000000; // 0.1 sec
124                     KeDelayExecutionThread(KernelMode, FALSE, &delay);
125                     try_return(RC);*/
126 //                }
127                 Vcb->EjectWaiter = NULL;
128                 UDFReleaseResource(&(Vcb->VCBResource));
129 
130                 KeSetEvent(WC->WaiterStopped, 0, FALSE);
131                 MyFreePool__(WC);
132                 WC = NULL;
133                 UDFPrint(("    UDFEjectReqWaiter: exit 3\n"));
134                 return;
135             }
136             BM_FlushPriod = Vcb->BM_FlushPriod;
137             Tree_FlushPriod = Vcb->Tree_FlushPriod;
138 
139             // check if we approaching end of disk
140             if(space_check_counter > 2) {
141                 // update FreeAllocUnits if it is necessary
142                 if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY) && Vcb->Modified) {
143                     Vcb->FreeAllocUnits = UDFGetFreeSpace(Vcb);
144                 }
145                 // update LowFreeSpace flag
146                 Vcb->LowFreeSpace = (Vcb->FreeAllocUnits < max(Vcb->FECharge,UDF_DEFAULT_FE_CHARGE)*128);
147                 if(Vcb->LowFreeSpace && !OldLowFreeSpace) {
148                     // initiate Flush process if we crossed LowFreeSpace boundary
149                     Vcb->Tree_FlushTime = Tree_FlushPriod+1;
150                     Vcb->VCBFlags &= ~UDF_VCB_SKIP_EJECT_CHECK;
151                 }
152                 OldLowFreeSpace = Vcb->LowFreeSpace;
153                 space_check_counter = 0;
154             }
155             space_check_counter++;
156 
157             if(Vcb->VCBFlags & UDF_VCB_SKIP_EJECT_CHECK) {
158                 SkipCount++;
159                 SkipEjectCount++;
160                 SkipEject = (SkipEjectCount <= Vcb->SkipEjectCountLimit);
161                 SkipFlush = (SkipEjectCount <= Vcb->SkipCountLimit);
162                 if(SkipEject || SkipFlush) {
163                     Vcb->VCBFlags &= ~UDF_VCB_SKIP_EJECT_CHECK;
164                 }
165             } else {
166                 SkipEject = FALSE;
167                 SkipFlush = FALSE;
168             }
169 
170             if(WC->SoftEjectReq) {
171                 SkipEject = FALSE;
172                 SkipFlush = FALSE;
173             }
174 
175             if(SkipFlush) {
176                 Vcb->BM_FlushTime =
177                 Vcb->Tree_FlushTime = 0;
178             } else {
179                 SkipCount = 0;
180             }
181             if(!SkipEject) {
182                 SkipEjectCount = 0;
183             }
184 
185             if(SkipEject && SkipFlush) {
186 wait_eject:
187                 delay.QuadPart = -10000000; // 1.0 sec
188                 WRC = KeWaitForSingleObject(&(Vcb->EjectWaiter->StopReq), Executive, KernelMode, FALSE, &delay);
189                 if(WRC == STATUS_SUCCESS) {
190                     goto stop_waiter;
191                 }
192                 try_return(RC);
193             }
194 
195             if(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_LOCKED) {
196                 goto wait_eject;
197             }
198 
199             // check if the door is still locked
200             if(!SkipEject &&
201                 (Vcb->VCBFlags & UDF_VCB_FLAGS_REMOVABLE_MEDIA) &&
202                 (Vcb->VCBFlags & UDF_VCB_FLAGS_OUR_DEVICE_DRIVER)) {
203 
204                 UDFAcquireResourceExclusive(&(Vcb->IoResource), TRUE);
205                 RC = UDFPhSendIOCTL( IOCTL_CDRW_GET_CAPABILITIES,
206                                  TargetDevObj,
207                                  NULL,0,
208                                  &(WC->DevCap),sizeof(GET_CAPABILITIES_USER_OUT),
209                                  FALSE,NULL);
210 
211                 Error = &(WC->Error);
212                 UDFPhSendIOCTL( IOCTL_CDRW_GET_LAST_ERROR, Vcb->TargetDeviceObject,
213                                 NULL,0,
214                                 Error,sizeof(GET_LAST_ERROR_USER_OUT),
215                                 TRUE,NULL);
216                 UDFReleaseResource(&(Vcb->IoResource));
217                 UDFPrint(("SK=%x ASC=%x, ASCQ=%x, IE=%x\n",
218                          Error->SenseKey, Error->AdditionalSenseCode, Error->AdditionalSenseCodeQualifier, Error->LastError));
219                 // check for Long Write In Progress
220                 if( ((Error->SenseKey == SCSI_SENSE_NOT_READY) &&
221                      (Error->AdditionalSenseCode == SCSI_ADSENSE_LUN_NOT_READY) &&
222                      (Error->AdditionalSenseCodeQualifier == SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS ||
223                       Error->AdditionalSenseCodeQualifier == SCSI_SENSEQ_FORMAT_IN_PROGRESS)) ) {
224                     if((!Vcb->Modified &&
225                         !(Vcb->VCBFlags & UDF_VCB_LAST_WRITE))
226                                 ||
227                        (Vcb->VCBFlags & UDF_VCB_FLAGS_UNSAFE_IOCTL)) {
228                           // we should forget about this disk...
229                         UDFPrint(("    LAST_WRITE %x\n", !!(Vcb->VCBFlags & UDF_VCB_LAST_WRITE)));
230                         UDFPrint(("    UDF_VCB_FLAGS_UNSAFE_IOCTL %x\n", !!(Vcb->VCBFlags & UDF_VCB_FLAGS_UNSAFE_IOCTL)));
231                         UDFPrint(("    UDFEjectReqWaiter: Unexpected write-in-progress on !Modified volume\n"));
232                         //ASSERT(FALSE);
233                         Vcb->ForgetVolume = TRUE;
234                         Vcb->VCBFlags |= UDF_VCB_FLAGS_VOLUME_READ_ONLY | UDF_VCB_FLAGS_MEDIA_READ_ONLY;
235                         MediaLoss = TRUE;
236                         goto device_failure;
237                     }
238                 }
239                 if(  OS_SUCCESS(RC) &&
240                     (Vcb->VCBFlags & UDF_VCB_FLAGS_REMOVABLE_MEDIA) &&
241                    !(WC->DevCap.Capabilities2 & DevCap_lock_state)) {
242                     // probably bus reset or power failure occured
243                     // re-lock tray
244                     UDFPrint(("    UDFEjectReqWaiter: Unexpected tray unlock encountered. Try to re-lock\n"));
245 
246                     UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE);
247                     VcbAcquired = TRUE;
248 
249 /*                    UDFResetDeviceDriver(Vcb, TargetDevObj, FALSE);
250                     delay.QuadPart = -1000000; // 0.1 sec
251                     KeDelayExecutionThread(KernelMode, FALSE, &delay);*/
252                     // lock it
253                     ((PPREVENT_MEDIA_REMOVAL_USER_IN)(&(WC->DevCap)))->PreventMediaRemoval = TRUE;
254                     UDFPhSendIOCTL( IOCTL_STORAGE_MEDIA_REMOVAL,
255                                          TargetDevObj,
256                                          &(WC->DevCap),sizeof(PREVENT_MEDIA_REMOVAL_USER_IN),
257                                          NULL,0,
258                                          FALSE,NULL);
259                     delay.QuadPart = -1000000; // 0.1 sec
260                     KeDelayExecutionThread(KernelMode, FALSE, &delay);
261                     // force write mode re-initialization
262                     Vcb->LastModifiedTrack = 0;
263 //                    try_return(RC);
264                 }
265             }
266 
267             UDFVVerify(Vcb, 0 /* partial verify */);
268 
269             if(!SkipFlush &&
270                !(Vcb->VCBFlags & UDF_VCB_FLAGS_RAW_DISK) &&
271                 (BM_FlushPriod || Tree_FlushPriod)) {
272                 KeQuerySystemTime(&delay);
273                 d = (uint32)((delay.QuadPart - time.QuadPart) / 10000000);
274                 time = delay;
275                 Vcb->BM_FlushTime += d;
276                 Vcb->Tree_FlushTime += d;
277 
278                 if(!Vcb->CDR_Mode) {
279 
280                     AllFlushed = FALSE;
281 
282                     UDFPrint(("    SkipCount=%x, SkipCountLimit=%x\n",
283                         SkipCount,
284                         Vcb->SkipCountLimit));
285 
286                     if( Tree_FlushPriod &&
287                        (Tree_FlushPriod < Vcb->Tree_FlushTime)) {
288 
289                         UDFPrint(("    Tree_FlushPriod %I64x, Vcb->Tree_FlushTime %I64x\n",
290                             Tree_FlushPriod,
291                             Vcb->Tree_FlushTime));
292 
293                         // do not touch unchanged volume
294                         if((Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY) ||
295                            !Vcb->Modified)
296                             goto skip_BM_flush;
297 
298                         // Acquire Vcb resource
299                         if(!VcbAcquired) {
300                             if(!UDFAcquireResourceExclusive(&(Vcb->VCBResource), FALSE)) {
301                                 delay.QuadPart = -10000000; // 1.0 sec
302                                 WRC = KeWaitForSingleObject(&(Vcb->EjectWaiter->StopReq), Executive, KernelMode, FALSE, &delay);
303                                 if(WRC == STATUS_SUCCESS) {
304                                     goto stop_waiter;
305                                 }
306                                 try_return(RC);
307                             }
308                             VcbAcquired = TRUE;
309                         }
310 
311                         UDFPrint(("UDF: Flushing Directory Tree....\n"));
312                         if( BM_FlushPriod &&
313                            (BM_FlushPriod < Vcb->BM_FlushTime)) {
314                             UDFPrint(("  full flush\n"));
315                             flush_stat = UDFFlushADirectory(Vcb, Vcb->RootDirFCB->FileInfo, &IoStatus, UDF_FLUSH_FLAGS_BREAKABLE);
316                         } else {
317                             UDFPrint(("  light flush\n"));
318                             flush_stat = UDFFlushADirectory(Vcb, Vcb->RootDirFCB->FileInfo, &IoStatus, UDF_FLUSH_FLAGS_BREAKABLE | UDF_FLUSH_FLAGS_LITE);
319                         }
320                         if(flush_stat & UDF_FLUSH_FLAGS_INTERRUPTED)
321                             try_return(RC);
322                         FlushWCache = TRUE;
323                         UDFVVerify(Vcb, UFD_VERIFY_FLAG_BG /* partial verify */);
324                         //UDFVFlush(Vcb);
325 skip_BM_flush:
326                         Vcb->Tree_FlushTime = 0;
327                     }
328                     if( BM_FlushPriod &&
329                        (BM_FlushPriod < Vcb->BM_FlushTime)) {
330 
331                         UDFPrint(("    BM_FlushPriod %I64x, Vcb->BM_FlushTime %I64x\n",
332                             BM_FlushPriod,
333                             Vcb->BM_FlushTime));
334 
335 
336                         // do not touch unchanged volume
337                         if(Vcb->VCBFlags & UDF_VCB_FLAGS_VOLUME_READ_ONLY)
338                             goto skip_BM_flush2;
339                         if(!Vcb->Modified)
340                             goto skip_BM_flush2;
341 
342                         if(!VcbAcquired) {
343                             if(!UDFAcquireResourceExclusive(&(Vcb->VCBResource), FlushWCache /*|| FALSE*/)) {
344                                 delay.QuadPart = -10000000; // 1.0 sec
345                                 WRC = KeWaitForSingleObject(&(Vcb->EjectWaiter->StopReq), Executive, KernelMode, FALSE, &delay);
346                                 if(WRC == STATUS_SUCCESS) {
347                                     goto stop_waiter;
348                                 }
349                                 try_return(RC);
350                             }
351                             VcbAcquired = TRUE;
352                         }
353 
354                         UDFAcquireResourceExclusive(&(Vcb->BitMapResource1),TRUE);
355 //                        UDF_CHECK_BITMAP_RESOURCE(Vcb);
356 
357                         if(Vcb->FSBM_ByteCount != RtlCompareMemory(Vcb->FSBM_Bitmap, Vcb->FSBM_OldBitmap, Vcb->FSBM_ByteCount)) {
358                             flags |= 1;
359                         }
360 /*                        if(FlushWCache) {
361                             AllFlushed = TRUE;
362                         }*/
363                         AllFlushed =
364                         FlushWCache = TRUE;
365 #ifndef UDF_READ_ONLY_BUILD
366                         UDFFlushAllCachedAllocations(Vcb, UDF_PREALLOC_CLASS_FE);
367                         UDFFlushAllCachedAllocations(Vcb, UDF_PREALLOC_CLASS_DIR);
368 
369                         UDFUpdateVolIdent(Vcb, Vcb->PVolDescAddr, &(Vcb->VolIdent));
370                         UDFUpdateVolIdent(Vcb, Vcb->PVolDescAddr2, &(Vcb->VolIdent));
371 
372                         if(Vcb->VerifyOnWrite) {
373                             UDFPrint(("UDF: Flushing cache for verify\n"));
374                             //WCacheFlushAll__(&(Vcb->FastCache), Vcb);
375                             WCacheFlushBlocks__(&(Vcb->FastCache), Vcb, 0, Vcb->LastLBA);
376                             UDFVFlush(Vcb);
377                         }
378 #ifdef UDF_DBG
379                         UDFPrint(("UDF: Flushing Free Space Bitmap....\n"));
380 
381 //                        if(!OS_SUCCESS(UDFUpdateVolIdent(Vcb, Vcb->PVolDescAddr, &(Vcb->VolIdent))))
382 //                            UDFPrint(("Error updating VolIdent\n"));
383                         if(!OS_SUCCESS(UDFUpdateVDS(Vcb, Vcb->VDS1, Vcb->VDS1 + Vcb->VDS1_Len, flags)))
384                             UDFPrint(("Error updating Main VDS\n"));
385                         if(!OS_SUCCESS(UDFUpdateVDS(Vcb, Vcb->VDS2, Vcb->VDS2 + Vcb->VDS2_Len, flags)))
386                             UDFPrint(("Error updating Reserve VDS\n"));
387 #else
388                         UDFUpdateVDS(Vcb, Vcb->VDS1, Vcb->VDS1 + Vcb->VDS1_Len, flags);
389                         UDFUpdateVDS(Vcb, Vcb->VDS2, Vcb->VDS2 + Vcb->VDS2_Len, flags);
390 #endif // UDF_DBG
391                         // Update Integrity Desc if any
392                         if(Vcb->LVid && Vcb->origIntegrityType == INTEGRITY_TYPE_CLOSE) {
393                             UDFUpdateLogicalVolInt(Vcb, TRUE);
394                         }
395                         if(flags & 1) {
396                             RtlCopyMemory(Vcb->FSBM_OldBitmap, Vcb->FSBM_Bitmap, Vcb->FSBM_ByteCount);
397                         }
398 #endif //UDF_READ_ONLY_BUILD
399                         UDFPreClrModified(Vcb);
400                         UDFReleaseResource(&(Vcb->BitMapResource1));
401 skip_BM_flush2:
402                         Vcb->BM_FlushTime = 0;
403                     }
404                     if(FlushWCache) {
405                         FlushWCache = FALSE;
406                         WCacheFlushAll__(&(Vcb->FastCache), Vcb);
407                     }
408 
409                     if(AllFlushed) {
410                         //Vcb->Modified = FALSE;
411                         UDFClrModified(Vcb);
412                     }
413 
414                     if(VcbAcquired) {
415                         VcbAcquired = FALSE;
416                         UDFReleaseResource(&(Vcb->VCBResource));
417                     }
418                     if(!Vcb->Tree_FlushTime &&
419                        !Vcb->BM_FlushTime)
420                         SkipCount = 0;
421                 }
422             } else {
423                 //SkipCount = 0;
424             }
425 
426             if(!(Vcb->VCBFlags & UDF_VCB_FLAGS_REMOVABLE_MEDIA))
427                 try_return(RC);
428 
429             UDFPrint(("    UDFEjectReqWaiter: check removable media\n"));
430             if(!WC->SoftEjectReq && SkipEject) {
431                 try_return(RC);
432             }
433 
434             if(!WC->SoftEjectReq) {
435 
436                 if(!UseEvent) {
437                     UDFPrint(("    Eject Button ignored\n"));
438                     try_return(RC);
439                 }
440 
441 /*                if( (Vcb->VCBFlags & UDF_VCB_LAST_WRITE) &&
442                    !(Vcb->VCBFlags & UDF_VCB_FLAGS_NO_SYNC_CACHE) ){
443             //        delay.QuadPart = -100000; // 0.01 sec
444             //        KeDelayExecutionThread(KernelMode, FALSE, &delay);
445                     OSSTATUS RC;
446 
447                     UDFPrint(("    Sync cache before GET_EVENT\n"));
448                     RC = UDFSyncCache(Vcb);
449                     if(RC == STATUS_INVALID_DEVICE_REQUEST) {
450                         Vcb->VCBFlags |= UDF_VCB_FLAGS_NO_SYNC_CACHE;
451                     }
452 
453             //        delay.QuadPart = -300000; // 0.03 sec
454             //        KeDelayExecutionThread(KernelMode, FALSE, &delay);
455                     Vcb->VCBFlags &= ~UDF_VCB_LAST_WRITE;
456                 }*/
457 
458                 ASSERT(sizeof(TEST_UNIT_READY_USER_OUT) <= sizeof(GET_EVENT_USER_OUT));
459 
460                 RC = UDFTSendIOCTL( IOCTL_CDRW_TEST_UNIT_READY,
461                                      Vcb,
462                                      NULL,0,
463                                      &(WC->EjectReqBuffer),sizeof(TEST_UNIT_READY_USER_OUT),
464                                      FALSE,NULL);
465 
466                 if(RC != STATUS_SUCCESS &&
467                    RC != STATUS_DATA_OVERRUN) {
468                     if(RC == STATUS_NO_SUCH_DEVICE) {
469                         UDFPrint(("    Device loss\n"));
470                         goto device_failure;
471                     }
472                     if(RC == STATUS_NO_MEDIA_IN_DEVICE) {
473                         UDFPrint(("    Media loss\n"));
474                         goto media_loss;
475                     }
476                 }
477                 UDFPrint(("    Reading events...\n"));
478                 if(supported_evt_classes) {
479                     for(i=1; i<=EventRetStat_Class_Mask;i++) {
480                         evt_type = (((UCHAR)1) << i);
481                         if( !(supported_evt_classes & evt_type) )
482                             continue;
483 /*
484                         if( evt_type == EventStat_Class_Media )
485                             continue;
486                         if( evt_type == EventStat_Class_ExternalReq )
487                             continue;
488 */
489                         ((PGET_EVENT_USER_IN)(&(WC->EjectReqBuffer)))->Immed = TRUE;
490                         ((PGET_EVENT_USER_IN)(&(WC->EjectReqBuffer)))->EventClass = evt_type;
491 
492                         RC = UDFTSendIOCTL( IOCTL_CDRW_GET_EVENT,
493                                              Vcb,
494                                              &(WC->EjectReqBuffer),sizeof(GET_EVENT_USER_IN),
495                                              &(WC->EjectReqBuffer),sizeof(GET_EVENT_USER_OUT),
496                                              FALSE,NULL);
497 
498                         if(RC == STATUS_INVALID_DEVICE_REQUEST) {
499                             supported_evt_classes &= ~evt_type;
500                             continue;
501                         }
502                         if(RC == STATUS_NO_SUCH_DEVICE) {
503                             UDFPrint(("    Device loss (2)\n"));
504                             goto device_failure;
505                         }
506                         if(!OS_SUCCESS(RC)) {
507                             continue;
508                         }
509 
510                         if(WC->EjectReqBuffer.MediaChange.Header.Flags.Flags & EventRetStat_NEA) {
511                             continue;
512                         }
513                         if( evt_type == EventStat_Class_Media ) {
514                             UDFPrint(("    EventStat_Class_Media:\n"));
515                             if((WC->EjectReqBuffer.MediaChange.Header.Flags.Flags & EventRetStat_Class_Mask) !=
516                                 EventRetStat_Class_Media) {
517                                 continue;
518                             }
519 retry_media_presence_check:
520                             if(!(WC->EjectReqBuffer.MediaChange.Byte1.Flags & EventStat_MediaStat_Present) ||
521                                 (WC->EjectReqBuffer.MediaChange.Byte1.Flags & EventStat_MediaStat_DoorOpen)) {
522                                 // something wrong....
523                                 RC = UDFTSendIOCTL( IOCTL_CDRW_TEST_UNIT_READY,
524                                                      Vcb,
525                                                      NULL,0,
526                                                      &(WC->EjectReqBuffer),sizeof(TEST_UNIT_READY_USER_OUT),
527                                                      FALSE,NULL);
528 
529                                 if(RC == STATUS_SUCCESS ||
530                                    RC == STATUS_DATA_OVERRUN) {
531                                     UDFPrint(("    Buggy GET_EVENT media presence flag %x\n",
532                                         WC->EjectReqBuffer.MediaChange.Byte1));
533                                     WC->EjectReqBuffer.MediaChange.Byte1.Flags |= EventStat_MediaStat_Present;
534                                     WC->EjectReqBuffer.MediaChange.Byte1.Flags &= ~EventStat_MediaStat_DoorOpen;
535                                     goto retry_media_presence_check;
536                                 }
537 media_loss:
538                                 UDFPrint(("    Unexpected media loss. Check device status\n"));
539                                 UseEject = FALSE;
540                                 MediaLoss = TRUE;
541                             } else
542                             // check if eject request occured
543                             if( (WC->EjectReqBuffer.MediaChange.Byte0.Flags & EventStat_MediaEvent_Mask) !=
544                                            EventStat_MediaEvent_EjectReq ) {
545                                 continue;
546                             }
547                             UDFPrint(("    eject requested\n"));
548                             WC->SoftEjectReq = TRUE;
549                             break;
550                         }
551                         if( evt_type == EventStat_Class_ExternalReq ) {
552                             UDFPrint(("    EventStat_Class_ExternalReq:\n"));
553                             if((WC->EjectReqBuffer.ExternalReq.Header.Flags.Flags & EventRetStat_Class_Mask) !=
554                                 EventRetStat_Class_ExternReq)
555                                 continue;
556                             switch(WC->EjectReqBuffer.ExternalReq.Byte0.Flags & EventStat_ExtrnReqEvent_Mask) {
557                             case EventStat_ExtrnReqEvent_KeyDown:
558                             case EventStat_ExtrnReqEvent_KeyUp:
559                             case EventStat_ExtrnReqEvent_ExtrnReq:
560                                 UDFPrint(("    eject requested (%x)\n", WC->EjectReqBuffer.ExternalReq.Byte0.Flags));
561                                 WC->SoftEjectReq = TRUE;
562                                 break;
563                             }
564                             continue;
565                         }
566                     }
567                     if(!supported_evt_classes) {
568                         UseEvent = FALSE;
569                     }
570                     if(!WC->SoftEjectReq) {
571                         try_return(RC);
572                     }
573                 } else {
574 
575                     UDFPrint(("    Reading Media Event...\n"));
576                     ((PGET_EVENT_USER_IN)(&(WC->EjectReqBuffer)))->Immed = TRUE;
577                     ((PGET_EVENT_USER_IN)(&(WC->EjectReqBuffer)))->EventClass = EventStat_Class_Media;
578 
579                     RC = UDFTSendIOCTL( IOCTL_CDRW_GET_EVENT,
580                                          Vcb,
581                                          &(WC->EjectReqBuffer),sizeof(GET_EVENT_USER_IN),
582                                          &(WC->EjectReqBuffer),sizeof(GET_EVENT_USER_OUT),
583                                          FALSE,NULL);
584 
585                     if(!OS_SUCCESS(RC)) {
586                         if(RC == STATUS_NO_SUCH_DEVICE)
587                             goto device_failure;
588                         ((PGET_EVENT_USER_IN)(&(WC->EjectReqBuffer)))->Immed = TRUE;
589                         ((PGET_EVENT_USER_IN)(&(WC->EjectReqBuffer)))->EventClass = EventStat_Class_ExternalReq;
590 
591                         RC = UDFTSendIOCTL( IOCTL_CDRW_GET_EVENT,
592                                              Vcb,
593                                              &(WC->EjectReqBuffer),sizeof(GET_EVENT_USER_IN),
594                                              &(WC->EjectReqBuffer),sizeof(GET_EVENT_USER_OUT),
595                                              FALSE,NULL);
596 
597                         if(RC == STATUS_NO_SUCH_DEVICE)
598                             goto device_failure;
599                         if(RC == STATUS_INVALID_DEVICE_REQUEST) {
600                             UseEvent = FALSE;
601                         }
602                         try_return(RC);
603                     }
604                     supported_evt_classes = WC->EjectReqBuffer.MediaChange.Header.SupportedClasses;
605                     try_return(RC);
606                 }
607             }
608 //            FlushAndEject = TRUE;
609 device_failure:
610             // Ok. Lets flush all we have in memory, dismount volume & eject disc
611             // Acquire Vcb resource
612             Vcb->SoftEjectReq = TRUE;
613 
614             UDFPrint(("    UDFEjectReqWaiter: ejecting...\n"));
615 #ifdef UDF_DELAYED_CLOSE
616             UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE);
617             UDFPrint(("    UDFEjectReqWaiter:     set UDF_VCB_FLAGS_NO_DELAYED_CLOSE\n"));
618             Vcb->VCBFlags |= UDF_VCB_FLAGS_NO_DELAYED_CLOSE;
619             UDFReleaseResource(&(Vcb->VCBResource));
620 #endif //UDF_DELAYED_CLOSE
621 
622             UDFPrint(("    UDFEjectReqWaiter:     UDFCloseAllSystemDelayedInDir\n"));
623             RC = UDFCloseAllSystemDelayedInDir(Vcb, Vcb->RootDirFCB->FileInfo);
624             ASSERT(OS_SUCCESS(RC));
625 #ifdef UDF_DELAYED_CLOSE
626             UDFPrint(("    UDFEjectReqWaiter:     UDFCloseAllDelayed\n"));
627             UDFCloseAllDelayed(Vcb);
628             //ASSERT(OS_SUCCESS(RC));
629 #endif //UDF_DELAYED_CLOSE
630 
631             UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE);
632 
633             UDFPrint(("    UDFEjectReqWaiter:     UDFDoDismountSequence\n"));
634             UDFDoDismountSequence(Vcb, (PPREVENT_MEDIA_REMOVAL_USER_IN)&(WC->EjectReqBuffer), UseEject);
635             if (MediaLoss) {
636                 Vcb->VCBFlags &= ~UDF_VCB_FLAGS_VOLUME_MOUNTED;
637                 Vcb->WriteSecurity = FALSE;
638             }
639             Vcb->EjectWaiter = NULL;
640             Vcb->SoftEjectReq = FALSE;
641             UDFReleaseResource(&(Vcb->VCBResource));
642 
643             UDFPrint(("    UDFEjectReqWaiter:     set WaiterStopped\n"));
644             KeSetEvent(WC->WaiterStopped, 0, FALSE);
645             MyFreePool__(WC);
646             WC = NULL;
647 
648             UDFPrint(("    UDFEjectReqWaiter: exit 1\n"));
649             return;
650 
651 try_exit:   NOTHING;
652         } _SEH2_FINALLY {
653 
654             if(VcbAcquired) {
655                 VcbAcquired = FALSE;
656                 UDFReleaseResource(&(Vcb->VCBResource));
657             }
658 
659 /*            if(WC) {
660                 delay.QuadPart = -10000000; // 1.0 sec
661                 WRC = KeWaitForSingleObject(&(Vcb->WaiterStopped), Executive, KernelMode, FALSE, &delay);
662                 if(WRC == STATUS_SUCCESS) {
663                     goto stop_waiter;
664                 }
665             }*/
666         } _SEH2_END;
667     }
668     // Simply make compiler happy
669     return;
670 } // end UDFEjectReqWaiter()
671 
672 void
UDFStopEjectWaiter(PVCB Vcb)673 UDFStopEjectWaiter(PVCB Vcb) {
674 
675     UDFPrint(("    UDFStopEjectWaiter: try\n"));
676     UDFAcquireResourceExclusive(&(Vcb->VCBResource), TRUE);
677     _SEH2_TRY {
678         if(Vcb->EjectWaiter) {
679             UDFPrint(("    UDFStopEjectWaiter: set flag\n"));
680             KeSetEvent( &(Vcb->EjectWaiter->StopReq), 0, FALSE );
681         } else {
682 //            return;
683         }
684     } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
685         BrutePoint();
686     } _SEH2_END;
687     UDFReleaseResource(&(Vcb->VCBResource));
688 
689     _SEH2_TRY {
690         if(Vcb->VCBFlags & UDF_VCB_FLAGS_STOP_WAITER_EVENT) {
691             UDFPrint(("    UDFStopEjectWaiter: wait\n"));
692             KeWaitForSingleObject(&(Vcb->WaiterStopped), Executive, KernelMode, FALSE, NULL);
693         }
694         Vcb->VCBFlags &= ~UDF_VCB_FLAGS_STOP_WAITER_EVENT;
695     } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
696         BrutePoint();
697     } _SEH2_END;
698     ASSERT(!Vcb->EjectWaiter);
699     UDFPrint(("    UDFStopEjectWaiter: exit\n"));
700 
701 } // end UDFStopEjectWaiter()
702 
703 OSSTATUS
UDFDoDismountSequence(IN PVCB Vcb,IN PPREVENT_MEDIA_REMOVAL_USER_IN Buf,IN BOOLEAN Eject)704 UDFDoDismountSequence(
705     IN PVCB Vcb,
706     IN PPREVENT_MEDIA_REMOVAL_USER_IN Buf,
707     IN BOOLEAN Eject
708     )
709 {
710     LARGE_INTEGER delay;
711 //    OSSTATUS      RC;
712     ULONG i;
713 
714     // flush system cache
715     UDFFlushLogicalVolume(NULL, NULL, Vcb, 0);
716     UDFPrint(("UDFDoDismountSequence:\n"));
717 
718     delay.QuadPart = -1000000; // 0.1 sec
719     KeDelayExecutionThread(KernelMode, FALSE, &delay);
720     // wait for completion of all backgroung writes
721     while(Vcb->BGWriters) {
722         delay.QuadPart = -5000000; // 0.5 sec
723         KeDelayExecutionThread(KernelMode, FALSE, &delay);
724     }
725     // release WCache
726     WCacheRelease__(&(Vcb->FastCache));
727 
728     UDFAcquireResourceExclusive(&(Vcb->IoResource), TRUE);
729 
730     // unlock media, drop our own Locks
731     if(Vcb->VCBFlags & UDF_VCB_FLAGS_REMOVABLE_MEDIA) {
732         UDFPrint(("  cleanup tray-lock (%d+2):\n", Vcb->MediaLockCount));
733         for(i=0; i<Vcb->MediaLockCount+2; i++) {
734             Buf->PreventMediaRemoval = FALSE;
735             UDFPhSendIOCTL(IOCTL_STORAGE_MEDIA_REMOVAL,
736                            Vcb->TargetDeviceObject,
737                            Buf,sizeof(PREVENT_MEDIA_REMOVAL_USER_IN),
738                            NULL,0,
739                            FALSE,NULL);
740             KeDelayExecutionThread(KernelMode, FALSE, &delay);
741         }
742         delay.QuadPart = -2000000; // 0.2 sec
743     }
744 
745     if(!Vcb->ForgetVolume) {
746 
747         if(!UDFIsDvdMedia(Vcb)) {
748             // send speed limits to drive
749             UDFPrint(("    Restore drive speed on dismount\n"));
750             Vcb->SpeedBuf.ReadSpeed  = Vcb->MaxReadSpeed;
751             Vcb->SpeedBuf.WriteSpeed = Vcb->MaxWriteSpeed;
752             UDFPhSendIOCTL(IOCTL_CDRW_SET_SPEED,
753                                 Vcb->TargetDeviceObject,
754                                 &(Vcb->SpeedBuf),sizeof(SET_CD_SPEED_USER_IN),
755                                 NULL,0,TRUE,NULL);
756         }
757 
758         if(Vcb->VCBFlags & UDF_VCB_FLAGS_OUR_DEVICE_DRIVER) {
759             CLOSE_TRK_SES_USER_IN CBuff;
760 
761             // reset driver
762             UDFResetDeviceDriver(Vcb, Vcb->TargetDeviceObject, TRUE);
763             delay.QuadPart = -2000000; // 0.2 sec
764             KeDelayExecutionThread(KernelMode, FALSE, &delay);
765 
766             memset(&CBuff,0,sizeof(CLOSE_TRK_SES_USER_IN));
767             // stop BG format
768             if(Vcb->MRWStatus) {
769                 UDFPrint(("    Stop background formatting\n"));
770 
771                 CBuff.Byte1.Flags = 0;//CloseTrkSes_Immed;
772                 CBuff.Byte2.Flags = CloseTrkSes_Ses;
773                 CBuff.TrackNum = 1;
774 
775                 UDFPhSendIOCTL(IOCTL_CDRW_CLOSE_TRK_SES,
776                                Vcb->TargetDeviceObject,
777                                &CBuff,sizeof(CLOSE_TRK_SES_USER_IN),
778                                &CBuff,sizeof(CLOSE_TRK_SES_USER_IN),
779                                FALSE, NULL );
780     /*        } else
781             if(Vcb->MediaClassEx == CdMediaClass_DVDRW) {
782                 UDFPrint(("    Close BG-formatted track\n"));
783 
784                 CBuff.Byte1.Flags = 0;//CloseTrkSes_Immed;
785                 CBuff.Byte2.Flags = CloseTrkSes_Trk;
786                 CBuff.TrackNum = 1;
787 
788                 RC = UDFPhSendIOCTL(IOCTL_CDRW_CLOSE_TRK_SES,
789                                     Vcb->TargetDeviceObject,
790                                     &CBuff,sizeof(CLOSE_TRK_SES_USER_IN),
791                                     &CBuff,sizeof(CLOSE_TRK_SES_USER_IN),
792                                     FALSE, NULL );
793     */
794             }
795             // reset driver
796             UDFResetDeviceDriver(Vcb, Vcb->TargetDeviceObject, TRUE);
797             delay.QuadPart = -1000000; // 0.1 sec
798             KeDelayExecutionThread(KernelMode, FALSE, &delay);
799         }
800         // eject media
801         if(Eject &&
802            (Vcb->VCBFlags & UDF_VCB_FLAGS_REMOVABLE_MEDIA)) {
803 
804             UDFPhSendIOCTL(IOCTL_STORAGE_EJECT_MEDIA,
805                            Vcb->TargetDeviceObject,
806                            NULL,0,
807                            NULL,0,
808                            FALSE,NULL);
809         }
810         // notify media change
811     /*    if(Vcb->VCBFlags & UDF_VCB_FLAGS_OUR_DEVICE_DRIVER) {
812             ((PNOTIFY_MEDIA_CHANGE_USER_IN)Buf)->Autorun = FALSE;
813             RC = UDFPhSendIOCTL(IOCTL_CDRW_NOTIFY_MEDIA_CHANGE,
814                                 Vcb->TargetDeviceObject,
815                                 Buf,sizeof(NOTIFY_MEDIA_CHANGE_USER_IN),
816                                 NULL,0,
817                                 FALSE,NULL);
818         }*/
819     }
820     UDFReleaseResource(&(Vcb->IoResource));
821     // unregister shutdown notification
822     if(Vcb->ShutdownRegistered) {
823         IoUnregisterShutdownNotification(Vcb->VCBDeviceObject);
824         Vcb->ShutdownRegistered = FALSE;
825     }
826     // allow media change checks (this will lead to dismount)
827     // ... and make it Read-Only...  :-\~
828     Vcb->VCBFlags &= ~UDF_VCB_FLAGS_MEDIA_LOCKED;
829 
830     // Return back XP CD Burner Volume
831 /*
832     if (Vcb->CDBurnerVolumeValid) {
833         RtlWriteRegistryValue(RTL_REGISTRY_USER | RTL_REGISTRY_OPTIONAL,
834                       REG_CD_BURNER_KEY_NAME,REG_CD_BURNER_VOLUME_NAME,
835                       REG_SZ,(PVOID)&(Vcb->CDBurnerVolume),sizeof(Vcb->CDBurnerVolume));
836         ExFreePool(Vcb->CDBurnerVolume.Buffer);
837     }
838 */
839     UDFPrint(("  set UnsafeIoctl\n"));
840     Vcb->VCBFlags |= UDF_VCB_FLAGS_UNSAFE_IOCTL;
841 
842     return STATUS_SUCCESS;
843 } // end UDFDoDismountSequence()
844 
845