From 0e207a07961d950877c55a483cee2b7ff42fb459 Mon Sep 17 00:00:00 2001 From: Liryna Date: Thu, 23 Dec 2021 20:10:02 -0500 Subject: [PATCH] Kernel / Library - Introduce thread & Memory poll to process and pull events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is highly based on #307 without the async logic. The reason is to avoid using the kernel NotificationLoop that was dispatching requests to workers (userland thread) sequentially. It has a high cost to do a thread switch each time to wake up the workers. The current logic is nice when you want workers to be async but as we have threads (and now a thread poll) dedicated to pull and process events, we can make them synchronously wait for new events and take them from the request list themself. This commit introduces the logic of a single main thread that polls events by batch and dispatches them to other threads and keeps the last one for itself to process and send the response to the kernel. Each thread waken will do the same and pull new events at the same time. If none is returned, the thread goes back to sleep and otherwise does the same as the main thread (dispatch and process). The difference is that the main thread waits indefinitely for new events while others wait 100ms in the kernel before returning back to userland.This and the memory poll also added, offer a great flexibility of resources especially on heavy load. This CL is huge but the perf that comes with it are about 10-35% on sequential requests but in the real world with the thread poll the perf are way above. @Corillian full rewrite of FindFiles improved the perf between 100-250%... --- dokan/cleanup.c | 35 +- dokan/close.c | 38 +- dokan/create.c | 182 ++-- dokan/directory.c | 496 +++++----- dokan/dokan.c | 1128 +++++++++++++--------- dokan/dokan.def | 2 + dokan/dokan.h | 160 ++- dokan/dokan.vcxproj | 4 + dokan/dokan_pool.c | 424 ++++++++ dokan/dokan_pool.h | 54 ++ dokan/dokan_vector.c | 271 ++++++ dokan/dokan_vector.h | 79 ++ dokan/dokani.h | 182 ++-- dokan/fileinfo.c | 376 ++++---- dokan/flush.c | 37 +- dokan/lock.c | 60 +- dokan/mount.c | 23 +- dokan/read.c | 51 +- dokan/security.c | 111 +-- dokan/setfile.c | 108 ++- dokan/timeout.c | 53 +- dokan/volume.c | 67 +- dokan/write.c | 166 ++-- dokan_fuse/include/dokanfuse.h | 4 + dokan_fuse/src/dokanfuse.cpp | 11 +- samples/dokan_memfs/main.cpp | 2 + samples/dokan_memfs/memfs.cpp | 4 +- samples/dokan_memfs/memfs_operations.cpp | 14 +- samples/dokan_mirror/mirror.c | 37 +- sys/close.c | 2 +- sys/device.c | 18 - sys/dokan.h | 16 +- sys/event.c | 69 +- sys/fileinfo.c | 43 +- sys/fscontrol.c | 101 +- sys/init.c | 4 +- sys/notification.c | 72 +- sys/public.h | 33 +- sys/util/log.c | 18 +- sys/util/log.h | 17 +- 40 files changed, 2803 insertions(+), 1769 deletions(-) create mode 100644 dokan/dokan_pool.c create mode 100644 dokan/dokan_pool.h create mode 100644 dokan/dokan_vector.c create mode 100644 dokan/dokan_vector.h diff --git a/dokan/cleanup.c b/dokan/cleanup.c index adfe1b3ce..4b95305b2 100644 --- a/dokan/cleanup.c +++ b/dokan/cleanup.c @@ -22,32 +22,25 @@ with this program. If not, see . #include "dokani.h" -VOID DispatchCleanup(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance) { - PEVENT_INFORMATION eventInfo; - DOKAN_FILE_INFO fileInfo; - PDOKAN_OPEN_INFO openInfo; - ULONG sizeOfEventInfo = DispatchGetEventInformationLength(0); +VOID DispatchCleanup(PDOKAN_IO_EVENT IoEvent) { + CheckFileName(IoEvent->EventContext->Operation.Cleanup.FileName); - CheckFileName(EventContext->Operation.Cleanup.FileName); + CreateDispatchCommon(IoEvent, 0); - eventInfo = DispatchCommon(EventContext, sizeOfEventInfo, DokanInstance, - &fileInfo, &openInfo); + IoEvent->EventResult->Status = STATUS_SUCCESS; // return success at any case - eventInfo->Status = STATUS_SUCCESS; // return success at any case + DbgPrint("###Cleanup file handle = 0x%p, eventID = %04d, event Info = 0x%p\n", + IoEvent->DokanOpenInfo, + IoEvent->DokanOpenInfo != NULL ? IoEvent->DokanOpenInfo->EventId + : -1, + IoEvent); - DbgPrint("###Cleanup %04d\n", openInfo != NULL ? openInfo->EventId : -1); - - if (DokanInstance->DokanOperations->Cleanup) { + if (IoEvent->DokanInstance->DokanOperations->Cleanup) { // ignore return value - DokanInstance->DokanOperations->Cleanup( - EventContext->Operation.Cleanup.FileName, &fileInfo); + IoEvent->DokanInstance->DokanOperations->Cleanup( + IoEvent->EventContext->Operation.Cleanup.FileName, + &IoEvent->DokanFileInfo); } - if (openInfo != NULL) - openInfo->UserContext = fileInfo.Context; - - SendEventInformation(Handle, eventInfo, sizeOfEventInfo); - ReleaseDokanOpenInfo(eventInfo, &fileInfo, DokanInstance); - free(eventInfo); + EventCompletion(IoEvent); } diff --git a/dokan/close.c b/dokan/close.c index 9b6a2484c..51f0c8dd2 100644 --- a/dokan/close.c +++ b/dokan/close.c @@ -21,35 +21,27 @@ with this program. If not, see . */ #include "dokani.h" +#include "dokan_pool.h" -VOID DispatchClose(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance) { - PEVENT_INFORMATION eventInfo; - DOKAN_FILE_INFO fileInfo; - PDOKAN_OPEN_INFO openInfo; - ULONG sizeOfEventInfo = DispatchGetEventInformationLength(0); +VOID DispatchClose(PDOKAN_IO_EVENT IoEvent) { + CheckFileName(IoEvent->EventContext->Operation.Close.FileName); - UNREFERENCED_PARAMETER(Handle); + DbgPrint("###Close file handle = 0x%p, eventID = %04d, event Info = 0x%p\n", + IoEvent->DokanOpenInfo, + IoEvent->DokanOpenInfo != NULL ? IoEvent->DokanOpenInfo->EventId + : -1, + IoEvent); - CheckFileName(EventContext->Operation.Close.FileName); - - eventInfo = DispatchCommon(EventContext, sizeOfEventInfo, DokanInstance, - &fileInfo, &openInfo); - - eventInfo->Status = STATUS_SUCCESS; // return success at any case - - DbgPrint("###Close %04d\n", openInfo != NULL ? openInfo->EventId : -1); - // Driver has simply notifying us of the Close request which he has // already completed at this stage. Driver is not expecting us // to reply from this so there is no need to send an EVENT_INFORMATION. - if (openInfo != NULL) { - EnterCriticalSection(&DokanInstance->CriticalSection); - openInfo->FileName = _wcsdup(EventContext->Operation.Close.FileName); - openInfo->OpenCount--; - LeaveCriticalSection(&DokanInstance->CriticalSection); + if (IoEvent->DokanOpenInfo) { + EnterCriticalSection(&IoEvent->DokanOpenInfo->CriticalSection); + IoEvent->DokanOpenInfo->FileName = + _wcsdup(IoEvent->EventContext->Operation.Close.FileName); + IoEvent->DokanOpenInfo->OpenCount--; + LeaveCriticalSection(&IoEvent->DokanOpenInfo->CriticalSection); + ReleaseDokanOpenInfo(IoEvent); } - ReleaseDokanOpenInfo(eventInfo, &fileInfo, DokanInstance); - free(eventInfo); } diff --git a/dokan/create.c b/dokan/create.c index e99746636..7dcf317dc 100644 --- a/dokan/create.c +++ b/dokan/create.c @@ -20,6 +20,9 @@ with this program. If not, see . */ #include "dokani.h" +#include "dokan_pool.h" + +#include VOID SetIOSecurityContext(PEVENT_CONTEXT EventContext, PDOKAN_IO_SECURITY_CONTEXT ioSecurityContext) { @@ -100,17 +103,11 @@ BOOL CreateSuccesStatusCheck(NTSTATUS status, ULONG disposition) { return FALSE; } -VOID DispatchCreate(HANDLE Handle, // This handle is not for a file. It is for - // Dokan Device Driver(which is doing - // EVENT_WAIT). - PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance) { - static int eventId = 0; - EVENT_INFORMATION eventInfo; +VOID DispatchCreate(PDOKAN_IO_EVENT IoEvent) { + static volatile LONG globalEventId = 0; + ULONG currentEventId = InterlockedIncrement(&globalEventId); NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES; - DOKAN_FILE_INFO fileInfo; ULONG disposition; - PDOKAN_OPEN_INFO openInfo = NULL; DWORD options; DOKAN_IO_SECURITY_CONTEXT ioSecurityContext; WCHAR *fileName; @@ -118,45 +115,33 @@ VOID DispatchCreate(HANDLE Handle, // This handle is not for a file. It is for WCHAR *origFileName = NULL; DWORD origOptions; - fileName = (WCHAR *)((char *)&EventContext->Operation.Create + - EventContext->Operation.Create.FileNameOffset); + fileName = (WCHAR *)((PCHAR)&IoEvent->EventContext->Operation.Create + + IoEvent->EventContext->Operation.Create.FileNameOffset); CheckFileName(fileName); - RtlZeroMemory(&eventInfo, sizeof(EVENT_INFORMATION)); - RtlZeroMemory(&fileInfo, sizeof(DOKAN_FILE_INFO)); - - eventInfo.BufferLength = 0; - eventInfo.SerialNumber = EventContext->SerialNumber; + CreateDispatchCommon(IoEvent, 0); - fileInfo.ProcessId = EventContext->ProcessId; - fileInfo.DokanOptions = DokanInstance->DokanOptions; + assert(IoEvent->DokanOpenInfo == NULL); - // DOKAN_OPEN_INFO is structure for a opened file - // this will be freed by Close - openInfo = malloc(sizeof(DOKAN_OPEN_INFO)); - if (openInfo == NULL) { - eventInfo.Status = STATUS_INSUFFICIENT_RESOURCES; - SendEventInformation(Handle, &eventInfo, sizeof(EVENT_INFORMATION)); - return; - } - ZeroMemory(openInfo, sizeof(DOKAN_OPEN_INFO)); - openInfo->OpenCount = 1; - openInfo->EventContext = EventContext; - openInfo->DokanInstance = DokanInstance; - fileInfo.DokanContext = (ULONG64)openInfo; + IoEvent->DokanOpenInfo = PopFileOpenInfo(); + IoEvent->DokanOpenInfo->OpenCount = 1; + IoEvent->DokanOpenInfo->EventContext = IoEvent->EventContext; + IoEvent->DokanOpenInfo->DokanInstance = IoEvent->DokanInstance; + IoEvent->DokanOpenInfo->EventId = currentEventId; + // CreateFile is the only event that has DokanOpenInfo as DokanContext. + // It allows DokanOpenRequestorToken to retrieve the open information. + IoEvent->DokanFileInfo.DokanContext = (ULONG64)IoEvent->DokanOpenInfo; - // pass it to driver and when the same handle is used get it back - eventInfo.Context = (ULONG64)openInfo; + // Pass it to the driver so we can retrieve it on the next call of the same context. + IoEvent->EventResult->Context = (ULONG64)IoEvent->DokanOpenInfo; // The high 8 bits of this parameter correspond to the Disposition parameter - disposition = - (EventContext->Operation.Create.CreateOptions >> 24) & 0x000000ff; - + disposition = (IoEvent->EventContext->Operation.Create.CreateOptions >> 24) & + 0x000000ff; // The low 24 bits of this member correspond to the CreateOptions parameter - options = - EventContext->Operation.Create.CreateOptions & FILE_VALID_OPTION_FLAGS; - // DbgPrint("Create.CreateOptions 0x%x\n", options); + options = IoEvent->EventContext->Operation.Create.CreateOptions & + FILE_VALID_OPTION_FLAGS; origOptions = options; @@ -165,8 +150,8 @@ VOID DispatchCreate(HANDLE Handle, // This handle is not for a file. It is for // there is a case to open a directory if (options & FILE_DIRECTORY_FILE) { // DbgPrint("FILE_DIRECTORY_FILE\n"); - fileInfo.IsDirectory = TRUE; - } else if (EventContext->Flags & SL_OPEN_TARGET_DIRECTORY) { + IoEvent->DokanFileInfo.IsDirectory = TRUE; + } else if (IoEvent->EventContext->Flags & SL_OPEN_TARGET_DIRECTORY) { // NOTE: SL_OPEN_TARGET_DIRECTORY means open the parent directory of the // specified file // We pull out the parent directory name and then switch the flags to make @@ -199,81 +184,73 @@ VOID DispatchCreate(HANDLE Handle, // This handle is not for a file. It is for } } - DbgPrint("###Create %04d\n", eventId); - - openInfo->EventId = eventId++; + DbgPrint("###Create file handle = 0x%p, eventID = %04d, event Info = 0x%p\n", + IoEvent->DokanOpenInfo, currentEventId, IoEvent); - if (DokanInstance->DokanOperations->ZwCreateFile) { + if (IoEvent->DokanInstance->DokanOperations->ZwCreateFile) { - SetIOSecurityContext(EventContext, &ioSecurityContext); + SetIOSecurityContext(IoEvent->EventContext, &ioSecurityContext); - if ((EventContext->Flags & SL_OPEN_TARGET_DIRECTORY) && - DokanInstance->DokanOperations->Cleanup && - DokanInstance->DokanOperations->CloseFile) { + if ((IoEvent->EventContext->Flags & SL_OPEN_TARGET_DIRECTORY) && + IoEvent->DokanInstance->DokanOperations->Cleanup && + IoEvent->DokanInstance->DokanOperations->CloseFile) { if (options & FILE_NON_DIRECTORY_FILE && options & FILE_DIRECTORY_FILE) status = STATUS_INVALID_PARAMETER; else - status = DokanInstance->DokanOperations->ZwCreateFile( + status = IoEvent->DokanInstance->DokanOperations->ZwCreateFile( origFileName, &ioSecurityContext, ioSecurityContext.DesiredAccess, - EventContext->Operation.Create.FileAttributes, - EventContext->Operation.Create.ShareAccess, disposition, - origOptions, &fileInfo); + IoEvent->EventContext->Operation.Create.FileAttributes, + IoEvent->EventContext->Operation.Create.ShareAccess, disposition, + origOptions, &IoEvent->DokanFileInfo); if (CreateSuccesStatusCheck(status, disposition)) { - DokanInstance->DokanOperations->Cleanup(origFileName, &fileInfo); - DokanInstance->DokanOperations->CloseFile(origFileName, &fileInfo); + IoEvent->DokanInstance->DokanOperations->Cleanup(origFileName, &IoEvent->DokanFileInfo); + IoEvent->DokanInstance->DokanOperations->CloseFile( + origFileName, &IoEvent->DokanFileInfo); } else if (status == STATUS_OBJECT_NAME_NOT_FOUND) { DbgPrint("SL_OPEN_TARGET_DIRECTORY file not found\n"); childExisted = FALSE; } - fileInfo.IsDirectory = TRUE; + IoEvent->DokanFileInfo.IsDirectory = TRUE; } if (options & FILE_NON_DIRECTORY_FILE && options & FILE_DIRECTORY_FILE) status = STATUS_INVALID_PARAMETER; else - status = DokanInstance->DokanOperations->ZwCreateFile( + status = IoEvent->DokanInstance->DokanOperations->ZwCreateFile( fileName, &ioSecurityContext, ioSecurityContext.DesiredAccess, - EventContext->Operation.Create.FileAttributes, - EventContext->Operation.Create.ShareAccess, disposition, options, - &fileInfo); + IoEvent->EventContext->Operation.Create.FileAttributes, + IoEvent->EventContext->Operation.Create.ShareAccess, disposition, + options, &IoEvent->DokanFileInfo); if (CreateSuccesStatusCheck(status, disposition) && !childExisted) { - eventInfo.Operation.Create.Information = FILE_DOES_NOT_EXIST; + IoEvent->EventResult->Operation.Create.Information = FILE_DOES_NOT_EXIST; } } else { status = STATUS_NOT_IMPLEMENTED; } // save the information about this access in DOKAN_OPEN_INFO - openInfo->IsDirectory = fileInfo.IsDirectory; - openInfo->UserContext = fileInfo.Context; - - // FILE_CREATED - // FILE_DOES_NOT_EXIST - // FILE_EXISTS - // FILE_OPENED - // FILE_OVERWRITTEN - // FILE_SUPERSEDED + IoEvent->DokanOpenInfo->IsDirectory = IoEvent->DokanFileInfo.IsDirectory; + IoEvent->DokanOpenInfo->UserContext = IoEvent->DokanFileInfo.Context; - DbgPrint("CreateFile status = %lx\n", status); if (!CreateSuccesStatusCheck(status, disposition)) { - if (EventContext->Flags & SL_OPEN_TARGET_DIRECTORY) { - DbgPrint("SL_OPEN_TARGET_DIRECTORY spcefied\n"); + if (IoEvent->EventContext->Flags & SL_OPEN_TARGET_DIRECTORY) { + DbgPrint("SL_OPEN_TARGET_DIRECTORY specified\n"); } - eventInfo.Operation.Create.Information = FILE_DOES_NOT_EXIST; - eventInfo.Status = status; + IoEvent->EventResult->Operation.Create.Information = FILE_DOES_NOT_EXIST; + IoEvent->EventResult->Status = status; if (status == STATUS_OBJECT_NAME_COLLISION) { - eventInfo.Operation.Create.Information = FILE_EXISTS; + IoEvent->EventResult->Operation.Create.Information = FILE_EXISTS; } if (STATUS_ACCESS_DENIED == status && - DokanInstance->DokanOperations->ZwCreateFile && - (EventContext->Operation.Create.SecurityContext.DesiredAccess & + IoEvent->DokanInstance->DokanOperations->ZwCreateFile && + (IoEvent->EventContext->Operation.Create.SecurityContext.DesiredAccess & DELETE)) { DbgPrint("Delete failed, ask parent folder if we have the right\n"); // strip the last section of the file path @@ -286,7 +263,7 @@ VOID DispatchCreate(HANDLE Handle, // This handle is not for a file. It is for *lastP = 0; } - SetIOSecurityContext(EventContext, &ioSecurityContext); + SetIOSecurityContext(IoEvent->EventContext, &ioSecurityContext); ACCESS_MASK newDesiredAccess = (MAXIMUM_ALLOWED & ioSecurityContext.DesiredAccess) ? (FILE_DELETE_CHILD | FILE_LIST_DIRECTORY) @@ -299,55 +276,64 @@ VOID DispatchCreate(HANDLE Handle, // This handle is not for a file. It is for options |= FILE_OPEN_FOR_BACKUP_INTENT; //Enable open directory options &= ~FILE_NON_DIRECTORY_FILE; //Remove non dir flag - status = DokanInstance->DokanOperations->ZwCreateFile( + status = IoEvent->DokanInstance->DokanOperations->ZwCreateFile( fileName, &ioSecurityContext, newDesiredAccess, - EventContext->Operation.Create.FileAttributes, - EventContext->Operation.Create.ShareAccess, disposition, options, - &fileInfo); + IoEvent->EventContext->Operation.Create.FileAttributes, + IoEvent->EventContext->Operation.Create.ShareAccess, disposition, + options, &IoEvent->DokanFileInfo); if (status == STATUS_SUCCESS) { DbgPrint("Parent give us the right to delete\n"); - eventInfo.Status = STATUS_SUCCESS; - eventInfo.Operation.Create.Information = FILE_OPENED; + IoEvent->EventResult->Status = STATUS_SUCCESS; + IoEvent->EventResult->Operation.Create.Information = FILE_OPENED; } else { DbgPrint("Parent CreateFile failed status = %lx\n", status); + PushFileOpenInfo(IoEvent->DokanOpenInfo); + IoEvent->DokanOpenInfo = NULL; } + } else { + PushFileOpenInfo(IoEvent->DokanOpenInfo); + IoEvent->DokanOpenInfo = NULL; } } else { - eventInfo.Status = STATUS_SUCCESS; - eventInfo.Operation.Create.Information = FILE_OPENED; + IoEvent->EventResult->Status = STATUS_SUCCESS; + IoEvent->EventResult->Operation.Create.Information = FILE_OPENED; if (disposition == FILE_CREATE || disposition == FILE_OPEN_IF || disposition == FILE_OVERWRITE_IF || disposition == FILE_SUPERSEDE) { - eventInfo.Operation.Create.Information = FILE_CREATED; + IoEvent->EventResult->Operation.Create.Information = FILE_CREATED; if (status == STATUS_OBJECT_NAME_COLLISION) { if (disposition == FILE_OPEN_IF) { - eventInfo.Operation.Create.Information = FILE_OPENED; + IoEvent->EventResult->Operation.Create.Information = FILE_OPENED; } else if (disposition == FILE_OVERWRITE_IF) { - eventInfo.Operation.Create.Information = FILE_OVERWRITTEN; + IoEvent->EventResult->Operation.Create.Information = + FILE_OVERWRITTEN; } else if (disposition == FILE_SUPERSEDE) { - eventInfo.Operation.Create.Information = FILE_SUPERSEDED; + IoEvent->EventResult->Operation.Create.Information = FILE_SUPERSEDED; } } } if (disposition == FILE_OVERWRITE) - eventInfo.Operation.Create.Information = FILE_OVERWRITTEN; + IoEvent->EventResult->Operation.Create.Information = FILE_OVERWRITTEN; - if (fileInfo.IsDirectory) - eventInfo.Operation.Create.Flags |= DOKAN_FILE_DIRECTORY; + if (IoEvent->DokanFileInfo.IsDirectory) + IoEvent->EventResult->Operation.Create.Flags |= DOKAN_FILE_DIRECTORY; } if (origFileName) free(origFileName); - if (!NT_SUCCESS(eventInfo.Status)) { - free((PDOKAN_OPEN_INFO)(UINT_PTR)eventInfo.Context); - eventInfo.Context = 0; + if (!NT_SUCCESS(IoEvent->EventResult->Status)) { + IoEvent->EventResult->Context = 0; } - SendEventInformation(Handle, &eventInfo, sizeof(EVENT_INFORMATION)); + DbgPrint("Dokan Information: DokanEndDispatchCreate() status = %lx, file " + "handle = 0x%p, eventID = %04d, result = 0x%x\n", + IoEvent->EventResult->Status, IoEvent->DokanOpenInfo, + IoEvent->DokanOpenInfo ? IoEvent->DokanOpenInfo->EventId : -1, + IoEvent->EventResult->Operation.Create.Information); } diff --git a/dokan/directory.c b/dokan/directory.c index a9963101a..72d48a047 100644 --- a/dokan/directory.c +++ b/dokan/directory.c @@ -23,6 +23,9 @@ with this program. If not, see . #include "dokani.h" #include "fileinfo.h" #include "list.h" +#include "dokan_pool.h" + +#include /** * \struct DOKAN_FIND_DATA @@ -368,113 +371,72 @@ DokanFillDirectoryInformation(FILE_INFORMATION_CLASS DirectoryInfo, return thisEntrySize; } -int DokanFillFileDataEx(PWIN32_FIND_DATAW FindData, PDOKAN_FILE_INFO FileInfo, - BOOLEAN InsertTail) { - PLIST_ENTRY listHead = - ((PDOKAN_OPEN_INFO)(UINT_PTR)FileInfo->DokanContext)->DirListHead; - PDOKAN_FIND_DATA findData; - - findData = (PDOKAN_FIND_DATA)malloc(sizeof(DOKAN_FIND_DATA)); - if (findData == NULL) { - return 0; - } - ZeroMemory(findData, sizeof(DOKAN_FIND_DATA)); - InitializeListHead(&findData->ListEntry); - - findData->FindData = *FindData; - - if (InsertTail) - InsertTailList(listHead, &findData->ListEntry); - else - InsertHeadList(listHead, &findData->ListEntry); - return 0; -} - int WINAPI DokanFillFileData(PWIN32_FIND_DATAW FindData, PDOKAN_FILE_INFO FileInfo) { - return DokanFillFileDataEx(FindData, FileInfo, TRUE); -} - -VOID ClearFindData(PLIST_ENTRY ListHead) { - // free all list entries - while (!IsListEmpty(ListHead)) { - PLIST_ENTRY entry = RemoveHeadList(ListHead); - PDOKAN_FIND_DATA find = - CONTAINING_RECORD(entry, DOKAN_FIND_DATA, ListEntry); - free(find); - } + assert(FileInfo->ProcessingContext); + PDOKAN_VECTOR dirList = (PDOKAN_VECTOR )FileInfo->ProcessingContext; + DokanVector_PushBack(dirList, FindData); + return 0; } -// add entry which matches the pattern specified in EventContext -// to the buffer specified in EventInfo +// add entry which matches the pattern specifed in EventContext +// to the buffer specifed in EventInfo // -LONG MatchFiles(PEVENT_CONTEXT EventContext, PEVENT_INFORMATION EventInfo, - PLIST_ENTRY FindDataList, BOOLEAN PatternCheck, - PDOKAN_INSTANCE DokanInstance) { - PLIST_ENTRY thisEntry, listHead, nextEntry; - - ULONG lengthRemaining = EventInfo->BufferLength; - PVOID currentBuffer = EventInfo->Buffer; +LONG MatchFiles(PDOKAN_IO_EVENT IoEvent, PDOKAN_VECTOR DirList) { + ULONG lengthRemaining = + IoEvent->EventContext->Operation.Directory.BufferLength; + PVOID currentBuffer = IoEvent->EventResult->Buffer; PVOID lastBuffer = currentBuffer; ULONG index = 0; - BOOL caseSensitive = FALSE; + BOOL patternCheck = FALSE; PWCHAR pattern = NULL; + BOOL bufferOverFlow = FALSE; - // search patten is specified - if (PatternCheck && - EventContext->Operation.Directory.SearchPatternLength != 0) { - pattern = (PWCHAR)( - (SIZE_T)&EventContext->Operation.Directory.SearchPatternBase[0] + - (SIZE_T)EventContext->Operation.Directory.SearchPatternOffset); + if (IoEvent->EventContext->Operation.Directory.SearchPatternLength > 0) { + pattern = (PWCHAR)((SIZE_T)&IoEvent->EventContext->Operation.Directory + .SearchPatternBase[0] + + (SIZE_T)IoEvent->EventContext->Operation.Directory + .SearchPatternOffset); } - caseSensitive = - DokanInstance->DokanOptions->Options & DOKAN_OPTION_CASE_SENSITIVE; - - listHead = FindDataList; - - for (thisEntry = listHead->Flink; thisEntry != listHead; - thisEntry = nextEntry) { - - PDOKAN_FIND_DATA find; - nextEntry = thisEntry->Flink; - - find = CONTAINING_RECORD(thisEntry, DOKAN_FIND_DATA, ListEntry); + if (pattern && wcscmp(pattern, L"*") != 0 && + !IoEvent->DokanInstance->DokanOperations->FindFilesWithPattern) { + patternCheck = TRUE; + } + for (size_t i = 0; i < DokanVector_GetCount(DirList); ++i) { + PDOKAN_FIND_DATA find = (PDOKAN_FIND_DATA)DokanVector_GetItem(DirList, i); DbgPrintW(L"FileMatch? : %s (%s,%d,%d)\n", find->FindData.cFileName, (pattern ? pattern : L"null"), - EventContext->Operation.Directory.FileIndex, index); + IoEvent->EventContext->Operation.Directory.FileIndex, index); // pattern is not specified or pattern match is ignore cases - if (!pattern || DokanIsNameInExpression(pattern, find->FindData.cFileName, - !caseSensitive)) { - - if (EventContext->Operation.Directory.FileIndex <= index) { + if (!patternCheck || + DokanIsNameInExpression(pattern, find->FindData.cFileName, TRUE)) { + if (IoEvent->EventContext->Operation.Directory.FileIndex <= index) { // index+1 is very important, should use next entry index ULONG entrySize = DokanFillDirectoryInformation( - EventContext->Operation.Directory.FileInformationClass, + IoEvent->EventContext->Operation.Directory.FileInformationClass, currentBuffer, &lengthRemaining, &find->FindData, index + 1, - DokanInstance); + IoEvent->DokanInstance); // buffer is full - if (entrySize == 0) + if (entrySize == 0) { + bufferOverFlow = TRUE; break; - + } // pointer of the current last entry lastBuffer = currentBuffer; - // end if needs to return single entry - if (EventContext->Flags & SL_RETURN_SINGLE_ENTRY) { + if (IoEvent->EventContext->Flags & SL_RETURN_SINGLE_ENTRY) { + DbgPrint(" =>return single entry\n"); index++; break; } - DbgPrint(" =>return\n"); - // the offset of next entry ((PFILE_BOTH_DIR_INFORMATION)currentBuffer)->NextEntryOffset = entrySize; - // next buffer position currentBuffer = (PCHAR)currentBuffer + entrySize; } @@ -484,93 +446,174 @@ LONG MatchFiles(PEVENT_CONTEXT EventContext, PEVENT_INFORMATION EventInfo, // Since next of the last entry doesn't exist, clear next offset ((PFILE_BOTH_DIR_INFORMATION)lastBuffer)->NextEntryOffset = 0; - // acctualy used length of buffer - EventInfo->BufferLength = - EventContext->Operation.Directory.BufferLength - lengthRemaining; - - if (index <= EventContext->Operation.Directory.FileIndex) { - - if (thisEntry != listHead) + IoEvent->EventResult->BufferLength = + IoEvent->EventContext->Operation.Directory.BufferLength - + lengthRemaining; + if (index <= IoEvent->EventContext->Operation.Directory.FileIndex) { + if (bufferOverFlow) return -2; // BUFFER_OVERFLOW - - return -1; // NO_MORE_FILES + return -1; // NO_MORE_FILES } - return index; } -VOID AddMissingCurrentAndParentFolder(PEVENT_CONTEXT EventContext, - PLIST_ENTRY FindDataList, - PDOKAN_FILE_INFO fileInfo) { - PLIST_ENTRY thisEntry, listHead, nextEntry; +VOID AddMissingCurrentAndParentFolder(PDOKAN_IO_EVENT IoEvent) { PWCHAR pattern = NULL; BOOLEAN currentFolder = FALSE, parentFolder = FALSE; WIN32_FIND_DATAW findData; FILETIME systime; - - if (EventContext->Operation.Directory.SearchPatternLength != 0) { - pattern = (PWCHAR)( - (SIZE_T)&EventContext->Operation.Directory.SearchPatternBase[0] + - (SIZE_T)EventContext->Operation.Directory.SearchPatternOffset); + PDOKAN_VECTOR dirList = (PDOKAN_VECTOR)IoEvent->DokanFileInfo.ProcessingContext; + + assert(dirList); + if (IoEvent->EventContext->Operation.Directory.SearchPatternLength != 0) { + pattern = (PWCHAR)((SIZE_T)&IoEvent->EventContext->Operation.Directory + .SearchPatternBase[0] + + (SIZE_T)IoEvent->EventContext->Operation.Directory + .SearchPatternOffset); } - if (wcscmp(EventContext->Operation.Directory.DirectoryName, L"\\") == 0 || - (pattern != NULL && wcscmp(pattern, L"*") != 0)) + if (wcscmp(IoEvent->EventContext->Operation.Directory.DirectoryName, + L"\\") == 0 || + (pattern != NULL && wcscmp(pattern, L"*") != 0)) { return; + } - listHead = FindDataList; - for (thisEntry = listHead->Flink; thisEntry != listHead; - thisEntry = nextEntry) { + for (size_t i = 0; + (!currentFolder || !parentFolder) && i < DokanVector_GetCount(dirList); + ++i) { + PDOKAN_FIND_DATA find = (PDOKAN_FIND_DATA)DokanVector_GetItem(dirList, i); + if (wcscmp(find->FindData.cFileName, L".") == 0) { + currentFolder = TRUE; + } - PDOKAN_FIND_DATA find; - nextEntry = thisEntry->Flink; + if (wcscmp(find->FindData.cFileName, L"..") == 0) { + parentFolder = TRUE; + } + } - find = CONTAINING_RECORD(thisEntry, DOKAN_FIND_DATA, ListEntry); + if (!currentFolder || !parentFolder) { + GetSystemTimeAsFileTime(&systime); + ZeroMemory(&findData, sizeof(WIN32_FIND_DATAW)); + findData.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY; + findData.ftCreationTime = systime; + findData.ftLastAccessTime = systime; + findData.ftLastWriteTime = systime; + // Folders times should be the real current and parent folder times... + // TODO: Should it be PushFront ? + if (!parentFolder) { + findData.cFileName[0] = '.'; + findData.cFileName[1] = '.'; + // NULL written during ZeroMemory() + DokanVector_PushBack(dirList, &findData); + } + if (!currentFolder) { + findData.cFileName[0] = '.'; + findData.cFileName[1] = '\0'; + DokanVector_PushBack(dirList, &findData); + } + } +} - if (wcscmp(find->FindData.cFileName, L".") == 0) - currentFolder = TRUE; - if (wcscmp(find->FindData.cFileName, L"..") == 0) - parentFolder = TRUE; - if (currentFolder == TRUE && parentFolder == TRUE) - return; // folders are already there +NTSTATUS WriteDirectoryResults(PDOKAN_IO_EVENT EventInfo, + PDOKAN_VECTOR dirList) { + // If this function is called then so far everything should be good + assert(EventInfo->EventResult->Status == STATUS_SUCCESS); + // Write the file info to the output buffer + int index = MatchFiles(EventInfo, dirList); + DbgPrint("WriteDirectoryResults() New directory index is %d.\n", index); + // there is no matched file + if (index < 0) { + if (index == -1) { + if (EventInfo->EventContext->Operation.Directory.FileIndex == 0) { + DbgPrint(" STATUS_NO_SUCH_FILE\n"); + EventInfo->EventResult->Status = STATUS_NO_SUCH_FILE; + } else { + + DbgPrint(" STATUS_NO_MORE_FILES\n"); + EventInfo->EventResult->Status = STATUS_NO_MORE_FILES; + } + } else if (index == -2) { + DbgPrint(" STATUS_BUFFER_OVERFLOW\n"); + EventInfo->EventResult->Status = STATUS_BUFFER_OVERFLOW; + } + EventInfo->EventResult->Operation.Directory.Index = + EventInfo->EventContext->Operation.Directory.FileIndex; + } else { + DbgPrint("index to %d\n", index); + EventInfo->EventResult->Operation.Directory.Index = index; } + return EventInfo->EventResult->Status; +} - GetSystemTimeAsFileTime(&systime); - ZeroMemory(&findData, sizeof(WIN32_FIND_DATAW)); - findData.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY; - findData.ftCreationTime = systime; - findData.ftLastAccessTime = systime; - findData.ftLastWriteTime = systime; - // Folders times should be the real current and parent folder times... - if (!parentFolder) { - findData.cFileName[0] = '.'; - findData.cFileName[1] = '.'; - DokanFillFileDataEx(&findData, fileInfo, FALSE); +VOID EndFindFilesCommon(PDOKAN_IO_EVENT IoEvent, NTSTATUS Status) { + PDOKAN_VECTOR dirList = + (PDOKAN_VECTOR)IoEvent->DokanFileInfo.ProcessingContext; + PDOKAN_VECTOR oldDirList = NULL; + + assert(IoEvent->EventResult->BufferLength == 0); + assert(IoEvent->DokanFileInfo.ProcessingContext); + + // STATUS_PENDING should not be passed to this function + if (Status == STATUS_PENDING) { + DbgPrint("Dokan Error: EndFindFilesCommon() failed because STATUS_PENDING " + "was supplied for ResultStatus.\n"); + Status = STATUS_INTERNAL_ERROR; } - if (!currentFolder) { - findData.cFileName[0] = '.'; - findData.cFileName[1] = '\0'; - DokanFillFileDataEx(&findData, fileInfo, FALSE); + if (Status == STATUS_SUCCESS) { + AddMissingCurrentAndParentFolder(IoEvent); + Status = WriteDirectoryResults(IoEvent, dirList); + EnterCriticalSection(&IoEvent->DokanOpenInfo->CriticalSection); + { + if (IoEvent->DokanOpenInfo->DirList != dirList) { + oldDirList = IoEvent->DokanOpenInfo->DirList; + IoEvent->DokanOpenInfo->DirList = dirList; + } else { + // They should never point to the same object + DbgPrint("Dokan Warning: EndFindFilesCommon() " + "EventInfo->DokanOpenInfo->DirList == dirList\n"); + } + if (IoEvent->DokanOpenInfo->DirListSearchPattern) { + + free(IoEvent->DokanOpenInfo->DirListSearchPattern); + IoEvent->DokanOpenInfo->DirListSearchPattern = NULL; + } + if (IoEvent->EventContext->Operation.Directory + .SearchPatternLength > 0) { + IoEvent->DokanOpenInfo->DirListSearchPattern = + _wcsdup((PWCHAR)((SIZE_T)&IoEvent->EventContext->Operation.Directory + .SearchPatternBase[0] + + (SIZE_T)IoEvent->EventContext->Operation.Directory + .SearchPatternOffset)); + } + } + LeaveCriticalSection(&IoEvent->DokanOpenInfo->CriticalSection); + if (oldDirList) { + PushDirectoryList(oldDirList); + } + } else { + PushDirectoryList(dirList); } + IoEvent->DokanFileInfo.ProcessingContext = NULL; + IoEvent->EventResult->Status = Status; + EventCompletion(IoEvent); } -VOID DispatchDirectoryInformation(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance) { - PEVENT_INFORMATION eventInfo; - DOKAN_FILE_INFO fileInfo; - PDOKAN_OPEN_INFO openInfo; +VOID DispatchDirectoryInformation(PDOKAN_IO_EVENT IoEvent) { + PWCHAR searchPattern = NULL; NTSTATUS status = STATUS_SUCCESS; - ULONG fileInfoClass = EventContext->Operation.Directory.FileInformationClass; - BOOLEAN patternCheck = TRUE; - ULONG sizeOfEventInfo = DispatchGetEventInformationLength( - EventContext->Operation.Directory.BufferLength); + ULONG fileInfoClass = + IoEvent->EventContext->Operation.Directory.FileInformationClass; + BOOL forceScan = FALSE; - CheckFileName(EventContext->Operation.Directory.DirectoryName); + DbgPrint( + "###FindFiles file handle = 0x%p, eventID = %04d, event Info = 0x%p\n", + IoEvent->DokanOpenInfo, + IoEvent->DokanOpenInfo != NULL ? IoEvent->DokanOpenInfo->EventId : -1, + IoEvent); - eventInfo = DispatchCommon(EventContext, sizeOfEventInfo, DokanInstance, - &fileInfo, &openInfo); + CheckFileName(IoEvent->EventContext->Operation.Directory.DirectoryName); // check whether this is handled FileInfoClass if (fileInfoClass != FileDirectoryInformation && @@ -581,135 +624,92 @@ VOID DispatchDirectoryInformation(HANDLE Handle, PEVENT_CONTEXT EventContext, fileInfoClass != FileIdFullDirectoryInformation && fileInfoClass != FileIdExtdDirectoryInformation && fileInfoClass != FileIdExtdBothDirectoryInformation) { - - DbgPrint("not suported type %d\n", fileInfoClass); - + DbgPrint("Dokan Information: Unsupported file information class %d\n", + fileInfoClass); // send directory info to driver - eventInfo->BufferLength = 0; - eventInfo->Status = STATUS_INVALID_PARAMETER; - SendEventInformation(Handle, eventInfo, sizeOfEventInfo); - ReleaseDokanOpenInfo(eventInfo, &fileInfo, DokanInstance); - free(eventInfo); + IoEvent->EventResult->BufferLength = 0; + IoEvent->EventResult->Status = STATUS_INVALID_PARAMETER; + EventCompletion(IoEvent); return; } - // IMPORTANT!! - // this buffer length is fixed in MatchFiles function - eventInfo->BufferLength = EventContext->Operation.Directory.BufferLength; + CreateDispatchCommon(IoEvent, + IoEvent->EventContext->Operation.Directory.BufferLength); - if (openInfo->DirListHead == NULL) { - openInfo->DirListHead = malloc(sizeof(LIST_ENTRY)); - if (openInfo->DirListHead != NULL) { - InitializeListHead(openInfo->DirListHead); - } else { - eventInfo->BufferLength = 0; - eventInfo->Status = STATUS_NO_MEMORY; - SendEventInformation(Handle, eventInfo, sizeOfEventInfo); - ReleaseDokanOpenInfo(eventInfo, &fileInfo, DokanInstance); - free(eventInfo); - return; - } - } + IoEvent->EventResult->Operation.Directory.Index = + IoEvent->EventContext->Operation.Directory.FileIndex; - if (EventContext->Operation.Directory.FileIndex == 0) { - ClearFindData(openInfo->DirListHead); + if (IoEvent->EventContext->Operation.Directory.SearchPatternLength > 0) { + searchPattern = (PWCHAR)((SIZE_T)&IoEvent->EventContext->Operation.Directory + .SearchPatternBase[0] + + (SIZE_T)IoEvent->EventContext->Operation.Directory + .SearchPatternOffset); } - if (IsListEmpty(openInfo->DirListHead)) { - - DbgPrint("###FindFiles %04d\n", openInfo->EventId); - - // if user defined FindFilesWithPattern - if (DokanInstance->DokanOperations->FindFilesWithPattern) { - LPCWSTR pattern = L"*"; - - // if search pattern is specified - if (EventContext->Operation.Directory.SearchPatternLength != 0) { - pattern = (PWCHAR)( - (SIZE_T)&EventContext->Operation.Directory.SearchPatternBase[0] + - (SIZE_T)EventContext->Operation.Directory.SearchPatternOffset); - } - - patternCheck = FALSE; // do not recheck pattern later in MatchFiles - - status = DokanInstance->DokanOperations->FindFilesWithPattern( - EventContext->Operation.Directory.DirectoryName, pattern, - DokanFillFileData, &fileInfo); - - } else { - status = STATUS_NOT_IMPLEMENTED; + EnterCriticalSection(&IoEvent->DokanOpenInfo->CriticalSection); + { + if (IoEvent->DokanOpenInfo->DirList == NULL) { + forceScan = TRUE; + } else if (searchPattern && IoEvent->DokanOpenInfo->DirListSearchPattern) { + forceScan = wcscmp(searchPattern, + IoEvent->DokanOpenInfo->DirListSearchPattern) != 0 + ? TRUE + : FALSE; + } else if (searchPattern) { + forceScan = wcscmp(searchPattern, L"*") != 0 ? TRUE : FALSE; + } else if (IoEvent->DokanOpenInfo->DirListSearchPattern) { + forceScan = + wcscmp(IoEvent->DokanOpenInfo->DirListSearchPattern, L"*") != 0 + ? TRUE + : FALSE; } - - if (status == STATUS_NOT_IMPLEMENTED && - DokanInstance->DokanOperations->FindFiles) { - - patternCheck = TRUE; // do pattern check later in MachFiles - - // call FileSystem specifeid callback routine - status = DokanInstance->DokanOperations->FindFiles( - EventContext->Operation.Directory.DirectoryName, DokanFillFileData, - &fileInfo); + // In FastFat SL_INDEX_SPECIFIED overrides SL_RESTART_SCAN + forceScan = + (forceScan || (!(IoEvent->EventContext->Flags & SL_INDEX_SPECIFIED) && + (IoEvent->EventContext->Flags & SL_RESTART_SCAN))) + ? TRUE + : FALSE; + if (!forceScan) { + status = WriteDirectoryResults(IoEvent, IoEvent->DokanOpenInfo->DirList); } } + LeaveCriticalSection(&IoEvent->DokanOpenInfo->CriticalSection); - if (status != STATUS_SUCCESS) { + if (!forceScan) { + IoEvent->EventResult->Status = status; + EventCompletion(IoEvent); + return; + } - if (EventContext->Operation.Directory.FileIndex == 0) { - DbgPrint(" STATUS_NO_SUCH_FILE\n"); - eventInfo->Status = STATUS_NO_SUCH_FILE; - } else { - DbgPrint(" STATUS_NO_MORE_FILES\n"); - eventInfo->Status = STATUS_NO_MORE_FILES; - } + IoEvent->DokanFileInfo.ProcessingContext = PopDirectoryList(); + if (!IoEvent->DokanFileInfo.ProcessingContext) { + DbgPrint( + "Dokan Error: Failed to allocate memory for a new directory list.\n"); + IoEvent->EventResult->Status = STATUS_NO_MEMORY; + EventCompletion(IoEvent); + return; + } - eventInfo->BufferLength = 0; - eventInfo->Operation.Directory.Index = - EventContext->Operation.Directory.FileIndex; - // free all of list entries - ClearFindData(openInfo->DirListHead); + if ((IoEvent->EventContext->Operation.Directory.SearchPatternLength == 0 || + !IoEvent->DokanInstance->DokanOperations->FindFilesWithPattern) && + IoEvent->DokanInstance->DokanOperations->FindFiles) { + status = IoEvent->DokanInstance->DokanOperations->FindFiles( + IoEvent->EventContext->Operation.Directory.DirectoryName, + DokanFillFileData, &IoEvent->DokanFileInfo); + EndFindFilesCommon(IoEvent, status); + + } else if (IoEvent->EventContext->Operation.Directory.SearchPatternLength != + 0 && + IoEvent->DokanInstance->DokanOperations->FindFilesWithPattern) { + status = IoEvent->DokanInstance->DokanOperations->FindFilesWithPattern( + IoEvent->EventContext->Operation.Directory.DirectoryName, + searchPattern ? searchPattern : L"*", DokanFillFileData, + &IoEvent->DokanFileInfo); + EndFindFilesCommon(IoEvent, status); } else { - LONG index; - eventInfo->Status = STATUS_SUCCESS; - - AddMissingCurrentAndParentFolder(EventContext, openInfo->DirListHead, - &fileInfo); - - DbgPrint("index from %d\n", EventContext->Operation.Directory.FileIndex); - // extract entries that match search pattern from FindFiles result - index = MatchFiles(EventContext, eventInfo, openInfo->DirListHead, - patternCheck, DokanInstance); - - // there is no matched file - if (index < 0) { - eventInfo->BufferLength = 0; - eventInfo->Operation.Directory.Index = - EventContext->Operation.Directory.FileIndex; - if (index == -1) { - if (EventContext->Operation.Directory.FileIndex == 0) { - DbgPrint(" STATUS_NO_SUCH_FILE\n"); - eventInfo->Status = STATUS_NO_SUCH_FILE; - } else { - DbgPrint(" STATUS_NO_MORE_FILES\n"); - eventInfo->Status = STATUS_NO_MORE_FILES; - } - } else { - DbgPrint(" STATUS_BUFFER_OVERFLOW\n"); - eventInfo->Status = STATUS_BUFFER_OVERFLOW; - } - ClearFindData(openInfo->DirListHead); - } else { - DbgPrint("index to %d\n", index); - eventInfo->Operation.Directory.Index = index; - } + IoEvent->EventResult->Status = STATUS_NOT_IMPLEMENTED; + EventCompletion(IoEvent); } - - // information for FileSystem - openInfo->UserContext = fileInfo.Context; - - // send directory information to driver - SendEventInformation(Handle, eventInfo, sizeOfEventInfo); - ReleaseDokanOpenInfo(eventInfo, &fileInfo, DokanInstance); - free(eventInfo); } #define DOS_STAR (L'<') diff --git a/dokan/dokan.c b/dokan/dokan.c index 53e24ceb9..bbc89f85c 100644 --- a/dokan/dokan.c +++ b/dokan/dokan.c @@ -23,11 +23,14 @@ with this program. If not, see . #include "dokani.h" #include "fileinfo.h" #include "list.h" +#include "dokan_pool.h" + #include #include #include #include #include +#include #define DokanMapKernelBit(dest, src, userBit, kernelBit) \ if (((src) & (kernelBit)) == (kernelBit)) \ @@ -39,26 +42,29 @@ BOOL g_DebugMode = TRUE; // DokanOptions->UseStdErr is ON? BOOL g_UseStdErr = FALSE; +// Dokan DLL critical section CRITICAL_SECTION g_InstanceCriticalSection; + +// Global linked list of mounted Dokan instances LIST_ENTRY g_InstanceList; -HANDLE g_notify_handle = INVALID_HANDLE_VALUE; + +volatile LONG g_DokanInitialized = 0; VOID DOKANAPI DokanUseStdErr(BOOL Status) { g_UseStdErr = Status; } VOID DOKANAPI DokanDebugMode(BOOL Status) { g_DebugMode = Status; } -VOID DispatchDriverLogs(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance) { - UNREFERENCED_PARAMETER(Handle); - UNREFERENCED_PARAMETER(DokanInstance); +VOID DispatchDriverLogs(PDOKAN_IO_EVENT IoEvent) { + UNREFERENCED_PARAMETER(IoEvent); PDOKAN_LOG_MESSAGE log_message = - (PDOKAN_LOG_MESSAGE)((PCHAR)EventContext + sizeof(EVENT_CONTEXT)); + (PDOKAN_LOG_MESSAGE)((PCHAR)IoEvent->EventContext + + sizeof(EVENT_CONTEXT)); if (log_message->MessageLength) { ULONG paquet_size = FIELD_OFFSET(DOKAN_LOG_MESSAGE, Message[0]) + log_message->MessageLength; if (((PCHAR)log_message + paquet_size) <= - ((PCHAR)EventContext + EventContext->Length)) { + ((PCHAR)IoEvent->EventContext + IoEvent->EventContext->Length)) { DbgPrint("DriverLog: %.*s\n", log_message->MessageLength, log_message->Message); } else { @@ -75,25 +81,89 @@ NewDokanInstance() { ZeroMemory(instance, sizeof(DOKAN_INSTANCE)); + instance->GlobalDevice = INVALID_HANDLE_VALUE; + instance->Device = INVALID_HANDLE_VALUE; + instance->NotifyHandle = INVALID_HANDLE_VALUE; + instance->KeepaliveHandle = INVALID_HANDLE_VALUE; + (void)InitializeCriticalSectionAndSpinCount(&instance->CriticalSection, 0x80000400); InitializeListHead(&instance->ListEntry); + instance->DeviceClosedWaitHandle = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!instance->DeviceClosedWaitHandle) { + DokanDbgPrint("Dokan Error: Cannot create Dokan instance because the " + "device closed wait handle could not be created.\n"); + DeleteCriticalSection(&instance->CriticalSection); + free(instance); + return NULL; + } + EnterCriticalSection(&g_InstanceCriticalSection); - InsertTailList(&g_InstanceList, &instance->ListEntry); - LeaveCriticalSection(&g_InstanceCriticalSection); + { + PTP_POOL threadPool = GetThreadPool(); + if (!threadPool) { + DokanDbgPrint("Dokan Error: Cannot create Dokan instance because the " + "thread pool hasn't been created.\n"); + LeaveCriticalSection(&g_InstanceCriticalSection); + DeleteCriticalSection(&instance->CriticalSection); + CloseHandle(instance->DeviceClosedWaitHandle); + free(instance); + return NULL; + } + instance->ThreadInfo.ThreadPool = threadPool; + instance->ThreadInfo.CleanupGroup = CreateThreadpoolCleanupGroup(); + if (!instance->ThreadInfo.CleanupGroup) { + DokanDbgPrint( + "Dokan Error: Failed to create thread pool cleanup group.\n"); + LeaveCriticalSection(&g_InstanceCriticalSection); + DeleteCriticalSection(&instance->CriticalSection); + CloseHandle(instance->DeviceClosedWaitHandle); + free(instance); + return NULL; + } + InitializeThreadpoolEnvironment(&instance->ThreadInfo.CallbackEnvironment); + SetThreadpoolCallbackPool(&instance->ThreadInfo.CallbackEnvironment, + threadPool); + SetThreadpoolCallbackCleanupGroup(&instance->ThreadInfo.CallbackEnvironment, + instance->ThreadInfo.CleanupGroup, NULL); + InsertTailList(&g_InstanceList, &instance->ListEntry); + } + LeaveCriticalSection(&g_InstanceCriticalSection); return instance; } VOID DeleteDokanInstance(PDOKAN_INSTANCE Instance) { + SetEvent(Instance->DeviceClosedWaitHandle); + if (Instance->ThreadInfo.CleanupGroup) { + CloseThreadpoolCleanupGroupMembers(Instance->ThreadInfo.CleanupGroup, FALSE, + Instance); + CloseThreadpoolCleanupGroup(Instance->ThreadInfo.CleanupGroup); + Instance->ThreadInfo.CleanupGroup = NULL; + DestroyThreadpoolEnvironment(&Instance->ThreadInfo.CallbackEnvironment); + } + if (Instance->NotifyHandle && + Instance->NotifyHandle != INVALID_HANDLE_VALUE) { + CloseHandle(Instance->NotifyHandle); + } + if (Instance->KeepaliveHandle && + Instance->KeepaliveHandle != INVALID_HANDLE_VALUE) { + CloseHandle(Instance->KeepaliveHandle); + } + if (Instance->Device && Instance->Device != INVALID_HANDLE_VALUE) { + CloseHandle(Instance->Device); + } + if (Instance->GlobalDevice && + Instance->GlobalDevice != INVALID_HANDLE_VALUE) { + CloseHandle(Instance->GlobalDevice); + } DeleteCriticalSection(&Instance->CriticalSection); - EnterCriticalSection(&g_InstanceCriticalSection); - RemoveEntryList(&Instance->ListEntry); + { RemoveEntryList(&Instance->ListEntry); } LeaveCriticalSection(&g_InstanceCriticalSection); - + CloseHandle(Instance->DeviceClosedWaitHandle); free(Instance); } @@ -172,7 +242,7 @@ BOOL CheckDriveLetterAvailability(WCHAR DriveLetter) { return TRUE; } -void CheckAllocationUnitSectorSize(PDOKAN_OPTIONS DokanOptions) { +VOID CheckAllocationUnitSectorSize(PDOKAN_OPTIONS DokanOptions) { ULONG allocationUnitSize = DokanOptions->AllocationUnitSize; ULONG sectorSize = DokanOptions->SectorSize; @@ -190,13 +260,353 @@ void CheckAllocationUnitSectorSize(PDOKAN_OPTIONS DokanOptions) { DokanOptions->AllocationUnitSize, DokanOptions->SectorSize); } +VOID SetupIOEventForProcessing(DOKAN_IO_EVENT *IoEvent) { + IoEvent->DokanOpenInfo = + (PDOKAN_OPEN_INFO)(UINT_PTR)IoEvent->EventContext->Context; + IoEvent->DokanFileInfo.DokanContext = (ULONG64)IoEvent; + IoEvent->DokanFileInfo.ProcessId = IoEvent->EventContext->ProcessId; + IoEvent->DokanFileInfo.DokanOptions = IoEvent->DokanInstance->DokanOptions; + + if (IoEvent->DokanOpenInfo) { + EnterCriticalSection(&IoEvent->DokanInstance->CriticalSection); + IoEvent->DokanOpenInfo->OpenCount++; + LeaveCriticalSection(&IoEvent->DokanInstance->CriticalSection); + IoEvent->DokanFileInfo.Context = + InterlockedAdd64(&IoEvent->DokanOpenInfo->UserContext, 0); + IoEvent->DokanFileInfo.IsDirectory = + (UCHAR)IoEvent->DokanOpenInfo->IsDirectory; + + if (IoEvent->EventContext->FileFlags & DOKAN_DELETE_ON_CLOSE) { + IoEvent->DokanFileInfo.DeleteOnClose = 1; + } + if (IoEvent->EventContext->FileFlags & DOKAN_PAGING_IO) { + IoEvent->DokanFileInfo.PagingIo = 1; + } + if (IoEvent->EventContext->FileFlags & DOKAN_WRITE_TO_END_OF_FILE) { + IoEvent->DokanFileInfo.WriteToEndOfFile = 1; + } + if (IoEvent->EventContext->FileFlags & DOKAN_SYNCHRONOUS_IO) { + IoEvent->DokanFileInfo.SynchronousIo = 1; + } + if (IoEvent->EventContext->FileFlags & DOKAN_NOCACHE) { + IoEvent->DokanFileInfo.Nocache = 1; + } + } + assert(IoEvent->EventResult == NULL); +} + +VOID DispatchEvent(PDOKAN_IO_EVENT ioEvent) { + SetupIOEventForProcessing(ioEvent); + switch (ioEvent->EventContext->MajorFunction) { + case IRP_MJ_CREATE: + DispatchCreate(ioEvent); + break; + case IRP_MJ_CLEANUP: + DispatchCleanup(ioEvent); + break; + case IRP_MJ_CLOSE: + DispatchClose(ioEvent); + break; + case IRP_MJ_DIRECTORY_CONTROL: + DispatchDirectoryInformation(ioEvent); + break; + case IRP_MJ_READ: + DispatchRead(ioEvent); + break; + case IRP_MJ_WRITE: + DispatchWrite(ioEvent); + break; + case IRP_MJ_QUERY_INFORMATION: + DispatchQueryInformation(ioEvent); + break; + case IRP_MJ_QUERY_VOLUME_INFORMATION: + DispatchQueryVolumeInformation(ioEvent); + break; + case IRP_MJ_LOCK_CONTROL: + DispatchLock(ioEvent); + break; + case IRP_MJ_SET_INFORMATION: + DispatchSetInformation(ioEvent); + break; + case IRP_MJ_FLUSH_BUFFERS: + DispatchFlush(ioEvent); + break; + case IRP_MJ_QUERY_SECURITY: + DispatchQuerySecurity(ioEvent); + break; + case IRP_MJ_SET_SECURITY: + DispatchSetSecurity(ioEvent); + break; + case DOKAN_IRP_LOG_MESSAGE: + DispatchDriverLogs(ioEvent); + break; + default: + DokanDbgPrintW(L"Dokan Warning: Unsupported IRP 0x%x, event Info = 0x%p.\n", + ioEvent->EventContext->MajorFunction, ioEvent->EventContext); + PushIoEventBuffer(ioEvent); + break; + } +} + +VOID OnDeviceIoCtlFailed(PDOKAN_INSTANCE Dokan, DWORD Result) { + DokanDbgPrintW(L"Dokan Fatal: Closing IO processing for dokan instance %s " + L"with error code 0x%x and unmounting volume.\n", + Dokan->DeviceName, Result); + if (Dokan->DokanOperations->Unmounted) { + DOKAN_FILE_INFO fileInfo; + RtlZeroMemory(&fileInfo, sizeof(DOKAN_FILE_INFO)); + fileInfo.DokanOptions = Dokan->DokanOptions; + // Ignore return value + Dokan->DokanOperations->Unmounted(&fileInfo); + } + + // set the device to a closed state + SetEvent(Dokan->DeviceClosedWaitHandle); +} + +// Don't know what went wrong +// Life will never be the same again +// End it all +VOID HandleProcessIoFatalError(PDOKAN_INSTANCE DokanInstance, + PDOKAN_IO_BATCH IoBatch, DWORD Result) { + PushIoBatchBuffer(IoBatch); + OnDeviceIoCtlFailed(DokanInstance, Result); +} + +VOID FreeIoEventResult(PEVENT_INFORMATION EventResult, BOOL PoolAllocated) { + if (EventResult) { + if (PoolAllocated) { + PushEventResult(EventResult); + } else { + FreeEventResult(EventResult); + } + } +} + +VOID CALLBACK DispatchCallback(PTP_CALLBACK_INSTANCE Instance, PVOID Parameter, + PTP_WORK Work); + +VOID QueueIoEvent(PDOKAN_IO_EVENT IoEvent) { + PTP_WORK work = CreateThreadpoolWork( + DispatchCallback, IoEvent, + &IoEvent->DokanInstance->ThreadInfo.CallbackEnvironment); + if (!work) { + DWORD lastError = GetLastError(); + DbgPrintW(L"Dokan Error: CreateThreadpoolWork() has returned error " + L"code %u.\n", + lastError); + OnDeviceIoCtlFailed(IoEvent->DokanInstance, lastError); + return; + } + SubmitThreadpoolWork(work); +} + +DWORD +GetEventInfoSize(__in ULONG MajorFunction, __in PEVENT_INFORMATION EventInfo) { + if (MajorFunction == IRP_MJ_WRITE) { + // For writes only, the reply is a fixed size and the BufferLength inside it + // is the "bytes written" value as opposed to the reply size. + return sizeof(EVENT_INFORMATION); + } + return (DWORD)max((ULONG)sizeof(EVENT_INFORMATION), + FIELD_OFFSET(EVENT_INFORMATION, Buffer[0]) + + EventInfo->BufferLength); +} + +DWORD SendAndPullEventInformation(PDOKAN_IO_EVENT IoEvent, + PDOKAN_IO_BATCH IoBatch) { + DWORD lastError = 0; + PCHAR inputBuffer = NULL; + DWORD eventInfoSize = 0; + PEVENT_INFORMATION eventInfo = NULL; + BOOL eventInfoPollAllocated = FALSE; + + if (IoEvent && IoEvent->EventResult) { + eventInfo = IoEvent->EventResult; + eventInfoPollAllocated = IoEvent->PoolAllocated; + inputBuffer = (PCHAR)eventInfo; + eventInfoSize = + GetEventInfoSize(IoEvent->EventContext->MajorFunction, eventInfo); + PushIoBatchBuffer(IoEvent->IoBatch); + PushIoEventBuffer(IoEvent); + DbgPrint( + "Dokan Information: SendAndPullEventInformation() with NTSTATUS 0x%x, " + "context 0x%lx, and result object 0x%p with size %d\n", + eventInfo->Status, eventInfo->Context, eventInfo, eventInfoSize); + } else { + // Main pull thread is allowed to pull events without having event results to send + assert(IoBatch->MainPullThread); + } + + if (!DeviceIoControl( + IoBatch->DokanInstance->Device, // Handle to device + FSCTL_EVENT_PROCESS_N_PULL, // IO Control code + inputBuffer, // Input Buffer to driver. + eventInfoSize, // Length of input buffer in bytes. + &IoBatch->EventContext[0], // Output Buffer from driver. + BATCH_EVENT_CONTEXT_SIZE, // Length of output buffer in bytes. + &IoBatch->NumberOfBytesTransferred, // Bytes placed in buffer. + NULL // asynchronous call + )) { + lastError = GetLastError(); + if (eventInfo) { + FreeIoEventResult(eventInfo, eventInfoPollAllocated); + } + DokanDbgPrintW( + L"Dokan Error: Dokan device result ioctl failed for wait with " + L"code %d.\n", + lastError); + return lastError; + } + if (eventInfo) { + FreeIoEventResult(eventInfo, eventInfoPollAllocated); + } + return 0; +} + +VOID CALLBACK DispatchCallback(PTP_CALLBACK_INSTANCE Instance, PVOID Parameter, + PTP_WORK Work) { + UNREFERENCED_PARAMETER(Instance); + UNREFERENCED_PARAMETER(Work); + + PDOKAN_IO_EVENT ioEvent = (PDOKAN_IO_EVENT)Parameter; + assert(ioEvent); + PDOKAN_INSTANCE instance = ioEvent->DokanInstance; + PDOKAN_IO_BATCH ioBatch = NULL; + BOOL mainPullThread = ioEvent->EventContext == NULL; + + while (TRUE) { + // 6 - Process events coming from: + // - Last event not dispatched to the pool (see bottom of this fct). + // - New pool thread that just started with a dispatched event. + // Note: Main pull thread does not have an EventContext when started. + if (ioEvent && ioEvent->EventContext) { + DispatchEvent(ioEvent); + if (!ioEvent->EventResult) { + // Some events like Close() do not have event results. + // Release the resource and terminate here unless we are the main pulling thread. + PushIoBatchBuffer(ioEvent->IoBatch); + PushIoEventBuffer(ioEvent); + if (mainPullThread) { + ioEvent = NULL; + continue; + } + return; + } + } + + ioBatch = PopIoBatchBuffer(); + ioBatch->MainPullThread = mainPullThread; + ioBatch->DokanInstance = instance; + + // 1 - Send event result and pull new events. + DWORD error = SendAndPullEventInformation(ioEvent, ioBatch); + if (error) { + HandleProcessIoFatalError(instance, ioBatch, error); + return; + } + + // 2 - Terminate thread as nothing needs to be proceed unless we are the mainPullThread. + if (!ioBatch->NumberOfBytesTransferred) { + PushIoBatchBuffer(ioBatch); + if (mainPullThread) { + ioEvent = NULL; + continue; + } + return; + } + + PEVENT_CONTEXT context = ioBatch->EventContext; + ULONG_PTR currentNumberOfBytesTransferred = + ioBatch->NumberOfBytesTransferred; + while (currentNumberOfBytesTransferred) { + ++ioBatch->EventContextBatchCount; + currentNumberOfBytesTransferred -= context->Length; + context = (PEVENT_CONTEXT)((PCHAR)(context) + context->Length); + } + // 3 - Dispatch Events + context = ioBatch->EventContext; + LONG eventContextBatchCount = ioBatch->EventContextBatchCount; + while (eventContextBatchCount) { + ioEvent = PopIoEventBuffer(); + if (!ioEvent) { + DbgPrintW(L"Dokan Error: IoEvent allocation failed.\n"); + OnDeviceIoCtlFailed(ioBatch->DokanInstance, ERROR_OUTOFMEMORY); + return; + } + ioEvent->DokanInstance = ioBatch->DokanInstance; + ioEvent->EventContext = context; + ioEvent->IoBatch = ioBatch; + --eventContextBatchCount; + // It is unsafe to access the context from here after Queuing the event. + context = (PEVENT_CONTEXT)((PCHAR)(context) + context->Length); + // 4 - All batched events are dispatched to the thread pool except the last event that is executed on the current thread. + // Note: Single thread mode has batching disabled and therefore only has one event which is executed on the main thread. + if (eventContextBatchCount) { + QueueIoEvent(ioEvent); + } + } + } +} + +BOOL DOKANAPI DokanIsFileSystemRunning(_In_ DOKAN_HANDLE DokanInstance) { + DOKAN_INSTANCE *instance = (DOKAN_INSTANCE *)DokanInstance; + if (!instance) { + return FALSE; + } + return WaitForSingleObject(instance->DeviceClosedWaitHandle, 0) == + WAIT_TIMEOUT + ? TRUE + : FALSE; +} + +DWORD DOKANAPI DokanWaitForFileSystemClosed(_In_ DOKAN_HANDLE DokanInstance, + _In_ DWORD dwMilliseconds) { + DOKAN_INSTANCE *instance = (DOKAN_INSTANCE *)DokanInstance; + if (!instance) { + return FALSE; + } + return WaitForSingleObject(instance->DeviceClosedWaitHandle, dwMilliseconds); +} + +VOID DOKANAPI DokanCloseHandle(_In_ DOKAN_HANDLE DokanInstance) { + DOKAN_INSTANCE *instance = (DOKAN_INSTANCE *)DokanInstance; + if (!instance) { + return; + } + // make sure the driver is unmounted + DokanRemoveMountPoint(instance->MountPoint); + DokanWaitForFileSystemClosed((DOKAN_HANDLE)instance, INFINITE); + DeleteDokanInstance(instance); +} + int DOKANAPI DokanMain(PDOKAN_OPTIONS DokanOptions, PDOKAN_OPERATIONS DokanOperations) { - HANDLE device; - HANDLE threadIds[DOKAN_MAX_THREAD]; - HANDLE legacyKeepAliveThreadIds = NULL; - BOOL keepalive_active = FALSE; + DOKAN_INSTANCE *instance = NULL; + int returnCode; + returnCode = DokanCreateFileSystem(DokanOptions, DokanOperations, + (DOKAN_HANDLE *)&instance); + if (returnCode != DOKAN_SUCCESS) { + return returnCode; + } + DokanWaitForFileSystemClosed((DOKAN_HANDLE)instance, INFINITE); + DeleteDokanInstance(instance); + return returnCode; +} + +int DOKANAPI DokanCreateFileSystem(_In_ PDOKAN_OPTIONS DokanOptions, + _In_ PDOKAN_OPERATIONS DokanOperations, + _Out_ DOKAN_HANDLE *DokanInstance) { PDOKAN_INSTANCE instance; + WCHAR rawDeviceName[MAX_PATH]; + + if (DokanInstance) { + *DokanInstance = NULL; + } + + if (InterlockedAdd(&g_DokanInitialized, 0) <= 0) { + RaiseException(DOKAN_EXCEPTION_NOT_INITIALIZED, 0, 0, NULL); + } g_DebugMode = DokanOptions->Options & DOKAN_OPTION_DEBUG; g_UseStdErr = DokanOptions->Options & DOKAN_OPTION_STDERR; @@ -210,7 +620,7 @@ int DOKANAPI DokanMain(PDOKAN_OPTIONS DokanOptions, g_DebugMode = TRUE; } - if (DokanOptions->Options & DOKAN_OPTION_NETWORK && + if ((DokanOptions->Options & DOKAN_OPTION_NETWORK) && !IsMountPointDriveLetter(DokanOptions->MountPoint)) { DokanOptions->Options &= ~DOKAN_OPTION_NETWORK; DbgPrintW(L"Dokan: Mount point folder is specified with network device " @@ -224,50 +634,45 @@ int DOKANAPI DokanMain(PDOKAN_OPTIONS DokanOptions, return DOKAN_VERSION_ERROR; } - CheckAllocationUnitSectorSize(DokanOptions); - - if (DokanOptions->ThreadCount == 0) { - DokanOptions->ThreadCount = 5; - - } else if (DOKAN_MAX_THREAD < DokanOptions->ThreadCount) { - DokanDbgPrintW(L"Dokan Error: too many thread count %d\n", - DokanOptions->ThreadCount); - DokanOptions->ThreadCount = DOKAN_MAX_THREAD; + if (DokanOptions->SingleThread) { + DbgPrintW(L"Dokan Info: Single thread mode enabled.\n"); } - device = CreateFile(DOKAN_GLOBAL_DEVICE_NAME, // lpFileName - GENERIC_READ | GENERIC_WRITE, // dwDesiredAccess - FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode - NULL, // lpSecurityAttributes - OPEN_EXISTING, // dwCreationDistribution - 0, // dwFlagsAndAttributes - NULL // hTemplateFile - ); - - if (device == INVALID_HANDLE_VALUE) { - DokanDbgPrintW(L"Dokan Error: CreateFile Failed %s: %d\n", - DOKAN_GLOBAL_DEVICE_NAME, GetLastError()); + CheckAllocationUnitSectorSize(DokanOptions); + instance = NewDokanInstance(); + if (!instance) { return DOKAN_DRIVER_INSTALL_ERROR; } - DbgPrint("Global device opened\n"); - instance = NewDokanInstance(); instance->DokanOptions = DokanOptions; instance->DokanOperations = DokanOperations; + instance->GlobalDevice = + CreateFile(DOKAN_GLOBAL_DEVICE_NAME, // lpFileName + 0, // dwDesiredAccess + FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode + NULL, // lpSecurityAttributes + OPEN_EXISTING, // dwCreationDistribution + 0, // dwFlagsAndAttributes + NULL // hTemplateFile + ); + if (instance->GlobalDevice == INVALID_HANDLE_VALUE) { + DWORD lastError = GetLastError(); + DokanDbgPrintW(L"Dokan Error: CreatFile failed to open %s: %d\n", + DOKAN_GLOBAL_DEVICE_NAME, lastError); + DeleteDokanInstance(instance); + return DOKAN_DRIVER_INSTALL_ERROR; + } + DbgPrint("Global device opened\n"); if (DokanOptions->MountPoint != NULL) { wcscpy_s(instance->MountPoint, sizeof(instance->MountPoint) / sizeof(WCHAR), DokanOptions->MountPoint); - if (IsMountPointDriveLetter(instance->MountPoint) - && !CheckDriveLetterAvailability(instance->MountPoint[0])) { - DokanDbgPrint("Dokan Error: CheckDriveLetterAvailability Failed\n"); - CloseHandle(device); - - EnterCriticalSection(&g_InstanceCriticalSection); - RemoveTailList(&g_InstanceList); - LeaveCriticalSection(&g_InstanceCriticalSection); - return DOKAN_MOUNT_ERROR; - } + if (IsMountPointDriveLetter(instance->MountPoint) && + !CheckDriveLetterAvailability(instance->MountPoint[0])) { + DokanDbgPrint("Dokan Error: CheckDriveLetterAvailability Failed\n"); + DeleteDokanInstance(instance); + return DOKAN_MOUNT_ERROR; + } } if (DokanOptions->UNCName != NULL) { @@ -276,293 +681,110 @@ int DOKANAPI DokanMain(PDOKAN_OPTIONS DokanOptions, } if (!DokanStart(instance)) { - CloseHandle(device); + DeleteDokanInstance(instance); return DOKAN_START_ERROR; } - for (ULONG i = 0; i < DokanOptions->ThreadCount; ++i) { - threadIds[i] = (HANDLE)_beginthreadex(NULL, // Security Attributes - 0, // stack size - DokanLoop, - (PVOID)instance, // param - 0, // create flag - NULL); + GetRawDeviceName(instance->DeviceName, rawDeviceName, MAX_PATH); + instance->Device = + CreateFile(rawDeviceName, // lpFileName + 0, // dwDesiredAccess + FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode + NULL, // lpSecurityAttributes + OPEN_EXISTING, // dwCreationDistribution + FILE_FLAG_OVERLAPPED, // dwFlagsAndAttributes + NULL // hTemplateFile + ); + if (instance->Device == INVALID_HANDLE_VALUE) { + DWORD lastError = GetLastError(); + DokanDbgPrintW(L"Dokan Error: CreatFile failed to open %s: %d\n", + rawDeviceName, lastError); + DeleteDokanInstance(instance); + return DOKAN_DRIVER_INSTALL_ERROR; + } + + PDOKAN_IO_EVENT ioEvent = PopIoEventBuffer(); + if (!ioEvent) { + DokanDbgPrintW(L"Dokan Error: IoEvent allocation failed."); + DeleteDokanInstance(instance); + return DOKAN_MOUNT_ERROR; } + ioEvent->DokanInstance = instance; + QueueIoEvent(ioEvent); if (!DokanMount(instance->MountPoint, instance->DeviceName, DokanOptions)) { SendReleaseIRP(instance->DeviceName); DokanDbgPrint("Dokan Error: DokanMount Failed\n"); - CloseHandle(device); + DeleteDokanInstance(instance); return DOKAN_MOUNT_ERROR; } wchar_t keepalive_path[128]; StringCbPrintfW(keepalive_path, sizeof(keepalive_path), L"\\\\?%s%s", instance->DeviceName, DOKAN_KEEPALIVE_FILE_NAME); - HANDLE keepalive_handle = + instance->KeepaliveHandle = CreateFile(keepalive_path, 0, 0, NULL, OPEN_EXISTING, 0, NULL); - if (keepalive_handle == INVALID_HANDLE_VALUE) { + if (instance->KeepaliveHandle == INVALID_HANDLE_VALUE) { // We don't consider this a fatal error because the keepalive handle is only // needed for abnormal termination cases anyway. - DbgPrintW(L"Failed to open keepalive file: %s\n", keepalive_path); + DbgPrintW(L"Failed to open keepalive file: %s error %d\n", keepalive_path, + GetLastError()); } else { DWORD keepalive_bytes_returned = 0; - keepalive_active = - DeviceIoControl(keepalive_handle, FSCTL_ACTIVATE_KEEPALIVE, NULL, 0, - NULL, 0, &keepalive_bytes_returned, NULL); - if (!keepalive_active) + if (!DeviceIoControl(instance->KeepaliveHandle, FSCTL_ACTIVATE_KEEPALIVE, + NULL, 0, NULL, 0, &keepalive_bytes_returned, NULL)) DbgPrintW(L"Failed to activate keepalive handle.\n"); } - if (!keepalive_active) { - DbgPrintW(L"Enable legacy keepalive.\n"); - // Start Legacy Keep Alive thread - We are probably using older driver - legacyKeepAliveThreadIds = - (HANDLE)_beginthreadex(NULL, // Security Attributes - 0, // stack size - DokanKeepAlive, - (PVOID)instance, // param - 0, // create flag - NULL); - } - - if (DokanOptions->Options & DOKAN_OPTION_ENABLE_NOTIFICATION_API) { - wchar_t notify_path[128]; - StringCbPrintfW(notify_path, sizeof(notify_path), L"\\\\?%s%s", - instance->DeviceName, DOKAN_NOTIFICATION_FILE_NAME); - g_notify_handle = CreateFile( - notify_path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (g_notify_handle == INVALID_HANDLE_VALUE) { - DbgPrintW(L"Failed to open notify handle: %s\n", notify_path); - } + wchar_t notify_path[128]; + StringCbPrintfW(notify_path, sizeof(notify_path), L"\\\\?%s%s", + instance->DeviceName, DOKAN_NOTIFICATION_FILE_NAME); + instance->NotifyHandle = CreateFile( + notify_path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (instance->NotifyHandle == INVALID_HANDLE_VALUE) { + DbgPrintW(L"Failed to open notify handle: %s\n", notify_path); } // Here we should have been mounter by mountmanager thanks to // IOCTL_MOUNTDEV_QUERY_SUGGESTED_LINK_NAME - DbgPrintW(L"mounted: %s -> %s\n", instance->MountPoint, instance->DeviceName); + DbgPrintW(L"Dokan Information: mounted: %s -> %s\n", instance->MountPoint, + instance->DeviceName); if (DokanOperations->Mounted) { DOKAN_FILE_INFO fileInfo; RtlZeroMemory(&fileInfo, sizeof(DOKAN_FILE_INFO)); fileInfo.DokanOptions = DokanOptions; - // ignore return value + // Ignore return value DokanOperations->Mounted(&fileInfo); } - // wait for loop thread terminations - WaitForMultipleObjects(DokanOptions->ThreadCount, threadIds, TRUE, INFINITE); - for (ULONG i = 0; i < DokanOptions->ThreadCount; ++i) { - CloseHandle(threadIds[i]); - } - - if (legacyKeepAliveThreadIds) { - WaitForSingleObject(legacyKeepAliveThreadIds, INFINITE); - CloseHandle(legacyKeepAliveThreadIds); + if (DokanInstance) { + *DokanInstance = instance; } - - if (g_notify_handle != INVALID_HANDLE_VALUE) - CloseHandle(g_notify_handle); - // Note that the keepalive close that actually has unmounting effect is the - // implicit one that happens if the process dies. If this one runs, it will be - // a no-op. - if (keepalive_handle != INVALID_HANDLE_VALUE) - CloseHandle(keepalive_handle); - CloseHandle(device); - - if (DokanOperations->Unmounted) { - DOKAN_FILE_INFO fileInfo; - RtlZeroMemory(&fileInfo, sizeof(DOKAN_FILE_INFO)); - fileInfo.DokanOptions = DokanOptions; - // ignore return value - DokanOperations->Unmounted(&fileInfo); - } - - Sleep(1000); - - DbgPrint("\nunload\n"); - - DeleteDokanInstance(instance); - return DOKAN_SUCCESS; } -VOID -GetRawDeviceName(LPCWSTR DeviceName, LPWSTR DestinationBuffer, - rsize_t DestinationBufferSizeInElements) { +VOID GetRawDeviceName(LPCWSTR DeviceName, LPWSTR DestinationBuffer, + rsize_t DestinationBufferSizeInElements) { if (DeviceName && DestinationBuffer && DestinationBufferSizeInElements > 0) { wcscpy_s(DestinationBuffer, DestinationBufferSizeInElements, L"\\\\."); wcscat_s(DestinationBuffer, DestinationBufferSizeInElements, DeviceName); } } -void ALIGN_ALLOCATION_SIZE(PLARGE_INTEGER size, PDOKAN_OPTIONS DokanOptions) { +VOID ALIGN_ALLOCATION_SIZE(PLARGE_INTEGER size, PDOKAN_OPTIONS DokanOptions) { long long r = size->QuadPart % DokanOptions->AllocationUnitSize; size->QuadPart = (size->QuadPart + (r > 0 ? DokanOptions->AllocationUnitSize - r : 0)); } -UINT WINAPI DokanLoop(PVOID pDokanInstance) { - HANDLE device = INVALID_HANDLE_VALUE; - char *buffer = NULL; - BOOL status; - ULONG returnedLength; - DWORD result = 0; - DWORD lastError = 0; - WCHAR rawDeviceName[MAX_PATH]; - PDOKAN_INSTANCE DokanInstance = pDokanInstance; - - buffer = malloc(sizeof(char) * EVENT_CONTEXT_MAX_SIZE); - if (buffer == NULL) { - result = (DWORD)-1; - _endthreadex(result); - return result; - } - RtlZeroMemory(buffer, sizeof(char) * EVENT_CONTEXT_MAX_SIZE); - - GetRawDeviceName(DokanInstance->DeviceName, rawDeviceName, MAX_PATH); - - status = TRUE; - while (status) { - - device = CreateFile(rawDeviceName, // lpFileName - 0, // dwDesiredAccess - FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode - NULL, // lpSecurityAttributes - OPEN_EXISTING, // dwCreationDistribution - 0, // dwFlagsAndAttributes - NULL // hTemplateFile - ); - - if (device == INVALID_HANDLE_VALUE) { - DbgPrintW( - L"Dokan Error: CreateFile failed %s: %d\n", - rawDeviceName, - GetLastError()); - free(buffer); - result = (DWORD)-1; - _endthreadex(result); - return result; - } - - status = DeviceIoControl( - device, // Handle to device - FSCTL_EVENT_WAIT, // IO Control code - NULL, // Input Buffer to driver. - 0, // Length of input buffer in bytes. - buffer, // Output Buffer from driver. - sizeof(char) * - EVENT_CONTEXT_MAX_SIZE, // Length of output buffer in bytes. - &returnedLength, // Bytes placed in buffer. - NULL // synchronous call - ); - - if (!status) { - lastError = GetLastError(); - DbgPrint("Ioctl failed for wait with code %d.\n", lastError); - if (lastError == ERROR_NO_SYSTEM_RESOURCES) { - DbgPrint("Processing will continue\n"); - status = TRUE; - CloseHandle(device); - Sleep(200); - continue; - } - DbgPrint("Thread will be terminated\n"); - break; - } - - // printf("#%d got notification %d\n", (ULONG)Param, count++); - - if (returnedLength > 0) { - PEVENT_CONTEXT context = (PEVENT_CONTEXT)buffer; - if (context->MountId != DokanInstance->MountId) { - DbgPrint("Dokan Error: Invalid MountId (expected:%d, acctual:%d)\n", - DokanInstance->MountId, context->MountId); - CloseHandle(device); - continue; - } - - switch (context->MajorFunction) { - case IRP_MJ_CREATE: - DispatchCreate(device, context, DokanInstance); - break; - case IRP_MJ_CLEANUP: - DispatchCleanup(device, context, DokanInstance); - break; - case IRP_MJ_CLOSE: - DispatchClose(device, context, DokanInstance); - break; - case IRP_MJ_DIRECTORY_CONTROL: - DispatchDirectoryInformation(device, context, DokanInstance); - break; - case IRP_MJ_READ: - DispatchRead(device, context, DokanInstance); - break; - case IRP_MJ_WRITE: - DispatchWrite(device, context, DokanInstance); - break; - case IRP_MJ_QUERY_INFORMATION: - DispatchQueryInformation(device, context, DokanInstance); - break; - case IRP_MJ_QUERY_VOLUME_INFORMATION: - DispatchQueryVolumeInformation(device, context, DokanInstance); - break; - case IRP_MJ_LOCK_CONTROL: - DispatchLock(device, context, DokanInstance); - break; - case IRP_MJ_SET_INFORMATION: - DispatchSetInformation(device, context, DokanInstance); - break; - case IRP_MJ_FLUSH_BUFFERS: - DispatchFlush(device, context, DokanInstance); - break; - case IRP_MJ_QUERY_SECURITY: - DispatchQuerySecurity(device, context, DokanInstance); - break; - case IRP_MJ_SET_SECURITY: - DispatchSetSecurity(device, context, DokanInstance); - break; - case DOKAN_IRP_LOG_MESSAGE: - DispatchDriverLogs(device, context, DokanInstance); - default: - break; - } - - } else { - DbgPrint("ReturnedLength %d\n", returnedLength); - } - - CloseHandle(device); - } - - CloseHandle(device); - free(buffer); - _endthreadex(result); - - return result; -} - -VOID SendEventInformation(HANDLE Handle, PEVENT_INFORMATION EventInfo, - ULONG EventLength) { - BOOL status; - ULONG returnedLength; - - // DbgPrint("###EventInfo->Context %X\n", EventInfo->Context); - - // send event info to driver - status = DeviceIoControl(Handle, // Handle to device - FSCTL_EVENT_INFO, // IO Control code - EventInfo, // Input Buffer to driver. - EventLength, // Length of input buffer in bytes. - NULL, // Output Buffer from driver. - 0, // Length of output buffer in bytes. - &returnedLength, // Bytes placed in buffer. - NULL // synchronous call - ); - - if (!status) { - DWORD errorCode = GetLastError(); - DbgPrint("Dokan Error: Ioctl failed with code %d\n", errorCode); +VOID EventCompletion(PDOKAN_IO_EVENT IoEvent) { + assert(IoEvent->EventResult); + if (IoEvent->DokanOpenInfo) { + InterlockedExchange64(&IoEvent->DokanOpenInfo->UserContext, + IoEvent->DokanFileInfo.Context); + ReleaseDokanOpenInfo(IoEvent); } } @@ -589,105 +811,59 @@ ULONG DispatchGetEventInformationLength(ULONG bufferSize) { // we remote it to the struct size and add the requested buffer size // but we need at least to have enough space to set EVENT_INFORMATION return max((ULONG)sizeof(EVENT_INFORMATION), - sizeof(EVENT_INFORMATION) - 8 + bufferSize); + FIELD_OFFSET(EVENT_INFORMATION, Buffer[0]) + bufferSize); } -PEVENT_INFORMATION -DispatchCommon(PEVENT_CONTEXT EventContext, ULONG SizeOfEventInfo, - PDOKAN_INSTANCE DokanInstance, PDOKAN_FILE_INFO DokanFileInfo, - PDOKAN_OPEN_INFO *DokanOpenInfo) { - PEVENT_INFORMATION eventInfo = (PEVENT_INFORMATION)malloc(SizeOfEventInfo); - - if (eventInfo == NULL) { - return NULL; - } - RtlZeroMemory(eventInfo, SizeOfEventInfo); - RtlZeroMemory(DokanFileInfo, sizeof(DOKAN_FILE_INFO)); - - eventInfo->BufferLength = 0; - eventInfo->SerialNumber = EventContext->SerialNumber; - - DokanFileInfo->ProcessId = EventContext->ProcessId; - DokanFileInfo->DokanOptions = DokanInstance->DokanOptions; - if (EventContext->FileFlags & DOKAN_DELETE_ON_CLOSE) { - DokanFileInfo->DeleteOnClose = 1; - } - if (EventContext->FileFlags & DOKAN_PAGING_IO) { - DokanFileInfo->PagingIo = 1; - } - if (EventContext->FileFlags & DOKAN_WRITE_TO_END_OF_FILE) { - DokanFileInfo->WriteToEndOfFile = 1; - } - if (EventContext->FileFlags & DOKAN_SYNCHRONOUS_IO) { - DokanFileInfo->SynchronousIo = 1; - } - if (EventContext->FileFlags & DOKAN_NOCACHE) { - DokanFileInfo->Nocache = 1; - } +VOID CreateDispatchCommon(PDOKAN_IO_EVENT IoEvent, ULONG SizeOfEventInfo) { + assert(IoEvent != NULL); + assert(IoEvent->EventResult == NULL && IoEvent->EventResultSize == 0); - *DokanOpenInfo = GetDokanOpenInfo(EventContext, DokanInstance); - if (*DokanOpenInfo == NULL) { - DbgPrint("error openInfo is NULL\n"); - return eventInfo; + if (SizeOfEventInfo <= DOKAN_EVENT_INFO_DEFAULT_BUFFER_SIZE) { + IoEvent->EventResult = PopEventResult(); + IoEvent->EventResultSize = DOKAN_EVENT_INFO_DEFAULT_SIZE; + IoEvent->PoolAllocated = TRUE; + } else { + IoEvent->EventResultSize = + DispatchGetEventInformationLength(SizeOfEventInfo); + IoEvent->EventResult = (PEVENT_INFORMATION)malloc(IoEvent->EventResultSize); + if (!IoEvent->EventResult) { + return; + } + ZeroMemory(IoEvent->EventResult, IoEvent->EventResultSize); } + assert(IoEvent->EventResult && + IoEvent->EventResultSize >= + DispatchGetEventInformationLength(SizeOfEventInfo)); - DokanFileInfo->Context = (ULONG64)(*DokanOpenInfo)->UserContext; - DokanFileInfo->IsDirectory = (UCHAR)(*DokanOpenInfo)->IsDirectory; - DokanFileInfo->DokanContext = (ULONG64)(*DokanOpenInfo); - - eventInfo->Context = (ULONG64)(*DokanOpenInfo); - - return eventInfo; -} - -PDOKAN_OPEN_INFO -GetDokanOpenInfo(PEVENT_CONTEXT EventContext, PDOKAN_INSTANCE DokanInstance) { - PDOKAN_OPEN_INFO openInfo; - EnterCriticalSection(&DokanInstance->CriticalSection); - - openInfo = (PDOKAN_OPEN_INFO)(UINT_PTR)EventContext->Context; - if (openInfo != NULL) { - openInfo->OpenCount++; - openInfo->EventContext = EventContext; - openInfo->DokanInstance = DokanInstance; - } - LeaveCriticalSection(&DokanInstance->CriticalSection); - return openInfo; + IoEvent->EventResult->SerialNumber = IoEvent->EventContext->SerialNumber; + IoEvent->EventResult->Context = IoEvent->EventContext->Context; + IoEvent->EventResult->PullEventTimeoutMs = + IoEvent->IoBatch->MainPullThread ? /*ms*/ 100 : 0; } -VOID ReleaseDokanOpenInfo(PEVENT_INFORMATION EventInformation, - PDOKAN_FILE_INFO FileInfo, - PDOKAN_INSTANCE DokanInstance) { - PDOKAN_OPEN_INFO openInfo; +VOID ReleaseDokanOpenInfo(PDOKAN_IO_EVENT IoEvent) { LPWSTR fileNameForClose = NULL; - EnterCriticalSection(&DokanInstance->CriticalSection); - - openInfo = (PDOKAN_OPEN_INFO)(UINT_PTR)EventInformation->Context; - if (openInfo != NULL) { - openInfo->OpenCount--; - if (openInfo->OpenCount < 1) { - if (openInfo->DirListHead != NULL) { - ClearFindData(openInfo->DirListHead); - free(openInfo->DirListHead); - openInfo->DirListHead = NULL; - } - if (openInfo->StreamListHead != NULL) { - ClearFindStreamData(openInfo->StreamListHead); - free(openInfo->StreamListHead); - openInfo->StreamListHead = NULL; - } - if (openInfo->FileName) { - fileNameForClose = openInfo->FileName; + + if (IoEvent->DokanOpenInfo != NULL) { + EnterCriticalSection(&IoEvent->DokanOpenInfo->CriticalSection); + IoEvent->DokanOpenInfo->OpenCount--; + if (IoEvent->DokanOpenInfo->OpenCount < 1) { + if (IoEvent->DokanOpenInfo->FileName) { + fileNameForClose = IoEvent->DokanOpenInfo->FileName; + IoEvent->DokanOpenInfo->FileName = NULL; } - free(openInfo); - EventInformation->Context = 0; + LeaveCriticalSection(&IoEvent->DokanOpenInfo->CriticalSection); + PushFileOpenInfo(IoEvent->DokanOpenInfo); + IoEvent->DokanOpenInfo = NULL; + } else { + LeaveCriticalSection(&IoEvent->DokanOpenInfo->CriticalSection); } } - LeaveCriticalSection(&DokanInstance->CriticalSection); if (fileNameForClose) { - if (DokanInstance->DokanOperations->CloseFile) { - DokanInstance->DokanOperations->CloseFile(fileNameForClose, FileInfo); + if (IoEvent->DokanInstance->DokanOperations->CloseFile) { + IoEvent->DokanInstance->DokanOperations->CloseFile( + fileNameForClose, &IoEvent->DokanFileInfo); } free(fileNameForClose); } @@ -701,8 +877,8 @@ BOOL SendReleaseIRP(LPCWSTR DeviceName) { DbgPrintW(L"send release to %s\n", DeviceName); GetRawDeviceName(DeviceName, rawDeviceName, MAX_PATH); - if (!SendToDevice(rawDeviceName, - FSCTL_EVENT_RELEASE, NULL, 0, NULL, 0, &returnedLength)) { + if (!SendToDevice(rawDeviceName, FSCTL_EVENT_RELEASE, NULL, 0, NULL, 0, + &returnedLength)) { DbgPrintW(L"Failed to unmount device:%s\n", DeviceName); return FALSE; @@ -726,7 +902,7 @@ BOOL SendGlobalReleaseIRP(LPCWSTR MountPoint) { szMountPoint->Length = (USHORT)(length * sizeof(WCHAR)); CopyMemory(szMountPoint->Buffer, MountPoint, szMountPoint->Length); - DbgPrintW(L"send global release for %s\n", MountPoint); + DbgPrintW(L"Send global Release for %s\n", MountPoint); if (!SendToDevice(DOKAN_GLOBAL_DEVICE_NAME, FSCTL_EVENT_RELEASE, szMountPoint, inputLength, NULL, 0, @@ -755,6 +931,9 @@ BOOL DokanStart(PDOKAN_INSTANCE Instance) { ZeroMemory(&driverInfo, sizeof(EVENT_DRIVER_INFO)); eventStart.UserVersion = DOKAN_DRIVER_VERSION; + if (!Instance->DokanOptions->SingleThread) { + eventStart.Flags |= DOKAN_EVENT_ALLOW_IPC_BATCHING; + } if (Instance->DokanOptions->Options & DOKAN_OPTION_ALT_STREAM) { eventStart.Flags |= DOKAN_EVENT_ALTERNATIVE_STREAM_ON; } @@ -776,12 +955,9 @@ BOOL DokanStart(PDOKAN_INSTANCE Instance) { if (Instance->DokanOptions->Options & DOKAN_OPTION_FILELOCK_USER_MODE) { eventStart.Flags |= DOKAN_EVENT_FILELOCK_USER_MODE; } - if (Instance->DokanOptions->Options & DOKAN_OPTION_ENABLE_UNMOUNT_NETWORK_DRIVE) { - eventStart.Flags |= DOKAN_EVENT_ENABLE_NETWORK_UNMOUNT; - } if (Instance->DokanOptions->Options & - DOKAN_OPTION_ENABLE_FCB_GARBAGE_COLLECTION) { - eventStart.Flags |= DOKAN_EVENT_ENABLE_FCB_GC; + DOKAN_OPTION_ENABLE_UNMOUNT_NETWORK_DRIVE) { + eventStart.Flags |= DOKAN_EVENT_ENABLE_NETWORK_UNMOUNT; } if (Instance->DokanOptions->Options & DOKAN_OPTION_CASE_SENSITIVE) { eventStart.Flags |= DOKAN_EVENT_CASE_SENSITIVE; @@ -826,9 +1002,9 @@ BOOL DOKANAPI DokanSetDebugMode(ULONG Mode) { } BOOL DOKANAPI DokanMountPointsCleanUp() { - ULONG returnedLength; - return SendToDevice(DOKAN_GLOBAL_DEVICE_NAME, FSCTL_MOUNTPOINT_CLEANUP, NULL, - 0, NULL, 0, &returnedLength); + ULONG returnedLength; + return SendToDevice(DOKAN_GLOBAL_DEVICE_NAME, FSCTL_MOUNTPOINT_CLEANUP, NULL, + 0, NULL, 0, &returnedLength); } BOOL SendToDevice(LPCWSTR DeviceName, DWORD IoControlCode, PVOID InputBuffer, @@ -849,7 +1025,7 @@ BOOL SendToDevice(LPCWSTR DeviceName, DWORD IoControlCode, PVOID InputBuffer, if (device == INVALID_HANDLE_VALUE) { DWORD dwErrorCode = GetLastError(); DbgPrintW(L"Dokan Error: Failed to open %ws with code %d\n", DeviceName, - dwErrorCode); + dwErrorCode); return FALSE; } @@ -861,7 +1037,7 @@ BOOL SendToDevice(LPCWSTR DeviceName, DWORD IoControlCode, PVOID InputBuffer, OutputLength, // Length of output buffer in bytes. ReturnedLength, // Bytes placed in buffer. NULL // synchronous call - ); + ); CloseHandle(device); @@ -928,24 +1104,10 @@ BOOL WINAPI DllMain(HINSTANCE Instance, DWORD Reason, LPVOID Reserved) { switch (Reason) { case DLL_PROCESS_ATTACH: { - (void)InitializeCriticalSectionAndSpinCount(&g_InstanceCriticalSection, - 0x80000400); - InitializeListHead(&g_InstanceList); } break; case DLL_PROCESS_DETACH: { - EnterCriticalSection(&g_InstanceCriticalSection); - - while (!IsListEmpty(&g_InstanceList)) { - PLIST_ENTRY entry = RemoveHeadList(&g_InstanceList); - PDOKAN_INSTANCE instance = - CONTAINING_RECORD(entry, DOKAN_INSTANCE, ListEntry); - DokanRemoveMountPointEx(instance->MountPoint, FALSE); - free(instance); - } - LeaveCriticalSection(&g_InstanceCriticalSection); - DeleteCriticalSection(&g_InstanceCriticalSection); } break; default: break; @@ -953,11 +1115,12 @@ BOOL WINAPI DllMain(HINSTANCE Instance, DWORD Reason, LPVOID Reserved) { return TRUE; } -void DOKANAPI DokanMapKernelToUserCreateFileFlags( - ACCESS_MASK DesiredAccess, ULONG FileAttributes, ULONG CreateOptions, ULONG CreateDisposition, - ACCESS_MASK* outDesiredAccess, DWORD *outFileAttributesAndFlags, DWORD *outCreationDisposition) { - BOOL genericRead = FALSE, genericWrite = FALSE, genericExecute = FALSE, - genericAll = FALSE; +VOID DOKANAPI DokanMapKernelToUserCreateFileFlags( + ACCESS_MASK DesiredAccess, ULONG FileAttributes, ULONG CreateOptions, + ULONG CreateDisposition, ACCESS_MASK *outDesiredAccess, + DWORD *outFileAttributesAndFlags, DWORD *outCreationDisposition) { + BOOL genericRead = FALSE, genericWrite = FALSE, genericExecute = FALSE, + genericAll = FALSE; if (outFileAttributesAndFlags) { @@ -1014,39 +1177,94 @@ void DOKANAPI DokanMapKernelToUserCreateFileFlags( if (outDesiredAccess) { - *outDesiredAccess = DesiredAccess; - - if ((*outDesiredAccess & FILE_GENERIC_READ) == FILE_GENERIC_READ) { - *outDesiredAccess |= GENERIC_READ; - genericRead = TRUE; - } - if ((*outDesiredAccess & FILE_GENERIC_WRITE) == FILE_GENERIC_WRITE) { - *outDesiredAccess |= GENERIC_WRITE; - genericWrite = TRUE; - } - if ((*outDesiredAccess & FILE_GENERIC_EXECUTE) == FILE_GENERIC_EXECUTE) { - *outDesiredAccess |= GENERIC_EXECUTE; - genericExecute = TRUE; - } - if ((*outDesiredAccess & FILE_ALL_ACCESS) == FILE_ALL_ACCESS) { - *outDesiredAccess |= GENERIC_ALL; - genericAll = TRUE; - } - - if (genericRead) - *outDesiredAccess &= ~FILE_GENERIC_READ; - if (genericWrite) - *outDesiredAccess &= ~FILE_GENERIC_WRITE; - if (genericExecute) - *outDesiredAccess &= ~FILE_GENERIC_EXECUTE; - if (genericAll) - *outDesiredAccess &= ~FILE_ALL_ACCESS; + *outDesiredAccess = DesiredAccess; + + if ((*outDesiredAccess & FILE_GENERIC_READ) == FILE_GENERIC_READ) { + *outDesiredAccess |= GENERIC_READ; + genericRead = TRUE; + } + if ((*outDesiredAccess & FILE_GENERIC_WRITE) == FILE_GENERIC_WRITE) { + *outDesiredAccess |= GENERIC_WRITE; + genericWrite = TRUE; + } + if ((*outDesiredAccess & FILE_GENERIC_EXECUTE) == FILE_GENERIC_EXECUTE) { + *outDesiredAccess |= GENERIC_EXECUTE; + genericExecute = TRUE; + } + if ((*outDesiredAccess & FILE_ALL_ACCESS) == FILE_ALL_ACCESS) { + *outDesiredAccess |= GENERIC_ALL; + genericAll = TRUE; + } + + if (genericRead) + *outDesiredAccess &= ~FILE_GENERIC_READ; + if (genericWrite) + *outDesiredAccess &= ~FILE_GENERIC_WRITE; + if (genericExecute) + *outDesiredAccess &= ~FILE_GENERIC_EXECUTE; + if (genericAll) + *outDesiredAccess &= ~FILE_ALL_ACCESS; + } +} + +VOID DOKANAPI DokanInit() { + // ensure 64-bit alignment + assert(FIELD_OFFSET(EVENT_INFORMATION, Buffer) % 8 == 0); + + // this is not as safe as a critical section so to some degree we rely on + // the user to do the right thing + LONG initRefCount = InterlockedIncrement(&g_DokanInitialized); + if (initRefCount <= 0) { + RaiseException(DOKAN_EXCEPTION_INITIALIZATION_FAILED, + EXCEPTION_NONCONTINUABLE, 0, NULL); + return; + } + if (initRefCount > 1) { + return; + } + + (void)InitializeCriticalSectionAndSpinCount(&g_InstanceCriticalSection, + 0x80000400); + + InitializeListHead(&g_InstanceList); + EnterCriticalSection(&g_InstanceCriticalSection); + { InitializePool(); } + LeaveCriticalSection(&g_InstanceCriticalSection); +} + +VOID DOKANAPI DokanShutdown() { + LONG initRefCount = InterlockedDecrement(&g_DokanInitialized); + if (initRefCount < 0) { + RaiseException(DOKAN_EXCEPTION_SHUTDOWN_FAILED, EXCEPTION_NONCONTINUABLE, 0, + NULL); + return; + } + if (initRefCount > 0) { + return; + } + + EnterCriticalSection(&g_InstanceCriticalSection); + { + while (!IsListEmpty(&g_InstanceList)) { + PLIST_ENTRY entry = RemoveHeadList(&g_InstanceList); + PDOKAN_INSTANCE instance = + CONTAINING_RECORD(entry, DOKAN_INSTANCE, ListEntry); + DokanCloseHandle((DOKAN_HANDLE)instance); + } + CleanupPool(); } + LeaveCriticalSection(&g_InstanceCriticalSection); + DeleteCriticalSection(&g_InstanceCriticalSection); } -BOOL DOKANAPI DokanNotifyPath(LPCWSTR FilePath, ULONG CompletionFilter, - ULONG Action) { - if (FilePath == NULL || g_notify_handle == INVALID_HANDLE_VALUE) { +BOOL DOKANAPI DokanNotifyPath(_In_ DOKAN_HANDLE DokanInstance, + _In_ LPCWSTR FilePath, + _In_ ULONG CompletionFilter, _In_ ULONG Action) { + DOKAN_INSTANCE *instance = (DOKAN_INSTANCE *)DokanInstance; + if (!instance) { + return FALSE; + } + if (FilePath == NULL || !instance || !instance->NotifyHandle) { return FALSE; } size_t length = wcslen(FilePath); @@ -1057,8 +1275,8 @@ BOOL DOKANAPI DokanNotifyPath(LPCWSTR FilePath, ULONG CompletionFilter, // remove the mount letter and colon from length, for example: "G:" length -= prefixSize; ULONG returnedLength; - ULONG inputLength = (ULONG)( - sizeof(DOKAN_NOTIFY_PATH_INTERMEDIATE) + (length * sizeof(WCHAR))); + ULONG inputLength = (ULONG)(sizeof(DOKAN_NOTIFY_PATH_INTERMEDIATE) + + (length * sizeof(WCHAR))); PDOKAN_NOTIFY_PATH_INTERMEDIATE pNotifyPath = malloc(inputLength); if (pNotifyPath == NULL) { DbgPrint("Failed to allocate NotifyPath\n"); @@ -1069,7 +1287,7 @@ BOOL DOKANAPI DokanNotifyPath(LPCWSTR FilePath, ULONG CompletionFilter, pNotifyPath->Action = Action; pNotifyPath->Length = (USHORT)(length * sizeof(WCHAR)); CopyMemory(pNotifyPath->Buffer, FilePath + prefixSize, pNotifyPath->Length); - if (!DeviceIoControl(g_notify_handle, FSCTL_NOTIFY_PATH, pNotifyPath, + if (!DeviceIoControl(instance->NotifyHandle, FSCTL_NOTIFY_PATH, pNotifyPath, inputLength, NULL, 0, &returnedLength, NULL)) { DbgPrint("Failed to send notify path command:%ws\n", FilePath); free(pNotifyPath); @@ -1079,38 +1297,44 @@ BOOL DOKANAPI DokanNotifyPath(LPCWSTR FilePath, ULONG CompletionFilter, return TRUE; } -BOOL DOKANAPI DokanNotifyCreate(LPCWSTR FilePath, BOOL IsDirectory) { - return DokanNotifyPath(FilePath, +BOOL DOKANAPI DokanNotifyCreate(_In_ DOKAN_HANDLE DokanInstance, + _In_ LPCWSTR FilePath, _In_ BOOL IsDirectory) { + return DokanNotifyPath(DokanInstance, FilePath, IsDirectory ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED); } -BOOL DOKANAPI DokanNotifyDelete(LPCWSTR FilePath, BOOL IsDirectory) { - return DokanNotifyPath(FilePath, +BOOL DOKANAPI DokanNotifyDelete(_In_ DOKAN_HANDLE DokanInstance, + _In_ LPCWSTR FilePath, _In_ BOOL IsDirectory) { + return DokanNotifyPath(DokanInstance, FilePath, IsDirectory ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_REMOVED); } -BOOL DOKANAPI DokanNotifyUpdate(LPCWSTR FilePath) { - return DokanNotifyPath(FilePath, FILE_NOTIFY_CHANGE_ATTRIBUTES, +BOOL DOKANAPI DokanNotifyUpdate(_In_ DOKAN_HANDLE DokanInstance, + _In_ LPCWSTR FilePath) { + return DokanNotifyPath(DokanInstance, FilePath, FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED); } -BOOL DOKANAPI DokanNotifyXAttrUpdate(LPCWSTR FilePath) { - return DokanNotifyPath(FilePath, FILE_NOTIFY_CHANGE_ATTRIBUTES, +BOOL DOKANAPI DokanNotifyXAttrUpdate(_In_ DOKAN_HANDLE DokanInstance, + _In_ LPCWSTR FilePath) { + return DokanNotifyPath(DokanInstance, FilePath, FILE_NOTIFY_CHANGE_ATTRIBUTES, FILE_ACTION_MODIFIED); } -BOOL DOKANAPI DokanNotifyRename(LPCWSTR OldPath, LPCWSTR NewPath, - BOOL IsDirectory, BOOL IsInSameDirectory) { +BOOL DOKANAPI DokanNotifyRename(_In_ DOKAN_HANDLE DokanInstance, + _In_ LPCWSTR OldPath, _In_ LPCWSTR NewPath, + _In_ BOOL IsDirectory, + _In_ BOOL IsInSameDirectory) { BOOL success = DokanNotifyPath( - OldPath, + DokanInstance, OldPath, IsDirectory ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, IsInSameDirectory ? FILE_ACTION_RENAMED_OLD_NAME : FILE_ACTION_REMOVED); success &= DokanNotifyPath( - NewPath, + DokanInstance, NewPath, IsDirectory ? FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME, IsInSameDirectory ? FILE_ACTION_RENAMED_NEW_NAME : FILE_ACTION_ADDED); return success; diff --git a/dokan/dokan.def b/dokan/dokan.def index 8ee6fca7f..360c63cb8 100644 --- a/dokan/dokan.def +++ b/dokan/dokan.def @@ -27,3 +27,5 @@ DokanNotifyDelete DokanNotifyUpdate DokanNotifyXAttrUpdate DokanNotifyRename +DokanInit +DokanShutdown diff --git a/dokan/dokan.h b/dokan/dokan.h index 2ae6e0586..b51a8bcad 100644 --- a/dokan/dokan.h +++ b/dokan/dokan.h @@ -56,8 +56,8 @@ extern "C" { /** @{ */ /** The current Dokan version (140 means ver 1.4.0). \ref DOKAN_OPTIONS.Version */ -#define DOKAN_VERSION 151 -/** Minimum Dokan version (ver 1.1.0) accepted. */ +#define DOKAN_VERSION 160 +/** Minimum Dokan version (ver 2.0.0) accepted. */ #define DOKAN_MINIMUM_COMPATIBLE_VERSION 110 /** Driver file name including the DOKAN_MAJOR_API_VERSION */ #define DOKAN_DRIVER_NAME L"dokan" DOKAN_MAJOR_API_VERSION L".sys" @@ -76,60 +76,49 @@ extern "C" { /** Enable ouput debug message */ #define DOKAN_OPTION_DEBUG 1 /** Enable ouput debug message to stderr */ -#define DOKAN_OPTION_STDERR 2 +#define DOKAN_OPTION_STDERR (1 << 1) /** * Enable the use of alternate stream paths in the form * :. If this is not specified then the driver will * fail any attempt to access a path with a colon. */ -#define DOKAN_OPTION_ALT_STREAM 4 +#define DOKAN_OPTION_ALT_STREAM (1 << 2) /** Enable mount drive as write-protected */ -#define DOKAN_OPTION_WRITE_PROTECT 8 +#define DOKAN_OPTION_WRITE_PROTECT (1 << 3) /** Use network drive - Dokan network provider needs to be installed */ -#define DOKAN_OPTION_NETWORK 16 +#define DOKAN_OPTION_NETWORK (1 << 4) /** * Use removable drive * Be aware that on some environments, the userland application will be denied * to communicate with the drive which will result in a unwanted unmount. * \see Issue #843 */ -#define DOKAN_OPTION_REMOVABLE 32 +#define DOKAN_OPTION_REMOVABLE (1 << 5) /** Use mount manager */ -#define DOKAN_OPTION_MOUNT_MANAGER 64 +#define DOKAN_OPTION_MOUNT_MANAGER (1 << 6) /** Mount the drive on current session only */ -#define DOKAN_OPTION_CURRENT_SESSION 128 +#define DOKAN_OPTION_CURRENT_SESSION (1 << 7) /** Enable Lockfile/Unlockfile operations. Otherwise Dokan will take care of it */ -#define DOKAN_OPTION_FILELOCK_USER_MODE 256 -/** - * Whether DokanNotifyXXX functions should be enabled, which requires this - * library to maintain a special handle while the file system is mounted. - * Without this flag, the functions always return FALSE if invoked. - */ -#define DOKAN_OPTION_ENABLE_NOTIFICATION_API 512 -/** - * The advantage of the FCB GC approach is that it prevents filter drivers (Anti-virus) - * from exponentially slowing down procedures like zip file extraction due to - * repeatedly rebuilding state that they attach to the FCB header. - */ -#define DOKAN_OPTION_ENABLE_FCB_GARBAGE_COLLECTION 2048 +#define DOKAN_OPTION_FILELOCK_USER_MODE (1 << 8) /** * Enable Case sensitive path. * By default all path are case insensitive. * For case sensitive: \dir\File & \diR\file are different files * but for case insensitive they are the same. */ -#define DOKAN_OPTION_CASE_SENSITIVE 4096 +#define DOKAN_OPTION_CASE_SENSITIVE (1 << 9) /** Allows unmounting of network drive via explorer */ -#define DOKAN_OPTION_ENABLE_UNMOUNT_NETWORK_DRIVE 8192 +#define DOKAN_OPTION_ENABLE_UNMOUNT_NETWORK_DRIVE (1 << 10) /** * Forward the kernel driver global and volume logs to the userland. - * - * This option is expected to be slow until IpcBatching is available on v2.x.x + * Can be very slow if single thread is enabled. */ -#define DOKAN_OPTION_DISPATCH_DRIVER_LOGS 16384 +#define DOKAN_OPTION_DISPATCH_DRIVER_LOGS (1 << 11) /** @} */ +typedef VOID *DOKAN_HANDLE, **PDOKAN_HANDLE; + /** * \struct DOKAN_OPTIONS * \brief Dokan mount options used to describe Dokan device behavior. @@ -138,8 +127,8 @@ extern "C" { typedef struct _DOKAN_OPTIONS { /** Version of the Dokan features requested without dots (version "123" is equal to Dokan version 1.2.3). */ USHORT Version; - /** Number of threads to be used by Dokan library internally. More threads will handle more events at the same time. */ - USHORT ThreadCount; + /** Only use a single thread to process events. This is highly not recommended as can easily create a bottleneck. */ + BOOLEAN SingleThread; /** Features enabled for the mount. See \ref DOKAN_OPTION. */ ULONG Options; /** FileSystem can store anything here. */ @@ -177,7 +166,7 @@ typedef struct _DOKAN_FILE_INFO { ULONG64 Context; /** Reserved. Used internally by Dokan library. Never modify. */ ULONG64 DokanContext; - /** A pointer to DOKAN_OPTIONS which was passed to DokanMain. */ + /** A pointer to DOKAN_OPTIONS which was passed to \ref DokanMain or \ref DokanCreateFileSystem. */ PDOKAN_OPTIONS DokanOptions; /** * Process ID for the thread that originally requested a given I/O operation. @@ -198,8 +187,18 @@ typedef struct _DOKAN_FILE_INFO { UCHAR Nocache; /** If \c TRUE, write to the current end of file instead of using the Offset parameter. */ UCHAR WriteToEndOfFile; + /** + * Reserved. Used internally by Dokan library. Never modify. + * If the processing for the event requires extra data to be associated with it + * then a pointer to that data can be placed here + */ + PVOID ProcessingContext; } DOKAN_FILE_INFO, *PDOKAN_FILE_INFO; +#define DOKAN_EXCEPTION_NOT_INITIALIZED 0x0f0ff0ff +#define DOKAN_EXCEPTION_INITIALIZATION_FAILED 0x0fbadbad +#define DOKAN_EXCEPTION_SHUTDOWN_FAILED 0x0fbadf00 + /** * \brief FillFindData Used to add an entry in FindFiles operation * \return 1 if buffer is full, otherwise 0 (currently it never returns 1) @@ -208,10 +207,9 @@ typedef int(WINAPI *PFillFindData)(PWIN32_FIND_DATAW, PDOKAN_FILE_INFO); /** * \brief FillFindStreamData Used to add an entry in FindStreams - * \return 1 if buffer is full, otherwise 0 (currently it never returns 1) + * \return FALSE if buffer is full, otherwise TRUE */ -typedef int(WINAPI *PFillFindStreamData)(PWIN32_FIND_STREAM_DATA, - PDOKAN_FILE_INFO); +typedef BOOL(WINAPI *PFillFindStreamData)(PWIN32_FIND_STREAM_DATA, PVOID); // clang-format off @@ -725,11 +723,13 @@ typedef struct _DOKAN_OPERATIONS { * \since Supported since version 0.8.0. The version must be specified in \ref DOKAN_OPTIONS.Version. * \param FileName File path requested by the Kernel on the FileSystem. * \param FillFindStreamData Callback that has to be called with PWIN32_FIND_STREAM_DATA that contain stream information. + * \param FindStreamContext Context for the event to pass to the callback FillFindStreamData. * \param DokanFileInfo Information about the file or directory. * \return \c STATUS_SUCCESS on success or NTSTATUS appropriate to the request result. */ NTSTATUS(DOKAN_CALLBACK *FindStreams)(LPCWSTR FileName, PFillFindStreamData FillFindStreamData, + PVOID FindStreamContext, PDOKAN_FILE_INFO DokanFileInfo); } DOKAN_OPERATIONS, *PDOKAN_OPERATIONS; @@ -738,7 +738,7 @@ typedef struct _DOKAN_OPERATIONS { /** * \defgroup DokanMainResult DokanMainResult - * \brief \ref DokanMain returns error codes + * \brief \ref DokanMain \ref DokanCreateFileSystem returns error codes */ /** @{ */ @@ -776,11 +776,30 @@ typedef struct _DOKAN_OPERATIONS { */ /** @{ */ +/** + * \brief Initialize all required Dokan internal resources. + * + * This needs to be called only once before trying to use \ref DokanCreateFileSystem for the first time. + * Otherwise \ref DokanCreateFileSystem will just fail. + */ +VOID DOKANAPI DokanInit(); + +/** + * \brief Release all allocated resources by \ref DokanInit + * + * This should be called when the application no longer expects to create a new FileSystem with \ref DokanCreateFileSystem and after all devices are unmount. + * + * \return \ref DokanMainResult status. + */ +VOID DOKANAPI DokanShutdown(); + /** * \brief Mount a new Dokan Volume. * * This function block until the device is unmounted. * If the mount fails, it will directly return a \ref DokanMainResult error. + * + * See \ref DokanCreateFileSystem to create mount Dokan Volume asynchronously. * * \param DokanOptions a \ref DOKAN_OPTIONS that describe the mount. * \param DokanOperations Instance of \ref DOKAN_OPERATIONS that will be called for each request made by the kernel. @@ -789,6 +808,52 @@ typedef struct _DOKAN_OPERATIONS { int DOKANAPI DokanMain(PDOKAN_OPTIONS DokanOptions, PDOKAN_OPERATIONS DokanOperations); +/** + * \brief Mount a new Dokan Volume. + * + * It is mandatory to have called \ref DokanInit previously to use this API. + * + * This function returns directly on device mount or on failure. + * See \ref DokanMainResult for possible errors. + * + * \ref DokanWaitForFileSystemClosed can be used to wait until the device is unmount. + * + * \param DokanOptions a \ref DOKAN_OPTIONS that describe the mount. + * \param DokanOperations Instance of \ref DOKAN_OPERATIONS that will be called for each request made by the kernel. + * \param DokanInstance Dokan mount instance context that can be used for related instance calls like \ref DokanIsFileSystemRunning . + * \return \ref DokanMainResult status. + */ +int DOKANAPI DokanCreateFileSystem( + _In_ PDOKAN_OPTIONS DokanOptions, + _In_ PDOKAN_OPERATIONS DokanOperations, + _Out_ DOKAN_HANDLE *DokanInstance); + +/** + * \brief Check if the FileSystem is still running or not. + * + * \param DokanInstance The dokan mount context created by \ref DokanCreateFileSystem . + * \return Whether the FileSystem is still running or not. + */ +BOOL DOKANAPI DokanIsFileSystemRunning(_In_ DOKAN_HANDLE DokanInstance); + +/** + * \brief Wait until the FileSystem is unmount. + * + * \param DokanInstance The dokan mount context created by \ref DokanCreateFileSystem . + * \return See WaitForSingleObject for a description of return values. + */ +DWORD DOKANAPI DokanWaitForFileSystemClosed(_In_ DOKAN_HANDLE DokanInstance, + _In_ DWORD dwMilliseconds); + +/** + * \brief Unmount the Dokan instance. + * + * Unmount and wait until all resources of the \c DokanInstance are released. + * + * \param DokanInstance The dokan mount context created by \ref DokanCreateFileSystem . + */ +VOID DOKANAPI DokanCloseHandle(_In_ DOKAN_HANDLE DokanInstance); + /** * \brief Unmount a Dokan device from a driver letter. * @@ -852,7 +917,7 @@ BOOL DOKANAPI DokanResetTimeout(ULONG Timeout, PDOKAN_FILE_INFO DokanFileInfo); /** * \brief Get the handle to Access Token. * - * This method needs be called in CreateFile callback. + * This method needs be called in \ref DOKAN_OPERATIONS.ZwCreateFile callback. * The caller must call CloseHandle * for the returned handle. * @@ -899,7 +964,7 @@ VOID DOKANAPI DokanReleaseMountPointList(PDOKAN_CONTROL list); * \param outCreationDisposition New CreateFile dwCreationDisposition. * \see CreateFile function (MSDN) */ -void DOKANAPI DokanMapKernelToUserCreateFileFlags( +VOID DOKANAPI DokanMapKernelToUserCreateFileFlags( ACCESS_MASK DesiredAccess, ULONG FileAttributes, ULONG CreateOptions, ULONG CreateDisposition, ACCESS_MASK *outDesiredAccess, DWORD *outFileAttributesAndFlags, DWORD *outCreationDisposition); @@ -927,49 +992,60 @@ void DOKANAPI DokanMapKernelToUserCreateFileFlags( /** * \brief Notify dokan that a file or a directory has been created. * + * \param DokanInstance The dokan mount context created by \ref DokanCreateFileSystem . * \param FilePath Absolute path to the file or directory, including the mount-point of the file system. * \param IsDirectory Indicates if the path is a directory. * \return \c TRUE if notification succeeded. */ -BOOL DOKANAPI DokanNotifyCreate(LPCWSTR FilePath, BOOL IsDirectory); +BOOL DOKANAPI DokanNotifyCreate(_In_ DOKAN_HANDLE DokanInstance, + _In_ LPCWSTR FilePath, _In_ BOOL IsDirectory); /** * \brief Notify dokan that a file or a directory has been deleted. * + * \param DokanInstance The dokan mount context created by \ref DokanCreateFileSystem . * \param FilePath Absolute path to the file or directory, including the mount-point of the file system. * \param IsDirectory Indicates if the path was a directory. * \return \c TRUE if notification succeeded. */ -BOOL DOKANAPI DokanNotifyDelete(LPCWSTR FilePath, BOOL IsDirectory); +BOOL DOKANAPI DokanNotifyDelete(_In_ DOKAN_HANDLE DokanInstance, + _In_ LPCWSTR FilePath, _In_ BOOL IsDirectory); /** * \brief Notify dokan that file or directory attributes have changed. * + * \param DokanInstance The dokan mount context created by \ref DokanCreateFileSystem . * \param FilePath Absolute path to the file or directory, including the mount-point of the file system. * \return \c TRUE if notification succeeded. */ -BOOL DOKANAPI DokanNotifyUpdate(LPCWSTR FilePath); +BOOL DOKANAPI DokanNotifyUpdate(_In_ DOKAN_HANDLE DokanInstance, + _In_ LPCWSTR FilePath); /** * \brief Notify dokan that file or directory extended attributes have changed. * + * \param DokanInstance The dokan mount context created by \ref DokanCreateFileSystem . * \param FilePath Absolute path to the file or directory, including the mount-point of the file system. * \return \c TRUE if notification succeeded. */ -BOOL DOKANAPI DokanNotifyXAttrUpdate(LPCWSTR FilePath); +BOOL DOKANAPI DokanNotifyXAttrUpdate(_In_ DOKAN_HANDLE DokanInstance, + _In_ LPCWSTR FilePath); /** * \brief Notify dokan that a file or a directory has been renamed. This method * supports in-place rename for file/directory within the same parent. * + * \param DokanInstance The dokan mount context created by \ref DokanCreateFileSystem . * \param OldPath Old, absolute path to the file or directory, including the mount-point of the file system. * \param NewPath New, absolute path to the file or directory, including the mount-point of the file system. * \param IsDirectory Indicates if the path is a directory. * \param IsInSameDirectory Indicates if the file or directory have the same parent directory. * \return \c TRUE if notification succeeded. */ -BOOL DOKANAPI DokanNotifyRename(LPCWSTR OldPath, LPCWSTR NewPath, - BOOL IsDirectory, BOOL IsInSameDirectory); +BOOL DOKANAPI DokanNotifyRename(_In_ DOKAN_HANDLE DokanInstance, + _In_ LPCWSTR OldPath, _In_ LPCWSTR NewPath, + _In_ BOOL IsDirectory, + _In_ BOOL IsInSameDirectory); /**@}*/ diff --git a/dokan/dokan.vcxproj b/dokan/dokan.vcxproj index 78d24df5f..3e7c464dd 100644 --- a/dokan/dokan.vcxproj +++ b/dokan/dokan.vcxproj @@ -350,6 +350,8 @@ + + @@ -367,6 +369,8 @@ + + diff --git a/dokan/dokan_pool.c b/dokan/dokan_pool.c new file mode 100644 index 000000000..722d05332 --- /dev/null +++ b/dokan/dokan_pool.c @@ -0,0 +1,424 @@ +/* + Dokan : user-mode file system library for Windows + + Copyright (C) 2016 Keith Newton + Copyright (C) 2021 Google, Inc. + + http://dokan-dev.github.io + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the Free +Software Foundation; either version 3 of the License, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program. If not, see . +*/ + +#include "dokan_pool.h" +#include "dokan_vector.h" + +#include +#include + +#define DOKAN_IO_BATCH_POOL_SIZE 1024 +#define DOKAN_IO_EVENT_POOL_SIZE 1024 +#define DOKAN_DIRECTORY_LIST_POOL_SIZE 128 + +// Global thread pool +PTP_POOL g_ThreadPool = NULL; + +// Global vector of event buffers +PDOKAN_VECTOR g_IoBatchBufferPool = NULL; +CRITICAL_SECTION g_IoBatchBufferCriticalSection; + +PDOKAN_VECTOR g_IoEventBufferPool = NULL; +CRITICAL_SECTION g_IoEventBufferCriticalSection; + +PDOKAN_VECTOR g_EventResultPool = NULL; +CRITICAL_SECTION g_EventResultCriticalSection; + +PDOKAN_VECTOR g_FileInfoPool = NULL; +CRITICAL_SECTION g_FileInfoCriticalSection; + +PDOKAN_VECTOR g_DirectoryListPool = NULL; +CRITICAL_SECTION g_DirectoryListCriticalSection; + +PTP_POOL GetThreadPool() { return g_ThreadPool; } + +VOID FreeIoEventBuffer(PDOKAN_IO_EVENT IoEvent) { + if (IoEvent) { + free(IoEvent); + } +} + +int InitializePool() { + (void)InitializeCriticalSectionAndSpinCount(&g_IoBatchBufferCriticalSection, + 0x80000400); + (void)InitializeCriticalSectionAndSpinCount(&g_IoEventBufferCriticalSection, + 0x80000400); + (void)InitializeCriticalSectionAndSpinCount(&g_EventResultCriticalSection, + 0x80000400); + (void)InitializeCriticalSectionAndSpinCount(&g_FileInfoCriticalSection, + 0x80000400); + (void)InitializeCriticalSectionAndSpinCount(&g_DirectoryListCriticalSection, + 0x80000400); + + if (g_ThreadPool) { + DokanDbgPrint("Dokan Error: Thread pool has already been created.\n"); + return DOKAN_DRIVER_INSTALL_ERROR; + } + + // It seems this is only needed if LoadLibrary() and FreeLibrary() are used and it should be called by the exe + // SetThreadpoolCallbackLibrary(&g_ThreadPoolCallbackEnvironment, hModule); + g_ThreadPool = CreateThreadpool(NULL); + if (!g_ThreadPool) { + DokanDbgPrint("Dokan Error: Failed to create thread pool.\n"); + return DOKAN_DRIVER_INSTALL_ERROR; + } + + g_IoBatchBufferPool = + DokanVector_AllocWithCapacity(sizeof(PVOID), DOKAN_IO_BATCH_POOL_SIZE); + g_IoEventBufferPool = + DokanVector_AllocWithCapacity(sizeof(PVOID), DOKAN_IO_EVENT_POOL_SIZE); + g_EventResultPool = + DokanVector_AllocWithCapacity(sizeof(PVOID), DOKAN_IO_EVENT_POOL_SIZE); + g_FileInfoPool = + DokanVector_AllocWithCapacity(sizeof(PVOID), DOKAN_IO_EVENT_POOL_SIZE); + g_DirectoryListPool = DokanVector_AllocWithCapacity( + sizeof(PVOID), DOKAN_DIRECTORY_LIST_POOL_SIZE); + return DOKAN_SUCCESS; +} + +VOID CleanupPool() { + if (g_ThreadPool) { + CloseThreadpool(g_ThreadPool); + g_ThreadPool = NULL; + } + //////////////////// IO batch buffer object pool //////////////////// + { + EnterCriticalSection(&g_IoBatchBufferCriticalSection); + { + for (size_t i = 0; i < DokanVector_GetCount(g_IoBatchBufferPool); ++i) { + FreeIoBatchBuffer( + *(PDOKAN_IO_BATCH *)DokanVector_GetItem(g_IoBatchBufferPool, i)); + } + DokanVector_Free(g_IoBatchBufferPool); + g_IoBatchBufferPool = NULL; + } + LeaveCriticalSection(&g_IoBatchBufferCriticalSection); + DeleteCriticalSection(&g_IoBatchBufferCriticalSection); + } + + //////////////////// IO event buffer object pool //////////////////// + { + EnterCriticalSection(&g_IoEventBufferCriticalSection); + { + for (size_t i = 0; i < DokanVector_GetCount(g_IoEventBufferPool); ++i) { + FreeIoEventBuffer( + *(PDOKAN_IO_EVENT *)DokanVector_GetItem(g_IoEventBufferPool, i)); + } + DokanVector_Free(g_IoEventBufferPool); + g_IoEventBufferPool = NULL; + } + LeaveCriticalSection(&g_IoEventBufferCriticalSection); + DeleteCriticalSection(&g_IoEventBufferCriticalSection); + } + + //////////////////// Event result object pool //////////////////// + { + EnterCriticalSection(&g_EventResultCriticalSection); + { + for (size_t i = 0; i < DokanVector_GetCount(g_EventResultPool); ++i) { + FreeEventResult( + *(PEVENT_INFORMATION *)DokanVector_GetItem(g_EventResultPool, i)); + } + DokanVector_Free(g_EventResultPool); + g_EventResultPool = NULL; + } + LeaveCriticalSection(&g_EventResultCriticalSection); + DeleteCriticalSection(&g_EventResultCriticalSection); + } + + //////////////////// File info object pool //////////////////// + { + EnterCriticalSection(&g_FileInfoCriticalSection); + { + for (size_t i = 0; i < DokanVector_GetCount(g_FileInfoPool); ++i) { + FreeFileOpenInfo( + *(PDOKAN_OPEN_INFO *)DokanVector_GetItem(g_FileInfoPool, i)); + } + DokanVector_Free(g_FileInfoPool); + g_FileInfoPool = NULL; + } + LeaveCriticalSection(&g_FileInfoCriticalSection); + DeleteCriticalSection(&g_FileInfoCriticalSection); + } + + //////////////////// Directory list pool //////////////////// + { + EnterCriticalSection(&g_DirectoryListCriticalSection); + { + for (size_t i = 0; i < DokanVector_GetCount(g_DirectoryListPool); ++i) { + DokanVector_Free( + *(PDOKAN_VECTOR *)DokanVector_GetItem(g_DirectoryListPool, i)); + } + DokanVector_Free(g_DirectoryListPool); + g_DirectoryListPool = NULL; + } + LeaveCriticalSection(&g_DirectoryListCriticalSection); + DeleteCriticalSection(&g_DirectoryListCriticalSection); + } + + //////////////////// Object pool cleanup finished //////////////////// +} + +/////////////////// DOKAN_IO_BATCH /////////////////// +PDOKAN_IO_BATCH PopIoBatchBuffer() { + PDOKAN_IO_BATCH ioBatch = NULL; + EnterCriticalSection(&g_IoEventBufferCriticalSection); + { + if (DokanVector_GetCount(g_IoBatchBufferPool) > 0) { + ioBatch = + *(PDOKAN_IO_BATCH *)DokanVector_GetLastItem(g_IoBatchBufferPool); + DokanVector_PopBack(g_IoBatchBufferPool); + } + } + LeaveCriticalSection(&g_IoEventBufferCriticalSection); + if (!ioBatch) { + ioBatch = (PDOKAN_IO_BATCH)malloc(DOKAN_IO_BATCH_SIZE); + } + if (ioBatch) { + RtlZeroMemory(ioBatch, DOKAN_IO_BATCH_SIZE); + ioBatch->PoolAllocated = TRUE; + } + return ioBatch; +} + +VOID FreeIoBatchBuffer(PDOKAN_IO_BATCH IoBatch) { + if (IoBatch) { + free(IoBatch); + } +} + +VOID PushIoBatchBuffer(PDOKAN_IO_BATCH IoBatch) { + assert(IoBatch); + LONG currentEventContextBatchCount = + InterlockedDecrement(&IoBatch->EventContextBatchCount); + if (currentEventContextBatchCount > 0) { + return; + } + if (!IoBatch->PoolAllocated) { + FreeIoBatchBuffer(IoBatch); + return; + } + EnterCriticalSection(&g_IoEventBufferCriticalSection); + { + if (DokanVector_GetCount(g_IoBatchBufferPool) < DOKAN_IO_BATCH_POOL_SIZE) { + DokanVector_PushBack(g_IoBatchBufferPool, &IoBatch); + IoBatch = NULL; + } + } + LeaveCriticalSection(&g_IoEventBufferCriticalSection); + if (IoBatch) { + FreeIoBatchBuffer(IoBatch); + } +} + +/////////////////// DOKAN_IO_EVENT /////////////////// +PDOKAN_IO_EVENT PopIoEventBuffer() { + PDOKAN_IO_EVENT ioEvent = NULL; + EnterCriticalSection(&g_IoEventBufferCriticalSection); + { + if (DokanVector_GetCount(g_IoEventBufferPool) > 0) { + ioEvent = + *(PDOKAN_IO_EVENT *)DokanVector_GetLastItem(g_IoEventBufferPool); + DokanVector_PopBack(g_IoEventBufferPool); + } + } + LeaveCriticalSection(&g_IoEventBufferCriticalSection); + if (!ioEvent) { + ioEvent = (PDOKAN_IO_EVENT)malloc(sizeof(DOKAN_IO_EVENT)); + } + if (ioEvent) { + RtlZeroMemory(ioEvent, sizeof(DOKAN_IO_EVENT)); + } + return ioEvent; +} + +VOID PushIoEventBuffer(PDOKAN_IO_EVENT IoEvent) { + assert(IoEvent); + EnterCriticalSection(&g_IoEventBufferCriticalSection); + { + if (DokanVector_GetCount(g_IoEventBufferPool) < DOKAN_IO_EVENT_POOL_SIZE) { + DokanVector_PushBack(g_IoEventBufferPool, &IoEvent); + IoEvent = NULL; + } + } + LeaveCriticalSection(&g_IoEventBufferCriticalSection); + if (IoEvent) { + FreeIoEventBuffer(IoEvent); + } +} + +/////////////////// EVENT_INFORMATION /////////////////// +PEVENT_INFORMATION PopEventResult() { + PEVENT_INFORMATION eventResult = NULL; + EnterCriticalSection(&g_EventResultCriticalSection); + { + if (DokanVector_GetCount(g_EventResultPool) > 0) { + eventResult = + *(PEVENT_INFORMATION *)DokanVector_GetLastItem(g_EventResultPool); + DokanVector_PopBack(g_EventResultPool); + } + } + LeaveCriticalSection(&g_EventResultCriticalSection); + if (!eventResult) { + eventResult = (PEVENT_INFORMATION)malloc(DOKAN_EVENT_INFO_DEFAULT_SIZE); + } + if (eventResult) { + RtlZeroMemory(eventResult, DOKAN_EVENT_INFO_DEFAULT_SIZE); + } + return eventResult; +} + +VOID FreeEventResult(PEVENT_INFORMATION EventResult) { + if (EventResult) { + free(EventResult); + } +} + +VOID PushEventResult(PEVENT_INFORMATION EventResult) { + assert(EventResult); + EnterCriticalSection(&g_EventResultCriticalSection); + { + if (DokanVector_GetCount(g_EventResultPool) < DOKAN_IO_EVENT_POOL_SIZE) { + DokanVector_PushBack(g_EventResultPool, &EventResult); + EventResult = NULL; + } + } + LeaveCriticalSection(&g_EventResultCriticalSection); + if (EventResult) { + FreeEventResult(EventResult); + } +} + +/////////////////// DOKAN_OPEN_INFO /////////////////// +PDOKAN_OPEN_INFO PopFileOpenInfo() { + PDOKAN_OPEN_INFO fileInfo = NULL; + EnterCriticalSection(&g_FileInfoCriticalSection); + { + if (DokanVector_GetCount(g_FileInfoPool) > 0) { + fileInfo = *(PDOKAN_OPEN_INFO *)DokanVector_GetLastItem(g_FileInfoPool); + DokanVector_PopBack(g_FileInfoPool); + } + } + LeaveCriticalSection(&g_FileInfoCriticalSection); + if (!fileInfo) { + fileInfo = (PDOKAN_OPEN_INFO)malloc(sizeof(DOKAN_OPEN_INFO)); + if (!fileInfo) { + DokanDbgPrint("Dokan Error: Failed to allocate DOKAN_OPEN_INFO.\n"); + return NULL; + } + RtlZeroMemory(fileInfo, sizeof(DOKAN_OPEN_INFO)); + InitializeCriticalSection(&fileInfo->CriticalSection); + } + + if (fileInfo) { + fileInfo->DokanInstance = NULL; + fileInfo->DirList = NULL; + InterlockedExchange64(&fileInfo->UserContext, 0); + fileInfo->EventId = 0; + fileInfo->IsDirectory = FALSE; + } + return fileInfo; +} + +VOID CleanupFileOpenInfo(PDOKAN_OPEN_INFO FileInfo) { + assert(FileInfo); + PDOKAN_VECTOR dirList = NULL; + EnterCriticalSection(&FileInfo->CriticalSection); + { + if (FileInfo->DirListSearchPattern) { + free(FileInfo->DirListSearchPattern); + FileInfo->DirListSearchPattern = NULL; + } + + if (FileInfo->DirList) { + dirList = FileInfo->DirList; + FileInfo->DirList = NULL; + } + } + LeaveCriticalSection(&FileInfo->CriticalSection); + if (dirList) { + PushDirectoryList(dirList); + } +} + +VOID FreeFileOpenInfo(PDOKAN_OPEN_INFO FileInfo) { + if (FileInfo) { + CleanupFileOpenInfo(FileInfo); + DeleteCriticalSection(&FileInfo->CriticalSection); + free(FileInfo); + } +} + +VOID PushFileOpenInfo(PDOKAN_OPEN_INFO FileInfo) { + assert(FileInfo); + CleanupFileOpenInfo(FileInfo); + EnterCriticalSection(&g_FileInfoCriticalSection); + { + if (DokanVector_GetCount(g_FileInfoPool) < DOKAN_IO_EVENT_POOL_SIZE) { + DokanVector_PushBack(g_FileInfoPool, &FileInfo); + FileInfo = NULL; + } + } + LeaveCriticalSection(&g_FileInfoCriticalSection); + if (FileInfo) { + FreeFileOpenInfo(FileInfo); + } +} + +/////////////////// Directory list /////////////////// +PDOKAN_VECTOR PopDirectoryList() { + PDOKAN_VECTOR directoryList = NULL; + EnterCriticalSection(&g_DirectoryListCriticalSection); + { + if (DokanVector_GetCount(g_DirectoryListPool) > 0) { + directoryList = + *(PDOKAN_VECTOR *)DokanVector_GetLastItem(g_DirectoryListPool); + DokanVector_PopBack(g_DirectoryListPool); + } + } + LeaveCriticalSection(&g_DirectoryListCriticalSection); + if (!directoryList) { + directoryList = DokanVector_Alloc(sizeof(WIN32_FIND_DATAW)); + } + if (directoryList) { + DokanVector_Clear(directoryList); + } + return directoryList; +} + +VOID PushDirectoryList(PDOKAN_VECTOR DirectoryList) { + assert(DirectoryList); + assert(DokanVector_GetItemSize(DirectoryList) == sizeof(WIN32_FIND_DATAW)); + EnterCriticalSection(&g_DirectoryListCriticalSection); + { + if (DokanVector_GetCount(g_DirectoryListPool) < + DOKAN_DIRECTORY_LIST_POOL_SIZE) { + DokanVector_PushBack(g_DirectoryListPool, &DirectoryList); + DirectoryList = NULL; + } + } + LeaveCriticalSection(&g_DirectoryListCriticalSection); + if (DirectoryList) { + DokanVector_Free(DirectoryList); + } +} + +/////////////////// Push/Pop pattern finished /////////////////// \ No newline at end of file diff --git a/dokan/dokan_pool.h b/dokan/dokan_pool.h new file mode 100644 index 000000000..58924fd3b --- /dev/null +++ b/dokan/dokan_pool.h @@ -0,0 +1,54 @@ +/* + Dokan : user-mode file system library for Windows + + Copyright (C) 2016 Keith Newton + Copyright (C) 2021 Google, Inc. + + http://dokan-dev.github.io + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the Free +Software Foundation; either version 3 of the License, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program. If not, see . +*/ + +#ifndef DOKAN_POOL_H_ +#define DOKAN_POOL_H_ + +#include "dokani.h" + +#define BATCH_EVENT_CONTEXT_SIZE (EVENT_CONTEXT_MAX_SIZE * 4) +#define DOKAN_IO_BATCH_SIZE \ + ((SIZE_T)(FIELD_OFFSET(DOKAN_IO_BATCH, EventContext)) + \ + BATCH_EVENT_CONTEXT_SIZE) + +PTP_POOL GetThreadPool(); +int InitializePool(); +VOID CleanupPool(); + +PDOKAN_IO_BATCH PopIoBatchBuffer(); +VOID PushIoBatchBuffer(PDOKAN_IO_BATCH IoBatch); +VOID FreeIoBatchBuffer(PDOKAN_IO_BATCH IoBatch); + +PDOKAN_IO_EVENT PopIoEventBuffer(); +VOID PushIoEventBuffer(PDOKAN_IO_EVENT IoEvent); + +PEVENT_INFORMATION PopEventResult(); +VOID PushEventResult(PEVENT_INFORMATION EventResult); +VOID FreeEventResult(PEVENT_INFORMATION EventResult); + +PDOKAN_OPEN_INFO PopFileOpenInfo(); +VOID PushFileOpenInfo(PDOKAN_OPEN_INFO FileInfo); +VOID FreeFileOpenInfo(PDOKAN_OPEN_INFO FileInfo); + +PDOKAN_VECTOR PopDirectoryList(); +VOID PushDirectoryList(PDOKAN_VECTOR DirectoryList); + +#endif \ No newline at end of file diff --git a/dokan/dokan_vector.c b/dokan/dokan_vector.c new file mode 100644 index 000000000..07e343283 --- /dev/null +++ b/dokan/dokan_vector.c @@ -0,0 +1,271 @@ +/* + Dokan : user-mode file system library for Windows + + Copyright (C) 2016 Keith Newton + Copyright (C) 2021 Google, Inc. + + http://dokan-dev.github.io + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the Free +Software Foundation; either version 3 of the License, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program. If not, see . +*/ + +#include "dokani.h" +#include "dokan_vector.h" + +#include + +#define DEFAULT_ITEM_COUNT 128 + +// Increases the internal capacity of the vector. +BOOL DokanVector_Grow(PDOKAN_VECTOR Vector, size_t MinimumIncrease); + +// Creates a new instance of DOKAN_VECTOR with default values. +PDOKAN_VECTOR DokanVector_Alloc(size_t ItemSize) { + assert(ItemSize > 0); + if (ItemSize == 0) { + DbgPrintW(L"Cannot allocate a DOKAN_VECTOR with an ItemSize of 0.\n"); + return NULL; + } + PDOKAN_VECTOR vector = (PDOKAN_VECTOR)malloc(sizeof(DOKAN_VECTOR)); + if (!vector) { + DbgPrintW(L"DOKAN_VECTOR allocation failed.\n"); + return NULL; + } + vector->Items = malloc(ItemSize * DEFAULT_ITEM_COUNT); + if (!vector->Items) { + DbgPrintW(L"DOKAN_VECTOR Items allocation failed.\n"); + free(vector); + return NULL; + } + vector->ItemCount = 0; + vector->ItemSize = ItemSize; + vector->MaxItems = DEFAULT_ITEM_COUNT; + vector->IsStackAllocated = FALSE; + return vector; +} + +// Creates a new instance of DOKAN_VECTOR with default values. +PDOKAN_VECTOR DokanVector_AllocWithCapacity(size_t ItemSize, size_t MaxItems) { + assert(ItemSize > 0); + if (ItemSize == 0) { + DbgPrintW(L"Cannot allocate a DOKAN_VECTOR with an ItemSize of 0.\n"); + return NULL; + } + PDOKAN_VECTOR vector = (PDOKAN_VECTOR)malloc(sizeof(DOKAN_VECTOR)); + if (!vector) { + DbgPrintW(L"DOKAN_VECTOR allocation failed.\n"); + return NULL; + } + if (MaxItems > 0) { + vector->Items = malloc(ItemSize * MaxItems); + if (!vector->Items) { + DbgPrintW(L"DOKAN_VECTOR Items allocation failed.\n"); + free(vector); + return NULL; + } + } else { + vector->Items = NULL; + } + vector->ItemCount = 0; + vector->ItemSize = ItemSize; + vector->MaxItems = MaxItems; + vector->IsStackAllocated = FALSE; + return vector; +} + +// Creates a new instance of DOKAN_VECTOR with default values on the stack. +BOOL DokanVector_StackAlloc(PDOKAN_VECTOR Vector, size_t ItemSize) { + assert(Vector && ItemSize > 0); + if (ItemSize == 0) { + DbgPrintW(L"Cannot allocate a DOKAN_VECTOR with an ItemSize of 0.\n"); + ZeroMemory(Vector, sizeof(DOKAN_VECTOR)); + return FALSE; + } + Vector->Items = malloc(ItemSize * DEFAULT_ITEM_COUNT); + if (!Vector->Items) { + DbgPrintW(L"DOKAN_VECTOR Items allocation failed.\n"); + return FALSE; + } + Vector->ItemCount = 0; + Vector->ItemSize = ItemSize; + Vector->MaxItems = DEFAULT_ITEM_COUNT; + Vector->IsStackAllocated = TRUE; + return TRUE; +} + +// Creates a new instance of DOKAN_VECTOR with default values on the stack. +BOOL DokanVector_StackAllocWithCapacity(PDOKAN_VECTOR Vector, size_t ItemSize, + size_t MaxItems) { + assert(Vector && ItemSize > 0); + if (ItemSize == 0) { + DbgPrintW(L"Cannot allocate a DOKAN_VECTOR with an ItemSize of 0.\n"); + ZeroMemory(Vector, sizeof(DOKAN_VECTOR)); + return FALSE; + } + if (MaxItems > 0) { + Vector->Items = malloc(ItemSize * MaxItems); + if (!Vector->Items) { + DbgPrintW(L"DOKAN_VECTOR Items allocation failed.\n"); + return FALSE; + } + } else { + Vector->Items = NULL; + } + Vector->ItemCount = 0; + Vector->ItemSize = ItemSize; + Vector->MaxItems = MaxItems; + Vector->IsStackAllocated = TRUE; + return TRUE; +} + +// Releases the memory associated with a DOKAN_VECTOR; +VOID DokanVector_Free(PDOKAN_VECTOR Vector) { + if (Vector) { + if (Vector->Items) { + free(Vector->Items); + } + if (!Vector->IsStackAllocated) { + free(Vector); + } + } +} + +// Appends an item to the vector. +BOOL DokanVector_PushBack(PDOKAN_VECTOR Vector, PVOID Item) { + assert(Vector && Item); + if (Vector->ItemCount + 1 >= Vector->MaxItems) { + if (!DokanVector_Grow(Vector, 1)) { + return FALSE; + } + } + memcpy_s(((BYTE *)Vector->Items) + Vector->ItemSize * Vector->ItemCount, + (Vector->MaxItems - Vector->ItemCount) * Vector->ItemSize, Item, + Vector->ItemSize); + ++Vector->ItemCount; + return TRUE; +} + +// Appends an array of items to the vector. +BOOL DokanVector_PushBackArray(PDOKAN_VECTOR Vector, PVOID Items, + size_t Count) { + assert(Vector && Items); + if (Count == 0) { + return TRUE; + } + if (Vector->ItemCount + Count >= Vector->MaxItems) { + if (!DokanVector_Grow(Vector, Count)) { + return FALSE; + } + } + memcpy_s(((BYTE *)Vector->Items) + Vector->ItemSize * Vector->ItemCount, + (Vector->MaxItems - Vector->ItemCount) * Vector->ItemSize, Items, + Vector->ItemSize * Count); + Vector->ItemCount += Count; + return TRUE; +} + +// Removes an item from the end of the vector. +VOID DokanVector_PopBack(PDOKAN_VECTOR Vector) { + assert(Vector && Vector->ItemCount > 0); + if (Vector->ItemCount > 0) { + --Vector->ItemCount; + } +} + +// Removes multiple items from the end of the vector. +VOID DokanVector_PopBackArray(PDOKAN_VECTOR Vector, size_t Count) { + assert(Count <= Vector->ItemCount); + if (Count > Vector->ItemCount) { + Vector->ItemCount = 0; + } else { + Vector->ItemCount -= Count; + } +} + +// Clears all items in the vector. +VOID DokanVector_Clear(PDOKAN_VECTOR Vector) { + assert(Vector); + Vector->ItemCount = 0; +} + +// Retrieves the item at the specified index +PVOID DokanVector_GetItem(PDOKAN_VECTOR Vector, size_t Index) { + assert(Vector && Index < Vector->ItemCount); + if (Index < Vector->ItemCount) { + return ((BYTE *)Vector->Items) + Vector->ItemSize * Index; + } + return NULL; +} + +// Retrieves the last item the vector. +PVOID DokanVector_GetLastItem(PDOKAN_VECTOR Vector) { + assert(Vector); + if (Vector->ItemCount > 0) { + return ((BYTE *)Vector->Items) + Vector->ItemSize * (Vector->ItemCount - 1); + } + return NULL; +} + +// Increases the internal capacity of the vector. +BOOL DokanVector_Grow(PDOKAN_VECTOR Vector, size_t MinimumIncrease) { + if (MinimumIncrease == 0 && Vector->MaxItems == 0) { + Vector->Items = malloc(Vector->ItemSize * DEFAULT_ITEM_COUNT); + if (!Vector->Items) { + DbgPrintW(L"DOKAN_VECTOR Items allocation failed.\n"); + return FALSE; + } + Vector->MaxItems = DEFAULT_ITEM_COUNT; + return TRUE; + } + size_t newSize = Vector->MaxItems * 2; + if (newSize < DEFAULT_ITEM_COUNT) { + newSize = DEFAULT_ITEM_COUNT; + } + if (newSize <= Vector->MaxItems + MinimumIncrease) { + newSize = Vector->MaxItems + MinimumIncrease + MinimumIncrease; + } + PVOID newItems = realloc(Vector->Items, newSize * Vector->ItemSize); + if (newItems) { + Vector->Items = newItems; + Vector->MaxItems = newSize; + return TRUE; + } + return FALSE; +} + +// Retrieves the number of items in the vector. +size_t DokanVector_GetCount(PDOKAN_VECTOR Vector) { + assert(Vector); + if (Vector) { + return Vector->ItemCount; + } + return 0; +} + +// Retrieves the current capacity of the vector. +size_t DokanVector_GetCapacity(PDOKAN_VECTOR Vector) { + assert(Vector); + if (Vector) { + return Vector->MaxItems; + } + return 0; +} + +// Retrieves the size of items within the vector. +size_t DokanVector_GetItemSize(PDOKAN_VECTOR Vector) { + assert(Vector); + if (Vector) { + return Vector->ItemSize; + } + return 0; +} \ No newline at end of file diff --git a/dokan/dokan_vector.h b/dokan/dokan_vector.h new file mode 100644 index 000000000..6a1b4c535 --- /dev/null +++ b/dokan/dokan_vector.h @@ -0,0 +1,79 @@ +/* + Dokan : user-mode file system library for Windows + + Copyright (C) 2016 Keith Newton + Copyright (C) 2021 Google, Inc. + + http://dokan-dev.github.io + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the Free +Software Foundation; either version 3 of the License, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program. If not, see . +*/ + +#ifndef DOKAN_VECTOR_H_ +#define DOKAN_VECTOR_H_ + +typedef struct _DOKAN_VECTOR { + PVOID Items; + size_t ItemCount; + size_t ItemSize; + size_t MaxItems; + BOOL IsStackAllocated; +} DOKAN_VECTOR, *PDOKAN_VECTOR; + +// Creates a new instance of DOKAN_VECTOR with default values. +DOKAN_VECTOR *DokanVector_Alloc(size_t ItemSize); + +// Creates a new instance of DOKAN_VECTOR with default values. +DOKAN_VECTOR *DokanVector_AllocWithCapacity(size_t ItemSize, size_t MaxItems); + +// Creates a new instance of DOKAN_VECTOR with default values on the stack. +BOOL DokanVector_StackAlloc(PDOKAN_VECTOR Vector, size_t ItemSize); + +// Creates a new instance of DOKAN_VECTOR with default values on the stack. +BOOL DokanVector_StackAllocWithCapacity(PDOKAN_VECTOR Vector, size_t ItemSize, + size_t MaxItems); + +// Releases the memory associated with a DOKAN_VECTOR; +VOID DokanVector_Free(PDOKAN_VECTOR Vector); + +// Appends an item to the vector. +BOOL DokanVector_PushBack(PDOKAN_VECTOR Vector, PVOID Item); + +// Appends an array of items to the vector. +BOOL DokanVector_PushBackArray(PDOKAN_VECTOR Vector, PVOID Items, size_t Count); + +// Removes an item from the end of the vector. +VOID DokanVector_PopBack(PDOKAN_VECTOR Vector); + +// Removes multiple items from the end of the vector. +VOID DokanVector_PopBackArray(PDOKAN_VECTOR Vector, size_t Count); + +// Clears all items in the vector. +VOID DokanVector_Clear(PDOKAN_VECTOR Vector); + +// Retrieves the item at the specified index +PVOID DokanVector_GetItem(PDOKAN_VECTOR Vector, size_t Index); + +// Retrieves the last item the vector. +PVOID DokanVector_GetLastItem(PDOKAN_VECTOR Vector); + +// Retrieves the number of items in the vector. +size_t DokanVector_GetCount(PDOKAN_VECTOR Vector); + +// Retrieves the current capacity of the vector. +size_t DokanVector_GetCapacity(PDOKAN_VECTOR Vector); + +// Retrieves the size of items within the vector. +size_t DokanVector_GetItemSize(PDOKAN_VECTOR Vector); + +#endif diff --git a/dokan/dokani.h b/dokan/dokani.h index 472594fa5..4203b163d 100644 --- a/dokan/dokani.h +++ b/dokan/dokani.h @@ -30,11 +30,18 @@ with this program. If not, see . #include "dokan.h" #include "dokanc.h" #include "list.h" +#include "dokan_vector.h" #ifdef __cplusplus extern "C" { #endif +typedef struct _DOKAN_INSTANCE_THREADINFO { + PTP_POOL ThreadPool; + PTP_CLEANUP_GROUP CleanupGroup; + TP_CALLBACK_ENVIRON CallbackEnvironment; +} DOKAN_INSTANCE_THREADINFO; + /** * \struct DOKAN_INSTANCE * \brief Dokan mount instance informations @@ -49,9 +56,9 @@ typedef struct _DOKAN_INSTANCE { CRITICAL_SECTION CriticalSection; /** - * Current DeviceName. - * When there are many mounts, each mount uses different DeviceName. - */ + * Current DeviceName. + * When there are many mounts, each mount uses different DeviceName. + */ WCHAR DeviceName[64]; /** Mount point. Can be "M:\" (drive letter) or "C:\mount\dokan" (path in NTFS) */ WCHAR MountPoint[MAX_PATH]; @@ -70,6 +77,14 @@ typedef struct _DOKAN_INSTANCE { /** Current list entry informations */ LIST_ENTRY ListEntry; + + HANDLE GlobalDevice; + HANDLE Device; + HANDLE DeviceClosedWaitHandle; + DOKAN_INSTANCE_THREADINFO ThreadInfo; + HANDLE NotifyHandle; + HANDLE KeepaliveHandle; + } DOKAN_INSTANCE, *PDOKAN_INSTANCE; /** @@ -79,26 +94,69 @@ typedef struct _DOKAN_INSTANCE { * This is created in CreateFile and will be freed in CloseFile. */ typedef struct _DOKAN_OPEN_INFO { - /** DOKAN_OPTIONS linked to the mount */ - BOOL IsDirectory; - /** Open count on the file */ - ULONG OpenCount; - /** Event context */ - PEVENT_CONTEXT EventContext; + CRITICAL_SECTION CriticalSection; /** Dokan instance linked to the open */ PDOKAN_INSTANCE DokanInstance; + PDOKAN_VECTOR DirList; + PWCHAR DirListSearchPattern; /** User Context see DOKAN_FILE_INFO.Context */ - ULONG64 UserContext; + volatile LONG64 UserContext; /** Event Id */ ULONG EventId; - /** Directories list. Used by FindFiles */ - PLIST_ENTRY DirListHead; - /** File streams list. Used by FindStreams */ - PLIST_ENTRY StreamListHead; + /** DOKAN_OPTIONS linked to the mount */ + BOOL IsDirectory; + /** Open count on the file */ + ULONG OpenCount; /** Used when dispatching the close once the OpenCount drops to 0 **/ LPWSTR FileName; + /** Event context */ + PEVENT_CONTEXT EventContext; } DOKAN_OPEN_INFO, *PDOKAN_OPEN_INFO; +typedef enum _DOKAN_OVERLAPPED_TYPE { + // The overlapped operation contains a DOKAN_IO_EVENT as its payload + DOKAN_OVERLAPPED_TYPE_IOEVENT = 0, + // The overlapped operation payload contains a result being passed back to the + // kernel driver. Results are represented as an EVENT_INFORMATION struct. + DOKAN_OVERLAPPED_TYPE_IOEVENT_RESULT, +} DOKAN_OVERLAPPED_TYPE; + +// See DeviceIoControl() for how InputPayload and OutputLoad are used as it's not entirely intuitive +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa363216%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396 +typedef struct _DOKAN_OVERLAPPED { + OVERLAPPED InternalOverlapped; + PVOID InputPayload; + PVOID OutputPayload; + DOKAN_OVERLAPPED_TYPE PayloadType; + BOOL PayloadPoolAllocated; +} DOKAN_OVERLAPPED, *PDOKAN_OVERLAPPED; + +typedef struct _DOKAN_IO_BATCH { + PDOKAN_INSTANCE DokanInstance; + DWORD NumberOfBytesTransferred; + BOOL MainPullThread; + BOOL PoolAllocated; + LONG EventContextBatchCount; + EVENT_CONTEXT EventContext[1]; +} DOKAN_IO_BATCH, *PDOKAN_IO_BATCH; + +typedef struct _DOKAN_IO_EVENT { + PDOKAN_INSTANCE DokanInstance; + PDOKAN_OPEN_INFO DokanOpenInfo; + PEVENT_INFORMATION EventResult; + ULONG EventResultSize; + BOOL PoolAllocated; + DOKAN_FILE_INFO DokanFileInfo; + PEVENT_CONTEXT EventContext; + PDOKAN_IO_BATCH IoBatch; +} DOKAN_IO_EVENT, *PDOKAN_IO_EVENT; + +#define IOEVENT_RESULT_BUFFER_SIZE(ioEvent) \ + ((ioEvent)->EventResultSize >= offsetof(EVENT_INFORMATION, Buffer) \ + ? (ioEvent)->EventResultSize - offsetof(EVENT_INFORMATION, Buffer) \ + : 0) + + BOOL DokanStart(PDOKAN_INSTANCE Instance); BOOL SendToDevice(LPCWSTR DeviceName, DWORD IoControlCode, PVOID InputBuffer, @@ -109,78 +167,42 @@ VOID GetRawDeviceName(LPCWSTR DeviceName, LPWSTR DestinationBuffer, rsize_t DestinationBufferSizeInElements); -void ALIGN_ALLOCATION_SIZE(PLARGE_INTEGER size, PDOKAN_OPTIONS DokanOptions); - -UINT __stdcall DokanLoop(PVOID Param); +VOID ALIGN_ALLOCATION_SIZE(PLARGE_INTEGER size, PDOKAN_OPTIONS DokanOptions); BOOL DokanMount(LPCWSTR MountPoint, LPCWSTR DeviceName, PDOKAN_OPTIONS DokanOptions); BOOL IsMountPointDriveLetter(LPCWSTR mountPoint); -VOID SendEventInformation(HANDLE Handle, PEVENT_INFORMATION EventInfo, - ULONG EventLength); - -ULONG DispatchGetEventInformationLength(ULONG bufferSize); - -PEVENT_INFORMATION -DispatchCommon(PEVENT_CONTEXT EventContext, ULONG SizeOfEventInfo, - PDOKAN_INSTANCE DokanInstance, PDOKAN_FILE_INFO DokanFileInfo, - PDOKAN_OPEN_INFO *DokanOpenInfo); +VOID EventCompletion(PDOKAN_IO_EVENT EventInfo); -VOID DispatchDirectoryInformation(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance); +VOID CreateDispatchCommon(PDOKAN_IO_EVENT IoEvent, ULONG SizeOfEventInfo); -VOID DispatchQueryInformation(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance); +VOID DispatchDirectoryInformation(PDOKAN_IO_EVENT IoEvent); -VOID DispatchQueryVolumeInformation(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance); +VOID DispatchQueryInformation(PDOKAN_IO_EVENT IoEvent); -VOID DispatchSetInformation(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance); +VOID DispatchQueryVolumeInformation(PDOKAN_IO_EVENT IoEvent); -VOID DispatchRead(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance); +VOID DispatchSetInformation(PDOKAN_IO_EVENT IoEvent); -VOID DispatchWrite(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance); +VOID DispatchRead(PDOKAN_IO_EVENT IoEvent); -VOID DispatchCreate(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance); +VOID DispatchWrite(PDOKAN_IO_EVENT IoEvent); -VOID DispatchClose(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance); +VOID DispatchCreate(PDOKAN_IO_EVENT IoEvent); -VOID DispatchCleanup(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance); +VOID DispatchClose(PDOKAN_IO_EVENT IoEvent); -VOID DispatchFlush(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance); +VOID DispatchCleanup(PDOKAN_IO_EVENT IoEvent); -VOID DispatchLock(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance); +VOID DispatchFlush(PDOKAN_IO_EVENT IoEvent); -VOID DispatchQuerySecurity(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance); +VOID DispatchLock(PDOKAN_IO_EVENT IoEvent); -VOID DispatchSetSecurity(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance); +VOID DispatchQuerySecurity(PDOKAN_IO_EVENT IoEvent); -BOOLEAN -InstallDriver(SC_HANDLE SchSCManager, LPCWSTR DriverName, LPCWSTR ServiceExe); - -BOOLEAN -RemoveDriver(SC_HANDLE SchSCManager, LPCWSTR DriverName); - -BOOLEAN -StartDriver(SC_HANDLE SchSCManager, LPCWSTR DriverName); - -BOOLEAN -StopDriver(SC_HANDLE SchSCManager, LPCWSTR DriverName); - -BOOLEAN -ManageDriver(LPCWSTR DriverName, LPCWSTR ServiceName, USHORT Function); +VOID DispatchSetSecurity(PDOKAN_IO_EVENT IoEvent); BOOL SendReleaseIRP(LPCWSTR DeviceName); @@ -188,33 +210,7 @@ BOOL SendGlobalReleaseIRP(LPCWSTR MountPoint); VOID CheckFileName(LPWSTR FileName); -VOID ClearFindData(PLIST_ENTRY ListHead); - -VOID ClearFindStreamData(PLIST_ENTRY ListHead); - -UINT WINAPI DokanKeepAlive(PVOID Param); - -PDOKAN_OPEN_INFO -GetDokanOpenInfo(PEVENT_CONTEXT EventInfomation, PDOKAN_INSTANCE DokanInstance); - -VOID ReleaseDokanOpenInfo(PEVENT_INFORMATION EventInfomation, - PDOKAN_FILE_INFO FileInfo, - PDOKAN_INSTANCE DokanInstance); - -/** - * \brief Unmount a Dokan device from a mount point - * - * Same as \ref DokanRemoveMountPoint - * If Safe is \c TRUE, it will broadcast to all desktops and Shells - * Safe should not be used during DLL_PROCESS_DETACH - * - * \see DokanRemoveMountPoint - * - * \param MountPoint Mount point to unmount ("Z", "Z:", "Z:\", "Z:\MyMountPoint"). - * \param Safe Process is not in DLL_PROCESS_DETACH state. - * \return \c TRUE if device was unmounted or \c FALSE in case of failure or device not found. - */ -BOOL DokanRemoveMountPointEx(LPCWSTR MountPoint, BOOL Safe); +VOID ReleaseDokanOpenInfo(PDOKAN_IO_EVENT IoEvent); #ifdef __cplusplus } diff --git a/dokan/fileinfo.c b/dokan/fileinfo.c index 72db75e48..eeac75784 100644 --- a/dokan/fileinfo.c +++ b/dokan/fileinfo.c @@ -22,8 +22,12 @@ with this program. If not, see . #include "dokani.h" #include "fileinfo.h" + #include #include +#include + +#define DOKAN_STREAM_ENTRY_ALIGNMENT 8 NTSTATUS DokanFillFileBasicInfo(PFILE_BASIC_INFORMATION BasicInfo, @@ -116,7 +120,7 @@ DokanFillInternalInfo(PFILE_INTERNAL_INFORMATION InternalInfo, NTSTATUS DokanFillFileAllInfo(PFILE_ALL_INFORMATION AllInfo, PBY_HANDLE_FILE_INFORMATION FileInfo, - PULONG RemainingLength, PEVENT_CONTEXT EventContext, + PULONG RemainingLength, PDOKAN_FILE_INFO DokanFileInfo, PDOKAN_INSTANCE DokanInstance) { ULONG allRemainingLength = *RemainingLength; @@ -142,35 +146,11 @@ DokanFillFileAllInfo(PFILE_ALL_INFORMATION AllInfo, DokanFillFilePositionInfo(&AllInfo->PositionInformation, FileInfo, RemainingLength); - // there is not enough space to fill FileNameInformation - if (allRemainingLength < sizeof(FILE_ALL_INFORMATION) + - EventContext->Operation.File.FileNameLength) { - // fill out to the limit - // FileNameInformation - AllInfo->NameInformation.FileNameLength = - EventContext->Operation.File.FileNameLength; - AllInfo->NameInformation.FileName[0] = - EventContext->Operation.File.FileName[0]; - - allRemainingLength -= sizeof(FILE_ALL_INFORMATION); - *RemainingLength = allRemainingLength; - return STATUS_BUFFER_OVERFLOW; - } - - // FileNameInformation - AllInfo->NameInformation.FileNameLength = - EventContext->Operation.File.FileNameLength; - RtlCopyMemory(&(AllInfo->NameInformation.FileName[0]), - EventContext->Operation.File.FileName, - EventContext->Operation.File.FileNameLength); + // AllInfo->NameInformation.FileName is populated by the Kernel // the size except of FILE_NAME_INFORMATION allRemainingLength -= - (sizeof(FILE_ALL_INFORMATION) - sizeof(FILE_NAME_INFORMATION)); - - // the size of FILE_NAME_INFORMATION - allRemainingLength -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]); - allRemainingLength -= AllInfo->NameInformation.FileNameLength; + FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName); *RemainingLength = allRemainingLength; @@ -280,210 +260,122 @@ typedef struct _DOKAN_FIND_STREAM_DATA { LIST_ENTRY ListEntry; } DOKAN_FIND_STREAM_DATA, *PDOKAN_FIND_STREAM_DATA; -int WINAPI DokanFillFindStreamData(PWIN32_FIND_STREAM_DATA FindStreamData, - PDOKAN_FILE_INFO FileInfo) { - PLIST_ENTRY listHead = - ((PDOKAN_OPEN_INFO)(UINT_PTR)FileInfo->DokanContext)->StreamListHead; - PDOKAN_FIND_STREAM_DATA findStreamData; - - findStreamData = - (PDOKAN_FIND_STREAM_DATA)malloc(sizeof(DOKAN_FIND_STREAM_DATA)); - if (findStreamData == NULL) { - return 0; - } - ZeroMemory(findStreamData, sizeof(DOKAN_FIND_STREAM_DATA)); - InitializeListHead(&findStreamData->ListEntry); +BOOL WINAPI DokanFillFindStreamData(PWIN32_FIND_STREAM_DATA FindStreamData, + PVOID FindStreamContext) { - findStreamData->FindStreamData = *FindStreamData; + PDOKAN_IO_EVENT ioEvent = (PDOKAN_IO_EVENT)FindStreamContext; + ULONG offset = (ULONG)ioEvent->EventResult->BufferLength; + ULONG resultBufferSize = + ioEvent->EventContext->Operation.File.BufferLength; - InsertTailList(listHead, &findStreamData->ListEntry); - return 0; -} + ULONG streamNameLength = + (ULONG)wcslen(FindStreamData->cStreamName) * sizeof(WCHAR); -VOID ClearFindStreamData(PLIST_ENTRY ListHead) { - // free all list entries - while (!IsListEmpty(ListHead)) { - PLIST_ENTRY entry = RemoveHeadList(ListHead); - PDOKAN_FIND_STREAM_DATA find = - CONTAINING_RECORD(entry, DOKAN_FIND_STREAM_DATA, ListEntry); - free(find); - } -} + // Must be aligned on a 8-byte boundary. + ULONG entrySize = + QuadAlign(sizeof(FILE_STREAM_INFORMATION) + streamNameLength); + assert(entrySize % DOKAN_STREAM_ENTRY_ALIGNMENT == 0); -NTSTATUS -DokanFindStreams(PFILE_STREAM_INFORMATION StreamInfo, PDOKAN_FILE_INFO FileInfo, - PEVENT_CONTEXT EventContext, PDOKAN_INSTANCE DokanInstance, - PULONG RemainingLength) { - PDOKAN_OPEN_INFO openInfo = - (PDOKAN_OPEN_INFO)(UINT_PTR)FileInfo->DokanContext; - NTSTATUS status = STATUS_SUCCESS; - - if (!DokanInstance->DokanOperations->FindStreams) { - return STATUS_NOT_IMPLEMENTED; + PFILE_STREAM_INFORMATION streamInfo = + (PFILE_STREAM_INFORMATION)&ioEvent->EventResult->Buffer[offset]; + if (offset + entrySize + streamInfo->NextEntryOffset > resultBufferSize) { + return FALSE; } - if (openInfo->StreamListHead == NULL) { - openInfo->StreamListHead = malloc(sizeof(LIST_ENTRY)); - if (openInfo->StreamListHead != NULL) { - InitializeListHead(openInfo->StreamListHead); - } else { - status = STATUS_NO_MEMORY; - } + // If this isn't the first entry move to the next + // memory location + if (streamInfo->NextEntryOffset != 0) { + offset += streamInfo->NextEntryOffset; + streamInfo = + (PFILE_STREAM_INFORMATION)&ioEvent->EventResult->Buffer[offset]; } - if (status == STATUS_SUCCESS && IsListEmpty(openInfo->StreamListHead)) { - status = DokanInstance->DokanOperations->FindStreams( - EventContext->Operation.File.FileName, DokanFillFindStreamData, - FileInfo); - } - - if (status == STATUS_SUCCESS) { - PLIST_ENTRY listHead, entry; - ULONG entrySize; - - listHead = openInfo->StreamListHead; - entrySize = 0; - - for (entry = listHead->Flink; entry != listHead; entry = entry->Flink) { - PDOKAN_FIND_STREAM_DATA find = - CONTAINING_RECORD(entry, DOKAN_FIND_STREAM_DATA, ListEntry); - - ULONG nextEntryOffset = entrySize; - - ULONG streamNameLength = - (ULONG)wcslen(find->FindStreamData.cStreamName) * sizeof(WCHAR); - entrySize = sizeof(FILE_STREAM_INFORMATION) + streamNameLength; - // Must be align on a 8-byte boundary. - entrySize = QuadAlign(entrySize); - if (*RemainingLength < entrySize) { - status = STATUS_BUFFER_OVERFLOW; - break; - } - - // Not the first entry, set the offset before filling the new entry - if (nextEntryOffset > 0) { - StreamInfo->NextEntryOffset = nextEntryOffset; - StreamInfo = (PFILE_STREAM_INFORMATION)((LPBYTE)StreamInfo + - StreamInfo->NextEntryOffset); - } - - // Fill the new entry - StreamInfo->StreamNameLength = streamNameLength; - memcpy(StreamInfo->StreamName, find->FindStreamData.cStreamName, - streamNameLength); - StreamInfo->StreamSize = find->FindStreamData.StreamSize; - StreamInfo->StreamAllocationSize = find->FindStreamData.StreamSize; - StreamInfo->NextEntryOffset = 0; - ALIGN_ALLOCATION_SIZE(&StreamInfo->StreamAllocationSize, - DokanInstance->DokanOptions); - - *RemainingLength -= entrySize; - } - - if (status != STATUS_BUFFER_OVERFLOW) { - ClearFindStreamData(openInfo->StreamListHead); - } - - } else { - ClearFindStreamData(openInfo->StreamListHead); - } - - return status; + assert(streamInfo->NextEntryOffset == 0); + + // Fill the new entry + streamInfo->StreamNameLength = streamNameLength; + memcpy_s(streamInfo->StreamName, streamNameLength, + FindStreamData->cStreamName, streamNameLength); + streamInfo->StreamSize = FindStreamData->StreamSize; + streamInfo->StreamAllocationSize = FindStreamData->StreamSize; + streamInfo->NextEntryOffset = entrySize; + ALIGN_ALLOCATION_SIZE(&streamInfo->StreamAllocationSize, + ioEvent->DokanInstance->DokanOptions); + ioEvent->EventResult->BufferLength = offset; + return TRUE; } -VOID DispatchQueryInformation(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance) { - PEVENT_INFORMATION eventInfo; - DOKAN_FILE_INFO fileInfo; - BY_HANDLE_FILE_INFORMATION byHandleFileInfo; - ULONG remainingLength; - NTSTATUS status = STATUS_INVALID_PARAMETER; - PDOKAN_OPEN_INFO openInfo; - ULONG sizeOfEventInfo = DispatchGetEventInformationLength( - EventContext->Operation.File.BufferLength); - - CheckFileName(EventContext->Operation.File.FileName); - - ZeroMemory(&byHandleFileInfo, sizeof(BY_HANDLE_FILE_INFORMATION)); +VOID DOKANAPI DokanEndDispatchGetFileInformation( + PDOKAN_IO_EVENT IoEvent, PBY_HANDLE_FILE_INFORMATION ByHandleFileInfo, + NTSTATUS Status) { + ULONG remainingLength = IoEvent->EventContext->Operation.File.BufferLength; - eventInfo = DispatchCommon(EventContext, sizeOfEventInfo, DokanInstance, - &fileInfo, &openInfo); + DbgPrint("\tresult = %lx\n", Status); - eventInfo->BufferLength = EventContext->Operation.File.BufferLength; - - DbgPrint("###GetFileInfo %04d\n", openInfo != NULL ? openInfo->EventId : -1); - - if (DokanInstance->DokanOperations->GetFileInformation) { - status = DokanInstance->DokanOperations->GetFileInformation( - EventContext->Operation.File.FileName, &byHandleFileInfo, &fileInfo); - } - - remainingLength = eventInfo->BufferLength; - - DbgPrint("\tresult = %lx\n", status); - - if (status != STATUS_SUCCESS) { - eventInfo->Status = STATUS_INVALID_PARAMETER; - eventInfo->BufferLength = 0; + if (Status != STATUS_SUCCESS) { + IoEvent->EventResult->Status = STATUS_INVALID_PARAMETER; + IoEvent->EventResult->BufferLength = 0; } else { - switch (EventContext->Operation.File.FileInformationClass) { + switch (IoEvent->EventContext->Operation.File.FileInformationClass) { case FileBasicInformation: DbgPrint("\tFileBasicInformation\n"); - status = - DokanFillFileBasicInfo((PFILE_BASIC_INFORMATION)eventInfo->Buffer, - &byHandleFileInfo, &remainingLength); + Status = DokanFillFileBasicInfo( + (PFILE_BASIC_INFORMATION)IoEvent->EventResult->Buffer, + ByHandleFileInfo, &remainingLength); break; case FileIdInformation: DbgPrint("\tFileIdInformation\n"); - status = DokanFillIdInfo((PFILE_ID_INFORMATION)eventInfo->Buffer, - &byHandleFileInfo, &remainingLength); + Status = + DokanFillIdInfo((PFILE_ID_INFORMATION)IoEvent->EventResult->Buffer, + ByHandleFileInfo, &remainingLength); break; case FileInternalInformation: DbgPrint("\tFileInternalInformation\n"); - status = - DokanFillInternalInfo((PFILE_INTERNAL_INFORMATION)eventInfo->Buffer, - &byHandleFileInfo, &remainingLength); + Status = DokanFillInternalInfo( + (PFILE_INTERNAL_INFORMATION)IoEvent->EventResult->Buffer, + ByHandleFileInfo, &remainingLength); break; case FileEaInformation: DbgPrint("\tFileEaInformation\n"); // status = STATUS_NOT_IMPLEMENTED; - status = STATUS_SUCCESS; + Status = STATUS_SUCCESS; remainingLength -= sizeof(FILE_EA_INFORMATION); break; case FileStandardInformation: DbgPrint("\tFileStandardInformation\n"); - status = DokanFillFileStandardInfo( - (PFILE_STANDARD_INFORMATION)eventInfo->Buffer, &byHandleFileInfo, - &remainingLength, &fileInfo, DokanInstance); + Status = DokanFillFileStandardInfo( + (PFILE_STANDARD_INFORMATION)IoEvent->EventResult->Buffer, + ByHandleFileInfo, &remainingLength, &IoEvent->DokanFileInfo, + IoEvent->DokanInstance); break; case FileAllInformation: DbgPrint("\tFileAllInformation\n"); - status = DokanFillFileAllInfo((PFILE_ALL_INFORMATION)eventInfo->Buffer, - &byHandleFileInfo, &remainingLength, - EventContext, &fileInfo, DokanInstance); + Status = DokanFillFileAllInfo( + (PFILE_ALL_INFORMATION)IoEvent->EventResult->Buffer, ByHandleFileInfo, + &remainingLength, &IoEvent->DokanFileInfo, IoEvent->DokanInstance); break; case FileAlternateNameInformation: DbgPrint("\tFileAlternateNameInformation\n"); - status = STATUS_NOT_IMPLEMENTED; + Status = STATUS_NOT_IMPLEMENTED; break; case FileAttributeTagInformation: DbgPrint("\tFileAttributeTagInformation\n"); - status = DokanFillFileAttributeTagInfo( - (PFILE_ATTRIBUTE_TAG_INFORMATION)eventInfo->Buffer, &byHandleFileInfo, - &remainingLength); + Status = DokanFillFileAttributeTagInfo( + (PFILE_ATTRIBUTE_TAG_INFORMATION)IoEvent->EventResult->Buffer, + ByHandleFileInfo, &remainingLength); break; case FileCompressionInformation: DbgPrint("\tFileCompressionInformation\n"); - status = STATUS_NOT_IMPLEMENTED; + Status = STATUS_NOT_IMPLEMENTED; break; case FileNormalizedNameInformation: @@ -491,50 +383,122 @@ VOID DispatchQueryInformation(HANDLE Handle, PEVENT_CONTEXT EventContext, case FileNameInformation: // this case is not used because driver deal with DbgPrint("\tFileNameInformation\n"); - status = DokanFillFileNameInfo((PFILE_NAME_INFORMATION)eventInfo->Buffer, - &byHandleFileInfo, &remainingLength, - EventContext); + Status = DokanFillFileNameInfo( + (PFILE_NAME_INFORMATION)IoEvent->EventResult->Buffer, + ByHandleFileInfo, &remainingLength, IoEvent->EventContext); break; case FileNetworkOpenInformation: DbgPrint("\tFileNetworkOpenInformation\n"); - status = DokanFillNetworkOpenInfo( - (PFILE_NETWORK_OPEN_INFORMATION)eventInfo->Buffer, &byHandleFileInfo, - &remainingLength, DokanInstance); + Status = DokanFillNetworkOpenInfo( + (PFILE_NETWORK_OPEN_INFORMATION)IoEvent->EventResult->Buffer, + ByHandleFileInfo, &remainingLength, IoEvent->DokanInstance); break; case FilePositionInformation: // this case is not used because driver deal with DbgPrint("\tFilePositionInformation\n"); - status = DokanFillFilePositionInfo( - (PFILE_POSITION_INFORMATION)eventInfo->Buffer, &byHandleFileInfo, - &remainingLength); + Status = DokanFillFilePositionInfo( + (PFILE_POSITION_INFORMATION)IoEvent->EventResult->Buffer, + ByHandleFileInfo, &remainingLength); break; case FileStreamInformation: - DbgPrint("FileStreamInformation\n"); - status = DokanFindStreams((PFILE_STREAM_INFORMATION)eventInfo->Buffer, - &fileInfo, EventContext, DokanInstance, - &remainingLength); + DbgPrint("FileStreamInformation (internal error)\n"); + // shouldn't get here + Status = STATUS_INTERNAL_ERROR; break; default: { - status = STATUS_INVALID_PARAMETER; + Status = STATUS_INVALID_PARAMETER; DbgPrint(" unknown type:%d\n", - EventContext->Operation.File.FileInformationClass); + IoEvent->EventContext->Operation.File.FileInformationClass); } break; } - eventInfo->Status = status; - eventInfo->BufferLength = - EventContext->Operation.File.BufferLength - remainingLength; + IoEvent->EventResult->Status = Status; + IoEvent->EventResult->BufferLength = + IoEvent->EventContext->Operation.File.BufferLength - remainingLength; } - DbgPrint("\tDispatchQueryInformation result = %lx\n", status); + DbgPrint("\tDispatchQueryInformation result = %lx\n", Status); + EventCompletion(IoEvent); +} + +VOID DOKANAPI DokanEndDispatchFindStreams(PDOKAN_IO_EVENT IoEvent, + NTSTATUS Status) { + ULONG resultBufferSize = IOEVENT_RESULT_BUFFER_SIZE(IoEvent); + PFILE_STREAM_INFORMATION streamInfo = + (PFILE_STREAM_INFORMATION)&IoEvent->EventResult + ->Buffer[IoEvent->EventResult->BufferLength]; + + DbgPrint("\tresult = %lx\n", Status); - // information for FileSystem - if (openInfo != NULL) - openInfo->UserContext = fileInfo.Context; + // Entries must be 8 byte aligned + assert(streamInfo->NextEntryOffset % DOKAN_STREAM_ENTRY_ALIGNMENT == 0); - SendEventInformation(Handle, eventInfo, sizeOfEventInfo); - ReleaseDokanOpenInfo(eventInfo, &fileInfo, DokanInstance); - free(eventInfo); + // Ensure that the last entry doesn't point to another entry. + IoEvent->EventResult->BufferLength += streamInfo->NextEntryOffset; + streamInfo->NextEntryOffset = 0; + + assert(IoEvent->EventResult->BufferLength <= resultBufferSize); + + if (IoEvent->EventResult->BufferLength > resultBufferSize) { + IoEvent->EventResult->BufferLength = 0; + Status = STATUS_BUFFER_OVERFLOW; + } + + // STATUS_PENDING should not be passed to this function + if (Status == STATUS_PENDING) { + DbgPrint("Dokan Error: DokanEndDispatchFindStreams() failed because " + "STATUS_PENDING was supplied for ResultStatus.\n"); + Status = STATUS_INTERNAL_ERROR; + } + + IoEvent->EventResult->Status = Status; + DbgPrint("\tDokanEndDispatchFindStreams result = 0x%x\n", Status); + EventCompletion(IoEvent); } + +VOID DispatchQueryInformation(PDOKAN_IO_EVENT IoEvent) { + BY_HANDLE_FILE_INFORMATION byHandleFileInfo; + NTSTATUS status = STATUS_INVALID_PARAMETER; + + DbgPrint( + "###GetFileInfo file handle = 0x%p, eventID = %04d, event Info = 0x%p\n", + IoEvent->DokanOpenInfo, + IoEvent->DokanOpenInfo != NULL ? IoEvent->DokanOpenInfo->EventId : -1, + IoEvent); + + CheckFileName(IoEvent->EventContext->Operation.File.FileName); + + CreateDispatchCommon(IoEvent, + IoEvent->EventContext->Operation.File.BufferLength); + + if (IoEvent->EventContext->Operation.File.FileInformationClass == + FileStreamInformation) { + DbgPrint("FileStreamInformation\n"); + // https://msdn.microsoft.com/en-us/library/windows/hardware/ff540364(v=vs.85).aspx + if (IoEvent->EventContext->Operation.File.BufferLength < + sizeof(FILE_STREAM_INFORMATION)) { + + status = STATUS_BUFFER_TOO_SMALL; + } else if (IoEvent->DokanInstance->DokanOperations->FindStreams) { + + status = IoEvent->DokanInstance->DokanOperations->FindStreams( + IoEvent->EventContext->Operation.File.FileName, + DokanFillFindStreamData, IoEvent, &IoEvent->DokanFileInfo); + DokanEndDispatchFindStreams(IoEvent, status); + } else { + status = STATUS_NOT_IMPLEMENTED; + } + } else if (IoEvent->DokanInstance->DokanOperations->GetFileInformation) { + + ZeroMemory(&byHandleFileInfo, sizeof(BY_HANDLE_FILE_INFORMATION)); + status = IoEvent->DokanInstance->DokanOperations->GetFileInformation( + IoEvent->EventContext->Operation.File.FileName, &byHandleFileInfo, + &IoEvent->DokanFileInfo); + DokanEndDispatchGetFileInformation(IoEvent, &byHandleFileInfo, status); + } else { + + status = STATUS_NOT_IMPLEMENTED; + } +} \ No newline at end of file diff --git a/dokan/flush.c b/dokan/flush.c index 29a4d1065..d1c8d37a0 100644 --- a/dokan/flush.c +++ b/dokan/flush.c @@ -22,41 +22,32 @@ with this program. If not, see . #include "dokani.h" -VOID DispatchFlush(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance) { - DOKAN_FILE_INFO fileInfo; - PEVENT_INFORMATION eventInfo; - PDOKAN_OPEN_INFO openInfo; +VOID DispatchFlush(PDOKAN_IO_EVENT IoEvent) { NTSTATUS status; - ULONG sizeOfEventInfo = DispatchGetEventInformationLength(0); - CheckFileName(EventContext->Operation.Flush.FileName); + CheckFileName(IoEvent->EventContext->Operation.Flush.FileName); - eventInfo = DispatchCommon(EventContext, sizeOfEventInfo, DokanInstance, - &fileInfo, &openInfo); + CreateDispatchCommon(IoEvent, 0); - DbgPrint("###Flush %04d\n", openInfo != NULL ? openInfo->EventId : -1); - - if (DokanInstance->DokanOperations->FlushFileBuffers) { - - status = DokanInstance->DokanOperations->FlushFileBuffers( - EventContext->Operation.Flush.FileName, &fileInfo); + DbgPrint("###Flush file handle = 0x%p, eventID = %04d, event Info = 0x%p\n", + IoEvent->DokanOpenInfo, + IoEvent->DokanOpenInfo != NULL ? IoEvent->DokanOpenInfo->EventId + : -1, + IoEvent); + if (IoEvent->DokanInstance->DokanOperations->FlushFileBuffers) { + status = IoEvent->DokanInstance->DokanOperations->FlushFileBuffers( + IoEvent->EventContext->Operation.Flush.FileName, &IoEvent->DokanFileInfo); } else { status = STATUS_NOT_IMPLEMENTED; } if (status == STATUS_NOT_IMPLEMENTED) { - eventInfo->Status = STATUS_SUCCESS; + IoEvent->EventResult->Status = STATUS_SUCCESS; } else { - eventInfo->Status = + IoEvent->EventResult->Status = status != STATUS_SUCCESS ? STATUS_NOT_SUPPORTED : STATUS_SUCCESS; } - if (openInfo != NULL) - openInfo->UserContext = fileInfo.Context; - - SendEventInformation(Handle, eventInfo, sizeOfEventInfo); - ReleaseDokanOpenInfo(eventInfo, &fileInfo, DokanInstance); - free(eventInfo); + EventCompletion(IoEvent); } diff --git a/dokan/lock.c b/dokan/lock.c index b151fa1e3..78b544ebd 100644 --- a/dokan/lock.c +++ b/dokan/lock.c @@ -23,36 +23,34 @@ with this program. If not, see . #include "dokani.h" #include "fileinfo.h" -VOID DispatchLock(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance) { - DOKAN_FILE_INFO fileInfo; - PEVENT_INFORMATION eventInfo; - PDOKAN_OPEN_INFO openInfo; +VOID DispatchLock(PDOKAN_IO_EVENT IoEvent) { NTSTATUS status; - ULONG sizeOfEventInfo = DispatchGetEventInformationLength(0); - CheckFileName(EventContext->Operation.Lock.FileName); + CheckFileName(IoEvent->EventContext->Operation.Lock.FileName); - eventInfo = DispatchCommon(EventContext, sizeOfEventInfo, DokanInstance, - &fileInfo, &openInfo); + CreateDispatchCommon(IoEvent, 0); - DbgPrint("###Lock %04d\n", openInfo != NULL ? openInfo->EventId : -1); + DbgPrint("###Lock file handle = 0x%p, eventID = %04d, event Info = 0x%p\n", + IoEvent->DokanOpenInfo, + IoEvent->DokanOpenInfo != NULL ? IoEvent->DokanOpenInfo->EventId + : -1, + IoEvent); - eventInfo->Status = STATUS_NOT_IMPLEMENTED; + IoEvent->EventResult->Status = STATUS_NOT_IMPLEMENTED; - switch (EventContext->MinorFunction) { + switch (IoEvent->EventContext->MinorFunction) { case IRP_MN_LOCK: - if (DokanInstance->DokanOperations->LockFile) { + if (IoEvent->DokanInstance->DokanOperations->LockFile) { - status = DokanInstance->DokanOperations->LockFile( - EventContext->Operation.Lock.FileName, - EventContext->Operation.Lock.ByteOffset.QuadPart, - EventContext->Operation.Lock.Length.QuadPart, + status = IoEvent->DokanInstance->DokanOperations->LockFile( + IoEvent->EventContext->Operation.Lock.FileName, + IoEvent->EventContext->Operation.Lock.ByteOffset.QuadPart, + IoEvent->EventContext->Operation.Lock.Length.QuadPart, // EventContext->Operation.Lock.Key, - &fileInfo); + &IoEvent->DokanFileInfo); if (status != STATUS_NOT_IMPLEMENTED) { - eventInfo->Status = + IoEvent->EventResult->Status = status != STATUS_SUCCESS ? STATUS_LOCK_NOT_GRANTED : STATUS_SUCCESS; } } @@ -62,29 +60,25 @@ VOID DispatchLock(HANDLE Handle, PEVENT_CONTEXT EventContext, case IRP_MN_UNLOCK_ALL_BY_KEY: break; case IRP_MN_UNLOCK_SINGLE: - if (DokanInstance->DokanOperations->UnlockFile) { + if (IoEvent->DokanInstance->DokanOperations->UnlockFile) { - status = DokanInstance->DokanOperations->UnlockFile( - EventContext->Operation.Lock.FileName, - EventContext->Operation.Lock.ByteOffset.QuadPart, - EventContext->Operation.Lock.Length.QuadPart, + status = IoEvent->DokanInstance->DokanOperations->UnlockFile( + IoEvent->EventContext->Operation.Lock.FileName, + IoEvent->EventContext->Operation.Lock.ByteOffset.QuadPart, + IoEvent->EventContext->Operation.Lock.Length.QuadPart, // EventContext->Operation.Lock.Key, - &fileInfo); + &IoEvent->DokanFileInfo); if (status != STATUS_NOT_IMPLEMENTED) { - eventInfo->Status = + IoEvent->EventResult->Status = STATUS_SUCCESS; // always succeeds so it cannot fail ? } } break; default: - DbgPrint("unknown lock function %d\n", EventContext->MinorFunction); + DbgPrint("unknown lock function %d\n", + IoEvent->EventContext->MinorFunction); } - if (openInfo != NULL) - openInfo->UserContext = fileInfo.Context; - - SendEventInformation(Handle, eventInfo, sizeOfEventInfo); - ReleaseDokanOpenInfo(eventInfo, &fileInfo, DokanInstance); - free(eventInfo); + EventCompletion(IoEvent); } diff --git a/dokan/mount.c b/dokan/mount.c index 393fa5477..eba935df0 100644 --- a/dokan/mount.c +++ b/dokan/mount.c @@ -504,7 +504,7 @@ BOOL EnableTokenPrivilege(LPCTSTR lpszSystemName, BOOL bEnable) { return FALSE; } -void DokanBroadcastLink(WCHAR cLetter, BOOL bRemoved, BOOL safe) { +VOID DokanBroadcastLink(WCHAR cLetter, BOOL bRemoved) { DWORD receipients; DWORD device_event; DEV_BROADCAST_VOLUME params; @@ -518,7 +518,7 @@ void DokanBroadcastLink(WCHAR cLetter, BOOL bRemoved, BOOL safe) { receipients = BSM_APPLICATIONS; // Unsafe to call Advapi32.dll during DLL_PROCESS_DETACH - if (safe && EnableTokenPrivilege(SE_TCB_NAME, TRUE)) { + if (EnableTokenPrivilege(SE_TCB_NAME, TRUE)) { receipients |= BSM_ALLDESKTOPS; } @@ -539,12 +539,9 @@ void DokanBroadcastLink(WCHAR cLetter, BOOL bRemoved, BOOL safe) { GetLastError()); } - // Unsafe to call ole32.dll during DLL_PROCESS_DETACH - if (safe) { - drive[0] = towupper(cLetter); - wEventId = bRemoved ? SHCNE_DRIVEREMOVED : SHCNE_DRIVEADD; - SHChangeNotify(wEventId, SHCNF_PATH, drive, NULL); - } + drive[0] = towupper(cLetter); + wEventId = bRemoved ? SHCNE_DRIVEREMOVED : SHCNE_DRIVEADD; + SHChangeNotify(wEventId, SHCNF_PATH, drive, NULL); } BOOL DokanMount(LPCWSTR MountPoint, LPCWSTR DeviceName, @@ -559,13 +556,13 @@ BOOL DokanMount(LPCWSTR MountPoint, LPCWSTR DeviceName, return CreateMountPoint(MountPoint, DeviceName); } else { // Notify applications / explorer - DokanBroadcastLink(MountPoint[0], FALSE, TRUE); + DokanBroadcastLink(MountPoint[0], FALSE); } } return TRUE; } -BOOL DokanRemoveMountPointEx(LPCWSTR MountPoint, BOOL Safe) { +BOOL DOKANAPI DokanRemoveMountPoint(LPCWSTR MountPoint) { if (MountPoint != NULL) { size_t length = wcslen(MountPoint); if (length > 0) { @@ -587,7 +584,7 @@ BOOL DokanRemoveMountPointEx(LPCWSTR MountPoint, BOOL Safe) { return DeleteMountPoint(mountPoint); } else { // Notify applications / explorer - DokanBroadcastLink(MountPoint[0], TRUE, Safe); + DokanBroadcastLink(MountPoint[0], TRUE); return TRUE; } } @@ -595,7 +592,3 @@ BOOL DokanRemoveMountPointEx(LPCWSTR MountPoint, BOOL Safe) { } return FALSE; } - -BOOL DOKANAPI DokanRemoveMountPoint(LPCWSTR MountPoint) { - return DokanRemoveMountPointEx(MountPoint, TRUE); -} diff --git a/dokan/read.c b/dokan/read.c index 007f8ed7f..cf798d706 100644 --- a/dokan/read.c +++ b/dokan/read.c @@ -22,46 +22,43 @@ with this program. If not, see . #include "dokani.h" -VOID DispatchRead(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance) { - PEVENT_INFORMATION eventInfo; - PDOKAN_OPEN_INFO openInfo; +VOID DispatchRead(PDOKAN_IO_EVENT IoEvent) { ULONG readLength = 0; NTSTATUS status = STATUS_NOT_IMPLEMENTED; - DOKAN_FILE_INFO fileInfo; - ULONG sizeOfEventInfo = DispatchGetEventInformationLength( - EventContext->Operation.Read.BufferLength); - CheckFileName(EventContext->Operation.Read.FileName); + CheckFileName(IoEvent->EventContext->Operation.Read.FileName); - eventInfo = DispatchCommon(EventContext, sizeOfEventInfo, DokanInstance, - &fileInfo, &openInfo); + CreateDispatchCommon(IoEvent, + IoEvent->EventContext->Operation.Read.BufferLength); - DbgPrint("###Read %04d\n", openInfo != NULL ? openInfo->EventId : -1); + DbgPrint("###Read file handle = 0x%p, eventID = %04d, event Info = 0x%p\n", + IoEvent->DokanOpenInfo, + IoEvent->DokanOpenInfo != NULL ? IoEvent->DokanOpenInfo->EventId + : -1, + IoEvent); - if (DokanInstance->DokanOperations->ReadFile) { - status = DokanInstance->DokanOperations->ReadFile( - EventContext->Operation.Read.FileName, eventInfo->Buffer, - EventContext->Operation.Read.BufferLength, &readLength, - EventContext->Operation.Read.ByteOffset.QuadPart, &fileInfo); + if (IoEvent->DokanInstance->DokanOperations->ReadFile) { + status = IoEvent->DokanInstance->DokanOperations->ReadFile( + IoEvent->EventContext->Operation.Read.FileName, + IoEvent->EventResult->Buffer, + IoEvent->EventContext->Operation.Read.BufferLength, &readLength, + IoEvent->EventContext->Operation.Read.ByteOffset.QuadPart, + &IoEvent->DokanFileInfo); } - if (openInfo != NULL) - openInfo->UserContext = fileInfo.Context; - eventInfo->BufferLength = 0; - eventInfo->Status = status; + IoEvent->EventResult->BufferLength = 0; + IoEvent->EventResult->Status = status; if (status == STATUS_SUCCESS) { if (readLength == 0) { - eventInfo->Status = STATUS_END_OF_FILE; + IoEvent->EventResult->Status = STATUS_END_OF_FILE; } else { - eventInfo->BufferLength = readLength; - eventInfo->Operation.Read.CurrentByteOffset.QuadPart = - EventContext->Operation.Read.ByteOffset.QuadPart + readLength; + IoEvent->EventResult->BufferLength = readLength; + IoEvent->EventResult->Operation.Read.CurrentByteOffset.QuadPart = + IoEvent->EventContext->Operation.Read.ByteOffset.QuadPart + + readLength; } } - SendEventInformation(Handle, eventInfo, sizeOfEventInfo); - ReleaseDokanOpenInfo(eventInfo, &fileInfo, DokanInstance); - free(eventInfo); + EventCompletion(IoEvent); } diff --git a/dokan/security.c b/dokan/security.c index ce4623e1e..815be71e5 100644 --- a/dokan/security.c +++ b/dokan/security.c @@ -127,95 +127,90 @@ NTSTATUS DefaultGetFileSecurity(LPCWSTR FileName, return STATUS_SUCCESS; } -VOID DispatchQuerySecurity(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance) { - PEVENT_INFORMATION eventInfo; - DOKAN_FILE_INFO fileInfo; - PDOKAN_OPEN_INFO openInfo; +VOID DispatchQuerySecurity(PDOKAN_IO_EVENT IoEvent) { NTSTATUS status = STATUS_NOT_IMPLEMENTED; ULONG lengthNeeded = 0; - ULONG eventInfoLength = DispatchGetEventInformationLength( - EventContext->Operation.Security.BufferLength); - CheckFileName(EventContext->Operation.Security.FileName); - - eventInfo = DispatchCommon(EventContext, eventInfoLength, DokanInstance, - &fileInfo, &openInfo); - - DbgPrint("###GetFileSecurity %04d\n", - openInfo != NULL ? openInfo->EventId : -1); - - if (DokanInstance->DokanOperations->GetFileSecurity) { - status = DokanInstance->DokanOperations->GetFileSecurity( - EventContext->Operation.Security.FileName, - &EventContext->Operation.Security.SecurityInformation, - &eventInfo->Buffer, EventContext->Operation.Security.BufferLength, - &lengthNeeded, &fileInfo); + CheckFileName(IoEvent->EventContext->Operation.Security.FileName); + + CreateDispatchCommon(IoEvent, + IoEvent->EventContext->Operation.Security.BufferLength); + + DbgPrint("###GetFileSecurity file handle = 0x%p, eventID = %04d, event Info " + "= 0x%p\n", + IoEvent->DokanOpenInfo, + IoEvent->DokanOpenInfo != NULL ? IoEvent->DokanOpenInfo->EventId + : -1, + IoEvent); + + if (IoEvent->DokanInstance->DokanOperations->GetFileSecurity) { + status = IoEvent->DokanInstance->DokanOperations->GetFileSecurity( + IoEvent->EventContext->Operation.Security.FileName, + &IoEvent->EventContext->Operation.Security.SecurityInformation, + &IoEvent->EventResult->Buffer, + IoEvent->EventContext->Operation.Security.BufferLength, &lengthNeeded, + &IoEvent->DokanFileInfo); } if (status == STATUS_NOT_IMPLEMENTED) { status = DefaultGetFileSecurity( - EventContext->Operation.Security.FileName, - &EventContext->Operation.Security.SecurityInformation, - &eventInfo->Buffer, EventContext->Operation.Security.BufferLength, - &lengthNeeded, &fileInfo); + IoEvent->EventContext->Operation.Security.FileName, + &IoEvent->EventContext->Operation.Security.SecurityInformation, + &IoEvent->EventResult->Buffer, + IoEvent->EventContext->Operation.Security.BufferLength, &lengthNeeded, + &IoEvent->DokanFileInfo); } - eventInfo->Status = status; + IoEvent->EventResult->Status = status; if (status != STATUS_SUCCESS && status != STATUS_BUFFER_OVERFLOW) { - eventInfo->BufferLength = 0; + IoEvent->EventResult->BufferLength = 0; } else { - eventInfo->BufferLength = lengthNeeded; + IoEvent->EventResult->BufferLength = lengthNeeded; - if (EventContext->Operation.Security.BufferLength < lengthNeeded) { + if (IoEvent->EventContext->Operation.Security.BufferLength < lengthNeeded) { // Filesystem Application should return STATUS_BUFFER_OVERFLOW in this // case. - eventInfo->Status = STATUS_BUFFER_OVERFLOW; + IoEvent->EventResult->Status = STATUS_BUFFER_OVERFLOW; } } - SendEventInformation(Handle, eventInfo, eventInfoLength); - ReleaseDokanOpenInfo(eventInfo, &fileInfo, DokanInstance); - free(eventInfo); + EventCompletion(IoEvent); } -VOID DispatchSetSecurity(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance) { - PEVENT_INFORMATION eventInfo; - DOKAN_FILE_INFO fileInfo; - PDOKAN_OPEN_INFO openInfo; +VOID DispatchSetSecurity(PDOKAN_IO_EVENT IoEvent) { NTSTATUS status = STATUS_NOT_IMPLEMENTED; PSECURITY_DESCRIPTOR securityDescriptor; - ULONG eventInfoLength = DispatchGetEventInformationLength(0); - CheckFileName(EventContext->Operation.SetSecurity.FileName); + CheckFileName(IoEvent->EventContext->Operation.SetSecurity.FileName); - eventInfo = DispatchCommon(EventContext, eventInfoLength, DokanInstance, - &fileInfo, &openInfo); + CreateDispatchCommon(IoEvent, 0); - DbgPrint("###SetSecurity %04d\n", openInfo != NULL ? openInfo->EventId : -1); + DbgPrint( + "###SetSecurity file handle = 0x%p, eventID = %04d, event Info = 0x%p\n", + IoEvent->DokanOpenInfo, + IoEvent->DokanOpenInfo != NULL ? IoEvent->DokanOpenInfo->EventId : -1, + IoEvent); securityDescriptor = - (PCHAR)EventContext + EventContext->Operation.SetSecurity.BufferOffset; - - if (DokanInstance->DokanOperations->SetFileSecurity) { - status = DokanInstance->DokanOperations->SetFileSecurity( - EventContext->Operation.SetSecurity.FileName, - &EventContext->Operation.SetSecurity.SecurityInformation, - securityDescriptor, EventContext->Operation.SetSecurity.BufferLength, - &fileInfo); + (PCHAR)IoEvent->EventContext + + IoEvent->EventContext->Operation.SetSecurity.BufferOffset; + + if (IoEvent->DokanInstance->DokanOperations->SetFileSecurity) { + status = IoEvent->DokanInstance->DokanOperations->SetFileSecurity( + IoEvent->EventContext->Operation.SetSecurity.FileName, + &IoEvent->EventContext->Operation.SetSecurity.SecurityInformation, + securityDescriptor, + IoEvent->EventContext->Operation.SetSecurity.BufferLength, &IoEvent->DokanFileInfo); } if (status != STATUS_SUCCESS) { - eventInfo->Status = STATUS_INVALID_PARAMETER; - eventInfo->BufferLength = 0; + IoEvent->EventResult->Status = STATUS_INVALID_PARAMETER; + IoEvent->EventResult->BufferLength = 0; } else { - eventInfo->Status = STATUS_SUCCESS; - eventInfo->BufferLength = 0; + IoEvent->EventResult->Status = STATUS_SUCCESS; + IoEvent->EventResult->BufferLength = 0; } - SendEventInformation(Handle, eventInfo, eventInfoLength); - ReleaseDokanOpenInfo(eventInfo, &fileInfo, DokanInstance); - free(eventInfo); + EventCompletion(IoEvent); } diff --git a/dokan/setfile.c b/dokan/setfile.c index 251d2bece..a338de076 100644 --- a/dokan/setfile.c +++ b/dokan/setfile.c @@ -199,51 +199,55 @@ DokanSetValidDataLengthInformation(PEVENT_CONTEXT EventContext, FileInfo); } -VOID DispatchSetInformation(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance) { - PEVENT_INFORMATION eventInfo; - PDOKAN_OPEN_INFO openInfo; - DOKAN_FILE_INFO fileInfo; +VOID DispatchSetInformation(PDOKAN_IO_EVENT IoEvent) { NTSTATUS status = STATUS_INVALID_PARAMETER; - ULONG sizeOfEventInfo = DispatchGetEventInformationLength(0); - - if (EventContext->Operation.SetFile.FileInformationClass == FileRenameInformation - || EventContext->Operation.SetFile.FileInformationClass == FileRenameInformationEx) { - PDOKAN_RENAME_INFORMATION renameInfo = (PDOKAN_RENAME_INFORMATION)( - (PCHAR)EventContext + EventContext->Operation.SetFile.BufferOffset); - sizeOfEventInfo = - DispatchGetEventInformationLength(renameInfo->FileNameLength); + ULONG fileInformationClass = + IoEvent->EventContext->Operation.SetFile.FileInformationClass; + + if (fileInformationClass == FileRenameInformation || + fileInformationClass == FileRenameInformationEx) { + PDOKAN_RENAME_INFORMATION renameInfo = + (PDOKAN_RENAME_INFORMATION)((PCHAR)IoEvent->EventContext + + IoEvent->EventContext->Operation.SetFile + .BufferOffset); + CreateDispatchCommon(IoEvent, renameInfo->FileNameLength); + } else { + CreateDispatchCommon(IoEvent, 0); } - CheckFileName(EventContext->Operation.SetFile.FileName); - - eventInfo = DispatchCommon(EventContext, sizeOfEventInfo, DokanInstance, - &fileInfo, &openInfo); + CheckFileName(IoEvent->EventContext->Operation.SetFile.FileName); - DbgPrint("###SetFileInfo %04d %d\n", - openInfo != NULL ? openInfo->EventId : -1, - EventContext->Operation.SetFile.FileInformationClass); + DbgPrint( + "###SetFileInfo file handle = 0x%p, eventID = %04d, FileInformationClass " + "= %d, event Info = 0x%p\n", + IoEvent->DokanOpenInfo, + IoEvent->DokanOpenInfo != NULL ? IoEvent->DokanOpenInfo->EventId : -1, + fileInformationClass, IoEvent); - switch (EventContext->Operation.SetFile.FileInformationClass) { + switch (fileInformationClass) { case FileAllocationInformation: - status = DokanSetAllocationInformation(EventContext, &fileInfo, - DokanInstance->DokanOperations); + status = + DokanSetAllocationInformation(IoEvent->EventContext, &IoEvent->DokanFileInfo, + IoEvent->DokanInstance->DokanOperations); break; case FileBasicInformation: - status = DokanSetBasicInformation(EventContext, &fileInfo, - DokanInstance->DokanOperations); + status = + DokanSetBasicInformation(IoEvent->EventContext, &IoEvent->DokanFileInfo, + IoEvent->DokanInstance->DokanOperations); break; case FileDispositionInformation: case FileDispositionInformationEx: - status = DokanSetDispositionInformation(EventContext, &fileInfo, - DokanInstance->DokanOperations); + status = DokanSetDispositionInformation( + IoEvent->EventContext, &IoEvent->DokanFileInfo, + IoEvent->DokanInstance->DokanOperations); break; case FileEndOfFileInformation: - status = DokanSetEndOfFileInformation(EventContext, &fileInfo, - DokanInstance->DokanOperations); + status = DokanSetEndOfFileInformation( + IoEvent->EventContext, &IoEvent->DokanFileInfo, + IoEvent->DokanInstance->DokanOperations); break; case FilePositionInformation: @@ -253,43 +257,43 @@ VOID DispatchSetInformation(HANDLE Handle, PEVENT_CONTEXT EventContext, case FileRenameInformation: case FileRenameInformationEx: - status = DokanSetRenameInformation(EventContext, &fileInfo, - DokanInstance->DokanOperations); + status = DokanSetRenameInformation(IoEvent->EventContext, + &IoEvent->DokanFileInfo, + IoEvent->DokanInstance->DokanOperations); break; case FileValidDataLengthInformation: - status = DokanSetValidDataLengthInformation(EventContext, &fileInfo, - DokanInstance->DokanOperations); + status = DokanSetValidDataLengthInformation( + IoEvent->EventContext, &IoEvent->DokanFileInfo, + IoEvent->DokanInstance->DokanOperations); break; default: - DbgPrint(" unknown FileInformationClass %d\n", - EventContext->Operation.SetFile.FileInformationClass); + DbgPrint(" unknown FileInformationClass %d\n", fileInformationClass); break; } - if (openInfo != NULL) - openInfo->UserContext = fileInfo.Context; - eventInfo->BufferLength = 0; - eventInfo->Status = status; + IoEvent->EventResult->BufferLength = 0; + IoEvent->EventResult->Status = status; if (status == STATUS_SUCCESS) { - if (EventContext->Operation.SetFile.FileInformationClass == FileDispositionInformation || - EventContext->Operation.SetFile.FileInformationClass == FileDispositionInformationEx) { - eventInfo->Operation.Delete.DeleteOnClose = fileInfo.DeleteOnClose; - DbgPrint(" dispositionInfo->DeleteFile = %d\n", fileInfo.DeleteOnClose); - } else if (EventContext->Operation.SetFile.FileInformationClass == FileRenameInformation || - EventContext->Operation.SetFile.FileInformationClass == FileRenameInformationEx) { - PDOKAN_RENAME_INFORMATION renameInfo = (PDOKAN_RENAME_INFORMATION)( - (PCHAR)EventContext + EventContext->Operation.SetFile.BufferOffset); - eventInfo->BufferLength = renameInfo->FileNameLength; - CopyMemory(eventInfo->Buffer, renameInfo->FileName, + if (fileInformationClass == FileDispositionInformation || + fileInformationClass == FileDispositionInformationEx) { + IoEvent->EventResult->Operation.Delete.DeleteOnClose = + IoEvent->DokanFileInfo.DeleteOnClose; + DbgPrint(" dispositionInfo->DeleteFile = %d\n", IoEvent->DokanFileInfo.DeleteOnClose); + } else if (fileInformationClass == FileRenameInformation || + fileInformationClass == FileRenameInformationEx) { + PDOKAN_RENAME_INFORMATION renameInfo = + (PDOKAN_RENAME_INFORMATION)((PCHAR)IoEvent->EventContext + + IoEvent->EventContext->Operation.SetFile + .BufferOffset); + IoEvent->EventResult->BufferLength = renameInfo->FileNameLength; + CopyMemory(IoEvent->EventResult->Buffer, renameInfo->FileName, renameInfo->FileNameLength); } } DbgPrint("\tDispatchSetInformation result = %lx\n", status); - SendEventInformation(Handle, eventInfo, sizeOfEventInfo); - ReleaseDokanOpenInfo(eventInfo, &fileInfo, DokanInstance); - free(eventInfo); + EventCompletion(IoEvent); } diff --git a/dokan/timeout.c b/dokan/timeout.c index 583ea6e07..0be5b63d9 100644 --- a/dokan/timeout.c +++ b/dokan/timeout.c @@ -62,55 +62,4 @@ BOOL DOKANAPI DokanResetTimeout(ULONG Timeout, PDOKAN_FILE_INFO FileInfo) { FSCTL_RESET_TIMEOUT, eventInfo, eventInfoSize, NULL, 0, &returnedLength); free(eventInfo); return status; -} - -/* Legacy KeepAlive - Remove for 2.0.0 */ -UINT WINAPI DokanKeepAlive(PVOID instance) { - PDOKAN_INSTANCE DokanInstance = (PDOKAN_INSTANCE)instance; - HANDLE device; - ULONG ReturnedLength; - WCHAR rawDeviceName[MAX_PATH]; - - GetRawDeviceName(DokanInstance->DeviceName, rawDeviceName, MAX_PATH); - - while (TRUE) { - - device = CreateFile(rawDeviceName, - GENERIC_READ | GENERIC_WRITE, // dwDesiredAccess - FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode - NULL, // lpSecurityAttributes - OPEN_EXISTING, // dwCreationDistribution - 0, // dwFlagsAndAttributes - NULL // hTemplateFile - ); - - if (device == INVALID_HANDLE_VALUE) { - DbgPrint( - "Dokan Error: DokanKeepAlive CreateFile failed %ws: %d\n", - rawDeviceName, - GetLastError()); - break; - } - - BOOL status = DeviceIoControl(device, // Handle to device - IOCTL_KEEPALIVE, // IO Control code - NULL, // Input Buffer to driver. - 0, // Length of input buffer in bytes. - NULL, // Output Buffer from driver. - 0, // Length of output buffer in bytes. - &ReturnedLength, // Bytes placed in buffer. - NULL // synchronous call - ); - - CloseHandle(device); - - if (!status) { - break; - } - - Sleep(DOKAN_KEEPALIVE_TIME); - } - - _endthreadex(0); - return STATUS_SUCCESS; -} +} \ No newline at end of file diff --git a/dokan/volume.c b/dokan/volume.c index 717e01500..da9d844a0 100644 --- a/dokan/volume.c +++ b/dokan/volume.c @@ -311,61 +311,44 @@ DokanFsFullSizeInformation(PEVENT_INFORMATION EventInfo, return STATUS_SUCCESS; } -VOID DispatchQueryVolumeInformation(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance) { - PEVENT_INFORMATION eventInfo; - DOKAN_FILE_INFO fileInfo; - PDOKAN_OPEN_INFO openInfo; - ULONG sizeOfEventInfo = DispatchGetEventInformationLength( - EventContext->Operation.Volume.BufferLength); - - eventInfo = (PEVENT_INFORMATION)malloc(sizeOfEventInfo); - if (eventInfo == NULL) { - return; - } - - RtlZeroMemory(eventInfo, sizeOfEventInfo); - RtlZeroMemory(&fileInfo, sizeof(DOKAN_FILE_INFO)); - - // There is no Context because file is not opened - // so DispatchCommon is not used here - openInfo = (PDOKAN_OPEN_INFO)(INT_PTR)EventContext->Context; - - eventInfo->BufferLength = 0; - eventInfo->SerialNumber = EventContext->SerialNumber; - - fileInfo.ProcessId = EventContext->ProcessId; - fileInfo.DokanOptions = DokanInstance->DokanOptions; +VOID DispatchQueryVolumeInformation(PDOKAN_IO_EVENT IoEvent) { + CreateDispatchCommon(IoEvent, + IoEvent->EventContext->Operation.Volume.BufferLength); - eventInfo->Status = STATUS_INVALID_PARAMETER; - eventInfo->BufferLength = 0; + DbgPrint("###QueryVolumeInfo file handle = 0x%p, eventID = %04d, event Info " + "= 0x%p\n", + IoEvent->DokanOpenInfo, + IoEvent->DokanOpenInfo != NULL ? IoEvent->DokanOpenInfo->EventId + : -1, + IoEvent); - DbgPrint("###QueryVolumeInfo %04d\n", - openInfo != NULL ? openInfo->EventId : -1); + IoEvent->EventResult->Status = STATUS_INVALID_PARAMETER; - switch (EventContext->Operation.Volume.FsInformationClass) { + switch (IoEvent->EventContext->Operation.Volume.FsInformationClass) { case FileFsVolumeInformation: - eventInfo->Status = DokanFsVolumeInformation( - eventInfo, EventContext, &fileInfo, DokanInstance->DokanOperations); + IoEvent->EventResult->Status = DokanFsVolumeInformation( + IoEvent->EventResult, IoEvent->EventContext, &IoEvent->DokanFileInfo, + IoEvent->DokanInstance->DokanOperations); break; case FileFsSizeInformation: - eventInfo->Status = DokanFsSizeInformation( - eventInfo, EventContext, &fileInfo, DokanInstance->DokanOperations); + IoEvent->EventResult->Status = DokanFsSizeInformation( + IoEvent->EventResult, IoEvent->EventContext, &IoEvent->DokanFileInfo, + IoEvent->DokanInstance->DokanOperations); break; case FileFsAttributeInformation: - eventInfo->Status = DokanFsAttributeInformation( - eventInfo, EventContext, &fileInfo, DokanInstance->DokanOperations); + IoEvent->EventResult->Status = DokanFsAttributeInformation( + IoEvent->EventResult, IoEvent->EventContext, &IoEvent->DokanFileInfo, + IoEvent->DokanInstance->DokanOperations); break; case FileFsFullSizeInformation: - eventInfo->Status = DokanFsFullSizeInformation( - eventInfo, EventContext, &fileInfo, DokanInstance->DokanOperations); + IoEvent->EventResult->Status = DokanFsFullSizeInformation( + IoEvent->EventResult, IoEvent->EventContext, &IoEvent->DokanFileInfo, + IoEvent->DokanInstance->DokanOperations); break; default: DbgPrint("error unknown volume info %d\n", - EventContext->Operation.Volume.FsInformationClass); + IoEvent->EventContext->Operation.Volume.FsInformationClass); } - SendEventInformation(Handle, eventInfo, sizeOfEventInfo); - ReleaseDokanOpenInfo(eventInfo, &fileInfo, DokanInstance); - free(eventInfo); + EventCompletion(IoEvent); } diff --git a/dokan/write.c b/dokan/write.c index c4e767d21..9d3596059 100644 --- a/dokan/write.c +++ b/dokan/write.c @@ -21,117 +21,99 @@ with this program. If not, see . */ #include "dokani.h" +#include "dokan_pool.h" -BOOL SendWriteRequest(_In_ HANDLE Handle, _In_ PEVENT_INFORMATION EventInfo, - _In_ ULONG EventLength, _Out_ PVOID Buffer, _In_ ULONG BufferLength, - _Out_ ULONG *ReturnedLengthOutPointer, _Out_ DWORD *LastError) { - - BOOL status; - ULONG returnedLength; - - DbgPrint("SendWriteRequest\n"); - - status = DeviceIoControl(Handle, // Handle to device - FSCTL_EVENT_WRITE, // IO Control code - EventInfo, // Input Buffer to driver. - EventLength, // Length of input buffer in bytes. - Buffer, // Output Buffer from driver. - BufferLength, // Length of output buffer in bytes. - &returnedLength, // Bytes placed in buffer. - NULL // synchronous call - ); - - if (!status) { - DWORD errorCode = GetLastError(); - DbgPrint("Ioctl failed with code %d\n", errorCode); - *LastError = errorCode; - } - else { - *LastError = 0; - } - - *ReturnedLengthOutPointer = returnedLength; +#include - DbgPrint("SendWriteRequest got %d bytes\n", returnedLength); - return status; +DWORD SendWriteRequest(PDOKAN_IO_EVENT IoEvent, ULONG WriteEventContextLength) { + DWORD WrittenLength = 0; + DOKAN_IO_BATCH *writeIoBatch = + malloc((SIZE_T)FIELD_OFFSET(DOKAN_IO_BATCH, EventContext) + + WriteEventContextLength); + if (!writeIoBatch) { + DokanDbgPrintW(L"Dokan Error: Failed to allocate IO event buffer.\n"); + return ERROR_NO_SYSTEM_RESOURCES; + } + ZeroMemory(writeIoBatch, (SIZE_T)FIELD_OFFSET(DOKAN_IO_BATCH, EventContext) + + WriteEventContextLength); + writeIoBatch->DokanInstance = IoEvent->DokanInstance; + PushIoBatchBuffer(IoEvent->IoBatch); + IoEvent->IoBatch = writeIoBatch; + IoEvent->EventContext = IoEvent->IoBatch->EventContext; + + if (!DeviceIoControl( + IoEvent->DokanInstance->Device, // Handle to device + FSCTL_EVENT_WRITE, // IO Control code + IoEvent->EventResult, // Input Buffer to driver. + IoEvent->EventResultSize, // Length of input buffer in bytes. + &writeIoBatch->EventContext[0], // Output Buffer from driver. + WriteEventContextLength, // Length of output buffer in bytes. + &WrittenLength, // Bytes placed in buffer. + NULL // asynchronous call + )) { + return GetLastError(); + } + return 0; } -VOID DispatchWrite(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance) { - PEVENT_INFORMATION eventInfo; - PDOKAN_OPEN_INFO openInfo; +VOID DispatchWrite(PDOKAN_IO_EVENT IoEvent) { ULONG writtenLength = 0; NTSTATUS status; - DOKAN_FILE_INFO fileInfo; - BOOL bufferAllocated = FALSE; - ULONG returnedLength = 0; - BOOL SendWriteRequestStatus = TRUE; // otherwise DokanInstance->DokanOperations->WriteFile cannot be called - DWORD SendWriteRequestLastError = 0; - ULONG sizeOfEventInfo = DispatchGetEventInformationLength(0); - eventInfo = DispatchCommon(EventContext, sizeOfEventInfo, DokanInstance, - &fileInfo, &openInfo); + CreateDispatchCommon(IoEvent, 0); + + CheckFileName(IoEvent->EventContext->Operation.Write.FileName); + + DbgPrint( + "###WriteFile file handle = 0x%p, eventID = %04d, event Info = 0x%p\n", + IoEvent->DokanOpenInfo, + IoEvent->DokanOpenInfo != NULL ? IoEvent->DokanOpenInfo->EventId : -1, + IoEvent); // Since driver requested bigger memory, // allocate enough memory and send it to driver - if (EventContext->Operation.Write.RequestLength > 0) { - ULONG contextLength = EventContext->Operation.Write.RequestLength; - PEVENT_CONTEXT contextBuf = (PEVENT_CONTEXT)malloc(contextLength); - if (contextBuf == NULL) { - free(eventInfo); + if (IoEvent->EventContext->Operation.Write.RequestLength > 0) { + DWORD error = SendWriteRequest(IoEvent, IoEvent->EventContext->Operation.Write.RequestLength); + if (error != ERROR_SUCCESS) { + if (error == ERROR_OPERATION_ABORTED) { + IoEvent->EventResult->Status = STATUS_CANCELLED; + DbgPrint( + "WriteFile Error : User should already canceled the operation. " + "Return STATUS_CANCELLED. \n"); + } else { + IoEvent->EventResult->Status = DokanNtStatusFromWin32(error); + DbgPrint("Unknown SendWriteRequest Error : LastError from " + "SendWriteRequest = %lu. \nUnknown SendWriteRequest error : " + "EventContext had been destoryed. Status = %X. \n", + error, IoEvent->EventResult->Status); + } + EventCompletion(IoEvent); return; } - - SendWriteRequestStatus = SendWriteRequest(Handle, eventInfo, sizeOfEventInfo, contextBuf, - contextLength, &returnedLength, &SendWriteRequestLastError); - EventContext = contextBuf; - bufferAllocated = TRUE; } - CheckFileName(EventContext->Operation.Write.FileName); - - DbgPrint("###WriteFile %04d\n", openInfo != NULL ? openInfo->EventId : -1); - - if (!SendWriteRequestStatus) { - if (SendWriteRequestLastError == ERROR_OPERATION_ABORTED) { - status = STATUS_CANCELLED; - DbgPrint("WriteFile Error : User should already canceled the operation. Return STATUS_CANCELLED. \n"); - } - else { - status = DokanNtStatusFromWin32(SendWriteRequestLastError); - DbgPrint("Unknown SendWriteRequest Error : LastError from SendWriteRequest = %lu. \nUnknown SendWriteRequest error : EventContext had been destoryed. Status = %X. \n", SendWriteRequestLastError, status); - } - } - else { - // for the case SendWriteRequest success - if (DokanInstance->DokanOperations->WriteFile) { - status = DokanInstance->DokanOperations->WriteFile( - EventContext->Operation.Write.FileName, - (PCHAR)EventContext + EventContext->Operation.Write.BufferOffset, - EventContext->Operation.Write.BufferLength, &writtenLength, - EventContext->Operation.Write.ByteOffset.QuadPart, &fileInfo); - } - else { - status = STATUS_NOT_IMPLEMENTED; - } + // for the case SendWriteRequest success + if (IoEvent->DokanInstance->DokanOperations->WriteFile) { + status = IoEvent->DokanInstance->DokanOperations->WriteFile( + IoEvent->EventContext->Operation.Write.FileName, + (PCHAR)IoEvent->EventContext + + IoEvent->EventContext->Operation.Write.BufferOffset, + IoEvent->EventContext->Operation.Write.BufferLength, &writtenLength, + IoEvent->EventContext->Operation.Write.ByteOffset.QuadPart, &IoEvent->DokanFileInfo); + } else { + status = STATUS_NOT_IMPLEMENTED; } - if (openInfo != NULL) - openInfo->UserContext = fileInfo.Context; - eventInfo->Status = status; - eventInfo->BufferLength = 0; + IoEvent->EventResult->Status = status; + IoEvent->EventResult->BufferLength = 0; if (status == STATUS_SUCCESS) { - eventInfo->BufferLength = writtenLength; - eventInfo->Operation.Write.CurrentByteOffset.QuadPart = - EventContext->Operation.Write.ByteOffset.QuadPart + writtenLength; + IoEvent->EventResult->BufferLength = writtenLength; + IoEvent->EventResult->Operation.Write.CurrentByteOffset.QuadPart = + IoEvent->EventContext->Operation.Write.ByteOffset.QuadPart + + writtenLength; } - SendEventInformation(Handle, eventInfo, sizeOfEventInfo); - ReleaseDokanOpenInfo(eventInfo, &fileInfo, DokanInstance); - free(eventInfo); - - if (bufferAllocated) - free(EventContext); + EventCompletion(IoEvent); } diff --git a/dokan_fuse/include/dokanfuse.h b/dokan_fuse/include/dokanfuse.h index 72cf5e198..4e0c03c7a 100644 --- a/dokan_fuse/include/dokanfuse.h +++ b/dokan_fuse/include/dokanfuse.h @@ -54,9 +54,13 @@ struct fuse_chan //This method dynamically loads DOKAN functions bool init(); + typedef VOID (__stdcall *DokanInitType)(); + typedef VOID(__stdcall *DokanShutdownType)(); typedef int (__stdcall *DokanMainType)(PDOKAN_OPTIONS,PDOKAN_OPERATIONS); typedef BOOL (__stdcall *DokanUnmountType)(WCHAR DriveLetter); typedef BOOL (__stdcall *DokanRemoveMountPointType)(LPCWSTR MountPoint); + DokanInitType ResolvedDokanInit = nullptr; + DokanShutdownType ResolvedDokanShutdown = nullptr; DokanMainType ResolvedDokanMain = nullptr; DokanUnmountType ResolvedDokanUnmount = nullptr; DokanRemoveMountPointType ResolvedDokanRemoveMountPoint = nullptr; diff --git a/dokan_fuse/src/dokanfuse.cpp b/dokan_fuse/src/dokanfuse.cpp index d00fb9e0c..d7ca26d6f 100644 --- a/dokan_fuse/src/dokanfuse.cpp +++ b/dokan_fuse/src/dokanfuse.cpp @@ -526,7 +526,7 @@ int do_fuse_loop(struct fuse *fs, bool mt) { dokanOptions->Version = DOKAN_VERSION; dokanOptions->MountPoint = mount; - dokanOptions->ThreadCount = mt ? FUSE_THREAD_COUNT : 1; + dokanOptions->SingleThread = !mt; dokanOptions->Timeout = fs->conf.timeoutInSec * 1000; dokanOptions->AllocationUnitSize = fs->conf.allocationUnitSize; dokanOptions->SectorSize = fs->conf.sectorSize; @@ -565,6 +565,10 @@ bool fuse_chan::init() { if (!ResolvedDokanVersion || ResolvedDokanVersion() < DOKAN_VERSION) return false; + ResolvedDokanInit = + reinterpret_cast(GetProcAddress(dokanDll, "DokanInit")); + ResolvedDokanShutdown = reinterpret_cast( + GetProcAddress(dokanDll, "DokanShutdown")); ResolvedDokanMain = reinterpret_cast(GetProcAddress(dokanDll, "DokanMain")); ResolvedDokanUnmount = reinterpret_cast(GetProcAddress(dokanDll, "DokanUnmount")); @@ -574,12 +578,15 @@ bool fuse_chan::init() { if (!ResolvedDokanMain || !ResolvedDokanUnmount || !ResolvedDokanRemoveMountPoint) return false; + ResolvedDokanInit(); return true; } fuse_chan::~fuse_chan() { - if (dokanDll) + if (dokanDll) { + ResolvedDokanShutdown(); FreeLibrary(dokanDll); + } } /////////////////////////////////////////////////////////////////////////////////////// diff --git a/samples/dokan_memfs/main.cpp b/samples/dokan_memfs/main.cpp index f5c213dca..449b7c04b 100644 --- a/samples/dokan_memfs/main.cpp +++ b/samples/dokan_memfs/main.cpp @@ -93,8 +93,10 @@ int __cdecl wmain(ULONG argc, PWCHAR argv[]) { } } } + DokanInit(); // Start the memory filesystem dokan_memfs->run(); + DokanShutdown(); } catch (const std::exception& ex) { spdlog::error("dokan_memfs failure: {}", ex.what()); return 1; diff --git a/samples/dokan_memfs/memfs.cpp b/samples/dokan_memfs/memfs.cpp index aaf40fdbb..686db6317 100644 --- a/samples/dokan_memfs/memfs.cpp +++ b/samples/dokan_memfs/memfs.cpp @@ -36,7 +36,8 @@ void memfs::run() { DOKAN_OPTIONS dokan_options; ZeroMemory(&dokan_options, sizeof(DOKAN_OPTIONS)); dokan_options.Version = DOKAN_VERSION; - dokan_options.Options = DOKAN_OPTION_ALT_STREAM | DOKAN_OPTION_CASE_SENSITIVE; + dokan_options.Options = DOKAN_OPTION_ALT_STREAM | + DOKAN_OPTION_CASE_SENSITIVE; dokan_options.MountPoint = mount_point; if (debug_log) { dokan_options.Options |= DOKAN_OPTION_STDERR | DOKAN_OPTION_DEBUG; @@ -66,7 +67,6 @@ void memfs::run() { dokan_options.Options |= DOKAN_OPTION_CURRENT_SESSION; } - dokan_options.ThreadCount = thread_number; dokan_options.Timeout = timeout; dokan_options.GlobalContext = reinterpret_cast(fs_filenodes.get()); diff --git a/samples/dokan_memfs/memfs_operations.cpp b/samples/dokan_memfs/memfs_operations.cpp index ec4df30d0..09e30783f 100644 --- a/samples/dokan_memfs/memfs_operations.cpp +++ b/samples/dokan_memfs/memfs_operations.cpp @@ -722,13 +722,14 @@ static NTSTATUS DOKAN_CALLBACK memfs_setfilesecurity( static NTSTATUS DOKAN_CALLBACK memfs_findstreams(LPCWSTR filename, PFillFindStreamData fill_findstreamdata, - PDOKAN_FILE_INFO dokanfileinfo) { + PVOID findstreamcontext, PDOKAN_FILE_INFO dokanfileinfo) { auto filenodes = GET_FS_INSTANCE; auto filename_str = std::wstring(filename); spdlog::info(L"FindStreams: {}", filename_str); auto f = filenodes->find(filename_str); - if (!f) return STATUS_OBJECT_NAME_NOT_FOUND; + if (!f) + return STATUS_OBJECT_NAME_NOT_FOUND; auto streams = f->get_streams(); @@ -744,11 +745,14 @@ memfs_findstreams(LPCWSTR filename, PFillFindStreamData fill_findstreamdata, stream_data.cStreamName[memfs_helper::DataStreamNameStr.length() + 1] = L'\0'; stream_data.StreamSize.QuadPart = f->get_filesize(); - fill_findstreamdata(&stream_data, dokanfileinfo); + if (!fill_findstreamdata(&stream_data, findstreamcontext)) { + return STATUS_BUFFER_OVERFLOW; + } } else if (streams.empty()) { // The node is a directory without any alternate streams return STATUS_END_OF_FILE; } + // Add the alternated stream attached // for \foo:bar we need to return in the form of bar:$DATA for (const auto &stream : streams) { @@ -772,7 +776,9 @@ memfs_findstreams(LPCWSTR filename, PFillFindStreamData fill_findstreamdata, stream_data.StreamSize.QuadPart = stream.second->get_filesize(); spdlog::info(L"FindStreams: {} StreamName: {} Size: {:x}", filename_str, stream_names.second, stream_data.StreamSize.QuadPart); - fill_findstreamdata(&stream_data, dokanfileinfo); + if (!fill_findstreamdata(&stream_data, findstreamcontext)) { + return STATUS_BUFFER_OVERFLOW; + } } return STATUS_SUCCESS; } diff --git a/samples/dokan_mirror/mirror.c b/samples/dokan_mirror/mirror.c index 641d7ee04..f2cf71d16 100644 --- a/samples/dokan_mirror/mirror.c +++ b/samples/dokan_mirror/mirror.c @@ -1454,7 +1454,10 @@ NTSYSCALLAPI NTSTATUS NTAPI NtQueryInformationFile( NTSTATUS DOKAN_CALLBACK MirrorFindStreams(LPCWSTR FileName, PFillFindStreamData FillFindStreamData, + PVOID FindStreamContext, PDOKAN_FILE_INFO DokanFileInfo) { + UNREFERENCED_PARAMETER(DokanFileInfo); + WCHAR filePath[DOKAN_MAX_PATH]; HANDLE hFind; WIN32_FIND_STREAM_DATA findData; @@ -1473,17 +1476,28 @@ MirrorFindStreams(LPCWSTR FileName, PFillFindStreamData FillFindStreamData, return DokanNtStatusFromWin32(error); } - FillFindStreamData(&findData, DokanFileInfo); - count++; - - while (FindNextStreamW(hFind, &findData) != 0) { - FillFindStreamData(&findData, DokanFileInfo); + BOOL bufferFull = FillFindStreamData(&findData, FindStreamContext); + if (bufferFull) { count++; + while (FindNextStreamW(hFind, &findData) != 0) { + bufferFull = FillFindStreamData(&findData, FindStreamContext); + if (!bufferFull) + break; + count++; + } } error = GetLastError(); FindClose(hFind); + if (!bufferFull) { + DbgPrint(L"\tFindStreams returned %d entries in %s with " + L"STATUS_BUFFER_OVERFLOW\n\n", + count, filePath); + // https://msdn.microsoft.com/en-us/library/windows/hardware/ff540364(v=vs.85).aspx + return STATUS_BUFFER_OVERFLOW; + } + if (error != ERROR_HANDLE_EOF) { DbgPrint(L"\tFindNextStreamW error. Error is %u\n\n", error); return DokanNtStatusFromWin32(error); @@ -1530,7 +1544,7 @@ void ShowUsage() { fprintf(stderr, "mirror.exe - Mirror a local device or folder to secondary device, an NTFS folder or a network device.\n" " /r RootDirectory (ex. /r c:\\test)\t\t Directory source to mirror.\n" " /l MountPoint (ex. /l m)\t\t\t Mount point. Can be M:\\ (drive letter) or empty NTFS folder C:\\mount\\dokan .\n" - " /t ThreadCount (ex. /t 5)\t\t\t Number of threads to be used internally by Dokan library.\n\t\t\t\t\t\t More threads will handle more event at the same time.\n" + " /t Single thread (ex. /t 5)\t\t\t Only use a single thread to process events.\n\t\t\t\t\t\t This is highly not recommended as can easily create a bottleneck.\n" " /d (enable debug output)\t\t\t Enable debug output to an attached debugger.\n" " /s (use stderr for output)\t\t\t Enable debug output to stderr.\n" " /n (use network drive)\t\t\t Show device as network device.\n" @@ -1582,7 +1596,6 @@ int __cdecl wmain(ULONG argc, PWCHAR argv[]) { ZeroMemory(&dokanOptions, sizeof(DOKAN_OPTIONS)); dokanOptions.Version = DOKAN_VERSION; - dokanOptions.ThreadCount = 0; // use default for (command = 1; command < argc; command++) { switch (towlower(argv[command][1])) { @@ -1602,9 +1615,8 @@ int __cdecl wmain(ULONG argc, PWCHAR argv[]) { wcscpy_s(MountPoint, sizeof(MountPoint) / sizeof(WCHAR), argv[command]); dokanOptions.MountPoint = MountPoint; break; - case L't': - CHECK_CMD_ARG(command, argc) - dokanOptions.ThreadCount = (USHORT)_wtoi(argv[command]); + case L't': + dokanOptions.SingleThread = TRUE; break; case L'd': g_DebugMode = TRUE; @@ -1630,9 +1642,6 @@ int __cdecl wmain(ULONG argc, PWCHAR argv[]) { case L'f': dokanOptions.Options |= DOKAN_OPTION_FILELOCK_USER_MODE; break; - case L'z': - dokanOptions.Options |= DOKAN_OPTION_ENABLE_FCB_GARBAGE_COLLECTION; - break; case L'x': dokanOptions.Options |= DOKAN_OPTION_ENABLE_UNMOUNT_NETWORK_DRIVE; break; @@ -1760,7 +1769,9 @@ int __cdecl wmain(ULONG argc, PWCHAR argv[]) { dokanOperations.FindStreams = MirrorFindStreams; dokanOperations.Mounted = MirrorMounted; + DokanInit(); status = DokanMain(&dokanOptions, &dokanOperations); + DokanShutdown(); switch (status) { case DOKAN_SUCCESS: fprintf(stderr, "Success\n"); diff --git a/sys/close.c b/sys/close.c index 0553f3036..7f365c05c 100644 --- a/sys/close.c +++ b/sys/close.c @@ -121,7 +121,7 @@ Return Value: // Close can not be pending status don't register this IRP // inform it to user-mode - DokanEventNotification(&RequestContext->Dcb->NotifyEvent, eventContext); + DokanEventNotification(RequestContext, &RequestContext->Dcb->NotifyEvent, eventContext); return STATUS_SUCCESS; } diff --git a/sys/device.c b/sys/device.c index ba7a64743..a4fcc2940 100644 --- a/sys/device.c +++ b/sys/device.c @@ -759,12 +759,6 @@ VolumeDeviceControl(__in PREQUEST_CONTEXT RequestContext) { NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST; switch (RequestContext->IrpSp->Parameters.DeviceIoControl.IoControlCode) { - case IOCTL_EVENT_WAIT: - status = DokanRegisterPendingIrpForEvent(RequestContext); - break; - case IOCTL_EVENT_INFO: - status = DokanCompleteIrp(RequestContext); - break; case IOCTL_EVENT_RELEASE: status = DokanEventRelease(RequestContext, RequestContext->DeviceObject); break; @@ -774,18 +768,6 @@ VolumeDeviceControl(__in PREQUEST_CONTEXT RequestContext) { case IOCTL_GET_VOLUME_METRICS: status = DokanGetVolumeMetrics(RequestContext); break; - case IOCTL_KEEPALIVE: { - //Remove for Dokan 2.x.x - if (!IsFlagOn(RequestContext->Vcb->Flags, VCB_MOUNTED)) { - status = STATUS_INSUFFICIENT_RESOURCES; - } - ExEnterCriticalRegionAndAcquireResourceExclusive( - &RequestContext->Dcb->Resource); - DokanUpdateTimeout(&RequestContext->Dcb->TickCount, - DOKAN_KEEPALIVE_TIMEOUT_DEFAULT); - ExReleaseResourceAndLeaveCriticalRegion(&RequestContext->Dcb->Resource); - status = STATUS_SUCCESS; - } break; case IOCTL_RESET_TIMEOUT: status = DokanResetPendingIrpTimeout(RequestContext); break; diff --git a/sys/dokan.h b/sys/dokan.h index cac69bb21..cca1b3ea8 100644 --- a/sys/dokan.h +++ b/sys/dokan.h @@ -225,10 +225,12 @@ typedef struct _DokanDiskControlBlock { PVOID Vcb; - // the list of waiting Event + // Pending IRPs IRP_LIST PendingIrp; - IRP_LIST PendingEvent; + // Pending IRPs waiting to be dispatched to userland IRP_LIST NotifyEvent; + LIST_ENTRY NotifyIrpEventQueueList; + KQUEUE NotifyIrpEventQueue; // IRPs that need to be retried in kernel mode, e.g. due to oplock breaks // asynchronously requested on an earlier try. These are IRPs that have never // yet been dispatched to user mode. The IRPs are supposed to be added here at @@ -912,13 +914,6 @@ VOID DokanOplockComplete(IN PVOID Context, IN PIRP Irp); VOID DokanPrePostIrp(IN PVOID Context, IN PIRP Irp); -NTSTATUS -DokanRegisterPendingIrpForEvent(__in PREQUEST_CONTEXT RequestContext); - -// Currently not used -NTSTATUS -DokanRegisterPendingIrpForService(__in PREQUEST_CONTEXT RequestContext); - NTSTATUS DokanCompleteIrp(__in PREQUEST_CONTEXT RequestContext); @@ -979,7 +974,8 @@ VOID DokanRegisterPendingRetryIrp(__in PREQUEST_CONTEXT RequestContext); VOID DokanRegisterAsyncCreateFailure(__in PREQUEST_CONTEXT RequestContext, __in NTSTATUS Status); -VOID DokanEventNotification(__in PIRP_LIST NotifyEvent, +VOID DokanEventNotification(__in PREQUEST_CONTEXT RequestContext, + __in PIRP_LIST NotifyEvent, __in PEVENT_CONTEXT EventContext); VOID DokanCompleteDirectoryControl(__in PREQUEST_CONTEXT RequestContext, diff --git a/sys/event.c b/sys/event.c index 3051bd677..013d58724 100644 --- a/sys/event.c +++ b/sys/event.c @@ -306,8 +306,10 @@ DokanRegisterPendingIrp(__in PREQUEST_CONTEXT RequestContext, /*SetEvent=*/FALSE); } + if (status == STATUS_PENDING) { - DokanEventNotification(&RequestContext->Dcb->NotifyEvent, EventContext); + DokanEventNotification(RequestContext, &RequestContext->Dcb->NotifyEvent, + EventContext); } else { DokanFreeEventContext(EventContext); } @@ -347,23 +349,6 @@ VOID DokanRegisterAsyncCreateFailure(__in PREQUEST_CONTEXT RequestContext, KeSetEvent(&RequestContext->Dcb->ForceTimeoutEvent, 0, FALSE); } -NTSTATUS -DokanRegisterPendingIrpForEvent(__in PREQUEST_CONTEXT RequestContext) { - // TODO(adrienj): Remove the check when moving to FSCTL only. - if (RequestContext->Vcb == NULL) { - return STATUS_INVALID_PARAMETER; - } - - RequestContext->Vcb->HasEventWait = TRUE; - - return RegisterPendingIrpMain(RequestContext, - NULL, // EventContext - &RequestContext->Dcb->PendingEvent, - TRUE, - /*CurrentStatus=*/STATUS_SUCCESS, - /*SetEvent=*/TRUE); -} - void DokanDispatchCompletion(__in PDEVICE_OBJECT DeviceObject, __in PIRP_ENTRY irpEntry, __in PEVENT_INFORMATION eventInfo) { @@ -454,9 +439,9 @@ GetEventInfoSize(__in ULONG MajorFunction, __in PEVENT_INFORMATION EventInfo) { // used length is a value not specified in the struct. return sizeof(EVENT_INFORMATION); } - return max(sizeof(EVENT_INFORMATION), - sizeof(EVENT_INFORMATION) - sizeof(EventInfo->Buffer) - + EventInfo->BufferLength); + return max((ULONG)sizeof(EVENT_INFORMATION), + FIELD_OFFSET(EVENT_INFORMATION, Buffer[0]) + + (ULONG)EventInfo->BufferLength); } // When user-mode file system application returns EventInformation, @@ -477,22 +462,6 @@ DokanCompleteIrp(__in PREQUEST_CONTEXT RequestContext) { ULONG bufferLength = 0; PCHAR buffer = NULL; - bufferLength = - RequestContext->IrpSp->Parameters.DeviceIoControl.InputBufferLength; - // Dokan 1.x.x Library can send buffer under EVENT_INFO struct size: - // - IRP_MJ_QUERY_SECURITY sending STATUS_BUFFER_OVERFLOW - // - IRP_MJ_READ with negative read size - // The behavior was fixed since but adding the next line would break - // backward compatiblity. - // TODO 2.x.x - use GET_IRP_BUFFER_OR_RETURN(Irp, eventInfo); - /*if (bufferLength < sizeof(EVENT_INFORMATION)) { - DOKAN_LOG_FINE_IRP(RequestContext, "Wrong input buffer length"); - return STATUS_BUFFER_TOO_SMALL; - }*/ - - buffer = (PCHAR)RequestContext->Irp->AssociatedIrp.SystemBuffer; - ASSERT(buffer != NULL); - // TODO(adrienj): Remove the check when moving to FSCTL only. if (RequestContext->Vcb == NULL) { return STATUS_INVALID_PARAMETER; @@ -502,6 +471,16 @@ DokanCompleteIrp(__in PREQUEST_CONTEXT RequestContext) { return STATUS_NO_SUCH_DEVICE; } + bufferLength = + RequestContext->IrpSp->Parameters.DeviceIoControl.InputBufferLength; + if (bufferLength < sizeof(EVENT_INFORMATION)) { + DOKAN_LOG_FINE_IRP(RequestContext, "Wrong input buffer length"); + return STATUS_BUFFER_TOO_SMALL; + } + + buffer = (PCHAR)RequestContext->Irp->AssociatedIrp.SystemBuffer; + ASSERT(buffer != NULL); + InitializeListHead(&completeList); ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); @@ -528,12 +507,6 @@ DokanCompleteIrp(__in PREQUEST_CONTEXT RequestContext) { } RemoveEntryList(thisEntry); InsertTailList(&completeList, thisEntry); - // We break until 2.x.x - See function head comment -__pragma(warning(push)) -__pragma(warning(disable : 4127)) - if (1 == 1) - break; -__pragma(warning(pop)) offset += GetEventInfoSize(irpEntry->RequestContext.IrpSp->MajorFunction, eventInfo); // Everything through offset - 1 must be readable by the completion function @@ -656,7 +629,6 @@ DokanEventStart(__in PREQUEST_CONTEXT RequestContext) { BOOLEAN useMountManager = FALSE; BOOLEAN mountGlobally = TRUE; BOOLEAN fileLockUserMode = FALSE; - BOOLEAN fcbGcEnabled = FALSE; ULONG sessionId = (ULONG)-1; BOOLEAN startFailure = FALSE; BOOLEAN isMountPointDriveLetter = FALSE; @@ -755,11 +727,6 @@ DokanEventStart(__in PREQUEST_CONTEXT RequestContext) { fileLockUserMode = TRUE; } - if (eventStart->Flags & DOKAN_EVENT_ENABLE_FCB_GC) { - DOKAN_LOG_FINE_IRP(RequestContext, "FCB GC enabled\n"); - fcbGcEnabled = TRUE; - } - if (eventStart->Flags & DOKAN_EVENT_CASE_SENSITIVE) { DOKAN_LOG_FINE_IRP(RequestContext, "Case sensitive enabled\n"); } @@ -839,10 +806,12 @@ DokanEventStart(__in PREQUEST_CONTEXT RequestContext) { return DokanLogError(&logger, status, L"Disk device creation failed."); } - dcb->FcbGarbageCollectionIntervalMs = fcbGcEnabled ? 2000 : 0; + dcb->FcbGarbageCollectionIntervalMs = 2000; dcb->MountOptions = eventStart->Flags; dcb->DispatchDriverLogs = (eventStart->Flags & DOKAN_EVENT_DISPATCH_DRIVER_LOGS) != 0; + dcb->AllowIpcBatching = + (eventStart->Flags & DOKAN_EVENT_ALLOW_IPC_BATCHING) != 0; isMountPointDriveLetter = IsMountPointDriveLetter(dcb->MountPoint); if (dcb->DispatchDriverLogs) { diff --git a/sys/fileinfo.c b/sys/fileinfo.c index d637f76e7..006486660 100644 --- a/sys/fileinfo.c +++ b/sys/fileinfo.c @@ -64,6 +64,7 @@ DokanDispatchQueryInformation(__in PREQUEST_CONTEXT RequestContext) { PDokanFCB fcb = NULL; ULONG eventLength; PEVENT_CONTEXT eventContext; + BOOLEAN fcbLocked = FALSE; // PAGED_CODE(); @@ -88,7 +89,6 @@ DokanDispatchQueryInformation(__in PREQUEST_CONTEXT RequestContext) { ASSERT(fcb != NULL); OplockDebugRecordMajorFunction(fcb, IRP_MJ_QUERY_INFORMATION); - DokanFCBLockRO(fcb); switch (infoClass) { case FileAllInformation: { PFILE_ALL_INFORMATION allInfo; @@ -110,6 +110,10 @@ DokanDispatchQueryInformation(__in PREQUEST_CONTEXT RequestContext) { __leave; } + if (!fcbLocked) { + DokanFCBLockRO(fcb); + fcbLocked = TRUE; + } status = FillNameInformation(RequestContext, fcb, nameInfo); __leave; } break; @@ -138,6 +142,34 @@ DokanDispatchQueryInformation(__in PREQUEST_CONTEXT RequestContext) { __leave; } break; + case FileNetworkPhysicalNameInformation: { + // This info class is generally not worth passing to the DLL. It will be + // filled in with info that is accessible to the driver. + + PFILE_NETWORK_PHYSICAL_NAME_INFORMATION netInfo; + if (!PrepareOutputHelper( + RequestContext->Irp, &netInfo, + FIELD_OFFSET(FILE_NETWORK_PHYSICAL_NAME_INFORMATION, FileName), + /*SetInformationOnFailure=*/FALSE)) { + status = STATUS_BUFFER_OVERFLOW; + __leave; + } + + if (!fcbLocked) { + DokanFCBLockRO(fcb); + fcbLocked = TRUE; + } + + if (!AppendVarSizeOutputString(RequestContext->Irp, &netInfo->FileName, + &fcb->FileName, + /*UpdateInformationOnFailure=*/FALSE, + /*FillSpaceWithPartialString=*/FALSE)) { + status = STATUS_BUFFER_OVERFLOW; + __leave; + } + status = STATUS_SUCCESS; + __leave; + } default: DOKAN_LOG_FINE_IRP(RequestContext, "Unsupported FileInfoClass %x", infoClass); } @@ -147,6 +179,11 @@ DokanDispatchQueryInformation(__in PREQUEST_CONTEXT RequestContext) { __leave; } + if (!fcbLocked) { + DokanFCBLockRO(fcb); + fcbLocked = TRUE; + } + // If the request is not handled by the switch case we send it to userland. eventLength = sizeof(EVENT_CONTEXT) + fcb->FileName.Length; eventContext = AllocateEventContext(RequestContext, eventLength, ccb); @@ -172,7 +209,7 @@ DokanDispatchQueryInformation(__in PREQUEST_CONTEXT RequestContext) { status = DokanRegisterPendingIrp(RequestContext, eventContext); } __finally { - if (fcb) + if (fcbLocked) DokanFCBUnlock(fcb); } @@ -272,7 +309,7 @@ VOID DokanCompleteQueryInformation(__in PREQUEST_CONTEXT RequestContext, InterlockedExchange64(&header->FileSize.QuadPart, fileSize); DOKAN_LOG_FINE_IRP(RequestContext, - "AllocationSize: %llu, EndOfFile: %llu\n", + "AllocationSize: %llu, EndOfFile: %llu", allocationSize, fileSize); } } diff --git a/sys/fscontrol.c b/sys/fscontrol.c index 57170ebac..f83e7f11f 100644 --- a/sys/fscontrol.c +++ b/sys/fscontrol.c @@ -454,6 +454,98 @@ DokanGlobalUserFsRequest(__in PREQUEST_CONTEXT RequestContext) { return STATUS_INVALID_DEVICE_REQUEST; } +NTSTATUS PullEvents(__in PREQUEST_CONTEXT RequestContext, + __in PIRP_LIST NotifyEvent) { + PDRIVER_EVENT_CONTEXT workItem = NULL; + PDRIVER_EVENT_CONTEXT alreadySeenWorkItem = NULL; + PLIST_ENTRY workItemListEntry = NULL; + KIRQL workQueueIrql; + ULONG workItemBytes = 0; + ULONG currentIoctlBufferBytesRemaining = + RequestContext->IrpSp->Parameters.DeviceIoControl.OutputBufferLength; + PCHAR currentIoctlBuffer = + (PCHAR)RequestContext->Irp->AssociatedIrp.SystemBuffer; + + ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); + KeAcquireSpinLock(&NotifyEvent->ListLock, &workQueueIrql); + while (!IsListEmpty(&NotifyEvent->ListHead)) { + workItemListEntry = RemoveHeadList(&NotifyEvent->ListHead); + workItem = + CONTAINING_RECORD(workItemListEntry, DRIVER_EVENT_CONTEXT, ListEntry); + workItemBytes = workItem->EventContext.Length; + // Buffer is not specified or short of length (this may mean we filled the + // space in one of the DLL's buffers in batch mode). Put the IRP back in + // the work queue; it will have to go in a different buffer. + if (currentIoctlBufferBytesRemaining < workItemBytes) { + InsertTailList(&NotifyEvent->ListHead, &workItem->ListEntry); + if (alreadySeenWorkItem == workItem) { + // We have reached the end of the list + break; + } + alreadySeenWorkItem = workItem; + continue; + } + // Send the work item back in the response to the current IOCTL. + RtlCopyMemory(currentIoctlBuffer, &workItem->EventContext, workItemBytes); + currentIoctlBufferBytesRemaining -= workItemBytes; + currentIoctlBuffer += workItemBytes; + RequestContext->Irp->IoStatus.Information += workItemBytes; + ExFreePool(workItem); + if (!RequestContext->Dcb->AllowIpcBatching) { + // If there is still pending items we need to reflag the queue for when we come back + if (!IsListEmpty(&NotifyEvent->ListHead) && + !KeReadStateQueue(&RequestContext->Dcb->NotifyIrpEventQueue)) { + KeInsertQueue(&RequestContext->Dcb->NotifyIrpEventQueue, + &RequestContext->Dcb->NotifyIrpEventQueueList); + } + break; + } + } + KeReleaseSpinLock(&NotifyEvent->ListLock, workQueueIrql); + + RequestContext->Irp->IoStatus.Status = STATUS_SUCCESS; + return RequestContext->Irp->IoStatus.Status; +} + + +NTSTATUS DokanProcessAndPullEvents(__in PREQUEST_CONTEXT RequestContext) { + NTSTATUS status = DokanCompleteIrp(RequestContext); + if (status != STATUS_BUFFER_TOO_SMALL && status != STATUS_SUCCESS) { + DOKAN_LOG_FINE_IRP(RequestContext, "Failed to process IRP"); + return status; + } + if (RequestContext->IrpSp->Parameters.DeviceIoControl.OutputBufferLength < + sizeof(EVENT_CONTEXT)) { + DOKAN_LOG_FINE_IRP(RequestContext, "No output buffer provided"); + return status; + } + RequestContext->Vcb->HasEventWait = TRUE; + + PEVENT_INFORMATION eventInfo = + (PEVENT_INFORMATION)(RequestContext->Irp->AssociatedIrp.SystemBuffer); + + ULONG waitTimeoutMs = + status == STATUS_BUFFER_TOO_SMALL ? 0 : eventInfo->PullEventTimeoutMs; + PLIST_ENTRY listEntry; + LARGE_INTEGER timeout; + KeQuerySystemTime(&timeout); + timeout.QuadPart += waitTimeoutMs * 1000; // Ms to 100 nano + + ULONG result = + KeRemoveQueueEx(&RequestContext->Dcb->NotifyIrpEventQueue, KernelMode, + TRUE, waitTimeoutMs ? &timeout : NULL, &listEntry, 1); + if (result == STATUS_TIMEOUT) { + return STATUS_SUCCESS; + } + // Were we awake due to the device being unmount ? + if (IsUnmountPendingVcb(RequestContext->Vcb)) { + DOKAN_LOG_FINE_IRP(RequestContext, "Volume is not mounted"); + return STATUS_NO_SUCH_DEVICE; + } + + return PullEvents(RequestContext, &RequestContext->Dcb->NotifyEvent); +} + NTSTATUS DokanDiskUserFsRequest(__in PREQUEST_CONTEXT RequestContext) { REQUEST_CONTEXT requestContext = *RequestContext; @@ -461,10 +553,8 @@ DokanDiskUserFsRequest(__in PREQUEST_CONTEXT RequestContext) { // following function to expected being called to a Dcb. requestContext.Vcb = requestContext.Dcb->Vcb; switch (RequestContext->IrpSp->Parameters.FileSystemControl.FsControlCode) { - case FSCTL_EVENT_WAIT: - return DokanRegisterPendingIrpForEvent(&requestContext); - case FSCTL_EVENT_INFO: - return DokanCompleteIrp(&requestContext); + case FSCTL_EVENT_PROCESS_N_PULL: + return DokanProcessAndPullEvents(&requestContext); case FSCTL_EVENT_RELEASE: return DokanEventRelease(&requestContext, requestContext.Vcb->DeviceObject); case FSCTL_EVENT_WRITE: @@ -772,9 +862,6 @@ NTSTATUS DokanMountVolume(__in PREQUEST_CONTEXT RequestContext) { ExReleaseResourceLite(&dcb->Resource); // Start check thread - ExAcquireResourceExclusiveLite(&dcb->Resource, TRUE); - DokanUpdateTimeout(&dcb->TickCount, DOKAN_KEEPALIVE_TIMEOUT_DEFAULT * 3); - ExReleaseResourceLite(&dcb->Resource); DokanStartCheckThread(dcb); BOOLEAN isDriveLetter = IsMountPointDriveLetter(dcb->MountPoint); diff --git a/sys/init.c b/sys/init.c index 9b9671b9f..0d8642486 100644 --- a/sys/init.c +++ b/sys/init.c @@ -995,9 +995,11 @@ DokanCreateDiskDevice(__in PDRIVER_OBJECT DriverObject, __in ULONG MountId, // initialize Event and Event queue DokanInitIrpList(&dcb->PendingIrp); - DokanInitIrpList(&dcb->PendingEvent); DokanInitIrpList(&dcb->NotifyEvent); DokanInitIrpList(&dcb->PendingRetryIrp); + RtlZeroMemory(&dcb->NotifyIrpEventQueueList, sizeof(LIST_ENTRY)); + InitializeListHead(&dcb->NotifyIrpEventQueueList); + KeInitializeQueue(&dcb->NotifyIrpEventQueue, 0); KeInitializeEvent(&dcb->ReleaseEvent, NotificationEvent, FALSE); ExInitializeResourceLite(&dcb->Resource); diff --git a/sys/notification.c b/sys/notification.c index 5f2ecfcc8..22fe8ae4e 100644 --- a/sys/notification.c +++ b/sys/notification.c @@ -21,35 +21,25 @@ with this program. If not, see . */ /* - -IOCTL_EVENT_START: -DokanStartEventNotificationThread - NotificationThread - # PendingEvent has pending IPRs (IOCTL_EVENT_WAIT) - # NotifyEvent has IO events (ex.IRP_MJ_READ) - # notify NotifyEvent using PendingEvent in this loop - NotificationLoop(&Dcb->PendingEvent, &Dcb->NotifyEvent); - -IOCTL_EVENT_RELEASE: -DokanStopEventNotificationThread - IRP_MJ_READ: DokanDispatchRead DokanRegisterPendingIrp # add IRP_MJ_READ to PendingIrp list - DokanRegisterPendingIrpMain(PendingIrp) - # put MJ_READ event into NotifyEvent + RegisterPendingIrpMain(PendingIrp) + # put MJ_READ event into NotifyEvent DokanEventNotification(NotifyEvent, EventContext) -IOCTL_EVENT_WAIT: - DokanRegisterPendingIrpForEvent - # add this irp to PendingEvent list - DokanRegisterPendingIrpMain(PendingEvent) - -IOCTL_EVENT_INFO: - DokanCompleteIrp - DokanCompleteRead - +FSCTL_EVENT_PROCESS_N_PULL: + DokanProcessAndPullEvents + # Pull the previously registered event + PullEvents(NotifyEvent) + +FSCTL_EVENT_PROCESS_N_PULL: + DokanProcessAndPullEvents + # Complete the IRP process by userland + DokanCompleteIrp + # Pull the new registered event + PullEvents(NotifyEvent) */ #include "dokan.h" @@ -115,7 +105,8 @@ VOID DokanFreeEventContext(__in PEVENT_CONTEXT EventContext) { ExFreePool(driverEventContext); } -VOID DokanEventNotification(__in PIRP_LIST NotifyEvent, +VOID DokanEventNotification(__in PREQUEST_CONTEXT RequestContext, + __in PIRP_LIST NotifyEvent, __in PEVENT_CONTEXT EventContext) { PDRIVER_EVENT_CONTEXT driverEventContext = CONTAINING_RECORD(EventContext, DRIVER_EVENT_CONTEXT, EventContext); @@ -124,11 +115,14 @@ VOID DokanEventNotification(__in PIRP_LIST NotifyEvent, ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); - ExInterlockedInsertTailList(&NotifyEvent->ListHead, - &driverEventContext->ListEntry, - &NotifyEvent->ListLock); - - KeSetEvent(&NotifyEvent->NotEmpty, IO_NO_INCREMENT, FALSE); + KIRQL oldIrql; + KeAcquireSpinLock(&NotifyEvent->ListLock, &oldIrql); + InsertTailList(&NotifyEvent->ListHead, &driverEventContext->ListEntry); + if (!KeReadStateQueue(&RequestContext->Dcb->NotifyIrpEventQueue)) { + KeInsertQueue(&RequestContext->Dcb->NotifyIrpEventQueue, + &RequestContext->Dcb->NotifyIrpEventQueueList); + } + KeReleaseSpinLock(&NotifyEvent->ListLock, oldIrql); } // Moves the contents of the given Source list to Dest, discarding IRPs that @@ -347,7 +341,7 @@ VOID NotificationLoop(__in PIRP_LIST PendingIoctls, __in PIRP_LIST WorkQueue, KSTART_ROUTINE NotificationThread; VOID NotificationThread(__in PVOID pDcb) { - PKEVENT events[4]; + PKEVENT events[2]; PKWAIT_BLOCK waitBlock; NTSTATUS status; PDokanDCB Dcb = pDcb; @@ -360,20 +354,12 @@ VOID NotificationThread(__in PVOID pDcb) { return; } events[0] = &Dcb->ReleaseEvent; - events[1] = &Dcb->NotifyEvent.NotEmpty; - events[2] = &Dcb->PendingEvent.NotEmpty; - events[3] = &Dcb->PendingRetryIrp.NotEmpty; + events[1] = &Dcb->PendingRetryIrp.NotEmpty; do { - status = KeWaitForMultipleObjects(4, events, WaitAny, Executive, KernelMode, + status = KeWaitForMultipleObjects(2, events, WaitAny, Executive, KernelMode, FALSE, NULL, waitBlock); - - if (status != STATUS_WAIT_0) { - if (status == STATUS_WAIT_1 || status == STATUS_WAIT_2) { - NotificationLoop(&Dcb->PendingEvent, &Dcb->NotifyEvent, - Dcb->AllowIpcBatching); - } else { - RetryIrps(&Dcb->PendingRetryIrp); - } + if (status == STATUS_WAIT_1) { + RetryIrps(&Dcb->PendingRetryIrp); } } while (status != STATUS_WAIT_0); @@ -489,10 +475,10 @@ NTSTATUS DokanEventRelease(__in_opt PREQUEST_CONTEXT RequestContext, dcb->DiskDeviceName); ReleasePendingIrp(&dcb->PendingIrp); - ReleasePendingIrp(&dcb->PendingEvent); ReleasePendingIrp(&dcb->PendingRetryIrp); DokanStopCheckThread(dcb); DokanStopEventNotificationThread(dcb); + KeRundownQueue(&dcb->NotifyIrpEventQueue); // Note that the garbage collector thread also gets signalled to stop by // DokanStopEventNotificationThread. TODO(drivefs-team): maybe seperate out diff --git a/sys/public.h b/sys/public.h index ade629843..88ca228bf 100644 --- a/sys/public.h +++ b/sys/public.h @@ -42,16 +42,6 @@ with this program. If not, see . #define FSCTL_SET_DEBUG_MODE \ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS) -#define IOCTL_EVENT_WAIT \ - CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS) -#define FSCTL_EVENT_WAIT \ - CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_EVENT_INFO \ - CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS) -#define FSCTL_EVENT_INFO \ - CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS) - #define IOCTL_EVENT_RELEASE \ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_EVENT_RELEASE \ @@ -67,10 +57,6 @@ with this program. If not, see . #define FSCTL_EVENT_WRITE \ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x806, METHOD_OUT_DIRECT, FILE_ANY_ACCESS) -#define IOCTL_KEEPALIVE \ - CTL_CODE(FILE_DEVICE_UNKNOWN, 0x809, METHOD_NEITHER, FILE_ANY_ACCESS) -// No IOCTL version as this is now deprecated - #define IOCTL_RESET_TIMEOUT \ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x80B, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_RESET_TIMEOUT \ @@ -107,6 +93,9 @@ with this program. If not, see . #define FSCTL_GET_VOLUME_METRICS \ CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x811, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define FSCTL_EVENT_PROCESS_N_PULL \ + CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 0x812, METHOD_BUFFERED, FILE_ANY_ACCESS) + #define DRIVER_FUNC_INSTALL 0x01 #define DRIVER_FUNC_REMOVE 0x02 @@ -381,6 +370,9 @@ typedef struct _VOLUME_METRICS { #define WRITE_MAX_SIZE \ (EVENT_CONTEXT_MAX_SIZE - sizeof(EVENT_CONTEXT) - 256 * sizeof(WCHAR)) +#define DOKAN_EVENT_INFO_MIN_BUFFER_SIZE 8 +#define DOKAN_EVENT_INFO_DEFAULT_BUFFER_SIZE (1024 * 4) + typedef struct _EVENT_INFORMATION { ULONG SerialNumber; NTSTATUS Status; @@ -411,10 +403,16 @@ typedef struct _EVENT_INFORMATION { } Operation; ULONG64 Context; ULONG BufferLength; - UCHAR Buffer[8]; - + ULONG PullEventTimeoutMs; + UCHAR Buffer[DOKAN_EVENT_INFO_MIN_BUFFER_SIZE]; } EVENT_INFORMATION, *PEVENT_INFORMATION; +// By default we pool EVENT_INFORMATION objects with a 4k buffer (1 page) as most read/writes are this size +// or smaller +#define DOKAN_EVENT_INFO_DEFAULT_SIZE \ + (FIELD_OFFSET(EVENT_INFORMATION, Buffer) + \ + DOKAN_EVENT_INFO_DEFAULT_BUFFER_SIZE) + // Dokan mount options #define DOKAN_EVENT_ALTERNATIVE_STREAM_ON 1 #define DOKAN_EVENT_WRITE_PROTECT (1 << 1) @@ -423,7 +421,7 @@ typedef struct _EVENT_INFORMATION { #define DOKAN_EVENT_CURRENT_SESSION (1 << 4) #define DOKAN_EVENT_FILELOCK_USER_MODE (1 << 5) // No longer used option (1 << 6) -#define DOKAN_EVENT_ENABLE_FCB_GC (1 << 7) +// No longer used option (1 << 7) // CaseSenitive FileName: NTFS can look to be case-insensitive // but in some situation it can also be case-sensitive : // * NTFS keep the filename casing used during Create internally. @@ -438,6 +436,7 @@ typedef struct _EVENT_INFORMATION { // Enables unmounting of network drives via file explorer #define DOKAN_EVENT_ENABLE_NETWORK_UNMOUNT (1 << 9) #define DOKAN_EVENT_DISPATCH_DRIVER_LOGS (1 << 10) +#define DOKAN_EVENT_ALLOW_IPC_BATCHING (1 << 11) // Non-exclusive bits that can be set in EVENT_DRIVER_INFO.Flags for the driver // to send back extra info about what happened during a mount attempt, whether diff --git a/sys/util/log.c b/sys/util/log.c index cf4d77bd2..507e496fd 100644 --- a/sys/util/log.c +++ b/sys/util/log.c @@ -39,7 +39,7 @@ BOOLEAN g_DokanDriverLogCacheEnabled = FALSE; LONG g_DokanVcbDriverLogCacheCount = 0; DOKAN_LOG_CACHE g_DokanLogEntryList; -VOID PopDokanLogEntry(_In_ PDokanVCB Vcb); +VOID PopDokanLogEntry(_In_opt_ PVOID RequestContext, _In_ PDokanVCB Vcb); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, PushDokanLogEntry) #pragma alloc_text(PAGE, PopDokanLogEntry) @@ -336,8 +336,6 @@ PCHAR DokanGetMinorFunctionStr(UCHAR MajorFunction, UCHAR MinorFunction) { switch (MinorFunction) { CASE_STR(IOCTL_GET_VERSION) CASE_STR(IOCTL_SET_DEBUG_MODE) - CASE_STR(IOCTL_EVENT_WAIT) - CASE_STR(IOCTL_EVENT_INFO) CASE_STR(IOCTL_EVENT_RELEASE) CASE_STR(IOCTL_EVENT_START) CASE_STR(IOCTL_EVENT_WRITE) @@ -491,10 +489,6 @@ PCHAR DokanGetIoctlStr(ULONG ControlCode) { CASE_STR(FSCTL_GET_VERSION) CASE_STR(IOCTL_SET_DEBUG_MODE) CASE_STR(FSCTL_SET_DEBUG_MODE) - CASE_STR(IOCTL_EVENT_WAIT) - CASE_STR(FSCTL_EVENT_WAIT) - CASE_STR(IOCTL_EVENT_INFO) - CASE_STR(FSCTL_EVENT_INFO) CASE_STR(IOCTL_EVENT_RELEASE) CASE_STR(FSCTL_EVENT_RELEASE) CASE_STR(IOCTL_EVENT_START) @@ -513,6 +507,7 @@ PCHAR DokanGetIoctlStr(ULONG ControlCode) { CASE_STR(FSCTL_GET_VOLUME_METRICS) CASE_STR(IOCTL_MOUNTPOINT_CLEANUP) CASE_STR(FSCTL_MOUNTPOINT_CLEANUP) + CASE_STR(FSCTL_EVENT_PROCESS_N_PULL) #include "ioctl.inc" } return "Unknown"; @@ -571,7 +566,7 @@ VOID PushDokanLogEntry(_In_opt_ PVOID RequestContext, _In_ PCSTR Format, ...) { InsertTailList(&g_DokanLogEntryList.Log, &logEntry->ListEntry); if (requestContext && requestContext->Vcb && requestContext->Vcb->Dcb) { - PopDokanLogEntry(requestContext->Vcb); + PopDokanLogEntry(requestContext, requestContext->Vcb); } } __finally { DokanResourceUnlock(&(g_DokanLogEntryList.Resource)); @@ -581,7 +576,7 @@ VOID PushDokanLogEntry(_In_opt_ PVOID RequestContext, _In_ PCSTR Format, ...) { // Dispatch global and specific Vcb log messages from global // log entry cache list to userland. // Need to be called with g_DokanLogEntryList Lock RW. -VOID PopDokanLogEntry(_In_ PDokanVCB Vcb) { +VOID PopDokanLogEntry(_In_opt_ PVOID RequestContext, _In_ PDokanVCB Vcb) { PLIST_ENTRY listEntry; PLIST_ENTRY nextListEntry; PDOKAN_LOG_ENTRY logEntry; @@ -612,7 +607,10 @@ VOID PopDokanLogEntry(_In_ PDokanVCB Vcb) { dokanLogString = (PDOKAN_LOG_MESSAGE)((PCHAR)(PCHAR)eventContext + sizeof(EVENT_CONTEXT)); RtlCopyMemory(dokanLogString, &logEntry->Log, messageFullSize); - DokanEventNotification(&Vcb->Dcb->NotifyEvent, eventContext); + if (RequestContext) { + DokanEventNotification(RequestContext, &Vcb->Dcb->NotifyEvent, + eventContext); + } nextListEntry = listEntry->Flink; RemoveEntryList(listEntry); diff --git a/sys/util/log.h b/sys/util/log.h index 7f5d7776b..0c2baf95d 100644 --- a/sys/util/log.h +++ b/sys/util/log.h @@ -139,18 +139,13 @@ VOID IncrementVcbLogCacheCount(); DOKAN_LOG_INTERNAL(RequestContext, "[%p]: " Format, \ (RequestContext ? RequestContext->Irp : 0), __VA_ARGS__) -// Only allow logging events that are not Wait & Info to avoid unnecessary log +// Only allow logging events that are not pulling events to avoid unnecessary log // flood -#define DOKAN_DENIED_LOG_EVENT(IrpSp) \ - (IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL && \ - (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_EVENT_WAIT || \ - IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_EVENT_INFO)) || \ - (IrpSp->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL && \ - IrpSp->MinorFunction == IRP_MN_USER_FS_REQUEST && \ - (IrpSp->Parameters.FileSystemControl.FsControlCode == \ - IOCTL_EVENT_WAIT || \ - IrpSp->Parameters.FileSystemControl.FsControlCode == \ - IOCTL_EVENT_INFO)) +#define DOKAN_DENIED_LOG_EVENT(IrpSp) (FALSE) // \ + // (IrpSp->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL && \ + // IrpSp->MinorFunction == IRP_MN_USER_FS_REQUEST && \ + // IrpSp->Parameters.FileSystemControl.FsControlCode == \ + // FSCTL_EVENT_PROCESS_N_PULL) // Log the Irp FSCTL or IOCTL Control code. #define DOKAN_LOG_IOCTL(RequestContext, ControlCode, format, ...) \