Hi Johri,
Thanks for the input. FBWF is our temporary solution until our own driver is ready. We are looking for FS level right now and not on sector/volume level.
Each file will get its stream context and stream handle context during file create. When IRP_MJ_WRITE (or any IRP that changes the data) comes, the PreWrite will copy the file to COWSTORAGE and set FileChanges = TRUE on Stream context. This flag tells the driver that the file has been modified. Then all next subsequent IRP will be redirected to new file which resides on COWSTORAGE. This works perfectly for normal file operation, but for file mapping it is not.
The user mode application below might explain the issue.
//
//? File is created normally. The driver will create stream context/stream handle context for this
//? file. The file already exists on current folder, but doesn’t exists yet on COWSTORAGE.
//? File contains “aaaa”.
//
??? hFile = CreateFile( TEXT(“fileop_test.txt”), GENERIC_READ | GENERIC_WRITE,
??? ??? FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
??? ??? NULL,
??? ??? OPEN_EXISTING,
??? ??? FILE_ATTRIBUTE_NORMAL,
??? ??? NULL);
??? if (INVALID_HANDLE_VALUE != hFile) {
??? ??? HANDLE hFileMap;
//
//?? Now we are writing the file using WriteFile API. This will emit the IRP_MJ_WRITE callback.
//?? My driver will perform file copy to COWSTORAGE folder on PreWrite. Then roll own IRP
//?? and issue the IRP_MJ_WRITE to new file on COWSTORAGE instead.
//
??? ??? printf(“Writing file: 1234\n”);
??? ??? WriteFile( hFile, “1234\0”, 5, &dwWritten, NULL );
??? ??? SetFilePointer( hFile, 0, 0, FILE_BEGIN );
//
//?? HERE the interesting part. All the IRP operation now will be redirected to the new file on COWSTORAGE but
//?? the CreateFileMapping API isn’t. It will use the file on current folder instead.
//
??? ??? printf(“Creating File Map\n”);
??? ??? hFileMap = CreateFileMapping( hFile, NULL, PAGE_READWRITE, 0, 5, NULL );
??? ??? if (INVALID_HANDLE_VALUE != hFileMap)
??? ??? {
??? ??? ??? printf(“Mapping file view\n”);
??? ??? ??? char* pMem = (char*) MapViewOfFile( hFileMap,
??? ??? ??? ??? FILE_MAP_WRITE,
??? ??? ??? ??? 0,
??? ??? ??? ??? 0,
??? ??? ??? ??? 5 );
??? ??? ??? if (pMem)
??? ??? ??? {
??? ??? ??? ??? char ReadBuff[32];
??? ??? ??? ??? printf(“Change memory: [0] = ‘x’\n”);
??? ??? ??? ??? __try {
??? ??? ??? ??? ??? pMem[0] = ‘x’;
??? ??? ??? ??? } __except (EXCEPTION_EXECUTE_HANDLER) {
??? ??? ??? ??? ??? printf(“Exception occurred!\n”);
??? ??? ??? ??? }
??? ??? ??? ??? printf(“Reading file: Expected = x234\n”);
??? ??? ??? ??? _getch();
??? ??? ??? ??? ReadFile( hFile, ReadBuff, 5, &dwWritten, NULL );
??? ??? ??? ??? printf(" Map : %s\n", pMem);??? ??? // Show: xaaa (Incorrect)
??? ??? ??? ??? printf(" Read: %s\n", ReadBuff);??? // Show: x234 (This is desired result for both Map and Read)
??? ??? ??? ??? //
??? ??? ??? ??? //
??? ??? ??? ??? //
??? ??? ??? ??? printf(“Setting file pointer to 0\n”);
??? ??? ??? ??? SetFilePointer( hFile, 0, 0, FILE_BEGIN );
//
//? Write the file again!
//
??? ??? ??? ??? printf(“Writing ‘4321’ to file\n”);
??? ??? ??? ??? WriteFile( hFile, “4321\0”, 5, &dwWritten, NULL );
??? ??? ??? ??? SetFilePointer( hFile, 0, 0, FILE_BEGIN );
??? ??? ??? ??? printf(“Reading file: Expected = 4321\n”);
??? ??? ??? ??? ReadFile( hFile, ReadBuff, 5, &dwWritten, NULL );
??? ??? ??? ??? printf(" Map : %s\n", pMem); // Still shows “xaaa”. Expected result is 4321.
??? ??? ??? ??? printf(" Read: %s\n", ReadBuff); // Shows 4321
??? ??? ??? ??? //
??? ??? ??? ??? //
??? ??? ??? ??? //
??? ??? ??? ??? printf(“Unmap view of file\n”);
??? ??? ??? ??? _getch();
??? ??? ??? ??? UnmapViewOfFile( pMem );
??? ??? ??? }
??? ??? ??? printf(“Closing hFileMap\n”);
??? ??? ??? _getch();
??? ??? ??? CloseHandle( hFileMap );
??? ??? }
??? ??? printf(“Closing hFile\n”);
??? ??? _getch();
??? ??? CloseHandle( hFile );
??? }
After running and stop the filter driver, I observed that the file contents on real path has changed to “xaaa”. This shouldn’t happens as it will defeat the purpose of this driver, the original file should remains untouched. On COWSTORAGE, the file content contains 4321.
I only got IRP_MJ_READ/WRITE or FASTIO_READ/WRITE for ReadFile/WriteFile operation. For CreateFileMapping, I only got IRP_MJ_XXX_FOR_SECTION_SYNCHRONIZATION but not IRP_MJ_READ since I believe the file already on system cache.
This question might already asked by someone before, sorry if I was repeating the question again.
How do I handle file mapping correctly?
How do I cancel the file mapping write operation?? It seems I can’t find equivalent API for this. I don’t want the file outside COWSTORAGE (the original file) got changed.
On PreWrite, I even do this… and then execute the test program above, uninstall the driver and rebooted the system, then opened the original file. The original contents has been changed to “xaaa”.
??? status = CtxFindOrCreateStreamContext( Data, FALSE, &StreamCtx, NULL );
??? if (!NT_SUCCESS( status ) || StreamCtx == NULL) {
??? ??? goto PreWriteCleanup;
??? }
??? CtxAcquireResourceShared( StreamCtx->Resource );
???
??? DataChanged = StreamCtx->Changes.DataChanged;
??? CtxReleaseResource( StreamCtx->Resource );
??? {
//
//? THIS
//
??? ??? Data->IoStatus.Status = STATUS_SUCCESS;
??? ??? Data->IoStatus.Information = Param->Write.Length;
??? ??? cbStatus = FLT_PREOP_COMPLETE;
??? ??? goto PreWriteCleanup;
??? }
I hope someone can shine some light on this one for me. Thanks.
Syahmi.