1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     FxFileObject.hpp
8 
9 Abstract:
10 
11     This module implements a frameworks managed FileObject
12 
13 Author:
14 
15 
16 
17 Environment:
18 
19     Both kernel and user mode
20 
21 Revision History:
22 
23 
24 --*/
25 
26 #include "coreprivshared.hpp"
27 
28 // Tracing support
29 extern "C" {
30 // #include "FxFileObject.tmh"
31 }
32 
33 //
34 // Public constructors
35 //
36 
37 
38 FxFileObject::FxFileObject(
39     __in PFX_DRIVER_GLOBALS FxDriverGlobals,
40     __in MdFileObject       pWdmFileObject,
41     __in FxDevice*   pDevice
42     ) :
43     FxNonPagedObject(FX_TYPE_FILEOBJECT, sizeof(FxFileObject), FxDriverGlobals)
44 {
45     m_FileObject.SetFileObject(pWdmFileObject);
46     m_PkgContext = NULL;
47     m_Device = pDevice;
48 
49 #if (FX_CORE_MODE == FX_CORE_USER_MODE)
50     RtlInitUnicodeString(&m_FileName, NULL);
51     m_RelatedFileObject = NULL;
52 #endif
53 
54     //
55     // Cannot be deleted by the device driver
56     //
57     MarkNoDeleteDDI();
58 }
59 
60 FxFileObject::~FxFileObject()
61 {
62 #if (FX_CORE_MODE == FX_CORE_USER_MODE)
63     SAFE_RELEASE(m_RelatedFileObject);
64 #endif
65 }
66 
67 //
68 // Create the WDFFILEOBJECT and associate it with the WDM PFILE_OBJECT.
69 //
70 _Must_inspect_result_
71 NTSTATUS
72 FxFileObject::_CreateFileObject(
73     __in FxDevice*                   pDevice,
74     __in MdIrp                        Irp,
75     __in WDF_FILEOBJECT_CLASS        FileObjectClass,
76     __in_opt PWDF_OBJECT_ATTRIBUTES  pObjectAttributes,
77     __in_opt MdFileObject            pWdmFileObject,
78     __deref_out_opt FxFileObject**   pFileObject
79     )
80 {
81     NTSTATUS Status;
82     FxFileObject* pfo;
83     KIRQL irql;
84     WDF_FILEOBJECT_CLASS normalizedFileClass;
85 
86     PFX_DRIVER_GLOBALS FxDriverGlobals = pDevice->GetDriverGlobals();
87 
88     //
89     // Create does require a WDM file obj based on the normalized file obj
90     // class value.
91     //
92     normalizedFileClass = FxFileObjectClassNormalize(FileObjectClass);
93 
94     //
95     // No FileObject support
96     //
97     if( normalizedFileClass == WdfFileObjectNotRequired ) {
98         if( pFileObject != NULL ) *pFileObject = NULL;
99         return STATUS_SUCCESS;
100     }
101 
102     //
103     // If fileobject support was specified, and a NULL
104     // WDM PFILE_OBJECT is supplied, then it's an error
105     //
106     if( pWdmFileObject == NULL ) {
107 
108         //
109         // Seems like some filter driver above us sending a create without fileobject.
110         // We support this only if the FileObjectClass class is set to
111         // WdfFileObjectWdfCannotUseFsContexts and device is created to be
112         // exclusive.
113         //
114         if ( normalizedFileClass == WdfFileObjectWdfCannotUseFsContexts  &&
115              pDevice->IsExclusive() ) {
116 
117             DO_NOTHING();
118 
119         } else {
120             DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
121               "WdfFileObjectWdfCanUseFsContexts is specified, but the Create "
122               "IRP %p doesn't have a fileObject\n",
123               Irp);
124             FxVerifierDbgBreakPoint(pDevice->GetDriverGlobals());
125             return STATUS_DEVICE_CONFIGURATION_ERROR;
126         }
127 
128     }
129 
130     // Allocate a new FxFileObject with context
131     pfo = new(pDevice->GetDriverGlobals(), pObjectAttributes)
132              FxFileObject(pDevice->GetDriverGlobals(), pWdmFileObject, pDevice);
133 
134     if( pfo == NULL ) {
135         return STATUS_INSUFFICIENT_RESOURCES;
136     }
137 
138     pfo->Initialize(Irp);
139 
140     // Assign FxDevice as the parent for the file object
141     Status = pfo->Commit(pObjectAttributes, NULL, pDevice);
142     if( !NT_SUCCESS(Status) ) {
143         pfo->DeleteFromFailedCreate();
144         return Status;
145     }
146 
147     //
148     // Place it on the list of FxFileObject's for this FxDevice
149     //
150     pDevice->Lock(&irql);
151     InsertHeadList(&pDevice->m_FileObjectListHead, &pfo->m_Link);
152     pDevice->Unlock(irql);
153 
154     //
155     // Set file object context in mode-specific manner
156     //
157     pfo->SetFileObjectContext(pWdmFileObject,
158                               normalizedFileClass,
159                               Irp,
160                               pDevice);
161 
162     // FxFileObject* to caller
163     if( pFileObject != NULL ) {
164         *pFileObject = pfo;
165     }
166 
167     return STATUS_SUCCESS;
168 }
169 
170 //
171 // Destroy (dereference) the WDFFILEOBJECT related to the
172 // WDM PFILE_OBJECT according to its FileObjectClass.
173 //
174 VOID
175 FxFileObject::_DestroyFileObject(
176     __in FxDevice*                   pDevice,
177     __in WDF_FILEOBJECT_CLASS        FileObjectClass,
178     __in_opt MdFileObject            pWdmFileObject
179     )
180 {
181     FxFileObject* pfo = NULL;
182     PFX_DRIVER_GLOBALS FxDriverGlobals = pDevice->GetDriverGlobals();
183     WDF_FILEOBJECT_CLASS normalizedFileClass;
184 
185     //
186     // Close does require a WDM file obj based on the normalized file obj
187     // class value.
188     //
189     normalizedFileClass = FxFileObjectClassNormalize(FileObjectClass);
190 
191     if( normalizedFileClass == WdfFileObjectNotRequired ) {
192         return;
193     }
194 
195     //
196     // Driver has specified file object support, and we
197     // allocated one at Create, so they must pass one
198     // to close, otherwise it's an error and we will leak
199     // the file object.
200     //
201     MxFileObject wdmFileObject(pWdmFileObject);
202     if( pWdmFileObject == NULL &&
203         normalizedFileClass != WdfFileObjectWdfCannotUseFsContexts) {
204 
205         //
206         // It's likely that IRP_MJ_CREATE had NULL for the Wdm FileObject as well.
207         //
208         // If a driver passes != NULL for Wdm FileObject to create, and NULL to
209         // this routine, a WDF FxFileObject object leak will occur, which will
210         // be reported at driver unload.
211         //
212         DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGIO,
213            "PFILE_OBJECT in close IRP is NULL, *Possible Leak of FxFileObject*\n");
214 
215         FxVerifierDbgBreakPoint(pDevice->GetDriverGlobals());
216 
217         return;
218     }
219     else if( normalizedFileClass == WdfFileObjectWdfCanUseFsContext ) {
220         pfo = (FxFileObject*)wdmFileObject.GetFsContext();
221         wdmFileObject.SetFsContext(NULL);
222 
223     }
224     else if( normalizedFileClass == WdfFileObjectWdfCanUseFsContext2 ) {
225         pfo = (FxFileObject*)wdmFileObject.GetFsContext2();
226         wdmFileObject.SetFsContext2(NULL);
227     }
228     else {
229         NTSTATUS status;
230         //
231         // We must find the associated FxFileObject from the list
232         // on the device
233         //
234         status = FxFileObject::_GetFileObjectFromWdm(
235                           pDevice,
236                           WdfFileObjectWdfCannotUseFsContexts,
237                           pWdmFileObject,
238                           &pfo
239                           );
240 
241         //
242         // We should find it, unless a different one was passed to IRP_MJ_CLOSE
243         // than to IRP_MJ_CREATE, which is an error.
244         //
245         if (NT_SUCCESS(status) == FALSE || pfo == NULL) {
246             DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE,
247                             "Could not find WDFFILEOBJECT for PFILE_OBJECT 0x%p",
248                                 pWdmFileObject);
249 
250             DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE,
251                             "Did a different PFILE_OBJECT get passed to "
252                             "IRP_MJ_CLOSE than did to IRP_MJ_CREATE?");
253             FxVerifierDbgBreakPoint(pDevice->GetDriverGlobals());
254         }
255     }
256 
257     if( pfo != NULL ) {
258         KIRQL irql;
259 
260         //
261         // Remove it from the list of FxFileObjects on the FxDevice
262         //
263         pDevice->Lock(&irql);
264         RemoveEntryList(&pfo->m_Link);
265         pDevice->Unlock(irql);
266 
267         // Delete the file object
268         pfo->DeleteObject();
269     }
270 
271     return;
272 }
273 
274 //
275 // Return the FxFileObject* for the given WDM PFILE_OBJECT
276 //
277 _Must_inspect_result_
278 NTSTATUS
279 FxFileObject::_GetFileObjectFromWdm(
280     __in  FxDevice*                   pDevice,
281     __in  WDF_FILEOBJECT_CLASS        FileObjectClass,
282     __in_opt  MdFileObject            pWdmFileObject,
283     __deref_out_opt FxFileObject**    ppFxFileObject
284     )
285 {
286     FxFileObject* pfo = NULL;
287     PFX_DRIVER_GLOBALS FxDriverGlobals = pDevice->GetDriverGlobals();
288     WDF_FILEOBJECT_CLASS normalizedFileClass;
289 
290     //
291     // Normalize file object class value.
292     //
293     normalizedFileClass = FxFileObjectClassNormalize(FileObjectClass);
294 
295     //
296     // No FileObject support
297     //
298     if( normalizedFileClass == WdfFileObjectNotRequired ) {
299         *ppFxFileObject = NULL;
300         return STATUS_SUCCESS;
301     }
302 
303     if( pWdmFileObject == NULL ) {
304 
305         //
306         // Warn if an I/O request has NULL for the WDM PFILE_OBJECT
307         //
308         if ( pDevice->IsExclusive() &&
309              normalizedFileClass == WdfFileObjectWdfCannotUseFsContexts ) {
310             //
311             // We allow a NULL file object iff the device is exclusive and
312             // we have to look up the WDFFILEOBJECT by PFILE_OBJECT value
313             //
314             DO_NOTHING();
315         }
316         else if ( FxIsFileObjectOptional(FileObjectClass) ) {
317             //
318             // Driver told us that it's able to handle this case.
319             //
320             *ppFxFileObject = NULL;
321             return STATUS_SUCCESS;
322         }
323         else {
324             DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE,
325                                     "NULL passed for PFILE_OBJECT when FileObject "
326                                     "support is requested in an I/O request");
327             FxVerifierDbgBreakPoint(pDevice->GetDriverGlobals());
328 
329             return STATUS_UNSUCCESSFUL;
330         }
331     }
332 
333     //
334     // Depending on the drivers configuration, we can quickly
335     // get the FxFileObject* from FxContext, or FxContext2.
336     //
337     // Some drivers can not touch either of the FxContext(s), and
338     // in that case we must resort to a list or hashtable.
339     //
340     MxFileObject wdmFileObject(pWdmFileObject);
341     if( normalizedFileClass == WdfFileObjectWdfCanUseFsContext ) {
342         pfo = (FxFileObject*)wdmFileObject.GetFsContext();
343     }
344     else if( normalizedFileClass == WdfFileObjectWdfCanUseFsContext2 ) {
345         pfo = (FxFileObject*)wdmFileObject.GetFsContext2();
346     }
347     else {
348         PLIST_ENTRY next;
349         FxFileObject* f;
350         KIRQL irql;
351 
352         //
353         // Must look it up from the FxDevice->m_FileObjectListHead.
354         //
355         pfo = NULL;
356 
357         pDevice->Lock(&irql);
358 
359         next = pDevice->m_FileObjectListHead.Flink;
360 
361         if(pWdmFileObject == NULL) {
362             //
363             // If the pWdmFileObject is NULL then we will pass the first entry
364             // in the list because the device must be exclusive and there
365             // can be only one fileobject in the list.
366             //
367             ASSERT(IsListEmpty(&pDevice->m_FileObjectListHead) == FALSE);
368 
369             f = CONTAINING_RECORD(next, FxFileObject, m_Link);
370             pfo = f;
371 
372         } else {
373 
374             while( next != &pDevice->m_FileObjectListHead ) {
375                 f = CONTAINING_RECORD(next, FxFileObject, m_Link);
376 
377                 if( f->m_FileObject.GetFileObject()== pWdmFileObject ) {
378                     pfo = f;
379                     break;
380                 }
381 
382                 next = next->Flink;
383             }
384         }
385 
386 
387 
388 
389 
390 
391 
392 
393 
394 
395         if(pfo == NULL
396              && pDevice->IsExclusive()
397              && pDevice->GetMxDeviceObject()->GetDeviceType() == FILE_DEVICE_SERIAL_PORT
398              && !IsListEmpty(&pDevice->m_FileObjectListHead)) {
399 
400             f = CONTAINING_RECORD(pDevice->m_FileObjectListHead.Flink,
401                                     FxFileObject, m_Link);
402             pfo = f;
403 
404             DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGDEVICE,
405                             "The PFILE_OBJECT 0x%p in this request (cleanup/close) "
406                             "is different from the one specified in "
407                             "create request 0x%p.This is bad!", pWdmFileObject,
408                             ((f != NULL) ? f->m_FileObject.GetFileObject(): NULL));
409             DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGDEVICE,
410                             "Since this is a serial port device, framework is "
411                             "using a workaround to allow this");
412         }
413 
414         pDevice->Unlock(irql);
415     }
416 
417     //
418     // This can happen if a different PFILE_OBJECT is passed to an I/O
419     // request than was presented to IRP_MJ_CREATE
420     //
421     if (pfo == NULL && FxIsFileObjectOptional(FileObjectClass) == FALSE) {
422 
423         DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE,
424                             "Could not locate WDFFILEOBJECT for "
425                             "PFILE_OBJECT 0x%p",pWdmFileObject);
426 
427         DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE,
428                             "Did a different PFILE_OBJECT get passed to the "
429                             "request than was to IRP_MJ_CREATE?");
430 
431 
432 
433 
434 
435 
436 
437 
438 
439 
440         if (FxDriverGlobals->IsVersionGreaterThanOrEqualTo(1,9)) {
441             FxVerifierDbgBreakPoint(pDevice->GetDriverGlobals());
442         }
443     }
444 
445     //
446     // We don't do an extra reference count since the file objects
447     // lifetime is controlled by the IoMgr. When the IRP_MJ_CLOSE
448     // occurs, the reference is released after the optional
449     // driver event callback.
450     //
451 
452     *ppFxFileObject = pfo;
453 
454     return STATUS_SUCCESS;
455 }
456 
457 VOID
458 FxFileObject::DeleteFileObjectFromFailedCreate(
459     VOID
460     )
461 {
462     KIRQL irql;
463 
464     //
465     // Remove it from the list of FxFileObjects on the FxDevice
466     //
467     m_Device->Lock(&irql);
468     RemoveEntryList(&m_Link);
469     m_Device->Unlock(irql);
470 
471     // Delete the file object
472     DeleteFromFailedCreate();
473 }
474 
475 _Must_inspect_result_
476 NTSTATUS
477 FxFileObject::QueryInterface(
478     __in FxQueryInterfaceParams* Params
479     )
480 /*++
481 
482 Routine Description:
483     This routine is used to return the pointer to the Object itself.
484 
485 Arguments:
486     Params - query interface parameters.
487 
488 Return Value:
489     NTSTATUS
490 
491 --*/
492 
493 {
494     switch (Params->Type) {
495     case FX_TYPE_FILEOBJECT:
496         *Params->Object = (FxFileObject*) this;
497         break;
498 
499     case FX_TYPE_IHASCALLBACKS:
500         *Params->Object = (IFxHasCallbacks*) this;
501         break;
502 
503     default:
504         return FxNonPagedObject::QueryInterface(Params); // __super call
505     }
506 
507     return STATUS_SUCCESS;
508 }
509 
510 VOID
511 FxFileObject::GetConstraints(
512     __in WDF_EXECUTION_LEVEL*       ExecutionLevel,
513     __in WDF_SYNCHRONIZATION_SCOPE* SynchronizationScope
514     )
515 /*++
516 
517 Routine Description:
518     This routine implements the  GetConstraints IfxCallback. The caller gets the
519     ExecutionLevel and the SynchronizationScope of the FileObject. The FileObject's
520     SynchronizationScope and ExecutionLevel is strored in the FxPkgGeneral object.
521 
522 Arguments:
523     ExecutionLevel - (opt) receives the execution level.
524 
525     SynchronizationScope - (opt) receives the synchronization scope.
526 
527 Return Value:
528     None
529 
530 --*/
531 
532 {
533     //
534     // Get it from FxPkgGeneral which has the File attributes passed to it at device Creation time.
535     //
536     return GetDevice()->m_PkgGeneral->GetConstraintsHelper(ExecutionLevel, SynchronizationScope);
537 }
538 
539 _Must_inspect_result_
540 FxCallbackLock*
541 FxFileObject::GetCallbackLockPtr(
542     __deref_out_opt FxObject** LockObject
543     )
544 /*++
545 
546 Routine Description:
547     This routine implements the GetCallbackLockPtr IfxCallback. The caller gets the
548     LockObject to be used before calling the driver callbacks.
549 
550 Arguments:
551     LockObject - receives the lock object.
552 
553 Return Value:
554     FxCallbackLock *
555 
556 --*/
557 
558 {
559     //
560     // Get it from FxPkgGeneral which has the File attributes passed to it at device Creation time.
561     //
562     return GetDevice()->m_PkgGeneral->GetCallbackLockPtrHelper(LockObject);
563 }
564 
565 
566