From 63b4f87c1e9180ceb2d9346ae86ca348e42fba60 Mon Sep 17 00:00:00 2001 From: Keith Newton Date: Mon, 9 May 2016 14:07:26 -0400 Subject: [PATCH 01/20] Updated option definitions for async IO --- dokan/dokan.h | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/dokan/dokan.h b/dokan/dokan.h index 0463a7ca5..645a9d88d 100644 --- a/dokan/dokan.h +++ b/dokan/dokan.h @@ -50,15 +50,17 @@ extern "C" { #define DOKAN_MAX_INSTANCES 32 // Maximum number of dokan instances -#define DOKAN_OPTION_DEBUG 1 // ouput debug message -#define DOKAN_OPTION_STDERR 2 // ouput debug message to stderr -#define DOKAN_OPTION_ALT_STREAM 4 // use alternate stream -#define DOKAN_OPTION_WRITE_PROTECT 8 // mount drive as write-protected. -#define DOKAN_OPTION_NETWORK 16 // use network drive, you need to - // install Dokan network provider. -#define DOKAN_OPTION_REMOVABLE 32 // use removable drive -#define DOKAN_OPTION_MOUNT_MANAGER 64 // use mount manager -#define DOKAN_OPTION_CURRENT_SESSION 128 // mount the drive on current session only +#define DOKAN_OPTION_DEBUG 1 // ouput debug message +#define DOKAN_OPTION_STDERR (1 << 1) // ouput debug message to stderr +#define DOKAN_OPTION_ALT_STREAM (1 << 2) // use alternate stream +#define DOKAN_OPTION_WRITE_PROTECT (1 << 3) // mount drive as write-protected. +#define DOKAN_OPTION_NETWORK (1 << 4) // use network drive, you need to + // install Dokan network provider. +#define DOKAN_OPTION_REMOVABLE (1 << 5) // use removable drive +#define DOKAN_OPTION_MOUNT_MANAGER (1 << 6) // use mount manager +#define DOKAN_OPTION_CURRENT_SESSION (1 << 7) // mount the drive on current session only +#define DOKAN_OPTION_ASYNC_IO (1 << 8) // use asynchronous IO +#define DOKAN_OPTION_FORCE_SINGLE_THREADED (1 << 8) // Dokan uses a single thread. If DOKAN_OPTION_ASYNC_IO is specified the thread waits until the async job is over before starting another. typedef struct _DOKAN_OPTIONS { USHORT Version; // Supported Dokan Version, ex. "530" (Dokan ver 0.5.3) From 1766d8ca8f97c2f062100d9a1af963d8fa520a98 Mon Sep 17 00:00:00 2001 From: Keith Newton Date: Mon, 1 Aug 2016 18:38:21 -0400 Subject: [PATCH 02/20] Initial commit for async IO support #210 More or less rewrote all of dokan.dll to support async IO. Rewrote the Mirror example to support the new Dokan API. Still need to add async IO support. User-mode drivers can reeturn STATUS_PENDING to indicate they are performing an async operation at which point they are responsible for calling EndDispatch*() to notify the driver that the operation has completed. Still some known issues that need to be fixed. --- dokan/access.c | 28 +- dokan/cleanup.c | 37 +- dokan/close.c | 44 +- dokan/create.c | 349 ++++---- dokan/directory.c | 696 +++++++++------ dokan/dokan.c | 1550 ++++++++++++++++++++++++++------- dokan/dokan.h | 405 +++++---- dokan/dokan.vcxproj | 2 + dokan/dokan_vector.c | 306 +++++++ dokan/dokan_vector.h | 68 ++ dokan/dokanc.h | 59 +- dokan/dokani.h | 224 +++-- dokan/fileinfo.c | 494 ++++++----- dokan/fileinfo.h | 2 +- dokan/flush.c | 64 +- dokan/lock.c | 119 ++- dokan/mount.c | 103 ++- dokan/read.c | 92 +- dokan/security.c | 198 +++-- dokan/setfile.c | 434 ++++----- dokan/timeout.c | 64 +- dokan/volume.c | 493 +++++------ dokan/write.c | 194 +++-- dokan_fuse/include/fusemain.h | 10 +- dokan_fuse/src/dokanfuse.cpp | 413 +++++---- dokan_fuse/src/fusemain.cpp | 26 +- samples/dokan_mirror/mirror.c | 778 ++++++++--------- sys/public.h | 10 +- sys/write.c | 2 +- 29 files changed, 4589 insertions(+), 2675 deletions(-) create mode 100644 dokan/dokan_vector.c create mode 100644 dokan/dokan_vector.h diff --git a/dokan/access.c b/dokan/access.c index 734c692d7..34b125a0b 100644 --- a/dokan/access.c +++ b/dokan/access.c @@ -22,55 +22,51 @@ with this program. If not, see . #include "dokani.h" #include "fileinfo.h" -HANDLE DOKANAPI DokanOpenRequestorToken(PDOKAN_FILE_INFO FileInfo) { +HANDLE DOKANAPI DokanOpenRequestorToken(PDOKAN_CREATE_FILE_EVENT FileInfo) { + + PDOKAN_IO_EVENT ioEvent = (PDOKAN_IO_EVENT)FileInfo; BOOL status; ULONG returnedLength; - PDOKAN_INSTANCE instance; - PDOKAN_OPEN_INFO openInfo; - PEVENT_CONTEXT eventContext; PEVENT_INFORMATION eventInfo; HANDLE handle = INVALID_HANDLE_VALUE; ULONG eventInfoSize; WCHAR rawDeviceName[MAX_PATH]; - openInfo = (PDOKAN_OPEN_INFO)(UINT_PTR)FileInfo->DokanContext; - if (openInfo == NULL) { + if (ioEvent->DokanOpenInfo == NULL) { return INVALID_HANDLE_VALUE; } - eventContext = openInfo->EventContext; - if (eventContext == NULL) { + if (ioEvent->DokanInstance == NULL) { return INVALID_HANDLE_VALUE; } - instance = openInfo->DokanInstance; - if (instance == NULL) { - return INVALID_HANDLE_VALUE; - } - - if (eventContext->MajorFunction != IRP_MJ_CREATE) { + if (ioEvent->KernelInfo.EventContext.MajorFunction != IRP_MJ_CREATE) { return INVALID_HANDLE_VALUE; } eventInfoSize = sizeof(EVENT_INFORMATION); eventInfo = (PEVENT_INFORMATION)malloc(eventInfoSize); + if (eventInfo == NULL) { return INVALID_HANDLE_VALUE; } RtlZeroMemory(eventInfo, eventInfoSize); - eventInfo->SerialNumber = eventContext->SerialNumber; + eventInfo->SerialNumber = ioEvent->KernelInfo.EventContext.SerialNumber; status = SendToDevice( - GetRawDeviceName(instance->DeviceName, rawDeviceName, MAX_PATH), + GetRawDeviceName(ioEvent->DokanInstance->DeviceName, rawDeviceName, MAX_PATH), IOCTL_GET_ACCESS_TOKEN, eventInfo, eventInfoSize, eventInfo, eventInfoSize, &returnedLength); + if (status) { handle = eventInfo->Operation.AccessToken.Handle; } else { DbgPrintW(L"IOCTL_GET_ACCESS_TOKEN failed\n"); } + free(eventInfo); + return handle; } \ No newline at end of file diff --git a/dokan/cleanup.c b/dokan/cleanup.c index c7d4c0683..5c4c23df6 100644 --- a/dokan/cleanup.c +++ b/dokan/cleanup.c @@ -22,33 +22,28 @@ with this program. If not, see . #include "dokani.h" #include "fileinfo.h" -VOID DispatchCleanup(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance) { - PEVENT_INFORMATION eventInfo; - DOKAN_FILE_INFO fileInfo; - PDOKAN_OPEN_INFO openInfo; - ULONG sizeOfEventInfo = sizeof(EVENT_INFORMATION); +void DispatchCleanup(DOKAN_IO_EVENT *EventInfo) { - CheckFileName(EventContext->Operation.Cleanup.FileName); + PDOKAN_INSTANCE dokan = EventInfo->DokanInstance; + DOKAN_CLEANUP_EVENT *cleanupFileEvent = &EventInfo->EventInfo.Cleanup; - eventInfo = DispatchCommon(EventContext, sizeOfEventInfo, DokanInstance, - &fileInfo, &openInfo); + CheckFileName(EventInfo->KernelInfo.EventContext.Operation.Cleanup.FileName); - eventInfo->Status = STATUS_SUCCESS; // return success at any case + CreateDispatchCommon(EventInfo, 0); - DbgPrint("###Cleanup %04d\n", openInfo != NULL ? openInfo->EventId : -1); + EventInfo->EventResult->Status = STATUS_SUCCESS; // return success at any case - if (DokanInstance->DokanOperations->Cleanup) { - // ignore return value - DokanInstance->DokanOperations->Cleanup( - EventContext->Operation.Cleanup.FileName, &fileInfo); - } + DbgPrint("###Cleanup file handle = 0x%p, eventID = %04d\n", + EventInfo->DokanOpenInfo, + EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1); - if (openInfo != NULL) - openInfo->UserContext = fileInfo.Context; + if (dokan->DokanOperations->Cleanup) { + + cleanupFileEvent->DokanFileInfo = &EventInfo->DokanFileInfo; + cleanupFileEvent->FileName = EventInfo->KernelInfo.EventContext.Operation.Close.FileName; - SendEventInformation(Handle, eventInfo, sizeOfEventInfo, DokanInstance); + dokan->DokanOperations->Cleanup(cleanupFileEvent); + } - free(eventInfo); - return; + SendIoEventResult(EventInfo); } diff --git a/dokan/close.c b/dokan/close.c index ddde28227..d930b0042 100644 --- a/dokan/close.c +++ b/dokan/close.c @@ -22,40 +22,34 @@ with this program. If not, see . #include "dokani.h" #include "fileinfo.h" -VOID DispatchClose(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance) { - PEVENT_INFORMATION eventInfo; - DOKAN_FILE_INFO fileInfo; - PDOKAN_OPEN_INFO openInfo; - ULONG sizeOfEventInfo = sizeof(EVENT_INFORMATION); +void DispatchClose(DOKAN_IO_EVENT *EventInfo) { - UNREFERENCED_PARAMETER(Handle); + PDOKAN_INSTANCE dokan = EventInfo->DokanInstance; + DOKAN_CLOSE_FILE_EVENT *closeFileEvent = &EventInfo->EventInfo.CloseFile; - CheckFileName(EventContext->Operation.Close.FileName); + CheckFileName(EventInfo->KernelInfo.EventContext.Operation.Close.FileName); - eventInfo = DispatchCommon(EventContext, sizeOfEventInfo, DokanInstance, - &fileInfo, &openInfo); + CreateDispatchCommon(EventInfo, 0); - eventInfo->Status = STATUS_SUCCESS; // return success at any case + EventInfo->EventResult->Status = STATUS_SUCCESS; // return success at any case + + DbgPrint("###Close file handle = 0x%p, eventID = %04d\n", + EventInfo->DokanOpenInfo, + EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1); - DbgPrint("###Close %04d\n", openInfo != NULL ? openInfo->EventId : -1); + if (dokan->DokanOperations->CloseFile) { - if (DokanInstance->DokanOperations->CloseFile) { - // ignore return value - DokanInstance->DokanOperations->CloseFile( - EventContext->Operation.Close.FileName, &fileInfo); + closeFileEvent->DokanFileInfo = &EventInfo->DokanFileInfo; + closeFileEvent->FileName = EventInfo->KernelInfo.EventContext.Operation.Close.FileName; + + dokan->DokanOperations->CloseFile(closeFileEvent); } - // do not send it to the driver - // SendEventInformation(Handle, eventInfo, length); + if(EventInfo->DokanOpenInfo) { - if (openInfo != NULL) { - EnterCriticalSection(&DokanInstance->CriticalSection); - openInfo->OpenCount--; - LeaveCriticalSection(&DokanInstance->CriticalSection); + PushFileOpenInfo(EventInfo->DokanOpenInfo); + EventInfo->DokanOpenInfo = NULL; } - ReleaseDokanOpenInfo(eventInfo, DokanInstance); - free(eventInfo); - return; + // do not send it to the driver } diff --git a/dokan/create.c b/dokan/create.c index 6d0dc52bb..811ea3b21 100644 --- a/dokan/create.c +++ b/dokan/create.c @@ -20,6 +20,7 @@ with this program. If not, see . */ #include "dokani.h" +#include VOID SetIOSecurityContext(PEVENT_CONTEXT EventContext, PDOKAN_IO_SECURITY_CONTEXT ioSecurityContext) { @@ -86,74 +87,48 @@ VOID SetIOSecurityContext(PEVENT_CONTEXT EventContext, EventContext->Operation.Create.SecurityContext.DesiredAccess; } -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; - DWORD lastError = 0; +void BeginDispatchCreate(DOKAN_IO_EVENT *EventInfo) { + + static volatile LONG globalEventId = 0; + + PDOKAN_INSTANCE dokan = EventInfo->DokanInstance; + DOKAN_CREATE_FILE_EVENT *createFileEvent = &EventInfo->EventInfo.ZwCreateFile; 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; - BOOL childExisted = TRUE; - WCHAR *origFileName = NULL; - DWORD origOptions; - - fileName = (WCHAR *)((char *)&EventContext->Operation.Create + - EventContext->Operation.Create.FileNameOffset); + ULONG currentEventId = InterlockedIncrement(&globalEventId); + WCHAR *fileName = (WCHAR *)((char *)&EventInfo->KernelInfo.EventContext.Operation.Create + + EventInfo->KernelInfo.EventContext.Operation.Create.FileNameOffset); - CheckFileName(fileName); + assert((void*)createFileEvent == (void*)EventInfo); + + DbgPrint("###Create file handle = 0x%p, eventID = %04d\n", + EventInfo->DokanOpenInfo, + EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1); - RtlZeroMemory(&eventInfo, sizeof(EVENT_INFORMATION)); - RtlZeroMemory(&fileInfo, sizeof(DOKAN_FILE_INFO)); + // The low 24 bits of this member correspond to the CreateOptions parameter + createFileEvent->CreateOptions = EventInfo->KernelInfo.EventContext.Operation.Create.CreateOptions & FILE_VALID_OPTION_FLAGS; + // DbgPrint("Create.CreateOptions 0x%x\n", options); - eventInfo.BufferLength = 0; - eventInfo.SerialNumber = EventContext->SerialNumber; + // The high 8 bits of this parameter correspond to the Disposition parameter + createFileEvent->CreateDisposition = (EventInfo->KernelInfo.EventContext.Operation.Create.CreateOptions >> 24) & 0x000000ff; + createFileEvent->FileAttributes = EventInfo->KernelInfo.EventContext.Operation.Create.FileAttributes; + createFileEvent->ShareAccess = EventInfo->KernelInfo.EventContext.Operation.Create.ShareAccess; - fileInfo.ProcessId = EventContext->ProcessId; - fileInfo.DokanOptions = DokanInstance->DokanOptions; + if((createFileEvent->CreateOptions & FILE_NON_DIRECTORY_FILE) && (createFileEvent->CreateOptions & FILE_DIRECTORY_FILE)) { + + EndDispatchCreate(createFileEvent, STATUS_INVALID_PARAMETER); - // 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), NULL); - return; + return; } - ZeroMemory(openInfo, sizeof(DOKAN_OPEN_INFO)); - openInfo->OpenCount = 2; - openInfo->EventContext = EventContext; - openInfo->DokanInstance = DokanInstance; - fileInfo.DokanContext = (ULONG64)openInfo; - // pass it to driver and when the same handle is used get it back - eventInfo.Context = (ULONG64)openInfo; + createFileEvent->FileName = fileName; - // The high 8 bits of this parameter correspond to the Disposition parameter - disposition = - (EventContext->Operation.Create.CreateOptions >> 24) & 0x000000ff; + CheckFileName(fileName); - // 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); + assert(createFileEvent->OriginalFileName == NULL); - origOptions = options; + createFileEvent->OriginalFileName = _wcsdup(createFileEvent->FileName); - // to open directory - // even if this flag is not specifed, - // 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) { + if (EventInfo->KernelInfo.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 @@ -161,10 +136,8 @@ VOID DispatchCreate(HANDLE Handle, // This handle is not for a file. It is for // a regular request to open a directory. // https://msdn.microsoft.com/en-us/library/windows/hardware/ff548630(v=vs.85).aspx - origFileName = _wcsdup(fileName); - - options |= FILE_DIRECTORY_FILE; - options &= ~FILE_NON_DIRECTORY_FILE; + createFileEvent->CreateOptions |= FILE_DIRECTORY_FILE; + createFileEvent->CreateOptions &= ~FILE_NON_DIRECTORY_FILE; DbgPrint("SL_OPEN_TARGET_DIRECTORY specified\n"); @@ -181,160 +154,162 @@ VOID DispatchCreate(HANDLE Handle, // This handle is not for a file. It is for } if (!fileName[0]) { - fileName[0] = '\\'; - fileName[1] = 0; + ((WCHAR*)fileName)[0] = L'\\'; + ((WCHAR*)fileName)[1] = 0; } } - DbgPrint("###Create %04d\n", eventId); + assert(EventInfo->DokanOpenInfo == NULL); - openInfo->EventId = eventId++; + EventInfo->DokanOpenInfo = PopFileOpenInfo(); + EventInfo->DokanOpenInfo->DokanInstance = dokan; + EventInfo->DokanOpenInfo->EventId = currentEventId; - if (DokanInstance->DokanOperations->ZwCreateFile) { + createFileEvent->DokanFileInfo = &EventInfo->DokanFileInfo; - SetIOSecurityContext(EventContext, &ioSecurityContext); + assert(EventInfo->DokanFileInfo.IsDirectory == FALSE && EventInfo->DokanOpenInfo->IsDirectory == FALSE); - if ((EventContext->Flags & SL_OPEN_TARGET_DIRECTORY) && - DokanInstance->DokanOperations->Cleanup && - DokanInstance->DokanOperations->CloseFile) { + // Even if this flag is not specifed there can be reasons to open a directory + // so this flag is ultimately up to the user mode driver. + if(createFileEvent->CreateOptions & FILE_DIRECTORY_FILE) { - // Call SetLastError() to reset the error code to a known state - // so we can check whether or not the user-mode driver set - // ERROR_ALREADY_EXISTS - SetLastError(ERROR_SUCCESS); + EventInfo->DokanOpenInfo->IsDirectory = TRUE; + EventInfo->DokanFileInfo.IsDirectory = TRUE; + } - if (options & FILE_NON_DIRECTORY_FILE && options & FILE_DIRECTORY_FILE) - status = STATUS_INVALID_PARAMETER; - else - // This should call SetLastError(ERROR_ALREADY_EXISTS) when appropriate - status = DokanInstance->DokanOperations->ZwCreateFile( - origFileName, &ioSecurityContext, ioSecurityContext.DesiredAccess, - EventContext->Operation.Create.FileAttributes, - EventContext->Operation.Create.ShareAccess, disposition, - origOptions, &fileInfo); + if (dokan->DokanOperations->ZwCreateFile) { - if (status == STATUS_SUCCESS) { - DokanInstance->DokanOperations->Cleanup(origFileName, &fileInfo); - DokanInstance->DokanOperations->CloseFile(origFileName, &fileInfo); - } else if (GetLastError() == ERROR_FILE_NOT_FOUND) { - DbgPrint("SL_OPEN_TARGET_DIRECTORY file not found\n"); - childExisted = FALSE; - } + SetIOSecurityContext(&EventInfo->KernelInfo.EventContext, &createFileEvent->SecurityContext); - fileInfo.IsDirectory = TRUE; - } + createFileEvent->DesiredAccess = createFileEvent->SecurityContext.DesiredAccess; // Call SetLastError() to reset the error code to a known state // so we can check whether or not the user-mode driver set // ERROR_ALREADY_EXISTS SetLastError(ERROR_SUCCESS); - if (options & FILE_NON_DIRECTORY_FILE && options & FILE_DIRECTORY_FILE) - status = STATUS_INVALID_PARAMETER; - else // This should call SetLastError(ERROR_ALREADY_EXISTS) when appropriate - status = DokanInstance->DokanOperations->ZwCreateFile( - fileName, &ioSecurityContext, ioSecurityContext.DesiredAccess, - EventContext->Operation.Create.FileAttributes, - EventContext->Operation.Create.ShareAccess, disposition, options, - &fileInfo); - - lastError = GetLastError(); - if (status == STATUS_SUCCESS) { - if (!childExisted) { - eventInfo.Operation.Create.Information = FILE_DOES_NOT_EXIST; - } - } - } else { + status = dokan->DokanOperations->ZwCreateFile(createFileEvent); + + } + 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 - - DbgPrint("CreateFile status = %lx - lastError = %d\n", status, lastError); - if (status != STATUS_SUCCESS) { - if (EventContext->Flags & SL_OPEN_TARGET_DIRECTORY) { - DbgPrint("SL_OPEN_TARGET_DIRECTORY spcefied\n"); - } - eventInfo.Operation.Create.Information = FILE_DOES_NOT_EXIST; - eventInfo.Status = status; + if(status != STATUS_PENDING) { - if (status == STATUS_OBJECT_NAME_COLLISION) { - eventInfo.Operation.Create.Information = FILE_EXISTS; - } + EndDispatchCreate(createFileEvent, status); + } +} - if (STATUS_ACCESS_DENIED == status && - (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 - WCHAR *lastP = NULL; - for (WCHAR *p = fileName; *p; p++) { - if ((*p == L'\\' || *p == L'/') && p[1]) - lastP = p; - } - if (lastP) { - *lastP = 0; - } - - SetIOSecurityContext(EventContext, &ioSecurityContext); - ACCESS_MASK newDesiredAccess = - (MAXIMUM_ALLOWED & ioSecurityContext.DesiredAccess) - ? (FILE_DELETE_CHILD | FILE_LIST_DIRECTORY) - : (((DELETE & ioSecurityContext.DesiredAccess) ? FILE_DELETE_CHILD - : 0) | - ((FILE_READ_ATTRIBUTES & ioSecurityContext.DesiredAccess) - ? FILE_LIST_DIRECTORY - : 0)); - - status = DokanInstance->DokanOperations->ZwCreateFile( - fileName, &ioSecurityContext, newDesiredAccess, - EventContext->Operation.Create.FileAttributes, - EventContext->Operation.Create.ShareAccess, disposition, - options | FILE_OPEN_FOR_BACKUP_INTENT, &fileInfo); - - if (status == STATUS_SUCCESS) { - DbgPrint("Parent give us the right to delete\n"); - eventInfo.Status = STATUS_SUCCESS; - eventInfo.Operation.Create.Information = FILE_OPENED; - } - } +void DOKANAPI EndDispatchCreate(DOKAN_CREATE_FILE_EVENT *EventInfo, NTSTATUS ResultStatus) { - } else { + DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; + DWORD lastError = GetLastError(); + DOKAN_CLEANUP_EVENT cleanupEvent; + DOKAN_CLOSE_FILE_EVENT closeFileEvent; - eventInfo.Status = STATUS_SUCCESS; - eventInfo.Operation.Create.Information = FILE_OPENED; + assert(ioEvent->DokanInstance); - if (disposition == FILE_CREATE || disposition == FILE_OPEN_IF || - disposition == FILE_OVERWRITE_IF) { - eventInfo.Operation.Create.Information = FILE_CREATED; + CreateDispatchCommon(ioEvent, 0); - if (lastError == ERROR_ALREADY_EXISTS) { - if (disposition == FILE_OPEN_IF) { - eventInfo.Operation.Create.Information = FILE_OPENED; - } else if (disposition == FILE_OVERWRITE_IF) { - eventInfo.Operation.Create.Information = FILE_OVERWRITTEN; - } - } - } + // STATUS_PENDING should not be passed to this function + if(ResultStatus == STATUS_PENDING) { - if (fileInfo.IsDirectory) - eventInfo.Operation.Create.Flags |= DOKAN_FILE_DIRECTORY; - } + DbgPrint("Dokan Error: EndDispatchCreate() failed because STATUS_PENDING was supplied for ResultStatus.\n"); + ResultStatus = STATUS_INTERNAL_ERROR; + } - if (origFileName) - free(origFileName); + ioEvent->EventResult->Status = ResultStatus; - SendEventInformation(Handle, &eventInfo, sizeof(EVENT_INFORMATION), - DokanInstance); - return; -} + if(ioEvent->EventInfo.ZwCreateFile.OriginalFileName) { + + free((void*)ioEvent->EventInfo.ZwCreateFile.OriginalFileName); + ioEvent->EventInfo.ZwCreateFile.OriginalFileName = NULL; + } + + DbgPrint("Dokan Information: EndDispatchCreate() status = %lx - lastError = %d\n", ResultStatus, lastError); + + // FILE_CREATED + // FILE_DOES_NOT_EXIST + // FILE_EXISTS + // FILE_OPENED + // FILE_OVERWRITTEN + // FILE_SUPERSEDED + + if(ResultStatus != STATUS_SUCCESS) { + + if(ioEvent->KernelInfo.EventContext.Flags & SL_OPEN_TARGET_DIRECTORY) { + + DbgPrint("SL_OPEN_TARGET_DIRECTORY specified\n"); + } + + if(ioEvent->DokanOpenInfo) { + + if(ioEvent->DokanInstance->DokanOperations->Cleanup) { + + cleanupEvent.DokanFileInfo = &ioEvent->DokanFileInfo; + cleanupEvent.FileName = EventInfo->FileName; + + ioEvent->DokanInstance->DokanOperations->Cleanup(&cleanupEvent); + } + + if(ioEvent->DokanInstance->DokanOperations->CloseFile) { + + closeFileEvent.DokanFileInfo = &ioEvent->DokanFileInfo; + closeFileEvent.FileName = EventInfo->FileName; + + ioEvent->DokanInstance->DokanOperations->CloseFile(&closeFileEvent); + } + + PushFileOpenInfo(ioEvent->DokanOpenInfo); + ioEvent->DokanOpenInfo = NULL; + } + + ioEvent->EventResult->Operation.Create.Information = FILE_DOES_NOT_EXIST; + ioEvent->EventResult->Status = ResultStatus; + + if(ResultStatus == STATUS_OBJECT_NAME_COLLISION) { + + ioEvent->EventResult->Operation.Create.Information = FILE_EXISTS; + } + } + else { + + assert(ioEvent->DokanOpenInfo); + + // Pass the file handle to the driver so we can use the same handle for + // future IO operations. + ioEvent->DokanOpenInfo->IsDirectory = ioEvent->DokanFileInfo.IsDirectory; + + ioEvent->EventResult->Context = (ULONG64)ioEvent->DokanOpenInfo; + ioEvent->EventResult->Operation.Create.Information = FILE_OPENED; + + if(EventInfo->CreateDisposition == FILE_CREATE + || EventInfo->CreateDisposition == FILE_OPEN_IF + || EventInfo->CreateDisposition == FILE_OVERWRITE_IF) { + + ioEvent->EventResult->Operation.Create.Information = FILE_CREATED; + + if(lastError == ERROR_ALREADY_EXISTS) { + + if(EventInfo->CreateDisposition == FILE_OPEN_IF) { + + ioEvent->EventResult->Operation.Create.Information = FILE_OPENED; + } + else if(EventInfo->CreateDisposition == FILE_OVERWRITE_IF) { + + ioEvent->EventResult->Operation.Create.Information = FILE_OVERWRITTEN; + } + } + } + + if(ioEvent->DokanFileInfo.IsDirectory) { + + ioEvent->EventResult->Operation.Create.Flags |= DOKAN_FILE_DIRECTORY; + } + } + + SendIoEventResult(ioEvent); +} \ No newline at end of file diff --git a/dokan/directory.c b/dokan/directory.c index 3f209a681..f803e1fb6 100644 --- a/dokan/directory.c +++ b/dokan/directory.c @@ -23,6 +23,8 @@ with this program. If not, see . #include "fileinfo.h" #include "list.h" +#include + #if _MSC_VER < 1300 // VC6 typedef ULONG ULONG_PTR; #endif @@ -272,203 +274,320 @@ 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; +int DokanFillFileDataEx(DOKAN_IO_EVENT *EventInfo, PWIN32_FIND_DATAW FindData) { + + assert(EventInfo->ProcessingContext); - findData = (PDOKAN_FIND_DATA)malloc(sizeof(DOKAN_FIND_DATA)); - if (findData == NULL) { - return 0; - } - ZeroMemory(findData, sizeof(DOKAN_FIND_DATA)); - InitializeListHead(&findData->ListEntry); + DOKAN_VECTOR *dirList = (DOKAN_VECTOR*)EventInfo->ProcessingContext; - findData->FindData = *FindData; + DokanVector_PushBack(dirList, FindData); - if (InsertTail) - InsertTailList(listHead, &findData->ListEntry); - else - InsertHeadList(listHead, &findData->ListEntry); - return 0; + return 0; } -int WINAPI DokanFillFileData(PWIN32_FIND_DATAW FindData, - PDOKAN_FILE_INFO FileInfo) { - return DokanFillFileDataEx(FindData, FileInfo, TRUE); +int WINAPI DokanFillFileData(PDOKAN_FIND_FILES_EVENT EventInfo, PWIN32_FIND_DATAW FindData) { + + return DokanFillFileDataEx((DOKAN_IO_EVENT*)EventInfo, FindData); } -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); - } +int WINAPI DokanFillFileDataWithPattern(PDOKAN_FIND_FILES_PATTERN_EVENT EventInfo, PWIN32_FIND_DATAW FindData) { + + return DokanFillFileDataEx((DOKAN_IO_EVENT*)EventInfo, FindData); } // 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; +LONG MatchFiles(DOKAN_IO_EVENT *EventInfo, DOKAN_VECTOR *dirList) { - ULONG lengthRemaining = EventInfo->BufferLength; - PVOID currentBuffer = EventInfo->Buffer; - PVOID lastBuffer = currentBuffer; - ULONG index = 0; + ULONG lengthRemaining = EventInfo->KernelInfo.EventContext.Operation.Directory.BufferLength; + PVOID currentBuffer = EventInfo->EventResult->Buffer; + PVOID lastBuffer = currentBuffer; + ULONG index = 0; + BOOL patternCheck = FALSE; + PWCHAR pattern = NULL; - PWCHAR pattern = NULL; + if(EventInfo->KernelInfo.EventContext.Operation.Directory.SearchPatternLength > 0) { - // 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); - } + pattern = (PWCHAR)( + (SIZE_T)&EventInfo->KernelInfo.EventContext.Operation.Directory.SearchPatternBase[0] + + (SIZE_T)EventInfo->KernelInfo.EventContext.Operation.Directory.SearchPatternOffset); + } - listHead = FindDataList; + if(pattern && wcscmp(pattern, L"*") != 0 + && !EventInfo->DokanInstance->DokanOperations->FindFilesWithPattern) { - for (thisEntry = listHead->Flink; thisEntry != listHead; - thisEntry = nextEntry) { + patternCheck = TRUE; + } - PDOKAN_FIND_DATA find; - nextEntry = thisEntry->Flink; + for(size_t i = 0; i < DokanVector_GetCount(dirList); ++i) { - find = CONTAINING_RECORD(thisEntry, DOKAN_FIND_DATA, ListEntry); + 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); + DbgPrintW(L"FileMatch? : %s (%s,%d,%d)\n", find->FindData.cFileName, + (pattern ? pattern : L"null"), + EventInfo->KernelInfo.EventContext.Operation.Directory.FileIndex, index); - // pattern is not specified or pattern match is ignore cases - if (!pattern || - DokanIsNameInExpression(pattern, find->FindData.cFileName, TRUE)) { + // pattern is not specified or pattern match is ignore cases + if(!patternCheck || + DokanIsNameInExpression(pattern, find->FindData.cFileName, TRUE)) { - if (EventContext->Operation.Directory.FileIndex <= index) { - // index+1 is very important, should use next entry index - ULONG entrySize = DokanFillDirectoryInformation( - EventContext->Operation.Directory.FileInformationClass, - currentBuffer, &lengthRemaining, &find->FindData, index + 1, - DokanInstance); - // buffer is full - if (entrySize == 0) - break; + if(EventInfo->KernelInfo.EventContext.Operation.Directory.FileIndex <= index) { - // pointer of the current last entry - lastBuffer = currentBuffer; + // index+1 is very important, should use next entry index - // end if needs to return single entry - if (EventContext->Flags & SL_RETURN_SINGLE_ENTRY) { - DbgPrint(" =>return single entry\n"); - index++; - break; - } + ULONG entrySize = DokanFillDirectoryInformation( + EventInfo->KernelInfo.EventContext.Operation.Directory.FileInformationClass, + currentBuffer, &lengthRemaining, &find->FindData, index + 1, + EventInfo->DokanInstance); - DbgPrint(" =>return\n"); + // buffer is full + if(entrySize == 0) { - // the offset of next entry - ((PFILE_BOTH_DIR_INFORMATION)currentBuffer)->NextEntryOffset = - entrySize; + break; + } - // next buffer position - currentBuffer = (PCHAR)currentBuffer + entrySize; - } - index++; - } - } + // pointer of the current last entry + lastBuffer = currentBuffer; + + // end if needs to return single entry + if(EventInfo->KernelInfo.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; + } + + index++; + } + } - // Since next of the last entry doesn't exist, clear next offset - ((PFILE_BOTH_DIR_INFORMATION)lastBuffer)->NextEntryOffset = 0; + // 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; + // acctualy used length of buffer + EventInfo->EventResult->BufferLength = + EventInfo->KernelInfo.EventContext.Operation.Directory.BufferLength - lengthRemaining; - // NO_MORE_FILES - if (index <= EventContext->Operation.Directory.FileIndex) - return -1; + // NO_MORE_FILES + if(index <= EventInfo->KernelInfo.EventContext.Operation.Directory.FileIndex) { - return index; + return -1; + } + + return index; } -VOID AddMissingCurrentAndParentFolder(PEVENT_CONTEXT EventContext, - PLIST_ENTRY FindDataList, - PDOKAN_FILE_INFO fileInfo) { - PLIST_ENTRY thisEntry, listHead, nextEntry; +void AddMissingCurrentAndParentFolder(DOKAN_IO_EVENT *EventInfo) { + PWCHAR pattern = NULL; BOOLEAN currentFolder = FALSE, parentFolder = FALSE; WIN32_FIND_DATAW findData; FILETIME systime; + DOKAN_VECTOR *dirList = (DOKAN_VECTOR*)EventInfo->ProcessingContext; + + assert(dirList); + + if (EventInfo->KernelInfo.EventContext.Operation.Directory.SearchPatternLength != 0) { - if (EventContext->Operation.Directory.SearchPatternLength != 0) { pattern = (PWCHAR)( - (SIZE_T)&EventContext->Operation.Directory.SearchPatternBase[0] + - (SIZE_T)EventContext->Operation.Directory.SearchPatternOffset); + (SIZE_T)&EventInfo->KernelInfo.EventContext.Operation.Directory.SearchPatternBase[0] + + (SIZE_T)EventInfo->KernelInfo.EventContext.Operation.Directory.SearchPatternOffset); } - if (wcscmp(EventContext->Operation.Directory.DirectoryName, L"\\") == 0 || - (pattern != NULL && wcscmp(pattern, L"*") != 0)) - return; + if(wcscmp(EventInfo->KernelInfo.EventContext.Operation.Directory.DirectoryName, L"\\") == 0 + || (pattern != NULL && wcscmp(pattern, L"*") != 0)) { - listHead = FindDataList; - for (thisEntry = listHead->Flink; thisEntry != listHead; - thisEntry = nextEntry) { + return; + } - PDOKAN_FIND_DATA find; - nextEntry = thisEntry->Flink; + for(size_t i = 0; (!currentFolder || !parentFolder) && i < DokanVector_GetCount(dirList); ++i) { - find = CONTAINING_RECORD(thisEntry, DOKAN_FIND_DATA, ListEntry); + PDOKAN_FIND_DATA find = (PDOKAN_FIND_DATA)DokanVector_GetItem(dirList, i); - 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 - } + if(wcscmp(find->FindData.cFileName, L".") == 0) { + + currentFolder = TRUE; + } + + if(wcscmp(find->FindData.cFileName, L"..") == 0) { - 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); + parentFolder = TRUE; + } } - if (!currentFolder) { - findData.cFileName[0] = '.'; - findData.cFileName[1] = '\0'; - DokanFillFileDataEx(&findData, fileInfo, FALSE); + 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... + 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); + } } } -VOID DispatchDirectoryInformation(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance) { - PEVENT_INFORMATION eventInfo; - DOKAN_FILE_INFO fileInfo; - PDOKAN_OPEN_INFO openInfo; +NTSTATUS WriteDirectoryResults(DOKAN_IO_EVENT *EventInfo, DOKAN_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(EventInfo->KernelInfo.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; + } + + EventInfo->EventResult->Operation.Directory.Index = + EventInfo->KernelInfo.EventContext.Operation.Directory.FileIndex; + } + else { + + DbgPrint("index to %d\n", index); + EventInfo->EventResult->Operation.Directory.Index = index; + } + + return EventInfo->EventResult->Status; +} + +void EndDirectoryDispatch(DOKAN_IO_EVENT *EventInfo, NTSTATUS ResultStatus) { + + PEVENT_INFORMATION result = EventInfo->EventResult; + + // STATUS_PENDING should not be passed to this function + if(ResultStatus == STATUS_PENDING) { + + DbgPrint("Dokan Error: EndDirectoryDispatch() failed because STATUS_PENDING was supplied for ResultStatus.\n"); + ResultStatus = STATUS_INTERNAL_ERROR; + } + + result->Status = ResultStatus; + + SendIoEventResult(EventInfo); +} + +void EndFindFilesCommon(DOKAN_IO_EVENT *EventInfo, NTSTATUS ResultStatus) { + + DOKAN_VECTOR *dirList = (DOKAN_VECTOR*)EventInfo->ProcessingContext; + DOKAN_VECTOR *oldDirList = NULL; + + assert(EventInfo->EventResult->BufferLength == 0); + assert(EventInfo->ProcessingContext); + + // STATUS_PENDING should not be passed to this function + if(ResultStatus == STATUS_PENDING) { + + DbgPrint("Dokan Error: EndFindFilesCommon() failed because STATUS_PENDING was supplied for ResultStatus.\n"); + ResultStatus = STATUS_INTERNAL_ERROR; + } + + if(ResultStatus == STATUS_SUCCESS) { + + AddMissingCurrentAndParentFolder(EventInfo); + + ResultStatus = WriteDirectoryResults(EventInfo, dirList); + + EnterCriticalSection(&EventInfo->DokanOpenInfo->CriticalSection); + + if(EventInfo->DokanOpenInfo->DirList != dirList) { + + oldDirList = EventInfo->DokanOpenInfo->DirList; + EventInfo->DokanOpenInfo->DirList = dirList; + } + else { + + // They should never point to the same object + DbgPrint("Dokan Warning: EndFindFilesCommon() EventInfo->DokanOpenInfo->DirList == dirList\n"); + } + + if(EventInfo->DokanOpenInfo->DirListSearchPattern) { + + free(EventInfo->DokanOpenInfo->DirListSearchPattern); + EventInfo->DokanOpenInfo->DirListSearchPattern = NULL; + } + + if(EventInfo->KernelInfo.EventContext.Operation.Directory.SearchPatternLength > 0) { + + EventInfo->DokanOpenInfo->DirListSearchPattern = + _wcsdup((PWCHAR)( + (SIZE_T)&EventInfo->KernelInfo.EventContext.Operation.Directory.SearchPatternBase[0] + + (SIZE_T)EventInfo->KernelInfo.EventContext.Operation.Directory.SearchPatternOffset)); + } + + LeaveCriticalSection(&EventInfo->DokanOpenInfo->CriticalSection); + + if(oldDirList) { + + PushDirectoryList(oldDirList); + } + } + else { + + PushDirectoryList(dirList); + } + + EventInfo->ProcessingContext = NULL; + + EndDirectoryDispatch(EventInfo, ResultStatus); +} + +void BeginDispatchDirectoryInformation(DOKAN_IO_EVENT *EventInfo) { + + PDOKAN_INSTANCE dokan = EventInfo->DokanInstance; + PWCHAR searchPattern = NULL; NTSTATUS status = STATUS_SUCCESS; - ULONG fileInfoClass = EventContext->Operation.Directory.FileInformationClass; - ULONG sizeOfEventInfo = sizeof(EVENT_INFORMATION) - 8 + - EventContext->Operation.Directory.BufferLength; + ULONG fileInfoClass = EventInfo->KernelInfo.EventContext.Operation.Directory.FileInformationClass; + BOOL forceScan = FALSE; - BOOLEAN patternCheck = TRUE; + DbgPrint("###FindFiles file handle = 0x%p, eventID = %04d\n", + EventInfo->DokanOpenInfo, + EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1); - CheckFileName(EventContext->Operation.Directory.DirectoryName); + assert(EventInfo->ProcessingContext == NULL); - eventInfo = DispatchCommon(EventContext, sizeOfEventInfo, DokanInstance, - &fileInfo, &openInfo); + CheckFileName(EventInfo->KernelInfo.EventContext.Operation.Directory.DirectoryName); // check whether this is handled FileInfoClass if (fileInfoClass != FileDirectoryInformation && @@ -477,129 +596,114 @@ VOID DispatchDirectoryInformation(HANDLE Handle, PEVENT_CONTEXT EventContext, fileInfoClass != FileIdBothDirectoryInformation && fileInfoClass != FileBothDirectoryInformation) { - 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_NOT_IMPLEMENTED; - SendEventInformation(Handle, eventInfo, sizeOfEventInfo, DokanInstance); - free(eventInfo); + EndDirectoryDispatch(EventInfo, STATUS_NOT_IMPLEMENTED); + return; } - // IMPORTANT!! - // this buffer length is fixed in MatchFiles funciton - eventInfo->BufferLength = 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, DokanInstance); - free(eventInfo); - return; - } - } + CreateDispatchCommon(EventInfo, EventInfo->KernelInfo.EventContext.Operation.Directory.BufferLength); + + EventInfo->EventResult->Operation.Directory.Index = + EventInfo->KernelInfo.EventContext.Operation.Directory.FileIndex; - if (EventContext->Operation.Directory.FileIndex == 0) { - ClearFindData(openInfo->DirListHead); + if(EventInfo->KernelInfo.EventContext.Operation.Directory.SearchPatternLength > 0) { + + searchPattern = (PWCHAR)( + (SIZE_T)&EventInfo->KernelInfo.EventContext.Operation.Directory.SearchPatternBase[0] + + (SIZE_T)EventInfo->KernelInfo.EventContext.Operation.Directory.SearchPatternOffset); } - if (IsListEmpty(openInfo->DirListHead)) { + EnterCriticalSection(&EventInfo->DokanOpenInfo->CriticalSection); - DbgPrint("###FindFiles %04d\n", openInfo->EventId); + if(EventInfo->DokanOpenInfo->DirList == NULL) { - // if user defined FindFilesWithPattern - if (DokanInstance->DokanOperations->FindFilesWithPattern) { - LPCWSTR pattern = L"*"; + forceScan = TRUE; + } + else if(searchPattern && EventInfo->DokanOpenInfo->DirListSearchPattern) { - // 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); - } + forceScan = wcscmp(searchPattern, EventInfo->DokanOpenInfo->DirListSearchPattern) != 0 ? TRUE : FALSE; + } + else if(searchPattern) { - patternCheck = FALSE; // do not recheck pattern later in MatchFiles + forceScan = wcscmp(searchPattern, L"*") != 0 ? TRUE : FALSE; + } + else if(EventInfo->DokanOpenInfo->DirListSearchPattern) { - status = DokanInstance->DokanOperations->FindFilesWithPattern( - EventContext->Operation.Directory.DirectoryName, pattern, - DokanFillFileData, &fileInfo); + forceScan = wcscmp(EventInfo->DokanOpenInfo->DirListSearchPattern, L"*") != 0 ? TRUE : FALSE; + } + + // In FastFat SL_INDEX_SPECIFIED overrides SL_RESTART_SCAN + forceScan = (forceScan + || (!(EventInfo->KernelInfo.EventContext.Flags & SL_INDEX_SPECIFIED) + && (EventInfo->KernelInfo.EventContext.Flags & SL_RESTART_SCAN))) ? TRUE : FALSE; - } else { - status = STATUS_NOT_IMPLEMENTED; - } + if(!forceScan) { - if (status == STATUS_NOT_IMPLEMENTED && - DokanInstance->DokanOperations->FindFiles) { + status = WriteDirectoryResults(EventInfo, EventInfo->DokanOpenInfo->DirList); + } - patternCheck = TRUE; // do pattern check later in MachFiles + LeaveCriticalSection(&EventInfo->DokanOpenInfo->CriticalSection); - // call FileSystem specifeid callback routine - status = DokanInstance->DokanOperations->FindFiles( - EventContext->Operation.Directory.DirectoryName, DokanFillFileData, - &fileInfo); - } + if(!forceScan) { + + EndDirectoryDispatch(EventInfo, status); } + else { - if (status != STATUS_SUCCESS) { + EventInfo->ProcessingContext = PopDirectoryList(); - 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; - } + if(!EventInfo->ProcessingContext) { - eventInfo->BufferLength = 0; - eventInfo->Operation.Directory.Index = - EventContext->Operation.Directory.FileIndex; - // free all of list entries - ClearFindData(openInfo->DirListHead); - } 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) { - 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; - } - eventInfo->BufferLength = 0; - eventInfo->Operation.Directory.Index = - EventContext->Operation.Directory.FileIndex; + DbgPrint("Dokan Error: Failed to allocate memory for a new directory list.\n"); - ClearFindData(openInfo->DirListHead); + EndDirectoryDispatch(EventInfo, STATUS_NO_MEMORY); - } else { - DbgPrint("index to %d\n", index); - eventInfo->Operation.Directory.Index = index; - } - } + return; + } - // information for FileSystem - openInfo->UserContext = fileInfo.Context; + if((EventInfo->KernelInfo.EventContext.Operation.Directory.SearchPatternLength == 0 + && dokan->DokanOperations->FindFiles) + || !dokan->DokanOperations->FindFilesWithPattern) { - // send directory information to driver - SendEventInformation(Handle, eventInfo, sizeOfEventInfo, DokanInstance); - free(eventInfo); - return; + DOKAN_FIND_FILES_EVENT *findFiles = &EventInfo->EventInfo.FindFiles; + + assert((void*)findFiles == (void*)EventInfo); + + findFiles->DokanFileInfo = &EventInfo->DokanFileInfo; + findFiles->PathName = EventInfo->KernelInfo.EventContext.Operation.Directory.DirectoryName; + findFiles->FillFindData = DokanFillFileData; + + status = dokan->DokanOperations->FindFiles(findFiles); + + if(status != STATUS_PENDING) { + + EndDispatchFindFiles(findFiles, status); + } + } + else if(dokan->DokanOperations->FindFilesWithPattern) { + + DOKAN_FIND_FILES_PATTERN_EVENT *findFilesPattern = &EventInfo->EventInfo.FindFilesWithPattern; + + findFilesPattern->DokanFileInfo = &EventInfo->DokanFileInfo; + findFilesPattern->PathName = EventInfo->KernelInfo.EventContext.Operation.Directory.DirectoryName; + findFilesPattern->SearchPattern = searchPattern ? searchPattern : L"*"; + findFilesPattern->FillFindData = DokanFillFileDataWithPattern; + + status = dokan->DokanOperations->FindFilesWithPattern(findFilesPattern); + + if(status != STATUS_PENDING) { + + EndDispatchFindFilesWithPattern(findFilesPattern, status); + } + } + else { + + EndDirectoryDispatch(EventInfo, STATUS_NOT_IMPLEMENTED); + } + } } #define DOS_STAR (L'<') @@ -625,86 +729,132 @@ BOOL DOKANAPI DokanIsNameInExpression(LPCWSTR Expression, // matching pattern ULONG ei = 0; ULONG ni = 0; - while (Expression[ei] != '\0') { + if((!Expression || !Expression[0]) && (!Name || !Name[0])) { + + return TRUE; + } + + if(!Expression || !Name || !Expression[0] || !Name[0]) { + + return FALSE; + } + + while (Expression[ei] && Name[ni]) { if (Expression[ei] == L'*') { + ei++; - if (Expression[ei] == '\0') - return TRUE; + + if(Expression[ei] == '\0') { + + return TRUE; + } while (Name[ni] != '\0') { - if (DokanIsNameInExpression(&Expression[ei], &Name[ni], IgnoreCase)) - return TRUE; + + if(DokanIsNameInExpression(&Expression[ei], &Name[ni], IgnoreCase)) { + + return TRUE; + } + ni++; } - - } else if (Expression[ei] == DOS_STAR) { + } + else if (Expression[ei] == DOS_STAR) { ULONG p = ni; ULONG lastDot = 0; ei++; while (Name[p] != '\0') { - if (Name[p] == L'.') - lastDot = p; + + if(Name[p] == L'.') { + + lastDot = p; + } + p++; } BOOL endReached = FALSE; + while (!endReached) { endReached = (Name[ni] == '\0' || ni == lastDot); if (!endReached) { - if (DokanIsNameInExpression(&Expression[ei], &Name[ni], IgnoreCase)) - return TRUE; + + if(DokanIsNameInExpression(&Expression[ei], &Name[ni], IgnoreCase)) { + + return TRUE; + } ni++; } } - - } else if (Expression[ei] == DOS_QM) { + } + else if (Expression[ei] == DOS_QM) { ei++; + if (Name[ni] != L'.') { + ni++; - } else { + } + else { ULONG p = ni + 1; + while (Name[p] != '\0') { - if (Name[p] == L'.') - break; + + if(Name[p] == L'.') { + + break; + } + p++; } - if (Name[p] == L'.') - ni++; + if(Name[p] == L'.') { + + ni++; + } } + } + else if (Expression[ei] == DOS_DOT) { - } else if (Expression[ei] == DOS_DOT) { ei++; - if (Name[ni] == L'.') - ni++; + if(Name[ni] == L'.') { + + ni++; + } + } + else { + + if (Expression[ei] == L'?' + || (IgnoreCase && towupper(Expression[ei]) == towupper(Name[ni])) + || (!IgnoreCase && Expression[ei] == Name[ni])) { - } else { - if (Expression[ei] == L'?') { - ei++; - ni++; - } else if (IgnoreCase && towupper(Expression[ei]) == towupper(Name[ni])) { - ei++; - ni++; - } else if (!IgnoreCase && Expression[ei] == Name[ni]) { ei++; ni++; - } else { + } + else { + return FALSE; } } } - if (ei == wcslen(Expression) && ni == wcslen(Name)) - return TRUE; + return !Expression[ei] && !Name[ni] ? TRUE : FALSE; +} + +void DOKANAPI EndDispatchFindFiles(DOKAN_FIND_FILES_EVENT *EventInfo, NTSTATUS ResultStatus) { - return FALSE; + EndFindFilesCommon((DOKAN_IO_EVENT*)EventInfo, ResultStatus); } + +void DOKANAPI EndDispatchFindFilesWithPattern(DOKAN_FIND_FILES_PATTERN_EVENT *EventInfo, NTSTATUS ResultStatus) { + + EndFindFilesCommon((DOKAN_IO_EVENT*)EventInfo, ResultStatus); +} \ No newline at end of file diff --git a/dokan/dokan.c b/dokan/dokan.c index 65e2e2871..408461821 100644 --- a/dokan/dokan.c +++ b/dokan/dokan.c @@ -22,6 +22,7 @@ with this program. If not, see . #include "dokani.h" #include "fileinfo.h" #include "list.h" + #include #include #include @@ -31,31 +32,486 @@ with this program. If not, see . #include #include #include +#include + +// 1024 * ~32k = 32mb when the pool is full +// We should probably optimize our usage of fixed size 32k buffers for +// EVENT_CONTEXT but that will be for another PR. +#define DOKAN_IO_EVENT_POOL_SIZE 1024 + +#define DOKAN_OVERLAPPED_POOL_SIZE 1024 + +#define DOKAN_DIRECTORY_LIST_POOL_SIZE 128 #define DokanMapKernelBit(dest, src, userBit, kernelBit) \ if (((src) & (kernelBit)) == (kernelBit)) \ (dest) |= (userBit) // DokanOptions->DebugMode is ON? -BOOL g_DebugMode = TRUE; +BOOL g_DebugMode = TRUE; // DokanOptions->UseStdErr is ON? -BOOL g_UseStdErr = FALSE; +BOOL g_UseStdErr = FALSE; + +// Dokan DLL critical section +CRITICAL_SECTION g_InstanceCriticalSection; + +// Global linked list of mounted Dokan instances +LIST_ENTRY g_InstanceList; + +// Global thread pool +PTP_POOL g_ThreadPool = NULL; + +// Global vector of event buffers +DOKAN_VECTOR *g_EventBufferPool = NULL; +CRITICAL_SECTION g_EventBufferCriticalSection; + +DOKAN_VECTOR *g_OverlappedPool = NULL; +CRITICAL_SECTION g_OverlappedCriticalSection; + +DOKAN_VECTOR *g_EventResultPool = NULL; +CRITICAL_SECTION g_EventResultCriticalSection; + +DOKAN_VECTOR *g_FileInfoPool = NULL; +CRITICAL_SECTION g_FileInfoCriticalSection; + +DOKAN_VECTOR *g_DirectoryListPool = NULL; +CRITICAL_SECTION g_DirectoryListCriticalSection; + + +// TODO NEXT: +// * Double check all DOKAN_VECTOR code. +// * Add functions for pushing and popping global event buffers +// * Finish DokanMain() by initiating the IO checks for the device +// * Finish DokanLoop() which needs to queue the next IO check for the device +// * Look at CancelThreadpoolIo (https://msdn.microsoft.com/en-us/library/windows/desktop/ms681983(v=vs.85).aspx) +// for cancelling pending IO operations +// * Need global event handler for cancelling pending IO operations +// * Need global event handler for cleanup of Dokan object +// * All shutdown/cleanup code needs to be reviewed. We need a way to guarantee a Dokan instance is done doing stuff before deallocating it. +// This probably needs to be done using event HANDLE's that get signalled when the object is clean -CRITICAL_SECTION g_InstanceCriticalSection; -LIST_ENTRY g_InstanceList; VOID DOKANAPI DokanUseStdErr(BOOL Status) { g_UseStdErr = Status; } VOID DOKANAPI DokanDebugMode(BOOL Status) { g_DebugMode = Status; } +int InitializeThreadPool(HMODULE hModule) { + + UNREFERENCED_PARAMETER(hModule); + + EnterCriticalSection(&g_InstanceCriticalSection); + + if(g_ThreadPool) { + + DokanDbgPrint("Dokan Error: Thread pool has already been created.\n"); + LeaveCriticalSection(&g_InstanceCriticalSection); + 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"); + LeaveCriticalSection(&g_InstanceCriticalSection); + return DOKAN_DRIVER_INSTALL_ERROR; + } + + LeaveCriticalSection(&g_InstanceCriticalSection); + + return DOKAN_SUCCESS; +} + +void CleanupThreadpool() { + + EnterCriticalSection(&g_InstanceCriticalSection); + + // TODO: Iterate all instances and deallocate their cleanup groups + + if(g_ThreadPool) { + CloseThreadpool(g_ThreadPool); + g_ThreadPool = NULL; + } + + LeaveCriticalSection(&g_InstanceCriticalSection); +} + +/////////////////// DOKAN_IO_EVENT /////////////////// + +DOKAN_IO_EVENT* PopIoEventBuffer() { + + DOKAN_IO_EVENT *ioEvent = NULL; + + EnterCriticalSection(&g_EventBufferCriticalSection); + + if(DokanVector_GetCount(g_EventBufferPool) > 0) + { + ioEvent = *(DOKAN_IO_EVENT**)DokanVector_GetLastItem(g_EventBufferPool); + DokanVector_PopBack(g_EventBufferPool); + } + + LeaveCriticalSection(&g_EventBufferCriticalSection); + + if(!ioEvent) { + + ioEvent = (DOKAN_IO_EVENT*)malloc(sizeof(DOKAN_IO_EVENT)); + } + + if(ioEvent) { + + RtlZeroMemory(ioEvent, sizeof(DOKAN_IO_EVENT)); + ioEvent->Flags = DOKAN_IO_EVENT_FLAGS_POOLED; + } + + return ioEvent; +} + +void FreeIOEventBuffer(DOKAN_IO_EVENT *IOEvent) { + + if(IOEvent) { + + free(IOEvent); + } +} + +void PushIoEventBuffer(DOKAN_IO_EVENT *IOEvent) { + + assert(IOEvent); + + if((IOEvent->Flags & DOKAN_IO_EVENT_FLAGS_POOLED) != DOKAN_IO_EVENT_FLAGS_POOLED) { + + FreeIOEventBuffer(IOEvent); + return; + } + + EnterCriticalSection(&g_EventBufferCriticalSection); + + if(DokanVector_GetCount(g_EventBufferPool) < DOKAN_IO_EVENT_POOL_SIZE) { + + DokanVector_PushBack(g_EventBufferPool, &IOEvent); + IOEvent = NULL; + } + + LeaveCriticalSection(&g_EventBufferCriticalSection); + + if(IOEvent) { + + FreeIOEventBuffer(IOEvent); + } +} + +/////////////////// DOKAN_OVERLAPPED /////////////////// + +void ResetOverlapped(DOKAN_OVERLAPPED *overlapped) { + + //HANDLE tempHandle; + + if(overlapped) { + + //tempHandle = overlapped->InternalOverlapped.hEvent; + + RtlZeroMemory(overlapped, sizeof(DOKAN_OVERLAPPED)); + + /*overlapped->InternalOverlapped.hEvent = tempHandle; + + if(tempHandle) { + + ResetEvent(tempHandle); + }*/ + } +} + +DOKAN_OVERLAPPED* PopOverlapped() { + + DOKAN_OVERLAPPED *overlapped = NULL; + + EnterCriticalSection(&g_OverlappedCriticalSection); + + if(DokanVector_GetCount(g_OverlappedPool) > 0) + { + overlapped = *(DOKAN_OVERLAPPED**)DokanVector_GetLastItem(g_OverlappedPool); + DokanVector_PopBack(g_OverlappedPool); + } + + LeaveCriticalSection(&g_OverlappedCriticalSection); + + if(!overlapped) { + + overlapped = (DOKAN_OVERLAPPED*)malloc(sizeof(DOKAN_OVERLAPPED)); + /*overlapped->InternalOverlapped.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); + + if(!overlapped->InternalOverlapped.hEvent) { + + DbgPrint("Dokan Warning: Failed to create DOKAN_OVERLAPPED event handle.\n"); + }*/ + } + + if(overlapped) { + + ResetOverlapped(overlapped); + } + + return overlapped; +} + +void FreeOverlapped(DOKAN_OVERLAPPED *Overlapped) { + + if(Overlapped) { + + if(Overlapped->InternalOverlapped.hEvent) { + + CloseHandle(Overlapped->InternalOverlapped.hEvent); + } + + free(Overlapped); + } +} + + +void PushOverlapped(DOKAN_OVERLAPPED *Overlapped) { + + assert(Overlapped); + + EnterCriticalSection(&g_OverlappedCriticalSection); + + if(DokanVector_GetCount(g_OverlappedPool) < DOKAN_OVERLAPPED_POOL_SIZE) { + + DokanVector_PushBack(g_OverlappedPool, &Overlapped); + Overlapped = NULL; + } + + LeaveCriticalSection(&g_OverlappedCriticalSection); + + if(Overlapped) { + + FreeOverlapped(Overlapped); + } +} + +/////////////////// EVENT_INFORMATION /////////////////// + +EVENT_INFORMATION* PopEventResult() { + + EVENT_INFORMATION *eventResult = NULL; + + EnterCriticalSection(&g_EventResultCriticalSection); + + if(DokanVector_GetCount(g_EventResultPool) > 0) + { + eventResult = *(EVENT_INFORMATION**)DokanVector_GetLastItem(g_EventResultPool); + DokanVector_PopBack(g_EventResultPool); + } + + LeaveCriticalSection(&g_EventResultCriticalSection); + + if(!eventResult) { + + eventResult = (EVENT_INFORMATION*)malloc(DOKAN_EVENT_INFO_DEFAULT_SIZE); + } + + if(eventResult) { + + RtlZeroMemory(eventResult, DOKAN_EVENT_INFO_DEFAULT_SIZE); + } + + return eventResult; +} + +void FreeEventResult(EVENT_INFORMATION *EventResult) { + + if(EventResult) { + + free(EventResult); + } +} + + +void PushEventResult(EVENT_INFORMATION *EventResult) { + + assert(EventResult); + + EnterCriticalSection(&g_EventResultCriticalSection); + + if(DokanVector_GetCount(g_EventResultPool) < DOKAN_OVERLAPPED_POOL_SIZE) { + + DokanVector_PushBack(g_EventResultPool, &EventResult); + EventResult = NULL; + } + + LeaveCriticalSection(&g_EventResultCriticalSection); + + if(EventResult) { + + FreeEventResult(EventResult); + } +} + +/////////////////// DOKAN_OPEN_INFO /////////////////// + +DOKAN_OPEN_INFO* PopFileOpenInfo() { + + DOKAN_OPEN_INFO *fileInfo = NULL; + + EnterCriticalSection(&g_FileInfoCriticalSection); + + if(DokanVector_GetCount(g_FileInfoPool) > 0) + { + fileInfo = *(DOKAN_OPEN_INFO**)DokanVector_GetLastItem(g_FileInfoPool); + DokanVector_PopBack(g_FileInfoPool); + } + + LeaveCriticalSection(&g_FileInfoCriticalSection); + + if(!fileInfo) { + + fileInfo = (DOKAN_OPEN_INFO*)malloc(sizeof(DOKAN_OPEN_INFO)); + + 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(DOKAN_OPEN_INFO *FileInfo) { + + assert(FileInfo); + + DOKAN_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(DOKAN_OPEN_INFO *FileInfo) { + + if(FileInfo) { + + CleanupFileOpenInfo(FileInfo); + + DeleteCriticalSection(&FileInfo->CriticalSection); + free(FileInfo); + } +} + +void PushFileOpenInfo(DOKAN_OPEN_INFO *FileInfo) { + + assert(FileInfo); + + CleanupFileOpenInfo(FileInfo); + + EnterCriticalSection(&g_FileInfoCriticalSection); + + if(DokanVector_GetCount(g_FileInfoPool) < DOKAN_OVERLAPPED_POOL_SIZE) { + + DokanVector_PushBack(g_FileInfoPool, &FileInfo); + FileInfo = NULL; + } + + LeaveCriticalSection(&g_FileInfoCriticalSection); + + if(FileInfo) { + + FreeFileOpenInfo(FileInfo); + } +} + +/////////////////// Directory list /////////////////// + +DOKAN_VECTOR* PopDirectoryList() { + + DOKAN_VECTOR *directoryList = NULL; + + EnterCriticalSection(&g_DirectoryListCriticalSection); + + if(DokanVector_GetCount(g_DirectoryListPool) > 0) + { + directoryList = *(DOKAN_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(DOKAN_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 /////////////////// + PDOKAN_INSTANCE NewDokanInstance() { PDOKAN_INSTANCE instance = (PDOKAN_INSTANCE)malloc(sizeof(DOKAN_INSTANCE)); - if (instance == NULL) - return NULL; - ZeroMemory(instance, sizeof(DOKAN_INSTANCE)); + if(instance == NULL) { + return NULL; + } + + RtlZeroMemory(instance, sizeof(DOKAN_INSTANCE)); + + instance->GlobalDevice = INVALID_HANDLE_VALUE; + instance->Device = INVALID_HANDLE_VALUE; #if _MSC_VER < 1300 InitializeCriticalSection(&instance->CriticalSection); @@ -64,21 +520,109 @@ NewDokanInstance() { #endif 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); + + if(!g_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 = g_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, g_ThreadPool); + + SetThreadpoolCallbackCleanupGroup(&instance->ThreadInfo.CallbackEnvironment, instance->ThreadInfo.CleanupGroup, NULL); + InsertTailList(&g_InstanceList, &instance->ListEntry); + LeaveCriticalSection(&g_InstanceCriticalSection); return instance; } -VOID DeleteDokanInstance(PDOKAN_INSTANCE Instance) { +void DeleteDokanInstance(PDOKAN_INSTANCE Instance) { + + SetEvent(Instance->DeviceClosedWaitHandle); + + if(Instance->ThreadInfo.KeepAliveTimer) { + + // cancel timer + SetThreadpoolTimer(Instance->ThreadInfo.KeepAliveTimer, NULL, 0, 0); + } + + if(Instance->ThreadInfo.CleanupGroup) { + + CloseThreadpoolCleanupGroupMembers(Instance->ThreadInfo.CleanupGroup, FALSE, Instance); + CloseThreadpoolCleanupGroup(Instance->ThreadInfo.CleanupGroup); + Instance->ThreadInfo.CleanupGroup = NULL; + + DestroyThreadpoolEnvironment(&Instance->ThreadInfo.CallbackEnvironment); + + // Members freed by CloseThreadpoolCleanupGroupMembers(): + + Instance->ThreadInfo.KeepAliveTimer = NULL; + Instance->ThreadInfo.IoCompletion = NULL; + } + + 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); LeaveCriticalSection(&g_InstanceCriticalSection); + CloseHandle(Instance->DeviceClosedWaitHandle); + free(Instance); } @@ -173,13 +717,141 @@ void CheckAllocationUnitSectorSize(PDOKAN_OPTIONS DokanOptions) { DokanOptions->AllocationUnitSize, DokanOptions->SectorSize); } +BOOL StartDeviceIO(PDOKAN_INSTANCE Dokan, DOKAN_OVERLAPPED *Overlapped) { + + DOKAN_IO_EVENT *ioEvent = PopIoEventBuffer(); + DWORD lastError = 0; + + if(!ioEvent) { + + DokanDbgPrint("Dokan Error: Failed to allocate IO event buffer.\n"); + + return FALSE; + } + + assert(ioEvent->EventResult == NULL && ioEvent->EventResultSize == 0); + + ioEvent->DokanInstance = Dokan; + + if(!Overlapped) { + + Overlapped = PopOverlapped(); + + if(!Overlapped) { + + DokanDbgPrint("Dokan Error: Failed to allocate overlapped info.\n"); + + PushIoEventBuffer(ioEvent); + + return FALSE; + } + } + + Overlapped->OutputPayload = ioEvent; + Overlapped->PayloadType = DOKAN_OVERLAPPED_TYPE_IOEVENT; + + StartThreadpoolIo(Dokan->ThreadInfo.IoCompletion); + + if(!DeviceIoControl( + Dokan->Device, // Handle to device + IOCTL_EVENT_WAIT, // IO Control code + NULL, // Input Buffer to driver. + 0, // Length of input buffer in bytes. + ioEvent->KernelInfo.EventContextBuffer, // Output Buffer from driver. + EVENT_CONTEXT_MAX_SIZE, // Length of output buffer in bytes. + NULL, // Bytes placed in buffer. + (OVERLAPPED*)Overlapped // asynchronous call + )) { + + lastError = GetLastError(); + + if(lastError != ERROR_IO_PENDING) { + + DbgPrint("Dokan Error: Dokan device ioctl failed for wait with code %d.\n", lastError); + + CancelThreadpoolIo(Dokan->ThreadInfo.IoCompletion); + + PushIoEventBuffer(ioEvent); + PushOverlapped(Overlapped); + + return FALSE; + } + } + + return TRUE; +} + +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( + DOKAN_HANDLE DokanInstance, + DWORD dwMilliseconds) { + + DOKAN_INSTANCE *instance = (DOKAN_INSTANCE*)DokanInstance; + + if(!instance) { + + return FALSE; + } + + return WaitForSingleObject(instance->DeviceClosedWaitHandle, dwMilliseconds); +} + +void DOKANAPI DokanCloseHandle(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) { - ULONG threadNum = 0; - ULONG i; - HANDLE device; - HANDLE threadIds[DOKAN_MAX_THREAD]; + PDOKAN_OPERATIONS DokanOperations) { + + DOKAN_INSTANCE *instance = NULL; + int returnCode; + + returnCode = DokanCreateFileSystem(DokanOptions, DokanOperations, (DOKAN_HANDLE*)&instance); + + if(DOKAN_FAILED(returnCode)) { + + 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; + ULARGE_INTEGER timerDueTime; + WCHAR rawDeviceName[MAX_PATH]; g_DebugMode = DokanOptions->Options & DOKAN_OPTION_DEBUG; g_UseStdErr = DokanOptions->Options & DOKAN_OPTION_STDERR; @@ -193,9 +865,11 @@ int DOKANAPI DokanMain(PDOKAN_OPTIONS DokanOptions, g_DebugMode = TRUE; } - if (DokanOptions->Options & DOKAN_OPTION_NETWORK && - !IsMountPointDriveLetter(DokanOptions->MountPoint)) { + 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 " L"option. Disable network device.\n"); } @@ -209,107 +883,166 @@ int DOKANAPI DokanMain(PDOKAN_OPTIONS DokanOptions, CheckAllocationUnitSectorSize(DokanOptions); - if (DokanOptions->ThreadCount == 0) { - DokanOptions->ThreadCount = 5; - - } else if ((DOKAN_MAX_THREAD - 1) < DokanOptions->ThreadCount) { - // DOKAN_MAX_THREAD includes DokanKeepAlive thread, so - // available thread is DOKAN_MAX_THREAD -1 - DokanDbgPrintW(L"Dokan Error: too many thread count %d\n", - DokanOptions->ThreadCount); - DokanOptions->ThreadCount = DOKAN_MAX_THREAD - 1; + if (DokanOptions->ThreadCount != 0) { + DbgPrintW(L"Dokan Warning: DOKAN_OPTIONS::ThreadCount is no longer used.\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 - ); + instance = NewDokanInstance(); - if (device == INVALID_HANDLE_VALUE) { - DokanDbgPrintW(L"Dokan Error: CreatFile Failed %s: %d\n", - DOKAN_GLOBAL_DEVICE_NAME, GetLastError()); - return DOKAN_DRIVER_INSTALL_ERROR; + if(!instance) { + return DOKAN_DRIVER_INSTALL_ERROR; } - DbgPrint("device opened\n"); - instance = NewDokanInstance(); instance->DokanOptions = DokanOptions; instance->DokanOperations = DokanOperations; + instance->GlobalDevice = 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(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); + + wcscpy_s(instance->MountPoint, sizeof(instance->MountPoint) / sizeof(WCHAR), DokanOptions->MountPoint); + if (IsMountPointDriveLetter(instance->MountPoint)) { if (!CheckDriveLetterAvailability(instance->MountPoint[0])) { + DokanDbgPrint("Dokan Error: CheckDriveLetterAvailability Failed\n"); - CloseHandle(device); - EnterCriticalSection(&g_InstanceCriticalSection); - RemoveTailList(&g_InstanceList); - LeaveCriticalSection(&g_InstanceCriticalSection); + DeleteDokanInstance(instance); + return DOKAN_MOUNT_ERROR; } } } if (DokanOptions->UNCName != NULL) { - wcscpy_s(instance->UNCName, sizeof(instance->UNCName) / sizeof(WCHAR), - DokanOptions->UNCName); + + wcscpy_s(instance->UNCName, sizeof(instance->UNCName) / sizeof(WCHAR), DokanOptions->UNCName); } if (!DokanStart(instance)) { - CloseHandle(device); + + DeleteDokanInstance(instance); + return DOKAN_START_ERROR; } + GetRawDeviceName(instance->DeviceName, rawDeviceName, MAX_PATH); + + instance->Device = CreateFile(rawDeviceName, // lpFileName + GENERIC_READ | GENERIC_WRITE, // 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; + } + + instance->ThreadInfo.IoCompletion = CreateThreadpoolIo(instance->Device, DokanLoop, instance, &instance->ThreadInfo.CallbackEnvironment); + + if(!instance->ThreadInfo.IoCompletion) { + + DokanDbgPrintW(L"Dokan Error: Failed to allocate IO completion port.\n"); + + DeleteDokanInstance(instance); + + return DOKAN_DRIVER_INSTALL_ERROR; + } + + instance->ThreadInfo.KeepAliveTimer = CreateThreadpoolTimer(DokanKeepAlive, (PVOID)instance->Device, &instance->ThreadInfo.CallbackEnvironment); + + if(!instance->ThreadInfo.KeepAliveTimer) { + + SendReleaseIRP(instance->DeviceName); + + DokanDbgPrint("Dokan Error: Failed to create keep alive timer.\n"); + + DeleteDokanInstance(instance); + + return DOKAN_START_ERROR; + } + + // convert milliseconds into 100 nanosecond units and make it negative for relative time + timerDueTime.QuadPart = (ULONGLONG)(-(DOKAN_KEEPALIVE_TIME * (1000000 / 100))); + // Start Keep Alive thread - threadIds[threadNum++] = (HANDLE)_beginthreadex(NULL, // Security Attributes - 0, // stack size - DokanKeepAlive, - (PVOID)instance, // param - 0, // create flag - NULL); - - for (i = 0; i < DokanOptions->ThreadCount; ++i) { - threadIds[threadNum++] = (HANDLE)_beginthreadex(NULL, // Security Attributes - 0, // stack size - DokanLoop, - (PVOID)instance, // param - 0, // create flag - NULL); + SetThreadpoolTimer(instance->ThreadInfo.KeepAliveTimer, (FILETIME*)&timerDueTime.QuadPart, DOKAN_KEEPALIVE_TIME, 0); + + if(!StartDeviceIO(instance, NULL)) { + + DokanDbgPrint("Dokan Error: Failed to start device IO.\n"); + DeleteDokanInstance(instance); + + return DOKAN_START_ERROR; + } + else { + + DbgPrint("Dokan Information: Started device IO.\n"); } if (!DokanMount(instance->MountPoint, instance->DeviceName, DokanOptions)) { + SendReleaseIRP(instance->DeviceName); + DokanDbgPrint("Dokan Error: DokanMount Failed\n"); - CloseHandle(device); + + DeleteDokanInstance(instance); + return DOKAN_MOUNT_ERROR; } // 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 - DokanOperations->Mounted(&fileInfo); + + DOKAN_MOUNTED_INFO mountedInfo; + + mountedInfo.DokanOptions = DokanOptions; + mountedInfo.ThreadPool = instance->ThreadInfo.ThreadPool; + + DokanOperations->Mounted(&mountedInfo); } - // wait for thread terminations - WaitForMultipleObjects(threadNum, threadIds, TRUE, INFINITE); - - for (i = 0; i < threadNum; ++i) { - CloseHandle(threadIds[i]); + if(DokanInstance) { + + *DokanInstance = instance; } - CloseHandle(device); + /*CloseHandle(device); if (DokanOperations->Unmounted) { DOKAN_FILE_INFO fileInfo; @@ -319,11 +1052,7 @@ int DOKANAPI DokanMain(PDOKAN_OPTIONS DokanOptions, DokanOperations->Unmounted(&fileInfo); } - Sleep(1000); - - DbgPrint("\nunload\n"); - - DeleteDokanInstance(instance); + DbgPrint("\nunload\n");*/ return DOKAN_SUCCESS; } @@ -345,163 +1074,319 @@ void ALIGN_ALLOCATION_SIZE(PLARGE_INTEGER size, PDOKAN_OPTIONS DokanOptions) { (size->QuadPart + (r > 0 ? DokanOptions->AllocationUnitSize - r : 0)); } -UINT WINAPI DokanLoop(PDOKAN_INSTANCE DokanInstance) { - HANDLE device; - char *buffer = NULL; - BOOL status; - ULONG returnedLength; - DWORD result = 0; - DWORD lastError = 0; - WCHAR rawDeviceName[MAX_PATH]; +void SetupIOEventForProcessing(DOKAN_IO_EVENT *EventInfo) { - 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); + EventInfo->DokanOpenInfo = (PDOKAN_OPEN_INFO)(UINT_PTR)EventInfo->KernelInfo.EventContext.Context; + EventInfo->DokanFileInfo.DokanContext = EventInfo; + EventInfo->DokanFileInfo.ProcessId = EventInfo->KernelInfo.EventContext.ProcessId; + EventInfo->DokanFileInfo.DokanOptions = EventInfo->DokanInstance->DokanOptions; - device = CreateFile(GetRawDeviceName(DokanInstance->DeviceName, rawDeviceName, - MAX_PATH), // lpFileName - GENERIC_READ | GENERIC_WRITE, // dwDesiredAccess - FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode - NULL, // lpSecurityAttributes - OPEN_EXISTING, // dwCreationDistribution - 0, // dwFlagsAndAttributes - NULL // hTemplateFile - ); + if(EventInfo->DokanOpenInfo) { - if (device == INVALID_HANDLE_VALUE) { - DbgPrint( - "Dokan Error: CreateFile failed %ws: %d\n", - GetRawDeviceName(DokanInstance->DeviceName, rawDeviceName, MAX_PATH), - GetLastError()); - free(buffer); - result = (DWORD)-1; - _endthreadex(result); - return result; - } - - status = TRUE; - while (status) { - - status = DeviceIoControl( - device, // Handle to device - IOCTL_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; - Sleep(200); - continue; - } - DbgPrint("Thread will be terminated\n"); - break; - } + EventInfo->DokanFileInfo.Context = InterlockedAdd64(&EventInfo->DokanOpenInfo->UserContext, 0); + EventInfo->DokanFileInfo.IsDirectory = EventInfo->DokanOpenInfo->IsDirectory; - // printf("#%d got notification %d\n", (ULONG)Param, count++); + if(EventInfo->KernelInfo.EventContext.FileFlags & DOKAN_DELETE_ON_CLOSE) { - 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); - continue; - } + EventInfo->DokanFileInfo.DeleteOnClose = 1; + } - 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; - default: - break; - } + if(EventInfo->KernelInfo.EventContext.FileFlags & DOKAN_PAGING_IO) { - } else { - DbgPrint("ReturnedLength %d\n", returnedLength); - } + EventInfo->DokanFileInfo.PagingIo = 1; + } + + if(EventInfo->KernelInfo.EventContext.FileFlags & DOKAN_WRITE_TO_END_OF_FILE) { + + EventInfo->DokanFileInfo.WriteToEndOfFile = 1; + } + + if(EventInfo->KernelInfo.EventContext.FileFlags & DOKAN_SYNCHRONOUS_IO) { + + EventInfo->DokanFileInfo.SynchronousIo = 1; + } + + if(EventInfo->KernelInfo.EventContext.FileFlags & DOKAN_NOCACHE) { + + EventInfo->DokanFileInfo.Nocache = 1; + } + } + + assert(EventInfo->EventResult == NULL); +} + +void OnDeviceIoCtlFailed(PDOKAN_INSTANCE Dokan, ULONG IoResult) { + + // disable keep alive timer + SetThreadpoolTimer(Dokan->ThreadInfo.KeepAliveTimer, NULL, 0, 0); + + DokanDbgPrintW(L"Dokan Warning: Closing IO processing for dokan instance %s with error code 0x%x and unmounting volume.\n", Dokan->DeviceName, IoResult); + + DokanNotifyUnmounted(Dokan); + + // set the device to a closed state + SetEvent(Dokan->DeviceClosedWaitHandle); +} + +void ProcessIOEvent( + PDOKAN_INSTANCE Dokan, + DOKAN_OVERLAPPED *Overlapped, + ULONG IoResult, + ULONG_PTR NumberOfBytesTransferred) { + + DOKAN_IO_EVENT *currentIoEvent = (DOKAN_IO_EVENT*)Overlapped->OutputPayload; + currentIoEvent->EventSize = (ULONG)NumberOfBytesTransferred; + + assert(currentIoEvent->EventResult == NULL && currentIoEvent->EventResultSize == 0); + + if(IoResult != NO_ERROR) { + + PushIoEventBuffer(currentIoEvent); + PushOverlapped(Overlapped); + + OnDeviceIoCtlFailed(Dokan, IoResult); + + return; + } + + // reuse Overlapped and queue up another async IO operation + ResetOverlapped(Overlapped); + + BOOL restartDeviceIOSucceeded = StartDeviceIO(Dokan, Overlapped); + DWORD lastError = ERROR_SUCCESS; + + if(!restartDeviceIOSucceeded) { + + lastError = GetLastError(); + SetLastError(ERROR_SUCCESS); + } + + // begin processing IO event + SetupIOEventForProcessing(currentIoEvent); + + if(NumberOfBytesTransferred > 0) { + + //MajorFunction + switch(currentIoEvent->KernelInfo.EventContext.MajorFunction) { + + case IRP_MJ_CREATE: + BeginDispatchCreate(currentIoEvent); + break; + case IRP_MJ_CLEANUP: + DispatchCleanup(currentIoEvent); + break; + case IRP_MJ_CLOSE: + DispatchClose(currentIoEvent); + break; + case IRP_MJ_DIRECTORY_CONTROL: + BeginDispatchDirectoryInformation(currentIoEvent); + break; + case IRP_MJ_READ: + BeginDispatchRead(currentIoEvent); + break; + case IRP_MJ_WRITE: + BeginDispatchWrite(currentIoEvent); + break; + case IRP_MJ_QUERY_INFORMATION: + BeginDispatchQueryInformation(currentIoEvent); + break; + case IRP_MJ_QUERY_VOLUME_INFORMATION: + BeginDispatchQueryVolumeInformation(currentIoEvent); + break; + case IRP_MJ_LOCK_CONTROL: + BeginDispatchLock(currentIoEvent); + break; + case IRP_MJ_SET_INFORMATION: + BeginDispatchSetInformation(currentIoEvent); + break; + case IRP_MJ_FLUSH_BUFFERS: + BeginDispatchFlush(currentIoEvent); + break; + case IRP_MJ_QUERY_SECURITY: + BeginDispatchQuerySecurity(currentIoEvent); + break; + case IRP_MJ_SET_SECURITY: + BeginDispatchSetSecurity(currentIoEvent); + break; + default: + DokanDbgPrintW(L"Dokan Warning: Unsupported IRP 0x%x.\n", currentIoEvent->KernelInfo.EventContext.MajorFunction); + PushIoEventBuffer(currentIoEvent); + break; + } + } + else { + + PushIoEventBuffer(currentIoEvent); + DbgPrint("ReturnedLength %d\n", NumberOfBytesTransferred); + } + + if(!restartDeviceIOSucceeded) { + + // NOTE: This MUST be handled at the end of this method. OnDeviceIoCtlFailed() will unmount the volume + // at which point the user-mode driver needs to wait on all outstanding IO operations in its Unmount() + // callback before returning control to this handler. If OnDeviceIoCtlFailed() is called above there will + // be 1 pending IO operation which could potentially queue an async operation. If everything gets cleaned up + // before that operation completes then bad things could happen. + + OnDeviceIoCtlFailed(Dokan, lastError); + } +} + +void ProcessWriteSizeEvent( + DOKAN_OVERLAPPED *Overlapped, + ULONG IoResult, + ULONG_PTR NumberOfBytesTransferred) { + + DOKAN_IO_EVENT *inputIoEvent = (DOKAN_IO_EVENT*)Overlapped->InputPayload; + DOKAN_IO_EVENT *outputIoEvent = (DOKAN_IO_EVENT*)Overlapped->OutputPayload; + + assert(inputIoEvent && outputIoEvent); + assert(inputIoEvent->EventResult); + assert(inputIoEvent->EventInfo.WriteFile.NumberOfBytesWritten == 0); + assert(outputIoEvent->DokanInstance); + + PushOverlapped(Overlapped); + + if(IoResult != NO_ERROR) { + + // This will push the input buffer so we don't need to manually do that + EndDispatchWrite(&inputIoEvent->EventInfo.WriteFile, STATUS_INTERNAL_ERROR); + + PushIoEventBuffer(outputIoEvent); + + return; + } + + FreeIoEventResult(inputIoEvent->EventResult, Overlapped->Flags); + PushIoEventBuffer(inputIoEvent); + + outputIoEvent->EventSize = (ULONG)NumberOfBytesTransferred; + + SetupIOEventForProcessing(outputIoEvent); + + BeginDispatchWrite(outputIoEvent); +} + +// Process the result of SendEventInformation() +void ProcessKernelResultEvent( + DOKAN_OVERLAPPED *Overlapped) { + + PEVENT_INFORMATION eventResult = (PEVENT_INFORMATION)Overlapped->InputPayload; + + assert(eventResult); + + FreeIoEventResult(eventResult, Overlapped->Flags); + + PushOverlapped(Overlapped); +} + +VOID CALLBACK DokanLoop( + _Inout_ PTP_CALLBACK_INSTANCE Instance, + _Inout_opt_ PVOID Context, + _Inout_opt_ PVOID Overlapped, + _In_ ULONG IoResult, + _In_ ULONG_PTR NumberOfBytesTransferred, + _Inout_ PTP_IO Io +) { + + UNREFERENCED_PARAMETER(Instance); + UNREFERENCED_PARAMETER(Io); + + PDOKAN_INSTANCE dokan = (PDOKAN_INSTANCE)Context; + DOKAN_OVERLAPPED *overlapped = (DOKAN_OVERLAPPED*)Overlapped; + + assert(dokan); + + switch(overlapped->PayloadType) { + + case DOKAN_OVERLAPPED_TYPE_IOEVENT: + ProcessIOEvent(dokan, overlapped, IoResult, NumberOfBytesTransferred); + break; + case DOKAN_OVERLAPPED_TYPE_IOEVENT_WRITE_SIZE: + ProcessWriteSizeEvent(overlapped, IoResult, NumberOfBytesTransferred); + break; + case DOKAN_OVERLAPPED_TYPE_IOEVENT_RESULT: + ProcessKernelResultEvent(overlapped); + break; + default: + DokanDbgPrintW(L"Unrecognized overlapped type of %d for dokan instance %s. The payload is probably being leaked.\n", overlapped->PayloadType, dokan->DeviceName); + PushOverlapped(overlapped); + break; } +} - CloseHandle(device); - free(buffer); - _endthreadex(result); +BOOL SendIoEventResult(DOKAN_IO_EVENT *EventInfo) { + + assert(EventInfo->EventResult); + + if(EventInfo->DokanOpenInfo) { + + InterlockedExchange64(&EventInfo->DokanOpenInfo->UserContext, EventInfo->DokanFileInfo.Context); + } - return result; + BOOL result = SendEventInformation(EventInfo->EventResult, EventInfo->DokanInstance, EventInfo->Flags); + + if(!result) { + + FreeIoEventResult(EventInfo->EventResult, EventInfo->Flags); + } + + PushIoEventBuffer(EventInfo); + + return result; } -VOID SendEventInformation(HANDLE Handle, PEVENT_INFORMATION EventInfo, - ULONG EventLength, PDOKAN_INSTANCE DokanInstance) { - BOOL status; - ULONG returnedLength; +BOOL SendEventInformation(PEVENT_INFORMATION EventInfo, + PDOKAN_INSTANCE DokanInstance, DOKAN_IO_EVENT_FLAGS EventFlags) { + + DOKAN_OVERLAPPED *overlapped = NULL; + DWORD lastError = 0; + DWORD eventSize = max(sizeof(EVENT_INFORMATION), DOKAN_EVENT_INFO_ALLOC_SIZE(EventInfo->BufferLength)); + + DbgPrint("Dokan Information: SendEventInformation() with NTSTATUS result 0x%x and context 0x%lx\n", + EventInfo->Status, EventInfo->Context); - // DbgPrint("###EventInfo->Context %X\n", EventInfo->Context); - if (DokanInstance != NULL) { - ReleaseDokanOpenInfo(EventInfo, DokanInstance); + overlapped = PopOverlapped(); + + if(!overlapped) { + + DbgPrint("Dokan Error: Failed to allocate overlapped info.\n"); + + return FALSE; } - // send event info to driver - status = DeviceIoControl(Handle, // Handle to device - IOCTL_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 - ); + overlapped->InputPayload = EventInfo; + overlapped->PayloadType = DOKAN_OVERLAPPED_TYPE_IOEVENT_RESULT; + overlapped->Flags = EventFlags; - if (!status) { - DWORD errorCode = GetLastError(); - DbgPrint("Dokan Error: Ioctl failed with code %d\n", errorCode); + StartThreadpoolIo(DokanInstance->ThreadInfo.IoCompletion); + + if(!DeviceIoControl( + DokanInstance->Device, // Handle to device + IOCTL_EVENT_INFO, // IO Control code + EventInfo, // Input Buffer to driver. + eventSize, // Length of input buffer in bytes. + NULL, // Output Buffer from driver. + 0, // Length of output buffer in bytes. + NULL, // Bytes placed in buffer. + (OVERLAPPED*)overlapped // asynchronous call + )) { + + lastError = GetLastError(); + + if(lastError != ERROR_IO_PENDING) { + + DbgPrint("Dokan Error: Dokan device result ioctl failed for wait with code %d.\n", lastError); + + CancelThreadpoolIo(DokanInstance->ThreadInfo.IoCompletion); + + PushOverlapped(overlapped); + + return FALSE; + } } + + return TRUE; } VOID CheckFileName(LPWSTR FileName) { @@ -522,93 +1407,45 @@ VOID CheckFileName(LPWSTR FileName) { FileName[len - 1] = '\0'; } -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); +void CreateDispatchCommon(DOKAN_IO_EVENT *EventInfo, ULONG SizeOfEventInfo) { - if (eventInfo == NULL) { - return NULL; - } - RtlZeroMemory(eventInfo, SizeOfEventInfo); - RtlZeroMemory(DokanFileInfo, sizeof(DOKAN_FILE_INFO)); + assert(EventInfo != NULL); + assert(EventInfo->EventResult == NULL && EventInfo->EventResultSize == 0); - eventInfo->BufferLength = 0; - eventInfo->SerialNumber = EventContext->SerialNumber; + if(SizeOfEventInfo <= DOKAN_EVENT_INFO_DEFAULT_BUFFER_SIZE) { - 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; + EventInfo->EventResult = PopEventResult(); + EventInfo->EventResultSize = DOKAN_EVENT_INFO_DEFAULT_SIZE; + EventInfo->Flags |= DOKAN_IO_EVENT_FLAGS_POOLED_RESULT; } + else { - *DokanOpenInfo = GetDokanOpenInfo(EventContext, DokanInstance); - if (*DokanOpenInfo == NULL) { - DbgPrint("error openInfo is NULL\n"); - return eventInfo; - } + EventInfo->EventResultSize = DOKAN_EVENT_INFO_ALLOC_SIZE(SizeOfEventInfo); + EventInfo->EventResult = (PEVENT_INFORMATION)malloc(EventInfo->EventResultSize); + EventInfo->Flags &= ~DOKAN_IO_EVENT_FLAGS_POOLED_RESULT; - DokanFileInfo->Context = (ULONG64)(*DokanOpenInfo)->UserContext; - DokanFileInfo->IsDirectory = (UCHAR)(*DokanOpenInfo)->IsDirectory; - DokanFileInfo->DokanContext = (ULONG64)(*DokanOpenInfo); + RtlZeroMemory(EventInfo->EventResult, EventInfo->EventResultSize); + } - eventInfo->Context = (ULONG64)(*DokanOpenInfo); + assert(EventInfo->EventResult && EventInfo->EventResultSize >= DOKAN_EVENT_INFO_ALLOC_SIZE(SizeOfEventInfo)); - return eventInfo; + EventInfo->EventResult->SerialNumber = EventInfo->KernelInfo.EventContext.SerialNumber; + EventInfo->EventResult->Context = EventInfo->KernelInfo.EventContext.Context; } -PDOKAN_OPEN_INFO -GetDokanOpenInfo(PEVENT_CONTEXT EventContext, PDOKAN_INSTANCE DokanInstance) { - PDOKAN_OPEN_INFO openInfo; - EnterCriticalSection(&DokanInstance->CriticalSection); +void FreeIoEventResult(PEVENT_INFORMATION EventResult, DOKAN_IO_EVENT_FLAGS Flags) { - openInfo = (PDOKAN_OPEN_INFO)(UINT_PTR)EventContext->Context; - if (openInfo != NULL) { - openInfo->OpenCount++; - openInfo->EventContext = EventContext; - openInfo->DokanInstance = DokanInstance; - } - LeaveCriticalSection(&DokanInstance->CriticalSection); - return openInfo; -} + if(EventResult) { + + if(Flags & DOKAN_IO_EVENT_FLAGS_POOLED_RESULT) { -VOID ReleaseDokanOpenInfo(PEVENT_INFORMATION EventInformation, - PDOKAN_INSTANCE DokanInstance) { - PDOKAN_OPEN_INFO openInfo; - EnterCriticalSection(&DokanInstance->CriticalSection); + PushEventResult(EventResult); + } + else { - 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; - } - free(openInfo); - EventInformation->Context = 0; - } - } - LeaveCriticalSection(&DokanInstance->CriticalSection); + free(EventResult); + } + } } // ask driver to release all pending IRP to prepare for Unmount. @@ -745,6 +1582,7 @@ BOOL SendToDevice(LPCWSTR DeviceName, DWORD IoControlCode, PVOID InputBuffer, ); if (device == INVALID_HANDLE_VALUE) { + DWORD dwErrorCode = GetLastError(); DbgPrint("Dokan Error: Failed to open %ws with code %d\n", DeviceName, dwErrorCode); @@ -764,7 +1602,10 @@ BOOL SendToDevice(LPCWSTR DeviceName, DWORD IoControlCode, PVOID InputBuffer, CloseHandle(device); if (!status) { - DbgPrint("DokanError: Ioctl failed with code %d\n", GetLastError()); + + DWORD dwErrorCode = GetLastError(); + + DbgPrint("DokanError: Ioctl failed with code %d\n", dwErrorCode); return FALSE; } @@ -808,26 +1649,137 @@ BOOL WINAPI DllMain(HINSTANCE Instance, DWORD Reason, LPVOID Reserved) { case DLL_PROCESS_ATTACH: { #if _MSC_VER < 1300 InitializeCriticalSection(&g_InstanceCriticalSection); + InitializeCriticalSection(&g_EventBufferCriticalSection); + InitializeCriticalSection(&g_OverlappedCriticalSection); + InitializeCriticalSection(&g_EventResultCriticalSection); + InitializeCriticalSection(&g_FileInfoCriticalSection); + InitializeCriticalSection(&g_DirectoryListCriticalSection); #else - InitializeCriticalSectionAndSpinCount(&g_InstanceCriticalSection, - 0x80000400); + InitializeCriticalSectionAndSpinCount(&g_InstanceCriticalSection, 0x80000400); + InitializeCriticalSectionAndSpinCount(&g_EventBufferCriticalSection, 0x80000400); + InitializeCriticalSectionAndSpinCount(&g_OverlappedCriticalSection, 0x80000400); + InitializeCriticalSectionAndSpinCount(&g_EventResultCriticalSection, 0x80000400); + InitializeCriticalSectionAndSpinCount(&g_FileInfoCriticalSection, 0x80000400); + InitializeCriticalSectionAndSpinCount(&g_DirectoryListCriticalSection, 0x80000400); #endif InitializeListHead(&g_InstanceList); + InitializeThreadPool(Instance); + + g_EventBufferPool = DokanVector_AllocWithCapacity(sizeof(void*), DOKAN_IO_EVENT_POOL_SIZE); + g_OverlappedPool = DokanVector_AllocWithCapacity(sizeof(void*), DOKAN_OVERLAPPED_POOL_SIZE); + g_EventResultPool = DokanVector_AllocWithCapacity(sizeof(void*), DOKAN_OVERLAPPED_POOL_SIZE); + g_FileInfoPool = DokanVector_AllocWithCapacity(sizeof(void*), DOKAN_OVERLAPPED_POOL_SIZE); + g_DirectoryListPool = DokanVector_AllocWithCapacity(sizeof(void*), DOKAN_DIRECTORY_LIST_POOL_SIZE); + } break; case DLL_PROCESS_DETACH: { + EnterCriticalSection(&g_InstanceCriticalSection); while (!IsListEmpty(&g_InstanceList)) { + PLIST_ENTRY entry = RemoveHeadList(&g_InstanceList); - PDOKAN_INSTANCE instance = + + PDOKAN_INSTANCE instance = CONTAINING_RECORD(entry, DOKAN_INSTANCE, ListEntry); - DokanRemoveMountPoint(instance->MountPoint); - free(instance); + + DokanCloseHandle((DOKAN_HANDLE)instance); } LeaveCriticalSection(&g_InstanceCriticalSection); + + CleanupThreadpool(); + + //////////////////// IO event buffer object pool //////////////////// + { + EnterCriticalSection(&g_EventBufferCriticalSection); + + for(size_t i = 0; i < DokanVector_GetCount(g_EventBufferPool); ++i) { + + FreeIOEventBuffer(*(DOKAN_IO_EVENT**)DokanVector_GetItem(g_EventBufferPool, i)); + } + + DokanVector_Free(g_EventBufferPool); + g_EventBufferPool = NULL; + + LeaveCriticalSection(&g_EventBufferCriticalSection); + + DeleteCriticalSection(&g_EventBufferCriticalSection); + } + + //////////////////// Overlapped object pool //////////////////// + { + EnterCriticalSection(&g_OverlappedCriticalSection); + + for(size_t i = 0; i < DokanVector_GetCount(g_OverlappedPool); ++i) { + + FreeOverlapped(*(DOKAN_OVERLAPPED**)DokanVector_GetItem(g_OverlappedPool, i)); + } + + DokanVector_Free(g_OverlappedPool); + g_OverlappedPool = NULL; + + LeaveCriticalSection(&g_OverlappedCriticalSection); + + DeleteCriticalSection(&g_OverlappedCriticalSection); + } + + //////////////////// Event result object pool //////////////////// + { + EnterCriticalSection(&g_EventResultCriticalSection); + + for(size_t i = 0; i < DokanVector_GetCount(g_EventResultPool); ++i) { + + FreeEventResult(*(EVENT_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(*(DOKAN_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(*(DOKAN_VECTOR**)DokanVector_GetItem(g_DirectoryListPool, i)); + } + + DokanVector_Free(g_DirectoryListPool); + g_DirectoryListPool = NULL; + + LeaveCriticalSection(&g_DirectoryListCriticalSection); + + DeleteCriticalSection(&g_DirectoryListCriticalSection); + } + + //////////////////// Object pool cleanup finished //////////////////// + DeleteCriticalSection(&g_InstanceCriticalSection); + } break; } return TRUE; diff --git a/dokan/dokan.h b/dokan/dokan.h index beb5899c9..e68534021 100644 --- a/dokan/dokan.h +++ b/dokan/dokan.h @@ -66,10 +66,11 @@ extern "C" { #define DOKAN_OPTION_ASYNC_IO (1 << 9) // use asynchronous IO #define DOKAN_OPTION_FORCE_SINGLE_THREADED (1 << 10) // Dokan uses a single thread. If DOKAN_OPTION_ASYNC_IO is specified the thread waits until the async job is over before starting another. +typedef void *DOKAN_HANDLE, **PDOKAN_HANDLE; + typedef struct _DOKAN_OPTIONS { USHORT Version; // Supported Dokan Version, ex. "530" (Dokan ver 0.5.3) - USHORT ThreadCount; // number of threads to be - // used internally by Dokan library + USHORT ThreadCount; // Unused ULONG Options; // combination of DOKAN_OPTIONS_* ULONG64 GlobalContext; // FileSystem can store anything here LPCWSTR MountPoint; // mount point "M:\" (drive letter) or "C:\mount\dokan" @@ -81,34 +82,191 @@ typedef struct _DOKAN_OPTIONS { } DOKAN_OPTIONS, *PDOKAN_OPTIONS; typedef struct _DOKAN_FILE_INFO { - ULONG64 Context; // FileSystem can store anything here - ULONG64 DokanContext; // Used internally, never modify - PDOKAN_OPTIONS DokanOptions; // A pointer to DOKAN_OPTIONS - // which was passed to DokanMain. - ULONG ProcessId; // process id for the thread that originally requested a - // given I/O operation - UCHAR IsDirectory; // requesting a directory file - UCHAR DeleteOnClose; // Delete on when "cleanup" is called - UCHAR PagingIo; // Read or write is paging IO. - UCHAR SynchronousIo; // Read or write is synchronous IO. - UCHAR Nocache; - UCHAR WriteToEndOfFile; // If true, write to the current end of file instead - // of Offset parameter. + ULONG64 Context; // FileSystem can store anything here + PVOID DokanContext; // For internal use only + PDOKAN_OPTIONS DokanOptions; // A pointer to DOKAN_OPTIONS + // which was passed to DokanMain. + BOOL IsDirectory; // requesting a directory file + ULONG ProcessId; // process id for the thread that originally requested a + // given I/O operation + UCHAR DeleteOnClose; // Delete on when "cleanup" is called + UCHAR PagingIo; // Read or write is paging IO. + UCHAR SynchronousIo; // Read or write is synchronous IO. + UCHAR Nocache; + UCHAR WriteToEndOfFile; // If true, write to the current end of file instead + // of Offset parameter. } DOKAN_FILE_INFO, *PDOKAN_FILE_INFO; +typedef struct _DOKAN_MOUNTED_INFO { + PDOKAN_OPTIONS DokanOptions; // A pointer to DOKAN_OPTIONS + PTP_POOL ThreadPool; // The thread pool associated with the Dokan context +} DOKAN_MOUNTED_INFO, *PDOKAN_MOUNTED_INFO; + +typedef struct _DOKAN_UNMOUNTED_INFO { + PDOKAN_OPTIONS DokanOptions; // A pointer to DOKAN_OPTIONS +} DOKAN_UNMOUNTED_INFO, *PDOKAN_UNMOUNTED_INFO; + +// Forward declarations +struct _DOKAN_FIND_FILES_EVENT; +typedef struct _DOKAN_FIND_FILES_EVENT DOKAN_FIND_FILES_EVENT, *PDOKAN_FIND_FILES_EVENT; + +struct _DOKAN_FIND_FILES_PATTERN_EVENT; +typedef struct _DOKAN_FIND_FILES_PATTERN_EVENT DOKAN_FIND_FILES_PATTERN_EVENT, *PDOKAN_FIND_FILES_PATTERN_EVENT; + +struct _DOKAN_FIND_STREAMS_EVENT; +typedef struct _DOKAN_FIND_STREAMS_EVENT DOKAN_FIND_STREAMS_EVENT, *PDOKAN_FIND_STREAMS_EVENT; + // FillFindData // is used to add an entry in FindFiles // returns 1 if buffer is full, otherwise 0 // (currently it never returns 1) -typedef int(WINAPI *PFillFindData)(PWIN32_FIND_DATAW, PDOKAN_FILE_INFO); +typedef int (WINAPI *PFillFindData)(PDOKAN_FIND_FILES_EVENT, PWIN32_FIND_DATAW); + +// FillFindDataWithPattern +// is used to add an entry in FindFilesWithPattern +// returns 1 if buffer is full, otherwise 0 +// (currently it never returns 1) +typedef int (WINAPI *PFillFindDataWithPattern)(PDOKAN_FIND_FILES_PATTERN_EVENT, PWIN32_FIND_DATAW); // FillFindStreamData // is used to add an entry in FindStreams // returns 1 if buffer is full, otherwise 0 -// (currently it never returns 1) -typedef int(WINAPI *PFillFindStreamData)(PWIN32_FIND_STREAM_DATA, - PDOKAN_FILE_INFO); +typedef int (WINAPI *PFillFindStreamData)(PDOKAN_FIND_STREAMS_EVENT, PWIN32_FIND_STREAM_DATA); + +typedef struct _DOKAN_CREATE_FILE_EVENT { + PDOKAN_FILE_INFO DokanFileInfo; + LPCWSTR FileName; + LPCWSTR OriginalFileName; + DOKAN_IO_SECURITY_CONTEXT SecurityContext; // https://msdn.microsoft.com/en-us/library/windows/hardware/ff550613(v=vs.85).aspx + + ACCESS_MASK DesiredAccess; + ULONG FileAttributes; + ULONG ShareAccess; + ULONG CreateDisposition; + ULONG CreateOptions; +} DOKAN_CREATE_FILE_EVENT, *PDOKAN_CREATE_FILE_EVENT; + +typedef struct _DOKAN_CLEANUP_EVENT { + PDOKAN_FILE_INFO DokanFileInfo; + LPCWSTR FileName; +} DOKAN_CLEANUP_EVENT, *PDOKAN_CLEANUP_EVENT; + +typedef DOKAN_CLEANUP_EVENT DOKAN_CLOSE_FILE_EVENT, *PDOKAN_CLOSE_FILE_EVENT; +typedef DOKAN_CLEANUP_EVENT DOKAN_FLUSH_BUFFERS_EVENT, *PDOKAN_FLUSH_BUFFERS_EVENT; +typedef DOKAN_CLEANUP_EVENT DOKAN_CAN_DELETE_FILE_EVENT, *PDOKAN_CAN_DELETE_FILE_EVENT; + +typedef struct _DOKAN_READ_FILE_EVENT { + PDOKAN_FILE_INFO DokanFileInfo; + LPCWSTR FileName; + LPVOID Buffer; + LONGLONG Offset; + DWORD NumberOfBytesToRead; + DWORD NumberOfBytesRead; +} DOKAN_READ_FILE_EVENT, *PDOKAN_READ_FILE_EVENT; + +typedef struct _DOKAN_WRITE_FILE_EVENT { + PDOKAN_FILE_INFO DokanFileInfo; + LPCWSTR FileName; + LPCVOID Buffer; + LONGLONG Offset; + DWORD NumberOfBytesToWrite; + DWORD NumberOfBytesWritten; +} DOKAN_WRITE_FILE_EVENT, *PDOKAN_WRITE_FILE_EVENT; + +typedef struct _DOKAN_GET_FILE_INFO_EVENT { + PDOKAN_FILE_INFO DokanFileInfo; + LPCWSTR FileName; + BY_HANDLE_FILE_INFORMATION FileHandleInfo; +} DOKAN_GET_FILE_INFO_EVENT, *PDOKAN_GET_FILE_INFO_EVENT; + +typedef struct _DOKAN_FIND_FILES_EVENT { + PDOKAN_FILE_INFO DokanFileInfo; + LPCWSTR PathName; + PFillFindData FillFindData; // call this function with PWIN32_FIND_DATAW +} DOKAN_FIND_FILES_EVENT, *PDOKAN_FIND_FILES_EVENT; + +typedef struct _DOKAN_FIND_FILES_PATTERN_EVENT { + PDOKAN_FILE_INFO DokanFileInfo; + LPCWSTR PathName; + LPCWSTR SearchPattern; + PFillFindDataWithPattern FillFindData; // call this function with PWIN32_FIND_DATAW +} DOKAN_FIND_FILES_PATTERN_EVENT, *PDOKAN_FIND_FILES_PATTERN_EVENT; + +typedef struct _DOKAN_SET_FILE_BASIC_INFO_EVENT { + PDOKAN_FILE_INFO DokanFileInfo; + LPCWSTR FileName; + FILE_BASIC_INFORMATION *Info; +} DOKAN_SET_FILE_BASIC_INFO_EVENT, *PDOKAN_SET_FILE_BASIC_INFO_EVENT; + +typedef struct _DOKAN_MOVE_FILE_EVENT { + PDOKAN_FILE_INFO DokanFileInfo; + LPCWSTR FileName; + LPCWSTR NewFileName; + BOOL ReplaceIfExists; +} DOKAN_MOVE_FILE_EVENT, *PDOKAN_MOVE_FILE_EVENT; + +typedef struct _DOKAN_SET_EOF_EVENT { + PDOKAN_FILE_INFO DokanFileInfo; + LPCWSTR FileName; + LONGLONG Length; +} DOKAN_SET_EOF_EVENT, *PDOKAN_SET_EOF_EVENT; + +typedef DOKAN_SET_EOF_EVENT DOKAN_SET_ALLOCATION_SIZE_EVENT, *PDOKAN_SET_ALLOCATION_SIZE_EVENT; + +typedef struct _DOKAN_LOCK_FILE_EVENT { + PDOKAN_FILE_INFO DokanFileInfo; + LPCWSTR FileName; + LONGLONG ByteOffset; + LONGLONG Length; + ULONG Key; +} DOKAN_LOCK_FILE_EVENT, *PDOKAN_LOCK_FILE_EVENT; + +typedef DOKAN_LOCK_FILE_EVENT DOKAN_UNLOCK_FILE_EVENT, *PDOKAN_UNLOCK_FILE_EVENT; + +// see FILE_FS_FULL_SIZE_INFORMATION for more information +// https://msdn.microsoft.com/en-us/library/windows/hardware/ff540267(v=vs.85).aspx +typedef struct _DOKAN_GET_DISK_FREE_SPACE_EVENT { + PDOKAN_FILE_INFO DokanFileInfo; + ULONGLONG FreeBytesAvailable; + ULONGLONG TotalNumberOfBytes; + ULONGLONG TotalNumberOfFreeBytes; +} DOKAN_GET_DISK_FREE_SPACE_EVENT, *PDOKAN_GET_DISK_FREE_SPACE_EVENT; + +typedef struct _DOKAN_GET_VOLUME_INFO_EVENT { + PDOKAN_FILE_INFO DokanFileInfo; + PFILE_FS_VOLUME_INFORMATION VolumeInfo; + DWORD MaxLabelLengthInChars; +} DOKAN_GET_VOLUME_INFO_EVENT, *PDOKAN_GET_VOLUME_INFO_EVENT; + +typedef struct _DOKAN_GET_VOLUME_ATTRIBUTES_EVENT { + PDOKAN_FILE_INFO DokanFileInfo; + PFILE_FS_ATTRIBUTE_INFORMATION Attributes; + DWORD MaxFileSystemNameLengthInChars; +} DOKAN_GET_VOLUME_ATTRIBUTES_EVENT, *PDOKAN_GET_VOLUME_ATTRIBUTES_EVENT; + +typedef struct _DOKAN_GET_FILE_SECURITY_EVENT { + PDOKAN_FILE_INFO DokanFileInfo; + LPWSTR FileName; + PSECURITY_INFORMATION SecurityInformation; // A pointer to SECURITY_INFORMATION value being requested + PSECURITY_DESCRIPTOR SecurityDescriptor; // A pointer to SECURITY_DESCRIPTOR buffer to be filled + ULONG SecurityDescriptorSize; // length of Security descriptor buffer + ULONG LengthNeeded; +} DOKAN_GET_FILE_SECURITY_EVENT, *PDOKAN_GET_FILE_SECURITY_EVENT; + +typedef struct _DOKAN_SET_FILE_SECURITY_EVENT { + PDOKAN_FILE_INFO DokanFileInfo; + LPWSTR FileName; + PSECURITY_INFORMATION SecurityInformation; + PSECURITY_DESCRIPTOR SecurityDescriptor; // A pointer to SECURITY_DESCRIPTOR buffer to be filled + ULONG SecurityDescriptorSize; // length of Security descriptor buffer +} DOKAN_SET_FILE_SECURITY_EVENT, *PDOKAN_SET_FILE_SECURITY_EVENT; + +typedef struct _DOKAN_FIND_STREAMS_EVENT { + PDOKAN_FILE_INFO DokanFileInfo; + LPWSTR FileName; + PFillFindStreamData FillFindStreamData; // call this function with PWIN32_FIND_STREAM_DATA +} DOKAN_FIND_STREAMS_EVENT, *PDOKAN_FIND_STREAMS_EVENT; typedef struct _DOKAN_OPERATIONS { @@ -127,80 +285,35 @@ typedef struct _DOKAN_OPERATIONS { // See ZwCreateFile() // https://msdn.microsoft.com/en-us/library/windows/hardware/ff566424(v=vs.85).aspx // for more information about the parameters of this callback. - NTSTATUS(DOKAN_CALLBACK *ZwCreateFile) - (LPCWSTR, // FileName - PDOKAN_IO_SECURITY_CONTEXT, // SecurityContext, see - // https://msdn.microsoft.com/en-us/library/windows/hardware/ff550613(v=vs.85).aspx - ACCESS_MASK, // DesiredAccess - ULONG, // FileAttributes - ULONG, // ShareAccess - ULONG, // CreateDisposition - ULONG, // CreateOptions - PDOKAN_FILE_INFO); - +NTSTATUS(DOKAN_CALLBACK *ZwCreateFile)(_In_ DOKAN_CREATE_FILE_EVENT *EventInfo); + // When FileInfo->DeleteOnClose is true, you must delete the file in Cleanup. // Refer to comment at DeleteFile definition below in this file for // explanation. - void(DOKAN_CALLBACK *Cleanup)(LPCWSTR, // FileName - PDOKAN_FILE_INFO); + void(DOKAN_CALLBACK *Cleanup)(_In_ DOKAN_CLEANUP_EVENT *EventInfo); - void(DOKAN_CALLBACK *CloseFile)(LPCWSTR, // FileName - PDOKAN_FILE_INFO); + void(DOKAN_CALLBACK *CloseFile)(_In_ DOKAN_CLOSE_FILE_EVENT *EventInfo); // ReadFile and WriteFile can be called from multiple threads in // the same time with the same DOKAN_FILE_INFO.Context if a OVERLAPPED is // requested. - NTSTATUS(DOKAN_CALLBACK *ReadFile) - (LPCWSTR, // FileName - LPVOID, // Buffer - DWORD, // NumberOfBytesToRead - LPDWORD, // NumberOfBytesRead - LONGLONG, // Offset - PDOKAN_FILE_INFO); - - NTSTATUS(DOKAN_CALLBACK *WriteFile) - (LPCWSTR, // FileName - LPCVOID, // Buffer - DWORD, // NumberOfBytesToWrite - LPDWORD, // NumberOfBytesWritten - LONGLONG, // Offset - PDOKAN_FILE_INFO); - - NTSTATUS(DOKAN_CALLBACK *FlushFileBuffers) - (LPCWSTR, // FileName - PDOKAN_FILE_INFO); - - NTSTATUS(DOKAN_CALLBACK *GetFileInformation) - (LPCWSTR, // FileName - LPBY_HANDLE_FILE_INFORMATION, // Buffer - PDOKAN_FILE_INFO); + NTSTATUS(DOKAN_CALLBACK *ReadFile)(_In_ DOKAN_READ_FILE_EVENT *EventInfo); + + NTSTATUS(DOKAN_CALLBACK *WriteFile)(_In_ DOKAN_WRITE_FILE_EVENT *EventInfo); + + NTSTATUS(DOKAN_CALLBACK *FlushFileBuffers)(_In_ DOKAN_FLUSH_BUFFERS_EVENT *EventInfo); + + NTSTATUS(DOKAN_CALLBACK *GetFileInformation)(_In_ DOKAN_GET_FILE_INFO_EVENT *EventInfo); // FindFilesWithPattern is checking first. If it is not implemented or // returns STATUS_NOT_IMPLEMENTED, then FindFiles is called, if implemented. - NTSTATUS(DOKAN_CALLBACK *FindFiles) - (LPCWSTR, // PathName - PFillFindData, // call this function with PWIN32_FIND_DATAW - PDOKAN_FILE_INFO); // (see PFillFindData definition) + NTSTATUS(DOKAN_CALLBACK *FindFiles)(_In_ DOKAN_FIND_FILES_EVENT *EventInfo); - NTSTATUS(DOKAN_CALLBACK *FindFilesWithPattern) - (LPCWSTR, // PathName - LPCWSTR, // SearchPattern - PFillFindData, // call this function with PWIN32_FIND_DATAW - PDOKAN_FILE_INFO); + NTSTATUS(DOKAN_CALLBACK *FindFilesWithPattern)(_In_ DOKAN_FIND_FILES_PATTERN_EVENT *EventInfo); // SetFileAttributes and SetFileTime are called only if both of them // are implemented. - NTSTATUS(DOKAN_CALLBACK *SetFileAttributes) - (LPCWSTR, // FileName - DWORD, // FileAttributes - PDOKAN_FILE_INFO); - - NTSTATUS(DOKAN_CALLBACK *SetFileTime) - (LPCWSTR, // FileName - CONST FILETIME *, // CreationTime - CONST FILETIME *, // LastAccessTime - CONST FILETIME *, // LastWriteTime - PDOKAN_FILE_INFO); + NTSTATUS(DOKAN_CALLBACK *SetFileBasicInformation)(_In_ DOKAN_SET_FILE_BASIC_INFO_EVENT *EventInfo); // You should not delete the file on DeleteFile or DeleteDirectory, but // instead @@ -212,53 +325,25 @@ typedef struct _DOKAN_OPERATIONS { // When you return STATUS_SUCCESS, you get a Cleanup call afterwards with // FileInfo->DeleteOnClose set to TRUE and only then you have to actually // delete the file being closed. - NTSTATUS(DOKAN_CALLBACK *DeleteFile) - (LPCWSTR, // FileName - PDOKAN_FILE_INFO); - - NTSTATUS(DOKAN_CALLBACK *DeleteDirectory) - (LPCWSTR, // FileName - PDOKAN_FILE_INFO); - - NTSTATUS(DOKAN_CALLBACK *MoveFile) - (LPCWSTR, // ExistingFileName - LPCWSTR, // NewFileName - BOOL, // ReplaceExisiting - PDOKAN_FILE_INFO); - - NTSTATUS(DOKAN_CALLBACK *SetEndOfFile) - (LPCWSTR, // FileName - LONGLONG, // Length - PDOKAN_FILE_INFO); - - NTSTATUS(DOKAN_CALLBACK *SetAllocationSize) - (LPCWSTR, // FileName - LONGLONG, // Length - PDOKAN_FILE_INFO); - - NTSTATUS(DOKAN_CALLBACK *LockFile) - (LPCWSTR, // FileName - LONGLONG, // ByteOffset - LONGLONG, // Length - PDOKAN_FILE_INFO); - - NTSTATUS(DOKAN_CALLBACK *UnlockFile) - (LPCWSTR, // FileName - LONGLONG, // ByteOffset - LONGLONG, // Length - PDOKAN_FILE_INFO); - - // Neither GetDiskFreeSpace nor GetVolumeInformation + NTSTATUS(DOKAN_CALLBACK *CanDeleteFile)(_In_ DOKAN_CAN_DELETE_FILE_EVENT *EventInfo); + + NTSTATUS(DOKAN_CALLBACK *MoveFileW)(_In_ DOKAN_MOVE_FILE_EVENT *EventInfo); + + NTSTATUS(DOKAN_CALLBACK *SetEndOfFile)(_In_ DOKAN_SET_EOF_EVENT *EventInfo); + + NTSTATUS(DOKAN_CALLBACK *SetAllocationSize)(_In_ DOKAN_SET_ALLOCATION_SIZE_EVENT *EventInfo); + + NTSTATUS(DOKAN_CALLBACK *LockFile)(_In_ DOKAN_LOCK_FILE_EVENT *EventInfo); + + NTSTATUS(DOKAN_CALLBACK *UnlockFile)(_In_ DOKAN_UNLOCK_FILE_EVENT *EventInfo); + + // Neither GetVolumeFreeSpace nor GetVolumeInformation // save the DokanFileContext->Context. // Before these methods are called, CreateFile may not be called. // (ditto CloseFile and Cleanup) // see Win32 API GetDiskFreeSpaceEx - NTSTATUS(DOKAN_CALLBACK *GetDiskFreeSpace) - (PULONGLONG, // FreeBytesAvailable - PULONGLONG, // TotalNumberOfBytes - PULONGLONG, // TotalNumberOfFreeBytes - PDOKAN_FILE_INFO); + NTSTATUS(DOKAN_CALLBACK *GetVolumeFreeSpace)(_In_ DOKAN_GET_DISK_FREE_SPACE_EVENT *EventInfo); // Note: // FILE_READ_ONLY_VOLUME is automatically added to the @@ -266,44 +351,23 @@ typedef struct _DOKAN_OPERATIONS { // specified in DOKAN_OPTIONS when the volume was mounted. // see Win32 API GetVolumeInformation - NTSTATUS(DOKAN_CALLBACK *GetVolumeInformation) - (LPWSTR, // VolumeNameBuffer - DWORD, // VolumeNameSize in num of chars - LPDWORD, // VolumeSerialNumber - LPDWORD, // MaximumComponentLength in num of chars - LPDWORD, // FileSystemFlags - LPWSTR, // FileSystemNameBuffer - DWORD, // FileSystemNameSize in num of chars - PDOKAN_FILE_INFO); + NTSTATUS(DOKAN_CALLBACK *GetVolumeInformationW)(_In_ DOKAN_GET_VOLUME_INFO_EVENT *EventInfo); + + NTSTATUS(DOKAN_CALLBACK *GetVolumeAttributes)(_In_ DOKAN_GET_VOLUME_ATTRIBUTES_EVENT *EventInfo); - NTSTATUS(DOKAN_CALLBACK *Mounted)(PDOKAN_FILE_INFO); + void(DOKAN_CALLBACK *Mounted)(DOKAN_MOUNTED_INFO *EventInfo); - NTSTATUS(DOKAN_CALLBACK *Unmounted)(PDOKAN_FILE_INFO); + void(DOKAN_CALLBACK *Unmounted)(DOKAN_UNMOUNTED_INFO *EventInfo); // Suported since 0.6.0. You must specify the version at // DOKAN_OPTIONS.Version. - NTSTATUS(DOKAN_CALLBACK *GetFileSecurity) - (LPCWSTR, // FileName - PSECURITY_INFORMATION, // A pointer to SECURITY_INFORMATION value being - // requested - PSECURITY_DESCRIPTOR, // A pointer to SECURITY_DESCRIPTOR buffer to be filled - ULONG, // length of Security descriptor buffer - PULONG, // LengthNeeded - PDOKAN_FILE_INFO); - - NTSTATUS(DOKAN_CALLBACK *SetFileSecurity) - (LPCWSTR, // FileName - PSECURITY_INFORMATION, - PSECURITY_DESCRIPTOR, // SecurityDescriptor - ULONG, // SecurityDescriptor length - PDOKAN_FILE_INFO); + NTSTATUS(DOKAN_CALLBACK *GetFileSecurityW)(_In_ DOKAN_GET_FILE_SECURITY_EVENT *EventInfo); + + NTSTATUS(DOKAN_CALLBACK *SetFileSecurityW)(_In_ DOKAN_SET_FILE_SECURITY_EVENT *EventInfo); // Supported since 0.8.0. You must specify the version at // DOKAN_OPTIONS.Version. - NTSTATUS(DOKAN_CALLBACK *FindStreams) - (LPCWSTR, // FileName - PFillFindStreamData, // call this function with PWIN32_FIND_STREAM_DATA - PDOKAN_FILE_INFO); // (see PFillFindStreamData definition) + NTSTATUS(DOKAN_CALLBACK *FindStreams)(_In_ DOKAN_FIND_STREAMS_EVENT *EventInfo); } DOKAN_OPERATIONS, *PDOKAN_OPERATIONS; @@ -325,9 +389,26 @@ typedef struct _DOKAN_CONTROL { #define DOKAN_MOUNT_POINT_ERROR -6 /* Mount point is invalid */ #define DOKAN_VERSION_ERROR -7 /* Requested an incompatible version */ +#define DOKAN_SUCCEEDED(x) ((x) == DOKAN_SUCCESS) +#define DOKAN_FAILED(x) ((x) != DOKAN_SUCCESS) + int DOKANAPI DokanMain(PDOKAN_OPTIONS DokanOptions, PDOKAN_OPERATIONS DokanOperations); +int DOKANAPI DokanCreateFileSystem( + _In_ PDOKAN_OPTIONS DokanOptions, + _In_ PDOKAN_OPERATIONS DokanOperations, + _Out_ DOKAN_HANDLE *DokanInstance); + +BOOL DOKANAPI DokanIsFileSystemRunning(_In_ DOKAN_HANDLE DokanInstance); + +// See WaitForSingleObject() for a description of return values +DWORD DOKANAPI DokanWaitForFileSystemClosed( + DOKAN_HANDLE DokanInstance, + DWORD dwMilliseconds); + +void DOKANAPI DokanCloseHandle(DOKAN_HANDLE DokanInstance); + BOOL DOKANAPI DokanUnmount(WCHAR DriveLetter); BOOL DOKANAPI DokanRemoveMountPoint(LPCWSTR MountPoint); @@ -352,7 +433,7 @@ BOOL DOKANAPI DokanResetTimeout(ULONG Timeout, // timeout in millisecond // This method needs be called in CreateFile, OpenDirectory or CreateDirectly // callback. // The caller must call CloseHandle for the returned handle. -HANDLE DOKANAPI DokanOpenRequestorToken(PDOKAN_FILE_INFO DokanFileInfo); +HANDLE DOKANAPI DokanOpenRequestorToken(PDOKAN_CREATE_FILE_EVENT DokanFileInfo); BOOL DOKANAPI DokanGetMountPointList(PDOKAN_CONTROL list, ULONG length, BOOL uncOnly, PULONG nbRead); @@ -364,6 +445,28 @@ VOID DOKANAPI DokanMapKernelToUserCreateFileFlags( // Translate Win32 Error code to the NtStatus code's corresponding NTSTATUS DOKANAPI DokanNtStatusFromWin32(DWORD Error); +// Async IO completion functions +void DOKANAPI EndDispatchCreate(DOKAN_CREATE_FILE_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI EndDispatchRead(DOKAN_READ_FILE_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI EndDispatchWrite(DOKAN_WRITE_FILE_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI EndDispatchFindFiles(DOKAN_FIND_FILES_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI EndDispatchFindFilesWithPattern(DOKAN_FIND_FILES_PATTERN_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI EndDispatchGetFileInformation(DOKAN_GET_FILE_INFO_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI EndDispatchFindStreams(DOKAN_FIND_STREAMS_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI EndDispatchGetVolumeInfo(DOKAN_GET_VOLUME_INFO_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI EndDispatchGetVolumeFreeSpace(DOKAN_GET_DISK_FREE_SPACE_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI EndDispatchGetVolumeAttributes(DOKAN_GET_VOLUME_ATTRIBUTES_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI EndDispatchLockFile(DOKAN_LOCK_FILE_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI EndDispatchUnlockFile(DOKAN_LOCK_FILE_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI EndDispatchSetAllocationSize(DOKAN_SET_ALLOCATION_SIZE_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI EndDispatchSetFileBasicInformation(DOKAN_SET_FILE_BASIC_INFO_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI EndDispatchCanDeleteFile(DOKAN_CAN_DELETE_FILE_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI EndDispatchSetEndOfFile(DOKAN_SET_EOF_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI EndDispatchMoveFile(DOKAN_MOVE_FILE_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI EndDispatchFlush(DOKAN_FLUSH_BUFFERS_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI EndDispatchGetFileSecurity(DOKAN_GET_FILE_SECURITY_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI EndDispatchSetFileSecurity(DOKAN_SET_FILE_SECURITY_EVENT *EventInfo, NTSTATUS ResultStatus); + #ifdef __cplusplus } #endif diff --git a/dokan/dokan.vcxproj b/dokan/dokan.vcxproj index f05891e32..0fd5a7bfb 100644 --- a/dokan/dokan.vcxproj +++ b/dokan/dokan.vcxproj @@ -171,6 +171,7 @@ + @@ -191,6 +192,7 @@ + diff --git a/dokan/dokan_vector.c b/dokan/dokan_vector.c new file mode 100644 index 000000000..01d724a39 --- /dev/null +++ b/dokan/dokan_vector.c @@ -0,0 +1,306 @@ +#include "dokani.h" +#include "dokan_vector.h" + +#include + +#define DEFAULT_ITEM_COUNT 128 + +// Creates a new instance of DOKAN_VECTOR with default values. +DOKAN_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; + } + + DOKAN_VECTOR *vector = (DOKAN_VECTOR*)malloc(sizeof(DOKAN_VECTOR)); + + vector->Items = malloc(ItemSize * DEFAULT_ITEM_COUNT); + 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. +DOKAN_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; + } + + DOKAN_VECTOR *vector = (DOKAN_VECTOR*)malloc(sizeof(DOKAN_VECTOR)); + + if(MaxItems > 0) { + + vector->Items = malloc(ItemSize * MaxItems); + } + 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(DOKAN_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); + 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(DOKAN_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); + } + 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(DOKAN_VECTOR *Vector) { + + if(Vector) { + + if(Vector->Items) { + + free(Vector->Items); + } + + if(!Vector->IsStackAllocated) { + + free(Vector); + } + } +} + +// Appends an item to the vector. +BOOL DokanVector_PushBack(DOKAN_VECTOR *Vector, void *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(DOKAN_VECTOR *Vector, void *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(DOKAN_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(DOKAN_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(DOKAN_VECTOR *Vector) { + + assert(Vector); + + Vector->ItemCount = 0; +} + +// Retrieves the item at the specified index +void* DokanVector_GetItem(DOKAN_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. +void* DokanVector_GetLastItem(DOKAN_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(DOKAN_VECTOR *Vector, size_t MinimumIncrease) { + + if(MinimumIncrease == 0 && Vector->MaxItems == 0) { + + Vector->Items = malloc(Vector->ItemSize * DEFAULT_ITEM_COUNT); + 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; + } + + void *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(DOKAN_VECTOR *Vector) { + + assert(Vector); + + if(Vector) { + + return Vector->ItemCount; + } + + return 0; +} + +// Retrieves the current capacity of the vector. +size_t DokanVector_GetCapacity(DOKAN_VECTOR *Vector) { + + assert(Vector); + + if(Vector) { + + return Vector->MaxItems; + } + + return 0; +} + +// Retrieves the size of items within the vector. +size_t DokanVector_GetItemSize(DOKAN_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..ca25f3a5a --- /dev/null +++ b/dokan/dokan_vector.h @@ -0,0 +1,68 @@ +#ifndef DOKAN_VECTOR_H_ +#define DOKAN_VECTOR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _DOKAN_VECTOR { + void *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(DOKAN_VECTOR *Vector, size_t ItemSize); + +// Creates a new instance of DOKAN_VECTOR with default values on the stack. +BOOL DokanVector_StackAllocWithCapacity(DOKAN_VECTOR *Vector, size_t ItemSize, size_t MaxItems); + +// Releases the memory associated with a DOKAN_VECTOR; +void DokanVector_Free(DOKAN_VECTOR *Vector); + +// Appends an item to the vector. +BOOL DokanVector_PushBack(DOKAN_VECTOR *Vector, void *Item); + +// Appends an array of items to the vector. +BOOL DokanVector_PushBackArray(DOKAN_VECTOR *Vector, void *Items, size_t Count); + +// Removes an item from the end of the vector. +void DokanVector_PopBack(DOKAN_VECTOR *Vector); + +// Removes multiple items from the end of the vector. +void DokanVector_PopBackArray(DOKAN_VECTOR *Vector, size_t Count); + +// Clears all items in the vector. +void DokanVector_Clear(DOKAN_VECTOR *Vector); + +// Retrieves the item at the specified index +void* DokanVector_GetItem(DOKAN_VECTOR *Vector, size_t Index); + +// Retrieves the last item the vector. +void* DokanVector_GetLastItem(DOKAN_VECTOR *Vector); + +// Increases the internal capacity of the vector. +BOOL DokanVector_Grow(DOKAN_VECTOR *Vector, size_t MinimumIncrease); + +// Retrieves the number of items in the vector. +size_t DokanVector_GetCount(DOKAN_VECTOR *Vector); + +// Retrieves the current capacity of the vector. +size_t DokanVector_GetCapacity(DOKAN_VECTOR *Vector); + +// Retrieves the size of items within the vector. +size_t DokanVector_GetItemSize(DOKAN_VECTOR *Vector); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/dokan/dokanc.h b/dokan/dokanc.h index b04ae1241..bfced2c4b 100644 --- a/dokan/dokanc.h +++ b/dokan/dokanc.h @@ -55,52 +55,79 @@ extern BOOL g_UseStdErr; #ifdef _MSC_VER static VOID DokanDbgPrint(LPCSTR format, ...) { + const char *outputString; char *buffer; size_t length; va_list argp; va_start(argp, format); + length = _vscprintf(format, argp) + 1; + buffer = (char *)_malloca(length * sizeof(char)); + if (buffer) { + vsprintf_s(buffer, length, format, argp); outputString = buffer; - } else { + } + else { + outputString = format; } - if (g_UseStdErr) - fputs(outputString, stderr); - else - OutputDebugStringA(outputString); - if (buffer) - _freea(buffer); + + OutputDebugStringA(outputString); + + if(g_UseStdErr) { + + fputs(outputString, stderr); + fflush(stderr); + } + + if(buffer) { + _freea(buffer); + } + va_end(argp); - if (g_UseStdErr) - fflush(stderr); } static VOID DokanDbgPrintW(LPCWSTR format, ...) { + const WCHAR *outputString; WCHAR *buffer; size_t length; va_list argp; va_start(argp, format); + length = _vscwprintf(format, argp) + 1; + buffer = (WCHAR *)_malloca(length * sizeof(WCHAR)); + if (buffer) { + vswprintf_s(buffer, length, format, argp); outputString = buffer; - } else { + } + else { + outputString = format; } - if (g_UseStdErr) - fputws(outputString, stderr); - else - OutputDebugStringW(outputString); - if (buffer) - _freea(buffer); + + OutputDebugStringW(outputString); + + if(g_UseStdErr) { + + fputws(outputString, stderr); + fflush(stderr); + } + + if(buffer) { + + _freea(buffer); + } + va_end(argp); } diff --git a/dokan/dokani.h b/dokan/dokani.h index 214f8cb9e..07662fdeb 100644 --- a/dokan/dokani.h +++ b/dokan/dokani.h @@ -31,41 +31,144 @@ 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; + PTP_TIMER KeepAliveTimer; + PTP_IO IoCompletion; + TP_CALLBACK_ENVIRON CallbackEnvironment; +} DOKAN_INSTANCE_THREADINFO; + typedef struct _DOKAN_INSTANCE { // to ensure that unmount dispatch is called at once - CRITICAL_SECTION CriticalSection; + CRITICAL_SECTION CriticalSection; // store CurrentDeviceName // (when there are many mounts, each mount uses different DeviceName) - WCHAR DeviceName[64]; - WCHAR MountPoint[MAX_PATH]; - WCHAR UNCName[64]; + WCHAR DeviceName[64]; + WCHAR MountPoint[MAX_PATH]; + WCHAR UNCName[64]; + + ULONG DeviceNumber; + ULONG MountId; - ULONG DeviceNumber; - ULONG MountId; + PDOKAN_OPTIONS DokanOptions; + PDOKAN_OPERATIONS DokanOperations; - PDOKAN_OPTIONS DokanOptions; - PDOKAN_OPERATIONS DokanOperations; + LIST_ENTRY ListEntry; + + HANDLE GlobalDevice; + HANDLE Device; + HANDLE DeviceClosedWaitHandle; + DOKAN_INSTANCE_THREADINFO ThreadInfo; - LIST_ENTRY ListEntry; } DOKAN_INSTANCE, *PDOKAN_INSTANCE; typedef struct _DOKAN_OPEN_INFO { - BOOL IsDirectory; - ULONG OpenCount; - PEVENT_CONTEXT EventContext; - PDOKAN_INSTANCE DokanInstance; - ULONG64 UserContext; - ULONG EventId; - PLIST_ENTRY DirListHead; - PLIST_ENTRY StreamListHead; + CRITICAL_SECTION CriticalSection; + PDOKAN_INSTANCE DokanInstance; + DOKAN_VECTOR *DirList; + PWCHAR DirListSearchPattern; + volatile LONG64 UserContext; + ULONG EventId; + BOOL IsDirectory; } 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, + + // The overlapped operation contains both an input and an output because + // the input IO event wasn't big enough to handle the write operation + DOKAN_OVERLAPPED_TYPE_IOEVENT_WRITE_SIZE, + +} DOKAN_OVERLAPPED_TYPE; + +typedef enum _DOKAN_IO_EVENT_FLAGS { + + // There are no flags set + DOKAN_IO_EVENT_FLAGS_NONE = 0, + + // The DOKAN_IO_EVENT object associated with the IO event + // was allocated from a global pool and should be returned to + // that pool instead of free'd + DOKAN_IO_EVENT_FLAGS_POOLED = 1, + + // The EVENT_INFORMATION object associated with the IO event + // was allocated from a global pool and should be returned to + // that pool instead of free'd + DOKAN_IO_EVENT_FLAGS_POOLED_RESULT = (1 << 1), + +} DOKAN_IO_EVENT_FLAGS; + +// 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; + void *InputPayload; + void *OutputPayload; + DOKAN_OVERLAPPED_TYPE PayloadType; + DOKAN_IO_EVENT_FLAGS Flags; +} DOKAN_OVERLAPPED; + +typedef struct _DOKAN_IO_EVENT { + union { + DOKAN_CREATE_FILE_EVENT ZwCreateFile; + DOKAN_CLEANUP_EVENT Cleanup; + DOKAN_CLOSE_FILE_EVENT CloseFile; + DOKAN_READ_FILE_EVENT ReadFile; + DOKAN_WRITE_FILE_EVENT WriteFile; + DOKAN_FLUSH_BUFFERS_EVENT FlushBuffers; + DOKAN_GET_FILE_INFO_EVENT GetFileInfo; + DOKAN_FIND_FILES_EVENT FindFiles; + DOKAN_FIND_FILES_PATTERN_EVENT FindFilesWithPattern; + DOKAN_SET_FILE_BASIC_INFO_EVENT SetFileBasicInformation; + DOKAN_CAN_DELETE_FILE_EVENT CanDeleteFile; + DOKAN_MOVE_FILE_EVENT MoveFileW; + DOKAN_SET_EOF_EVENT SetEOF; + DOKAN_SET_ALLOCATION_SIZE_EVENT SetAllocationSize; + DOKAN_LOCK_FILE_EVENT LockFile; + DOKAN_UNLOCK_FILE_EVENT UnlockFile; + DOKAN_GET_DISK_FREE_SPACE_EVENT GetVolumeFreeSpace; + DOKAN_GET_VOLUME_INFO_EVENT GetVolumeInfo; + DOKAN_GET_VOLUME_ATTRIBUTES_EVENT GetVolumeAttributes; + DOKAN_GET_FILE_SECURITY_EVENT GetFileSecurityW; + DOKAN_SET_FILE_SECURITY_EVENT SetFileSecurityW; + DOKAN_FIND_STREAMS_EVENT FindStreams; + } EventInfo; + + // If the processing for the event requires extra data to be associated with it + // then a pointer to that data can be placed here + void *ProcessingContext; + + PDOKAN_INSTANCE DokanInstance; + DOKAN_OPEN_INFO *DokanOpenInfo; + PEVENT_INFORMATION EventResult; + ULONG EventResultSize; + ULONG EventSize; + DOKAN_IO_EVENT_FLAGS Flags; + DOKAN_FILE_INFO DokanFileInfo; + + union { + BYTE EventContextBuffer[EVENT_CONTEXT_MAX_SIZE]; + EVENT_CONTEXT EventContext; + } KernelInfo; +} DOKAN_IO_EVENT, *PDOKAN_IO_EVENT; + +#define IoEventResultBufferSize(ioEvent) ((ioEvent)->EventResultSize >= offsetof(EVENT_INFORMATION, Buffer) ? (ioEvent)->EventResultSize - offsetof(EVENT_INFORMATION, Buffer) : 0) +#define DOKAN_IO_EVENT_ALLOC_SIZE(bufSize) (offsetof(DOKAN_IO_EVENT, KernelInfo) + (bufSize)) + BOOL DokanStart(PDOKAN_INSTANCE Instance); BOOL SendToDevice(LPCWSTR DeviceName, DWORD IoControlCode, PVOID InputBuffer, @@ -78,59 +181,52 @@ GetRawDeviceName(LPCWSTR DeviceName, LPWSTR DestinationBuffer, void ALIGN_ALLOCATION_SIZE(PLARGE_INTEGER size, PDOKAN_OPTIONS DokanOptions); -UINT __stdcall DokanLoop(PVOID Param); +VOID CALLBACK DokanLoop( + _Inout_ PTP_CALLBACK_INSTANCE Instance, + _Inout_opt_ PVOID Context, + _Inout_opt_ PVOID Overlapped, + _In_ ULONG IoResult, + _In_ ULONG_PTR NumberOfBytesTransferred, + _Inout_ PTP_IO Io +); BOOL DokanMount(LPCWSTR MountPoint, LPCWSTR DeviceName, PDOKAN_OPTIONS DokanOptions); BOOL IsMountPointDriveLetter(LPCWSTR mountPoint); -VOID SendEventInformation(HANDLE Handle, PEVENT_INFORMATION EventInfo, - ULONG EventLength, PDOKAN_INSTANCE DokanInstance); +BOOL SendEventInformation(PEVENT_INFORMATION EventInfo, + PDOKAN_INSTANCE DokanInstance, DOKAN_IO_EVENT_FLAGS EventFlags); -PEVENT_INFORMATION -DispatchCommon(PEVENT_CONTEXT EventContext, ULONG SizeOfEventInfo, - PDOKAN_INSTANCE DokanInstance, PDOKAN_FILE_INFO DokanFileInfo, - PDOKAN_OPEN_INFO *DokanOpenInfo); +void CreateDispatchCommon(DOKAN_IO_EVENT *EventInfo, ULONG SizeOfEventInfo); -VOID DispatchDirectoryInformation(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance); +void FreeIoEventResult(PEVENT_INFORMATION EventResult, DOKAN_IO_EVENT_FLAGS Flags); -VOID DispatchQueryInformation(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance); +void BeginDispatchDirectoryInformation(DOKAN_IO_EVENT *EventInfo); -VOID DispatchQueryVolumeInformation(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance); +void BeginDispatchQueryInformation(DOKAN_IO_EVENT *EventInfo); -VOID DispatchSetInformation(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance); +void BeginDispatchQueryVolumeInformation(DOKAN_IO_EVENT *EventInfo); -VOID DispatchRead(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance); +void BeginDispatchSetInformation(DOKAN_IO_EVENT *EventInfo); -VOID DispatchWrite(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance); +void BeginDispatchRead(DOKAN_IO_EVENT *EventInfo); -VOID DispatchCreate(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance); +void BeginDispatchWrite(DOKAN_IO_EVENT *EventInfo); -VOID DispatchClose(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance); +void BeginDispatchCreate(DOKAN_IO_EVENT *EventInfo); -VOID DispatchCleanup(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance); +void DispatchClose(DOKAN_IO_EVENT *EventInfo); -VOID DispatchFlush(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance); +void DispatchCleanup(DOKAN_IO_EVENT *EventInfo); -VOID DispatchLock(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance); +void BeginDispatchFlush(DOKAN_IO_EVENT *EventInfo); -VOID DispatchQuerySecurity(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance); +void BeginDispatchLock(DOKAN_IO_EVENT *EventInfo); -VOID DispatchSetSecurity(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance); +void BeginDispatchQuerySecurity(DOKAN_IO_EVENT *EventInfo); + +void BeginDispatchSetSecurity(DOKAN_IO_EVENT *EventInfo); BOOLEAN InstallDriver(SC_HANDLE SchSCManager, LPCWSTR DriverName, LPCWSTR ServiceExe); @@ -157,13 +253,31 @@ VOID ClearFindData(PLIST_ENTRY ListHead); VOID ClearFindStreamData(PLIST_ENTRY ListHead); -UINT WINAPI DokanKeepAlive(PVOID Param); +void NTAPI DokanKeepAlive( + _Inout_ PTP_CALLBACK_INSTANCE Instance, + _Inout_opt_ PVOID Context, + _Inout_ PTP_TIMER Timer +); + +BOOL SendIoEventResult(DOKAN_IO_EVENT *EventInfo); + +DOKAN_IO_EVENT* PopIoEventBuffer(); +void PushIoEventBuffer(DOKAN_IO_EVENT *IOEvent); + +DOKAN_OVERLAPPED* PopOverlapped(); +void PushOverlapped(DOKAN_OVERLAPPED *overlapped); +void ResetOverlapped(DOKAN_OVERLAPPED *overlapped); + +EVENT_INFORMATION* PopEventResult(); +void PushEventResult(EVENT_INFORMATION *EventResult); + +DOKAN_OPEN_INFO* PopFileOpenInfo(); +void PushFileOpenInfo(DOKAN_OPEN_INFO *FileInfo); -PDOKAN_OPEN_INFO -GetDokanOpenInfo(PEVENT_CONTEXT EventInfomation, PDOKAN_INSTANCE DokanInstance); +DOKAN_VECTOR* PopDirectoryList(); +void PushDirectoryList(DOKAN_VECTOR *DirectoryList); -VOID ReleaseDokanOpenInfo(PEVENT_INFORMATION EventInfomation, - PDOKAN_INSTANCE DokanInstance); +void DokanNotifyUnmounted(DOKAN_INSTANCE *Instance); #ifdef __cplusplus } diff --git a/dokan/fileinfo.c b/dokan/fileinfo.c index 6970672e6..569752b0d 100644 --- a/dokan/fileinfo.c +++ b/dokan/fileinfo.c @@ -24,6 +24,10 @@ with this program. If not, see . #include #include #include +#include + +#define DOKAN_STREAM_BUFFER_FULL 1 +#define DOKAN_STREAM_BUFFER_CONTINUE 0 NTSTATUS DokanFillFileBasicInfo(PFILE_BASIC_INFORMATION BasicInfo, @@ -284,276 +288,270 @@ DokanFillIdInfo(PFILE_ID_INFORMATION IdInfo, return STATUS_SUCCESS; } -typedef struct _DOKAN_FIND_STREAM_DATA { - WIN32_FIND_STREAM_DATA FindStreamData; - 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); +int WINAPI DokanFillFindStreamData(PDOKAN_FIND_STREAMS_EVENT EventInfo, PWIN32_FIND_STREAM_DATA FindStreamData) { - findStreamData->FindStreamData = *FindStreamData; + DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; + ULONG offset = ioEvent->EventResult->BufferLength; + ULONG resultBufferSize = IoEventResultBufferSize(ioEvent); + + ULONG streamNameLength = + (ULONG)wcslen(FindStreamData->cStreamName) * sizeof(WCHAR); - InsertTailList(listHead, &findStreamData->ListEntry); - return 0; -} + ULONG entrySize = sizeof(FILE_STREAM_INFORMATION) + streamNameLength; -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); - } -} + if(offset + entrySize > resultBufferSize) { -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; - } + return DOKAN_STREAM_BUFFER_FULL; + } - if (openInfo->StreamListHead == NULL) { - openInfo->StreamListHead = malloc(sizeof(LIST_ENTRY)); - if (openInfo->StreamListHead != NULL) { - InitializeListHead(openInfo->StreamListHead); - } else { - status = STATUS_NO_MEMORY; - } - } + PFILE_STREAM_INFORMATION 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); - } + // Fill the new entry + streamInfo->StreamNameLength = streamNameLength; + + memcpy(streamInfo->StreamName, FindStreamData->cStreamName, streamNameLength); - 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); - } + streamInfo->StreamSize = FindStreamData->StreamSize; + streamInfo->StreamAllocationSize = FindStreamData->StreamSize; + streamInfo->NextEntryOffset = 0; + + ALIGN_ALLOCATION_SIZE(&streamInfo->StreamAllocationSize, ioEvent->DokanInstance->DokanOptions); + + // Must be align on a 8-byte boundary. + offset += QuadAlign(entrySize); + ioEvent->EventResult->BufferLength = offset; - return status; + return DOKAN_STREAM_BUFFER_CONTINUE; } -VOID DispatchQueryInformation(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance) { - PEVENT_INFORMATION eventInfo; - DOKAN_FILE_INFO fileInfo; - BY_HANDLE_FILE_INFORMATION byHandleFileInfo; - ULONG remainingLength; +void BeginDispatchQueryInformation(DOKAN_IO_EVENT *EventInfo) { + + DOKAN_GET_FILE_INFO_EVENT *getFileInfo = &EventInfo->EventInfo.GetFileInfo; + DOKAN_FIND_STREAMS_EVENT *findStreams = &EventInfo->EventInfo.FindStreams; NTSTATUS status = STATUS_INVALID_PARAMETER; - PDOKAN_OPEN_INFO openInfo; - ULONG sizeOfEventInfo; - sizeOfEventInfo = - sizeof(EVENT_INFORMATION) - 8 + EventContext->Operation.File.BufferLength; + DbgPrint("###GetFileInfo file handle = 0x%p, eventID = %04d\n", + EventInfo->DokanOpenInfo, + EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1); - CheckFileName(EventContext->Operation.File.FileName); + assert((void*)getFileInfo == (void*)EventInfo && (void*)findStreams == (void*)EventInfo); + assert(EventInfo->ProcessingContext == NULL); - ZeroMemory(&byHandleFileInfo, sizeof(BY_HANDLE_FILE_INFORMATION)); + CheckFileName(EventInfo->KernelInfo.EventContext.Operation.File.FileName); - eventInfo = DispatchCommon(EventContext, sizeOfEventInfo, DokanInstance, - &fileInfo, &openInfo); + CreateDispatchCommon(EventInfo, EventInfo->KernelInfo.EventContext.Operation.File.BufferLength); - eventInfo->BufferLength = EventContext->Operation.File.BufferLength; + if(EventInfo->KernelInfo.EventContext.Operation.File.FileInformationClass == FileStreamInformation) { - DbgPrint("###GetFileInfo %04d\n", openInfo != NULL ? openInfo->EventId : -1); + DbgPrint("FileStreamInformation\n"); - if (DokanInstance->DokanOperations->GetFileInformation) { - status = DokanInstance->DokanOperations->GetFileInformation( - EventContext->Operation.File.FileName, &byHandleFileInfo, &fileInfo); - } else { - status = STATUS_NOT_IMPLEMENTED; + if(EventInfo->DokanInstance->DokanOperations->FindStreams) { + + findStreams->DokanFileInfo = &EventInfo->DokanFileInfo; + findStreams->FileName = EventInfo->KernelInfo.EventContext.Operation.File.FileName; + findStreams->FillFindStreamData = DokanFillFindStreamData; + + status = EventInfo->DokanInstance->DokanOperations->FindStreams(findStreams); + } + else { + + status = STATUS_NOT_IMPLEMENTED; + } } + else if(EventInfo->DokanInstance->DokanOperations->GetFileInformation) { + + getFileInfo->DokanFileInfo = &EventInfo->DokanFileInfo; + getFileInfo->FileName = EventInfo->KernelInfo.EventContext.Operation.File.FileName; - remainingLength = eventInfo->BufferLength; - - DbgPrint("\tresult = %lx\n", status); - - if (status != STATUS_SUCCESS) { - eventInfo->Status = STATUS_INVALID_PARAMETER; - eventInfo->BufferLength = 0; - } else { - - switch (EventContext->Operation.File.FileInformationClass) { - case FileBasicInformation: - DbgPrint("\tFileBasicInformation\n"); - status = - DokanFillFileBasicInfo((PFILE_BASIC_INFORMATION)eventInfo->Buffer, - &byHandleFileInfo, &remainingLength); - break; - - case FileIdInformation: - DbgPrint("\tFileIdInformation\n"); - status = DokanFillIdInfo((PFILE_ID_INFORMATION)eventInfo->Buffer, - &byHandleFileInfo, &remainingLength); - break; - - case FileInternalInformation: - DbgPrint("\tFileInternalInformation\n"); - status = - DokanFillInternalInfo((PFILE_INTERNAL_INFORMATION)eventInfo->Buffer, - &byHandleFileInfo, &remainingLength); - break; - - case FileEaInformation: - DbgPrint("\tFileEaInformation\n"); - // status = STATUS_NOT_IMPLEMENTED; - status = STATUS_SUCCESS; - remainingLength -= sizeof(FILE_EA_INFORMATION); - break; - - case FileStandardInformation: - DbgPrint("\tFileStandardInformation\n"); - status = DokanFillFileStandardInfo( - (PFILE_STANDARD_INFORMATION)eventInfo->Buffer, &byHandleFileInfo, - &remainingLength, DokanInstance); - break; - - case FileAllInformation: - DbgPrint("\tFileAllInformation\n"); - status = DokanFillFileAllInfo((PFILE_ALL_INFORMATION)eventInfo->Buffer, - &byHandleFileInfo, &remainingLength, - EventContext, DokanInstance); - break; - - case FileAlternateNameInformation: - DbgPrint("\tFileAlternateNameInformation\n"); - status = STATUS_NOT_IMPLEMENTED; - break; - - case FileAttributeTagInformation: - DbgPrint("\tFileAttributeTagInformation\n"); - status = DokanFillFileAttributeTagInfo( - (PFILE_ATTRIBUTE_TAG_INFORMATION)eventInfo->Buffer, &byHandleFileInfo, - &remainingLength); - break; - - case FileCompressionInformation: - DbgPrint("\tFileCompressionInformation\n"); - status = STATUS_NOT_IMPLEMENTED; - break; - - case FileNormalizedNameInformation: - DbgPrint("\tFileNormalizedNameInformation\n"); - case FileNameInformation: - // this case is not used because driver deal with - DbgPrint("\tFileNameInformation\n"); - status = DokanFillFileNameInfo((PFILE_NAME_INFORMATION)eventInfo->Buffer, - &byHandleFileInfo, &remainingLength, - EventContext); - break; - - case FileNetworkOpenInformation: - DbgPrint("\tFileNetworkOpenInformation\n"); - status = DokanFillNetworkOpenInfo( - (PFILE_NETWORK_OPEN_INFORMATION)eventInfo->Buffer, &byHandleFileInfo, - &remainingLength, 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); - break; - case FileStreamInformation: - DbgPrint("FileStreamInformation\n"); - status = DokanFindStreams((PFILE_STREAM_INFORMATION)eventInfo->Buffer, - &fileInfo, EventContext, DokanInstance, - &remainingLength); - break; - case FileNetworkPhysicalNameInformation: - DbgPrint("FileNetworkPhysicalNameInformation\n"); - status = DokanFillNetworkPhysicalNameInfo( - (PFILE_NETWORK_PHYSICAL_NAME_INFORMATION)eventInfo->Buffer, - &byHandleFileInfo, &remainingLength, EventContext); - break; - default: { - status = STATUS_INVALID_PARAMETER; - DbgPrint(" unknown type:%d\n", - EventContext->Operation.File.FileInformationClass); - } break; - } - - eventInfo->Status = status; - eventInfo->BufferLength = - EventContext->Operation.File.BufferLength - remainingLength; + status = EventInfo->DokanInstance->DokanOperations->GetFileInformation(getFileInfo); } + else { - DbgPrint("\tDispatchQueryInformation result = %lx\n", status); + status = STATUS_NOT_IMPLEMENTED; + } + + if(status != STATUS_PENDING) { + + if(EventInfo->KernelInfo.EventContext.Operation.File.FileInformationClass == FileStreamInformation) { - // information for FileSystem - if (openInfo != NULL) - openInfo->UserContext = fileInfo.Context; + EndDispatchFindStreams(findStreams, status); + } + else { - SendEventInformation(Handle, eventInfo, sizeOfEventInfo, DokanInstance); - free(eventInfo); - return; + EndDispatchGetFileInformation(getFileInfo, status); + } + } +} + +void DOKANAPI EndDispatchGetFileInformation(DOKAN_GET_FILE_INFO_EVENT *EventInfo, NTSTATUS ResultStatus) { + + DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; + ULONG remainingLength = ioEvent->KernelInfo.EventContext.Operation.File.BufferLength; + + DbgPrint("\tresult = %lx\n", ResultStatus); + + // STATUS_PENDING should not be passed to this function + if(ResultStatus == STATUS_PENDING) { + + DbgPrint("Dokan Error: EndDispatchGetFileInformation() failed because STATUS_PENDING was supplied for ResultStatus.\n"); + ResultStatus = STATUS_INTERNAL_ERROR; + } + + assert(ioEvent->EventResult->BufferLength == 0); + + if(ResultStatus == STATUS_SUCCESS) { + + switch(ioEvent->KernelInfo.EventContext.Operation.File.FileInformationClass) { + case FileBasicInformation: + DbgPrint("\tFileBasicInformation\n"); + ResultStatus = + DokanFillFileBasicInfo((PFILE_BASIC_INFORMATION)ioEvent->EventResult->Buffer, + &EventInfo->FileHandleInfo, &remainingLength); + break; + + case FileIdInformation: + DbgPrint("\tFileIdInformation\n"); + ResultStatus = DokanFillIdInfo((PFILE_ID_INFORMATION)ioEvent->EventResult->Buffer, + &EventInfo->FileHandleInfo, &remainingLength); + break; + + case FileInternalInformation: + DbgPrint("\tFileInternalInformation\n"); + ResultStatus = + DokanFillInternalInfo((PFILE_INTERNAL_INFORMATION)ioEvent->EventResult->Buffer, + &EventInfo->FileHandleInfo, &remainingLength); + break; + + case FileEaInformation: + DbgPrint("\tFileEaInformation\n"); + // status = STATUS_NOT_IMPLEMENTED; + ResultStatus = STATUS_SUCCESS; + remainingLength -= sizeof(FILE_EA_INFORMATION); + break; + + case FileStandardInformation: + DbgPrint("\tFileStandardInformation\n"); + ResultStatus = DokanFillFileStandardInfo( + (PFILE_STANDARD_INFORMATION)ioEvent->EventResult->Buffer, &EventInfo->FileHandleInfo, + &remainingLength, ioEvent->DokanInstance); + break; + + case FileAllInformation: + DbgPrint("\tFileAllInformation\n"); + ResultStatus = DokanFillFileAllInfo((PFILE_ALL_INFORMATION)ioEvent->EventResult->Buffer, + &EventInfo->FileHandleInfo, &remainingLength, + &ioEvent->KernelInfo.EventContext, ioEvent->DokanInstance); + break; + + case FileAlternateNameInformation: + DbgPrint("\tFileAlternateNameInformation (not supported)\n"); + ResultStatus = STATUS_NOT_IMPLEMENTED; + break; + + case FileAttributeTagInformation: + DbgPrint("\tFileAttributeTagInformation\n"); + ResultStatus = DokanFillFileAttributeTagInfo( + (PFILE_ATTRIBUTE_TAG_INFORMATION)ioEvent->EventResult->Buffer, &EventInfo->FileHandleInfo, + &remainingLength); + break; + + case FileCompressionInformation: + DbgPrint("\tFileCompressionInformation (not supported)\n"); + + if(remainingLength < sizeof(FILE_COMPRESSION_INFORMATION)) { + + ResultStatus = STATUS_BUFFER_OVERFLOW; + } + else { + + // https://msdn.microsoft.com/en-us/library/cc232096.aspx?f=255&MSPPError=-2147217396 + + ResultStatus = STATUS_SUCCESS; + + // zero disables compression + RtlZeroMemory(ioEvent->EventResult->Buffer, sizeof(FILE_COMPRESSION_INFORMATION)); + + remainingLength -= sizeof(FILE_COMPRESSION_INFORMATION); + } + + break; + + case FileNormalizedNameInformation: + DbgPrint("\tFileNormalizedNameInformation\n"); + case FileNameInformation: + // this case is not used because driver deal with + DbgPrint("\tFileNameInformation\n"); + ResultStatus = DokanFillFileNameInfo((PFILE_NAME_INFORMATION)ioEvent->EventResult->Buffer, + &EventInfo->FileHandleInfo, &remainingLength, + &ioEvent->KernelInfo.EventContext); + break; + + case FileNetworkOpenInformation: + DbgPrint("\tFileNetworkOpenInformation\n"); + ResultStatus = DokanFillNetworkOpenInfo( + (PFILE_NETWORK_OPEN_INFORMATION)ioEvent->EventResult->Buffer, &EventInfo->FileHandleInfo, + &remainingLength, ioEvent->DokanInstance); + break; + + case FilePositionInformation: + // this case is not used because driver deal with + DbgPrint("\tFilePositionInformation\n"); + ResultStatus = DokanFillFilePositionInfo( + (PFILE_POSITION_INFORMATION)ioEvent->EventResult->Buffer, &EventInfo->FileHandleInfo, + &remainingLength); + break; + case FileStreamInformation: + DbgPrint("FileStreamInformation (internal error)\n"); + // shouldn't get here + ResultStatus = STATUS_INTERNAL_ERROR; + break; + case FileNetworkPhysicalNameInformation: + DbgPrint("FileNetworkPhysicalNameInformation\n"); + ResultStatus = DokanFillNetworkPhysicalNameInfo( + (PFILE_NETWORK_PHYSICAL_NAME_INFORMATION)ioEvent->EventResult->Buffer, + &EventInfo->FileHandleInfo, &remainingLength, &ioEvent->KernelInfo.EventContext); + break; + case FileStandardLinkInformation: + DbgPrint("FileStandardLinkInformation (not supported)\n"); + // https://msdn.microsoft.com/en-us/library/dd414603.aspx?f=255&MSPPError=-2147217396 + ResultStatus = STATUS_NOT_SUPPORTED; + break; + default: { + ResultStatus = STATUS_INVALID_PARAMETER; + DbgPrint(" unknown type:%d\n", + ioEvent->KernelInfo.EventContext.Operation.File.FileInformationClass); + } break; + } + + ioEvent->EventResult->BufferLength = + ioEvent->KernelInfo.EventContext.Operation.File.BufferLength - remainingLength; + } + + ioEvent->EventResult->Status = ResultStatus; + + DbgPrint("\tDispatchQueryInformation result = %lx\n", ResultStatus); + + SendIoEventResult(ioEvent); } + +void DOKANAPI EndDispatchFindStreams(DOKAN_FIND_STREAMS_EVENT *EventInfo, NTSTATUS ResultStatus) { + + DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; + + DbgPrint("\tresult = %lx\n", ResultStatus); + + // STATUS_PENDING should not be passed to this function + if(ResultStatus == STATUS_PENDING) { + + DbgPrint("Dokan Error: EndDispatchFindStreams() failed because STATUS_PENDING was supplied for ResultStatus.\n"); + ResultStatus = STATUS_INTERNAL_ERROR; + } + + assert(ioEvent->EventResult->BufferLength == 0); + + ioEvent->EventResult->Status = ResultStatus; + + DbgPrint("\tDispatchQueryInformation result = %lx\n", ResultStatus); + + SendIoEventResult(ioEvent); +} \ No newline at end of file diff --git a/dokan/fileinfo.h b/dokan/fileinfo.h index 0d7a72bcb..1a555bd67 100644 --- a/dokan/fileinfo.h +++ b/dokan/fileinfo.h @@ -165,7 +165,7 @@ typedef struct _FILE_ATTRIBUTE_TAG_INFORMATION { } FILE_ATTRIBUTE_TAG_INFORMATION, *PFILE_ATTRIBUTE_TAG_INFORMATION; typedef struct _FILE_DISPOSITION_INFORMATION { - BOOLEAN DeleteFile; + BOOLEAN QueryDeleteFile; } FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION; typedef struct _FILE_END_OF_FILE_INFORMATION { diff --git a/dokan/flush.c b/dokan/flush.c index 3a1e7bfa3..a39b9c8b2 100644 --- a/dokan/flush.c +++ b/dokan/flush.c @@ -21,43 +21,51 @@ with this program. If not, see . #include "dokani.h" #include "fileinfo.h" +#include -VOID DispatchFlush(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance) { - DOKAN_FILE_INFO fileInfo; - PEVENT_INFORMATION eventInfo; - ULONG sizeOfEventInfo = sizeof(EVENT_INFORMATION); - PDOKAN_OPEN_INFO openInfo; - NTSTATUS status; +void BeginDispatchFlush(DOKAN_IO_EVENT *EventInfo) { - CheckFileName(EventContext->Operation.Flush.FileName); + DOKAN_FLUSH_BUFFERS_EVENT *flushBuffers = &EventInfo->EventInfo.FlushBuffers; + NTSTATUS status = STATUS_NOT_IMPLEMENTED; - eventInfo = DispatchCommon(EventContext, sizeOfEventInfo, DokanInstance, - &fileInfo, &openInfo); + DbgPrint("###Flush file handle = 0x%p, eventID = %04d\n", + EventInfo->DokanOpenInfo, + EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1); - DbgPrint("###Flush %04d\n", openInfo != NULL ? openInfo->EventId : -1); + assert(EventInfo->DokanOpenInfo); + assert((void*)flushBuffers == (void*)EventInfo); + assert(EventInfo->ProcessingContext == NULL); - if (DokanInstance->DokanOperations->FlushFileBuffers) { + CheckFileName(EventInfo->KernelInfo.EventContext.Operation.Flush.FileName); - status = DokanInstance->DokanOperations->FlushFileBuffers( - EventContext->Operation.Flush.FileName, &fileInfo); + CreateDispatchCommon(EventInfo, 0); - } else { - status = STATUS_NOT_IMPLEMENTED; - } + if(EventInfo->DokanInstance->DokanOperations->FlushFileBuffers) { - if (status == STATUS_NOT_IMPLEMENTED) { - eventInfo->Status = STATUS_SUCCESS; - } else { - eventInfo->Status = - status != STATUS_SUCCESS ? STATUS_NOT_SUPPORTED : STATUS_SUCCESS; - } + flushBuffers->DokanFileInfo = &EventInfo->DokanFileInfo; + flushBuffers->FileName = EventInfo->KernelInfo.EventContext.Operation.Flush.FileName; - if (openInfo != NULL) - openInfo->UserContext = fileInfo.Context; + status = EventInfo->DokanInstance->DokanOperations->FlushFileBuffers(flushBuffers); + } - SendEventInformation(Handle, eventInfo, sizeOfEventInfo, DokanInstance); + if(status != STATUS_PENDING) { - free(eventInfo); - return; + EndDispatchFlush(flushBuffers, status); + } } + +void DOKANAPI EndDispatchFlush(DOKAN_FLUSH_BUFFERS_EVENT *EventInfo, NTSTATUS ResultStatus) { + + DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; + + // STATUS_PENDING should not be passed to this function + if(ResultStatus == STATUS_PENDING) { + + DbgPrint("Dokan Error: EndDispatchFlush() failed because STATUS_PENDING was supplied for ResultStatus.\n"); + ResultStatus = STATUS_INTERNAL_ERROR; + } + + ioEvent->EventResult->Status = ResultStatus; + + SendIoEventResult(ioEvent); +} \ No newline at end of file diff --git a/dokan/lock.c b/dokan/lock.c index 6694049a1..50e817528 100644 --- a/dokan/lock.c +++ b/dokan/lock.c @@ -21,70 +21,99 @@ with this program. If not, see . #include "dokani.h" #include "fileinfo.h" +#include -VOID DispatchLock(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance) { - DOKAN_FILE_INFO fileInfo; - PEVENT_INFORMATION eventInfo; - ULONG sizeOfEventInfo = sizeof(EVENT_INFORMATION); - PDOKAN_OPEN_INFO openInfo; - NTSTATUS status; +void EndDispatchLockCommon(DOKAN_IO_EVENT *EventInfo, NTSTATUS ResultStatus) { - CheckFileName(EventContext->Operation.Lock.FileName); + DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; + PEVENT_INFORMATION result = ioEvent->EventResult; - eventInfo = DispatchCommon(EventContext, sizeOfEventInfo, DokanInstance, - &fileInfo, &openInfo); + assert(result->BufferLength == 0); - DbgPrint("###Lock %04d\n", openInfo != NULL ? openInfo->EventId : -1); + // STATUS_PENDING should not be passed to this function + if(ResultStatus == STATUS_PENDING) { - eventInfo->Status = STATUS_NOT_IMPLEMENTED; + DbgPrint("Dokan Error: EndDispatchLockCommon() failed because STATUS_PENDING was supplied for ResultStatus.\n"); + ResultStatus = STATUS_INTERNAL_ERROR; + } + + result->Status = ResultStatus; + + SendIoEventResult(ioEvent); +} + +void BeginDispatchLock(DOKAN_IO_EVENT *EventInfo) { + + DOKAN_LOCK_FILE_EVENT *lockFileEvent = &EventInfo->EventInfo.LockFile; + DOKAN_UNLOCK_FILE_EVENT *unlockFileEvent = &EventInfo->EventInfo.UnlockFile; + NTSTATUS status = STATUS_NOT_IMPLEMENTED; + + DbgPrint("###Lock file handle = 0x%p, eventID = %04d\n", + EventInfo->DokanOpenInfo, + EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1); + + assert(EventInfo->DokanOpenInfo); + assert(EventInfo->ProcessingContext == NULL); + + CheckFileName(EventInfo->KernelInfo.EventContext.Operation.Lock.FileName); + + CreateDispatchCommon(EventInfo, 0); + + switch (EventInfo->KernelInfo.EventContext.MinorFunction) { - switch (EventContext->MinorFunction) { case IRP_MN_LOCK: - if (DokanInstance->DokanOperations->LockFile) { - - status = DokanInstance->DokanOperations->LockFile( - EventContext->Operation.Lock.FileName, - EventContext->Operation.Lock.ByteOffset.QuadPart, - EventContext->Operation.Lock.Length.QuadPart, - // EventContext->Operation.Lock.Key, - &fileInfo); - - if (status != STATUS_NOT_IMPLEMENTED) { - eventInfo->Status = - status != STATUS_SUCCESS ? STATUS_LOCK_NOT_GRANTED : STATUS_SUCCESS; - } + + assert((void*)lockFileEvent == (void*)EventInfo); + + if (EventInfo->DokanInstance->DokanOperations->LockFile) { + + lockFileEvent->DokanFileInfo = &EventInfo->DokanFileInfo; + lockFileEvent->FileName = EventInfo->KernelInfo.EventContext.Operation.Lock.FileName; + lockFileEvent->ByteOffset = EventInfo->KernelInfo.EventContext.Operation.Lock.ByteOffset.QuadPart; + lockFileEvent->Length = EventInfo->KernelInfo.EventContext.Operation.Lock.Length.QuadPart; + lockFileEvent->Key = EventInfo->KernelInfo.EventContext.Operation.Lock.Key; + + status = EventInfo->DokanInstance->DokanOperations->LockFile(lockFileEvent); } + break; case IRP_MN_UNLOCK_ALL: break; case IRP_MN_UNLOCK_ALL_BY_KEY: break; case IRP_MN_UNLOCK_SINGLE: - if (DokanInstance->DokanOperations->UnlockFile) { - - status = DokanInstance->DokanOperations->UnlockFile( - EventContext->Operation.Lock.FileName, - EventContext->Operation.Lock.ByteOffset.QuadPart, - EventContext->Operation.Lock.Length.QuadPart, - // EventContext->Operation.Lock.Key, - &fileInfo); - - if (status != STATUS_NOT_IMPLEMENTED) { - eventInfo->Status = - STATUS_SUCCESS; // always succeeds so it cannot fail ? - } + + if (EventInfo->DokanInstance->DokanOperations->UnlockFile) { + + assert((void*)unlockFileEvent == (void*)EventInfo); + + unlockFileEvent->DokanFileInfo = &EventInfo->DokanFileInfo; + unlockFileEvent->FileName = EventInfo->KernelInfo.EventContext.Operation.Lock.FileName; + unlockFileEvent->ByteOffset = EventInfo->KernelInfo.EventContext.Operation.Lock.ByteOffset.QuadPart; + unlockFileEvent->Length = EventInfo->KernelInfo.EventContext.Operation.Lock.Length.QuadPart; + unlockFileEvent->Key = EventInfo->KernelInfo.EventContext.Operation.Lock.Key; + + status = EventInfo->DokanInstance->DokanOperations->UnlockFile(unlockFileEvent); } + break; default: - DbgPrint("unkown lock function %d\n", EventContext->MinorFunction); + DbgPrint("unkown lock function %d\n", EventInfo->KernelInfo.EventContext.MinorFunction); + break; } - if (openInfo != NULL) - openInfo->UserContext = fileInfo.Context; + if(status != STATUS_PENDING) { - SendEventInformation(Handle, eventInfo, sizeOfEventInfo, DokanInstance); + EndDispatchLockCommon(EventInfo, status); + } +} + +void DOKANAPI EndDispatchLockFile(DOKAN_LOCK_FILE_EVENT *EventInfo, NTSTATUS ResultStatus) { - free(eventInfo); - return; + EndDispatchLockCommon((DOKAN_IO_EVENT*)EventInfo, ResultStatus); } + +void DOKANAPI EndDispatchUnlockFile(DOKAN_LOCK_FILE_EVENT *EventInfo, NTSTATUS ResultStatus) { + + EndDispatchLockCommon((DOKAN_IO_EVENT*)EventInfo, ResultStatus); +} \ No newline at end of file diff --git a/dokan/mount.c b/dokan/mount.c index 65843fed6..7d1fd4717 100644 --- a/dokan/mount.c +++ b/dokan/mount.c @@ -490,39 +490,78 @@ BOOL DokanMount(LPCWSTR MountPoint, LPCWSTR DeviceName, return TRUE; } +void GenerateUnmountPoint(LPCWSTR MountPoint, WCHAR *Result, size_t ResultMaxChars) { + + size_t length = wcslen(MountPoint); + + if(IsMountPointDriveLetter(MountPoint)) { + + wcscpy_s(Result, ResultMaxChars, L"C:"); + Result[0] = MountPoint[0]; + } + else { + + wcscpy_s(Result, ResultMaxChars, MountPoint); + + if(Result[length - 1] == L'\\') { + + Result[length - 1] = L'\0'; + } + } +} + BOOL DOKANAPI DokanRemoveMountPoint(LPCWSTR MountPoint) { - if (MountPoint != NULL) { - size_t length = wcslen(MountPoint); - if (length > 0) { - WCHAR mountPoint[MAX_PATH]; - if (IsMountPointDriveLetter(MountPoint)) { - wcscpy_s(mountPoint, sizeof(mountPoint) / sizeof(WCHAR), L"C:"); - mountPoint[0] = MountPoint[0]; - } else { - wcscpy_s(mountPoint, sizeof(mountPoint) / sizeof(WCHAR), MountPoint); - if (mountPoint[length - 1] == L'\\') { - mountPoint[length - 1] = L'\0'; - } - } + if(MountPoint != NULL) { + + size_t length = wcslen(MountPoint); + + if(length > 0) { - if (SendGlobalReleaseIRP(mountPoint)) { - if (!IsMountPointDriveLetter(MountPoint)) { - length = wcslen(mountPoint); - if (length+1 < MAX_PATH) { - mountPoint[length] = L'\\'; - mountPoint[length+1] = L'\0'; - // Required to remove reparse point (could also be done through - // FSCTL_DELETE_REPARSE_POINT with DeleteMountPoint function) - DeleteVolumeMountPoint(mountPoint); - } - } else { - // Notify applications / explorer - DokanBroadcastLink(MountPoint[0], TRUE); - return TRUE; - } - } - } - } - return FALSE; + WCHAR unmountPoint[MAX_PATH]; + + GenerateUnmountPoint(MountPoint, unmountPoint, ARRAYSIZE(unmountPoint)); + + return SendGlobalReleaseIRP(unmountPoint) ? TRUE : FALSE; + } + } + + return FALSE; } + +void DokanNotifyUnmounted(DOKAN_INSTANCE *Instance) { + + WCHAR unmountPoint[MAX_PATH]; + + GenerateUnmountPoint(Instance->MountPoint, unmountPoint, ARRAYSIZE(unmountPoint)); + + if(!IsMountPointDriveLetter(Instance->MountPoint)) { + + size_t length = wcslen(unmountPoint); + + if(length + 1 < MAX_PATH) { + + unmountPoint[length] = L'\\'; + unmountPoint[length + 1] = L'\0'; + + // Required to remove reparse point (could also be done through + // FSCTL_DELETE_REPARSE_POINT with DeleteMountPoint function) + DeleteVolumeMountPoint(unmountPoint); + } + } + else { + + // Notify applications / explorer + DokanBroadcastLink(Instance->MountPoint[0], TRUE); + } + + if(Instance->DokanOperations->Unmounted) { + + DOKAN_UNMOUNTED_INFO fileInfo; + RtlZeroMemory(&fileInfo, sizeof(DOKAN_UNMOUNTED_INFO)); + + fileInfo.DokanOptions = Instance->DokanOptions; + + Instance->DokanOperations->Unmounted(&fileInfo); + } +} \ No newline at end of file diff --git a/dokan/read.c b/dokan/read.c index 410cf1fea..903295a60 100644 --- a/dokan/read.c +++ b/dokan/read.c @@ -22,50 +22,70 @@ with this program. If not, see . #include "dokani.h" #include "fileinfo.h" -VOID DispatchRead(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance) { - PEVENT_INFORMATION eventInfo; - PDOKAN_OPEN_INFO openInfo; - ULONG readLength = 0; +#include + +void BeginDispatchRead(DOKAN_IO_EVENT *EventInfo) { + + PDOKAN_INSTANCE dokan = EventInfo->DokanInstance; + DOKAN_READ_FILE_EVENT *readFileEvent = &EventInfo->EventInfo.ReadFile; NTSTATUS status = STATUS_NOT_IMPLEMENTED; - DOKAN_FILE_INFO fileInfo; - ULONG sizeOfEventInfo; - sizeOfEventInfo = - sizeof(EVENT_INFORMATION) - 8 + EventContext->Operation.Read.BufferLength; + assert(EventInfo->DokanOpenInfo); + assert((void*)readFileEvent == (void*)EventInfo); + assert(EventInfo->ProcessingContext == NULL); - CheckFileName(EventContext->Operation.Read.FileName); + CheckFileName(EventInfo->KernelInfo.EventContext.Operation.Read.FileName); - eventInfo = DispatchCommon(EventContext, sizeOfEventInfo, DokanInstance, - &fileInfo, &openInfo); + DbgPrint("###Read file handle = 0x%p, eventID = %04d\n", + EventInfo->DokanOpenInfo, + EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1); - DbgPrint("###Read %04d\n", openInfo != NULL ? openInfo->EventId : -1); + CreateDispatchCommon(EventInfo, EventInfo->KernelInfo.EventContext.Operation.Read.BufferLength); - 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); - } else { - status = STATUS_NOT_IMPLEMENTED; + if (dokan->DokanOperations->ReadFile) { + + readFileEvent->DokanFileInfo = &EventInfo->DokanFileInfo; + readFileEvent->FileName = EventInfo->KernelInfo.EventContext.Operation.Read.FileName; + readFileEvent->Buffer = EventInfo->EventResult->Buffer; + readFileEvent->Offset = EventInfo->KernelInfo.EventContext.Operation.Read.ByteOffset.QuadPart; + + assert(readFileEvent->NumberOfBytesRead == 0); + + readFileEvent->NumberOfBytesToRead = EventInfo->KernelInfo.EventContext.Operation.Read.BufferLength; + + status = dokan->DokanOperations->ReadFile(readFileEvent); } + else { - if (openInfo != NULL) - openInfo->UserContext = fileInfo.Context; - eventInfo->BufferLength = 0; - eventInfo->Status = status; - - if (status == STATUS_SUCCESS) { - if (readLength == 0) { - eventInfo->Status = STATUS_END_OF_FILE; - } else { - eventInfo->BufferLength = readLength; - eventInfo->Operation.Read.CurrentByteOffset.QuadPart = - EventContext->Operation.Read.ByteOffset.QuadPart + readLength; - } + status = STATUS_NOT_IMPLEMENTED; } - SendEventInformation(Handle, eventInfo, sizeOfEventInfo, DokanInstance); - free(eventInfo); - return; + if(status != STATUS_PENDING) { + + EndDispatchRead(readFileEvent, status); + } } + +void DOKANAPI EndDispatchRead(DOKAN_READ_FILE_EVENT *EventInfo, NTSTATUS ResultStatus) { + + DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; + PEVENT_INFORMATION result = ioEvent->EventResult; + + // STATUS_PENDING should not be passed to this function + if(ResultStatus == STATUS_PENDING) { + + DbgPrint("Dokan Error: EndDispatchRead() failed because STATUS_PENDING was supplied for ResultStatus.\n"); + ResultStatus = STATUS_INTERNAL_ERROR; + } + + result->Status = ResultStatus; + result->BufferLength = EventInfo->NumberOfBytesRead; + result->Operation.Read.CurrentByteOffset.QuadPart = EventInfo->Offset + EventInfo->NumberOfBytesRead; + + if(ResultStatus == STATUS_SUCCESS && EventInfo->NumberOfBytesRead == 0) { + + result->Status = STATUS_END_OF_FILE; + } + + SendIoEventResult(ioEvent); +} \ No newline at end of file diff --git a/dokan/security.c b/dokan/security.c index 3ff81cf59..73933e599 100644 --- a/dokan/security.c +++ b/dokan/security.c @@ -21,88 +21,124 @@ with this program. If not, see . #include "dokani.h" #include "fileinfo.h" +#include -VOID DispatchQuerySecurity(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance) { - PEVENT_INFORMATION eventInfo; - DOKAN_FILE_INFO fileInfo; - PDOKAN_OPEN_INFO openInfo; - ULONG eventInfoLength; - NTSTATUS status = STATUS_NOT_IMPLEMENTED; - ULONG lengthNeeded = 0; - - eventInfoLength = sizeof(EVENT_INFORMATION) - 8 + - 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); - } - - eventInfo->Status = status; - - if (status != STATUS_SUCCESS && status != STATUS_BUFFER_OVERFLOW) { - eventInfo->BufferLength = 0; - } else { - eventInfo->BufferLength = lengthNeeded; - - if (EventContext->Operation.Security.BufferLength < lengthNeeded) { - // Filesystem Application should return STATUS_BUFFER_OVERFLOW in this - // case. - eventInfo->Status = STATUS_BUFFER_OVERFLOW; - } - } - - SendEventInformation(Handle, eventInfo, eventInfoLength, DokanInstance); - free(eventInfo); +void BeginDispatchQuerySecurity(DOKAN_IO_EVENT *EventInfo) { + + DOKAN_GET_FILE_SECURITY_EVENT *getFileSecurity = &EventInfo->EventInfo.GetFileSecurityW; + NTSTATUS status = STATUS_NOT_IMPLEMENTED; + + DbgPrint("###GetFileSecurity file handle = 0x%p, eventID = %04d\n", + EventInfo->DokanOpenInfo, + EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1); + + assert(EventInfo->DokanOpenInfo); + assert((void*)getFileSecurity == (void*)EventInfo); + assert(EventInfo->ProcessingContext == NULL); + + CheckFileName(EventInfo->KernelInfo.EventContext.Operation.Security.FileName); + + CreateDispatchCommon(EventInfo, EventInfo->KernelInfo.EventContext.Operation.Security.BufferLength); + + if(EventInfo->DokanInstance->DokanOperations->GetFileSecurityW) { + + getFileSecurity->DokanFileInfo = &EventInfo->DokanFileInfo; + getFileSecurity->FileName = EventInfo->KernelInfo.EventContext.Operation.Security.FileName; + getFileSecurity->SecurityInformation = &EventInfo->KernelInfo.EventContext.Operation.Security.SecurityInformation; + getFileSecurity->SecurityDescriptor = (PSECURITY_DESCRIPTOR)&EventInfo->EventResult->Buffer[0]; + getFileSecurity->SecurityDescriptorSize = IoEventResultBufferSize(EventInfo); + + assert(getFileSecurity->LengthNeeded == 0); + + status = EventInfo->DokanInstance->DokanOperations->GetFileSecurityW(getFileSecurity); + } + + if(status != STATUS_PENDING) { + + EndDispatchGetFileSecurity(getFileSecurity, status); + } +} + +void DOKANAPI EndDispatchGetFileSecurity(DOKAN_GET_FILE_SECURITY_EVENT *EventInfo, NTSTATUS ResultStatus) { + + DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; + PEVENT_INFORMATION result = ioEvent->EventResult; + + // STATUS_PENDING should not be passed to this function + if(ResultStatus == STATUS_PENDING) { + + DbgPrint("Dokan Error: EndDispatchGetFileSecurity() failed because STATUS_PENDING was supplied for ResultStatus.\n"); + ResultStatus = STATUS_INTERNAL_ERROR; + } + + assert(result->BufferLength == 0); + + if(ResultStatus == STATUS_SUCCESS) { + + if(EventInfo->LengthNeeded > IoEventResultBufferSize(ioEvent)) { + + ResultStatus = STATUS_BUFFER_OVERFLOW; + } + else { + + result->BufferLength = EventInfo->LengthNeeded; + } + } + + result->Status = ResultStatus; + + SendIoEventResult(ioEvent); } -VOID DispatchSetSecurity(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance) { - PEVENT_INFORMATION eventInfo; - DOKAN_FILE_INFO fileInfo; - PDOKAN_OPEN_INFO openInfo; - ULONG eventInfoLength; - NTSTATUS status = STATUS_NOT_IMPLEMENTED; - PSECURITY_DESCRIPTOR securityDescriptor; - - eventInfoLength = sizeof(EVENT_INFORMATION); - CheckFileName(EventContext->Operation.SetSecurity.FileName); - - eventInfo = DispatchCommon(EventContext, eventInfoLength, DokanInstance, - &fileInfo, &openInfo); - - DbgPrint("###SetSecurity %04d\n", openInfo != NULL ? openInfo->EventId : -1); - - 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); - } - - if (status != STATUS_SUCCESS) { - eventInfo->Status = STATUS_INVALID_PARAMETER; - eventInfo->BufferLength = 0; - } else { - eventInfo->Status = STATUS_SUCCESS; - eventInfo->BufferLength = 0; - } - - SendEventInformation(Handle, eventInfo, eventInfoLength, DokanInstance); - free(eventInfo); +void BeginDispatchSetSecurity(DOKAN_IO_EVENT *EventInfo) { + + DOKAN_SET_FILE_SECURITY_EVENT *setFileSecurity = &EventInfo->EventInfo.SetFileSecurityW; + NTSTATUS status = STATUS_NOT_IMPLEMENTED; + + DbgPrint("###SetSecurity file handle = 0x%p, eventID = %04d\n", + EventInfo->DokanOpenInfo, + EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1); + + assert(EventInfo->DokanOpenInfo); + assert((void*)setFileSecurity == (void*)EventInfo); + assert(EventInfo->ProcessingContext == NULL); + + CheckFileName(EventInfo->KernelInfo.EventContext.Operation.SetSecurity.FileName); + + CreateDispatchCommon(EventInfo, 0); + + if(EventInfo->DokanInstance->DokanOperations->SetFileSecurityW) { + + setFileSecurity->DokanFileInfo = &EventInfo->DokanFileInfo; + setFileSecurity->FileName = EventInfo->KernelInfo.EventContext.Operation.Security.FileName; + setFileSecurity->SecurityInformation = &EventInfo->KernelInfo.EventContext.Operation.SetSecurity.SecurityInformation; + setFileSecurity->SecurityDescriptor = (PCHAR)&EventInfo->KernelInfo.EventContext + EventInfo->KernelInfo.EventContext.Operation.SetSecurity.BufferOffset; + setFileSecurity->SecurityDescriptorSize = EventInfo->KernelInfo.EventContext.Operation.SetSecurity.BufferLength; + + status = EventInfo->DokanInstance->DokanOperations->SetFileSecurityW(setFileSecurity); + } + + if(status != STATUS_PENDING) { + + EndDispatchSetFileSecurity(setFileSecurity, status); + } } + +void DOKANAPI EndDispatchSetFileSecurity(DOKAN_SET_FILE_SECURITY_EVENT *EventInfo, NTSTATUS ResultStatus) { + + DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; + PEVENT_INFORMATION result = ioEvent->EventResult; + + // STATUS_PENDING should not be passed to this function + if(ResultStatus == STATUS_PENDING) { + + DbgPrint("Dokan Error: EndDispatchSetFileSecurity() failed because STATUS_PENDING was supplied for ResultStatus.\n"); + ResultStatus = STATUS_INTERNAL_ERROR; + } + + assert(result->BufferLength == 0); + + result->Status = ResultStatus; + + SendIoEventResult(ioEvent); +} \ No newline at end of file diff --git a/dokan/setfile.c b/dokan/setfile.c index 90ef464eb..e0f2946cf 100644 --- a/dokan/setfile.c +++ b/dokan/setfile.c @@ -23,281 +23,301 @@ with this program. If not, see . #include #include "dokani.h" #include "fileinfo.h" +#include -NTSTATUS -DokanSetAllocationInformation(PEVENT_CONTEXT EventContext, - PDOKAN_FILE_INFO FileInfo, - PDOKAN_OPERATIONS DokanOperations) { - PFILE_ALLOCATION_INFORMATION allocInfo = (PFILE_ALLOCATION_INFORMATION)( - (PCHAR)EventContext + EventContext->Operation.SetFile.BufferOffset); - - // A file's allocation size and end-of-file position are independent of each - // other, - // with the following exception: The end-of-file position must always be less - // than - // or equal to the allocation size. If the allocation size is set to a value - // that - // is less than the end-of-file position, the end-of-file position is - // automatically - // adjusted to match the allocation size. - NTSTATUS status; - - if (DokanOperations->SetAllocationSize) { - status = DokanOperations->SetAllocationSize( - EventContext->Operation.SetFile.FileName, - allocInfo->AllocationSize.QuadPart, FileInfo); - } else { - status = STATUS_NOT_IMPLEMENTED; - } +void EndGenericSetOperation(DOKAN_IO_EVENT *EventInfo, NTSTATUS ResultStatus) { + + // STATUS_PENDING should not be passed to this function + if(ResultStatus == STATUS_PENDING) { + + DbgPrint("Dokan Error: EndGenericSetOperation() failed because STATUS_PENDING was supplied for ResultStatus.\n"); + ResultStatus = STATUS_INTERNAL_ERROR; + } + + EventInfo->EventResult->Status = ResultStatus; + + DbgPrint("\tDispatchSetInformation result = %lx\n", ResultStatus); + + SendIoEventResult(EventInfo); +} + +void DokanSetAllocationInformation(DOKAN_IO_EVENT *EventInfo) { + + DOKAN_SET_ALLOCATION_SIZE_EVENT *allocEvent = &EventInfo->EventInfo.SetAllocationSize; + PFILE_ALLOCATION_INFORMATION allocInfo = (PFILE_ALLOCATION_INFORMATION)( + (PCHAR)&EventInfo->KernelInfo.EventContext + EventInfo->KernelInfo.EventContext.Operation.SetFile.BufferOffset); + + assert((void*)allocEvent == (void*)EventInfo); + + // A file's allocation size and end-of-file position are independent of each + // other, + // with the following exception: The end-of-file position must always be less + // than + // or equal to the allocation size. If the allocation size is set to a value + // that + // is less than the end-of-file position, the end-of-file position is + // automatically + // adjusted to match the allocation size. + + NTSTATUS status = STATUS_NOT_IMPLEMENTED; + + if(EventInfo->DokanInstance->DokanOperations->SetAllocationSize) { + + allocEvent->DokanFileInfo = &EventInfo->DokanFileInfo; + allocEvent->FileName = EventInfo->KernelInfo.EventContext.Operation.SetFile.FileName; + allocEvent->Length = allocInfo->AllocationSize.QuadPart; + + status = EventInfo->DokanInstance->DokanOperations->SetAllocationSize(allocEvent); + } + + if(status != STATUS_PENDING) { - return status; + EndDispatchSetAllocationSize(allocEvent, status); + } } -NTSTATUS -DokanSetBasicInformation(PEVENT_CONTEXT EventContext, PDOKAN_FILE_INFO FileInfo, - PDOKAN_OPERATIONS DokanOperations) { - FILETIME creation, lastAccess, lastWrite; - NTSTATUS status = STATUS_NOT_IMPLEMENTED; +void DOKANAPI EndDispatchSetAllocationSize(DOKAN_SET_ALLOCATION_SIZE_EVENT *EventInfo, NTSTATUS ResultStatus) { - PFILE_BASIC_INFORMATION basicInfo = (PFILE_BASIC_INFORMATION)( - (PCHAR)EventContext + EventContext->Operation.SetFile.BufferOffset); + EndGenericSetOperation((DOKAN_IO_EVENT*)EventInfo, ResultStatus); +} + +void DokanSetBasicInformation(DOKAN_IO_EVENT *EventInfo) { + + DOKAN_SET_FILE_BASIC_INFO_EVENT *setBasicInfo = &EventInfo->EventInfo.SetFileBasicInformation; + NTSTATUS status = STATUS_NOT_IMPLEMENTED; + + assert((void*)setBasicInfo == (void*)EventInfo); - if (!DokanOperations->SetFileAttributes) - return STATUS_NOT_IMPLEMENTED; + if(EventInfo->DokanInstance->DokanOperations->SetFileBasicInformation) { - if (!DokanOperations->SetFileTime) - return STATUS_NOT_IMPLEMENTED; + setBasicInfo->DokanFileInfo = &EventInfo->DokanFileInfo; + setBasicInfo->FileName = EventInfo->KernelInfo.EventContext.Operation.SetFile.FileName; + setBasicInfo->Info = (PFILE_BASIC_INFORMATION)( + (PCHAR)&EventInfo->KernelInfo.EventContext + EventInfo->KernelInfo.EventContext.Operation.SetFile.BufferOffset); - status = DokanOperations->SetFileAttributes( - EventContext->Operation.SetFile.FileName, basicInfo->FileAttributes, - FileInfo); + status = EventInfo->DokanInstance->DokanOperations->SetFileBasicInformation(setBasicInfo); + } - if (status != STATUS_SUCCESS) - return status; + if(status != STATUS_PENDING) { - creation.dwLowDateTime = basicInfo->CreationTime.LowPart; - creation.dwHighDateTime = basicInfo->CreationTime.HighPart; - lastAccess.dwLowDateTime = basicInfo->LastAccessTime.LowPart; - lastAccess.dwHighDateTime = basicInfo->LastAccessTime.HighPart; - lastWrite.dwLowDateTime = basicInfo->LastWriteTime.LowPart; - lastWrite.dwHighDateTime = basicInfo->LastWriteTime.HighPart; + EndDispatchSetFileBasicInformation(setBasicInfo, status); + } +} + +void DOKANAPI EndDispatchSetFileBasicInformation(DOKAN_SET_FILE_BASIC_INFO_EVENT *EventInfo, NTSTATUS ResultStatus) { - return DokanOperations->SetFileTime(EventContext->Operation.SetFile.FileName, - &creation, &lastAccess, &lastWrite, - FileInfo); + EndGenericSetOperation((DOKAN_IO_EVENT*)EventInfo, ResultStatus); } -NTSTATUS -DokanSetDispositionInformation(PEVENT_CONTEXT EventContext, - PDOKAN_FILE_INFO FileInfo, - PDOKAN_OPERATIONS DokanOperations) { - PFILE_DISPOSITION_INFORMATION dispositionInfo = +void DokanSetDispositionInformation(DOKAN_IO_EVENT *EventInfo) { + + DOKAN_CAN_DELETE_FILE_EVENT *canDeleteFile = &EventInfo->EventInfo.CanDeleteFile; + PFILE_DISPOSITION_INFORMATION dispositionInfo = (PFILE_DISPOSITION_INFORMATION)( - (PCHAR)EventContext + EventContext->Operation.SetFile.BufferOffset); + (PCHAR)&EventInfo->KernelInfo.EventContext + EventInfo->KernelInfo.EventContext.Operation.SetFile.BufferOffset); - if (!DokanOperations->DeleteFile || !DokanOperations->DeleteDirectory) - return STATUS_NOT_IMPLEMENTED; + NTSTATUS status = STATUS_NOT_IMPLEMENTED; - if (!dispositionInfo->DeleteFile) { - return STATUS_SUCCESS; - } + assert((void*)canDeleteFile == (void*)EventInfo); - if (DokanOperations->GetFileInformation) { - BY_HANDLE_FILE_INFORMATION byHandleFileInfo; - ZeroMemory(&byHandleFileInfo, sizeof(BY_HANDLE_FILE_INFORMATION)); - NTSTATUS result = DokanOperations->GetFileInformation( - EventContext->Operation.SetFile.FileName, &byHandleFileInfo, FileInfo); + if(!dispositionInfo->QueryDeleteFile) { + + EndDispatchCanDeleteFile(canDeleteFile, STATUS_SUCCESS); + return; + } - if (result == STATUS_SUCCESS && - (byHandleFileInfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0) - return STATUS_CANNOT_DELETE; - } + if(EventInfo->DokanInstance->DokanOperations->CanDeleteFile) { - if (FileInfo->IsDirectory) { - return DokanOperations->DeleteDirectory( - EventContext->Operation.SetFile.FileName, FileInfo); - } else { - return DokanOperations->DeleteFile(EventContext->Operation.SetFile.FileName, - FileInfo); - } + canDeleteFile->DokanFileInfo = &EventInfo->DokanFileInfo; + canDeleteFile->FileName = EventInfo->KernelInfo.EventContext.Operation.SetFile.FileName; + + status = EventInfo->DokanInstance->DokanOperations->CanDeleteFile(canDeleteFile); + } + + if(status != STATUS_PENDING) { + + EndDispatchCanDeleteFile(canDeleteFile, status); + } +} + +void DOKANAPI EndDispatchCanDeleteFile(DOKAN_CAN_DELETE_FILE_EVENT *EventInfo, NTSTATUS ResultStatus) { + + DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; + PFILE_DISPOSITION_INFORMATION dispositionInfo = + (PFILE_DISPOSITION_INFORMATION)( + (PCHAR)&ioEvent->KernelInfo.EventContext + ioEvent->KernelInfo.EventContext.Operation.SetFile.BufferOffset); + + if(ResultStatus == STATUS_SUCCESS) { + + ioEvent->EventResult->Operation.Delete.DeleteOnClose = dispositionInfo->QueryDeleteFile ? TRUE : FALSE; + } + + EndGenericSetOperation((DOKAN_IO_EVENT*)EventInfo, ResultStatus); } -NTSTATUS -DokanSetEndOfFileInformation(PEVENT_CONTEXT EventContext, - PDOKAN_FILE_INFO FileInfo, - PDOKAN_OPERATIONS DokanOperations) { - PFILE_END_OF_FILE_INFORMATION endInfo = (PFILE_END_OF_FILE_INFORMATION)( - (PCHAR)EventContext + EventContext->Operation.SetFile.BufferOffset); +void DokanSetEndOfFileInformation(DOKAN_IO_EVENT *EventInfo) { + + DOKAN_SET_EOF_EVENT *setEOF = &EventInfo->EventInfo.SetEOF; + PFILE_END_OF_FILE_INFORMATION endInfo = (PFILE_END_OF_FILE_INFORMATION)( + (PCHAR)&EventInfo->KernelInfo.EventContext + EventInfo->KernelInfo.EventContext.Operation.SetFile.BufferOffset); + + NTSTATUS status = STATUS_NOT_IMPLEMENTED; + + assert((void*)setEOF == (void*)EventInfo); + + if(EventInfo->DokanInstance->DokanOperations->SetEndOfFile) { + + setEOF->DokanFileInfo = &EventInfo->DokanFileInfo; + setEOF->FileName = EventInfo->KernelInfo.EventContext.Operation.SetFile.FileName; + setEOF->Length = endInfo->EndOfFile.QuadPart; - if (!DokanOperations->SetEndOfFile) - return STATUS_NOT_IMPLEMENTED; + status = EventInfo->DokanInstance->DokanOperations->SetEndOfFile(setEOF); + } - return DokanOperations->SetEndOfFile(EventContext->Operation.SetFile.FileName, - endInfo->EndOfFile.QuadPart, FileInfo); + if(status != STATUS_PENDING) { + + EndDispatchSetEndOfFile(setEOF, status); + } } -NTSTATUS -DokanSetLinkInformation(PEVENT_CONTEXT EventContext, PDOKAN_FILE_INFO FileInfo, - PDOKAN_OPERATIONS DokanOperations) { - UNREFERENCED_PARAMETER(EventContext); - UNREFERENCED_PARAMETER(FileInfo); - UNREFERENCED_PARAMETER(DokanOperations); - // PDOKAN_LINK_INFORMATION linkInfo = - // (PDOKAN_LINK_INFORMATION)((PCHAR)EventContext + - // EventContext->Operation.SetFile.BufferOffset); - - return STATUS_NOT_IMPLEMENTED; +void DOKANAPI EndDispatchSetEndOfFile(DOKAN_SET_EOF_EVENT *EventInfo, NTSTATUS ResultStatus) { + + EndGenericSetOperation((DOKAN_IO_EVENT*)EventInfo, ResultStatus); } -NTSTATUS -DokanSetRenameInformation(PEVENT_CONTEXT EventContext, - PDOKAN_FILE_INFO FileInfo, - PDOKAN_OPERATIONS DokanOperations) { - PDOKAN_RENAME_INFORMATION renameInfo = (PDOKAN_RENAME_INFORMATION)( - (PCHAR)EventContext + EventContext->Operation.SetFile.BufferOffset); - - WCHAR newName[MAX_PATH]; - ZeroMemory(newName, sizeof(newName)); - - if (renameInfo->FileName[0] != L'\\') { - ULONG pos; - for (pos = EventContext->Operation.SetFile.FileNameLength / sizeof(WCHAR); - pos != 0; --pos) { - if (EventContext->Operation.SetFile.FileName[pos] == '\\') - break; - } - RtlCopyMemory(newName, EventContext->Operation.SetFile.FileName, - (pos + 1) * sizeof(WCHAR)); - RtlCopyMemory((PCHAR)newName + (pos + 1) * sizeof(WCHAR), - renameInfo->FileName, renameInfo->FileNameLength); - } else { - RtlCopyMemory(newName, renameInfo->FileName, renameInfo->FileNameLength); - } +void DokanSetRenameInformation(DOKAN_IO_EVENT *EventInfo) { + + DOKAN_MOVE_FILE_EVENT *moveFile = &EventInfo->EventInfo.MoveFileW; + PDOKAN_RENAME_INFORMATION renameInfo = (PDOKAN_RENAME_INFORMATION)( + (PCHAR)&EventInfo->KernelInfo.EventContext + EventInfo->KernelInfo.EventContext.Operation.SetFile.BufferOffset); + + NTSTATUS status = STATUS_NOT_IMPLEMENTED; + + assert((void*)moveFile == (void*)EventInfo); + + if(EventInfo->DokanInstance->DokanOperations->MoveFileW) { + + moveFile->DokanFileInfo = &EventInfo->DokanFileInfo; + moveFile->FileName = EventInfo->KernelInfo.EventContext.Operation.SetFile.FileName; + moveFile->NewFileName = renameInfo->FileName; + moveFile->ReplaceIfExists = renameInfo->ReplaceIfExists; + + status = EventInfo->DokanInstance->DokanOperations->MoveFileW(moveFile); + } + + if(status != STATUS_PENDING) { + + EndDispatchMoveFile(moveFile, status); + } +} + +void DOKANAPI EndDispatchMoveFile(DOKAN_MOVE_FILE_EVENT *EventInfo, NTSTATUS ResultStatus) { + + DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; + PDOKAN_RENAME_INFORMATION renameInfo = (PDOKAN_RENAME_INFORMATION)( + (PCHAR)&ioEvent->KernelInfo.EventContext + ioEvent->KernelInfo.EventContext.Operation.SetFile.BufferOffset); - if (!DokanOperations->MoveFile) - return STATUS_NOT_IMPLEMENTED; + if(ResultStatus == STATUS_SUCCESS) { - return DokanOperations->MoveFile(EventContext->Operation.SetFile.FileName, - newName, renameInfo->ReplaceIfExists, - FileInfo); + ioEvent->EventResult->BufferLength = renameInfo->FileNameLength; + + CopyMemory(ioEvent->EventResult->Buffer, renameInfo->FileName, renameInfo->FileNameLength); + } + + EndGenericSetOperation(ioEvent, ResultStatus); } -NTSTATUS -DokanSetValidDataLengthInformation(PEVENT_CONTEXT EventContext, - PDOKAN_FILE_INFO FileInfo, - PDOKAN_OPERATIONS DokanOperations) { - PFILE_VALID_DATA_LENGTH_INFORMATION validInfo = - (PFILE_VALID_DATA_LENGTH_INFORMATION)( - (PCHAR)EventContext + EventContext->Operation.SetFile.BufferOffset); +void DokanSetValidDataLengthInformation(DOKAN_IO_EVENT *EventInfo) { + + DOKAN_SET_EOF_EVENT *setEOF = &EventInfo->EventInfo.SetEOF; + PFILE_VALID_DATA_LENGTH_INFORMATION validInfo = + (PFILE_VALID_DATA_LENGTH_INFORMATION)( + (PCHAR)&EventInfo->KernelInfo.EventContext + EventInfo->KernelInfo.EventContext.Operation.SetFile.BufferOffset); + + NTSTATUS status = STATUS_NOT_IMPLEMENTED; + + assert((void*)setEOF == (void*)EventInfo); + + if(EventInfo->DokanInstance->DokanOperations->SetEndOfFile) { + + setEOF->DokanFileInfo = &EventInfo->DokanFileInfo; + setEOF->FileName = EventInfo->KernelInfo.EventContext.Operation.SetFile.FileName; + setEOF->Length = validInfo->ValidDataLength.QuadPart; + + status = EventInfo->DokanInstance->DokanOperations->SetEndOfFile(setEOF); + } - if (!DokanOperations->SetEndOfFile) - return STATUS_NOT_IMPLEMENTED; + if(status != STATUS_PENDING) { - return DokanOperations->SetEndOfFile(EventContext->Operation.SetFile.FileName, - validInfo->ValidDataLength.QuadPart, - FileInfo); + EndDispatchSetEndOfFile(setEOF, status); + } } -VOID DispatchSetInformation(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance) { - PEVENT_INFORMATION eventInfo; - PDOKAN_OPEN_INFO openInfo; - DOKAN_FILE_INFO fileInfo; - NTSTATUS status = STATUS_NOT_IMPLEMENTED; - ULONG sizeOfEventInfo = sizeof(EVENT_INFORMATION); +void BeginDispatchSetInformation(DOKAN_IO_EVENT *EventInfo) { + + ULONG sizeOfEventInfo = 0; + + DbgPrint("###SetFileInfo file handle = 0x%p, eventID = %04d, FileInformationClass = %d\n", + EventInfo->DokanOpenInfo, + EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1, + EventInfo->KernelInfo.EventContext.Operation.SetFile.FileInformationClass); + + assert(EventInfo->ProcessingContext == NULL); + assert(EventInfo->DokanOpenInfo); + + if (EventInfo->KernelInfo.EventContext.Operation.SetFile.FileInformationClass == FileRenameInformation) { - if (EventContext->Operation.SetFile.FileInformationClass == - FileRenameInformation) { PDOKAN_RENAME_INFORMATION renameInfo = (PDOKAN_RENAME_INFORMATION)( - (PCHAR)EventContext + EventContext->Operation.SetFile.BufferOffset); + (PCHAR)&EventInfo->KernelInfo.EventContext + EventInfo->KernelInfo.EventContext.Operation.SetFile.BufferOffset); + sizeOfEventInfo += renameInfo->FileNameLength; } - CheckFileName(EventContext->Operation.SetFile.FileName); + CheckFileName(EventInfo->KernelInfo.EventContext.Operation.SetFile.FileName); - eventInfo = DispatchCommon(EventContext, sizeOfEventInfo, DokanInstance, - &fileInfo, &openInfo); + CreateDispatchCommon(EventInfo, sizeOfEventInfo); - DbgPrint("###SetFileInfo %04d %d\n", - openInfo != NULL ? openInfo->EventId : -1, - EventContext->Operation.SetFile.FileInformationClass); + switch (EventInfo->KernelInfo.EventContext.Operation.SetFile.FileInformationClass) { - switch (EventContext->Operation.SetFile.FileInformationClass) { case FileAllocationInformation: - status = DokanSetAllocationInformation(EventContext, &fileInfo, - DokanInstance->DokanOperations); + DokanSetAllocationInformation(EventInfo); break; case FileBasicInformation: - status = DokanSetBasicInformation(EventContext, &fileInfo, - DokanInstance->DokanOperations); + DokanSetBasicInformation(EventInfo); break; case FileDispositionInformation: - status = DokanSetDispositionInformation(EventContext, &fileInfo, - DokanInstance->DokanOperations); + DokanSetDispositionInformation(EventInfo); break; case FileEndOfFileInformation: - status = DokanSetEndOfFileInformation(EventContext, &fileInfo, - DokanInstance->DokanOperations); + DokanSetEndOfFileInformation(EventInfo); break; case FileLinkInformation: - status = DokanSetLinkInformation(EventContext, &fileInfo, - DokanInstance->DokanOperations); + EndGenericSetOperation(EventInfo, STATUS_NOT_IMPLEMENTED); break; case FilePositionInformation: // this case is dealed with by driver - status = STATUS_NOT_IMPLEMENTED; + EndGenericSetOperation(EventInfo, STATUS_NOT_IMPLEMENTED); break; case FileRenameInformation: - status = DokanSetRenameInformation(EventContext, &fileInfo, - DokanInstance->DokanOperations); + DokanSetRenameInformation(EventInfo); break; case FileValidDataLengthInformation: - status = DokanSetValidDataLengthInformation(EventContext, &fileInfo, - DokanInstance->DokanOperations); + DokanSetValidDataLengthInformation(EventInfo); break; - } - if (openInfo != NULL) - openInfo->UserContext = fileInfo.Context; - eventInfo->BufferLength = 0; - eventInfo->Status = status; - - if (EventContext->Operation.SetFile.FileInformationClass == - FileDispositionInformation) { - if (status == STATUS_SUCCESS) { - PFILE_DISPOSITION_INFORMATION dispositionInfo = - (PFILE_DISPOSITION_INFORMATION)( - (PCHAR)EventContext + - EventContext->Operation.SetFile.BufferOffset); - eventInfo->Operation.Delete.DeleteOnClose = - dispositionInfo->DeleteFile ? TRUE : FALSE; - DbgPrint(" dispositionInfo->DeleteFile = %d\n", - dispositionInfo->DeleteFile); - } - - } else { - // notice new file name to driver - if (status == STATUS_SUCCESS && - EventContext->Operation.SetFile.FileInformationClass == - FileRenameInformation) { - PDOKAN_RENAME_INFORMATION renameInfo = (PDOKAN_RENAME_INFORMATION)( - (PCHAR)EventContext + EventContext->Operation.SetFile.BufferOffset); - eventInfo->BufferLength = renameInfo->FileNameLength; - CopyMemory(eventInfo->Buffer, renameInfo->FileName, - renameInfo->FileNameLength); - } - } + default: + DbgPrint("Dokan Warning: Unrecognized EventInfo->KernelInfo.EventContext.Operation.SetFile.FileInformationClass: 0x%x\n", + EventInfo->KernelInfo.EventContext.Operation.SetFile.FileInformationClass); - DbgPrint("\tDispatchSetInformation result = %lx\n", status); + EndGenericSetOperation(EventInfo, STATUS_NOT_IMPLEMENTED); - SendEventInformation(Handle, eventInfo, sizeOfEventInfo, DokanInstance); - free(eventInfo); - return; + break; + } } diff --git a/dokan/timeout.c b/dokan/timeout.c index 509794abb..7a59a123f 100644 --- a/dokan/timeout.c +++ b/dokan/timeout.c @@ -25,61 +25,47 @@ with this program. If not, see . BOOL DOKANAPI DokanResetTimeout(ULONG Timeout, PDOKAN_FILE_INFO FileInfo) { BOOL status; ULONG returnedLength; - PDOKAN_INSTANCE instance; - PDOKAN_OPEN_INFO openInfo; - PEVENT_CONTEXT eventContext; + DOKAN_IO_EVENT *ioEvent; PEVENT_INFORMATION eventInfo; ULONG eventInfoSize = sizeof(EVENT_INFORMATION); WCHAR rawDeviceName[MAX_PATH]; - openInfo = (PDOKAN_OPEN_INFO)(UINT_PTR)FileInfo->DokanContext; + ioEvent = (DOKAN_IO_EVENT*)FileInfo->DokanContext; - if (openInfo == NULL) { - return FALSE; - } - - eventContext = openInfo->EventContext; - if (eventContext == NULL) { - return FALSE; - } - - instance = openInfo->DokanInstance; - if (instance == NULL) { + if (ioEvent == NULL) { return FALSE; } eventInfo = (PEVENT_INFORMATION)malloc(eventInfoSize); + if (eventInfo == NULL) { return FALSE; } RtlZeroMemory(eventInfo, eventInfoSize); - eventInfo->SerialNumber = eventContext->SerialNumber; + eventInfo->SerialNumber = ioEvent->KernelInfo.EventContext.SerialNumber; eventInfo->Operation.ResetTimeout.Timeout = Timeout; status = SendToDevice( - GetRawDeviceName(instance->DeviceName, rawDeviceName, MAX_PATH), + GetRawDeviceName(ioEvent->DokanInstance->DeviceName, rawDeviceName, MAX_PATH), IOCTL_RESET_TIMEOUT, eventInfo, eventInfoSize, NULL, 0, &returnedLength); + free(eventInfo); + return status; } -UINT WINAPI DokanKeepAlive(PDOKAN_INSTANCE DokanInstance) { - HANDLE device; - ULONG ReturnedLength; - WCHAR rawDeviceName[MAX_PATH]; +void NTAPI DokanKeepAlive( + _Inout_ PTP_CALLBACK_INSTANCE Instance, + _Inout_opt_ PVOID Context, + _Inout_ PTP_TIMER Timer + ) { + + UNREFERENCED_PARAMETER(Instance); - device = CreateFile( - GetRawDeviceName(DokanInstance->DeviceName, rawDeviceName, MAX_PATH), - GENERIC_READ | GENERIC_WRITE, // dwDesiredAccess - FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode - NULL, // lpSecurityAttributes - OPEN_EXISTING, // dwCreationDistribution - 0, // dwFlagsAndAttributes - NULL // hTemplateFile - ); + HANDLE device = (HANDLE)Context; + ULONG ReturnedLength; - while (device != INVALID_HANDLE_VALUE) { BOOL status = DeviceIoControl(device, // Handle to device IOCTL_KEEPALIVE, // IO Control code @@ -90,14 +76,14 @@ UINT WINAPI DokanKeepAlive(PDOKAN_INSTANCE DokanInstance) { &ReturnedLength, // Bytes placed in buffer. NULL // synchronous call ); - if (!status) { - break; - } - Sleep(DOKAN_KEEPALIVE_TIME); - } - CloseHandle(device); + if(!status) { + + // disable timer + SetThreadpoolTimer(Timer, NULL, 0, 0); + + DWORD errCode = GetLastError(); - _endthreadex(0); - return STATUS_SUCCESS; + DbgPrint("Dokan Error: Dokan device ioctl failed for keepalive with code %d. Disabling keepalive timer.\n", errCode); + } } diff --git a/dokan/volume.c b/dokan/volume.c index 2fd117a1f..3d63f6242 100644 --- a/dokan/volume.c +++ b/dokan/volume.c @@ -21,349 +21,326 @@ with this program. If not, see . #include "dokani.h" #include "fileinfo.h" +#include -NTSTATUS DOKAN_CALLBACK DokanGetDiskFreeSpace(PULONGLONG FreeBytesAvailable, - PULONGLONG TotalNumberOfBytes, - PULONGLONG TotalNumberOfFreeBytes, - PDOKAN_FILE_INFO DokanFileInfo) { - UNREFERENCED_PARAMETER(DokanFileInfo); +NTSTATUS DOKAN_CALLBACK DokanGetDiskFreeSpace(DOKAN_GET_DISK_FREE_SPACE_EVENT *EventInfo) { - *FreeBytesAvailable = 512 * 1024 * 1024; - *TotalNumberOfBytes = 1024 * 1024 * 1024; - *TotalNumberOfFreeBytes = 512 * 1024 * 1024; - - return STATUS_SUCCESS; + EventInfo->FreeBytesAvailable = 0; + EventInfo->TotalNumberOfBytes = 1024 * 1024 * 1024; + EventInfo->TotalNumberOfFreeBytes = 0; + + return STATUS_SUCCESS; } -NTSTATUS DOKAN_CALLBACK DokanGetVolumeInformation( - LPWSTR VolumeNameBuffer, DWORD VolumeNameSize, LPDWORD VolumeSerialNumber, - LPDWORD MaximumComponentLength, LPDWORD FileSystemFlags, - LPWSTR FileSystemNameBuffer, DWORD FileSystemNameSize, - PDOKAN_FILE_INFO DokanFileInfo) { - UNREFERENCED_PARAMETER(DokanFileInfo); +NTSTATUS DOKAN_CALLBACK DokanGetVolumeInformation(DOKAN_GET_VOLUME_INFO_EVENT *EventInfo) { - wcscpy_s(VolumeNameBuffer, VolumeNameSize, L"DOKAN"); - *VolumeSerialNumber = 0x19831116; - *MaximumComponentLength = 256; - *FileSystemFlags = FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES | - FILE_SUPPORTS_REMOTE_STORAGE | FILE_UNICODE_ON_DISK; + wcscpy_s(EventInfo->VolumeInfo->VolumeLabel, EventInfo->MaxLabelLengthInChars, L"DOKAN"); + EventInfo->VolumeInfo->VolumeLabelLength = (ULONG)(wcslen(EventInfo->VolumeInfo->VolumeLabel) * sizeof(WCHAR)); - wcscpy_s(FileSystemNameBuffer, FileSystemNameSize, L"NTFS"); + EventInfo->VolumeInfo->VolumeSerialNumber = 0x19831116; return STATUS_SUCCESS; } -NTSTATUS -DokanFsVolumeInformation(PEVENT_INFORMATION EventInfo, - PEVENT_CONTEXT EventContext, PDOKAN_FILE_INFO FileInfo, - PDOKAN_OPERATIONS DokanOperations) { - WCHAR volumeName[MAX_PATH]; - DWORD volumeSerial = 0; - DWORD maxComLength = 0; - DWORD fsFlags = 0; - WCHAR fsName[MAX_PATH]; - ULONG remainingLength; - ULONG bytesToCopy; +void DokanFsVolumeInformation(DOKAN_IO_EVENT *EventInfo) { + + DOKAN_GET_VOLUME_INFO_EVENT *getVolumeInfo = &EventInfo->EventInfo.GetVolumeInfo; NTSTATUS status = STATUS_NOT_IMPLEMENTED; - PFILE_FS_VOLUME_INFORMATION volumeInfo = - (PFILE_FS_VOLUME_INFORMATION)EventInfo->Buffer; + assert((void*)getVolumeInfo == (void*)EventInfo); - remainingLength = EventContext->Operation.Volume.BufferLength; + if (EventInfo->KernelInfo.EventContext.Operation.Volume.BufferLength < sizeof(FILE_FS_VOLUME_INFORMATION)) { + + EndDispatchGetVolumeInfo(getVolumeInfo, STATUS_BUFFER_OVERFLOW); - if (remainingLength < sizeof(FILE_FS_VOLUME_INFORMATION)) { - return STATUS_BUFFER_OVERFLOW; + return; } - RtlZeroMemory(volumeName, sizeof(volumeName)); - RtlZeroMemory(fsName, sizeof(fsName)); - - if (DokanOperations->GetVolumeInformation) { - status = DokanOperations->GetVolumeInformation( - volumeName, // VolumeNameBuffer - sizeof(volumeName) / sizeof(WCHAR), // VolumeNameSize - &volumeSerial, // VolumeSerialNumber - &maxComLength, // MaximumComponentLength - &fsFlags, // FileSystemFlags - fsName, // FileSystemNameBuffer - sizeof(fsName) / sizeof(WCHAR), // FileSystemNameSize - FileInfo); - } + getVolumeInfo->DokanFileInfo = &EventInfo->DokanFileInfo; + getVolumeInfo->VolumeInfo = (PFILE_FS_VOLUME_INFORMATION)&EventInfo->EventResult->Buffer[0]; + getVolumeInfo->MaxLabelLengthInChars = + (EventInfo->KernelInfo.EventContext.Operation.Volume.BufferLength - offsetof(FILE_FS_VOLUME_INFORMATION, VolumeLabel)) / sizeof(WCHAR); + + if (EventInfo->DokanInstance->DokanOperations->GetVolumeInformationW) { - if (status == STATUS_NOT_IMPLEMENTED) { - status = DokanGetVolumeInformation( - volumeName, // VolumeNameBuffer - sizeof(volumeName) / sizeof(WCHAR), // VolumeNameSize - &volumeSerial, // VolumeSerialNumber - &maxComLength, // MaximumComponentLength - &fsFlags, // FileSystemFlags - fsName, // FileSystemNameBuffer - sizeof(fsName) / sizeof(WCHAR), // FileSystemNameSize - FileInfo); + status = EventInfo->DokanInstance->DokanOperations->GetVolumeInformationW(getVolumeInfo); } - if (status != STATUS_SUCCESS) { - return status; + if(status != STATUS_PENDING) { + + EndDispatchGetVolumeInfo(getVolumeInfo, status); } +} - volumeInfo->VolumeCreationTime.QuadPart = 0; - volumeInfo->VolumeSerialNumber = volumeSerial; - volumeInfo->SupportsObjects = FALSE; +void DOKANAPI EndDispatchGetVolumeInfo(DOKAN_GET_VOLUME_INFO_EVENT *EventInfo, NTSTATUS ResultStatus) { - remainingLength -= FIELD_OFFSET(FILE_FS_VOLUME_INFORMATION, VolumeLabel[0]); + DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; + PEVENT_INFORMATION result = ioEvent->EventResult; + PFILE_FS_VOLUME_INFORMATION volumeInfo = (PFILE_FS_VOLUME_INFORMATION)result->Buffer; - bytesToCopy = (ULONG)wcslen(volumeName) * sizeof(WCHAR); - if (remainingLength < bytesToCopy) { - bytesToCopy = remainingLength; - } + assert(result->BufferLength == 0); - volumeInfo->VolumeLabelLength = bytesToCopy; - RtlCopyMemory(volumeInfo->VolumeLabel, volumeName, bytesToCopy); - remainingLength -= bytesToCopy; + // STATUS_PENDING should not be passed to this function + if(ResultStatus == STATUS_PENDING) { - EventInfo->BufferLength = - EventContext->Operation.Volume.BufferLength - remainingLength; + DbgPrint("Dokan Error: EndDispatchGetVolumeInfo() failed because STATUS_PENDING was supplied for ResultStatus.\n"); + ResultStatus = STATUS_INTERNAL_ERROR; + } - return STATUS_SUCCESS; + if(ResultStatus == STATUS_NOT_IMPLEMENTED) { + + ResultStatus = DokanGetVolumeInformation(EventInfo); + } + + if(ResultStatus == STATUS_SUCCESS) { + + if(volumeInfo->VolumeLabelLength > EventInfo->MaxLabelLengthInChars) { + + DbgPrint("Dokan Error: EndDispatchGetVolumeInfo() failed because VolumeLabelLength is greater than MaxLabelLengthInChars.\n"); + ResultStatus = STATUS_BUFFER_OVERFLOW; + } + else { + + // convert from chars to bytes + volumeInfo->VolumeLabelLength *= sizeof(WCHAR); + volumeInfo->SupportsObjects = FALSE; + + result->BufferLength = offsetof(FILE_FS_VOLUME_INFORMATION, VolumeLabel) + volumeInfo->VolumeLabelLength; + } + } + + result->Status = ResultStatus; + + SendIoEventResult(ioEvent); } -NTSTATUS -DokanFsSizeInformation(PEVENT_INFORMATION EventInfo, - PEVENT_CONTEXT EventContext, PDOKAN_FILE_INFO FileInfo, - PDOKAN_OPERATIONS DokanOperations) { - ULONGLONG freeBytesAvailable = 0; - ULONGLONG totalBytes = 0; - ULONGLONG freeBytes = 0; +void DokanFsSizeInformation(DOKAN_IO_EVENT *EventInfo) { + NTSTATUS status = STATUS_NOT_IMPLEMENTED; + DOKAN_GET_DISK_FREE_SPACE_EVENT *getFreeSpace = &EventInfo->EventInfo.GetVolumeFreeSpace; - ULONG allocationUnitSize = FileInfo->DokanOptions->AllocationUnitSize; - ULONG sectorSize = FileInfo->DokanOptions->SectorSize; + assert((void*)getFreeSpace == (void*)EventInfo); - PFILE_FS_SIZE_INFORMATION sizeInfo = - (PFILE_FS_SIZE_INFORMATION)EventInfo->Buffer; + if (EventInfo->KernelInfo.EventContext.Operation.Volume.BufferLength < sizeof(FILE_FS_SIZE_INFORMATION)) { - if (EventContext->Operation.Volume.BufferLength < - sizeof(FILE_FS_SIZE_INFORMATION)) { - return STATUS_BUFFER_OVERFLOW; + EndDispatchGetVolumeFreeSpace(getFreeSpace, STATUS_BUFFER_OVERFLOW); + return; } - if (DokanOperations->GetDiskFreeSpace) { - status = DokanOperations->GetDiskFreeSpace( - &freeBytesAvailable, // FreeBytesAvailable - &totalBytes, // TotalNumberOfBytes - &freeBytes, // TotalNumberOfFreeBytes - FileInfo); - } + if (EventInfo->DokanInstance->DokanOperations->GetVolumeFreeSpace) { + + getFreeSpace->DokanFileInfo = &EventInfo->DokanFileInfo; - if (status == STATUS_NOT_IMPLEMENTED) { - status = DokanGetDiskFreeSpace(&freeBytesAvailable, // FreeBytesAvailable - &totalBytes, // TotalNumberOfBytes - &freeBytes, // TotalNumberOfFreeBytes - FileInfo); + status = EventInfo->DokanInstance->DokanOperations->GetVolumeFreeSpace(getFreeSpace); } - if (status != STATUS_SUCCESS) { - return status; + if (status != STATUS_PENDING) { + + EndDispatchGetVolumeFreeSpace(getFreeSpace, status); } +} - sizeInfo->TotalAllocationUnits.QuadPart = - totalBytes / allocationUnitSize; - sizeInfo->AvailableAllocationUnits.QuadPart = - freeBytesAvailable / allocationUnitSize; - sizeInfo->SectorsPerAllocationUnit = - allocationUnitSize / sectorSize; - sizeInfo->BytesPerSector = sectorSize; +void DOKANAPI EndDispatchGetVolumeFreeSpace(DOKAN_GET_DISK_FREE_SPACE_EVENT *EventInfo, NTSTATUS ResultStatus) { - EventInfo->BufferLength = sizeof(FILE_FS_SIZE_INFORMATION); + DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; + PEVENT_INFORMATION result = ioEvent->EventResult; + ULONG allocationUnitSize = ioEvent->DokanInstance->DokanOptions->AllocationUnitSize; + ULONG sectorSize = ioEvent->DokanInstance->DokanOptions->SectorSize; - return STATUS_SUCCESS; -} + // STATUS_PENDING should not be passed to this function + if(ResultStatus == STATUS_PENDING) { -NTSTATUS -DokanFsAttributeInformation(PEVENT_INFORMATION EventInfo, - PEVENT_CONTEXT EventContext, - PDOKAN_FILE_INFO FileInfo, - PDOKAN_OPERATIONS DokanOperations) { - WCHAR volumeName[MAX_PATH]; - DWORD volumeSerial; - DWORD maxComLength = 0; - DWORD fsFlags = 0; - WCHAR fsName[MAX_PATH]; - ULONG remainingLength; - ULONG bytesToCopy; - NTSTATUS status = STATUS_NOT_IMPLEMENTED; + DbgPrint("Dokan Error: EndDispatchGetVolumeInfo() failed because STATUS_PENDING was supplied for ResultStatus.\n"); + ResultStatus = STATUS_INTERNAL_ERROR; + } - PFILE_FS_ATTRIBUTE_INFORMATION attrInfo = - (PFILE_FS_ATTRIBUTE_INFORMATION)EventInfo->Buffer; + if(ResultStatus == STATUS_NOT_IMPLEMENTED) { - remainingLength = EventContext->Operation.Volume.BufferLength; + ResultStatus = DokanGetDiskFreeSpace(EventInfo); + } - if (remainingLength < sizeof(FILE_FS_ATTRIBUTE_INFORMATION)) { - return STATUS_BUFFER_OVERFLOW; - } + if(ResultStatus == STATUS_SUCCESS) { - RtlZeroMemory(volumeName, sizeof(volumeName)); - RtlZeroMemory(fsName, sizeof(fsName)); - - if (DokanOperations->GetVolumeInformation) { - status = DokanOperations->GetVolumeInformation( - volumeName, // VolumeNameBuffer - sizeof(volumeName) / sizeof(WCHAR), // VolumeNameSize - &volumeSerial, // VolumeSerialNumber - &maxComLength, // MaximumComponentLength - &fsFlags, // FileSystemFlags - fsName, // FileSystemNameBuffer - sizeof(fsName) / sizeof(WCHAR), // FileSystemNameSize - FileInfo); - } + if(ioEvent->KernelInfo.EventContext.Operation.Volume.FsInformationClass == FileFsSizeInformation) { - if (status == STATUS_NOT_IMPLEMENTED) { - status = DokanGetVolumeInformation( - volumeName, // VolumeNameBuffer - sizeof(volumeName) / sizeof(WCHAR), // VolumeNameSize - &volumeSerial, // VolumeSerialNumber - &maxComLength, // MaximumComponentLength - &fsFlags, // FileSystemFlags - fsName, // FileSystemNameBuffer - sizeof(fsName) / sizeof(WCHAR), // FileSystemNameSize - FileInfo); - } + PFILE_FS_SIZE_INFORMATION freeSpaceInfo = (PFILE_FS_SIZE_INFORMATION)result->Buffer; - if (status != STATUS_SUCCESS) { - return status; - } + freeSpaceInfo->TotalAllocationUnits.QuadPart = + EventInfo->TotalNumberOfBytes / allocationUnitSize; - attrInfo->FileSystemAttributes = fsFlags; - attrInfo->MaximumComponentNameLength = maxComLength; + freeSpaceInfo->AvailableAllocationUnits.QuadPart = + EventInfo->FreeBytesAvailable / allocationUnitSize; - remainingLength -= - FIELD_OFFSET(FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName[0]); + freeSpaceInfo->SectorsPerAllocationUnit = + allocationUnitSize / sectorSize; - bytesToCopy = (ULONG)wcslen(fsName) * sizeof(WCHAR); - if (remainingLength < bytesToCopy) { - bytesToCopy = remainingLength; - } + freeSpaceInfo->BytesPerSector = sectorSize; - attrInfo->FileSystemNameLength = bytesToCopy; - RtlCopyMemory(attrInfo->FileSystemName, fsName, bytesToCopy); - remainingLength -= bytesToCopy; + result->BufferLength = sizeof(FILE_FS_SIZE_INFORMATION); + } + else if(ioEvent->KernelInfo.EventContext.Operation.Volume.FsInformationClass == FileFsFullSizeInformation) { - EventInfo->BufferLength = - EventContext->Operation.Volume.BufferLength - remainingLength; + PFILE_FS_FULL_SIZE_INFORMATION freeSpaceInfo = (PFILE_FS_FULL_SIZE_INFORMATION)result->Buffer; - return STATUS_SUCCESS; + freeSpaceInfo->TotalAllocationUnits.QuadPart = + EventInfo->TotalNumberOfBytes / allocationUnitSize; + + freeSpaceInfo->ActualAvailableAllocationUnits.QuadPart = + EventInfo->TotalNumberOfFreeBytes / allocationUnitSize; + + freeSpaceInfo->CallerAvailableAllocationUnits.QuadPart = + EventInfo->FreeBytesAvailable / allocationUnitSize; + + freeSpaceInfo->SectorsPerAllocationUnit = + allocationUnitSize / sectorSize; + + freeSpaceInfo->BytesPerSector = sectorSize; + + result->BufferLength = sizeof(FILE_FS_FULL_SIZE_INFORMATION); + } + else { + + DbgPrint("Dokan Error: EndDispatchGetVolumeFreeSpace() received an invalid FsInformationClass: 0x%x\n", + ioEvent->KernelInfo.EventContext.Operation.Volume.FsInformationClass); + + ResultStatus = STATUS_INTERNAL_ERROR; + } + } + + result->Status = ResultStatus; + + SendIoEventResult(ioEvent); } -NTSTATUS -DokanFsFullSizeInformation(PEVENT_INFORMATION EventInfo, - PEVENT_CONTEXT EventContext, - PDOKAN_FILE_INFO FileInfo, - PDOKAN_OPERATIONS DokanOperations) { - ULONGLONG freeBytesAvailable = 0; - ULONGLONG totalBytes = 0; - ULONGLONG freeBytes = 0; - NTSTATUS status = STATUS_NOT_IMPLEMENTED; +void DokanFsAttributeInformation(DOKAN_IO_EVENT *EventInfo) { - ULONG allocationUnitSize = FileInfo->DokanOptions->AllocationUnitSize; - ULONG sectorSize = FileInfo->DokanOptions->SectorSize; + NTSTATUS status = STATUS_NOT_IMPLEMENTED; + DOKAN_GET_VOLUME_ATTRIBUTES_EVENT *getAttributes = &EventInfo->EventInfo.GetVolumeAttributes; - PFILE_FS_FULL_SIZE_INFORMATION sizeInfo = - (PFILE_FS_FULL_SIZE_INFORMATION)EventInfo->Buffer; + if (EventInfo->KernelInfo.EventContext.Operation.Volume.BufferLength < sizeof(FILE_FS_ATTRIBUTE_INFORMATION)) { - if (EventContext->Operation.Volume.BufferLength < - sizeof(FILE_FS_FULL_SIZE_INFORMATION)) { - return STATUS_BUFFER_OVERFLOW; + EndDispatchGetVolumeAttributes(getAttributes, STATUS_BUFFER_OVERFLOW); + return; } - if (DokanOperations->GetDiskFreeSpace) { - status = DokanOperations->GetDiskFreeSpace( - &freeBytesAvailable, // FreeBytesAvailable - &totalBytes, // TotalNumberOfBytes - &freeBytes, // TotalNumberOfFreeBytes - FileInfo); - } + if (EventInfo->DokanInstance->DokanOperations->GetVolumeAttributes) { + + getAttributes->DokanFileInfo = &EventInfo->DokanFileInfo; + getAttributes->Attributes = (PFILE_FS_ATTRIBUTE_INFORMATION)&EventInfo->EventResult->Buffer[0]; + getAttributes->MaxFileSystemNameLengthInChars = + (EventInfo->KernelInfo.EventContext.Operation.Volume.BufferLength - offsetof(FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName)) / sizeof(WCHAR); - if (status == STATUS_NOT_IMPLEMENTED) { - status = DokanGetDiskFreeSpace(&freeBytesAvailable, // FreeBytesAvailable - &totalBytes, // TotalNumberOfBytes - &freeBytes, // TotalNumberOfFreeBytes - FileInfo); + status = EventInfo->DokanInstance->DokanOperations->GetVolumeAttributes(getAttributes); } - if (status != STATUS_SUCCESS) { - return status; + if (status != STATUS_PENDING) { + + EndDispatchGetVolumeAttributes(getAttributes, status); } +} - sizeInfo->TotalAllocationUnits.QuadPart = - totalBytes / allocationUnitSize; - sizeInfo->ActualAvailableAllocationUnits.QuadPart = - freeBytes / allocationUnitSize; - sizeInfo->CallerAvailableAllocationUnits.QuadPart = - freeBytesAvailable / allocationUnitSize; - sizeInfo->SectorsPerAllocationUnit = - allocationUnitSize / sectorSize; - sizeInfo->BytesPerSector = sectorSize; +void DOKANAPI EndDispatchGetVolumeAttributes(DOKAN_GET_VOLUME_ATTRIBUTES_EVENT *EventInfo, NTSTATUS ResultStatus) { - EventInfo->BufferLength = sizeof(FILE_FS_FULL_SIZE_INFORMATION); + DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; + PEVENT_INFORMATION result = ioEvent->EventResult; + PFILE_FS_ATTRIBUTE_INFORMATION freeSpaceInfo = (PFILE_FS_ATTRIBUTE_INFORMATION)result->Buffer; - return STATUS_SUCCESS; + // STATUS_PENDING should not be passed to this function + if(ResultStatus == STATUS_PENDING) { + + DbgPrint("Dokan Error: EndDispatchGetVolumeAttributes() failed because STATUS_PENDING was supplied for ResultStatus.\n"); + ResultStatus = STATUS_INTERNAL_ERROR; + } + + if(ResultStatus == STATUS_NOT_IMPLEMENTED) { + + freeSpaceInfo->FileSystemAttributes = FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES | + FILE_SUPPORTS_REMOTE_STORAGE | FILE_UNICODE_ON_DISK; + + wcscpy_s(&freeSpaceInfo->FileSystemName[0], EventInfo->MaxFileSystemNameLengthInChars, L"DokanFS"); + + freeSpaceInfo->FileSystemNameLength = (ULONG)wcslen(&freeSpaceInfo->FileSystemName[0]); + freeSpaceInfo->MaximumComponentNameLength = 256; + + ResultStatus = STATUS_SUCCESS; + } + + if(ResultStatus == STATUS_SUCCESS) { + + if(freeSpaceInfo->FileSystemNameLength > EventInfo->MaxFileSystemNameLengthInChars) { + + DbgPrint("Dokan Error: EndDispatchGetVolumeAttributes() failed because FileSystemNameLength is greater than MaxFileSystemNameLengthInChars.\n"); + + ResultStatus = STATUS_BUFFER_OVERFLOW; + } + else { + + freeSpaceInfo->FileSystemNameLength *= sizeof(WCHAR); + result->BufferLength = offsetof(FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName) + freeSpaceInfo->FileSystemNameLength; + } + } + + result->Status = ResultStatus; + + SendIoEventResult(ioEvent); } -VOID DispatchQueryVolumeInformation(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance) { - PEVENT_INFORMATION eventInfo; - DOKAN_FILE_INFO fileInfo; - PDOKAN_OPEN_INFO openInfo; - ULONG sizeOfEventInfo = sizeof(EVENT_INFORMATION) - 8 + - EventContext->Operation.Volume.BufferLength; - - eventInfo = (PEVENT_INFORMATION)malloc(sizeOfEventInfo); - if (eventInfo == NULL) { - return; - } +void DokanFsFullSizeInformation(DOKAN_IO_EVENT *EventInfo) { + + NTSTATUS status = STATUS_NOT_IMPLEMENTED; + DOKAN_GET_DISK_FREE_SPACE_EVENT *getFreeSpace = &EventInfo->EventInfo.GetVolumeFreeSpace; - RtlZeroMemory(eventInfo, sizeOfEventInfo); - RtlZeroMemory(&fileInfo, sizeof(DOKAN_FILE_INFO)); + assert((void*)getFreeSpace == (void*)EventInfo); - // There is no Context because file is not opened - // so DispatchCommon is not used here - openInfo = (PDOKAN_OPEN_INFO)(INT_PTR)EventContext->Context; + if(EventInfo->KernelInfo.EventContext.Operation.Volume.BufferLength < sizeof(FILE_FS_FULL_SIZE_INFORMATION)) { - eventInfo->BufferLength = 0; - eventInfo->SerialNumber = EventContext->SerialNumber; + EndDispatchGetVolumeFreeSpace(getFreeSpace, STATUS_BUFFER_OVERFLOW); + return; + } - fileInfo.ProcessId = EventContext->ProcessId; - fileInfo.DokanOptions = DokanInstance->DokanOptions; + if(EventInfo->DokanInstance->DokanOperations->GetVolumeFreeSpace) { - eventInfo->Status = STATUS_NOT_IMPLEMENTED; - eventInfo->BufferLength = 0; + getFreeSpace->DokanFileInfo = &EventInfo->DokanFileInfo; - DbgPrint("###QueryVolumeInfo %04d\n", - openInfo != NULL ? openInfo->EventId : -1); + status = EventInfo->DokanInstance->DokanOperations->GetVolumeFreeSpace(getFreeSpace); + } - switch (EventContext->Operation.Volume.FsInformationClass) { + if(status != STATUS_PENDING) { + + EndDispatchGetVolumeFreeSpace(getFreeSpace, status); + } +} + +void BeginDispatchQueryVolumeInformation(DOKAN_IO_EVENT *EventInfo) { + + CreateDispatchCommon(EventInfo, EventInfo->KernelInfo.EventContext.Operation.Volume.BufferLength); + + DbgPrint("###QueryVolumeInfo file handle = 0x%p, eventID = %04d\n", + EventInfo->DokanOpenInfo, + EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1); + + switch (EventInfo->KernelInfo.EventContext.Operation.Volume.FsInformationClass) { case FileFsVolumeInformation: - eventInfo->Status = DokanFsVolumeInformation( - eventInfo, EventContext, &fileInfo, DokanInstance->DokanOperations); + DokanFsVolumeInformation(EventInfo); break; case FileFsSizeInformation: - eventInfo->Status = DokanFsSizeInformation( - eventInfo, EventContext, &fileInfo, DokanInstance->DokanOperations); + DokanFsSizeInformation(EventInfo); break; case FileFsAttributeInformation: - eventInfo->Status = DokanFsAttributeInformation( - eventInfo, EventContext, &fileInfo, DokanInstance->DokanOperations); + DokanFsAttributeInformation(EventInfo); break; case FileFsFullSizeInformation: - eventInfo->Status = DokanFsFullSizeInformation( - eventInfo, EventContext, &fileInfo, DokanInstance->DokanOperations); + DokanFsFullSizeInformation(EventInfo); break; default: - DbgPrint("error unknown volume info %d\n", - EventContext->Operation.Volume.FsInformationClass); - } + + DbgPrint("Dokan Error: Unknown volume info FsInformationClass 0x%x\n", + EventInfo->KernelInfo.EventContext.Operation.Volume.FsInformationClass); + + EventInfo->EventResult->Status = STATUS_NOT_IMPLEMENTED; - SendEventInformation(Handle, eventInfo, sizeOfEventInfo, NULL); - free(eventInfo); - return; + SendIoEventResult(EventInfo); + + break; + } } diff --git a/dokan/write.c b/dokan/write.c index 0e1c5cfc8..d86abf77c 100644 --- a/dokan/write.c +++ b/dokan/write.c @@ -21,92 +21,148 @@ with this program. If not, see . #include "dokani.h" #include "fileinfo.h" +#include -VOID SendWriteRequest(HANDLE Handle, PEVENT_INFORMATION EventInfo, - ULONG EventLength, PVOID Buffer, ULONG BufferLength) { - BOOL status; - ULONG returnedLength; - - DbgPrint("SendWriteRequest\n"); - - status = DeviceIoControl(Handle, // Handle to device - IOCTL_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); +NTSTATUS SendWriteRequest(DOKAN_IO_EVENT *EventInfo) { + + DOKAN_IO_EVENT *writeEvent; + DOKAN_OVERLAPPED *overlapped; + DWORD lastError; + + DbgPrint("Dokan Information: Requesting write buffer of size %u.\n", EventInfo->KernelInfo.EventContext.Operation.Write.RequestLength); + + assert(EventInfo->EventResult); + assert(EventInfo->EventResultSize > 0); + + overlapped = PopOverlapped(); + + if(!overlapped) { + + DbgPrint("Failed to allocate overlapped.\n"); + + return STATUS_INTERNAL_ERROR; + } + + writeEvent = (DOKAN_IO_EVENT*)malloc(DOKAN_IO_EVENT_ALLOC_SIZE(EventInfo->KernelInfo.EventContext.Operation.Write.RequestLength)); + + if(!writeEvent) { + + DbgPrint("Dokan Error: Failed to allocate memory for write operation.\n"); + + PushOverlapped(overlapped); + + return STATUS_NO_MEMORY; } - DbgPrint("SendWriteRequest got %d bytes\n", returnedLength); + RtlZeroMemory(writeEvent, EventInfo->KernelInfo.EventContext.Operation.Write.RequestLength); + writeEvent->DokanInstance = EventInfo->DokanInstance; + + overlapped->InputPayload = EventInfo; + overlapped->OutputPayload = writeEvent; + overlapped->PayloadType = DOKAN_OVERLAPPED_TYPE_IOEVENT_WRITE_SIZE; + overlapped->Flags = EventInfo->Flags; + + StartThreadpoolIo(EventInfo->DokanInstance->ThreadInfo.IoCompletion); + + if(!DeviceIoControl(EventInfo->DokanInstance->Device, // Handle to device + IOCTL_EVENT_WRITE, // IO Control code + EventInfo->EventResult, // Input Buffer to driver. + EventInfo->EventResultSize, // Length of input buffer in bytes. + writeEvent->KernelInfo.EventContextBuffer, // Output Buffer from driver. + EventInfo->KernelInfo.EventContext.Operation.Write.RequestLength, // Length of output buffer in bytes. + NULL, // Bytes placed in buffer. + (LPOVERLAPPED)overlapped // asynchronous call + )) { + + lastError = GetLastError(); + + if(lastError != ERROR_IO_PENDING) { + + DbgPrint("Dokan Error: Dokan device ioctl failed for wait with code %d.\n", lastError); + + CancelThreadpoolIo(EventInfo->DokanInstance->ThreadInfo.IoCompletion); + + PushOverlapped(overlapped); + + free(writeEvent); + + return DokanNtStatusFromWin32(lastError); + } + } + + return STATUS_SUCCESS; } -VOID DispatchWrite(HANDLE Handle, PEVENT_CONTEXT EventContext, - PDOKAN_INSTANCE DokanInstance) { - PEVENT_INFORMATION eventInfo; - PDOKAN_OPEN_INFO openInfo; - ULONG writtenLength = 0; +void BeginDispatchWrite(DOKAN_IO_EVENT *EventInfo) { + + DOKAN_WRITE_FILE_EVENT *writeFileEvent = &EventInfo->EventInfo.WriteFile; + PDOKAN_INSTANCE dokan = EventInfo->DokanInstance; NTSTATUS status; - DOKAN_FILE_INFO fileInfo; - BOOL bufferAllocated = FALSE; - ULONG sizeOfEventInfo = sizeof(EVENT_INFORMATION); - eventInfo = DispatchCommon(EventContext, sizeOfEventInfo, DokanInstance, - &fileInfo, &openInfo); + assert(EventInfo->DokanOpenInfo); + assert((void*)writeFileEvent == (void*)EventInfo); + assert(EventInfo->ProcessingContext == NULL); + + CreateDispatchCommon(EventInfo, 0); // 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); - return; - } - SendWriteRequest(Handle, eventInfo, sizeOfEventInfo, contextBuf, - contextLength); - EventContext = contextBuf; - bufferAllocated = TRUE; + if (EventInfo->KernelInfo.EventContext.Operation.Write.RequestLength > 0) { + + if((status = SendWriteRequest(EventInfo)) != STATUS_SUCCESS) { + + EndDispatchWrite(writeFileEvent, status); + } + + return; } - CheckFileName(EventContext->Operation.Write.FileName); + CheckFileName(EventInfo->KernelInfo.EventContext.Operation.Write.FileName); + + DbgPrint("###WriteFile file handle = 0x%p, eventID = %04d\n", + EventInfo->DokanOpenInfo, + EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1); + + if (dokan->DokanOperations->WriteFile) { - DbgPrint("###WriteFile %04d\n", openInfo != NULL ? openInfo->EventId : -1); + writeFileEvent->DokanFileInfo = &EventInfo->DokanFileInfo; + writeFileEvent->FileName = EventInfo->KernelInfo.EventContext.Operation.Write.FileName; + writeFileEvent->Buffer = ((PCHAR)&EventInfo->KernelInfo.EventContext) + EventInfo->KernelInfo.EventContext.Operation.Write.BufferOffset; + writeFileEvent->Offset = EventInfo->KernelInfo.EventContext.Operation.Write.ByteOffset.QuadPart; + writeFileEvent->NumberOfBytesToWrite = EventInfo->KernelInfo.EventContext.Operation.Write.BufferLength; - 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; + assert(writeFileEvent->NumberOfBytesWritten == 0); + + status = dokan->DokanOperations->WriteFile(writeFileEvent); + } + else { + + status = STATUS_NOT_IMPLEMENTED; } - if (openInfo != NULL) - openInfo->UserContext = fileInfo.Context; - eventInfo->BufferLength = 0; - - if (status == STATUS_SUCCESS) { - eventInfo->Status = status; - eventInfo->BufferLength = writtenLength; - eventInfo->Operation.Write.CurrentByteOffset.QuadPart = - EventContext->Operation.Write.ByteOffset.QuadPart + writtenLength; - } else { - eventInfo->Status = STATUS_INVALID_PARAMETER; + if(status != STATUS_PENDING) { + + EndDispatchWrite(writeFileEvent, status); } +} - SendEventInformation(Handle, eventInfo, sizeOfEventInfo, DokanInstance); - free(eventInfo); +void DOKANAPI EndDispatchWrite(DOKAN_WRITE_FILE_EVENT *EventInfo, NTSTATUS ResultStatus) { - if (bufferAllocated) - free(EventContext); + DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; + PEVENT_INFORMATION result; - return; -} + result = ioEvent->EventResult; + + // STATUS_PENDING should not be passed to this function + if(ResultStatus == STATUS_PENDING) { + + DbgPrint("Dokan Error: EndDispatchWrite() failed because STATUS_PENDING was supplied for ResultStatus.\n"); + ResultStatus = STATUS_INTERNAL_ERROR; + } + + result->Status = ResultStatus; + result->BufferLength = EventInfo->NumberOfBytesWritten; + result->Operation.Write.CurrentByteOffset.QuadPart = EventInfo->Offset + EventInfo->NumberOfBytesWritten; + + SendIoEventResult(ioEvent); +} \ No newline at end of file diff --git a/dokan_fuse/include/fusemain.h b/dokan_fuse/include/fusemain.h index 94a6f80a1..410f465d6 100644 --- a/dokan_fuse/include/fusemain.h +++ b/dokan_fuse/include/fusemain.h @@ -108,8 +108,7 @@ class impl_fuse_context { impl_fuse_context *ctx; std::string dirname; - PDOKAN_FILE_INFO DokanFileInfo; - PFillFindData delegate; + PDOKAN_FIND_FILES_EVENT eventInfo; std::vector getdir_data; //Used only in walk_directory_getdir() }; static int walk_directory(void *buf, const char *name, @@ -117,8 +116,7 @@ class impl_fuse_context static int walk_directory_getdir(fuse_dirh_t hndl, const char *name, int type,ino_t ino); ///////////////////////////////////Delegates////////////////////////////// - int find_files(LPCWSTR file_name, PFillFindData fill_find_data, - PDOKAN_FILE_INFO dokan_file_info); + int find_files(PDOKAN_FIND_FILES_EVENT EventInfo); int open_directory(LPCWSTR file_name, PDOKAN_FILE_INFO dokan_file_info); @@ -180,9 +178,9 @@ class impl_fuse_context LPWSTR file_system_name_buffer, DWORD file_system_name_size, PDOKAN_FILE_INFO dokan_file_info, LPDWORD volume_flags); - int mounted(PDOKAN_FILE_INFO DokanFileInfo); + int mounted(DOKAN_MOUNTED_INFO *EventInfo); - int unmounted(PDOKAN_FILE_INFO DokanFileInfo); + int unmounted(DOKAN_UNMOUNTED_INFO *EventInfo); }; diff --git a/dokan_fuse/src/dokanfuse.cpp b/dokan_fuse/src/dokanfuse.cpp index 83a2c6d60..0c308608d 100755 --- a/dokan_fuse/src/dokanfuse.cpp +++ b/dokan_fuse/src/dokanfuse.cpp @@ -21,7 +21,7 @@ #define the_impl \ reinterpret_cast( \ - DokanFileInfo->DokanOptions->GlobalContext) + EventInfo->DokanFileInfo->DokanOptions->GlobalContext) HINSTANCE hFuseDllInstance; @@ -35,37 +35,43 @@ extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, } static NTSTATUS DOKAN_CALLBACK -FuseFindFiles(LPCWSTR FileName, - PFillFindData FillFindData, // function pointer - PDOKAN_FILE_INFO DokanFileInfo) { +FuseFindFiles(DOKAN_FIND_FILES_EVENT *EventInfo) { + impl_fuse_context *impl = the_impl; + if (impl->debug()) - FPRINTF(stderr, "FindFiles: %ls\n", FileName); + FPRINTF(stderr, "FindFiles: %ls\n", EventInfo->PathName); + + impl_chain_guard guard(impl, EventInfo->DokanFileInfo->ProcessId); - impl_chain_guard guard(impl, DokanFileInfo->ProcessId); return errno_to_ntstatus_error( - impl->find_files(FileName, FillFindData, DokanFileInfo)); + impl->find_files(EventInfo)); } -static void DOKAN_CALLBACK FuseCleanup(LPCWSTR FileName, - PDOKAN_FILE_INFO DokanFileInfo) { +static void DOKAN_CALLBACK FuseCleanup(DOKAN_CLEANUP_EVENT *EventInfo) { + impl_fuse_context *impl = the_impl; + if (impl->debug()) - FPRINTF(stderr, "Cleanup: %ls\n\n", FileName); + FPRINTF(stderr, "Cleanup: %ls\n\n", EventInfo->FileName); - impl_chain_guard guard(impl, DokanFileInfo->ProcessId); - impl->cleanup(FileName, DokanFileInfo); + impl_chain_guard guard(impl, EventInfo->DokanFileInfo->ProcessId); + + impl->cleanup(EventInfo->FileName, EventInfo->DokanFileInfo); } static NTSTATUS DOKAN_CALLBACK -FuseDeleteDirectory(LPCWSTR FileName, PDOKAN_FILE_INFO DokanFileInfo) { +FuseCanDeleteDirectory(DOKAN_CAN_DELETE_FILE_EVENT *EventInfo) { + impl_fuse_context *impl = the_impl; + if (impl->debug()) - FPRINTF(stderr, "DeleteDirectory: %ls\n", FileName); + FPRINTF(stderr, "DeleteDirectory: %ls\n", EventInfo->FileName); + + impl_chain_guard guard(impl, EventInfo->DokanFileInfo->ProcessId); - impl_chain_guard guard(impl, DokanFileInfo->ProcessId); return errno_to_ntstatus_error( - impl->delete_directory(FileName, DokanFileInfo)); + impl->delete_directory(EventInfo->FileName, EventInfo->DokanFileInfo)); } struct Constant { @@ -166,178 +172,200 @@ void DebugConstantBit(const char *name, DWORD value, Constant *cs) { } static NTSTATUS DOKAN_CALLBACK -FuseCreateFile(LPCWSTR FileName, PDOKAN_IO_SECURITY_CONTEXT SecurityContext, - ACCESS_MASK DesiredAccess, ULONG FileAttributes, - ULONG ShareAccess, ULONG CreateDisposition, ULONG CreateOptions, - PDOKAN_FILE_INFO DokanFileInfo) { +FuseCreateFile(DOKAN_CREATE_FILE_EVENT *EventInfo) { + impl_fuse_context *impl = the_impl; if (impl->debug()) { - FPRINTF(stderr, "CreateFile: %ls\n", FileName); - DebugConstantBit("\tDesiredAccess", DesiredAccess, cAccessMode); - DebugConstantBit("\tShareAccess", ShareAccess, cShareMode); - DebugConstant("\tDisposition", CreateDisposition, cDisposition); - FPRINTF(stderr, "\tAttributes: %u (0x%x)\n", FileAttributes, - FileAttributes); - FPRINTF(stderr, "\tOptions: %u (0x%x)\n", CreateOptions, CreateOptions); + FPRINTF(stderr, "CreateFile: %ls\n", EventInfo->FileName); + DebugConstantBit("\tDesiredAccess", EventInfo->DesiredAccess, cAccessMode); + DebugConstantBit("\tShareAccess", EventInfo->ShareAccess, cShareMode); + DebugConstant("\tDisposition", EventInfo->CreateDisposition, cDisposition); + FPRINTF(stderr, "\tAttributes: %u (0x%x)\n", EventInfo->FileAttributes, + EventInfo->FileAttributes); + FPRINTF(stderr, "\tOptions: %u (0x%x)\n", EventInfo->CreateOptions, EventInfo->CreateOptions); fflush(stderr); } - impl_chain_guard guard(impl, DokanFileInfo->ProcessId); + impl_chain_guard guard(impl, EventInfo->DokanFileInfo->ProcessId); - if ((CreateOptions & FILE_DIRECTORY_FILE) == FILE_DIRECTORY_FILE) { + if ((EventInfo->CreateOptions & FILE_DIRECTORY_FILE) == FILE_DIRECTORY_FILE) { - if (CreateDisposition == FILE_CREATE || CreateDisposition == FILE_OPEN_IF) { + if (EventInfo->CreateDisposition == FILE_CREATE || EventInfo->CreateDisposition == FILE_OPEN_IF) { return errno_to_ntstatus_error( - impl->create_directory(FileName, DokanFileInfo)); - } else if (CreateDisposition == FILE_OPEN) { + impl->create_directory(EventInfo->FileName, EventInfo->DokanFileInfo)); + } else if (EventInfo->CreateDisposition == FILE_OPEN) { return errno_to_ntstatus_error( - impl->open_directory(FileName, DokanFileInfo)); + impl->open_directory(EventInfo->FileName, EventInfo->DokanFileInfo)); } } - return impl->create_file(FileName, DesiredAccess, ShareAccess, - CreateDisposition, FileAttributes, - DokanFileInfo); + return impl->create_file(EventInfo->FileName, EventInfo->DesiredAccess, EventInfo->ShareAccess, + EventInfo->CreateDisposition, EventInfo->FileAttributes, EventInfo->DokanFileInfo); } -static void DOKAN_CALLBACK FuseCloseFile(LPCWSTR FileName, - PDOKAN_FILE_INFO DokanFileInfo) { +static void DOKAN_CALLBACK FuseCloseFile(DOKAN_CLOSE_FILE_EVENT *EventInfo) { + impl_fuse_context *impl = the_impl; if (impl->debug()) - FPRINTF(stderr, "Close: %ls\n\n", FileName); + FPRINTF(stderr, "Close: %ls\n\n", EventInfo->FileName); - impl_chain_guard guard(impl, DokanFileInfo->ProcessId); - impl->close_file(FileName, DokanFileInfo); + impl_chain_guard guard(impl, EventInfo->DokanFileInfo->ProcessId); + impl->close_file(EventInfo->FileName, EventInfo->DokanFileInfo); } -static NTSTATUS DOKAN_CALLBACK FuseReadFile(LPCWSTR FileName, LPVOID Buffer, - DWORD BufferLength, - LPDWORD ReadLength, LONGLONG Offset, - PDOKAN_FILE_INFO DokanFileInfo) { +static NTSTATUS DOKAN_CALLBACK FuseReadFile(DOKAN_READ_FILE_EVENT *EventInfo) { + impl_fuse_context *impl = the_impl; if (impl->debug()) - FPRINTF(stderr, "ReadFile: %ls from %lld len %u\n", FileName, - (__int64)Offset, (unsigned)BufferLength); + FPRINTF(stderr, "ReadFile: %ls from %lld len %u\n", EventInfo->FileName, + (__int64)EventInfo->Offset, (unsigned)EventInfo->NumberOfBytesToRead); + + impl_chain_guard guard(impl, EventInfo->DokanFileInfo->ProcessId); - impl_chain_guard guard(impl, DokanFileInfo->ProcessId); return errno_to_ntstatus_error(impl->read_file( - FileName, Buffer, BufferLength, ReadLength, Offset, DokanFileInfo)); + EventInfo->FileName, + EventInfo->Buffer, + EventInfo->NumberOfBytesToRead, + &EventInfo->NumberOfBytesRead, + EventInfo->Offset, + EventInfo->DokanFileInfo)); } -static NTSTATUS DOKAN_CALLBACK FuseWriteFile(LPCWSTR FileName, LPCVOID Buffer, - DWORD NumberOfBytesToWrite, - LPDWORD NumberOfBytesWritten, - LONGLONG Offset, - PDOKAN_FILE_INFO DokanFileInfo) { +static NTSTATUS DOKAN_CALLBACK FuseWriteFile(DOKAN_WRITE_FILE_EVENT *EventInfo) { + impl_fuse_context *impl = the_impl; if (impl->debug()) - FPRINTF(stderr, "WriteFile: %ls, offset %lld, length %lu\n", FileName, - Offset, NumberOfBytesToWrite); + FPRINTF(stderr, "WriteFile: %ls, offset %lld, length %lu\n", EventInfo->FileName, + EventInfo->Offset, EventInfo->NumberOfBytesToWrite); + + impl_chain_guard guard(impl, EventInfo->DokanFileInfo->ProcessId); - impl_chain_guard guard(impl, DokanFileInfo->ProcessId); return errno_to_ntstatus_error( - impl->write_file(FileName, Buffer, NumberOfBytesToWrite, - NumberOfBytesWritten, Offset, DokanFileInfo)); + impl->write_file(EventInfo->FileName, + EventInfo->Buffer, + EventInfo->NumberOfBytesToWrite, + &EventInfo->NumberOfBytesWritten, + EventInfo->Offset, + EventInfo->DokanFileInfo)); } static NTSTATUS DOKAN_CALLBACK -FuseFlushFileBuffers(LPCWSTR FileName, PDOKAN_FILE_INFO DokanFileInfo) { +FuseFlushFileBuffers(DOKAN_FLUSH_BUFFERS_EVENT *EventInfo) { + impl_fuse_context *impl = the_impl; + if (impl->debug()) - FPRINTF(stderr, "FlushFileBuffers: %ls\n", FileName); + FPRINTF(stderr, "FlushFileBuffers: %ls\n", EventInfo->FileName); + + impl_chain_guard guard(impl, EventInfo->DokanFileInfo->ProcessId); - impl_chain_guard guard(impl, DokanFileInfo->ProcessId); return errno_to_ntstatus_error( - impl->flush_file_buffers(FileName, DokanFileInfo)); + impl->flush_file_buffers(EventInfo->FileName, EventInfo->DokanFileInfo)); } -static NTSTATUS DOKAN_CALLBACK FuseGetFileInformation( - LPCWSTR FileName, LPBY_HANDLE_FILE_INFORMATION HandleFileInformation, - PDOKAN_FILE_INFO DokanFileInfo) { +static NTSTATUS DOKAN_CALLBACK FuseGetFileInformation(DOKAN_GET_FILE_INFO_EVENT *EventInfo) { + impl_fuse_context *impl = the_impl; + if (impl->debug()) - FPRINTF(stderr, "GetFileInfo: : %ls\n", FileName); + FPRINTF(stderr, "GetFileInfo: : %ls\n", EventInfo->FileName); + + impl_chain_guard guard(impl, EventInfo->DokanFileInfo->ProcessId); - impl_chain_guard guard(impl, DokanFileInfo->ProcessId); return errno_to_ntstatus_error(impl->get_file_information( - FileName, HandleFileInformation, DokanFileInfo)); + EventInfo->FileName, + &EventInfo->FileHandleInfo, + EventInfo->DokanFileInfo)); } -static NTSTATUS DOKAN_CALLBACK FuseDeleteFile(LPCWSTR FileName, - PDOKAN_FILE_INFO DokanFileInfo) { +static NTSTATUS DOKAN_CALLBACK FuseCanDeleteFile(DOKAN_CAN_DELETE_FILE_EVENT *EventInfo) { + impl_fuse_context *impl = the_impl; + if (impl->debug()) - FPRINTF(stderr, "DeleteFile: %ls\n", FileName); + FPRINTF(stderr, "DeleteFile: %ls\n", EventInfo->FileName); + + impl_chain_guard guard(impl, EventInfo->DokanFileInfo->ProcessId); - impl_chain_guard guard(impl, DokanFileInfo->ProcessId); - return errno_to_ntstatus_error(impl->delete_file(FileName, DokanFileInfo)); + return errno_to_ntstatus_error(impl->delete_file(EventInfo->FileName, EventInfo->DokanFileInfo)); } static NTSTATUS DOKAN_CALLBACK -FuseMoveFile(LPCWSTR FileName, // existing file name - LPCWSTR NewFileName, BOOL ReplaceIfExisting, - PDOKAN_FILE_INFO DokanFileInfo) { +FuseMoveFile(DOKAN_MOVE_FILE_EVENT *EventInfo) { + impl_fuse_context *impl = the_impl; if (impl->debug()) - FPRINTF(stderr, "MoveFile: %ls -> %ls\n\n", FileName, NewFileName); + FPRINTF(stderr, "MoveFile: %ls -> %ls\n\n", EventInfo->FileName, EventInfo->NewFileName); + + impl_chain_guard guard(impl, EventInfo->DokanFileInfo->ProcessId); - impl_chain_guard guard(impl, DokanFileInfo->ProcessId); return errno_to_ntstatus_error( - impl->move_file(FileName, NewFileName, ReplaceIfExisting, DokanFileInfo)); + impl->move_file(EventInfo->FileName, + EventInfo->NewFileName, + EventInfo->ReplaceIfExists, + EventInfo->DokanFileInfo)); } -static NTSTATUS DOKAN_CALLBACK FuseLockFile(LPCWSTR FileName, - LONGLONG ByteOffset, - LONGLONG Length, - PDOKAN_FILE_INFO DokanFileInfo) { +static NTSTATUS DOKAN_CALLBACK FuseLockFile(DOKAN_LOCK_FILE_EVENT *EventInfo) { + impl_fuse_context *impl = the_impl; + if (impl->debug()) - FPRINTF(stderr, "LockFile: %ls\n", FileName); + FPRINTF(stderr, "LockFile: %ls\n", EventInfo->FileName); + + impl_chain_guard guard(impl, EventInfo->DokanFileInfo->ProcessId); - impl_chain_guard guard(impl, DokanFileInfo->ProcessId); return errno_to_ntstatus_error( - impl->lock_file(FileName, ByteOffset, Length, DokanFileInfo)); + impl->lock_file(EventInfo->FileName, + EventInfo->ByteOffset, + EventInfo->Length, + EventInfo->DokanFileInfo)); } -static NTSTATUS DOKAN_CALLBACK FuseUnlockFile(LPCWSTR FileName, - LONGLONG ByteOffset, - LONGLONG Length, - PDOKAN_FILE_INFO DokanFileInfo) { +static NTSTATUS DOKAN_CALLBACK FuseUnlockFile(DOKAN_UNLOCK_FILE_EVENT *EventInfo) { + impl_fuse_context *impl = the_impl; + if (impl->debug()) - FPRINTF(stderr, "UnlockFile: %ls\n", FileName); + FPRINTF(stderr, "UnlockFile: %ls\n", EventInfo->FileName); + + impl_chain_guard guard(impl, EventInfo->DokanFileInfo->ProcessId); - impl_chain_guard guard(impl, DokanFileInfo->ProcessId); return errno_to_ntstatus_error( - impl->unlock_file(FileName, ByteOffset, Length, DokanFileInfo)); + impl->unlock_file(EventInfo->FileName, + EventInfo->ByteOffset, + EventInfo->Length, + EventInfo->DokanFileInfo)); } -static NTSTATUS DOKAN_CALLBACK FuseSetEndOfFile( - LPCWSTR FileName, LONGLONG ByteOffset, PDOKAN_FILE_INFO DokanFileInfo) { +static NTSTATUS DOKAN_CALLBACK FuseSetEndOfFile(DOKAN_SET_EOF_EVENT *EventInfo) { + impl_fuse_context *impl = the_impl; + if (impl->debug()) - FPRINTF(stderr, "SetEndOfFile: %ls, %lld\n", FileName, ByteOffset); + FPRINTF(stderr, "SetEndOfFile: %ls, %lld\n", EventInfo->FileName, EventInfo->Length); - impl_chain_guard guard(impl, DokanFileInfo->ProcessId); + impl_chain_guard guard(impl, EventInfo->DokanFileInfo->ProcessId); return errno_to_ntstatus_error( - impl->set_end_of_file(FileName, ByteOffset, DokanFileInfo)); + impl->set_end_of_file(EventInfo->FileName, EventInfo->Length, EventInfo->DokanFileInfo)); } -static NTSTATUS DOKAN_CALLBACK FuseSetAllocationSize( - LPCWSTR FileName, LONGLONG ByteOffset, PDOKAN_FILE_INFO DokanFileInfo) { +static NTSTATUS DOKAN_CALLBACK FuseSetAllocationSize(DOKAN_SET_ALLOCATION_SIZE_EVENT *EventInfo) { + impl_fuse_context *impl = the_impl; + if (impl->debug()) - FPRINTF(stderr, "SetAllocationSize: %ls, %lld\n", FileName, ByteOffset); + FPRINTF(stderr, "SetAllocationSize: %ls, %lld\n", EventInfo->FileName, EventInfo->Length); - impl_chain_guard guard(impl, DokanFileInfo->ProcessId); + impl_chain_guard guard(impl, EventInfo->DokanFileInfo->ProcessId); BY_HANDLE_FILE_INFORMATION byHandleFileInfo; ZeroMemory(&byHandleFileInfo, sizeof(BY_HANDLE_FILE_INFORMATION)); NTSTATUS ret = errno_to_ntstatus_error( - impl->get_file_information(FileName, &byHandleFileInfo, DokanFileInfo)); + impl->get_file_information(EventInfo->FileName, &byHandleFileInfo, EventInfo->DokanFileInfo)); LARGE_INTEGER fileSize; fileSize.LowPart = byHandleFileInfo.nFileSizeLow; @@ -346,109 +374,164 @@ static NTSTATUS DOKAN_CALLBACK FuseSetAllocationSize( if (ret != 0) { return ret; } - else if (ByteOffset < fileSize.QuadPart) { + else if (EventInfo->Length < fileSize.QuadPart) { /* https://msdn.microsoft.com/en-us/library/windows/hardware/ff540232(v=vs.85).aspx * The end-of-file position must always be less than or equal to the * allocation size. If the allocation size is set to a value that is * less than the end - of - file position, the end - of - file position * is automatically adjusted to match the allocation size.*/ return errno_to_ntstatus_error( - impl->set_end_of_file(FileName, ByteOffset, DokanFileInfo)); + impl->set_end_of_file(EventInfo->FileName, EventInfo->Length, EventInfo->DokanFileInfo)); } else { return 0; } } -static NTSTATUS DOKAN_CALLBACK FuseSetFileAttributes( - LPCWSTR FileName, DWORD FileAttributes, PDOKAN_FILE_INFO DokanFileInfo) { +static NTSTATUS DOKAN_CALLBACK FuseSetFileBasicInformation(DOKAN_SET_FILE_BASIC_INFO_EVENT *EventInfo) { + impl_fuse_context *impl = the_impl; + if (impl->debug()) - FPRINTF(stderr, "SetFileAttributes: %ls\n", FileName); + FPRINTF(stderr, "SetFileBasicInformation: %ls\n", EventInfo->FileName); - impl_chain_guard guard(impl, DokanFileInfo->ProcessId); - return errno_to_ntstatus_error( - impl->set_file_attributes(FileName, FileAttributes, DokanFileInfo)); -} + impl_chain_guard guard(impl, EventInfo->DokanFileInfo->ProcessId); -static NTSTATUS DOKAN_CALLBACK FuseSetFileTime(LPCWSTR FileName, - CONST FILETIME *CreationTime, - CONST FILETIME *LastAccessTime, - CONST FILETIME *LastWriteTime, - PDOKAN_FILE_INFO DokanFileInfo) { - impl_fuse_context *impl = the_impl; - if (impl->debug()) - FPRINTF(stderr, "SetFileTime: %ls\n", FileName); + NTSTATUS result = errno_to_ntstatus_error( + impl->set_file_attributes(EventInfo->FileName, EventInfo->Info->FileAttributes, EventInfo->DokanFileInfo)); + + if(result == STATUS_SUCCESS) + { + result = errno_to_ntstatus_error(impl->set_file_time( + EventInfo->FileName, + (const FILETIME*)&EventInfo->Info->CreationTime, + (const FILETIME*)&EventInfo->Info->LastAccessTime, + (const FILETIME*)&EventInfo->Info->LastWriteTime, + EventInfo->DokanFileInfo)); + } - impl_chain_guard guard(impl, DokanFileInfo->ProcessId); - return errno_to_ntstatus_error(impl->set_file_time( - FileName, CreationTime, LastAccessTime, LastWriteTime, DokanFileInfo)); + return result; } -static NTSTATUS DOKAN_CALLBACK FuseGetDiskFreeSpace( - PULONGLONG FreeBytesAvailable, PULONGLONG TotalNumberOfBytes, - PULONGLONG TotalNumberOfFreeBytes, PDOKAN_FILE_INFO DokanFileInfo) { +static NTSTATUS DOKAN_CALLBACK FuseGetVolumeFreeSpace(DOKAN_GET_DISK_FREE_SPACE_EVENT *EventInfo) { + impl_fuse_context *impl = the_impl; + if (impl->debug()) - FPRINTF(stderr, "GetDiskFreeSpace\n"); + FPRINTF(stderr, "GetVolumeFreeSpace\n"); + + impl_chain_guard guard(impl, EventInfo->DokanFileInfo->ProcessId); - impl_chain_guard guard(impl, DokanFileInfo->ProcessId); return errno_to_ntstatus_error( - impl->get_disk_free_space(FreeBytesAvailable, TotalNumberOfBytes, - TotalNumberOfFreeBytes, DokanFileInfo)); + impl->get_disk_free_space( + &EventInfo->FreeBytesAvailable, + &EventInfo->TotalNumberOfBytes, + &EventInfo->TotalNumberOfFreeBytes, + EventInfo->DokanFileInfo)); } static NTSTATUS DOKAN_CALLBACK -GetVolumeInformation(LPWSTR VolumeNameBuffer, DWORD VolumeNameSize, - LPDWORD VolumeSerialNumber, LPDWORD MaximumComponentLength, - LPDWORD FileSystemFlags, LPWSTR FileSystemNameBuffer, - DWORD FileSystemNameSize, PDOKAN_FILE_INFO DokanFileInfo) { +FuseGetVolumeInformation(DOKAN_GET_VOLUME_INFO_EVENT *EventInfo) { + impl_fuse_context *impl = the_impl; + if (impl->debug()) FPRINTF(stderr, "GetVolumeInformation\n"); - impl_chain_guard guard(impl, DokanFileInfo->ProcessId); - *VolumeSerialNumber = 0; - *MaximumComponentLength = 255; - return errno_to_ntstatus_error(impl->get_volume_information( - VolumeNameBuffer, VolumeNameSize, FileSystemNameBuffer, - FileSystemNameSize, DokanFileInfo, FileSystemFlags)); + impl_chain_guard guard(impl, EventInfo->DokanFileInfo->ProcessId); + + EventInfo->VolumeInfo->SupportsObjects = FALSE; + EventInfo->VolumeInfo->VolumeSerialNumber = 0; + + DWORD fileSystemFlags = 0; + WCHAR fileSystemName[64]; + + NTSTATUS result = errno_to_ntstatus_error(impl->get_volume_information( + EventInfo->VolumeInfo->VolumeLabel, + EventInfo->MaxLabelLengthInChars, + fileSystemName, + (DWORD)ARRAYSIZE(fileSystemName), + EventInfo->DokanFileInfo, + &fileSystemFlags)); + + if(result == STATUS_SUCCESS) + { + EventInfo->VolumeInfo->VolumeLabelLength = (ULONG)wcslen(EventInfo->VolumeInfo->VolumeLabel); + } + + return result; } -static NTSTATUS DOKAN_CALLBACK FuseMounted(PDOKAN_FILE_INFO DokanFileInfo) { - impl_fuse_context *impl = the_impl; +static NTSTATUS DOKAN_CALLBACK +FuseGetVolumeAttributes(DOKAN_GET_VOLUME_ATTRIBUTES_EVENT *EventInfo) { + + impl_fuse_context *impl = the_impl; + + if(impl->debug()) + FPRINTF(stderr, "GetVolumeAttributes\n"); + + impl_chain_guard guard(impl, EventInfo->DokanFileInfo->ProcessId); + + EventInfo->Attributes->MaximumComponentNameLength = 255; + + WCHAR volumeName[256]; + + NTSTATUS result = errno_to_ntstatus_error(impl->get_volume_information( + volumeName, + (DWORD)ARRAYSIZE(volumeName), + EventInfo->Attributes->FileSystemName, + EventInfo->MaxFileSystemNameLengthInChars, + EventInfo->DokanFileInfo, + &EventInfo->Attributes->FileSystemAttributes)); + + if(result == STATUS_SUCCESS) + { + EventInfo->Attributes->FileSystemNameLength = (ULONG)wcslen(EventInfo->Attributes->FileSystemName); + } + + return result; +} + +static void DOKAN_CALLBACK FuseMounted(DOKAN_MOUNTED_INFO *EventInfo) { + + impl_fuse_context *impl = reinterpret_cast(EventInfo->DokanOptions->GlobalContext); + if (impl->debug()) FPRINTF(stderr, "Mounted\n"); - impl_chain_guard guard(impl, DokanFileInfo->ProcessId); - return errno_to_ntstatus_error(impl->mounted(DokanFileInfo)); + impl_chain_guard guard(impl, 0); + + impl->mounted(EventInfo); } -static NTSTATUS DOKAN_CALLBACK FuseUnmounted(PDOKAN_FILE_INFO DokanFileInfo) { - impl_fuse_context *impl = the_impl; +static void DOKAN_CALLBACK FuseUnmounted(DOKAN_UNMOUNTED_INFO *EventInfo) { + + impl_fuse_context *impl = reinterpret_cast(EventInfo->DokanOptions->GlobalContext); + if (impl->debug()) FPRINTF(stderr, "Unmount\n"); - impl_chain_guard guard(impl, DokanFileInfo->ProcessId); - return errno_to_ntstatus_error(impl->unmounted(DokanFileInfo)); + impl_chain_guard guard(impl, 0); + + impl->unmounted(EventInfo); } static NTSTATUS DOKAN_CALLBACK -FuseGetFileSecurity(LPCWSTR FileName, PSECURITY_INFORMATION SecurityInformation, - PSECURITY_DESCRIPTOR SecurityDescriptor, ULONG BufferLength, - PULONG LengthNeeded, PDOKAN_FILE_INFO DokanFileInfo) { +FuseGetFileSecurity(DOKAN_GET_FILE_SECURITY_EVENT *EventInfo) { + impl_fuse_context *impl = the_impl; + if (impl->debug()) - FPRINTF(stderr, "GetFileSecurity: %x\n", *SecurityInformation); + FPRINTF(stderr, "GetFileSecurity: %x\n", *EventInfo->SecurityInformation); BY_HANDLE_FILE_INFORMATION byHandleFileInfo; ZeroMemory(&byHandleFileInfo, sizeof(BY_HANDLE_FILE_INFORMATION)); int ret; { - impl_chain_guard guard(impl, DokanFileInfo->ProcessId); + impl_chain_guard guard(impl, EventInfo->DokanFileInfo->ProcessId); ret = - impl->get_file_information(FileName, &byHandleFileInfo, DokanFileInfo); + impl->get_file_information(EventInfo->FileName, &byHandleFileInfo, EventInfo->DokanFileInfo); } if (0 != ret) { @@ -462,20 +545,20 @@ FuseGetFileSecurity(LPCWSTR FileName, PSECURITY_INFORMATION SecurityInformation, // SDDL used by dokan driver if (!ConvertStringSecurityDescriptorToSecurityDescriptor( "D:P(A;;GA;;;SY)(A;;GRGWGX;;;BA)(A;;GRGWGX;;;WD)(A;;GRGX;;;RC)", - SDDL_REVISION_1, &SecurityDescriptor, &BufferLength)) { + SDDL_REVISION_1, &EventInfo->SecurityDescriptor, &EventInfo->SecurityDescriptorSize)) { return STATUS_NOT_IMPLEMENTED; } LPTSTR pStringBuffer = NULL; if (!ConvertSecurityDescriptorToStringSecurityDescriptor( - SecurityDescriptor, SDDL_REVISION_1, *SecurityInformation, + EventInfo->SecurityDescriptor, SDDL_REVISION_1, *EventInfo->SecurityInformation, &pStringBuffer, NULL)) { return STATUS_NOT_IMPLEMENTED; } if (!ConvertStringSecurityDescriptorToSecurityDescriptor( - pStringBuffer, SDDL_REVISION_1, &SecurityDescriptor, - &BufferLength)) { + pStringBuffer, SDDL_REVISION_1, &EventInfo->SecurityDescriptor, + &EventInfo->SecurityDescriptorSize)) { return STATUS_NOT_IMPLEMENTED; } @@ -502,21 +585,21 @@ static DOKAN_OPERATIONS dokanOperations = { FuseGetFileInformation, FuseFindFiles, NULL, // FindFilesWithPattern - FuseSetFileAttributes, - FuseSetFileTime, - FuseDeleteFile, - FuseDeleteDirectory, + FuseSetFileBasicInformation, + FuseCanDeleteFile, FuseMoveFile, FuseSetEndOfFile, FuseSetAllocationSize, FuseLockFile, FuseUnlockFile, - FuseGetDiskFreeSpace, - GetVolumeInformation, + FuseGetVolumeFreeSpace, + FuseGetVolumeInformation, + FuseGetVolumeAttributes, FuseMounted, FuseUnmounted, FuseGetFileSecurity, NULL, // SetFileSecurity + NULL, // FindStreams }; int do_fuse_loop(struct fuse *fs, bool mt) { diff --git a/dokan_fuse/src/fusemain.cpp b/dokan_fuse/src/fusemain.cpp index b16a7c095..e4703d731 100644 --- a/dokan_fuse/src/fusemain.cpp +++ b/dokan_fuse/src/fusemain.cpp @@ -306,7 +306,7 @@ int impl_fuse_context::walk_directory(void *buf, const char *name, if (attrs != 0xFFFFFFFFu) find_data.dwFileAttributes = attrs; - return wd->delegate(&find_data, wd->DokanFileInfo); + return wd->eventInfo->FillFindData(wd->eventInfo, &find_data); } int impl_fuse_context::walk_directory_getdir(fuse_dirh_t hndl, const char *name, @@ -316,26 +316,28 @@ int impl_fuse_context::walk_directory_getdir(fuse_dirh_t hndl, const char *name, return 0; // Get more entries } -int impl_fuse_context::find_files(LPCWSTR file_name, - PFillFindData fill_find_data, - PDOKAN_FILE_INFO dokan_file_info) { +int impl_fuse_context::find_files(PDOKAN_FIND_FILES_EVENT EventInfo) { + if ((!ops_.readdir && !ops_.getdir) || !ops_.getattr) return -EINVAL; - std::string fname = unixify(wchar_to_utf8_cstr(file_name)); + std::string fname = unixify(wchar_to_utf8_cstr(EventInfo->PathName)); CHECKED(check_and_resolve(&fname)); walk_data wd; wd.ctx = this; wd.dirname = fname; - if (*fname.rbegin() != '/') - wd.dirname.append("/"); - wd.delegate = fill_find_data; - wd.DokanFileInfo = dokan_file_info; + + if(*fname.rbegin() != '/') { + + wd.dirname.append("/"); + } + + wd.eventInfo = EventInfo; if (ops_.readdir) { impl_file_handle *hndl = - reinterpret_cast(dokan_file_info->Context); + reinterpret_cast(EventInfo->DokanFileInfo->Context); if (hndl != NULL) { fuse_file_info finfo(hndl->make_finfo()); return ops_.readdir(fname.c_str(), &wd, &walk_directory, 0, &finfo); @@ -910,11 +912,11 @@ int impl_fuse_context::get_volume_information(LPWSTR volume_name_buffer, return 0; } -int impl_fuse_context::mounted(PDOKAN_FILE_INFO DokanFileInfo) { +int impl_fuse_context::mounted(DOKAN_MOUNTED_INFO *EventInfo) { return 0; } -int impl_fuse_context::unmounted(PDOKAN_FILE_INFO DokanFileInfo) { +int impl_fuse_context::unmounted(DOKAN_UNMOUNTED_INFO *EventInfo) { if (ops_.destroy) ops_.destroy(user_data_); // Ignoring result return 0; diff --git a/samples/dokan_mirror/mirror.c b/samples/dokan_mirror/mirror.c index 06ad4686f..b31e65319 100644 --- a/samples/dokan_mirror/mirror.c +++ b/samples/dokan_mirror/mirror.c @@ -31,35 +31,51 @@ THE SOFTWARE. #include #include #include +#include BOOL g_UseStdErr; BOOL g_DebugMode; static void DbgPrint(LPCWSTR format, ...) { + if (g_DebugMode) { + const WCHAR *outputString; WCHAR *buffer = NULL; size_t length; va_list argp; va_start(argp, format); + length = _vscwprintf(format, argp) + 1; + buffer = _malloca(length * sizeof(WCHAR)); + if (buffer) { + vswprintf_s(buffer, length, format, argp); outputString = buffer; - } else { + } + else { + outputString = format; } - if (g_UseStdErr) - fputws(outputString, stderr); - else - OutputDebugStringW(outputString); - if (buffer) - _freea(buffer); + + OutputDebugStringW(outputString); + + if(g_UseStdErr) { + + fputws(outputString, stderr); + fflush(stderr); + } + + + if(buffer) { + + _freea(buffer); + } + va_end(argp); - if (g_UseStdErr) - fflush(stderr); } } @@ -81,7 +97,7 @@ static void GetFilePath(PWCHAR filePath, ULONG numberOfElements, } } -static void PrintUserName(PDOKAN_FILE_INFO DokanFileInfo) { +static void PrintUserName(DOKAN_CREATE_FILE_EVENT *EventInfo) { HANDLE handle; UCHAR buffer[1024]; DWORD returnLength; @@ -92,7 +108,7 @@ static void PrintUserName(PDOKAN_FILE_INFO DokanFileInfo) { PTOKEN_USER tokenUser; SID_NAME_USE snu; - handle = DokanOpenRequestorToken(DokanFileInfo); + handle = DokanOpenRequestorToken(EventInfo); if (handle == INVALID_HANDLE_VALUE) { DbgPrint(L" DokanOpenRequestorToken failed\n"); return; @@ -181,10 +197,8 @@ static BOOL AddSeSecurityNamePrivilege() { } static NTSTATUS DOKAN_CALLBACK -MirrorCreateFile(LPCWSTR FileName, PDOKAN_IO_SECURITY_CONTEXT SecurityContext, - ACCESS_MASK DesiredAccess, ULONG FileAttributes, - ULONG ShareAccess, ULONG CreateDisposition, - ULONG CreateOptions, PDOKAN_FILE_INFO DokanFileInfo) { +MirrorCreateFile(DOKAN_CREATE_FILE_EVENT *EventInfo) { + WCHAR filePath[MAX_PATH]; HANDLE handle; DWORD fileAttr; @@ -196,18 +210,22 @@ MirrorCreateFile(LPCWSTR FileName, PDOKAN_IO_SECURITY_CONTEXT SecurityContext, securityAttrib.nLength = sizeof(securityAttrib); securityAttrib.lpSecurityDescriptor = - SecurityContext->AccessState.SecurityDescriptor; + EventInfo->SecurityContext.AccessState.SecurityDescriptor; securityAttrib.bInheritHandle = FALSE; + DokanMapKernelToUserCreateFileFlags( - FileAttributes, CreateOptions, CreateDisposition, &fileAttributesAndFlags, + EventInfo->FileAttributes, + EventInfo->CreateOptions, + EventInfo->CreateDisposition, + &fileAttributesAndFlags, &creationDisposition); - GetFilePath(filePath, MAX_PATH, FileName); + GetFilePath(filePath, MAX_PATH, EventInfo->FileName); DbgPrint(L"CreateFile : %s\n", filePath); - PrintUserName(DokanFileInfo); + PrintUserName(EventInfo); /* if (ShareMode == 0 && AccessMode & FILE_WRITE_DATA) @@ -216,46 +234,48 @@ MirrorCreateFile(LPCWSTR FileName, PDOKAN_IO_SECURITY_CONTEXT SecurityContext, ShareMode = FILE_SHARE_READ; */ - DbgPrint(L"\tShareMode = 0x%x\n", ShareAccess); - - MirrorCheckFlag(ShareAccess, FILE_SHARE_READ); - MirrorCheckFlag(ShareAccess, FILE_SHARE_WRITE); - MirrorCheckFlag(ShareAccess, FILE_SHARE_DELETE); - - DbgPrint(L"\tAccessMode = 0x%x\n", DesiredAccess); - - MirrorCheckFlag(DesiredAccess, GENERIC_READ); - MirrorCheckFlag(DesiredAccess, GENERIC_WRITE); - MirrorCheckFlag(DesiredAccess, GENERIC_EXECUTE); - - MirrorCheckFlag(DesiredAccess, DELETE); - MirrorCheckFlag(DesiredAccess, FILE_READ_DATA); - MirrorCheckFlag(DesiredAccess, FILE_READ_ATTRIBUTES); - MirrorCheckFlag(DesiredAccess, FILE_READ_EA); - MirrorCheckFlag(DesiredAccess, READ_CONTROL); - MirrorCheckFlag(DesiredAccess, FILE_WRITE_DATA); - MirrorCheckFlag(DesiredAccess, FILE_WRITE_ATTRIBUTES); - MirrorCheckFlag(DesiredAccess, FILE_WRITE_EA); - MirrorCheckFlag(DesiredAccess, FILE_APPEND_DATA); - MirrorCheckFlag(DesiredAccess, WRITE_DAC); - MirrorCheckFlag(DesiredAccess, WRITE_OWNER); - MirrorCheckFlag(DesiredAccess, SYNCHRONIZE); - MirrorCheckFlag(DesiredAccess, FILE_EXECUTE); - MirrorCheckFlag(DesiredAccess, STANDARD_RIGHTS_READ); - MirrorCheckFlag(DesiredAccess, STANDARD_RIGHTS_WRITE); - MirrorCheckFlag(DesiredAccess, STANDARD_RIGHTS_EXECUTE); + DbgPrint(L"\tShareMode = 0x%x\n", EventInfo->ShareAccess); + + MirrorCheckFlag(EventInfo->ShareAccess, FILE_SHARE_READ); + MirrorCheckFlag(EventInfo->ShareAccess, FILE_SHARE_WRITE); + MirrorCheckFlag(EventInfo->ShareAccess, FILE_SHARE_DELETE); + + DbgPrint(L"\tAccessMode = 0x%x\n", EventInfo->DesiredAccess); + + MirrorCheckFlag(EventInfo->DesiredAccess, GENERIC_READ); + MirrorCheckFlag(EventInfo->DesiredAccess, GENERIC_WRITE); + MirrorCheckFlag(EventInfo->DesiredAccess, GENERIC_EXECUTE); + + MirrorCheckFlag(EventInfo->DesiredAccess, DELETE); + MirrorCheckFlag(EventInfo->DesiredAccess, FILE_READ_DATA); + MirrorCheckFlag(EventInfo->DesiredAccess, FILE_READ_ATTRIBUTES); + MirrorCheckFlag(EventInfo->DesiredAccess, FILE_READ_EA); + MirrorCheckFlag(EventInfo->DesiredAccess, READ_CONTROL); + MirrorCheckFlag(EventInfo->DesiredAccess, FILE_WRITE_DATA); + MirrorCheckFlag(EventInfo->DesiredAccess, FILE_WRITE_ATTRIBUTES); + MirrorCheckFlag(EventInfo->DesiredAccess, FILE_WRITE_EA); + MirrorCheckFlag(EventInfo->DesiredAccess, FILE_APPEND_DATA); + MirrorCheckFlag(EventInfo->DesiredAccess, WRITE_DAC); + MirrorCheckFlag(EventInfo->DesiredAccess, WRITE_OWNER); + MirrorCheckFlag(EventInfo->DesiredAccess, SYNCHRONIZE); + MirrorCheckFlag(EventInfo->DesiredAccess, FILE_EXECUTE); + MirrorCheckFlag(EventInfo->DesiredAccess, STANDARD_RIGHTS_READ); + MirrorCheckFlag(EventInfo->DesiredAccess, STANDARD_RIGHTS_WRITE); + MirrorCheckFlag(EventInfo->DesiredAccess, STANDARD_RIGHTS_EXECUTE); // When filePath is a directory, needs to change the flag so that the file can // be opened. fileAttr = GetFileAttributes(filePath); - if (fileAttr != INVALID_FILE_ATTRIBUTES && - (fileAttr & FILE_ATTRIBUTE_DIRECTORY) && - !(CreateOptions & FILE_NON_DIRECTORY_FILE)) { - DokanFileInfo->IsDirectory = TRUE; - if (DesiredAccess & DELETE) { - // Needed by FindFirstFile to see if directory is empty or not - ShareAccess |= FILE_SHARE_READ; + if (fileAttr != INVALID_FILE_ATTRIBUTES + && (fileAttr & FILE_ATTRIBUTE_DIRECTORY) + && !(EventInfo->CreateOptions & FILE_NON_DIRECTORY_FILE)) { + + EventInfo->DokanFileInfo->IsDirectory = TRUE; + + if (EventInfo->DesiredAccess & DELETE) { + // Needed by FindFirstFile to see if directory is empty or not + EventInfo->ShareAccess |= FILE_SHARE_READ; } } @@ -302,7 +322,7 @@ MirrorCreateFile(LPCWSTR FileName, PDOKAN_IO_SECURITY_CONTEXT SecurityContext, DbgPrint(L"\tUNKNOWN creationDisposition!\n"); } - if (DokanFileInfo->IsDirectory) { + if (EventInfo->DokanFileInfo->IsDirectory) { // It is a create directory request if (creationDisposition == CREATE_NEW) { if (!CreateDirectory(filePath, &securityAttrib)) { @@ -325,7 +345,7 @@ MirrorCreateFile(LPCWSTR FileName, PDOKAN_IO_SECURITY_CONTEXT SecurityContext, if (status == STATUS_SUCCESS) { // FILE_FLAG_BACKUP_SEMANTICS is required for opening directory handles handle = CreateFile( - filePath, DesiredAccess, ShareAccess, &securityAttrib, OPEN_EXISTING, + filePath, EventInfo->DesiredAccess, EventInfo->ShareAccess, &securityAttrib, OPEN_EXISTING, fileAttributesAndFlags | FILE_FLAG_BACKUP_SEMANTICS, NULL); if (handle == INVALID_HANDLE_VALUE) { @@ -334,7 +354,7 @@ MirrorCreateFile(LPCWSTR FileName, PDOKAN_IO_SECURITY_CONTEXT SecurityContext, status = DokanNtStatusFromWin32(error); } else { - DokanFileInfo->Context = + EventInfo->DokanFileInfo->Context = (ULONG64)handle; // save the file handle in Context } } @@ -343,13 +363,13 @@ MirrorCreateFile(LPCWSTR FileName, PDOKAN_IO_SECURITY_CONTEXT SecurityContext, if (fileAttr != INVALID_FILE_ATTRIBUTES && (fileAttr & FILE_ATTRIBUTE_DIRECTORY) && - CreateDisposition == FILE_CREATE) + EventInfo->CreateDisposition == FILE_CREATE) return STATUS_OBJECT_NAME_COLLISION; // File already exist because // GetFileAttributes found it handle = CreateFile(filePath, - DesiredAccess, // GENERIC_READ|GENERIC_WRITE|GENERIC_EXECUTE, - ShareAccess, + EventInfo->DesiredAccess, // GENERIC_READ|GENERIC_WRITE|GENERIC_EXECUTE, + EventInfo->ShareAccess, &securityAttrib, // security attribute creationDisposition, fileAttributesAndFlags, // |FILE_FLAG_NO_BUFFERING, @@ -361,7 +381,7 @@ MirrorCreateFile(LPCWSTR FileName, PDOKAN_IO_SECURITY_CONTEXT SecurityContext, status = DokanNtStatusFromWin32(error); } else { - DokanFileInfo->Context = + EventInfo->DokanFileInfo->Context = (ULONG64)handle; // save the file handle in Context if (creationDisposition == OPEN_ALWAYS || @@ -384,39 +404,37 @@ MirrorCreateFile(LPCWSTR FileName, PDOKAN_IO_SECURITY_CONTEXT SecurityContext, #pragma warning(push) #pragma warning(disable : 4305) -static void DOKAN_CALLBACK MirrorCloseFile(LPCWSTR FileName, - PDOKAN_FILE_INFO DokanFileInfo) { +static void DOKAN_CALLBACK MirrorCloseFile(DOKAN_CLOSE_FILE_EVENT *EventInfo) { WCHAR filePath[MAX_PATH]; - GetFilePath(filePath, MAX_PATH, FileName); + GetFilePath(filePath, MAX_PATH, EventInfo->FileName); - if (DokanFileInfo->Context) { + if (EventInfo->DokanFileInfo->Context) { DbgPrint(L"CloseFile: %s\n", filePath); DbgPrint(L"\terror : not cleanuped file\n\n"); - CloseHandle((HANDLE)DokanFileInfo->Context); - DokanFileInfo->Context = 0; + CloseHandle((HANDLE)EventInfo->DokanFileInfo->Context); + EventInfo->DokanFileInfo->Context = 0; } else { DbgPrint(L"Close: %s\n\n", filePath); } } -static void DOKAN_CALLBACK MirrorCleanup(LPCWSTR FileName, - PDOKAN_FILE_INFO DokanFileInfo) { +static void DOKAN_CALLBACK MirrorCleanup(DOKAN_CLEANUP_EVENT *EventInfo) { WCHAR filePath[MAX_PATH]; - GetFilePath(filePath, MAX_PATH, FileName); + GetFilePath(filePath, MAX_PATH, EventInfo->FileName); - if (DokanFileInfo->Context) { + if (EventInfo->DokanFileInfo->Context) { DbgPrint(L"Cleanup: %s\n\n", filePath); - CloseHandle((HANDLE)(DokanFileInfo->Context)); - DokanFileInfo->Context = 0; + CloseHandle((HANDLE)(EventInfo->DokanFileInfo->Context)); + EventInfo->DokanFileInfo->Context = 0; } else { DbgPrint(L"Cleanup: %s\n\tinvalid handle\n\n", filePath); } - if (DokanFileInfo->DeleteOnClose) { + if (EventInfo->DokanFileInfo->DeleteOnClose) { // Should already be deleted by CloseHandle // if open with FILE_FLAG_DELETE_ON_CLOSE DbgPrint(L"\tDeleteOnClose\n"); - if (DokanFileInfo->IsDirectory) { + if (EventInfo->DokanFileInfo->IsDirectory) { DbgPrint(L" DeleteDirectory "); if (!RemoveDirectory(filePath)) { DbgPrint(L"error code = %d\n\n", GetLastError()); @@ -434,17 +452,14 @@ static void DOKAN_CALLBACK MirrorCleanup(LPCWSTR FileName, } } -static NTSTATUS DOKAN_CALLBACK MirrorReadFile(LPCWSTR FileName, LPVOID Buffer, - DWORD BufferLength, - LPDWORD ReadLength, - LONGLONG Offset, - PDOKAN_FILE_INFO DokanFileInfo) { +static NTSTATUS DOKAN_CALLBACK MirrorReadFile(DOKAN_READ_FILE_EVENT *EventInfo) { + WCHAR filePath[MAX_PATH]; - HANDLE handle = (HANDLE)DokanFileInfo->Context; - ULONG offset = (ULONG)Offset; + HANDLE handle = (HANDLE)EventInfo->DokanFileInfo->Context; + ULONG offset = (ULONG)EventInfo->Offset; BOOL opened = FALSE; - GetFilePath(filePath, MAX_PATH, FileName); + GetFilePath(filePath, MAX_PATH, EventInfo->FileName); DbgPrint(L"ReadFile : %s\n", filePath); @@ -461,7 +476,7 @@ static NTSTATUS DOKAN_CALLBACK MirrorReadFile(LPCWSTR FileName, LPVOID Buffer, } LARGE_INTEGER distanceToMove; - distanceToMove.QuadPart = Offset; + distanceToMove.QuadPart = EventInfo->Offset; if (!SetFilePointerEx(handle, distanceToMove, NULL, FILE_BEGIN)) { DWORD error = GetLastError(); DbgPrint(L"\tseek error, offset = %d\n\n", offset); @@ -470,17 +485,17 @@ static NTSTATUS DOKAN_CALLBACK MirrorReadFile(LPCWSTR FileName, LPVOID Buffer, return DokanNtStatusFromWin32(error); } - if (!ReadFile(handle, Buffer, BufferLength, ReadLength, NULL)) { + if (!ReadFile(handle, EventInfo->Buffer, EventInfo->NumberOfBytesToRead, &EventInfo->NumberOfBytesRead, NULL)) { DWORD error = GetLastError(); DbgPrint(L"\tread error = %u, buffer length = %d, read length = %d\n\n", - error, BufferLength, *ReadLength); + error, EventInfo->NumberOfBytesToRead, EventInfo->NumberOfBytesRead); if (opened) CloseHandle(handle); return DokanNtStatusFromWin32(error); } else { - DbgPrint(L"\tByte to read: %d, Byte read %d, offset %d\n\n", BufferLength, - *ReadLength, offset); + DbgPrint(L"\tByte to read: %d, Byte read %d, offset %d\n\n", EventInfo->NumberOfBytesToRead, + EventInfo->NumberOfBytesRead, offset); } if (opened) @@ -489,20 +504,17 @@ static NTSTATUS DOKAN_CALLBACK MirrorReadFile(LPCWSTR FileName, LPVOID Buffer, return STATUS_SUCCESS; } -static NTSTATUS DOKAN_CALLBACK MirrorWriteFile(LPCWSTR FileName, LPCVOID Buffer, - DWORD NumberOfBytesToWrite, - LPDWORD NumberOfBytesWritten, - LONGLONG Offset, - PDOKAN_FILE_INFO DokanFileInfo) { +static NTSTATUS DOKAN_CALLBACK MirrorWriteFile(DOKAN_WRITE_FILE_EVENT *EventInfo) { + WCHAR filePath[MAX_PATH]; - HANDLE handle = (HANDLE)DokanFileInfo->Context; - ULONG offset = (ULONG)Offset; + HANDLE handle = (HANDLE)EventInfo->DokanFileInfo->Context; + ULONG offset = (ULONG)EventInfo->Offset; BOOL opened = FALSE; - GetFilePath(filePath, MAX_PATH, FileName); + GetFilePath(filePath, MAX_PATH, EventInfo->FileName); - DbgPrint(L"WriteFile : %s, offset %I64d, length %d\n", filePath, Offset, - NumberOfBytesToWrite); + DbgPrint(L"WriteFile : %s, offset %I64d, length %d\n", filePath, EventInfo->Offset, + EventInfo->NumberOfBytesToWrite); // reopen the file if (!handle || handle == INVALID_HANDLE_VALUE) { @@ -518,9 +530,9 @@ static NTSTATUS DOKAN_CALLBACK MirrorWriteFile(LPCWSTR FileName, LPCVOID Buffer, } LARGE_INTEGER distanceToMove; - distanceToMove.QuadPart = Offset; + distanceToMove.QuadPart = EventInfo->Offset; - if (DokanFileInfo->WriteToEndOfFile) { + if (EventInfo->DokanFileInfo->WriteToEndOfFile) { LARGE_INTEGER z; z.QuadPart = 0; if (!SetFilePointerEx(handle, z, NULL, FILE_END)) { @@ -538,17 +550,17 @@ static NTSTATUS DOKAN_CALLBACK MirrorWriteFile(LPCWSTR FileName, LPCVOID Buffer, return DokanNtStatusFromWin32(error); } - if (!WriteFile(handle, Buffer, NumberOfBytesToWrite, NumberOfBytesWritten, + if (!WriteFile(handle, EventInfo->Buffer, EventInfo->NumberOfBytesToWrite, &EventInfo->NumberOfBytesWritten, NULL)) { DWORD error = GetLastError(); DbgPrint(L"\twrite error = %u, buffer length = %d, write length = %d\n", - error, NumberOfBytesToWrite, *NumberOfBytesWritten); + error, EventInfo->NumberOfBytesToWrite, EventInfo->NumberOfBytesWritten); if (opened) CloseHandle(handle); return DokanNtStatusFromWin32(error); } else { - DbgPrint(L"\twrite %d, offset %d\n\n", *NumberOfBytesWritten, offset); + DbgPrint(L"\twrite %d, offset %d\n\n", EventInfo->NumberOfBytesWritten, offset); } // close the file when it is reopened @@ -559,11 +571,12 @@ static NTSTATUS DOKAN_CALLBACK MirrorWriteFile(LPCWSTR FileName, LPCVOID Buffer, } static NTSTATUS DOKAN_CALLBACK -MirrorFlushFileBuffers(LPCWSTR FileName, PDOKAN_FILE_INFO DokanFileInfo) { +MirrorFlushFileBuffers(DOKAN_FLUSH_BUFFERS_EVENT *EventInfo) { + WCHAR filePath[MAX_PATH]; - HANDLE handle = (HANDLE)DokanFileInfo->Context; + HANDLE handle = (HANDLE)EventInfo->DokanFileInfo->Context; - GetFilePath(filePath, MAX_PATH, FileName); + GetFilePath(filePath, MAX_PATH, EventInfo->FileName); DbgPrint(L"FlushFileBuffers : %s\n", filePath); @@ -581,13 +594,12 @@ MirrorFlushFileBuffers(LPCWSTR FileName, PDOKAN_FILE_INFO DokanFileInfo) { } } -static NTSTATUS DOKAN_CALLBACK MirrorGetFileInformation( - LPCWSTR FileName, LPBY_HANDLE_FILE_INFORMATION HandleFileInformation, - PDOKAN_FILE_INFO DokanFileInfo) { +static NTSTATUS DOKAN_CALLBACK MirrorGetFileInformation(DOKAN_GET_FILE_INFO_EVENT *EventInfo) { + WCHAR filePath[MAX_PATH]; - HANDLE handle = (HANDLE)DokanFileInfo->Context; + HANDLE handle = (HANDLE)EventInfo->DokanFileInfo->Context; - GetFilePath(filePath, MAX_PATH, FileName); + GetFilePath(filePath, MAX_PATH, EventInfo->FileName); DbgPrint(L"GetFileInfo : %s\n", filePath); @@ -596,14 +608,14 @@ static NTSTATUS DOKAN_CALLBACK MirrorGetFileInformation( return STATUS_INVALID_PARAMETER; } - if (!GetFileInformationByHandle(handle, HandleFileInformation)) { + if (!GetFileInformationByHandle(handle, &EventInfo->FileHandleInfo)) { DbgPrint(L"\terror code = %d\n", GetLastError()); // FileName is a root directory // in this case, FindFirstFile can't get directory information - if (wcslen(FileName) == 1) { + if (wcslen(EventInfo->FileName) == 1) { DbgPrint(L" root dir\n"); - HandleFileInformation->dwFileAttributes = GetFileAttributes(filePath); + EventInfo->FileHandleInfo.dwFileAttributes = GetFileAttributes(filePath); } else { WIN32_FIND_DATAW find; @@ -614,18 +626,20 @@ static NTSTATUS DOKAN_CALLBACK MirrorGetFileInformation( DbgPrint(L"\tFindFirstFile error code = %d\n\n", error); return DokanNtStatusFromWin32(error); } - HandleFileInformation->dwFileAttributes = find.dwFileAttributes; - HandleFileInformation->ftCreationTime = find.ftCreationTime; - HandleFileInformation->ftLastAccessTime = find.ftLastAccessTime; - HandleFileInformation->ftLastWriteTime = find.ftLastWriteTime; - HandleFileInformation->nFileSizeHigh = find.nFileSizeHigh; - HandleFileInformation->nFileSizeLow = find.nFileSizeLow; + EventInfo->FileHandleInfo.dwFileAttributes = find.dwFileAttributes; + EventInfo->FileHandleInfo.ftCreationTime = find.ftCreationTime; + EventInfo->FileHandleInfo.ftLastAccessTime = find.ftLastAccessTime; + EventInfo->FileHandleInfo.ftLastWriteTime = find.ftLastWriteTime; + EventInfo->FileHandleInfo.nFileSizeHigh = find.nFileSizeHigh; + EventInfo->FileHandleInfo.nFileSizeLow = find.nFileSizeLow; + DbgPrint(L"\tFindFiles OK, file size = %d\n", find.nFileSizeLow); + FindClose(findHandle); } } else { DbgPrint(L"\tGetFileInformationByHandle success, file size = %d\n", - HandleFileInformation->nFileSizeLow); + EventInfo->FileHandleInfo.nFileSizeLow); } DbgPrint(L"\n"); @@ -634,9 +648,8 @@ static NTSTATUS DOKAN_CALLBACK MirrorGetFileInformation( } static NTSTATUS DOKAN_CALLBACK -MirrorFindFiles(LPCWSTR FileName, - PFillFindData FillFindData, // function pointer - PDOKAN_FILE_INFO DokanFileInfo) { +MirrorFindFiles(DOKAN_FIND_FILES_EVENT *EventInfo) { + WCHAR filePath[MAX_PATH]; size_t fileLen; HANDLE hFind; @@ -644,7 +657,7 @@ MirrorFindFiles(LPCWSTR FileName, DWORD error; int count = 0; - GetFilePath(filePath, MAX_PATH, FileName); + GetFilePath(filePath, MAX_PATH, EventInfo->PathName); DbgPrint(L"FindFiles :%s\n", filePath); @@ -664,12 +677,17 @@ MirrorFindFiles(LPCWSTR FileName, } // Root folder does not have . and .. folder - we remove them - BOOLEAN rootFolder = (wcscmp(FileName, L"\\") == 0); + BOOLEAN rootFolder = (wcscmp(EventInfo->PathName, L"\\") == 0); + do { - if (!rootFolder || (wcscmp(findData.cFileName, L".") != 0 && - wcscmp(findData.cFileName, L"..") != 0)) - FillFindData(&findData, DokanFileInfo); + if(!rootFolder || (wcscmp(findData.cFileName, L".") != 0 && + wcscmp(findData.cFileName, L"..") != 0)) + { + EventInfo->FillFindData(EventInfo, &findData); + } + count++; + } while (FindNextFile(hFind, &findData) != 0); error = GetLastError(); @@ -686,79 +704,28 @@ MirrorFindFiles(LPCWSTR FileName, } static NTSTATUS DOKAN_CALLBACK -MirrorDeleteFile(LPCWSTR FileName, PDOKAN_FILE_INFO DokanFileInfo) { - UNREFERENCED_PARAMETER(DokanFileInfo); +MirrorCanDeleteFile(DOKAN_CAN_DELETE_FILE_EVENT *EventInfo) { WCHAR filePath[MAX_PATH]; // HANDLE handle = (HANDLE)DokanFileInfo->Context; - GetFilePath(filePath, MAX_PATH, FileName); + GetFilePath(filePath, MAX_PATH, EventInfo->FileName); DbgPrint(L"DeleteFile %s\n", filePath); DWORD dwAttrib = GetFileAttributes(filePath); - if (dwAttrib != INVALID_FILE_ATTRIBUTES && - (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) - return STATUS_ACCESS_DENIED; - - return STATUS_SUCCESS; -} - -static NTSTATUS DOKAN_CALLBACK -MirrorDeleteDirectory(LPCWSTR FileName, PDOKAN_FILE_INFO DokanFileInfo) { - UNREFERENCED_PARAMETER(DokanFileInfo); - - WCHAR filePath[MAX_PATH]; - // HANDLE handle = (HANDLE)DokanFileInfo->Context; - HANDLE hFind; - WIN32_FIND_DATAW findData; - size_t fileLen; - - ZeroMemory(filePath, sizeof(filePath)); - GetFilePath(filePath, MAX_PATH, FileName); - - DbgPrint(L"DeleteDirectory %s\n", filePath); - - fileLen = wcslen(filePath); - if (filePath[fileLen - 1] != L'\\') { - filePath[fileLen++] = L'\\'; - } - filePath[fileLen] = L'*'; - filePath[fileLen + 1] = L'\0'; - - hFind = FindFirstFile(filePath, &findData); - - if (hFind == INVALID_HANDLE_VALUE) { - DWORD error = GetLastError(); - DbgPrint(L"\tDeleteDirectory error code = %d\n\n", error); - return DokanNtStatusFromWin32(error); + if(dwAttrib != INVALID_FILE_ATTRIBUTES && + (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) + { + return STATUS_ACCESS_DENIED; } - do { - if (wcscmp(findData.cFileName, L"..") != 0 && - wcscmp(findData.cFileName, L".") != 0) { - FindClose(hFind); - DbgPrint(L"\tDirectory is not empty: %s\n", findData.cFileName); - return STATUS_DIRECTORY_NOT_EMPTY; - } - } while (FindNextFile(hFind, &findData) != 0); - - DWORD error = GetLastError(); - - if (error != ERROR_NO_MORE_FILES) { - DbgPrint(L"\tDeleteDirectory error code = %d\n\n", error); - return DokanNtStatusFromWin32(error); - } - - FindClose(hFind); - return STATUS_SUCCESS; } static NTSTATUS DOKAN_CALLBACK -MirrorMoveFile(LPCWSTR FileName, // existing file name - LPCWSTR NewFileName, BOOL ReplaceIfExisting, - PDOKAN_FILE_INFO DokanFileInfo) { +MirrorMoveFile(DOKAN_MOVE_FILE_EVENT *EventInfo) { + WCHAR filePath[MAX_PATH]; WCHAR newFilePath[MAX_PATH]; HANDLE handle; @@ -768,11 +735,12 @@ MirrorMoveFile(LPCWSTR FileName, // existing file name PFILE_RENAME_INFO renameInfo = NULL; - GetFilePath(filePath, MAX_PATH, FileName); - GetFilePath(newFilePath, MAX_PATH, NewFileName); + GetFilePath(filePath, MAX_PATH, EventInfo->FileName); + GetFilePath(newFilePath, MAX_PATH, EventInfo->NewFileName); DbgPrint(L"MoveFile %s -> %s\n\n", filePath, newFilePath); - handle = (HANDLE)DokanFileInfo->Context; + handle = (HANDLE)EventInfo->DokanFileInfo->Context; + if (!handle || handle == INVALID_HANDLE_VALUE) { DbgPrint(L"\tinvalid handle\n\n"); return STATUS_INVALID_HANDLE; @@ -794,7 +762,7 @@ MirrorMoveFile(LPCWSTR FileName, // existing file name ZeroMemory(renameInfo, bufferSize); renameInfo->ReplaceIfExists = - ReplaceIfExisting + EventInfo->ReplaceIfExists ? TRUE : FALSE; // some warning about converting BOOL to BOOLEAN renameInfo->RootDirectory = NULL; // hope it is never needed, shouldn't be @@ -818,60 +786,60 @@ MirrorMoveFile(LPCWSTR FileName, // existing file name } } -static NTSTATUS DOKAN_CALLBACK MirrorLockFile(LPCWSTR FileName, - LONGLONG ByteOffset, - LONGLONG Length, - PDOKAN_FILE_INFO DokanFileInfo) { +static NTSTATUS DOKAN_CALLBACK MirrorLockFile(DOKAN_LOCK_FILE_EVENT *EventInfo) { + WCHAR filePath[MAX_PATH]; HANDLE handle; LARGE_INTEGER offset; LARGE_INTEGER length; - GetFilePath(filePath, MAX_PATH, FileName); + GetFilePath(filePath, MAX_PATH, EventInfo->FileName); DbgPrint(L"LockFile %s\n", filePath); - handle = (HANDLE)DokanFileInfo->Context; + handle = (HANDLE)EventInfo->DokanFileInfo->Context; if (!handle || handle == INVALID_HANDLE_VALUE) { DbgPrint(L"\tinvalid handle\n\n"); return STATUS_INVALID_HANDLE; } - length.QuadPart = Length; - offset.QuadPart = ByteOffset; + length.QuadPart = EventInfo->Length; + offset.QuadPart = EventInfo->ByteOffset; if (!LockFile(handle, offset.LowPart, offset.HighPart, length.LowPart, length.HighPart)) { + DWORD error = GetLastError(); DbgPrint(L"\terror code = %d\n\n", error); return DokanNtStatusFromWin32(error); } DbgPrint(L"\tsuccess\n\n"); + return STATUS_SUCCESS; } -static NTSTATUS DOKAN_CALLBACK MirrorSetEndOfFile( - LPCWSTR FileName, LONGLONG ByteOffset, PDOKAN_FILE_INFO DokanFileInfo) { +static NTSTATUS DOKAN_CALLBACK MirrorSetEndOfFile(DOKAN_SET_EOF_EVENT *EventInfo) { + WCHAR filePath[MAX_PATH]; HANDLE handle; LARGE_INTEGER offset; - GetFilePath(filePath, MAX_PATH, FileName); + GetFilePath(filePath, MAX_PATH, EventInfo->FileName); - DbgPrint(L"SetEndOfFile %s, %I64d\n", filePath, ByteOffset); + DbgPrint(L"SetEndOfFile %s, %I64d\n", filePath, EventInfo->Length); - handle = (HANDLE)DokanFileInfo->Context; + handle = (HANDLE)EventInfo->DokanFileInfo->Context; if (!handle || handle == INVALID_HANDLE_VALUE) { DbgPrint(L"\tinvalid handle\n\n"); return STATUS_INVALID_HANDLE; } - offset.QuadPart = ByteOffset; + offset.QuadPart = EventInfo->Length; if (!SetFilePointerEx(handle, offset, NULL, FILE_BEGIN)) { DWORD error = GetLastError(); DbgPrint(L"\tSetFilePointer error: %d, offset = %I64d\n\n", error, - ByteOffset); + EventInfo->Length); return DokanNtStatusFromWin32(error); } @@ -884,85 +852,71 @@ static NTSTATUS DOKAN_CALLBACK MirrorSetEndOfFile( return STATUS_SUCCESS; } -static NTSTATUS DOKAN_CALLBACK MirrorSetAllocationSize( - LPCWSTR FileName, LONGLONG AllocSize, PDOKAN_FILE_INFO DokanFileInfo) { +static NTSTATUS DOKAN_CALLBACK MirrorSetAllocationSize(DOKAN_SET_ALLOCATION_SIZE_EVENT *EventInfo) { + WCHAR filePath[MAX_PATH]; HANDLE handle; LARGE_INTEGER fileSize; - GetFilePath(filePath, MAX_PATH, FileName); + GetFilePath(filePath, MAX_PATH, EventInfo->FileName); - DbgPrint(L"SetAllocationSize %s, %I64d\n", filePath, AllocSize); + DbgPrint(L"SetAllocationSize %s, %I64d\n", filePath, EventInfo->Length); + + handle = (HANDLE)EventInfo->DokanFileInfo->Context; - handle = (HANDLE)DokanFileInfo->Context; if (!handle || handle == INVALID_HANDLE_VALUE) { DbgPrint(L"\tinvalid handle\n\n"); return STATUS_INVALID_HANDLE; } if (GetFileSizeEx(handle, &fileSize)) { - if (AllocSize < fileSize.QuadPart) { - fileSize.QuadPart = AllocSize; + + if (EventInfo->Length < fileSize.QuadPart) { + + fileSize.QuadPart = EventInfo->Length; + if (!SetFilePointerEx(handle, fileSize, NULL, FILE_BEGIN)) { - DWORD error = GetLastError(); + + DWORD error = GetLastError(); + DbgPrint(L"\tSetAllocationSize: SetFilePointer eror: %d, " L"offset = %I64d\n\n", - error, AllocSize); + error, EventInfo->Length); + return DokanNtStatusFromWin32(error); } + if (!SetEndOfFile(handle)) { - DWORD error = GetLastError(); + + DWORD error = GetLastError(); DbgPrint(L"\tSetEndOfFile error code = %d\n\n", error); + return DokanNtStatusFromWin32(error); } } - } else { - DWORD error = GetLastError(); - DbgPrint(L"\terror code = %d\n\n", error); - return DokanNtStatusFromWin32(error); } - return STATUS_SUCCESS; -} - -static NTSTATUS DOKAN_CALLBACK MirrorSetFileAttributes( - LPCWSTR FileName, DWORD FileAttributes, PDOKAN_FILE_INFO DokanFileInfo) { - UNREFERENCED_PARAMETER(DokanFileInfo); - - WCHAR filePath[MAX_PATH]; - - GetFilePath(filePath, MAX_PATH, FileName); - - DbgPrint(L"SetFileAttributes %s\n", filePath); - - if (!SetFileAttributes(filePath, FileAttributes)) { + else { DWORD error = GetLastError(); DbgPrint(L"\terror code = %d\n\n", error); return DokanNtStatusFromWin32(error); } - DbgPrint(L"\n"); return STATUS_SUCCESS; } -static NTSTATUS DOKAN_CALLBACK -MirrorSetFileTime(LPCWSTR FileName, CONST FILETIME *CreationTime, - CONST FILETIME *LastAccessTime, CONST FILETIME *LastWriteTime, - PDOKAN_FILE_INFO DokanFileInfo) { +static NTSTATUS DOKAN_CALLBACK MirrorSetFileBasicInformation(DOKAN_SET_FILE_BASIC_INFO_EVENT *EventInfo) { + WCHAR filePath[MAX_PATH]; HANDLE handle; - GetFilePath(filePath, MAX_PATH, FileName); + GetFilePath(filePath, MAX_PATH, EventInfo->FileName); + handle = (HANDLE)EventInfo->DokanFileInfo->Context; - DbgPrint(L"SetFileTime %s\n", filePath); + DbgPrint(L"SetFileBasicInformation %s\n", filePath); - handle = (HANDLE)DokanFileInfo->Context; + assert(sizeof(FILE_BASIC_INFORMATION) == sizeof(FILE_BASIC_INFO)); - if (!handle || handle == INVALID_HANDLE_VALUE) { - DbgPrint(L"\tinvalid handle\n\n"); - return STATUS_INVALID_HANDLE; - } - - if (!SetFileTime(handle, CreationTime, LastAccessTime, LastWriteTime)) { + if (!SetFileInformationByHandle(handle, FileBasicInfo, (LPVOID)EventInfo->Info, (DWORD)sizeof(FILE_BASIC_INFORMATION))) { DWORD error = GetLastError(); DbgPrint(L"\terror code = %d\n\n", error); return DokanNtStatusFromWin32(error); @@ -973,25 +927,25 @@ MirrorSetFileTime(LPCWSTR FileName, CONST FILETIME *CreationTime, } static NTSTATUS DOKAN_CALLBACK -MirrorUnlockFile(LPCWSTR FileName, LONGLONG ByteOffset, LONGLONG Length, - PDOKAN_FILE_INFO DokanFileInfo) { +MirrorUnlockFile(DOKAN_UNLOCK_FILE_EVENT *EventInfo) { + WCHAR filePath[MAX_PATH]; HANDLE handle; LARGE_INTEGER length; LARGE_INTEGER offset; - GetFilePath(filePath, MAX_PATH, FileName); + GetFilePath(filePath, MAX_PATH, EventInfo->FileName); DbgPrint(L"UnlockFile %s\n", filePath); - handle = (HANDLE)DokanFileInfo->Context; + handle = (HANDLE)EventInfo->DokanFileInfo->Context; if (!handle || handle == INVALID_HANDLE_VALUE) { DbgPrint(L"\tinvalid handle\n\n"); return STATUS_INVALID_HANDLE; } - length.QuadPart = Length; - offset.QuadPart = ByteOffset; + length.QuadPart = EventInfo->Length; + offset.QuadPart = EventInfo->ByteOffset; if (!UnlockFile(handle, offset.LowPart, offset.HighPart, length.LowPart, length.HighPart)) { @@ -1004,39 +958,35 @@ MirrorUnlockFile(LPCWSTR FileName, LONGLONG ByteOffset, LONGLONG Length, return STATUS_SUCCESS; } -static NTSTATUS DOKAN_CALLBACK MirrorGetFileSecurity( - LPCWSTR FileName, PSECURITY_INFORMATION SecurityInformation, - PSECURITY_DESCRIPTOR SecurityDescriptor, ULONG BufferLength, - PULONG LengthNeeded, PDOKAN_FILE_INFO DokanFileInfo) { - WCHAR filePath[MAX_PATH]; +static NTSTATUS DOKAN_CALLBACK MirrorGetFileSecurity(DOKAN_GET_FILE_SECURITY_EVENT *EventInfo) { - UNREFERENCED_PARAMETER(DokanFileInfo); + WCHAR filePath[MAX_PATH]; - GetFilePath(filePath, MAX_PATH, FileName); + GetFilePath(filePath, MAX_PATH, EventInfo->FileName); DbgPrint(L"GetFileSecurity %s\n", filePath); - MirrorCheckFlag(*SecurityInformation, FILE_SHARE_READ); - MirrorCheckFlag(*SecurityInformation, OWNER_SECURITY_INFORMATION); - MirrorCheckFlag(*SecurityInformation, GROUP_SECURITY_INFORMATION); - MirrorCheckFlag(*SecurityInformation, DACL_SECURITY_INFORMATION); - MirrorCheckFlag(*SecurityInformation, SACL_SECURITY_INFORMATION); - MirrorCheckFlag(*SecurityInformation, LABEL_SECURITY_INFORMATION); - MirrorCheckFlag(*SecurityInformation, ATTRIBUTE_SECURITY_INFORMATION); - MirrorCheckFlag(*SecurityInformation, SCOPE_SECURITY_INFORMATION); - MirrorCheckFlag(*SecurityInformation, + MirrorCheckFlag(*EventInfo->SecurityInformation, FILE_SHARE_READ); + MirrorCheckFlag(*EventInfo->SecurityInformation, OWNER_SECURITY_INFORMATION); + MirrorCheckFlag(*EventInfo->SecurityInformation, GROUP_SECURITY_INFORMATION); + MirrorCheckFlag(*EventInfo->SecurityInformation, DACL_SECURITY_INFORMATION); + MirrorCheckFlag(*EventInfo->SecurityInformation, SACL_SECURITY_INFORMATION); + MirrorCheckFlag(*EventInfo->SecurityInformation, LABEL_SECURITY_INFORMATION); + MirrorCheckFlag(*EventInfo->SecurityInformation, ATTRIBUTE_SECURITY_INFORMATION); + MirrorCheckFlag(*EventInfo->SecurityInformation, SCOPE_SECURITY_INFORMATION); + MirrorCheckFlag(*EventInfo->SecurityInformation, PROCESS_TRUST_LABEL_SECURITY_INFORMATION); - MirrorCheckFlag(*SecurityInformation, BACKUP_SECURITY_INFORMATION); - MirrorCheckFlag(*SecurityInformation, PROTECTED_DACL_SECURITY_INFORMATION); - MirrorCheckFlag(*SecurityInformation, PROTECTED_SACL_SECURITY_INFORMATION); - MirrorCheckFlag(*SecurityInformation, UNPROTECTED_DACL_SECURITY_INFORMATION); - MirrorCheckFlag(*SecurityInformation, UNPROTECTED_SACL_SECURITY_INFORMATION); + MirrorCheckFlag(*EventInfo->SecurityInformation, BACKUP_SECURITY_INFORMATION); + MirrorCheckFlag(*EventInfo->SecurityInformation, PROTECTED_DACL_SECURITY_INFORMATION); + MirrorCheckFlag(*EventInfo->SecurityInformation, PROTECTED_SACL_SECURITY_INFORMATION); + MirrorCheckFlag(*EventInfo->SecurityInformation, UNPROTECTED_DACL_SECURITY_INFORMATION); + MirrorCheckFlag(*EventInfo->SecurityInformation, UNPROTECTED_SACL_SECURITY_INFORMATION); DbgPrint(L" Opening new handle with READ_CONTROL access\n"); HANDLE handle = CreateFile( filePath, - READ_CONTROL | (((*SecurityInformation & SACL_SECURITY_INFORMATION) || - (*SecurityInformation & BACKUP_SECURITY_INFORMATION)) + READ_CONTROL | (((*EventInfo->SecurityInformation & SACL_SECURITY_INFORMATION) || + (*EventInfo->SecurityInformation & BACKUP_SECURITY_INFORMATION)) ? ACCESS_SYSTEM_SECURITY : 0), FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, @@ -1051,87 +1001,121 @@ static NTSTATUS DOKAN_CALLBACK MirrorGetFileSecurity( return DokanNtStatusFromWin32(error); } - if (!GetUserObjectSecurity(handle, SecurityInformation, SecurityDescriptor, - BufferLength, LengthNeeded)) { + if (!GetUserObjectSecurity(handle, EventInfo->SecurityInformation, EventInfo->SecurityDescriptor, + EventInfo->SecurityDescriptorSize, &EventInfo->LengthNeeded)) { + int error = GetLastError(); + if (error == ERROR_INSUFFICIENT_BUFFER) { DbgPrint(L" GetUserObjectSecurity error: ERROR_INSUFFICIENT_BUFFER\n"); CloseHandle(handle); return STATUS_BUFFER_OVERFLOW; - } else { + } + else { DbgPrint(L" GetUserObjectSecurity error: %d\n", error); CloseHandle(handle); return DokanNtStatusFromWin32(error); } } + CloseHandle(handle); return STATUS_SUCCESS; } -static NTSTATUS DOKAN_CALLBACK MirrorSetFileSecurity( - LPCWSTR FileName, PSECURITY_INFORMATION SecurityInformation, - PSECURITY_DESCRIPTOR SecurityDescriptor, ULONG SecurityDescriptorLength, - PDOKAN_FILE_INFO DokanFileInfo) { +static NTSTATUS DOKAN_CALLBACK MirrorSetFileSecurity(DOKAN_SET_FILE_SECURITY_EVENT *EventInfo) { + HANDLE handle; WCHAR filePath[MAX_PATH]; - UNREFERENCED_PARAMETER(SecurityDescriptorLength); - - GetFilePath(filePath, MAX_PATH, FileName); + GetFilePath(filePath, MAX_PATH, EventInfo->FileName); DbgPrint(L"SetFileSecurity %s\n", filePath); - handle = (HANDLE)DokanFileInfo->Context; + handle = (HANDLE)EventInfo->DokanFileInfo->Context; + if (!handle || handle == INVALID_HANDLE_VALUE) { DbgPrint(L"\tinvalid handle\n\n"); return STATUS_INVALID_HANDLE; } - if (!SetUserObjectSecurity(handle, SecurityInformation, SecurityDescriptor)) { + if (!SetUserObjectSecurity(handle, EventInfo->SecurityInformation, EventInfo->SecurityDescriptor)) { int error = GetLastError(); DbgPrint(L" SetUserObjectSecurity error: %d\n", error); return DokanNtStatusFromWin32(error); } + return STATUS_SUCCESS; } -static NTSTATUS DOKAN_CALLBACK MirrorGetVolumeInformation( - LPWSTR VolumeNameBuffer, DWORD VolumeNameSize, LPDWORD VolumeSerialNumber, - LPDWORD MaximumComponentLength, LPDWORD FileSystemFlags, - LPWSTR FileSystemNameBuffer, DWORD FileSystemNameSize, - PDOKAN_FILE_INFO DokanFileInfo) { - UNREFERENCED_PARAMETER(DokanFileInfo); - - wcscpy_s(VolumeNameBuffer, VolumeNameSize, L"DOKAN"); - *VolumeSerialNumber = 0x19831116; - *MaximumComponentLength = 256; - *FileSystemFlags = FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES | - FILE_SUPPORTS_REMOTE_STORAGE | FILE_UNICODE_ON_DISK | - FILE_PERSISTENT_ACLS; - - // File system name could be anything up to 10 characters. - // But Windows check few feature availability based on file system name. - // For this, it is recommended to set NTFS or FAT here. - wcscpy_s(FileSystemNameBuffer, FileSystemNameSize, L"NTFS"); +static NTSTATUS DOKAN_CALLBACK MirrorGetVolumeInformation(DOKAN_GET_VOLUME_INFO_EVENT *EventInfo) { + + LPCWSTR volumeName = L"DOKAN"; + size_t volumeNameLengthInBytes = wcslen(volumeName) * sizeof(WCHAR); + size_t maxVolumeNameLengthInBytes = EventInfo->MaxLabelLengthInChars * sizeof(WCHAR); + size_t bytesToWrite = min(maxVolumeNameLengthInBytes, volumeNameLengthInBytes); + + memcpy_s( + EventInfo->VolumeInfo->VolumeLabel, + maxVolumeNameLengthInBytes, + volumeName, + bytesToWrite); + + + EventInfo->VolumeInfo->VolumeSerialNumber = 0x19831116; + EventInfo->VolumeInfo->VolumeLabelLength = (ULONG)(bytesToWrite / sizeof(WCHAR)); + EventInfo->VolumeInfo->SupportsObjects = FALSE; return STATUS_SUCCESS; } -/* -//Uncomment for personalize disk space -static NTSTATUS DOKAN_CALLBACK MirrorDokanGetDiskFreeSpace( - PULONGLONG FreeBytesAvailable, PULONGLONG TotalNumberOfBytes, - PULONGLONG TotalNumberOfFreeBytes, PDOKAN_FILE_INFO DokanFileInfo) { - UNREFERENCED_PARAMETER(DokanFileInfo); +static NTSTATUS DOKAN_CALLBACK MirrorGetVolumeAttributes(DOKAN_GET_VOLUME_ATTRIBUTES_EVENT *EventInfo) { - *FreeBytesAvailable = (ULONGLONG)(512 * 1024 * 1024); - *TotalNumberOfBytes = 9223372036854775807; - *TotalNumberOfFreeBytes = 9223372036854775807; + LPCWSTR fileSystemName = L"NTFS"; + size_t fileSystemNameLengthInBytes = wcslen(fileSystemName) * sizeof(WCHAR); + size_t maxFileSystemNameLengthInBytes = EventInfo->MaxFileSystemNameLengthInChars * sizeof(WCHAR); + size_t bytesToWrite = min(maxFileSystemNameLengthInBytes, fileSystemNameLengthInBytes); + + EventInfo->Attributes->FileSystemAttributes = + FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES | + FILE_SUPPORTS_REMOTE_STORAGE | FILE_UNICODE_ON_DISK | + FILE_PERSISTENT_ACLS; - return STATUS_SUCCESS; + EventInfo->Attributes->MaximumComponentNameLength = 256; + + // File system name could be anything up to 10 characters. + // But Windows check few feature availability based on file system name. + // For this, it is recommended to set NTFS or FAT here. + memcpy_s( + EventInfo->Attributes->FileSystemName, + maxFileSystemNameLengthInBytes, + fileSystemName, + bytesToWrite); + + EventInfo->Attributes->FileSystemNameLength = (ULONG)(bytesToWrite / sizeof(WCHAR)); + + return STATUS_SUCCESS; } -*/ + + +//Uncomment for personalize disk space +static NTSTATUS DOKAN_CALLBACK MirrorDokanGetDiskFreeSpace(DOKAN_GET_DISK_FREE_SPACE_EVENT *EventInfo) { + + if(!GetDiskFreeSpaceExW( + RootDirectory, + (ULARGE_INTEGER*)&EventInfo->FreeBytesAvailable, + (ULARGE_INTEGER*)&EventInfo->TotalNumberOfBytes, + (ULARGE_INTEGER*)&EventInfo->TotalNumberOfFreeBytes)) + { + int error = GetLastError(); + DbgPrint(L" GetDiskFreeSpaceExW error: %d\n", error); + + return DokanNtStatusFromWin32(error); + } + + return STATUS_SUCCESS; +} + /** * Avoid #include which as conflict with FILE_INFORMATION_CLASS @@ -1163,15 +1147,15 @@ NTSYSCALLAPI NTSTATUS NTAPI NtQueryInformationFile( */ NTSTATUS DOKAN_CALLBACK -MirrorFindStreams(LPCWSTR FileName, PFillFindStreamData FillFindStreamData, - PDOKAN_FILE_INFO DokanFileInfo) { +MirrorFindStreams(DOKAN_FIND_STREAMS_EVENT *EventInfo) { + WCHAR filePath[MAX_PATH]; HANDLE hFind; WIN32_FIND_STREAM_DATA findData; DWORD error; int count = 0; - GetFilePath(filePath, MAX_PATH, FileName); + GetFilePath(filePath, MAX_PATH, EventInfo->FileName); DbgPrint(L"FindStreams :%s\n", filePath); @@ -1183,11 +1167,11 @@ MirrorFindStreams(LPCWSTR FileName, PFillFindStreamData FillFindStreamData, return DokanNtStatusFromWin32(error); } - FillFindStreamData(&findData, DokanFileInfo); + EventInfo->FillFindStreamData(EventInfo, &findData); count++; while (FindNextStreamW(hFind, &findData) != 0) { - FillFindStreamData(&findData, DokanFileInfo); + EventInfo->FillFindStreamData(EventInfo, &findData); count++; } @@ -1204,15 +1188,15 @@ MirrorFindStreams(LPCWSTR FileName, PFillFindStreamData FillFindStreamData, return STATUS_SUCCESS; } -static NTSTATUS DOKAN_CALLBACK MirrorMounted(PDOKAN_FILE_INFO DokanFileInfo) { - UNREFERENCED_PARAMETER(DokanFileInfo); +static NTSTATUS DOKAN_CALLBACK MirrorMounted(DOKAN_MOUNTED_INFO *EventInfo) { + UNREFERENCED_PARAMETER(EventInfo); DbgPrint(L"Mounted\n"); return STATUS_SUCCESS; } -static NTSTATUS DOKAN_CALLBACK MirrorUnmounted(PDOKAN_FILE_INFO DokanFileInfo) { - UNREFERENCED_PARAMETER(DokanFileInfo); +static NTSTATUS DOKAN_CALLBACK MirrorUnmounted(DOKAN_UNMOUNTED_INFO *EventInfo) { + UNREFERENCED_PARAMETER(EventInfo); DbgPrint(L"Unmounted\n"); return STATUS_SUCCESS; @@ -1236,18 +1220,11 @@ BOOL WINAPI CtrlHandler(DWORD dwCtrlType) { } int __cdecl wmain(ULONG argc, PWCHAR argv[]) { + int status; ULONG command; - PDOKAN_OPERATIONS dokanOperations = - (PDOKAN_OPERATIONS)malloc(sizeof(DOKAN_OPERATIONS)); - if (dokanOperations == NULL) { - return EXIT_FAILURE; - } - PDOKAN_OPTIONS dokanOptions = (PDOKAN_OPTIONS)malloc(sizeof(DOKAN_OPTIONS)); - if (dokanOptions == NULL) { - free(dokanOperations); - return EXIT_FAILURE; - } + DOKAN_OPTIONS dokanOptions; + DOKAN_OPERATIONS dokanOperations; if (argc < 3) { fprintf(stderr, "mirror.exe\n" @@ -1265,17 +1242,16 @@ int __cdecl wmain(ULONG argc, PWCHAR argv[]) { " /a Allocation unit size (ex. /a 512)\n" " /k Sector size (ex. /k 512)\n" " /i (Timeout in Milliseconds ex. /i 30000)\n"); - free(dokanOperations); - free(dokanOptions); + return EXIT_FAILURE; } g_DebugMode = FALSE; g_UseStdErr = FALSE; - ZeroMemory(dokanOptions, sizeof(DOKAN_OPTIONS)); - dokanOptions->Version = DOKAN_VERSION; - dokanOptions->ThreadCount = 0; // use default + 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])) { @@ -1288,11 +1264,11 @@ int __cdecl wmain(ULONG argc, PWCHAR argv[]) { case L'l': command++; wcscpy_s(MountPoint, sizeof(MountPoint) / sizeof(WCHAR), argv[command]); - dokanOptions->MountPoint = MountPoint; + dokanOptions.MountPoint = MountPoint; break; case L't': command++; - dokanOptions->ThreadCount = (USHORT)_wtoi(argv[command]); + dokanOptions.ThreadCount = (USHORT)_wtoi(argv[command]); break; case L'd': g_DebugMode = TRUE; @@ -1301,85 +1277,83 @@ int __cdecl wmain(ULONG argc, PWCHAR argv[]) { g_UseStdErr = TRUE; break; case L'n': - dokanOptions->Options |= DOKAN_OPTION_NETWORK; + dokanOptions.Options |= DOKAN_OPTION_NETWORK; break; case L'm': - dokanOptions->Options |= DOKAN_OPTION_REMOVABLE; + dokanOptions.Options |= DOKAN_OPTION_REMOVABLE; break; case L'w': - dokanOptions->Options |= DOKAN_OPTION_WRITE_PROTECT; + dokanOptions.Options |= DOKAN_OPTION_WRITE_PROTECT; break; case L'o': - dokanOptions->Options |= DOKAN_OPTION_MOUNT_MANAGER; + dokanOptions.Options |= DOKAN_OPTION_MOUNT_MANAGER; break; case L'c': - dokanOptions->Options |= DOKAN_OPTION_CURRENT_SESSION; + dokanOptions.Options |= DOKAN_OPTION_CURRENT_SESSION; break; case L'u': command++; wcscpy_s(UNCName, sizeof(UNCName) / sizeof(WCHAR), argv[command]); - dokanOptions->UNCName = UNCName; + dokanOptions.UNCName = UNCName; DbgPrint(L"UNC Name: %ls\n", UNCName); break; case L'i': command++; - dokanOptions->Timeout = (ULONG)_wtol(argv[command]); + dokanOptions.Timeout = (ULONG)_wtol(argv[command]); break; case L'a': command++; - dokanOptions->AllocationUnitSize = (ULONG)_wtol(argv[command]); + dokanOptions.AllocationUnitSize = (ULONG)_wtol(argv[command]); break; case L'k': command++; - dokanOptions->SectorSize = (ULONG)_wtol(argv[command]); + dokanOptions.SectorSize = (ULONG)_wtol(argv[command]); break; default: fwprintf(stderr, L"unknown command: %s\n", argv[command]); - free(dokanOperations); - free(dokanOptions); return EXIT_FAILURE; } } if (wcscmp(UNCName, L"") != 0 && - !(dokanOptions->Options & DOKAN_OPTION_NETWORK)) { + !(dokanOptions.Options & DOKAN_OPTION_NETWORK)) { fwprintf( stderr, L" Warning: UNC provider name should be set on network drive only.\n"); } - if (dokanOptions->Options & DOKAN_OPTION_NETWORK && - dokanOptions->Options & DOKAN_OPTION_MOUNT_MANAGER) { + if (dokanOptions.Options & DOKAN_OPTION_NETWORK && + dokanOptions.Options & DOKAN_OPTION_MOUNT_MANAGER) { + fwprintf(stderr, L"Mount manager cannot be used on network drive.\n"); - free(dokanOperations); - free(dokanOptions); + return -1; } - if (!(dokanOptions->Options & DOKAN_OPTION_MOUNT_MANAGER) && + if (!(dokanOptions.Options & DOKAN_OPTION_MOUNT_MANAGER) && wcscmp(MountPoint, L"") == 0) { + fwprintf(stderr, L"Mount Point required.\n"); - free(dokanOperations); - free(dokanOptions); return -1; } - if ((dokanOptions->Options & DOKAN_OPTION_MOUNT_MANAGER) && - (dokanOptions->Options & DOKAN_OPTION_CURRENT_SESSION)) { + if ((dokanOptions.Options & DOKAN_OPTION_MOUNT_MANAGER) && + (dokanOptions.Options & DOKAN_OPTION_CURRENT_SESSION)) { + fwprintf(stderr, L"Mount Manager always mount the drive for all user sessions.\n"); - free(dokanOperations); - free(dokanOptions); return -1; } if (!SetConsoleCtrlHandler(CtrlHandler, TRUE)) { + fwprintf(stderr, L"Control Handler is not set.\n"); } // Add security name privilege. Required here to handle GetFileSecurity // properly. if (!AddSeSecurityNamePrivilege()) { + fwprintf(stderr, L"Failed to add security privilege to process\n"); fwprintf(stderr, L"\t=> GetFileSecurity/SetFileSecurity may not work properly\n"); @@ -1388,42 +1362,42 @@ int __cdecl wmain(ULONG argc, PWCHAR argv[]) { } if (g_DebugMode) { - dokanOptions->Options |= DOKAN_OPTION_DEBUG; + dokanOptions.Options |= DOKAN_OPTION_DEBUG; } if (g_UseStdErr) { - dokanOptions->Options |= DOKAN_OPTION_STDERR; - } + dokanOptions.Options |= DOKAN_OPTION_STDERR; + } + + dokanOptions.Options |= DOKAN_OPTION_ALT_STREAM; + + ZeroMemory(&dokanOperations, sizeof(DOKAN_OPERATIONS)); + dokanOperations.ZwCreateFile = MirrorCreateFile; + dokanOperations.Cleanup = MirrorCleanup; + dokanOperations.CloseFile = MirrorCloseFile; + dokanOperations.ReadFile = MirrorReadFile; + dokanOperations.WriteFile = MirrorWriteFile; + dokanOperations.FlushFileBuffers = MirrorFlushFileBuffers; + dokanOperations.GetFileInformation = MirrorGetFileInformation; + dokanOperations.FindFiles = MirrorFindFiles; + dokanOperations.FindFilesWithPattern = NULL; + dokanOperations.SetFileBasicInformation = MirrorSetFileBasicInformation; + dokanOperations.CanDeleteFile = MirrorCanDeleteFile; + dokanOperations.MoveFileW = MirrorMoveFile; + dokanOperations.SetEndOfFile = MirrorSetEndOfFile; + dokanOperations.SetAllocationSize = MirrorSetAllocationSize; + dokanOperations.LockFile = MirrorLockFile; + dokanOperations.UnlockFile = MirrorUnlockFile; + dokanOperations.GetVolumeFreeSpace = MirrorDokanGetDiskFreeSpace; + dokanOperations.GetVolumeInformationW = MirrorGetVolumeInformation; + dokanOperations.GetVolumeAttributes = MirrorGetVolumeAttributes; + dokanOperations.Mounted = MirrorMounted; + dokanOperations.Unmounted = MirrorUnmounted; + dokanOperations.GetFileSecurityW = MirrorGetFileSecurity; + dokanOperations.SetFileSecurityW = MirrorSetFileSecurity; + dokanOperations.FindStreams = MirrorFindStreams; + + status = DokanMain(&dokanOptions, &dokanOperations); - dokanOptions->Options |= DOKAN_OPTION_ALT_STREAM; - - ZeroMemory(dokanOperations, sizeof(DOKAN_OPERATIONS)); - dokanOperations->ZwCreateFile = MirrorCreateFile; - dokanOperations->Cleanup = MirrorCleanup; - dokanOperations->CloseFile = MirrorCloseFile; - dokanOperations->ReadFile = MirrorReadFile; - dokanOperations->WriteFile = MirrorWriteFile; - dokanOperations->FlushFileBuffers = MirrorFlushFileBuffers; - dokanOperations->GetFileInformation = MirrorGetFileInformation; - dokanOperations->FindFiles = MirrorFindFiles; - dokanOperations->FindFilesWithPattern = NULL; - dokanOperations->SetFileAttributes = MirrorSetFileAttributes; - dokanOperations->SetFileTime = MirrorSetFileTime; - dokanOperations->DeleteFile = MirrorDeleteFile; - dokanOperations->DeleteDirectory = MirrorDeleteDirectory; - dokanOperations->MoveFile = MirrorMoveFile; - dokanOperations->SetEndOfFile = MirrorSetEndOfFile; - dokanOperations->SetAllocationSize = MirrorSetAllocationSize; - dokanOperations->LockFile = MirrorLockFile; - dokanOperations->UnlockFile = MirrorUnlockFile; - dokanOperations->GetFileSecurity = MirrorGetFileSecurity; - dokanOperations->SetFileSecurity = MirrorSetFileSecurity; - dokanOperations->GetDiskFreeSpace = NULL; // MirrorDokanGetDiskFreeSpace; - dokanOperations->GetVolumeInformation = MirrorGetVolumeInformation; - dokanOperations->Unmounted = MirrorUnmounted; - dokanOperations->FindStreams = MirrorFindStreams; - dokanOperations->Mounted = MirrorMounted; - - status = DokanMain(dokanOptions, dokanOperations); switch (status) { case DOKAN_SUCCESS: fprintf(stderr, "Success\n"); @@ -1454,7 +1428,5 @@ int __cdecl wmain(ULONG argc, PWCHAR argv[]) { break; } - free(dokanOptions); - free(dokanOperations); return EXIT_SUCCESS; } diff --git a/sys/public.h b/sys/public.h index 2e255a663..18c9d8cdd 100644 --- a/sys/public.h +++ b/sys/public.h @@ -299,6 +299,9 @@ typedef struct _EVENT_CONTEXT { #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; @@ -329,10 +332,15 @@ typedef struct _EVENT_INFORMATION { } Operation; ULONG64 Context; ULONG BufferLength; - UCHAR Buffer[8]; + 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 (offsetof(EVENT_INFORMATION, Buffer) + DOKAN_EVENT_INFO_DEFAULT_BUFFER_SIZE) +#define DOKAN_EVENT_INFO_ALLOC_SIZE(bufSize) (offsetof(EVENT_INFORMATION, Buffer) + max(DOKAN_EVENT_INFO_MIN_BUFFER_SIZE, (bufSize))) + #define DOKAN_EVENT_ALTERNATIVE_STREAM_ON 1 #define DOKAN_EVENT_WRITE_PROTECT 2 #define DOKAN_EVENT_REMOVABLE 4 diff --git a/sys/write.c b/sys/write.c index 00369cbe5..7b286230a 100644 --- a/sys/write.c +++ b/sys/write.c @@ -257,7 +257,7 @@ VOID DokanCompleteWrite(__in PIRP_ENTRY IrpEntry, irp->IoStatus.Information = EventInfo->BufferLength; if (NT_SUCCESS(status) && EventInfo->BufferLength != 0 && - fileObject->Flags & FO_SYNCHRONOUS_IO && !(irp->Flags & IRP_PAGING_IO)) { + (fileObject->Flags & FO_SYNCHRONOUS_IO) && !(irp->Flags & IRP_PAGING_IO)) { // update current byte offset only when synchronous IO and not paging IO fileObject->CurrentByteOffset.QuadPart = EventInfo->Operation.Write.CurrentByteOffset.QuadPart; From 1eab0ccf2ba633c666c9ff8391f0a848abe2cf7a Mon Sep 17 00:00:00 2001 From: Keith Newton Date: Mon, 1 Aug 2016 19:56:25 -0400 Subject: [PATCH 03/20] Fixed deleting directories --- samples/dokan_mirror/mirror.c | 67 ++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/samples/dokan_mirror/mirror.c b/samples/dokan_mirror/mirror.c index b31e65319..9279dd6a5 100644 --- a/samples/dokan_mirror/mirror.c +++ b/samples/dokan_mirror/mirror.c @@ -703,6 +703,61 @@ MirrorFindFiles(DOKAN_FIND_FILES_EVENT *EventInfo) { return STATUS_SUCCESS; } +NTSTATUS +MirrorCanDeleteDirectory(LPWSTR filePath) { + + HANDLE hFind; + WIN32_FIND_DATAW findData; + size_t fileLen; + + DbgPrint(L"CanDeleteDirectory %s\n", filePath); + + fileLen = wcslen(filePath); + + if(filePath[fileLen - 1] != L'\\') { + + filePath[fileLen++] = L'\\'; + } + + filePath[fileLen] = L'*'; + filePath[fileLen + 1] = L'\0'; + + hFind = FindFirstFile(filePath, &findData); + + if(hFind == INVALID_HANDLE_VALUE) { + + DWORD error = GetLastError(); + DbgPrint(L"\tDeleteDirectory error code = %d\n\n", error); + + return DokanNtStatusFromWin32(error); + } + + do { + + if(wcscmp(findData.cFileName, L"..") != 0 && + wcscmp(findData.cFileName, L".") != 0) { + + FindClose(hFind); + DbgPrint(L"\tDirectory is not empty: %s\n", findData.cFileName); + + return STATUS_DIRECTORY_NOT_EMPTY; + } + + } while(FindNextFile(hFind, &findData) != 0); + + DWORD error = GetLastError(); + + if(error != ERROR_NO_MORE_FILES) { + + DbgPrint(L"\tDeleteDirectory error code = %d\n\n", error); + return DokanNtStatusFromWin32(error); + } + + FindClose(hFind); + + return STATUS_SUCCESS; +} + static NTSTATUS DOKAN_CALLBACK MirrorCanDeleteFile(DOKAN_CAN_DELETE_FILE_EVENT *EventInfo) { @@ -710,14 +765,18 @@ MirrorCanDeleteFile(DOKAN_CAN_DELETE_FILE_EVENT *EventInfo) { // HANDLE handle = (HANDLE)DokanFileInfo->Context; GetFilePath(filePath, MAX_PATH, EventInfo->FileName); - DbgPrint(L"DeleteFile %s\n", filePath); + DbgPrint(L"CanDeleteFile %s\n", filePath); + + if(EventInfo->DokanFileInfo->IsDirectory) { + + return MirrorCanDeleteDirectory(filePath); + } DWORD dwAttrib = GetFileAttributes(filePath); - if(dwAttrib != INVALID_FILE_ATTRIBUTES && - (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)) + if(dwAttrib == INVALID_FILE_ATTRIBUTES) { - return STATUS_ACCESS_DENIED; + return DokanNtStatusFromWin32(GetLastError()); } return STATUS_SUCCESS; From f5353df96bd76a8c13ff7c9a038b337aa38ae18f Mon Sep 17 00:00:00 2001 From: Keith Newton Date: Tue, 2 Aug 2016 15:38:29 -0400 Subject: [PATCH 04/20] Fixed a bug with how Mirror was handling close/cleanup #210 Close/Cleanup no longer gets called if Create fails and no user context is supplied Renamed EndDispatch*() functions to DokanEndDispatch*() since they're public The DOKAN_VECTOR API is now exported from dokan.dll --- dokan/create.c | 34 +- dokan/directory.c | 8 +- dokan/dokan.c | 15 +- dokan/dokan.def | 16 + dokan/dokan.h | 47 ++- dokan/dokan_vector.c | 33 +- dokan/dokan_vector.h | 33 +- dokan/dokani.h | 1 - dokan/fileinfo.c | 12 +- dokan/flush.c | 6 +- dokan/lock.c | 4 +- dokan/read.c | 6 +- dokan/security.c | 12 +- dokan/setfile.c | 24 +- dokan/volume.c | 34 +- dokan/write.c | 8 +- samples/dokan_mirror/mirror.c | 768 ++++++++++++++++++++++++---------- 17 files changed, 710 insertions(+), 351 deletions(-) diff --git a/dokan/create.c b/dokan/create.c index 811ea3b21..270a673a1 100644 --- a/dokan/create.c +++ b/dokan/create.c @@ -102,7 +102,7 @@ void BeginDispatchCreate(DOKAN_IO_EVENT *EventInfo) { DbgPrint("###Create file handle = 0x%p, eventID = %04d\n", EventInfo->DokanOpenInfo, - EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1); + currentEventId); // The low 24 bits of this member correspond to the CreateOptions parameter createFileEvent->CreateOptions = EventInfo->KernelInfo.EventContext.Operation.Create.CreateOptions & FILE_VALID_OPTION_FLAGS; @@ -115,7 +115,7 @@ void BeginDispatchCreate(DOKAN_IO_EVENT *EventInfo) { if((createFileEvent->CreateOptions & FILE_NON_DIRECTORY_FILE) && (createFileEvent->CreateOptions & FILE_DIRECTORY_FILE)) { - EndDispatchCreate(createFileEvent, STATUS_INVALID_PARAMETER); + DokanEndDispatchCreate(createFileEvent, STATUS_INVALID_PARAMETER); return; } @@ -199,11 +199,11 @@ void BeginDispatchCreate(DOKAN_IO_EVENT *EventInfo) { if(status != STATUS_PENDING) { - EndDispatchCreate(createFileEvent, status); + DokanEndDispatchCreate(createFileEvent, status); } } -void DOKANAPI EndDispatchCreate(DOKAN_CREATE_FILE_EVENT *EventInfo, NTSTATUS ResultStatus) { +void DOKANAPI DokanEndDispatchCreate(DOKAN_CREATE_FILE_EVENT *EventInfo, NTSTATUS ResultStatus) { DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; DWORD lastError = GetLastError(); @@ -217,7 +217,7 @@ void DOKANAPI EndDispatchCreate(DOKAN_CREATE_FILE_EVENT *EventInfo, NTSTATUS Res // STATUS_PENDING should not be passed to this function if(ResultStatus == STATUS_PENDING) { - DbgPrint("Dokan Error: EndDispatchCreate() failed because STATUS_PENDING was supplied for ResultStatus.\n"); + DbgPrint("Dokan Error: DokanEndDispatchCreate() failed because STATUS_PENDING was supplied for ResultStatus.\n"); ResultStatus = STATUS_INTERNAL_ERROR; } @@ -229,7 +229,8 @@ void DOKANAPI EndDispatchCreate(DOKAN_CREATE_FILE_EVENT *EventInfo, NTSTATUS Res ioEvent->EventInfo.ZwCreateFile.OriginalFileName = NULL; } - DbgPrint("Dokan Information: EndDispatchCreate() status = %lx - lastError = %d\n", ResultStatus, lastError); + DbgPrint("Dokan Information: DokanEndDispatchCreate() status = %lx, lastError = %d, file handle = 0x%p\n, eventID = %04d", + ResultStatus, lastError, ioEvent->DokanOpenInfo, ioEvent->DokanOpenInfo ? ioEvent->DokanOpenInfo->EventId : -1); // FILE_CREATED // FILE_DOES_NOT_EXIST @@ -247,20 +248,23 @@ void DOKANAPI EndDispatchCreate(DOKAN_CREATE_FILE_EVENT *EventInfo, NTSTATUS Res if(ioEvent->DokanOpenInfo) { - if(ioEvent->DokanInstance->DokanOperations->Cleanup) { + if(ioEvent->DokanFileInfo.Context) { + + if(ioEvent->DokanInstance->DokanOperations->Cleanup) { - cleanupEvent.DokanFileInfo = &ioEvent->DokanFileInfo; - cleanupEvent.FileName = EventInfo->FileName; + cleanupEvent.DokanFileInfo = &ioEvent->DokanFileInfo; + cleanupEvent.FileName = EventInfo->FileName; - ioEvent->DokanInstance->DokanOperations->Cleanup(&cleanupEvent); - } + ioEvent->DokanInstance->DokanOperations->Cleanup(&cleanupEvent); + } - if(ioEvent->DokanInstance->DokanOperations->CloseFile) { + if(ioEvent->DokanInstance->DokanOperations->CloseFile) { - closeFileEvent.DokanFileInfo = &ioEvent->DokanFileInfo; - closeFileEvent.FileName = EventInfo->FileName; + closeFileEvent.DokanFileInfo = &ioEvent->DokanFileInfo; + closeFileEvent.FileName = EventInfo->FileName; - ioEvent->DokanInstance->DokanOperations->CloseFile(&closeFileEvent); + ioEvent->DokanInstance->DokanOperations->CloseFile(&closeFileEvent); + } } PushFileOpenInfo(ioEvent->DokanOpenInfo); diff --git a/dokan/directory.c b/dokan/directory.c index f803e1fb6..44026a70a 100644 --- a/dokan/directory.c +++ b/dokan/directory.c @@ -680,7 +680,7 @@ void BeginDispatchDirectoryInformation(DOKAN_IO_EVENT *EventInfo) { if(status != STATUS_PENDING) { - EndDispatchFindFiles(findFiles, status); + DokanEndDispatchFindFiles(findFiles, status); } } else if(dokan->DokanOperations->FindFilesWithPattern) { @@ -696,7 +696,7 @@ void BeginDispatchDirectoryInformation(DOKAN_IO_EVENT *EventInfo) { if(status != STATUS_PENDING) { - EndDispatchFindFilesWithPattern(findFilesPattern, status); + DokanEndDispatchFindFilesWithPattern(findFilesPattern, status); } } else { @@ -849,12 +849,12 @@ BOOL DOKANAPI DokanIsNameInExpression(LPCWSTR Expression, // matching pattern return !Expression[ei] && !Name[ni] ? TRUE : FALSE; } -void DOKANAPI EndDispatchFindFiles(DOKAN_FIND_FILES_EVENT *EventInfo, NTSTATUS ResultStatus) { +void DOKANAPI DokanEndDispatchFindFiles(DOKAN_FIND_FILES_EVENT *EventInfo, NTSTATUS ResultStatus) { EndFindFilesCommon((DOKAN_IO_EVENT*)EventInfo, ResultStatus); } -void DOKANAPI EndDispatchFindFilesWithPattern(DOKAN_FIND_FILES_PATTERN_EVENT *EventInfo, NTSTATUS ResultStatus) { +void DOKANAPI DokanEndDispatchFindFilesWithPattern(DOKAN_FIND_FILES_PATTERN_EVENT *EventInfo, NTSTATUS ResultStatus) { EndFindFilesCommon((DOKAN_IO_EVENT*)EventInfo, ResultStatus); } \ No newline at end of file diff --git a/dokan/dokan.c b/dokan/dokan.c index 408461821..8596afb10 100644 --- a/dokan/dokan.c +++ b/dokan/dokan.c @@ -1250,7 +1250,7 @@ void ProcessWriteSizeEvent( if(IoResult != NO_ERROR) { // This will push the input buffer so we don't need to manually do that - EndDispatchWrite(&inputIoEvent->EventInfo.WriteFile, STATUS_INTERNAL_ERROR); + DokanEndDispatchWrite(&inputIoEvent->EventInfo.WriteFile, STATUS_INTERNAL_ERROR); PushIoEventBuffer(outputIoEvent); @@ -1841,3 +1841,16 @@ VOID DOKANAPI DokanMapKernelToUserCreateFileFlags( } } } + +DOKAN_API PTP_POOL DOKAN_CALLBACK DokanGetThreadPool() { + + PTP_POOL threadPool; + + EnterCriticalSection(&g_InstanceCriticalSection); + + threadPool = g_ThreadPool; + + LeaveCriticalSection(&g_InstanceCriticalSection); + + return threadPool; +} \ No newline at end of file diff --git a/dokan/dokan.def b/dokan/dokan.def index b36d2907b..7b895fdb5 100644 --- a/dokan/dokan.def +++ b/dokan/dokan.def @@ -21,3 +21,19 @@ DokanDebugMode DokanMapKernelToUserCreateFileFlags DokanGetMountPointList DokanNtStatusFromWin32 +DokanVector_Alloc +DokanVector_AllocWithCapacity +DokanVector_StackAlloc +DokanVector_StackAllocWithCapacity +DokanVector_Free +DokanVector_PushBack +DokanVector_PushBackArray +DokanVector_PopBack +DokanVector_PopBackArray +DokanVector_Clear +DokanVector_GetItem +DokanVector_GetLastItem +DokanVector_GetCount +DokanVector_GetCapacity +DokanVector_GetItemSize +DokanGetThreadPool \ No newline at end of file diff --git a/dokan/dokan.h b/dokan/dokan.h index e68534021..113fe6ab1 100644 --- a/dokan/dokan.h +++ b/dokan/dokan.h @@ -36,12 +36,16 @@ with this program. If not, see . #ifdef _EXPORTING #define DOKANAPI /*__declspec(dllexport)*/ \ __stdcall // exports defined in dokan.def +#define DOKAN_API #else #define DOKANAPI __declspec(dllimport) __stdcall +#define DOKAN_API __declspec(dllimport) #endif #define DOKAN_CALLBACK __stdcall +#include "dokan_vector.h" + #ifdef __cplusplus extern "C" { #endif @@ -446,26 +450,29 @@ VOID DOKANAPI DokanMapKernelToUserCreateFileFlags( NTSTATUS DOKANAPI DokanNtStatusFromWin32(DWORD Error); // Async IO completion functions -void DOKANAPI EndDispatchCreate(DOKAN_CREATE_FILE_EVENT *EventInfo, NTSTATUS ResultStatus); -void DOKANAPI EndDispatchRead(DOKAN_READ_FILE_EVENT *EventInfo, NTSTATUS ResultStatus); -void DOKANAPI EndDispatchWrite(DOKAN_WRITE_FILE_EVENT *EventInfo, NTSTATUS ResultStatus); -void DOKANAPI EndDispatchFindFiles(DOKAN_FIND_FILES_EVENT *EventInfo, NTSTATUS ResultStatus); -void DOKANAPI EndDispatchFindFilesWithPattern(DOKAN_FIND_FILES_PATTERN_EVENT *EventInfo, NTSTATUS ResultStatus); -void DOKANAPI EndDispatchGetFileInformation(DOKAN_GET_FILE_INFO_EVENT *EventInfo, NTSTATUS ResultStatus); -void DOKANAPI EndDispatchFindStreams(DOKAN_FIND_STREAMS_EVENT *EventInfo, NTSTATUS ResultStatus); -void DOKANAPI EndDispatchGetVolumeInfo(DOKAN_GET_VOLUME_INFO_EVENT *EventInfo, NTSTATUS ResultStatus); -void DOKANAPI EndDispatchGetVolumeFreeSpace(DOKAN_GET_DISK_FREE_SPACE_EVENT *EventInfo, NTSTATUS ResultStatus); -void DOKANAPI EndDispatchGetVolumeAttributes(DOKAN_GET_VOLUME_ATTRIBUTES_EVENT *EventInfo, NTSTATUS ResultStatus); -void DOKANAPI EndDispatchLockFile(DOKAN_LOCK_FILE_EVENT *EventInfo, NTSTATUS ResultStatus); -void DOKANAPI EndDispatchUnlockFile(DOKAN_LOCK_FILE_EVENT *EventInfo, NTSTATUS ResultStatus); -void DOKANAPI EndDispatchSetAllocationSize(DOKAN_SET_ALLOCATION_SIZE_EVENT *EventInfo, NTSTATUS ResultStatus); -void DOKANAPI EndDispatchSetFileBasicInformation(DOKAN_SET_FILE_BASIC_INFO_EVENT *EventInfo, NTSTATUS ResultStatus); -void DOKANAPI EndDispatchCanDeleteFile(DOKAN_CAN_DELETE_FILE_EVENT *EventInfo, NTSTATUS ResultStatus); -void DOKANAPI EndDispatchSetEndOfFile(DOKAN_SET_EOF_EVENT *EventInfo, NTSTATUS ResultStatus); -void DOKANAPI EndDispatchMoveFile(DOKAN_MOVE_FILE_EVENT *EventInfo, NTSTATUS ResultStatus); -void DOKANAPI EndDispatchFlush(DOKAN_FLUSH_BUFFERS_EVENT *EventInfo, NTSTATUS ResultStatus); -void DOKANAPI EndDispatchGetFileSecurity(DOKAN_GET_FILE_SECURITY_EVENT *EventInfo, NTSTATUS ResultStatus); -void DOKANAPI EndDispatchSetFileSecurity(DOKAN_SET_FILE_SECURITY_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI DokanEndDispatchCreate(DOKAN_CREATE_FILE_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI DokanEndDispatchRead(DOKAN_READ_FILE_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI DokanEndDispatchWrite(DOKAN_WRITE_FILE_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI DokanEndDispatchFindFiles(DOKAN_FIND_FILES_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI DokanEndDispatchFindFilesWithPattern(DOKAN_FIND_FILES_PATTERN_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI DokanEndDispatchGetFileInformation(DOKAN_GET_FILE_INFO_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI DokanEndDispatchFindStreams(DOKAN_FIND_STREAMS_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI DokanEndDispatchGetVolumeInfo(DOKAN_GET_VOLUME_INFO_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI DokanEndDispatchGetVolumeFreeSpace(DOKAN_GET_DISK_FREE_SPACE_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI DokanEndDispatchGetVolumeAttributes(DOKAN_GET_VOLUME_ATTRIBUTES_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI DokanEndDispatchLockFile(DOKAN_LOCK_FILE_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI DokanEndDispatchUnlockFile(DOKAN_LOCK_FILE_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI DokanEndDispatchSetAllocationSize(DOKAN_SET_ALLOCATION_SIZE_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI DokanEndDispatchSetFileBasicInformation(DOKAN_SET_FILE_BASIC_INFO_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI DokanEndDispatchCanDeleteFile(DOKAN_CAN_DELETE_FILE_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI DokanEndDispatchSetEndOfFile(DOKAN_SET_EOF_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI DokanEndDispatchMoveFile(DOKAN_MOVE_FILE_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI DokanEndDispatchFlush(DOKAN_FLUSH_BUFFERS_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI DokanEndDispatchGetFileSecurity(DOKAN_GET_FILE_SECURITY_EVENT *EventInfo, NTSTATUS ResultStatus); +void DOKANAPI DokanEndDispatchSetFileSecurity(DOKAN_SET_FILE_SECURITY_EVENT *EventInfo, NTSTATUS ResultStatus); + +// Threading helpers +DOKAN_API PTP_POOL DOKAN_CALLBACK DokanGetThreadPool(); #ifdef __cplusplus } diff --git a/dokan/dokan_vector.c b/dokan/dokan_vector.c index 01d724a39..53fea2baa 100644 --- a/dokan/dokan_vector.c +++ b/dokan/dokan_vector.c @@ -5,8 +5,11 @@ #define DEFAULT_ITEM_COUNT 128 +// Increases the internal capacity of the vector. +BOOL DokanVector_Grow(DOKAN_VECTOR *Vector, size_t MinimumIncrease); + // Creates a new instance of DOKAN_VECTOR with default values. -DOKAN_VECTOR* DokanVector_Alloc(size_t ItemSize) { +DOKAN_VECTOR* DOKANAPI DokanVector_Alloc(size_t ItemSize) { assert(ItemSize > 0); @@ -28,7 +31,7 @@ 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) { +DOKAN_VECTOR* DOKANAPI DokanVector_AllocWithCapacity(size_t ItemSize, size_t MaxItems) { assert(ItemSize > 0); @@ -57,7 +60,7 @@ 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(DOKAN_VECTOR *Vector, size_t ItemSize) { +BOOL DOKANAPI DokanVector_StackAlloc(DOKAN_VECTOR *Vector, size_t ItemSize) { assert(Vector && ItemSize > 0); @@ -80,7 +83,7 @@ BOOL DokanVector_StackAlloc(DOKAN_VECTOR *Vector, size_t ItemSize) { } // Creates a new instance of DOKAN_VECTOR with default values on the stack. -BOOL DokanVector_StackAllocWithCapacity(DOKAN_VECTOR *Vector, size_t ItemSize, size_t MaxItems) { +BOOL DOKANAPI DokanVector_StackAllocWithCapacity(DOKAN_VECTOR *Vector, size_t ItemSize, size_t MaxItems) { assert(Vector && ItemSize > 0); @@ -111,7 +114,7 @@ BOOL DokanVector_StackAllocWithCapacity(DOKAN_VECTOR *Vector, size_t ItemSize, s } // Releases the memory associated with a DOKAN_VECTOR; -void DokanVector_Free(DOKAN_VECTOR *Vector) { +void DOKANAPI DokanVector_Free(DOKAN_VECTOR *Vector) { if(Vector) { @@ -128,7 +131,7 @@ void DokanVector_Free(DOKAN_VECTOR *Vector) { } // Appends an item to the vector. -BOOL DokanVector_PushBack(DOKAN_VECTOR *Vector, void *Item) { +BOOL DOKANAPI DokanVector_PushBack(DOKAN_VECTOR *Vector, void *Item) { assert(Vector && Item); @@ -148,7 +151,7 @@ BOOL DokanVector_PushBack(DOKAN_VECTOR *Vector, void *Item) { } // Appends an array of items to the vector. -BOOL DokanVector_PushBackArray(DOKAN_VECTOR *Vector, void *Items, size_t Count) { +BOOL DOKANAPI DokanVector_PushBackArray(DOKAN_VECTOR *Vector, void *Items, size_t Count) { assert(Vector && Items); @@ -171,7 +174,7 @@ BOOL DokanVector_PushBackArray(DOKAN_VECTOR *Vector, void *Items, size_t Count) } // Removes an item from the end of the vector. -void DokanVector_PopBack(DOKAN_VECTOR *Vector) { +void DOKANAPI DokanVector_PopBack(DOKAN_VECTOR *Vector) { assert(Vector && Vector->ItemCount > 0); @@ -182,7 +185,7 @@ void DokanVector_PopBack(DOKAN_VECTOR *Vector) { } // Removes multiple items from the end of the vector. -void DokanVector_PopBackArray(DOKAN_VECTOR *Vector, size_t Count) { +void DOKANAPI DokanVector_PopBackArray(DOKAN_VECTOR *Vector, size_t Count) { assert(Count <= Vector->ItemCount); @@ -197,7 +200,7 @@ void DokanVector_PopBackArray(DOKAN_VECTOR *Vector, size_t Count) { } // Clears all items in the vector. -void DokanVector_Clear(DOKAN_VECTOR *Vector) { +void DOKANAPI DokanVector_Clear(DOKAN_VECTOR *Vector) { assert(Vector); @@ -205,7 +208,7 @@ void DokanVector_Clear(DOKAN_VECTOR *Vector) { } // Retrieves the item at the specified index -void* DokanVector_GetItem(DOKAN_VECTOR *Vector, size_t Index) { +void* DOKANAPI DokanVector_GetItem(DOKAN_VECTOR *Vector, size_t Index) { assert(Vector && Index < Vector->ItemCount); @@ -218,7 +221,7 @@ void* DokanVector_GetItem(DOKAN_VECTOR *Vector, size_t Index) { } // Retrieves the last item the vector. -void* DokanVector_GetLastItem(DOKAN_VECTOR *Vector) { +void* DOKANAPI DokanVector_GetLastItem(DOKAN_VECTOR *Vector) { assert(Vector); @@ -267,7 +270,7 @@ BOOL DokanVector_Grow(DOKAN_VECTOR *Vector, size_t MinimumIncrease) { } // Retrieves the number of items in the vector. -size_t DokanVector_GetCount(DOKAN_VECTOR *Vector) { +size_t DOKANAPI DokanVector_GetCount(DOKAN_VECTOR *Vector) { assert(Vector); @@ -280,7 +283,7 @@ size_t DokanVector_GetCount(DOKAN_VECTOR *Vector) { } // Retrieves the current capacity of the vector. -size_t DokanVector_GetCapacity(DOKAN_VECTOR *Vector) { +size_t DOKANAPI DokanVector_GetCapacity(DOKAN_VECTOR *Vector) { assert(Vector); @@ -293,7 +296,7 @@ size_t DokanVector_GetCapacity(DOKAN_VECTOR *Vector) { } // Retrieves the size of items within the vector. -size_t DokanVector_GetItemSize(DOKAN_VECTOR *Vector) { +size_t DOKANAPI DokanVector_GetItemSize(DOKAN_VECTOR *Vector) { assert(Vector); diff --git a/dokan/dokan_vector.h b/dokan/dokan_vector.h index ca25f3a5a..425bd91fb 100644 --- a/dokan/dokan_vector.h +++ b/dokan/dokan_vector.h @@ -14,52 +14,49 @@ typedef struct _DOKAN_VECTOR { } DOKAN_VECTOR, *PDOKAN_VECTOR; // Creates a new instance of DOKAN_VECTOR with default values. -DOKAN_VECTOR* DokanVector_Alloc(size_t ItemSize); +DOKAN_API DOKAN_VECTOR* DOKAN_CALLBACK 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); +DOKAN_API DOKAN_VECTOR* DOKAN_CALLBACK DokanVector_AllocWithCapacity(size_t ItemSize, size_t MaxItems); // Creates a new instance of DOKAN_VECTOR with default values on the stack. -BOOL DokanVector_StackAlloc(DOKAN_VECTOR *Vector, size_t ItemSize); +BOOL DOKANAPI DokanVector_StackAlloc(DOKAN_VECTOR *Vector, size_t ItemSize); // Creates a new instance of DOKAN_VECTOR with default values on the stack. -BOOL DokanVector_StackAllocWithCapacity(DOKAN_VECTOR *Vector, size_t ItemSize, size_t MaxItems); +BOOL DOKANAPI DokanVector_StackAllocWithCapacity(DOKAN_VECTOR *Vector, size_t ItemSize, size_t MaxItems); // Releases the memory associated with a DOKAN_VECTOR; -void DokanVector_Free(DOKAN_VECTOR *Vector); +void DOKANAPI DokanVector_Free(DOKAN_VECTOR *Vector); // Appends an item to the vector. -BOOL DokanVector_PushBack(DOKAN_VECTOR *Vector, void *Item); +BOOL DOKANAPI DokanVector_PushBack(DOKAN_VECTOR *Vector, void *Item); // Appends an array of items to the vector. -BOOL DokanVector_PushBackArray(DOKAN_VECTOR *Vector, void *Items, size_t Count); +BOOL DOKANAPI DokanVector_PushBackArray(DOKAN_VECTOR *Vector, void *Items, size_t Count); // Removes an item from the end of the vector. -void DokanVector_PopBack(DOKAN_VECTOR *Vector); +void DOKANAPI DokanVector_PopBack(DOKAN_VECTOR *Vector); // Removes multiple items from the end of the vector. -void DokanVector_PopBackArray(DOKAN_VECTOR *Vector, size_t Count); +void DOKANAPI DokanVector_PopBackArray(DOKAN_VECTOR *Vector, size_t Count); // Clears all items in the vector. -void DokanVector_Clear(DOKAN_VECTOR *Vector); +void DOKANAPI DokanVector_Clear(DOKAN_VECTOR *Vector); // Retrieves the item at the specified index -void* DokanVector_GetItem(DOKAN_VECTOR *Vector, size_t Index); +DOKAN_API void* DOKAN_CALLBACK DokanVector_GetItem(DOKAN_VECTOR *Vector, size_t Index); // Retrieves the last item the vector. -void* DokanVector_GetLastItem(DOKAN_VECTOR *Vector); - -// Increases the internal capacity of the vector. -BOOL DokanVector_Grow(DOKAN_VECTOR *Vector, size_t MinimumIncrease); +DOKAN_API void* DOKAN_CALLBACK DokanVector_GetLastItem(DOKAN_VECTOR *Vector); // Retrieves the number of items in the vector. -size_t DokanVector_GetCount(DOKAN_VECTOR *Vector); +size_t DOKANAPI DokanVector_GetCount(DOKAN_VECTOR *Vector); // Retrieves the current capacity of the vector. -size_t DokanVector_GetCapacity(DOKAN_VECTOR *Vector); +size_t DOKANAPI DokanVector_GetCapacity(DOKAN_VECTOR *Vector); // Retrieves the size of items within the vector. -size_t DokanVector_GetItemSize(DOKAN_VECTOR *Vector); +size_t DOKANAPI DokanVector_GetItemSize(DOKAN_VECTOR *Vector); #ifdef __cplusplus } diff --git a/dokan/dokani.h b/dokan/dokani.h index 07662fdeb..5d1069434 100644 --- a/dokan/dokani.h +++ b/dokan/dokani.h @@ -31,7 +31,6 @@ with this program. If not, see . #include "dokan.h" #include "dokanc.h" #include "list.h" -#include "dokan_vector.h" #ifdef __cplusplus extern "C" { diff --git a/dokan/fileinfo.c b/dokan/fileinfo.c index 569752b0d..87ca66ab0 100644 --- a/dokan/fileinfo.c +++ b/dokan/fileinfo.c @@ -374,16 +374,16 @@ void BeginDispatchQueryInformation(DOKAN_IO_EVENT *EventInfo) { if(EventInfo->KernelInfo.EventContext.Operation.File.FileInformationClass == FileStreamInformation) { - EndDispatchFindStreams(findStreams, status); + DokanEndDispatchFindStreams(findStreams, status); } else { - EndDispatchGetFileInformation(getFileInfo, status); + DokanEndDispatchGetFileInformation(getFileInfo, status); } } } -void DOKANAPI EndDispatchGetFileInformation(DOKAN_GET_FILE_INFO_EVENT *EventInfo, NTSTATUS ResultStatus) { +void DOKANAPI DokanEndDispatchGetFileInformation(DOKAN_GET_FILE_INFO_EVENT *EventInfo, NTSTATUS ResultStatus) { DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; ULONG remainingLength = ioEvent->KernelInfo.EventContext.Operation.File.BufferLength; @@ -393,7 +393,7 @@ void DOKANAPI EndDispatchGetFileInformation(DOKAN_GET_FILE_INFO_EVENT *EventInfo // STATUS_PENDING should not be passed to this function if(ResultStatus == STATUS_PENDING) { - DbgPrint("Dokan Error: EndDispatchGetFileInformation() failed because STATUS_PENDING was supplied for ResultStatus.\n"); + DbgPrint("Dokan Error: DokanEndDispatchGetFileInformation() failed because STATUS_PENDING was supplied for ResultStatus.\n"); ResultStatus = STATUS_INTERNAL_ERROR; } @@ -534,7 +534,7 @@ void DOKANAPI EndDispatchGetFileInformation(DOKAN_GET_FILE_INFO_EVENT *EventInfo SendIoEventResult(ioEvent); } -void DOKANAPI EndDispatchFindStreams(DOKAN_FIND_STREAMS_EVENT *EventInfo, NTSTATUS ResultStatus) { +void DOKANAPI DokanEndDispatchFindStreams(DOKAN_FIND_STREAMS_EVENT *EventInfo, NTSTATUS ResultStatus) { DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; @@ -543,7 +543,7 @@ void DOKANAPI EndDispatchFindStreams(DOKAN_FIND_STREAMS_EVENT *EventInfo, NTSTAT // STATUS_PENDING should not be passed to this function if(ResultStatus == STATUS_PENDING) { - DbgPrint("Dokan Error: EndDispatchFindStreams() failed because STATUS_PENDING was supplied for ResultStatus.\n"); + DbgPrint("Dokan Error: DokanEndDispatchFindStreams() failed because STATUS_PENDING was supplied for ResultStatus.\n"); ResultStatus = STATUS_INTERNAL_ERROR; } diff --git a/dokan/flush.c b/dokan/flush.c index a39b9c8b2..8365a06bf 100644 --- a/dokan/flush.c +++ b/dokan/flush.c @@ -50,18 +50,18 @@ void BeginDispatchFlush(DOKAN_IO_EVENT *EventInfo) { if(status != STATUS_PENDING) { - EndDispatchFlush(flushBuffers, status); + DokanEndDispatchFlush(flushBuffers, status); } } -void DOKANAPI EndDispatchFlush(DOKAN_FLUSH_BUFFERS_EVENT *EventInfo, NTSTATUS ResultStatus) { +void DOKANAPI DokanEndDispatchFlush(DOKAN_FLUSH_BUFFERS_EVENT *EventInfo, NTSTATUS ResultStatus) { DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; // STATUS_PENDING should not be passed to this function if(ResultStatus == STATUS_PENDING) { - DbgPrint("Dokan Error: EndDispatchFlush() failed because STATUS_PENDING was supplied for ResultStatus.\n"); + DbgPrint("Dokan Error: DokanEndDispatchFlush() failed because STATUS_PENDING was supplied for ResultStatus.\n"); ResultStatus = STATUS_INTERNAL_ERROR; } diff --git a/dokan/lock.c b/dokan/lock.c index 50e817528..1043bf517 100644 --- a/dokan/lock.c +++ b/dokan/lock.c @@ -108,12 +108,12 @@ void BeginDispatchLock(DOKAN_IO_EVENT *EventInfo) { } } -void DOKANAPI EndDispatchLockFile(DOKAN_LOCK_FILE_EVENT *EventInfo, NTSTATUS ResultStatus) { +void DOKANAPI DokanEndDispatchLockFile(DOKAN_LOCK_FILE_EVENT *EventInfo, NTSTATUS ResultStatus) { EndDispatchLockCommon((DOKAN_IO_EVENT*)EventInfo, ResultStatus); } -void DOKANAPI EndDispatchUnlockFile(DOKAN_LOCK_FILE_EVENT *EventInfo, NTSTATUS ResultStatus) { +void DOKANAPI DokanEndDispatchUnlockFile(DOKAN_LOCK_FILE_EVENT *EventInfo, NTSTATUS ResultStatus) { EndDispatchLockCommon((DOKAN_IO_EVENT*)EventInfo, ResultStatus); } \ No newline at end of file diff --git a/dokan/read.c b/dokan/read.c index 903295a60..438e43d09 100644 --- a/dokan/read.c +++ b/dokan/read.c @@ -62,11 +62,11 @@ void BeginDispatchRead(DOKAN_IO_EVENT *EventInfo) { if(status != STATUS_PENDING) { - EndDispatchRead(readFileEvent, status); + DokanEndDispatchRead(readFileEvent, status); } } -void DOKANAPI EndDispatchRead(DOKAN_READ_FILE_EVENT *EventInfo, NTSTATUS ResultStatus) { +void DOKANAPI DokanEndDispatchRead(DOKAN_READ_FILE_EVENT *EventInfo, NTSTATUS ResultStatus) { DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; PEVENT_INFORMATION result = ioEvent->EventResult; @@ -74,7 +74,7 @@ void DOKANAPI EndDispatchRead(DOKAN_READ_FILE_EVENT *EventInfo, NTSTATUS ResultS // STATUS_PENDING should not be passed to this function if(ResultStatus == STATUS_PENDING) { - DbgPrint("Dokan Error: EndDispatchRead() failed because STATUS_PENDING was supplied for ResultStatus.\n"); + DbgPrint("Dokan Error: DokanEndDispatchRead() failed because STATUS_PENDING was supplied for ResultStatus.\n"); ResultStatus = STATUS_INTERNAL_ERROR; } diff --git a/dokan/security.c b/dokan/security.c index 73933e599..62155b706 100644 --- a/dokan/security.c +++ b/dokan/security.c @@ -55,11 +55,11 @@ void BeginDispatchQuerySecurity(DOKAN_IO_EVENT *EventInfo) { if(status != STATUS_PENDING) { - EndDispatchGetFileSecurity(getFileSecurity, status); + DokanEndDispatchGetFileSecurity(getFileSecurity, status); } } -void DOKANAPI EndDispatchGetFileSecurity(DOKAN_GET_FILE_SECURITY_EVENT *EventInfo, NTSTATUS ResultStatus) { +void DOKANAPI DokanEndDispatchGetFileSecurity(DOKAN_GET_FILE_SECURITY_EVENT *EventInfo, NTSTATUS ResultStatus) { DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; PEVENT_INFORMATION result = ioEvent->EventResult; @@ -67,7 +67,7 @@ void DOKANAPI EndDispatchGetFileSecurity(DOKAN_GET_FILE_SECURITY_EVENT *EventInf // STATUS_PENDING should not be passed to this function if(ResultStatus == STATUS_PENDING) { - DbgPrint("Dokan Error: EndDispatchGetFileSecurity() failed because STATUS_PENDING was supplied for ResultStatus.\n"); + DbgPrint("Dokan Error: DokanEndDispatchGetFileSecurity() failed because STATUS_PENDING was supplied for ResultStatus.\n"); ResultStatus = STATUS_INTERNAL_ERROR; } @@ -120,11 +120,11 @@ void BeginDispatchSetSecurity(DOKAN_IO_EVENT *EventInfo) { if(status != STATUS_PENDING) { - EndDispatchSetFileSecurity(setFileSecurity, status); + DokanEndDispatchSetFileSecurity(setFileSecurity, status); } } -void DOKANAPI EndDispatchSetFileSecurity(DOKAN_SET_FILE_SECURITY_EVENT *EventInfo, NTSTATUS ResultStatus) { +void DOKANAPI DokanEndDispatchSetFileSecurity(DOKAN_SET_FILE_SECURITY_EVENT *EventInfo, NTSTATUS ResultStatus) { DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; PEVENT_INFORMATION result = ioEvent->EventResult; @@ -132,7 +132,7 @@ void DOKANAPI EndDispatchSetFileSecurity(DOKAN_SET_FILE_SECURITY_EVENT *EventInf // STATUS_PENDING should not be passed to this function if(ResultStatus == STATUS_PENDING) { - DbgPrint("Dokan Error: EndDispatchSetFileSecurity() failed because STATUS_PENDING was supplied for ResultStatus.\n"); + DbgPrint("Dokan Error: DokanEndDispatchSetFileSecurity() failed because STATUS_PENDING was supplied for ResultStatus.\n"); ResultStatus = STATUS_INTERNAL_ERROR; } diff --git a/dokan/setfile.c b/dokan/setfile.c index e0f2946cf..3388863de 100644 --- a/dokan/setfile.c +++ b/dokan/setfile.c @@ -72,11 +72,11 @@ void DokanSetAllocationInformation(DOKAN_IO_EVENT *EventInfo) { if(status != STATUS_PENDING) { - EndDispatchSetAllocationSize(allocEvent, status); + DokanEndDispatchSetAllocationSize(allocEvent, status); } } -void DOKANAPI EndDispatchSetAllocationSize(DOKAN_SET_ALLOCATION_SIZE_EVENT *EventInfo, NTSTATUS ResultStatus) { +void DOKANAPI DokanEndDispatchSetAllocationSize(DOKAN_SET_ALLOCATION_SIZE_EVENT *EventInfo, NTSTATUS ResultStatus) { EndGenericSetOperation((DOKAN_IO_EVENT*)EventInfo, ResultStatus); } @@ -100,11 +100,11 @@ void DokanSetBasicInformation(DOKAN_IO_EVENT *EventInfo) { if(status != STATUS_PENDING) { - EndDispatchSetFileBasicInformation(setBasicInfo, status); + DokanEndDispatchSetFileBasicInformation(setBasicInfo, status); } } -void DOKANAPI EndDispatchSetFileBasicInformation(DOKAN_SET_FILE_BASIC_INFO_EVENT *EventInfo, NTSTATUS ResultStatus) { +void DOKANAPI DokanEndDispatchSetFileBasicInformation(DOKAN_SET_FILE_BASIC_INFO_EVENT *EventInfo, NTSTATUS ResultStatus) { EndGenericSetOperation((DOKAN_IO_EVENT*)EventInfo, ResultStatus); } @@ -122,7 +122,7 @@ void DokanSetDispositionInformation(DOKAN_IO_EVENT *EventInfo) { if(!dispositionInfo->QueryDeleteFile) { - EndDispatchCanDeleteFile(canDeleteFile, STATUS_SUCCESS); + DokanEndDispatchCanDeleteFile(canDeleteFile, STATUS_SUCCESS); return; } @@ -136,11 +136,11 @@ void DokanSetDispositionInformation(DOKAN_IO_EVENT *EventInfo) { if(status != STATUS_PENDING) { - EndDispatchCanDeleteFile(canDeleteFile, status); + DokanEndDispatchCanDeleteFile(canDeleteFile, status); } } -void DOKANAPI EndDispatchCanDeleteFile(DOKAN_CAN_DELETE_FILE_EVENT *EventInfo, NTSTATUS ResultStatus) { +void DOKANAPI DokanEndDispatchCanDeleteFile(DOKAN_CAN_DELETE_FILE_EVENT *EventInfo, NTSTATUS ResultStatus) { DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; PFILE_DISPOSITION_INFORMATION dispositionInfo = @@ -176,11 +176,11 @@ void DokanSetEndOfFileInformation(DOKAN_IO_EVENT *EventInfo) { if(status != STATUS_PENDING) { - EndDispatchSetEndOfFile(setEOF, status); + DokanEndDispatchSetEndOfFile(setEOF, status); } } -void DOKANAPI EndDispatchSetEndOfFile(DOKAN_SET_EOF_EVENT *EventInfo, NTSTATUS ResultStatus) { +void DOKANAPI DokanEndDispatchSetEndOfFile(DOKAN_SET_EOF_EVENT *EventInfo, NTSTATUS ResultStatus) { EndGenericSetOperation((DOKAN_IO_EVENT*)EventInfo, ResultStatus); } @@ -207,11 +207,11 @@ void DokanSetRenameInformation(DOKAN_IO_EVENT *EventInfo) { if(status != STATUS_PENDING) { - EndDispatchMoveFile(moveFile, status); + DokanEndDispatchMoveFile(moveFile, status); } } -void DOKANAPI EndDispatchMoveFile(DOKAN_MOVE_FILE_EVENT *EventInfo, NTSTATUS ResultStatus) { +void DOKANAPI DokanEndDispatchMoveFile(DOKAN_MOVE_FILE_EVENT *EventInfo, NTSTATUS ResultStatus) { DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; PDOKAN_RENAME_INFORMATION renameInfo = (PDOKAN_RENAME_INFORMATION)( @@ -249,7 +249,7 @@ void DokanSetValidDataLengthInformation(DOKAN_IO_EVENT *EventInfo) { if(status != STATUS_PENDING) { - EndDispatchSetEndOfFile(setEOF, status); + DokanEndDispatchSetEndOfFile(setEOF, status); } } diff --git a/dokan/volume.c b/dokan/volume.c index 3d63f6242..505dba0ea 100644 --- a/dokan/volume.c +++ b/dokan/volume.c @@ -51,7 +51,7 @@ void DokanFsVolumeInformation(DOKAN_IO_EVENT *EventInfo) { if (EventInfo->KernelInfo.EventContext.Operation.Volume.BufferLength < sizeof(FILE_FS_VOLUME_INFORMATION)) { - EndDispatchGetVolumeInfo(getVolumeInfo, STATUS_BUFFER_OVERFLOW); + DokanEndDispatchGetVolumeInfo(getVolumeInfo, STATUS_BUFFER_OVERFLOW); return; } @@ -68,11 +68,11 @@ void DokanFsVolumeInformation(DOKAN_IO_EVENT *EventInfo) { if(status != STATUS_PENDING) { - EndDispatchGetVolumeInfo(getVolumeInfo, status); + DokanEndDispatchGetVolumeInfo(getVolumeInfo, status); } } -void DOKANAPI EndDispatchGetVolumeInfo(DOKAN_GET_VOLUME_INFO_EVENT *EventInfo, NTSTATUS ResultStatus) { +void DOKANAPI DokanEndDispatchGetVolumeInfo(DOKAN_GET_VOLUME_INFO_EVENT *EventInfo, NTSTATUS ResultStatus) { DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; PEVENT_INFORMATION result = ioEvent->EventResult; @@ -83,7 +83,7 @@ void DOKANAPI EndDispatchGetVolumeInfo(DOKAN_GET_VOLUME_INFO_EVENT *EventInfo, N // STATUS_PENDING should not be passed to this function if(ResultStatus == STATUS_PENDING) { - DbgPrint("Dokan Error: EndDispatchGetVolumeInfo() failed because STATUS_PENDING was supplied for ResultStatus.\n"); + DbgPrint("Dokan Error: DokanEndDispatchGetVolumeInfo() failed because STATUS_PENDING was supplied for ResultStatus.\n"); ResultStatus = STATUS_INTERNAL_ERROR; } @@ -96,7 +96,7 @@ void DOKANAPI EndDispatchGetVolumeInfo(DOKAN_GET_VOLUME_INFO_EVENT *EventInfo, N if(volumeInfo->VolumeLabelLength > EventInfo->MaxLabelLengthInChars) { - DbgPrint("Dokan Error: EndDispatchGetVolumeInfo() failed because VolumeLabelLength is greater than MaxLabelLengthInChars.\n"); + DbgPrint("Dokan Error: DokanEndDispatchGetVolumeInfo() failed because VolumeLabelLength is greater than MaxLabelLengthInChars.\n"); ResultStatus = STATUS_BUFFER_OVERFLOW; } else { @@ -123,7 +123,7 @@ void DokanFsSizeInformation(DOKAN_IO_EVENT *EventInfo) { if (EventInfo->KernelInfo.EventContext.Operation.Volume.BufferLength < sizeof(FILE_FS_SIZE_INFORMATION)) { - EndDispatchGetVolumeFreeSpace(getFreeSpace, STATUS_BUFFER_OVERFLOW); + DokanEndDispatchGetVolumeFreeSpace(getFreeSpace, STATUS_BUFFER_OVERFLOW); return; } @@ -136,11 +136,11 @@ void DokanFsSizeInformation(DOKAN_IO_EVENT *EventInfo) { if (status != STATUS_PENDING) { - EndDispatchGetVolumeFreeSpace(getFreeSpace, status); + DokanEndDispatchGetVolumeFreeSpace(getFreeSpace, status); } } -void DOKANAPI EndDispatchGetVolumeFreeSpace(DOKAN_GET_DISK_FREE_SPACE_EVENT *EventInfo, NTSTATUS ResultStatus) { +void DOKANAPI DokanEndDispatchGetVolumeFreeSpace(DOKAN_GET_DISK_FREE_SPACE_EVENT *EventInfo, NTSTATUS ResultStatus) { DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; PEVENT_INFORMATION result = ioEvent->EventResult; @@ -150,7 +150,7 @@ void DOKANAPI EndDispatchGetVolumeFreeSpace(DOKAN_GET_DISK_FREE_SPACE_EVENT *Eve // STATUS_PENDING should not be passed to this function if(ResultStatus == STATUS_PENDING) { - DbgPrint("Dokan Error: EndDispatchGetVolumeInfo() failed because STATUS_PENDING was supplied for ResultStatus.\n"); + DbgPrint("Dokan Error: DokanEndDispatchGetVolumeInfo() failed because STATUS_PENDING was supplied for ResultStatus.\n"); ResultStatus = STATUS_INTERNAL_ERROR; } @@ -200,7 +200,7 @@ void DOKANAPI EndDispatchGetVolumeFreeSpace(DOKAN_GET_DISK_FREE_SPACE_EVENT *Eve } else { - DbgPrint("Dokan Error: EndDispatchGetVolumeFreeSpace() received an invalid FsInformationClass: 0x%x\n", + DbgPrint("Dokan Error: DokanEndDispatchGetVolumeFreeSpace() received an invalid FsInformationClass: 0x%x\n", ioEvent->KernelInfo.EventContext.Operation.Volume.FsInformationClass); ResultStatus = STATUS_INTERNAL_ERROR; @@ -219,7 +219,7 @@ void DokanFsAttributeInformation(DOKAN_IO_EVENT *EventInfo) { if (EventInfo->KernelInfo.EventContext.Operation.Volume.BufferLength < sizeof(FILE_FS_ATTRIBUTE_INFORMATION)) { - EndDispatchGetVolumeAttributes(getAttributes, STATUS_BUFFER_OVERFLOW); + DokanEndDispatchGetVolumeAttributes(getAttributes, STATUS_BUFFER_OVERFLOW); return; } @@ -235,11 +235,11 @@ void DokanFsAttributeInformation(DOKAN_IO_EVENT *EventInfo) { if (status != STATUS_PENDING) { - EndDispatchGetVolumeAttributes(getAttributes, status); + DokanEndDispatchGetVolumeAttributes(getAttributes, status); } } -void DOKANAPI EndDispatchGetVolumeAttributes(DOKAN_GET_VOLUME_ATTRIBUTES_EVENT *EventInfo, NTSTATUS ResultStatus) { +void DOKANAPI DokanEndDispatchGetVolumeAttributes(DOKAN_GET_VOLUME_ATTRIBUTES_EVENT *EventInfo, NTSTATUS ResultStatus) { DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; PEVENT_INFORMATION result = ioEvent->EventResult; @@ -248,7 +248,7 @@ void DOKANAPI EndDispatchGetVolumeAttributes(DOKAN_GET_VOLUME_ATTRIBUTES_EVENT * // STATUS_PENDING should not be passed to this function if(ResultStatus == STATUS_PENDING) { - DbgPrint("Dokan Error: EndDispatchGetVolumeAttributes() failed because STATUS_PENDING was supplied for ResultStatus.\n"); + DbgPrint("Dokan Error: DokanEndDispatchGetVolumeAttributes() failed because STATUS_PENDING was supplied for ResultStatus.\n"); ResultStatus = STATUS_INTERNAL_ERROR; } @@ -269,7 +269,7 @@ void DOKANAPI EndDispatchGetVolumeAttributes(DOKAN_GET_VOLUME_ATTRIBUTES_EVENT * if(freeSpaceInfo->FileSystemNameLength > EventInfo->MaxFileSystemNameLengthInChars) { - DbgPrint("Dokan Error: EndDispatchGetVolumeAttributes() failed because FileSystemNameLength is greater than MaxFileSystemNameLengthInChars.\n"); + DbgPrint("Dokan Error: DokanEndDispatchGetVolumeAttributes() failed because FileSystemNameLength is greater than MaxFileSystemNameLengthInChars.\n"); ResultStatus = STATUS_BUFFER_OVERFLOW; } @@ -294,7 +294,7 @@ void DokanFsFullSizeInformation(DOKAN_IO_EVENT *EventInfo) { if(EventInfo->KernelInfo.EventContext.Operation.Volume.BufferLength < sizeof(FILE_FS_FULL_SIZE_INFORMATION)) { - EndDispatchGetVolumeFreeSpace(getFreeSpace, STATUS_BUFFER_OVERFLOW); + DokanEndDispatchGetVolumeFreeSpace(getFreeSpace, STATUS_BUFFER_OVERFLOW); return; } @@ -307,7 +307,7 @@ void DokanFsFullSizeInformation(DOKAN_IO_EVENT *EventInfo) { if(status != STATUS_PENDING) { - EndDispatchGetVolumeFreeSpace(getFreeSpace, status); + DokanEndDispatchGetVolumeFreeSpace(getFreeSpace, status); } } diff --git a/dokan/write.c b/dokan/write.c index d86abf77c..691dbdcd9 100644 --- a/dokan/write.c +++ b/dokan/write.c @@ -111,7 +111,7 @@ void BeginDispatchWrite(DOKAN_IO_EVENT *EventInfo) { if((status = SendWriteRequest(EventInfo)) != STATUS_SUCCESS) { - EndDispatchWrite(writeFileEvent, status); + DokanEndDispatchWrite(writeFileEvent, status); } return; @@ -142,11 +142,11 @@ void BeginDispatchWrite(DOKAN_IO_EVENT *EventInfo) { if(status != STATUS_PENDING) { - EndDispatchWrite(writeFileEvent, status); + DokanEndDispatchWrite(writeFileEvent, status); } } -void DOKANAPI EndDispatchWrite(DOKAN_WRITE_FILE_EVENT *EventInfo, NTSTATUS ResultStatus) { +void DOKANAPI DokanEndDispatchWrite(DOKAN_WRITE_FILE_EVENT *EventInfo, NTSTATUS ResultStatus) { DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; PEVENT_INFORMATION result; @@ -156,7 +156,7 @@ void DOKANAPI EndDispatchWrite(DOKAN_WRITE_FILE_EVENT *EventInfo, NTSTATUS Resul // STATUS_PENDING should not be passed to this function if(ResultStatus == STATUS_PENDING) { - DbgPrint("Dokan Error: EndDispatchWrite() failed because STATUS_PENDING was supplied for ResultStatus.\n"); + DbgPrint("Dokan Error: DokanEndDispatchWrite() failed because STATUS_PENDING was supplied for ResultStatus.\n"); ResultStatus = STATUS_INTERNAL_ERROR; } diff --git a/samples/dokan_mirror/mirror.c b/samples/dokan_mirror/mirror.c index 9279dd6a5..a507713f3 100644 --- a/samples/dokan_mirror/mirror.c +++ b/samples/dokan_mirror/mirror.c @@ -33,6 +33,47 @@ THE SOFTWARE. #include #include +#define USE_ASYNC_IO 1 + +#if USE_ASYNC_IO + +TP_CALLBACK_ENVIRON g_ThreadPoolEnvironment; +PTP_CLEANUP_GROUP g_ThreadPoolCleanupGroup = NULL; +PTP_POOL g_ThreadPool = NULL; + +DOKAN_VECTOR g_OverlappedPool; +CRITICAL_SECTION g_OverlappedPoolCS; + +typedef struct _MIRROR_OVERLAPPED { + OVERLAPPED InternalOverlapped; +} MIRROR_OVERLAPPED; + +void CALLBACK MirrorIoCallback( + _Inout_ PTP_CALLBACK_INSTANCE Instance, + _Inout_opt_ PVOID Context, + _Inout_opt_ PVOID Overlapped, + _In_ ULONG IoResult, + _In_ ULONG_PTR NumberOfBytesTransferred, + _Inout_ PTP_IO Io +); + +#endif + +typedef struct _MIRROR_FILE_HANDLE { + + HANDLE FileHandle; + +#if USE_ASYNC_IO + PTP_IO IoCompletion; +#endif + +} MIRROR_FILE_HANDLE; + +#define MIRROR_HANDLE_ASSERT(mirrorHandle) assert((mirrorHandle) && (mirrorHandle)->FileHandle && (mirrorHandle)->FileHandle != INVALID_HANDLE_VALUE) + +DOKAN_VECTOR g_FileHandlePool; +CRITICAL_SECTION g_FileHandlePoolCS; + BOOL g_UseStdErr; BOOL g_DebugMode; @@ -79,6 +120,88 @@ static void DbgPrint(LPCWSTR format, ...) { } } +/////////////////// MIRROR_FILE_HANDLE /////////////////// + +void FreeMirrorFileHandle(MIRROR_FILE_HANDLE *FileHandle) { + + if(FileHandle) { + +#if USE_ASYNC_IO + if(FileHandle->IoCompletion) { + + CloseThreadpoolIo(FileHandle->IoCompletion); + FileHandle->IoCompletion = NULL; + } +#endif + + free(FileHandle); + } +} + +void PushMirrorFileHandle(MIRROR_FILE_HANDLE *FileHandle) { + + assert(FileHandle); + +#if USE_ASYNC_IO + if(FileHandle->IoCompletion) { + + CloseThreadpoolIo(FileHandle->IoCompletion); + FileHandle->IoCompletion = NULL; + } +#endif + + EnterCriticalSection(&g_FileHandlePoolCS); + + DokanVector_PushBack(&g_FileHandlePool, &FileHandle); + + LeaveCriticalSection(&g_FileHandlePoolCS); +} + +MIRROR_FILE_HANDLE* PopMirrorFileHandle(HANDLE ActualFileHandle) { + + if(ActualFileHandle == NULL || ActualFileHandle == INVALID_HANDLE_VALUE) { + + return NULL; + } + + MIRROR_FILE_HANDLE *mirrorHandle = NULL; + + EnterCriticalSection(&g_FileHandlePoolCS); + + if(DokanVector_GetCount(&g_FileHandlePool) > 0) + { + mirrorHandle = *(MIRROR_FILE_HANDLE**)DokanVector_GetLastItem(&g_FileHandlePool); + DokanVector_PopBack(&g_FileHandlePool); + } + + LeaveCriticalSection(&g_FileHandlePoolCS); + + if(!mirrorHandle) { + + mirrorHandle = (MIRROR_FILE_HANDLE*)malloc(sizeof(MIRROR_FILE_HANDLE)); + } + + if(mirrorHandle) { + + RtlZeroMemory(mirrorHandle, sizeof(MIRROR_FILE_HANDLE)); + mirrorHandle->FileHandle = ActualFileHandle; + +#if USE_ASYNC_IO + mirrorHandle->IoCompletion = CreateThreadpoolIo(ActualFileHandle, MirrorIoCallback, mirrorHandle, &g_ThreadPoolEnvironment); + + if(!mirrorHandle->IoCompletion) { + + PushMirrorFileHandle(mirrorHandle); + mirrorHandle = NULL; + } +#endif + } + + return mirrorHandle; +} + +/////////////////// Push/Pop pattern finished /////////////////// + static WCHAR RootDirectory[MAX_PATH] = L"C:"; static WCHAR MountPoint[MAX_PATH] = L"M:\\"; static WCHAR UNCName[MAX_PATH] = L""; @@ -308,6 +431,10 @@ MirrorCreateFile(DOKAN_CREATE_FILE_EVENT *EventInfo) { MirrorCheckFlag(fileAttributesAndFlags, SECURITY_EFFECTIVE_ONLY); MirrorCheckFlag(fileAttributesAndFlags, SECURITY_SQOS_PRESENT); +#if USE_ASYNC_IO + fileAttributesAndFlags |= FILE_FLAG_OVERLAPPED; +#endif + if (creationDisposition == CREATE_NEW) { DbgPrint(L"\tCREATE_NEW\n"); } else if (creationDisposition == OPEN_ALWAYS) { @@ -323,49 +450,78 @@ MirrorCreateFile(DOKAN_CREATE_FILE_EVENT *EventInfo) { } if (EventInfo->DokanFileInfo->IsDirectory) { + // It is a create directory request if (creationDisposition == CREATE_NEW) { + if (!CreateDirectory(filePath, &securityAttrib)) { + error = GetLastError(); DbgPrint(L"\terror code = %d\n\n", error); status = DokanNtStatusFromWin32(error); } - } else if (creationDisposition == OPEN_ALWAYS) { + } + else if (creationDisposition == OPEN_ALWAYS) { if (!CreateDirectory(filePath, &securityAttrib)) { error = GetLastError(); if (error != ERROR_ALREADY_EXISTS) { + DbgPrint(L"\terror code = %d\n\n", error); status = DokanNtStatusFromWin32(error); } } } + if (status == STATUS_SUCCESS) { + // FILE_FLAG_BACKUP_SEMANTICS is required for opening directory handles + handle = CreateFile( filePath, EventInfo->DesiredAccess, EventInfo->ShareAccess, &securityAttrib, OPEN_EXISTING, fileAttributesAndFlags | FILE_FLAG_BACKUP_SEMANTICS, NULL); if (handle == INVALID_HANDLE_VALUE) { + error = GetLastError(); DbgPrint(L"\terror code = %d\n\n", error); status = DokanNtStatusFromWin32(error); - } else { - EventInfo->DokanFileInfo->Context = - (ULONG64)handle; // save the file handle in Context + } + else { + + MIRROR_FILE_HANDLE *mirrorHandle = PopMirrorFileHandle(handle); + + if(!mirrorHandle) { + + DbgPrint(L"\tFailed to create MIRROR_FILE_HANDLE\n"); + + SetLastError(ERROR_INTERNAL_ERROR); + + CloseHandle(handle); + + return STATUS_INTERNAL_ERROR; + } + + // save the file handle in Context + EventInfo->DokanFileInfo->Context = (ULONG64)mirrorHandle; } } - } else { + } + else { + // It is a create file request - if (fileAttr != INVALID_FILE_ATTRIBUTES && - (fileAttr & FILE_ATTRIBUTE_DIRECTORY) && - EventInfo->CreateDisposition == FILE_CREATE) - return STATUS_OBJECT_NAME_COLLISION; // File already exist because - // GetFileAttributes found it + if(fileAttr != INVALID_FILE_ATTRIBUTES && + (fileAttr & FILE_ATTRIBUTE_DIRECTORY) && + EventInfo->CreateDisposition == FILE_CREATE) { + + return STATUS_OBJECT_NAME_COLLISION; // File already exist because + // GetFileAttributes found it + } + handle = CreateFile(filePath, EventInfo->DesiredAccess, // GENERIC_READ|GENERIC_WRITE|GENERIC_EXECUTE, @@ -376,18 +532,37 @@ MirrorCreateFile(DOKAN_CREATE_FILE_EVENT *EventInfo) { NULL); // template file handle if (handle == INVALID_HANDLE_VALUE) { + error = GetLastError(); DbgPrint(L"\terror code = %d\n\n", error); status = DokanNtStatusFromWin32(error); - } else { - EventInfo->DokanFileInfo->Context = - (ULONG64)handle; // save the file handle in Context + } + else { + + MIRROR_FILE_HANDLE *mirrorHandle = PopMirrorFileHandle(handle); + + if(!mirrorHandle) { + + DbgPrint(L"\tFailed to create MIRROR_FILE_HANDLE\n"); + + SetLastError(ERROR_INTERNAL_ERROR); + + CloseHandle(handle); + + return STATUS_INTERNAL_ERROR; + } + + // save the file handle in Context + EventInfo->DokanFileInfo->Context = (ULONG64)mirrorHandle; if (creationDisposition == OPEN_ALWAYS || creationDisposition == CREATE_ALWAYS) { + error = GetLastError(); + if (error == ERROR_ALREADY_EXISTS) { + DbgPrint(L"\tOpen an already existing file\n"); SetLastError(ERROR_ALREADY_EXISTS); // Inform the driver that we have // open a already existing file @@ -398,6 +573,7 @@ MirrorCreateFile(DOKAN_CREATE_FILE_EVENT *EventInfo) { } DbgPrint(L"\n"); + return status; } @@ -405,168 +581,189 @@ MirrorCreateFile(DOKAN_CREATE_FILE_EVENT *EventInfo) { #pragma warning(disable : 4305) static void DOKAN_CALLBACK MirrorCloseFile(DOKAN_CLOSE_FILE_EVENT *EventInfo) { + WCHAR filePath[MAX_PATH]; + MIRROR_FILE_HANDLE *mirrorHandle = (MIRROR_FILE_HANDLE*)EventInfo->DokanFileInfo->Context; + GetFilePath(filePath, MAX_PATH, EventInfo->FileName); - if (EventInfo->DokanFileInfo->Context) { - DbgPrint(L"CloseFile: %s\n", filePath); - DbgPrint(L"\terror : not cleanuped file\n\n"); - CloseHandle((HANDLE)EventInfo->DokanFileInfo->Context); + DbgPrint(L"CloseFile: %s\n", filePath); + + MIRROR_HANDLE_ASSERT(mirrorHandle); + + if (mirrorHandle) { + + CloseHandle(mirrorHandle->FileHandle); + + mirrorHandle->FileHandle = NULL; EventInfo->DokanFileInfo->Context = 0; - } else { - DbgPrint(L"Close: %s\n\n", filePath); + + if(EventInfo->DokanFileInfo->DeleteOnClose) { + + // Should already be deleted by CloseHandle + // if open with FILE_FLAG_DELETE_ON_CLOSE + DbgPrint(L"\tDeleteOnClose\n"); + + if(EventInfo->DokanFileInfo->IsDirectory) { + + DbgPrint(L" DeleteDirectory "); + + if(!RemoveDirectory(filePath)) { + + DbgPrint(L"error code = %d\n\n", GetLastError()); + } + else { + DbgPrint(L"success\n\n"); + } + } + else { + + DbgPrint(L" DeleteFile "); + + if(DeleteFile(filePath) == 0) { + + DbgPrint(L" error code = %d\n\n", GetLastError()); + } + else { + + DbgPrint(L"success\n\n"); + } + } + } } } static void DOKAN_CALLBACK MirrorCleanup(DOKAN_CLEANUP_EVENT *EventInfo) { + WCHAR filePath[MAX_PATH]; GetFilePath(filePath, MAX_PATH, EventInfo->FileName); - if (EventInfo->DokanFileInfo->Context) { - DbgPrint(L"Cleanup: %s\n\n", filePath); - CloseHandle((HANDLE)(EventInfo->DokanFileInfo->Context)); - EventInfo->DokanFileInfo->Context = 0; - } else { - DbgPrint(L"Cleanup: %s\n\tinvalid handle\n\n", filePath); - } - - if (EventInfo->DokanFileInfo->DeleteOnClose) { - // Should already be deleted by CloseHandle - // if open with FILE_FLAG_DELETE_ON_CLOSE - DbgPrint(L"\tDeleteOnClose\n"); - if (EventInfo->DokanFileInfo->IsDirectory) { - DbgPrint(L" DeleteDirectory "); - if (!RemoveDirectory(filePath)) { - DbgPrint(L"error code = %d\n\n", GetLastError()); - } else { - DbgPrint(L"success\n\n"); - } - } else { - DbgPrint(L" DeleteFile "); - if (DeleteFile(filePath) == 0) { - DbgPrint(L" error code = %d\n\n", GetLastError()); - } else { - DbgPrint(L"success\n\n"); - } - } - } + DbgPrint(L"Cleanup: %s\n\n", filePath); + + /* This gets called BEFORE MirrorCloseFile(). Per the documentation: + * + * Receipt of the IRP_MJ_CLEANUP request indicates that the handle reference count on a file object has reached zero. + * (In other words, all handles to the file object have been closed.) + * Often it is sent when a user-mode application has called the Microsoft Win32 CloseHandle function + * (or when a kernel-mode driver has called ZwClose) on the last outstanding handle to a file object. + * + * It is important to note that when all handles to a file object have been closed, this does not necessarily + * mean that the file object is no longer being used. System components, such as the Cache Manager and the Memory Manager, + * might hold outstanding references to the file object. These components can still read to or write from a file, even + * after an IRP_MJ_CLEANUP request is received. + */ } static NTSTATUS DOKAN_CALLBACK MirrorReadFile(DOKAN_READ_FILE_EVENT *EventInfo) { WCHAR filePath[MAX_PATH]; - HANDLE handle = (HANDLE)EventInfo->DokanFileInfo->Context; + MIRROR_FILE_HANDLE *mirrorHandle = (MIRROR_FILE_HANDLE*)EventInfo->DokanFileInfo->Context; ULONG offset = (ULONG)EventInfo->Offset; - BOOL opened = FALSE; GetFilePath(filePath, MAX_PATH, EventInfo->FileName); DbgPrint(L"ReadFile : %s\n", filePath); - if (!handle || handle == INVALID_HANDLE_VALUE) { - DbgPrint(L"\tinvalid handle, cleanuped?\n"); - handle = CreateFile(filePath, GENERIC_READ, FILE_SHARE_READ, NULL, - OPEN_EXISTING, 0, NULL); - if (handle == INVALID_HANDLE_VALUE) { - DWORD error = GetLastError(); - DbgPrint(L"\tCreateFile error : %d\n\n", error); - return DokanNtStatusFromWin32(error); - } - opened = TRUE; + if(!mirrorHandle) { + + return STATUS_FILE_CLOSED; } LARGE_INTEGER distanceToMove; + distanceToMove.QuadPart = EventInfo->Offset; - if (!SetFilePointerEx(handle, distanceToMove, NULL, FILE_BEGIN)) { + + if (!SetFilePointerEx(mirrorHandle->FileHandle, distanceToMove, NULL, FILE_BEGIN)) { + DWORD error = GetLastError(); + DbgPrint(L"\tseek error, offset = %d\n\n", offset); - if (opened) - CloseHandle(handle); + return DokanNtStatusFromWin32(error); } - if (!ReadFile(handle, EventInfo->Buffer, EventInfo->NumberOfBytesToRead, &EventInfo->NumberOfBytesRead, NULL)) { + if (!ReadFile(mirrorHandle->FileHandle, + EventInfo->Buffer, + EventInfo->NumberOfBytesToRead, + &EventInfo->NumberOfBytesRead, + NULL)) { + DWORD error = GetLastError(); + DbgPrint(L"\tread error = %u, buffer length = %d, read length = %d\n\n", error, EventInfo->NumberOfBytesToRead, EventInfo->NumberOfBytesRead); - if (opened) - CloseHandle(handle); + return DokanNtStatusFromWin32(error); + } + else { - } else { DbgPrint(L"\tByte to read: %d, Byte read %d, offset %d\n\n", EventInfo->NumberOfBytesToRead, EventInfo->NumberOfBytesRead, offset); } - if (opened) - CloseHandle(handle); - return STATUS_SUCCESS; } static NTSTATUS DOKAN_CALLBACK MirrorWriteFile(DOKAN_WRITE_FILE_EVENT *EventInfo) { WCHAR filePath[MAX_PATH]; - HANDLE handle = (HANDLE)EventInfo->DokanFileInfo->Context; + MIRROR_FILE_HANDLE *mirrorHandle = (MIRROR_FILE_HANDLE*)EventInfo->DokanFileInfo->Context; ULONG offset = (ULONG)EventInfo->Offset; - BOOL opened = FALSE; GetFilePath(filePath, MAX_PATH, EventInfo->FileName); DbgPrint(L"WriteFile : %s, offset %I64d, length %d\n", filePath, EventInfo->Offset, EventInfo->NumberOfBytesToWrite); - // reopen the file - if (!handle || handle == INVALID_HANDLE_VALUE) { - DbgPrint(L"\tinvalid handle, cleanuped?\n"); - handle = CreateFile(filePath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, - OPEN_EXISTING, 0, NULL); - if (handle == INVALID_HANDLE_VALUE) { - DWORD error = GetLastError(); - DbgPrint(L"\tCreateFile error : %d\n\n", error); - return DokanNtStatusFromWin32(error); - } - opened = TRUE; + if(!mirrorHandle) { + + return STATUS_FILE_CLOSED; } LARGE_INTEGER distanceToMove; distanceToMove.QuadPart = EventInfo->Offset; if (EventInfo->DokanFileInfo->WriteToEndOfFile) { + LARGE_INTEGER z; z.QuadPart = 0; - if (!SetFilePointerEx(handle, z, NULL, FILE_END)) { + + if (!SetFilePointerEx(mirrorHandle->FileHandle, z, NULL, FILE_END)) { + DWORD error = GetLastError(); + DbgPrint(L"\tseek error, offset = EOF, error = %d\n", error); - if (opened) - CloseHandle(handle); + return DokanNtStatusFromWin32(error); } - } else if (!SetFilePointerEx(handle, distanceToMove, NULL, FILE_BEGIN)) { + } + else if (!SetFilePointerEx(mirrorHandle->FileHandle, distanceToMove, NULL, FILE_BEGIN)) { + DWORD error = GetLastError(); + DbgPrint(L"\tseek error, offset = %d, error = %d\n", offset, error); - if (opened) - CloseHandle(handle); + return DokanNtStatusFromWin32(error); } - if (!WriteFile(handle, EventInfo->Buffer, EventInfo->NumberOfBytesToWrite, &EventInfo->NumberOfBytesWritten, - NULL)) { + if (!WriteFile(mirrorHandle->FileHandle, + EventInfo->Buffer, + EventInfo->NumberOfBytesToWrite, + &EventInfo->NumberOfBytesWritten, + NULL)) { + DWORD error = GetLastError(); + DbgPrint(L"\twrite error = %u, buffer length = %d, write length = %d\n", error, EventInfo->NumberOfBytesToWrite, EventInfo->NumberOfBytesWritten); - if (opened) - CloseHandle(handle); + return DokanNtStatusFromWin32(error); + } + else { - } else { DbgPrint(L"\twrite %d, offset %d\n\n", EventInfo->NumberOfBytesWritten, offset); } - // close the file when it is reopened - if (opened) - CloseHandle(handle); - return STATUS_SUCCESS; } @@ -574,22 +771,27 @@ static NTSTATUS DOKAN_CALLBACK MirrorFlushFileBuffers(DOKAN_FLUSH_BUFFERS_EVENT *EventInfo) { WCHAR filePath[MAX_PATH]; - HANDLE handle = (HANDLE)EventInfo->DokanFileInfo->Context; + MIRROR_FILE_HANDLE *mirrorHandle = (MIRROR_FILE_HANDLE*)EventInfo->DokanFileInfo->Context; GetFilePath(filePath, MAX_PATH, EventInfo->FileName); DbgPrint(L"FlushFileBuffers : %s\n", filePath); - if (!handle || handle == INVALID_HANDLE_VALUE) { - DbgPrint(L"\tinvalid handle\n\n"); - return STATUS_SUCCESS; + if(!mirrorHandle) { + + return STATUS_FILE_CLOSED; } - if (FlushFileBuffers(handle)) { + if (FlushFileBuffers(mirrorHandle->FileHandle)) { + return STATUS_SUCCESS; - } else { + } + else { + DWORD error = GetLastError(); - DbgPrint(L"\tflush error code = %d\n", error); + + DbgPrint(L"\tflush error code = %d\n", error); + return DokanNtStatusFromWin32(error); } } @@ -597,35 +799,42 @@ MirrorFlushFileBuffers(DOKAN_FLUSH_BUFFERS_EVENT *EventInfo) { static NTSTATUS DOKAN_CALLBACK MirrorGetFileInformation(DOKAN_GET_FILE_INFO_EVENT *EventInfo) { WCHAR filePath[MAX_PATH]; - HANDLE handle = (HANDLE)EventInfo->DokanFileInfo->Context; + MIRROR_FILE_HANDLE *mirrorHandle = (MIRROR_FILE_HANDLE*)EventInfo->DokanFileInfo->Context; GetFilePath(filePath, MAX_PATH, EventInfo->FileName); DbgPrint(L"GetFileInfo : %s\n", filePath); - if (!handle || handle == INVALID_HANDLE_VALUE) { - DbgPrint(L"\tinvalid handle\n\n"); - return STATUS_INVALID_PARAMETER; - } + MIRROR_HANDLE_ASSERT(mirrorHandle); + + if (!GetFileInformationByHandle(mirrorHandle->FileHandle, &EventInfo->FileHandleInfo)) { - if (!GetFileInformationByHandle(handle, &EventInfo->FileHandleInfo)) { DbgPrint(L"\terror code = %d\n", GetLastError()); // FileName is a root directory // in this case, FindFirstFile can't get directory information if (wcslen(EventInfo->FileName) == 1) { + DbgPrint(L" root dir\n"); EventInfo->FileHandleInfo.dwFileAttributes = GetFileAttributes(filePath); - } else { + } + else { + WIN32_FIND_DATAW find; ZeroMemory(&find, sizeof(WIN32_FIND_DATAW)); + HANDLE findHandle = FindFirstFile(filePath, &find); + if (findHandle == INVALID_HANDLE_VALUE) { + DWORD error = GetLastError(); + DbgPrint(L"\tFindFirstFile error code = %d\n\n", error); + return DokanNtStatusFromWin32(error); } + EventInfo->FileHandleInfo.dwFileAttributes = find.dwFileAttributes; EventInfo->FileHandleInfo.ftCreationTime = find.ftCreationTime; EventInfo->FileHandleInfo.ftLastAccessTime = find.ftLastAccessTime; @@ -637,7 +846,9 @@ static NTSTATUS DOKAN_CALLBACK MirrorGetFileInformation(DOKAN_GET_FILE_INFO_EVEN FindClose(findHandle); } - } else { + } + else { + DbgPrint(L"\tGetFileInformationByHandle success, file size = %d\n", EventInfo->FileHandleInfo.nFileSizeLow); } @@ -762,7 +973,6 @@ static NTSTATUS DOKAN_CALLBACK MirrorCanDeleteFile(DOKAN_CAN_DELETE_FILE_EVENT *EventInfo) { WCHAR filePath[MAX_PATH]; - // HANDLE handle = (HANDLE)DokanFileInfo->Context; GetFilePath(filePath, MAX_PATH, EventInfo->FileName); DbgPrint(L"CanDeleteFile %s\n", filePath); @@ -787,7 +997,7 @@ MirrorMoveFile(DOKAN_MOVE_FILE_EVENT *EventInfo) { WCHAR filePath[MAX_PATH]; WCHAR newFilePath[MAX_PATH]; - HANDLE handle; + MIRROR_FILE_HANDLE *mirrorHandle = (MIRROR_FILE_HANDLE*)EventInfo->DokanFileInfo->Context; DWORD bufferSize; BOOL result; size_t newFilePathLen; @@ -798,12 +1008,8 @@ MirrorMoveFile(DOKAN_MOVE_FILE_EVENT *EventInfo) { GetFilePath(newFilePath, MAX_PATH, EventInfo->NewFileName); DbgPrint(L"MoveFile %s -> %s\n\n", filePath, newFilePath); - handle = (HANDLE)EventInfo->DokanFileInfo->Context; - if (!handle || handle == INVALID_HANDLE_VALUE) { - DbgPrint(L"\tinvalid handle\n\n"); - return STATUS_INVALID_HANDLE; - } + MIRROR_HANDLE_ASSERT(mirrorHandle); newFilePathLen = wcslen(newFilePath); @@ -815,32 +1021,41 @@ MirrorMoveFile(DOKAN_MOVE_FILE_EVENT *EventInfo) { newFilePathLen * sizeof(newFilePath[0])); renameInfo = (PFILE_RENAME_INFO)malloc(bufferSize); + if (!renameInfo) { + return STATUS_BUFFER_OVERFLOW; } + ZeroMemory(renameInfo, bufferSize); renameInfo->ReplaceIfExists = EventInfo->ReplaceIfExists ? TRUE : FALSE; // some warning about converting BOOL to BOOLEAN + renameInfo->RootDirectory = NULL; // hope it is never needed, shouldn't be + renameInfo->FileNameLength = (DWORD)newFilePathLen * sizeof(newFilePath[0]); // they want length in bytes wcscpy_s(renameInfo->FileName, newFilePathLen + 1, newFilePath); - result = SetFileInformationByHandle(handle, FileRenameInfo, renameInfo, - bufferSize); + result = SetFileInformationByHandle(mirrorHandle->FileHandle, FileRenameInfo, renameInfo, bufferSize); free(renameInfo); if (result) { + return STATUS_SUCCESS; - } else { + } + else { + DWORD error = GetLastError(); - DbgPrint(L"\tMoveFile error = %u\n", error); + + DbgPrint(L"\tMoveFile error = %u\n", error); + return DokanNtStatusFromWin32(error); } } @@ -848,7 +1063,7 @@ MirrorMoveFile(DOKAN_MOVE_FILE_EVENT *EventInfo) { static NTSTATUS DOKAN_CALLBACK MirrorLockFile(DOKAN_LOCK_FILE_EVENT *EventInfo) { WCHAR filePath[MAX_PATH]; - HANDLE handle; + MIRROR_FILE_HANDLE *mirrorHandle = (MIRROR_FILE_HANDLE*)EventInfo->DokanFileInfo->Context; LARGE_INTEGER offset; LARGE_INTEGER length; @@ -856,20 +1071,17 @@ static NTSTATUS DOKAN_CALLBACK MirrorLockFile(DOKAN_LOCK_FILE_EVENT *EventInfo) DbgPrint(L"LockFile %s\n", filePath); - handle = (HANDLE)EventInfo->DokanFileInfo->Context; - if (!handle || handle == INVALID_HANDLE_VALUE) { - DbgPrint(L"\tinvalid handle\n\n"); - return STATUS_INVALID_HANDLE; - } + MIRROR_HANDLE_ASSERT(mirrorHandle); length.QuadPart = EventInfo->Length; offset.QuadPart = EventInfo->ByteOffset; - if (!LockFile(handle, offset.LowPart, offset.HighPart, length.LowPart, - length.HighPart)) { + if (!LockFile(mirrorHandle->FileHandle, offset.LowPart, offset.HighPart, length.LowPart, length.HighPart)) { DWORD error = GetLastError(); + DbgPrint(L"\terror code = %d\n\n", error); + return DokanNtStatusFromWin32(error); } @@ -881,30 +1093,33 @@ static NTSTATUS DOKAN_CALLBACK MirrorLockFile(DOKAN_LOCK_FILE_EVENT *EventInfo) static NTSTATUS DOKAN_CALLBACK MirrorSetEndOfFile(DOKAN_SET_EOF_EVENT *EventInfo) { WCHAR filePath[MAX_PATH]; - HANDLE handle; + MIRROR_FILE_HANDLE *mirrorHandle = (MIRROR_FILE_HANDLE*)EventInfo->DokanFileInfo->Context; LARGE_INTEGER offset; GetFilePath(filePath, MAX_PATH, EventInfo->FileName); DbgPrint(L"SetEndOfFile %s, %I64d\n", filePath, EventInfo->Length); - handle = (HANDLE)EventInfo->DokanFileInfo->Context; - if (!handle || handle == INVALID_HANDLE_VALUE) { - DbgPrint(L"\tinvalid handle\n\n"); - return STATUS_INVALID_HANDLE; - } + MIRROR_HANDLE_ASSERT(mirrorHandle); offset.QuadPart = EventInfo->Length; - if (!SetFilePointerEx(handle, offset, NULL, FILE_BEGIN)) { + + if (!SetFilePointerEx(mirrorHandle->FileHandle, offset, NULL, FILE_BEGIN)) { + DWORD error = GetLastError(); + DbgPrint(L"\tSetFilePointer error: %d, offset = %I64d\n\n", error, EventInfo->Length); + return DokanNtStatusFromWin32(error); } - if (!SetEndOfFile(handle)) { + if (!SetEndOfFile(mirrorHandle->FileHandle)) { + DWORD error = GetLastError(); + DbgPrint(L"\tSetEndOfFile error code = %d\n\n", error); + return DokanNtStatusFromWin32(error); } @@ -914,29 +1129,24 @@ static NTSTATUS DOKAN_CALLBACK MirrorSetEndOfFile(DOKAN_SET_EOF_EVENT *EventInfo static NTSTATUS DOKAN_CALLBACK MirrorSetAllocationSize(DOKAN_SET_ALLOCATION_SIZE_EVENT *EventInfo) { WCHAR filePath[MAX_PATH]; - HANDLE handle; + MIRROR_FILE_HANDLE *mirrorHandle = (MIRROR_FILE_HANDLE*)EventInfo->DokanFileInfo->Context; LARGE_INTEGER fileSize; GetFilePath(filePath, MAX_PATH, EventInfo->FileName); DbgPrint(L"SetAllocationSize %s, %I64d\n", filePath, EventInfo->Length); - handle = (HANDLE)EventInfo->DokanFileInfo->Context; - - if (!handle || handle == INVALID_HANDLE_VALUE) { - DbgPrint(L"\tinvalid handle\n\n"); - return STATUS_INVALID_HANDLE; - } + MIRROR_HANDLE_ASSERT(mirrorHandle); - if (GetFileSizeEx(handle, &fileSize)) { + if (GetFileSizeEx(mirrorHandle->FileHandle, &fileSize)) { if (EventInfo->Length < fileSize.QuadPart) { fileSize.QuadPart = EventInfo->Length; - if (!SetFilePointerEx(handle, fileSize, NULL, FILE_BEGIN)) { + if (!SetFilePointerEx(mirrorHandle->FileHandle, fileSize, NULL, FILE_BEGIN)) { - DWORD error = GetLastError(); + DWORD error = GetLastError(); DbgPrint(L"\tSetAllocationSize: SetFilePointer eror: %d, " L"offset = %I64d\n\n", @@ -945,9 +1155,10 @@ static NTSTATUS DOKAN_CALLBACK MirrorSetAllocationSize(DOKAN_SET_ALLOCATION_SIZE return DokanNtStatusFromWin32(error); } - if (!SetEndOfFile(handle)) { + if (!SetEndOfFile(mirrorHandle->FileHandle)) { - DWORD error = GetLastError(); + DWORD error = GetLastError(); + DbgPrint(L"\tSetEndOfFile error code = %d\n\n", error); return DokanNtStatusFromWin32(error); @@ -955,8 +1166,11 @@ static NTSTATUS DOKAN_CALLBACK MirrorSetAllocationSize(DOKAN_SET_ALLOCATION_SIZE } } else { + DWORD error = GetLastError(); + DbgPrint(L"\terror code = %d\n\n", error); + return DokanNtStatusFromWin32(error); } @@ -966,22 +1180,29 @@ static NTSTATUS DOKAN_CALLBACK MirrorSetAllocationSize(DOKAN_SET_ALLOCATION_SIZE static NTSTATUS DOKAN_CALLBACK MirrorSetFileBasicInformation(DOKAN_SET_FILE_BASIC_INFO_EVENT *EventInfo) { WCHAR filePath[MAX_PATH]; - HANDLE handle; + MIRROR_FILE_HANDLE *mirrorHandle = (MIRROR_FILE_HANDLE*)EventInfo->DokanFileInfo->Context; GetFilePath(filePath, MAX_PATH, EventInfo->FileName); - handle = (HANDLE)EventInfo->DokanFileInfo->Context; DbgPrint(L"SetFileBasicInformation %s\n", filePath); + MIRROR_HANDLE_ASSERT(mirrorHandle); assert(sizeof(FILE_BASIC_INFORMATION) == sizeof(FILE_BASIC_INFO)); - if (!SetFileInformationByHandle(handle, FileBasicInfo, (LPVOID)EventInfo->Info, (DWORD)sizeof(FILE_BASIC_INFORMATION))) { + if (!SetFileInformationByHandle(mirrorHandle->FileHandle, + FileBasicInfo, + (LPVOID)EventInfo->Info, + (DWORD)sizeof(FILE_BASIC_INFORMATION))) { + DWORD error = GetLastError(); + DbgPrint(L"\terror code = %d\n\n", error); + return DokanNtStatusFromWin32(error); } DbgPrint(L"\n"); + return STATUS_SUCCESS; } @@ -989,7 +1210,7 @@ static NTSTATUS DOKAN_CALLBACK MirrorUnlockFile(DOKAN_UNLOCK_FILE_EVENT *EventInfo) { WCHAR filePath[MAX_PATH]; - HANDLE handle; + MIRROR_FILE_HANDLE *mirrorHandle = (MIRROR_FILE_HANDLE*)EventInfo->DokanFileInfo->Context; LARGE_INTEGER length; LARGE_INTEGER offset; @@ -997,110 +1218,120 @@ MirrorUnlockFile(DOKAN_UNLOCK_FILE_EVENT *EventInfo) { DbgPrint(L"UnlockFile %s\n", filePath); - handle = (HANDLE)EventInfo->DokanFileInfo->Context; - if (!handle || handle == INVALID_HANDLE_VALUE) { - DbgPrint(L"\tinvalid handle\n\n"); - return STATUS_INVALID_HANDLE; - } + MIRROR_HANDLE_ASSERT(mirrorHandle); length.QuadPart = EventInfo->Length; offset.QuadPart = EventInfo->ByteOffset; - if (!UnlockFile(handle, offset.LowPart, offset.HighPart, length.LowPart, - length.HighPart)) { + if (!UnlockFile(mirrorHandle->FileHandle, offset.LowPart, offset.HighPart, length.LowPart, length.HighPart)) { + DWORD error = GetLastError(); + DbgPrint(L"\terror code = %d\n\n", error); + return DokanNtStatusFromWin32(error); } DbgPrint(L"\tsuccess\n\n"); + return STATUS_SUCCESS; } static NTSTATUS DOKAN_CALLBACK MirrorGetFileSecurity(DOKAN_GET_FILE_SECURITY_EVENT *EventInfo) { - WCHAR filePath[MAX_PATH]; + WCHAR filePath[MAX_PATH]; + MIRROR_FILE_HANDLE *mirrorHandle = (MIRROR_FILE_HANDLE*)EventInfo->DokanFileInfo->Context; + + GetFilePath(filePath, MAX_PATH, EventInfo->FileName); + + DbgPrint(L"GetFileSecurity %s\n", filePath); + + MIRROR_HANDLE_ASSERT(mirrorHandle); + + MirrorCheckFlag(*EventInfo->SecurityInformation, FILE_SHARE_READ); + MirrorCheckFlag(*EventInfo->SecurityInformation, OWNER_SECURITY_INFORMATION); + MirrorCheckFlag(*EventInfo->SecurityInformation, GROUP_SECURITY_INFORMATION); + MirrorCheckFlag(*EventInfo->SecurityInformation, DACL_SECURITY_INFORMATION); + MirrorCheckFlag(*EventInfo->SecurityInformation, SACL_SECURITY_INFORMATION); + MirrorCheckFlag(*EventInfo->SecurityInformation, LABEL_SECURITY_INFORMATION); + MirrorCheckFlag(*EventInfo->SecurityInformation, ATTRIBUTE_SECURITY_INFORMATION); + MirrorCheckFlag(*EventInfo->SecurityInformation, SCOPE_SECURITY_INFORMATION); + MirrorCheckFlag(*EventInfo->SecurityInformation, + PROCESS_TRUST_LABEL_SECURITY_INFORMATION); + MirrorCheckFlag(*EventInfo->SecurityInformation, BACKUP_SECURITY_INFORMATION); + MirrorCheckFlag(*EventInfo->SecurityInformation, PROTECTED_DACL_SECURITY_INFORMATION); + MirrorCheckFlag(*EventInfo->SecurityInformation, PROTECTED_SACL_SECURITY_INFORMATION); + MirrorCheckFlag(*EventInfo->SecurityInformation, UNPROTECTED_DACL_SECURITY_INFORMATION); + MirrorCheckFlag(*EventInfo->SecurityInformation, UNPROTECTED_SACL_SECURITY_INFORMATION); + + DbgPrint(L" Opening new handle with READ_CONTROL access\n"); + + HANDLE handle = CreateFile( + filePath, + READ_CONTROL | (((*EventInfo->SecurityInformation & SACL_SECURITY_INFORMATION) || + (*EventInfo->SecurityInformation & BACKUP_SECURITY_INFORMATION)) + ? ACCESS_SYSTEM_SECURITY + : 0), + FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, + NULL, // security attribute + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, // |FILE_FLAG_NO_BUFFERING, + NULL); + + if(!handle || handle == INVALID_HANDLE_VALUE) { + + DbgPrint(L"\tinvalid handle\n\n"); - GetFilePath(filePath, MAX_PATH, EventInfo->FileName); + int error = GetLastError(); - DbgPrint(L"GetFileSecurity %s\n", filePath); - - MirrorCheckFlag(*EventInfo->SecurityInformation, FILE_SHARE_READ); - MirrorCheckFlag(*EventInfo->SecurityInformation, OWNER_SECURITY_INFORMATION); - MirrorCheckFlag(*EventInfo->SecurityInformation, GROUP_SECURITY_INFORMATION); - MirrorCheckFlag(*EventInfo->SecurityInformation, DACL_SECURITY_INFORMATION); - MirrorCheckFlag(*EventInfo->SecurityInformation, SACL_SECURITY_INFORMATION); - MirrorCheckFlag(*EventInfo->SecurityInformation, LABEL_SECURITY_INFORMATION); - MirrorCheckFlag(*EventInfo->SecurityInformation, ATTRIBUTE_SECURITY_INFORMATION); - MirrorCheckFlag(*EventInfo->SecurityInformation, SCOPE_SECURITY_INFORMATION); - MirrorCheckFlag(*EventInfo->SecurityInformation, - PROCESS_TRUST_LABEL_SECURITY_INFORMATION); - MirrorCheckFlag(*EventInfo->SecurityInformation, BACKUP_SECURITY_INFORMATION); - MirrorCheckFlag(*EventInfo->SecurityInformation, PROTECTED_DACL_SECURITY_INFORMATION); - MirrorCheckFlag(*EventInfo->SecurityInformation, PROTECTED_SACL_SECURITY_INFORMATION); - MirrorCheckFlag(*EventInfo->SecurityInformation, UNPROTECTED_DACL_SECURITY_INFORMATION); - MirrorCheckFlag(*EventInfo->SecurityInformation, UNPROTECTED_SACL_SECURITY_INFORMATION); - - DbgPrint(L" Opening new handle with READ_CONTROL access\n"); - HANDLE handle = CreateFile( - filePath, - READ_CONTROL | (((*EventInfo->SecurityInformation & SACL_SECURITY_INFORMATION) || - (*EventInfo->SecurityInformation & BACKUP_SECURITY_INFORMATION)) - ? ACCESS_SYSTEM_SECURITY - : 0), - FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, - NULL, // security attribute - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, // |FILE_FLAG_NO_BUFFERING, - NULL); - - if (!handle || handle == INVALID_HANDLE_VALUE) { - DbgPrint(L"\tinvalid handle\n\n"); - int error = GetLastError(); - return DokanNtStatusFromWin32(error); - } + return DokanNtStatusFromWin32(error); + } - if (!GetUserObjectSecurity(handle, EventInfo->SecurityInformation, EventInfo->SecurityDescriptor, - EventInfo->SecurityDescriptorSize, &EventInfo->LengthNeeded)) { + if(!GetUserObjectSecurity(handle, EventInfo->SecurityInformation, EventInfo->SecurityDescriptor, + EventInfo->SecurityDescriptorSize, &EventInfo->LengthNeeded)) { - int error = GetLastError(); + int error = GetLastError(); - if (error == ERROR_INSUFFICIENT_BUFFER) { - DbgPrint(L" GetUserObjectSecurity error: ERROR_INSUFFICIENT_BUFFER\n"); - CloseHandle(handle); - return STATUS_BUFFER_OVERFLOW; - } - else { - DbgPrint(L" GetUserObjectSecurity error: %d\n", error); - CloseHandle(handle); - return DokanNtStatusFromWin32(error); - } - } + if(error == ERROR_INSUFFICIENT_BUFFER) { + + DbgPrint(L" GetUserObjectSecurity error: ERROR_INSUFFICIENT_BUFFER\n"); + + CloseHandle(handle); + + return STATUS_BUFFER_OVERFLOW; + } + else { + + DbgPrint(L" GetUserObjectSecurity error: %d\n", error); + + CloseHandle(handle); + + return DokanNtStatusFromWin32(error); + } + } - CloseHandle(handle); + CloseHandle(handle); - return STATUS_SUCCESS; + return STATUS_SUCCESS; } static NTSTATUS DOKAN_CALLBACK MirrorSetFileSecurity(DOKAN_SET_FILE_SECURITY_EVENT *EventInfo) { - HANDLE handle; + MIRROR_FILE_HANDLE *mirrorHandle = (MIRROR_FILE_HANDLE*)EventInfo->DokanFileInfo->Context; WCHAR filePath[MAX_PATH]; GetFilePath(filePath, MAX_PATH, EventInfo->FileName); DbgPrint(L"SetFileSecurity %s\n", filePath); - handle = (HANDLE)EventInfo->DokanFileInfo->Context; + MIRROR_HANDLE_ASSERT(mirrorHandle); - if (!handle || handle == INVALID_HANDLE_VALUE) { - DbgPrint(L"\tinvalid handle\n\n"); - return STATUS_INVALID_HANDLE; - } + if (!SetUserObjectSecurity(mirrorHandle->FileHandle, EventInfo->SecurityInformation, EventInfo->SecurityDescriptor)) { - if (!SetUserObjectSecurity(handle, EventInfo->SecurityInformation, EventInfo->SecurityDescriptor)) { int error = GetLastError(); + DbgPrint(L" SetUserObjectSecurity error: %d\n", error); + return DokanNtStatusFromWin32(error); } @@ -1251,6 +1482,7 @@ static NTSTATUS DOKAN_CALLBACK MirrorMounted(DOKAN_MOUNTED_INFO *EventInfo) { UNREFERENCED_PARAMETER(EventInfo); DbgPrint(L"Mounted\n"); + return STATUS_SUCCESS; } @@ -1258,11 +1490,71 @@ static NTSTATUS DOKAN_CALLBACK MirrorUnmounted(DOKAN_UNMOUNTED_INFO *EventInfo) UNREFERENCED_PARAMETER(EventInfo); DbgPrint(L"Unmounted\n"); + return STATUS_SUCCESS; } #pragma warning(pop) +#if USE_ASYNC_IO + +BOOL InitializeAsyncIO() { + + g_ThreadPool = DokanGetThreadPool(); + + if(!g_ThreadPool) { + + return FALSE; + } + + g_ThreadPoolCleanupGroup = CreateThreadpoolCleanupGroup(); + + if(!g_ThreadPoolCleanupGroup) { + + return FALSE; + } + + InitializeThreadpoolEnvironment(&g_ThreadPoolEnvironment); + + SetThreadpoolCallbackPool(&g_ThreadPoolEnvironment, g_ThreadPool); + SetThreadpoolCallbackCleanupGroup(&g_ThreadPoolEnvironment, g_ThreadPoolCleanupGroup, NULL); + + InitializeCriticalSection(&g_OverlappedPoolCS); + DokanVector_StackAlloc(&g_OverlappedPool, sizeof(MIRROR_OVERLAPPED)); + + return TRUE; +} + +void CleanupAsyncIO() { + + if(g_ThreadPoolCleanupGroup) { + + CloseThreadpoolCleanupGroupMembers(g_ThreadPoolCleanupGroup, FALSE, NULL); + CloseThreadpoolCleanupGroup(g_ThreadPoolCleanupGroup); + g_ThreadPoolCleanupGroup = NULL; + + DestroyThreadpoolEnvironment(&g_ThreadPoolEnvironment); + } + + EnterCriticalSection(&g_OverlappedPoolCS); + DokanVector_Free(&g_OverlappedPool); + LeaveCriticalSection(&g_OverlappedPoolCS); + + DeleteCriticalSection(&g_OverlappedPoolCS); +} + +void CALLBACK MirrorIoCallback( + _Inout_ PTP_CALLBACK_INSTANCE Instance, + _Inout_opt_ PVOID Context, + _Inout_opt_ PVOID Overlapped, + _In_ ULONG IoResult, + _In_ ULONG_PTR NumberOfBytesTransferred, + _Inout_ PTP_IO Io +) { +} + +#endif + BOOL WINAPI CtrlHandler(DWORD dwCtrlType) { switch (dwCtrlType) { case CTRL_C_EVENT: @@ -1455,8 +1747,36 @@ int __cdecl wmain(ULONG argc, PWCHAR argv[]) { dokanOperations.SetFileSecurityW = MirrorSetFileSecurity; dokanOperations.FindStreams = MirrorFindStreams; +#if USE_ASYNC_IO + if(!InitializeAsyncIO()) { + + fprintf(stderr, "Failed to initialize async IO.\n"); + return -2; + } +#endif + + InitializeCriticalSection(&g_FileHandlePoolCS); + DokanVector_StackAlloc(&g_FileHandlePool, sizeof(MIRROR_FILE_HANDLE)); + status = DokanMain(&dokanOptions, &dokanOperations); + EnterCriticalSection(&g_FileHandlePoolCS); + + for(size_t i = 0; i < DokanVector_GetCount(&g_FileHandlePool); ++i) { + + FreeMirrorFileHandle(*(MIRROR_FILE_HANDLE**)DokanVector_GetItem(&g_FileHandlePool, i)); + } + + DokanVector_Free(&g_FileHandlePool); + + LeaveCriticalSection(&g_FileHandlePoolCS); + + DeleteCriticalSection(&g_FileHandlePoolCS); + +#if USE_ASYNC_IO + CleanupAsyncIO(); +#endif + switch (status) { case DOKAN_SUCCESS: fprintf(stderr, "Success\n"); @@ -1488,4 +1808,4 @@ int __cdecl wmain(ULONG argc, PWCHAR argv[]) { } return EXIT_SUCCESS; -} +} \ No newline at end of file From 6ae87d818d4be2f53110d3dfe827a49afc2e9dce Mon Sep 17 00:00:00 2001 From: Keith Newton Date: Tue, 2 Aug 2016 16:15:12 -0400 Subject: [PATCH 05/20] Added memory leak detection and then fixed some memory leaks --- dokan/create.c | 2 +- samples/dokan_mirror/mirror.c | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/dokan/create.c b/dokan/create.c index 270a673a1..6d90a759b 100644 --- a/dokan/create.c +++ b/dokan/create.c @@ -229,7 +229,7 @@ void DOKANAPI DokanEndDispatchCreate(DOKAN_CREATE_FILE_EVENT *EventInfo, NTSTATU ioEvent->EventInfo.ZwCreateFile.OriginalFileName = NULL; } - DbgPrint("Dokan Information: DokanEndDispatchCreate() status = %lx, lastError = %d, file handle = 0x%p\n, eventID = %04d", + DbgPrint("Dokan Information: DokanEndDispatchCreate() status = %lx, lastError = %d, file handle = 0x%p, eventID = %04d\n", ResultStatus, lastError, ioEvent->DokanOpenInfo, ioEvent->DokanOpenInfo ? ioEvent->DokanOpenInfo->EventId : -1); // FILE_CREATED diff --git a/samples/dokan_mirror/mirror.c b/samples/dokan_mirror/mirror.c index a507713f3..1d38e3ce0 100644 --- a/samples/dokan_mirror/mirror.c +++ b/samples/dokan_mirror/mirror.c @@ -25,6 +25,15 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#define MIRROR_DEBUG_MEMORY 1 +#define MIRROR_IS_DEBUGGING_MEMORY (_DEBUG && MIRROR_DEBUG_MEMORY) + +#if MIRROR_IS_DEBUGGING_MEMORY +#define _CRTDBG_MAP_ALLOC +#include +#include +#endif + #include "../../dokan/dokan.h" #include "../../dokan/fileinfo.h" #include @@ -598,6 +607,8 @@ static void DOKAN_CALLBACK MirrorCloseFile(DOKAN_CLOSE_FILE_EVENT *EventInfo) { mirrorHandle->FileHandle = NULL; EventInfo->DokanFileInfo->Context = 0; + PushMirrorFileHandle(mirrorHandle); + if(EventInfo->DokanFileInfo->DeleteOnClose) { // Should already be deleted by CloseHandle @@ -1577,6 +1588,10 @@ int __cdecl wmain(ULONG argc, PWCHAR argv[]) { DOKAN_OPTIONS dokanOptions; DOKAN_OPERATIONS dokanOperations; +#if MIRROR_IS_DEBUGGING_MEMORY + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); +#endif + if (argc < 3) { fprintf(stderr, "mirror.exe\n" " /r RootDirectory (ex. /r c:\\test)\n" @@ -1756,7 +1771,7 @@ int __cdecl wmain(ULONG argc, PWCHAR argv[]) { #endif InitializeCriticalSection(&g_FileHandlePoolCS); - DokanVector_StackAlloc(&g_FileHandlePool, sizeof(MIRROR_FILE_HANDLE)); + DokanVector_StackAlloc(&g_FileHandlePool, sizeof(MIRROR_FILE_HANDLE*)); status = DokanMain(&dokanOptions, &dokanOperations); From b12011844b4c99eaf25c58e6e5d0499e83620589 Mon Sep 17 00:00:00 2001 From: Keith Newton Date: Tue, 2 Aug 2016 17:53:27 -0400 Subject: [PATCH 06/20] Finished async IO support in Mirror #210 Made critical sections a bit easier to identify Added missing exports --- dokan/directory.c | 76 ++++----- dokan/dokan.c | 287 ++++++++++++++++--------------- dokan/dokan.def | 22 ++- samples/dokan_mirror/mirror.c | 313 +++++++++++++++++++++++++++++----- 4 files changed, 478 insertions(+), 220 deletions(-) diff --git a/dokan/directory.c b/dokan/directory.c index 44026a70a..9013a0f32 100644 --- a/dokan/directory.c +++ b/dokan/directory.c @@ -530,32 +530,32 @@ void EndFindFilesCommon(DOKAN_IO_EVENT *EventInfo, NTSTATUS ResultStatus) { ResultStatus = WriteDirectoryResults(EventInfo, dirList); EnterCriticalSection(&EventInfo->DokanOpenInfo->CriticalSection); + { + if(EventInfo->DokanOpenInfo->DirList != dirList) { - if(EventInfo->DokanOpenInfo->DirList != dirList) { - - oldDirList = EventInfo->DokanOpenInfo->DirList; - EventInfo->DokanOpenInfo->DirList = dirList; - } - else { + oldDirList = EventInfo->DokanOpenInfo->DirList; + EventInfo->DokanOpenInfo->DirList = dirList; + } + else { - // They should never point to the same object - DbgPrint("Dokan Warning: EndFindFilesCommon() EventInfo->DokanOpenInfo->DirList == dirList\n"); - } + // They should never point to the same object + DbgPrint("Dokan Warning: EndFindFilesCommon() EventInfo->DokanOpenInfo->DirList == dirList\n"); + } - if(EventInfo->DokanOpenInfo->DirListSearchPattern) { + if(EventInfo->DokanOpenInfo->DirListSearchPattern) { - free(EventInfo->DokanOpenInfo->DirListSearchPattern); - EventInfo->DokanOpenInfo->DirListSearchPattern = NULL; - } + free(EventInfo->DokanOpenInfo->DirListSearchPattern); + EventInfo->DokanOpenInfo->DirListSearchPattern = NULL; + } - if(EventInfo->KernelInfo.EventContext.Operation.Directory.SearchPatternLength > 0) { + if(EventInfo->KernelInfo.EventContext.Operation.Directory.SearchPatternLength > 0) { - EventInfo->DokanOpenInfo->DirListSearchPattern = - _wcsdup((PWCHAR)( + EventInfo->DokanOpenInfo->DirListSearchPattern = + _wcsdup((PWCHAR)( (SIZE_T)&EventInfo->KernelInfo.EventContext.Operation.Directory.SearchPatternBase[0] + - (SIZE_T)EventInfo->KernelInfo.EventContext.Operation.Directory.SearchPatternOffset)); + (SIZE_T)EventInfo->KernelInfo.EventContext.Operation.Directory.SearchPatternOffset)); + } } - LeaveCriticalSection(&EventInfo->DokanOpenInfo->CriticalSection); if(oldDirList) { @@ -617,34 +617,34 @@ void BeginDispatchDirectoryInformation(DOKAN_IO_EVENT *EventInfo) { } EnterCriticalSection(&EventInfo->DokanOpenInfo->CriticalSection); + { + if(EventInfo->DokanOpenInfo->DirList == NULL) { - if(EventInfo->DokanOpenInfo->DirList == NULL) { + forceScan = TRUE; + } + else if(searchPattern && EventInfo->DokanOpenInfo->DirListSearchPattern) { - forceScan = TRUE; - } - else if(searchPattern && EventInfo->DokanOpenInfo->DirListSearchPattern) { + forceScan = wcscmp(searchPattern, EventInfo->DokanOpenInfo->DirListSearchPattern) != 0 ? TRUE : FALSE; + } + else if(searchPattern) { - forceScan = wcscmp(searchPattern, EventInfo->DokanOpenInfo->DirListSearchPattern) != 0 ? TRUE : FALSE; - } - else if(searchPattern) { + forceScan = wcscmp(searchPattern, L"*") != 0 ? TRUE : FALSE; + } + else if(EventInfo->DokanOpenInfo->DirListSearchPattern) { - forceScan = wcscmp(searchPattern, L"*") != 0 ? TRUE : FALSE; - } - else if(EventInfo->DokanOpenInfo->DirListSearchPattern) { + forceScan = wcscmp(EventInfo->DokanOpenInfo->DirListSearchPattern, L"*") != 0 ? TRUE : FALSE; + } - forceScan = wcscmp(EventInfo->DokanOpenInfo->DirListSearchPattern, L"*") != 0 ? TRUE : FALSE; - } - - // In FastFat SL_INDEX_SPECIFIED overrides SL_RESTART_SCAN - forceScan = (forceScan - || (!(EventInfo->KernelInfo.EventContext.Flags & SL_INDEX_SPECIFIED) - && (EventInfo->KernelInfo.EventContext.Flags & SL_RESTART_SCAN))) ? TRUE : FALSE; + // In FastFat SL_INDEX_SPECIFIED overrides SL_RESTART_SCAN + forceScan = (forceScan + || (!(EventInfo->KernelInfo.EventContext.Flags & SL_INDEX_SPECIFIED) + && (EventInfo->KernelInfo.EventContext.Flags & SL_RESTART_SCAN))) ? TRUE : FALSE; - if(!forceScan) { + if(!forceScan) { - status = WriteDirectoryResults(EventInfo, EventInfo->DokanOpenInfo->DirList); + status = WriteDirectoryResults(EventInfo, EventInfo->DokanOpenInfo->DirList); + } } - LeaveCriticalSection(&EventInfo->DokanOpenInfo->CriticalSection); if(!forceScan) { diff --git a/dokan/dokan.c b/dokan/dokan.c index 8596afb10..a8b63edce 100644 --- a/dokan/dokan.c +++ b/dokan/dokan.c @@ -101,26 +101,26 @@ int InitializeThreadPool(HMODULE hModule) { UNREFERENCED_PARAMETER(hModule); EnterCriticalSection(&g_InstanceCriticalSection); + { + if(g_ThreadPool) { - if(g_ThreadPool) { - - DokanDbgPrint("Dokan Error: Thread pool has already been created.\n"); - LeaveCriticalSection(&g_InstanceCriticalSection); - return DOKAN_DRIVER_INSTALL_ERROR; - } + DokanDbgPrint("Dokan Error: Thread pool has already been created.\n"); + LeaveCriticalSection(&g_InstanceCriticalSection); + 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); + // 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); + g_ThreadPool = CreateThreadpool(NULL); - if(!g_ThreadPool) { + if(!g_ThreadPool) { - DokanDbgPrint("Dokan Error: Failed to create thread pool.\n"); - LeaveCriticalSection(&g_InstanceCriticalSection); - return DOKAN_DRIVER_INSTALL_ERROR; + DokanDbgPrint("Dokan Error: Failed to create thread pool.\n"); + LeaveCriticalSection(&g_InstanceCriticalSection); + return DOKAN_DRIVER_INSTALL_ERROR; + } } - LeaveCriticalSection(&g_InstanceCriticalSection); return DOKAN_SUCCESS; @@ -129,14 +129,15 @@ int InitializeThreadPool(HMODULE hModule) { void CleanupThreadpool() { EnterCriticalSection(&g_InstanceCriticalSection); - - // TODO: Iterate all instances and deallocate their cleanup groups + { + // TODO: Iterate all instances and deallocate their cleanup groups - if(g_ThreadPool) { - CloseThreadpool(g_ThreadPool); - g_ThreadPool = NULL; - } + if(g_ThreadPool) { + CloseThreadpool(g_ThreadPool); + g_ThreadPool = NULL; + } + } LeaveCriticalSection(&g_InstanceCriticalSection); } @@ -147,13 +148,13 @@ DOKAN_IO_EVENT* PopIoEventBuffer() { DOKAN_IO_EVENT *ioEvent = NULL; EnterCriticalSection(&g_EventBufferCriticalSection); - - if(DokanVector_GetCount(g_EventBufferPool) > 0) { - ioEvent = *(DOKAN_IO_EVENT**)DokanVector_GetLastItem(g_EventBufferPool); - DokanVector_PopBack(g_EventBufferPool); + if(DokanVector_GetCount(g_EventBufferPool) > 0) + { + ioEvent = *(DOKAN_IO_EVENT**)DokanVector_GetLastItem(g_EventBufferPool); + DokanVector_PopBack(g_EventBufferPool); + } } - LeaveCriticalSection(&g_EventBufferCriticalSection); if(!ioEvent) { @@ -189,13 +190,13 @@ void PushIoEventBuffer(DOKAN_IO_EVENT *IOEvent) { } EnterCriticalSection(&g_EventBufferCriticalSection); + { + if(DokanVector_GetCount(g_EventBufferPool) < DOKAN_IO_EVENT_POOL_SIZE) { - if(DokanVector_GetCount(g_EventBufferPool) < DOKAN_IO_EVENT_POOL_SIZE) { - - DokanVector_PushBack(g_EventBufferPool, &IOEvent); - IOEvent = NULL; + DokanVector_PushBack(g_EventBufferPool, &IOEvent); + IOEvent = NULL; + } } - LeaveCriticalSection(&g_EventBufferCriticalSection); if(IOEvent) { @@ -230,13 +231,13 @@ DOKAN_OVERLAPPED* PopOverlapped() { DOKAN_OVERLAPPED *overlapped = NULL; EnterCriticalSection(&g_OverlappedCriticalSection); - - if(DokanVector_GetCount(g_OverlappedPool) > 0) { - overlapped = *(DOKAN_OVERLAPPED**)DokanVector_GetLastItem(g_OverlappedPool); - DokanVector_PopBack(g_OverlappedPool); + if(DokanVector_GetCount(g_OverlappedPool) > 0) + { + overlapped = *(DOKAN_OVERLAPPED**)DokanVector_GetLastItem(g_OverlappedPool); + DokanVector_PopBack(g_OverlappedPool); + } } - LeaveCriticalSection(&g_OverlappedCriticalSection); if(!overlapped) { @@ -277,13 +278,13 @@ void PushOverlapped(DOKAN_OVERLAPPED *Overlapped) { assert(Overlapped); EnterCriticalSection(&g_OverlappedCriticalSection); + { + if(DokanVector_GetCount(g_OverlappedPool) < DOKAN_OVERLAPPED_POOL_SIZE) { - if(DokanVector_GetCount(g_OverlappedPool) < DOKAN_OVERLAPPED_POOL_SIZE) { - - DokanVector_PushBack(g_OverlappedPool, &Overlapped); - Overlapped = NULL; + DokanVector_PushBack(g_OverlappedPool, &Overlapped); + Overlapped = NULL; + } } - LeaveCriticalSection(&g_OverlappedCriticalSection); if(Overlapped) { @@ -299,13 +300,13 @@ EVENT_INFORMATION* PopEventResult() { EVENT_INFORMATION *eventResult = NULL; EnterCriticalSection(&g_EventResultCriticalSection); - - if(DokanVector_GetCount(g_EventResultPool) > 0) { - eventResult = *(EVENT_INFORMATION**)DokanVector_GetLastItem(g_EventResultPool); - DokanVector_PopBack(g_EventResultPool); - } + if(DokanVector_GetCount(g_EventResultPool) > 0) { + eventResult = *(EVENT_INFORMATION**)DokanVector_GetLastItem(g_EventResultPool); + DokanVector_PopBack(g_EventResultPool); + } + } LeaveCriticalSection(&g_EventResultCriticalSection); if(!eventResult) { @@ -335,13 +336,13 @@ void PushEventResult(EVENT_INFORMATION *EventResult) { assert(EventResult); EnterCriticalSection(&g_EventResultCriticalSection); + { + if(DokanVector_GetCount(g_EventResultPool) < DOKAN_OVERLAPPED_POOL_SIZE) { - if(DokanVector_GetCount(g_EventResultPool) < DOKAN_OVERLAPPED_POOL_SIZE) { - - DokanVector_PushBack(g_EventResultPool, &EventResult); - EventResult = NULL; + DokanVector_PushBack(g_EventResultPool, &EventResult); + EventResult = NULL; + } } - LeaveCriticalSection(&g_EventResultCriticalSection); if(EventResult) { @@ -357,13 +358,13 @@ DOKAN_OPEN_INFO* PopFileOpenInfo() { DOKAN_OPEN_INFO *fileInfo = NULL; EnterCriticalSection(&g_FileInfoCriticalSection); - - if(DokanVector_GetCount(g_FileInfoPool) > 0) { - fileInfo = *(DOKAN_OPEN_INFO**)DokanVector_GetLastItem(g_FileInfoPool); - DokanVector_PopBack(g_FileInfoPool); + if(DokanVector_GetCount(g_FileInfoPool) > 0) + { + fileInfo = *(DOKAN_OPEN_INFO**)DokanVector_GetLastItem(g_FileInfoPool); + DokanVector_PopBack(g_FileInfoPool); + } } - LeaveCriticalSection(&g_FileInfoCriticalSection); if(!fileInfo) { @@ -394,19 +395,19 @@ void CleanupFileOpenInfo(DOKAN_OPEN_INFO *FileInfo) { DOKAN_VECTOR *dirList = NULL; EnterCriticalSection(&FileInfo->CriticalSection); + { + if(FileInfo->DirListSearchPattern) { - if(FileInfo->DirListSearchPattern) { - - free(FileInfo->DirListSearchPattern); - FileInfo->DirListSearchPattern = NULL; - } + free(FileInfo->DirListSearchPattern); + FileInfo->DirListSearchPattern = NULL; + } - if(FileInfo->DirList) { + if(FileInfo->DirList) { - dirList = FileInfo->DirList; - FileInfo->DirList = NULL; + dirList = FileInfo->DirList; + FileInfo->DirList = NULL; + } } - LeaveCriticalSection(&FileInfo->CriticalSection); if(dirList) { @@ -433,13 +434,13 @@ void PushFileOpenInfo(DOKAN_OPEN_INFO *FileInfo) { CleanupFileOpenInfo(FileInfo); EnterCriticalSection(&g_FileInfoCriticalSection); + { + if(DokanVector_GetCount(g_FileInfoPool) < DOKAN_OVERLAPPED_POOL_SIZE) { - if(DokanVector_GetCount(g_FileInfoPool) < DOKAN_OVERLAPPED_POOL_SIZE) { - - DokanVector_PushBack(g_FileInfoPool, &FileInfo); - FileInfo = NULL; + DokanVector_PushBack(g_FileInfoPool, &FileInfo); + FileInfo = NULL; + } } - LeaveCriticalSection(&g_FileInfoCriticalSection); if(FileInfo) { @@ -455,13 +456,13 @@ DOKAN_VECTOR* PopDirectoryList() { DOKAN_VECTOR *directoryList = NULL; EnterCriticalSection(&g_DirectoryListCriticalSection); - - if(DokanVector_GetCount(g_DirectoryListPool) > 0) { - directoryList = *(DOKAN_VECTOR**)DokanVector_GetLastItem(g_DirectoryListPool); - DokanVector_PopBack(g_DirectoryListPool); + if(DokanVector_GetCount(g_DirectoryListPool) > 0) + { + directoryList = *(DOKAN_VECTOR**)DokanVector_GetLastItem(g_DirectoryListPool); + DokanVector_PopBack(g_DirectoryListPool); + } } - LeaveCriticalSection(&g_DirectoryListCriticalSection); if(!directoryList) { @@ -483,13 +484,13 @@ void PushDirectoryList(DOKAN_VECTOR *DirectoryList) { assert(DokanVector_GetItemSize(DirectoryList) == sizeof(WIN32_FIND_DATAW)); EnterCriticalSection(&g_DirectoryListCriticalSection); + { + if(DokanVector_GetCount(g_DirectoryListPool) < DOKAN_DIRECTORY_LIST_POOL_SIZE) { - if(DokanVector_GetCount(g_DirectoryListPool) < DOKAN_DIRECTORY_LIST_POOL_SIZE) { - - DokanVector_PushBack(g_DirectoryListPool, &DirectoryList); - DirectoryList = NULL; + DokanVector_PushBack(g_DirectoryListPool, &DirectoryList); + DirectoryList = NULL; + } } - LeaveCriticalSection(&g_DirectoryListCriticalSection); if(DirectoryList) { @@ -535,47 +536,47 @@ NewDokanInstance() { } EnterCriticalSection(&g_InstanceCriticalSection); + { + if(!g_ThreadPool) { - if(!g_ThreadPool) { + DokanDbgPrint("Dokan Error: Cannot create Dokan instance because the thread pool hasn't been created.\n"); + LeaveCriticalSection(&g_InstanceCriticalSection); - DokanDbgPrint("Dokan Error: Cannot create Dokan instance because the thread pool hasn't been created.\n"); - LeaveCriticalSection(&g_InstanceCriticalSection); + DeleteCriticalSection(&instance->CriticalSection); - DeleteCriticalSection(&instance->CriticalSection); + CloseHandle(instance->DeviceClosedWaitHandle); - CloseHandle(instance->DeviceClosedWaitHandle); + free(instance); - free(instance); - - return NULL; - } + return NULL; + } - instance->ThreadInfo.ThreadPool = g_ThreadPool; - instance->ThreadInfo.CleanupGroup = CreateThreadpoolCleanupGroup(); + instance->ThreadInfo.ThreadPool = g_ThreadPool; + instance->ThreadInfo.CleanupGroup = CreateThreadpoolCleanupGroup(); - if(!instance->ThreadInfo.CleanupGroup) { + if(!instance->ThreadInfo.CleanupGroup) { - DokanDbgPrint("Dokan Error: Failed to create thread pool cleanup group.\n"); + DokanDbgPrint("Dokan Error: Failed to create thread pool cleanup group.\n"); - LeaveCriticalSection(&g_InstanceCriticalSection); + LeaveCriticalSection(&g_InstanceCriticalSection); - DeleteCriticalSection(&instance->CriticalSection); + DeleteCriticalSection(&instance->CriticalSection); - CloseHandle(instance->DeviceClosedWaitHandle); + CloseHandle(instance->DeviceClosedWaitHandle); - free(instance); + free(instance); - return NULL; - } + return NULL; + } - InitializeThreadpoolEnvironment(&instance->ThreadInfo.CallbackEnvironment); - - SetThreadpoolCallbackPool(&instance->ThreadInfo.CallbackEnvironment, g_ThreadPool); + InitializeThreadpoolEnvironment(&instance->ThreadInfo.CallbackEnvironment); - SetThreadpoolCallbackCleanupGroup(&instance->ThreadInfo.CallbackEnvironment, instance->ThreadInfo.CleanupGroup, NULL); + SetThreadpoolCallbackPool(&instance->ThreadInfo.CallbackEnvironment, g_ThreadPool); - InsertTailList(&g_InstanceList, &instance->ListEntry); + SetThreadpoolCallbackCleanupGroup(&instance->ThreadInfo.CallbackEnvironment, instance->ThreadInfo.CleanupGroup, NULL); + InsertTailList(&g_InstanceList, &instance->ListEntry); + } LeaveCriticalSection(&g_InstanceCriticalSection); return instance; @@ -618,7 +619,9 @@ void DeleteDokanInstance(PDOKAN_INSTANCE Instance) { DeleteCriticalSection(&Instance->CriticalSection); EnterCriticalSection(&g_InstanceCriticalSection); - RemoveEntryList(&Instance->ListEntry); + { + RemoveEntryList(&Instance->ListEntry); + } LeaveCriticalSection(&g_InstanceCriticalSection); CloseHandle(Instance->DeviceClosedWaitHandle); @@ -1676,17 +1679,17 @@ BOOL WINAPI DllMain(HINSTANCE Instance, DWORD Reason, LPVOID Reserved) { case DLL_PROCESS_DETACH: { EnterCriticalSection(&g_InstanceCriticalSection); + { + while(!IsListEmpty(&g_InstanceList)) { - while (!IsListEmpty(&g_InstanceList)) { - - PLIST_ENTRY entry = RemoveHeadList(&g_InstanceList); - - PDOKAN_INSTANCE instance = - CONTAINING_RECORD(entry, DOKAN_INSTANCE, ListEntry); + PLIST_ENTRY entry = RemoveHeadList(&g_InstanceList); - DokanCloseHandle((DOKAN_HANDLE)instance); - } + PDOKAN_INSTANCE instance = + CONTAINING_RECORD(entry, DOKAN_INSTANCE, ListEntry); + DokanCloseHandle((DOKAN_HANDLE)instance); + } + } LeaveCriticalSection(&g_InstanceCriticalSection); CleanupThreadpool(); @@ -1694,15 +1697,15 @@ BOOL WINAPI DllMain(HINSTANCE Instance, DWORD Reason, LPVOID Reserved) { //////////////////// IO event buffer object pool //////////////////// { EnterCriticalSection(&g_EventBufferCriticalSection); + { + for(size_t i = 0; i < DokanVector_GetCount(g_EventBufferPool); ++i) { - for(size_t i = 0; i < DokanVector_GetCount(g_EventBufferPool); ++i) { + FreeIOEventBuffer(*(DOKAN_IO_EVENT**)DokanVector_GetItem(g_EventBufferPool, i)); + } - FreeIOEventBuffer(*(DOKAN_IO_EVENT**)DokanVector_GetItem(g_EventBufferPool, i)); + DokanVector_Free(g_EventBufferPool); + g_EventBufferPool = NULL; } - - DokanVector_Free(g_EventBufferPool); - g_EventBufferPool = NULL; - LeaveCriticalSection(&g_EventBufferCriticalSection); DeleteCriticalSection(&g_EventBufferCriticalSection); @@ -1711,15 +1714,15 @@ BOOL WINAPI DllMain(HINSTANCE Instance, DWORD Reason, LPVOID Reserved) { //////////////////// Overlapped object pool //////////////////// { EnterCriticalSection(&g_OverlappedCriticalSection); + { + for(size_t i = 0; i < DokanVector_GetCount(g_OverlappedPool); ++i) { - for(size_t i = 0; i < DokanVector_GetCount(g_OverlappedPool); ++i) { + FreeOverlapped(*(DOKAN_OVERLAPPED**)DokanVector_GetItem(g_OverlappedPool, i)); + } - FreeOverlapped(*(DOKAN_OVERLAPPED**)DokanVector_GetItem(g_OverlappedPool, i)); + DokanVector_Free(g_OverlappedPool); + g_OverlappedPool = NULL; } - - DokanVector_Free(g_OverlappedPool); - g_OverlappedPool = NULL; - LeaveCriticalSection(&g_OverlappedCriticalSection); DeleteCriticalSection(&g_OverlappedCriticalSection); @@ -1728,15 +1731,15 @@ BOOL WINAPI DllMain(HINSTANCE Instance, DWORD Reason, LPVOID Reserved) { //////////////////// Event result object pool //////////////////// { EnterCriticalSection(&g_EventResultCriticalSection); + { + for(size_t i = 0; i < DokanVector_GetCount(g_EventResultPool); ++i) { - for(size_t i = 0; i < DokanVector_GetCount(g_EventResultPool); ++i) { + FreeEventResult(*(EVENT_INFORMATION**)DokanVector_GetItem(g_EventResultPool, i)); + } - FreeEventResult(*(EVENT_INFORMATION**)DokanVector_GetItem(g_EventResultPool, i)); + DokanVector_Free(g_EventResultPool); + g_EventResultPool = NULL; } - - DokanVector_Free(g_EventResultPool); - g_EventResultPool = NULL; - LeaveCriticalSection(&g_EventResultCriticalSection); DeleteCriticalSection(&g_EventResultCriticalSection); @@ -1745,15 +1748,15 @@ BOOL WINAPI DllMain(HINSTANCE Instance, DWORD Reason, LPVOID Reserved) { //////////////////// File info object pool //////////////////// { EnterCriticalSection(&g_FileInfoCriticalSection); + { + for(size_t i = 0; i < DokanVector_GetCount(g_FileInfoPool); ++i) { - for(size_t i = 0; i < DokanVector_GetCount(g_FileInfoPool); ++i) { + FreeFileOpenInfo(*(DOKAN_OPEN_INFO**)DokanVector_GetItem(g_FileInfoPool, i)); + } - FreeFileOpenInfo(*(DOKAN_OPEN_INFO**)DokanVector_GetItem(g_FileInfoPool, i)); + DokanVector_Free(g_FileInfoPool); + g_FileInfoPool = NULL; } - - DokanVector_Free(g_FileInfoPool); - g_FileInfoPool = NULL; - LeaveCriticalSection(&g_FileInfoCriticalSection); DeleteCriticalSection(&g_FileInfoCriticalSection); @@ -1762,15 +1765,15 @@ BOOL WINAPI DllMain(HINSTANCE Instance, DWORD Reason, LPVOID Reserved) { //////////////////// Directory list pool //////////////////// { EnterCriticalSection(&g_DirectoryListCriticalSection); + { + for(size_t i = 0; i < DokanVector_GetCount(g_DirectoryListPool); ++i) { - for(size_t i = 0; i < DokanVector_GetCount(g_DirectoryListPool); ++i) { + DokanVector_Free(*(DOKAN_VECTOR**)DokanVector_GetItem(g_DirectoryListPool, i)); + } - DokanVector_Free(*(DOKAN_VECTOR**)DokanVector_GetItem(g_DirectoryListPool, i)); + DokanVector_Free(g_DirectoryListPool); + g_DirectoryListPool = NULL; } - - DokanVector_Free(g_DirectoryListPool); - g_DirectoryListPool = NULL; - LeaveCriticalSection(&g_DirectoryListCriticalSection); DeleteCriticalSection(&g_DirectoryListCriticalSection); diff --git a/dokan/dokan.def b/dokan/dokan.def index 7b895fdb5..da6dbd023 100644 --- a/dokan/dokan.def +++ b/dokan/dokan.def @@ -36,4 +36,24 @@ DokanVector_GetLastItem DokanVector_GetCount DokanVector_GetCapacity DokanVector_GetItemSize -DokanGetThreadPool \ No newline at end of file +DokanGetThreadPool +DokanEndDispatchCreate +DokanEndDispatchRead +DokanEndDispatchWrite +DokanEndDispatchFindFiles +DokanEndDispatchFindFilesWithPattern +DokanEndDispatchGetFileInformation +DokanEndDispatchFindStreams +DokanEndDispatchGetVolumeInfo +DokanEndDispatchGetVolumeFreeSpace +DokanEndDispatchGetVolumeAttributes +DokanEndDispatchLockFile +DokanEndDispatchUnlockFile +DokanEndDispatchSetAllocationSize +DokanEndDispatchSetFileBasicInformation +DokanEndDispatchCanDeleteFile +DokanEndDispatchSetEndOfFile +DokanEndDispatchMoveFile +DokanEndDispatchFlush +DokanEndDispatchGetFileSecurity +DokanEndDispatchSetFileSecurity \ No newline at end of file diff --git a/samples/dokan_mirror/mirror.c b/samples/dokan_mirror/mirror.c index 1d38e3ce0..0cf61cd39 100644 --- a/samples/dokan_mirror/mirror.c +++ b/samples/dokan_mirror/mirror.c @@ -44,17 +44,39 @@ THE SOFTWARE. #define USE_ASYNC_IO 1 +typedef struct _MIRROR_FILE_HANDLE { + + HANDLE FileHandle; + +#if USE_ASYNC_IO + PTP_IO IoCompletion; +#endif + +} MIRROR_FILE_HANDLE; + +#define MIRROR_HANDLE_ASSERT(mirrorHandle) assert((mirrorHandle) && (mirrorHandle)->FileHandle && (mirrorHandle)->FileHandle != INVALID_HANDLE_VALUE) + #if USE_ASYNC_IO TP_CALLBACK_ENVIRON g_ThreadPoolEnvironment; PTP_CLEANUP_GROUP g_ThreadPoolCleanupGroup = NULL; PTP_POOL g_ThreadPool = NULL; +CRITICAL_SECTION g_ThreadPoolCS; DOKAN_VECTOR g_OverlappedPool; CRITICAL_SECTION g_OverlappedPoolCS; +typedef enum _MIRROR_IOTYPE { + MIRROR_IOTYPE_UNKNOWN = 0, + MIRROR_IOTYPE_READ, + MIRROR_IOTYPE_WRITE +} MIRROR_IO_OP_TYPE; + typedef struct _MIRROR_OVERLAPPED { - OVERLAPPED InternalOverlapped; + OVERLAPPED InternalOverlapped; + MIRROR_FILE_HANDLE *FileHandle; + void *Context; + MIRROR_IO_OP_TYPE IoType; } MIRROR_OVERLAPPED; void CALLBACK MirrorIoCallback( @@ -66,25 +88,16 @@ void CALLBACK MirrorIoCallback( _Inout_ PTP_IO Io ); -#endif - -typedef struct _MIRROR_FILE_HANDLE { - - HANDLE FileHandle; +void CleanupPendingAsyncIO(); -#if USE_ASYNC_IO - PTP_IO IoCompletion; #endif -} MIRROR_FILE_HANDLE; - -#define MIRROR_HANDLE_ASSERT(mirrorHandle) assert((mirrorHandle) && (mirrorHandle)->FileHandle && (mirrorHandle)->FileHandle != INVALID_HANDLE_VALUE) - DOKAN_VECTOR g_FileHandlePool; CRITICAL_SECTION g_FileHandlePoolCS; BOOL g_UseStdErr; BOOL g_DebugMode; +volatile LONG g_IsUnmounted = 0; static void DbgPrint(LPCWSTR format, ...) { @@ -138,7 +151,15 @@ void FreeMirrorFileHandle(MIRROR_FILE_HANDLE *FileHandle) { #if USE_ASYNC_IO if(FileHandle->IoCompletion) { - CloseThreadpoolIo(FileHandle->IoCompletion); + EnterCriticalSection(&g_ThreadPoolCS); + { + if(g_ThreadPoolCleanupGroup) { + + CloseThreadpoolIo(FileHandle->IoCompletion); + } + } + LeaveCriticalSection(&g_ThreadPoolCS); + FileHandle->IoCompletion = NULL; } #endif @@ -154,21 +175,31 @@ void PushMirrorFileHandle(MIRROR_FILE_HANDLE *FileHandle) { #if USE_ASYNC_IO if(FileHandle->IoCompletion) { - CloseThreadpoolIo(FileHandle->IoCompletion); + EnterCriticalSection(&g_ThreadPoolCS); + { + if(g_ThreadPoolCleanupGroup) { + + CloseThreadpoolIo(FileHandle->IoCompletion); + } + } + LeaveCriticalSection(&g_ThreadPoolCS); + FileHandle->IoCompletion = NULL; } #endif EnterCriticalSection(&g_FileHandlePoolCS); - - DokanVector_PushBack(&g_FileHandlePool, &FileHandle); - + { + DokanVector_PushBack(&g_FileHandlePool, &FileHandle); + } LeaveCriticalSection(&g_FileHandlePoolCS); } MIRROR_FILE_HANDLE* PopMirrorFileHandle(HANDLE ActualFileHandle) { - if(ActualFileHandle == NULL || ActualFileHandle == INVALID_HANDLE_VALUE) { + if(ActualFileHandle == NULL + || ActualFileHandle == INVALID_HANDLE_VALUE + || InterlockedAdd(&g_IsUnmounted, 0) != FALSE) { return NULL; } @@ -176,13 +207,13 @@ MIRROR_FILE_HANDLE* PopMirrorFileHandle(HANDLE ActualFileHandle) { MIRROR_FILE_HANDLE *mirrorHandle = NULL; EnterCriticalSection(&g_FileHandlePoolCS); - - if(DokanVector_GetCount(&g_FileHandlePool) > 0) { - mirrorHandle = *(MIRROR_FILE_HANDLE**)DokanVector_GetLastItem(&g_FileHandlePool); - DokanVector_PopBack(&g_FileHandlePool); + if(DokanVector_GetCount(&g_FileHandlePool) > 0) + { + mirrorHandle = *(MIRROR_FILE_HANDLE**)DokanVector_GetLastItem(&g_FileHandlePool); + DokanVector_PopBack(&g_FileHandlePool); + } } - LeaveCriticalSection(&g_FileHandlePoolCS); if(!mirrorHandle) { @@ -196,7 +227,15 @@ MIRROR_FILE_HANDLE* PopMirrorFileHandle(HANDLE ActualFileHandle) { mirrorHandle->FileHandle = ActualFileHandle; #if USE_ASYNC_IO - mirrorHandle->IoCompletion = CreateThreadpoolIo(ActualFileHandle, MirrorIoCallback, mirrorHandle, &g_ThreadPoolEnvironment); + + EnterCriticalSection(&g_ThreadPoolCS); + { + if(g_ThreadPoolCleanupGroup) { + + mirrorHandle->IoCompletion = CreateThreadpoolIo(ActualFileHandle, MirrorIoCallback, mirrorHandle, &g_ThreadPoolEnvironment); + } + } + LeaveCriticalSection(&g_ThreadPoolCS); if(!mirrorHandle->IoCompletion) { @@ -209,6 +248,58 @@ MIRROR_FILE_HANDLE* PopMirrorFileHandle(HANDLE ActualFileHandle) { return mirrorHandle; } +/////////////////// MIRROR_OVERLAPPED /////////////////// + +#if USE_ASYNC_IO + +void FreeMirrorOverlapped(MIRROR_OVERLAPPED *Overlapped) { + + if(Overlapped) { + + free(Overlapped); + } +} + +void PushMirrorOverlapped(MIRROR_OVERLAPPED *Overlapped) { + + assert(Overlapped); + + EnterCriticalSection(&g_OverlappedPoolCS); + { + DokanVector_PushBack(&g_OverlappedPool, &Overlapped); + } + LeaveCriticalSection(&g_OverlappedPoolCS); +} + +MIRROR_OVERLAPPED* PopMirrorOverlapped() { + + MIRROR_OVERLAPPED *overlapped = NULL; + + EnterCriticalSection(&g_OverlappedPoolCS); + { + if(DokanVector_GetCount(&g_OverlappedPool) > 0) + { + overlapped = *(MIRROR_OVERLAPPED**)DokanVector_GetLastItem(&g_OverlappedPool); + DokanVector_PopBack(&g_OverlappedPool); + } + } + LeaveCriticalSection(&g_OverlappedPoolCS); + + if(!overlapped) { + + overlapped = (MIRROR_OVERLAPPED*)malloc(sizeof(MIRROR_OVERLAPPED)); + } + + if(overlapped) { + + RtlZeroMemory(overlapped, sizeof(MIRROR_OVERLAPPED)); + } + + return overlapped; +} + +#endif + /////////////////// Push/Pop pattern finished /////////////////// static WCHAR RootDirectory[MAX_PATH] = L"C:"; @@ -669,7 +760,6 @@ static NTSTATUS DOKAN_CALLBACK MirrorReadFile(DOKAN_READ_FILE_EVENT *EventInfo) WCHAR filePath[MAX_PATH]; MIRROR_FILE_HANDLE *mirrorHandle = (MIRROR_FILE_HANDLE*)EventInfo->DokanFileInfo->Context; - ULONG offset = (ULONG)EventInfo->Offset; GetFilePath(filePath, MAX_PATH, EventInfo->FileName); @@ -680,6 +770,45 @@ static NTSTATUS DOKAN_CALLBACK MirrorReadFile(DOKAN_READ_FILE_EVENT *EventInfo) return STATUS_FILE_CLOSED; } +#if USE_ASYNC_IO + + MIRROR_OVERLAPPED *overlapped = PopMirrorOverlapped(); + + if(!overlapped) { + + return STATUS_MEMORY_NOT_ALLOCATED; + } + + overlapped->InternalOverlapped.Offset = (DWORD)(EventInfo->Offset & 0xffffffff); + overlapped->InternalOverlapped.OffsetHigh = (DWORD)((EventInfo->Offset >> 32) & 0xffffffff); + overlapped->FileHandle = mirrorHandle; + overlapped->Context = EventInfo; + overlapped->IoType = MIRROR_IOTYPE_READ; + + StartThreadpoolIo(mirrorHandle->IoCompletion); + + if(!ReadFile(mirrorHandle->FileHandle, + EventInfo->Buffer, + EventInfo->NumberOfBytesToRead, + &EventInfo->NumberOfBytesRead, + (LPOVERLAPPED)overlapped)) { + + int lastError = GetLastError(); + + if(lastError != ERROR_IO_PENDING) { + + CancelThreadpoolIo(mirrorHandle->IoCompletion); + + DbgPrint(L"\tread error = %u, buffer length = %d, read length = %d\n\n", + lastError, EventInfo->NumberOfBytesToRead, EventInfo->NumberOfBytesRead); + + return DokanNtStatusFromWin32(lastError); + } + } + + return STATUS_PENDING; + +#else LARGE_INTEGER distanceToMove; distanceToMove.QuadPart = EventInfo->Offset; @@ -713,13 +842,13 @@ static NTSTATUS DOKAN_CALLBACK MirrorReadFile(DOKAN_READ_FILE_EVENT *EventInfo) } return STATUS_SUCCESS; +#endif } static NTSTATUS DOKAN_CALLBACK MirrorWriteFile(DOKAN_WRITE_FILE_EVENT *EventInfo) { WCHAR filePath[MAX_PATH]; MIRROR_FILE_HANDLE *mirrorHandle = (MIRROR_FILE_HANDLE*)EventInfo->DokanFileInfo->Context; - ULONG offset = (ULONG)EventInfo->Offset; GetFilePath(filePath, MAX_PATH, EventInfo->FileName); @@ -731,6 +860,45 @@ static NTSTATUS DOKAN_CALLBACK MirrorWriteFile(DOKAN_WRITE_FILE_EVENT *EventInfo return STATUS_FILE_CLOSED; } +#if USE_ASYNC_IO + + MIRROR_OVERLAPPED *overlapped = PopMirrorOverlapped(); + + if(!overlapped) { + + return STATUS_MEMORY_NOT_ALLOCATED; + } + + overlapped->InternalOverlapped.Offset = (DWORD)(EventInfo->Offset & 0xffffffff); + overlapped->InternalOverlapped.OffsetHigh = (DWORD)((EventInfo->Offset >> 32) & 0xffffffff); + overlapped->FileHandle = mirrorHandle; + overlapped->Context = EventInfo; + overlapped->IoType = MIRROR_IOTYPE_WRITE; + + StartThreadpoolIo(mirrorHandle->IoCompletion); + + if(!WriteFile(mirrorHandle->FileHandle, + EventInfo->Buffer, + EventInfo->NumberOfBytesToWrite, + &EventInfo->NumberOfBytesWritten, + (LPOVERLAPPED)overlapped)) { + + int lastError = GetLastError(); + + if(lastError != ERROR_IO_PENDING) { + + CancelThreadpoolIo(mirrorHandle->IoCompletion); + + DbgPrint(L"\twrite error = %u, buffer length = %d, write length = %d\n", + lastError, EventInfo->NumberOfBytesToWrite, EventInfo->NumberOfBytesWritten); + + return DokanNtStatusFromWin32(lastError); + } + } + + return STATUS_PENDING; + +#else LARGE_INTEGER distanceToMove; distanceToMove.QuadPart = EventInfo->Offset; @@ -776,6 +944,7 @@ static NTSTATUS DOKAN_CALLBACK MirrorWriteFile(DOKAN_WRITE_FILE_EVENT *EventInfo } return STATUS_SUCCESS; +#endif } static NTSTATUS DOKAN_CALLBACK @@ -1502,6 +1671,12 @@ static NTSTATUS DOKAN_CALLBACK MirrorUnmounted(DOKAN_UNMOUNTED_INFO *EventInfo) DbgPrint(L"Unmounted\n"); + InterlockedExchange(&g_IsUnmounted, TRUE); + +#if USE_ASYNC_IO + CleanupPendingAsyncIO(); +#endif + return STATUS_SUCCESS; } @@ -1531,27 +1706,46 @@ BOOL InitializeAsyncIO() { SetThreadpoolCallbackCleanupGroup(&g_ThreadPoolEnvironment, g_ThreadPoolCleanupGroup, NULL); InitializeCriticalSection(&g_OverlappedPoolCS); - DokanVector_StackAlloc(&g_OverlappedPool, sizeof(MIRROR_OVERLAPPED)); + DokanVector_StackAlloc(&g_OverlappedPool, sizeof(MIRROR_OVERLAPPED*)); + + InitializeCriticalSection(&g_ThreadPoolCS); return TRUE; } -void CleanupAsyncIO() { +void CleanupPendingAsyncIO() { - if(g_ThreadPoolCleanupGroup) { + EnterCriticalSection(&g_ThreadPoolCS); + { + if(g_ThreadPoolCleanupGroup) { - CloseThreadpoolCleanupGroupMembers(g_ThreadPoolCleanupGroup, FALSE, NULL); - CloseThreadpoolCleanupGroup(g_ThreadPoolCleanupGroup); - g_ThreadPoolCleanupGroup = NULL; + CloseThreadpoolCleanupGroupMembers(g_ThreadPoolCleanupGroup, FALSE, NULL); + CloseThreadpoolCleanupGroup(g_ThreadPoolCleanupGroup); + g_ThreadPoolCleanupGroup = NULL; - DestroyThreadpoolEnvironment(&g_ThreadPoolEnvironment); + DestroyThreadpoolEnvironment(&g_ThreadPoolEnvironment); + } } + LeaveCriticalSection(&g_ThreadPoolCS); +} + +void CleanupAsyncIO() { + + CleanupPendingAsyncIO(); EnterCriticalSection(&g_OverlappedPoolCS); - DokanVector_Free(&g_OverlappedPool); + { + for(size_t i = 0; i < DokanVector_GetCount(&g_OverlappedPool); ++i) { + + FreeMirrorOverlapped(*(MIRROR_OVERLAPPED**)DokanVector_GetItem(&g_OverlappedPool, i)); + } + + DokanVector_Free(&g_OverlappedPool); + } LeaveCriticalSection(&g_OverlappedPoolCS); DeleteCriticalSection(&g_OverlappedPoolCS); + DeleteCriticalSection(&g_ThreadPoolCS); } void CALLBACK MirrorIoCallback( @@ -1562,6 +1756,47 @@ void CALLBACK MirrorIoCallback( _In_ ULONG_PTR NumberOfBytesTransferred, _Inout_ PTP_IO Io ) { + + UNREFERENCED_PARAMETER(Instance); + UNREFERENCED_PARAMETER(Context); + UNREFERENCED_PARAMETER(Io); + + MIRROR_OVERLAPPED *overlapped = (MIRROR_OVERLAPPED*)Overlapped; + DOKAN_READ_FILE_EVENT *readFile = NULL; + DOKAN_WRITE_FILE_EVENT *writeFile = NULL; + + switch(overlapped->IoType) { + + case MIRROR_IOTYPE_READ: + + readFile = (DOKAN_READ_FILE_EVENT*)overlapped->Context; + + assert(readFile); + + readFile->NumberOfBytesRead = (DWORD)NumberOfBytesTransferred; + + DokanEndDispatchRead(readFile, DokanNtStatusFromWin32(IoResult)); + + break; + + case MIRROR_IOTYPE_WRITE: + + writeFile = (DOKAN_WRITE_FILE_EVENT*)overlapped->Context; + + assert(writeFile); + + writeFile->NumberOfBytesWritten = (DWORD)NumberOfBytesTransferred; + + DokanEndDispatchWrite(writeFile, DokanNtStatusFromWin32(IoResult)); + + break; + + default: + DbgPrint(L"Unrecognized async IO operation: %d\n", overlapped->IoType); + break; + } + + PushMirrorOverlapped(overlapped); } #endif @@ -1776,14 +2011,14 @@ int __cdecl wmain(ULONG argc, PWCHAR argv[]) { status = DokanMain(&dokanOptions, &dokanOperations); EnterCriticalSection(&g_FileHandlePoolCS); + { + for(size_t i = 0; i < DokanVector_GetCount(&g_FileHandlePool); ++i) { - for(size_t i = 0; i < DokanVector_GetCount(&g_FileHandlePool); ++i) { + FreeMirrorFileHandle(*(MIRROR_FILE_HANDLE**)DokanVector_GetItem(&g_FileHandlePool, i)); + } - FreeMirrorFileHandle(*(MIRROR_FILE_HANDLE**)DokanVector_GetItem(&g_FileHandlePool, i)); + DokanVector_Free(&g_FileHandlePool); } - - DokanVector_Free(&g_FileHandlePool); - LeaveCriticalSection(&g_FileHandlePoolCS); DeleteCriticalSection(&g_FileHandlePoolCS); From 6a67e8546a07a0e0a690f00cb7f5701e020368cf Mon Sep 17 00:00:00 2001 From: Keith Newton Date: Tue, 2 Aug 2016 21:28:08 -0400 Subject: [PATCH 07/20] Added memory allocation hooks #210 Added DokanInit() and DokanShutdown() FIxed a bunch of memory leaks caused by the Close handler --- dokan/access.c | 4 +- dokan/cleanup.c | 5 +- dokan/close.c | 11 +- dokan/create.c | 9 +- dokan/directory.c | 9 +- dokan/dokan.c | 343 ++++++++++++++++++++++++--------- dokan/dokan.def | 4 +- dokan/dokan.h | 18 ++ dokan/dokan_vector.c | 20 +- dokan/dokani.h | 10 + dokan/fileinfo.c | 5 +- dokan/flush.c | 5 +- dokan/lock.c | 5 +- dokan/mount.c | 13 +- dokan/read.c | 5 +- dokan/security.c | 10 +- dokan/setfile.c | 5 +- dokan/timeout.c | 4 +- dokan/volume.c | 5 +- dokan/write.c | 9 +- dokan_control/dokanctl.c | 16 +- dokan_fuse/include/dokanfuse.h | 12 +- dokan_fuse/src/dokanfuse.cpp | 51 ++++- samples/dokan_mirror/mirror.c | 37 ++++ 24 files changed, 457 insertions(+), 158 deletions(-) diff --git a/dokan/access.c b/dokan/access.c index 34b125a0b..a549de45b 100644 --- a/dokan/access.c +++ b/dokan/access.c @@ -45,7 +45,7 @@ HANDLE DOKANAPI DokanOpenRequestorToken(PDOKAN_CREATE_FILE_EVENT FileInfo) { } eventInfoSize = sizeof(EVENT_INFORMATION); - eventInfo = (PEVENT_INFORMATION)malloc(eventInfoSize); + eventInfo = (PEVENT_INFORMATION)DokanMalloc(eventInfoSize); if (eventInfo == NULL) { return INVALID_HANDLE_VALUE; @@ -66,7 +66,7 @@ HANDLE DOKANAPI DokanOpenRequestorToken(PDOKAN_CREATE_FILE_EVENT FileInfo) { DbgPrintW(L"IOCTL_GET_ACCESS_TOKEN failed\n"); } - free(eventInfo); + DokanFree(eventInfo); return handle; } \ No newline at end of file diff --git a/dokan/cleanup.c b/dokan/cleanup.c index 5c4c23df6..edd36e2ba 100644 --- a/dokan/cleanup.c +++ b/dokan/cleanup.c @@ -33,9 +33,10 @@ void DispatchCleanup(DOKAN_IO_EVENT *EventInfo) { EventInfo->EventResult->Status = STATUS_SUCCESS; // return success at any case - DbgPrint("###Cleanup file handle = 0x%p, eventID = %04d\n", + DbgPrint("###Cleanup file handle = 0x%p, eventID = %04d, event Info = 0x%p\n", EventInfo->DokanOpenInfo, - EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1); + EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1, + EventInfo); if (dokan->DokanOperations->Cleanup) { diff --git a/dokan/close.c b/dokan/close.c index d930b0042..4b96d7adb 100644 --- a/dokan/close.c +++ b/dokan/close.c @@ -21,6 +21,7 @@ with this program. If not, see . #include "dokani.h" #include "fileinfo.h" +#include void DispatchClose(DOKAN_IO_EVENT *EventInfo) { @@ -29,13 +30,12 @@ void DispatchClose(DOKAN_IO_EVENT *EventInfo) { CheckFileName(EventInfo->KernelInfo.EventContext.Operation.Close.FileName); - CreateDispatchCommon(EventInfo, 0); + assert(!EventInfo->EventResult); - EventInfo->EventResult->Status = STATUS_SUCCESS; // return success at any case - - DbgPrint("###Close file handle = 0x%p, eventID = %04d\n", + DbgPrint("###Close file handle = 0x%p, eventID = %04d, event Info = 0x%p\n", EventInfo->DokanOpenInfo, - EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1); + EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1, + EventInfo); if (dokan->DokanOperations->CloseFile) { @@ -52,4 +52,5 @@ void DispatchClose(DOKAN_IO_EVENT *EventInfo) { } // do not send it to the driver + PushIoEventBuffer(EventInfo); } diff --git a/dokan/create.c b/dokan/create.c index 6d90a759b..8f5e14772 100644 --- a/dokan/create.c +++ b/dokan/create.c @@ -100,9 +100,10 @@ void BeginDispatchCreate(DOKAN_IO_EVENT *EventInfo) { assert((void*)createFileEvent == (void*)EventInfo); - DbgPrint("###Create file handle = 0x%p, eventID = %04d\n", + DbgPrint("###Create file handle = 0x%p, eventID = %04d, event Info = 0x%p\n", EventInfo->DokanOpenInfo, - currentEventId); + currentEventId, + EventInfo); // The low 24 bits of this member correspond to the CreateOptions parameter createFileEvent->CreateOptions = EventInfo->KernelInfo.EventContext.Operation.Create.CreateOptions & FILE_VALID_OPTION_FLAGS; @@ -126,7 +127,7 @@ void BeginDispatchCreate(DOKAN_IO_EVENT *EventInfo) { assert(createFileEvent->OriginalFileName == NULL); - createFileEvent->OriginalFileName = _wcsdup(createFileEvent->FileName); + createFileEvent->OriginalFileName = DokanDupW(createFileEvent->FileName); if (EventInfo->KernelInfo.EventContext.Flags & SL_OPEN_TARGET_DIRECTORY) { // NOTE: SL_OPEN_TARGET_DIRECTORY means open the parent directory of the @@ -225,7 +226,7 @@ void DOKANAPI DokanEndDispatchCreate(DOKAN_CREATE_FILE_EVENT *EventInfo, NTSTATU if(ioEvent->EventInfo.ZwCreateFile.OriginalFileName) { - free((void*)ioEvent->EventInfo.ZwCreateFile.OriginalFileName); + DokanFree((void*)ioEvent->EventInfo.ZwCreateFile.OriginalFileName); ioEvent->EventInfo.ZwCreateFile.OriginalFileName = NULL; } diff --git a/dokan/directory.c b/dokan/directory.c index 9013a0f32..e91352904 100644 --- a/dokan/directory.c +++ b/dokan/directory.c @@ -544,14 +544,14 @@ void EndFindFilesCommon(DOKAN_IO_EVENT *EventInfo, NTSTATUS ResultStatus) { if(EventInfo->DokanOpenInfo->DirListSearchPattern) { - free(EventInfo->DokanOpenInfo->DirListSearchPattern); + DokanFree(EventInfo->DokanOpenInfo->DirListSearchPattern); EventInfo->DokanOpenInfo->DirListSearchPattern = NULL; } if(EventInfo->KernelInfo.EventContext.Operation.Directory.SearchPatternLength > 0) { EventInfo->DokanOpenInfo->DirListSearchPattern = - _wcsdup((PWCHAR)( + DokanDupW((PWCHAR)( (SIZE_T)&EventInfo->KernelInfo.EventContext.Operation.Directory.SearchPatternBase[0] + (SIZE_T)EventInfo->KernelInfo.EventContext.Operation.Directory.SearchPatternOffset)); } @@ -581,9 +581,10 @@ void BeginDispatchDirectoryInformation(DOKAN_IO_EVENT *EventInfo) { ULONG fileInfoClass = EventInfo->KernelInfo.EventContext.Operation.Directory.FileInformationClass; BOOL forceScan = FALSE; - DbgPrint("###FindFiles file handle = 0x%p, eventID = %04d\n", + DbgPrint("###FindFiles file handle = 0x%p, eventID = %04d, event Info = 0x%p\n", EventInfo->DokanOpenInfo, - EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1); + EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1, + EventInfo); assert(EventInfo->ProcessingContext == NULL); diff --git a/dokan/dokan.c b/dokan/dokan.c index a8b63edce..61e61c22f 100644 --- a/dokan/dokan.c +++ b/dokan/dokan.c @@ -78,6 +78,11 @@ CRITICAL_SECTION g_FileInfoCriticalSection; DOKAN_VECTOR *g_DirectoryListPool = NULL; CRITICAL_SECTION g_DirectoryListCriticalSection; +volatile PDokanMalloc g_DokanMalloc = NULL; +volatile PDokanFree g_DokanFree = NULL; +volatile PDokanRealloc g_DokanRealloc = NULL; + +volatile LONG g_DokanInitialized = 0; // TODO NEXT: // * Double check all DOKAN_VECTOR code. @@ -96,9 +101,7 @@ VOID DOKANAPI DokanUseStdErr(BOOL Status) { g_UseStdErr = Status; } VOID DOKANAPI DokanDebugMode(BOOL Status) { g_DebugMode = Status; } -int InitializeThreadPool(HMODULE hModule) { - - UNREFERENCED_PARAMETER(hModule); +int InitializeThreadPool() { EnterCriticalSection(&g_InstanceCriticalSection); { @@ -130,8 +133,6 @@ void CleanupThreadpool() { EnterCriticalSection(&g_InstanceCriticalSection); { - // TODO: Iterate all instances and deallocate their cleanup groups - if(g_ThreadPool) { CloseThreadpool(g_ThreadPool); @@ -159,7 +160,7 @@ DOKAN_IO_EVENT* PopIoEventBuffer() { if(!ioEvent) { - ioEvent = (DOKAN_IO_EVENT*)malloc(sizeof(DOKAN_IO_EVENT)); + ioEvent = (DOKAN_IO_EVENT*)DokanMalloc(sizeof(DOKAN_IO_EVENT)); } if(ioEvent) { @@ -175,7 +176,7 @@ void FreeIOEventBuffer(DOKAN_IO_EVENT *IOEvent) { if(IOEvent) { - free(IOEvent); + DokanFree(IOEvent); } } @@ -242,7 +243,7 @@ DOKAN_OVERLAPPED* PopOverlapped() { if(!overlapped) { - overlapped = (DOKAN_OVERLAPPED*)malloc(sizeof(DOKAN_OVERLAPPED)); + overlapped = (DOKAN_OVERLAPPED*)DokanMalloc(sizeof(DOKAN_OVERLAPPED)); /*overlapped->InternalOverlapped.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); if(!overlapped->InternalOverlapped.hEvent) { @@ -268,7 +269,7 @@ void FreeOverlapped(DOKAN_OVERLAPPED *Overlapped) { CloseHandle(Overlapped->InternalOverlapped.hEvent); } - free(Overlapped); + DokanFree(Overlapped); } } @@ -311,7 +312,7 @@ EVENT_INFORMATION* PopEventResult() { if(!eventResult) { - eventResult = (EVENT_INFORMATION*)malloc(DOKAN_EVENT_INFO_DEFAULT_SIZE); + eventResult = (EVENT_INFORMATION*)DokanMalloc(DOKAN_EVENT_INFO_DEFAULT_SIZE); } if(eventResult) { @@ -326,7 +327,7 @@ void FreeEventResult(EVENT_INFORMATION *EventResult) { if(EventResult) { - free(EventResult); + DokanFree(EventResult); } } @@ -369,7 +370,7 @@ DOKAN_OPEN_INFO* PopFileOpenInfo() { if(!fileInfo) { - fileInfo = (DOKAN_OPEN_INFO*)malloc(sizeof(DOKAN_OPEN_INFO)); + fileInfo = (DOKAN_OPEN_INFO*)DokanMalloc(sizeof(DOKAN_OPEN_INFO)); RtlZeroMemory(fileInfo, sizeof(DOKAN_OPEN_INFO)); @@ -398,7 +399,7 @@ void CleanupFileOpenInfo(DOKAN_OPEN_INFO *FileInfo) { { if(FileInfo->DirListSearchPattern) { - free(FileInfo->DirListSearchPattern); + DokanFree(FileInfo->DirListSearchPattern); FileInfo->DirListSearchPattern = NULL; } @@ -423,7 +424,7 @@ void FreeFileOpenInfo(DOKAN_OPEN_INFO *FileInfo) { CleanupFileOpenInfo(FileInfo); DeleteCriticalSection(&FileInfo->CriticalSection); - free(FileInfo); + DokanFree(FileInfo); } } @@ -503,7 +504,8 @@ void PushDirectoryList(DOKAN_VECTOR *DirectoryList) { PDOKAN_INSTANCE NewDokanInstance() { - PDOKAN_INSTANCE instance = (PDOKAN_INSTANCE)malloc(sizeof(DOKAN_INSTANCE)); + + PDOKAN_INSTANCE instance = (PDOKAN_INSTANCE)DokanMalloc(sizeof(DOKAN_INSTANCE)); if(instance == NULL) { return NULL; @@ -530,7 +532,7 @@ NewDokanInstance() { DeleteCriticalSection(&instance->CriticalSection); - free(instance); + DokanFree(instance); return NULL; } @@ -546,7 +548,7 @@ NewDokanInstance() { CloseHandle(instance->DeviceClosedWaitHandle); - free(instance); + DokanFree(instance); return NULL; } @@ -564,7 +566,7 @@ NewDokanInstance() { CloseHandle(instance->DeviceClosedWaitHandle); - free(instance); + DokanFree(instance); return NULL; } @@ -626,7 +628,7 @@ void DeleteDokanInstance(PDOKAN_INSTANCE Instance) { CloseHandle(Instance->DeviceClosedWaitHandle); - free(Instance); + DokanFree(Instance); } BOOL IsMountPointDriveLetter(LPCWSTR mountPoint) { @@ -856,6 +858,11 @@ int DOKANAPI DokanCreateFileSystem( ULARGE_INTEGER timerDueTime; WCHAR rawDeviceName[MAX_PATH]; + 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; @@ -1212,7 +1219,10 @@ void ProcessIOEvent( BeginDispatchSetSecurity(currentIoEvent); break; default: - DokanDbgPrintW(L"Dokan Warning: Unsupported IRP 0x%x.\n", currentIoEvent->KernelInfo.EventContext.MajorFunction); + DokanDbgPrintW(L"Dokan Warning: Unsupported IRP 0x%x, event Info = 0x%p.\n", + currentIoEvent->KernelInfo.EventContext.MajorFunction, + currentIoEvent); + PushIoEventBuffer(currentIoEvent); break; } @@ -1346,8 +1356,10 @@ BOOL SendEventInformation(PEVENT_INFORMATION EventInfo, DWORD lastError = 0; DWORD eventSize = max(sizeof(EVENT_INFORMATION), DOKAN_EVENT_INFO_ALLOC_SIZE(EventInfo->BufferLength)); - DbgPrint("Dokan Information: SendEventInformation() with NTSTATUS result 0x%x and context 0x%lx\n", - EventInfo->Status, EventInfo->Context); + DbgPrint("Dokan Information: SendEventInformation() with NTSTATUS 0x%x, context 0x%lx, and result object 0x%p\n", + EventInfo->Status, + EventInfo->Context, + EventInfo); overlapped = PopOverlapped(); @@ -1424,7 +1436,7 @@ void CreateDispatchCommon(DOKAN_IO_EVENT *EventInfo, ULONG SizeOfEventInfo) { else { EventInfo->EventResultSize = DOKAN_EVENT_INFO_ALLOC_SIZE(SizeOfEventInfo); - EventInfo->EventResult = (PEVENT_INFORMATION)malloc(EventInfo->EventResultSize); + EventInfo->EventResult = (PEVENT_INFORMATION)DokanMalloc(EventInfo->EventResultSize); EventInfo->Flags &= ~DOKAN_IO_EVENT_FLAGS_POOLED_RESULT; RtlZeroMemory(EventInfo->EventResult, EventInfo->EventResultSize); @@ -1446,7 +1458,7 @@ void FreeIoEventResult(PEVENT_INFORMATION EventResult, DOKAN_IO_EVENT_FLAGS Flag } else { - free(EventResult); + DokanFree(EventResult); } } } @@ -1469,18 +1481,25 @@ BOOL SendReleaseIRP(LPCWSTR DeviceName) { } BOOL SendGlobalReleaseIRP(LPCWSTR MountPoint) { + if (MountPoint != NULL) { + size_t length = wcslen(MountPoint); + if (length > 0) { + ULONG returnedLength; ULONG inputLength = sizeof(DOKAN_UNICODE_STRING_INTERMEDIATE) + (MAX_PATH * sizeof(WCHAR)); - PDOKAN_UNICODE_STRING_INTERMEDIATE szMountPoint = malloc(inputLength); + PDOKAN_UNICODE_STRING_INTERMEDIATE szMountPoint = DokanMalloc(inputLength); if (szMountPoint != NULL) { + ZeroMemory(szMountPoint, inputLength); + szMountPoint->MaximumLength = MAX_PATH * sizeof(WCHAR); szMountPoint->Length = (USHORT)(length * sizeof(WCHAR)); + CopyMemory(szMountPoint->Buffer, MountPoint, szMountPoint->Length); DbgPrint("send global release for %ws\n", MountPoint); @@ -1490,11 +1509,14 @@ BOOL SendGlobalReleaseIRP(LPCWSTR MountPoint) { &returnedLength)) { DbgPrint("Failed to unmount: %ws\n", MountPoint); - free(szMountPoint); + + DokanFree(szMountPoint); + return FALSE; } - free(szMountPoint); + DokanFree(szMountPoint); + return TRUE; } } @@ -1650,15 +1672,125 @@ BOOL WINAPI DllMain(HINSTANCE Instance, DWORD Reason, LPVOID Reserved) { switch (Reason) { case DLL_PROCESS_ATTACH: { + + } break; + case DLL_PROCESS_DETACH: { + + } break; + } + return TRUE; +} + +VOID DOKANAPI DokanMapKernelToUserCreateFileFlags( + ULONG FileAttributes, ULONG CreateOptions, ULONG CreateDisposition, + DWORD *outFileAttributesAndFlags, DWORD *outCreationDisposition) { + if (outFileAttributesAndFlags) { + + *outFileAttributesAndFlags = FileAttributes; + + DokanMapKernelBit(*outFileAttributesAndFlags, CreateOptions, + FILE_FLAG_WRITE_THROUGH, FILE_WRITE_THROUGH); + DokanMapKernelBit(*outFileAttributesAndFlags, CreateOptions, + FILE_FLAG_SEQUENTIAL_SCAN, FILE_SEQUENTIAL_ONLY); + DokanMapKernelBit(*outFileAttributesAndFlags, CreateOptions, + FILE_FLAG_RANDOM_ACCESS, FILE_RANDOM_ACCESS); + DokanMapKernelBit(*outFileAttributesAndFlags, CreateOptions, + FILE_FLAG_NO_BUFFERING, FILE_NO_INTERMEDIATE_BUFFERING); + DokanMapKernelBit(*outFileAttributesAndFlags, CreateOptions, + FILE_FLAG_OPEN_REPARSE_POINT, FILE_OPEN_REPARSE_POINT); + DokanMapKernelBit(*outFileAttributesAndFlags, CreateOptions, + FILE_FLAG_DELETE_ON_CLOSE, FILE_DELETE_ON_CLOSE); + DokanMapKernelBit(*outFileAttributesAndFlags, CreateOptions, + FILE_FLAG_BACKUP_SEMANTICS, FILE_OPEN_FOR_BACKUP_INTENT); + +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + DokanMapKernelBit(*outFileAttributesAndFlags, CreateOptions, + FILE_FLAG_SESSION_AWARE, FILE_SESSION_AWARE); +#endif + } + + if (outCreationDisposition) { + + switch (CreateDisposition) { + case FILE_CREATE: + *outCreationDisposition = CREATE_NEW; + break; + case FILE_OPEN: + *outCreationDisposition = OPEN_EXISTING; + break; + case FILE_OPEN_IF: + *outCreationDisposition = OPEN_ALWAYS; + break; + case FILE_OVERWRITE: + *outCreationDisposition = TRUNCATE_EXISTING; + break; + case FILE_SUPERSEDE: + // The documentation isn't clear on the difference between replacing a file + // and truncating it. + // For now we just map it to create/truncate + case FILE_OVERWRITE_IF: + *outCreationDisposition = CREATE_ALWAYS; + break; + default: + *outCreationDisposition = 0; + break; + } + } +} + +DOKAN_API PTP_POOL DOKAN_CALLBACK DokanGetThreadPool() { + + PTP_POOL threadPool; + + EnterCriticalSection(&g_InstanceCriticalSection); + + threadPool = g_ThreadPool; + + LeaveCriticalSection(&g_InstanceCriticalSection); + + return threadPool; +} + +void DOKANAPI DokanInit(DOKAN_MEMORY_CALLBACKS *memoryCallbacks) { + + // 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; + } + + if(memoryCallbacks) { +#if INTPTR_MAX == INT64_MAX + InterlockedExchange64((volatile LONG64*)&g_DokanMalloc, (LONG64)memoryCallbacks->Malloc); + InterlockedExchange64((volatile LONG64*)&g_DokanFree, (LONG64)memoryCallbacks->Free); + InterlockedExchange64((volatile LONG64*)&g_DokanRealloc, (LONG64)memoryCallbacks->Realloc); +#elif INTPTR_MAX == INT32_MAX + InterlockedExchange((volatile LONG*)&g_DokanMalloc, (LONG)memoryCallbacks->Malloc); + InterlockedExchange((volatile LONG*)&g_DokanFree, (LONG)memoryCallbacks->Free); + InterlockedExchange((volatile LONG*)&g_DokanRealloc, (LONG)memoryCallbacks->Realloc); +#else +#error Unsupported architecture! +#endif + } + #if _MSC_VER < 1300 - InitializeCriticalSection(&g_InstanceCriticalSection); + InitializeCriticalSection(&g_InstanceCriticalSection); InitializeCriticalSection(&g_EventBufferCriticalSection); InitializeCriticalSection(&g_OverlappedCriticalSection); InitializeCriticalSection(&g_EventResultCriticalSection); InitializeCriticalSection(&g_FileInfoCriticalSection); InitializeCriticalSection(&g_DirectoryListCriticalSection); #else - InitializeCriticalSectionAndSpinCount(&g_InstanceCriticalSection, 0x80000400); + InitializeCriticalSectionAndSpinCount(&g_InstanceCriticalSection, 0x80000400); InitializeCriticalSectionAndSpinCount(&g_EventBufferCriticalSection, 0x80000400); InitializeCriticalSectionAndSpinCount(&g_OverlappedCriticalSection, 0x80000400); InitializeCriticalSectionAndSpinCount(&g_EventResultCriticalSection, 0x80000400); @@ -1666,19 +1798,32 @@ BOOL WINAPI DllMain(HINSTANCE Instance, DWORD Reason, LPVOID Reserved) { InitializeCriticalSectionAndSpinCount(&g_DirectoryListCriticalSection, 0x80000400); #endif - InitializeListHead(&g_InstanceList); - InitializeThreadPool(Instance); + InitializeListHead(&g_InstanceList); + InitializeThreadPool(); g_EventBufferPool = DokanVector_AllocWithCapacity(sizeof(void*), DOKAN_IO_EVENT_POOL_SIZE); g_OverlappedPool = DokanVector_AllocWithCapacity(sizeof(void*), DOKAN_OVERLAPPED_POOL_SIZE); g_EventResultPool = DokanVector_AllocWithCapacity(sizeof(void*), DOKAN_OVERLAPPED_POOL_SIZE); g_FileInfoPool = DokanVector_AllocWithCapacity(sizeof(void*), DOKAN_OVERLAPPED_POOL_SIZE); g_DirectoryListPool = DokanVector_AllocWithCapacity(sizeof(void*), DOKAN_DIRECTORY_LIST_POOL_SIZE); +} - } break; - case DLL_PROCESS_DETACH: { +void DOKANAPI DokanShutdown() { + + LONG initRefCount = InterlockedDecrement(&g_DokanInitialized); + + if(initRefCount < 0) { + + RaiseException(DOKAN_EXCEPTION_SHUTDOWN_FAILED, EXCEPTION_NONCONTINUABLE, 0, NULL); + return; + } + + if(initRefCount > 0) { - EnterCriticalSection(&g_InstanceCriticalSection); + return; + } + + EnterCriticalSection(&g_InstanceCriticalSection); { while(!IsListEmpty(&g_InstanceList)) { @@ -1690,7 +1835,7 @@ BOOL WINAPI DllMain(HINSTANCE Instance, DWORD Reason, LPVOID Reserved) { DokanCloseHandle((DOKAN_HANDLE)instance); } } - LeaveCriticalSection(&g_InstanceCriticalSection); + LeaveCriticalSection(&g_InstanceCriticalSection); CleanupThreadpool(); @@ -1781,79 +1926,87 @@ BOOL WINAPI DllMain(HINSTANCE Instance, DWORD Reason, LPVOID Reserved) { //////////////////// Object pool cleanup finished //////////////////// - DeleteCriticalSection(&g_InstanceCriticalSection); - - } break; - } - return TRUE; + DeleteCriticalSection(&g_InstanceCriticalSection); } -VOID DOKANAPI DokanMapKernelToUserCreateFileFlags( - ULONG FileAttributes, ULONG CreateOptions, ULONG CreateDisposition, - DWORD *outFileAttributesAndFlags, DWORD *outCreationDisposition) { - if (outFileAttributesAndFlags) { +void* DokanMallocImpl(size_t size, const char *fileName, int lineNumber) { - *outFileAttributesAndFlags = FileAttributes; + if(InterlockedAdd(&g_DokanInitialized, 0) <= 0) { - DokanMapKernelBit(*outFileAttributesAndFlags, CreateOptions, - FILE_FLAG_WRITE_THROUGH, FILE_WRITE_THROUGH); - DokanMapKernelBit(*outFileAttributesAndFlags, CreateOptions, - FILE_FLAG_SEQUENTIAL_SCAN, FILE_SEQUENTIAL_ONLY); - DokanMapKernelBit(*outFileAttributesAndFlags, CreateOptions, - FILE_FLAG_RANDOM_ACCESS, FILE_RANDOM_ACCESS); - DokanMapKernelBit(*outFileAttributesAndFlags, CreateOptions, - FILE_FLAG_NO_BUFFERING, FILE_NO_INTERMEDIATE_BUFFERING); - DokanMapKernelBit(*outFileAttributesAndFlags, CreateOptions, - FILE_FLAG_OPEN_REPARSE_POINT, FILE_OPEN_REPARSE_POINT); - DokanMapKernelBit(*outFileAttributesAndFlags, CreateOptions, - FILE_FLAG_DELETE_ON_CLOSE, FILE_DELETE_ON_CLOSE); - DokanMapKernelBit(*outFileAttributesAndFlags, CreateOptions, - FILE_FLAG_BACKUP_SEMANTICS, FILE_OPEN_FOR_BACKUP_INTENT); + RaiseException(DOKAN_EXCEPTION_NOT_INITIALIZED, 0, 0, NULL); -#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) - DokanMapKernelBit(*outFileAttributesAndFlags, CreateOptions, - FILE_FLAG_SESSION_AWARE, FILE_SESSION_AWARE); + return malloc(size); + } + + PDokanMalloc dokanMalloc = NULL; + +#if INTPTR_MAX == INT64_MAX + dokanMalloc = (PDokanMalloc)InterlockedAdd64((volatile LONG64*)&g_DokanMalloc, 0); +#elif INTPTR_MAX == INT32_MAX + dokanMalloc = (PDokanMalloc)InterlockedAdd((volatile LONG*)&g_DokanMalloc, 0); +#else +#error Unsupported architecture! #endif - } - if (outCreationDisposition) { + if(dokanMalloc) { - switch (CreateDisposition) { - case FILE_CREATE: - *outCreationDisposition = CREATE_NEW; - break; - case FILE_OPEN: - *outCreationDisposition = OPEN_EXISTING; - break; - case FILE_OPEN_IF: - *outCreationDisposition = OPEN_ALWAYS; - break; - case FILE_OVERWRITE: - *outCreationDisposition = TRUNCATE_EXISTING; - break; - case FILE_SUPERSEDE: - // The documentation isn't clear on the difference between replacing a file - // and truncating it. - // For now we just map it to create/truncate - case FILE_OVERWRITE_IF: - *outCreationDisposition = CREATE_ALWAYS; - break; - default: - *outCreationDisposition = 0; - break; - } - } + return dokanMalloc(size, fileName, lineNumber); + } + + return malloc(size); } -DOKAN_API PTP_POOL DOKAN_CALLBACK DokanGetThreadPool() { +void DokanFreeImpl(void *userData) { - PTP_POOL threadPool; + PDokanFree dokanFree = NULL; - EnterCriticalSection(&g_InstanceCriticalSection); +#if INTPTR_MAX == INT64_MAX + dokanFree = (PDokanFree)InterlockedAdd64((volatile LONG64*)&g_DokanFree, 0); +#elif INTPTR_MAX == INT32_MAX + dokanFree = (PDokanFree)InterlockedAdd((volatile LONG*)&g_DokanFree, 0); +#else +#error Unsupported architecture! +#endif - threadPool = g_ThreadPool; + if(dokanFree) { - LeaveCriticalSection(&g_InstanceCriticalSection); + dokanFree(userData); + } + else { - return threadPool; + free(userData); + } +} + +void* DokanReallocImpl(void *userData, size_t newSize, const char *fileName, int lineNumber) { + + PDokanRealloc dokanRealloc = NULL; + +#if INTPTR_MAX == INT64_MAX + dokanRealloc = (PDokanRealloc)InterlockedAdd64((volatile LONG64*)&g_DokanRealloc, 0); +#elif INTPTR_MAX == INT32_MAX + dokanRealloc = (PDokanRealloc)InterlockedAdd((volatile LONG*)&g_DokanRealloc, 0); +#else +#error Unsupported architecture! +#endif + + if(dokanRealloc) { + + return dokanRealloc(userData, newSize, fileName, lineNumber); + } + + return realloc(userData, newSize); +} + +LPWSTR DokanDupWImpl(LPCWSTR str, const char *fileName, int lineNumber) { + + size_t strLength = wcslen(str); + LPWSTR newStr = DokanMallocImpl((strLength + 1) * sizeof(WCHAR), fileName, lineNumber); + + if(newStr) { + + wcscpy_s(newStr, strLength + 1, str); + } + + return newStr; } \ No newline at end of file diff --git a/dokan/dokan.def b/dokan/dokan.def index da6dbd023..d473dcccd 100644 --- a/dokan/dokan.def +++ b/dokan/dokan.def @@ -56,4 +56,6 @@ DokanEndDispatchSetEndOfFile DokanEndDispatchMoveFile DokanEndDispatchFlush DokanEndDispatchGetFileSecurity -DokanEndDispatchSetFileSecurity \ No newline at end of file +DokanEndDispatchSetFileSecurity +DokanInit +DokanShutdown \ No newline at end of file diff --git a/dokan/dokan.h b/dokan/dokan.h index 113fe6ab1..2d3b2bb93 100644 --- a/dokan/dokan.h +++ b/dokan/dokan.h @@ -111,6 +111,20 @@ typedef struct _DOKAN_UNMOUNTED_INFO { PDOKAN_OPTIONS DokanOptions; // A pointer to DOKAN_OPTIONS } DOKAN_UNMOUNTED_INFO, *PDOKAN_UNMOUNTED_INFO; +typedef void* (WINAPI *PDokanMalloc)(size_t size, const char *fileName, int lineNumber); +typedef void (WINAPI *PDokanFree)(void *userData); +typedef void* (WINAPI *PDokanRealloc)(void *userData, size_t newSize, const char *fileName, int lineNumber); + +typedef struct _DOKAN_MEMORY_CALLBACKS { + PDokanMalloc Malloc; + PDokanFree Free; + PDokanRealloc Realloc; +} DOKAN_MEMORY_CALLBACKS, *PDOKAN_MEMORY_CALLBACKS; + +#define DOKAN_EXCEPTION_NOT_INITIALIZED 0x0f0ff0ff +#define DOKAN_EXCEPTION_INITIALIZATION_FAILED 0x0fbadbad +#define DOKAN_EXCEPTION_SHUTDOWN_FAILED 0x0fbadf00 + // Forward declarations struct _DOKAN_FIND_FILES_EVENT; typedef struct _DOKAN_FIND_FILES_EVENT DOKAN_FIND_FILES_EVENT, *PDOKAN_FIND_FILES_EVENT; @@ -474,6 +488,10 @@ void DOKANAPI DokanEndDispatchSetFileSecurity(DOKAN_SET_FILE_SECURITY_EVENT *Eve // Threading helpers DOKAN_API PTP_POOL DOKAN_CALLBACK DokanGetThreadPool(); +// Init/shutdown +void DOKANAPI DokanInit(DOKAN_MEMORY_CALLBACKS *memoryCallbacks); +void DOKANAPI DokanShutdown(); + #ifdef __cplusplus } #endif diff --git a/dokan/dokan_vector.c b/dokan/dokan_vector.c index 53fea2baa..5891b44c6 100644 --- a/dokan/dokan_vector.c +++ b/dokan/dokan_vector.c @@ -19,9 +19,9 @@ DOKAN_VECTOR* DOKANAPI DokanVector_Alloc(size_t ItemSize) { return NULL; } - DOKAN_VECTOR *vector = (DOKAN_VECTOR*)malloc(sizeof(DOKAN_VECTOR)); + DOKAN_VECTOR *vector = (DOKAN_VECTOR*)DokanMalloc(sizeof(DOKAN_VECTOR)); - vector->Items = malloc(ItemSize * DEFAULT_ITEM_COUNT); + vector->Items = DokanMalloc(ItemSize * DEFAULT_ITEM_COUNT); vector->ItemCount = 0; vector->ItemSize = ItemSize; vector->MaxItems = DEFAULT_ITEM_COUNT; @@ -40,11 +40,11 @@ DOKAN_VECTOR* DOKANAPI DokanVector_AllocWithCapacity(size_t ItemSize, size_t Max return NULL; } - DOKAN_VECTOR *vector = (DOKAN_VECTOR*)malloc(sizeof(DOKAN_VECTOR)); + DOKAN_VECTOR *vector = (DOKAN_VECTOR*)DokanMalloc(sizeof(DOKAN_VECTOR)); if(MaxItems > 0) { - vector->Items = malloc(ItemSize * MaxItems); + vector->Items = DokanMalloc(ItemSize * MaxItems); } else { @@ -73,7 +73,7 @@ BOOL DOKANAPI DokanVector_StackAlloc(DOKAN_VECTOR *Vector, size_t ItemSize) { return FALSE; } - Vector->Items = malloc(ItemSize * DEFAULT_ITEM_COUNT); + Vector->Items = DokanMalloc(ItemSize * DEFAULT_ITEM_COUNT); Vector->ItemCount = 0; Vector->ItemSize = ItemSize; Vector->MaxItems = DEFAULT_ITEM_COUNT; @@ -98,7 +98,7 @@ BOOL DOKANAPI DokanVector_StackAllocWithCapacity(DOKAN_VECTOR *Vector, size_t It if(MaxItems > 0) { - Vector->Items = malloc(ItemSize * MaxItems); + Vector->Items = DokanMalloc(ItemSize * MaxItems); } else { @@ -120,12 +120,12 @@ void DOKANAPI DokanVector_Free(DOKAN_VECTOR *Vector) { if(Vector->Items) { - free(Vector->Items); + DokanFree(Vector->Items); } if(!Vector->IsStackAllocated) { - free(Vector); + DokanFree(Vector); } } } @@ -238,7 +238,7 @@ BOOL DokanVector_Grow(DOKAN_VECTOR *Vector, size_t MinimumIncrease) { if(MinimumIncrease == 0 && Vector->MaxItems == 0) { - Vector->Items = malloc(Vector->ItemSize * DEFAULT_ITEM_COUNT); + Vector->Items = DokanMalloc(Vector->ItemSize * DEFAULT_ITEM_COUNT); Vector->MaxItems = DEFAULT_ITEM_COUNT; return TRUE; @@ -256,7 +256,7 @@ BOOL DokanVector_Grow(DOKAN_VECTOR *Vector, size_t MinimumIncrease) { newSize = Vector->MaxItems + MinimumIncrease + MinimumIncrease; } - void *newItems = realloc(Vector->Items, newSize * Vector->ItemSize); + void *newItems = DokanRealloc(Vector->Items, newSize * Vector->ItemSize); if(newItems) { diff --git a/dokan/dokani.h b/dokan/dokani.h index 5d1069434..fd31767b9 100644 --- a/dokan/dokani.h +++ b/dokan/dokani.h @@ -278,6 +278,16 @@ void PushDirectoryList(DOKAN_VECTOR *DirectoryList); void DokanNotifyUnmounted(DOKAN_INSTANCE *Instance); +void* DokanMallocImpl(size_t size, const char *fileName, int lineNumber); +void DokanFreeImpl(void *userData); +void* DokanReallocImpl(void *userData, size_t newSize, const char *fileName, int lineNumber); +LPWSTR DokanDupWImpl(LPCWSTR str, const char *fileName, int lineNumber); + +#define DokanMalloc(size) DokanMallocImpl((size), __FILE__, __LINE__) +#define DokanFree(userData) DokanFreeImpl(userData) +#define DokanRealloc(userData, newSize) DokanReallocImpl((userData), (newSize), __FILE__, __LINE__) +#define DokanDupW(str) DokanDupWImpl((str), __FILE__, __LINE__) + #ifdef __cplusplus } #endif diff --git a/dokan/fileinfo.c b/dokan/fileinfo.c index 87ca66ab0..8b3779700 100644 --- a/dokan/fileinfo.c +++ b/dokan/fileinfo.c @@ -330,9 +330,10 @@ void BeginDispatchQueryInformation(DOKAN_IO_EVENT *EventInfo) { DOKAN_FIND_STREAMS_EVENT *findStreams = &EventInfo->EventInfo.FindStreams; NTSTATUS status = STATUS_INVALID_PARAMETER; - DbgPrint("###GetFileInfo file handle = 0x%p, eventID = %04d\n", + DbgPrint("###GetFileInfo file handle = 0x%p, eventID = %04d, event Info = 0x%p\n", EventInfo->DokanOpenInfo, - EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1); + EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1, + EventInfo); assert((void*)getFileInfo == (void*)EventInfo && (void*)findStreams == (void*)EventInfo); assert(EventInfo->ProcessingContext == NULL); diff --git a/dokan/flush.c b/dokan/flush.c index 8365a06bf..6b9fa9984 100644 --- a/dokan/flush.c +++ b/dokan/flush.c @@ -28,9 +28,10 @@ void BeginDispatchFlush(DOKAN_IO_EVENT *EventInfo) { DOKAN_FLUSH_BUFFERS_EVENT *flushBuffers = &EventInfo->EventInfo.FlushBuffers; NTSTATUS status = STATUS_NOT_IMPLEMENTED; - DbgPrint("###Flush file handle = 0x%p, eventID = %04d\n", + DbgPrint("###Flush file handle = 0x%p, eventID = %04d, event Info = 0x%p\n", EventInfo->DokanOpenInfo, - EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1); + EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1, + EventInfo); assert(EventInfo->DokanOpenInfo); assert((void*)flushBuffers == (void*)EventInfo); diff --git a/dokan/lock.c b/dokan/lock.c index 1043bf517..010da7b40 100644 --- a/dokan/lock.c +++ b/dokan/lock.c @@ -48,9 +48,10 @@ void BeginDispatchLock(DOKAN_IO_EVENT *EventInfo) { DOKAN_UNLOCK_FILE_EVENT *unlockFileEvent = &EventInfo->EventInfo.UnlockFile; NTSTATUS status = STATUS_NOT_IMPLEMENTED; - DbgPrint("###Lock file handle = 0x%p, eventID = %04d\n", + DbgPrint("###Lock file handle = 0x%p, eventID = %04d, event Info = 0x%p\n", EventInfo->DokanOpenInfo, - EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1); + EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1, + EventInfo); assert(EventInfo->DokanOpenInfo); assert(EventInfo->ProcessingContext == NULL); diff --git a/dokan/mount.c b/dokan/mount.c index 7d1fd4717..5968f03d2 100644 --- a/dokan/mount.c +++ b/dokan/mount.c @@ -363,8 +363,10 @@ BOOL CreateMountPoint(LPCWSTR MountPoint, LPCWSTR DeviceName) { FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) + targetLength + sizeof(WCHAR) + sizeof(WCHAR); - reparseData = (PREPARSE_DATA_BUFFER)malloc(bufferLength); + reparseData = (PREPARSE_DATA_BUFFER)DokanMalloc(bufferLength); + if (reparseData == NULL) { + CloseHandle(handle); return FALSE; } @@ -388,15 +390,20 @@ BOOL CreateMountPoint(LPCWSTR MountPoint, LPCWSTR DeviceName) { bufferLength, NULL, 0, &resultLength, NULL); CloseHandle(handle); - free(reparseData); + + DokanFree(reparseData); if (result) { + DbgPrintW(L"CreateMountPoint %s -> %s success\n", MountPoint, targetDeviceName); - } else { + } + else { + DbgPrintW(L"CreateMountPoint %s -> %s failed: %d\n", MountPoint, targetDeviceName, GetLastError()); } + return result; } diff --git a/dokan/read.c b/dokan/read.c index 438e43d09..c96b4ff57 100644 --- a/dokan/read.c +++ b/dokan/read.c @@ -36,9 +36,10 @@ void BeginDispatchRead(DOKAN_IO_EVENT *EventInfo) { CheckFileName(EventInfo->KernelInfo.EventContext.Operation.Read.FileName); - DbgPrint("###Read file handle = 0x%p, eventID = %04d\n", + DbgPrint("###Read file handle = 0x%p, eventID = %04d, event Info = 0x%p\n", EventInfo->DokanOpenInfo, - EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1); + EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1, + EventInfo); CreateDispatchCommon(EventInfo, EventInfo->KernelInfo.EventContext.Operation.Read.BufferLength); diff --git a/dokan/security.c b/dokan/security.c index 62155b706..143cac3a3 100644 --- a/dokan/security.c +++ b/dokan/security.c @@ -28,9 +28,10 @@ void BeginDispatchQuerySecurity(DOKAN_IO_EVENT *EventInfo) { DOKAN_GET_FILE_SECURITY_EVENT *getFileSecurity = &EventInfo->EventInfo.GetFileSecurityW; NTSTATUS status = STATUS_NOT_IMPLEMENTED; - DbgPrint("###GetFileSecurity file handle = 0x%p, eventID = %04d\n", + DbgPrint("###GetFileSecurity file handle = 0x%p, eventID = %04d, event Info = 0x%p\n", EventInfo->DokanOpenInfo, - EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1); + EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1, + EventInfo); assert(EventInfo->DokanOpenInfo); assert((void*)getFileSecurity == (void*)EventInfo); @@ -95,9 +96,10 @@ void BeginDispatchSetSecurity(DOKAN_IO_EVENT *EventInfo) { DOKAN_SET_FILE_SECURITY_EVENT *setFileSecurity = &EventInfo->EventInfo.SetFileSecurityW; NTSTATUS status = STATUS_NOT_IMPLEMENTED; - DbgPrint("###SetSecurity file handle = 0x%p, eventID = %04d\n", + DbgPrint("###SetSecurity file handle = 0x%p, eventID = %04d, event Info = 0x%p\n", EventInfo->DokanOpenInfo, - EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1); + EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1, + EventInfo); assert(EventInfo->DokanOpenInfo); assert((void*)setFileSecurity == (void*)EventInfo); diff --git a/dokan/setfile.c b/dokan/setfile.c index 3388863de..40063fd2a 100644 --- a/dokan/setfile.c +++ b/dokan/setfile.c @@ -257,10 +257,11 @@ void BeginDispatchSetInformation(DOKAN_IO_EVENT *EventInfo) { ULONG sizeOfEventInfo = 0; - DbgPrint("###SetFileInfo file handle = 0x%p, eventID = %04d, FileInformationClass = %d\n", + DbgPrint("###SetFileInfo file handle = 0x%p, eventID = %04d, FileInformationClass = %d, event Info = 0x%p\n", EventInfo->DokanOpenInfo, EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1, - EventInfo->KernelInfo.EventContext.Operation.SetFile.FileInformationClass); + EventInfo->KernelInfo.EventContext.Operation.SetFile.FileInformationClass, + EventInfo); assert(EventInfo->ProcessingContext == NULL); assert(EventInfo->DokanOpenInfo); diff --git a/dokan/timeout.c b/dokan/timeout.c index 7a59a123f..5413cb77c 100644 --- a/dokan/timeout.c +++ b/dokan/timeout.c @@ -36,7 +36,7 @@ BOOL DOKANAPI DokanResetTimeout(ULONG Timeout, PDOKAN_FILE_INFO FileInfo) { return FALSE; } - eventInfo = (PEVENT_INFORMATION)malloc(eventInfoSize); + eventInfo = (PEVENT_INFORMATION)DokanMalloc(eventInfoSize); if (eventInfo == NULL) { return FALSE; @@ -50,7 +50,7 @@ BOOL DOKANAPI DokanResetTimeout(ULONG Timeout, PDOKAN_FILE_INFO FileInfo) { GetRawDeviceName(ioEvent->DokanInstance->DeviceName, rawDeviceName, MAX_PATH), IOCTL_RESET_TIMEOUT, eventInfo, eventInfoSize, NULL, 0, &returnedLength); - free(eventInfo); + DokanFree(eventInfo); return status; } diff --git a/dokan/volume.c b/dokan/volume.c index 505dba0ea..09c2965ad 100644 --- a/dokan/volume.c +++ b/dokan/volume.c @@ -315,9 +315,10 @@ void BeginDispatchQueryVolumeInformation(DOKAN_IO_EVENT *EventInfo) { CreateDispatchCommon(EventInfo, EventInfo->KernelInfo.EventContext.Operation.Volume.BufferLength); - DbgPrint("###QueryVolumeInfo file handle = 0x%p, eventID = %04d\n", + DbgPrint("###QueryVolumeInfo file handle = 0x%p, eventID = %04d, event Info = 0x%p\n", EventInfo->DokanOpenInfo, - EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1); + EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1, + EventInfo); switch (EventInfo->KernelInfo.EventContext.Operation.Volume.FsInformationClass) { case FileFsVolumeInformation: diff --git a/dokan/write.c b/dokan/write.c index 691dbdcd9..4fea67cda 100644 --- a/dokan/write.c +++ b/dokan/write.c @@ -43,7 +43,7 @@ NTSTATUS SendWriteRequest(DOKAN_IO_EVENT *EventInfo) { return STATUS_INTERNAL_ERROR; } - writeEvent = (DOKAN_IO_EVENT*)malloc(DOKAN_IO_EVENT_ALLOC_SIZE(EventInfo->KernelInfo.EventContext.Operation.Write.RequestLength)); + writeEvent = (DOKAN_IO_EVENT*)DokanMalloc(DOKAN_IO_EVENT_ALLOC_SIZE(EventInfo->KernelInfo.EventContext.Operation.Write.RequestLength)); if(!writeEvent) { @@ -84,7 +84,7 @@ NTSTATUS SendWriteRequest(DOKAN_IO_EVENT *EventInfo) { PushOverlapped(overlapped); - free(writeEvent); + DokanFree(writeEvent); return DokanNtStatusFromWin32(lastError); } @@ -119,9 +119,10 @@ void BeginDispatchWrite(DOKAN_IO_EVENT *EventInfo) { CheckFileName(EventInfo->KernelInfo.EventContext.Operation.Write.FileName); - DbgPrint("###WriteFile file handle = 0x%p, eventID = %04d\n", + DbgPrint("###WriteFile file handle = 0x%p, eventID = %04d, event Info = 0x%p\n", EventInfo->DokanOpenInfo, - EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1); + EventInfo->DokanOpenInfo != NULL ? EventInfo->DokanOpenInfo->EventId : -1, + EventInfo); if (dokan->DokanOperations->WriteFile) { diff --git a/dokan_control/dokanctl.c b/dokan_control/dokanctl.c index 6503a2e22..7e3527091 100644 --- a/dokan_control/dokanctl.c +++ b/dokan_control/dokanctl.c @@ -111,6 +111,8 @@ int __cdecl wmain(int argc, PWCHAR argv[]) { WCHAR type; PVOID wow64OldValue; + DokanInit(NULL); + DokanUseStdErr(TRUE); // Set dokan library debug output Wow64DisableWow64FsRedirection(&wow64OldValue); // Disable system32 direct @@ -151,7 +153,11 @@ int __cdecl wmain(int argc, PWCHAR argv[]) { case L'i': if (type == L'd') { - return InstallDriver(driverFullPath); + int result = InstallDriver(driverFullPath); + + DokanShutdown(); + + return result; } else if (type == L'n') { if (DokanNetworkProviderInstall()) @@ -164,7 +170,11 @@ int __cdecl wmain(int argc, PWCHAR argv[]) { case L'r': if (type == L'd') { - return DeleteDokanService(DOKAN_DRIVER_SERVICE); + int result = DeleteDokanService(DOKAN_DRIVER_SERVICE); + + DokanShutdown(); + + return result; } else if (type == L'n') { if (DokanNetworkProviderUninstall()) @@ -205,5 +215,7 @@ int __cdecl wmain(int argc, PWCHAR argv[]) { fprintf(stderr, "unknown option\n"); } + DokanShutdown(); + return EXIT_SUCCESS; } diff --git a/dokan_fuse/include/dokanfuse.h b/dokan_fuse/include/dokanfuse.h index 787788d29..4becefcef 100644 --- a/dokan_fuse/include/dokanfuse.h +++ b/dokan_fuse/include/dokanfuse.h @@ -25,7 +25,14 @@ struct fuse_session struct fuse_chan { - fuse_chan():ResolvedDokanMain(NULL), ResolvedDokanUnmount(NULL), ResolvedDokanRemoveMountPoint(NULL), dokanDll(NULL) {} + fuse_chan() + : ResolvedDokanMain(NULL) + , ResolvedDokanUnmount(NULL) + , ResolvedDokanRemoveMountPoint(NULL) + , ResolvedShutdown(NULL) + , dokanDll(NULL) + {} + ~fuse_chan(); //This method dynamically loads DOKAN functions @@ -34,9 +41,12 @@ struct fuse_chan typedef int (__stdcall *DokanMainType)(PDOKAN_OPTIONS,PDOKAN_OPERATIONS); typedef BOOL (__stdcall *DokanUnmountType)(WCHAR DriveLetter); typedef BOOL (__stdcall *DokanRemoveMountPointType)(LPCWSTR MountPoint); + typedef void(__stdcall *DokanShutdownType)(); + DokanMainType ResolvedDokanMain; DokanUnmountType ResolvedDokanUnmount; DokanRemoveMountPointType ResolvedDokanRemoveMountPoint; + DokanShutdownType ResolvedShutdown; std::string mountpoint; private: diff --git a/dokan_fuse/src/dokanfuse.cpp b/dokan_fuse/src/dokanfuse.cpp index 0c308608d..49a3e4a93 100755 --- a/dokan_fuse/src/dokanfuse.cpp +++ b/dokan_fuse/src/dokanfuse.cpp @@ -663,27 +663,64 @@ bool fuse_chan::init() { // check version typedef ULONG(__stdcall * DokanVersionType)(); + typedef void(__stdcall *DokanInitType)(DOKAN_MEMORY_CALLBACKS *memoryCallbacks); + DokanVersionType ResolvedDokanVersion; + DokanInitType ResolvedInit; + + ResolvedInit = (DokanInitType)GetProcAddress(dokanDll, "DokanInit"); + ResolvedShutdown = (DokanShutdownType)GetProcAddress(dokanDll, "DokanShutdown"); + + if(!ResolvedInit || !ResolvedShutdown) { + + return false; + } + + ResolvedInit(NULL); + ResolvedDokanVersion = (DokanVersionType)GetProcAddress(dokanDll, "DokanVersion"); - if (!ResolvedDokanVersion || ResolvedDokanVersion() < DOKAN_VERSION) - return false; + + if(!ResolvedDokanVersion || ResolvedDokanVersion() < DOKAN_VERSION) { + + ResolvedShutdown(); + ResolvedShutdown = NULL; + + return false; + } ResolvedDokanMain = (DokanMainType)GetProcAddress(dokanDll, "DokanMain"); + ResolvedDokanUnmount = (DokanUnmountType)GetProcAddress(dokanDll, "DokanUnmount"); + ResolvedDokanRemoveMountPoint = (DokanRemoveMountPointType)GetProcAddress( dokanDll, "DokanRemoveMountPoint"); - if (!ResolvedDokanMain || !ResolvedDokanUnmount || - !ResolvedDokanRemoveMountPoint) - return false; + if(!ResolvedDokanMain + || !ResolvedDokanUnmount + || !ResolvedDokanRemoveMountPoint) { + + ResolvedShutdown(); + ResolvedShutdown = NULL; + + return false; + } + return true; } fuse_chan::~fuse_chan() { - if (dokanDll) - FreeLibrary(dokanDll); + + if(dokanDll) { + + if(ResolvedShutdown) { + + ResolvedShutdown(); + } + + FreeLibrary(dokanDll); + } } /////////////////////////////////////////////////////////////////////////////////////// diff --git a/samples/dokan_mirror/mirror.c b/samples/dokan_mirror/mirror.c index 0cf61cd39..c2274add8 100644 --- a/samples/dokan_mirror/mirror.c +++ b/samples/dokan_mirror/mirror.c @@ -1801,6 +1801,25 @@ void CALLBACK MirrorIoCallback( #endif +#if MIRROR_IS_DEBUGGING_MEMORY + +void* WINAPI MirrorMalloc(size_t size, const char *fileName, int lineNumber) { + + return _malloc_dbg(size, _NORMAL_BLOCK, fileName, lineNumber); +} + +void WINAPI MirrorFree(void *userData) { + + _free_dbg(userData, _NORMAL_BLOCK); +} + +void* WINAPI MirrorRealloc(void *userData, size_t newSize, const char *fileName, int lineNumber) { + + return _realloc_dbg(userData, newSize, _NORMAL_BLOCK, fileName, lineNumber); +} + +#endif + BOOL WINAPI CtrlHandler(DWORD dwCtrlType) { switch (dwCtrlType) { case CTRL_C_EVENT: @@ -1997,6 +2016,22 @@ int __cdecl wmain(ULONG argc, PWCHAR argv[]) { dokanOperations.SetFileSecurityW = MirrorSetFileSecurity; dokanOperations.FindStreams = MirrorFindStreams; +#if MIRROR_IS_DEBUGGING_MEMORY + + DOKAN_MEMORY_CALLBACKS memoryCallbacks; + + memoryCallbacks.Malloc = MirrorMalloc; + memoryCallbacks.Free = MirrorFree; + memoryCallbacks.Realloc = MirrorRealloc; + + DokanInit(&memoryCallbacks); + +#else + + DokanInit(NULL); + +#endif + #if USE_ASYNC_IO if(!InitializeAsyncIO()) { @@ -2057,5 +2092,7 @@ int __cdecl wmain(ULONG argc, PWCHAR argv[]) { break; } + DokanShutdown(); + return EXIT_SUCCESS; } \ No newline at end of file From 963f3054ad671845c50a290fa6b24dfae7cddc66 Mon Sep 17 00:00:00 2001 From: Keith Newton Date: Tue, 2 Aug 2016 22:35:57 -0400 Subject: [PATCH 08/20] Fixing the merge --- dokan/dokan.h | 4 -- dokan/mount.c | 11 +++-- samples/dokan_mirror/mirror.c | 83 +++++++++++++++++++++++------------ 3 files changed, 61 insertions(+), 37 deletions(-) diff --git a/dokan/dokan.h b/dokan/dokan.h index b441535aa..661c6052c 100644 --- a/dokan/dokan.h +++ b/dokan/dokan.h @@ -431,10 +431,6 @@ BOOL DOKANAPI DokanUnmount(WCHAR DriveLetter); BOOL DOKANAPI DokanRemoveMountPoint(LPCWSTR MountPoint); -// If Safe is TRUE, will broadcast to all desktop and Shell -// Safe should not be used during DLL_PROCESS_DETACH -BOOL DOKANAPI DokanRemoveMountPointEx(LPCWSTR MountPoint, BOOL Safe); - // DokanIsNameInExpression // checks whether Name can match Expression // Expression can contain wildcard characters (? and *) diff --git a/dokan/mount.c b/dokan/mount.c index 7347a792c..578728794 100644 --- a/dokan/mount.c +++ b/dokan/mount.c @@ -539,7 +539,7 @@ void GenerateUnmountPoint(LPCWSTR MountPoint, WCHAR *Result, size_t ResultMaxCha } } -BOOL DOKANAPI DokanRemoveMountPointEx(LPCWSTR MountPoint) { +BOOL DOKANAPI DokanRemoveMountPoint(LPCWSTR MountPoint) { if(MountPoint != NULL) { @@ -573,14 +573,14 @@ void DokanNotifyUnmounted(DOKAN_INSTANCE *Instance) { unmountPoint[length] = L'\\'; unmountPoint[length + 1] = L'\0'; - // Required to remove reparse point (could also be done through - // FSCTL_DELETE_REPARSE_POINT with DeleteMountPoint function) + // Required to remove reparse point (could also be done through + // FSCTL_DELETE_REPARSE_POINT with DeleteMountPoint function) DeleteVolumeMountPoint(unmountPoint); } } else { - // Notify applications / explorer + // Notify applications / explorer DokanBroadcastLink(Instance->MountPoint[0], TRUE, TRUE); } @@ -593,5 +593,4 @@ void DokanNotifyUnmounted(DOKAN_INSTANCE *Instance) { Instance->DokanOperations->Unmounted(&fileInfo); } - mountPoint[length + 1] = L'\0'; - DokanBroadcastLink(MountPoint[0], TRUE, Safe); +} \ No newline at end of file diff --git a/samples/dokan_mirror/mirror.c b/samples/dokan_mirror/mirror.c index d0d37da97..408310a72 100644 --- a/samples/dokan_mirror/mirror.c +++ b/samples/dokan_mirror/mirror.c @@ -817,7 +817,7 @@ static NTSTATUS DOKAN_CALLBACK MirrorReadFile(DOKAN_READ_FILE_EVENT *EventInfo) DWORD error = GetLastError(); - DbgPrint(L"\tseek error, offset = %d\n\n", offset); + DbgPrint(L"\tseek error, offset = %d\n\n", EventInfo->Offset); return DokanNtStatusFromWin32(error); } @@ -837,8 +837,10 @@ static NTSTATUS DOKAN_CALLBACK MirrorReadFile(DOKAN_READ_FILE_EVENT *EventInfo) } else { - DbgPrint(L"\tByte to read: %d, Byte read %d, offset %d\n\n", EventInfo->NumberOfBytesToRead, - EventInfo->NumberOfBytesRead, offset); + DbgPrint(L"\tByte to read: %d, Byte read %d, offset %d\n\n", + EventInfo->NumberOfBytesToRead, + EventInfo->NumberOfBytesRead, + EventInfo->Offset); } return STATUS_SUCCESS; @@ -864,7 +866,7 @@ static NTSTATUS DOKAN_CALLBACK MirrorWriteFile(DOKAN_WRITE_FILE_EVENT *EventInfo DWORD fileSizeLow = 0; DWORD fileSizeHigh = 0; - fileSizeLow = GetFileSize(handle, &fileSizeHigh); + fileSizeLow = GetFileSize(mirrorHandle->FileHandle, &fileSizeHigh); if (fileSizeLow == INVALID_FILE_SIZE) { @@ -872,11 +874,6 @@ static NTSTATUS DOKAN_CALLBACK MirrorWriteFile(DOKAN_WRITE_FILE_EVENT *EventInfo DbgPrint(L"\tcan not get a file size error = %d\n", error); - if (opened) { - - CloseHandle(handle); - } - return DokanNtStatusFromWin32(error); } @@ -884,6 +881,30 @@ static NTSTATUS DOKAN_CALLBACK MirrorWriteFile(DOKAN_WRITE_FILE_EVENT *EventInfo #if USE_ASYNC_IO + if(EventInfo->DokanFileInfo->PagingIo) { + + if((UINT64)EventInfo->Offset >= fileSize) { + + EventInfo->NumberOfBytesWritten = 0; + + return STATUS_SUCCESS; + } + + if(((UINT64)EventInfo->Offset + EventInfo->NumberOfBytesToWrite) > fileSize) { + + UINT64 bytes = fileSize - EventInfo->Offset; + + if(bytes >> 32) { + + EventInfo->NumberOfBytesToWrite = (DWORD)(bytes & 0xFFFFFFFFUL); + } + else { + + EventInfo->NumberOfBytesToWrite = (DWORD)bytes; + } + } + } + MIRROR_OVERLAPPED *overlapped = PopMirrorOverlapped(); if(!overlapped) { @@ -939,37 +960,45 @@ static NTSTATUS DOKAN_CALLBACK MirrorWriteFile(DOKAN_WRITE_FILE_EVENT *EventInfo } else { // Paging IO cannot write after allocate file size. - if (DokanFileInfo->PagingIo) { - if ((UINT64)Offset >= fileSize) { - *NumberOfBytesWritten = 0; - if (opened) - CloseHandle(handle); - return STATUS_SUCCESS; + if (EventInfo->DokanFileInfo->PagingIo) { + + if ((UINT64)EventInfo->Offset >= fileSize) { + + EventInfo->NumberOfBytesWritten = 0; + + return STATUS_SUCCESS; } - if (((UINT64)Offset + NumberOfBytesToWrite) > fileSize) { - UINT64 bytes = fileSize - Offset; + if (((UINT64)EventInfo->Offset + EventInfo->NumberOfBytesToWrite) > fileSize) { + + UINT64 bytes = fileSize - EventInfo->Offset; + if (bytes >> 32) { - NumberOfBytesToWrite = (DWORD)(bytes & 0xFFFFFFFFUL); - } else { - NumberOfBytesToWrite = (DWORD)bytes; + + EventInfo->NumberOfBytesToWrite = (DWORD)(bytes & 0xFFFFFFFFUL); + } + else { + + EventInfo->NumberOfBytesToWrite = (DWORD)bytes; } } } - if ((UINT64)Offset > fileSize) { + if ((UINT64)EventInfo->Offset > fileSize) { // In the mirror sample helperZeroFileData is not necessary. NTFS will // zero a hole. // But if user's file system is different from NTFS( or other Windows's // file systems ) then users will have to zero the hole themselves. } - distanceToMove.QuadPart = Offset; - if (!SetFilePointerEx(handle, distanceToMove, NULL, FILE_BEGIN)) { + distanceToMove.QuadPart = EventInfo->Offset; + + if (!SetFilePointerEx(mirrorHandle->FileHandle, distanceToMove, NULL, FILE_BEGIN)) { + DWORD error = GetLastError(); - DbgPrint(L"\tseek error, offset = %I64d, error = %d\n", Offset, error); - if (opened) - CloseHandle(handle); + + DbgPrint(L"\tseek error, offset = %I64d, error = %d\n", EventInfo->Offset, error); + return DokanNtStatusFromWin32(error); } } @@ -989,7 +1018,7 @@ static NTSTATUS DOKAN_CALLBACK MirrorWriteFile(DOKAN_WRITE_FILE_EVENT *EventInfo } else { - DbgPrint(L"\twrite %d, offset %I64d\n\n", *NumberOfBytesWritten, Offset); + DbgPrint(L"\twrite %d, offset %I64d\n\n", EventInfo->NumberOfBytesWritten, EventInfo->Offset); } return STATUS_SUCCESS; From 5921eabdfb3969416309a9deaec450c7fd380a0c Mon Sep 17 00:00:00 2001 From: Keith Newton Date: Mon, 8 Aug 2016 14:55:54 -0400 Subject: [PATCH 09/20] Fixed a bug in Mirror that caused SetFileBasicInformation to fail Added a unit tets project so that we can port winfstest to it instead of having a separate unit tests repo. --- UnitTests/ErrorCodes.cpp | 2264 +++++++++++++++++++++++++++ UnitTests/FileSystemTests.cpp | 272 ++++ UnitTests/UnitTests.vcxproj | 173 ++ UnitTests/UnitTests.vcxproj.filters | 36 + UnitTests/stdafx.cpp | 8 + UnitTests/stdafx.h | 13 + UnitTests/targetver.h | 8 + dokan.sln | 44 +- samples/dokan_mirror/mirror.c | 8 +- 9 files changed, 2823 insertions(+), 3 deletions(-) create mode 100644 UnitTests/ErrorCodes.cpp create mode 100644 UnitTests/FileSystemTests.cpp create mode 100644 UnitTests/UnitTests.vcxproj create mode 100644 UnitTests/UnitTests.vcxproj.filters create mode 100644 UnitTests/stdafx.cpp create mode 100644 UnitTests/stdafx.h create mode 100644 UnitTests/targetver.h diff --git a/UnitTests/ErrorCodes.cpp b/UnitTests/ErrorCodes.cpp new file mode 100644 index 000000000..5b9c6ca2a --- /dev/null +++ b/UnitTests/ErrorCodes.cpp @@ -0,0 +1,2264 @@ +#include "stdafx.h" +#include + +#define ERR(code) case code: return L#code + +const wchar_t* GetErrorCodeStr(const DWORD errCode) +{ + switch(errCode) + { + ERR(ERROR_SUCCESS); + ERR(ERROR_INVALID_FUNCTION); + ERR(ERROR_FILE_NOT_FOUND); + ERR(ERROR_PATH_NOT_FOUND); + ERR(ERROR_TOO_MANY_OPEN_FILES); + ERR(ERROR_ACCESS_DENIED); + ERR(ERROR_INVALID_HANDLE); + ERR(ERROR_ARENA_TRASHED); + ERR(ERROR_NOT_ENOUGH_MEMORY); + ERR(ERROR_INVALID_BLOCK); + ERR(ERROR_BAD_ENVIRONMENT); + ERR(ERROR_BAD_FORMAT); + ERR(ERROR_INVALID_ACCESS); + ERR(ERROR_INVALID_DATA); + ERR(ERROR_OUTOFMEMORY); + ERR(ERROR_INVALID_DRIVE); + ERR(ERROR_CURRENT_DIRECTORY); + ERR(ERROR_NOT_SAME_DEVICE); + ERR(ERROR_NO_MORE_FILES); + ERR(ERROR_WRITE_PROTECT); + ERR(ERROR_BAD_UNIT); + ERR(ERROR_NOT_READY); + ERR(ERROR_BAD_COMMAND); + ERR(ERROR_CRC); + ERR(ERROR_BAD_LENGTH); + ERR(ERROR_SEEK); + ERR(ERROR_NOT_DOS_DISK); + ERR(ERROR_SECTOR_NOT_FOUND); + ERR(ERROR_OUT_OF_PAPER); + ERR(ERROR_WRITE_FAULT); + ERR(ERROR_READ_FAULT); + ERR(ERROR_GEN_FAILURE); + ERR(ERROR_SHARING_VIOLATION); + ERR(ERROR_LOCK_VIOLATION); + ERR(ERROR_WRONG_DISK); + ERR(ERROR_SHARING_BUFFER_EXCEEDED); + ERR(ERROR_HANDLE_EOF); + ERR(ERROR_HANDLE_DISK_FULL); + ERR(ERROR_NOT_SUPPORTED); + ERR(ERROR_REM_NOT_LIST); + ERR(ERROR_DUP_NAME); + ERR(ERROR_BAD_NETPATH); + ERR(ERROR_NETWORK_BUSY); + ERR(ERROR_DEV_NOT_EXIST); + ERR(ERROR_TOO_MANY_CMDS); + ERR(ERROR_ADAP_HDW_ERR); + ERR(ERROR_BAD_NET_RESP); + ERR(ERROR_UNEXP_NET_ERR); + ERR(ERROR_BAD_REM_ADAP); + ERR(ERROR_PRINTQ_FULL); + ERR(ERROR_NO_SPOOL_SPACE); + ERR(ERROR_PRINT_CANCELLED); + ERR(ERROR_NETNAME_DELETED); + ERR(ERROR_NETWORK_ACCESS_DENIED); + ERR(ERROR_BAD_DEV_TYPE); + ERR(ERROR_BAD_NET_NAME); + ERR(ERROR_TOO_MANY_NAMES); + ERR(ERROR_TOO_MANY_SESS); + ERR(ERROR_SHARING_PAUSED); + ERR(ERROR_REQ_NOT_ACCEP); + ERR(ERROR_REDIR_PAUSED); + ERR(ERROR_FILE_EXISTS); + ERR(ERROR_CANNOT_MAKE); + ERR(ERROR_FAIL_I24); + ERR(ERROR_OUT_OF_STRUCTURES); + ERR(ERROR_ALREADY_ASSIGNED); + ERR(ERROR_INVALID_PASSWORD); + ERR(ERROR_INVALID_PARAMETER); + ERR(ERROR_NET_WRITE_FAULT); + ERR(ERROR_NO_PROC_SLOTS); + ERR(ERROR_TOO_MANY_SEMAPHORES); + ERR(ERROR_EXCL_SEM_ALREADY_OWNED); + ERR(ERROR_SEM_IS_SET); + ERR(ERROR_TOO_MANY_SEM_REQUESTS); + ERR(ERROR_INVALID_AT_INTERRUPT_TIME); + ERR(ERROR_SEM_OWNER_DIED); + ERR(ERROR_SEM_USER_LIMIT); + ERR(ERROR_DISK_CHANGE); + ERR(ERROR_DRIVE_LOCKED); + ERR(ERROR_BROKEN_PIPE); + ERR(ERROR_OPEN_FAILED); + ERR(ERROR_BUFFER_OVERFLOW); + ERR(ERROR_DISK_FULL); + ERR(ERROR_NO_MORE_SEARCH_HANDLES); + ERR(ERROR_INVALID_TARGET_HANDLE); + ERR(ERROR_INVALID_CATEGORY); + ERR(ERROR_INVALID_VERIFY_SWITCH); + ERR(ERROR_BAD_DRIVER_LEVEL); + ERR(ERROR_CALL_NOT_IMPLEMENTED); + ERR(ERROR_SEM_TIMEOUT); + ERR(ERROR_INSUFFICIENT_BUFFER); + ERR(ERROR_INVALID_NAME); + ERR(ERROR_INVALID_LEVEL); + ERR(ERROR_NO_VOLUME_LABEL); + ERR(ERROR_MOD_NOT_FOUND); + ERR(ERROR_PROC_NOT_FOUND); + ERR(ERROR_WAIT_NO_CHILDREN); + ERR(ERROR_CHILD_NOT_COMPLETE); + ERR(ERROR_DIRECT_ACCESS_HANDLE); + ERR(ERROR_NEGATIVE_SEEK); + ERR(ERROR_SEEK_ON_DEVICE); + ERR(ERROR_IS_JOIN_TARGET); + ERR(ERROR_IS_JOINED); + ERR(ERROR_IS_SUBSTED); + ERR(ERROR_NOT_JOINED); + ERR(ERROR_NOT_SUBSTED); + ERR(ERROR_JOIN_TO_JOIN); + ERR(ERROR_SUBST_TO_SUBST); + ERR(ERROR_JOIN_TO_SUBST); + ERR(ERROR_SUBST_TO_JOIN); + ERR(ERROR_BUSY_DRIVE); + ERR(ERROR_SAME_DRIVE); + ERR(ERROR_DIR_NOT_ROOT); + ERR(ERROR_DIR_NOT_EMPTY); + ERR(ERROR_IS_SUBST_PATH); + ERR(ERROR_IS_JOIN_PATH); + ERR(ERROR_PATH_BUSY); + ERR(ERROR_IS_SUBST_TARGET); + ERR(ERROR_SYSTEM_TRACE); + ERR(ERROR_INVALID_EVENT_COUNT); + ERR(ERROR_TOO_MANY_MUXWAITERS); + ERR(ERROR_INVALID_LIST_FORMAT); + ERR(ERROR_LABEL_TOO_LONG); + ERR(ERROR_TOO_MANY_TCBS); + ERR(ERROR_SIGNAL_REFUSED); + ERR(ERROR_DISCARDED); + ERR(ERROR_NOT_LOCKED); + ERR(ERROR_BAD_THREADID_ADDR); + ERR(ERROR_BAD_ARGUMENTS); + ERR(ERROR_BAD_PATHNAME); + ERR(ERROR_SIGNAL_PENDING); + ERR(ERROR_MAX_THRDS_REACHED); + ERR(ERROR_LOCK_FAILED); + ERR(ERROR_BUSY); + ERR(ERROR_DEVICE_SUPPORT_IN_PROGRESS); + ERR(ERROR_CANCEL_VIOLATION); + ERR(ERROR_ATOMIC_LOCKS_NOT_SUPPORTED); + ERR(ERROR_INVALID_SEGMENT_NUMBER); + ERR(ERROR_INVALID_ORDINAL); + ERR(ERROR_ALREADY_EXISTS); + ERR(ERROR_INVALID_FLAG_NUMBER); + ERR(ERROR_SEM_NOT_FOUND); + ERR(ERROR_INVALID_STARTING_CODESEG); + ERR(ERROR_INVALID_STACKSEG); + ERR(ERROR_INVALID_MODULETYPE); + ERR(ERROR_INVALID_EXE_SIGNATURE); + ERR(ERROR_EXE_MARKED_INVALID); + ERR(ERROR_BAD_EXE_FORMAT); + ERR(ERROR_ITERATED_DATA_EXCEEDS_64k); + ERR(ERROR_INVALID_MINALLOCSIZE); + ERR(ERROR_DYNLINK_FROM_INVALID_RING); + ERR(ERROR_IOPL_NOT_ENABLED); + ERR(ERROR_INVALID_SEGDPL); + ERR(ERROR_AUTODATASEG_EXCEEDS_64k); + ERR(ERROR_RING2SEG_MUST_BE_MOVABLE); + ERR(ERROR_RELOC_CHAIN_XEEDS_SEGLIM); + ERR(ERROR_INFLOOP_IN_RELOC_CHAIN); + ERR(ERROR_ENVVAR_NOT_FOUND); + ERR(ERROR_NO_SIGNAL_SENT); + ERR(ERROR_FILENAME_EXCED_RANGE); + ERR(ERROR_RING2_STACK_IN_USE); + ERR(ERROR_META_EXPANSION_TOO_LONG); + ERR(ERROR_INVALID_SIGNAL_NUMBER); + ERR(ERROR_THREAD_1_INACTIVE); + ERR(ERROR_LOCKED); + ERR(ERROR_TOO_MANY_MODULES); + ERR(ERROR_NESTING_NOT_ALLOWED); + ERR(ERROR_EXE_MACHINE_TYPE_MISMATCH); + ERR(ERROR_EXE_CANNOT_MODIFY_SIGNED_BINARY); + ERR(ERROR_EXE_CANNOT_MODIFY_STRONG_SIGNED_BINARY); + ERR(ERROR_FILE_CHECKED_OUT); + ERR(ERROR_CHECKOUT_REQUIRED); + ERR(ERROR_BAD_FILE_TYPE); + ERR(ERROR_FILE_TOO_LARGE); + ERR(ERROR_FORMS_AUTH_REQUIRED); + ERR(ERROR_VIRUS_INFECTED); + ERR(ERROR_VIRUS_DELETED); + ERR(ERROR_PIPE_LOCAL); + ERR(ERROR_BAD_PIPE); + ERR(ERROR_PIPE_BUSY); + ERR(ERROR_NO_DATA); + ERR(ERROR_PIPE_NOT_CONNECTED); + ERR(ERROR_MORE_DATA); + ERR(ERROR_VC_DISCONNECTED); + ERR(ERROR_INVALID_EA_NAME); + ERR(ERROR_EA_LIST_INCONSISTENT); + ERR(ERROR_NO_MORE_ITEMS); + ERR(ERROR_CANNOT_COPY); + ERR(ERROR_DIRECTORY); + ERR(ERROR_EAS_DIDNT_FIT); + ERR(ERROR_EA_FILE_CORRUPT); + ERR(ERROR_EA_TABLE_FULL); + ERR(ERROR_INVALID_EA_HANDLE); + ERR(ERROR_EAS_NOT_SUPPORTED); + ERR(ERROR_NOT_OWNER); + ERR(ERROR_TOO_MANY_POSTS); + ERR(ERROR_PARTIAL_COPY); + ERR(ERROR_OPLOCK_NOT_GRANTED); + ERR(ERROR_INVALID_OPLOCK_PROTOCOL); + ERR(ERROR_DISK_TOO_FRAGMENTED); + ERR(ERROR_DELETE_PENDING); + ERR(ERROR_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING); + ERR(ERROR_SHORT_NAMES_NOT_ENABLED_ON_VOLUME); + ERR(ERROR_SECURITY_STREAM_IS_INCONSISTENT); + ERR(ERROR_INVALID_LOCK_RANGE); + ERR(ERROR_IMAGE_SUBSYSTEM_NOT_PRESENT); + ERR(ERROR_NOTIFICATION_GUID_ALREADY_DEFINED); + ERR(ERROR_INVALID_EXCEPTION_HANDLER); + ERR(ERROR_DUPLICATE_PRIVILEGES); + ERR(ERROR_NO_RANGES_PROCESSED); + ERR(ERROR_NOT_ALLOWED_ON_SYSTEM_FILE); + ERR(ERROR_DISK_RESOURCES_EXHAUSTED); + ERR(ERROR_INVALID_TOKEN); + ERR(ERROR_DEVICE_FEATURE_NOT_SUPPORTED); + ERR(ERROR_MR_MID_NOT_FOUND); + ERR(ERROR_SCOPE_NOT_FOUND); + ERR(ERROR_UNDEFINED_SCOPE); + ERR(ERROR_INVALID_CAP); + ERR(ERROR_DEVICE_UNREACHABLE); + ERR(ERROR_DEVICE_NO_RESOURCES); + ERR(ERROR_DATA_CHECKSUM_ERROR); + ERR(ERROR_INTERMIXED_KERNEL_EA_OPERATION); + ERR(ERROR_FILE_LEVEL_TRIM_NOT_SUPPORTED); + ERR(ERROR_OFFSET_ALIGNMENT_VIOLATION); + ERR(ERROR_INVALID_FIELD_IN_PARAMETER_LIST); + ERR(ERROR_OPERATION_IN_PROGRESS); + ERR(ERROR_BAD_DEVICE_PATH); + ERR(ERROR_TOO_MANY_DESCRIPTORS); + ERR(ERROR_SCRUB_DATA_DISABLED); + ERR(ERROR_NOT_REDUNDANT_STORAGE); + ERR(ERROR_RESIDENT_FILE_NOT_SUPPORTED); + ERR(ERROR_COMPRESSED_FILE_NOT_SUPPORTED); + ERR(ERROR_DIRECTORY_NOT_SUPPORTED); + ERR(ERROR_NOT_READ_FROM_COPY); + ERR(ERROR_FT_WRITE_FAILURE); + ERR(ERROR_FT_DI_SCAN_REQUIRED); + ERR(ERROR_INVALID_KERNEL_INFO_VERSION); + ERR(ERROR_INVALID_PEP_INFO_VERSION); + ERR(ERROR_OBJECT_NOT_EXTERNALLY_BACKED); + ERR(ERROR_EXTERNAL_BACKING_PROVIDER_UNKNOWN); + /*ERR(ERROR_COMPRESSION_NOT_BENEFICIAL); + ERR(ERROR_STORAGE_TOPOLOGY_ID_MISMATCH); + ERR(ERROR_BLOCKED_BY_PARENTAL_CONTROLS); + ERR(ERROR_BLOCK_TOO_MANY_REFERENCES);*/ + ERR(ERROR_FAIL_NOACTION_REBOOT); + ERR(ERROR_FAIL_SHUTDOWN); + ERR(ERROR_FAIL_RESTART); + ERR(ERROR_MAX_SESSIONS_REACHED); + ERR(ERROR_THREAD_MODE_ALREADY_BACKGROUND); + ERR(ERROR_THREAD_MODE_NOT_BACKGROUND); + ERR(ERROR_PROCESS_MODE_ALREADY_BACKGROUND); + ERR(ERROR_PROCESS_MODE_NOT_BACKGROUND); + ERR(ERROR_DEVICE_HARDWARE_ERROR); + ERR(ERROR_INVALID_ADDRESS); + ERR(ERROR_USER_PROFILE_LOAD); + ERR(ERROR_ARITHMETIC_OVERFLOW); + ERR(ERROR_PIPE_CONNECTED); + ERR(ERROR_PIPE_LISTENING); + ERR(ERROR_VERIFIER_STOP); + ERR(ERROR_ABIOS_ERROR); + ERR(ERROR_WX86_WARNING); + ERR(ERROR_WX86_ERROR); + ERR(ERROR_TIMER_NOT_CANCELED); + ERR(ERROR_UNWIND); + ERR(ERROR_BAD_STACK); + ERR(ERROR_INVALID_UNWIND_TARGET); + ERR(ERROR_INVALID_PORT_ATTRIBUTES); + ERR(ERROR_PORT_MESSAGE_TOO_LONG); + ERR(ERROR_INVALID_QUOTA_LOWER); + ERR(ERROR_DEVICE_ALREADY_ATTACHED); + ERR(ERROR_INSTRUCTION_MISALIGNMENT); + ERR(ERROR_PROFILING_NOT_STARTED); + ERR(ERROR_PROFILING_NOT_STOPPED); + ERR(ERROR_COULD_NOT_INTERPRET); + ERR(ERROR_PROFILING_AT_LIMIT); + ERR(ERROR_CANT_WAIT); + ERR(ERROR_CANT_TERMINATE_SELF); + ERR(ERROR_UNEXPECTED_MM_CREATE_ERR); + ERR(ERROR_UNEXPECTED_MM_MAP_ERROR); + ERR(ERROR_UNEXPECTED_MM_EXTEND_ERR); + ERR(ERROR_BAD_FUNCTION_TABLE); + ERR(ERROR_NO_GUID_TRANSLATION); + ERR(ERROR_INVALID_LDT_SIZE); + ERR(ERROR_INVALID_LDT_OFFSET); + ERR(ERROR_INVALID_LDT_DESCRIPTOR); + ERR(ERROR_TOO_MANY_THREADS); + ERR(ERROR_THREAD_NOT_IN_PROCESS); + ERR(ERROR_PAGEFILE_QUOTA_EXCEEDED); + ERR(ERROR_LOGON_SERVER_CONFLICT); + ERR(ERROR_SYNCHRONIZATION_REQUIRED); + ERR(ERROR_NET_OPEN_FAILED); + ERR(ERROR_IO_PRIVILEGE_FAILED); + ERR(ERROR_CONTROL_C_EXIT); + ERR(ERROR_MISSING_SYSTEMFILE); + ERR(ERROR_UNHANDLED_EXCEPTION); + ERR(ERROR_APP_INIT_FAILURE); + ERR(ERROR_PAGEFILE_CREATE_FAILED); + ERR(ERROR_INVALID_IMAGE_HASH); + ERR(ERROR_NO_PAGEFILE); + ERR(ERROR_ILLEGAL_FLOAT_CONTEXT); + ERR(ERROR_NO_EVENT_PAIR); + ERR(ERROR_DOMAIN_CTRLR_CONFIG_ERROR); + ERR(ERROR_ILLEGAL_CHARACTER); + ERR(ERROR_UNDEFINED_CHARACTER); + ERR(ERROR_FLOPPY_VOLUME); + ERR(ERROR_BIOS_FAILED_TO_CONNECT_INTERRUPT); + ERR(ERROR_BACKUP_CONTROLLER); + ERR(ERROR_MUTANT_LIMIT_EXCEEDED); + ERR(ERROR_FS_DRIVER_REQUIRED); + ERR(ERROR_CANNOT_LOAD_REGISTRY_FILE); + ERR(ERROR_DEBUG_ATTACH_FAILED); + ERR(ERROR_SYSTEM_PROCESS_TERMINATED); + ERR(ERROR_DATA_NOT_ACCEPTED); + ERR(ERROR_VDM_HARD_ERROR); + ERR(ERROR_DRIVER_CANCEL_TIMEOUT); + ERR(ERROR_REPLY_MESSAGE_MISMATCH); + ERR(ERROR_LOST_WRITEBEHIND_DATA); + ERR(ERROR_CLIENT_SERVER_PARAMETERS_INVALID); + ERR(ERROR_NOT_TINY_STREAM); + ERR(ERROR_STACK_OVERFLOW_READ); + ERR(ERROR_CONVERT_TO_LARGE); + ERR(ERROR_FOUND_OUT_OF_SCOPE); + ERR(ERROR_ALLOCATE_BUCKET); + ERR(ERROR_MARSHALL_OVERFLOW); + ERR(ERROR_INVALID_VARIANT); + ERR(ERROR_BAD_COMPRESSION_BUFFER); + ERR(ERROR_AUDIT_FAILED); + ERR(ERROR_TIMER_RESOLUTION_NOT_SET); + ERR(ERROR_INSUFFICIENT_LOGON_INFO); + ERR(ERROR_BAD_DLL_ENTRYPOINT); + ERR(ERROR_BAD_SERVICE_ENTRYPOINT); + ERR(ERROR_IP_ADDRESS_CONFLICT1); + ERR(ERROR_IP_ADDRESS_CONFLICT2); + ERR(ERROR_REGISTRY_QUOTA_LIMIT); + ERR(ERROR_NO_CALLBACK_ACTIVE); + ERR(ERROR_PWD_TOO_SHORT); + ERR(ERROR_PWD_TOO_RECENT); + ERR(ERROR_PWD_HISTORY_CONFLICT); + ERR(ERROR_UNSUPPORTED_COMPRESSION); + ERR(ERROR_INVALID_HW_PROFILE); + ERR(ERROR_INVALID_PLUGPLAY_DEVICE_PATH); + ERR(ERROR_QUOTA_LIST_INCONSISTENT); + ERR(ERROR_EVALUATION_EXPIRATION); + ERR(ERROR_ILLEGAL_DLL_RELOCATION); + ERR(ERROR_DLL_INIT_FAILED_LOGOFF); + ERR(ERROR_VALIDATE_CONTINUE); + ERR(ERROR_NO_MORE_MATCHES); + ERR(ERROR_RANGE_LIST_CONFLICT); + ERR(ERROR_SERVER_SID_MISMATCH); + ERR(ERROR_CANT_ENABLE_DENY_ONLY); + ERR(ERROR_FLOAT_MULTIPLE_FAULTS); + ERR(ERROR_FLOAT_MULTIPLE_TRAPS); + ERR(ERROR_NOINTERFACE); + ERR(ERROR_DRIVER_FAILED_SLEEP); + ERR(ERROR_CORRUPT_SYSTEM_FILE); + ERR(ERROR_COMMITMENT_MINIMUM); + ERR(ERROR_PNP_RESTART_ENUMERATION); + ERR(ERROR_SYSTEM_IMAGE_BAD_SIGNATURE); + ERR(ERROR_PNP_REBOOT_REQUIRED); + ERR(ERROR_INSUFFICIENT_POWER); + ERR(ERROR_MULTIPLE_FAULT_VIOLATION); + ERR(ERROR_SYSTEM_SHUTDOWN); + ERR(ERROR_PORT_NOT_SET); + ERR(ERROR_DS_VERSION_CHECK_FAILURE); + ERR(ERROR_RANGE_NOT_FOUND); + ERR(ERROR_NOT_SAFE_MODE_DRIVER); + ERR(ERROR_FAILED_DRIVER_ENTRY); + ERR(ERROR_DEVICE_ENUMERATION_ERROR); + ERR(ERROR_MOUNT_POINT_NOT_RESOLVED); + ERR(ERROR_INVALID_DEVICE_OBJECT_PARAMETER); + ERR(ERROR_MCA_OCCURED); + ERR(ERROR_DRIVER_DATABASE_ERROR); + ERR(ERROR_SYSTEM_HIVE_TOO_LARGE); + ERR(ERROR_DRIVER_FAILED_PRIOR_UNLOAD); + ERR(ERROR_VOLSNAP_PREPARE_HIBERNATE); + ERR(ERROR_HIBERNATION_FAILURE); + ERR(ERROR_PWD_TOO_LONG); + ERR(ERROR_FILE_SYSTEM_LIMITATION); + ERR(ERROR_ASSERTION_FAILURE); + ERR(ERROR_ACPI_ERROR); + ERR(ERROR_WOW_ASSERTION); + ERR(ERROR_PNP_BAD_MPS_TABLE); + ERR(ERROR_PNP_TRANSLATION_FAILED); + ERR(ERROR_PNP_IRQ_TRANSLATION_FAILED); + ERR(ERROR_PNP_INVALID_ID); + ERR(ERROR_WAKE_SYSTEM_DEBUGGER); + ERR(ERROR_HANDLES_CLOSED); + ERR(ERROR_EXTRANEOUS_INFORMATION); + ERR(ERROR_RXACT_COMMIT_NECESSARY); + ERR(ERROR_MEDIA_CHECK); + ERR(ERROR_GUID_SUBSTITUTION_MADE); + ERR(ERROR_STOPPED_ON_SYMLINK); + ERR(ERROR_LONGJUMP); + ERR(ERROR_PLUGPLAY_QUERY_VETOED); + ERR(ERROR_UNWIND_CONSOLIDATE); + ERR(ERROR_REGISTRY_HIVE_RECOVERED); + ERR(ERROR_DLL_MIGHT_BE_INSECURE); + ERR(ERROR_DLL_MIGHT_BE_INCOMPATIBLE); + ERR(ERROR_DBG_EXCEPTION_NOT_HANDLED); + ERR(ERROR_DBG_REPLY_LATER); + ERR(ERROR_DBG_UNABLE_TO_PROVIDE_HANDLE); + ERR(ERROR_DBG_TERMINATE_THREAD); + ERR(ERROR_DBG_TERMINATE_PROCESS); + ERR(ERROR_DBG_CONTROL_C); + ERR(ERROR_DBG_PRINTEXCEPTION_C); + ERR(ERROR_DBG_RIPEXCEPTION); + ERR(ERROR_DBG_CONTROL_BREAK); + ERR(ERROR_DBG_COMMAND_EXCEPTION); + ERR(ERROR_OBJECT_NAME_EXISTS); + ERR(ERROR_THREAD_WAS_SUSPENDED); + ERR(ERROR_IMAGE_NOT_AT_BASE); + ERR(ERROR_RXACT_STATE_CREATED); + ERR(ERROR_SEGMENT_NOTIFICATION); + ERR(ERROR_BAD_CURRENT_DIRECTORY); + ERR(ERROR_FT_READ_RECOVERY_FROM_BACKUP); + ERR(ERROR_FT_WRITE_RECOVERY); + ERR(ERROR_IMAGE_MACHINE_TYPE_MISMATCH); + ERR(ERROR_RECEIVE_PARTIAL); + ERR(ERROR_RECEIVE_EXPEDITED); + ERR(ERROR_RECEIVE_PARTIAL_EXPEDITED); + ERR(ERROR_EVENT_DONE); + ERR(ERROR_EVENT_PENDING); + ERR(ERROR_CHECKING_FILE_SYSTEM); + ERR(ERROR_FATAL_APP_EXIT); + ERR(ERROR_PREDEFINED_HANDLE); + ERR(ERROR_WAS_UNLOCKED); + ERR(ERROR_SERVICE_NOTIFICATION); + ERR(ERROR_WAS_LOCKED); + ERR(ERROR_LOG_HARD_ERROR); + ERR(ERROR_ALREADY_WIN32); + ERR(ERROR_IMAGE_MACHINE_TYPE_MISMATCH_EXE); + ERR(ERROR_NO_YIELD_PERFORMED); + ERR(ERROR_TIMER_RESUME_IGNORED); + ERR(ERROR_ARBITRATION_UNHANDLED); + ERR(ERROR_CARDBUS_NOT_SUPPORTED); + ERR(ERROR_MP_PROCESSOR_MISMATCH); + ERR(ERROR_HIBERNATED); + ERR(ERROR_RESUME_HIBERNATION); + ERR(ERROR_FIRMWARE_UPDATED); + ERR(ERROR_DRIVERS_LEAKING_LOCKED_PAGES); + ERR(ERROR_WAKE_SYSTEM); + ERR(ERROR_WAIT_1); + ERR(ERROR_WAIT_2); + ERR(ERROR_WAIT_3); + ERR(ERROR_WAIT_63); + ERR(ERROR_ABANDONED_WAIT_0); + ERR(ERROR_ABANDONED_WAIT_63); + ERR(ERROR_USER_APC); + ERR(ERROR_KERNEL_APC); + ERR(ERROR_ALERTED); + ERR(ERROR_ELEVATION_REQUIRED); + ERR(ERROR_REPARSE); + ERR(ERROR_OPLOCK_BREAK_IN_PROGRESS); + ERR(ERROR_VOLUME_MOUNTED); + ERR(ERROR_RXACT_COMMITTED); + ERR(ERROR_NOTIFY_CLEANUP); + ERR(ERROR_PRIMARY_TRANSPORT_CONNECT_FAILED); + ERR(ERROR_PAGE_FAULT_TRANSITION); + ERR(ERROR_PAGE_FAULT_DEMAND_ZERO); + ERR(ERROR_PAGE_FAULT_COPY_ON_WRITE); + ERR(ERROR_PAGE_FAULT_GUARD_PAGE); + ERR(ERROR_PAGE_FAULT_PAGING_FILE); + ERR(ERROR_CACHE_PAGE_LOCKED); + ERR(ERROR_CRASH_DUMP); + ERR(ERROR_BUFFER_ALL_ZEROS); + ERR(ERROR_REPARSE_OBJECT); + ERR(ERROR_RESOURCE_REQUIREMENTS_CHANGED); + ERR(ERROR_TRANSLATION_COMPLETE); + ERR(ERROR_NOTHING_TO_TERMINATE); + ERR(ERROR_PROCESS_NOT_IN_JOB); + ERR(ERROR_PROCESS_IN_JOB); + ERR(ERROR_VOLSNAP_HIBERNATE_READY); + ERR(ERROR_FSFILTER_OP_COMPLETED_SUCCESSFULLY); + ERR(ERROR_INTERRUPT_VECTOR_ALREADY_CONNECTED); + ERR(ERROR_INTERRUPT_STILL_CONNECTED); + ERR(ERROR_WAIT_FOR_OPLOCK); + ERR(ERROR_DBG_EXCEPTION_HANDLED); + ERR(ERROR_DBG_CONTINUE); + ERR(ERROR_CALLBACK_POP_STACK); + ERR(ERROR_COMPRESSION_DISABLED); + ERR(ERROR_CANTFETCHBACKWARDS); + ERR(ERROR_CANTSCROLLBACKWARDS); + ERR(ERROR_ROWSNOTRELEASED); + ERR(ERROR_BAD_ACCESSOR_FLAGS); + ERR(ERROR_ERRORS_ENCOUNTERED); + ERR(ERROR_NOT_CAPABLE); + ERR(ERROR_REQUEST_OUT_OF_SEQUENCE); + ERR(ERROR_VERSION_PARSE_ERROR); + ERR(ERROR_BADSTARTPOSITION); + ERR(ERROR_MEMORY_HARDWARE); + ERR(ERROR_DISK_REPAIR_DISABLED); + ERR(ERROR_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE); + ERR(ERROR_SYSTEM_POWERSTATE_TRANSITION); + ERR(ERROR_SYSTEM_POWERSTATE_COMPLEX_TRANSITION); + ERR(ERROR_MCA_EXCEPTION); + ERR(ERROR_ACCESS_AUDIT_BY_POLICY); + ERR(ERROR_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY); + ERR(ERROR_ABANDON_HIBERFILE); + ERR(ERROR_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED); + ERR(ERROR_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR); + ERR(ERROR_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR); + ERR(ERROR_BAD_MCFG_TABLE); + ERR(ERROR_DISK_REPAIR_REDIRECTED); + ERR(ERROR_DISK_REPAIR_UNSUCCESSFUL); + ERR(ERROR_CORRUPT_LOG_OVERFULL); + ERR(ERROR_CORRUPT_LOG_CORRUPTED); + ERR(ERROR_CORRUPT_LOG_UNAVAILABLE); + ERR(ERROR_CORRUPT_LOG_DELETED_FULL); + ERR(ERROR_CORRUPT_LOG_CLEARED); + ERR(ERROR_ORPHAN_NAME_EXHAUSTED); + ERR(ERROR_OPLOCK_SWITCHED_TO_NEW_HANDLE); + ERR(ERROR_CANNOT_GRANT_REQUESTED_OPLOCK); + ERR(ERROR_CANNOT_BREAK_OPLOCK); + ERR(ERROR_OPLOCK_HANDLE_CLOSED); + ERR(ERROR_NO_ACE_CONDITION); + ERR(ERROR_INVALID_ACE_CONDITION); + ERR(ERROR_FILE_HANDLE_REVOKED); + ERR(ERROR_IMAGE_AT_DIFFERENT_BASE); + ERR(ERROR_ENCRYPTED_IO_NOT_POSSIBLE); + /*ERR(ERROR_FILE_METADATA_OPTIMIZATION_IN_PROGRESS); + ERR(ERROR_QUOTA_ACTIVITY); + ERR(ERROR_HANDLE_REVOKED); + ERR(ERROR_CALLBACK_INVOKE_INLINE); + ERR(ERROR_CPU_SET_INVALID);*/ + ERR(ERROR_EA_ACCESS_DENIED); + ERR(ERROR_OPERATION_ABORTED); + ERR(ERROR_IO_INCOMPLETE); + ERR(ERROR_IO_PENDING); + ERR(ERROR_NOACCESS); + ERR(ERROR_SWAPERROR); + ERR(ERROR_STACK_OVERFLOW); + ERR(ERROR_INVALID_MESSAGE); + ERR(ERROR_CAN_NOT_COMPLETE); + ERR(ERROR_INVALID_FLAGS); + ERR(ERROR_UNRECOGNIZED_VOLUME); + ERR(ERROR_FILE_INVALID); + ERR(ERROR_FULLSCREEN_MODE); + ERR(ERROR_NO_TOKEN); + ERR(ERROR_BADDB); + ERR(ERROR_BADKEY); + ERR(ERROR_CANTOPEN); + ERR(ERROR_CANTREAD); + ERR(ERROR_CANTWRITE); + ERR(ERROR_REGISTRY_RECOVERED); + ERR(ERROR_REGISTRY_CORRUPT); + ERR(ERROR_REGISTRY_IO_FAILED); + ERR(ERROR_NOT_REGISTRY_FILE); + ERR(ERROR_KEY_DELETED); + ERR(ERROR_NO_LOG_SPACE); + ERR(ERROR_KEY_HAS_CHILDREN); + ERR(ERROR_CHILD_MUST_BE_VOLATILE); + ERR(ERROR_NOTIFY_ENUM_DIR); + ERR(ERROR_DEPENDENT_SERVICES_RUNNING); + ERR(ERROR_INVALID_SERVICE_CONTROL); + ERR(ERROR_SERVICE_REQUEST_TIMEOUT); + ERR(ERROR_SERVICE_NO_THREAD); + ERR(ERROR_SERVICE_DATABASE_LOCKED); + ERR(ERROR_SERVICE_ALREADY_RUNNING); + ERR(ERROR_INVALID_SERVICE_ACCOUNT); + ERR(ERROR_SERVICE_DISABLED); + ERR(ERROR_CIRCULAR_DEPENDENCY); + ERR(ERROR_SERVICE_DOES_NOT_EXIST); + ERR(ERROR_SERVICE_CANNOT_ACCEPT_CTRL); + ERR(ERROR_SERVICE_NOT_ACTIVE); + ERR(ERROR_FAILED_SERVICE_CONTROLLER_CONNECT); + ERR(ERROR_EXCEPTION_IN_SERVICE); + ERR(ERROR_DATABASE_DOES_NOT_EXIST); + ERR(ERROR_SERVICE_SPECIFIC_ERROR); + ERR(ERROR_PROCESS_ABORTED); + ERR(ERROR_SERVICE_DEPENDENCY_FAIL); + ERR(ERROR_SERVICE_LOGON_FAILED); + ERR(ERROR_SERVICE_START_HANG); + ERR(ERROR_INVALID_SERVICE_LOCK); + ERR(ERROR_SERVICE_MARKED_FOR_DELETE); + ERR(ERROR_SERVICE_EXISTS); + ERR(ERROR_ALREADY_RUNNING_LKG); + ERR(ERROR_SERVICE_DEPENDENCY_DELETED); + ERR(ERROR_BOOT_ALREADY_ACCEPTED); + ERR(ERROR_SERVICE_NEVER_STARTED); + ERR(ERROR_DUPLICATE_SERVICE_NAME); + ERR(ERROR_DIFFERENT_SERVICE_ACCOUNT); + ERR(ERROR_CANNOT_DETECT_DRIVER_FAILURE); + ERR(ERROR_CANNOT_DETECT_PROCESS_ABORT); + ERR(ERROR_NO_RECOVERY_PROGRAM); + ERR(ERROR_SERVICE_NOT_IN_EXE); + ERR(ERROR_NOT_SAFEBOOT_SERVICE); + ERR(ERROR_END_OF_MEDIA); + ERR(ERROR_FILEMARK_DETECTED); + ERR(ERROR_BEGINNING_OF_MEDIA); + ERR(ERROR_SETMARK_DETECTED); + ERR(ERROR_NO_DATA_DETECTED); + ERR(ERROR_PARTITION_FAILURE); + ERR(ERROR_INVALID_BLOCK_LENGTH); + ERR(ERROR_DEVICE_NOT_PARTITIONED); + ERR(ERROR_UNABLE_TO_LOCK_MEDIA); + ERR(ERROR_UNABLE_TO_UNLOAD_MEDIA); + ERR(ERROR_MEDIA_CHANGED); + ERR(ERROR_BUS_RESET); + ERR(ERROR_NO_MEDIA_IN_DRIVE); + ERR(ERROR_NO_UNICODE_TRANSLATION); + ERR(ERROR_DLL_INIT_FAILED); + ERR(ERROR_SHUTDOWN_IN_PROGRESS); + ERR(ERROR_NO_SHUTDOWN_IN_PROGRESS); + ERR(ERROR_IO_DEVICE); + ERR(ERROR_SERIAL_NO_DEVICE); + ERR(ERROR_IRQ_BUSY); + ERR(ERROR_MORE_WRITES); + ERR(ERROR_COUNTER_TIMEOUT); + ERR(ERROR_FLOPPY_ID_MARK_NOT_FOUND); + ERR(ERROR_FLOPPY_WRONG_CYLINDER); + ERR(ERROR_FLOPPY_UNKNOWN_ERROR); + ERR(ERROR_FLOPPY_BAD_REGISTERS); + ERR(ERROR_DISK_RECALIBRATE_FAILED); + ERR(ERROR_DISK_OPERATION_FAILED); + ERR(ERROR_DISK_RESET_FAILED); + ERR(ERROR_EOM_OVERFLOW); + ERR(ERROR_NOT_ENOUGH_SERVER_MEMORY); + ERR(ERROR_POSSIBLE_DEADLOCK); + ERR(ERROR_MAPPED_ALIGNMENT); + ERR(ERROR_SET_POWER_STATE_VETOED); + ERR(ERROR_SET_POWER_STATE_FAILED); + ERR(ERROR_TOO_MANY_LINKS); + ERR(ERROR_OLD_WIN_VERSION); + ERR(ERROR_APP_WRONG_OS); + ERR(ERROR_SINGLE_INSTANCE_APP); + ERR(ERROR_RMODE_APP); + ERR(ERROR_INVALID_DLL); + ERR(ERROR_NO_ASSOCIATION); + ERR(ERROR_DDE_FAIL); + ERR(ERROR_DLL_NOT_FOUND); + ERR(ERROR_NO_MORE_USER_HANDLES); + ERR(ERROR_MESSAGE_SYNC_ONLY); + ERR(ERROR_SOURCE_ELEMENT_EMPTY); + ERR(ERROR_DESTINATION_ELEMENT_FULL); + ERR(ERROR_ILLEGAL_ELEMENT_ADDRESS); + ERR(ERROR_MAGAZINE_NOT_PRESENT); + ERR(ERROR_DEVICE_REINITIALIZATION_NEEDED); + ERR(ERROR_DEVICE_REQUIRES_CLEANING); + ERR(ERROR_DEVICE_DOOR_OPEN); + ERR(ERROR_DEVICE_NOT_CONNECTED); + ERR(ERROR_NOT_FOUND); + ERR(ERROR_NO_MATCH); + ERR(ERROR_SET_NOT_FOUND); + ERR(ERROR_POINT_NOT_FOUND); + ERR(ERROR_NO_TRACKING_SERVICE); + ERR(ERROR_NO_VOLUME_ID); + ERR(ERROR_UNABLE_TO_REMOVE_REPLACED); + ERR(ERROR_UNABLE_TO_MOVE_REPLACEMENT); + ERR(ERROR_UNABLE_TO_MOVE_REPLACEMENT_2); + ERR(ERROR_JOURNAL_DELETE_IN_PROGRESS); + ERR(ERROR_JOURNAL_NOT_ACTIVE); + ERR(ERROR_POTENTIAL_FILE_FOUND); + ERR(ERROR_JOURNAL_ENTRY_DELETED); + ERR(ERROR_SHUTDOWN_IS_SCHEDULED); + ERR(ERROR_SHUTDOWN_USERS_LOGGED_ON); + ERR(ERROR_BAD_DEVICE); + ERR(ERROR_CONNECTION_UNAVAIL); + ERR(ERROR_DEVICE_ALREADY_REMEMBERED); + ERR(ERROR_NO_NET_OR_BAD_PATH); + ERR(ERROR_BAD_PROVIDER); + ERR(ERROR_CANNOT_OPEN_PROFILE); + ERR(ERROR_BAD_PROFILE); + ERR(ERROR_NOT_CONTAINER); + ERR(ERROR_EXTENDED_ERROR); + ERR(ERROR_INVALID_GROUPNAME); + ERR(ERROR_INVALID_COMPUTERNAME); + ERR(ERROR_INVALID_EVENTNAME); + ERR(ERROR_INVALID_DOMAINNAME); + ERR(ERROR_INVALID_SERVICENAME); + ERR(ERROR_INVALID_NETNAME); + ERR(ERROR_INVALID_SHARENAME); + ERR(ERROR_INVALID_PASSWORDNAME); + ERR(ERROR_INVALID_MESSAGENAME); + ERR(ERROR_INVALID_MESSAGEDEST); + ERR(ERROR_SESSION_CREDENTIAL_CONFLICT); + ERR(ERROR_REMOTE_SESSION_LIMIT_EXCEEDED); + ERR(ERROR_DUP_DOMAINNAME); + ERR(ERROR_NO_NETWORK); + ERR(ERROR_CANCELLED); + ERR(ERROR_USER_MAPPED_FILE); + ERR(ERROR_CONNECTION_REFUSED); + ERR(ERROR_GRACEFUL_DISCONNECT); + ERR(ERROR_ADDRESS_ALREADY_ASSOCIATED); + ERR(ERROR_ADDRESS_NOT_ASSOCIATED); + ERR(ERROR_CONNECTION_INVALID); + ERR(ERROR_CONNECTION_ACTIVE); + ERR(ERROR_NETWORK_UNREACHABLE); + ERR(ERROR_HOST_UNREACHABLE); + ERR(ERROR_PROTOCOL_UNREACHABLE); + ERR(ERROR_PORT_UNREACHABLE); + ERR(ERROR_REQUEST_ABORTED); + ERR(ERROR_CONNECTION_ABORTED); + ERR(ERROR_RETRY); + ERR(ERROR_CONNECTION_COUNT_LIMIT); + ERR(ERROR_LOGIN_TIME_RESTRICTION); + ERR(ERROR_LOGIN_WKSTA_RESTRICTION); + ERR(ERROR_INCORRECT_ADDRESS); + ERR(ERROR_ALREADY_REGISTERED); + ERR(ERROR_SERVICE_NOT_FOUND); + ERR(ERROR_NOT_AUTHENTICATED); + ERR(ERROR_NOT_LOGGED_ON); + ERR(ERROR_CONTINUE); + ERR(ERROR_ALREADY_INITIALIZED); + ERR(ERROR_NO_MORE_DEVICES); + ERR(ERROR_NO_SUCH_SITE); + ERR(ERROR_DOMAIN_CONTROLLER_EXISTS); + ERR(ERROR_ONLY_IF_CONNECTED); + ERR(ERROR_OVERRIDE_NOCHANGES); + ERR(ERROR_BAD_USER_PROFILE); + ERR(ERROR_NOT_SUPPORTED_ON_SBS); + ERR(ERROR_SERVER_SHUTDOWN_IN_PROGRESS); + ERR(ERROR_HOST_DOWN); + ERR(ERROR_NON_ACCOUNT_SID); + ERR(ERROR_NON_DOMAIN_SID); + ERR(ERROR_APPHELP_BLOCK); + ERR(ERROR_ACCESS_DISABLED_BY_POLICY); + ERR(ERROR_REG_NAT_CONSUMPTION); + ERR(ERROR_CSCSHARE_OFFLINE); + ERR(ERROR_PKINIT_FAILURE); + ERR(ERROR_SMARTCARD_SUBSYSTEM_FAILURE); + ERR(ERROR_DOWNGRADE_DETECTED); + ERR(ERROR_MACHINE_LOCKED); + ERR(ERROR_CALLBACK_SUPPLIED_INVALID_DATA); + ERR(ERROR_SYNC_FOREGROUND_REFRESH_REQUIRED); + ERR(ERROR_DRIVER_BLOCKED); + ERR(ERROR_INVALID_IMPORT_OF_NON_DLL); + ERR(ERROR_ACCESS_DISABLED_WEBBLADE); + ERR(ERROR_ACCESS_DISABLED_WEBBLADE_TAMPER); + ERR(ERROR_RECOVERY_FAILURE); + ERR(ERROR_ALREADY_FIBER); + ERR(ERROR_ALREADY_THREAD); + ERR(ERROR_STACK_BUFFER_OVERRUN); + ERR(ERROR_PARAMETER_QUOTA_EXCEEDED); + ERR(ERROR_DEBUGGER_INACTIVE); + ERR(ERROR_DELAY_LOAD_FAILED); + ERR(ERROR_VDM_DISALLOWED); + ERR(ERROR_UNIDENTIFIED_ERROR); + ERR(ERROR_INVALID_CRUNTIME_PARAMETER); + ERR(ERROR_BEYOND_VDL); + ERR(ERROR_INCOMPATIBLE_SERVICE_SID_TYPE); + ERR(ERROR_DRIVER_PROCESS_TERMINATED); + ERR(ERROR_IMPLEMENTATION_LIMIT); + ERR(ERROR_PROCESS_IS_PROTECTED); + ERR(ERROR_SERVICE_NOTIFY_CLIENT_LAGGING); + ERR(ERROR_DISK_QUOTA_EXCEEDED); + ERR(ERROR_CONTENT_BLOCKED); + ERR(ERROR_INCOMPATIBLE_SERVICE_PRIVILEGE); + ERR(ERROR_APP_HANG); + ERR(ERROR_INVALID_LABEL); + ERR(ERROR_NOT_ALL_ASSIGNED); + ERR(ERROR_SOME_NOT_MAPPED); + ERR(ERROR_NO_QUOTAS_FOR_ACCOUNT); + ERR(ERROR_LOCAL_USER_SESSION_KEY); + ERR(ERROR_NULL_LM_PASSWORD); + ERR(ERROR_UNKNOWN_REVISION); + ERR(ERROR_REVISION_MISMATCH); + ERR(ERROR_INVALID_OWNER); + ERR(ERROR_INVALID_PRIMARY_GROUP); + ERR(ERROR_NO_IMPERSONATION_TOKEN); + ERR(ERROR_CANT_DISABLE_MANDATORY); + ERR(ERROR_NO_LOGON_SERVERS); + ERR(ERROR_NO_SUCH_LOGON_SESSION); + ERR(ERROR_NO_SUCH_PRIVILEGE); + ERR(ERROR_PRIVILEGE_NOT_HELD); + ERR(ERROR_INVALID_ACCOUNT_NAME); + ERR(ERROR_USER_EXISTS); + ERR(ERROR_NO_SUCH_USER); + ERR(ERROR_GROUP_EXISTS); + ERR(ERROR_NO_SUCH_GROUP); + ERR(ERROR_MEMBER_IN_GROUP); + ERR(ERROR_MEMBER_NOT_IN_GROUP); + ERR(ERROR_LAST_ADMIN); + ERR(ERROR_WRONG_PASSWORD); + ERR(ERROR_ILL_FORMED_PASSWORD); + ERR(ERROR_PASSWORD_RESTRICTION); + ERR(ERROR_LOGON_FAILURE); + ERR(ERROR_ACCOUNT_RESTRICTION); + ERR(ERROR_INVALID_LOGON_HOURS); + ERR(ERROR_INVALID_WORKSTATION); + ERR(ERROR_PASSWORD_EXPIRED); + ERR(ERROR_ACCOUNT_DISABLED); + ERR(ERROR_NONE_MAPPED); + ERR(ERROR_TOO_MANY_LUIDS_REQUESTED); + ERR(ERROR_LUIDS_EXHAUSTED); + ERR(ERROR_INVALID_SUB_AUTHORITY); + ERR(ERROR_INVALID_ACL); + ERR(ERROR_INVALID_SID); + ERR(ERROR_INVALID_SECURITY_DESCR); + ERR(ERROR_BAD_INHERITANCE_ACL); + ERR(ERROR_SERVER_DISABLED); + ERR(ERROR_SERVER_NOT_DISABLED); + ERR(ERROR_INVALID_ID_AUTHORITY); + ERR(ERROR_ALLOTTED_SPACE_EXCEEDED); + ERR(ERROR_INVALID_GROUP_ATTRIBUTES); + ERR(ERROR_BAD_IMPERSONATION_LEVEL); + ERR(ERROR_CANT_OPEN_ANONYMOUS); + ERR(ERROR_BAD_VALIDATION_CLASS); + ERR(ERROR_BAD_TOKEN_TYPE); + ERR(ERROR_NO_SECURITY_ON_OBJECT); + ERR(ERROR_CANT_ACCESS_DOMAIN_INFO); + ERR(ERROR_INVALID_SERVER_STATE); + ERR(ERROR_INVALID_DOMAIN_STATE); + ERR(ERROR_INVALID_DOMAIN_ROLE); + ERR(ERROR_NO_SUCH_DOMAIN); + ERR(ERROR_DOMAIN_EXISTS); + ERR(ERROR_DOMAIN_LIMIT_EXCEEDED); + ERR(ERROR_INTERNAL_DB_CORRUPTION); + ERR(ERROR_INTERNAL_ERROR); + ERR(ERROR_GENERIC_NOT_MAPPED); + ERR(ERROR_BAD_DESCRIPTOR_FORMAT); + ERR(ERROR_NOT_LOGON_PROCESS); + ERR(ERROR_LOGON_SESSION_EXISTS); + ERR(ERROR_NO_SUCH_PACKAGE); + ERR(ERROR_BAD_LOGON_SESSION_STATE); + ERR(ERROR_LOGON_SESSION_COLLISION); + ERR(ERROR_INVALID_LOGON_TYPE); + ERR(ERROR_CANNOT_IMPERSONATE); + ERR(ERROR_RXACT_INVALID_STATE); + ERR(ERROR_RXACT_COMMIT_FAILURE); + ERR(ERROR_SPECIAL_ACCOUNT); + ERR(ERROR_SPECIAL_GROUP); + ERR(ERROR_SPECIAL_USER); + ERR(ERROR_MEMBERS_PRIMARY_GROUP); + ERR(ERROR_TOKEN_ALREADY_IN_USE); + ERR(ERROR_NO_SUCH_ALIAS); + ERR(ERROR_MEMBER_NOT_IN_ALIAS); + ERR(ERROR_MEMBER_IN_ALIAS); + ERR(ERROR_ALIAS_EXISTS); + ERR(ERROR_LOGON_NOT_GRANTED); + ERR(ERROR_TOO_MANY_SECRETS); + ERR(ERROR_SECRET_TOO_LONG); + ERR(ERROR_INTERNAL_DB_ERROR); + ERR(ERROR_TOO_MANY_CONTEXT_IDS); + ERR(ERROR_LOGON_TYPE_NOT_GRANTED); + ERR(ERROR_NT_CROSS_ENCRYPTION_REQUIRED); + ERR(ERROR_NO_SUCH_MEMBER); + ERR(ERROR_INVALID_MEMBER); + ERR(ERROR_TOO_MANY_SIDS); + ERR(ERROR_LM_CROSS_ENCRYPTION_REQUIRED); + ERR(ERROR_NO_INHERITANCE); + ERR(ERROR_FILE_CORRUPT); + ERR(ERROR_DISK_CORRUPT); + ERR(ERROR_NO_USER_SESSION_KEY); + ERR(ERROR_LICENSE_QUOTA_EXCEEDED); + ERR(ERROR_WRONG_TARGET_NAME); + ERR(ERROR_MUTUAL_AUTH_FAILED); + ERR(ERROR_TIME_SKEW); + ERR(ERROR_CURRENT_DOMAIN_NOT_ALLOWED); + ERR(ERROR_INVALID_WINDOW_HANDLE); + ERR(ERROR_INVALID_MENU_HANDLE); + ERR(ERROR_INVALID_CURSOR_HANDLE); + ERR(ERROR_INVALID_ACCEL_HANDLE); + ERR(ERROR_INVALID_HOOK_HANDLE); + ERR(ERROR_INVALID_DWP_HANDLE); + ERR(ERROR_TLW_WITH_WSCHILD); + ERR(ERROR_CANNOT_FIND_WND_CLASS); + ERR(ERROR_WINDOW_OF_OTHER_THREAD); + ERR(ERROR_HOTKEY_ALREADY_REGISTERED); + ERR(ERROR_CLASS_ALREADY_EXISTS); + ERR(ERROR_CLASS_DOES_NOT_EXIST); + ERR(ERROR_CLASS_HAS_WINDOWS); + ERR(ERROR_INVALID_INDEX); + ERR(ERROR_INVALID_ICON_HANDLE); + ERR(ERROR_PRIVATE_DIALOG_INDEX); + ERR(ERROR_LISTBOX_ID_NOT_FOUND); + ERR(ERROR_NO_WILDCARD_CHARACTERS); + ERR(ERROR_CLIPBOARD_NOT_OPEN); + ERR(ERROR_HOTKEY_NOT_REGISTERED); + ERR(ERROR_WINDOW_NOT_DIALOG); + ERR(ERROR_CONTROL_ID_NOT_FOUND); + ERR(ERROR_INVALID_COMBOBOX_MESSAGE); + ERR(ERROR_WINDOW_NOT_COMBOBOX); + ERR(ERROR_INVALID_EDIT_HEIGHT); + ERR(ERROR_DC_NOT_FOUND); + ERR(ERROR_INVALID_HOOK_FILTER); + ERR(ERROR_INVALID_FILTER_PROC); + ERR(ERROR_HOOK_NEEDS_HMOD); + ERR(ERROR_GLOBAL_ONLY_HOOK); + ERR(ERROR_JOURNAL_HOOK_SET); + ERR(ERROR_HOOK_NOT_INSTALLED); + ERR(ERROR_INVALID_LB_MESSAGE); + ERR(ERROR_SETCOUNT_ON_BAD_LB); + ERR(ERROR_LB_WITHOUT_TABSTOPS); + ERR(ERROR_DESTROY_OBJECT_OF_OTHER_THREAD); + ERR(ERROR_CHILD_WINDOW_MENU); + ERR(ERROR_NO_SYSTEM_MENU); + ERR(ERROR_INVALID_MSGBOX_STYLE); + ERR(ERROR_INVALID_SPI_VALUE); + ERR(ERROR_SCREEN_ALREADY_LOCKED); + ERR(ERROR_HWNDS_HAVE_DIFF_PARENT); + ERR(ERROR_NOT_CHILD_WINDOW); + ERR(ERROR_INVALID_GW_COMMAND); + ERR(ERROR_INVALID_THREAD_ID); + ERR(ERROR_NON_MDICHILD_WINDOW); + ERR(ERROR_POPUP_ALREADY_ACTIVE); + ERR(ERROR_NO_SCROLLBARS); + ERR(ERROR_INVALID_SCROLLBAR_RANGE); + ERR(ERROR_INVALID_SHOWWIN_COMMAND); + ERR(ERROR_NO_SYSTEM_RESOURCES); + ERR(ERROR_NONPAGED_SYSTEM_RESOURCES); + ERR(ERROR_PAGED_SYSTEM_RESOURCES); + ERR(ERROR_WORKING_SET_QUOTA); + ERR(ERROR_PAGEFILE_QUOTA); + ERR(ERROR_COMMITMENT_LIMIT); + ERR(ERROR_MENU_ITEM_NOT_FOUND); + ERR(ERROR_INVALID_KEYBOARD_HANDLE); + ERR(ERROR_HOOK_TYPE_NOT_ALLOWED); + ERR(ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION); + ERR(ERROR_TIMEOUT); + ERR(ERROR_INVALID_MONITOR_HANDLE); + ERR(ERROR_INCORRECT_SIZE); + ERR(ERROR_SYMLINK_CLASS_DISABLED); + ERR(ERROR_SYMLINK_NOT_SUPPORTED); + ERR(ERROR_XML_PARSE_ERROR); + ERR(ERROR_XMLDSIG_ERROR); + ERR(ERROR_RESTART_APPLICATION); + ERR(ERROR_WRONG_COMPARTMENT); + ERR(ERROR_AUTHIP_FAILURE); + ERR(ERROR_NO_NVRAM_RESOURCES); + ERR(ERROR_NOT_GUI_PROCESS); + ERR(ERROR_EVENTLOG_FILE_CORRUPT); + ERR(ERROR_EVENTLOG_CANT_START); + ERR(ERROR_LOG_FILE_FULL); + ERR(ERROR_EVENTLOG_FILE_CHANGED); + /*ERR(ERROR_CONTAINER_ASSIGNED); + ERR(ERROR_JOB_NO_CONTAINER);*/ + ERR(ERROR_INVALID_TASK_NAME); + ERR(ERROR_INVALID_TASK_INDEX); + ERR(ERROR_THREAD_ALREADY_IN_TASK); + ERR(ERROR_INSTALL_SERVICE_FAILURE); + ERR(ERROR_INSTALL_USEREXIT); + ERR(ERROR_INSTALL_FAILURE); + ERR(ERROR_INSTALL_SUSPEND); + ERR(ERROR_UNKNOWN_PRODUCT); + ERR(ERROR_UNKNOWN_FEATURE); + ERR(ERROR_UNKNOWN_COMPONENT); + ERR(ERROR_UNKNOWN_PROPERTY); + ERR(ERROR_INVALID_HANDLE_STATE); + ERR(ERROR_BAD_CONFIGURATION); + ERR(ERROR_INDEX_ABSENT); + ERR(ERROR_INSTALL_SOURCE_ABSENT); + ERR(ERROR_INSTALL_PACKAGE_VERSION); + ERR(ERROR_PRODUCT_UNINSTALLED); + ERR(ERROR_BAD_QUERY_SYNTAX); + ERR(ERROR_INVALID_FIELD); + ERR(ERROR_DEVICE_REMOVED); + ERR(ERROR_INSTALL_ALREADY_RUNNING); + ERR(ERROR_INSTALL_PACKAGE_OPEN_FAILED); + ERR(ERROR_INSTALL_PACKAGE_INVALID); + ERR(ERROR_INSTALL_UI_FAILURE); + ERR(ERROR_INSTALL_LOG_FAILURE); + ERR(ERROR_INSTALL_LANGUAGE_UNSUPPORTED); + ERR(ERROR_INSTALL_TRANSFORM_FAILURE); + ERR(ERROR_INSTALL_PACKAGE_REJECTED); + ERR(ERROR_FUNCTION_NOT_CALLED); + ERR(ERROR_FUNCTION_FAILED); + ERR(ERROR_INVALID_TABLE); + ERR(ERROR_DATATYPE_MISMATCH); + ERR(ERROR_UNSUPPORTED_TYPE); + ERR(ERROR_CREATE_FAILED); + ERR(ERROR_INSTALL_TEMP_UNWRITABLE); + ERR(ERROR_INSTALL_PLATFORM_UNSUPPORTED); + ERR(ERROR_INSTALL_NOTUSED); + ERR(ERROR_PATCH_PACKAGE_OPEN_FAILED); + ERR(ERROR_PATCH_PACKAGE_INVALID); + ERR(ERROR_PATCH_PACKAGE_UNSUPPORTED); + ERR(ERROR_PRODUCT_VERSION); + ERR(ERROR_INVALID_COMMAND_LINE); + ERR(ERROR_INSTALL_REMOTE_DISALLOWED); + ERR(ERROR_SUCCESS_REBOOT_INITIATED); + ERR(ERROR_PATCH_TARGET_NOT_FOUND); + ERR(ERROR_PATCH_PACKAGE_REJECTED); + ERR(ERROR_INSTALL_TRANSFORM_REJECTED); + ERR(ERROR_INSTALL_REMOTE_PROHIBITED); + ERR(ERROR_PATCH_REMOVAL_UNSUPPORTED); + ERR(ERROR_UNKNOWN_PATCH); + ERR(ERROR_PATCH_NO_SEQUENCE); + ERR(ERROR_PATCH_REMOVAL_DISALLOWED); + ERR(ERROR_INVALID_PATCH_XML); + ERR(ERROR_PATCH_MANAGED_ADVERTISED_PRODUCT); + ERR(ERROR_INSTALL_SERVICE_SAFEBOOT); + ERR(ERROR_FAIL_FAST_EXCEPTION); + ERR(ERROR_INSTALL_REJECTED); + ERR(ERROR_DYNAMIC_CODE_BLOCKED); + /*ERR(ERROR_NOT_SAME_OBJECT);*/ + ERR(ERROR_INVALID_USER_BUFFER); + ERR(ERROR_UNRECOGNIZED_MEDIA); + ERR(ERROR_NO_TRUST_LSA_SECRET); + ERR(ERROR_NO_TRUST_SAM_ACCOUNT); + ERR(ERROR_TRUSTED_DOMAIN_FAILURE); + ERR(ERROR_TRUSTED_RELATIONSHIP_FAILURE); + ERR(ERROR_TRUST_FAILURE); + ERR(ERROR_NETLOGON_NOT_STARTED); + ERR(ERROR_ACCOUNT_EXPIRED); + ERR(ERROR_REDIRECTOR_HAS_OPEN_HANDLES); + ERR(ERROR_PRINTER_DRIVER_ALREADY_INSTALLED); + ERR(ERROR_UNKNOWN_PORT); + ERR(ERROR_UNKNOWN_PRINTER_DRIVER); + ERR(ERROR_UNKNOWN_PRINTPROCESSOR); + ERR(ERROR_INVALID_SEPARATOR_FILE); + ERR(ERROR_INVALID_PRIORITY); + ERR(ERROR_INVALID_PRINTER_NAME); + ERR(ERROR_PRINTER_ALREADY_EXISTS); + ERR(ERROR_INVALID_PRINTER_COMMAND); + ERR(ERROR_INVALID_DATATYPE); + ERR(ERROR_INVALID_ENVIRONMENT); + ERR(ERROR_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT); + ERR(ERROR_NOLOGON_WORKSTATION_TRUST_ACCOUNT); + ERR(ERROR_NOLOGON_SERVER_TRUST_ACCOUNT); + ERR(ERROR_DOMAIN_TRUST_INCONSISTENT); + ERR(ERROR_SERVER_HAS_OPEN_HANDLES); + ERR(ERROR_RESOURCE_DATA_NOT_FOUND); + ERR(ERROR_RESOURCE_TYPE_NOT_FOUND); + ERR(ERROR_RESOURCE_NAME_NOT_FOUND); + ERR(ERROR_RESOURCE_LANG_NOT_FOUND); + ERR(ERROR_NOT_ENOUGH_QUOTA); + ERR(ERROR_INVALID_TIME); + ERR(ERROR_INVALID_FORM_NAME); + ERR(ERROR_INVALID_FORM_SIZE); + ERR(ERROR_ALREADY_WAITING); + ERR(ERROR_PRINTER_DELETED); + ERR(ERROR_INVALID_PRINTER_STATE); + ERR(ERROR_PASSWORD_MUST_CHANGE); + ERR(ERROR_DOMAIN_CONTROLLER_NOT_FOUND); + ERR(ERROR_ACCOUNT_LOCKED_OUT); + ERR(ERROR_NO_SITENAME); + ERR(ERROR_CANT_ACCESS_FILE); + ERR(ERROR_CANT_RESOLVE_FILENAME); + ERR(ERROR_KM_DRIVER_BLOCKED); + ERR(ERROR_CONTEXT_EXPIRED); + ERR(ERROR_PER_USER_TRUST_QUOTA_EXCEEDED); + ERR(ERROR_ALL_USER_TRUST_QUOTA_EXCEEDED); + ERR(ERROR_USER_DELETE_TRUST_QUOTA_EXCEEDED); + ERR(ERROR_AUTHENTICATION_FIREWALL_FAILED); + ERR(ERROR_REMOTE_PRINT_CONNECTIONS_BLOCKED); + ERR(ERROR_NTLM_BLOCKED); + ERR(ERROR_PASSWORD_CHANGE_REQUIRED); + ERR(ERROR_INVALID_PIXEL_FORMAT); + ERR(ERROR_BAD_DRIVER); + ERR(ERROR_INVALID_WINDOW_STYLE); + ERR(ERROR_METAFILE_NOT_SUPPORTED); + ERR(ERROR_TRANSFORM_NOT_SUPPORTED); + ERR(ERROR_CLIPPING_NOT_SUPPORTED); + ERR(ERROR_INVALID_CMM); + ERR(ERROR_QUORUM_RESOURCE_ONLINE_FAILED); + ERR(ERROR_QUORUMLOG_OPEN_FAILED); + ERR(ERROR_CLUSTERLOG_CORRUPT); + ERR(ERROR_CLUSTERLOG_RECORD_EXCEEDS_MAXSIZE); + ERR(ERROR_CLUSTERLOG_EXCEEDS_MAXSIZE); + ERR(ERROR_CLUSTERLOG_CHKPOINT_NOT_FOUND); + ERR(ERROR_CLUSTERLOG_NOT_ENOUGH_SPACE); + ERR(ERROR_QUORUM_OWNER_ALIVE); + ERR(ERROR_NETWORK_NOT_AVAILABLE); + ERR(ERROR_NODE_NOT_AVAILABLE); + ERR(ERROR_ALL_NODES_NOT_AVAILABLE); + ERR(ERROR_RESOURCE_FAILED); + ERR(ERROR_CLUSTER_INVALID_NODE); + ERR(ERROR_CLUSTER_NODE_EXISTS); + ERR(ERROR_CLUSTER_JOIN_IN_PROGRESS); + ERR(ERROR_CLUSTER_NODE_NOT_FOUND); + ERR(ERROR_CLUSTER_LOCAL_NODE_NOT_FOUND); + ERR(ERROR_CLUSTER_NETWORK_EXISTS); + ERR(ERROR_CLUSTER_NETWORK_NOT_FOUND); + ERR(ERROR_CLUSTER_NETINTERFACE_EXISTS); + ERR(ERROR_CLUSTER_NETINTERFACE_NOT_FOUND); + ERR(ERROR_CLUSTER_INVALID_REQUEST); + ERR(ERROR_CLUSTER_INVALID_NETWORK_PROVIDER); + ERR(ERROR_CLUSTER_NODE_DOWN); + ERR(ERROR_CLUSTER_NODE_UNREACHABLE); + ERR(ERROR_CLUSTER_NODE_NOT_MEMBER); + ERR(ERROR_CLUSTER_JOIN_NOT_IN_PROGRESS); + ERR(ERROR_CLUSTER_INVALID_NETWORK); + ERR(ERROR_CLUSTER_NODE_UP); + ERR(ERROR_CLUSTER_IPADDR_IN_USE); + ERR(ERROR_CLUSTER_NODE_NOT_PAUSED); + ERR(ERROR_CLUSTER_NO_SECURITY_CONTEXT); + ERR(ERROR_CLUSTER_NETWORK_NOT_INTERNAL); + ERR(ERROR_CLUSTER_NODE_ALREADY_UP); + ERR(ERROR_CLUSTER_NODE_ALREADY_DOWN); + ERR(ERROR_CLUSTER_NETWORK_ALREADY_ONLINE); + ERR(ERROR_CLUSTER_NETWORK_ALREADY_OFFLINE); + ERR(ERROR_CLUSTER_NODE_ALREADY_MEMBER); + ERR(ERROR_CLUSTER_LAST_INTERNAL_NETWORK); + ERR(ERROR_CLUSTER_NETWORK_HAS_DEPENDENTS); + ERR(ERROR_INVALID_OPERATION_ON_QUORUM); + ERR(ERROR_DEPENDENCY_NOT_ALLOWED); + ERR(ERROR_CLUSTER_NODE_PAUSED); + ERR(ERROR_NODE_CANT_HOST_RESOURCE); + ERR(ERROR_CLUSTER_NODE_NOT_READY); + ERR(ERROR_CLUSTER_NODE_SHUTTING_DOWN); + ERR(ERROR_CLUSTER_JOIN_ABORTED); + ERR(ERROR_CLUSTER_INCOMPATIBLE_VERSIONS); + ERR(ERROR_CLUSTER_MAXNUM_OF_RESOURCES_EXCEEDED); + ERR(ERROR_CLUSTER_SYSTEM_CONFIG_CHANGED); + ERR(ERROR_CLUSTER_RESOURCE_TYPE_NOT_FOUND); + ERR(ERROR_CLUSTER_RESTYPE_NOT_SUPPORTED); + ERR(ERROR_CLUSTER_RESNAME_NOT_FOUND); + ERR(ERROR_CLUSTER_NO_RPC_PACKAGES_REGISTERED); + ERR(ERROR_CLUSTER_OWNER_NOT_IN_PREFLIST); + ERR(ERROR_CLUSTER_DATABASE_SEQMISMATCH); + ERR(ERROR_RESMON_INVALID_STATE); + ERR(ERROR_CLUSTER_GUM_NOT_LOCKER); + ERR(ERROR_QUORUM_DISK_NOT_FOUND); + ERR(ERROR_DATABASE_BACKUP_CORRUPT); + ERR(ERROR_CLUSTER_NODE_ALREADY_HAS_DFS_ROOT); + ERR(ERROR_RESOURCE_PROPERTY_UNCHANGEABLE); + ERR(ERROR_NO_ADMIN_ACCESS_POINT); + ERR(ERROR_CLUSTER_MEMBERSHIP_INVALID_STATE); + ERR(ERROR_CLUSTER_QUORUMLOG_NOT_FOUND); + ERR(ERROR_CLUSTER_MEMBERSHIP_HALT); + ERR(ERROR_CLUSTER_INSTANCE_ID_MISMATCH); + ERR(ERROR_CLUSTER_NETWORK_NOT_FOUND_FOR_IP); + ERR(ERROR_CLUSTER_PROPERTY_DATA_TYPE_MISMATCH); + ERR(ERROR_CLUSTER_EVICT_WITHOUT_CLEANUP); + ERR(ERROR_CLUSTER_PARAMETER_MISMATCH); + ERR(ERROR_NODE_CANNOT_BE_CLUSTERED); + ERR(ERROR_CLUSTER_WRONG_OS_VERSION); + ERR(ERROR_CLUSTER_CANT_CREATE_DUP_CLUSTER_NAME); + ERR(ERROR_CLUSCFG_ALREADY_COMMITTED); + ERR(ERROR_CLUSCFG_ROLLBACK_FAILED); + ERR(ERROR_CLUSCFG_SYSTEM_DISK_DRIVE_LETTER_CONFLICT); + ERR(ERROR_CLUSTER_OLD_VERSION); + ERR(ERROR_CLUSTER_MISMATCHED_COMPUTER_ACCT_NAME); + ERR(ERROR_CLUSTER_NO_NET_ADAPTERS); + ERR(ERROR_CLUSTER_POISONED); + ERR(ERROR_CLUSTER_GROUP_MOVING); + ERR(ERROR_CLUSTER_RESOURCE_TYPE_BUSY); + ERR(ERROR_RESOURCE_CALL_TIMED_OUT); + ERR(ERROR_INVALID_CLUSTER_IPV6_ADDRESS); + ERR(ERROR_CLUSTER_INTERNAL_INVALID_FUNCTION); + ERR(ERROR_CLUSTER_PARAMETER_OUT_OF_BOUNDS); + ERR(ERROR_CLUSTER_PARTIAL_SEND); + ERR(ERROR_CLUSTER_REGISTRY_INVALID_FUNCTION); + ERR(ERROR_CLUSTER_INVALID_STRING_TERMINATION); + ERR(ERROR_CLUSTER_INVALID_STRING_FORMAT); + ERR(ERROR_CLUSTER_DATABASE_TRANSACTION_IN_PROGRESS); + ERR(ERROR_CLUSTER_DATABASE_TRANSACTION_NOT_IN_PROGRESS); + ERR(ERROR_CLUSTER_NULL_DATA); + ERR(ERROR_CLUSTER_PARTIAL_READ); + ERR(ERROR_CLUSTER_PARTIAL_WRITE); + ERR(ERROR_CLUSTER_CANT_DESERIALIZE_DATA); + ERR(ERROR_DEPENDENT_RESOURCE_PROPERTY_CONFLICT); + ERR(ERROR_CLUSTER_NO_QUORUM); + ERR(ERROR_CLUSTER_INVALID_IPV6_NETWORK); + ERR(ERROR_CLUSTER_INVALID_IPV6_TUNNEL_NETWORK); + ERR(ERROR_QUORUM_NOT_ALLOWED_IN_THIS_GROUP); + ERR(ERROR_DEPENDENCY_TREE_TOO_COMPLEX); + ERR(ERROR_EXCEPTION_IN_RESOURCE_CALL); + ERR(ERROR_CLUSTER_RHS_FAILED_INITIALIZATION); + ERR(ERROR_CLUSTER_NOT_INSTALLED); + ERR(ERROR_CLUSTER_RESOURCES_MUST_BE_ONLINE_ON_THE_SAME_NODE); + ERR(ERROR_CLUSTER_MAX_NODES_IN_CLUSTER); + ERR(ERROR_CLUSTER_TOO_MANY_NODES); + ERR(ERROR_CLUSTER_OBJECT_ALREADY_USED); + ERR(ERROR_NONCORE_GROUPS_FOUND); + ERR(ERROR_FILE_SHARE_RESOURCE_CONFLICT); + ERR(ERROR_CLUSTER_EVICT_INVALID_REQUEST); + ERR(ERROR_CLUSTER_SINGLETON_RESOURCE); + ERR(ERROR_CLUSTER_GROUP_SINGLETON_RESOURCE); + ERR(ERROR_CLUSTER_RESOURCE_PROVIDER_FAILED); + ERR(ERROR_CLUSTER_RESOURCE_CONFIGURATION_ERROR); + ERR(ERROR_CLUSTER_GROUP_BUSY); + ERR(ERROR_CLUSTER_NOT_SHARED_VOLUME); + ERR(ERROR_CLUSTER_INVALID_SECURITY_DESCRIPTOR); + ERR(ERROR_CLUSTER_SHARED_VOLUMES_IN_USE); + ERR(ERROR_CLUSTER_USE_SHARED_VOLUMES_API); + ERR(ERROR_CLUSTER_BACKUP_IN_PROGRESS); + ERR(ERROR_NON_CSV_PATH); + ERR(ERROR_CSV_VOLUME_NOT_LOCAL); + ERR(ERROR_CLUSTER_WATCHDOG_TERMINATING); + ERR(ERROR_CLUSTER_RESOURCE_VETOED_MOVE_INCOMPATIBLE_NODES); + ERR(ERROR_CLUSTER_INVALID_NODE_WEIGHT); + ERR(ERROR_CLUSTER_RESOURCE_VETOED_CALL); + ERR(ERROR_RESMON_SYSTEM_RESOURCES_LACKING); + ERR(ERROR_CLUSTER_RESOURCE_VETOED_MOVE_NOT_ENOUGH_RESOURCES_ON_DESTINATION); + ERR(ERROR_CLUSTER_RESOURCE_VETOED_MOVE_NOT_ENOUGH_RESOURCES_ON_SOURCE); + ERR(ERROR_CLUSTER_GROUP_QUEUED); + ERR(ERROR_CLUSTER_RESOURCE_LOCKED_STATUS); + ERR(ERROR_CLUSTER_SHARED_VOLUME_FAILOVER_NOT_ALLOWED); + ERR(ERROR_CLUSTER_NODE_DRAIN_IN_PROGRESS); + ERR(ERROR_CLUSTER_DISK_NOT_CONNECTED); + ERR(ERROR_DISK_NOT_CSV_CAPABLE); + ERR(ERROR_RESOURCE_NOT_IN_AVAILABLE_STORAGE); + ERR(ERROR_CLUSTER_SHARED_VOLUME_REDIRECTED); + ERR(ERROR_CLUSTER_SHARED_VOLUME_NOT_REDIRECTED); + ERR(ERROR_CLUSTER_CANNOT_RETURN_PROPERTIES); + ERR(ERROR_CLUSTER_RESOURCE_CONTAINS_UNSUPPORTED_DIFF_AREA_FOR_SHARED_VOLUMES); + ERR(ERROR_CLUSTER_RESOURCE_IS_IN_MAINTENANCE_MODE); + ERR(ERROR_CLUSTER_AFFINITY_CONFLICT); + ERR(ERROR_CLUSTER_RESOURCE_IS_REPLICA_VIRTUAL_MACHINE); + /*ERR(ERROR_CLUSTER_UPGRADE_INCOMPATIBLE_VERSIONS); + ERR(ERROR_CLUSTER_UPGRADE_FIX_QUORUM_NOT_SUPPORTED); + ERR(ERROR_CLUSTER_UPGRADE_RESTART_REQUIRED); + ERR(ERROR_CLUSTER_UPGRADE_IN_PROGRESS); + ERR(ERROR_CLUSTER_UPGRADE_INCOMPLETE); + ERR(ERROR_CLUSTER_NODE_IN_GRACE_PERIOD); + ERR(ERROR_CLUSTER_CSV_IO_PAUSE_TIMEOUT); + ERR(ERROR_NODE_NOT_ACTIVE_CLUSTER_MEMBER); + ERR(ERROR_CLUSTER_RESOURCE_NOT_MONITORED); + ERR(ERROR_CLUSTER_RESOURCE_DOES_NOT_SUPPORT_UNMONITORED); + ERR(ERROR_CLUSTER_RESOURCE_IS_REPLICATED); + ERR(ERROR_CLUSTER_NODE_ISOLATED); + ERR(ERROR_CLUSTER_NODE_QUARANTINED); + ERR(ERROR_CLUSTER_DATABASE_UPDATE_CONDITION_FAILED); + ERR(ERROR_CLUSTER_SPACE_DEGRADED); + ERR(ERROR_CLUSTER_TOKEN_DELEGATION_NOT_SUPPORTED);*/ + ERR(ERROR_ENCRYPTION_FAILED); + ERR(ERROR_DECRYPTION_FAILED); + ERR(ERROR_FILE_ENCRYPTED); + ERR(ERROR_NO_RECOVERY_POLICY); + ERR(ERROR_NO_EFS); + ERR(ERROR_WRONG_EFS); + ERR(ERROR_NO_USER_KEYS); + ERR(ERROR_FILE_NOT_ENCRYPTED); + ERR(ERROR_NOT_EXPORT_FORMAT); + ERR(ERROR_FILE_READ_ONLY); + ERR(ERROR_DIR_EFS_DISALLOWED); + ERR(ERROR_EFS_SERVER_NOT_TRUSTED); + ERR(ERROR_BAD_RECOVERY_POLICY); + ERR(ERROR_EFS_ALG_BLOB_TOO_BIG); + ERR(ERROR_VOLUME_NOT_SUPPORT_EFS); + ERR(ERROR_EFS_DISABLED); + ERR(ERROR_EFS_VERSION_NOT_SUPPORT); + ERR(ERROR_CS_ENCRYPTION_INVALID_SERVER_RESPONSE); + ERR(ERROR_CS_ENCRYPTION_UNSUPPORTED_SERVER); + ERR(ERROR_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE); + ERR(ERROR_CS_ENCRYPTION_NEW_ENCRYPTED_FILE); + ERR(ERROR_CS_ENCRYPTION_FILE_NOT_CSE); + ERR(ERROR_ENCRYPTION_POLICY_DENIES_OPERATION); + ERR(ERROR_NO_BROWSER_SERVERS_FOUND); + ERR(ERROR_LOG_SECTOR_INVALID); + ERR(ERROR_LOG_SECTOR_PARITY_INVALID); + ERR(ERROR_LOG_SECTOR_REMAPPED); + ERR(ERROR_LOG_BLOCK_INCOMPLETE); + ERR(ERROR_LOG_INVALID_RANGE); + ERR(ERROR_LOG_BLOCKS_EXHAUSTED); + ERR(ERROR_LOG_READ_CONTEXT_INVALID); + ERR(ERROR_LOG_RESTART_INVALID); + ERR(ERROR_LOG_BLOCK_VERSION); + ERR(ERROR_LOG_BLOCK_INVALID); + ERR(ERROR_LOG_READ_MODE_INVALID); + ERR(ERROR_LOG_NO_RESTART); + ERR(ERROR_LOG_METADATA_CORRUPT); + ERR(ERROR_LOG_METADATA_INVALID); + ERR(ERROR_LOG_METADATA_INCONSISTENT); + ERR(ERROR_LOG_RESERVATION_INVALID); + ERR(ERROR_LOG_CANT_DELETE); + ERR(ERROR_LOG_CONTAINER_LIMIT_EXCEEDED); + ERR(ERROR_LOG_START_OF_LOG); + ERR(ERROR_LOG_POLICY_ALREADY_INSTALLED); + ERR(ERROR_LOG_POLICY_NOT_INSTALLED); + ERR(ERROR_LOG_POLICY_INVALID); + ERR(ERROR_LOG_POLICY_CONFLICT); + ERR(ERROR_LOG_PINNED_ARCHIVE_TAIL); + ERR(ERROR_LOG_RECORD_NONEXISTENT); + ERR(ERROR_LOG_RECORDS_RESERVED_INVALID); + ERR(ERROR_LOG_SPACE_RESERVED_INVALID); + ERR(ERROR_LOG_TAIL_INVALID); + ERR(ERROR_LOG_FULL); + ERR(ERROR_COULD_NOT_RESIZE_LOG); + ERR(ERROR_LOG_MULTIPLEXED); + ERR(ERROR_LOG_DEDICATED); + ERR(ERROR_LOG_ARCHIVE_NOT_IN_PROGRESS); + ERR(ERROR_LOG_ARCHIVE_IN_PROGRESS); + ERR(ERROR_LOG_EPHEMERAL); + ERR(ERROR_LOG_NOT_ENOUGH_CONTAINERS); + ERR(ERROR_LOG_CLIENT_ALREADY_REGISTERED); + ERR(ERROR_LOG_CLIENT_NOT_REGISTERED); + ERR(ERROR_LOG_FULL_HANDLER_IN_PROGRESS); + ERR(ERROR_LOG_CONTAINER_READ_FAILED); + ERR(ERROR_LOG_CONTAINER_WRITE_FAILED); + ERR(ERROR_LOG_CONTAINER_OPEN_FAILED); + ERR(ERROR_LOG_CONTAINER_STATE_INVALID); + ERR(ERROR_LOG_STATE_INVALID); + ERR(ERROR_LOG_PINNED); + ERR(ERROR_LOG_METADATA_FLUSH_FAILED); + ERR(ERROR_LOG_INCONSISTENT_SECURITY); + ERR(ERROR_LOG_APPENDED_FLUSH_FAILED); + ERR(ERROR_LOG_PINNED_RESERVATION); + ERR(ERROR_INVALID_TRANSACTION); + ERR(ERROR_TRANSACTION_NOT_ACTIVE); + ERR(ERROR_TRANSACTION_REQUEST_NOT_VALID); + ERR(ERROR_TRANSACTION_NOT_REQUESTED); + ERR(ERROR_TRANSACTION_ALREADY_ABORTED); + ERR(ERROR_TRANSACTION_ALREADY_COMMITTED); + ERR(ERROR_TM_INITIALIZATION_FAILED); + ERR(ERROR_RESOURCEMANAGER_READ_ONLY); + ERR(ERROR_TRANSACTION_NOT_JOINED); + ERR(ERROR_TRANSACTION_SUPERIOR_EXISTS); + ERR(ERROR_CRM_PROTOCOL_ALREADY_EXISTS); + ERR(ERROR_TRANSACTION_PROPAGATION_FAILED); + ERR(ERROR_CRM_PROTOCOL_NOT_FOUND); + ERR(ERROR_TRANSACTION_INVALID_MARSHALL_BUFFER); + ERR(ERROR_CURRENT_TRANSACTION_NOT_VALID); + ERR(ERROR_TRANSACTION_NOT_FOUND); + ERR(ERROR_RESOURCEMANAGER_NOT_FOUND); + ERR(ERROR_ENLISTMENT_NOT_FOUND); + ERR(ERROR_TRANSACTIONMANAGER_NOT_FOUND); + ERR(ERROR_TRANSACTIONMANAGER_NOT_ONLINE); + ERR(ERROR_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION); + ERR(ERROR_TRANSACTION_NOT_ROOT); + ERR(ERROR_TRANSACTION_OBJECT_EXPIRED); + ERR(ERROR_TRANSACTION_RESPONSE_NOT_ENLISTED); + ERR(ERROR_TRANSACTION_RECORD_TOO_LONG); + ERR(ERROR_IMPLICIT_TRANSACTION_NOT_SUPPORTED); + ERR(ERROR_TRANSACTION_INTEGRITY_VIOLATED); + ERR(ERROR_TRANSACTIONMANAGER_IDENTITY_MISMATCH); + ERR(ERROR_RM_CANNOT_BE_FROZEN_FOR_SNAPSHOT); + ERR(ERROR_TRANSACTION_MUST_WRITETHROUGH); + ERR(ERROR_TRANSACTION_NO_SUPERIOR); + ERR(ERROR_HEURISTIC_DAMAGE_POSSIBLE); + ERR(ERROR_TRANSACTIONAL_CONFLICT); + ERR(ERROR_RM_NOT_ACTIVE); + ERR(ERROR_RM_METADATA_CORRUPT); + ERR(ERROR_DIRECTORY_NOT_RM); + ERR(ERROR_TRANSACTIONS_UNSUPPORTED_REMOTE); + ERR(ERROR_LOG_RESIZE_INVALID_SIZE); + ERR(ERROR_OBJECT_NO_LONGER_EXISTS); + ERR(ERROR_STREAM_MINIVERSION_NOT_FOUND); + ERR(ERROR_STREAM_MINIVERSION_NOT_VALID); + ERR(ERROR_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION); + ERR(ERROR_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT); + ERR(ERROR_CANT_CREATE_MORE_STREAM_MINIVERSIONS); + ERR(ERROR_REMOTE_FILE_VERSION_MISMATCH); + ERR(ERROR_HANDLE_NO_LONGER_VALID); + ERR(ERROR_NO_TXF_METADATA); + ERR(ERROR_LOG_CORRUPTION_DETECTED); + ERR(ERROR_CANT_RECOVER_WITH_HANDLE_OPEN); + ERR(ERROR_RM_DISCONNECTED); + ERR(ERROR_ENLISTMENT_NOT_SUPERIOR); + ERR(ERROR_RECOVERY_NOT_NEEDED); + ERR(ERROR_RM_ALREADY_STARTED); + ERR(ERROR_FILE_IDENTITY_NOT_PERSISTENT); + ERR(ERROR_CANT_BREAK_TRANSACTIONAL_DEPENDENCY); + ERR(ERROR_CANT_CROSS_RM_BOUNDARY); + ERR(ERROR_TXF_DIR_NOT_EMPTY); + ERR(ERROR_INDOUBT_TRANSACTIONS_EXIST); + ERR(ERROR_TM_VOLATILE); + ERR(ERROR_ROLLBACK_TIMER_EXPIRED); + ERR(ERROR_TXF_ATTRIBUTE_CORRUPT); + ERR(ERROR_EFS_NOT_ALLOWED_IN_TRANSACTION); + ERR(ERROR_TRANSACTIONAL_OPEN_NOT_ALLOWED); + ERR(ERROR_LOG_GROWTH_FAILED); + ERR(ERROR_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE); + ERR(ERROR_TXF_METADATA_ALREADY_PRESENT); + ERR(ERROR_TRANSACTION_SCOPE_CALLBACKS_NOT_SET); + ERR(ERROR_TRANSACTION_REQUIRED_PROMOTION); + ERR(ERROR_CANNOT_EXECUTE_FILE_IN_TRANSACTION); + ERR(ERROR_TRANSACTIONS_NOT_FROZEN); + ERR(ERROR_TRANSACTION_FREEZE_IN_PROGRESS); + ERR(ERROR_NOT_SNAPSHOT_VOLUME); + ERR(ERROR_NO_SAVEPOINT_WITH_OPEN_FILES); + ERR(ERROR_DATA_LOST_REPAIR); + ERR(ERROR_SPARSE_NOT_ALLOWED_IN_TRANSACTION); + ERR(ERROR_TM_IDENTITY_MISMATCH); + ERR(ERROR_FLOATED_SECTION); + ERR(ERROR_CANNOT_ACCEPT_TRANSACTED_WORK); + ERR(ERROR_CANNOT_ABORT_TRANSACTIONS); + ERR(ERROR_BAD_CLUSTERS); + ERR(ERROR_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION); + ERR(ERROR_VOLUME_DIRTY); + ERR(ERROR_NO_LINK_TRACKING_IN_TRANSACTION); + ERR(ERROR_OPERATION_NOT_SUPPORTED_IN_TRANSACTION); + ERR(ERROR_EXPIRED_HANDLE); + ERR(ERROR_TRANSACTION_NOT_ENLISTED); + ERR(ERROR_CTX_WINSTATION_NAME_INVALID); + ERR(ERROR_CTX_INVALID_PD); + ERR(ERROR_CTX_PD_NOT_FOUND); + ERR(ERROR_CTX_WD_NOT_FOUND); + ERR(ERROR_CTX_CANNOT_MAKE_EVENTLOG_ENTRY); + ERR(ERROR_CTX_SERVICE_NAME_COLLISION); + ERR(ERROR_CTX_CLOSE_PENDING); + ERR(ERROR_CTX_NO_OUTBUF); + ERR(ERROR_CTX_MODEM_INF_NOT_FOUND); + ERR(ERROR_CTX_INVALID_MODEMNAME); + ERR(ERROR_CTX_MODEM_RESPONSE_ERROR); + ERR(ERROR_CTX_MODEM_RESPONSE_TIMEOUT); + ERR(ERROR_CTX_MODEM_RESPONSE_NO_CARRIER); + ERR(ERROR_CTX_MODEM_RESPONSE_NO_DIALTONE); + ERR(ERROR_CTX_MODEM_RESPONSE_BUSY); + ERR(ERROR_CTX_MODEM_RESPONSE_VOICE); + ERR(ERROR_CTX_TD_ERROR); + ERR(ERROR_CTX_WINSTATION_NOT_FOUND); + ERR(ERROR_CTX_WINSTATION_ALREADY_EXISTS); + ERR(ERROR_CTX_WINSTATION_BUSY); + ERR(ERROR_CTX_BAD_VIDEO_MODE); + ERR(ERROR_CTX_GRAPHICS_INVALID); + ERR(ERROR_CTX_LOGON_DISABLED); + ERR(ERROR_CTX_NOT_CONSOLE); + ERR(ERROR_CTX_CLIENT_QUERY_TIMEOUT); + ERR(ERROR_CTX_CONSOLE_DISCONNECT); + ERR(ERROR_CTX_CONSOLE_CONNECT); + ERR(ERROR_CTX_SHADOW_DENIED); + ERR(ERROR_CTX_WINSTATION_ACCESS_DENIED); + ERR(ERROR_CTX_INVALID_WD); + ERR(ERROR_CTX_SHADOW_INVALID); + ERR(ERROR_CTX_SHADOW_DISABLED); + ERR(ERROR_CTX_CLIENT_LICENSE_IN_USE); + ERR(ERROR_CTX_CLIENT_LICENSE_NOT_SET); + ERR(ERROR_CTX_LICENSE_NOT_AVAILABLE); + ERR(ERROR_CTX_LICENSE_CLIENT_INVALID); + ERR(ERROR_CTX_LICENSE_EXPIRED); + ERR(ERROR_CTX_SHADOW_NOT_RUNNING); + ERR(ERROR_CTX_SHADOW_ENDED_BY_MODE_CHANGE); + ERR(ERROR_ACTIVATION_COUNT_EXCEEDED); + ERR(ERROR_CTX_WINSTATIONS_DISABLED); + ERR(ERROR_CTX_ENCRYPTION_LEVEL_REQUIRED); + ERR(ERROR_CTX_SESSION_IN_USE); + ERR(ERROR_CTX_NO_FORCE_LOGOFF); + ERR(ERROR_CTX_ACCOUNT_RESTRICTION); + ERR(ERROR_RDP_PROTOCOL_ERROR); + ERR(ERROR_CTX_CDM_CONNECT); + ERR(ERROR_CTX_CDM_DISCONNECT); + ERR(ERROR_CTX_SECURITY_LAYER_ERROR); + ERR(ERROR_TS_INCOMPATIBLE_SESSIONS); + ERR(ERROR_TS_VIDEO_SUBSYSTEM_ERROR); + ERR(ERROR_DS_NOT_INSTALLED); + ERR(ERROR_DS_MEMBERSHIP_EVALUATED_LOCALLY); + ERR(ERROR_DS_NO_ATTRIBUTE_OR_VALUE); + ERR(ERROR_DS_INVALID_ATTRIBUTE_SYNTAX); + ERR(ERROR_DS_ATTRIBUTE_TYPE_UNDEFINED); + ERR(ERROR_DS_ATTRIBUTE_OR_VALUE_EXISTS); + ERR(ERROR_DS_BUSY); + ERR(ERROR_DS_UNAVAILABLE); + ERR(ERROR_DS_NO_RIDS_ALLOCATED); + ERR(ERROR_DS_NO_MORE_RIDS); + ERR(ERROR_DS_INCORRECT_ROLE_OWNER); + ERR(ERROR_DS_RIDMGR_INIT_ERROR); + ERR(ERROR_DS_OBJ_CLASS_VIOLATION); + ERR(ERROR_DS_CANT_ON_NON_LEAF); + ERR(ERROR_DS_CANT_ON_RDN); + ERR(ERROR_DS_CANT_MOD_OBJ_CLASS); + ERR(ERROR_DS_CROSS_DOM_MOVE_ERROR); + ERR(ERROR_DS_GC_NOT_AVAILABLE); + ERR(ERROR_SHARED_POLICY); + ERR(ERROR_POLICY_OBJECT_NOT_FOUND); + ERR(ERROR_POLICY_ONLY_IN_DS); + ERR(ERROR_PROMOTION_ACTIVE); + ERR(ERROR_NO_PROMOTION_ACTIVE); + ERR(ERROR_DS_OPERATIONS_ERROR); + ERR(ERROR_DS_PROTOCOL_ERROR); + ERR(ERROR_DS_TIMELIMIT_EXCEEDED); + ERR(ERROR_DS_SIZELIMIT_EXCEEDED); + ERR(ERROR_DS_ADMIN_LIMIT_EXCEEDED); + ERR(ERROR_DS_COMPARE_FALSE); + ERR(ERROR_DS_COMPARE_TRUE); + ERR(ERROR_DS_AUTH_METHOD_NOT_SUPPORTED); + ERR(ERROR_DS_STRONG_AUTH_REQUIRED); + ERR(ERROR_DS_INAPPROPRIATE_AUTH); + ERR(ERROR_DS_AUTH_UNKNOWN); + ERR(ERROR_DS_REFERRAL); + ERR(ERROR_DS_UNAVAILABLE_CRIT_EXTENSION); + ERR(ERROR_DS_CONFIDENTIALITY_REQUIRED); + ERR(ERROR_DS_INAPPROPRIATE_MATCHING); + ERR(ERROR_DS_CONSTRAINT_VIOLATION); + ERR(ERROR_DS_NO_SUCH_OBJECT); + ERR(ERROR_DS_ALIAS_PROBLEM); + ERR(ERROR_DS_INVALID_DN_SYNTAX); + ERR(ERROR_DS_IS_LEAF); + ERR(ERROR_DS_ALIAS_DEREF_PROBLEM); + ERR(ERROR_DS_UNWILLING_TO_PERFORM); + ERR(ERROR_DS_LOOP_DETECT); + ERR(ERROR_DS_NAMING_VIOLATION); + ERR(ERROR_DS_OBJECT_RESULTS_TOO_LARGE); + ERR(ERROR_DS_AFFECTS_MULTIPLE_DSAS); + ERR(ERROR_DS_SERVER_DOWN); + ERR(ERROR_DS_LOCAL_ERROR); + ERR(ERROR_DS_ENCODING_ERROR); + ERR(ERROR_DS_DECODING_ERROR); + ERR(ERROR_DS_FILTER_UNKNOWN); + ERR(ERROR_DS_PARAM_ERROR); + ERR(ERROR_DS_NOT_SUPPORTED); + ERR(ERROR_DS_NO_RESULTS_RETURNED); + ERR(ERROR_DS_CONTROL_NOT_FOUND); + ERR(ERROR_DS_CLIENT_LOOP); + ERR(ERROR_DS_REFERRAL_LIMIT_EXCEEDED); + ERR(ERROR_DS_SORT_CONTROL_MISSING); + ERR(ERROR_DS_OFFSET_RANGE_ERROR); + ERR(ERROR_DS_RIDMGR_DISABLED); + ERR(ERROR_DS_ROOT_MUST_BE_NC); + ERR(ERROR_DS_ADD_REPLICA_INHIBITED); + ERR(ERROR_DS_ATT_NOT_DEF_IN_SCHEMA); + ERR(ERROR_DS_MAX_OBJ_SIZE_EXCEEDED); + ERR(ERROR_DS_OBJ_STRING_NAME_EXISTS); + ERR(ERROR_DS_NO_RDN_DEFINED_IN_SCHEMA); + ERR(ERROR_DS_RDN_DOESNT_MATCH_SCHEMA); + ERR(ERROR_DS_NO_REQUESTED_ATTS_FOUND); + ERR(ERROR_DS_USER_BUFFER_TO_SMALL); + ERR(ERROR_DS_ATT_IS_NOT_ON_OBJ); + ERR(ERROR_DS_ILLEGAL_MOD_OPERATION); + ERR(ERROR_DS_OBJ_TOO_LARGE); + ERR(ERROR_DS_BAD_INSTANCE_TYPE); + ERR(ERROR_DS_MASTERDSA_REQUIRED); + ERR(ERROR_DS_OBJECT_CLASS_REQUIRED); + ERR(ERROR_DS_MISSING_REQUIRED_ATT); + ERR(ERROR_DS_ATT_NOT_DEF_FOR_CLASS); + ERR(ERROR_DS_ATT_ALREADY_EXISTS); + ERR(ERROR_DS_CANT_ADD_ATT_VALUES); + ERR(ERROR_DS_SINGLE_VALUE_CONSTRAINT); + ERR(ERROR_DS_RANGE_CONSTRAINT); + ERR(ERROR_DS_ATT_VAL_ALREADY_EXISTS); + ERR(ERROR_DS_CANT_REM_MISSING_ATT); + ERR(ERROR_DS_CANT_REM_MISSING_ATT_VAL); + ERR(ERROR_DS_ROOT_CANT_BE_SUBREF); + ERR(ERROR_DS_NO_CHAINING); + ERR(ERROR_DS_NO_CHAINED_EVAL); + ERR(ERROR_DS_NO_PARENT_OBJECT); + ERR(ERROR_DS_PARENT_IS_AN_ALIAS); + ERR(ERROR_DS_CANT_MIX_MASTER_AND_REPS); + ERR(ERROR_DS_CHILDREN_EXIST); + ERR(ERROR_DS_OBJ_NOT_FOUND); + ERR(ERROR_DS_ALIASED_OBJ_MISSING); + ERR(ERROR_DS_BAD_NAME_SYNTAX); + ERR(ERROR_DS_ALIAS_POINTS_TO_ALIAS); + ERR(ERROR_DS_CANT_DEREF_ALIAS); + ERR(ERROR_DS_OUT_OF_SCOPE); + ERR(ERROR_DS_OBJECT_BEING_REMOVED); + ERR(ERROR_DS_CANT_DELETE_DSA_OBJ); + ERR(ERROR_DS_GENERIC_ERROR); + ERR(ERROR_DS_DSA_MUST_BE_INT_MASTER); + ERR(ERROR_DS_CLASS_NOT_DSA); + ERR(ERROR_DS_INSUFF_ACCESS_RIGHTS); + ERR(ERROR_DS_ILLEGAL_SUPERIOR); + ERR(ERROR_DS_ATTRIBUTE_OWNED_BY_SAM); + ERR(ERROR_DS_NAME_TOO_MANY_PARTS); + ERR(ERROR_DS_NAME_TOO_LONG); + ERR(ERROR_DS_NAME_VALUE_TOO_LONG); + ERR(ERROR_DS_NAME_UNPARSEABLE); + ERR(ERROR_DS_NAME_TYPE_UNKNOWN); + ERR(ERROR_DS_NOT_AN_OBJECT); + ERR(ERROR_DS_SEC_DESC_TOO_SHORT); + ERR(ERROR_DS_SEC_DESC_INVALID); + ERR(ERROR_DS_NO_DELETED_NAME); + ERR(ERROR_DS_SUBREF_MUST_HAVE_PARENT); + ERR(ERROR_DS_NCNAME_MUST_BE_NC); + ERR(ERROR_DS_CANT_ADD_SYSTEM_ONLY); + ERR(ERROR_DS_CLASS_MUST_BE_CONCRETE); + ERR(ERROR_DS_INVALID_DMD); + ERR(ERROR_DS_OBJ_GUID_EXISTS); + ERR(ERROR_DS_NOT_ON_BACKLINK); + ERR(ERROR_DS_NO_CROSSREF_FOR_NC); + ERR(ERROR_DS_SHUTTING_DOWN); + ERR(ERROR_DS_UNKNOWN_OPERATION); + ERR(ERROR_DS_INVALID_ROLE_OWNER); + ERR(ERROR_DS_COULDNT_CONTACT_FSMO); + ERR(ERROR_DS_CROSS_NC_DN_RENAME); + ERR(ERROR_DS_CANT_MOD_SYSTEM_ONLY); + ERR(ERROR_DS_REPLICATOR_ONLY); + ERR(ERROR_DS_OBJ_CLASS_NOT_DEFINED); + ERR(ERROR_DS_OBJ_CLASS_NOT_SUBCLASS); + ERR(ERROR_DS_NAME_REFERENCE_INVALID); + ERR(ERROR_DS_CROSS_REF_EXISTS); + ERR(ERROR_DS_CANT_DEL_MASTER_CROSSREF); + ERR(ERROR_DS_SUBTREE_NOTIFY_NOT_NC_HEAD); + ERR(ERROR_DS_NOTIFY_FILTER_TOO_COMPLEX); + ERR(ERROR_DS_DUP_RDN); + ERR(ERROR_DS_DUP_OID); + ERR(ERROR_DS_DUP_MAPI_ID); + ERR(ERROR_DS_DUP_SCHEMA_ID_GUID); + ERR(ERROR_DS_DUP_LDAP_DISPLAY_NAME); + ERR(ERROR_DS_SEMANTIC_ATT_TEST); + ERR(ERROR_DS_SYNTAX_MISMATCH); + ERR(ERROR_DS_EXISTS_IN_MUST_HAVE); + ERR(ERROR_DS_EXISTS_IN_MAY_HAVE); + ERR(ERROR_DS_NONEXISTENT_MAY_HAVE); + ERR(ERROR_DS_NONEXISTENT_MUST_HAVE); + ERR(ERROR_DS_AUX_CLS_TEST_FAIL); + ERR(ERROR_DS_NONEXISTENT_POSS_SUP); + ERR(ERROR_DS_SUB_CLS_TEST_FAIL); + ERR(ERROR_DS_BAD_RDN_ATT_ID_SYNTAX); + ERR(ERROR_DS_EXISTS_IN_AUX_CLS); + ERR(ERROR_DS_EXISTS_IN_SUB_CLS); + ERR(ERROR_DS_EXISTS_IN_POSS_SUP); + ERR(ERROR_DS_RECALCSCHEMA_FAILED); + ERR(ERROR_DS_TREE_DELETE_NOT_FINISHED); + ERR(ERROR_DS_CANT_DELETE); + ERR(ERROR_DS_ATT_SCHEMA_REQ_ID); + ERR(ERROR_DS_BAD_ATT_SCHEMA_SYNTAX); + ERR(ERROR_DS_CANT_CACHE_ATT); + ERR(ERROR_DS_CANT_CACHE_CLASS); + ERR(ERROR_DS_CANT_REMOVE_ATT_CACHE); + ERR(ERROR_DS_CANT_REMOVE_CLASS_CACHE); + ERR(ERROR_DS_CANT_RETRIEVE_DN); + ERR(ERROR_DS_MISSING_SUPREF); + ERR(ERROR_DS_CANT_RETRIEVE_INSTANCE); + ERR(ERROR_DS_CODE_INCONSISTENCY); + ERR(ERROR_DS_DATABASE_ERROR); + ERR(ERROR_DS_GOVERNSID_MISSING); + ERR(ERROR_DS_MISSING_EXPECTED_ATT); + ERR(ERROR_DS_NCNAME_MISSING_CR_REF); + ERR(ERROR_DS_SECURITY_CHECKING_ERROR); + ERR(ERROR_DS_SCHEMA_NOT_LOADED); + ERR(ERROR_DS_SCHEMA_ALLOC_FAILED); + ERR(ERROR_DS_ATT_SCHEMA_REQ_SYNTAX); + ERR(ERROR_DS_GCVERIFY_ERROR); + ERR(ERROR_DS_DRA_SCHEMA_MISMATCH); + ERR(ERROR_DS_CANT_FIND_DSA_OBJ); + ERR(ERROR_DS_CANT_FIND_EXPECTED_NC); + ERR(ERROR_DS_CANT_FIND_NC_IN_CACHE); + ERR(ERROR_DS_CANT_RETRIEVE_CHILD); + ERR(ERROR_DS_SECURITY_ILLEGAL_MODIFY); + ERR(ERROR_DS_CANT_REPLACE_HIDDEN_REC); + ERR(ERROR_DS_BAD_HIERARCHY_FILE); + ERR(ERROR_DS_BUILD_HIERARCHY_TABLE_FAILED); + ERR(ERROR_DS_CONFIG_PARAM_MISSING); + ERR(ERROR_DS_COUNTING_AB_INDICES_FAILED); + ERR(ERROR_DS_HIERARCHY_TABLE_MALLOC_FAILED); + ERR(ERROR_DS_INTERNAL_FAILURE); + ERR(ERROR_DS_UNKNOWN_ERROR); + ERR(ERROR_DS_ROOT_REQUIRES_CLASS_TOP); + ERR(ERROR_DS_REFUSING_FSMO_ROLES); + ERR(ERROR_DS_MISSING_FSMO_SETTINGS); + ERR(ERROR_DS_UNABLE_TO_SURRENDER_ROLES); + ERR(ERROR_DS_DRA_GENERIC); + ERR(ERROR_DS_DRA_INVALID_PARAMETER); + ERR(ERROR_DS_DRA_BUSY); + ERR(ERROR_DS_DRA_BAD_DN); + ERR(ERROR_DS_DRA_BAD_NC); + ERR(ERROR_DS_DRA_DN_EXISTS); + ERR(ERROR_DS_DRA_INTERNAL_ERROR); + ERR(ERROR_DS_DRA_INCONSISTENT_DIT); + ERR(ERROR_DS_DRA_CONNECTION_FAILED); + ERR(ERROR_DS_DRA_BAD_INSTANCE_TYPE); + ERR(ERROR_DS_DRA_OUT_OF_MEM); + ERR(ERROR_DS_DRA_MAIL_PROBLEM); + ERR(ERROR_DS_DRA_REF_ALREADY_EXISTS); + ERR(ERROR_DS_DRA_REF_NOT_FOUND); + ERR(ERROR_DS_DRA_OBJ_IS_REP_SOURCE); + ERR(ERROR_DS_DRA_DB_ERROR); + ERR(ERROR_DS_DRA_NO_REPLICA); + ERR(ERROR_DS_DRA_ACCESS_DENIED); + ERR(ERROR_DS_DRA_NOT_SUPPORTED); + ERR(ERROR_DS_DRA_RPC_CANCELLED); + ERR(ERROR_DS_DRA_SOURCE_DISABLED); + ERR(ERROR_DS_DRA_SINK_DISABLED); + ERR(ERROR_DS_DRA_NAME_COLLISION); + ERR(ERROR_DS_DRA_SOURCE_REINSTALLED); + ERR(ERROR_DS_DRA_MISSING_PARENT); + ERR(ERROR_DS_DRA_PREEMPTED); + ERR(ERROR_DS_DRA_ABANDON_SYNC); + ERR(ERROR_DS_DRA_SHUTDOWN); + ERR(ERROR_DS_DRA_INCOMPATIBLE_PARTIAL_SET); + ERR(ERROR_DS_DRA_SOURCE_IS_PARTIAL_REPLICA); + ERR(ERROR_DS_DRA_EXTN_CONNECTION_FAILED); + ERR(ERROR_DS_INSTALL_SCHEMA_MISMATCH); + ERR(ERROR_DS_DUP_LINK_ID); + ERR(ERROR_DS_NAME_ERROR_RESOLVING); + ERR(ERROR_DS_NAME_ERROR_NOT_FOUND); + ERR(ERROR_DS_NAME_ERROR_NOT_UNIQUE); + ERR(ERROR_DS_NAME_ERROR_NO_MAPPING); + ERR(ERROR_DS_NAME_ERROR_DOMAIN_ONLY); + ERR(ERROR_DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING); + ERR(ERROR_DS_CONSTRUCTED_ATT_MOD); + ERR(ERROR_DS_WRONG_OM_OBJ_CLASS); + ERR(ERROR_DS_DRA_REPL_PENDING); + ERR(ERROR_DS_DS_REQUIRED); + ERR(ERROR_DS_INVALID_LDAP_DISPLAY_NAME); + ERR(ERROR_DS_NON_BASE_SEARCH); + ERR(ERROR_DS_CANT_RETRIEVE_ATTS); + ERR(ERROR_DS_BACKLINK_WITHOUT_LINK); + ERR(ERROR_DS_EPOCH_MISMATCH); + ERR(ERROR_DS_SRC_NAME_MISMATCH); + ERR(ERROR_DS_SRC_AND_DST_NC_IDENTICAL); + ERR(ERROR_DS_DST_NC_MISMATCH); + ERR(ERROR_DS_NOT_AUTHORITIVE_FOR_DST_NC); + ERR(ERROR_DS_SRC_GUID_MISMATCH); + ERR(ERROR_DS_CANT_MOVE_DELETED_OBJECT); + ERR(ERROR_DS_PDC_OPERATION_IN_PROGRESS); + ERR(ERROR_DS_CROSS_DOMAIN_CLEANUP_REQD); + ERR(ERROR_DS_ILLEGAL_XDOM_MOVE_OPERATION); + ERR(ERROR_DS_CANT_WITH_ACCT_GROUP_MEMBERSHPS); + ERR(ERROR_DS_NC_MUST_HAVE_NC_PARENT); + ERR(ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE); + ERR(ERROR_DS_DST_DOMAIN_NOT_NATIVE); + ERR(ERROR_DS_MISSING_INFRASTRUCTURE_CONTAINER); + ERR(ERROR_DS_CANT_MOVE_ACCOUNT_GROUP); + ERR(ERROR_DS_CANT_MOVE_RESOURCE_GROUP); + ERR(ERROR_DS_INVALID_SEARCH_FLAG); + ERR(ERROR_DS_NO_TREE_DELETE_ABOVE_NC); + ERR(ERROR_DS_COULDNT_LOCK_TREE_FOR_DELETE); + ERR(ERROR_DS_COULDNT_IDENTIFY_OBJECTS_FOR_TREE_DELETE); + ERR(ERROR_DS_SAM_INIT_FAILURE); + ERR(ERROR_DS_SENSITIVE_GROUP_VIOLATION); + ERR(ERROR_DS_CANT_MOD_PRIMARYGROUPID); + ERR(ERROR_DS_ILLEGAL_BASE_SCHEMA_MOD); + ERR(ERROR_DS_NONSAFE_SCHEMA_CHANGE); + ERR(ERROR_DS_SCHEMA_UPDATE_DISALLOWED); + ERR(ERROR_DS_CANT_CREATE_UNDER_SCHEMA); + ERR(ERROR_DS_INSTALL_NO_SRC_SCH_VERSION); + ERR(ERROR_DS_INSTALL_NO_SCH_VERSION_IN_INIFILE); + ERR(ERROR_DS_INVALID_GROUP_TYPE); + ERR(ERROR_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN); + ERR(ERROR_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN); + ERR(ERROR_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER); + ERR(ERROR_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER); + ERR(ERROR_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER); + ERR(ERROR_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER); + ERR(ERROR_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER); + ERR(ERROR_DS_HAVE_PRIMARY_MEMBERS); + ERR(ERROR_DS_STRING_SD_CONVERSION_FAILED); + ERR(ERROR_DS_NAMING_MASTER_GC); + ERR(ERROR_DS_DNS_LOOKUP_FAILURE); + ERR(ERROR_DS_COULDNT_UPDATE_SPNS); + ERR(ERROR_DS_CANT_RETRIEVE_SD); + ERR(ERROR_DS_KEY_NOT_UNIQUE); + ERR(ERROR_DS_WRONG_LINKED_ATT_SYNTAX); + ERR(ERROR_DS_SAM_NEED_BOOTKEY_PASSWORD); + ERR(ERROR_DS_SAM_NEED_BOOTKEY_FLOPPY); + ERR(ERROR_DS_CANT_START); + ERR(ERROR_DS_INIT_FAILURE); + ERR(ERROR_DS_NO_PKT_PRIVACY_ON_CONNECTION); + ERR(ERROR_DS_SOURCE_DOMAIN_IN_FOREST); + ERR(ERROR_DS_DESTINATION_DOMAIN_NOT_IN_FOREST); + ERR(ERROR_DS_DESTINATION_AUDITING_NOT_ENABLED); + ERR(ERROR_DS_CANT_FIND_DC_FOR_SRC_DOMAIN); + ERR(ERROR_DS_SRC_OBJ_NOT_GROUP_OR_USER); + ERR(ERROR_DS_SRC_SID_EXISTS_IN_FOREST); + ERR(ERROR_DS_SRC_AND_DST_OBJECT_CLASS_MISMATCH); + ERR(ERROR_SAM_INIT_FAILURE); + ERR(ERROR_DS_DRA_SCHEMA_INFO_SHIP); + ERR(ERROR_DS_DRA_SCHEMA_CONFLICT); + ERR(ERROR_DS_DRA_EARLIER_SCHEMA_CONFLICT); + ERR(ERROR_DS_DRA_OBJ_NC_MISMATCH); + ERR(ERROR_DS_NC_STILL_HAS_DSAS); + ERR(ERROR_DS_GC_REQUIRED); + ERR(ERROR_DS_LOCAL_MEMBER_OF_LOCAL_ONLY); + ERR(ERROR_DS_NO_FPO_IN_UNIVERSAL_GROUPS); + ERR(ERROR_DS_CANT_ADD_TO_GC); + ERR(ERROR_DS_NO_CHECKPOINT_WITH_PDC); + ERR(ERROR_DS_SOURCE_AUDITING_NOT_ENABLED); + ERR(ERROR_DS_CANT_CREATE_IN_NONDOMAIN_NC); + ERR(ERROR_DS_INVALID_NAME_FOR_SPN); + ERR(ERROR_DS_FILTER_USES_CONTRUCTED_ATTRS); + ERR(ERROR_DS_UNICODEPWD_NOT_IN_QUOTES); + ERR(ERROR_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED); + ERR(ERROR_DS_MUST_BE_RUN_ON_DST_DC); + ERR(ERROR_DS_SRC_DC_MUST_BE_SP4_OR_GREATER); + ERR(ERROR_DS_CANT_TREE_DELETE_CRITICAL_OBJ); + ERR(ERROR_DS_INIT_FAILURE_CONSOLE); + ERR(ERROR_DS_SAM_INIT_FAILURE_CONSOLE); + ERR(ERROR_DS_FOREST_VERSION_TOO_HIGH); + ERR(ERROR_DS_DOMAIN_VERSION_TOO_HIGH); + ERR(ERROR_DS_FOREST_VERSION_TOO_LOW); + ERR(ERROR_DS_DOMAIN_VERSION_TOO_LOW); + ERR(ERROR_DS_INCOMPATIBLE_VERSION); + ERR(ERROR_DS_LOW_DSA_VERSION); + ERR(ERROR_DS_NO_BEHAVIOR_VERSION_IN_MIXEDDOMAIN); + ERR(ERROR_DS_NOT_SUPPORTED_SORT_ORDER); + ERR(ERROR_DS_NAME_NOT_UNIQUE); + ERR(ERROR_DS_MACHINE_ACCOUNT_CREATED_PRENT4); + ERR(ERROR_DS_OUT_OF_VERSION_STORE); + ERR(ERROR_DS_INCOMPATIBLE_CONTROLS_USED); + ERR(ERROR_DS_NO_REF_DOMAIN); + ERR(ERROR_DS_RESERVED_LINK_ID); + ERR(ERROR_DS_LINK_ID_NOT_AVAILABLE); + ERR(ERROR_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER); + ERR(ERROR_DS_MODIFYDN_DISALLOWED_BY_INSTANCE_TYPE); + ERR(ERROR_DS_NO_OBJECT_MOVE_IN_SCHEMA_NC); + ERR(ERROR_DS_MODIFYDN_DISALLOWED_BY_FLAG); + ERR(ERROR_DS_MODIFYDN_WRONG_GRANDPARENT); + ERR(ERROR_DS_NAME_ERROR_TRUST_REFERRAL); + ERR(ERROR_NOT_SUPPORTED_ON_STANDARD_SERVER); + ERR(ERROR_DS_CANT_ACCESS_REMOTE_PART_OF_AD); + ERR(ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE_V2); + ERR(ERROR_DS_THREAD_LIMIT_EXCEEDED); + ERR(ERROR_DS_NOT_CLOSEST); + ERR(ERROR_DS_CANT_DERIVE_SPN_WITHOUT_SERVER_REF); + ERR(ERROR_DS_SINGLE_USER_MODE_FAILED); + ERR(ERROR_DS_NTDSCRIPT_SYNTAX_ERROR); + ERR(ERROR_DS_NTDSCRIPT_PROCESS_ERROR); + ERR(ERROR_DS_DIFFERENT_REPL_EPOCHS); + ERR(ERROR_DS_DRS_EXTENSIONS_CHANGED); + ERR(ERROR_DS_REPLICA_SET_CHANGE_NOT_ALLOWED_ON_DISABLED_CR); + ERR(ERROR_DS_NO_MSDS_INTID); + ERR(ERROR_DS_DUP_MSDS_INTID); + ERR(ERROR_DS_EXISTS_IN_RDNATTID); + ERR(ERROR_DS_AUTHORIZATION_FAILED); + ERR(ERROR_DS_INVALID_SCRIPT); + ERR(ERROR_DS_REMOTE_CROSSREF_OP_FAILED); + ERR(ERROR_DS_CROSS_REF_BUSY); + ERR(ERROR_DS_CANT_DERIVE_SPN_FOR_DELETED_DOMAIN); + ERR(ERROR_DS_CANT_DEMOTE_WITH_WRITEABLE_NC); + ERR(ERROR_DS_DUPLICATE_ID_FOUND); + ERR(ERROR_DS_INSUFFICIENT_ATTR_TO_CREATE_OBJECT); + ERR(ERROR_DS_GROUP_CONVERSION_ERROR); + ERR(ERROR_DS_CANT_MOVE_APP_BASIC_GROUP); + ERR(ERROR_DS_CANT_MOVE_APP_QUERY_GROUP); + ERR(ERROR_DS_ROLE_NOT_VERIFIED); + ERR(ERROR_DS_WKO_CONTAINER_CANNOT_BE_SPECIAL); + ERR(ERROR_DS_DOMAIN_RENAME_IN_PROGRESS); + ERR(ERROR_DS_EXISTING_AD_CHILD_NC); + ERR(ERROR_DS_REPL_LIFETIME_EXCEEDED); + ERR(ERROR_DS_DISALLOWED_IN_SYSTEM_CONTAINER); + ERR(ERROR_DS_LDAP_SEND_QUEUE_FULL); + ERR(ERROR_DS_DRA_OUT_SCHEDULE_WINDOW); + ERR(ERROR_DS_POLICY_NOT_KNOWN); + ERR(ERROR_NO_SITE_SETTINGS_OBJECT); + ERR(ERROR_NO_SECRETS); + ERR(ERROR_NO_WRITABLE_DC_FOUND); + ERR(ERROR_DS_NO_SERVER_OBJECT); + ERR(ERROR_DS_NO_NTDSA_OBJECT); + ERR(ERROR_DS_NON_ASQ_SEARCH); + ERR(ERROR_DS_AUDIT_FAILURE); + ERR(ERROR_DS_INVALID_SEARCH_FLAG_SUBTREE); + ERR(ERROR_DS_INVALID_SEARCH_FLAG_TUPLE); + ERR(ERROR_DS_HIERARCHY_TABLE_TOO_DEEP); + ERR(ERROR_DS_DRA_CORRUPT_UTD_VECTOR); + ERR(ERROR_DS_DRA_SECRETS_DENIED); + ERR(ERROR_DS_RESERVED_MAPI_ID); + ERR(ERROR_DS_MAPI_ID_NOT_AVAILABLE); + ERR(ERROR_DS_DRA_MISSING_KRBTGT_SECRET); + ERR(ERROR_DS_DOMAIN_NAME_EXISTS_IN_FOREST); + ERR(ERROR_DS_FLAT_NAME_EXISTS_IN_FOREST); + ERR(ERROR_INVALID_USER_PRINCIPAL_NAME); + ERR(ERROR_DS_OID_MAPPED_GROUP_CANT_HAVE_MEMBERS); + ERR(ERROR_DS_OID_NOT_FOUND); + ERR(ERROR_DS_DRA_RECYCLED_TARGET); + ERR(ERROR_DS_DISALLOWED_NC_REDIRECT); + ERR(ERROR_DS_HIGH_ADLDS_FFL); + ERR(ERROR_DS_HIGH_DSA_VERSION); + ERR(ERROR_DS_LOW_ADLDS_FFL); + ERR(ERROR_DOMAIN_SID_SAME_AS_LOCAL_WORKSTATION); + ERR(ERROR_DS_UNDELETE_SAM_VALIDATION_FAILED); + ERR(ERROR_INCORRECT_ACCOUNT_TYPE); + ERR(ERROR_DS_SPN_VALUE_NOT_UNIQUE_IN_FOREST); + ERR(ERROR_DS_UPN_VALUE_NOT_UNIQUE_IN_FOREST); + /*ERR(ERROR_DS_MISSING_FOREST_TRUST); + ERR(ERROR_DS_VALUE_KEY_NOT_UNIQUE);*/ + ERR(ERROR_IPSEC_QM_POLICY_EXISTS); + ERR(ERROR_IPSEC_QM_POLICY_NOT_FOUND); + ERR(ERROR_IPSEC_QM_POLICY_IN_USE); + ERR(ERROR_IPSEC_MM_POLICY_EXISTS); + ERR(ERROR_IPSEC_MM_POLICY_NOT_FOUND); + ERR(ERROR_IPSEC_MM_POLICY_IN_USE); + ERR(ERROR_IPSEC_MM_FILTER_EXISTS); + ERR(ERROR_IPSEC_MM_FILTER_NOT_FOUND); + ERR(ERROR_IPSEC_TRANSPORT_FILTER_EXISTS); + ERR(ERROR_IPSEC_TRANSPORT_FILTER_NOT_FOUND); + ERR(ERROR_IPSEC_MM_AUTH_EXISTS); + ERR(ERROR_IPSEC_MM_AUTH_NOT_FOUND); + ERR(ERROR_IPSEC_MM_AUTH_IN_USE); + ERR(ERROR_IPSEC_DEFAULT_MM_POLICY_NOT_FOUND); + ERR(ERROR_IPSEC_DEFAULT_MM_AUTH_NOT_FOUND); + ERR(ERROR_IPSEC_DEFAULT_QM_POLICY_NOT_FOUND); + ERR(ERROR_IPSEC_TUNNEL_FILTER_EXISTS); + ERR(ERROR_IPSEC_TUNNEL_FILTER_NOT_FOUND); + ERR(ERROR_IPSEC_MM_FILTER_PENDING_DELETION); + ERR(ERROR_IPSEC_TRANSPORT_FILTER_PENDING_DELETION); + ERR(ERROR_IPSEC_TUNNEL_FILTER_PENDING_DELETION); + ERR(ERROR_IPSEC_MM_POLICY_PENDING_DELETION); + ERR(ERROR_IPSEC_MM_AUTH_PENDING_DELETION); + ERR(ERROR_IPSEC_QM_POLICY_PENDING_DELETION); + ERR(ERROR_IPSEC_IKE_NEG_STATUS_BEGIN); + ERR(ERROR_IPSEC_IKE_AUTH_FAIL); + ERR(ERROR_IPSEC_IKE_ATTRIB_FAIL); + ERR(ERROR_IPSEC_IKE_NEGOTIATION_PENDING); + ERR(ERROR_IPSEC_IKE_GENERAL_PROCESSING_ERROR); + ERR(ERROR_IPSEC_IKE_TIMED_OUT); + ERR(ERROR_IPSEC_IKE_NO_CERT); + ERR(ERROR_IPSEC_IKE_SA_DELETED); + ERR(ERROR_IPSEC_IKE_SA_REAPED); + ERR(ERROR_IPSEC_IKE_MM_ACQUIRE_DROP); + ERR(ERROR_IPSEC_IKE_QM_ACQUIRE_DROP); + ERR(ERROR_IPSEC_IKE_QUEUE_DROP_MM); + ERR(ERROR_IPSEC_IKE_QUEUE_DROP_NO_MM); + ERR(ERROR_IPSEC_IKE_DROP_NO_RESPONSE); + ERR(ERROR_IPSEC_IKE_MM_DELAY_DROP); + ERR(ERROR_IPSEC_IKE_QM_DELAY_DROP); + ERR(ERROR_IPSEC_IKE_ERROR); + ERR(ERROR_IPSEC_IKE_CRL_FAILED); + ERR(ERROR_IPSEC_IKE_INVALID_KEY_USAGE); + ERR(ERROR_IPSEC_IKE_INVALID_CERT_TYPE); + ERR(ERROR_IPSEC_IKE_NO_PRIVATE_KEY); + ERR(ERROR_IPSEC_IKE_SIMULTANEOUS_REKEY); + ERR(ERROR_IPSEC_IKE_DH_FAIL); + ERR(ERROR_IPSEC_IKE_CRITICAL_PAYLOAD_NOT_RECOGNIZED); + ERR(ERROR_IPSEC_IKE_INVALID_HEADER); + ERR(ERROR_IPSEC_IKE_NO_POLICY); + ERR(ERROR_IPSEC_IKE_INVALID_SIGNATURE); + ERR(ERROR_IPSEC_IKE_KERBEROS_ERROR); + ERR(ERROR_IPSEC_IKE_NO_PUBLIC_KEY); + ERR(ERROR_IPSEC_IKE_PROCESS_ERR); + ERR(ERROR_IPSEC_IKE_PROCESS_ERR_SA); + ERR(ERROR_IPSEC_IKE_PROCESS_ERR_PROP); + ERR(ERROR_IPSEC_IKE_PROCESS_ERR_TRANS); + ERR(ERROR_IPSEC_IKE_PROCESS_ERR_KE); + ERR(ERROR_IPSEC_IKE_PROCESS_ERR_ID); + ERR(ERROR_IPSEC_IKE_PROCESS_ERR_CERT); + ERR(ERROR_IPSEC_IKE_PROCESS_ERR_CERT_REQ); + ERR(ERROR_IPSEC_IKE_PROCESS_ERR_HASH); + ERR(ERROR_IPSEC_IKE_PROCESS_ERR_SIG); + ERR(ERROR_IPSEC_IKE_PROCESS_ERR_NONCE); + ERR(ERROR_IPSEC_IKE_PROCESS_ERR_NOTIFY); + ERR(ERROR_IPSEC_IKE_PROCESS_ERR_DELETE); + ERR(ERROR_IPSEC_IKE_PROCESS_ERR_VENDOR); + ERR(ERROR_IPSEC_IKE_INVALID_PAYLOAD); + ERR(ERROR_IPSEC_IKE_LOAD_SOFT_SA); + ERR(ERROR_IPSEC_IKE_SOFT_SA_TORN_DOWN); + ERR(ERROR_IPSEC_IKE_INVALID_COOKIE); + ERR(ERROR_IPSEC_IKE_NO_PEER_CERT); + ERR(ERROR_IPSEC_IKE_PEER_CRL_FAILED); + ERR(ERROR_IPSEC_IKE_POLICY_CHANGE); + ERR(ERROR_IPSEC_IKE_NO_MM_POLICY); + ERR(ERROR_IPSEC_IKE_NOTCBPRIV); + ERR(ERROR_IPSEC_IKE_SECLOADFAIL); + ERR(ERROR_IPSEC_IKE_FAILSSPINIT); + ERR(ERROR_IPSEC_IKE_FAILQUERYSSP); + ERR(ERROR_IPSEC_IKE_SRVACQFAIL); + ERR(ERROR_IPSEC_IKE_SRVQUERYCRED); + ERR(ERROR_IPSEC_IKE_GETSPIFAIL); + ERR(ERROR_IPSEC_IKE_INVALID_FILTER); + ERR(ERROR_IPSEC_IKE_OUT_OF_MEMORY); + ERR(ERROR_IPSEC_IKE_ADD_UPDATE_KEY_FAILED); + ERR(ERROR_IPSEC_IKE_INVALID_POLICY); + ERR(ERROR_IPSEC_IKE_UNKNOWN_DOI); + ERR(ERROR_IPSEC_IKE_INVALID_SITUATION); + ERR(ERROR_IPSEC_IKE_DH_FAILURE); + ERR(ERROR_IPSEC_IKE_INVALID_GROUP); + ERR(ERROR_IPSEC_IKE_ENCRYPT); + ERR(ERROR_IPSEC_IKE_DECRYPT); + ERR(ERROR_IPSEC_IKE_POLICY_MATCH); + ERR(ERROR_IPSEC_IKE_UNSUPPORTED_ID); + ERR(ERROR_IPSEC_IKE_INVALID_HASH); + ERR(ERROR_IPSEC_IKE_INVALID_HASH_ALG); + ERR(ERROR_IPSEC_IKE_INVALID_HASH_SIZE); + ERR(ERROR_IPSEC_IKE_INVALID_ENCRYPT_ALG); + ERR(ERROR_IPSEC_IKE_INVALID_AUTH_ALG); + ERR(ERROR_IPSEC_IKE_INVALID_SIG); + ERR(ERROR_IPSEC_IKE_LOAD_FAILED); + ERR(ERROR_IPSEC_IKE_RPC_DELETE); + ERR(ERROR_IPSEC_IKE_BENIGN_REINIT); + ERR(ERROR_IPSEC_IKE_INVALID_RESPONDER_LIFETIME_NOTIFY); + ERR(ERROR_IPSEC_IKE_INVALID_MAJOR_VERSION); + ERR(ERROR_IPSEC_IKE_INVALID_CERT_KEYLEN); + ERR(ERROR_IPSEC_IKE_MM_LIMIT); + ERR(ERROR_IPSEC_IKE_NEGOTIATION_DISABLED); + ERR(ERROR_IPSEC_IKE_QM_LIMIT); + ERR(ERROR_IPSEC_IKE_MM_EXPIRED); + ERR(ERROR_IPSEC_IKE_PEER_MM_ASSUMED_INVALID); + ERR(ERROR_IPSEC_IKE_CERT_CHAIN_POLICY_MISMATCH); + ERR(ERROR_IPSEC_IKE_UNEXPECTED_MESSAGE_ID); + ERR(ERROR_IPSEC_IKE_INVALID_AUTH_PAYLOAD); + ERR(ERROR_IPSEC_IKE_DOS_COOKIE_SENT); + ERR(ERROR_IPSEC_IKE_SHUTTING_DOWN); + ERR(ERROR_IPSEC_IKE_CGA_AUTH_FAILED); + ERR(ERROR_IPSEC_IKE_PROCESS_ERR_NATOA); + ERR(ERROR_IPSEC_IKE_INVALID_MM_FOR_QM); + ERR(ERROR_IPSEC_IKE_QM_EXPIRED); + ERR(ERROR_IPSEC_IKE_TOO_MANY_FILTERS); + ERR(ERROR_IPSEC_IKE_NEG_STATUS_END); + ERR(ERROR_IPSEC_IKE_KILL_DUMMY_NAP_TUNNEL); + ERR(ERROR_IPSEC_IKE_INNER_IP_ASSIGNMENT_FAILURE); + ERR(ERROR_IPSEC_IKE_REQUIRE_CP_PAYLOAD_MISSING); + ERR(ERROR_IPSEC_KEY_MODULE_IMPERSONATION_NEGOTIATION_PENDING); + ERR(ERROR_IPSEC_IKE_COEXISTENCE_SUPPRESS); + ERR(ERROR_IPSEC_IKE_RATELIMIT_DROP); + ERR(ERROR_IPSEC_IKE_PEER_DOESNT_SUPPORT_MOBIKE); + ERR(ERROR_IPSEC_IKE_AUTHORIZATION_FAILURE); + ERR(ERROR_IPSEC_IKE_STRONG_CRED_AUTHORIZATION_FAILURE); + ERR(ERROR_IPSEC_IKE_AUTHORIZATION_FAILURE_WITH_OPTIONAL_RETRY); + ERR(ERROR_IPSEC_IKE_STRONG_CRED_AUTHORIZATION_AND_CERTMAP_FAILURE); + ERR(ERROR_IPSEC_IKE_NEG_STATUS_EXTENDED_END); + ERR(ERROR_IPSEC_BAD_SPI); + ERR(ERROR_IPSEC_SA_LIFETIME_EXPIRED); + ERR(ERROR_IPSEC_WRONG_SA); + ERR(ERROR_IPSEC_REPLAY_CHECK_FAILED); + ERR(ERROR_IPSEC_INVALID_PACKET); + ERR(ERROR_IPSEC_INTEGRITY_CHECK_FAILED); + ERR(ERROR_IPSEC_CLEAR_TEXT_DROP); + ERR(ERROR_IPSEC_AUTH_FIREWALL_DROP); + ERR(ERROR_IPSEC_THROTTLE_DROP); + ERR(ERROR_IPSEC_DOSP_BLOCK); + ERR(ERROR_IPSEC_DOSP_RECEIVED_MULTICAST); + ERR(ERROR_IPSEC_DOSP_INVALID_PACKET); + ERR(ERROR_IPSEC_DOSP_STATE_LOOKUP_FAILED); + ERR(ERROR_IPSEC_DOSP_MAX_ENTRIES); + ERR(ERROR_IPSEC_DOSP_KEYMOD_NOT_ALLOWED); + ERR(ERROR_IPSEC_DOSP_NOT_INSTALLED); + ERR(ERROR_IPSEC_DOSP_MAX_PER_IP_RATELIMIT_QUEUES); + ERR(ERROR_SXS_SECTION_NOT_FOUND); + ERR(ERROR_SXS_CANT_GEN_ACTCTX); + ERR(ERROR_SXS_INVALID_ACTCTXDATA_FORMAT); + ERR(ERROR_SXS_ASSEMBLY_NOT_FOUND); + ERR(ERROR_SXS_MANIFEST_FORMAT_ERROR); + ERR(ERROR_SXS_MANIFEST_PARSE_ERROR); + ERR(ERROR_SXS_ACTIVATION_CONTEXT_DISABLED); + ERR(ERROR_SXS_KEY_NOT_FOUND); + ERR(ERROR_SXS_VERSION_CONFLICT); + ERR(ERROR_SXS_WRONG_SECTION_TYPE); + ERR(ERROR_SXS_THREAD_QUERIES_DISABLED); + ERR(ERROR_SXS_PROCESS_DEFAULT_ALREADY_SET); + ERR(ERROR_SXS_UNKNOWN_ENCODING_GROUP); + ERR(ERROR_SXS_UNKNOWN_ENCODING); + ERR(ERROR_SXS_INVALID_XML_NAMESPACE_URI); + ERR(ERROR_SXS_ROOT_MANIFEST_DEPENDENCY_NOT_INSTALLED); + ERR(ERROR_SXS_LEAF_MANIFEST_DEPENDENCY_NOT_INSTALLED); + ERR(ERROR_SXS_INVALID_ASSEMBLY_IDENTITY_ATTRIBUTE); + ERR(ERROR_SXS_MANIFEST_MISSING_REQUIRED_DEFAULT_NAMESPACE); + ERR(ERROR_SXS_MANIFEST_INVALID_REQUIRED_DEFAULT_NAMESPACE); + ERR(ERROR_SXS_PRIVATE_MANIFEST_CROSS_PATH_WITH_REPARSE_POINT); + ERR(ERROR_SXS_DUPLICATE_DLL_NAME); + ERR(ERROR_SXS_DUPLICATE_WINDOWCLASS_NAME); + ERR(ERROR_SXS_DUPLICATE_CLSID); + ERR(ERROR_SXS_DUPLICATE_IID); + ERR(ERROR_SXS_DUPLICATE_TLBID); + ERR(ERROR_SXS_DUPLICATE_PROGID); + ERR(ERROR_SXS_DUPLICATE_ASSEMBLY_NAME); + ERR(ERROR_SXS_FILE_HASH_MISMATCH); + ERR(ERROR_SXS_POLICY_PARSE_ERROR); + ERR(ERROR_SXS_XML_E_MISSINGQUOTE); + ERR(ERROR_SXS_XML_E_COMMENTSYNTAX); + ERR(ERROR_SXS_XML_E_BADSTARTNAMECHAR); + ERR(ERROR_SXS_XML_E_BADNAMECHAR); + ERR(ERROR_SXS_XML_E_BADCHARINSTRING); + ERR(ERROR_SXS_XML_E_XMLDECLSYNTAX); + ERR(ERROR_SXS_XML_E_BADCHARDATA); + ERR(ERROR_SXS_XML_E_MISSINGWHITESPACE); + ERR(ERROR_SXS_XML_E_EXPECTINGTAGEND); + ERR(ERROR_SXS_XML_E_MISSINGSEMICOLON); + ERR(ERROR_SXS_XML_E_UNBALANCEDPAREN); + ERR(ERROR_SXS_XML_E_INTERNALERROR); + ERR(ERROR_SXS_XML_E_UNEXPECTED_WHITESPACE); + ERR(ERROR_SXS_XML_E_INCOMPLETE_ENCODING); + ERR(ERROR_SXS_XML_E_MISSING_PAREN); + ERR(ERROR_SXS_XML_E_EXPECTINGCLOSEQUOTE); + ERR(ERROR_SXS_XML_E_MULTIPLE_COLONS); + ERR(ERROR_SXS_XML_E_INVALID_DECIMAL); + ERR(ERROR_SXS_XML_E_INVALID_HEXIDECIMAL); + ERR(ERROR_SXS_XML_E_INVALID_UNICODE); + ERR(ERROR_SXS_XML_E_WHITESPACEORQUESTIONMARK); + ERR(ERROR_SXS_XML_E_UNEXPECTEDENDTAG); + ERR(ERROR_SXS_XML_E_UNCLOSEDTAG); + ERR(ERROR_SXS_XML_E_DUPLICATEATTRIBUTE); + ERR(ERROR_SXS_XML_E_MULTIPLEROOTS); + ERR(ERROR_SXS_XML_E_INVALIDATROOTLEVEL); + ERR(ERROR_SXS_XML_E_BADXMLDECL); + ERR(ERROR_SXS_XML_E_MISSINGROOT); + ERR(ERROR_SXS_XML_E_UNEXPECTEDEOF); + ERR(ERROR_SXS_XML_E_BADPEREFINSUBSET); + ERR(ERROR_SXS_XML_E_UNCLOSEDSTARTTAG); + ERR(ERROR_SXS_XML_E_UNCLOSEDENDTAG); + ERR(ERROR_SXS_XML_E_UNCLOSEDSTRING); + ERR(ERROR_SXS_XML_E_UNCLOSEDCOMMENT); + ERR(ERROR_SXS_XML_E_UNCLOSEDDECL); + ERR(ERROR_SXS_XML_E_UNCLOSEDCDATA); + ERR(ERROR_SXS_XML_E_RESERVEDNAMESPACE); + ERR(ERROR_SXS_XML_E_INVALIDENCODING); + ERR(ERROR_SXS_XML_E_INVALIDSWITCH); + ERR(ERROR_SXS_XML_E_BADXMLCASE); + ERR(ERROR_SXS_XML_E_INVALID_STANDALONE); + ERR(ERROR_SXS_XML_E_UNEXPECTED_STANDALONE); + ERR(ERROR_SXS_XML_E_INVALID_VERSION); + ERR(ERROR_SXS_XML_E_MISSINGEQUALS); + ERR(ERROR_SXS_PROTECTION_RECOVERY_FAILED); + ERR(ERROR_SXS_PROTECTION_PUBLIC_KEY_TOO_SHORT); + ERR(ERROR_SXS_PROTECTION_CATALOG_NOT_VALID); + ERR(ERROR_SXS_UNTRANSLATABLE_HRESULT); + ERR(ERROR_SXS_PROTECTION_CATALOG_FILE_MISSING); + ERR(ERROR_SXS_MISSING_ASSEMBLY_IDENTITY_ATTRIBUTE); + ERR(ERROR_SXS_INVALID_ASSEMBLY_IDENTITY_ATTRIBUTE_NAME); + ERR(ERROR_SXS_ASSEMBLY_MISSING); + ERR(ERROR_SXS_CORRUPT_ACTIVATION_STACK); + ERR(ERROR_SXS_CORRUPTION); + ERR(ERROR_SXS_EARLY_DEACTIVATION); + ERR(ERROR_SXS_INVALID_DEACTIVATION); + ERR(ERROR_SXS_MULTIPLE_DEACTIVATION); + ERR(ERROR_SXS_PROCESS_TERMINATION_REQUESTED); + ERR(ERROR_SXS_RELEASE_ACTIVATION_CONTEXT); + ERR(ERROR_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY); + ERR(ERROR_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE); + ERR(ERROR_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME); + ERR(ERROR_SXS_IDENTITY_DUPLICATE_ATTRIBUTE); + ERR(ERROR_SXS_IDENTITY_PARSE_ERROR); + ERR(ERROR_MALFORMED_SUBSTITUTION_STRING); + ERR(ERROR_SXS_INCORRECT_PUBLIC_KEY_TOKEN); + ERR(ERROR_UNMAPPED_SUBSTITUTION_STRING); + ERR(ERROR_SXS_ASSEMBLY_NOT_LOCKED); + ERR(ERROR_SXS_COMPONENT_STORE_CORRUPT); + ERR(ERROR_ADVANCED_INSTALLER_FAILED); + ERR(ERROR_XML_ENCODING_MISMATCH); + ERR(ERROR_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT); + ERR(ERROR_SXS_IDENTITIES_DIFFERENT); + ERR(ERROR_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT); + ERR(ERROR_SXS_FILE_NOT_PART_OF_ASSEMBLY); + ERR(ERROR_SXS_MANIFEST_TOO_BIG); + ERR(ERROR_SXS_SETTING_NOT_REGISTERED); + ERR(ERROR_SXS_TRANSACTION_CLOSURE_INCOMPLETE); + ERR(ERROR_SMI_PRIMITIVE_INSTALLER_FAILED); + ERR(ERROR_GENERIC_COMMAND_FAILED); + ERR(ERROR_SXS_FILE_HASH_MISSING); + ERR(ERROR_EVT_INVALID_CHANNEL_PATH); + ERR(ERROR_EVT_INVALID_QUERY); + ERR(ERROR_EVT_PUBLISHER_METADATA_NOT_FOUND); + ERR(ERROR_EVT_EVENT_TEMPLATE_NOT_FOUND); + ERR(ERROR_EVT_INVALID_PUBLISHER_NAME); + ERR(ERROR_EVT_INVALID_EVENT_DATA); + ERR(ERROR_EVT_CHANNEL_NOT_FOUND); + ERR(ERROR_EVT_MALFORMED_XML_TEXT); + ERR(ERROR_EVT_SUBSCRIPTION_TO_DIRECT_CHANNEL); + ERR(ERROR_EVT_CONFIGURATION_ERROR); + ERR(ERROR_EVT_QUERY_RESULT_STALE); + ERR(ERROR_EVT_QUERY_RESULT_INVALID_POSITION); + ERR(ERROR_EVT_NON_VALIDATING_MSXML); + ERR(ERROR_EVT_FILTER_ALREADYSCOPED); + ERR(ERROR_EVT_FILTER_NOTELTSET); + ERR(ERROR_EVT_FILTER_INVARG); + ERR(ERROR_EVT_FILTER_INVTEST); + ERR(ERROR_EVT_FILTER_INVTYPE); + ERR(ERROR_EVT_FILTER_PARSEERR); + ERR(ERROR_EVT_FILTER_UNSUPPORTEDOP); + ERR(ERROR_EVT_FILTER_UNEXPECTEDTOKEN); + ERR(ERROR_EVT_INVALID_OPERATION_OVER_ENABLED_DIRECT_CHANNEL); + ERR(ERROR_EVT_INVALID_CHANNEL_PROPERTY_VALUE); + ERR(ERROR_EVT_INVALID_PUBLISHER_PROPERTY_VALUE); + ERR(ERROR_EVT_CHANNEL_CANNOT_ACTIVATE); + ERR(ERROR_EVT_FILTER_TOO_COMPLEX); + ERR(ERROR_EVT_MESSAGE_NOT_FOUND); + ERR(ERROR_EVT_MESSAGE_ID_NOT_FOUND); + ERR(ERROR_EVT_UNRESOLVED_VALUE_INSERT); + ERR(ERROR_EVT_UNRESOLVED_PARAMETER_INSERT); + ERR(ERROR_EVT_MAX_INSERTS_REACHED); + ERR(ERROR_EVT_EVENT_DEFINITION_NOT_FOUND); + ERR(ERROR_EVT_MESSAGE_LOCALE_NOT_FOUND); + ERR(ERROR_EVT_VERSION_TOO_OLD); + ERR(ERROR_EVT_VERSION_TOO_NEW); + ERR(ERROR_EVT_CANNOT_OPEN_CHANNEL_OF_QUERY); + ERR(ERROR_EVT_PUBLISHER_DISABLED); + ERR(ERROR_EVT_FILTER_OUT_OF_RANGE); + ERR(ERROR_EC_SUBSCRIPTION_CANNOT_ACTIVATE); + ERR(ERROR_EC_LOG_DISABLED); + ERR(ERROR_EC_CIRCULAR_FORWARDING); + ERR(ERROR_EC_CREDSTORE_FULL); + ERR(ERROR_EC_CRED_NOT_FOUND); + ERR(ERROR_EC_NO_ACTIVE_CHANNEL); + ERR(ERROR_MUI_FILE_NOT_FOUND); + ERR(ERROR_MUI_INVALID_FILE); + ERR(ERROR_MUI_INVALID_RC_CONFIG); + ERR(ERROR_MUI_INVALID_LOCALE_NAME); + ERR(ERROR_MUI_INVALID_ULTIMATEFALLBACK_NAME); + ERR(ERROR_MUI_FILE_NOT_LOADED); + ERR(ERROR_RESOURCE_ENUM_USER_STOP); + ERR(ERROR_MUI_INTLSETTINGS_UILANG_NOT_INSTALLED); + ERR(ERROR_MUI_INTLSETTINGS_INVALID_LOCALE_NAME); + ERR(ERROR_MRM_RUNTIME_NO_DEFAULT_OR_NEUTRAL_RESOURCE); + ERR(ERROR_MRM_INVALID_PRICONFIG); + ERR(ERROR_MRM_INVALID_FILE_TYPE); + ERR(ERROR_MRM_UNKNOWN_QUALIFIER); + ERR(ERROR_MRM_INVALID_QUALIFIER_VALUE); + ERR(ERROR_MRM_NO_CANDIDATE); + ERR(ERROR_MRM_NO_MATCH_OR_DEFAULT_CANDIDATE); + ERR(ERROR_MRM_RESOURCE_TYPE_MISMATCH); + ERR(ERROR_MRM_DUPLICATE_MAP_NAME); + ERR(ERROR_MRM_DUPLICATE_ENTRY); + ERR(ERROR_MRM_INVALID_RESOURCE_IDENTIFIER); + ERR(ERROR_MRM_FILEPATH_TOO_LONG); + ERR(ERROR_MRM_UNSUPPORTED_DIRECTORY_TYPE); + ERR(ERROR_MRM_INVALID_PRI_FILE); + ERR(ERROR_MRM_NAMED_RESOURCE_NOT_FOUND); + ERR(ERROR_MRM_MAP_NOT_FOUND); + ERR(ERROR_MRM_UNSUPPORTED_PROFILE_TYPE); + ERR(ERROR_MRM_INVALID_QUALIFIER_OPERATOR); + ERR(ERROR_MRM_INDETERMINATE_QUALIFIER_VALUE); + ERR(ERROR_MRM_AUTOMERGE_ENABLED); + ERR(ERROR_MRM_TOO_MANY_RESOURCES); + ERR(ERROR_MRM_UNSUPPORTED_FILE_TYPE_FOR_MERGE); + ERR(ERROR_MRM_UNSUPPORTED_FILE_TYPE_FOR_LOAD_UNLOAD_PRI_FILE); + ERR(ERROR_MRM_NO_CURRENT_VIEW_ON_THREAD); + ERR(ERROR_DIFFERENT_PROFILE_RESOURCE_MANAGER_EXIST); + ERR(ERROR_OPERATION_NOT_ALLOWED_FROM_SYSTEM_COMPONENT); + ERR(ERROR_MRM_DIRECT_REF_TO_NON_DEFAULT_RESOURCE); + ERR(ERROR_MRM_GENERATION_COUNT_MISMATCH); + ERR(ERROR_MCA_INVALID_CAPABILITIES_STRING); + ERR(ERROR_MCA_INVALID_VCP_VERSION); + ERR(ERROR_MCA_MONITOR_VIOLATES_MCCS_SPECIFICATION); + ERR(ERROR_MCA_MCCS_VERSION_MISMATCH); + ERR(ERROR_MCA_UNSUPPORTED_MCCS_VERSION); + ERR(ERROR_MCA_INTERNAL_ERROR); + ERR(ERROR_MCA_INVALID_TECHNOLOGY_TYPE_RETURNED); + ERR(ERROR_MCA_UNSUPPORTED_COLOR_TEMPERATURE); + ERR(ERROR_AMBIGUOUS_SYSTEM_DEVICE); + ERR(ERROR_SYSTEM_DEVICE_NOT_FOUND); + ERR(ERROR_HASH_NOT_SUPPORTED); + ERR(ERROR_HASH_NOT_PRESENT); + ERR(ERROR_SECONDARY_IC_PROVIDER_NOT_REGISTERED); + ERR(ERROR_GPIO_CLIENT_INFORMATION_INVALID); + ERR(ERROR_GPIO_VERSION_NOT_SUPPORTED); + ERR(ERROR_GPIO_INVALID_REGISTRATION_PACKET); + ERR(ERROR_GPIO_OPERATION_DENIED); + ERR(ERROR_GPIO_INCOMPATIBLE_CONNECT_MODE); + ERR(ERROR_GPIO_INTERRUPT_ALREADY_UNMASKED); + ERR(ERROR_CANNOT_SWITCH_RUNLEVEL); + ERR(ERROR_INVALID_RUNLEVEL_SETTING); + ERR(ERROR_RUNLEVEL_SWITCH_TIMEOUT); + ERR(ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT); + ERR(ERROR_RUNLEVEL_SWITCH_IN_PROGRESS); + ERR(ERROR_SERVICES_FAILED_AUTOSTART); + ERR(ERROR_COM_TASK_STOP_PENDING); + ERR(ERROR_INSTALL_OPEN_PACKAGE_FAILED); + ERR(ERROR_INSTALL_PACKAGE_NOT_FOUND); + ERR(ERROR_INSTALL_INVALID_PACKAGE); + ERR(ERROR_INSTALL_RESOLVE_DEPENDENCY_FAILED); + ERR(ERROR_INSTALL_OUT_OF_DISK_SPACE); + ERR(ERROR_INSTALL_NETWORK_FAILURE); + ERR(ERROR_INSTALL_REGISTRATION_FAILURE); + ERR(ERROR_INSTALL_DEREGISTRATION_FAILURE); + ERR(ERROR_INSTALL_CANCEL); + ERR(ERROR_INSTALL_FAILED); + ERR(ERROR_REMOVE_FAILED); + ERR(ERROR_PACKAGE_ALREADY_EXISTS); + ERR(ERROR_NEEDS_REMEDIATION); + ERR(ERROR_INSTALL_PREREQUISITE_FAILED); + ERR(ERROR_PACKAGE_REPOSITORY_CORRUPTED); + ERR(ERROR_INSTALL_POLICY_FAILURE); + ERR(ERROR_PACKAGE_UPDATING); + ERR(ERROR_DEPLOYMENT_BLOCKED_BY_POLICY); + ERR(ERROR_PACKAGES_IN_USE); + ERR(ERROR_RECOVERY_FILE_CORRUPT); + ERR(ERROR_INVALID_STAGED_SIGNATURE); + ERR(ERROR_DELETING_EXISTING_APPLICATIONDATA_STORE_FAILED); + ERR(ERROR_INSTALL_PACKAGE_DOWNGRADE); + ERR(ERROR_SYSTEM_NEEDS_REMEDIATION); + ERR(ERROR_APPX_INTEGRITY_FAILURE_CLR_NGEN); + ERR(ERROR_RESILIENCY_FILE_CORRUPT); + ERR(ERROR_INSTALL_FIREWALL_SERVICE_NOT_RUNNING); + /*ERR(ERROR_PACKAGE_MOVE_FAILED); + ERR(ERROR_INSTALL_VOLUME_NOT_EMPTY); + ERR(ERROR_INSTALL_VOLUME_OFFLINE); + ERR(ERROR_INSTALL_VOLUME_CORRUPT); + ERR(ERROR_NEEDS_REGISTRATION); + ERR(ERROR_INSTALL_WRONG_PROCESSOR_ARCHITECTURE); + ERR(ERROR_DEV_SIDELOAD_LIMIT_EXCEEDED);*/ + ERR(ERROR_STATE_LOAD_STORE_FAILED); + ERR(ERROR_STATE_GET_VERSION_FAILED); + ERR(ERROR_STATE_SET_VERSION_FAILED); + ERR(ERROR_STATE_STRUCTURED_RESET_FAILED); + ERR(ERROR_STATE_OPEN_CONTAINER_FAILED); + ERR(ERROR_STATE_CREATE_CONTAINER_FAILED); + ERR(ERROR_STATE_DELETE_CONTAINER_FAILED); + ERR(ERROR_STATE_READ_SETTING_FAILED); + ERR(ERROR_STATE_WRITE_SETTING_FAILED); + ERR(ERROR_STATE_DELETE_SETTING_FAILED); + ERR(ERROR_STATE_QUERY_SETTING_FAILED); + ERR(ERROR_STATE_READ_COMPOSITE_SETTING_FAILED); + ERR(ERROR_STATE_WRITE_COMPOSITE_SETTING_FAILED); + ERR(ERROR_STATE_ENUMERATE_CONTAINER_FAILED); + ERR(ERROR_STATE_ENUMERATE_SETTINGS_FAILED); + ERR(ERROR_STATE_COMPOSITE_SETTING_VALUE_SIZE_LIMIT_EXCEEDED); + ERR(ERROR_STATE_SETTING_VALUE_SIZE_LIMIT_EXCEEDED); + ERR(ERROR_STATE_SETTING_NAME_SIZE_LIMIT_EXCEEDED); + ERR(ERROR_STATE_CONTAINER_NAME_SIZE_LIMIT_EXCEEDED); + ERR(ERROR_API_UNAVAILABLE); + } + + return L""; +} \ No newline at end of file diff --git a/UnitTests/FileSystemTests.cpp b/UnitTests/FileSystemTests.cpp new file mode 100644 index 000000000..0c068c40a --- /dev/null +++ b/UnitTests/FileSystemTests.cpp @@ -0,0 +1,272 @@ +#include "stdafx.h" +#include "CppUnitTest.h" + +#include +#include +#include +#include + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +#define FILESYSTEM_ROOT L"M:\\" +#define FILESYSTEM_TESTDIR L"M:\\MirrorUnitTests" + +const wchar_t* GetErrorCodeStr(const DWORD errCode); + +#define USE_MIRROR 1 + +namespace UnitTests +{ + TEST_CLASS(FileSystemTests) + { + private: + std::wstring GetTempPathStr() + { + WCHAR path[MAX_PATH]; + + GetTempPathW(MAX_PATH, path); + + return path; + } + + std::wstring CombinePath(const std::wstring &left, const std::wstring &right) + { + if(left.length() == 0) + { + return right; + } + + if(right.length() == 0) + { + return left; + } + + const bool leftHasDelimeter = left[left.length() - 1] == L'\\' || left[left.length() - 1] == L'/'; + const bool rightHasDelimeter = right[right.length() - 1] == L'\\' || right[right.length() - 1] == L'/'; + + if(leftHasDelimeter && rightHasDelimeter) + { + return left.substr(0, left.length() - 1) + right; + } + else if(!leftHasDelimeter && !rightHasDelimeter) + { + return left + L"\\" + right; + } + + return left + right; + } + + std::wstring CreateTestDirectory() + { +#if USE_MIRROR + std::wstring testDir = FILESYSTEM_TESTDIR; +#else + std::wstring testDir = CombinePath(GetTempPathStr(), L"MirrorUnitTests"); +#endif + + if(!PathFileExistsW(testDir.c_str())) + { + if(!CreateDirectoryW(testDir.c_str(), NULL)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CreateDirectoryW for directory \'%s\'.", testDir.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + } + + return testDir; + } + + void DeleteTestDirectory() + { +#if USE_MIRROR + std::wstring testDir = FILESYSTEM_TESTDIR; +#else + std::wstring testDir = CombinePath(GetTempPathStr(), L"MirrorUnitTests"); +#endif + + if(PathFileExistsW(testDir.c_str())) + { + if(!RemoveDirectoryW(testDir.c_str())) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to RemoveDirectoryW for directory \'%s\'.", testDir.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + } + } + + std::wstring FormatString(const wchar_t *str, ...) + { + size_t size = 256; + wchar_t *buf = (wchar_t*)malloc(size * sizeof(wchar_t)); + int written = 0; + + va_list args; + va_start(args, str); + + while((written = _vsnwprintf_s(buf, size - 1, size - 1, str, args)) == -1) + { + size *= 2; + buf = (wchar_t*)realloc(buf, size * sizeof(wchar_t)); + + if(!buf) + { + written = 0; + break; + } + } + + va_end(args); + + assert(static_cast(written) <= size - 2); + + buf[written] = 0; + + std::wstring temp = buf; + + free(buf); + + return temp; + } + + std::wstring GetLastErrorStr(const DWORD lastErr) + { + LPWSTR buffer = NULL; + + // we force english because the error messages are for developers and english is the common language + // developers of all countries use to communicate + DWORD errCode = FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + lastErr, + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + (LPWSTR)&buffer, + 0, + NULL); + + if(errCode == 0) + { + if(GetLastError() == ERROR_RESOURCE_LANG_NOT_FOUND) + { + errCode = FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + lastErr, + 0, + (LPWSTR)&buffer, + 0, + NULL); + } + + if(errCode == 0) + { + return std::wstring(); + } + } + + std::wstring retStr = buffer; + + LocalFree(buffer); + + return retStr; + } + + std::wstring CreateSystemErrorMessage(const std::wstring &msg) + { + const DWORD errCode = GetLastError(); + const std::wstring errCodeMsg = GetLastErrorStr(errCode); + + return FormatString(L"%s\nError code is \'%s\' (%d).\n%s", + msg.c_str(), + GetErrorCodeStr(errCode), + errCode, + errCodeMsg.c_str()); + } + + public: + + TEST_METHOD(TestFileBasicInfo) + { + std::wstring testDir = CreateTestDirectory(); + std::wstring testFile = CombinePath(testDir, L"TestFileBasicInfo.txt"); + + DWORD fileDesiredAccess = + FILE_READ_ATTRIBUTES + | READ_CONTROL + | FILE_WRITE_DATA + | FILE_WRITE_ATTRIBUTES + | FILE_WRITE_EA + | FILE_APPEND_DATA + | SYNCHRONIZE + | STANDARD_RIGHTS_READ + | STANDARD_RIGHTS_WRITE + | STANDARD_RIGHTS_EXECUTE; + + HANDLE handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if(!handle) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + BY_HANDLE_FILE_INFORMATION fileInfo; + ZeroMemory(&fileInfo, sizeof(fileInfo)); + + if(!GetFileInformationByHandle(handle, &fileInfo)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to GetFileInformationByHandle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + FILE_BASIC_INFO basicInfo; + basicInfo.ChangeTime = *(LARGE_INTEGER*)&fileInfo.ftLastWriteTime; + basicInfo.CreationTime = *(LARGE_INTEGER*)&fileInfo.ftCreationTime; + basicInfo.FileAttributes = fileInfo.dwFileAttributes; + basicInfo.LastAccessTime = *(LARGE_INTEGER*)&fileInfo.ftLastAccessTime; + basicInfo.LastWriteTime = *(LARGE_INTEGER*)&fileInfo.ftLastWriteTime; + + basicInfo.CreationTime.QuadPart -= 500; + + if(!SetFileInformationByHandle(handle, FileBasicInfo, &basicInfo, sizeof(basicInfo))) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to SetFileInformationByHandle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + BY_HANDLE_FILE_INFORMATION fileInfo2; + ZeroMemory(&fileInfo2, sizeof(fileInfo2)); + + if(!GetFileInformationByHandle(handle, &fileInfo2)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to GetFileInformationByHandle for modified file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + Assert::IsTrue(((LARGE_INTEGER*)&fileInfo2.ftCreationTime)->QuadPart == basicInfo.CreationTime.QuadPart, + L"Failed to properly set modified file creation time.", + LINE_INFO()); + + if(!CloseHandle(handle)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!DeleteFileW(testFile.c_str())) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed DeleteFileW for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + DeleteTestDirectory(); + } + }; +} \ No newline at end of file diff --git a/UnitTests/UnitTests.vcxproj b/UnitTests/UnitTests.vcxproj new file mode 100644 index 000000000..420964e12 --- /dev/null +++ b/UnitTests/UnitTests.vcxproj @@ -0,0 +1,173 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA} + Win32Proj + UnitTests + 10.0.10586.0 + + + + DynamicLibrary + true + v140 + Unicode + false + + + DynamicLibrary + false + v140 + true + Unicode + false + + + DynamicLibrary + true + v140 + Unicode + false + + + DynamicLibrary + false + v140 + true + Unicode + false + + + + + + + + + + + + + + + + + + + + + true + + + true + + + true + + + true + + + + Use + Level3 + Disabled + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;%(PreprocessorDefinitions) + true + + + Windows + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + Shlwapi.lib;%(AdditionalDependencies) + + + + + Use + Level3 + Disabled + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + _DEBUG;%(PreprocessorDefinitions) + true + + + Windows + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + Shlwapi.lib;%(AdditionalDependencies) + + + + + Level3 + Use + MaxSpeed + true + true + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;%(PreprocessorDefinitions) + true + + + Windows + true + true + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + Shlwapi.lib;%(AdditionalDependencies) + + + + + Level3 + Use + MaxSpeed + true + true + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + NDEBUG;%(PreprocessorDefinitions) + true + + + Windows + true + true + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + Shlwapi.lib;%(AdditionalDependencies) + + + + + + + + + + Create + Create + Create + Create + + + + + + + \ No newline at end of file diff --git a/UnitTests/UnitTests.vcxproj.filters b/UnitTests/UnitTests.vcxproj.filters new file mode 100644 index 000000000..f90576e27 --- /dev/null +++ b/UnitTests/UnitTests.vcxproj.filters @@ -0,0 +1,36 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/UnitTests/stdafx.cpp b/UnitTests/stdafx.cpp new file mode 100644 index 000000000..7185e1143 --- /dev/null +++ b/UnitTests/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// UnitTests.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/UnitTests/stdafx.h b/UnitTests/stdafx.h new file mode 100644 index 000000000..43280fca3 --- /dev/null +++ b/UnitTests/stdafx.h @@ -0,0 +1,13 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +// Headers for CppUnitTest +#include "CppUnitTest.h" + +// TODO: reference additional headers your program requires here diff --git a/UnitTests/targetver.h b/UnitTests/targetver.h new file mode 100644 index 000000000..87c0086de --- /dev/null +++ b/UnitTests/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/dokan.sln b/dokan.sln index 12400c6df..65af92ec6 100644 --- a/dokan.sln +++ b/dokan.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.25123.0 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sys", "sys\sys.vcxproj", "{C0269F86-24E0-40DD-B5C8-29CDFDFF2AFB}" EndProject @@ -15,6 +15,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dokan_np", "dokan_np\dokan_ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dokan_fuse", "dokan_fuse\dokan_fuse.vcxproj", "{4AF8149D-C526-4D38-A4BC-9FFA67EDF924}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests", "UnitTests\UnitTests.vcxproj", "{46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -270,6 +272,46 @@ Global {4AF8149D-C526-4D38-A4BC-9FFA67EDF924}.Win8.1 Debug|x64.Build.0 = Debug|x64 {4AF8149D-C526-4D38-A4BC-9FFA67EDF924}.Win8.1 Release|Win32.ActiveCfg = Release|Win32 {4AF8149D-C526-4D38-A4BC-9FFA67EDF924}.Win8.1 Release|x64.ActiveCfg = Release|x64 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Debug|Win32.ActiveCfg = Debug|Win32 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Debug|Win32.Build.0 = Debug|Win32 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Debug|x64.ActiveCfg = Debug|x64 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Debug|x64.Build.0 = Debug|x64 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Release|Win32.ActiveCfg = Release|Win32 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Release|Win32.Build.0 = Release|Win32 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Release|x64.ActiveCfg = Release|x64 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Release|x64.Build.0 = Release|x64 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win10 Debug|Win32.ActiveCfg = Debug|Win32 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win10 Debug|Win32.Build.0 = Debug|Win32 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win10 Debug|x64.ActiveCfg = Debug|x64 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win10 Debug|x64.Build.0 = Debug|x64 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win10 Release|Win32.ActiveCfg = Release|Win32 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win10 Release|Win32.Build.0 = Release|Win32 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win10 Release|x64.ActiveCfg = Release|x64 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win10 Release|x64.Build.0 = Release|x64 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win7 Debug|Win32.ActiveCfg = Debug|Win32 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win7 Debug|Win32.Build.0 = Debug|Win32 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win7 Debug|x64.ActiveCfg = Debug|x64 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win7 Debug|x64.Build.0 = Debug|x64 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win7 Release|Win32.ActiveCfg = Release|Win32 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win7 Release|Win32.Build.0 = Release|Win32 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win7 Release|x64.ActiveCfg = Release|x64 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win7 Release|x64.Build.0 = Release|x64 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win8 Debug|Win32.ActiveCfg = Debug|Win32 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win8 Debug|Win32.Build.0 = Debug|Win32 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win8 Debug|x64.ActiveCfg = Debug|x64 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win8 Debug|x64.Build.0 = Debug|x64 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win8 Release|Win32.ActiveCfg = Release|Win32 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win8 Release|Win32.Build.0 = Release|Win32 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win8 Release|x64.ActiveCfg = Release|x64 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win8 Release|x64.Build.0 = Release|x64 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win8.1 Debug|Win32.ActiveCfg = Debug|Win32 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win8.1 Debug|Win32.Build.0 = Debug|Win32 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win8.1 Debug|x64.ActiveCfg = Debug|x64 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win8.1 Debug|x64.Build.0 = Debug|x64 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win8.1 Release|Win32.ActiveCfg = Release|Win32 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win8.1 Release|Win32.Build.0 = Release|Win32 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win8.1 Release|x64.ActiveCfg = Release|x64 + {46A0F41C-33E1-4912-86E9-14ECEB0D9AAA}.Win8.1 Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/samples/dokan_mirror/mirror.c b/samples/dokan_mirror/mirror.c index 408310a72..c263e459e 100644 --- a/samples/dokan_mirror/mirror.c +++ b/samples/dokan_mirror/mirror.c @@ -1447,10 +1447,14 @@ static NTSTATUS DOKAN_CALLBACK MirrorSetFileBasicInformation(DOKAN_SET_FILE_BASI MIRROR_HANDLE_ASSERT(mirrorHandle); assert(sizeof(FILE_BASIC_INFORMATION) == sizeof(FILE_BASIC_INFO)); + // Due to potential byte alignment issues we need to make a copy of the input data + FILE_BASIC_INFO basicInfo; + memcpy_s(&basicInfo, sizeof(basicInfo), EventInfo->Info, sizeof(FILE_BASIC_INFORMATION)); + if (!SetFileInformationByHandle(mirrorHandle->FileHandle, FileBasicInfo, - (LPVOID)EventInfo->Info, - (DWORD)sizeof(FILE_BASIC_INFORMATION))) { + &basicInfo, + (DWORD)sizeof(basicInfo))) { DWORD error = GetLastError(); From 531d973add9e32f00c2154fadf54667269989d11 Mon Sep 17 00:00:00 2001 From: Keith Newton Date: Mon, 8 Aug 2016 16:13:29 -0400 Subject: [PATCH 10/20] Fixed SetFileBasicInformation alignment issues in the kernel This is a better fix for the previous Mirror fix --- dokan/dokan.c | 5 ++++- dokan/fileinfo.c | 2 +- samples/dokan_mirror/mirror.c | 9 +++------ sys/directory.c | 2 +- sys/fileinfo.c | 16 +++++++++------- sys/public.h | 6 +++++- sys/read.c | 2 +- sys/security.c | 4 ++-- sys/volume.c | 2 +- 9 files changed, 27 insertions(+), 21 deletions(-) diff --git a/dokan/dokan.c b/dokan/dokan.c index 61e61c22f..1d862c60c 100644 --- a/dokan/dokan.c +++ b/dokan/dokan.c @@ -1354,7 +1354,7 @@ BOOL SendEventInformation(PEVENT_INFORMATION EventInfo, DOKAN_OVERLAPPED *overlapped = NULL; DWORD lastError = 0; - DWORD eventSize = max(sizeof(EVENT_INFORMATION), DOKAN_EVENT_INFO_ALLOC_SIZE(EventInfo->BufferLength)); + DWORD eventSize = (DWORD)max(sizeof(EVENT_INFORMATION), DOKAN_EVENT_INFO_ALLOC_SIZE(EventInfo->BufferLength)); DbgPrint("Dokan Information: SendEventInformation() with NTSTATUS 0x%x, context 0x%lx, and result object 0x%p\n", EventInfo->Status, @@ -1753,6 +1753,9 @@ DOKAN_API PTP_POOL DOKAN_CALLBACK DokanGetThreadPool() { void DOKANAPI DokanInit(DOKAN_MEMORY_CALLBACKS *memoryCallbacks) { + // ensure 64-bit alignment + assert(offsetof(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); diff --git a/dokan/fileinfo.c b/dokan/fileinfo.c index e4336c242..dca7c3375 100644 --- a/dokan/fileinfo.c +++ b/dokan/fileinfo.c @@ -290,7 +290,7 @@ DokanFillIdInfo(PFILE_ID_INFORMATION IdInfo, int WINAPI DokanFillFindStreamData(PDOKAN_FIND_STREAMS_EVENT EventInfo, PWIN32_FIND_STREAM_DATA FindStreamData) { DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; - ULONG offset = ioEvent->EventResult->BufferLength; + ULONG offset = (ULONG)ioEvent->EventResult->BufferLength; ULONG resultBufferSize = IoEventResultBufferSize(ioEvent); ULONG streamNameLength = diff --git a/samples/dokan_mirror/mirror.c b/samples/dokan_mirror/mirror.c index 8fe199dd0..98fc1b43e 100644 --- a/samples/dokan_mirror/mirror.c +++ b/samples/dokan_mirror/mirror.c @@ -1446,15 +1446,12 @@ static NTSTATUS DOKAN_CALLBACK MirrorSetFileBasicInformation(DOKAN_SET_FILE_BASI MIRROR_HANDLE_ASSERT(mirrorHandle); assert(sizeof(FILE_BASIC_INFORMATION) == sizeof(FILE_BASIC_INFO)); - - // Due to potential byte alignment issues we need to make a copy of the input data - FILE_BASIC_INFO basicInfo; - memcpy_s(&basicInfo, sizeof(basicInfo), EventInfo->Info, sizeof(FILE_BASIC_INFORMATION)); + assert((ULONG_PTR)EventInfo->Info % sizeof(ULONG_PTR) == 0); if (!SetFileInformationByHandle(mirrorHandle->FileHandle, FileBasicInfo, - &basicInfo, - (DWORD)sizeof(basicInfo))) { + EventInfo->Info, + (DWORD)sizeof(FILE_BASIC_INFORMATION))) { DWORD error = GetLastError(); diff --git a/sys/directory.c b/sys/directory.c index ad22e8d4f..01fdae864 100644 --- a/sys/directory.c +++ b/sys/directory.c @@ -358,7 +358,7 @@ VOID DokanCompleteDirectoryControl(__in PIRP_ENTRY IrpEntry, status = EventInfo->Status; - info = EventInfo->BufferLength; + info = (ULONG)EventInfo->BufferLength; } if (IrpEntry->Flags & DOKAN_MDL_ALLOCATED) { diff --git a/sys/fileinfo.c b/sys/fileinfo.c index 0b0ecc8fc..aa9a4348f 100644 --- a/sys/fileinfo.c +++ b/sys/fileinfo.c @@ -260,7 +260,7 @@ VOID DokanCompleteQueryInformation(__in PIRP_ENTRY IrpEntry, RtlCopyMemory(buffer, EventInfo->Buffer, EventInfo->BufferLength); // written bytes - info = EventInfo->BufferLength; + info = (ULONG)EventInfo->BufferLength; status = EventInfo->Status; if (NT_SUCCESS(status) && @@ -402,8 +402,12 @@ DokanDispatchSetInformation(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { // calcurate the size of EVENT_CONTEXT // it is sum of file name length and size of FileInformation - eventLength = sizeof(EVENT_CONTEXT) + fcb->FileName.Length + - irpSp->Parameters.SetFile.Length; + ULONG bufferOffset = FIELD_OFFSET(EVENT_CONTEXT, Operation.SetFile.FileName) + fcb->FileName.Length + sizeof(WCHAR); + + // the set file information must be ptr aligned + bufferOffset = (ULONG)ALIGN_UP(bufferOffset, ULONG_PTR); + + eventLength = max(sizeof(EVENT_CONTEXT), bufferOffset + irpSp->Parameters.SetFile.Length); targetFileObject = irpSp->Parameters.SetFile.FileObject; @@ -429,9 +433,7 @@ DokanDispatchSetInformation(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { irpSp->Parameters.SetFile.Length; // the offset from begining of structure to fill FileInfo - eventContext->Operation.SetFile.BufferOffset = - FIELD_OFFSET(EVENT_CONTEXT, Operation.SetFile.FileName[0]) + - fcb->FileName.Length + sizeof(WCHAR); // the last null char + eventContext->Operation.SetFile.BufferOffset = bufferOffset; // copy FileInformation RtlCopyMemory( @@ -548,7 +550,7 @@ VOID DokanCompleteSetInformation(__in PIRP_ENTRY IrpEntry, ccb->UserContext = EventInfo->Context; - info = EventInfo->BufferLength; + info = (ULONG)EventInfo->BufferLength; infoClass = irpSp->Parameters.SetFile.FileInformationClass; diff --git a/sys/public.h b/sys/public.h index 18c9d8cdd..f6745f8ec 100644 --- a/sys/public.h +++ b/sys/public.h @@ -331,7 +331,11 @@ typedef struct _EVENT_INFORMATION { } AccessToken; } Operation; ULONG64 Context; - ULONG BufferLength; + + // This must be 64-bit to maintain 8 byte alignment for Buffer on x64 + ULONG64 BufferLength; + + // This must have 8 byte alignment for x64 UCHAR Buffer[DOKAN_EVENT_INFO_MIN_BUFFER_SIZE]; } EVENT_INFORMATION, *PEVENT_INFORMATION; diff --git a/sys/read.c b/sys/read.c index c10d41911..3f621c5bd 100644 --- a/sys/read.c +++ b/sys/read.c @@ -308,7 +308,7 @@ VOID DokanCompleteRead(__in PIRP_ENTRY IrpEntry, RtlCopyMemory(buffer, EventInfo->Buffer, EventInfo->BufferLength); // read length which is actually read - readLength = EventInfo->BufferLength; + readLength = (ULONG)EventInfo->BufferLength; status = EventInfo->Status; if (NT_SUCCESS(status) && EventInfo->BufferLength > 0 && diff --git a/sys/security.c b/sys/security.c index 27198e4d5..d98ef4b53 100644 --- a/sys/security.c +++ b/sys/security.c @@ -158,13 +158,13 @@ VOID DokanCompleteQuerySecurity(__in PIRP_ENTRY IrpEntry, if (EventInfo->Status == STATUS_SUCCESS && EventInfo->BufferLength <= bufferLength && buffer != NULL) { RtlCopyMemory(buffer, EventInfo->Buffer, EventInfo->BufferLength); - info = EventInfo->BufferLength; + info = (ULONG)EventInfo->BufferLength; status = STATUS_SUCCESS; } else if (EventInfo->Status == STATUS_BUFFER_OVERFLOW || (EventInfo->Status == STATUS_SUCCESS && bufferLength < EventInfo->BufferLength)) { - info = EventInfo->BufferLength; + info = (ULONG)EventInfo->BufferLength; status = STATUS_BUFFER_OVERFLOW; } else { diff --git a/sys/volume.c b/sys/volume.c index 520916b42..7f7ded39d 100644 --- a/sys/volume.c +++ b/sys/volume.c @@ -342,7 +342,7 @@ VOID DokanCompleteQueryVolumeInformation(__in PIRP_ENTRY IrpEntry, RtlCopyMemory(buffer, EventInfo->Buffer, EventInfo->BufferLength); // the written length - info = EventInfo->BufferLength; + info = (ULONG)EventInfo->BufferLength; status = EventInfo->Status; } From ec3ca06b4a1b4d65f157395b5ea658074dded3cf Mon Sep 17 00:00:00 2001 From: Keith Newton Date: Mon, 8 Aug 2016 16:21:15 -0400 Subject: [PATCH 11/20] Removed a rogue assert --- dokan/fileinfo.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/dokan/fileinfo.c b/dokan/fileinfo.c index dca7c3375..835b51ba8 100644 --- a/dokan/fileinfo.c +++ b/dokan/fileinfo.c @@ -547,8 +547,6 @@ void DOKANAPI DokanEndDispatchFindStreams(DOKAN_FIND_STREAMS_EVENT *EventInfo, N ResultStatus = STATUS_INTERNAL_ERROR; } - assert(ioEvent->EventResult->BufferLength == 0); - ioEvent->EventResult->Status = ResultStatus; DbgPrint("\tDispatchQueryInformation result = %lx\n", ResultStatus); From c1d8febe79586dac95489427bf1f25c8da214b8b Mon Sep 17 00:00:00 2001 From: Keith Newton Date: Mon, 8 Aug 2016 20:54:55 -0400 Subject: [PATCH 12/20] Fixed more pointer alignment issues #210 Access tokens now get passed to the user mode driver for SetFileSecurity and CreateFile Fixed an issue in Mirror with readonly files not returning access denied in CanDelete --- UnitTests/FileSystemTests.cpp | 230 +++++++++ UnitTests/FileSystemTests.cpp~RF9a2366d.TMP | 486 ++++++++++++++++++++ dokan/create.c | 1 + dokan/dokan.h | 2 + dokan/security.c | 3 +- samples/dokan_mirror/mirror.c | 21 +- sys/cleanup.c | 2 +- sys/close.c | 2 +- sys/create.c | 44 +- sys/directory.c | 2 +- sys/dokan.h | 29 +- sys/event.c | 59 ++- sys/fileinfo.c | 7 +- sys/flush.c | 2 +- sys/lock.c | 2 +- sys/public.h | 2 + sys/read.c | 2 +- sys/security.c | 179 ++++++- sys/volume.c | 2 +- sys/write.c | 4 +- 20 files changed, 1028 insertions(+), 53 deletions(-) create mode 100644 UnitTests/FileSystemTests.cpp~RF9a2366d.TMP diff --git a/UnitTests/FileSystemTests.cpp b/UnitTests/FileSystemTests.cpp index 0c068c40a..0c4100456 100644 --- a/UnitTests/FileSystemTests.cpp +++ b/UnitTests/FileSystemTests.cpp @@ -268,5 +268,235 @@ namespace UnitTests DeleteTestDirectory(); } + + // This is test 01 in winfstest + TEST_METHOD(TestCreateModifyAttribsDelete) + { + std::wstring testDir = CreateTestDirectory(); + std::wstring testFile = CombinePath(testDir, L"TestCreateModifyAttribsDelete.txt"); + std::wstring errMsg; + + DWORD fileDesiredAccess = + FILE_READ_ATTRIBUTES + | READ_CONTROL + | FILE_WRITE_DATA + | FILE_WRITE_ATTRIBUTES + | FILE_WRITE_EA + | FILE_APPEND_DATA + | SYNCHRONIZE + | STANDARD_RIGHTS_READ + | STANDARD_RIGHTS_WRITE + | STANDARD_RIGHTS_EXECUTE; + + HANDLE handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if(!handle) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + BY_HANDLE_FILE_INFORMATION fileInfo; + ZeroMemory(&fileInfo, sizeof(fileInfo)); + + if(!GetFileInformationByHandle(handle, &fileInfo)) + { + CloseHandle(handle); + + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to GetFileInformationByHandle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if((fileInfo.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) != 0) + { + CloseHandle(handle); + + errMsg = CreateSystemErrorMessage(FormatString(L"FILE_ATTRIBUTE_NORMAL should be set for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, NULL); + + if(!handle) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + ZeroMemory(&fileInfo, sizeof(fileInfo)); + + if(!GetFileInformationByHandle(handle, &fileInfo)) + { + CloseHandle(handle); + + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to GetFileInformationByHandle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if((fileInfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0) + { + CloseHandle(handle); + + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to set FILE_ATTRIBUTE_READONLY for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!SetFileAttributesW(testFile.c_str(), FILE_ATTRIBUTE_NORMAL)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed SetFileAttributesW() to set FILE_ATTRIBUTE_NORMAL for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_SYSTEM, NULL); + + if(!handle) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + ZeroMemory(&fileInfo, sizeof(fileInfo)); + + if(!GetFileInformationByHandle(handle, &fileInfo)) + { + CloseHandle(handle); + + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to GetFileInformationByHandle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if((fileInfo.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) == 0) + { + CloseHandle(handle); + + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to set FILE_ATTRIBUTE_SYSTEM for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!SetFileAttributesW(testFile.c_str(), FILE_ATTRIBUTE_NORMAL)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed SetFileAttributesW() to set FILE_ATTRIBUTE_NORMAL for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if((GetFileAttributesW(testFile.c_str()) & FILE_ATTRIBUTE_NORMAL) == 0) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to set FILE_ATTRIBUTE_NORMAL for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL); + + if(!handle) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + ZeroMemory(&fileInfo, sizeof(fileInfo)); + + if(!GetFileInformationByHandle(handle, &fileInfo)) + { + CloseHandle(handle); + + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to GetFileInformationByHandle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if((fileInfo.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) == 0) + { + CloseHandle(handle); + + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to set FILE_ATTRIBUTE_HIDDEN for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!DeleteFileW(testFile.c_str())) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to delete file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, NULL); + + if(!handle) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(DeleteFileW(testFile.c_str()) || GetLastError() != ERROR_ACCESS_DENIED) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Successfully deleted file \'%s\' when it was expected to fail.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!SetFileAttributesW(testFile.c_str(), FILE_ATTRIBUTE_NORMAL)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed SetFileAttributesW() to set FILE_ATTRIBUTE_NORMAL for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!DeleteFileW(testFile.c_str())) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to delete file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + } }; } \ No newline at end of file diff --git a/UnitTests/FileSystemTests.cpp~RF9a2366d.TMP b/UnitTests/FileSystemTests.cpp~RF9a2366d.TMP new file mode 100644 index 000000000..f37a29565 --- /dev/null +++ b/UnitTests/FileSystemTests.cpp~RF9a2366d.TMP @@ -0,0 +1,486 @@ +#include "stdafx.h" +#include "CppUnitTest.h" + +#include +#include +#include +#include + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +#define FILESYSTEM_ROOT L"M:\\" +#define FILESYSTEM_TESTDIR L"M:\\MirrorUnitTests" + +const wchar_t* GetErrorCodeStr(const DWORD errCode); + +#define USE_MIRROR 0 + +namespace UnitTests +{ + TEST_CLASS(FileSystemTests) + { + private: + std::wstring GetTempPathStr() + { + WCHAR path[MAX_PATH]; + + GetTempPathW(MAX_PATH, path); + + return path; + } + + std::wstring CombinePath(const std::wstring &left, const std::wstring &right) + { + if(left.length() == 0) + { + return right; + } + + if(right.length() == 0) + { + return left; + } + + const bool leftHasDelimeter = left[left.length() - 1] == L'\\' || left[left.length() - 1] == L'/'; + const bool rightHasDelimeter = right[right.length() - 1] == L'\\' || right[right.length() - 1] == L'/'; + + if(leftHasDelimeter && rightHasDelimeter) + { + return left.substr(0, left.length() - 1) + right; + } + else if(!leftHasDelimeter && !rightHasDelimeter) + { + return left + L"\\" + right; + } + + return left + right; + } + + std::wstring CreateTestDirectory() + { +#if USE_MIRROR + std::wstring testDir = FILESYSTEM_TESTDIR; +#else + std::wstring testDir = CombinePath(GetTempPathStr(), L"MirrorUnitTests"); +#endif + + if(!PathFileExistsW(testDir.c_str())) + { + if(!CreateDirectoryW(testDir.c_str(), NULL)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CreateDirectoryW for directory \'%s\'.", testDir.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + } + + return testDir; + } + + void DeleteTestDirectory() + { +#if USE_MIRROR + std::wstring testDir = FILESYSTEM_TESTDIR; +#else + std::wstring testDir = CombinePath(GetTempPathStr(), L"MirrorUnitTests"); +#endif + + if(PathFileExistsW(testDir.c_str())) + { + if(!RemoveDirectoryW(testDir.c_str())) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to RemoveDirectoryW for directory \'%s\'.", testDir.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + } + } + + std::wstring FormatString(const wchar_t *str, ...) + { + size_t size = 256; + wchar_t *buf = (wchar_t*)malloc(size * sizeof(wchar_t)); + int written = 0; + + va_list args; + va_start(args, str); + + while((written = _vsnwprintf_s(buf, size - 1, size - 1, str, args)) == -1) + { + size *= 2; + buf = (wchar_t*)realloc(buf, size * sizeof(wchar_t)); + + if(!buf) + { + written = 0; + break; + } + } + + va_end(args); + + assert(static_cast(written) <= size - 2); + + buf[written] = 0; + + std::wstring temp = buf; + + free(buf); + + return temp; + } + + std::wstring GetLastErrorStr(const DWORD lastErr) + { + LPWSTR buffer = NULL; + + // we force english because the error messages are for developers and english is the common language + // developers of all countries use to communicate + DWORD errCode = FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + lastErr, + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + (LPWSTR)&buffer, + 0, + NULL); + + if(errCode == 0) + { + if(GetLastError() == ERROR_RESOURCE_LANG_NOT_FOUND) + { + errCode = FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + lastErr, + 0, + (LPWSTR)&buffer, + 0, + NULL); + } + + if(errCode == 0) + { + return std::wstring(); + } + } + + std::wstring retStr = buffer; + + LocalFree(buffer); + + return retStr; + } + + std::wstring CreateSystemErrorMessage(const std::wstring &msg) + { + const DWORD errCode = GetLastError(); + const std::wstring errCodeMsg = GetLastErrorStr(errCode); + + return FormatString(L"%s\nError code is \'%s\' (%d).\n%s", + msg.c_str(), + GetErrorCodeStr(errCode), + errCode, + errCodeMsg.c_str()); + } + + public: + + TEST_METHOD(TestFileBasicInfo) + { + std::wstring testDir = CreateTestDirectory(); + std::wstring testFile = CombinePath(testDir, L"TestFileBasicInfo.txt"); + + DWORD fileDesiredAccess = + FILE_READ_ATTRIBUTES + | READ_CONTROL + | FILE_WRITE_DATA + | FILE_WRITE_ATTRIBUTES + | FILE_WRITE_EA + | FILE_APPEND_DATA + | SYNCHRONIZE + | STANDARD_RIGHTS_READ + | STANDARD_RIGHTS_WRITE + | STANDARD_RIGHTS_EXECUTE; + + HANDLE handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if(!handle) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + BY_HANDLE_FILE_INFORMATION fileInfo; + ZeroMemory(&fileInfo, sizeof(fileInfo)); + + if(!GetFileInformationByHandle(handle, &fileInfo)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to GetFileInformationByHandle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + FILE_BASIC_INFO basicInfo; + basicInfo.ChangeTime = *(LARGE_INTEGER*)&fileInfo.ftLastWriteTime; + basicInfo.CreationTime = *(LARGE_INTEGER*)&fileInfo.ftCreationTime; + basicInfo.FileAttributes = fileInfo.dwFileAttributes; + basicInfo.LastAccessTime = *(LARGE_INTEGER*)&fileInfo.ftLastAccessTime; + basicInfo.LastWriteTime = *(LARGE_INTEGER*)&fileInfo.ftLastWriteTime; + + basicInfo.CreationTime.QuadPart -= 500; + + if(!SetFileInformationByHandle(handle, FileBasicInfo, &basicInfo, sizeof(basicInfo))) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to SetFileInformationByHandle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + BY_HANDLE_FILE_INFORMATION fileInfo2; + ZeroMemory(&fileInfo2, sizeof(fileInfo2)); + + if(!GetFileInformationByHandle(handle, &fileInfo2)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to GetFileInformationByHandle for modified file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + Assert::IsTrue(((LARGE_INTEGER*)&fileInfo2.ftCreationTime)->QuadPart == basicInfo.CreationTime.QuadPart, + L"Failed to properly set modified file creation time.", + LINE_INFO()); + + if(!CloseHandle(handle)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!DeleteFileW(testFile.c_str())) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed DeleteFileW for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + DeleteTestDirectory(); + } + + // This is test 01 in winfstest + TEST_METHOD(TestCreateModifyAttribsDelete) + { + std::wstring testDir = CreateTestDirectory(); + std::wstring testFile = CombinePath(testDir, L"TestCreateModifyAttribsDelete.txt"); + std::wstring errMsg; + + DWORD fileDesiredAccess = + FILE_READ_ATTRIBUTES + | READ_CONTROL + | FILE_WRITE_DATA + | FILE_WRITE_ATTRIBUTES + | FILE_WRITE_EA + | FILE_APPEND_DATA + | SYNCHRONIZE + | STANDARD_RIGHTS_READ + | STANDARD_RIGHTS_WRITE + | STANDARD_RIGHTS_EXECUTE; + + HANDLE handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if(!handle) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + BY_HANDLE_FILE_INFORMATION fileInfo; + ZeroMemory(&fileInfo, sizeof(fileInfo)); + + if(!GetFileInformationByHandle(handle, &fileInfo)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to GetFileInformationByHandle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if((fileInfo.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) == 0) + { + errMsg = CreateSystemErrorMessage(FormatString(L"FILE_ATTRIBUTE_NORMAL should be set for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, NULL); + + if(!handle) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + ZeroMemory(&fileInfo, sizeof(fileInfo)); + + if(!GetFileInformationByHandle(handle, &fileInfo)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to GetFileInformationByHandle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if((fileInfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to set FILE_ATTRIBUTE_READONLY for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!SetFileAttributesW(testFile.c_str(), FILE_ATTRIBUTE_NORMAL)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed SetFileAttributesW() to set FILE_ATTRIBUTE_NORMAL for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_SYSTEM, NULL); + + if(!handle) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + ZeroMemory(&fileInfo, sizeof(fileInfo)); + + if(!GetFileInformationByHandle(handle, &fileInfo)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to GetFileInformationByHandle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if((fileInfo.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) == 0) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to set FILE_ATTRIBUTE_SYSTEM for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!SetFileAttributesW(testFile.c_str(), FILE_ATTRIBUTE_NORMAL)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed SetFileAttributesW() to set FILE_ATTRIBUTE_NORMAL for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if((GetFileAttributesW(testFile.c_str()) & FILE_ATTRIBUTE_NORMAL) == 0) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to set FILE_ATTRIBUTE_NORMAL for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL); + + if(!handle) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + ZeroMemory(&fileInfo, sizeof(fileInfo)); + + if(!GetFileInformationByHandle(handle, &fileInfo)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to GetFileInformationByHandle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if((fileInfo.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) == 0) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to set FILE_ATTRIBUTE_HIDDEN for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!DeleteFileW(testFile.c_str())) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to delete file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, NULL); + + if(!handle) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(DeleteFileW(testFile.c_str()) || GetLastError() != ERROR_ACCESS_DENIED) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Successfully deleted file \'%s\' when it was expected to fail.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!SetFileAttributesW(testFile.c_str(), FILE_ATTRIBUTE_NORMAL)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed SetFileAttributesW() to set FILE_ATTRIBUTE_NORMAL for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!DeleteFileW(testFile.c_str())) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to delete file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + } + }; +} \ No newline at end of file diff --git a/dokan/create.c b/dokan/create.c index 8f5e14772..9cb5e53ae 100644 --- a/dokan/create.c +++ b/dokan/create.c @@ -113,6 +113,7 @@ void BeginDispatchCreate(DOKAN_IO_EVENT *EventInfo) { createFileEvent->CreateDisposition = (EventInfo->KernelInfo.EventContext.Operation.Create.CreateOptions >> 24) & 0x000000ff; createFileEvent->FileAttributes = EventInfo->KernelInfo.EventContext.Operation.Create.FileAttributes; createFileEvent->ShareAccess = EventInfo->KernelInfo.EventContext.Operation.Create.ShareAccess; + createFileEvent->AccessToken = EventInfo->KernelInfo.EventContext.Operation.Create.AccessToken; if((createFileEvent->CreateOptions & FILE_NON_DIRECTORY_FILE) && (createFileEvent->CreateOptions & FILE_DIRECTORY_FILE)) { diff --git a/dokan/dokan.h b/dokan/dokan.h index 661c6052c..5abde5604 100644 --- a/dokan/dokan.h +++ b/dokan/dokan.h @@ -156,6 +156,7 @@ typedef struct _DOKAN_CREATE_FILE_EVENT { PDOKAN_FILE_INFO DokanFileInfo; LPCWSTR FileName; LPCWSTR OriginalFileName; + HANDLE AccessToken; DOKAN_IO_SECURITY_CONTEXT SecurityContext; // https://msdn.microsoft.com/en-us/library/windows/hardware/ff550613(v=vs.85).aspx ACCESS_MASK DesiredAccess; @@ -275,6 +276,7 @@ typedef struct _DOKAN_GET_FILE_SECURITY_EVENT { typedef struct _DOKAN_SET_FILE_SECURITY_EVENT { PDOKAN_FILE_INFO DokanFileInfo; LPWSTR FileName; + HANDLE AccessToken; PSECURITY_INFORMATION SecurityInformation; PSECURITY_DESCRIPTOR SecurityDescriptor; // A pointer to SECURITY_DESCRIPTOR buffer to be filled ULONG SecurityDescriptorSize; // length of Security descriptor buffer diff --git a/dokan/security.c b/dokan/security.c index e63784eb3..bab703432 100644 --- a/dokan/security.c +++ b/dokan/security.c @@ -111,7 +111,8 @@ void BeginDispatchSetSecurity(DOKAN_IO_EVENT *EventInfo) { if(EventInfo->DokanInstance->DokanOperations->SetFileSecurityW) { setFileSecurity->DokanFileInfo = &EventInfo->DokanFileInfo; - setFileSecurity->FileName = EventInfo->KernelInfo.EventContext.Operation.Security.FileName; + setFileSecurity->FileName = EventInfo->KernelInfo.EventContext.Operation.SetSecurity.FileName; + setFileSecurity->AccessToken = EventInfo->KernelInfo.EventContext.Operation.SetSecurity.AccessToken; setFileSecurity->SecurityInformation = &EventInfo->KernelInfo.EventContext.Operation.SetSecurity.SecurityInformation; setFileSecurity->SecurityDescriptor = (PCHAR)&EventInfo->KernelInfo.EventContext + EventInfo->KernelInfo.EventContext.Operation.SetSecurity.BufferOffset; setFileSecurity->SecurityDescriptorSize = EventInfo->KernelInfo.EventContext.Operation.SetSecurity.BufferLength; diff --git a/samples/dokan_mirror/mirror.c b/samples/dokan_mirror/mirror.c index 98fc1b43e..481795009 100644 --- a/samples/dokan_mirror/mirror.c +++ b/samples/dokan_mirror/mirror.c @@ -1231,22 +1231,31 @@ static NTSTATUS DOKAN_CALLBACK MirrorCanDeleteFile(DOKAN_CAN_DELETE_FILE_EVENT *EventInfo) { WCHAR filePath[MAX_PATH]; + MIRROR_FILE_HANDLE *mirrorHandle = (MIRROR_FILE_HANDLE*)EventInfo->DokanFileInfo->Context; + BY_HANDLE_FILE_INFORMATION fileInfo; GetFilePath(filePath, MAX_PATH, EventInfo->FileName); DbgPrint(L"CanDeleteFile %s\n", filePath); - if(EventInfo->DokanFileInfo->IsDirectory) { - - return MirrorCanDeleteDirectory(filePath); - } + MIRROR_HANDLE_ASSERT(mirrorHandle); - DWORD dwAttrib = GetFileAttributes(filePath); + ZeroMemory(&fileInfo, sizeof(fileInfo)); - if(dwAttrib == INVALID_FILE_ATTRIBUTES) + if(!GetFileInformationByHandle(mirrorHandle->FileHandle, &fileInfo)) { return DokanNtStatusFromWin32(GetLastError()); } + if((fileInfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY) { + + return STATUS_ACCESS_DENIED; + } + + if(EventInfo->DokanFileInfo->IsDirectory) { + + return MirrorCanDeleteDirectory(filePath); + } + return STATUS_SUCCESS; } diff --git a/sys/cleanup.c b/sys/cleanup.c index c0dec5035..57fc5df4d 100644 --- a/sys/cleanup.c +++ b/sys/cleanup.c @@ -128,7 +128,7 @@ Return Value: } // register this IRP to pending IRP list - status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0); + status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0, NULL); } __finally { diff --git a/sys/close.c b/sys/close.c index 31605895e..d16a58d04 100644 --- a/sys/close.c +++ b/sys/close.c @@ -130,7 +130,7 @@ Return Value: // Close can not be pending status // don't register this IRP // status = DokanRegisterPendingIrp(DeviceObject, Irp, - // eventContext->SerialNumber, 0); + // eventContext->SerialNumber, 0, NULL); // inform it to user-mode DokanEventNotification(&vcb->Dcb->NotifyEvent, eventContext); diff --git a/sys/create.c b/sys/create.c index 5e1da35db..f4f89ce86 100755 --- a/sys/create.c +++ b/sys/create.c @@ -471,9 +471,13 @@ Return Value: BOOLEAN BackoutOplock = FALSE; BOOLEAN EventContextConsumed = FALSE; DWORD disposition = 0; + IRP_ENTRY_CONTEXT irpContext; PAGED_CODE(); + // must be done up here outside of the __try block + RtlZeroMemory(&irpContext, sizeof(irpContext)); + __try { DDbgPrint("==> DokanCreate\n"); @@ -937,10 +941,22 @@ Return Value: eventContext->Operation.Create.FileNameOffset), parentDir ? fileName : fcb->FileName.Buffer, parentDir ? fileNameLength : fcb->FileName.Length); + *(PWCHAR)((char *)&eventContext->Operation.Create + eventContext->Operation.Create.FileNameOffset + (parentDir ? fileNameLength : fcb->FileName.Length)) = 0; + status = DokanCreateProcessAccessToken( + &irpContext.Security.UserModeAccessToken, + &irpContext.Security.Process); + + if(!NT_SUCCESS(status)) { + + __leave; + } + + eventContext->Operation.Create.AccessToken = irpContext.Security.UserModeAccessToken; + // // Oplock // @@ -1167,7 +1183,7 @@ Return Value: } // register this IRP to waiting IPR list - status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0); + status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0, &irpContext); EventContextConsumed = TRUE; @@ -1205,23 +1221,38 @@ Return Value: if (!NT_SUCCESS(status)) { + if(irpContext.Security.UserModeAccessToken) { + + DokanCleanupProcessAccessToken( + irpContext.Security.UserModeAccessToken, + irpContext.Security.Process); + } + // DokanRegisterPendingIrp consumes event context if (!EventContextConsumed && eventContext) { + DokanFreeEventContext(eventContext); } + if (ccb) { + DokanFreeCCB(ccb); } + if (fcb) { + DokanFreeFCB(fcb); } } if (parentDir) { // SL_OPEN_TARGET_DIRECTORY + // fcb owns parentDir, not fileName - if (fileName) - ExFreePool(fileName); + if(fileName) { + + ExFreePool(fileName); + } } DokanCompleteIrpRequest(Irp, status, info); @@ -1246,6 +1277,13 @@ VOID DokanCompleteCreate(__in PIRP_ENTRY IrpEntry, DDbgPrint("==> DokanCompleteCreate\n"); + if(IrpEntry->ContextInfo.Security.UserModeAccessToken) { + + DokanCleanupProcessAccessToken( + IrpEntry->ContextInfo.Security.UserModeAccessToken, + IrpEntry->ContextInfo.Security.Process); + } + ccb = IrpEntry->FileObject->FsContext2; ASSERT(ccb != NULL); diff --git a/sys/directory.c b/sys/directory.c index 01fdae864..d96b96ea7 100644 --- a/sys/directory.c +++ b/sys/directory.c @@ -251,7 +251,7 @@ DokanQueryDirectory(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { DDbgPrint(" ccb->SearchPattern %ws\n", ccb->SearchPattern); } - status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, flags); + status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, flags, NULL); return status; } diff --git a/sys/dokan.h b/sys/dokan.h index 5038b4b71..3ba20c6b9 100644 --- a/sys/dokan.h +++ b/sys/dokan.h @@ -372,18 +372,26 @@ typedef struct _DokanContextControlBlock { #define DokanGetFcbOplock(F) &(F)->Oplock #endif +typedef union _IRP_ENTRY_CONTEXT { + struct { + PEPROCESS Process; + HANDLE UserModeAccessToken; + } Security; +} IRP_ENTRY_CONTEXT, *PIRP_ENTRY_CONTEXT; + // IRP list which has pending status // this structure is also used to store event notification IRP typedef struct _IRP_ENTRY { LIST_ENTRY ListEntry; - ULONG SerialNumber; PIRP Irp; PIO_STACK_LOCATION IrpSp; PFILE_OBJECT FileObject; - BOOLEAN CancelRoutineFreeMemory; - ULONG Flags; - LARGE_INTEGER TickCount; PIRP_LIST IrpList; + LARGE_INTEGER TickCount; + ULONG SerialNumber; + ULONG Flags; + IRP_ENTRY_CONTEXT ContextInfo; + BOOLEAN CancelRoutineFreeMemory; } IRP_ENTRY, *PIRP_ENTRY; typedef struct _DEVICE_ENTRY { @@ -532,8 +540,11 @@ AllocateEventContext(__in PDokanDCB Dcb, __in PIRP Irp, VOID DokanFreeEventContext(__in PEVENT_CONTEXT EventContext); NTSTATUS -DokanRegisterPendingIrp(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp, - __in PEVENT_CONTEXT EventContext, __in ULONG Flags); +DokanRegisterPendingIrp(__in PDEVICE_OBJECT DeviceObject, + __in PIRP Irp, + __in PEVENT_CONTEXT EventContext, + __in ULONG Flags, + __in IRP_ENTRY_CONTEXT *IrpContext); VOID DokanEventNotification(__in PIRP_LIST NotifyEvent, __in PEVENT_CONTEXT EventContext); @@ -577,6 +588,12 @@ VOID DokanCompleteSetSecurity(__in PIRP_ENTRY IrpEntry, VOID DokanNoOpRelease(__in PVOID Fcb); +NTSTATUS DokanCreateProcessAccessToken(__out HANDLE *AccessToken, + __out PEPROCESS *Process); + +VOID DokanCleanupProcessAccessToken(__in HANDLE AccessToken, + __in PEPROCESS Process); + BOOLEAN DokanNoOpAcquire(__in PVOID Fcb, __in BOOLEAN Wait); diff --git a/sys/event.c b/sys/event.c index 713d672b1..e04fe0c15 100644 --- a/sys/event.c +++ b/sys/event.c @@ -113,9 +113,11 @@ None. // Check on the return value in the Irp. // if (Irp->IoStatus.Status == STATUS_SUCCESS) { - DokanRegisterPendingIrp(irpSp->DeviceObject, Irp, (PEVENT_CONTEXT)Context, - 0); - } else { + + DokanRegisterPendingIrp(irpSp->DeviceObject, Irp, (PEVENT_CONTEXT)Context, 0, NULL); + } + else { + DokanCompleteIrpRequest(Irp, Irp->IoStatus.Status, 0); } @@ -146,9 +148,14 @@ None. } NTSTATUS -RegisterPendingIrpMain(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp, - __in ULONG SerialNumber, __in PIRP_LIST IrpList, - __in ULONG Flags, __in ULONG CheckMount) { +RegisterPendingIrpMain(__in PDEVICE_OBJECT DeviceObject, + __in PIRP Irp, + __in ULONG SerialNumber, + __in PIRP_LIST IrpList, + __in ULONG Flags, + __in ULONG CheckMount, + __in IRP_ENTRY_CONTEXT *IrpContext) { + PIRP_ENTRY irpEntry; PIO_STACK_LOCATION irpSp; KIRQL oldIrql; @@ -157,8 +164,11 @@ RegisterPendingIrpMain(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp, DDbgPrint("==> DokanRegisterPendingIrpMain\n"); if (GetIdentifierType(DeviceObject->DeviceExtension) == VCB) { + vcb = DeviceObject->DeviceExtension; + if (CheckMount && IsUnmountPendingVcb(vcb)) { + DDbgPrint(" device is not mounted\n"); return STATUS_NO_SUCH_DEVICE; } @@ -170,6 +180,7 @@ RegisterPendingIrpMain(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp, irpEntry = DokanAllocateIrpEntry(); if (NULL == irpEntry) { + DDbgPrint(" can't allocate IRP_ENTRY\n"); return STATUS_INSUFFICIENT_RESOURCES; } @@ -185,12 +196,20 @@ RegisterPendingIrpMain(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp, irpEntry->IrpList = IrpList; irpEntry->Flags = Flags; + if(IrpContext) { + + irpEntry->ContextInfo = *IrpContext; + } + // Update the irp timeout for the entry if (vcb) { + ExAcquireResourceExclusiveLite(&vcb->Dcb->Resource, TRUE); DokanUpdateTimeout(&irpEntry->TickCount, vcb->Dcb->IrpTimeout); ExReleaseResourceLite(&vcb->Dcb->Resource); - } else { + } + else { + DokanUpdateTimeout(&irpEntry->TickCount, DOKAN_IRP_PENDING_TIMEOUT); } @@ -201,6 +220,7 @@ RegisterPendingIrpMain(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp, IoSetCancelRoutine(Irp, DokanIrpCancelRoutine); if (Irp->Cancel) { + if (IoSetCancelRoutine(Irp, NULL) != NULL) { // DDbgPrint(" Release IrpList.ListLock %d\n", __LINE__); KeReleaseSpinLock(&IrpList->ListLock, oldIrql); @@ -230,28 +250,39 @@ RegisterPendingIrpMain(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp, } NTSTATUS -DokanRegisterPendingIrp(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp, - __in PEVENT_CONTEXT EventContext, __in ULONG Flags) { +DokanRegisterPendingIrp(__in PDEVICE_OBJECT DeviceObject, + __in PIRP Irp, + __in PEVENT_CONTEXT EventContext, + __in ULONG Flags, + __in IRP_ENTRY_CONTEXT *IrpContext) { + PDokanVCB vcb = DeviceObject->DeviceExtension; NTSTATUS status; DDbgPrint("==> DokanRegisterPendingIrp\n"); if (GetIdentifierType(vcb) != VCB) { + DDbgPrint(" IdentifierType is not VCB\n"); + DokanFreeEventContext(EventContext); + return STATUS_INVALID_PARAMETER; } status = RegisterPendingIrpMain(DeviceObject, Irp, EventContext->SerialNumber, - &vcb->Dcb->PendingIrp, Flags, TRUE); + &vcb->Dcb->PendingIrp, Flags, TRUE, IrpContext); if (status == STATUS_PENDING) { + DokanEventNotification(&vcb->Dcb->NotifyEvent, EventContext); - } else { + } + else { + DokanFreeEventContext(EventContext); } DDbgPrint("<== DokanRegisterPendingIrp\n"); + return status; } @@ -277,7 +308,8 @@ DokanRegisterPendingIrpForEvent(__in PDEVICE_OBJECT DeviceObject, 0, // SerialNumber &vcb->Dcb->PendingEvent, 0, // Flags - TRUE); + TRUE, + NULL); } NTSTATUS @@ -295,7 +327,8 @@ DokanRegisterPendingIrpForService(__in PDEVICE_OBJECT DeviceObject, 0, // SerialNumber &dokanGlobal->PendingService, 0, // Flags - FALSE); + FALSE, + NULL); } // When user-mode file system application returns EventInformation, diff --git a/sys/fileinfo.c b/sys/fileinfo.c index aa9a4348f..205b0be79 100644 --- a/sys/fileinfo.c +++ b/sys/fileinfo.c @@ -204,7 +204,7 @@ DokanDispatchQueryInformation(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { fcb->FileName.Length); // register this IRP to pending IPR list - status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0); + status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0, NULL); } __finally { @@ -293,6 +293,7 @@ DokanDispatchSetInformation(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { PEVENT_CONTEXT eventContext; BOOLEAN isPagingIo = FALSE; PFILE_END_OF_FILE_INFORMATION pInfoEoF = NULL; + ULONG bufferOffset; vcb = DeviceObject->DeviceExtension; @@ -402,7 +403,7 @@ DokanDispatchSetInformation(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { // calcurate the size of EVENT_CONTEXT // it is sum of file name length and size of FileInformation - ULONG bufferOffset = FIELD_OFFSET(EVENT_CONTEXT, Operation.SetFile.FileName) + fcb->FileName.Length + sizeof(WCHAR); + bufferOffset = FIELD_OFFSET(EVENT_CONTEXT, Operation.SetFile.FileName) + fcb->FileName.Length + sizeof(WCHAR); // the set file information must be ptr aligned bufferOffset = (ULONG)ALIGN_UP(bufferOffset, ULONG_PTR); @@ -507,7 +508,7 @@ DokanDispatchSetInformation(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { } // register this IRP to waiting IRP list and make it pending status - status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0); + status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0, NULL); } __finally { diff --git a/sys/flush.c b/sys/flush.c index e7ce46536..670161248 100644 --- a/sys/flush.c +++ b/sys/flush.c @@ -95,7 +95,7 @@ DokanDispatchFlush(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { } // register this IRP to waiting IRP list and make it pending status - status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0); + status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0, NULL); } __finally { diff --git a/sys/lock.c b/sys/lock.c index 64b7e6b86..a6beec912 100644 --- a/sys/lock.c +++ b/sys/lock.c @@ -214,7 +214,7 @@ DokanDispatchLock(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { eventContext->Operation.Lock.Key = irpSp->Parameters.LockControl.Key; // register this IRP to waiting IRP list and make it pending status - status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0); + status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0, NULL); } else { status = DokanCommonLockControl(Irp); completeIrp = FALSE; diff --git a/sys/public.h b/sys/public.h index f6745f8ec..3b2aa7a1b 100644 --- a/sys/public.h +++ b/sys/public.h @@ -164,6 +164,7 @@ typedef struct _DOKAN_IO_SECURITY_CONTEXT { } DOKAN_IO_SECURITY_CONTEXT, *PDOKAN_IO_SECURITY_CONTEXT; typedef struct _CREATE_CONTEXT { + HANDLE AccessToken; DOKAN_IO_SECURITY_CONTEXT_INTERMEDIATE SecurityContext; ULONG FileAttributes; ULONG CreateOptions; @@ -261,6 +262,7 @@ typedef struct _SECURITY_CONTEXT { } SECURITY_CONTEXT, *PSECURITY_CONTEXT; typedef struct _SET_SECURITY_CONTEXT { + HANDLE AccessToken; SECURITY_INFORMATION SecurityInformation; ULONG BufferLength; ULONG BufferOffset; diff --git a/sys/read.c b/sys/read.c index 3f621c5bd..8c2e3c6b1 100644 --- a/sys/read.c +++ b/sys/read.c @@ -246,7 +246,7 @@ Return Value: } // register this IRP to pending IPR list and make it pending status - status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0); + status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0, NULL); } __finally { DokanCompleteIrpRequest(Irp, status, readLength); diff --git a/sys/security.c b/sys/security.c index d98ef4b53..49bd28681 100644 --- a/sys/security.c +++ b/sys/security.c @@ -121,7 +121,7 @@ DokanDispatchQuerySecurity(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { RtlCopyMemory(eventContext->Operation.Security.FileName, fcb->FileName.Buffer, fcb->FileName.Length); - status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, flags); + status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, flags, NULL); } __finally { @@ -194,6 +194,7 @@ VOID DokanCompleteQuerySecurity(__in PIRP_ENTRY IrpEntry, NTSTATUS DokanDispatchSetSecurity(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { + PIO_STACK_LOCATION irpSp; PDokanVCB vcb; PDokanDCB dcb; @@ -207,38 +208,50 @@ DokanDispatchSetSecurity(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { ULONG securityDescLength; ULONG eventLength; PEVENT_CONTEXT eventContext; + IRP_ENTRY_CONTEXT irpContext; + ULONG bufferOffset; __try { + DDbgPrint("==> DokanSetSecurity\n"); irpSp = IoGetCurrentIrpStackLocation(Irp); fileObject = irpSp->FileObject; if (fileObject == NULL) { + DDbgPrint(" fileObject == NULL\n"); status = STATUS_INVALID_PARAMETER; __leave; } vcb = DeviceObject->DeviceExtension; + if (GetIdentifierType(vcb) != VCB) { + DbgPrint(" DeviceExtension != VCB\n"); status = STATUS_INVALID_PARAMETER; __leave; } + dcb = vcb->Dcb; DDbgPrint(" ProcessId %lu\n", IoGetRequestorProcessId(Irp)); DokanPrintFileName(fileObject); ccb = fileObject->FsContext2; + if (ccb == NULL) { + DDbgPrint(" ccb == NULL\n"); status = STATUS_INVALID_PARAMETER; __leave; } + fcb = ccb->Fcb; + if (fcb == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; __leave; } @@ -246,18 +259,27 @@ DokanDispatchSetSecurity(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { securityInfo = &irpSp->Parameters.SetSecurity.SecurityInformation; if (*securityInfo & OWNER_SECURITY_INFORMATION) { + DDbgPrint(" OWNER_SECURITY_INFORMATION\n"); } + if (*securityInfo & GROUP_SECURITY_INFORMATION) { + DDbgPrint(" GROUP_SECURITY_INFORMATION\n"); } + if (*securityInfo & DACL_SECURITY_INFORMATION) { + DDbgPrint(" DACL_SECURITY_INFORMATION\n"); } + if (*securityInfo & SACL_SECURITY_INFORMATION) { + DDbgPrint(" SACL_SECURITY_INFORMATION\n"); } + if (*securityInfo & LABEL_SECURITY_INFORMATION) { + DDbgPrint(" LABEL_SECURITY_INFORMATION\n"); } @@ -266,38 +288,68 @@ DokanDispatchSetSecurity(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { // Assumes the parameter is self relative SD. securityDescLength = RtlLengthSecurityDescriptor(securityDescriptor); - eventLength = - sizeof(EVENT_CONTEXT) + securityDescLength + fcb->FileName.Length; + bufferOffset = FIELD_OFFSET(EVENT_CONTEXT, Operation.SetSecurity.FileName) + fcb->FileName.Length + sizeof(WCHAR); + + // the set security information must be ptr aligned + bufferOffset = (ULONG)ALIGN_UP(bufferOffset, ULONG_PTR); + + eventLength = max(sizeof(EVENT_CONTEXT), bufferOffset + securityDescLength); if (EVENT_CONTEXT_MAX_SIZE < eventLength) { + // TODO: Handle this case like DispatchWrite. DDbgPrint(" SecurityDescriptor is too big: %d (limit %d)\n", eventLength, EVENT_CONTEXT_MAX_SIZE); + status = STATUS_INSUFFICIENT_RESOURCES; + __leave; } eventContext = AllocateEventContext(vcb->Dcb, Irp, eventLength, ccb); if (eventContext == NULL) { + status = STATUS_INSUFFICIENT_RESOURCES; __leave; } + + RtlZeroMemory(&irpContext, sizeof(irpContext)); + + status = DokanCreateProcessAccessToken( + &irpContext.Security.UserModeAccessToken, + &irpContext.Security.Process); + + if(!NT_SUCCESS(status)) { + + DokanFreeEventContext(eventContext); + __leave; + } + eventContext->Context = ccb->UserContext; + eventContext->Operation.SetSecurity.AccessToken = irpContext.Security.UserModeAccessToken; eventContext->Operation.SetSecurity.SecurityInformation = *securityInfo; eventContext->Operation.SetSecurity.BufferLength = securityDescLength; - eventContext->Operation.SetSecurity.BufferOffset = - FIELD_OFFSET(EVENT_CONTEXT, Operation.SetSecurity.FileName[0]) + - fcb->FileName.Length + sizeof(WCHAR); - RtlCopyMemory((PCHAR)eventContext + - eventContext->Operation.SetSecurity.BufferOffset, - securityDescriptor, securityDescLength); + eventContext->Operation.SetSecurity.BufferOffset = bufferOffset; + + RtlCopyMemory((PCHAR)eventContext + eventContext->Operation.SetSecurity.BufferOffset, + securityDescriptor, + securityDescLength); eventContext->Operation.SetSecurity.FileNameLength = fcb->FileName.Length; + RtlCopyMemory(eventContext->Operation.SetSecurity.FileName, - fcb->FileName.Buffer, fcb->FileName.Length); + fcb->FileName.Buffer, + fcb->FileName.Length); - status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0); + status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0, &irpContext); + + if(status != STATUS_PENDING) { + + DokanCleanupProcessAccessToken( + irpContext.Security.UserModeAccessToken, + irpContext.Security.Process); + } } __finally { @@ -318,6 +370,13 @@ VOID DokanCompleteSetSecurity(__in PIRP_ENTRY IrpEntry, DDbgPrint("==> DokanCompleteSetSecurity\n"); + if(IrpEntry->ContextInfo.Security.UserModeAccessToken) { + + DokanCleanupProcessAccessToken( + IrpEntry->ContextInfo.Security.UserModeAccessToken, + IrpEntry->ContextInfo.Security.Process); + } + irp = IrpEntry->Irp; irpSp = IrpEntry->IrpSp; @@ -325,13 +384,109 @@ VOID DokanCompleteSetSecurity(__in PIRP_ENTRY IrpEntry, ASSERT(fileObject != NULL); ccb = fileObject->FsContext2; + if (ccb != NULL) { + ccb->UserContext = EventInfo->Context; - } else { + } + else { + DDbgPrint(" ccb == NULL\n"); } DokanCompleteIrpRequest(irp, EventInfo->Status, 0); DDbgPrint("<== DokanCompleteSetSecurity\n"); +} + +NTSTATUS DokanCreateProcessAccessToken( + __out HANDLE *AccessToken, + __out PEPROCESS *Process) { + + SECURITY_SUBJECT_CONTEXT securitySubjectContext; + SECURITY_QUALITY_OF_SERVICE securityQualityOfService; + SECURITY_CLIENT_CONTEXT securityClientContext; + NTSTATUS status = STATUS_SUCCESS; + + if(!AccessToken || !Process) { + + return STATUS_INVALID_PARAMETER; + } + + // Duplicate the subject context access token into an impersonation token + securityQualityOfService.Length = sizeof(securityQualityOfService); + securityQualityOfService.ImpersonationLevel = SecurityIdentification; + securityQualityOfService.ContextTrackingMode = SECURITY_STATIC_TRACKING; + securityQualityOfService.EffectiveOnly = FALSE; + + SeCaptureSubjectContext(&securitySubjectContext); + + SeLockSubjectContext(&securitySubjectContext); + + status = SeCreateClientSecurityFromSubjectContext( + &securitySubjectContext, + &securityQualityOfService, + FALSE, + &securityClientContext); + + SeUnlockSubjectContext(&securitySubjectContext); + + SeReleaseSubjectContext(&securitySubjectContext); + + if(!NT_SUCCESS(status)) { + + DDbgPrint(" Failed to create client security from subject context.\n"); + + return status; + } + + // Get a user-mode handle to the impersonation token + status = ObOpenObjectByPointer(securityClientContext.ClientToken, + 0, 0, TOKEN_QUERY, *SeTokenObjectType, UserMode, AccessToken); + + SeDeleteClientSecurity(&securityClientContext); + + if(!NT_SUCCESS(status)) { + + DDbgPrint(" Failed to create user mode impersonation token.\n"); + + return status; + } + + // Get a pointer to the current process so that we can close the impersonation token later + *Process = PsGetCurrentProcess(); + + ObReferenceObject(*Process); + + return STATUS_SUCCESS; +} + +VOID DokanCleanupProcessAccessToken( + __in HANDLE AccessToken, + __in PEPROCESS Process) { + + DDbgPrint(" Closing access token\n"); + + KAPC_STATE apcState; + NTSTATUS status = STATUS_SUCCESS; + BOOLEAN attach = Process && Process != PsGetCurrentProcess(); + + if(attach) { + + KeStackAttachProcess(Process, &apcState); + } + + status = ObCloseHandle(AccessToken, UserMode); + + if(attach) { + + KeUnstackDetachProcess(&apcState); + } + + ObDereferenceObject(Process); + + if(!NT_SUCCESS(status)) { + + DDbgPrint(" Failed to close user mode access token.\n"); + } } \ No newline at end of file diff --git a/sys/volume.c b/sys/volume.c index 7f7ded39d..57cffa87b 100644 --- a/sys/volume.c +++ b/sys/volume.c @@ -255,7 +255,7 @@ DokanDispatchQueryVolumeInformation(__in PDEVICE_OBJECT DeviceObject, eventContext->Operation.Volume.BufferLength = irpSp->Parameters.QueryVolume.Length; - status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0); + status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0, NULL); } } __finally { diff --git a/sys/write.c b/sys/write.c index 3679e7620..6d518d81c 100644 --- a/sys/write.c +++ b/sys/write.c @@ -245,7 +245,7 @@ DokanDispatchWrite(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { } // register this IRP to IRP waiting list and make it pending status - status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0); + status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0, NULL); // Resuests bigger memory // eventContext will be freed later using @@ -303,7 +303,7 @@ DokanDispatchWrite(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { } // regiters this IRP to IRP wainting list and make it pending status - status = DokanRegisterPendingIrp(DeviceObject, Irp, requestContext, 0); + status = DokanRegisterPendingIrp(DeviceObject, Irp, requestContext, 0, NULL); } } __finally { From 56c49605593dab75a54b9f0c566fd1ca5e3cfc90 Mon Sep 17 00:00:00 2001 From: Keith Newton Date: Tue, 9 Aug 2016 15:32:17 -0400 Subject: [PATCH 13/20] Fixed issues with enumerating file streams #210 Added unit tests for file streams --- UnitTests/FileSystemTests.cpp | 528 +++++++++++++++++++- UnitTests/FileSystemTests.cpp~RF9a2366d.TMP | 486 ------------------ dokan/dokan.h | 8 +- dokan/fileinfo.c | 63 ++- samples/dokan_mirror/mirror.c | 27 +- 5 files changed, 594 insertions(+), 518 deletions(-) delete mode 100644 UnitTests/FileSystemTests.cpp~RF9a2366d.TMP diff --git a/UnitTests/FileSystemTests.cpp b/UnitTests/FileSystemTests.cpp index 0c4100456..340bbef6e 100644 --- a/UnitTests/FileSystemTests.cpp +++ b/UnitTests/FileSystemTests.cpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include using namespace Microsoft::VisualStudio::CppUnitTestFramework; @@ -20,6 +22,9 @@ namespace UnitTests TEST_CLASS(FileSystemTests) { private: + + inline bool IsValidHandle(HANDLE handle) { return handle && handle != INVALID_HANDLE_VALUE; } + std::wstring GetTempPathStr() { WCHAR path[MAX_PATH]; @@ -99,7 +104,7 @@ namespace UnitTests std::wstring FormatString(const wchar_t *str, ...) { size_t size = 256; - wchar_t *buf = (wchar_t*)malloc(size * sizeof(wchar_t)); + wchar_t *buf = static_cast(malloc(size * sizeof(wchar_t))); int written = 0; va_list args; @@ -108,7 +113,7 @@ namespace UnitTests while((written = _vsnwprintf_s(buf, size - 1, size - 1, str, args)) == -1) { size *= 2; - buf = (wchar_t*)realloc(buf, size * sizeof(wchar_t)); + buf = static_cast(realloc(buf, size * sizeof(wchar_t))); if(!buf) { @@ -205,7 +210,7 @@ namespace UnitTests HANDLE handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if(!handle) + if(!IsValidHandle(handle)) { std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); @@ -223,11 +228,11 @@ namespace UnitTests } FILE_BASIC_INFO basicInfo; - basicInfo.ChangeTime = *(LARGE_INTEGER*)&fileInfo.ftLastWriteTime; - basicInfo.CreationTime = *(LARGE_INTEGER*)&fileInfo.ftCreationTime; + basicInfo.ChangeTime = *reinterpret_cast(&fileInfo.ftLastWriteTime); + basicInfo.CreationTime = *reinterpret_cast(&fileInfo.ftCreationTime); basicInfo.FileAttributes = fileInfo.dwFileAttributes; - basicInfo.LastAccessTime = *(LARGE_INTEGER*)&fileInfo.ftLastAccessTime; - basicInfo.LastWriteTime = *(LARGE_INTEGER*)&fileInfo.ftLastWriteTime; + basicInfo.LastAccessTime = *reinterpret_cast(&fileInfo.ftLastAccessTime); + basicInfo.LastWriteTime = *reinterpret_cast(&fileInfo.ftLastWriteTime); basicInfo.CreationTime.QuadPart -= 500; @@ -290,7 +295,7 @@ namespace UnitTests HANDLE handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if(!handle) + if(!IsValidHandle(handle)) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); @@ -327,7 +332,7 @@ namespace UnitTests handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, NULL); - if(!handle) + if(!IsValidHandle(handle)) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); @@ -370,7 +375,7 @@ namespace UnitTests handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_SYSTEM, NULL); - if(!handle) + if(!IsValidHandle(handle)) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); @@ -420,7 +425,7 @@ namespace UnitTests handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL); - if(!handle) + if(!IsValidHandle(handle)) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); @@ -463,7 +468,7 @@ namespace UnitTests handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, NULL); - if(!handle) + if(!IsValidHandle(handle)) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); @@ -497,6 +502,505 @@ namespace UnitTests Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); } + + DeleteTestDirectory(); + } + + // This is test 10 in winfstest + /*TEST_METHOD(TestFileSecurity) + { + + }*/ + + // This is stream test 02 in winfstest + TEST_METHOD(TestFileStreams02) + { + std::wstring testDir = CreateTestDirectory(); + std::wstring testFile = CombinePath(testDir, L"TestFileStreams02.txt"); + std::wstring errMsg; + + DWORD fileDesiredAccess = + FILE_READ_ATTRIBUTES + | READ_CONTROL + | FILE_WRITE_DATA + | FILE_WRITE_ATTRIBUTES + | FILE_WRITE_EA + | FILE_APPEND_DATA + | SYNCHRONIZE + | STANDARD_RIGHTS_READ + | STANDARD_RIGHTS_WRITE + | STANDARD_RIGHTS_EXECUTE; + + HANDLE handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + + if(!IsValidHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + WIN32_FIND_STREAM_DATA findStreamData; + std::vector streams; + + handle = FindFirstStreamW(testFile.c_str(), FindStreamInfoStandard, &findStreamData, 0); + + if(!IsValidHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to find streams for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + do + { + streams.push_back(findStreamData); + + } while(FindNextStreamW(handle, &findStreamData)); + + if(!FindClose(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close streams handle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(streams.size() != 1) + { + errMsg = FormatString( + L"Invalid number of streams (%u) for file \'%s\' - expected 1 stream.", + static_cast(streams.size()), + testFile.c_str()); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(wcscmp(streams[0].cStreamName, L"::$DATA") != 0) + { + errMsg = FormatString( + L"Expected stream \'::$DATA\' and instead got stream \'%s\' for file \'%s\'.", + streams[0].cStreamName, + testFile.c_str()); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!DeleteFileW(testFile.c_str())) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to delete file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + handle = FindFirstStreamW(testFile.c_str(), FindStreamInfoStandard, &findStreamData, 0); + + if((handle && handle != INVALID_HANDLE_VALUE) || GetLastError() != ERROR_FILE_NOT_FOUND) + { + if(handle) + { + FindClose(handle); + } + + errMsg = CreateSystemErrorMessage(FormatString(L"File \'%s\' should have no streams.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + std::wstring fooStreamName = testFile + L":foo"; + std::wstring barStreamName = testFile + L":bar"; + + handle = CreateFileW(fooStreamName.c_str(), fileDesiredAccess, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + + if(!IsValidHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", fooStreamName.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", fooStreamName.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + handle = CreateFileW(barStreamName.c_str(), fileDesiredAccess, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + + if(!IsValidHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", barStreamName.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", barStreamName.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + streams.clear(); + + handle = FindFirstStreamW(testFile.c_str(), FindStreamInfoStandard, &findStreamData, 0); + + if(!IsValidHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to find streams for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + do + { + streams.push_back(findStreamData); + + } while(FindNextStreamW(handle, &findStreamData)); + + if(!FindClose(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close streams handle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(streams.size() != 3) + { + errMsg = FormatString( + L"Invalid number of streams (%u) for file \'%s\' - expected 3 streams.", + static_cast(streams.size()), + testFile.c_str()); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + bool foundDataStream = false; + bool foundFooStream = false; + bool foundBarStream = false; + + for(size_t i = 0; i < streams.size(); ++i) + { + if(wcscmp(streams[i].cStreamName, L"::$DATA") == 0) + { + foundDataStream = true; + } + else if(wcscmp(streams[i].cStreamName, L":foo:$DATA") == 0) + { + foundFooStream = true; + } + else if(wcscmp(streams[i].cStreamName, L":bar:$DATA") == 0) + { + foundBarStream = true; + } + else + { + errMsg = FormatString( + L"Unexpected stream \'%s\' for file \'%s\'.", + streams[i].cStreamName, + testFile.c_str()); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + } + + if(!foundDataStream || !foundFooStream || !foundBarStream) + { + errMsg = FormatString(L"Didn't find expected streams for file \'%s\'.", testFile.c_str()); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!DeleteFileW(testFile.c_str())) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to delete file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + handle = FindFirstStreamW(testFile.c_str(), FindStreamInfoStandard, &findStreamData, 0); + + if((handle && handle != INVALID_HANDLE_VALUE) || GetLastError() != ERROR_FILE_NOT_FOUND) + { + if(handle) + { + FindClose(handle); + } + + errMsg = CreateSystemErrorMessage(FormatString(L"File \'%s\' should have no streams.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!CreateDirectoryW(testFile.c_str(), NULL)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create directory \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + handle = CreateFileW(fooStreamName.c_str(), fileDesiredAccess, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + + if(!IsValidHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", fooStreamName.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", fooStreamName.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + handle = CreateFileW(barStreamName.c_str(), fileDesiredAccess, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + + if(!IsValidHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", barStreamName.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", barStreamName.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + streams.clear(); + + handle = FindFirstStreamW(testFile.c_str(), FindStreamInfoStandard, &findStreamData, 0); + + if(!IsValidHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to find streams for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + do + { + streams.push_back(findStreamData); + + } while(FindNextStreamW(handle, &findStreamData)); + + if(!FindClose(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close streams handle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(streams.size() != 2) + { + errMsg = FormatString( + L"Invalid number of streams (%u) for file \'%s\' - expected 2 streams.", + static_cast(streams.size()), + testFile.c_str()); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + //foundDataStream = false; + foundFooStream = false; + foundBarStream = false; + + for(size_t i = 0; i < streams.size(); ++i) + { + if(wcscmp(streams[i].cStreamName, L":foo:$DATA") == 0) + { + foundFooStream = true; + } + else if(wcscmp(streams[i].cStreamName, L":bar:$DATA") == 0) + { + foundBarStream = true; + } + else + { + errMsg = FormatString( + L"Unexpected stream \'%s\' for file \'%s\'.", + streams[i].cStreamName, + testFile.c_str()); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + } + + if(!foundFooStream || !foundBarStream) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Didn't find expected streams for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!RemoveDirectoryW(testFile.c_str())) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to remove directory \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + wchar_t temp[16]; + + const int FILE_ARRAY_SIZE = 100; + const std::wstring fileArryaBaseName = testFile + L":strm"; + + for(int createIndex = 0; createIndex < FILE_ARRAY_SIZE; ++createIndex) + { + _itow_s(createIndex, temp, 10); + + std::wstring fileName = fileArryaBaseName + temp; + + handle = CreateFileW(fileName.c_str(), fileDesiredAccess, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + + if(!IsValidHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", fileName.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + else if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", fileName.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + } + + streams.clear(); + + handle = FindFirstStreamW(testFile.c_str(), FindStreamInfoStandard, &findStreamData, 0); + + if(!IsValidHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to find streams for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + do + { + streams.push_back(findStreamData); + + } while(FindNextStreamW(handle, &findStreamData)); + + if(!FindClose(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close streams handle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(streams.size() != FILE_ARRAY_SIZE + 1) + { + errMsg = FormatString( + L"Invalid number of streams (%u) for file \'%s\' - expected %u streams.", + static_cast(streams.size()), + testFile.c_str(), + static_cast(FILE_ARRAY_SIZE + 1)); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + foundDataStream = false; + + std::unique_ptr streamsFound = std::make_unique(FILE_ARRAY_SIZE); + ZeroMemory(streamsFound.get(), sizeof(bool) * FILE_ARRAY_SIZE); + + const size_t trimStreamBegin = wcslen(L":strm"); + + for(size_t i = 0; i < streams.size(); ++i) + { + if(wcscmp(streams[i].cStreamName, L"::$DATA") == 0) + { + foundDataStream = true; + } + else + { + if(wcslen(streams[i].cStreamName) <= trimStreamBegin) + { + errMsg = FormatString(L"Invalid stream name \'%s\'.", streams[i].cStreamName); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + wchar_t *indexStrPtr = &streams[i].cStreamName[trimStreamBegin]; + wchar_t *indexStrEndPtr = wcschr(indexStrPtr, L':'); + + if(!indexStrEndPtr) + { + errMsg = FormatString(L"Invalid stream name \'%s\'.", streams[i].cStreamName); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + std::wstring indexStr(indexStrPtr, static_cast(indexStrEndPtr - indexStrPtr)); + + int streamIndex = _wtoi(indexStr.c_str()); + + if((streamIndex == 0 && errno == ERANGE) || streamIndex >= FILE_ARRAY_SIZE) + { + errMsg = FormatString(L"Unexpected stream name \'%s\'.", streams[i].cStreamName); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + streamsFound[streamIndex] = true; + } + } + + if(!foundDataStream) + { + errMsg = FormatString(L"Couldn't find stream \'::$DATA\' for file \'%s\'.", testFile.c_str()); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + for(size_t i = 0; i < FILE_ARRAY_SIZE; ++i) + { + if(!streamsFound[i]) + { + errMsg = FormatString(L"Couldn't find stream \'%u\' for file \'%s\'.", + static_cast(i), + testFile.c_str()); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + } + + if(!DeleteFileW(testFile.c_str())) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to delete file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + handle = FindFirstStreamW(testFile.c_str(), FindStreamInfoStandard, &findStreamData, 0); + + if((handle && handle != INVALID_HANDLE_VALUE) || GetLastError() != ERROR_FILE_NOT_FOUND) + { + if(handle) + { + FindClose(handle); + } + + errMsg = CreateSystemErrorMessage(FormatString(L"File \'%s\' should have no streams.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + DeleteTestDirectory(); } }; } \ No newline at end of file diff --git a/UnitTests/FileSystemTests.cpp~RF9a2366d.TMP b/UnitTests/FileSystemTests.cpp~RF9a2366d.TMP deleted file mode 100644 index f37a29565..000000000 --- a/UnitTests/FileSystemTests.cpp~RF9a2366d.TMP +++ /dev/null @@ -1,486 +0,0 @@ -#include "stdafx.h" -#include "CppUnitTest.h" - -#include -#include -#include -#include - -using namespace Microsoft::VisualStudio::CppUnitTestFramework; - -#define FILESYSTEM_ROOT L"M:\\" -#define FILESYSTEM_TESTDIR L"M:\\MirrorUnitTests" - -const wchar_t* GetErrorCodeStr(const DWORD errCode); - -#define USE_MIRROR 0 - -namespace UnitTests -{ - TEST_CLASS(FileSystemTests) - { - private: - std::wstring GetTempPathStr() - { - WCHAR path[MAX_PATH]; - - GetTempPathW(MAX_PATH, path); - - return path; - } - - std::wstring CombinePath(const std::wstring &left, const std::wstring &right) - { - if(left.length() == 0) - { - return right; - } - - if(right.length() == 0) - { - return left; - } - - const bool leftHasDelimeter = left[left.length() - 1] == L'\\' || left[left.length() - 1] == L'/'; - const bool rightHasDelimeter = right[right.length() - 1] == L'\\' || right[right.length() - 1] == L'/'; - - if(leftHasDelimeter && rightHasDelimeter) - { - return left.substr(0, left.length() - 1) + right; - } - else if(!leftHasDelimeter && !rightHasDelimeter) - { - return left + L"\\" + right; - } - - return left + right; - } - - std::wstring CreateTestDirectory() - { -#if USE_MIRROR - std::wstring testDir = FILESYSTEM_TESTDIR; -#else - std::wstring testDir = CombinePath(GetTempPathStr(), L"MirrorUnitTests"); -#endif - - if(!PathFileExistsW(testDir.c_str())) - { - if(!CreateDirectoryW(testDir.c_str(), NULL)) - { - std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CreateDirectoryW for directory \'%s\'.", testDir.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - } - - return testDir; - } - - void DeleteTestDirectory() - { -#if USE_MIRROR - std::wstring testDir = FILESYSTEM_TESTDIR; -#else - std::wstring testDir = CombinePath(GetTempPathStr(), L"MirrorUnitTests"); -#endif - - if(PathFileExistsW(testDir.c_str())) - { - if(!RemoveDirectoryW(testDir.c_str())) - { - std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to RemoveDirectoryW for directory \'%s\'.", testDir.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - } - } - - std::wstring FormatString(const wchar_t *str, ...) - { - size_t size = 256; - wchar_t *buf = (wchar_t*)malloc(size * sizeof(wchar_t)); - int written = 0; - - va_list args; - va_start(args, str); - - while((written = _vsnwprintf_s(buf, size - 1, size - 1, str, args)) == -1) - { - size *= 2; - buf = (wchar_t*)realloc(buf, size * sizeof(wchar_t)); - - if(!buf) - { - written = 0; - break; - } - } - - va_end(args); - - assert(static_cast(written) <= size - 2); - - buf[written] = 0; - - std::wstring temp = buf; - - free(buf); - - return temp; - } - - std::wstring GetLastErrorStr(const DWORD lastErr) - { - LPWSTR buffer = NULL; - - // we force english because the error messages are for developers and english is the common language - // developers of all countries use to communicate - DWORD errCode = FormatMessageW( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - lastErr, - MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), - (LPWSTR)&buffer, - 0, - NULL); - - if(errCode == 0) - { - if(GetLastError() == ERROR_RESOURCE_LANG_NOT_FOUND) - { - errCode = FormatMessageW( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - lastErr, - 0, - (LPWSTR)&buffer, - 0, - NULL); - } - - if(errCode == 0) - { - return std::wstring(); - } - } - - std::wstring retStr = buffer; - - LocalFree(buffer); - - return retStr; - } - - std::wstring CreateSystemErrorMessage(const std::wstring &msg) - { - const DWORD errCode = GetLastError(); - const std::wstring errCodeMsg = GetLastErrorStr(errCode); - - return FormatString(L"%s\nError code is \'%s\' (%d).\n%s", - msg.c_str(), - GetErrorCodeStr(errCode), - errCode, - errCodeMsg.c_str()); - } - - public: - - TEST_METHOD(TestFileBasicInfo) - { - std::wstring testDir = CreateTestDirectory(); - std::wstring testFile = CombinePath(testDir, L"TestFileBasicInfo.txt"); - - DWORD fileDesiredAccess = - FILE_READ_ATTRIBUTES - | READ_CONTROL - | FILE_WRITE_DATA - | FILE_WRITE_ATTRIBUTES - | FILE_WRITE_EA - | FILE_APPEND_DATA - | SYNCHRONIZE - | STANDARD_RIGHTS_READ - | STANDARD_RIGHTS_WRITE - | STANDARD_RIGHTS_EXECUTE; - - HANDLE handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - - if(!handle) - { - std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - BY_HANDLE_FILE_INFORMATION fileInfo; - ZeroMemory(&fileInfo, sizeof(fileInfo)); - - if(!GetFileInformationByHandle(handle, &fileInfo)) - { - std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to GetFileInformationByHandle for file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - FILE_BASIC_INFO basicInfo; - basicInfo.ChangeTime = *(LARGE_INTEGER*)&fileInfo.ftLastWriteTime; - basicInfo.CreationTime = *(LARGE_INTEGER*)&fileInfo.ftCreationTime; - basicInfo.FileAttributes = fileInfo.dwFileAttributes; - basicInfo.LastAccessTime = *(LARGE_INTEGER*)&fileInfo.ftLastAccessTime; - basicInfo.LastWriteTime = *(LARGE_INTEGER*)&fileInfo.ftLastWriteTime; - - basicInfo.CreationTime.QuadPart -= 500; - - if(!SetFileInformationByHandle(handle, FileBasicInfo, &basicInfo, sizeof(basicInfo))) - { - std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to SetFileInformationByHandle for file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - BY_HANDLE_FILE_INFORMATION fileInfo2; - ZeroMemory(&fileInfo2, sizeof(fileInfo2)); - - if(!GetFileInformationByHandle(handle, &fileInfo2)) - { - std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to GetFileInformationByHandle for modified file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - Assert::IsTrue(((LARGE_INTEGER*)&fileInfo2.ftCreationTime)->QuadPart == basicInfo.CreationTime.QuadPart, - L"Failed to properly set modified file creation time.", - LINE_INFO()); - - if(!CloseHandle(handle)) - { - std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - if(!DeleteFileW(testFile.c_str())) - { - std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed DeleteFileW for file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - DeleteTestDirectory(); - } - - // This is test 01 in winfstest - TEST_METHOD(TestCreateModifyAttribsDelete) - { - std::wstring testDir = CreateTestDirectory(); - std::wstring testFile = CombinePath(testDir, L"TestCreateModifyAttribsDelete.txt"); - std::wstring errMsg; - - DWORD fileDesiredAccess = - FILE_READ_ATTRIBUTES - | READ_CONTROL - | FILE_WRITE_DATA - | FILE_WRITE_ATTRIBUTES - | FILE_WRITE_EA - | FILE_APPEND_DATA - | SYNCHRONIZE - | STANDARD_RIGHTS_READ - | STANDARD_RIGHTS_WRITE - | STANDARD_RIGHTS_EXECUTE; - - HANDLE handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - - if(!handle) - { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - BY_HANDLE_FILE_INFORMATION fileInfo; - ZeroMemory(&fileInfo, sizeof(fileInfo)); - - if(!GetFileInformationByHandle(handle, &fileInfo)) - { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to GetFileInformationByHandle for file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - if((fileInfo.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) == 0) - { - errMsg = CreateSystemErrorMessage(FormatString(L"FILE_ATTRIBUTE_NORMAL should be set for file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - if(!CloseHandle(handle)) - { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, NULL); - - if(!handle) - { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - ZeroMemory(&fileInfo, sizeof(fileInfo)); - - if(!GetFileInformationByHandle(handle, &fileInfo)) - { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to GetFileInformationByHandle for file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - if((fileInfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0) - { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to set FILE_ATTRIBUTE_READONLY for file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - if(!CloseHandle(handle)) - { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - if(!SetFileAttributesW(testFile.c_str(), FILE_ATTRIBUTE_NORMAL)) - { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed SetFileAttributesW() to set FILE_ATTRIBUTE_NORMAL for file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_SYSTEM, NULL); - - if(!handle) - { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - ZeroMemory(&fileInfo, sizeof(fileInfo)); - - if(!GetFileInformationByHandle(handle, &fileInfo)) - { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to GetFileInformationByHandle for file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - if((fileInfo.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) == 0) - { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to set FILE_ATTRIBUTE_SYSTEM for file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - if(!CloseHandle(handle)) - { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - if(!SetFileAttributesW(testFile.c_str(), FILE_ATTRIBUTE_NORMAL)) - { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed SetFileAttributesW() to set FILE_ATTRIBUTE_NORMAL for file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - if((GetFileAttributesW(testFile.c_str()) & FILE_ATTRIBUTE_NORMAL) == 0) - { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to set FILE_ATTRIBUTE_NORMAL for file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL); - - if(!handle) - { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - ZeroMemory(&fileInfo, sizeof(fileInfo)); - - if(!GetFileInformationByHandle(handle, &fileInfo)) - { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to GetFileInformationByHandle for file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - if((fileInfo.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) == 0) - { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to set FILE_ATTRIBUTE_HIDDEN for file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - if(!CloseHandle(handle)) - { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - if(!DeleteFileW(testFile.c_str())) - { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to delete file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, NULL); - - if(!handle) - { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - if(!CloseHandle(handle)) - { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - if(DeleteFileW(testFile.c_str()) || GetLastError() != ERROR_ACCESS_DENIED) - { - errMsg = CreateSystemErrorMessage(FormatString(L"Successfully deleted file \'%s\' when it was expected to fail.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - if(!SetFileAttributesW(testFile.c_str(), FILE_ATTRIBUTE_NORMAL)) - { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed SetFileAttributesW() to set FILE_ATTRIBUTE_NORMAL for file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - - if(!DeleteFileW(testFile.c_str())) - { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to delete file \'%s\'.", testFile.c_str())); - - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); - } - } - }; -} \ No newline at end of file diff --git a/dokan/dokan.h b/dokan/dokan.h index 5abde5604..a0f0e146d 100644 --- a/dokan/dokan.h +++ b/dokan/dokan.h @@ -147,10 +147,16 @@ typedef int (WINAPI *PFillFindData)(PDOKAN_FIND_FILES_EVENT, PWIN32_FIND_DATAW); // (currently it never returns 1) typedef int (WINAPI *PFillFindDataWithPattern)(PDOKAN_FIND_FILES_PATTERN_EVENT, PWIN32_FIND_DATAW); +typedef enum _DOKAN_STREAM_FIND_RESULT { + + DOKAN_STREAM_BUFFER_CONTINUE = 0, + DOKAN_STREAM_BUFFER_FULL = 1 +} DOKAN_STREAM_FIND_RESULT, *PDOKAN_STREAM_FIND_RESULT; + // FillFindStreamData // is used to add an entry in FindStreams // returns 1 if buffer is full, otherwise 0 -typedef int (WINAPI *PFillFindStreamData)(PDOKAN_FIND_STREAMS_EVENT, PWIN32_FIND_STREAM_DATA); +typedef DOKAN_STREAM_FIND_RESULT (WINAPI *PFillFindStreamData)(PDOKAN_FIND_STREAMS_EVENT, PWIN32_FIND_STREAM_DATA); typedef struct _DOKAN_CREATE_FILE_EVENT { PDOKAN_FILE_INFO DokanFileInfo; diff --git a/dokan/fileinfo.c b/dokan/fileinfo.c index 835b51ba8..2676708b4 100644 --- a/dokan/fileinfo.c +++ b/dokan/fileinfo.c @@ -25,14 +25,15 @@ with this program. If not, see . #include #include -#define DOKAN_STREAM_BUFFER_FULL 1 -#define DOKAN_STREAM_BUFFER_CONTINUE 0 +#define DOKAN_STREAM_ENTRY_ALIGNMENT 8 NTSTATUS DokanFillFileBasicInfo(PFILE_BASIC_INFORMATION BasicInfo, PBY_HANDLE_FILE_INFORMATION FileInfo, PULONG RemainingLength) { + if (*RemainingLength < sizeof(FILE_BASIC_INFORMATION)) { + return STATUS_BUFFER_OVERFLOW; } @@ -287,37 +288,50 @@ DokanFillIdInfo(PFILE_ID_INFORMATION IdInfo, return STATUS_SUCCESS; } -int WINAPI DokanFillFindStreamData(PDOKAN_FIND_STREAMS_EVENT EventInfo, PWIN32_FIND_STREAM_DATA FindStreamData) { +DOKAN_STREAM_FIND_RESULT WINAPI DokanFillFindStreamData( + PDOKAN_FIND_STREAMS_EVENT EventInfo, + PWIN32_FIND_STREAM_DATA FindStreamData) { DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; ULONG offset = (ULONG)ioEvent->EventResult->BufferLength; - ULONG resultBufferSize = IoEventResultBufferSize(ioEvent); + ULONG resultBufferSize = ioEvent->KernelInfo.EventContext.Operation.File.BufferLength; ULONG streamNameLength = (ULONG)wcslen(FindStreamData->cStreamName) * sizeof(WCHAR); - ULONG entrySize = sizeof(FILE_STREAM_INFORMATION) + streamNameLength; + // Must be aligned on a 8-byte boundary. + ULONG entrySize = QuadAlign(sizeof(FILE_STREAM_INFORMATION) + streamNameLength); + + assert(entrySize % DOKAN_STREAM_ENTRY_ALIGNMENT == 0); + + PFILE_STREAM_INFORMATION streamInfo = (PFILE_STREAM_INFORMATION)&ioEvent->EventResult->Buffer[offset]; - if(offset + entrySize > resultBufferSize) { + if(offset + entrySize + streamInfo->NextEntryOffset > resultBufferSize) { return DOKAN_STREAM_BUFFER_FULL; } - PFILE_STREAM_INFORMATION streamInfo = (PFILE_STREAM_INFORMATION)&ioEvent->EventResult->Buffer[offset]; + // 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]; + } + + assert(streamInfo->NextEntryOffset == 0); // Fill the new entry streamInfo->StreamNameLength = streamNameLength; - memcpy(streamInfo->StreamName, FindStreamData->cStreamName, streamNameLength); + memcpy_s(streamInfo->StreamName, streamNameLength, FindStreamData->cStreamName, streamNameLength); streamInfo->StreamSize = FindStreamData->StreamSize; streamInfo->StreamAllocationSize = FindStreamData->StreamSize; - streamInfo->NextEntryOffset = 0; + streamInfo->NextEntryOffset = entrySize; ALIGN_ALLOCATION_SIZE(&streamInfo->StreamAllocationSize, ioEvent->DokanInstance->DokanOptions); - // Must be align on a 8-byte boundary. - offset += QuadAlign(entrySize); ioEvent->EventResult->BufferLength = offset; return DOKAN_STREAM_BUFFER_CONTINUE; @@ -345,7 +359,12 @@ void BeginDispatchQueryInformation(DOKAN_IO_EVENT *EventInfo) { DbgPrint("FileStreamInformation\n"); - if(EventInfo->DokanInstance->DokanOperations->FindStreams) { + // https://msdn.microsoft.com/en-us/library/windows/hardware/ff540364(v=vs.85).aspx + if(EventInfo->KernelInfo.EventContext.Operation.File.BufferLength < sizeof(FILE_STREAM_INFORMATION)) { + + status = STATUS_BUFFER_TOO_SMALL; + } + else if(EventInfo->DokanInstance->DokanOperations->FindStreams) { findStreams->DokanFileInfo = &EventInfo->DokanFileInfo; findStreams->FileName = EventInfo->KernelInfo.EventContext.Operation.File.FileName; @@ -537,9 +556,27 @@ void DOKANAPI DokanEndDispatchGetFileInformation(DOKAN_GET_FILE_INFO_EVENT *Even void DOKANAPI DokanEndDispatchFindStreams(DOKAN_FIND_STREAMS_EVENT *EventInfo, NTSTATUS ResultStatus) { DOKAN_IO_EVENT *ioEvent = (DOKAN_IO_EVENT*)EventInfo; + ULONG resultBufferSize = IoEventResultBufferSize(ioEvent); + PFILE_STREAM_INFORMATION streamInfo = + (PFILE_STREAM_INFORMATION)&ioEvent->EventResult->Buffer[ioEvent->EventResult->BufferLength]; DbgPrint("\tresult = %lx\n", ResultStatus); + // Entries must be 8 byte aligned + assert(streamInfo->NextEntryOffset % DOKAN_STREAM_ENTRY_ALIGNMENT == 0); + + // 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; + ResultStatus = STATUS_BUFFER_OVERFLOW; + } + // STATUS_PENDING should not be passed to this function if(ResultStatus == STATUS_PENDING) { @@ -549,7 +586,7 @@ void DOKANAPI DokanEndDispatchFindStreams(DOKAN_FIND_STREAMS_EVENT *EventInfo, N ioEvent->EventResult->Status = ResultStatus; - DbgPrint("\tDispatchQueryInformation result = %lx\n", ResultStatus); + DbgPrint("\tDokanEndDispatchFindStreams result = 0x%x\n", ResultStatus); SendIoEventResult(ioEvent); } \ No newline at end of file diff --git a/samples/dokan_mirror/mirror.c b/samples/dokan_mirror/mirror.c index 481795009..cb741de13 100644 --- a/samples/dokan_mirror/mirror.c +++ b/samples/dokan_mirror/mirror.c @@ -1637,7 +1637,7 @@ static NTSTATUS DOKAN_CALLBACK MirrorGetVolumeAttributes(DOKAN_GET_VOLUME_ATTRIB EventInfo->Attributes->FileSystemAttributes = FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES | FILE_SUPPORTS_REMOTE_STORAGE | FILE_UNICODE_ON_DISK | - FILE_PERSISTENT_ACLS; + FILE_PERSISTENT_ACLS | FILE_NAMED_STREAMS; EventInfo->Attributes->MaximumComponentNameLength = 256; @@ -1710,6 +1710,7 @@ MirrorFindStreams(DOKAN_FIND_STREAMS_EVENT *EventInfo) { WCHAR filePath[MAX_PATH]; HANDLE hFind; WIN32_FIND_STREAM_DATA findData; + DOKAN_STREAM_FIND_RESULT findResult = DOKAN_STREAM_BUFFER_CONTINUE; DWORD error; int count = 0; @@ -1720,24 +1721,38 @@ MirrorFindStreams(DOKAN_FIND_STREAMS_EVENT *EventInfo) { hFind = FindFirstStreamW(filePath, FindStreamInfoStandard, &findData, 0); if (hFind == INVALID_HANDLE_VALUE) { + error = GetLastError(); DbgPrint(L"\tinvalid file handle. Error is %u\n\n", error); return DokanNtStatusFromWin32(error); } - EventInfo->FillFindStreamData(EventInfo, &findData); - count++; + if((findResult = EventInfo->FillFindStreamData(EventInfo, &findData)) == DOKAN_STREAM_BUFFER_CONTINUE) { - while (FindNextStreamW(hFind, &findData) != 0) { - EventInfo->FillFindStreamData(EventInfo, &findData); - count++; + count++; + + while(FindNextStreamW(hFind, &findData) != 0 + && (findResult = EventInfo->FillFindStreamData(EventInfo, &findData)) == DOKAN_STREAM_BUFFER_CONTINUE) { + + count++; + } } error = GetLastError(); FindClose(hFind); + if(findResult == DOKAN_STREAM_BUFFER_FULL) { + + DbgPrint(L"\tFindStreams returned %d entries in %s with 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); } From 1b7bf065a818a56ad5a6f3b47429c25267f04812 Mon Sep 17 00:00:00 2001 From: Keith Newton Date: Tue, 9 Aug 2016 16:27:54 -0400 Subject: [PATCH 14/20] Compilation fixes for Cygwin FUSE and x86 Fixed a bunch of x86 warnings --- dokan/directory.c | 5 ++++- dokan/dokan.c | 12 ++++++------ dokan/dokani.h | 22 ++++++++++++++++++++++ sys/directory.c | 2 +- sys/fileinfo.c | 8 ++++---- sys/read.c | 2 +- sys/security.c | 4 +++- sys/volume.c | 2 +- sys/write.c | 2 +- 9 files changed, 43 insertions(+), 16 deletions(-) diff --git a/dokan/directory.c b/dokan/directory.c index e91352904..7249ad5d3 100644 --- a/dokan/directory.c +++ b/dokan/directory.c @@ -338,7 +338,10 @@ LONG MatchFiles(DOKAN_IO_EVENT *EventInfo, DOKAN_VECTOR *dirList) { ULONG entrySize = DokanFillDirectoryInformation( EventInfo->KernelInfo.EventContext.Operation.Directory.FileInformationClass, - currentBuffer, &lengthRemaining, &find->FindData, index + 1, + currentBuffer, + &lengthRemaining, + &find->FindData, + index + 1, EventInfo->DokanInstance); // buffer is full diff --git a/dokan/dokan.c b/dokan/dokan.c index 1d862c60c..8a23f33be 100644 --- a/dokan/dokan.c +++ b/dokan/dokan.c @@ -1943,9 +1943,9 @@ void* DokanMallocImpl(size_t size, const char *fileName, int lineNumber) { PDokanMalloc dokanMalloc = NULL; -#if INTPTR_MAX == INT64_MAX +#if _M_X64 dokanMalloc = (PDokanMalloc)InterlockedAdd64((volatile LONG64*)&g_DokanMalloc, 0); -#elif INTPTR_MAX == INT32_MAX +#elif _M_IX86 dokanMalloc = (PDokanMalloc)InterlockedAdd((volatile LONG*)&g_DokanMalloc, 0); #else #error Unsupported architecture! @@ -1963,9 +1963,9 @@ void DokanFreeImpl(void *userData) { PDokanFree dokanFree = NULL; -#if INTPTR_MAX == INT64_MAX +#if _M_X64 dokanFree = (PDokanFree)InterlockedAdd64((volatile LONG64*)&g_DokanFree, 0); -#elif INTPTR_MAX == INT32_MAX +#elif _M_IX86 dokanFree = (PDokanFree)InterlockedAdd((volatile LONG*)&g_DokanFree, 0); #else #error Unsupported architecture! @@ -1985,9 +1985,9 @@ void* DokanReallocImpl(void *userData, size_t newSize, const char *fileName, int PDokanRealloc dokanRealloc = NULL; -#if INTPTR_MAX == INT64_MAX +#if _M_X64 dokanRealloc = (PDokanRealloc)InterlockedAdd64((volatile LONG64*)&g_DokanRealloc, 0); -#elif INTPTR_MAX == INT32_MAX +#elif _M_IX86 dokanRealloc = (PDokanRealloc)InterlockedAdd((volatile LONG*)&g_DokanRealloc, 0); #else #error Unsupported architecture! diff --git a/dokan/dokani.h b/dokan/dokani.h index 1dbf1769b..7756fc844 100644 --- a/dokan/dokani.h +++ b/dokan/dokani.h @@ -31,6 +31,28 @@ with this program. If not, see . #include "dokanc.h" #include "list.h" +// In appveyor FUSE gets compiled using GCC in Cygwin which doesn't recognize +// the following definitions +#ifndef _MSC_VER + +#ifndef _Inout_ +#define _Inout_ +#endif + +#ifndef _Inout_opt_ +#define _Inout_opt_ +#endif + +#ifndef _In_ +#define _In_ +#endif + +#ifndef _Out_ +#define _Out_ +#endif + +#endif + #ifdef __cplusplus extern "C" { #endif diff --git a/sys/directory.c b/sys/directory.c index d96b96ea7..1bbffa4a3 100644 --- a/sys/directory.c +++ b/sys/directory.c @@ -337,7 +337,7 @@ VOID DokanCompleteDirectoryControl(__in PIRP_ENTRY IrpEntry, RtlZeroMemory(buffer, bufferLen); // DDbgPrint(" copy DirectoryInfo\n"); - RtlCopyMemory(buffer, EventInfo->Buffer, EventInfo->BufferLength); + RtlCopyMemory(buffer, EventInfo->Buffer, (SIZE_T)EventInfo->BufferLength); DDbgPrint(" eventInfo->Directory.Index = %lu\n", EventInfo->Operation.Directory.Index); diff --git a/sys/fileinfo.c b/sys/fileinfo.c index 205b0be79..f2d148a15 100644 --- a/sys/fileinfo.c +++ b/sys/fileinfo.c @@ -257,7 +257,7 @@ VOID DokanCompleteQueryInformation(__in PIRP_ENTRY IrpEntry, ASSERT(buffer != NULL); RtlZeroMemory(buffer, bufferLen); - RtlCopyMemory(buffer, EventInfo->Buffer, EventInfo->BufferLength); + RtlCopyMemory(buffer, EventInfo->Buffer, (SIZE_T)EventInfo->BufferLength); // written bytes info = (ULONG)EventInfo->BufferLength; @@ -594,7 +594,7 @@ VOID DokanCompleteSetInformation(__in PIRP_ENTRY IrpEntry, oldFileName.MaximumLength = (USHORT)fcb->FileName.Length; // copy new file name - buffer = ExAllocatePool(EventInfo->BufferLength + sizeof(WCHAR)); + buffer = ExAllocatePool((SIZE_T)(EventInfo->BufferLength + sizeof(WCHAR))); if (buffer == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; @@ -609,9 +609,9 @@ VOID DokanCompleteSetInformation(__in PIRP_ENTRY IrpEntry, ASSERT(fcb->FileName.Buffer != NULL); RtlZeroMemory(fcb->FileName.Buffer, - EventInfo->BufferLength + sizeof(WCHAR)); + (SIZE_T)(EventInfo->BufferLength + sizeof(WCHAR))); RtlCopyMemory(fcb->FileName.Buffer, EventInfo->Buffer, - EventInfo->BufferLength); + (SIZE_T)EventInfo->BufferLength); fcb->FileName.Length = (USHORT)EventInfo->BufferLength; fcb->FileName.MaximumLength = (USHORT)EventInfo->BufferLength; diff --git a/sys/read.c b/sys/read.c index 8c2e3c6b1..1f09fd70e 100644 --- a/sys/read.c +++ b/sys/read.c @@ -305,7 +305,7 @@ VOID DokanCompleteRead(__in PIRP_ENTRY IrpEntry, } else { RtlZeroMemory(buffer, bufferLen); - RtlCopyMemory(buffer, EventInfo->Buffer, EventInfo->BufferLength); + RtlCopyMemory(buffer, EventInfo->Buffer, (SIZE_T)EventInfo->BufferLength); // read length which is actually read readLength = (ULONG)EventInfo->BufferLength; diff --git a/sys/security.c b/sys/security.c index 49bd28681..7ccd6d79f 100644 --- a/sys/security.c +++ b/sys/security.c @@ -157,7 +157,9 @@ VOID DokanCompleteQuerySecurity(__in PIRP_ENTRY IrpEntry, if (EventInfo->Status == STATUS_SUCCESS && EventInfo->BufferLength <= bufferLength && buffer != NULL) { - RtlCopyMemory(buffer, EventInfo->Buffer, EventInfo->BufferLength); + + RtlCopyMemory(buffer, EventInfo->Buffer, (SIZE_T)EventInfo->BufferLength); + info = (ULONG)EventInfo->BufferLength; status = STATUS_SUCCESS; diff --git a/sys/volume.c b/sys/volume.c index 57cffa87b..3cea9a225 100644 --- a/sys/volume.c +++ b/sys/volume.c @@ -339,7 +339,7 @@ VOID DokanCompleteQueryVolumeInformation(__in PIRP_ENTRY IrpEntry, } RtlZeroMemory(buffer, bufferLen); - RtlCopyMemory(buffer, EventInfo->Buffer, EventInfo->BufferLength); + RtlCopyMemory(buffer, EventInfo->Buffer, (SIZE_T)EventInfo->BufferLength); // the written length info = (ULONG)EventInfo->BufferLength; diff --git a/sys/write.c b/sys/write.c index 6d518d81c..8acc8d0b3 100644 --- a/sys/write.c +++ b/sys/write.c @@ -341,7 +341,7 @@ VOID DokanCompleteWrite(__in PIRP_ENTRY IrpEntry, status = EventInfo->Status; irp->IoStatus.Status = status; - irp->IoStatus.Information = EventInfo->BufferLength; + irp->IoStatus.Information = (ULONG_PTR)EventInfo->BufferLength; if (NT_SUCCESS(status) && EventInfo->BufferLength != 0 && (fileObject->Flags & FO_SYNCHRONOUS_IO) && !(irp->Flags & IRP_PAGING_IO)) { From 53e4d26ed00e7aa5f10f77dd492dc4e14b1e7488 Mon Sep 17 00:00:00 2001 From: Keith Newton Date: Tue, 9 Aug 2016 17:15:29 -0400 Subject: [PATCH 15/20] Added more unit tests #210 --- UnitTests/FileSystemTests.cpp | 299 ++++++++++++++++++++++++++++++++++ 1 file changed, 299 insertions(+) diff --git a/UnitTests/FileSystemTests.cpp b/UnitTests/FileSystemTests.cpp index 340bbef6e..77a23d215 100644 --- a/UnitTests/FileSystemTests.cpp +++ b/UnitTests/FileSystemTests.cpp @@ -1002,5 +1002,304 @@ namespace UnitTests DeleteTestDirectory(); } + + // This is test 08 in winfstest + TEST_METHOD(TestSharedFileHandles) + { + std::wstring testDir = CreateTestDirectory(); + std::wstring testFile = CombinePath(testDir, L"TestSharedFilehandles.txt"); + std::wstring errMsg; + + HANDLE origHandle = CreateFileW( + testFile.c_str(), + GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if(!IsValidHandle(origHandle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + HANDLE sharedHandle = CreateFileW( + testFile.c_str(), + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if(!IsValidHandle(sharedHandle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to open shared handle for file \'%s\'.", testFile.c_str())); + + CloseHandle(origHandle); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!DeleteFileW(testFile.c_str())) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to set pending delete for file \'%s\'.", testFile.c_str())); + + CloseHandle(sharedHandle); + CloseHandle(origHandle); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + HANDLE sharedHandle2 = CreateFileW( + testFile.c_str(), + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if(IsValidHandle(sharedHandle2) || GetLastError() != ERROR_ACCESS_DENIED) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Expected file \'%s\' with pending delete to prevent shared handle from being opened.", testFile.c_str())); + + if(IsValidHandle(sharedHandle2)) + { + CloseHandle(sharedHandle2); + } + + CloseHandle(sharedHandle); + CloseHandle(origHandle); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(sharedHandle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close shared handle for file \'%s\'.", testFile.c_str())); + + CloseHandle(origHandle); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(origHandle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + origHandle = CreateFileW( + testFile.c_str(), + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if(IsValidHandle(sharedHandle2) || GetLastError() != ERROR_FILE_NOT_FOUND) + { + errMsg = CreateSystemErrorMessage(FormatString(L"File \'%s\' exists while it was expected to be deleted on close.", testFile.c_str())); + + if(IsValidHandle(origHandle)) + { + CloseHandle(origHandle); + } + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!CreateDirectoryW(testFile.c_str(), NULL)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create directory \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + std::wstring fooPath = CombinePath(testFile, L"foo.txt"); + + origHandle = CreateFileW( + fooPath.c_str(), + GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if(!IsValidHandle(origHandle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", fooPath.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!DeleteFileW(fooPath.c_str())) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to set pending delete for file \'%s\'.", fooPath.c_str())); + + CloseHandle(origHandle); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + std::wstring filter = CombinePath(testFile, L"*"); + + WIN32_FIND_DATAW findData; + std::vector files; + HANDLE findHandle = FindFirstFileW(filter.c_str(), &findData); + + if(!IsValidHandle(findHandle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to find files for filter \'%s\'.", filter.c_str())); + + CloseHandle(origHandle); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + do + { + files.push_back(findData); + + } while(FindNextFileW(findHandle, &findData)); + + FindClose(findHandle); + + if(files.size() != 3) + { + errMsg = FormatString(L"FindFiles() expected to return 3 files but instead returned %u files for filter \'%s\'.", + static_cast(files.size()), + filter.c_str()); + + CloseHandle(origHandle); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + bool foundCurrentDir = false; + bool foundParentDir = false; + bool foundFoo = false; + + for(size_t i = 0; i < files.size(); ++i) + { + if(wcscmp(files[i].cFileName, L".") == 0) + { + foundCurrentDir = true; + } + else if(wcscmp(files[i].cFileName, L"..") == 0) + { + foundParentDir = true; + } + else if(wcscmp(files[i].cFileName, L"foo.txt") == 0) + { + foundFoo = true; + } + else + { + errMsg = FormatString(L"FindFiles() returned an unexpected file \'%s\' for filter \'%s\'.", + files[i].cFileName, + filter.c_str()); + + CloseHandle(origHandle); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + } + + if(!foundCurrentDir || !foundParentDir || !foundFoo) + { + errMsg = FormatString(L"FindFiles() did not return the expected list of files for filter \'%s\'.", + filter.c_str()); + + CloseHandle(origHandle); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(origHandle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", fooPath.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + files.clear(); + + findHandle = FindFirstFileW(filter.c_str(), &findData); + + if(!IsValidHandle(findHandle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to find files for filter \'%s\'.", filter.c_str())); + + CloseHandle(origHandle); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + do + { + files.push_back(findData); + + } while(FindNextFileW(findHandle, &findData)); + + FindClose(findHandle); + + if(files.size() != 2) + { + errMsg = FormatString(L"FindFiles() expected to return 2 files but instead returned %u files for filter \'%s\'.", + static_cast(files.size()), + filter.c_str()); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + foundCurrentDir = false; + foundParentDir = false; + //foundFoo = false; + + for(size_t i = 0; i < files.size(); ++i) + { + if(wcscmp(files[i].cFileName, L".") == 0) + { + foundCurrentDir = true; + } + else if(wcscmp(files[i].cFileName, L"..") == 0) + { + foundParentDir = true; + } + else + { + errMsg = FormatString(L"FindFiles() returned an unexpected file \'%s\' for filter \'%s\'.", + files[i].cFileName, + filter.c_str()); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + } + + if(!foundCurrentDir || !foundParentDir) + { + errMsg = FormatString(L"FindFiles() did not return the expected list of files for filter \'%s\'.", + filter.c_str()); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + if(!RemoveDirectoryW(testFile.c_str())) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to remove directory \'%s\'.", testFile.c_str())); + + Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + } + + DeleteTestDirectory(); + } }; } \ No newline at end of file From 6e2df36178a0bc883ebfb9c62a86598b9f07e7a6 Mon Sep 17 00:00:00 2001 From: Keith Newton Date: Tue, 9 Aug 2016 19:57:29 -0400 Subject: [PATCH 16/20] Added FSX to unit tests #210 --- UnitTests/FileSystemTests.cpp | 341 +++- UnitTests/UnitTests.vcxproj | 22 +- UnitTests/UnitTests.vcxproj.filters | 49 +- UnitTests/fsx/fsx.cpp | 2642 +++++++++++++++++++++++++++ UnitTests/fsx/win/getopt.cpp | 78 + UnitTests/fsx/win/getopt.h | 32 + UnitTests/fsx/win/mman.cpp | 414 +++++ UnitTests/fsx/win/mman.h | 58 + UnitTests/fsx/win/random.cpp | 494 +++++ 9 files changed, 4011 insertions(+), 119 deletions(-) create mode 100644 UnitTests/fsx/fsx.cpp create mode 100644 UnitTests/fsx/win/getopt.cpp create mode 100644 UnitTests/fsx/win/getopt.h create mode 100644 UnitTests/fsx/win/mman.cpp create mode 100644 UnitTests/fsx/win/mman.h create mode 100644 UnitTests/fsx/win/random.cpp diff --git a/UnitTests/FileSystemTests.cpp b/UnitTests/FileSystemTests.cpp index 77a23d215..eccb9a144 100644 --- a/UnitTests/FileSystemTests.cpp +++ b/UnitTests/FileSystemTests.cpp @@ -13,9 +13,14 @@ using namespace Microsoft::VisualStudio::CppUnitTestFramework; #define FILESYSTEM_ROOT L"M:\\" #define FILESYSTEM_TESTDIR L"M:\\MirrorUnitTests" +#define FILESYSTEM_ROOT_A "M:\\" +#define FILESYSTEM_TESTDIR_A "M:\\MirrorUnitTests" + const wchar_t* GetErrorCodeStr(const DWORD errCode); -#define USE_MIRROR 1 +#define USE_MIRROR 0 + +int fsx_main(int argc, char **argv); namespace UnitTests { @@ -34,6 +39,15 @@ namespace UnitTests return path; } + std::string GetTempPathStrA() + { + char path[MAX_PATH]; + + GetTempPathA(MAX_PATH, path); + + return path; + } + std::wstring CombinePath(const std::wstring &left, const std::wstring &right) { if(left.length() == 0) @@ -61,6 +75,33 @@ namespace UnitTests return left + right; } + std::string CombinePath(const std::string &left, const std::string &right) + { + if(left.length() == 0) + { + return right; + } + + if(right.length() == 0) + { + return left; + } + + const bool leftHasDelimeter = left[left.length() - 1] == '\\' || left[left.length() - 1] == '/'; + const bool rightHasDelimeter = right[right.length() - 1] == '\\' || right[right.length() - 1] == '/'; + + if(leftHasDelimeter && rightHasDelimeter) + { + return left.substr(0, left.length() - 1) + right; + } + else if(!leftHasDelimeter && !rightHasDelimeter) + { + return left + "\\" + right; + } + + return left + right; + } + std::wstring CreateTestDirectory() { #if USE_MIRROR @@ -75,7 +116,28 @@ namespace UnitTests { std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CreateDirectoryW for directory \'%s\'.", testDir.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + } + + return testDir; + } + + std::string CreateTestDirectoryA() + { +#if USE_MIRROR + std::string testDir = FILESYSTEM_TESTDIR_A; +#else + std::string testDir = CombinePath(GetTempPathStrA(), "MirrorUnitTests"); +#endif + + if(!PathFileExistsA(testDir.c_str())) + { + if(!CreateDirectoryA(testDir.c_str(), NULL)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CreateDirectoryW for directory \'%s\'.", testDir.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); } } @@ -96,7 +158,7 @@ namespace UnitTests { std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to RemoveDirectoryW for directory \'%s\'.", testDir.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } } } @@ -135,6 +197,40 @@ namespace UnitTests return temp; } + std::string FormatString(const char *str, ...) + { + size_t size = 256; + char *buf = static_cast(malloc(size)); + int written = 0; + + va_list args; + va_start(args, str); + + while((written = _vsnprintf_s(buf, size - 1, size - 1, str, args)) == -1) + { + size *= 2; + buf = static_cast(realloc(buf, size)); + + if(!buf) + { + written = 0; + break; + } + } + + va_end(args); + + assert(static_cast(written) <= size - 2); + + buf[written] = 0; + + std::string temp = buf; + + free(buf); + + return temp; + } + std::wstring GetLastErrorStr(const DWORD lastErr) { LPWSTR buffer = NULL; @@ -214,7 +310,7 @@ namespace UnitTests { std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } BY_HANDLE_FILE_INFORMATION fileInfo; @@ -224,7 +320,7 @@ namespace UnitTests { std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to GetFileInformationByHandle for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } FILE_BASIC_INFO basicInfo; @@ -240,7 +336,7 @@ namespace UnitTests { std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to SetFileInformationByHandle for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } BY_HANDLE_FILE_INFORMATION fileInfo2; @@ -250,7 +346,7 @@ namespace UnitTests { std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to GetFileInformationByHandle for modified file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } Assert::IsTrue(((LARGE_INTEGER*)&fileInfo2.ftCreationTime)->QuadPart == basicInfo.CreationTime.QuadPart, @@ -261,14 +357,14 @@ namespace UnitTests { std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(!DeleteFileW(testFile.c_str())) { std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed DeleteFileW for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } DeleteTestDirectory(); @@ -299,7 +395,7 @@ namespace UnitTests { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } BY_HANDLE_FILE_INFORMATION fileInfo; @@ -311,7 +407,7 @@ namespace UnitTests errMsg = CreateSystemErrorMessage(FormatString(L"Failed to GetFileInformationByHandle for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if((fileInfo.dwFileAttributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) != 0) @@ -320,14 +416,14 @@ namespace UnitTests errMsg = CreateSystemErrorMessage(FormatString(L"FILE_ATTRIBUTE_NORMAL should be set for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(!CloseHandle(handle)) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, NULL); @@ -336,7 +432,7 @@ namespace UnitTests { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } ZeroMemory(&fileInfo, sizeof(fileInfo)); @@ -347,7 +443,7 @@ namespace UnitTests errMsg = CreateSystemErrorMessage(FormatString(L"Failed to GetFileInformationByHandle for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if((fileInfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0) @@ -356,21 +452,21 @@ namespace UnitTests errMsg = CreateSystemErrorMessage(FormatString(L"Failed to set FILE_ATTRIBUTE_READONLY for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(!CloseHandle(handle)) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(!SetFileAttributesW(testFile.c_str(), FILE_ATTRIBUTE_NORMAL)) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed SetFileAttributesW() to set FILE_ATTRIBUTE_NORMAL for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_SYSTEM, NULL); @@ -379,7 +475,7 @@ namespace UnitTests { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } ZeroMemory(&fileInfo, sizeof(fileInfo)); @@ -390,7 +486,7 @@ namespace UnitTests errMsg = CreateSystemErrorMessage(FormatString(L"Failed to GetFileInformationByHandle for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if((fileInfo.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) == 0) @@ -399,28 +495,28 @@ namespace UnitTests errMsg = CreateSystemErrorMessage(FormatString(L"Failed to set FILE_ATTRIBUTE_SYSTEM for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(!CloseHandle(handle)) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(!SetFileAttributesW(testFile.c_str(), FILE_ATTRIBUTE_NORMAL)) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed SetFileAttributesW() to set FILE_ATTRIBUTE_NORMAL for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if((GetFileAttributesW(testFile.c_str()) & FILE_ATTRIBUTE_NORMAL) == 0) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to set FILE_ATTRIBUTE_NORMAL for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL); @@ -429,7 +525,7 @@ namespace UnitTests { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } ZeroMemory(&fileInfo, sizeof(fileInfo)); @@ -440,7 +536,7 @@ namespace UnitTests errMsg = CreateSystemErrorMessage(FormatString(L"Failed to GetFileInformationByHandle for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if((fileInfo.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) == 0) @@ -449,21 +545,21 @@ namespace UnitTests errMsg = CreateSystemErrorMessage(FormatString(L"Failed to set FILE_ATTRIBUTE_HIDDEN for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(!CloseHandle(handle)) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(!DeleteFileW(testFile.c_str())) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to delete file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, NULL); @@ -472,35 +568,35 @@ namespace UnitTests { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(!CloseHandle(handle)) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(DeleteFileW(testFile.c_str()) || GetLastError() != ERROR_ACCESS_DENIED) { errMsg = CreateSystemErrorMessage(FormatString(L"Successfully deleted file \'%s\' when it was expected to fail.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(!SetFileAttributesW(testFile.c_str(), FILE_ATTRIBUTE_NORMAL)) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed SetFileAttributesW() to set FILE_ATTRIBUTE_NORMAL for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(!DeleteFileW(testFile.c_str())) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to delete file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } DeleteTestDirectory(); @@ -537,14 +633,14 @@ namespace UnitTests { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(!CloseHandle(handle)) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } WIN32_FIND_STREAM_DATA findStreamData; @@ -556,7 +652,7 @@ namespace UnitTests { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to find streams for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } do @@ -569,7 +665,7 @@ namespace UnitTests { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close streams handle for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(streams.size() != 1) @@ -579,7 +675,7 @@ namespace UnitTests static_cast(streams.size()), testFile.c_str()); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(wcscmp(streams[0].cStreamName, L"::$DATA") != 0) @@ -589,14 +685,14 @@ namespace UnitTests streams[0].cStreamName, testFile.c_str()); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(!DeleteFileW(testFile.c_str())) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to delete file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } handle = FindFirstStreamW(testFile.c_str(), FindStreamInfoStandard, &findStreamData, 0); @@ -610,7 +706,7 @@ namespace UnitTests errMsg = CreateSystemErrorMessage(FormatString(L"File \'%s\' should have no streams.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } std::wstring fooStreamName = testFile + L":foo"; @@ -622,14 +718,14 @@ namespace UnitTests { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", fooStreamName.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(!CloseHandle(handle)) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", fooStreamName.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } handle = CreateFileW(barStreamName.c_str(), fileDesiredAccess, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); @@ -638,14 +734,14 @@ namespace UnitTests { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", barStreamName.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(!CloseHandle(handle)) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", barStreamName.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } streams.clear(); @@ -656,7 +752,7 @@ namespace UnitTests { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to find streams for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } do @@ -669,7 +765,7 @@ namespace UnitTests { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close streams handle for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(streams.size() != 3) @@ -679,7 +775,7 @@ namespace UnitTests static_cast(streams.size()), testFile.c_str()); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } bool foundDataStream = false; @@ -707,7 +803,7 @@ namespace UnitTests streams[i].cStreamName, testFile.c_str()); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } } @@ -715,14 +811,14 @@ namespace UnitTests { errMsg = FormatString(L"Didn't find expected streams for file \'%s\'.", testFile.c_str()); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(!DeleteFileW(testFile.c_str())) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to delete file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } handle = FindFirstStreamW(testFile.c_str(), FindStreamInfoStandard, &findStreamData, 0); @@ -736,14 +832,14 @@ namespace UnitTests errMsg = CreateSystemErrorMessage(FormatString(L"File \'%s\' should have no streams.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(!CreateDirectoryW(testFile.c_str(), NULL)) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create directory \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } handle = CreateFileW(fooStreamName.c_str(), fileDesiredAccess, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); @@ -752,14 +848,14 @@ namespace UnitTests { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", fooStreamName.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(!CloseHandle(handle)) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", fooStreamName.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } handle = CreateFileW(barStreamName.c_str(), fileDesiredAccess, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); @@ -768,14 +864,14 @@ namespace UnitTests { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", barStreamName.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(!CloseHandle(handle)) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", barStreamName.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } streams.clear(); @@ -786,7 +882,7 @@ namespace UnitTests { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to find streams for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } do @@ -799,7 +895,7 @@ namespace UnitTests { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close streams handle for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(streams.size() != 2) @@ -809,7 +905,7 @@ namespace UnitTests static_cast(streams.size()), testFile.c_str()); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } //foundDataStream = false; @@ -833,7 +929,7 @@ namespace UnitTests streams[i].cStreamName, testFile.c_str()); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } } @@ -841,14 +937,14 @@ namespace UnitTests { errMsg = CreateSystemErrorMessage(FormatString(L"Didn't find expected streams for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(!RemoveDirectoryW(testFile.c_str())) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to remove directory \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } wchar_t temp[16]; @@ -868,13 +964,13 @@ namespace UnitTests { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", fileName.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } else if(!CloseHandle(handle)) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", fileName.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } } @@ -886,7 +982,7 @@ namespace UnitTests { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to find streams for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } do @@ -899,7 +995,7 @@ namespace UnitTests { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close streams handle for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(streams.size() != FILE_ARRAY_SIZE + 1) @@ -910,7 +1006,7 @@ namespace UnitTests testFile.c_str(), static_cast(FILE_ARRAY_SIZE + 1)); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } foundDataStream = false; @@ -932,7 +1028,7 @@ namespace UnitTests { errMsg = FormatString(L"Invalid stream name \'%s\'.", streams[i].cStreamName); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } wchar_t *indexStrPtr = &streams[i].cStreamName[trimStreamBegin]; @@ -942,7 +1038,7 @@ namespace UnitTests { errMsg = FormatString(L"Invalid stream name \'%s\'.", streams[i].cStreamName); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } std::wstring indexStr(indexStrPtr, static_cast(indexStrEndPtr - indexStrPtr)); @@ -953,7 +1049,7 @@ namespace UnitTests { errMsg = FormatString(L"Unexpected stream name \'%s\'.", streams[i].cStreamName); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } streamsFound[streamIndex] = true; @@ -964,7 +1060,7 @@ namespace UnitTests { errMsg = FormatString(L"Couldn't find stream \'::$DATA\' for file \'%s\'.", testFile.c_str()); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } for(size_t i = 0; i < FILE_ARRAY_SIZE; ++i) @@ -975,7 +1071,7 @@ namespace UnitTests static_cast(i), testFile.c_str()); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } } @@ -983,7 +1079,7 @@ namespace UnitTests { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to delete file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } handle = FindFirstStreamW(testFile.c_str(), FindStreamInfoStandard, &findStreamData, 0); @@ -997,7 +1093,7 @@ namespace UnitTests errMsg = CreateSystemErrorMessage(FormatString(L"File \'%s\' should have no streams.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } DeleteTestDirectory(); @@ -1023,7 +1119,7 @@ namespace UnitTests { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } HANDLE sharedHandle = CreateFileW( @@ -1041,7 +1137,7 @@ namespace UnitTests CloseHandle(origHandle); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(!DeleteFileW(testFile.c_str())) @@ -1051,7 +1147,7 @@ namespace UnitTests CloseHandle(sharedHandle); CloseHandle(origHandle); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } HANDLE sharedHandle2 = CreateFileW( @@ -1075,7 +1171,7 @@ namespace UnitTests CloseHandle(sharedHandle); CloseHandle(origHandle); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(!CloseHandle(sharedHandle)) @@ -1084,14 +1180,14 @@ namespace UnitTests CloseHandle(origHandle); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(!CloseHandle(origHandle)) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } origHandle = CreateFileW( @@ -1112,14 +1208,14 @@ namespace UnitTests CloseHandle(origHandle); } - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(!CreateDirectoryW(testFile.c_str(), NULL)) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create directory \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } std::wstring fooPath = CombinePath(testFile, L"foo.txt"); @@ -1137,7 +1233,7 @@ namespace UnitTests { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", fooPath.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(!DeleteFileW(fooPath.c_str())) @@ -1146,7 +1242,7 @@ namespace UnitTests CloseHandle(origHandle); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } std::wstring filter = CombinePath(testFile, L"*"); @@ -1161,7 +1257,7 @@ namespace UnitTests CloseHandle(origHandle); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } do @@ -1180,7 +1276,7 @@ namespace UnitTests CloseHandle(origHandle); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } bool foundCurrentDir = false; @@ -1209,7 +1305,7 @@ namespace UnitTests CloseHandle(origHandle); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } } @@ -1220,14 +1316,14 @@ namespace UnitTests CloseHandle(origHandle); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(!CloseHandle(origHandle)) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", fooPath.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } files.clear(); @@ -1240,7 +1336,7 @@ namespace UnitTests CloseHandle(origHandle); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } do @@ -1257,7 +1353,7 @@ namespace UnitTests static_cast(files.size()), filter.c_str()); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } foundCurrentDir = false; @@ -1280,7 +1376,7 @@ namespace UnitTests files[i].cFileName, filter.c_str()); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } } @@ -1289,14 +1385,67 @@ namespace UnitTests errMsg = FormatString(L"FindFiles() did not return the expected list of files for filter \'%s\'.", filter.c_str()); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); } if(!RemoveDirectoryW(testFile.c_str())) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to remove directory \'%s\'.", testFile.c_str())); - Assert::IsTrue(false, errMsg.c_str(), LINE_INFO()); + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + DeleteTestDirectory(); + } + + TEST_METHOD(TestFSX) + { + std::string testDir = CreateTestDirectoryA(); + std::string testFile = CombinePath(testDir, "testfile"); + std::string goodFile = CombinePath(testDir, "testfile.fsxgood"); + std::string logFile = CombinePath(testDir, "testfile.fsxlog"); + + std::string argN("-N"); + std::string argNAmt("5000"); + + char pathToExe[MAX_PATH]; + + GetModuleFileNameA(NULL, pathToExe, sizeof(pathToExe)); + + std::vector argv; + + argv.push_back(pathToExe); + argv.push_back(const_cast(argN.c_str())); + argv.push_back(const_cast(argNAmt.c_str())); + argv.push_back(const_cast(testFile.c_str())); + argv.push_back(nullptr); + + Logger::WriteMessage(L"Running FSX..."); + + Assert::IsTrue(fsx_main(static_cast(argv.size()) - 1, &argv[0]) == 0, L"FSX failed.", LINE_INFO()); + + if(PathFileExistsA(testFile.c_str())) + { + if(!DeleteFileA(testFile.c_str())) + { + Assert::Fail(L"Failed to delete FSX test file.", LINE_INFO()); + } + } + + if(PathFileExistsA(goodFile.c_str())) + { + if(!DeleteFileA(goodFile.c_str())) + { + Assert::Fail(L"Failed to delete FSX good file.", LINE_INFO()); + } + } + + if(PathFileExistsA(logFile.c_str())) + { + if(!DeleteFileA(logFile.c_str())) + { + Assert::Fail(L"Failed to delete FSX log file.", LINE_INFO()); + } } DeleteTestDirectory(); diff --git a/UnitTests/UnitTests.vcxproj b/UnitTests/UnitTests.vcxproj index 420964e12..cea5d0ff8 100644 --- a/UnitTests/UnitTests.vcxproj +++ b/UnitTests/UnitTests.vcxproj @@ -90,14 +90,14 @@ Use Level3 Disabled - $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + $(VCInstallDir)UnitTest\include;$(ProjectDir);%(AdditionalIncludeDirectories) WIN32;_DEBUG;%(PreprocessorDefinitions) true Windows $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) - Shlwapi.lib;%(AdditionalDependencies) + Shlwapi.lib;ntdll.lib;%(AdditionalDependencies) @@ -105,14 +105,14 @@ Use Level3 Disabled - $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + $(VCInstallDir)UnitTest\include;$(ProjectDir);%(AdditionalIncludeDirectories) _DEBUG;%(PreprocessorDefinitions) true Windows $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) - Shlwapi.lib;%(AdditionalDependencies) + Shlwapi.lib;ntdll.lib;%(AdditionalDependencies) @@ -122,7 +122,7 @@ MaxSpeed true true - $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + $(VCInstallDir)UnitTest\include;$(ProjectDir);%(AdditionalIncludeDirectories) WIN32;NDEBUG;%(PreprocessorDefinitions) true @@ -131,7 +131,7 @@ true true $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) - Shlwapi.lib;%(AdditionalDependencies) + Shlwapi.lib;ntdll.lib;%(AdditionalDependencies) @@ -141,7 +141,7 @@ MaxSpeed true true - $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) + $(VCInstallDir)UnitTest\include;$(ProjectDir);%(AdditionalIncludeDirectories) NDEBUG;%(PreprocessorDefinitions) true @@ -150,15 +150,21 @@ true true $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) - Shlwapi.lib;%(AdditionalDependencies) + Shlwapi.lib;ntdll.lib;%(AdditionalDependencies) + + + + + + Create Create diff --git a/UnitTests/UnitTests.vcxproj.filters b/UnitTests/UnitTests.vcxproj.filters index f90576e27..abf7f30b6 100644 --- a/UnitTests/UnitTests.vcxproj.filters +++ b/UnitTests/UnitTests.vcxproj.filters @@ -1,36 +1,55 @@  - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + {a53dfd67-4225-44a6-847b-dc024bf9728d} + + + {7e8ff853-6fa7-473d-a40a-54561cd685da} + + + {8dc39220-2d5c-408b-9078-90b6daa0919b} + - Header Files + Source - Header Files + Source + + + Source\FSX\win + + + Source\FSX\win - - Source Files + + Source - Source Files + Source - - Source Files + + Source + + + Source\FSX\win + + + Source\FSX\win + + + Source\FSX\win + + + Source\FSX \ No newline at end of file diff --git a/UnitTests/fsx/fsx.cpp b/UnitTests/fsx/fsx.cpp new file mode 100644 index 000000000..062234bca --- /dev/null +++ b/UnitTests/fsx/fsx.cpp @@ -0,0 +1,2642 @@ +#include + +/* + * Copyright (c) 1998-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + * + * File: fsx.c + * Author: Avadis Tevanian, Jr. + * + * File system exerciser. + * + * Rewrite and enhancements 1998-2003 Conrad Minshall -- conrad@mac.com + * + * Various features from Joe Sokol, Pat Dirks, and Clark Warner. + * + * Small changes to work under Linux -- davej@suse.de + * + * Sundry porting patches from Guy Harris 12/2001 + * + * Checks for mmap last-page zero fill. + * + * Oct 2006: Now includes Wenguang's changes, Jim's Named Fork support, + * Peter's EA changes, and XILog additions + * + * Various features/enhancements from Mike Mackovitch -- macko@apple.com + * + * Added no-cached r/w option May 2008 -- rtucker@apple.com, bsuinn@apple.com + * + * Compile with: + cc -Wall -O3 fsx.c -o fsx + gcc -arch ppc -arch i386 -arch ppc64 -arch x86_64 -Wall -O3 fsx.c -o fsx -DXILOG -F/AppleInternal/Library/Frameworks -framework XILog + * + */ + +#if defined(_WIN32) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "win/mman.h" +#include "win/getopt.h" +#else +#include +#include +#include +#include +#include +#if defined(__APPLE__) +#include +#endif +#include +#ifdef _UWIN +# include +# include +#endif +#include +#include +#ifndef MAP_FILE +# define MAP_FILE 0 +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif +#ifdef XILOG +# include +#endif + +#if defined(_WIN32) +#define MAXPATHLEN MAX_PATH +#undef _IOLBF +#define _IOLBF _IONBF + +#define _PATH_FORKSPECIFIER ":" +#define F_NOCACHE 1000000 +#define SIGHUP 1000 +#define SIGPIPE 1000 +#define SIGALRM 1000 +#define SIGXCPU 1000 +#define SIGXFSZ 1000 +#define SIGVTALRM 1000 +#define SIGUSR1 1000 +#define SIGUSR2 1000 + +#define open(path, oflag, mode) open(path, oflag | O_BINARY, mode) +#define signal(sig, func) (SIGINT == (sig) ? signal(sig, (_crt_signal_t)func) : 0) + +#if defined(_WIN64) +typedef __int64 ssize_t; +#define SSIZE_MAX _I64_MAX +#else +typedef int ssize_t; +#define SSIZE_MAX INT_MAX +#endif + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +int __acrt_errno_map_os_error(const DWORD err); + +static inline int syserror(int err) +{ + //void __cdecl __acrt_errno_map_os_error(unsigned long const oserrno); + __acrt_errno_map_os_error(0 == err ? GetLastError() : err); + return -1; +} +static inline void bzero(void *s, size_t n) +{ + memset(s, 0, n); +} +static inline int fcntl(int fildes, int cmd, ...) +{ + return 0; +} +static inline int fsync(int fd) +{ + return FlushFileBuffers((HANDLE)_get_osfhandle(fd)) ? 0 : syserror(0); +} +static inline int ftruncate(int fd, off_t length) +{ + return _chsize(fd, length); +} +static inline int getopt_long(int argc, char **argv, char *optstring, + const struct option *longopts, int *longindex) +{ + return getopt(argc, argv, optstring); +} +static inline int getpagesize(void) +{ + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + return sysinfo.dwAllocationGranularity; +} +static inline int getpid(void) +{ + return GetCurrentProcessId(); +} +static inline unsigned int sleep(unsigned int seconds) +{ + Sleep(seconds * 1000); + return 0; +} + +char *initstate(unsigned long seed, char *state, long size); +long random(void); +char *setstate(const char *state); +void srandom(unsigned long seed); + +/* extended attribute implementation */ +#define EA_NAMEMAX (255) +#define EA_SIZEMAX (64 * 1024) + +extern "C" +{ + typedef struct _FILE_GET_EA_INFORMATION { + ULONG NextEntryOffset; + UCHAR EaNameLength; + CHAR EaName[1]; + } FILE_GET_EA_INFORMATION, *PFILE_GET_EA_INFORMATION; + + typedef struct _FILE_FULL_EA_INFORMATION { + ULONG NextEntryOffset; + UCHAR Flags; + UCHAR EaNameLength; + USHORT EaValueLength; + CHAR EaName[1]; + } FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION; + + typedef struct _IO_STATUS_BLOCK { + union { + NTSTATUS Status; + PVOID Pointer; + }; + ULONG_PTR Information; + } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; + + NTSYSAPI NTSTATUS NTAPI NtQueryEaFile( + IN HANDLE FileHandle, + OUT PIO_STATUS_BLOCK IoStatusBlock, + OUT PVOID Buffer, + IN ULONG Length, + IN BOOLEAN ReturnSingleEntry, + IN PVOID EaList OPTIONAL, + IN ULONG EaListLength, + IN PULONG EaIndex OPTIONAL, + IN BOOLEAN RestartScan); + + NTSYSAPI NTSTATUS NTAPI NtSetEaFile( + IN HANDLE FileHandle, + OUT PIO_STATUS_BLOCK IoStatusBlock, + IN PVOID EaBuffer, + IN ULONG EaBufferSize); +} + +static inline ssize_t fgetxattr(int fd, const char *name, void *value, size_t size, + uint32_t position, int options) +{ + size_t namelen = strlen(name); + if (namelen > EA_NAMEMAX) + { + errno = ENAMETOOLONG; + return -1; + } + PFILE_FULL_EA_INFORMATION eainfo = (PFILE_FULL_EA_INFORMATION)malloc(EA_SIZEMAX); + if (0 == eainfo) + { + //errno = ENOMEM; /* malloc already sets this */ + return -1; + } + size_t geasize = sizeof(FILE_GET_EA_INFORMATION) + namelen; + PFILE_GET_EA_INFORMATION geainfo = (PFILE_GET_EA_INFORMATION)_alloca(geasize); + geainfo->NextEntryOffset = 0; + geainfo->EaNameLength = (UCHAR)namelen; + memcpy(geainfo->EaName, name, geainfo->EaNameLength + 1); + IO_STATUS_BLOCK iosb; + int errno_ = 0; + ssize_t res = 0 <= NtQueryEaFile((HANDLE)_get_osfhandle(fd), &iosb, + eainfo, EA_SIZEMAX, TRUE, geainfo, geasize, 0, FALSE) ? eainfo->EaValueLength : -1; + if (0 < res) + { + if (res <= size) + memcpy(value, eainfo->EaName + eainfo->EaNameLength + 1, res); + else if (0 != value) + { + errno_ = ERANGE; + res = -1; + } + } + else + { + errno_ = EPERM; /* catchall error */ + res = -1; + } + free(eainfo); + if (-1 == res) + errno = errno_; + return res; +} +static inline int fsetxattr(int fd, const char *name, void *value, size_t size, + uint32_t position, int options) +{ + size_t namelen = strlen(name); + if (namelen > EA_NAMEMAX) + { + errno = ENAMETOOLONG; + return -1; + } + size_t easize = offsetof(FILE_FULL_EA_INFORMATION, EaName) + namelen + 1 + size; + if (easize > EA_SIZEMAX) + { + errno = ERANGE; + return -1; + } + PFILE_FULL_EA_INFORMATION eainfo = (PFILE_FULL_EA_INFORMATION)malloc(easize); + if (0 == eainfo) + { + //errno = ENOMEM; /* malloc already sets this */ + return -1; + } + eainfo->NextEntryOffset = 0; + eainfo->Flags = 0; + eainfo->EaNameLength = (UCHAR)namelen; + eainfo->EaValueLength = (USHORT)size; + memcpy(eainfo->EaName, name, eainfo->EaNameLength + 1); + memcpy(eainfo->EaName + eainfo->EaNameLength + 1, value, eainfo->EaValueLength); + IO_STATUS_BLOCK iosb; + int res = 0 <= NtSetEaFile((HANDLE)_get_osfhandle(fd), &iosb, eainfo, easize) ? 0 : -1; + free(eainfo); + if (-1 == res) + errno = EPERM; /* catchall error */ + return res; +} +static inline int fremovexattr(int fd, const char *name, int options) +{ + return fsetxattr(fd, name, 0, 0, 0, options); +} +#endif + +#if defined(__linux__) +#include + +#define _PATH_FORKSPECIFIER "/..namedfork/" + +#define F_NOCACHE 1000000 +#define fcntl(fd, cmd, ...)\ + (F_NOCACHE == cmd ? 0 : fcntl(fd, cmd, __VA_ARGS__)) + +#define fgetxattr(fd, name, value, size, position, options)\ + fgetxattr(fd, name, value, size) +#define fsetxattr(fd, name, value, size, position, options)\ + fsetxattr(fd, name, value, size, options) +#define fremovexattr(fd, name, options)\ + fremovexattr(fd, name) +#endif + +#if defined(_WIN64) || defined(__linux__) +static size_t +strlcpy(char *dst, const char *src, size_t maxlen) { + const size_t srclen = strlen(src); + if (srclen < maxlen) { + memcpy(dst, src, srclen+1); + } else if (maxlen != 0) { + memcpy(dst, src, maxlen-1); + dst[maxlen-1] = '\0'; + } + return srclen; +} +static size_t +strlcat(char *dst, const char *src, size_t maxlen) { + const size_t srclen = strlen(src); + const size_t dstlen = strnlen(dst, maxlen); + if (dstlen == maxlen) return maxlen+srclen; + if (srclen < maxlen-dstlen) { + memcpy(dst+dstlen, src, srclen+1); + } else { + memcpy(dst+dstlen, src, maxlen-dstlen-1); + dst[maxlen-1] = '\0'; + } + return dstlen + srclen; +} +#endif + +/* + * A log entry is an operation and a bunch of arguments. + */ + +struct log_entry { + int opnum; + int operation; + int args[3]; +}; + +#define DEFAULT_LOGSIZE 1024 + +struct log_entry *oplog; /* the log */ +int logptr = 0; /* current position in log */ +int logcount = 0; /* total ops */ + +/* + * Define operations + */ + +#define OP_READ 1 +#define OP_WRITE 2 +#define OP_TRUNCATE 3 +#define OP_CLOSEOPEN 4 +#define OP_MAPREAD 5 +#define OP_MAPWRITE 6 +#define OP_SKIPPED 7 +#define OP_NOCACHEREAD 8 +#define OP_NOCACHEWRITE 9 + +int page_size; +int page_mask; + +char *good_buf; /* a pointer to the correct data */ +char *temp_buf; /* a pointer to the current data */ +char fname[MAXPATHLEN]; /* name of our test file */ +int fd; /* fd for our test file */ +int ea_lastwrite = 0; /* Size of the last EA Write */ +char *eaname; /* Name of the EA key */ + +off_t file_size = 0; +off_t biggest = 0; +char state[256]; +unsigned long testcalls = 0; /* calls to function "test" */ + +unsigned long simulatedopcount = 0; /* -b flag */ +int closeprob = 0; /* -c flag */ +int debug = 0; /* -v flag */ +unsigned long debugstart = 0; /* -D flag */ +int ea = 0; /* -e flag */ +unsigned long maxfilelen = 256 * 1024; /* -l flag */ +int sizechecks = 1; /* -n flag disables them */ +int maxoplen = 64 * 1024; /* -o flag */ +int quiet = 0; /* -q flag */ +unsigned long progressinterval = 0; /* -p flag */ +int readbdy = 1; /* -r flag */ +int style = 0; /* -s flag */ +int truncbdy = 1; /* -t flag */ +int writebdy = 1; /* -w flag */ +long monitorstart = -1; /* -m flag */ +long monitorend = -1; /* -m flag */ +int lite = 0; /* -L flag */ +long numops = -1; /* -N flag */ +int randomoplen = 1; /* -O flag disables it */ +int seed = 0; /* -S flag */ +int mapped_writes = 1; /* -W flag disables */ +int mapped_reads = 1; /* -R flag disables it */ +int logsize = DEFAULT_LOGSIZE; /* -G flag */ +int datasize = 4; /* -T flag */ +int modsize = 0; +int fsxgoodfd = -1; +FILE * fsxlogf = NULL; +int badoff = -1; +int closeopen = 0; +int sync_before_close = 0; /* -y flag enables it */ +int interactive = 0; /* -i flag interactive */ +int interactiveSince = -1; /* -I flag when to start interactive */ +int usehole = 1; // by default use hole (sparse file) +int slow_motion = 0; +long duration = 0; /* -d flag */ +unsigned int pinginterval = 10000; // About every 30sec? +#ifdef XILOG +XILogRef xilogref; +#endif +int gUseRandomNoCache = 0; /* -C randomly mix cached and un-cached r/w ops */ + +char *msgbuf; +int msgbuflen; + +void dotruncate(unsigned size); +void writefileimage(void); + +#define get_data_at(cp) \ + (((*(((unsigned char *)(cp)) + 0)) << 0) | \ + ((datasize < 2) ? 0 : \ + ((*(((unsigned char *)(cp)) + 1)) << 8)) | \ + ((datasize < 4) ? 0 : \ + (((*(((unsigned char *)(cp)) + 2)) << 16) | \ + ((*(((unsigned char *)(cp)) + 3)) << 24)))) + +#define set_data_at(cp, val) \ + do { \ + (*(((unsigned char *)(cp)) + 0)) = ((val) >> 0) & 0xff; \ + if (datasize < 2) break; \ + (*(((unsigned char *)(cp)) + 1)) = (((val) >> 8) & 0xff); \ + if (datasize < 4) break; \ + (*(((unsigned char *)(cp)) + 2)) = (((val) >> 16) & 0xff); \ + (*(((unsigned char *)(cp)) + 3)) = (((val) >> 24) & 0xff); \ + } while (0) + +#define SHOWLOGENTRY(OPSTART, OPEND) \ + (!quiet && \ + ((progressinterval && (testcalls % progressinterval == 0)) || \ + (debug && \ + ((monitorstart == -1) || \ + (((OPEND) > monitorstart) && \ + ((monitorend == -1) || ((OPSTART) <= monitorend))))))) + +void docloseopen(void); + +int parsetime(char *t) +{ + int i = 0; + int secs = 0; + char b[128]; bzero(b, 128); + + for (i=0; i < strlen(t); i++) { + switch (t[i]) { + case 's': + secs += atoi(b); + bzero(b, 128); + break; + case 'm': + secs += atoi(b) * 60; + bzero(b, 128); + break; + case 'h': + secs += atoi(b) * 60 * 60; + bzero(b, 128); + break; + case 'd': + secs += atoi(b) * 60 * 60 * 24; + bzero(b, 128); + break; + case 'w': + secs += atoi(b) * 60 * 60 * 24 * 7; + bzero(b, 128); + break; + case 'y': + secs += atoi(b) * 60 * 60 * 24 * 365; + bzero(b, 128); + break; + default: + sprintf(b, "%s%c", b, t[i]); + } + } + if (secs == 0) // maybe they just gave us a number? + secs = atoi(t); + return(secs); +} + + +void +mvwarnc(int code, const char *fmt, va_list ap) +{ + char buf[1024]; + + ZeroMemory(buf, sizeof(buf)); + + buf[0] = 'f'; + buf[1] = 's'; + buf[2] = 'x'; + buf[3] = ':'; + buf[4] = ' '; + + vsprintf_s(&buf[5], sizeof(buf) - 5, fmt, ap); + + size_t len = strlen(buf); + + if(len < sizeof(buf) - 3) + { + buf[len] = ':'; + ++len; + + buf[len] = ' '; + ++len; + } + + strcat_s(&buf[len], sizeof(buf) - len, strerror(code)); + + Logger::WriteMessage(buf); +} + + +void +mwarn(const char * fmt, ...) +{ + va_list ap; + va_start(ap, fmt); +#ifdef XILOG + XILogMsg(fmt, ap); + XILogMsg("%s", strerror(errno)); + va_end(ap); + va_start(ap, fmt); +#endif + mvwarnc(errno, fmt, ap); + va_end(ap); +} + + +void +prt(char *fmt, ...) +{ + va_list args; + char buf[1024]; + + ZeroMemory(buf, sizeof(buf)); + + va_start(args, fmt); +#ifdef XILOG + XILogMsg(fmt, args); + va_end(args); + va_start(args, fmt); +#endif + vsprintf_s(buf, fmt, args); + + va_end(args); + + Logger::WriteMessage(buf); + + if (fsxlogf) { + va_start(args, fmt); + vfprintf(fsxlogf, fmt, args); + fflush(fsxlogf); + va_end(args); + } +} + +void +prt2(char *fmt, ...) +{ + va_list args; + char buf[1024]; + + ZeroMemory(buf, sizeof(buf)); + + va_start(args, fmt); + + vsprintf_s(buf, fmt, args); + + va_end(args); + + Logger::WriteMessage(buf); + + if (fsxlogf) { + va_start(args, fmt); + vfprintf(fsxlogf, fmt, args); + va_end(args); + } +} + + +void +prterr(char *prefix) +{ +#ifdef XILOG + XILogErr("%s%s%s", prefix, prefix ? ": " : "", strerror(errno)); +#endif + prt2("%s%s%s\n", prefix, prefix ? ": " : "", strerror(errno)); +} + + +int +log4(int operation, int arg0, int arg1, int arg2) +{ + int logent = logptr; + struct log_entry *le = &oplog[logent]; + le->opnum = logcount + 1; + le->operation = operation; + if (closeopen) + le->operation = ~ le->operation; + le->args[0] = arg0; + le->args[1] = arg1; + le->args[2] = arg2; + logptr++; + logcount++; + if (msgbuflen) { + snprintf(msgbuf, msgbuflen+1, "%d", logcount); + } + if (logptr >= logsize) + logptr = 0; + return logent; +} + + +void +offset_last_mod(int offset, int *write, int *tdown, int *tup) +{ + int i, count, down, opnum, operation; + struct log_entry *lp; + + *write = *tdown = *tup = -1; + + opnum = logcount; + i = logptr - 1; + if (i < 0) + i = logsize - 1; + if (logcount < logsize) { + count = logcount; + } else { + count = logsize; + } + for ( ; count > 0; count--, opnum--) { + lp = &oplog[i]; + operation = lp->operation; + if ((closeopen = operation < 0)) + operation = ~ operation; + switch (operation) { + case OP_MAPWRITE: + case OP_WRITE: + if (*write != -1) + break; + if ((offset >= lp->args[0]) && (offset < (lp->args[0] + lp->args[1]))) + *write = opnum; + break; + case OP_TRUNCATE: + down = lp->args[0] < lp->args[1]; + if ((offset >= lp->args[!down]) && (offset < lp->args[!!down])) { + if (down && (*tdown == -1)) + *tdown = opnum; + else if (!down && (*tup == -1)) + *tup = opnum; + } + break; + } + if ((*write != -1) && (*tdown != -1) && (*tup != -1)) + return; + i--; + if (i < 0) + i = logsize - 1; + } +} + +void +logentrydump(int logent, int opnum) +{ + struct log_entry *lp = &oplog[logent]; + int down; + + if (modsize) + prt("%d(%d): ", opnum, opnum%modsize); + else + prt("%d: ", opnum); + if ((closeopen = lp->operation < 0)) + lp->operation = ~ lp->operation; + + switch (lp->operation) { + case OP_MAPREAD: + prt("%-15s 0x%x (%d) thru 0x%x (%d)\t(0x%x (%d) bytes)", "MAPREAD", + lp->args[0], lp->args[0], + lp->args[0] + lp->args[1] - 1, lp->args[0] + lp->args[1] - 1, + lp->args[1], lp->args[1]); + if ((badoff >= lp->args[0]) && (badoff < lp->args[0] + lp->args[1])) + { + prt("\t***RRRR***"); + } + break; + case OP_MAPWRITE: + prt("%-15s 0x%x (%d) thru 0x%x (%d)\t(0x%x (%d) bytes)", "MAPWRITE", + lp->args[0], lp->args[0], + lp->args[0] + lp->args[1] - 1, lp->args[0] + lp->args[1] - 1, + lp->args[1], lp->args[1]); + if ((badoff >= lp->args[0]) && (badoff < lp->args[0] + lp->args[1])) + { + prt("\t******WWWW"); + } + break; + case OP_READ: + prt("%-15s 0x%x (%d) thru 0x%x (%d)\t(0x%x (%d) bytes)", "READ", + lp->args[0], lp->args[0], + lp->args[0] + lp->args[1] - 1, lp->args[0] + lp->args[1] - 1, + lp->args[1], lp->args[1]); + if (badoff >= lp->args[0] && + badoff < lp->args[0] + lp->args[1]) + { + prt("\t***RRRR***"); + } + break; + case OP_WRITE: + prt("%-15s 0x%x (%d) thru 0x%x (%d)\t(0x%x (%d) bytes)", "WRITE", + lp->args[0], lp->args[0], + lp->args[0] + lp->args[1] - 1, lp->args[0] + lp->args[1] - 1, + lp->args[1], lp->args[1]); + if (lp->args[0] > lp->args[2]) + { + prt(" HOLE"); + } + else if ((lp->args[0] + lp->args[1]) > lp->args[2]) + { + prt(" EXTEND"); + } + if (((badoff >= lp->args[0]) || (badoff >= lp->args[2])) && + (badoff < (lp->args[0] + lp->args[1]))) + { + prt("\t***WWWW"); + } + break; + case OP_NOCACHEREAD: + prt("%-15s 0x%x (%d) thru 0x%x (%d)\t(0x%x (%d) bytes)", "NOCACHEREAD", + lp->args[0], lp->args[0], + lp->args[0] + lp->args[1] - 1, lp->args[0] + lp->args[1] - 1, + lp->args[1], lp->args[1]); + if (badoff >= lp->args[0] && + badoff < lp->args[0] + lp->args[1]) + { + prt("\t***RRRR***"); + } + break; + case OP_NOCACHEWRITE: + prt("%-15s 0x%x (%d) thru 0x%x (%d)\t(0x%x (%d) bytes)", "NOCACHEWRITE", + lp->args[0], lp->args[0], + lp->args[0] + lp->args[1] - 1, lp->args[0] + lp->args[1] - 1, + lp->args[1], lp->args[1]); + if (lp->args[0] > lp->args[2]) + { + prt(" HOLE"); + } + else if ((lp->args[0] + lp->args[1]) > lp->args[2]) + { + prt(" EXTEND"); + } + if (((badoff >= lp->args[0]) || (badoff >= lp->args[2])) && + (badoff < (lp->args[0] + lp->args[1]))) + { + prt("\t***WWWW"); + } + break; + case OP_TRUNCATE: + down = lp->args[0] < lp->args[1]; + prt("%-15s from 0x%x (%d) to 0x%x (%d)", + down ? "TRUNCATE DOWN" : "TRUNCATE UP", + lp->args[1], lp->args[1], + lp->args[0], lp->args[0]); + if ((badoff >= lp->args[!down]) && + (badoff < lp->args[!!down])) + { + prt("\t******WWWW"); + } + break; + case OP_SKIPPED: + prt("SKIPPED (no operation)"); + break; + default: + prt("BOGUS LOG ENTRY (operation code = %d)!", + lp->operation); + } + if (closeopen) + { + prt("\n\t\tCLOSE/OPEN"); + } + prt("\n"); +} + + +void +logdump(void) +{ + int i, count, opnum; + + // don't dump log if we've been logging ops via debug + if (debug) return; + + prt("LOG DUMP (%d total operations):\n", logcount); + if (logcount < logsize) { + i = 0; + count = logcount; + opnum = 1; + } else { + i = logptr; + count = logsize; + opnum = 1 + logcount - logsize; + } + for ( ; count > 0; count--, opnum++) { + logentrydump(i, opnum); + i++; + if (i == logsize) + i = 0; + } +} + + +void +save_buffer(char *buffer, off_t bufferlength, int fd) +{ + off_t ret; + ssize_t byteswritten; + + if (fd <= 0 || bufferlength == 0) + return; + + if (bufferlength > SSIZE_MAX) { + prt("fsx flaw: overflow in save_buffer\n"); + Assert::Fail(L"fsx flaw: overflow in save_buffer.", LINE_INFO()); + //exit(67); + } + if (lite) { + off_t size_by_seek = lseek(fd, (off_t)0, SEEK_END); + if (size_by_seek == (off_t)-1) + prterr("save_buffer: lseek eof"); + else if (bufferlength > size_by_seek) { + mwarn("WARNING: save_buffer: .fsxgood file too short... will save 0x%llx bytes instead of 0x%llx\n", (unsigned long long)size_by_seek, + (unsigned long long)bufferlength); + bufferlength = size_by_seek; + } + } + + ret = lseek(fd, (off_t)0, SEEK_SET); + if (ret == (off_t)-1) + prterr("save_buffer: lseek 0"); + + byteswritten = write(fd, buffer, (size_t)bufferlength); + if (byteswritten != bufferlength) { + if (byteswritten == -1) + prterr("save_buffer write"); + else + mwarn("WARNING: save_buffer: short write, 0x%x bytes instead of 0x%llx\n", + (unsigned)byteswritten, + (unsigned long long)bufferlength); + } +} + + +void +failure(int status) +{ + if (fsxgoodfd >= 0) { + if (good_buf) { + save_buffer(good_buf, file_size, fsxgoodfd); + prt("Correct content saved for comparison\n"); + prt("(maybe hexdump \"%s\" vs \"%s.fsxgood\")\n", + fname, fname); + } + close(fsxgoodfd); + } + prt("Seed was set to %d\n", seed); + prt("Failure with status %d\n", status); + //exit(status); + Assert::Fail(L"failure()", LINE_INFO()); +} + + +void +check_buffers(unsigned offset, unsigned size) +{ + unsigned int c, t; + unsigned i = 0; + unsigned n = 0; + unsigned sizeleft = size; + unsigned start, good, bad; + int op, w, td, tu; + + start = good = bad = 0; + op = -1; + + if (memcmp(good_buf + offset, temp_buf, size) != 0) { + prt("data miscompare @ %d\n", offset); + while (sizeleft > 0) { + c = get_data_at(&good_buf[offset+i]); + t = get_data_at(&temp_buf[i]); + if (c != t) { + if (n == 0) { + start = offset + i; + good = c; + bad = t; + op = ((t > 0) && (t <= logcount)) ? t : -1; + } + n+=datasize; + if (badoff < 0) { + badoff = offset + i; + logdump(); + prt("data miscompare @ %d\n", offset); + prt("%-10s %-10s %-10s %-10s %-8s Last: %-8s %-8s %-8s\n", + "OFFSET", "GOOD", "BAD", "LENGTH", "BADOP#", + "WRITE", "TRUNC-", "TRUNC+"); + } + } + i+=datasize; + sizeleft-=datasize; + if (n && ((c == t) || (sizeleft <= 0))) { + w = td = tu = -1; + offset_last_mod(start, &w, &td, &tu); + prt("0x%08x 0x%08x 0x%08x 0x%08x %-8d %-8d %-8d %-8d\n", + start, good, bad, n, op, w, td, tu); + if (c == t) + n = 0; + } + } + if (badoff == -1) { + logdump(); + prt("transient data miscompare @ %d ????????\n", offset); + prt("memcmp(%d,%d) failed but no differences found ????????\n", offset, size); + } + failure(110); + } +} + + +void +check_size(void) +{ + struct stat statbuf; + off_t size_by_seek; + + if (fstat(fd, &statbuf)) { + prterr("check_size: fstat"); + statbuf.st_size = -1; + } + size_by_seek = lseek(fd, (off_t)0, SEEK_END); + if (file_size != statbuf.st_size || file_size != size_by_seek) { + logdump(); + prt("Size error: expected 0x%llx stat 0x%llx seek 0x%llx\n", + (unsigned long long)file_size, + (unsigned long long)statbuf.st_size, + (unsigned long long)size_by_seek); + failure(120); + } +} + + +void +check_trunc_hack(void) +{ + struct stat statbuf; + ftruncate(fd, (off_t)0); + + if (!usehole) + return; + + ftruncate(fd, (off_t)100000); + fstat(fd, &statbuf); + if (statbuf.st_size != (off_t)100000) { + prt("no extend on truncate! not posix!\n"); + //exit(130); + Assert::Fail(L"No extend on truncate! not posix!", LINE_INFO()); + } + ftruncate(fd, (off_t)0); +} + + +void +doread(unsigned offset, unsigned size) +{ + off_t ret; + unsigned iret; + int logent; + int cache_off = 0; + + if (ea) + size = ea_lastwrite; + + offset -= offset % readbdy; + if (size == 0) { + if (debug && testcalls > simulatedopcount) + { + prt("skipping zero size read\n"); + } + log4(OP_SKIPPED, OP_READ, offset, size); + return; + } + if (size + offset > file_size) { + if (debug && testcalls > simulatedopcount) + { + prt("skipping seek/read past end of file\n"); + } + log4(OP_SKIPPED, OP_READ, offset, size); + return; + } + + /* When the gUseRandomNoCache option is enable (-C), + * randomly turn caching off for 30% of the read calls. + * Re-enable caching after the read is complete. + */ + if (gUseRandomNoCache && ((random() % 100) < 30)) { + cache_off = 1; + logent = log4(OP_NOCACHEREAD, offset, size, 0); + } else { + logent = log4(OP_READ, offset, size, 0); + } + + if (SHOWLOGENTRY(offset, offset + size)) + { + logentrydump(logent, logcount); + } + + if (testcalls <= simulatedopcount) + return; + + if (interactive) { + printf("Hit return when ready..."); + getchar(); + } + + if (!ea) { + ret = lseek(fd, (off_t)offset, SEEK_SET); + if (ret == (off_t)-1) { + logdump(); + prterr("doread: lseek"); + failure(140); + } + + if (cache_off && (fcntl(fd, F_NOCACHE, 1) != 0)) { // turn data caching off + logdump(); + prterr("doread: fcntl(F_NOCACHE, 1)"); + failure(201); + } + iret = read(fd, temp_buf, size); + if (cache_off && (fcntl(fd, F_NOCACHE, 0) != 0)) { + logdump(); + prterr("doread: fcntl(F_NOCACHE, 0)"); + failure(201); + } + } else { + iret = fgetxattr(fd, eaname, temp_buf, size, 0, 0); + } + + if (iret != size) { + logdump(); + if (iret == -1) + prterr("doread: read"); + else + prt("short read: 0x%x bytes instead of 0x%x\n", iret, size); + failure(141); + } + + check_buffers(offset, size); +} + + +void +check_eofpage(char *s, unsigned offset, char *p, int size) +{ +#if !defined(_WIN32) + uintptr_t last_page, should_be_zero; + + if (offset + size <= (file_size & ~page_mask)) + return; + /* + * we landed in the last page of the file + * test to make sure the VM system provided 0's + * beyond the true end of the file mapping + * (as required by mmap def in 1996 posix 1003.1) + */ + last_page = ((uintptr_t)p + (offset & page_mask) + size) & ~page_mask; + + for (should_be_zero = last_page + (file_size & page_mask); + should_be_zero < last_page + page_size; + should_be_zero++) + if (*(char *)should_be_zero) { + logdump(); + prt("Mapped %s: non-zero data past EOF (0x%llx) page offset 0x%x is 0x%04x\n", + s, file_size - 1, should_be_zero & page_mask, + (*(char *)should_be_zero)); + failure(205); + } +#endif +} + + +void +domapread(unsigned offset, unsigned size) +{ + unsigned pg_offset; + unsigned map_size; + char *p; + int logent; + + if (ea) + return; + + offset -= offset % readbdy; + if (size == 0) { + if (debug && testcalls > simulatedopcount) + { + prt("skipping zero size read\n"); + } + log4(OP_SKIPPED, OP_MAPREAD, offset, size); + return; + } + if (size + offset > file_size) { + if (debug && testcalls > simulatedopcount) + { + prt("skipping seek/read past end of file\n"); + } + log4(OP_SKIPPED, OP_MAPREAD, offset, size); + return; + } + + logent = log4(OP_MAPREAD, offset, size, 0); + + if (SHOWLOGENTRY(offset, offset + size)) + { + logentrydump(logent, logcount); + } + + if (testcalls <= simulatedopcount) + return; + + pg_offset = offset & page_mask; + map_size = pg_offset + size; + + if (interactive) { + printf("Hit return when ready..."); + getchar(); + } + + if ((p = (char *)mmap(0, map_size, PROT_READ, MAP_FILE | MAP_SHARED, fd, + (off_t)(offset - pg_offset))) == (char *)-1) { + logdump(); + prterr("domapread: mmap"); + failure(190); + } + memcpy(temp_buf, p + pg_offset, size); + + check_eofpage("Read", offset, p, size); + + if (munmap(p, map_size) != 0) { + logdump(); + prterr("domapread: munmap"); + failure(191); + } + + check_buffers(offset, size); +} + + +void +gendata(char *good_buf, unsigned offset, unsigned size) +{ + while (size > 0) { + size -= datasize; + set_data_at(&good_buf[offset], testcalls); + offset += datasize; + } +} + + +void +dowrite(unsigned offset, unsigned size) +{ + off_t ret; + unsigned iret; + int logent; + int cache_off = 0; + + offset -= offset % writebdy; + if (size == 0) { + if (debug && testcalls > simulatedopcount) + { + prt("skipping zero size write\n"); + } + log4(OP_SKIPPED, OP_WRITE, offset, size); + return; + } + + /* When the gUseRandomNoCache option is enable (-C), + * randomly turn caching off for 30% of the write calls. + * Re-enable caching after the write is complete. + */ + if (gUseRandomNoCache && ((random() % 100) < 30)) { // turn data caching off + cache_off = 1; + logent = log4(OP_NOCACHEWRITE, offset, size, file_size); + } else { + logent = log4(OP_WRITE, offset, size, file_size); + } + + gendata(good_buf, offset, size); + + if (SHOWLOGENTRY(offset, offset + size)) + { + logentrydump(logent, logcount); + } + + if (file_size < offset + size) { + if (SHOWLOGENTRY(offset, offset + size)) + { + prt("extend file size from 0x%x (%d) to 0x%x (%d)\n", (int)file_size, (int)file_size, offset+size, offset+size); + } + + if (file_size < offset) { + memset(good_buf + file_size, '\0', offset - file_size); + if (!usehole) { + off_t ret; + unsigned iret; + + if (interactive) { + printf("Hit return when ready..."); + getchar(); + } + + ret = lseek(fd, (off_t)file_size, SEEK_SET); + if (ret == (off_t)-1) { + logdump(); + prterr("dowrite: lseek"); + failure(150); + } + if (cache_off && (fcntl(fd, F_NOCACHE, 1) != 0)) { // turn data caching off + logdump(); + prterr("dowrite: fcntl(F_NOCACHE, 1)"); + failure(201); + } + iret = write(fd, good_buf + file_size, offset - file_size); + if (iret != offset - file_size) { + logdump(); + if (iret == -1) + prterr("dowrite: write 0s"); + else + { + prt("short write: 0x%x bytes instead of 0x%x\n", + iret, size); + } + failure(151); + } + if (cache_off && (fcntl(fd, F_NOCACHE, 0) != 0)) { + logdump(); + prterr("dowrite: fcntl(F_NOCACHE, 0)"); + failure(201); + } + } + } + file_size = offset + size; + if (lite) { + logdump(); + mwarn("WARNING: Lite file size bug in fsx!"); + failure(149); + } + } + + if (testcalls <= simulatedopcount) + return; + + if (interactive) { + printf("Hit return when ready..."); + getchar(); + } + + if (!ea) { + ret = lseek(fd, (off_t)offset, SEEK_SET); + if (ret == (off_t)-1) { + logdump(); + prterr("dowrite: lseek"); + failure(150); + } + if (cache_off && (fcntl(fd, F_NOCACHE, 1) != 0)) { // turn data caching off + logdump(); + prterr("dowrite: fcntl(F_NOCACHE, 1)"); + failure(201); + } + iret = write(fd, good_buf + offset, size); + if (iret != size) { + logdump(); + if (iret == -1) + prterr("dowrite: write"); + else + { + prt("short write: 0x%x bytes instead of 0x%x\n", + iret, size); + } + failure(151); + } + if (cache_off && (fcntl(fd, F_NOCACHE, 0) != 0)) { + logdump(); + prterr("dowrite: fcntl(F_NOCACHE, 0)"); + failure(201); + } + } else { + if (random() % 2000 == 0) { + iret = fremovexattr(fd, eaname, 0); + if (iret != 0) { + logdump(); + prterr("ea_dowrite: removexattr"); + failure(151); + } + } + iret = fsetxattr(fd, eaname, good_buf, size, 0, 0); + ea_lastwrite = size; + if (iret != 0) { + logdump(); + prterr("ea_dowrite: setxattr"); + failure(151); + } + } +} + + +void +domapwrite(unsigned offset, unsigned size) +{ + unsigned pg_offset; + unsigned map_size; + char *p; + int logent; + + offset -= offset % writebdy; + if (size == 0) { + if (debug && testcalls > simulatedopcount) + { + prt("skipping zero size write\n"); + } + log4(OP_SKIPPED, OP_MAPWRITE, offset, size); + return; + } + + if (file_size < offset + size) { + if (file_size < offset) + memset(good_buf + file_size, '\0', offset - file_size); + if (lite) { + logdump(); + mwarn("WARNING: Lite file size bug in fsx!"); + failure(200); + } + if (SHOWLOGENTRY(offset, offset + size)) + { + prt("extend file size from 0x%x (%d) to 0x%x (%d)\n", (int)file_size, (int)file_size, offset+size, offset+size); + } + dotruncate(offset + size); + if (closeopen) + docloseopen(); + if (simulatedopcount > 0 && testcalls == simulatedopcount) + writefileimage(); + testcalls++; + } + gendata(good_buf, offset, size); + + logent = log4(OP_MAPWRITE, offset, size, 0); + + if (SHOWLOGENTRY(offset, offset + size)) + { + logentrydump(logent, logcount); + } + + if (testcalls <= simulatedopcount) + return; + + pg_offset = offset & page_mask; + map_size = pg_offset + size; + + if (interactive) { + printf("Hit return when ready..."); + getchar(); + } + + if ((p = (char *)mmap(0, map_size, PROT_READ | PROT_WRITE, + MAP_FILE | MAP_SHARED, fd, + (off_t)(offset - pg_offset))) == (char *)-1) { + logdump(); + prterr("domapwrite: mmap"); + failure(202); + } + memcpy(p + pg_offset, good_buf + offset, size); + if (msync(p, map_size, 0) != 0) { + logdump(); + prterr("domapwrite: msync"); + failure(203); + } + + check_eofpage("Write", offset, p, size); + + if (munmap(p, map_size) != 0) { + logdump(); + prterr("domapwrite: munmap"); + failure(204); + } +} + + +void +dotruncate(unsigned size) +{ + off_t oldsize, start, end; + int logent; + + oldsize = file_size; + if (oldsize < size) { + start = oldsize; + end = size; + } else { + start = size; + end = oldsize; + } + + size -= size % truncbdy; + if (size > biggest) { + biggest = size; + if (!quiet && testcalls > simulatedopcount) + { + prt("truncating to largest ever: 0x%x\n", size); + } + } + + logent = log4(OP_TRUNCATE, size, (unsigned)file_size, 0); + + if (size > file_size) + memset(good_buf + file_size, '\0', size - file_size); + file_size = size; + + if (SHOWLOGENTRY(start, end)) + { + logentrydump(logent, logcount); + } + + if (testcalls <= simulatedopcount) + return; + + if (interactive) { + printf("Hit return when ready..."); + getchar(); + } + + if (usehole || size <= oldsize) { // shrink the file or we want a sparse file + if (ftruncate(fd, (off_t)size) == -1) { + logdump(); + prt("ftruncate1: %x\n", size); + prterr("dotruncate: ftruncate"); + failure(160); + } + } else { // write zeros instead of creating a sparse file to extend the file + off_t ret; + unsigned iret; + ret = lseek(fd, (off_t)oldsize, SEEK_SET); + if (ret == (off_t)-1) { + logdump(); + prterr("dowrite: lseek"); + failure(150); + } + iret = write(fd, good_buf + oldsize, size - oldsize); + if (iret != size - oldsize) { + logdump(); + if (iret == -1) + prterr("dotruncate: write"); + else + { + prt("short write: 0x%x bytes instead of 0x%x\n", + iret, size); + } + failure(151); + } + } +} + + +void +writefileimage(void) +{ + ssize_t iret; + + if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) { + logdump(); + prterr("writefileimage: lseek"); + failure(171); + } + iret = write(fd, good_buf, file_size); + if ((off_t)iret != file_size) { + logdump(); + if (iret == -1) + prterr("writefileimage: write"); + else + { + prt("short write: 0x%x bytes instead of 0x%llx\n", + iret, (unsigned long long)file_size); + } + failure(172); + } + if (usehole) { + if (lite ? 0 : ftruncate(fd, file_size) == -1) { + logdump(); + prt("ftruncate2: %llx\n", (unsigned long long)file_size); + prterr("writefileimage: ftruncate"); + failure(173); + } + } +} + + +void +docloseopen(void) +{ + if (testcalls <= simulatedopcount) + return; + + if (debug) + { + prt("%lu close/open\n", testcalls); + } + if (sync_before_close && fsync(fd)) { + logdump(); + prterr("docloseopen: fsync"); + failure(182); + } + if (close(fd)) { + logdump(); + prterr("docloseopen: close"); + failure(180); + } + fd = open(fname, O_RDWR, 0); + if (fd < 0) { + logdump(); + prterr("docloseopen: open"); + failure(181); + } +} + + +void +test(void) +{ + unsigned long offset; + unsigned long size = maxoplen; + unsigned long rv = random(); + unsigned long op = rv % (3 + !lite + mapped_writes); + + /* turn off the map read if necessary */ + + if (op == 2 && !mapped_reads) + op = 0; + + if (simulatedopcount > 0 && testcalls == simulatedopcount) + writefileimage(); + + testcalls++; + + if (closeprob) + closeopen = (rv >> 3) < (1 << 28) / closeprob; + + if (debugstart > 0 && testcalls >= debugstart) + debug = 1; + + if (!quiet && testcalls < simulatedopcount && testcalls % 100000 == 0) + { + prt("%lu...\n", testcalls); + } + + /* + * READ: op = 0 + * WRITE: op = 1 + * MAPREAD: op = 2 + * TRUNCATE: op = 3 + * MAPWRITE: op = 3 or 4 + */ + if (lite ? 0 : op == 3 && style == 0 && ea == 0) /* vanilla truncate? */ + dotruncate(((random() + datasize - 1) & ~(datasize - 1)) % maxfilelen); + else { + if (randomoplen) + size = random() % (maxoplen+1); + size = (size + datasize - 1) & ~(datasize - 1); // round up to multiple of datasize + if (lite ? 0 : op == 3 && ea == 0) + dotruncate(size); + else { + offset = random(); + offset &= ~(datasize - 1); // trunc to multiple of datasize + if (op == 1 || op == (lite ? 3 : 4)) { + offset %= maxfilelen; + if (ea != 0) + offset = 0; + if (offset + size > maxfilelen) + size = maxfilelen - offset; + if (op != 1) + domapwrite(offset, size); + else + dowrite(offset, size); + } else { + if (ea || !file_size) { + offset = 0; + } else { + offset %= file_size; + offset &= ~(datasize - 1); + } + if (offset + size > file_size) { + size = file_size - offset; + size &= ~(datasize - 1); // trunc to multiple of datasize + } + if (op != 0) + domapread(offset, size); + else + doread(offset, size); + } + } + } + if (sizechecks && testcalls > simulatedopcount) + check_size(); + if (closeopen) + docloseopen(); +} + + +void +cleanup(int sig) +{ + if (sig) + { + prt("signal %d\n", sig); + } + prt("testcalls = %lu\n", testcalls); +#ifdef XILOG + XILogEndTestCase(xilogref, kXILogTestPassOnErrorLevel); + XILogCloseLog(xilogref); +#endif + //exit(0); +} + + +void +usage(void) +{ + Logger::WriteMessage( + "usage: fsx [-ehinqvxCLMORW] [-b opnum] [-c Prob] [-d duration] [-f forkname] [-l logpath] [-m start:end] [-o oplen] [-p progressinterval] [-r readbdy] [-s style] [-t truncbdy] [-w writebdy] [-D startingop] [-F flen] [-G logsize] [-I opnum] [-N numops] [-P dirpath] [-S seed] [-T datasize] fname [xxxxx]\n\ + -b opnum: beginning operation number (default 1)\n\ + -c P: 1 in P chance of file close+open at each op (default infinity)\n\ + -d duration: number of hours for the tool to run\n\ + -e: tests using an extended attribute rather than a file\n\ + -f forkname: test the named fork of fname\n\ + -g logpath: path for .fsxlog file\n\ + -h: write 0s instead of creating holes (i.e. sparse file)\n\ + -i: interactive mode, hit return before performing the current operation\n\ + -l logpath: path for XILog file\n\ + -m startop:endop: monitor (print debug output) specified byte range (default 0:infinity)\n\ + -n: no verifications of file size\n\ + -o oplen: the upper bound on operation size (default 65536)\n\ + -p progressinterval: debug output at specified operation interval\n\ + -q: quieter operation\n\ + -r readbdy: 4096 would make reads page aligned (default 1)\n\ + -s style: 1 gives smaller truncates (default 0)\n\ + -t truncbdy: 4096 would make truncates page aligned (default 1)\n\ + -v: debug output for all operations\n\ + -w writebdy: 4096 would make writes page aligned (default 1)\n\ + -x: write output in XML (XILOG)\n\ + -y: call fsync before closing the file\n\ + -C mix cached and un-cached read/write ops\n\ + -D startingop: debug output starting at specified operation\n\ + -G logsize: #entries in oplog (default 1024)\n\ + -F flen: the upper bound on file size (default 262144)\n\ + -I: start interactive mode since operation opnum\n\ + -L: fsxLite - no file creations & no file size changes\n\ + -M: slow motion mode, wait 1 second before each op\n\ + -N numops: total # operations to do (default infinity)\n\ + -O: use oplen (see -o flag) for every op (default random)\n\ + -P dirpath: save .fsxlog and .fsxgood files in dirpath (default ./)\n\ + -R: mapped read operations DISabled\n\ + -S seed: for random # generator (default 0, gets timestamp+pid)\n\ + -T datasize: size of atomic data elements written to file [1,2,4] (default 4)\n\ + -W: mapped write operations DISabled\n\ + fname: this filename is REQUIRED (no default)\n\ + xxxxx: will be overwritten with operation #s, viewable in \"ps\"\n"); + + Assert::IsTrue(false, L"Invalid arguments.", LINE_INFO()); +} + + +int +getnum(char *s, char **e) +{ + int ret = -1; + + *e = (char *) 0; + ret = strtol(s, e, 0); + if (*e) + switch (**e) { + case 'b': + case 'B': + ret *= 512; + *e = *e + 1; + break; + case 'k': + case 'K': + ret *= 1024; + *e = *e + 1; + break; + case 'm': + case 'M': + ret *= 1024*1024; + *e = *e + 1; + break; + case 'w': + case 'W': + ret *= 4; + *e = *e + 1; + break; + } + return (ret); +} + + +//int +//main(int argc, char **argv) +//{ +// int ch; +// char *endp, **oargv = argv; +// char goodfile[MAXPATHLEN]; bzero(goodfile, MAXPATHLEN); +// char logfile[MAXPATHLEN]; bzero(logfile, MAXPATHLEN); +// char forkname[MAXPATHLEN]; bzero(forkname, MAXPATHLEN); +// char* logpath = NULL; +// eaname = argv[0]; +// int xml = 0; +// +//#if defined(_WIN32) +// eaname = "fsx.exe"; /* Windows does not look certain characters in EA's */ +//#endif +// +// goodfile[0] = 0; +// logfile[0] = 0; +// forkname[0] = 0; +// +// page_size = getpagesize(); +// page_mask = page_size - 1; +// +// setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */ +// +// while ((ch = getopt_long(argc, argv, "b:c:d:ef:g:hil:m:no:p:qr:s:t:vw:xCD:F:G:I:LMN:OP:RS:T:W", NULL, NULL)) +// != EOF) +// switch (ch) { +// case 'b': +// simulatedopcount = getnum(optarg, &endp); +// if (!quiet) +// fprintf(stdout, "Will begin at operation %ld\n", +// simulatedopcount); +// if (simulatedopcount == 0) +// usage(); +// simulatedopcount -= 1; +// break; +// case 'c': +// closeprob = getnum(optarg, &endp); +// if (!quiet) +// fprintf(stdout, "Chance of close/open is 1 in %d\n", closeprob); +// if (closeprob <= 0) +// usage(); +// break; +// case 'd': +// duration = parsetime(optarg); +// if(duration <= 0) +// duration = 0; +// printf("Running for %ld seconds\n", duration); +// break; +// case 'e': +// ea = 1; +// mapped_writes = 0; +// mapped_reads = 0; +// sizechecks = 0; +// closeopen = 0; +// maxoplen = 3802; +// maxoplen &= ~(datasize - 1); // round down to multiple of datasize +// maxfilelen = 3802; +// maxfilelen &= ~(datasize - 1); // round down to multiple of datasize +// printf("Writing into extended attribute\n"); +// break; +// case 'f': +// if (strlcpy(forkname, optarg, sizeof(forkname)) >= sizeof(forkname)) +// usage(); +// break; +// case 'g': +// if (strlcpy(logfile, optarg, sizeof(logfile)) >= sizeof(logfile)) +// usage(); +// if (strlcat(logfile, "/", sizeof(logfile)) >= sizeof(logfile)) +// usage(); +// break; +// case 'h': +// usehole = 0; +// break; +// case 'i': +// interactive = 1; +// break; +// case 'l': +// logpath = optarg; +// break; +// case 'm': +// monitorstart = getnum(optarg, &endp); +// if (monitorstart < 0) +// usage(); +// if (!endp || *endp++ != ':') +// usage(); +// monitorend = getnum(endp, &endp); +// if (monitorend < 0) +// usage(); +// if (monitorend == 0) +// monitorend = -1; /* aka infinity */ +// debug = 1; +// case 'n': +// sizechecks = 0; +// break; +// case 'o': +// maxoplen = getnum(optarg, &endp); +// if (maxoplen <= 0) +// usage(); +// break; +// case 'p': +// progressinterval = getnum(optarg, &endp); +// if (progressinterval < 0) +// usage(); +// break; +// case 'q': +// quiet = 1; +// break; +// case 'r': +// readbdy = getnum(optarg, &endp); +// if (readbdy <= 0) +// usage(); +// break; +// case 's': +// style = getnum(optarg, &endp); +// if (style < 0 || style > 1) +// usage(); +// break; +// case 't': +// truncbdy = getnum(optarg, &endp); +// if (truncbdy <= 0) +// usage(); +// break; +// case 'v': +// debug = 1; +// break; +// case 'w': +// writebdy = getnum(optarg, &endp); +// if (writebdy <= 0) +// usage(); +// break; +// case 'x': +// xml = 1; +// break; +// case 'y': +// sync_before_close = 1; +// break; +// case 'C': +// gUseRandomNoCache = 1; +// break; +// case 'D': +// debugstart = getnum(optarg, &endp); +// if (debugstart < 1) +// usage(); +// break; +// case 'F': +// maxfilelen = getnum(optarg, &endp); +// if (maxfilelen <= 0) +// usage(); +// break; +// case 'I': +// interactiveSince = getnum(optarg, &endp); +// break; +// case 'G': +// logsize = getnum(optarg, &endp); +// if (logsize < 0) +// usage(); +// break; +// case 'L': +// lite = 1; +// break; +// case 'M': +// slow_motion = 1; +// break; +// case 'N': +// numops = getnum(optarg, &endp); +// if (numops < 0) +// usage(); +// break; +// case 'O': +// randomoplen = 0; +// break; +// case 'P': +// if (strlcpy(goodfile, optarg, sizeof(goodfile)) >= sizeof(goodfile)) +// usage(); +// if (strlcat(goodfile, "/", sizeof(goodfile)) >= sizeof(goodfile)) +// usage(); +// if (strlcpy(logfile, optarg, sizeof(logfile)) >= sizeof(logfile)) +// usage(); +// if (strlcat(logfile, "/", sizeof(logfile)) >= sizeof(logfile)) +// usage(); +// break; +// case 'R': +// mapped_reads = 0; +// break; +// case 'S': +// seed = getnum(optarg, &endp); +// if (seed < 0) +// usage(); +// break; +// case 'T': +// datasize = getnum(optarg, &endp); +// if ((datasize != 1) && (datasize != 2) && (datasize != 4)) +// usage(); +// break; +// case 'W': +// mapped_writes = 0; +// if (!quiet) +// fprintf(stdout, "mapped writes DISABLED\n"); +// break; +// +// default: +// usage(); +// /* NOTREACHED */ +// } +// +// /* for (i = optind; i < argc; i++) +// fname = argv[i]; */ +// +// fname[0] = 0; +// argc -= optind; +// argv += optind; +// if (argc != 1 && argc != 2) +// usage(); +// if (strlcpy(fname, argv[0], sizeof(fname)) >= sizeof(fname)) +// usage(); +// +// if (argc == 2) { +// for (msgbuf = argv[1]; *msgbuf; msgbuf++) +// *msgbuf = ' '; +// msgbuflen = msgbuf - argv[1]; +// msgbuf = argv[1]; +// } +// +// if (fname[0]) { +// if (!quiet) { +// printf("Using file %s\n", fname); +// } +// } else { +// usage(); +// } +// +// modsize = (datasize == 4) ? 0 : (datasize == 2) ? 1<<16 : 1<<8; +// maxfilelen = (maxfilelen + datasize - 1) & ~(datasize - 1); // round up to multiple of datasize +// maxoplen = (maxoplen + datasize - 1) & ~(datasize - 1); // round up to multiple of datasize +// +// signal(SIGHUP, cleanup); +// signal(SIGINT, cleanup); +// signal(SIGPIPE, cleanup); +// signal(SIGALRM, cleanup); +// signal(SIGTERM, cleanup); +// signal(SIGXCPU, cleanup); +// signal(SIGXFSZ, cleanup); +// signal(SIGVTALRM, cleanup); +// signal(SIGUSR1, cleanup); +// signal(SIGUSR2, cleanup); +// +// // Open the log for writing +//#ifdef XILOG +// xilogref = XILogOpenLogExtended(logpath, "fsx", "com.apple.xintegration", NULL, xml, 0, NULL,"ResultOwner","com.apple.fsx",NULL); +// if(xilogref == NULL) { +// fprintf(stderr, "Couldn't create log for path: %s\n", logpath); +// exit(-1); +// } +// +// XILogBeginTestCase(xilogref,"Begin Tests", "Read/Write/MapRead/Truncate/MapWrite Tests"); +//#endif +// +// /* +// * create goodfile and logfile names from fname before potentially adding +// * a fork name to fname +// */ +// if (strlcat(goodfile, fname, sizeof(goodfile)) >= sizeof(goodfile)) +// usage(); +// if (strlcat(goodfile, ".fsxgood", sizeof(goodfile)) >= sizeof(goodfile)) +// usage(); +// if (strlcat(logfile, fname, sizeof(logfile)) >= sizeof(logfile)) +// usage(); +// if (strlcat(logfile, ".fsxlog", sizeof(logfile)) >= sizeof(logfile)) +// usage(); +// /* +// * add forkname to fname if forkname was supplied +// */ +// if (forkname[0] != 0) { +// /* make sure fname exists if working with a named fork */ +// fd = open(fname, O_RDWR|(lite ? 0 : O_CREAT), 0666); +// if (fd < 0) { +// prterr(fname); +// exit(91); +// } else { +// /* don't need it open */ +// close(fd); +// } +// if (strlcat(fname, _PATH_FORKSPECIFIER, sizeof(fname)) >= sizeof(fname)) +// usage(); +// if (strlcat(fname, forkname, sizeof(fname)) >= sizeof(fname)) +// usage(); +// } +// +// oplog = (struct log_entry *) malloc(logsize * sizeof(struct log_entry)); +// if (!oplog) { +// prt("unable to allocate %d entry log", logsize); +// exit(99); +// } +// +// if (seed == 0) +// seed = (time(NULL) + getpid()) | 1; +// initstate(seed, state, 256); +// setstate(state); +// fd = open(fname, O_RDWR|(lite ? 0 : O_CREAT|O_TRUNC), 0666); +// if (fd < 0) { +// prterr(fname); +// exit(91); +// } +// fsxgoodfd = open(goodfile, O_RDWR|O_CREAT|O_TRUNC, 0666); +// if (fsxgoodfd < 0) { +// prterr(goodfile); +// exit(92); +// } +// fsxlogf = fopen(logfile, "w"); +// if (fsxlogf == NULL) { +// prterr(logfile); +// exit(93); +// } +// if (!quiet) { +// prt("command line:"); +// while (*oargv) +// prt(" %s", *oargv++); +// prt("\n"); +// } +// prt("Seed set to %d\n", seed); +// if (lite) { +// off_t ret; +// file_size = maxfilelen = lseek(fd, (off_t)0, SEEK_END); +// if (file_size == (off_t)-1) { +// prterr(fname); +// mwarn("WARNING: main: lseek eof"); +// exit(94); +// } +// ret = lseek(fd, (off_t)0, SEEK_SET); +// if (ret == (off_t)-1) { +// prterr(fname); +// mwarn("WARNING: main: lseek 0"); +// exit(95); +// } +// maxfilelen &= ~(datasize - 1); // round down to multiple of datasize +// } +// good_buf = (char *) malloc(maxfilelen); +// memset(good_buf, '\0', maxfilelen); +// temp_buf = (char *) malloc(maxoplen); +// memset(temp_buf, '\0', maxoplen); +// if (lite) { /* zero entire existing file */ +// ssize_t written; +// +// written = write(fd, good_buf, (size_t)maxfilelen); +// if (written != maxfilelen) { +// if (written == -1) { +// prterr(fname); +// mwarn("WARNING: main: error on write"); +// } else +// mwarn("WARNING: main: short write, 0x%x bytes instead of 0x%x\n", +// (unsigned)written, maxfilelen); +// exit(98); +// } +// } else +// if (!ea) check_trunc_hack(); +// +//#ifdef XILOG +// XIPing(0); +//#endif /* XILOG */ +// +// if(duration > 0) // time based cycling +// { +// time_t currentTime = time(NULL); +// struct tm* tmPtr = localtime(¤tTime); +// tmPtr->tm_sec += duration; +// time_t stopTime = mktime(tmPtr); +// while(time(NULL) < stopTime) { +// if (slow_motion) +// sleep(2); /* wait for 1 second */ +// test(); +//#ifdef XILOG +// if((testcalls % pinginterval) == 0) +// XIPing(testcalls); +//#endif +// } +// } +// else +// { +// while (numops == -1 || numops--) { +// if (interactiveSince == testcalls) { +// interactive = 1; +// debug = 1; +// quiet = 0; +// } +// if (slow_motion) +// sleep(2); /* wait for 1 second */ +// test(); +//#ifdef XILOG +// if((testcalls % pinginterval) == 0) +// XIPing(testcalls); +//#endif +// } +// } +// +// if (sync_before_close && fsync(fd)) { +// logdump(); +// prterr("docloseopen: fsync"); +// failure(182); +// } +// if (close(fd)) { +// logdump(); +// prterr("close"); +// failure(99); +// } +// if (!quiet) +// prt("All operations - %lu - completed A-OK!\n", testcalls); +// +//#ifdef XILOG +// XILogEndTestCase(xilogref, kXILogTestPassOnErrorLevel); +// XILogCloseLog(xilogref); +//#endif +// +// exit(0); +// return 0; +//} + +int fsx_main(int argc, char **argv) +{ + int ch; + char *endp, **oargv = argv; + char goodfile[MAXPATHLEN]; bzero(goodfile, MAXPATHLEN); + char logfile[MAXPATHLEN]; bzero(logfile, MAXPATHLEN); + char forkname[MAXPATHLEN]; bzero(forkname, MAXPATHLEN); + char* logpath = NULL; + eaname = argv[0]; + int xml = 0; + +#if defined(_WIN32) + eaname = "fsx.exe"; /* Windows does not look certain characters in EA's */ +#endif + + goodfile[0] = 0; + logfile[0] = 0; + forkname[0] = 0; + + page_size = getpagesize(); + page_mask = page_size - 1; + + setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */ + + while((ch = getopt_long(argc, argv, "b:c:d:ef:g:hil:m:no:p:qr:s:t:vw:xCD:F:G:I:LMN:OP:RS:T:W", NULL, NULL)) + != EOF) + switch(ch) { + case 'b': + simulatedopcount = getnum(optarg, &endp); + if(!quiet) + fprintf(stdout, "Will begin at operation %ld\n", + simulatedopcount); + if(simulatedopcount == 0) + usage(); + simulatedopcount -= 1; + break; + case 'c': + closeprob = getnum(optarg, &endp); + if(!quiet) + fprintf(stdout, "Chance of close/open is 1 in %d\n", closeprob); + if(closeprob <= 0) + usage(); + break; + case 'd': + duration = parsetime(optarg); + if(duration <= 0) + duration = 0; + printf("Running for %ld seconds\n", duration); + break; + case 'e': + ea = 1; + mapped_writes = 0; + mapped_reads = 0; + sizechecks = 0; + closeopen = 0; + maxoplen = 3802; + maxoplen &= ~(datasize - 1); // round down to multiple of datasize + maxfilelen = 3802; + maxfilelen &= ~(datasize - 1); // round down to multiple of datasize + printf("Writing into extended attribute\n"); + break; + case 'f': + if(strlcpy(forkname, optarg, sizeof(forkname)) >= sizeof(forkname)) + usage(); + break; + case 'g': + if(strlcpy(logfile, optarg, sizeof(logfile)) >= sizeof(logfile)) + usage(); + if(strlcat(logfile, "/", sizeof(logfile)) >= sizeof(logfile)) + usage(); + break; + case 'h': + usehole = 0; + break; + case 'i': + interactive = 1; + break; + case 'l': + logpath = optarg; + break; + case 'm': + monitorstart = getnum(optarg, &endp); + if(monitorstart < 0) + usage(); + if(!endp || *endp++ != ':') + usage(); + monitorend = getnum(endp, &endp); + if(monitorend < 0) + usage(); + if(monitorend == 0) + monitorend = -1; /* aka infinity */ + debug = 1; + case 'n': + sizechecks = 0; + break; + case 'o': + maxoplen = getnum(optarg, &endp); + if(maxoplen <= 0) + usage(); + break; + case 'p': + progressinterval = getnum(optarg, &endp); + if(progressinterval < 0) + usage(); + break; + case 'q': + quiet = 1; + break; + case 'r': + readbdy = getnum(optarg, &endp); + if(readbdy <= 0) + usage(); + break; + case 's': + style = getnum(optarg, &endp); + if(style < 0 || style > 1) + usage(); + break; + case 't': + truncbdy = getnum(optarg, &endp); + if(truncbdy <= 0) + usage(); + break; + case 'v': + debug = 1; + break; + case 'w': + writebdy = getnum(optarg, &endp); + if(writebdy <= 0) + usage(); + break; + case 'x': + xml = 1; + break; + case 'y': + sync_before_close = 1; + break; + case 'C': + gUseRandomNoCache = 1; + break; + case 'D': + debugstart = getnum(optarg, &endp); + if(debugstart < 1) + usage(); + break; + case 'F': + maxfilelen = getnum(optarg, &endp); + if(maxfilelen <= 0) + usage(); + break; + case 'I': + interactiveSince = getnum(optarg, &endp); + break; + case 'G': + logsize = getnum(optarg, &endp); + if(logsize < 0) + usage(); + break; + case 'L': + lite = 1; + break; + case 'M': + slow_motion = 1; + break; + case 'N': + numops = getnum(optarg, &endp); + if(numops < 0) + usage(); + break; + case 'O': + randomoplen = 0; + break; + case 'P': + if(strlcpy(goodfile, optarg, sizeof(goodfile)) >= sizeof(goodfile)) + usage(); + if(strlcat(goodfile, "/", sizeof(goodfile)) >= sizeof(goodfile)) + usage(); + if(strlcpy(logfile, optarg, sizeof(logfile)) >= sizeof(logfile)) + usage(); + if(strlcat(logfile, "/", sizeof(logfile)) >= sizeof(logfile)) + usage(); + break; + case 'R': + mapped_reads = 0; + break; + case 'S': + seed = getnum(optarg, &endp); + if(seed < 0) + usage(); + break; + case 'T': + datasize = getnum(optarg, &endp); + if((datasize != 1) && (datasize != 2) && (datasize != 4)) + usage(); + break; + case 'W': + mapped_writes = 0; + if(!quiet) + fprintf(stdout, "mapped writes DISABLED\n"); + break; + + default: + usage(); + /* NOTREACHED */ + } + + /* for (i = optind; i < argc; i++) + fname = argv[i]; */ + + fname[0] = 0; + argc -= optind; + argv += optind; + if(argc != 1 && argc != 2) + usage(); + if(strlcpy(fname, argv[0], sizeof(fname)) >= sizeof(fname)) + usage(); + + if(argc == 2) { + for(msgbuf = argv[1]; *msgbuf; msgbuf++) + *msgbuf = ' '; + msgbuflen = msgbuf - argv[1]; + msgbuf = argv[1]; + } + + if(fname[0]) { + if(!quiet) { + printf("Using file %s\n", fname); + } + } + else { + usage(); + } + + modsize = (datasize == 4) ? 0 : (datasize == 2) ? 1 << 16 : 1 << 8; + maxfilelen = (maxfilelen + datasize - 1) & ~(datasize - 1); // round up to multiple of datasize + maxoplen = (maxoplen + datasize - 1) & ~(datasize - 1); // round up to multiple of datasize + + /*signal(SIGHUP, cleanup); + signal(SIGINT, cleanup); + signal(SIGPIPE, cleanup); + signal(SIGALRM, cleanup); + signal(SIGTERM, cleanup); + signal(SIGXCPU, cleanup); + signal(SIGXFSZ, cleanup); + signal(SIGVTALRM, cleanup); + signal(SIGUSR1, cleanup); + signal(SIGUSR2, cleanup);*/ + + // Open the log for writing +#ifdef XILOG + xilogref = XILogOpenLogExtended(logpath, "fsx", "com.apple.xintegration", NULL, xml, 0, NULL, "ResultOwner", "com.apple.fsx", NULL); + if(xilogref == NULL) { + fprintf(stderr, "Couldn't create log for path: %s\n", logpath); + exit(-1); + } + + XILogBeginTestCase(xilogref, "Begin Tests", "Read/Write/MapRead/Truncate/MapWrite Tests"); +#endif + + /* + * create goodfile and logfile names from fname before potentially adding + * a fork name to fname + */ + if(strlcat(goodfile, fname, sizeof(goodfile)) >= sizeof(goodfile)) + usage(); + if(strlcat(goodfile, ".fsxgood", sizeof(goodfile)) >= sizeof(goodfile)) + usage(); + if(strlcat(logfile, fname, sizeof(logfile)) >= sizeof(logfile)) + usage(); + if(strlcat(logfile, ".fsxlog", sizeof(logfile)) >= sizeof(logfile)) + usage(); + /* + * add forkname to fname if forkname was supplied + */ + if(forkname[0] != 0) { + /* make sure fname exists if working with a named fork */ + fd = open(fname, O_RDWR | (lite ? 0 : O_CREAT), 0666); + if(fd < 0) { + prterr(fname); + //exit(91); + Assert::Fail(L"File does not exist.", LINE_INFO()); + } + else { + /* don't need it open */ + close(fd); + } + if(strlcat(fname, _PATH_FORKSPECIFIER, sizeof(fname)) >= sizeof(fname)) + usage(); + if(strlcat(fname, forkname, sizeof(fname)) >= sizeof(fname)) + usage(); + } + + oplog = (struct log_entry *) malloc(logsize * sizeof(struct log_entry)); + if(!oplog) { + prt("unable to allocate %d entry log", logsize); + //exit(99); + Assert::Fail(L"Unable to allocate entry log.", LINE_INFO()); + } + + if(seed == 0) + seed = (time(NULL) + getpid()) | 1; + initstate(seed, state, 256); + setstate(state); + + fd = open(fname, O_RDWR | (lite ? 0 : O_CREAT | O_TRUNC), 0666); + + if(fd < 0) { + prterr(fname); + //exit(91); + Assert::Fail(L"Failed to open file.", LINE_INFO()); + } + + fsxgoodfd = open(goodfile, O_RDWR | O_CREAT | O_TRUNC, 0666); + + if(fsxgoodfd < 0) { + + prterr(goodfile); + + close(fd); + + //exit(92); + Assert::Fail(L"Failed to open good file.", LINE_INFO()); + } + + fsxlogf = fopen(logfile, "w"); + + if(fsxlogf == NULL) { + + prterr(logfile); + + close(fsxgoodfd); + close(fd); + + //exit(93); + Assert::Fail(L"Failed to open log file.", LINE_INFO()); + } + + if(!quiet) { + prt("command line:"); + while(*oargv) + prt(" %s", *oargv++); + prt("\n"); + } + + prt("Seed set to %d\n", seed); + + if(lite) { + + off_t ret; + file_size = maxfilelen = lseek(fd, (off_t)0, SEEK_END); + + if(file_size == (off_t)-1) { + prterr(fname); + mwarn("WARNING: main: lseek eof"); + + fclose(fsxlogf); + close(fsxgoodfd); + close(fd); + + //exit(94); + Assert::Fail(L"Failed to seek to end of file.", LINE_INFO()); + } + + ret = lseek(fd, (off_t)0, SEEK_SET); + + if(ret == (off_t)-1) { + + prterr(fname); + mwarn("WARNING: main: lseek 0"); + + fclose(fsxlogf); + close(fsxgoodfd); + close(fd); + + //exit(95); + Assert::Fail(L"Failed to seek to file location.", LINE_INFO()); + } + + maxfilelen &= ~(datasize - 1); // round down to multiple of datasize + } + good_buf = (char *)malloc(maxfilelen); + memset(good_buf, '\0', maxfilelen); + temp_buf = (char *)malloc(maxoplen); + memset(temp_buf, '\0', maxoplen); + + if(lite) { /* zero entire existing file */ + ssize_t written; + + written = write(fd, good_buf, (size_t)maxfilelen); + if(written != maxfilelen) { + if(written == -1) { + prterr(fname); + mwarn("WARNING: main: error on write"); + } + else + mwarn("WARNING: main: short write, 0x%x bytes instead of 0x%x\n", + (unsigned)written, maxfilelen); + + fclose(fsxlogf); + close(fsxgoodfd); + close(fd); + + //exit(98); + Assert::Fail(L"Failed to zero file.", LINE_INFO()); + } + } + else + if(!ea) check_trunc_hack(); + +#ifdef XILOG + XIPing(0); +#endif /* XILOG */ + + if(duration > 0) // time based cycling + { + time_t currentTime = time(NULL); + struct tm* tmPtr = localtime(¤tTime); + tmPtr->tm_sec += duration; + time_t stopTime = mktime(tmPtr); + while(time(NULL) < stopTime) { + if(slow_motion) + sleep(2); /* wait for 1 second */ + test(); +#ifdef XILOG + if((testcalls % pinginterval) == 0) + XIPing(testcalls); +#endif + } + } + else + { + while(numops == -1 || numops--) { + if(interactiveSince == testcalls) { + interactive = 1; + debug = 1; + quiet = 0; + } + if(slow_motion) + sleep(2); /* wait for 1 second */ + test(); +#ifdef XILOG + if((testcalls % pinginterval) == 0) + XIPing(testcalls); +#endif + } + } + + if(sync_before_close && fsync(fd)) { + logdump(); + prterr("docloseopen: fsync"); + failure(182); + } + + if(close(fd)) { + + logdump(); + prterr("close"); + + fclose(fsxlogf); + close(fsxgoodfd); + + failure(99); + } + + if(!quiet) + prt("All operations - %lu - completed A-OK!\n", testcalls); + + fclose(fsxlogf); + close(fsxgoodfd); + +#ifdef XILOG + XILogEndTestCase(xilogref, kXILogTestPassOnErrorLevel); + XILogCloseLog(xilogref); +#endif + + return 0; +} + diff --git a/UnitTests/fsx/win/getopt.cpp b/UnitTests/fsx/win/getopt.cpp new file mode 100644 index 000000000..fd0f3ced9 --- /dev/null +++ b/UnitTests/fsx/win/getopt.cpp @@ -0,0 +1,78 @@ +#include + +/* +POSIX getopt for Windows + +AT&T Public License + +Code given out at the 1985 UNIFORUM conference in Dallas. +*/ + +#ifndef __GNUC__ + +#include "getopt.h" +#include +#include + +//#define NULL 0 +#define EOF (-1) +#define ERR(s, c) if(opterr){\ + char errbuf[2];\ + errbuf[0] = c; errbuf[1] = '\n';\ + fputs(argv[0], stderr);\ + fputs(s, stderr);\ + fputc(c, stderr);} + //(void) write(2, argv[0], (unsigned)strlen(argv[0]));\ + //(void) write(2, s, (unsigned)strlen(s));\ + //(void) write(2, errbuf, 2);} + +int opterr = 1; +int optind = 1; +int optopt; +char *optarg; + +int +getopt(int argc, char **argv, char *opts) +{ + static int sp = 1; + register int c; + register char *cp; + + if(sp == 1) + if(optind >= argc || + argv[optind][0] != '-' || argv[optind][1] == '\0') + return(EOF); + else if(strcmp(argv[optind], "--") == 0) { + optind++; + return(EOF); + } + optopt = c = argv[optind][sp]; + if(c == ':' || (cp=strchr(opts, c)) == NULL) { + ERR(": illegal option -- ", c); + if(argv[optind][++sp] == '\0') { + optind++; + sp = 1; + } + return('?'); + } + if(*++cp == ':') { + if(argv[optind][sp+1] != '\0') + optarg = &argv[optind++][sp+1]; + else if(++optind >= argc) { + ERR(": option requires an argument -- ", c); + sp = 1; + return('?'); + } else + optarg = argv[optind++]; + sp = 1; + } else { + if(argv[optind][++sp] == '\0') { + sp = 1; + optind++; + } + optarg = NULL; + } + return(c); +} + +#endif /* __GNUC__ */ \ No newline at end of file diff --git a/UnitTests/fsx/win/getopt.h b/UnitTests/fsx/win/getopt.h new file mode 100644 index 000000000..64d786a43 --- /dev/null +++ b/UnitTests/fsx/win/getopt.h @@ -0,0 +1,32 @@ +/* +POSIX getopt for Windows + +AT&T Public License + +Code given out at the 1985 UNIFORUM conference in Dallas. +*/ + +#ifdef __GNUC__ +#include +#endif +#ifndef __GNUC__ + +#ifndef _WINGETOPT_H_ +#define _WINGETOPT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern int opterr; +extern int optind; +extern int optopt; +extern char *optarg; +extern int getopt(int argc, char **argv, char *opts); + +#ifdef __cplusplus +} +#endif + +#endif /* _GETOPT_H_ */ +#endif /* __GNUC__ */ diff --git a/UnitTests/fsx/win/mman.cpp b/UnitTests/fsx/win/mman.cpp new file mode 100644 index 000000000..9dd76a2b6 --- /dev/null +++ b/UnitTests/fsx/win/mman.cpp @@ -0,0 +1,414 @@ +#include + +/* + * sys/mman.c + * mman-win32 + * + * https://code.google.com/p/mman-win32/ + * MIT License + */ + +#include +#include +#include + +#include "mman.h" + +#ifndef FILE_MAP_EXECUTE +#define FILE_MAP_EXECUTE 0x0020 +#endif /* FILE_MAP_EXECUTE */ + +int __acrt_errno_map_os_error(const DWORD err) +{ + if (err == 0) + return 0; + + switch(err) + { + // https://github.com/Feuerlabs/fnotify/blob/master/c_src/dosmap.c + case 1: return EINVAL; + case 2: return ENOENT; + case 3: return ENOENT; + case 4: return EMFILE; + case 5: return EACCES; + case 6: return EBADF ; + case 7: return ENOMEM; + case 8: return ENOMEM; + case 9: return ENOMEM; + case 10: return E2BIG ; + case 11: return ENOEXEC; + case 12: return EINVAL; + case 13: return EINVAL; + case 14: return EINVAL; + case 15: return ENOENT; + case 16: return EACCES; + case 17: return EXDEV ; + case 18: return ENOENT; + case 19: return EACCES; + case 20: return EACCES; + case 21: return EACCES; + case 22: return EACCES; + case 23: return EACCES; + case 24: return EACCES; + case 25: return EACCES; + case 26: return EACCES; + case 27: return EACCES; + case 28: return EACCES; + case 29: return EACCES; + case 30: return EACCES; + case 31: return EACCES; + case 32: return EACCES; + case 33: return EACCES; + case 34: return EACCES; + case 35: return EACCES; + case 36: return EACCES; + case 37: return EINVAL; + case 38: return EINVAL; + case 39: return EINVAL; + case 40: return EINVAL; + case 41: return EINVAL; + case 42: return EINVAL; + case 43: return EINVAL; + case 44: return EINVAL; + case 45: return EINVAL; + case 46: return EINVAL; + case 47: return EINVAL; + case 48: return EINVAL; + case 49: return EINVAL; + case 50: return EINVAL; + case 51: return EINVAL; + case 52: return EINVAL; + case 53: return ENOENT; + case 54: return EINVAL; + case 55: return EINVAL; + case 56: return EINVAL; + case 57: return EINVAL; + case 58: return EINVAL; + case 59: return EINVAL; + case 60: return EINVAL; + case 61: return EINVAL; + case 62: return EINVAL; + case 63: return EINVAL; + case 64: return EINVAL; + case 65: return EACCES; + case 66: return EINVAL; + case 67: return ENOENT; + case 68: return EINVAL; + case 69: return EINVAL; + case 70: return EINVAL; + case 71: return EINVAL; + case 72: return EINVAL; + case 73: return EINVAL; + case 74: return EINVAL; + case 75: return EINVAL; + case 76: return EINVAL; + case 77: return EINVAL; + case 78: return EINVAL; + case 79: return EINVAL; + case 80: return EEXIST; + case 81: return EINVAL; + case 82: return EACCES; + case 83: return EACCES; + case 84: return EINVAL; + case 85: return EINVAL; + case 86: return EINVAL; + case 87: return EINVAL; + case 88: return EINVAL; + case 89: return EAGAIN; + case 90: return EINVAL; + case 91: return EINVAL; + case 92: return EINVAL; + case 93: return EINVAL; + case 94: return EINVAL; + case 95: return EINVAL; + case 96: return EINVAL; + case 97: return EINVAL; + case 98: return EINVAL; + case 99: return EINVAL; + case 100: return EINVAL; + case 101: return EINVAL; + case 102: return EINVAL; + case 103: return EINVAL; + case 104: return EINVAL; + case 105: return EINVAL; + case 106: return EINVAL; + case 107: return EINVAL; + case 108: return EACCES; + case 109: return EPIPE ; + case 110: return EINVAL; + case 111: return EINVAL; + case 112: return ENOSPC; + case 113: return EINVAL; + case 114: return EBADF ; + case 115: return EINVAL; + case 116: return EINVAL; + case 117: return EINVAL; + case 118: return EINVAL; + case 119: return EINVAL; + case 120: return EINVAL; + case 121: return EINVAL; + case 122: return EINVAL; + case 123: return EINVAL; + case 124: return EINVAL; + case 125: return EINVAL; + case 126: return EINVAL; + case 127: return EINVAL; + case 128: return ECHILD; + case 129: return ECHILD; + case 130: return EBADF ; + case 131: return EINVAL; + case 132: return EACCES; + case 133: return EINVAL; + case 134: return EINVAL; + case 135: return EINVAL; + case 136: return EINVAL; + case 137: return EINVAL; + case 138: return EINVAL; + case 139: return EINVAL; + case 140: return EINVAL; + case 141: return EINVAL; + case 142: return EINVAL; + case 143: return EINVAL; + case 144: return EINVAL; + case 145: return ENOTEMPTY; + case 146: return EINVAL; + case 147: return EINVAL; + case 148: return EINVAL; + case 149: return EINVAL; + case 150: return EINVAL; + case 151: return EINVAL; + case 152: return EINVAL; + case 153: return EINVAL; + case 154: return EINVAL; + case 155: return EINVAL; + case 156: return EINVAL; + case 157: return EINVAL; + case 158: return EACCES; + case 159: return EINVAL; + case 160: return EINVAL; + case 161: return ENOENT; + case 162: return EINVAL; + case 163: return EINVAL; + case 164: return EAGAIN; + case 165: return EINVAL; + case 166: return EINVAL; + case 167: return EACCES; + case 168: return EINVAL; + case 169: return EINVAL; + case 170: return EINVAL; + case 171: return EINVAL; + case 172: return EINVAL; + case 173: return EINVAL; + case 174: return EINVAL; + case 175: return EINVAL; + case 176: return EINVAL; + case 177: return EINVAL; + case 178: return EINVAL; + case 179: return EINVAL; + case 180: return EINVAL; + case 181: return EINVAL; + case 182: return EINVAL; + case 183: return EEXIST; + case 184: return EINVAL; + case 185: return EINVAL; + case 186: return EINVAL; + case 187: return EINVAL; + case 188: return ENOEXEC; + case 189: return ENOEXEC; + case 190: return ENOEXEC; + case 191: return ENOEXEC; + case 192: return ENOEXEC; + case 193: return ENOEXEC; + case 194: return ENOEXEC; + case 195: return ENOEXEC; + case 196: return ENOEXEC; + case 197: return ENOEXEC; + case 198: return ENOEXEC; + case 199: return ENOEXEC; + case 200: return ENOEXEC; + case 201: return ENOEXEC; + case 202: return ENOEXEC; + case 203: return EINVAL; + case 204: return EINVAL; + case 205: return EINVAL; + case 206: return ENOENT; + case 207: return EINVAL; + case 208: return EINVAL; + case 209: return EINVAL; + case 210: return EINVAL; + case 211: return EINVAL; + case 212: return EINVAL; + case 213: return EINVAL; + case 214: return EINVAL; + case 215: return EAGAIN; + } + + return EINVAL; +} + +static int __map_mman_error(const DWORD err, const int deferr) +{ + return __acrt_errno_map_os_error(err); +} + +static DWORD __map_mmap_prot_page(const int prot) +{ + DWORD protect = 0; + + if (prot == PROT_NONE) + return protect; + + if ((prot & PROT_EXEC) != 0) + { + protect = ((prot & PROT_WRITE) != 0) ? + PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ; + } + else + { + protect = ((prot & PROT_WRITE) != 0) ? + PAGE_READWRITE : PAGE_READONLY; + } + + return protect; +} + +static DWORD __map_mmap_prot_file(const int prot) +{ + DWORD desiredAccess = 0; + + if (prot == PROT_NONE) + return desiredAccess; + + if ((prot & PROT_READ) != 0) + desiredAccess |= FILE_MAP_READ; + if ((prot & PROT_WRITE) != 0) + desiredAccess |= FILE_MAP_WRITE; + if ((prot & PROT_EXEC) != 0) + desiredAccess |= FILE_MAP_EXECUTE; + + return desiredAccess; +} + +void* mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off) +{ + HANDLE fm, h; + + void * map = MAP_FAILED; + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4293) +#endif + + const DWORD dwFileOffsetLow = (sizeof(off_t) <= sizeof(DWORD)) ? + (DWORD)off : (DWORD)(off & 0xFFFFFFFFL); + const DWORD dwFileOffsetHigh = (sizeof(off_t) <= sizeof(DWORD)) ? + (DWORD)0 : (DWORD)((off >> 32) & 0xFFFFFFFFL); + const DWORD protect = __map_mmap_prot_page(prot); + const DWORD desiredAccess = __map_mmap_prot_file(prot); + + const off_t maxSize = off + (off_t)len; + + const DWORD dwMaxSizeLow = (sizeof(off_t) <= sizeof(DWORD)) ? + (DWORD)maxSize : (DWORD)(maxSize & 0xFFFFFFFFL); + const DWORD dwMaxSizeHigh = (sizeof(off_t) <= sizeof(DWORD)) ? + (DWORD)0 : (DWORD)((maxSize >> 32) & 0xFFFFFFFFL); + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + errno = 0; + + if (len == 0 + /* Unsupported flag combinations */ + || (flags & MAP_FIXED) != 0 + /* Usupported protection combinations */ + || prot == PROT_EXEC) + { + errno = EINVAL; + return MAP_FAILED; + } + + h = ((flags & MAP_ANONYMOUS) == 0) ? + (HANDLE)_get_osfhandle(fildes) : INVALID_HANDLE_VALUE; + + if ((flags & MAP_ANONYMOUS) == 0 && h == INVALID_HANDLE_VALUE) + { + errno = EBADF; + return MAP_FAILED; + } + + fm = CreateFileMapping(h, NULL, protect, dwMaxSizeHigh, dwMaxSizeLow, NULL); + + if (fm == NULL) + { + errno = __map_mman_error(GetLastError(), EPERM); + return MAP_FAILED; + } + + map = MapViewOfFile(fm, desiredAccess, dwFileOffsetHigh, dwFileOffsetLow, len); + + CloseHandle(fm); + + if (map == NULL) + { + errno = __map_mman_error(GetLastError(), EPERM); + return MAP_FAILED; + } + + return map; +} + +int munmap(void *addr, size_t len) +{ + if (UnmapViewOfFile(addr)) + return 0; + + errno = __map_mman_error(GetLastError(), EPERM); + + return -1; +} + +int mprotect(void *addr, size_t len, int prot) +{ + DWORD newProtect = __map_mmap_prot_page(prot); + DWORD oldProtect = 0; + + if (VirtualProtect(addr, len, newProtect, &oldProtect)) + return 0; + + errno = __map_mman_error(GetLastError(), EPERM); + + return -1; +} + +int msync(void *addr, size_t len, int flags) +{ + if (FlushViewOfFile(addr, len)) + return 0; + + errno = __map_mman_error(GetLastError(), EPERM); + + return -1; +} + +int mlock(const void *addr, size_t len) +{ + if (VirtualLock((LPVOID)addr, len)) + return 0; + + errno = __map_mman_error(GetLastError(), EPERM); + + return -1; +} + +int munlock(const void *addr, size_t len) +{ + if (VirtualUnlock((LPVOID)addr, len)) + return 0; + + errno = __map_mman_error(GetLastError(), EPERM); + + return -1; +} diff --git a/UnitTests/fsx/win/mman.h b/UnitTests/fsx/win/mman.h new file mode 100644 index 000000000..46785a874 --- /dev/null +++ b/UnitTests/fsx/win/mman.h @@ -0,0 +1,58 @@ +/* + * sys/mman.h + * mman-win32 + * + * https://code.google.com/p/mman-win32/ + * MIT License + */ + +#ifndef _SYS_MMAN_H_ +#define _SYS_MMAN_H_ + +#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. +#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +/* All the headers include this file. */ +#ifndef _MSC_VER +#include <_mingw.h> +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define PROT_NONE 0 +#define PROT_READ 1 +#define PROT_WRITE 2 +#define PROT_EXEC 4 + +#define MAP_FILE 0 +#define MAP_SHARED 1 +#define MAP_PRIVATE 2 +#define MAP_TYPE 0xf +#define MAP_FIXED 0x10 +#define MAP_ANONYMOUS 0x20 +#define MAP_ANON MAP_ANONYMOUS + +#define MAP_FAILED ((void *)-1) + +/* Flags for msync. */ +#define MS_ASYNC 1 +#define MS_SYNC 2 +#define MS_INVALIDATE 4 + +void* mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off); +int munmap(void *addr, size_t len); +int mprotect(void *addr, size_t len, int prot); +int msync(void *addr, size_t len, int flags); +int mlock(const void *addr, size_t len); +int munlock(const void *addr, size_t len); + +#ifdef __cplusplus +}; +#endif + +#endif /* _SYS_MMAN_H_ */ diff --git a/UnitTests/fsx/win/random.cpp b/UnitTests/fsx/win/random.cpp new file mode 100644 index 000000000..85561ef82 --- /dev/null +++ b/UnitTests/fsx/win/random.cpp @@ -0,0 +1,494 @@ +#include + +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if !defined(_WIN32) +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)random.c 8.2 (Berkeley) 5/19/95"; +#endif /* LIBC_SCCS and not lint */ +#include +__FBSDID("$FreeBSD$"); +#endif + +#if !defined(_WIN32) +#include "namespace.h" +#include +#include +#endif +#include +#include +#include +#if !defined(_WIN32) +#include "un-namespace.h" +#endif + +/* + * random.c: + * + * An improved random number generation package. In addition to the standard + * rand()/srand() like interface, this package also has a special state info + * interface. The initstate() routine is called with a seed, an array of + * bytes, and a count of how many bytes are being passed in; this array is + * then initialized to contain information for random number generation with + * that much state information. Good sizes for the amount of state + * information are 32, 64, 128, and 256 bytes. The state can be switched by + * calling the setstate() routine with the same array as was initiallized + * with initstate(). By default, the package runs with 128 bytes of state + * information and generates far better random numbers than a linear + * congruential generator. If the amount of state information is less than + * 32 bytes, a simple linear congruential R.N.G. is used. + * + * Internally, the state information is treated as an array of uint32_t's; the + * zeroeth element of the array is the type of R.N.G. being used (small + * integer); the remainder of the array is the state information for the + * R.N.G. Thus, 32 bytes of state information will give 7 ints worth of + * state information, which will allow a degree seven polynomial. (Note: + * the zeroeth word of state information also has some other information + * stored in it -- see setstate() for details). + * + * The random number generation technique is a linear feedback shift register + * approach, employing trinomials (since there are fewer terms to sum up that + * way). In this approach, the least significant bit of all the numbers in + * the state table will act as a linear feedback shift register, and will + * have period 2^deg - 1 (where deg is the degree of the polynomial being + * used, assuming that the polynomial is irreducible and primitive). The + * higher order bits will have longer periods, since their values are also + * influenced by pseudo-random carries out of the lower bits. The total + * period of the generator is approximately deg*(2**deg - 1); thus doubling + * the amount of state information has a vast influence on the period of the + * generator. Note: the deg*(2**deg - 1) is an approximation only good for + * large deg, when the period of the shift is the dominant factor. + * With deg equal to seven, the period is actually much longer than the + * 7*(2**7 - 1) predicted by this formula. + * + * Modified 28 December 1994 by Jacob S. Rosenberg. + * The following changes have been made: + * All references to the type u_int have been changed to unsigned long. + * All references to type int have been changed to type long. Other + * cleanups have been made as well. A warning for both initstate and + * setstate has been inserted to the effect that on Sparc platforms + * the 'arg_state' variable must be forced to begin on word boundaries. + * This can be easily done by casting a long integer array to char *. + * The overall logic has been left STRICTLY alone. This software was + * tested on both a VAX and Sun SpacsStation with exactly the same + * results. The new version and the original give IDENTICAL results. + * The new version is somewhat faster than the original. As the + * documentation says: "By default, the package runs with 128 bytes of + * state information and generates far better random numbers than a linear + * congruential generator. If the amount of state information is less than + * 32 bytes, a simple linear congruential R.N.G. is used." For a buffer of + * 128 bytes, this new version runs about 19 percent faster and for a 16 + * byte buffer it is about 5 percent faster. + */ + +/* + * For each of the currently supported random number generators, we have a + * break value on the amount of state information (you need at least this + * many bytes of state info to support this random number generator), a degree + * for the polynomial (actually a trinomial) that the R.N.G. is based on, and + * the separation between the two lower order coefficients of the trinomial. + */ +#define TYPE_0 0 /* linear congruential */ +#define BREAK_0 8 +#define DEG_0 0 +#define SEP_0 0 + +#define TYPE_1 1 /* x**7 + x**3 + 1 */ +#define BREAK_1 32 +#define DEG_1 7 +#define SEP_1 3 + +#define TYPE_2 2 /* x**15 + x + 1 */ +#define BREAK_2 64 +#define DEG_2 15 +#define SEP_2 1 + +#define TYPE_3 3 /* x**31 + x**3 + 1 */ +#define BREAK_3 128 +#define DEG_3 31 +#define SEP_3 3 + +#define TYPE_4 4 /* x**63 + x + 1 */ +#define BREAK_4 256 +#define DEG_4 63 +#define SEP_4 1 + +/* + * Array versions of the above information to make code run faster -- + * relies on fact that TYPE_i == i. + */ +#define MAX_TYPES 5 /* max number of types above */ + +#ifdef USE_WEAK_SEEDING +#define NSHUFF 0 +#else /* !USE_WEAK_SEEDING */ +#define NSHUFF 50 /* to drop some "seed -> 1st value" linearity */ +#endif /* !USE_WEAK_SEEDING */ + +static const int degrees[MAX_TYPES] = { DEG_0, DEG_1, DEG_2, DEG_3, DEG_4 }; +static const int seps [MAX_TYPES] = { SEP_0, SEP_1, SEP_2, SEP_3, SEP_4 }; + +/* + * Initially, everything is set up as if from: + * + * initstate(1, randtbl, 128); + * + * Note that this initialization takes advantage of the fact that srandom() + * advances the front and rear pointers 10*rand_deg times, and hence the + * rear pointer which starts at 0 will also end up at zero; thus the zeroeth + * element of the state information, which contains info about the current + * position of the rear pointer is just + * + * MAX_TYPES * (rptr - state) + TYPE_3 == TYPE_3. + */ + +static uint32_t randtbl[DEG_3 + 1] = { + TYPE_3, +#ifdef USE_WEAK_SEEDING +/* Historic implementation compatibility */ +/* The random sequences do not vary much with the seed */ + 0x9a319039, 0x32d9c024, 0x9b663182, 0x5da1f342, 0xde3b81e0, 0xdf0a6fb5, + 0xf103bc02, 0x48f340fb, 0x7449e56b, 0xbeb1dbb0, 0xab5c5918, 0x946554fd, + 0x8c2e680f, 0xeb3d799f, 0xb11ee0b7, 0x2d436b86, 0xda672e2a, 0x1588ca88, + 0xe369735d, 0x904f35f7, 0xd7158fd6, 0x6fa6f051, 0x616e6b96, 0xac94efdc, + 0x36413f93, 0xc622c298, 0xf5a42ab8, 0x8a88d77b, 0xf5ad9d0e, 0x8999220b, + 0x27fb47b9, +#else /* !USE_WEAK_SEEDING */ + 0x991539b1, 0x16a5bce3, 0x6774a4cd, 0x3e01511e, 0x4e508aaa, 0x61048c05, + 0xf5500617, 0x846b7115, 0x6a19892c, 0x896a97af, 0xdb48f936, 0x14898454, + 0x37ffd106, 0xb58bff9c, 0x59e17104, 0xcf918a49, 0x09378c83, 0x52c7a471, + 0x8d293ea9, 0x1f4fc301, 0xc3db71be, 0x39b44e1c, 0xf8a44ef9, 0x4c8b80b1, + 0x19edc328, 0x87bf4bdd, 0xc9b240e5, 0xe9ee4b1b, 0x4382aee7, 0x535b6b41, + 0xf3bec5da +#endif /* !USE_WEAK_SEEDING */ +}; + +/* + * fptr and rptr are two pointers into the state info, a front and a rear + * pointer. These two pointers are always rand_sep places aparts, as they + * cycle cyclically through the state information. (Yes, this does mean we + * could get away with just one pointer, but the code for random() is more + * efficient this way). The pointers are left positioned as they would be + * from the call + * + * initstate(1, randtbl, 128); + * + * (The position of the rear pointer, rptr, is really 0 (as explained above + * in the initialization of randtbl) because the state table pointer is set + * to point to randtbl[1] (as explained below). + */ +static uint32_t *fptr = &randtbl[SEP_3 + 1]; +static uint32_t *rptr = &randtbl[1]; + +/* + * The following things are the pointer to the state information table, the + * type of the current generator, the degree of the current polynomial being + * used, and the separation between the two pointers. Note that for efficiency + * of random(), we remember the first location of the state information, not + * the zeroeth. Hence it is valid to access state[-1], which is used to + * store the type of the R.N.G. Also, we remember the last location, since + * this is more efficient than indexing every time to find the address of + * the last element to see if the front and rear pointers have wrapped. + */ +static uint32_t *state = &randtbl[1]; +static int rand_type = TYPE_3; +static int rand_deg = DEG_3; +static int rand_sep = SEP_3; +static uint32_t *end_ptr = &randtbl[DEG_3 + 1]; + +long random(); + +static inline uint32_t +good_rand(int32_t x) +{ +#ifdef USE_WEAK_SEEDING +/* + * Historic implementation compatibility. + * The random sequences do not vary much with the seed, + * even with overflowing. + */ + return (1103515245 * x + 12345); +#else /* !USE_WEAK_SEEDING */ +/* + * Compute x = (7^5 * x) mod (2^31 - 1) + * wihout overflowing 31 bits: + * (2^31 - 1) = 127773 * (7^5) + 2836 + * From "Random number generators: good ones are hard to find", + * Park and Miller, Communications of the ACM, vol. 31, no. 10, + * October 1988, p. 1195. + */ + int32_t hi, lo; + + /* Can't be initialized with 0, so use another value. */ + if (x == 0) + x = 123459876; + hi = x / 127773; + lo = x % 127773; + x = 16807 * lo - 2836 * hi; + if (x < 0) + x += 0x7fffffff; + return (x); +#endif /* !USE_WEAK_SEEDING */ +} + +/* + * srandom: + * + * Initialize the random number generator based on the given seed. If the + * type is the trivial no-state-information type, just remember the seed. + * Otherwise, initializes state[] based on the given "seed" via a linear + * congruential generator. Then, the pointers are set to known locations + * that are exactly rand_sep places apart. Lastly, it cycles the state + * information a given number of times to get rid of any initial dependencies + * introduced by the L.C.R.N.G. Note that the initialization of randtbl[] + * for default usage relies on values produced by this routine. + */ +void +srandom(unsigned long x) +{ + int i, lim; + + state[0] = (uint32_t)x; + if (rand_type == TYPE_0) + lim = NSHUFF; + else { + for (i = 1; i < rand_deg; i++) + state[i] = good_rand(state[i - 1]); + fptr = &state[rand_sep]; + rptr = &state[0]; + lim = 10 * rand_deg; + } + for (i = 0; i < lim; i++) + (void)random(); +} + +#if !defined(_WIN32) +/* + * srandomdev: + * + * Many programs choose the seed value in a totally predictable manner. + * This often causes problems. We seed the generator using pseudo-random + * data from the kernel. + * + * Note that this particular seeding procedure can generate states + * which are impossible to reproduce by calling srandom() with any + * value, since the succeeding terms in the state buffer are no longer + * derived from the LC algorithm applied to a fixed seed. + */ +void +srandomdev(void) +{ + int mib[2]; + size_t len; + + if (rand_type == TYPE_0) + len = sizeof(state[0]); + else + len = rand_deg * sizeof(state[0]); + + mib[0] = CTL_KERN; + mib[1] = KERN_ARND; + sysctl(mib, 2, state, &len, NULL, 0); + + if (rand_type != TYPE_0) { + fptr = &state[rand_sep]; + rptr = &state[0]; + } +} +#endif + +/* + * initstate: + * + * Initialize the state information in the given array of n bytes for future + * random number generation. Based on the number of bytes we are given, and + * the break values for the different R.N.G.'s, we choose the best (largest) + * one we can and set things up for it. srandom() is then called to + * initialize the state information. + * + * Note that on return from srandom(), we set state[-1] to be the type + * multiplexed with the current value of the rear pointer; this is so + * successive calls to initstate() won't lose this information and will be + * able to restart with setstate(). + * + * Note: the first thing we do is save the current state, if any, just like + * setstate() so that it doesn't matter when initstate is called. + * + * Returns a pointer to the old state. + * + * Note: The Sparc platform requires that arg_state begin on an int + * word boundary; otherwise a bus error will occur. Even so, lint will + * complain about mis-alignment, but you should disregard these messages. + */ +char * +initstate(unsigned long seed, char *arg_state, long n) +{ + char *ostate = (char *)(&state[-1]); + uint32_t *int_arg_state = (uint32_t *)arg_state; + + if (rand_type == TYPE_0) + state[-1] = rand_type; + else + state[-1] = MAX_TYPES * (rptr - state) + rand_type; + if (n < BREAK_0) { + (void)fprintf(stderr, + "random: not enough state (%ld bytes); ignored.\n", n); + return (0); + } + if (n < BREAK_1) { + rand_type = TYPE_0; + rand_deg = DEG_0; + rand_sep = SEP_0; + } else if (n < BREAK_2) { + rand_type = TYPE_1; + rand_deg = DEG_1; + rand_sep = SEP_1; + } else if (n < BREAK_3) { + rand_type = TYPE_2; + rand_deg = DEG_2; + rand_sep = SEP_2; + } else if (n < BREAK_4) { + rand_type = TYPE_3; + rand_deg = DEG_3; + rand_sep = SEP_3; + } else { + rand_type = TYPE_4; + rand_deg = DEG_4; + rand_sep = SEP_4; + } + state = int_arg_state + 1; /* first location */ + end_ptr = &state[rand_deg]; /* must set end_ptr before srandom */ + srandom(seed); + if (rand_type == TYPE_0) + int_arg_state[0] = rand_type; + else + int_arg_state[0] = MAX_TYPES * (rptr - state) + rand_type; + return (ostate); +} + +/* + * setstate: + * + * Restore the state from the given state array. + * + * Note: it is important that we also remember the locations of the pointers + * in the current state information, and restore the locations of the pointers + * from the old state information. This is done by multiplexing the pointer + * location into the zeroeth word of the state information. + * + * Note that due to the order in which things are done, it is OK to call + * setstate() with the same state as the current state. + * + * Returns a pointer to the old state information. + * + * Note: The Sparc platform requires that arg_state begin on an int + * word boundary; otherwise a bus error will occur. Even so, lint will + * complain about mis-alignment, but you should disregard these messages. + */ +char * +setstate(const char *arg_state) +{ + uint32_t *new_state = (uint32_t *)arg_state; + uint32_t type = new_state[0] % MAX_TYPES; + uint32_t rear = new_state[0] / MAX_TYPES; + char *ostate = (char *)(&state[-1]); + + if (rand_type == TYPE_0) + state[-1] = rand_type; + else + state[-1] = MAX_TYPES * (rptr - state) + rand_type; + switch(type) { + case TYPE_0: + case TYPE_1: + case TYPE_2: + case TYPE_3: + case TYPE_4: + rand_type = type; + rand_deg = degrees[type]; + rand_sep = seps[type]; + break; + default: + (void)fprintf(stderr, + "random: state info corrupted; not changed.\n"); + } + state = new_state + 1; + if (rand_type != TYPE_0) { + rptr = &state[rear]; + fptr = &state[(rear + rand_sep) % rand_deg]; + } + end_ptr = &state[rand_deg]; /* set end_ptr too */ + return (ostate); +} + +/* + * random: + * + * If we are using the trivial TYPE_0 R.N.G., just do the old linear + * congruential bit. Otherwise, we do our fancy trinomial stuff, which is + * the same in all the other cases due to all the global variables that have + * been set up. The basic operation is to add the number at the rear pointer + * into the one at the front pointer. Then both pointers are advanced to + * the next location cyclically in the table. The value returned is the sum + * generated, reduced to 31 bits by throwing away the "least random" low bit. + * + * Note: the code takes advantage of the fact that both the front and + * rear pointers can't wrap on the same call by not testing the rear + * pointer if the front one has wrapped. + * + * Returns a 31-bit random number. + */ +long +random(void) +{ + uint32_t i; + uint32_t *f, *r; + + if (rand_type == TYPE_0) { + i = state[0]; + state[0] = i = (good_rand(i)) & 0x7fffffff; + } else { + /* + * Use local variables rather than static variables for speed. + */ + f = fptr; r = rptr; + *f += *r; + i = (*f >> 1) & 0x7fffffff; /* chucking least random bit */ + if (++f >= end_ptr) { + f = state; + ++r; + } + else if (++r >= end_ptr) { + r = state; + } + + fptr = f; rptr = r; + } + return ((long)i); +} From a0d9eedadb570085e327c906cce90b3caf8b2e1e Mon Sep 17 00:00:00 2001 From: Keith Newton Date: Tue, 9 Aug 2016 21:25:22 -0400 Subject: [PATCH 17/20] Added unit tests for file security #210 --- UnitTests/FileSystemTests.cpp | 427 ++++++++++++++++++++++++++++++---- UnitTests/UnitTests.vcxproj | 8 +- 2 files changed, 384 insertions(+), 51 deletions(-) diff --git a/UnitTests/FileSystemTests.cpp b/UnitTests/FileSystemTests.cpp index eccb9a144..4226a7d48 100644 --- a/UnitTests/FileSystemTests.cpp +++ b/UnitTests/FileSystemTests.cpp @@ -3,6 +3,8 @@ #include #include +#include + #include #include #include @@ -18,7 +20,7 @@ using namespace Microsoft::VisualStudio::CppUnitTestFramework; const wchar_t* GetErrorCodeStr(const DWORD errCode); -#define USE_MIRROR 0 +#define USE_MIRROR 1 int fsx_main(int argc, char **argv); @@ -30,6 +32,83 @@ namespace UnitTests inline bool IsValidHandle(HANDLE handle) { return handle && handle != INVALID_HANDLE_VALUE; } + std::unique_ptr GetSecurityForFile( + const wchar_t *filePath, + const SECURITY_INFORMATION requestedInformation, + DWORD &securitySize) + { + std::wstring errMsg; + + if(!GetFileSecurityW(filePath, requestedInformation, nullptr, 0, &securitySize) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to retrieve security information size for file \'%s\'.", filePath)); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + std::unique_ptr securityDesc = std::make_unique(securitySize); + + if(!GetFileSecurityW(filePath, DACL_SECURITY_INFORMATION, (PSECURITY_DESCRIPTOR)securityDesc.get(), securitySize, &securitySize)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to retrieve security information for file \'%s\'.", filePath)); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + return securityDesc; + } + + std::wstring SecurityDescriptorToString(const PSECURITY_DESCRIPTOR security, const SECURITY_INFORMATION requestedInformation) + { + LPWSTR buf; + + if(!ConvertSecurityDescriptorToStringSecurityDescriptorW( + security, + SDDL_REVISION_1, + requestedInformation, + &buf, + nullptr)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString( + L"Failed to convert a security descriptor with requested information 0x%x into its string representation.", + requestedInformation)); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + std::wstring retStr = buf; + + LocalFree(buf); + + return retStr; + } + + std::unique_ptr SecurityDescriptorFromString(const wchar_t *str, ULONG &size) + { + PSECURITY_DESCRIPTOR temp; + + if(!ConvertStringSecurityDescriptorToSecurityDescriptorW( + str, + SDDL_REVISION_1, + &temp, + &size)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString( + L"Failed to convert string \'%s\' into a security descriptor.", + str)); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + std::unique_ptr securityDesc = std::make_unique(size); + + memcpy_s(securityDesc.get(), size, temp, size); + + LocalFree(temp); + + return securityDesc; + } + std::wstring GetTempPathStr() { WCHAR path[MAX_PATH]; @@ -112,7 +191,7 @@ namespace UnitTests if(!PathFileExistsW(testDir.c_str())) { - if(!CreateDirectoryW(testDir.c_str(), NULL)) + if(!CreateDirectoryW(testDir.c_str(), nullptr)) { std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CreateDirectoryW for directory \'%s\'.", testDir.c_str())); @@ -133,7 +212,7 @@ namespace UnitTests if(!PathFileExistsA(testDir.c_str())) { - if(!CreateDirectoryA(testDir.c_str(), NULL)) + if(!CreateDirectoryA(testDir.c_str(), nullptr)) { std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CreateDirectoryW for directory \'%s\'.", testDir.c_str())); @@ -233,18 +312,18 @@ namespace UnitTests std::wstring GetLastErrorStr(const DWORD lastErr) { - LPWSTR buffer = NULL; + LPWSTR buffer = nullptr; // we force english because the error messages are for developers and english is the common language // developers of all countries use to communicate DWORD errCode = FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, + nullptr, lastErr, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), (LPWSTR)&buffer, 0, - NULL); + nullptr); if(errCode == 0) { @@ -252,12 +331,12 @@ namespace UnitTests { errCode = FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, + nullptr, lastErr, 0, (LPWSTR)&buffer, 0, - NULL); + nullptr); } if(errCode == 0) @@ -304,7 +383,7 @@ namespace UnitTests | STANDARD_RIGHTS_WRITE | STANDARD_RIGHTS_EXECUTE; - HANDLE handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); if(!IsValidHandle(handle)) { @@ -355,7 +434,7 @@ namespace UnitTests if(!CloseHandle(handle)) { - std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); Assert::Fail(errMsg.c_str(), LINE_INFO()); } @@ -389,7 +468,7 @@ namespace UnitTests | STANDARD_RIGHTS_WRITE | STANDARD_RIGHTS_EXECUTE; - HANDLE handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); if(!IsValidHandle(handle)) { @@ -421,12 +500,12 @@ namespace UnitTests if(!CloseHandle(handle)) { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); Assert::Fail(errMsg.c_str(), LINE_INFO()); } - handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, NULL); + handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, nullptr); if(!IsValidHandle(handle)) { @@ -457,7 +536,7 @@ namespace UnitTests if(!CloseHandle(handle)) { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); Assert::Fail(errMsg.c_str(), LINE_INFO()); } @@ -469,7 +548,7 @@ namespace UnitTests Assert::Fail(errMsg.c_str(), LINE_INFO()); } - handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_SYSTEM, NULL); + handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_SYSTEM, nullptr); if(!IsValidHandle(handle)) { @@ -500,7 +579,7 @@ namespace UnitTests if(!CloseHandle(handle)) { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); Assert::Fail(errMsg.c_str(), LINE_INFO()); } @@ -519,7 +598,7 @@ namespace UnitTests Assert::Fail(errMsg.c_str(), LINE_INFO()); } - handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL); + handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, nullptr); if(!IsValidHandle(handle)) { @@ -550,7 +629,7 @@ namespace UnitTests if(!CloseHandle(handle)) { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); Assert::Fail(errMsg.c_str(), LINE_INFO()); } @@ -562,7 +641,7 @@ namespace UnitTests Assert::Fail(errMsg.c_str(), LINE_INFO()); } - handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, NULL); + handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_READONLY, nullptr); if(!IsValidHandle(handle)) { @@ -573,7 +652,7 @@ namespace UnitTests if(!CloseHandle(handle)) { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); Assert::Fail(errMsg.c_str(), LINE_INFO()); } @@ -603,10 +682,264 @@ namespace UnitTests } // This is test 10 in winfstest - /*TEST_METHOD(TestFileSecurity) + TEST_METHOD(TestFileSecurity) { + const wchar_t *DESCRIPTOR1 = L"D:P(A;;GA;;;WD)"; + const wchar_t *DESCRIPTOR1_COMP = L"D:P(A;;FA;;;WD)"; + + const wchar_t *DESCRIPTOR2 = L"D:P(A;;GA;;;WD)(A;;GR;;;SY)"; + const wchar_t *DESCRIPTOR2_COMP = L"D:P(A;;FA;;;WD)(A;;FR;;;SY)"; + + const wchar_t *DESCRIPTOR3 = L"D:P(D;;GR;;;WD)"; + + std::wstring testDir = CreateTestDirectory(); + std::wstring testFile = CombinePath(testDir, L"TestFileSecurity.txt"); + std::wstring errMsg; + DWORD securitySize; + std::unique_ptr securityDesc = SecurityDescriptorFromString(DESCRIPTOR1, securitySize); + SECURITY_ATTRIBUTES securityAttrib; + + securityAttrib.nLength = sizeof(securityAttrib); + securityAttrib.lpSecurityDescriptor = reinterpret_cast(securityDesc.get()); + securityAttrib.bInheritHandle = FALSE; + + HANDLE handle = CreateFileW( + testFile.c_str(), + GENERIC_WRITE, + 0, + &securityAttrib, + CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if(!IsValidHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + securityDesc = GetSecurityForFile(testFile.c_str(), DACL_SECURITY_INFORMATION, securitySize); + + Assert::IsTrue(securityDesc.operator bool(), L"Invalid security descriptor.", LINE_INFO()); + + std::wstring securityDescStr = SecurityDescriptorToString( + reinterpret_cast(securityDesc.get()), + DACL_SECURITY_INFORMATION); + + if(securityDescStr != DESCRIPTOR1_COMP) + { + errMsg = FormatString(L"Security descriptor \'%s\' for file \'%s\' did not equal \'%s\'.", + securityDescStr.c_str(), + testFile.c_str(), + DESCRIPTOR1_COMP); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + securityDesc = SecurityDescriptorFromString(DESCRIPTOR2, securitySize); + + if(!SetFileSecurityW( + testFile.c_str(), + DACL_SECURITY_INFORMATION, + reinterpret_cast(securityDesc.get()))) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to set security descriptor \'%s\' for file \'%s\'.", + DESCRIPTOR2, + testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + securityDesc = GetSecurityForFile(testFile.c_str(), DACL_SECURITY_INFORMATION, securitySize); + + securityDescStr = SecurityDescriptorToString( + reinterpret_cast(securityDesc.get()), + DACL_SECURITY_INFORMATION); + + if(securityDescStr != DESCRIPTOR2_COMP) + { + errMsg = FormatString(L"Security descriptor \'%s\' for file \'%s\' did not equal \'%s\'.", + securityDescStr.c_str(), + testFile.c_str(), + DESCRIPTOR2_COMP); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(!DeleteFileW(testFile.c_str())) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to delete file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + securityDesc = SecurityDescriptorFromString(DESCRIPTOR1, securitySize); + securityAttrib.lpSecurityDescriptor = reinterpret_cast(securityDesc.get()); + + handle = CreateFileW( + testFile.c_str(), + GENERIC_WRITE, + 0, + &securityAttrib, + CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if(!IsValidHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + handle = CreateFileW( + testFile.c_str(), + GENERIC_READ, + 0, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if(!IsValidHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + securityDesc = SecurityDescriptorFromString(DESCRIPTOR3, securitySize); + + if(!SetFileSecurityW( + testFile.c_str(), + DACL_SECURITY_INFORMATION, + reinterpret_cast(securityDesc.get()))) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to set security descriptor \'%s\' for file \'%s\'.", + DESCRIPTOR3, + testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + handle = CreateFileW( + testFile.c_str(), + GENERIC_READ, + 0, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if(IsValidHandle(handle) || GetLastError() != ERROR_ACCESS_DENIED) + { + if(IsValidHandle(handle)) + { + CloseHandle(handle); + } + + errMsg = CreateSystemErrorMessage(FormatString(L"File \'%s\' was expected to fail opening with ERROR_ACCESS_DENIED.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(!DeleteFileW(testFile.c_str())) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to delete file \'%s\'.", testFile.c_str())); - }*/ + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + securityDesc = SecurityDescriptorFromString(DESCRIPTOR1, securitySize); + securityAttrib.lpSecurityDescriptor = reinterpret_cast(securityDesc.get()); + + if(!CreateDirectoryW(testFile.c_str(), &securityAttrib)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create directory \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + securityDesc = GetSecurityForFile(testFile.c_str(), DACL_SECURITY_INFORMATION, securitySize); + + Assert::IsTrue(securityDesc.operator bool(), L"Invalid security descriptor.", LINE_INFO()); + + securityDescStr = SecurityDescriptorToString( + reinterpret_cast(securityDesc.get()), + DACL_SECURITY_INFORMATION); + + if(securityDescStr != DESCRIPTOR1_COMP) + { + errMsg = FormatString(L"Security descriptor \'%s\' for directory \'%s\' did not equal \'%s\'.", + securityDescStr.c_str(), + testFile.c_str(), + DESCRIPTOR1_COMP); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + securityDesc = SecurityDescriptorFromString(DESCRIPTOR2, securitySize); + + if(!SetFileSecurityW( + testFile.c_str(), + DACL_SECURITY_INFORMATION, + reinterpret_cast(securityDesc.get()))) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to set security descriptor \'%s\' for directory \'%s\'.", + DESCRIPTOR2, + testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + securityDesc = GetSecurityForFile(testFile.c_str(), DACL_SECURITY_INFORMATION, securitySize); + + securityDescStr = SecurityDescriptorToString( + reinterpret_cast(securityDesc.get()), + DACL_SECURITY_INFORMATION); + + if(securityDescStr != DESCRIPTOR2_COMP) + { + errMsg = FormatString(L"Security descriptor \'%s\' for directory \'%s\' did not equal \'%s\'.", + securityDescStr.c_str(), + testFile.c_str(), + DESCRIPTOR2_COMP); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(!RemoveDirectoryW(testFile.c_str())) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed remove directory \'%s\'.", + testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + DeleteTestDirectory(); + } // This is stream test 02 in winfstest TEST_METHOD(TestFileStreams02) @@ -627,7 +960,7 @@ namespace UnitTests | STANDARD_RIGHTS_WRITE | STANDARD_RIGHTS_EXECUTE; - HANDLE handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + HANDLE handle = CreateFileW(testFile.c_str(), fileDesiredAccess, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr); if(!IsValidHandle(handle)) { @@ -638,7 +971,7 @@ namespace UnitTests if(!CloseHandle(handle)) { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", testFile.c_str())); + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); Assert::Fail(errMsg.c_str(), LINE_INFO()); } @@ -712,7 +1045,7 @@ namespace UnitTests std::wstring fooStreamName = testFile + L":foo"; std::wstring barStreamName = testFile + L":bar"; - handle = CreateFileW(fooStreamName.c_str(), fileDesiredAccess, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + handle = CreateFileW(fooStreamName.c_str(), fileDesiredAccess, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr); if(!IsValidHandle(handle)) { @@ -723,12 +1056,12 @@ namespace UnitTests if(!CloseHandle(handle)) { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", fooStreamName.c_str())); + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", fooStreamName.c_str())); Assert::Fail(errMsg.c_str(), LINE_INFO()); } - handle = CreateFileW(barStreamName.c_str(), fileDesiredAccess, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + handle = CreateFileW(barStreamName.c_str(), fileDesiredAccess, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr); if(!IsValidHandle(handle)) { @@ -739,7 +1072,7 @@ namespace UnitTests if(!CloseHandle(handle)) { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", barStreamName.c_str())); + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", barStreamName.c_str())); Assert::Fail(errMsg.c_str(), LINE_INFO()); } @@ -835,14 +1168,14 @@ namespace UnitTests Assert::Fail(errMsg.c_str(), LINE_INFO()); } - if(!CreateDirectoryW(testFile.c_str(), NULL)) + if(!CreateDirectoryW(testFile.c_str(), nullptr)) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create directory \'%s\'.", testFile.c_str())); Assert::Fail(errMsg.c_str(), LINE_INFO()); } - handle = CreateFileW(fooStreamName.c_str(), fileDesiredAccess, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + handle = CreateFileW(fooStreamName.c_str(), fileDesiredAccess, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr); if(!IsValidHandle(handle)) { @@ -853,12 +1186,12 @@ namespace UnitTests if(!CloseHandle(handle)) { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", fooStreamName.c_str())); + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", fooStreamName.c_str())); Assert::Fail(errMsg.c_str(), LINE_INFO()); } - handle = CreateFileW(barStreamName.c_str(), fileDesiredAccess, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + handle = CreateFileW(barStreamName.c_str(), fileDesiredAccess, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr); if(!IsValidHandle(handle)) { @@ -869,7 +1202,7 @@ namespace UnitTests if(!CloseHandle(handle)) { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", barStreamName.c_str())); + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", barStreamName.c_str())); Assert::Fail(errMsg.c_str(), LINE_INFO()); } @@ -958,7 +1291,7 @@ namespace UnitTests std::wstring fileName = fileArryaBaseName + temp; - handle = CreateFileW(fileName.c_str(), fileDesiredAccess, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + handle = CreateFileW(fileName.c_str(), fileDesiredAccess, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr); if(!IsValidHandle(handle)) { @@ -968,7 +1301,7 @@ namespace UnitTests } else if(!CloseHandle(handle)) { - errMsg = CreateSystemErrorMessage(FormatString(L"Failed to CloseHandle for file \'%s\'.", fileName.c_str())); + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", fileName.c_str())); Assert::Fail(errMsg.c_str(), LINE_INFO()); } @@ -1110,10 +1443,10 @@ namespace UnitTests testFile.c_str(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, + nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, - NULL); + nullptr); if(!IsValidHandle(origHandle)) { @@ -1126,10 +1459,10 @@ namespace UnitTests testFile.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, + nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, - NULL); + nullptr); if(!IsValidHandle(sharedHandle)) { @@ -1154,10 +1487,10 @@ namespace UnitTests testFile.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, + nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, - NULL); + nullptr); if(IsValidHandle(sharedHandle2) || GetLastError() != ERROR_ACCESS_DENIED) { @@ -1194,10 +1527,10 @@ namespace UnitTests testFile.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, + nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, - NULL); + nullptr); if(IsValidHandle(sharedHandle2) || GetLastError() != ERROR_FILE_NOT_FOUND) { @@ -1211,7 +1544,7 @@ namespace UnitTests Assert::Fail(errMsg.c_str(), LINE_INFO()); } - if(!CreateDirectoryW(testFile.c_str(), NULL)) + if(!CreateDirectoryW(testFile.c_str(), nullptr)) { errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create directory \'%s\'.", testFile.c_str())); @@ -1224,10 +1557,10 @@ namespace UnitTests fooPath.c_str(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, + nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, - NULL); + nullptr); if(!IsValidHandle(origHandle)) { @@ -1410,7 +1743,7 @@ namespace UnitTests char pathToExe[MAX_PATH]; - GetModuleFileNameA(NULL, pathToExe, sizeof(pathToExe)); + GetModuleFileNameA(nullptr, pathToExe, sizeof(pathToExe)); std::vector argv; diff --git a/UnitTests/UnitTests.vcxproj b/UnitTests/UnitTests.vcxproj index cea5d0ff8..b0966825d 100644 --- a/UnitTests/UnitTests.vcxproj +++ b/UnitTests/UnitTests.vcxproj @@ -97,7 +97,7 @@ Windows $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) - Shlwapi.lib;ntdll.lib;%(AdditionalDependencies) + Shlwapi.lib;ntdll.lib;Advapi32.lib;%(AdditionalDependencies) @@ -112,7 +112,7 @@ Windows $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) - Shlwapi.lib;ntdll.lib;%(AdditionalDependencies) + Shlwapi.lib;ntdll.lib;Advapi32.lib;%(AdditionalDependencies) @@ -131,7 +131,7 @@ true true $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) - Shlwapi.lib;ntdll.lib;%(AdditionalDependencies) + Shlwapi.lib;ntdll.lib;Advapi32.lib;%(AdditionalDependencies) @@ -150,7 +150,7 @@ true true $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) - Shlwapi.lib;ntdll.lib;%(AdditionalDependencies) + Shlwapi.lib;ntdll.lib;Advapi32.lib;%(AdditionalDependencies) From 292e99b5fa0f9618a8893a9db1ba9c4d53a96729 Mon Sep 17 00:00:00 2001 From: Keith Newton Date: Mon, 29 Aug 2016 16:58:55 -0400 Subject: [PATCH 18/20] Improvements to management of file security #210 --- dokan/access.c | 27 +- dokan/create.c | 1 - dokan/dokan.c | 73 ++++- dokan/dokan.h | 13 +- dokan/fileinfo.h | 29 ++ dokan/security.c | 5 +- dokan_fuse/src/dokanfuse.cpp | 4 +- samples/dokan_mirror/mirror.c | 530 +++++++++++++++++++++++++++------- sys/access.c | 93 ++++-- sys/create.c | 70 +---- sys/dokan.h | 11 +- sys/public.h | 2 - sys/security.c | 128 +------- 13 files changed, 631 insertions(+), 355 deletions(-) diff --git a/dokan/access.c b/dokan/access.c index a549de45b..c27623841 100644 --- a/dokan/access.c +++ b/dokan/access.c @@ -22,9 +22,9 @@ with this program. If not, see . #include "dokani.h" #include "fileinfo.h" -HANDLE DOKANAPI DokanOpenRequestorToken(PDOKAN_CREATE_FILE_EVENT FileInfo) { +HANDLE DOKANAPI DokanOpenRequestorToken(PDOKAN_FILE_INFO FileInfo) { - PDOKAN_IO_EVENT ioEvent = (PDOKAN_IO_EVENT)FileInfo; + PDOKAN_IO_EVENT ioEvent = (PDOKAN_IO_EVENT)FileInfo->DokanContext; BOOL status; ULONG returnedLength; PEVENT_INFORMATION eventInfo; @@ -32,15 +32,20 @@ HANDLE DOKANAPI DokanOpenRequestorToken(PDOKAN_CREATE_FILE_EVENT FileInfo) { ULONG eventInfoSize; WCHAR rawDeviceName[MAX_PATH]; - if (ioEvent->DokanOpenInfo == NULL) { - return INVALID_HANDLE_VALUE; - } + if (ioEvent->DokanOpenInfo == NULL + || ioEvent->DokanInstance == NULL + || ioEvent->DokanFileInfo.DokanContext != (PVOID)ioEvent) { + + SetLastError(ERROR_INVALID_PARAMETER); - if (ioEvent->DokanInstance == NULL) { return INVALID_HANDLE_VALUE; } - if (ioEvent->KernelInfo.EventContext.MajorFunction != IRP_MJ_CREATE) { + if (ioEvent->KernelInfo.EventContext.MajorFunction != IRP_MJ_CREATE + && ioEvent->KernelInfo.EventContext.MajorFunction != IRP_MJ_SET_SECURITY) { + + SetLastError(ERROR_INVALID_PARAMETER); + return INVALID_HANDLE_VALUE; } @@ -48,6 +53,9 @@ HANDLE DOKANAPI DokanOpenRequestorToken(PDOKAN_CREATE_FILE_EVENT FileInfo) { eventInfo = (PEVENT_INFORMATION)DokanMalloc(eventInfoSize); if (eventInfo == NULL) { + + SetLastError(ERROR_OUTOFMEMORY); + return INVALID_HANDLE_VALUE; } @@ -61,8 +69,11 @@ HANDLE DOKANAPI DokanOpenRequestorToken(PDOKAN_CREATE_FILE_EVENT FileInfo) { eventInfoSize, &returnedLength); if (status) { + handle = eventInfo->Operation.AccessToken.Handle; - } else { + } + else { + DbgPrintW(L"IOCTL_GET_ACCESS_TOKEN failed\n"); } diff --git a/dokan/create.c b/dokan/create.c index 9cb5e53ae..8f5e14772 100644 --- a/dokan/create.c +++ b/dokan/create.c @@ -113,7 +113,6 @@ void BeginDispatchCreate(DOKAN_IO_EVENT *EventInfo) { createFileEvent->CreateDisposition = (EventInfo->KernelInfo.EventContext.Operation.Create.CreateOptions >> 24) & 0x000000ff; createFileEvent->FileAttributes = EventInfo->KernelInfo.EventContext.Operation.Create.FileAttributes; createFileEvent->ShareAccess = EventInfo->KernelInfo.EventContext.Operation.Create.ShareAccess; - createFileEvent->AccessToken = EventInfo->KernelInfo.EventContext.Operation.Create.AccessToken; if((createFileEvent->CreateOptions & FILE_NON_DIRECTORY_FILE) && (createFileEvent->CreateOptions & FILE_DIRECTORY_FILE)) { diff --git a/dokan/dokan.c b/dokan/dokan.c index 8a23f33be..1f2daa5c3 100644 --- a/dokan/dokan.c +++ b/dokan/dokan.c @@ -1681,37 +1681,86 @@ BOOL WINAPI DllMain(HINSTANCE Instance, DWORD Reason, LPVOID Reserved) { return TRUE; } +// https://msdn.microsoft.com/en-us/library/windows/hardware/bb530716(v=vs.85).aspx +BOOL DokanIsBackupName(DOKAN_CREATE_FILE_EVENT *EventInfo) { + + // mask for SE_BACKUP_NAME privilege + DWORD mask = + READ_CONTROL + | FILE_READ_ATTRIBUTES + | STANDARD_RIGHTS_READ; + + if((EventInfo->DesiredAccess & mask) == mask + && (EventInfo->SecurityContext.AccessState.Flags & TOKEN_HAS_TRAVERSE_PRIVILEGE) + && (EventInfo->SecurityContext.AccessState.Flags & (TOKEN_HAS_BACKUP_PRIVILEGE | SE_BACKUP_PRIVILEGES_CHECKED))) { + + return TRUE; + } + + return FALSE; +} + +// https://msdn.microsoft.com/en-us/library/windows/hardware/bb530716(v=vs.85).aspx +BOOL DokanIsRestoreName(DOKAN_CREATE_FILE_EVENT *EventInfo) { + + // mask for SE_RESTORE_NAME privilege + DWORD mask = + WRITE_DAC + | WRITE_OWNER + //| ACCESS_SYSTEM_SECURITY + | STANDARD_RIGHTS_WRITE; + //| FILE_ADD_FILE + //| FILE_ADD_SUBDIRECTORY + //| DELETE; + + if((EventInfo->DesiredAccess & mask) == mask + && (EventInfo->SecurityContext.AccessState.Flags & (TOKEN_HAS_RESTORE_PRIVILEGE | SE_BACKUP_PRIVILEGES_CHECKED))) { + + return TRUE; + } + + return FALSE; +} + VOID DOKANAPI DokanMapKernelToUserCreateFileFlags( - ULONG FileAttributes, ULONG CreateOptions, ULONG CreateDisposition, - DWORD *outFileAttributesAndFlags, DWORD *outCreationDisposition) { + DOKAN_CREATE_FILE_EVENT *EventInfo, + DWORD *outFileAttributesAndFlags, + DWORD *outCreationDisposition) { + if (outFileAttributesAndFlags) { - *outFileAttributesAndFlags = FileAttributes; + *outFileAttributesAndFlags = EventInfo->FileAttributes; - DokanMapKernelBit(*outFileAttributesAndFlags, CreateOptions, + DokanMapKernelBit(*outFileAttributesAndFlags, EventInfo->CreateOptions, FILE_FLAG_WRITE_THROUGH, FILE_WRITE_THROUGH); - DokanMapKernelBit(*outFileAttributesAndFlags, CreateOptions, + DokanMapKernelBit(*outFileAttributesAndFlags, EventInfo->CreateOptions, FILE_FLAG_SEQUENTIAL_SCAN, FILE_SEQUENTIAL_ONLY); - DokanMapKernelBit(*outFileAttributesAndFlags, CreateOptions, + DokanMapKernelBit(*outFileAttributesAndFlags, EventInfo->CreateOptions, FILE_FLAG_RANDOM_ACCESS, FILE_RANDOM_ACCESS); - DokanMapKernelBit(*outFileAttributesAndFlags, CreateOptions, + DokanMapKernelBit(*outFileAttributesAndFlags, EventInfo->CreateOptions, FILE_FLAG_NO_BUFFERING, FILE_NO_INTERMEDIATE_BUFFERING); - DokanMapKernelBit(*outFileAttributesAndFlags, CreateOptions, + DokanMapKernelBit(*outFileAttributesAndFlags, EventInfo->CreateOptions, FILE_FLAG_OPEN_REPARSE_POINT, FILE_OPEN_REPARSE_POINT); - DokanMapKernelBit(*outFileAttributesAndFlags, CreateOptions, + DokanMapKernelBit(*outFileAttributesAndFlags, EventInfo->CreateOptions, FILE_FLAG_DELETE_ON_CLOSE, FILE_DELETE_ON_CLOSE); - DokanMapKernelBit(*outFileAttributesAndFlags, CreateOptions, + DokanMapKernelBit(*outFileAttributesAndFlags, EventInfo->CreateOptions, FILE_FLAG_BACKUP_SEMANTICS, FILE_OPEN_FOR_BACKUP_INTENT); #if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) - DokanMapKernelBit(*outFileAttributesAndFlags, CreateOptions, + DokanMapKernelBit(*outFileAttributesAndFlags, EventInfo->CreateOptions, FILE_FLAG_SESSION_AWARE, FILE_SESSION_AWARE); #endif + + if(DokanIsBackupName(EventInfo) + || DokanIsRestoreName(EventInfo)) { + + *outFileAttributesAndFlags |= FILE_FLAG_BACKUP_SEMANTICS; + } } if (outCreationDisposition) { - switch (CreateDisposition) { + switch (EventInfo->CreateDisposition) { case FILE_CREATE: *outCreationDisposition = CREATE_NEW; break; diff --git a/dokan/dokan.h b/dokan/dokan.h index a0f0e146d..b56ba5599 100644 --- a/dokan/dokan.h +++ b/dokan/dokan.h @@ -162,7 +162,6 @@ typedef struct _DOKAN_CREATE_FILE_EVENT { PDOKAN_FILE_INFO DokanFileInfo; LPCWSTR FileName; LPCWSTR OriginalFileName; - HANDLE AccessToken; DOKAN_IO_SECURITY_CONTEXT SecurityContext; // https://msdn.microsoft.com/en-us/library/windows/hardware/ff550613(v=vs.85).aspx ACCESS_MASK DesiredAccess; @@ -273,8 +272,8 @@ typedef struct _DOKAN_GET_VOLUME_ATTRIBUTES_EVENT { typedef struct _DOKAN_GET_FILE_SECURITY_EVENT { PDOKAN_FILE_INFO DokanFileInfo; LPWSTR FileName; - PSECURITY_INFORMATION SecurityInformation; // A pointer to SECURITY_INFORMATION value being requested PSECURITY_DESCRIPTOR SecurityDescriptor; // A pointer to SECURITY_DESCRIPTOR buffer to be filled + SECURITY_INFORMATION SecurityInformation; // The types of security information being requested ULONG SecurityDescriptorSize; // length of Security descriptor buffer ULONG LengthNeeded; } DOKAN_GET_FILE_SECURITY_EVENT, *PDOKAN_GET_FILE_SECURITY_EVENT; @@ -282,9 +281,8 @@ typedef struct _DOKAN_GET_FILE_SECURITY_EVENT { typedef struct _DOKAN_SET_FILE_SECURITY_EVENT { PDOKAN_FILE_INFO DokanFileInfo; LPWSTR FileName; - HANDLE AccessToken; - PSECURITY_INFORMATION SecurityInformation; PSECURITY_DESCRIPTOR SecurityDescriptor; // A pointer to SECURITY_DESCRIPTOR buffer to be filled + SECURITY_INFORMATION SecurityInformation; ULONG SecurityDescriptorSize; // length of Security descriptor buffer } DOKAN_SET_FILE_SECURITY_EVENT, *PDOKAN_SET_FILE_SECURITY_EVENT; @@ -458,14 +456,15 @@ BOOL DOKANAPI DokanResetTimeout(ULONG Timeout, // timeout in millisecond // Get the handle to Access Token // This method needs be called in CreateFile callback. // The caller must call CloseHandle for the returned handle. -HANDLE DOKANAPI DokanOpenRequestorToken(PDOKAN_CREATE_FILE_EVENT DokanFileInfo); +HANDLE DOKANAPI DokanOpenRequestorToken(PDOKAN_FILE_INFO DokanFileInfo); BOOL DOKANAPI DokanGetMountPointList(PDOKAN_CONTROL list, ULONG length, BOOL uncOnly, PULONG nbRead); VOID DOKANAPI DokanMapKernelToUserCreateFileFlags( - ULONG FileAttributes, ULONG CreateOptions, ULONG CreateDisposition, - DWORD *outFileAttributesAndFlags, DWORD *outCreationDisposition); + DOKAN_CREATE_FILE_EVENT *EventInfo, + DWORD *outFileAttributesAndFlags, + DWORD *outCreationDisposition); // Translate Win32 Error code to the NtStatus code's corresponding NTSTATUS DOKANAPI DokanNtStatusFromWin32(DWORD Error); diff --git a/dokan/fileinfo.h b/dokan/fileinfo.h index 1a555bd67..d8a4658f2 100644 --- a/dokan/fileinfo.h +++ b/dokan/fileinfo.h @@ -510,4 +510,33 @@ typedef struct _UNICODE_STRING { PWSTR Buffer; } UNICODE_STRING, *PUNICODE_STRING; +// +// Token Flags +// +// Flags that may be defined in the TokenFlags field of the token object, +// or in an ACCESS_STATE structure +// + +#define TOKEN_HAS_TRAVERSE_PRIVILEGE 0x0001 +#define TOKEN_HAS_BACKUP_PRIVILEGE 0x0002 +#define TOKEN_HAS_RESTORE_PRIVILEGE 0x0004 +#define TOKEN_WRITE_RESTRICTED 0x0008 +#define TOKEN_IS_RESTRICTED 0x0010 +#define TOKEN_SESSION_NOT_REFERENCED 0x0020 +#define TOKEN_SANDBOX_INERT 0x0040 +#define TOKEN_HAS_IMPERSONATE_PRIVILEGE 0x0080 +#define SE_BACKUP_PRIVILEGES_CHECKED 0x0100 +#define TOKEN_VIRTUALIZE_ALLOWED 0x0200 +#define TOKEN_VIRTUALIZE_ENABLED 0x0400 +#define TOKEN_IS_FILTERED 0x0800 +#define TOKEN_UIACCESS 0x1000 +#define TOKEN_NOT_LOW 0x2000 +#define TOKEN_LOWBOX 0x4000 +#define TOKEN_HAS_OWN_CLAIM_ATTRIBUTES 0x8000 + +#define TOKEN_PRIVATE_NAMESPACE 0x00010000 +#define TOKEN_DO_NOT_USE_GLOBAL_ATTRIBS_FOR_QUERY 0x00020000 +#define SPECIAL_ENCRYPTED_OPEN 0x00040000 +#define TOKEN_NO_CHILD_PROCESS 0x00080000 + #endif // FILEINFO_H_ diff --git a/dokan/security.c b/dokan/security.c index bab703432..5702003c5 100644 --- a/dokan/security.c +++ b/dokan/security.c @@ -44,8 +44,8 @@ void BeginDispatchQuerySecurity(DOKAN_IO_EVENT *EventInfo) { getFileSecurity->DokanFileInfo = &EventInfo->DokanFileInfo; getFileSecurity->FileName = EventInfo->KernelInfo.EventContext.Operation.Security.FileName; - getFileSecurity->SecurityInformation = &EventInfo->KernelInfo.EventContext.Operation.Security.SecurityInformation; getFileSecurity->SecurityDescriptor = (PSECURITY_DESCRIPTOR)&EventInfo->EventResult->Buffer[0]; + getFileSecurity->SecurityInformation = EventInfo->KernelInfo.EventContext.Operation.Security.SecurityInformation; getFileSecurity->SecurityDescriptorSize = IoEventResultBufferSize(EventInfo); assert(getFileSecurity->LengthNeeded == 0); @@ -112,9 +112,8 @@ void BeginDispatchSetSecurity(DOKAN_IO_EVENT *EventInfo) { setFileSecurity->DokanFileInfo = &EventInfo->DokanFileInfo; setFileSecurity->FileName = EventInfo->KernelInfo.EventContext.Operation.SetSecurity.FileName; - setFileSecurity->AccessToken = EventInfo->KernelInfo.EventContext.Operation.SetSecurity.AccessToken; - setFileSecurity->SecurityInformation = &EventInfo->KernelInfo.EventContext.Operation.SetSecurity.SecurityInformation; setFileSecurity->SecurityDescriptor = (PCHAR)&EventInfo->KernelInfo.EventContext + EventInfo->KernelInfo.EventContext.Operation.SetSecurity.BufferOffset; + setFileSecurity->SecurityInformation = EventInfo->KernelInfo.EventContext.Operation.SetSecurity.SecurityInformation; setFileSecurity->SecurityDescriptorSize = EventInfo->KernelInfo.EventContext.Operation.SetSecurity.BufferLength; status = EventInfo->DokanInstance->DokanOperations->SetFileSecurityW(setFileSecurity); diff --git a/dokan_fuse/src/dokanfuse.cpp b/dokan_fuse/src/dokanfuse.cpp index 49a3e4a93..5a498d786 100755 --- a/dokan_fuse/src/dokanfuse.cpp +++ b/dokan_fuse/src/dokanfuse.cpp @@ -522,7 +522,7 @@ FuseGetFileSecurity(DOKAN_GET_FILE_SECURITY_EVENT *EventInfo) { impl_fuse_context *impl = the_impl; if (impl->debug()) - FPRINTF(stderr, "GetFileSecurity: %x\n", *EventInfo->SecurityInformation); + FPRINTF(stderr, "GetFileSecurity: %x\n", EventInfo->SecurityInformation); BY_HANDLE_FILE_INFORMATION byHandleFileInfo; ZeroMemory(&byHandleFileInfo, sizeof(BY_HANDLE_FILE_INFORMATION)); @@ -551,7 +551,7 @@ FuseGetFileSecurity(DOKAN_GET_FILE_SECURITY_EVENT *EventInfo) { LPTSTR pStringBuffer = NULL; if (!ConvertSecurityDescriptorToStringSecurityDescriptor( - EventInfo->SecurityDescriptor, SDDL_REVISION_1, *EventInfo->SecurityInformation, + EventInfo->SecurityDescriptor, SDDL_REVISION_1, EventInfo->SecurityInformation, &pStringBuffer, NULL)) { return STATUS_NOT_IMPLEMENTED; } diff --git a/samples/dokan_mirror/mirror.c b/samples/dokan_mirror/mirror.c index cb741de13..b40c29b5b 100644 --- a/samples/dokan_mirror/mirror.c +++ b/samples/dokan_mirror/mirror.c @@ -41,6 +41,7 @@ THE SOFTWARE. #include #include #include +#include #define USE_ASYNC_IO 1 @@ -51,6 +52,8 @@ typedef struct _MIRROR_FILE_HANDLE { #if USE_ASYNC_IO PTP_IO IoCompletion; #endif + + CRITICAL_SECTION Lock; } MIRROR_FILE_HANDLE; @@ -99,6 +102,14 @@ BOOL g_UseStdErr; BOOL g_DebugMode; volatile LONG g_IsUnmounted = 0; +static GENERIC_MAPPING g_GenericMapping = +{ + FILE_GENERIC_READ, + FILE_GENERIC_WRITE, + FILE_GENERIC_EXECUTE, + FILE_ALL_ACCESS +}; + static void DbgPrint(LPCWSTR format, ...) { if (g_DebugMode) { @@ -164,6 +175,8 @@ void FreeMirrorFileHandle(MIRROR_FILE_HANDLE *FileHandle) { } #endif + DeleteCriticalSection(&FileHandle->Lock); + free(FileHandle); } } @@ -219,11 +232,14 @@ MIRROR_FILE_HANDLE* PopMirrorFileHandle(HANDLE ActualFileHandle) { if(!mirrorHandle) { mirrorHandle = (MIRROR_FILE_HANDLE*)malloc(sizeof(MIRROR_FILE_HANDLE)); + + RtlZeroMemory(mirrorHandle, sizeof(MIRROR_FILE_HANDLE)); + + InitializeCriticalSection(&mirrorHandle->Lock); } if(mirrorHandle) { - RtlZeroMemory(mirrorHandle, sizeof(MIRROR_FILE_HANDLE)); mirrorHandle->FileHandle = ActualFileHandle; #if USE_ASYNC_IO @@ -331,7 +347,8 @@ static void PrintUserName(DOKAN_CREATE_FILE_EVENT *EventInfo) { PTOKEN_USER tokenUser; SID_NAME_USE snu; - handle = DokanOpenRequestorToken(EventInfo); + handle = DokanOpenRequestorToken(EventInfo->DokanFileInfo); + if (handle == INVALID_HANDLE_VALUE) { DbgPrint(L" DokanOpenRequestorToken failed\n"); return; @@ -357,68 +374,218 @@ static void PrintUserName(DOKAN_CREATE_FILE_EVENT *EventInfo) { } static BOOL AddSeSecurityNamePrivilege() { + HANDLE token = 0; - DbgPrint( - L"## Attempting to add SE_SECURITY_NAME privilege to process token ##\n"); + + DbgPrint(L"## Attempting to add SE_SECURITY_NAME and SE_RESTORE_NAME privileges to process token ##\n"); + DWORD err; - LUID luid; - if (!LookupPrivilegeValue(0, SE_SECURITY_NAME, &luid)) { + LUID securityLuid; + LUID restoreLuid; + LUID backupLuid; + + if (!LookupPrivilegeValue(0, SE_SECURITY_NAME, &securityLuid)) { + err = GetLastError(); + if (err != ERROR_SUCCESS) { - DbgPrint(L" failed: Unable to lookup privilege value. error = %u\n", - err); + + DbgPrint(L" failed: Unable to lookup SE_SECURITY_NAME value. error = %u\n", err); + return FALSE; } } - LUID_AND_ATTRIBUTES attr; - attr.Attributes = SE_PRIVILEGE_ENABLED; - attr.Luid = luid; + if(!LookupPrivilegeValue(0, SE_RESTORE_NAME, &restoreLuid)) { + + err = GetLastError(); + + if(err != ERROR_SUCCESS) { + + DbgPrint(L" failed: Unable to lookup SE_RESTORE_NAME value. error = %u\n", err); + + return FALSE; + } + } - TOKEN_PRIVILEGES priv; - priv.PrivilegeCount = 1; - priv.Privileges[0] = attr; + if(!LookupPrivilegeValue(0, SE_BACKUP_NAME, &backupLuid)) { + + err = GetLastError(); + + if(err != ERROR_SUCCESS) { + + DbgPrint(L" failed: Unable to lookup SE_BACKUP_NAME value. error = %u\n", err); + + return FALSE; + } + } + + size_t privSize = sizeof(TOKEN_PRIVILEGES) + (sizeof(LUID_AND_ATTRIBUTES) * 2); + PTOKEN_PRIVILEGES privs = (PTOKEN_PRIVILEGES)malloc(privSize); + PTOKEN_PRIVILEGES oldPrivs = (PTOKEN_PRIVILEGES)malloc(privSize); + + privs->PrivilegeCount = 3; + privs->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + privs->Privileges[0].Luid = securityLuid; + privs->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED; + privs->Privileges[1].Luid = restoreLuid; + privs->Privileges[2].Attributes = SE_PRIVILEGE_ENABLED; + privs->Privileges[2].Luid = backupLuid; + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) { - if (!OpenProcessToken(GetCurrentProcess(), - TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) { err = GetLastError(); + if (err != ERROR_SUCCESS) { + DbgPrint(L" failed: Unable obtain process token. error = %u\n", err); + + free(privs); + free(oldPrivs); + return FALSE; } } - TOKEN_PRIVILEGES oldPriv; DWORD retSize; - AdjustTokenPrivileges(token, FALSE, &priv, sizeof(TOKEN_PRIVILEGES), &oldPriv, - &retSize); + + AdjustTokenPrivileges(token, FALSE, privs, (DWORD)privSize, oldPrivs, &retSize); + err = GetLastError(); + + CloseHandle(token); + if (err != ERROR_SUCCESS) { + DbgPrint(L" failed: Unable to adjust token privileges: %u\n", err); - CloseHandle(token); + + free(privs); + free(oldPrivs); + return FALSE; } - BOOL privAlreadyPresent = FALSE; - for (unsigned int i = 0; i < oldPriv.PrivilegeCount; i++) { - if (oldPriv.Privileges[i].Luid.HighPart == luid.HighPart && - oldPriv.Privileges[i].Luid.LowPart == luid.LowPart) { - privAlreadyPresent = TRUE; - break; + BOOL securityPrivPresent = FALSE; + BOOL restorePrivPresent = FALSE; + + for (unsigned int i = 0; i < oldPrivs->PrivilegeCount && (!securityPrivPresent || !restorePrivPresent); i++) { + + if (oldPrivs->Privileges[i].Luid.HighPart == securityLuid.HighPart + && oldPrivs->Privileges[i].Luid.LowPart == securityLuid.LowPart) { + + securityPrivPresent = TRUE; } + else if(oldPrivs->Privileges[i].Luid.HighPart == restoreLuid.HighPart + && oldPrivs->Privileges[i].Luid.LowPart == restoreLuid.LowPart) { + + restorePrivPresent = TRUE; + } } - DbgPrint(privAlreadyPresent ? L" success: privilege already present\n" - : L" success: privilege added\n"); - if (token) - CloseHandle(token); + + DbgPrint(securityPrivPresent ? L" success: SE_SECURITY_NAME privilege already present\n" + : L" success: SE_SECURITY_NAME privilege added\n"); + + DbgPrint(restorePrivPresent ? L" success: SE_RESTORE_NAME privilege already present\n" + : L" success: SE_RESTORE_NAME privilege added\n"); + + free(privs); + free(oldPrivs); + return TRUE; } #define MirrorCheckFlag(val, flag) \ - if (val & flag) { \ + if ((val & flag) == flag) { \ DbgPrint(L"\t" L#flag L"\n"); \ } +static DWORD MirrorGetParentSecurity(WCHAR *FilePath, PSECURITY_DESCRIPTOR *ParentSecurity) { + + int lastPathSeparator = -1; + + for(int i = 0; i < MAX_PATH && FilePath[i]; ++i) { + + if(FilePath[i] == '\\') { + + lastPathSeparator = i; + } + } + + if(lastPathSeparator == -1) { + + return ERROR_PATH_NOT_FOUND; + } + + WCHAR parentPath[MAX_PATH]; + + memcpy_s(parentPath, MAX_PATH * sizeof(WCHAR), FilePath, lastPathSeparator * sizeof(WCHAR)); + + parentPath[lastPathSeparator] = 0; + + // Must LocalFree() ParentSecurity + + return GetNamedSecurityInfoW( + parentPath, + SE_FILE_OBJECT, + BACKUP_SECURITY_INFORMATION, // give us everything + NULL, + NULL, + NULL, + NULL, + ParentSecurity); +} + +static DWORD MirrorCreateNewSecurity( + DOKAN_CREATE_FILE_EVENT *EventInfo, + WCHAR *FilePath, + PSECURITY_DESCRIPTOR RequestedSecurity, + PSECURITY_DESCRIPTOR *NewSecurity) { + + PSECURITY_DESCRIPTOR parentDescriptor = NULL; + int error = ERROR_SUCCESS; + + if(!EventInfo || !FilePath || !RequestedSecurity || !NewSecurity) { + + return ERROR_INVALID_PARAMETER; + } + + if((error = MirrorGetParentSecurity(FilePath, &parentDescriptor)) != ERROR_SUCCESS) { + + DbgPrint(L"\tFailed to get parent security descriptor for file \'%s\' and error code: %d\n", FilePath, error); + } + else { + + HANDLE accessToken = DokanOpenRequestorToken(EventInfo->DokanFileInfo); + + if(accessToken && accessToken != INVALID_HANDLE_VALUE) { + + if(!CreatePrivateObjectSecurity(parentDescriptor, + EventInfo->SecurityContext.AccessState.SecurityDescriptor, + NewSecurity, + EventInfo->DokanFileInfo->IsDirectory, + accessToken, + &g_GenericMapping)) { + + error = GetLastError(); + + DbgPrint(L"\tFailed to create file security descriptor with error code: %d\n", error); + } + + CloseHandle(accessToken); + } + else { + + error = GetLastError(); + + DbgPrint(L"\tFailed to retrieve file access token with error code: %d\n", error); + } + + LocalFree(parentDescriptor); + } + + return error; +} + static NTSTATUS DOKAN_CALLBACK MirrorCreateFile(DOKAN_CREATE_FILE_EVENT *EventInfo) { @@ -430,17 +597,9 @@ MirrorCreateFile(DOKAN_CREATE_FILE_EVENT *EventInfo) { DWORD fileAttributesAndFlags; DWORD error = 0; SECURITY_ATTRIBUTES securityAttrib; - - securityAttrib.nLength = sizeof(securityAttrib); - securityAttrib.lpSecurityDescriptor = - EventInfo->SecurityContext.AccessState.SecurityDescriptor; - securityAttrib.bInheritHandle = FALSE; - DokanMapKernelToUserCreateFileFlags( - EventInfo->FileAttributes, - EventInfo->CreateOptions, - EventInfo->CreateDisposition, + EventInfo, &fileAttributesAndFlags, &creationDisposition); @@ -463,7 +622,7 @@ MirrorCreateFile(DOKAN_CREATE_FILE_EVENT *EventInfo) { MirrorCheckFlag(EventInfo->ShareAccess, FILE_SHARE_WRITE); MirrorCheckFlag(EventInfo->ShareAccess, FILE_SHARE_DELETE); - DbgPrint(L"\tAccessMode = 0x%x\n", EventInfo->DesiredAccess); + DbgPrint(L"\n\tAccessMode = 0x%x\n", EventInfo->DesiredAccess); MirrorCheckFlag(EventInfo->DesiredAccess, GENERIC_READ); MirrorCheckFlag(EventInfo->DesiredAccess, GENERIC_WRITE); @@ -485,6 +644,13 @@ MirrorCreateFile(DOKAN_CREATE_FILE_EVENT *EventInfo) { MirrorCheckFlag(EventInfo->DesiredAccess, STANDARD_RIGHTS_READ); MirrorCheckFlag(EventInfo->DesiredAccess, STANDARD_RIGHTS_WRITE); MirrorCheckFlag(EventInfo->DesiredAccess, STANDARD_RIGHTS_EXECUTE); + MirrorCheckFlag(EventInfo->DesiredAccess, ACCESS_SYSTEM_SECURITY); + MirrorCheckFlag(EventInfo->DesiredAccess, FILE_TRAVERSE); + MirrorCheckFlag(EventInfo->DesiredAccess, FILE_ADD_FILE); + MirrorCheckFlag(EventInfo->DesiredAccess, FILE_ADD_SUBDIRECTORY); + MirrorCheckFlag(EventInfo->DesiredAccess, FILE_GENERIC_WRITE); + MirrorCheckFlag(EventInfo->DesiredAccess, FILE_GENERIC_READ); + MirrorCheckFlag(EventInfo->DesiredAccess, FILE_GENERIC_EXECUTE); // When filePath is a directory, needs to change the flag so that the file can // be opened. @@ -502,7 +668,7 @@ MirrorCreateFile(DOKAN_CREATE_FILE_EVENT *EventInfo) { } } - DbgPrint(L"\tFlagsAndAttributes = 0x%x\n", fileAttributesAndFlags); + DbgPrint(L"\n\tFlagsAndAttributes = 0x%x\n", fileAttributesAndFlags); MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_ARCHIVE); MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_ENCRYPTED); @@ -531,24 +697,63 @@ MirrorCreateFile(DOKAN_CREATE_FILE_EVENT *EventInfo) { MirrorCheckFlag(fileAttributesAndFlags, SECURITY_EFFECTIVE_ONLY); MirrorCheckFlag(fileAttributesAndFlags, SECURITY_SQOS_PRESENT); + DbgPrint(L"\n\tAccessState.Flags = 0x%x\n", EventInfo->SecurityContext.AccessState.Flags); + + MirrorCheckFlag(EventInfo->SecurityContext.AccessState.Flags, TOKEN_HAS_TRAVERSE_PRIVILEGE); + MirrorCheckFlag(EventInfo->SecurityContext.AccessState.Flags, TOKEN_HAS_BACKUP_PRIVILEGE); + MirrorCheckFlag(EventInfo->SecurityContext.AccessState.Flags, TOKEN_HAS_RESTORE_PRIVILEGE); + MirrorCheckFlag(EventInfo->SecurityContext.AccessState.Flags, TOKEN_WRITE_RESTRICTED); + MirrorCheckFlag(EventInfo->SecurityContext.AccessState.Flags, TOKEN_IS_RESTRICTED); + MirrorCheckFlag(EventInfo->SecurityContext.AccessState.Flags, TOKEN_SESSION_NOT_REFERENCED); + MirrorCheckFlag(EventInfo->SecurityContext.AccessState.Flags, TOKEN_SANDBOX_INERT); + MirrorCheckFlag(EventInfo->SecurityContext.AccessState.Flags, TOKEN_HAS_IMPERSONATE_PRIVILEGE); + MirrorCheckFlag(EventInfo->SecurityContext.AccessState.Flags, SE_BACKUP_PRIVILEGES_CHECKED); + MirrorCheckFlag(EventInfo->SecurityContext.AccessState.Flags, TOKEN_VIRTUALIZE_ALLOWED); + MirrorCheckFlag(EventInfo->SecurityContext.AccessState.Flags, TOKEN_VIRTUALIZE_ENABLED); + MirrorCheckFlag(EventInfo->SecurityContext.AccessState.Flags, TOKEN_IS_FILTERED); + MirrorCheckFlag(EventInfo->SecurityContext.AccessState.Flags, TOKEN_UIACCESS); + MirrorCheckFlag(EventInfo->SecurityContext.AccessState.Flags, TOKEN_NOT_LOW); + MirrorCheckFlag(EventInfo->SecurityContext.AccessState.Flags, TOKEN_LOWBOX); + MirrorCheckFlag(EventInfo->SecurityContext.AccessState.Flags, TOKEN_HAS_OWN_CLAIM_ATTRIBUTES); + MirrorCheckFlag(EventInfo->SecurityContext.AccessState.Flags, TOKEN_PRIVATE_NAMESPACE); + MirrorCheckFlag(EventInfo->SecurityContext.AccessState.Flags, TOKEN_DO_NOT_USE_GLOBAL_ATTRIBS_FOR_QUERY); + MirrorCheckFlag(EventInfo->SecurityContext.AccessState.Flags, SPECIAL_ENCRYPTED_OPEN); + MirrorCheckFlag(EventInfo->SecurityContext.AccessState.Flags, TOKEN_NO_CHILD_PROCESS); + #if USE_ASYNC_IO fileAttributesAndFlags |= FILE_FLAG_OVERLAPPED; #endif if (creationDisposition == CREATE_NEW) { - DbgPrint(L"\tCREATE_NEW\n"); + DbgPrint(L"\n\tCreation Disposition: CREATE_NEW\n"); } else if (creationDisposition == OPEN_ALWAYS) { - DbgPrint(L"\tOPEN_ALWAYS\n"); + DbgPrint(L"\n\tCreation Disposition: OPEN_ALWAYS\n"); } else if (creationDisposition == CREATE_ALWAYS) { - DbgPrint(L"\tCREATE_ALWAYS\n"); + DbgPrint(L"\n\tCreation Disposition: CREATE_ALWAYS\n"); } else if (creationDisposition == OPEN_EXISTING) { - DbgPrint(L"\tOPEN_EXISTING\n"); + DbgPrint(L"\n\tCreation Disposition: OPEN_EXISTING\n"); } else if (creationDisposition == TRUNCATE_EXISTING) { - DbgPrint(L"\tTRUNCATE_EXISTING\n"); + DbgPrint(L"\n\tCreation Disposition: TRUNCATE_EXISTING\n"); } else { - DbgPrint(L"\tUNKNOWN creationDisposition!\n"); + DbgPrint(L"\n\tCreation Disposition: UNKNOWN creationDisposition!\n"); + } + + PSECURITY_DESCRIPTOR fileSecurity = NULL; + + if(wcscmp(EventInfo->FileName, L"\\") != 0 + && wcscmp(EventInfo->FileName, L"/") != 0 + && creationDisposition != OPEN_EXISTING + && creationDisposition != TRUNCATE_EXISTING) { + + // We only need security information if there's a possibility a new file could be created + + MirrorCreateNewSecurity(EventInfo, filePath, EventInfo->SecurityContext.AccessState.SecurityDescriptor, &fileSecurity); } + securityAttrib.nLength = sizeof(securityAttrib); + securityAttrib.lpSecurityDescriptor = fileSecurity; + securityAttrib.bInheritHandle = FALSE; + if (EventInfo->DokanFileInfo->IsDirectory) { // It is a create directory request @@ -580,8 +785,13 @@ MirrorCreateFile(DOKAN_CREATE_FILE_EVENT *EventInfo) { // FILE_FLAG_BACKUP_SEMANTICS is required for opening directory handles handle = CreateFile( - filePath, EventInfo->DesiredAccess, EventInfo->ShareAccess, &securityAttrib, OPEN_EXISTING, - fileAttributesAndFlags | FILE_FLAG_BACKUP_SEMANTICS, NULL); + filePath, + EventInfo->DesiredAccess, + EventInfo->ShareAccess, + &securityAttrib, + OPEN_EXISTING, + fileAttributesAndFlags | FILE_FLAG_BACKUP_SEMANTICS, + NULL); if (handle == INVALID_HANDLE_VALUE) { @@ -602,7 +812,7 @@ MirrorCreateFile(DOKAN_CREATE_FILE_EVENT *EventInfo) { CloseHandle(handle); - return STATUS_INTERNAL_ERROR; + status = STATUS_INTERNAL_ERROR; } // save the file handle in Context @@ -612,68 +822,73 @@ MirrorCreateFile(DOKAN_CREATE_FILE_EVENT *EventInfo) { } else { - // It is a create file request + // It is a create file request if(fileAttr != INVALID_FILE_ATTRIBUTES && (fileAttr & FILE_ATTRIBUTE_DIRECTORY) && EventInfo->CreateDisposition == FILE_CREATE) { - return STATUS_OBJECT_NAME_COLLISION; // File already exist because + status = STATUS_OBJECT_NAME_COLLISION; // File already exist because // GetFileAttributes found it } + else { - handle = - CreateFile(filePath, - EventInfo->DesiredAccess, // GENERIC_READ|GENERIC_WRITE|GENERIC_EXECUTE, - EventInfo->ShareAccess, - &securityAttrib, // security attribute - creationDisposition, - fileAttributesAndFlags, // |FILE_FLAG_NO_BUFFERING, - NULL); // template file handle + handle = + CreateFile(filePath, + EventInfo->DesiredAccess, // GENERIC_READ|GENERIC_WRITE|GENERIC_EXECUTE, + EventInfo->ShareAccess, + &securityAttrib, // security attribute + creationDisposition, + fileAttributesAndFlags, // |FILE_FLAG_NO_BUFFERING, + NULL); // template file handle - if (handle == INVALID_HANDLE_VALUE) { + error = GetLastError(); - error = GetLastError(); - DbgPrint(L"\terror code = %d\n\n", error); + if(handle == INVALID_HANDLE_VALUE) { - status = DokanNtStatusFromWin32(error); - } - else { + DbgPrint(L"\terror code = %d\n\n", error); - MIRROR_FILE_HANDLE *mirrorHandle = PopMirrorFileHandle(handle); + status = DokanNtStatusFromWin32(error); + } + else { - if(!mirrorHandle) { + MIRROR_FILE_HANDLE *mirrorHandle = PopMirrorFileHandle(handle); - DbgPrint(L"\tFailed to create MIRROR_FILE_HANDLE\n"); + if(!mirrorHandle) { - SetLastError(ERROR_INTERNAL_ERROR); + DbgPrint(L"\tFailed to create MIRROR_FILE_HANDLE\n"); - CloseHandle(handle); - - return STATUS_INTERNAL_ERROR; - } + SetLastError(ERROR_INTERNAL_ERROR); - // save the file handle in Context - EventInfo->DokanFileInfo->Context = (ULONG64)mirrorHandle; + CloseHandle(handle); - if (creationDisposition == OPEN_ALWAYS || - creationDisposition == CREATE_ALWAYS) { + status = STATUS_INTERNAL_ERROR; + } + else { - error = GetLastError(); + // save the file handle in Context + EventInfo->DokanFileInfo->Context = (ULONG64)mirrorHandle; - if (error == ERROR_ALREADY_EXISTS) { + if(creationDisposition == OPEN_ALWAYS || + creationDisposition == CREATE_ALWAYS) { - DbgPrint(L"\tOpen an already existing file\n"); - SetLastError(ERROR_ALREADY_EXISTS); // Inform the driver that we have - // open a already existing file - return STATUS_SUCCESS; - } - } - } + DbgPrint(L"\tOpen an already existing file\n"); + SetLastError(ERROR_ALREADY_EXISTS); // Inform the driver that we have + // open a already existing file + status = STATUS_SUCCESS; + } + } + } + } } DbgPrint(L"\n"); + if(fileSecurity) { + + DestroyPrivateObjectSecurity(&fileSecurity); + } + return status; } @@ -1516,28 +1731,28 @@ static NTSTATUS DOKAN_CALLBACK MirrorGetFileSecurity(DOKAN_GET_FILE_SECURITY_EVE MIRROR_HANDLE_ASSERT(mirrorHandle); - MirrorCheckFlag(*EventInfo->SecurityInformation, FILE_SHARE_READ); - MirrorCheckFlag(*EventInfo->SecurityInformation, OWNER_SECURITY_INFORMATION); - MirrorCheckFlag(*EventInfo->SecurityInformation, GROUP_SECURITY_INFORMATION); - MirrorCheckFlag(*EventInfo->SecurityInformation, DACL_SECURITY_INFORMATION); - MirrorCheckFlag(*EventInfo->SecurityInformation, SACL_SECURITY_INFORMATION); - MirrorCheckFlag(*EventInfo->SecurityInformation, LABEL_SECURITY_INFORMATION); - MirrorCheckFlag(*EventInfo->SecurityInformation, ATTRIBUTE_SECURITY_INFORMATION); - MirrorCheckFlag(*EventInfo->SecurityInformation, SCOPE_SECURITY_INFORMATION); - MirrorCheckFlag(*EventInfo->SecurityInformation, - PROCESS_TRUST_LABEL_SECURITY_INFORMATION); - MirrorCheckFlag(*EventInfo->SecurityInformation, BACKUP_SECURITY_INFORMATION); - MirrorCheckFlag(*EventInfo->SecurityInformation, PROTECTED_DACL_SECURITY_INFORMATION); - MirrorCheckFlag(*EventInfo->SecurityInformation, PROTECTED_SACL_SECURITY_INFORMATION); - MirrorCheckFlag(*EventInfo->SecurityInformation, UNPROTECTED_DACL_SECURITY_INFORMATION); - MirrorCheckFlag(*EventInfo->SecurityInformation, UNPROTECTED_SACL_SECURITY_INFORMATION); - - DbgPrint(L" Opening new handle with READ_CONTROL access\n"); + MirrorCheckFlag(EventInfo->SecurityInformation, FILE_SHARE_READ); + MirrorCheckFlag(EventInfo->SecurityInformation, OWNER_SECURITY_INFORMATION); + MirrorCheckFlag(EventInfo->SecurityInformation, GROUP_SECURITY_INFORMATION); + MirrorCheckFlag(EventInfo->SecurityInformation, DACL_SECURITY_INFORMATION); + MirrorCheckFlag(EventInfo->SecurityInformation, SACL_SECURITY_INFORMATION); + MirrorCheckFlag(EventInfo->SecurityInformation, LABEL_SECURITY_INFORMATION); + MirrorCheckFlag(EventInfo->SecurityInformation, ATTRIBUTE_SECURITY_INFORMATION); + MirrorCheckFlag(EventInfo->SecurityInformation, SCOPE_SECURITY_INFORMATION); + MirrorCheckFlag(EventInfo->SecurityInformation, PROCESS_TRUST_LABEL_SECURITY_INFORMATION); + + MirrorCheckFlag(EventInfo->SecurityInformation, BACKUP_SECURITY_INFORMATION); + MirrorCheckFlag(EventInfo->SecurityInformation, PROTECTED_DACL_SECURITY_INFORMATION); + MirrorCheckFlag(EventInfo->SecurityInformation, PROTECTED_SACL_SECURITY_INFORMATION); + MirrorCheckFlag(EventInfo->SecurityInformation, UNPROTECTED_DACL_SECURITY_INFORMATION); + MirrorCheckFlag(EventInfo->SecurityInformation, UNPROTECTED_SACL_SECURITY_INFORMATION); + + /*DbgPrint(L" Opening new handle with READ_CONTROL access\n"); HANDLE handle = CreateFile( filePath, - READ_CONTROL | (((*EventInfo->SecurityInformation & SACL_SECURITY_INFORMATION) || - (*EventInfo->SecurityInformation & BACKUP_SECURITY_INFORMATION)) + READ_CONTROL | (((EventInfo->SecurityInformation & SACL_SECURITY_INFORMATION) || + (EventInfo->SecurityInformation & BACKUP_SECURITY_INFORMATION)) ? ACCESS_SYSTEM_SECURITY : 0), FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, @@ -1578,7 +1793,70 @@ static NTSTATUS DOKAN_CALLBACK MirrorGetFileSecurity(DOKAN_GET_FILE_SECURITY_EVE } } - CloseHandle(handle); + CloseHandle(handle);*/ + + PSECURITY_DESCRIPTOR tempSecurityDesc = NULL; + int error = 0; + + // GetSecurityInfo() is not thread safe so we use a critical section here to synchronize + EnterCriticalSection(&mirrorHandle->Lock); + { + error = GetSecurityInfo(mirrorHandle->FileHandle, SE_FILE_OBJECT, EventInfo->SecurityInformation, NULL, NULL, NULL, NULL, &tempSecurityDesc); + } + LeaveCriticalSection(&mirrorHandle->Lock); + + if(error != ERROR_SUCCESS) { + + DbgPrint(L" GetSecurityInfo error: %d\n", error); + + return DokanNtStatusFromWin32(error); + } + + assert(tempSecurityDesc); + + SECURITY_DESCRIPTOR_CONTROL control = 0; + DWORD revision; + + if(!GetSecurityDescriptorControl(tempSecurityDesc, &control, &revision)) { + + DbgPrint(L" GetSecurityDescriptorControl error: %d\n", GetLastError()); + } + + if(!(control & SE_SELF_RELATIVE)) { + + if(!MakeSelfRelativeSD(tempSecurityDesc, EventInfo->SecurityDescriptor, &EventInfo->LengthNeeded)) { + + error = GetLastError(); + + DbgPrint(L" MakeSelfRelativeSD error: %d\n", error); + + LocalFree(tempSecurityDesc); + + if(error == ERROR_INSUFFICIENT_BUFFER) { + + return STATUS_BUFFER_OVERFLOW; + } + + return DokanNtStatusFromWin32(error); + } + } + else { + + EventInfo->LengthNeeded = GetSecurityDescriptorLength(tempSecurityDesc); + + assert(EventInfo->LengthNeeded > 0); + + if(EventInfo->LengthNeeded > EventInfo->SecurityDescriptorSize) { + + LocalFree(tempSecurityDesc); + + return STATUS_BUFFER_OVERFLOW; + } + + memcpy_s(EventInfo->SecurityDescriptor, EventInfo->SecurityDescriptorSize, tempSecurityDesc, EventInfo->LengthNeeded); + } + + LocalFree(tempSecurityDesc); return STATUS_SUCCESS; } @@ -1587,6 +1865,7 @@ static NTSTATUS DOKAN_CALLBACK MirrorSetFileSecurity(DOKAN_SET_FILE_SECURITY_EVE MIRROR_FILE_HANDLE *mirrorHandle = (MIRROR_FILE_HANDLE*)EventInfo->DokanFileInfo->Context; WCHAR filePath[MAX_PATH]; + int error = 0; GetFilePath(filePath, MAX_PATH, EventInfo->FileName); @@ -1594,13 +1873,40 @@ static NTSTATUS DOKAN_CALLBACK MirrorSetFileSecurity(DOKAN_SET_FILE_SECURITY_EVE MIRROR_HANDLE_ASSERT(mirrorHandle); - if (!SetUserObjectSecurity(mirrorHandle->FileHandle, EventInfo->SecurityInformation, EventInfo->SecurityDescriptor)) { + // SecurityDescriptor must be 4-byte aligned + assert(((size_t)EventInfo->SecurityDescriptor & 3) == 0); - int error = GetLastError(); + MirrorCheckFlag(EventInfo->SecurityInformation, OWNER_SECURITY_INFORMATION); + MirrorCheckFlag(EventInfo->SecurityInformation, GROUP_SECURITY_INFORMATION); + MirrorCheckFlag(EventInfo->SecurityInformation, DACL_SECURITY_INFORMATION); + MirrorCheckFlag(EventInfo->SecurityInformation, SACL_SECURITY_INFORMATION); + MirrorCheckFlag(EventInfo->SecurityInformation, LABEL_SECURITY_INFORMATION); + MirrorCheckFlag(EventInfo->SecurityInformation, ATTRIBUTE_SECURITY_INFORMATION); + MirrorCheckFlag(EventInfo->SecurityInformation, SCOPE_SECURITY_INFORMATION); + MirrorCheckFlag(EventInfo->SecurityInformation, PROCESS_TRUST_LABEL_SECURITY_INFORMATION); + MirrorCheckFlag(EventInfo->SecurityInformation, BACKUP_SECURITY_INFORMATION); - DbgPrint(L" SetUserObjectSecurity error: %d\n", error); + MirrorCheckFlag(EventInfo->SecurityInformation, PROTECTED_DACL_SECURITY_INFORMATION); + MirrorCheckFlag(EventInfo->SecurityInformation, PROTECTED_SACL_SECURITY_INFORMATION); + MirrorCheckFlag(EventInfo->SecurityInformation, UNPROTECTED_DACL_SECURITY_INFORMATION); + MirrorCheckFlag(EventInfo->SecurityInformation, UNPROTECTED_SACL_SECURITY_INFORMATION); - return DokanNtStatusFromWin32(error); + BOOL setSecurity = FALSE; + + EnterCriticalSection(&mirrorHandle->Lock); + { + // For some reason this appears to be only variant of SetSecurity that works without returning an ERROR_ACCESS_DENIED + setSecurity = SetUserObjectSecurity(mirrorHandle->FileHandle, &EventInfo->SecurityInformation, EventInfo->SecurityDescriptor); + } + LeaveCriticalSection(&mirrorHandle->Lock); + + if(!setSecurity) { + + error = GetLastError(); + + DbgPrint(L" SetUserObjectSecurity error: %d\n", error); + + return DokanNtStatusFromWin32(error); } return STATUS_SUCCESS; diff --git a/sys/access.c b/sys/access.c index 4b9b7b406..0ac479374 100644 --- a/sys/access.c +++ b/sys/access.c @@ -23,19 +23,21 @@ with this program. If not, see . NTSTATUS DokanGetAccessToken(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { + KIRQL oldIrql = 0; PLIST_ENTRY thisEntry, nextEntry, listHead; - PIRP_ENTRY irpEntry; + PIRP_ENTRY irpEntry = NULL; PDokanVCB vcb; PEVENT_INFORMATION eventInfo; - PACCESS_TOKEN accessToken; NTSTATUS status = STATUS_INVALID_PARAMETER; - HANDLE handle; PIO_STACK_LOCATION irpSp = NULL; BOOLEAN hasLock = FALSE; ULONG outBufferLen; ULONG inBufferLen; - PACCESS_STATE accessState = NULL; + PSECURITY_SUBJECT_CONTEXT subjectContext = NULL; + SECURITY_SUBJECT_CONTEXT tmpSubjectContext; + SECURITY_QUALITY_OF_SERVICE securityQualityOfService; + SECURITY_CLIENT_CONTEXT securityClientContext; DDbgPrint("==> DokanGetAccessToken\n"); vcb = DeviceObject->DeviceExtension; @@ -57,16 +59,21 @@ DokanGetAccessToken(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { } irpSp = IoGetCurrentIrpStackLocation(Irp); + outBufferLen = irpSp->Parameters.DeviceIoControl.OutputBufferLength; inBufferLen = irpSp->Parameters.DeviceIoControl.InputBufferLength; + if (outBufferLen != sizeof(EVENT_INFORMATION) || inBufferLen != sizeof(EVENT_INFORMATION)) { + DDbgPrint(" wrong input or output buffer length\n"); status = STATUS_INVALID_PARAMETER; + __leave; } ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); + KeAcquireSpinLock(&vcb->Dcb->PendingIrp.ListLock, &oldIrql); hasLock = TRUE; @@ -81,40 +88,86 @@ DokanGetAccessToken(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { irpEntry = CONTAINING_RECORD(thisEntry, IRP_ENTRY, ListEntry); if (irpEntry->SerialNumber != eventInfo->SerialNumber) { + continue; } - // this irp must be IRP_MJ_CREATE - if (irpEntry->IrpSp->Parameters.Create.SecurityContext) { - accessState = - irpEntry->IrpSp->Parameters.Create.SecurityContext->AccessState; - } + // this irp must be IRP_MJ_CREATE or IRP_MJ_SET_SECURITY + + if(irpEntry->IrpSp->MajorFunction == IRP_MJ_CREATE) { + + if(irpEntry->IrpSp->Parameters.Create.SecurityContext) { + + subjectContext = &irpEntry->IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext; + } + } + else if(irpEntry->IrpSp->MajorFunction == IRP_MJ_SET_SECURITY) { + + //subjectContext = &irpEntry->ContextInfo.Security.SecuritySubjectContext; + //SeCaptureSubjectContext(&tmpSubjectContext); + subjectContext = &tmpSubjectContext; + } + break; } + KeReleaseSpinLock(&vcb->Dcb->PendingIrp.ListLock, oldIrql); hasLock = FALSE; - if (accessState == NULL) { + if (!irpEntry || !subjectContext) { + DDbgPrint(" can't find pending Irp: %d\n", eventInfo->SerialNumber); __leave; } - accessToken = - SeQuerySubjectContextToken(&accessState->SubjectSecurityContext); - if (accessToken == NULL) { - DDbgPrint(" accessToken == NULL\n"); - __leave; - } - // NOTE: Accessing *SeTokenObjectType while acquring sping lock causes + if(irpEntry && irpEntry->IrpSp->MajorFunction != IRP_MJ_CREATE) { + + SeCaptureSubjectContext(subjectContext); + } + + securityQualityOfService.Length = sizeof(securityQualityOfService); + securityQualityOfService.ImpersonationLevel = SecurityIdentification; + securityQualityOfService.ContextTrackingMode = SECURITY_STATIC_TRACKING; + securityQualityOfService.EffectiveOnly = FALSE; + + SeLockSubjectContext(subjectContext); + + status = SeCreateClientSecurityFromSubjectContext(subjectContext, &securityQualityOfService, FALSE, &securityClientContext); + + SeUnlockSubjectContext(subjectContext); + + if(irpEntry && irpEntry->IrpSp->MajorFunction != IRP_MJ_CREATE) { + + SeReleaseSubjectContext(subjectContext); + } + + if(!NT_SUCCESS(status)) { + + DDbgPrint(" SeCreateClientSecurityFromSubjectContext failed: 0x%x\n", status); + __leave; + } + + ASSERT(TokenImpersonation == SeTokenType(securityClientContext.ClientToken)); + + // NOTE: Accessing *SeTokenObjectType while acquiring a spin lock causes // BSOD on Windows XP. - status = ObOpenObjectByPointer(accessToken, 0, NULL, GENERIC_ALL, - *SeTokenObjectType, KernelMode, &handle); + status = ObOpenObjectByPointer( + securityClientContext.ClientToken, + 0, + NULL, + TOKEN_READ | TOKEN_DUPLICATE, + *SeTokenObjectType, + UserMode, + &eventInfo->Operation.AccessToken.Handle); + + SeDeleteClientSecurity(&securityClientContext); + if (!NT_SUCCESS(status)) { + DDbgPrint(" ObOpenObjectByPointer failed: 0x%x\n", status); __leave; } - eventInfo->Operation.AccessToken.Handle = handle; Irp->IoStatus.Information = sizeof(EVENT_INFORMATION); status = STATUS_SUCCESS; diff --git a/sys/create.c b/sys/create.c index f4f89ce86..ed4ff7999 100755 --- a/sys/create.c +++ b/sys/create.c @@ -465,19 +465,14 @@ Return Value: PointerAlignSize(sizeof(DOKAN_UNICODE_STRING_INTERMEDIATE)); PDOKAN_UNICODE_STRING_INTERMEDIATE intermediateUnicodeStr = NULL; PUNICODE_STRING relatedFileName = NULL; - PSECURITY_DESCRIPTOR newFileSecurityDescriptor = NULL; BOOLEAN OpenRequiringOplock = FALSE; BOOLEAN UnwindShareAccess = FALSE; BOOLEAN BackoutOplock = FALSE; BOOLEAN EventContextConsumed = FALSE; DWORD disposition = 0; - IRP_ENTRY_CONTEXT irpContext; PAGED_CODE(); - // must be done up here outside of the __try block - RtlZeroMemory(&irpContext, sizeof(irpContext)); - __try { DDbgPrint("==> DokanCreate\n"); @@ -733,30 +728,14 @@ Return Value: if (irpSp->Parameters.Create.SecurityContext->AccessState) { - if (irpSp->Parameters.Create.SecurityContext->AccessState - ->SecurityDescriptor) { - // (CreateOptions & FILE_DIRECTORY_FILE) == FILE_DIRECTORY_FILE - if (SeAssignSecurity( - NULL, // we don't keep track of parents, this will have to be - // handled in user mode - irpSp->Parameters.Create.SecurityContext->AccessState - ->SecurityDescriptor, - &newFileSecurityDescriptor, - (irpSp->Parameters.Create.Options & FILE_DIRECTORY_FILE) || - (irpSp->Flags & SL_OPEN_TARGET_DIRECTORY), - &irpSp->Parameters.Create.SecurityContext->AccessState - ->SubjectSecurityContext, - IoGetFileObjectGenericMapping(), PagedPool) == STATUS_SUCCESS) { + if (irpSp->Parameters.Create.SecurityContext->AccessState->SecurityDescriptor) { securityDescriptorSize = PointerAlignSize( - RtlLengthSecurityDescriptor(newFileSecurityDescriptor)); - } else { - newFileSecurityDescriptor = NULL; - } + RtlLengthSecurityDescriptor(irpSp->Parameters.Create.SecurityContext->AccessState->SecurityDescriptor)); } - if (irpSp->Parameters.Create.SecurityContext->AccessState->ObjectName - .Length > 0) { + if (irpSp->Parameters.Create.SecurityContext->AccessState->ObjectName.Length > 0) { + // add 1 WCHAR for NULL alignedObjectNameSize = PointerAlignSize(sizeof(DOKAN_UNICODE_STRING_INTERMEDIATE) + @@ -764,12 +743,13 @@ Return Value: ->AccessState->ObjectName.Length + sizeof(WCHAR)); } + // else alignedObjectNameSize = // PointerAlignSize(sizeof(DOKAN_UNICODE_STRING_INTERMEDIATE)) SEE // DECLARATION - if (irpSp->Parameters.Create.SecurityContext->AccessState->ObjectTypeName - .Length > 0) { + if (irpSp->Parameters.Create.SecurityContext->AccessState->ObjectTypeName.Length > 0) { + // add 1 WCHAR for NULL alignedObjectTypeNameSize = PointerAlignSize(sizeof(DOKAN_UNICODE_STRING_INTERMEDIATE) + @@ -874,13 +854,12 @@ Return Value: alignedObjectTypeNameSize) - (char *)&eventContext->Operation.Create); - if (newFileSecurityDescriptor != NULL) { + if (securityDescriptorSize > 0) { + // Copy security descriptor RtlCopyMemory((char *)eventContext + alignedEventContextSize, - newFileSecurityDescriptor, - RtlLengthSecurityDescriptor(newFileSecurityDescriptor)); - SeDeassignSecurity(&newFileSecurityDescriptor); - newFileSecurityDescriptor = NULL; + irpSp->Parameters.Create.SecurityContext->AccessState->SecurityDescriptor, + RtlLengthSecurityDescriptor(irpSp->Parameters.Create.SecurityContext->AccessState->SecurityDescriptor)); } if (irpSp->Parameters.Create.SecurityContext->AccessState) { @@ -946,17 +925,6 @@ Return Value: eventContext->Operation.Create.FileNameOffset + (parentDir ? fileNameLength : fcb->FileName.Length)) = 0; - status = DokanCreateProcessAccessToken( - &irpContext.Security.UserModeAccessToken, - &irpContext.Security.Process); - - if(!NT_SUCCESS(status)) { - - __leave; - } - - eventContext->Operation.Create.AccessToken = irpContext.Security.UserModeAccessToken; - // // Oplock // @@ -1183,7 +1151,7 @@ Return Value: } // register this IRP to waiting IPR list - status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0, &irpContext); + status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0, NULL); EventContextConsumed = TRUE; @@ -1221,13 +1189,6 @@ Return Value: if (!NT_SUCCESS(status)) { - if(irpContext.Security.UserModeAccessToken) { - - DokanCleanupProcessAccessToken( - irpContext.Security.UserModeAccessToken, - irpContext.Security.Process); - } - // DokanRegisterPendingIrp consumes event context if (!EventContextConsumed && eventContext) { @@ -1277,13 +1238,6 @@ VOID DokanCompleteCreate(__in PIRP_ENTRY IrpEntry, DDbgPrint("==> DokanCompleteCreate\n"); - if(IrpEntry->ContextInfo.Security.UserModeAccessToken) { - - DokanCleanupProcessAccessToken( - IrpEntry->ContextInfo.Security.UserModeAccessToken, - IrpEntry->ContextInfo.Security.Process); - } - ccb = IrpEntry->FileObject->FsContext2; ASSERT(ccb != NULL); diff --git a/sys/dokan.h b/sys/dokan.h index 3ba20c6b9..9bdba1bc0 100644 --- a/sys/dokan.h +++ b/sys/dokan.h @@ -373,10 +373,7 @@ typedef struct _DokanContextControlBlock { #endif typedef union _IRP_ENTRY_CONTEXT { - struct { - PEPROCESS Process; - HANDLE UserModeAccessToken; - } Security; + SIZE_T Reserved; // remove this if future fields are added here } IRP_ENTRY_CONTEXT, *PIRP_ENTRY_CONTEXT; // IRP list which has pending status @@ -588,12 +585,6 @@ VOID DokanCompleteSetSecurity(__in PIRP_ENTRY IrpEntry, VOID DokanNoOpRelease(__in PVOID Fcb); -NTSTATUS DokanCreateProcessAccessToken(__out HANDLE *AccessToken, - __out PEPROCESS *Process); - -VOID DokanCleanupProcessAccessToken(__in HANDLE AccessToken, - __in PEPROCESS Process); - BOOLEAN DokanNoOpAcquire(__in PVOID Fcb, __in BOOLEAN Wait); diff --git a/sys/public.h b/sys/public.h index 3b2aa7a1b..f6745f8ec 100644 --- a/sys/public.h +++ b/sys/public.h @@ -164,7 +164,6 @@ typedef struct _DOKAN_IO_SECURITY_CONTEXT { } DOKAN_IO_SECURITY_CONTEXT, *PDOKAN_IO_SECURITY_CONTEXT; typedef struct _CREATE_CONTEXT { - HANDLE AccessToken; DOKAN_IO_SECURITY_CONTEXT_INTERMEDIATE SecurityContext; ULONG FileAttributes; ULONG CreateOptions; @@ -262,7 +261,6 @@ typedef struct _SECURITY_CONTEXT { } SECURITY_CONTEXT, *PSECURITY_CONTEXT; typedef struct _SET_SECURITY_CONTEXT { - HANDLE AccessToken; SECURITY_INFORMATION SecurityInformation; ULONG BufferLength; ULONG BufferOffset; diff --git a/sys/security.c b/sys/security.c index 7ccd6d79f..01f58a234 100644 --- a/sys/security.c +++ b/sys/security.c @@ -210,9 +210,11 @@ DokanDispatchSetSecurity(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { ULONG securityDescLength; ULONG eventLength; PEVENT_CONTEXT eventContext; - IRP_ENTRY_CONTEXT irpContext; + IRP_ENTRY_CONTEXT irpEntryContext; ULONG bufferOffset; + RtlZeroMemory(&irpEntryContext, sizeof(irpEntryContext)); + __try { DDbgPrint("==> DokanSetSecurity\n"); @@ -316,20 +318,7 @@ DokanDispatchSetSecurity(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { __leave; } - RtlZeroMemory(&irpContext, sizeof(irpContext)); - - status = DokanCreateProcessAccessToken( - &irpContext.Security.UserModeAccessToken, - &irpContext.Security.Process); - - if(!NT_SUCCESS(status)) { - - DokanFreeEventContext(eventContext); - __leave; - } - eventContext->Context = ccb->UserContext; - eventContext->Operation.SetSecurity.AccessToken = irpContext.Security.UserModeAccessToken; eventContext->Operation.SetSecurity.SecurityInformation = *securityInfo; eventContext->Operation.SetSecurity.BufferLength = securityDescLength; eventContext->Operation.SetSecurity.BufferOffset = bufferOffset; @@ -344,14 +333,10 @@ DokanDispatchSetSecurity(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { fcb->FileName.Buffer, fcb->FileName.Length); - status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0, &irpContext); - - if(status != STATUS_PENDING) { + //SeCaptureSubjectContext(&irpEntryContext.Security.SecuritySubjectContext); - DokanCleanupProcessAccessToken( - irpContext.Security.UserModeAccessToken, - irpContext.Security.Process); - } + //status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0, &irpEntryContext); + status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0, NULL); } __finally { @@ -372,13 +357,8 @@ VOID DokanCompleteSetSecurity(__in PIRP_ENTRY IrpEntry, DDbgPrint("==> DokanCompleteSetSecurity\n"); - if(IrpEntry->ContextInfo.Security.UserModeAccessToken) { - - DokanCleanupProcessAccessToken( - IrpEntry->ContextInfo.Security.UserModeAccessToken, - IrpEntry->ContextInfo.Security.Process); - } - + //SeReleaseSubjectContext(&IrpEntry->ContextInfo.Security.SecuritySubjectContext); + irp = IrpEntry->Irp; irpSp = IrpEntry->IrpSp; @@ -399,96 +379,4 @@ VOID DokanCompleteSetSecurity(__in PIRP_ENTRY IrpEntry, DokanCompleteIrpRequest(irp, EventInfo->Status, 0); DDbgPrint("<== DokanCompleteSetSecurity\n"); -} - -NTSTATUS DokanCreateProcessAccessToken( - __out HANDLE *AccessToken, - __out PEPROCESS *Process) { - - SECURITY_SUBJECT_CONTEXT securitySubjectContext; - SECURITY_QUALITY_OF_SERVICE securityQualityOfService; - SECURITY_CLIENT_CONTEXT securityClientContext; - NTSTATUS status = STATUS_SUCCESS; - - if(!AccessToken || !Process) { - - return STATUS_INVALID_PARAMETER; - } - - // Duplicate the subject context access token into an impersonation token - securityQualityOfService.Length = sizeof(securityQualityOfService); - securityQualityOfService.ImpersonationLevel = SecurityIdentification; - securityQualityOfService.ContextTrackingMode = SECURITY_STATIC_TRACKING; - securityQualityOfService.EffectiveOnly = FALSE; - - SeCaptureSubjectContext(&securitySubjectContext); - - SeLockSubjectContext(&securitySubjectContext); - - status = SeCreateClientSecurityFromSubjectContext( - &securitySubjectContext, - &securityQualityOfService, - FALSE, - &securityClientContext); - - SeUnlockSubjectContext(&securitySubjectContext); - - SeReleaseSubjectContext(&securitySubjectContext); - - if(!NT_SUCCESS(status)) { - - DDbgPrint(" Failed to create client security from subject context.\n"); - - return status; - } - - // Get a user-mode handle to the impersonation token - status = ObOpenObjectByPointer(securityClientContext.ClientToken, - 0, 0, TOKEN_QUERY, *SeTokenObjectType, UserMode, AccessToken); - - SeDeleteClientSecurity(&securityClientContext); - - if(!NT_SUCCESS(status)) { - - DDbgPrint(" Failed to create user mode impersonation token.\n"); - - return status; - } - - // Get a pointer to the current process so that we can close the impersonation token later - *Process = PsGetCurrentProcess(); - - ObReferenceObject(*Process); - - return STATUS_SUCCESS; -} - -VOID DokanCleanupProcessAccessToken( - __in HANDLE AccessToken, - __in PEPROCESS Process) { - - DDbgPrint(" Closing access token\n"); - - KAPC_STATE apcState; - NTSTATUS status = STATUS_SUCCESS; - BOOLEAN attach = Process && Process != PsGetCurrentProcess(); - - if(attach) { - - KeStackAttachProcess(Process, &apcState); - } - - status = ObCloseHandle(AccessToken, UserMode); - - if(attach) { - - KeUnstackDetachProcess(&apcState); - } - - ObDereferenceObject(Process); - - if(!NT_SUCCESS(status)) { - - DDbgPrint(" Failed to close user mode access token.\n"); - } } \ No newline at end of file From 4e6c1f3460e63e7bf0de03c0e4a0ed7f9716b24f Mon Sep 17 00:00:00 2001 From: Keith Newton Date: Tue, 30 Aug 2016 15:55:32 -0400 Subject: [PATCH 19/20] Fixes for issues caused by the previous merge #210 Fixes for DOKAN_OPTION_FORCE_SINGLE_THREADED Mirror now closes a file handle in Cleanup again Added more unit tests Some unit tests are still failing due to issues with setting file security --- UnitTests/FileSystemTests.cpp | 883 +++++++++++++++++++++++++++++++++- dokan/create.c | 26 +- dokan/dokan.c | 33 +- dokan/dokan.h | 7 +- samples/dokan_mirror/mirror.c | 466 +++++++++++++----- 5 files changed, 1259 insertions(+), 156 deletions(-) diff --git a/UnitTests/FileSystemTests.cpp b/UnitTests/FileSystemTests.cpp index 4226a7d48..7530de18a 100644 --- a/UnitTests/FileSystemTests.cpp +++ b/UnitTests/FileSystemTests.cpp @@ -1433,10 +1433,10 @@ namespace UnitTests } // This is test 08 in winfstest - TEST_METHOD(TestSharedFileHandles) + TEST_METHOD(TestFindFilesDeletePending) { std::wstring testDir = CreateTestDirectory(); - std::wstring testFile = CombinePath(testDir, L"TestSharedFilehandles.txt"); + std::wstring testFile = CombinePath(testDir, L"TestSharedFileHandles.txt"); std::wstring errMsg; HANDLE origHandle = CreateFileW( @@ -1783,5 +1783,884 @@ namespace UnitTests DeleteTestDirectory(); } + + // This is test 00 in winfstest + TEST_METHOD(TestCreateFile) + { + std::wstring testDir = CreateTestDirectory(); + std::wstring testFile = CombinePath(testDir, L"TestCreateFile.txt"); + std::wstring errMsg; + + //////////////////// Set 1 //////////////////// + + HANDLE handle = CreateFileW( + testFile.c_str(), + GENERIC_WRITE, + 0, + nullptr, + CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if(!IsValidHandle(handle)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + handle = CreateFileW( + testFile.c_str(), + GENERIC_WRITE, + 0, + nullptr, + CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if(IsValidHandle(handle) || GetLastError() != ERROR_FILE_EXISTS) + { + errMsg = CreateSystemErrorMessage(FormatString(L"File \'%s\' was expected to fail opening with ERROR_FILE_EXISTS.", testFile.c_str())); + + if(IsValidHandle(handle)) + { + CloseHandle(handle); + } + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(!DeleteFileW(testFile.c_str())) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed DeleteFileW for file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(DeleteFileW(testFile.c_str()) || GetLastError() != ERROR_FILE_NOT_FOUND) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"DeleteFileW for file \'%s\' was expecting ERROR_FILE_NOT_FOUND.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + //////////////////// Set 2 //////////////////// + + handle = CreateFileW( + testFile.c_str(), + GENERIC_WRITE, + 0, + nullptr, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if(!IsValidHandle(handle)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + handle = CreateFileW( + testFile.c_str(), + GENERIC_WRITE, + 0, + nullptr, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if(!IsValidHandle(handle)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + handle = CreateFileW( + testFile.c_str(), + GENERIC_WRITE, + 0, + nullptr, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + DWORD lastError = GetLastError(); + + if(!IsValidHandle(handle)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(lastError != ERROR_ALREADY_EXISTS) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Was expecting ERROR_ALREADY_EXISTS for file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(!DeleteFileW(testFile.c_str())) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed DeleteFileW for file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + //////////////////// Set 3 //////////////////// + + handle = CreateFileW( + testFile.c_str(), + GENERIC_WRITE, + 0, + nullptr, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if(!IsValidHandle(handle)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + handle = CreateFileW( + testFile.c_str(), + GENERIC_WRITE, + 0, + nullptr, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if(!IsValidHandle(handle)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + handle = CreateFileW( + testFile.c_str(), + GENERIC_WRITE, + 0, + nullptr, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + lastError = GetLastError(); + + if(!IsValidHandle(handle)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(lastError != ERROR_ALREADY_EXISTS) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Was expecting ERROR_ALREADY_EXISTS for file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(!DeleteFileW(testFile.c_str())) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed DeleteFileW for file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + //////////////////// Set 4 //////////////////// + + handle = CreateFileW( + testFile.c_str(), + GENERIC_WRITE, + 0, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if(IsValidHandle(handle) || GetLastError() != ERROR_FILE_NOT_FOUND) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Was expecting ERROR_FILE_NOT_FOUND for file \'%s\'.", testFile.c_str())); + + if(IsValidHandle(handle)) + { + CloseHandle(handle); + } + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + handle = CreateFileW( + testFile.c_str(), + GENERIC_WRITE, + 0, + nullptr, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if(!IsValidHandle(handle)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + handle = CreateFileW( + testFile.c_str(), + GENERIC_WRITE, + 0, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if(!IsValidHandle(handle)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(!DeleteFileW(testFile.c_str())) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed DeleteFileW for file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + //////////////////// Set 5 //////////////////// + + handle = CreateFileW( + testFile.c_str(), + GENERIC_WRITE, + 0, + nullptr, + TRUNCATE_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if(IsValidHandle(handle) || GetLastError() != ERROR_FILE_NOT_FOUND) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Was expecting ERROR_FILE_NOT_FOUND for file \'%s\'.", testFile.c_str())); + + if(IsValidHandle(handle)) + { + CloseHandle(handle); + } + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + handle = CreateFileW( + testFile.c_str(), + GENERIC_WRITE, + 0, + nullptr, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if(!IsValidHandle(handle)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + handle = CreateFileW( + testFile.c_str(), + GENERIC_WRITE, + 0, + nullptr, + TRUNCATE_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if(!IsValidHandle(handle)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(handle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(!DeleteFileW(testFile.c_str())) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed DeleteFileW for file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + //////////////////// Set 6 //////////////////// + + std::wstring missing = CombinePath(testDir, L"TestCreateFile\\Bar"); + + handle = CreateFileW( + missing.c_str(), + GENERIC_WRITE, + 0, + nullptr, + CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + lastError = GetLastError(); + + if(IsValidHandle(handle) || lastError != ERROR_PATH_NOT_FOUND) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Expected ERROR_PATH_NOT_FOUND for file \'%s\' instead got %d.", + missing.c_str(), lastError)); + + if(IsValidHandle(handle)) + { + CloseHandle(handle); + } + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + DeleteTestDirectory(); + } + + // This is test 09 in winfstest + TEST_METHOD(TestCreateFileWithSharing) + { + std::wstring testDir = CreateTestDirectory(); + std::wstring testFile = CombinePath(testDir, L"TestCreateFileWithSharing"); + std::wstring errMsg; + DWORD lastError = 0; + + //////////////////// Set 1 //////////////////// + + HANDLE origHandle = CreateFileW( + testFile.c_str(), + GENERIC_WRITE, + FILE_SHARE_READ, + nullptr, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if(!IsValidHandle(origHandle)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + { + HANDLE sharedHandle = CreateFileW( + testFile.c_str(), + GENERIC_READ, + FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + lastError = GetLastError(); + + if(IsValidHandle(sharedHandle) || lastError != ERROR_SHARING_VIOLATION) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Was expecting ERROR_SHARING_VIOLATION for file \'%s\' instead got %d.", + testFile.c_str(), lastError)); + + if(IsValidHandle(sharedHandle)) + { + CloseHandle(sharedHandle); + } + + CloseHandle(origHandle); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + sharedHandle = CreateFileW( + testFile.c_str(), + GENERIC_READ, + FILE_SHARE_WRITE, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if(!IsValidHandle(origHandle)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + CloseHandle(origHandle); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(sharedHandle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); + + CloseHandle(origHandle); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + sharedHandle = CreateFileW( + testFile.c_str(), + DELETE, + FILE_SHARE_DELETE, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + lastError = GetLastError(); + + if(IsValidHandle(sharedHandle) || lastError != ERROR_SHARING_VIOLATION) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Was expecting ERROR_SHARING_VIOLATION for file \'%s\' instead got %d.", + testFile.c_str(), lastError)); + + if(IsValidHandle(sharedHandle)) + { + CloseHandle(sharedHandle); + } + + CloseHandle(origHandle); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + sharedHandle = CreateFileW( + testFile.c_str(), + DELETE, + FILE_SHARE_WRITE, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + lastError = GetLastError(); + + if(IsValidHandle(sharedHandle) || lastError != ERROR_SHARING_VIOLATION) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Was expecting ERROR_SHARING_VIOLATION for file \'%s\' instead got %d.", + testFile.c_str(), lastError)); + + if(IsValidHandle(sharedHandle)) + { + CloseHandle(sharedHandle); + } + + CloseHandle(origHandle); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + } + + if(!CloseHandle(origHandle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + //////////////////// Set 2 //////////////////// + + origHandle = CreateFileW( + testFile.c_str(), + GENERIC_READ, + FILE_SHARE_WRITE, + nullptr, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if(!IsValidHandle(origHandle)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + { + HANDLE sharedHandle = CreateFileW( + testFile.c_str(), + GENERIC_WRITE, + FILE_SHARE_WRITE, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + lastError = GetLastError(); + + if(IsValidHandle(sharedHandle) || lastError != ERROR_SHARING_VIOLATION) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Was expecting ERROR_SHARING_VIOLATION for file \'%s\' instead got %d.", + testFile.c_str(), lastError)); + + if(IsValidHandle(sharedHandle)) + { + CloseHandle(sharedHandle); + } + + CloseHandle(origHandle); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + sharedHandle = CreateFileW( + testFile.c_str(), + GENERIC_WRITE, + FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if(!IsValidHandle(origHandle)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + CloseHandle(origHandle); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(sharedHandle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); + + CloseHandle(origHandle); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + sharedHandle = CreateFileW( + testFile.c_str(), + DELETE, + FILE_SHARE_DELETE, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + lastError = GetLastError(); + + if(IsValidHandle(sharedHandle) || lastError != ERROR_SHARING_VIOLATION) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Was expecting ERROR_SHARING_VIOLATION for file \'%s\' instead got %d.", + testFile.c_str(), lastError)); + + if(IsValidHandle(sharedHandle)) + { + CloseHandle(sharedHandle); + } + + CloseHandle(origHandle); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + sharedHandle = CreateFileW( + testFile.c_str(), + DELETE, + FILE_SHARE_WRITE, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + lastError = GetLastError(); + + if(IsValidHandle(sharedHandle) || lastError != ERROR_SHARING_VIOLATION) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Was expecting ERROR_SHARING_VIOLATION for file \'%s\' instead got %d.", + testFile.c_str(), lastError)); + + if(IsValidHandle(sharedHandle)) + { + CloseHandle(sharedHandle); + } + + CloseHandle(origHandle); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + } + + if(!CloseHandle(origHandle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + //////////////////// Set 3 //////////////////// + + origHandle = CreateFileW( + testFile.c_str(), + DELETE, + FILE_SHARE_DELETE, + nullptr, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if(!IsValidHandle(origHandle)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + { + HANDLE sharedHandle = CreateFileW( + testFile.c_str(), + GENERIC_WRITE, + FILE_SHARE_DELETE, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + lastError = GetLastError(); + + if(IsValidHandle(sharedHandle) || lastError != ERROR_SHARING_VIOLATION) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Was expecting ERROR_SHARING_VIOLATION for file \'%s\' instead got %d.", + testFile.c_str(), lastError)); + + if(IsValidHandle(sharedHandle)) + { + CloseHandle(sharedHandle); + } + + CloseHandle(origHandle); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + sharedHandle = CreateFileW( + testFile.c_str(), + DELETE, + FILE_SHARE_WRITE, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + lastError = GetLastError(); + + if(IsValidHandle(sharedHandle) || lastError != ERROR_SHARING_VIOLATION) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Was expecting ERROR_SHARING_VIOLATION for file \'%s\' instead got %d.", + testFile.c_str(), lastError)); + + if(IsValidHandle(sharedHandle)) + { + CloseHandle(sharedHandle); + } + + CloseHandle(origHandle); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + sharedHandle = CreateFileW( + testFile.c_str(), + DELETE, + FILE_SHARE_DELETE, + nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + nullptr); + + if(!IsValidHandle(origHandle)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + CloseHandle(origHandle); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(!CloseHandle(sharedHandle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); + + CloseHandle(origHandle); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + } + + if(!CloseHandle(origHandle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(!DeleteFileW(testFile.c_str())) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to delete file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + //////////////////// Set 4 //////////////////// + + if(!CreateDirectoryW(testFile.c_str(), nullptr)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create directory \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + origHandle = CreateFileW( + testFile.c_str(), + GENERIC_READ, + 0, + nullptr, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + nullptr); + + if(!IsValidHandle(origHandle)) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Failed to create file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + { + HANDLE sharedHandle = CreateFileW( + testFile.c_str(), + GENERIC_READ, + 0, + nullptr, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + nullptr); + + lastError = GetLastError(); + + if(IsValidHandle(sharedHandle) || lastError != ERROR_SHARING_VIOLATION) + { + std::wstring errMsg = CreateSystemErrorMessage(FormatString(L"Was expecting ERROR_SHARING_VIOLATION for file \'%s\' instead got %d.", + testFile.c_str(), lastError)); + + if(IsValidHandle(sharedHandle)) + { + CloseHandle(sharedHandle); + } + + CloseHandle(origHandle); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + } + + if(!CloseHandle(origHandle)) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to close handle for file \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + if(!RemoveDirectoryW(testFile.c_str())) + { + errMsg = CreateSystemErrorMessage(FormatString(L"Failed to remove directory \'%s\'.", testFile.c_str())); + + Assert::Fail(errMsg.c_str(), LINE_INFO()); + } + + DeleteTestDirectory(); + } }; } \ No newline at end of file diff --git a/dokan/create.c b/dokan/create.c index 75b4f01d9..1372d94a2 100644 --- a/dokan/create.c +++ b/dokan/create.c @@ -230,17 +230,12 @@ void DOKANAPI DokanEndDispatchCreate(DOKAN_CREATE_FILE_EVENT *EventInfo, NTSTATU ResultStatus = STATUS_INTERNAL_ERROR; } - ioEvent->EventResult->Status = ResultStatus; - if(ioEvent->EventInfo.ZwCreateFile.OriginalFileName) { DokanFree((void*)ioEvent->EventInfo.ZwCreateFile.OriginalFileName); ioEvent->EventInfo.ZwCreateFile.OriginalFileName = NULL; } - DbgPrint("Dokan Information: DokanEndDispatchCreate() status = %lx, file handle = 0x%p, eventID = %04d\n", - ResultStatus, ioEvent->DokanOpenInfo, ioEvent->DokanOpenInfo ? ioEvent->DokanOpenInfo->EventId : -1); - // FILE_CREATED // FILE_DOES_NOT_EXIST // FILE_EXISTS @@ -281,7 +276,6 @@ void DOKANAPI DokanEndDispatchCreate(DOKAN_CREATE_FILE_EVENT *EventInfo, NTSTATU } ioEvent->EventResult->Operation.Create.Information = FILE_DOES_NOT_EXIST; - ioEvent->EventResult->Status = ResultStatus; if(ResultStatus == STATUS_OBJECT_NAME_COLLISION) { @@ -301,7 +295,8 @@ void DOKANAPI DokanEndDispatchCreate(DOKAN_CREATE_FILE_EVENT *EventInfo, NTSTATU if(EventInfo->CreateDisposition == FILE_CREATE || EventInfo->CreateDisposition == FILE_OPEN_IF - || EventInfo->CreateDisposition == FILE_OVERWRITE_IF) { + || EventInfo->CreateDisposition == FILE_OVERWRITE_IF + || EventInfo->CreateDisposition == FILE_SUPERSEDE) { ioEvent->EventResult->Operation.Create.Information = FILE_CREATED; @@ -311,11 +306,14 @@ void DOKANAPI DokanEndDispatchCreate(DOKAN_CREATE_FILE_EVENT *EventInfo, NTSTATU ioEvent->EventResult->Operation.Create.Information = FILE_OPENED; } - else if(EventInfo->CreateDisposition == FILE_OVERWRITE_IF - || EventInfo->CreateDisposition == FILE_SUPERSEDE) { + else if(EventInfo->CreateDisposition == FILE_OVERWRITE_IF) { ioEvent->EventResult->Operation.Create.Information = FILE_OVERWRITTEN; } + else if(EventInfo->CreateDisposition == FILE_SUPERSEDE) { + + ioEvent->EventResult->Operation.Create.Information = FILE_SUPERSEDED; + } } } @@ -323,7 +321,17 @@ void DOKANAPI DokanEndDispatchCreate(DOKAN_CREATE_FILE_EVENT *EventInfo, NTSTATU ioEvent->EventResult->Operation.Create.Flags |= DOKAN_FILE_DIRECTORY; } + + ResultStatus = STATUS_SUCCESS; } + ioEvent->EventResult->Status = ResultStatus; + + DbgPrint("Dokan Information: DokanEndDispatchCreate() status = %lx, file handle = 0x%p, eventID = %04d, result = 0x%x\n", + ResultStatus, + ioEvent->DokanOpenInfo, + ioEvent->DokanOpenInfo ? ioEvent->DokanOpenInfo->EventId : -1, + ioEvent->EventResult->Operation.Create.Information); + SendIoEventResult(ioEvent); } \ No newline at end of file diff --git a/dokan/dokan.c b/dokan/dokan.c index 1f2daa5c3..3f0f03f1e 100644 --- a/dokan/dokan.c +++ b/dokan/dokan.c @@ -1159,16 +1159,21 @@ void ProcessIOEvent( return; } - // reuse Overlapped and queue up another async IO operation ResetOverlapped(Overlapped); - BOOL restartDeviceIOSucceeded = StartDeviceIO(Dokan, Overlapped); + BOOL restartDeviceIOSucceeded = FALSE; DWORD lastError = ERROR_SUCCESS; - - if(!restartDeviceIOSucceeded) { - - lastError = GetLastError(); - SetLastError(ERROR_SUCCESS); + + // If not single threaded reuse Overlapped and queue up another async IO operation + if((Dokan->DokanOptions->Options & DOKAN_OPTION_FORCE_SINGLE_THREADED) == 0) { + + restartDeviceIOSucceeded = StartDeviceIO(Dokan, Overlapped); + + if(!restartDeviceIOSucceeded) { + + lastError = GetLastError(); + SetLastError(ERROR_SUCCESS); + } } // begin processing IO event @@ -1233,6 +1238,20 @@ void ProcessIOEvent( DbgPrint("ReturnedLength %d\n", NumberOfBytesTransferred); } + // If single threaded then processing is now complete unless a larger write size was requested, + // which I'm choosing to ignore, so request another event + + if(Dokan->DokanOptions->Options & DOKAN_OPTION_FORCE_SINGLE_THREADED) { + + restartDeviceIOSucceeded = StartDeviceIO(Dokan, Overlapped); + + if(!restartDeviceIOSucceeded) { + + lastError = GetLastError(); + SetLastError(ERROR_SUCCESS); + } + } + if(!restartDeviceIOSucceeded) { // NOTE: This MUST be handled at the end of this method. OnDeviceIoCtlFailed() will unmount the volume diff --git a/dokan/dokan.h b/dokan/dokan.h index 21049f591..5d087855f 100644 --- a/dokan/dokan.h +++ b/dokan/dokan.h @@ -105,11 +105,8 @@ extern "C" { /** Enable Lockfile/Unlockfile operations. Otherwise Dokan will take care of it */ #define DOKAN_OPTION_FILELOCK_USER_MODE (1 << 8) // FileLock in User Mode -/** Enable asynchronous IO */ -#define DOKAN_OPTION_ASYNC_IO (1 << 9) - -/** Dokan uses a single thread. If DOKAN_OPTION_ASYNC_IO is specified the thread waits until the async job is over before starting another. */ -#define DOKAN_OPTION_FORCE_SINGLE_THREADED (1 << 10) +/** Dokan uses a single thread. */ +#define DOKAN_OPTION_FORCE_SINGLE_THREADED (1 << 9) /** @} */ diff --git a/samples/dokan_mirror/mirror.c b/samples/dokan_mirror/mirror.c index c84ac1701..8677b17ca 100644 --- a/samples/dokan_mirror/mirror.c +++ b/samples/dokan_mirror/mirror.c @@ -54,6 +54,8 @@ typedef struct _MIRROR_FILE_HANDLE { #endif CRITICAL_SECTION Lock; + BOOL IsCleanedUp; + BOOL IsClosed; } MIRROR_FILE_HANDLE; @@ -241,6 +243,8 @@ MIRROR_FILE_HANDLE* PopMirrorFileHandle(HANDLE ActualFileHandle) { if(mirrorHandle) { mirrorHandle->FileHandle = ActualFileHandle; + mirrorHandle->IsCleanedUp = FALSE; + mirrorHandle->IsClosed = FALSE; #if USE_ASYNC_IO @@ -336,6 +340,26 @@ static void GetFilePath(PWCHAR filePath, ULONG numberOfElements, } } +void GetMirrorFileHandleState(MIRROR_FILE_HANDLE *fileHandle, BOOL *isCleanedUp, BOOL *isClosed) { + + if(fileHandle && (isCleanedUp || isClosed)) { + + EnterCriticalSection(&fileHandle->Lock); + { + if(isCleanedUp) { + + *isCleanedUp = fileHandle->IsCleanedUp; + } + + if(isClosed) { + + *isClosed = fileHandle->IsClosed; + } + } + LeaveCriticalSection(&fileHandle->Lock); + } +} + static void PrintUserName(DOKAN_CREATE_FILE_EVENT *EventInfo) { HANDLE handle; UCHAR buffer[1024]; @@ -899,37 +923,19 @@ MirrorCreateFile(DOKAN_CREATE_FILE_EVENT *EventInfo) { #pragma warning(push) #pragma warning(disable : 4305) -static void DOKAN_CALLBACK MirrorCloseFile(DOKAN_CLOSE_FILE_EVENT *EventInfo) { - - WCHAR filePath[MAX_PATH]; - MIRROR_FILE_HANDLE *mirrorHandle = (MIRROR_FILE_HANDLE*)EventInfo->DokanFileInfo->Context; - - GetFilePath(filePath, MAX_PATH, EventInfo->FileName); - - DbgPrint(L"CloseFile: %s\n", filePath); - - MIRROR_HANDLE_ASSERT(mirrorHandle); - - if (mirrorHandle) { - - CloseHandle(mirrorHandle->FileHandle); - - mirrorHandle->FileHandle = NULL; - EventInfo->DokanFileInfo->Context = 0; - - PushMirrorFileHandle(mirrorHandle); - - if(EventInfo->DokanFileInfo->DeleteOnClose) { +static void CheckDeleteOnClose(PDOKAN_FILE_INFO FileInfo, LPWSTR FilePath) +{ + if(FileInfo->DeleteOnClose) { // Should already be deleted by CloseHandle // if open with FILE_FLAG_DELETE_ON_CLOSE DbgPrint(L"\tDeleteOnClose\n"); - if(EventInfo->DokanFileInfo->IsDirectory) { + if(FileInfo->IsDirectory) { DbgPrint(L" DeleteDirectory "); - if(!RemoveDirectory(filePath)) { + if(!RemoveDirectory(FilePath)) { DbgPrint(L"error code = %d\n\n", GetLastError()); } @@ -941,7 +947,7 @@ static void DOKAN_CALLBACK MirrorCloseFile(DOKAN_CLOSE_FILE_EVENT *EventInfo) { DbgPrint(L" DeleteFile "); - if(DeleteFile(filePath) == 0) { + if(DeleteFile(FilePath) == 0) { DbgPrint(L" error code = %d\n\n", GetLastError()); } @@ -951,6 +957,36 @@ static void DOKAN_CALLBACK MirrorCloseFile(DOKAN_CLOSE_FILE_EVENT *EventInfo) { } } } +} + +static void DOKAN_CALLBACK MirrorCloseFile(DOKAN_CLOSE_FILE_EVENT *EventInfo) { + + WCHAR filePath[MAX_PATH]; + MIRROR_FILE_HANDLE *mirrorHandle = (MIRROR_FILE_HANDLE*)EventInfo->DokanFileInfo->Context; + + GetFilePath(filePath, MAX_PATH, EventInfo->FileName); + + DbgPrint(L"CloseFile: %s\n", filePath); + + if (mirrorHandle) { + + EnterCriticalSection(&mirrorHandle->Lock); + { + mirrorHandle->IsClosed = TRUE; + + if(mirrorHandle->FileHandle && mirrorHandle->FileHandle != INVALID_HANDLE_VALUE) { + + CloseHandle(mirrorHandle->FileHandle); + mirrorHandle->FileHandle = NULL; + + CheckDeleteOnClose(EventInfo->DokanFileInfo, filePath); + } + } + LeaveCriticalSection(&mirrorHandle->Lock); + + EventInfo->DokanFileInfo->Context = 0; + + PushMirrorFileHandle(mirrorHandle); } } @@ -973,6 +1009,63 @@ static void DOKAN_CALLBACK MirrorCleanup(DOKAN_CLEANUP_EVENT *EventInfo) { * might hold outstanding references to the file object. These components can still read to or write from a file, even * after an IRP_MJ_CLEANUP request is received. */ + + MIRROR_FILE_HANDLE *mirrorHandle = (MIRROR_FILE_HANDLE*)EventInfo->DokanFileInfo->Context; + + if(mirrorHandle) { + + DbgPrint(L"\tClosing file handle.\n"); + + EnterCriticalSection(&mirrorHandle->Lock); + { + CloseHandle(mirrorHandle->FileHandle); + + mirrorHandle->FileHandle = NULL; + mirrorHandle->IsCleanedUp = TRUE; + + CheckDeleteOnClose(EventInfo->DokanFileInfo, filePath); + } + LeaveCriticalSection(&mirrorHandle->Lock); + } +} + +static NTSTATUS MirrorReadFileSynchronous(DOKAN_READ_FILE_EVENT *EventInfo, HANDLE FileHandle) { + + LARGE_INTEGER distanceToMove; + + distanceToMove.QuadPart = EventInfo->Offset; + + if(!SetFilePointerEx(FileHandle, distanceToMove, NULL, FILE_BEGIN)) { + + DWORD error = GetLastError(); + + DbgPrint(L"\tseek error, offset = %d\n\n", EventInfo->Offset); + + return DokanNtStatusFromWin32(error); + } + + if(!ReadFile(FileHandle, + EventInfo->Buffer, + EventInfo->NumberOfBytesToRead, + &EventInfo->NumberOfBytesRead, + NULL)) { + + DWORD error = GetLastError(); + + DbgPrint(L"\tread error = %u, buffer length = %d, read length = %d\n\n", + error, EventInfo->NumberOfBytesToRead, EventInfo->NumberOfBytesRead); + + return DokanNtStatusFromWin32(error); + } + else { + + DbgPrint(L"\tByte to read: %d, Byte read %d, offset %d\n\n", + EventInfo->NumberOfBytesToRead, + EventInfo->NumberOfBytesRead, + EventInfo->Offset); + } + + return STATUS_SUCCESS; } static NTSTATUS DOKAN_CALLBACK MirrorReadFile(DOKAN_READ_FILE_EVENT *EventInfo) { @@ -989,6 +1082,37 @@ static NTSTATUS DOKAN_CALLBACK MirrorReadFile(DOKAN_READ_FILE_EVENT *EventInfo) return STATUS_FILE_CLOSED; } + BOOL isCleanedUp = FALSE; + BOOL isClosed = FALSE; + GetMirrorFileHandleState(mirrorHandle, &isCleanedUp, &isClosed); + + if(isClosed) { + + DbgPrint(L"\tIsClosed = TRUE\n"); + return STATUS_FILE_CLOSED; + } + + if(isCleanedUp) { + + DbgPrint(L"\tIsCleanedUp = TRUE\n"); + + HANDLE tmpHandle = CreateFile(filePath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + + if(tmpHandle == INVALID_HANDLE_VALUE) { + + DWORD error = GetLastError(); + DbgPrint(L"\tCreateFile error : %d\n\n", error); + + return DokanNtStatusFromWin32(error); + } + + NTSTATUS status = MirrorReadFileSynchronous(EventInfo, tmpHandle); + + CloseHandle(tmpHandle); + + return status; + } + #if USE_ASYNC_IO MIRROR_OVERLAPPED *overlapped = PopMirrorOverlapped(); @@ -1028,42 +1152,92 @@ static NTSTATUS DOKAN_CALLBACK MirrorReadFile(DOKAN_READ_FILE_EVENT *EventInfo) return STATUS_PENDING; #else - LARGE_INTEGER distanceToMove; + return MirrorReadFileSynchronous(EventInfo, mirrorHandle->FileHandle); +#endif +} - distanceToMove.QuadPart = EventInfo->Offset; +static NTSTATUS MirrorWriteFileSynchronous(DOKAN_WRITE_FILE_EVENT *EventInfo, HANDLE FileHandle, UINT64 FileSize) { - if (!SetFilePointerEx(mirrorHandle->FileHandle, distanceToMove, NULL, FILE_BEGIN)) { + LARGE_INTEGER distanceToMove; - DWORD error = GetLastError(); + if(EventInfo->DokanFileInfo->WriteToEndOfFile) { - DbgPrint(L"\tseek error, offset = %d\n\n", EventInfo->Offset); + LARGE_INTEGER z; + z.QuadPart = 0; - return DokanNtStatusFromWin32(error); - } + if(!SetFilePointerEx(FileHandle, z, NULL, FILE_END)) { - if (!ReadFile(mirrorHandle->FileHandle, - EventInfo->Buffer, - EventInfo->NumberOfBytesToRead, - &EventInfo->NumberOfBytesRead, - NULL)) { + DWORD error = GetLastError(); - DWORD error = GetLastError(); + DbgPrint(L"\tseek error, offset = EOF, error = %d\n", error); - DbgPrint(L"\tread error = %u, buffer length = %d, read length = %d\n\n", - error, EventInfo->NumberOfBytesToRead, EventInfo->NumberOfBytesRead); + return DokanNtStatusFromWin32(error); + } + } + else { + // Paging IO cannot write after allocate file size. + if(EventInfo->DokanFileInfo->PagingIo) { - return DokanNtStatusFromWin32(error); - } - else { + if((UINT64)EventInfo->Offset >= FileSize) { - DbgPrint(L"\tByte to read: %d, Byte read %d, offset %d\n\n", - EventInfo->NumberOfBytesToRead, - EventInfo->NumberOfBytesRead, - EventInfo->Offset); - } + EventInfo->NumberOfBytesWritten = 0; - return STATUS_SUCCESS; -#endif + return STATUS_SUCCESS; + } + + if(((UINT64)EventInfo->Offset + EventInfo->NumberOfBytesToWrite) > FileSize) { + + UINT64 bytes = FileSize - EventInfo->Offset; + + if(bytes >> 32) { + + EventInfo->NumberOfBytesToWrite = (DWORD)(bytes & 0xFFFFFFFFUL); + } + else { + + EventInfo->NumberOfBytesToWrite = (DWORD)bytes; + } + } + } + + if((UINT64)EventInfo->Offset > FileSize) { + // In the mirror sample helperZeroFileData is not necessary. NTFS will + // zero a hole. + // But if user's file system is different from NTFS( or other Windows's + // file systems ) then users will have to zero the hole themselves. + } + + distanceToMove.QuadPart = EventInfo->Offset; + + if(!SetFilePointerEx(FileHandle, distanceToMove, NULL, FILE_BEGIN)) { + + DWORD error = GetLastError(); + + DbgPrint(L"\tseek error, offset = %I64d, error = %d\n", EventInfo->Offset, error); + + return DokanNtStatusFromWin32(error); + } + } + + if(!WriteFile(FileHandle, + EventInfo->Buffer, + EventInfo->NumberOfBytesToWrite, + &EventInfo->NumberOfBytesWritten, + NULL)) { + + DWORD error = GetLastError(); + + DbgPrint(L"\twrite error = %u, buffer length = %d, write length = %d\n", + error, EventInfo->NumberOfBytesToWrite, EventInfo->NumberOfBytesWritten); + + return DokanNtStatusFromWin32(error); + } + else { + + DbgPrint(L"\twrite %d, offset %I64d\n\n", EventInfo->NumberOfBytesWritten, EventInfo->Offset); + } + + return STATUS_SUCCESS; } static NTSTATUS DOKAN_CALLBACK MirrorWriteFile(DOKAN_WRITE_FILE_EVENT *EventInfo) { @@ -1084,6 +1258,50 @@ static NTSTATUS DOKAN_CALLBACK MirrorWriteFile(DOKAN_WRITE_FILE_EVENT *EventInfo UINT64 fileSize = 0; DWORD fileSizeLow = 0; DWORD fileSizeHigh = 0; + BOOL isCleanedUp = FALSE; + BOOL isClosed = FALSE; + + GetMirrorFileHandleState(mirrorHandle, &isCleanedUp, &isClosed); + + if(isClosed) { + + DbgPrint(L"\tIsClosed = TRUE\n"); + return STATUS_FILE_CLOSED; + } + + if(isCleanedUp) { + + DbgPrint(L"\tIsCleanedUp = TRUE\n"); + + HANDLE tmpHandle = CreateFile(filePath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + + if(tmpHandle == INVALID_HANDLE_VALUE) { + + DWORD error = GetLastError(); + DbgPrint(L"\tCreateFile error : %d\n\n", error); + + return DokanNtStatusFromWin32(error); + } + + fileSizeLow = GetFileSize(tmpHandle, &fileSizeHigh); + + if(fileSizeLow == INVALID_FILE_SIZE) { + + DWORD error = GetLastError(); + + DbgPrint(L"\tcan not get a file size error = %d\n", error); + + return DokanNtStatusFromWin32(error); + } + + fileSize = ((UINT64)fileSizeHigh << 32) | fileSizeLow; + + NTSTATUS status = MirrorWriteFileSynchronous(EventInfo, mirrorHandle->FileHandle, fileSize); + + CloseHandle(tmpHandle); + + return status; + } fileSizeLow = GetFileSize(mirrorHandle->FileHandle, &fileSizeHigh); @@ -1161,86 +1379,7 @@ static NTSTATUS DOKAN_CALLBACK MirrorWriteFile(DOKAN_WRITE_FILE_EVENT *EventInfo return STATUS_PENDING; #else - LARGE_INTEGER distanceToMove; - - if (EventInfo->DokanFileInfo->WriteToEndOfFile) { - - LARGE_INTEGER z; - z.QuadPart = 0; - - if (!SetFilePointerEx(mirrorHandle->FileHandle, z, NULL, FILE_END)) { - - DWORD error = GetLastError(); - - DbgPrint(L"\tseek error, offset = EOF, error = %d\n", error); - - return DokanNtStatusFromWin32(error); - } - } - else { - // Paging IO cannot write after allocate file size. - if (EventInfo->DokanFileInfo->PagingIo) { - - if ((UINT64)EventInfo->Offset >= fileSize) { - - EventInfo->NumberOfBytesWritten = 0; - - return STATUS_SUCCESS; - } - - if (((UINT64)EventInfo->Offset + EventInfo->NumberOfBytesToWrite) > fileSize) { - - UINT64 bytes = fileSize - EventInfo->Offset; - - if (bytes >> 32) { - - EventInfo->NumberOfBytesToWrite = (DWORD)(bytes & 0xFFFFFFFFUL); - } - else { - - EventInfo->NumberOfBytesToWrite = (DWORD)bytes; - } - } - } - - if ((UINT64)EventInfo->Offset > fileSize) { - // In the mirror sample helperZeroFileData is not necessary. NTFS will - // zero a hole. - // But if user's file system is different from NTFS( or other Windows's - // file systems ) then users will have to zero the hole themselves. - } - - distanceToMove.QuadPart = EventInfo->Offset; - - if (!SetFilePointerEx(mirrorHandle->FileHandle, distanceToMove, NULL, FILE_BEGIN)) { - - DWORD error = GetLastError(); - - DbgPrint(L"\tseek error, offset = %I64d, error = %d\n", EventInfo->Offset, error); - - return DokanNtStatusFromWin32(error); - } - } - - if (!WriteFile(mirrorHandle->FileHandle, - EventInfo->Buffer, - EventInfo->NumberOfBytesToWrite, - &EventInfo->NumberOfBytesWritten, - NULL)) { - - DWORD error = GetLastError(); - - DbgPrint(L"\twrite error = %u, buffer length = %d, write length = %d\n", - error, EventInfo->NumberOfBytesToWrite, EventInfo->NumberOfBytesWritten); - - return DokanNtStatusFromWin32(error); - } - else { - - DbgPrint(L"\twrite %d, offset %I64d\n\n", EventInfo->NumberOfBytesWritten, EventInfo->Offset); - } - - return STATUS_SUCCESS; + return MirrorWriteFileSynchronous(EventInfo, mirrorHandle->FileHandle, fileSize); #endif } @@ -1467,7 +1606,7 @@ MirrorCanDeleteFile(DOKAN_CAN_DELETE_FILE_EVENT *EventInfo) { if((fileInfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY) { - return STATUS_ACCESS_DENIED; + return STATUS_CANNOT_DELETE; } if(EventInfo->DokanFileInfo->IsDirectory) { @@ -1757,7 +1896,18 @@ static NTSTATUS DOKAN_CALLBACK MirrorGetFileSecurity(DOKAN_GET_FILE_SECURITY_EVE // GetSecurityInfo() is not thread safe so we use a critical section here to synchronize EnterCriticalSection(&mirrorHandle->Lock); { - error = GetSecurityInfo(mirrorHandle->FileHandle, SE_FILE_OBJECT, EventInfo->SecurityInformation, NULL, NULL, NULL, NULL, &tempSecurityDesc); + if(mirrorHandle->IsClosed) { + + error = ERROR_INVALID_HANDLE; + } + else if(mirrorHandle->IsCleanedUp) { + + error = GetNamedSecurityInfoW(filePath, SE_FILE_OBJECT, EventInfo->SecurityInformation, NULL, NULL, NULL, NULL, &tempSecurityDesc); + } + else { + + error = GetSecurityInfo(mirrorHandle->FileHandle, SE_FILE_OBJECT, EventInfo->SecurityInformation, NULL, NULL, NULL, NULL, &tempSecurityDesc); + } } LeaveCriticalSection(&mirrorHandle->Lock); @@ -1851,8 +2001,58 @@ static NTSTATUS DOKAN_CALLBACK MirrorSetFileSecurity(DOKAN_SET_FILE_SECURITY_EVE EnterCriticalSection(&mirrorHandle->Lock); { - // For some reason this appears to be only variant of SetSecurity that works without returning an ERROR_ACCESS_DENIED - setSecurity = SetUserObjectSecurity(mirrorHandle->FileHandle, &EventInfo->SecurityInformation, EventInfo->SecurityDescriptor); + if(mirrorHandle->IsClosed) { + + setSecurity = FALSE; + SetLastError(ERROR_INVALID_HANDLE); + } + else if(mirrorHandle->IsCleanedUp) { + + /*PSID owner = NULL; + PSID group = NULL; + PACL dacl = NULL; + PACL sacl = NULL; + BOOL ownerDefault = FALSE; + + if(EventInfo->SecurityInformation & (OWNER_SECURITY_INFORMATION | BACKUP_SECURITY_INFORMATION)) { + + GetSecurityDescriptorOwner(EventInfo->SecurityDescriptor, &owner, &ownerDefault); + } + + if(EventInfo->SecurityInformation & (GROUP_SECURITY_INFORMATION | BACKUP_SECURITY_INFORMATION)) { + + GetSecurityDescriptorGroup(EventInfo->SecurityDescriptor, &group, &ownerDefault); + } + + if(EventInfo->SecurityInformation & (DACL_SECURITY_INFORMATION | BACKUP_SECURITY_INFORMATION)) { + + BOOL hasDacl = FALSE; + + GetSecurityDescriptorDacl(EventInfo->SecurityDescriptor, &hasDacl, &dacl, &ownerDefault); + } + + if(EventInfo->SecurityInformation & + (GROUP_SECURITY_INFORMATION + | BACKUP_SECURITY_INFORMATION + | LABEL_SECURITY_INFORMATION + | SACL_SECURITY_INFORMATION + | SCOPE_SECURITY_INFORMATION + | ATTRIBUTE_SECURITY_INFORMATION)) { + + BOOL hasSacl = FALSE; + + GetSecurityDescriptorSacl(EventInfo->SecurityDescriptor, &hasSacl, &sacl, &ownerDefault); + } + + setSecurity = SetNamedSecurityInfoW(filePath, SE_FILE_OBJECT, EventInfo->SecurityInformation, owner, group, dacl, sacl) == ERROR_SUCCESS;*/ + + setSecurity = SetFileSecurityW(filePath, EventInfo->SecurityInformation, EventInfo->SecurityDescriptor); + } + else { + + // For some reason this appears to be only variant of SetSecurity that works without returning an ERROR_ACCESS_DENIED + setSecurity = SetUserObjectSecurity(mirrorHandle->FileHandle, &EventInfo->SecurityInformation, EventInfo->SecurityDescriptor); + } } LeaveCriticalSection(&mirrorHandle->Lock); From 0b4f619fd4f58b9e004e4745f8a9a304495a5fac Mon Sep 17 00:00:00 2001 From: Keith Newton Date: Mon, 24 Oct 2016 14:06:45 -0400 Subject: [PATCH 20/20] Fixed large writes Removed the need for multiple ioctl's by allowing the driver to pass ERROR_MORE_DATA and ERROR_INSUFFICIENT_BUFFER to a ioctl Optimized write return status as it now no longer copies the number of bytes written back to the kernel --- dokan/dokan.c | 204 +++++++++++++++++++++++++++++++++++---------- dokan/dokani.h | 8 +- dokan/write.c | 85 +------------------ sys/device.c | 5 -- sys/dokan.c | 13 ++- sys/dokan.h | 2 - sys/event.c | 114 ------------------------- sys/notification.c | 49 ++++++++--- sys/public.h | 16 ++-- sys/write.c | 142 ++++++++++--------------------- 10 files changed, 268 insertions(+), 370 deletions(-) diff --git a/dokan/dokan.c b/dokan/dokan.c index 2fb4b19ff..a17456d33 100644 --- a/dokan/dokan.c +++ b/dokan/dokan.c @@ -165,8 +165,9 @@ DOKAN_IO_EVENT* PopIoEventBuffer() { if(ioEvent) { - RtlZeroMemory(ioEvent, sizeof(DOKAN_IO_EVENT)); + RtlZeroMemory(ioEvent, offsetof(DOKAN_IO_EVENT, KernelInfo) + sizeof(EVENT_CONTEXT)); ioEvent->Flags = DOKAN_IO_EVENT_FLAGS_POOLED; + ioEvent->Size = sizeof(DOKAN_IO_EVENT); } return ioEvent; @@ -1070,6 +1071,7 @@ int DOKANAPI DokanCreateFileSystem( LPWSTR GetRawDeviceName(LPCWSTR DeviceName, LPWSTR DestinationBuffer, rsize_t DestinationBufferSizeInElements) { + if (DeviceName && DestinationBuffer && DestinationBufferSizeInElements > 0) { wcscpy_s(DestinationBuffer, DestinationBufferSizeInElements, L"\\\\."); wcscat_s(DestinationBuffer, DestinationBufferSizeInElements, DeviceName); @@ -1079,7 +1081,9 @@ GetRawDeviceName(LPCWSTR DeviceName, LPWSTR DestinationBuffer, } 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)); } @@ -1130,7 +1134,7 @@ void OnDeviceIoCtlFailed(PDOKAN_INSTANCE Dokan, ULONG IoResult) { // disable keep alive timer SetThreadpoolTimer(Dokan->ThreadInfo.KeepAliveTimer, NULL, 0, 0); - DokanDbgPrintW(L"Dokan Warning: Closing IO processing for dokan instance %s with error code 0x%x and unmounting volume.\n", Dokan->DeviceName, IoResult); + DokanDbgPrintW(L"Dokan Fatal: Closing IO processing for dokan instance %s with error code 0x%x and unmounting volume.\n", Dokan->DeviceName, IoResult); DokanNotifyUnmounted(Dokan); @@ -1138,6 +1142,142 @@ void OnDeviceIoCtlFailed(PDOKAN_INSTANCE Dokan, ULONG IoResult) { SetEvent(Dokan->DeviceClosedWaitHandle); } +BOOL ResizeIoEvent(DOKAN_IO_EVENT **IoEvent, ULONG newSize) { + + assert(newSize > sizeof(EVENT_CONTEXT_MAX_SIZE)); + + size_t newEventSize = DOKAN_IO_EVENT_ALLOC_SIZE(newSize); + DOKAN_IO_EVENT *newEvent = NULL; + + if((*IoEvent)->Flags & DOKAN_IO_EVENT_FLAGS_POOLED) { + + newEvent = (DOKAN_IO_EVENT*)DokanMalloc(newEventSize); + + if(!newEvent) { + + return FALSE; + } + + memcpy_s(newEvent, newEventSize, *IoEvent, offsetof(DOKAN_IO_EVENT, KernelInfo)); + + newEvent->Flags &= ~DOKAN_IO_EVENT_FLAGS_POOLED; + newEvent->Size = (ULONG)newEventSize; + + PushIoEventBuffer(*IoEvent); + + *IoEvent = newEvent; + } + else { + + newEvent = (DOKAN_IO_EVENT*)DokanRealloc(*IoEvent, newEventSize); + + if(!newEvent) { + + return FALSE; + } + + newEvent->Size = (ULONG)newEventSize; + + *IoEvent = newEvent; + } + + return TRUE; +} + +// Don't know what went wrong +// Life will never be the same again +// End it all +void HandleProcessIoFatalError( + PDOKAN_INSTANCE Dokan, + DOKAN_IO_EVENT *IoEvent, + DOKAN_OVERLAPPED *Overlapped, + ULONG IoResult) { + + PushIoEventBuffer(IoEvent); + PushOverlapped(Overlapped); + + OnDeviceIoCtlFailed(Dokan, IoResult); +} + +void ProcessPartialWrite( + PDOKAN_INSTANCE Dokan, + DOKAN_OVERLAPPED *Overlapped, + ULONG_PTR NumberOfBytesTransferred) { + + DOKAN_IO_EVENT *currentIoEvent = (DOKAN_IO_EVENT*)Overlapped->OutputPayload; + + assert(NumberOfBytesTransferred <= UINT_MAX); + + if(NumberOfBytesTransferred < offsetof(EVENT_CONTEXT, Operation) + offsetof(WRITE_CONTEXT, RequestLength) + sizeof(ULONG)) { + + DOKAN_WRITE_FILE_EVENT *writeFileEvent = ¤tIoEvent->EventInfo.WriteFile; + + CreateDispatchCommon(currentIoEvent, 0); + + RtlZeroMemory(writeFileEvent, sizeof(DOKAN_WRITE_FILE_EVENT)); + + DokanEndDispatchWrite(writeFileEvent, STATUS_INTERNAL_ERROR); + + ResetOverlapped(Overlapped); + + if(!StartDeviceIO(Dokan, Overlapped)) { + + OnDeviceIoCtlFailed(Dokan, GetLastError()); + } + + return; + } + + if(!ResizeIoEvent( + ¤tIoEvent, + currentIoEvent->KernelInfo.EventContext.Operation.Write.RequestLength)) { + + DOKAN_WRITE_FILE_EVENT *writeFileEvent = ¤tIoEvent->EventInfo.WriteFile; + + CreateDispatchCommon(currentIoEvent, 0); + + RtlZeroMemory(writeFileEvent, sizeof(DOKAN_WRITE_FILE_EVENT)); + + DokanEndDispatchWrite(writeFileEvent, STATUS_INSUFFICIENT_RESOURCES); + + ResetOverlapped(Overlapped); + + if(!StartDeviceIO(Dokan, Overlapped)) { + + OnDeviceIoCtlFailed(Dokan, GetLastError()); + } + + return; + } + + Overlapped->OutputPayload = currentIoEvent; + + StartThreadpoolIo(Dokan->ThreadInfo.IoCompletion); + + if(!DeviceIoControl( + Dokan->Device, // Handle to device + IOCTL_EVENT_WAIT, // IO Control code + NULL, // Input Buffer to driver. + 0, // Length of input buffer in bytes. + currentIoEvent->KernelInfo.EventContextBuffer, // Output Buffer from driver. + DOKAN_IO_EVENT_KERNEL_INFO_SIZE(currentIoEvent), // Length of output buffer in bytes. + NULL, // Bytes placed in buffer. + (OVERLAPPED*)Overlapped // asynchronous call + )) { + + DWORD lastError = GetLastError(); + + if(lastError != ERROR_IO_PENDING) { + + DbgPrint("Dokan Error: Dokan device ioctl failed for wait with code %d.\n", lastError); + + CancelThreadpoolIo(Dokan->ThreadInfo.IoCompletion); + + HandleProcessIoFatalError(Dokan, currentIoEvent, Overlapped, lastError); + } + } +} + void ProcessIOEvent( PDOKAN_INSTANCE Dokan, DOKAN_OVERLAPPED *Overlapped, @@ -1145,16 +1285,26 @@ void ProcessIOEvent( ULONG_PTR NumberOfBytesTransferred) { DOKAN_IO_EVENT *currentIoEvent = (DOKAN_IO_EVENT*)Overlapped->OutputPayload; - currentIoEvent->EventSize = (ULONG)NumberOfBytesTransferred; + currentIoEvent->KernelInfoSize = (ULONG)NumberOfBytesTransferred; assert(currentIoEvent->EventResult == NULL && currentIoEvent->EventResultSize == 0); if(IoResult != NO_ERROR) { - PushIoEventBuffer(currentIoEvent); - PushOverlapped(Overlapped); + DbgPrintW(L"Dokan Warning: DeviceIoCtrl() has returned error code %u.\n", IoResult); + + if(IoResult == ERROR_MORE_DATA + && NumberOfBytesTransferred > offsetof(EVENT_CONTEXT, Operation)) { - OnDeviceIoCtlFailed(Dokan, IoResult); + switch(currentIoEvent->KernelInfo.EventContext.MajorFunction) { + + case IRP_MJ_WRITE: + ProcessPartialWrite(Dokan, Overlapped, NumberOfBytesTransferred); + return; + } + } + + HandleProcessIoFatalError(Dokan, currentIoEvent, Overlapped, IoResult); return; } @@ -1264,41 +1414,6 @@ void ProcessIOEvent( } } -void ProcessWriteSizeEvent( - DOKAN_OVERLAPPED *Overlapped, - ULONG IoResult, - ULONG_PTR NumberOfBytesTransferred) { - - DOKAN_IO_EVENT *inputIoEvent = (DOKAN_IO_EVENT*)Overlapped->InputPayload; - DOKAN_IO_EVENT *outputIoEvent = (DOKAN_IO_EVENT*)Overlapped->OutputPayload; - - assert(inputIoEvent && outputIoEvent); - assert(inputIoEvent->EventResult); - assert(inputIoEvent->EventInfo.WriteFile.NumberOfBytesWritten == 0); - assert(outputIoEvent->DokanInstance); - - PushOverlapped(Overlapped); - - if(IoResult != NO_ERROR) { - - // This will push the input buffer so we don't need to manually do that - DokanEndDispatchWrite(&inputIoEvent->EventInfo.WriteFile, STATUS_INTERNAL_ERROR); - - PushIoEventBuffer(outputIoEvent); - - return; - } - - FreeIoEventResult(inputIoEvent->EventResult, Overlapped->Flags); - PushIoEventBuffer(inputIoEvent); - - outputIoEvent->EventSize = (ULONG)NumberOfBytesTransferred; - - SetupIOEventForProcessing(outputIoEvent); - - BeginDispatchWrite(outputIoEvent); -} - // Process the result of SendEventInformation() void ProcessKernelResultEvent( DOKAN_OVERLAPPED *Overlapped) { @@ -1334,9 +1449,6 @@ VOID CALLBACK DokanLoop( case DOKAN_OVERLAPPED_TYPE_IOEVENT: ProcessIOEvent(dokan, overlapped, IoResult, NumberOfBytesTransferred); break; - case DOKAN_OVERLAPPED_TYPE_IOEVENT_WRITE_SIZE: - ProcessWriteSizeEvent(overlapped, IoResult, NumberOfBytesTransferred); - break; case DOKAN_OVERLAPPED_TYPE_IOEVENT_RESULT: ProcessKernelResultEvent(overlapped); break; @@ -1450,13 +1562,13 @@ void CreateDispatchCommon(DOKAN_IO_EVENT *EventInfo, ULONG SizeOfEventInfo) { EventInfo->EventResult = PopEventResult(); EventInfo->EventResultSize = DOKAN_EVENT_INFO_DEFAULT_SIZE; - EventInfo->Flags |= DOKAN_IO_EVENT_FLAGS_POOLED_RESULT; + EventInfo->Flags = DOKAN_IO_EVENT_FLAGS_POOLED_RESULT; } else { EventInfo->EventResultSize = DOKAN_EVENT_INFO_ALLOC_SIZE(SizeOfEventInfo); EventInfo->EventResult = (PEVENT_INFORMATION)DokanMalloc(EventInfo->EventResultSize); - EventInfo->Flags &= ~DOKAN_IO_EVENT_FLAGS_POOLED_RESULT; + EventInfo->Flags = 0; RtlZeroMemory(EventInfo->EventResult, EventInfo->EventResultSize); } diff --git a/dokan/dokani.h b/dokan/dokani.h index 8891be86d..85a85b622 100644 --- a/dokan/dokani.h +++ b/dokan/dokani.h @@ -137,10 +137,6 @@ typedef enum _DOKAN_OVERLAPPED_TYPE { // kernel driver. Results are represented as an EVENT_INFORMATION struct. DOKAN_OVERLAPPED_TYPE_IOEVENT_RESULT, - // The overlapped operation contains both an input and an output because - // the input IO event wasn't big enough to handle the write operation - DOKAN_OVERLAPPED_TYPE_IOEVENT_WRITE_SIZE, - } DOKAN_OVERLAPPED_TYPE; typedef enum _DOKAN_IO_EVENT_FLAGS { @@ -204,7 +200,8 @@ typedef struct _DOKAN_IO_EVENT { DOKAN_OPEN_INFO *DokanOpenInfo; PEVENT_INFORMATION EventResult; ULONG EventResultSize; - ULONG EventSize; + ULONG KernelInfoSize; + ULONG Size; DOKAN_IO_EVENT_FLAGS Flags; DOKAN_FILE_INFO DokanFileInfo; @@ -216,6 +213,7 @@ typedef struct _DOKAN_IO_EVENT { #define IoEventResultBufferSize(ioEvent) ((ioEvent)->EventResultSize >= offsetof(EVENT_INFORMATION, Buffer) ? (ioEvent)->EventResultSize - offsetof(EVENT_INFORMATION, Buffer) : 0) #define DOKAN_IO_EVENT_ALLOC_SIZE(bufSize) (offsetof(DOKAN_IO_EVENT, KernelInfo) + (bufSize)) +#define DOKAN_IO_EVENT_KERNEL_INFO_SIZE(ioEvent) ((ioEvent)->Size - offsetof(DOKAN_IO_EVENT, KernelInfo)) BOOL DokanStart(PDOKAN_INSTANCE Instance); diff --git a/dokan/write.c b/dokan/write.c index 54856db2c..7ef63ada9 100644 --- a/dokan/write.c +++ b/dokan/write.c @@ -22,76 +22,6 @@ with this program. If not, see . #include "dokani.h" #include -NTSTATUS SendWriteRequest(DOKAN_IO_EVENT *EventInfo) { - - DOKAN_IO_EVENT *writeEvent; - DOKAN_OVERLAPPED *overlapped; - DWORD lastError; - - DbgPrint("Dokan Information: Requesting write buffer of size %u.\n", EventInfo->KernelInfo.EventContext.Operation.Write.RequestLength); - - assert(EventInfo->EventResult); - assert(EventInfo->EventResultSize > 0); - - overlapped = PopOverlapped(); - - if(!overlapped) { - - DbgPrint("Failed to allocate overlapped.\n"); - - return STATUS_INTERNAL_ERROR; - } - - writeEvent = (DOKAN_IO_EVENT*)DokanMalloc(DOKAN_IO_EVENT_ALLOC_SIZE(EventInfo->KernelInfo.EventContext.Operation.Write.RequestLength)); - - if(!writeEvent) { - - DbgPrint("Dokan Error: Failed to allocate memory for write operation.\n"); - - PushOverlapped(overlapped); - - return STATUS_NO_MEMORY; - } - - RtlZeroMemory(writeEvent, EventInfo->KernelInfo.EventContext.Operation.Write.RequestLength); - writeEvent->DokanInstance = EventInfo->DokanInstance; - - overlapped->InputPayload = EventInfo; - overlapped->OutputPayload = writeEvent; - overlapped->PayloadType = DOKAN_OVERLAPPED_TYPE_IOEVENT_WRITE_SIZE; - overlapped->Flags = EventInfo->Flags; - - StartThreadpoolIo(EventInfo->DokanInstance->ThreadInfo.IoCompletion); - - if(!DeviceIoControl(EventInfo->DokanInstance->Device, // Handle to device - IOCTL_EVENT_WRITE, // IO Control code - EventInfo->EventResult, // Input Buffer to driver. - EventInfo->EventResultSize, // Length of input buffer in bytes. - writeEvent->KernelInfo.EventContextBuffer, // Output Buffer from driver. - EventInfo->KernelInfo.EventContext.Operation.Write.RequestLength, // Length of output buffer in bytes. - NULL, // Bytes placed in buffer. - (LPOVERLAPPED)overlapped // asynchronous call - )) { - - lastError = GetLastError(); - - if(lastError != ERROR_IO_PENDING) { - - DbgPrint("Dokan Error: Dokan device ioctl failed for wait with code %d.\n", lastError); - - CancelThreadpoolIo(EventInfo->DokanInstance->ThreadInfo.IoCompletion); - - PushOverlapped(overlapped); - - DokanFree(writeEvent); - - return DokanNtStatusFromWin32(lastError); - } - } - - return STATUS_SUCCESS; -} - void BeginDispatchWrite(DOKAN_IO_EVENT *EventInfo) { DOKAN_WRITE_FILE_EVENT *writeFileEvent = &EventInfo->EventInfo.WriteFile; @@ -104,18 +34,6 @@ void BeginDispatchWrite(DOKAN_IO_EVENT *EventInfo) { CreateDispatchCommon(EventInfo, 0); - // Since driver requested bigger memory, - // allocate enough memory and send it to driver - if (EventInfo->KernelInfo.EventContext.Operation.Write.RequestLength > 0) { - - if((status = SendWriteRequest(EventInfo)) != STATUS_SUCCESS) { - - DokanEndDispatchWrite(writeFileEvent, status); - } - - return; - } - CheckFileName(EventInfo->KernelInfo.EventContext.Operation.Write.FileName); DbgPrint("###WriteFile file handle = 0x%p, eventID = %04d, event Info = 0x%p\n", @@ -161,8 +79,9 @@ void DOKANAPI DokanEndDispatchWrite(DOKAN_WRITE_FILE_EVENT *EventInfo, NTSTATUS } result->Status = ResultStatus; - result->BufferLength = EventInfo->NumberOfBytesWritten; + result->BufferLength = 0; result->Operation.Write.CurrentByteOffset.QuadPart = EventInfo->Offset + EventInfo->NumberOfBytesWritten; + result->Operation.Write.BytesWritten = EventInfo->NumberOfBytesWritten; SendIoEventResult(ioEvent); } \ No newline at end of file diff --git a/sys/device.c b/sys/device.c index 29733b42f..055d29a72 100644 --- a/sys/device.c +++ b/sys/device.c @@ -933,11 +933,6 @@ Return Value: status = DokanEventRelease(DeviceObject, Irp); break; - case IOCTL_EVENT_WRITE: - DDbgPrint(" IOCTL_EVENT_WRITE\n"); - status = DokanEventWrite(DeviceObject, Irp); - break; - case IOCTL_KEEPALIVE: DDbgPrint(" IOCTL_KEEPALIVE\n"); if (IsFlagOn(vcb->Flags, VCB_MOUNTED)) { diff --git a/sys/dokan.c b/sys/dokan.c index e9a0fabaa..e90b8c4a7 100644 --- a/sys/dokan.c +++ b/sys/dokan.c @@ -421,24 +421,35 @@ VOID DokanPrintNTStatus(NTSTATUS Status) { PrintStatus(Status, STATUS_INVALID_DEVICE_REQUEST); PrintStatus(Status, STATUS_VOLUME_DISMOUNTED); PrintStatus(Status, STATUS_NO_SUCH_DEVICE); + PrintStatus(Status, STATUS_BUFFER_TOO_SMALL); } VOID DokanCompleteIrpRequest(__in PIRP Irp, __in NTSTATUS Status, __in ULONG_PTR Info) { if (Irp == NULL) { + DDbgPrint(" Irp is NULL, so no complete required\n"); return; } + if (Status == -1) { + DDbgPrint(" Status is -1 which is not valid NTSTATUS\n"); Status = STATUS_INVALID_PARAMETER; } + if (Status != STATUS_PENDING) { + Irp->IoStatus.Status = Status; Irp->IoStatus.Information = Info; IoCompleteRequest(Irp, IO_NO_INCREMENT); } - DokanPrintNTStatus(Status); + + // early out avoiding a bazillion checks of g_Debug + if(g_Debug) { + + DokanPrintNTStatus(Status); + } } VOID DokanNotifyReportChange0(__in PDokanFCB Fcb, __in PUNICODE_STRING FileName, diff --git a/sys/dokan.h b/sys/dokan.h index 1c9df7906..9fcd78215 100644 --- a/sys/dokan.h +++ b/sys/dokan.h @@ -554,8 +554,6 @@ DokanExceptionHandler(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp, DRIVER_DISPATCH DokanEventStart; -DRIVER_DISPATCH DokanEventWrite; - PEVENT_CONTEXT AllocateEventContextRaw(__in ULONG EventContextLength); diff --git a/sys/event.c b/sys/event.c index 6b90d44ee..ef6d7a874 100644 --- a/sys/event.c +++ b/sys/event.c @@ -679,117 +679,3 @@ DokanEventStart(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { return Irp->IoStatus.Status; } - -// user assinged bigger buffer that is enough to return WriteEventContext -NTSTATUS -DokanEventWrite(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { - KIRQL oldIrql; - PLIST_ENTRY thisEntry, nextEntry, listHead; - PIRP_ENTRY irpEntry; - PDokanVCB vcb; - PEVENT_INFORMATION eventInfo; - PIRP writeIrp; - - eventInfo = (PEVENT_INFORMATION)Irp->AssociatedIrp.SystemBuffer; - ASSERT(eventInfo != NULL); - - DDbgPrint("==> DokanEventWrite [EventInfo #%X]\n", eventInfo->SerialNumber); - - vcb = DeviceObject->DeviceExtension; - - if (GetIdentifierType(vcb) != VCB) { - return STATUS_INVALID_PARAMETER; - } - - ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); - KeAcquireSpinLock(&vcb->Dcb->PendingIrp.ListLock, &oldIrql); - - // search corresponding write IRP through pending IRP list - listHead = &vcb->Dcb->PendingIrp.ListHead; - - for (thisEntry = listHead->Flink; thisEntry != listHead; - thisEntry = nextEntry) { - - PIO_STACK_LOCATION writeIrpSp, eventIrpSp; - PEVENT_CONTEXT eventContext; - ULONG info = 0; - NTSTATUS status; - - nextEntry = thisEntry->Flink; - irpEntry = CONTAINING_RECORD(thisEntry, IRP_ENTRY, ListEntry); - - // check whehter this is corresponding IRP - - // DDbgPrint("SerialNumber irpEntry %X eventInfo %X\n", - // irpEntry->SerialNumber, eventInfo->SerialNumber); - - if (irpEntry->SerialNumber != eventInfo->SerialNumber) { - continue; - } - - // do NOT free irpEntry here - writeIrp = irpEntry->Irp; - if (writeIrp == NULL) { - // this IRP has already been canceled - ASSERT(irpEntry->CancelRoutineFreeMemory == FALSE); - DokanFreeIrpEntry(irpEntry); - continue; - } - - if (IoSetCancelRoutine(writeIrp, DokanIrpCancelRoutine) == NULL) { - // if (IoSetCancelRoutine(writeIrp, NULL) != NULL) { - // Cancel routine will run as soon as we release the lock - InitializeListHead(&irpEntry->ListEntry); - irpEntry->CancelRoutineFreeMemory = TRUE; - continue; - } - - writeIrpSp = irpEntry->IrpSp; - eventIrpSp = IoGetCurrentIrpStackLocation(Irp); - - ASSERT(writeIrpSp != NULL); - ASSERT(eventIrpSp != NULL); - - eventContext = - (PEVENT_CONTEXT) - writeIrp->Tail.Overlay.DriverContext[DRIVER_CONTEXT_EVENT]; - ASSERT(eventContext != NULL); - - // short of buffer length - if (eventIrpSp->Parameters.DeviceIoControl.OutputBufferLength < - eventContext->Length) { - DDbgPrint(" EventWrite: STATUS_INSUFFICIENT_RESOURCE\n"); - status = STATUS_INSUFFICIENT_RESOURCES; - } else { - PVOID buffer; - // DDbgPrint(" EventWrite CopyMemory\n"); - // DDbgPrint(" EventLength %d, BufLength %d\n", eventContext->Length, - // eventIrpSp->Parameters.DeviceIoControl.OutputBufferLength); - if (Irp->MdlAddress) - buffer = MmGetSystemAddressForMdlNormalSafe(Irp->MdlAddress); - else - buffer = Irp->AssociatedIrp.SystemBuffer; - - ASSERT(buffer != NULL); - RtlCopyMemory(buffer, eventContext, eventContext->Length); - - info = eventContext->Length; - status = STATUS_SUCCESS; - } - - DokanFreeEventContext(eventContext); - writeIrp->Tail.Overlay.DriverContext[DRIVER_CONTEXT_EVENT] = 0; - - KeReleaseSpinLock(&vcb->Dcb->PendingIrp.ListLock, oldIrql); - - Irp->IoStatus.Status = status; - Irp->IoStatus.Information = info; - - // this IRP will be completed by caller function - return Irp->IoStatus.Status; - } - - KeReleaseSpinLock(&vcb->Dcb->PendingIrp.ListLock, oldIrql); - - return STATUS_SUCCESS; -} diff --git a/sys/notification.c b/sys/notification.c index 5eff6f8be..437a9436f 100644 --- a/sys/notification.c +++ b/sys/notification.c @@ -266,51 +266,76 @@ VOID NotificationLoop(__in PIRP_LIST PendingIrp, __in PIRP_LIST NotifyEvent) { buffer = irp->AssociatedIrp.SystemBuffer; // buffer is not specified or short of length - if (bufferLen == 0 || buffer == NULL || bufferLen < eventLen) { - DDbgPrint("EventNotice : STATUS_INSUFFICIENT_RESOURCES\n"); - DDbgPrint(" bufferLen: %d, eventLen: %d\n", bufferLen, eventLen); + if (bufferLen == 0 || buffer == NULL) { + + DDbgPrint("EventNotice : STATUS_BUFFER_TOO_SMALL\n bufferLen: %d, eventLen: %d\n", bufferLen, eventLen); + // push back InsertTailList(&NotifyEvent->ListHead, &driverEventContext->ListEntry); - // marks as STATUS_INSUFFICIENT_RESOURCES + irpEntry->SerialNumber = 0; - } else { + irp->IoStatus.Information = 0; + irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + + } + else if(bufferLen < eventLen) { + + DDbgPrint("EventNotice : STATUS_BUFFER_OVERFLOW\n bufferLen: %d, eventLen: %d\n", bufferLen, eventLen); + + // push back + InsertTailList(&NotifyEvent->ListHead, &driverEventContext->ListEntry); + + irpEntry->SerialNumber = min(bufferLen, (ULONG)sizeof(EVENT_CONTEXT)); + irp->IoStatus.Information = irpEntry->SerialNumber; + irp->IoStatus.Status = STATUS_BUFFER_OVERFLOW; + + RtlCopyMemory(buffer, &driverEventContext->EventContext, irpEntry->SerialNumber); + } + else { + // let's copy EVENT_CONTEXT RtlCopyMemory(buffer, &driverEventContext->EventContext, eventLen); + // save event length irpEntry->SerialNumber = eventLen; + irp->IoStatus.Information = irpEntry->SerialNumber; + irp->IoStatus.Status = STATUS_SUCCESS; if (driverEventContext->Completed) { + KeSetEvent(driverEventContext->Completed, IO_NO_INCREMENT, FALSE); } + ExFreePool(driverEventContext); } + InsertTailList(&completeList, &irpEntry->ListEntry); } DDbgPrint("Clear Events...\n"); KeClearEvent(&NotifyEvent->NotEmpty); + DDbgPrint("Notify event cleared\n"); KeClearEvent(&PendingIrp->NotEmpty); + DDbgPrint("Pending event cleared\n"); DDbgPrint("Release SpinLock...\n"); KeReleaseSpinLock(&NotifyEvent->ListLock, notifyIrql); + DDbgPrint("SpinLock notify Released\n"); KeReleaseSpinLock(&PendingIrp->ListLock, irpIrql); + DDbgPrint("SpinLock irp Released\n"); while (!IsListEmpty(&completeList)) { + listHead = RemoveHeadList(&completeList); irpEntry = CONTAINING_RECORD(listHead, IRP_ENTRY, ListEntry); irp = irpEntry->Irp; - if (irpEntry->SerialNumber == 0) { - irp->IoStatus.Information = 0; - irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; - } else { - irp->IoStatus.Information = irpEntry->SerialNumber; - irp->IoStatus.Status = STATUS_SUCCESS; - } + DokanFreeIrpEntry(irpEntry); + DokanCompleteIrpRequest(irp, irp->IoStatus.Status, irp->IoStatus.Information); } diff --git a/sys/public.h b/sys/public.h index f6745f8ec..cad20d63e 100644 --- a/sys/public.h +++ b/sys/public.h @@ -48,8 +48,6 @@ with this program. If not, see . #define IOCTL_EVENT_START \ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x805, METHOD_BUFFERED, FILE_ANY_ACCESS) -#define IOCTL_EVENT_WRITE \ - CTL_CODE(FILE_DEVICE_UNKNOWN, 0x806, METHOD_OUT_DIRECT, FILE_ANY_ACCESS) #define IOCTL_KEEPALIVE \ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x809, METHOD_NEITHER, FILE_ANY_ACCESS) @@ -296,40 +294,48 @@ typedef struct _EVENT_CONTEXT { } Operation; } EVENT_CONTEXT, *PEVENT_CONTEXT; -#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; ULONG Flags; + union { struct { ULONG Index; } Directory; + struct { ULONG Flags; ULONG Information; } Create; + struct { LARGE_INTEGER CurrentByteOffset; } Read; + struct { LARGE_INTEGER CurrentByteOffset; + ULONG64 BytesWritten; } Write; + struct { UCHAR DeleteOnClose; } Delete; + struct { ULONG Timeout; } ResetTimeout; + struct { HANDLE Handle; } AccessToken; + } Operation; + ULONG64 Context; // This must be 64-bit to maintain 8 byte alignment for Buffer on x64 diff --git a/sys/write.c b/sys/write.c index 68f5703c3..e34095efc 100644 --- a/sys/write.c +++ b/sys/write.c @@ -196,6 +196,7 @@ DokanDispatchWrite(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { // the size of buffer to write eventContext->Operation.Write.BufferLength = irpSp->Parameters.Write.Length; + eventContext->Operation.Write.RequestLength = eventLength; // the offset from the begining of structure // the contents to write will be copyed to this offset @@ -213,102 +214,45 @@ DokanDispatchWrite(__in PDEVICE_OBJECT DeviceObject, __in PIRP Irp) { RtlCopyMemory(eventContext->Operation.Write.FileName, fcb->FileName.Buffer, fcb->FileName.Length); - // When eventlength is less than event notification buffer, - // returns it to user-mode using pending event. - if (eventLength <= EVENT_CONTEXT_MAX_SIZE) { - - DDbgPrint(" Offset %d:%d, Length %d\n", - irpSp->Parameters.Write.ByteOffset.HighPart, - irpSp->Parameters.Write.ByteOffset.LowPart, - irpSp->Parameters.Write.Length); - - // EventContext is no longer needed, clear it - Irp->Tail.Overlay.DriverContext[DRIVER_CONTEXT_EVENT] = 0; - - // - // We now check whether we can proceed based on the state of - // the file oplocks. - // - if (!FlagOn(Irp->Flags, IRP_PAGING_IO)) { - status = FsRtlCheckOplock(DokanGetFcbOplock(fcb), Irp, eventContext, - DokanOplockComplete, DokanPrePostIrp); - - // - // if FsRtlCheckOplock returns STATUS_PENDING the IRP has been posted - // to service an oplock break and we need to leave now. - // - if (status != STATUS_SUCCESS) { - if (status == STATUS_PENDING) { - DDbgPrint(" FsRtlCheckOplock returned STATUS_PENDING\n"); - } else { - DokanFreeEventContext(eventContext); - } - __leave; - } - } - - // register this IRP to IRP waiting list and make it pending status - status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0, NULL); - - // Resuests bigger memory - // eventContext will be freed later using - // Irp->Tail.Overlay.DriverContext[DRIVER_CONTEXT_EVENT] - } else { - // the length at lest file name can be stored - ULONG requestContextLength = max( - sizeof(EVENT_CONTEXT), eventContext->Operation.Write.BufferOffset); - PEVENT_CONTEXT requestContext = - AllocateEventContext(vcb->Dcb, Irp, requestContextLength, ccb); - - // no more memory! - if (requestContext == NULL) { - status = STATUS_INSUFFICIENT_RESOURCES; - Irp->Tail.Overlay.DriverContext[DRIVER_CONTEXT_EVENT] = 0; - DokanFreeEventContext(eventContext); - __leave; - } - - DDbgPrint(" Offset %d:%d, Length %d (request)\n", - irpSp->Parameters.Write.ByteOffset.HighPart, - irpSp->Parameters.Write.ByteOffset.LowPart, - irpSp->Parameters.Write.Length); - - // copies from begining of EventContext to the end of file name - RtlCopyMemory(requestContext, eventContext, - eventContext->Operation.Write.BufferOffset); - // puts actual size of RequestContext - requestContext->Length = requestContextLength; - // requsts enough size to copy EventContext - requestContext->Operation.Write.RequestLength = eventLength; - - // - // We now check whether we can proceed based on the state of - // the file oplocks. - // - if (!FlagOn(Irp->Flags, IRP_PAGING_IO)) { - status = FsRtlCheckOplock(DokanGetFcbOplock(fcb), Irp, requestContext, - DokanOplockComplete, DokanPrePostIrp); - - // - // if FsRtlCheckOplock returns STATUS_PENDING the IRP has been posted - // to service an oplock break and we need to leave now. - // - if (status != STATUS_SUCCESS) { - if (status == STATUS_PENDING) { - DDbgPrint(" FsRtlCheckOplock returned STATUS_PENDING\n"); - } else { - DokanFreeEventContext(requestContext); - Irp->Tail.Overlay.DriverContext[DRIVER_CONTEXT_EVENT] = 0; - DokanFreeEventContext(eventContext); - } - __leave; - } - } - - // regiters this IRP to IRP wainting list and make it pending status - status = DokanRegisterPendingIrp(DeviceObject, Irp, requestContext, 0, NULL); + DDbgPrint(" Offset %d:%d, Length %d\n", + irpSp->Parameters.Write.ByteOffset.HighPart, + irpSp->Parameters.Write.ByteOffset.LowPart, + irpSp->Parameters.Write.Length); + + // EventContext is no longer needed, clear it + Irp->Tail.Overlay.DriverContext[DRIVER_CONTEXT_EVENT] = 0; + + // + // We now check whether we can proceed based on the state of + // the file oplocks. + // + if (!FlagOn(Irp->Flags, IRP_PAGING_IO)) { + + status = FsRtlCheckOplock(DokanGetFcbOplock(fcb), Irp, eventContext, + DokanOplockComplete, DokanPrePostIrp); + + // + // if FsRtlCheckOplock returns STATUS_PENDING the IRP has been posted + // to service an oplock break and we need to leave now. + // + if (status != STATUS_SUCCESS) { + + if (status == STATUS_PENDING) { + + DDbgPrint(" FsRtlCheckOplock returned STATUS_PENDING\n"); + } + else { + + DokanFreeEventContext(eventContext); + } + + __leave; + } } + // register this IRP to IRP waiting list and make it pending status + status = DokanRegisterPendingIrp(DeviceObject, Irp, eventContext, 0, NULL); + } __finally { if(fcbLocked) DokanFCBUnlock(fcb); @@ -346,13 +290,17 @@ VOID DokanCompleteWrite(__in PIRP_ENTRY IrpEntry, status = EventInfo->Status; irp->IoStatus.Status = status; - irp->IoStatus.Information = (ULONG_PTR)EventInfo->BufferLength; + irp->IoStatus.Information = (ULONG_PTR)EventInfo->Operation.Write.BytesWritten; + + if (NT_SUCCESS(status) + && EventInfo->Operation.Write.BytesWritten != 0 + && (fileObject->Flags & FO_SYNCHRONOUS_IO) + && !(irp->Flags & IRP_PAGING_IO)) { - if (NT_SUCCESS(status) && EventInfo->BufferLength != 0 && - (fileObject->Flags & FO_SYNCHRONOUS_IO) && !(irp->Flags & IRP_PAGING_IO)) { // update current byte offset only when synchronous IO and not paging IO fileObject->CurrentByteOffset.QuadPart = EventInfo->Operation.Write.CurrentByteOffset.QuadPart; + DDbgPrint(" Updated CurrentByteOffset %I64d\n", fileObject->CurrentByteOffset.QuadPart); }