1 ////////////////////////////////////////////////////////////////////
2 // Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine
4 ////////////////////////////////////////////////////////////////////
5 /*************************************************************************
9 * Module: UDF File System Driver (Kernel mode execution only)
12 * This module implements the Plug and Play routines for UDF called by
13 * the dispatch driver.
15 *************************************************************************/
19 // define the file specific bug-check id
20 #define UDF_BUG_CHECK_ID UDF_FILE_PNP
25 PtrUDFIrpContext PtrIrpContext
,
32 PtrUDFIrpContext PtrIrpContext
,
38 UDFPnpSurpriseRemove (
39 PtrUDFIrpContext PtrIrpContext
,
46 PtrUDFIrpContext PtrIrpContext
,
53 UDFPnpCompletionRoutine (
54 IN PDEVICE_OBJECT DeviceObject
,
61 PtrUDFIrpContext PtrIrpContext
,
66 This routine implements the FSD part of PnP operations
70 VolumeDeviceObject - Supplies the volume device object where the
72 Irp - Supplies the Irp being processed
76 NTSTATUS - The FSD status for the IRP
81 IN PDEVICE_OBJECT DeviceObject
,
86 PtrUDFIrpContext PtrIrpContext
= NULL
;
87 BOOLEAN AreWeTopLevel
;
89 KdPrint(("UDFPnp\n"));
92 FsRtlEnterFileSystem();
96 // set the top level context
97 AreWeTopLevel
= UDFIsIrpTopLevel(Irp
);
100 // We expect there to never be a fileobject, in which case we will always
101 // wait. Since at the moment we don't have any concept of pending Pnp
102 // operations, this is a bit nitpicky.
104 // get an IRP context structure and issue the request
105 PtrIrpContext
= UDFAllocateIrpContext(Irp
, DeviceObject
);
107 RC
= UDFCommonPnp(PtrIrpContext
, Irp
);
109 RC
= STATUS_INSUFFICIENT_RESOURCES
;
110 Irp
->IoStatus
.Status
= RC
;
111 Irp
->IoStatus
.Information
= 0;
113 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
116 } _SEH2_EXCEPT(UDFExceptionFilter( PtrIrpContext
, _SEH2_GetExceptionInformation() )) {
118 RC
= UDFExceptionHandler(PtrIrpContext
, Irp
);
119 UDFLogEvent(UDF_ERROR_INTERNAL_ERROR
, RC
);
123 IoSetTopLevelIrp(NULL
);
126 FsRtlExitFileSystem();
132 This is the common routine for doing PnP operations called
133 by both the fsd and fsp threads
137 Irp - Supplies the Irp to process
141 NTSTATUS - The return status for the operation
145 PtrUDFIrpContext PtrIrpContext
,
150 PIO_STACK_LOCATION IrpSp
;
152 KdPrint(("UDFCommonPnp\n"));
155 // Get the current Irp stack location.
156 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
158 // Make sure this device object really is big enough to be a volume device
159 // object. If it isn't, we need to get out before we try to reference some
160 // field that takes us past the end of an ordinary device object.
161 Vcb
= (PVCB
)(IrpSp
->DeviceObject
->DeviceExtension
);
163 if (Vcb
->NodeIdentifier
.NodeType
!= UDF_NODE_TYPE_VCB
) {
164 // We were called with something we don't understand.
165 if(Irp
->Flags
& IRP_INPUT_OPERATION
) {
166 Irp
->IoStatus
.Information
= 0;
168 Irp
->IoStatus
.Status
= STATUS_INVALID_PARAMETER
;
170 IoCompleteRequest( Irp
, IO_DISK_INCREMENT
);
171 try_return (RC
= STATUS_INVALID_PARAMETER
);
174 // Force everything to wait.
175 PtrIrpContext
->IrpContextFlags
|= UDF_IRP_CONTEXT_CAN_BLOCK
;
177 // Case on the minor code.
178 switch ( IrpSp
->MinorFunction
) {
180 case IRP_MN_QUERY_REMOVE_DEVICE
:
181 RC
= UDFPnpQueryRemove( PtrIrpContext
, Irp
, Vcb
);
184 case IRP_MN_SURPRISE_REMOVAL
:
185 RC
= UDFPnpSurpriseRemove( PtrIrpContext
, Irp
, Vcb
);
188 case IRP_MN_REMOVE_DEVICE
:
189 RC
= UDFPnpRemove( PtrIrpContext
, Irp
, Vcb
);
192 /* case IRP_MN_CANCEL_REMOVE_DEVICE:
193 RC = UDFPnpCancelRemove( PtrIrpContext, Irp, Vcb );
197 KdPrint(("UDFCommonPnp: pass through\n"));
198 // Just pass the IRP on. As we do not need to be in the
199 // way on return, ellide ourselves out of the stack.
200 IoSkipCurrentIrpStackLocation( Irp
);
201 RC
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
202 ASSERT(RC
!= STATUS_PENDING
);
210 UDFReleaseIrpContext(PtrIrpContext
);
219 This routine handles the PnP query remove operation. The filesystem
220 is responsible for answering whether there are any reasons it sees
221 that the volume can not go away (and the device removed). Initiation
222 of the dismount begins when we answer yes to this question.
224 Query will be followed by a Cancel or Remove.
227 Irp - Supplies the Irp to process
228 Vcb - Supplies the volume being queried.
231 NTSTATUS - The return status for the operation
235 PtrUDFIrpContext PtrIrpContext
,
242 BOOLEAN VcbDeleted
= FALSE
;
243 BOOLEAN GlobalHeld
= FALSE
;
244 BOOLEAN VcbAcquired
= FALSE
;
245 PPREVENT_MEDIA_REMOVAL_USER_IN Buf
= NULL
;
247 // Having said yes to a QUERY, any communication with the
248 // underlying storage stack is undefined (and may block)
249 // until the bounding CANCEL or REMOVE is sent.
253 // Acquire the global resource so that we can try to vaporize
254 // the volume, and the vcb resource itself.
255 UDFAcquireResourceExclusive(&(UDFGlobalData
.GlobalDataResource
), TRUE
);
258 if(!(Vcb
->VCBFlags
& UDF_VCB_FLAGS_RAW_DISK
))
259 UDFCloseAllSystemDelayedInDir(Vcb
, Vcb
->RootDirFCB
->FileInfo
);
260 #ifdef UDF_DELAYED_CLOSE
261 UDFCloseAllDelayed(Vcb
);
262 #endif //UDF_DELAYED_CLOSE
264 UDFAcquireResourceExclusive(&(Vcb
->VCBResource
),TRUE
);
267 Buf
= (PPREVENT_MEDIA_REMOVAL_USER_IN
)MyAllocatePool__(NonPagedPool
, sizeof(PREVENT_MEDIA_REMOVAL_USER_IN
));
268 // With the volume held locked, note that we must finalize as much
269 // as possible right now.
270 UDFDoDismountSequence(Vcb
, Buf
, FALSE
);
272 // disable Eject Request Waiter if any
273 UDFReleaseResource( &(Vcb
->VCBResource
) );
275 UDFStopEjectWaiter(Vcb
);
276 UDFAcquireResourceExclusive(&(Vcb
->VCBResource
),TRUE
);
279 // We need to pass this down before starting the dismount, which
280 // could disconnect us immediately from the stack.
282 // Get the next stack location, and copy over the stack location
283 IoCopyCurrentIrpStackLocationToNext( Irp
);
285 // Set up the completion routine
286 KeInitializeEvent( &Event
, NotificationEvent
, FALSE
);
287 IoSetCompletionRoutine( Irp
,
288 UDFPnpCompletionRoutine
,
293 // Send the request and wait.
294 RC
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
296 if (RC
== STATUS_PENDING
) {
297 KeWaitForSingleObject( &Event
,
303 RC
= Irp
->IoStatus
.Status
;
306 // Now if no one below us failed already, initiate the dismount
307 // on this volume, make it go away. PnP needs to see our internal
308 // streams close and drop their references to the target device.
310 // Since we were able to lock the volume, we are guaranteed to
311 // move this volume into dismount state and disconnect it from
312 // the underlying storage stack. The force on our part is actually
313 // unnecesary, though complete.
315 // What is not strictly guaranteed, though, is that the closes
316 // for the metadata streams take effect synchronously underneath
317 // of this call. This would leave references on the target device
318 // even though we are disconnected!
319 if (NT_SUCCESS( RC
)) {
320 VcbDeleted
= !UDFCheckForDismount( PtrIrpContext
, Vcb
, TRUE
);
321 ASSERT( VcbDeleted
);
324 // Release the Vcb if it could still remain.
326 // Note: if everything else succeeded and the Vcb is persistent because the
327 // internal streams did not vaporize, we really need to pend this IRP off on
328 // the side until the dismount is completed. I can't think of a reasonable
329 // case (in UDF) where this would actually happen, though it might still need
330 // to be implemented.
332 // The reason this is the case is that handles/fileobjects place a reference
333 // on the device objects they overly. In the filesystem case, these references
334 // are on our target devices. PnP correcly thinks that if references remain
335 // on the device objects in the stack that someone has a handle, and that this
336 // counts as a reason to not succeed the query - even though every interrogated
337 // driver thinks that it is OK.
338 ASSERT( !(NT_SUCCESS( RC
) && !VcbDeleted
));
342 if (!VcbDeleted
&& VcbAcquired
) {
343 UDFReleaseResource( &(Vcb
->VCBResource
) );
347 UDFReleaseResource( &(UDFGlobalData
.GlobalDataResource
) );
350 if (!_SEH2_AbnormalTermination()) {
351 Irp
->IoStatus
.Status
= RC
;
352 // Free up the Irp Context
353 UDFReleaseIrpContext(PtrIrpContext
);
355 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
360 } // end UDFPnpQueryRemove()
365 This routine handles the PnP remove operation. This is our notification
366 that the underlying storage device for the volume we have is gone, and
367 an excellent indication that the volume will never reappear. The filesystem
368 is responsible for initiation or completion of the dismount.
371 Irp - Supplies the Irp to process
372 Vcb - Supplies the volume being removed.
375 NTSTATUS - The return status for the operation
380 PtrUDFIrpContext PtrIrpContext
,
389 PPREVENT_MEDIA_REMOVAL_USER_IN Buf
= NULL
;
391 // REMOVE - a storage device is now gone. We either got
392 // QUERY'd and said yes OR got a SURPRISE OR a storage
393 // stack failed to spin back up from a sleep/stop state
394 // (the only case in which this will be the first warning).
396 // Note that it is entirely unlikely that we will be around
397 // for a REMOVE in the first two cases, as we try to intiate
400 // Acquire the global resource so that we can try to vaporize
401 // the volume, and the vcb resource itself.
402 UDFAcquireResourceExclusive(&(UDFGlobalData
.GlobalDataResource
), TRUE
);
404 if(!(Vcb
->VCBFlags
& UDF_VCB_FLAGS_RAW_DISK
))
405 UDFCloseAllSystemDelayedInDir(Vcb
, Vcb
->RootDirFCB
->FileInfo
);
406 #ifdef UDF_DELAYED_CLOSE
407 UDFCloseAllDelayed(Vcb
);
408 #endif //UDF_DELAYED_CLOSE
410 UDFAcquireResourceExclusive(&(Vcb
->VCBResource
),TRUE
);
413 // The device will be going away. Remove our lock (benign
414 // if we never had it).
415 if((Vcb
->Vpb
->Flags
& VPB_LOCKED
) ||
416 (Vcb
->VolumeLockPID
!= -1) ) {
417 Vcb
->Vpb
->Flags
&= ~VPB_LOCKED
;
418 Vcb
->VCBFlags
&= ~UDF_VCB_FLAGS_VOLUME_LOCKED
;
419 Vcb
->VolumeLockFileObject
= NULL
;
420 Vcb
->VolumeLockPID
= -1;
424 // We need to pass this down before starting the dismount, which
425 // could disconnect us immediately from the stack.
427 // Get the next stack location, and copy over the stack location
428 IoCopyCurrentIrpStackLocationToNext( Irp
);
430 // Set up the completion routine
431 KeInitializeEvent( &Event
, NotificationEvent
, FALSE
);
432 IoSetCompletionRoutine( Irp
,
433 UDFPnpCompletionRoutine
,
439 // Send the request and wait.
440 RC
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
442 if (RC
== STATUS_PENDING
) {
444 KeWaitForSingleObject( &Event
,
450 RC
= Irp
->IoStatus
.Status
;
455 // Knock as many files down for this volume as we can.
457 // Now make our dismount happen. This may not vaporize the
458 // Vcb, of course, since there could be any number of handles
459 // outstanding if we were not preceeded by a QUERY.
461 // PnP will take care of disconnecting this stack if we
462 // couldn't get off of it immediately.
463 Vcb
->Vpb
->RealDevice
->Flags
|= DO_VERIFY_VOLUME
;
464 Buf
= (PPREVENT_MEDIA_REMOVAL_USER_IN
)MyAllocatePool__(NonPagedPool
, sizeof(PREVENT_MEDIA_REMOVAL_USER_IN
));
465 if(!Buf
) try_return(RC
= STATUS_INSUFFICIENT_RESOURCES
);
466 UDFDoDismountSequence(Vcb
, Buf
, FALSE
);
467 Vcb
->VCBFlags
&= ~UDF_VCB_FLAGS_VOLUME_MOUNTED
;
468 Vcb
->WriteSecurity
= FALSE
;
469 // disable Eject Request Waiter if any
470 UDFReleaseResource( &(Vcb
->VCBResource
) );
473 UDFStopEjectWaiter(Vcb
);
475 VcbDeleted
= !UDFCheckForDismount( PtrIrpContext
, Vcb
, FALSE
);
480 // Release the Vcb if it could still remain.
481 if (!VcbDeleted
&& VcbAcquired
) {
482 UDFReleaseResource(&(Vcb
->VCBResource
));
484 UDFReleaseResource(&(UDFGlobalData
.GlobalDataResource
));
489 if (!_SEH2_AbnormalTermination()) {
490 Irp
->IoStatus
.Status
= RC
;
491 // Free up the Irp Context
492 UDFReleaseIrpContext(PtrIrpContext
);
494 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
503 UDFPnpSurpriseRemove (
504 PtrUDFIrpContext PtrIrpContext
,
513 This routine handles the PnP surprise remove operation. This is another
514 type of notification that the underlying storage device for the volume we
515 have is gone, and is excellent indication that the volume will never reappear.
516 The filesystem is responsible for initiation or completion the dismount.
518 For the most part, only "real" drivers care about the distinction of a
519 surprise remove, which is a result of our noticing that a user (usually)
520 physically reached into the machine and pulled something out.
522 Surprise will be followed by a Remove when all references have been shut down.
526 Irp - Supplies the Irp to process
528 Vcb - Supplies the volume being removed.
532 NTSTATUS - The return status for the operation
541 PPREVENT_MEDIA_REMOVAL_USER_IN Buf
= NULL
;
543 // SURPRISE - a device was physically yanked away without
544 // any warning. This means external forces.
546 UDFAcquireResourceExclusive(&(UDFGlobalData
.GlobalDataResource
), TRUE
);
548 if(!(Vcb
->VCBFlags
& UDF_VCB_FLAGS_RAW_DISK
))
549 UDFCloseAllSystemDelayedInDir(Vcb
, Vcb
->RootDirFCB
->FileInfo
);
550 #ifdef UDF_DELAYED_CLOSE
551 UDFCloseAllDelayed(Vcb
);
552 #endif //UDF_DELAYED_CLOSE
554 UDFAcquireResourceExclusive(&(Vcb
->VCBResource
),TRUE
);
557 // We need to pass this down before starting the dismount, which
558 // could disconnect us immediately from the stack.
560 // Get the next stack location, and copy over the stack location
561 IoCopyCurrentIrpStackLocationToNext( Irp
);
563 // Set up the completion routine
564 KeInitializeEvent( &Event
, NotificationEvent
, FALSE
);
565 IoSetCompletionRoutine( Irp
,
566 UDFPnpCompletionRoutine
,
572 // Send the request and wait.
573 RC
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
575 if (RC
== STATUS_PENDING
) {
577 KeWaitForSingleObject( &Event
,
583 RC
= Irp
->IoStatus
.Status
;
587 // Knock as many files down for this volume as we can.
588 Vcb
->Vpb
->RealDevice
->Flags
|= DO_VERIFY_VOLUME
;
589 Buf
= (PPREVENT_MEDIA_REMOVAL_USER_IN
)MyAllocatePool__(NonPagedPool
, sizeof(PREVENT_MEDIA_REMOVAL_USER_IN
));
590 if(!Buf
) try_return(RC
= STATUS_INSUFFICIENT_RESOURCES
);
591 UDFDoDismountSequence(Vcb
, Buf
, FALSE
);
592 Vcb
->VCBFlags
&= ~UDF_VCB_FLAGS_VOLUME_MOUNTED
;
593 Vcb
->WriteSecurity
= FALSE
;
595 UDFReleaseResource(&(Vcb
->VCBResource
));
598 UDFStopEjectWaiter(Vcb
);
600 // Now make our dismount happen. This may not vaporize the
601 // Vcb, of course, since there could be any number of handles
602 // outstanding since this is an out of band notification.
603 VcbDeleted
= !UDFCheckForDismount( PtrIrpContext
, Vcb
, FALSE
);
609 // Release the Vcb if it could still remain.
610 if (!VcbDeleted
&& VcbAcquired
) {
611 UDFReleaseResource(&(Vcb
->VCBResource
));
613 UDFReleaseResource(&(UDFGlobalData
.GlobalDataResource
));
618 if (!_SEH2_AbnormalTermination()) {
619 Irp
->IoStatus
.Status
= RC
;
620 // Free up the Irp Context
621 UDFReleaseIrpContext(PtrIrpContext
);
623 IoCompleteRequest(Irp
, IO_DISK_INCREMENT
);
633 PtrUDFIrpContext PtrIrpContext,
642 This routine handles the PnP cancel remove operation. This is our
643 notification that a previously proposed remove (query) was eventually
644 vetoed by a component. The filesystem is responsible for cleaning up
645 and getting ready for more IO.
649 Irp - Supplies the Irp to process
651 Vcb - Supplies the volume being removed.
655 NTSTATUS - The return status for the operation
662 // CANCEL - a previous QUERY has been rescinded as a result
663 // of someone vetoing. Since PnP cannot figure out who may
664 // have gotten the QUERY (think about it: stacked drivers),
665 // we must expect to deal with getting a CANCEL without having
668 // For UDF, this is quite easy. In fact, we can't get a
669 // CANCEL if the underlying drivers succeeded the QUERY since
670 // we disconnect the Vpb on our dismount initiation. This is
671 // actually pretty important because if PnP could get to us
672 // after the disconnect we'd be thoroughly unsynchronized
673 // with respect to the Vcb getting torn apart - merely referencing
674 // the volume device object is insufficient to keep us intact.
676 UDFAcquireResourceExclusive(&(Vcb->VCBResource),TRUE);
678 // Unlock the volume. This is benign if we never had seen
680 if(Vcb->Vpb->Flags & VPB_LOCKED) {
681 Vcb->Vpb->Flags &= ~VPB_LOCKED;
682 Vcb->VCBFlags &= ~UDF_VCB_FLAGS_VOLUME_LOCKED;
683 Vcb->VolumeLockFileObject = NULL;
686 RC = STATUS_NOT_LOCKED;
691 // We must re-enable allocation support if we got through
692 // the first stages of a QUERY_REMOVE; i.e., we decided we
693 // could place a lock on the volume.
694 if (NT_SUCCESS( RC )) {
695 FatSetupAllocationSupport( PtrIrpContext, Vcb );
699 UDFReleaseResource(&(Vcb->VCBResource));
702 // Send the request. The underlying driver will complete the
703 // IRP. Since we don't need to be in the way, simply ellide
704 // ourselves out of the IRP stack.
705 IoSkipCurrentIrpStackLocation( Irp );
707 RC = IoCallDriver(Vcb->TargetDeviceObject, Irp);
709 // if (!AbnormalTermination()) {
710 Irp->IoStatus.Status = RC;
711 // Free up the Irp Context
712 UDFReleaseIrpContext(PtrIrpContext);
714 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
721 // Local support routine
724 UDFPnpCompletionRoutine (
725 IN PDEVICE_OBJECT DeviceObject
,
730 PKEVENT Event
= (PKEVENT
) Contxt
;
732 KeSetEvent( Event
, 0, FALSE
);
734 return STATUS_MORE_PROCESSING_REQUIRED
;
736 UNREFERENCED_PARAMETER( DeviceObject
);
737 UNREFERENCED_PARAMETER( Contxt
);