FILE_LOCK_CONFLICT on FltWriteFileEx() from a worker Thread

Hello,

I’m facing a problem that I don’t know how to overcome.
I have this code snippet, it just lock some part of a file (c:\lock_test.txt) and then tries to write on some bytes of this part. It runs well, no problems.

h_file=CreateFileA(p_file_name, GENERIC_READ|GENERIC_WRITE,
	FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL,
	OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL , NULL);
LockFile(h_file, 0, 0, 1, 0);
WriteFile(h_file, buff, 10, &ucb_written, NULL);

Then, I have a minifilter, that intercepts this WRITE request and posts it to a worker thread. The worker thread tries to perform some WRITE on this file using FltWriteFileEx(). In the sample I just issue the same WRITE request I received and then complete the I/O.

/*------------------------------------------------------------------------------
IRPFlagsToIoOperationFlags
------------------------------------------------------------------------------*/
FLT_IO_OPERATION_FLAGS IRPFlagsToIoOperationFlags(ULONG uIRPFlags)
{
    FLT_IO_OPERATION_FLAGS ioFlags=0;
	
	if (uIRPFlags&IRP_NOCACHE) {
	    ioFlags=FLTFL_IO_OPERATION_NON_CACHED;
	}
	if (uIRPFlags&IRP_PAGING_IO) {
	    ioFlags=FLTFL_IO_OPERATION_PAGING;
	}
	if (uIRPFlags&IRP_SYNCHRONOUS_PAGING_IO) {
	    ioFlags=FLTFL_IO_OPERATION_SYNCHRONOUS_PAGING;
	}
	return ioFlags;
}
/*------------------------------------------------------------------------------
pWriteCB
------------------------------------------------------------------------------*/
void pWriteCB(PFLT_DEFERRED_IO_WORKITEM pDW, PFLT_CALLBACK_DATA pCBData,
    void *pCtx)
{
	NTSTATUS rc;
	ULONG ucbWritten=0;
	
	rc=FltWriteFileEx(
		pCBData->Iopb->TargetInstance,
		pCBData->Iopb->TargetFileObject,
		&pCBData->Iopb->Parameters.Write.ByteOffset,
		pCBData->Iopb->Parameters.Write.Length,
		NULL,
		IRPFlagsToIoOperationFlags(pCBData->Iopb->IrpFlags),
		&ucbWritten,
		NULL, NULL, &pCBData->Iopb->Parameters.Write.Key,
		pCBData->Iopb->Parameters.Write.MdlAddress);
		
	__DBG_ERR("Write request ended with 0x%x, bytes written %u.\n", rc, ucbWritten);
	
	pCBData->IoStatus.Status=rc;
	pCBData->IoStatus.Information=ucbWritten;
	
	FltCompletePendedPreOperation(pCBData, FLT_PREOP_COMPLETE, NULL);
	FltFreeDeferredIoWorkItem(pDW);
}

DECLARE_CONST_UNICODE_STRING(usLockTest, L"\\device\\harddiskvolume3\\lock_test.txt");
/*------------------------------------------------------------------------------
WritePreOp
------------------------------------------------------------------------------*/
FLT_PREOP_CALLBACK_STATUS WritePreOp(
    _Inout_ PFLT_CALLBACK_DATA pCBData, _In_ PCFLT_RELATED_OBJECTS FltObjects,
    _Flt_CompletionContext_Outptr_ PVOID *CompletionContext)
{
    NTSTATUS rc, ret;
	PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
    PFLT_DEFERRED_IO_WORKITEM pDW;
		
	rc=FltGetFileNameInformation(pCBData,
		FLT_FILE_NAME_OPENED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
	if (rc!=STATUS_SUCCESS) {
		ret=FLT_PREOP_SUCCESS_NO_CALLBACK;
		goto end;
	}
	if (RtlEqualUnicodeString(&nameInfo->Name, &usLockTest, TRUE)==FALSE) {
	    ret=FLT_PREOP_SUCCESS_NO_CALLBACK;
		goto end;
	}
	__DBG("Got a WRITE on %wZ.\n", nameInfo->Name);

	rc=FltLockUserBuffer(pCBData);
	if (rc!=STATUS_SUCCESS) {
		__DBG_ERR("Failed to lock UserBuffer %p.\n", pCBData->Iopb->Parameters.Write.WriteBuffer);
		ret=FLT_PREOP_SUCCESS_NO_CALLBACK;
		goto end;
	}
	pDW=FltAllocateDeferredIoWorkItem();
	if (pDW==NULL) {
		__DBG_ERR("Failed allocating a WORKITEM.\n");
		ret=FLT_PREOP_SUCCESS_NO_CALLBACK;
		goto end;
	}
	rc=FltQueueDeferredIoWorkItem(pDW, pCBData, pWriteCB, DelayedWorkQueue, NULL);
	if (rc!=STATUS_SUCCESS) {
		__DBG_ERR("Failed posting WORKITEM.\n");
		FltFreeDeferredIoWorkItem(pDW);
		ret=FLT_PREOP_SUCCESS_NO_CALLBACK;
		goto end;
	}
	__DBG("THREAD IS %p.\n", pCBData->Thread);
	ret=FLT_PREOP_PENDING;

end:
    if (nameInfo!=NULL) {
        FltReleaseFileNameInformation( nameInfo );
	}
    return ret;
}

But the FltWriteFileEx() ends with a FILE_LOCK_CONFLICT. I imagine it happends because I switch thread context.
I also tried to do FltAllocateCallbackDataEx() & FltPerformSynchronousIo() but it just ends the same way.

If I forward the posted request, it works well :

/*------------------------------------------------------------------------------
pWriteCB3
------------------------------------------------------------------------------*/
void pWriteCB3(PFLT_DEFERRED_IO_WORKITEM pDW, PFLT_CALLBACK_DATA pCBData,
    void *pCtx)
{
	UNREFERENCED_PARAMETER(pCtx);
		
	__DBG("Write request TRANSMITTED.");
	
	FltCompletePendedPreOperation(pCBData, FLT_PREOP_SUCCESS_NO_CALLBACK, NULL);
	FltFreeDeferredIoWorkItem(pDW);
}

What can I do to no have this FILE_LOCK_CONFLICT ? Because I really need to post this request and then perform some other requests …

Thank you !

I think that the problem is that there is an implied process in the CBD you get given in your PreWrite and issuing a FltWrite doesn’t have a slot for it.

Is there any reason why you cannot, in your worker thread, just restart the CBD you get given in your PreWrite call?

If not - and I have no idea if this would work - you might get lucky by allocating the CBD you are going to use in the context of your pre-write, passing it to your worker thread, and FltPerformSynchronousIo-ing it there.

Thank you a lot for your reply Rod.

I tried to alter the “Thread” field of the CBD before calling FltPerformSynchronousIo() (even if it is const) but it didn’t work.

typedef struct _FLT_CALLBACK_DATA {
  FLT_CALLBACK_DATA_FLAGS     Flags;
  PETHREAD                    Thread;
  PFLT_IO_PARAMETER_BLOCK     Iopb;

What do you mean by “just restart the CBD you get given in your PreWrite call” ?

I just tried your idea of allocating the CBD in the pre-write and use it in the worker thread, but I get the same result.

If by “just restart the CBD you get given in your PreWrite call” you mean passing the CBD to FltPerformSynchronousIo(), it doesn’t work.
FILTER VERIFIER checks this an issues an error :

A filter is trying to use a callbackdata wich wasn't generated using FltAllocateCallbackData().

No, I mean calling FltCompletePendedPreOperation with FLT_PREOP_SUCCESS_WITH_CALLBACK

Yes this works, but I want to send more than one I/O (READs and WRITEs).
I don’t see a way to do this without getting FILE_LOCK_CONFLICT …

I’m starting to wonder if it’s not something the guys at Microsoft forgot… because there is no way to perform a FltReadFile() or a FltWriteFile() on a portion of a file that has been locked in a worker thread …
If someone has any idea I am very interested !