├── src ├── resource.h ├── winmd.inf ├── winmd.rc.in ├── raid1.c ├── mountmgr.c ├── logger.c ├── linear.c ├── winmd.h ├── pnp.c ├── raid0.c ├── io.c ├── raid45.c └── winmd.c ├── mingw-amd64.cmake ├── mingw-x86.cmake ├── README.md ├── CMakeLists.txt └── LICENCE /src/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by winmd.rc 4 | 5 | // Next default values for new objects 6 | // 7 | #ifdef APSTUDIO_INVOKED 8 | #ifndef APSTUDIO_READONLY_SYMBOLS 9 | #define _APS_NEXT_RESOURCE_VALUE 101 10 | #define _APS_NEXT_COMMAND_VALUE 40001 11 | #define _APS_NEXT_CONTROL_VALUE 1001 12 | #define _APS_NEXT_SYMED_VALUE 101 13 | #endif 14 | #endif 15 | -------------------------------------------------------------------------------- /mingw-amd64.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # To Cross-compile, use: 3 | # cmake -DCMAKE_TOOLCHAIN_FILE=../mingw.cmake .. 4 | # 5 | # the name of the target operating system 6 | SET(CMAKE_SYSTEM_NAME Windows) 7 | 8 | # which compilers to use for C and C++ 9 | SET(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) 10 | SET(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++) 11 | SET(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres) 12 | 13 | # here is the target environment located 14 | SET(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32) 15 | 16 | # adjust the default behaviour of the FIND_XXX() commands: 17 | # search headers and libraries in the target environment, search 18 | # programs in the host environment 19 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 20 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 21 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 22 | 23 | set(CMAKE_PREFIX_PATH /usr/x86_64-w64-mingw32/usr/lib/cmake) 24 | 25 | set(CMAKE_SYSTEM_PROCESSOR x86_64) 26 | -------------------------------------------------------------------------------- /mingw-x86.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # To Cross-compile, use: 3 | # cmake -DCMAKE_TOOLCHAIN_FILE=../mingw.cmake .. 4 | # 5 | # the name of the target operating system 6 | SET(CMAKE_SYSTEM_NAME Windows) 7 | 8 | # which compilers to use for C and C++ 9 | SET(CMAKE_C_COMPILER i686-w64-mingw32-gcc) 10 | SET(CMAKE_CXX_COMPILER i686-w64-mingw32-g++) 11 | SET(CMAKE_RC_COMPILER i686-w64-mingw32-windres) 12 | 13 | SET(CMAKE_RC_FLAGS "-F pe-i386") 14 | 15 | # here is the target environment located 16 | SET(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32) 17 | 18 | # adjust the default behaviour of the FIND_XXX() commands: 19 | # search headers and libraries in the target environment, search 20 | # programs in the host environment 21 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 22 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 23 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 24 | 25 | set(CMAKE_PREFIX_PATH /usr/i686-w64-mingw32/usr/lib/cmake) 26 | 27 | set(CMAKE_SYSTEM_PROCESSOR x86) 28 | -------------------------------------------------------------------------------- /src/winmd.inf: -------------------------------------------------------------------------------- 1 | ;;; 2 | ;;; WinMD 3 | ;;; 4 | ;;; 5 | ;;; Copyright (c) 2019 Mark Harmstone 6 | ;;; 7 | 8 | [Version] 9 | Signature = "$Windows NT$" 10 | Class = Volume 11 | ClassGuid = {71a27cdd-812a-11d0-bec7-08002be2092f} 12 | Provider = %Me% 13 | DriverVer = 07/31/2019,0.1.0.0 14 | CatalogFile = winmd.cat 15 | 16 | [DestinationDirs] 17 | DefaultDestDir = 12 18 | WinMD.DriverFiles = 12 ;%windir%\system32\drivers 19 | 20 | ;; 21 | ;; Default install sections 22 | ;; 23 | 24 | [DefaultInstall] 25 | OptionDesc = %ServiceDescription% 26 | CopyFiles = WinMD.DriverFiles 27 | CopyINF = winmd.inf 28 | 29 | [DefaultInstall.Services] 30 | AddService = %ServiceName%,0x802,WinMD.Service 31 | 32 | [Manufacturer] 33 | %Me%=Standard,NTamd64,NTx86 34 | 35 | [Standard.NTamd64] 36 | %VolumeName% = WinMD_Install, WinMDVolume 37 | %ControllerName% = WinMD_Install, ROOT\winmd 38 | 39 | [Standard.NTx86] 40 | %VolumeName% = WinMD_Install, WinMDVolume 41 | %ControllerName% = WinMD_Install, ROOT\winmd 42 | 43 | [WinMD_Install] 44 | OptionDesc = %ServiceDescription% 45 | CopyFiles = WinMD.DriverFiles 46 | 47 | [WinMD_Install.Services] 48 | AddService = %ServiceName%,2,WinMD.Service 49 | 50 | ;; 51 | ;; Default uninstall sections 52 | ;; 53 | 54 | [DefaultUninstall] 55 | DelFiles = WinMD.DriverFiles 56 | 57 | [DefaultUninstall.Services] 58 | DelService = %ServiceName%,0x200 ;Ensure service is stopped before deleting 59 | 60 | ; 61 | ; Services Section 62 | ; 63 | 64 | [WinMD.Service] 65 | DisplayName = %ServiceName% 66 | Description = %ServiceDescription% 67 | ServiceBinary = %12%\%DriverName%.sys ;%windir%\system32\drivers\ 68 | ServiceType = 1 69 | StartType = 1 ;SERVICE_SYSTEM_START 70 | ErrorControl = 1 71 | 72 | ; 73 | ; Copy Files 74 | ; 75 | 76 | [WinMD.DriverFiles] 77 | %DriverName%.sys 78 | 79 | [SourceDisksFiles] 80 | winmd.sys = 1,, 81 | 82 | [SourceDisksNames.x86] 83 | 1 = %DiskId1%,,,\x86 84 | 85 | [SourceDisksNames.amd64] 86 | 1 = %DiskId1%,,,\x64 87 | 88 | ;; 89 | ;; String Section 90 | ;; 91 | 92 | [Strings] 93 | Me = "Mark Harmstone" 94 | ServiceDescription = "WinMD driver" 95 | ServiceName = "winmd" 96 | DriverName = "winmd" 97 | DiskId1 = "winmd Device Installation Disk" 98 | VolumeName = "WinMD volume" 99 | ControllerName = "WinMD controller" 100 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | WinMD v0.1 2 | ---------- 3 | 4 | WinMD is a driver allowing Windows to access MD RAID devices - software RAID 5 | volumes created by `mdadm` on Linux. Bear in mind that you will still need a 6 | filesystem driver: see, for instance, [WinBtrfs](https://github.com/maharmstone/btrfs) and [Ext2fsd](https://sourceforge.net/projects/ext2fsd/). 7 | 8 | Everything here is released under the GNU Lesser General Public Licence (LGPL); 9 | see the file LICENCE for more info. You are encouraged to play about with the 10 | source code as you will, and I'd appreciate a note (mark@harmstone.com) if you 11 | come up with anything nifty. 12 | 13 | Donations 14 | --------- 15 | 16 | I've been developing this driver for fun, and in the hopes that someone out there 17 | will find it useful. But if you want to provide some pecuniary encouragement, it'd 18 | be very much appreciated: 19 | 20 | * [Paypal](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=3XQVCQ6YB55L2&lc=GB&item_name=WinMD%20donation¤cy_code=GBP&bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHosted) 21 | 22 | Features 23 | -------- 24 | 25 | * RAID 0 26 | * RAID 1 27 | * RAID 4 28 | * RAID 5 29 | * RAID 6 30 | * RAID 10 (near, far, offset) 31 | * Linear 32 | * Recognizes version 1 superblocks (1.0, 1.1, 1.2) 33 | * Nested sets 34 | 35 | Todo 36 | ---- 37 | 38 | * whole-disk RAID (i.e. recognizing partitions on MD device) 39 | * reshaping 40 | * rebuilding 41 | * checking 42 | * degraded mounts 43 | * adding and removing devices 44 | * creating new sets from Windows 45 | * RAID4/5/6 journal 46 | * write-intent bitmaps 47 | * version 0.9 superblocks 48 | 49 | Installation 50 | ------------ 51 | 52 | To install the driver, [download and extract the latest release](https://github.com/maharmstone/winmd/releases), 53 | right-click winmd.inf, and choose Install. The driver is signed, so should work out 54 | of the box on modern versions of Windows. 55 | 56 | For the very latest versions of Windows 10, Microsoft introduced more onerous 57 | requirements for signing, which are only available to corporations and not individuals. 58 | If this affects you (i.e. you get a signing error when trying to install the driver), 59 | try disabling Secure Boot in your BIOS settings. 60 | 61 | There's also a [Chocolatey package](https://chocolatey.org/packages/winmd) available - 62 | if you have Chocolatey installed, try running `choco install winmd`. 63 | 64 | Uninstalling 65 | ------------ 66 | 67 | From an elevated command prompt, run: 68 | 69 | `RUNDLL32.EXE SETUPAPI.DLL,InstallHinfSection DefaultUninstall 132 winmd.inf` 70 | 71 | You will probably need to give the full path to winmd.inf. Next time you reboot, Windows 72 | will remove the driver from your system. 73 | 74 | You can also disable it by opening regedit and setting the value of 75 | HKLM\SYSTEM\CurrentControlSet\services\winmd\Start to 4. 76 | 77 | Changelog 78 | --------- 79 | 80 | v0.1 (2019-07-31): 81 | * Initial release 82 | -------------------------------------------------------------------------------- /src/winmd.rc.in: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #include "@CMAKE_CURRENT_SOURCE_DIR@/src/resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // English (United Kingdom) resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) 19 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK 20 | #pragma code_page(1252) 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Version 51 | // 52 | 53 | VS_VERSION_INFO VERSIONINFO 54 | FILEVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@,0 55 | PRODUCTVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@,0 56 | FILEFLAGSMASK 0x3fL 57 | #ifdef _DEBUG 58 | FILEFLAGS 0x1L 59 | #else 60 | FILEFLAGS 0x0L 61 | #endif 62 | FILEOS 0x40004L 63 | FILETYPE 0x1L 64 | FILESUBTYPE 0x0L 65 | BEGIN 66 | BLOCK "StringFileInfo" 67 | BEGIN 68 | BLOCK "080904b0" 69 | BEGIN 70 | VALUE "CompanyName", "Mark Harmstone" 71 | VALUE "FileDescription", "Windows MD RAID driver" 72 | VALUE "FileVersion", "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@" 73 | VALUE "InternalName", "winmd.sys" 74 | VALUE "LegalCopyright", "Copyright (C) Mark Harmstone 2019" 75 | VALUE "OriginalFilename", "winmd.sys" 76 | VALUE "ProductName", "WinMD" 77 | VALUE "ProductVersion", "@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@" 78 | END 79 | END 80 | BLOCK "VarFileInfo" 81 | BEGIN 82 | VALUE "Translation", 0x809, 1200 83 | END 84 | END 85 | 86 | #endif // English (United Kingdom) resources 87 | ///////////////////////////////////////////////////////////////////////////// 88 | 89 | 90 | 91 | #ifndef APSTUDIO_INVOKED 92 | ///////////////////////////////////////////////////////////////////////////// 93 | // 94 | // Generated from the TEXTINCLUDE 3 resource. 95 | // 96 | 97 | 98 | ///////////////////////////////////////////////////////////////////////////// 99 | #endif // not APSTUDIO_INVOKED 100 | -------------------------------------------------------------------------------- /src/raid1.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Mark Harmstone 2019 2 | * 3 | * This file is part of WinMD. 4 | * 5 | * WinMD is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public Licence as published by 7 | * the Free Software Foundation, either version 3 of the Licence, or 8 | * (at your option) any later version. 9 | * 10 | * WinBtrfs is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Lesser General Public Licence for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public Licence 16 | * along with WinMD. If not, see . */ 17 | 18 | #include "winmd.h" 19 | 20 | typedef struct { 21 | PIRP Irp; 22 | KEVENT Event; 23 | IO_STATUS_BLOCK iosb; 24 | NTSTATUS Status; 25 | } io_context_raid1; 26 | 27 | NTSTATUS read_raid1(set_pdo* pdo, PIRP Irp, bool* no_complete) { 28 | NTSTATUS Status; 29 | 30 | ExAcquireResourceSharedLite(&pdo->lock, true); 31 | 32 | pdo->read_device++; 33 | 34 | set_child* c = pdo->child_list[pdo->read_device % pdo->array_info.raid_disks]; 35 | 36 | IoCopyCurrentIrpStackLocationToNext(Irp); 37 | 38 | PIO_STACK_LOCATION IrpSp = IoGetNextIrpStackLocation(Irp); 39 | 40 | IrpSp->FileObject = c->fileobj; 41 | IrpSp->Parameters.Read.ByteOffset.QuadPart += c->disk_info.data_offset * 512; 42 | 43 | *no_complete = true; 44 | 45 | Status = IoCallDriver(c->device, Irp); 46 | 47 | ExReleaseResourceLite(&pdo->lock); 48 | 49 | return Status; 50 | } 51 | 52 | _Function_class_(IO_COMPLETION_ROUTINE) 53 | static NTSTATUS __stdcall io_completion_raid1(PDEVICE_OBJECT devobj, PIRP Irp, PVOID ctx) { 54 | io_context_raid1* context = ctx; 55 | 56 | context->iosb = Irp->IoStatus; 57 | KeSetEvent(&context->Event, 0, FALSE); 58 | 59 | return STATUS_MORE_PROCESSING_REQUIRED; 60 | } 61 | 62 | NTSTATUS write_raid1(set_pdo* pdo, PIRP Irp) { 63 | NTSTATUS Status; 64 | 65 | PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 66 | 67 | io_context_raid1* ctxs = ExAllocatePoolWithTag(NonPagedPool, sizeof(io_context_raid1) * pdo->array_info.raid_disks, ALLOC_TAG); 68 | if (!ctxs) { 69 | ERR("out of memory\n"); 70 | return STATUS_INSUFFICIENT_RESOURCES; 71 | } 72 | 73 | RtlZeroMemory(ctxs, sizeof(io_context_raid1) * pdo->array_info.raid_disks); 74 | 75 | for (unsigned int i = 0; i < pdo->array_info.raid_disks; i++) { 76 | ctxs[i].Irp = IoAllocateIrp(pdo->child_list[i]->device->StackSize, false); 77 | 78 | if (!ctxs[i].Irp) { 79 | ERR("IoAllocateIrp failed\n"); 80 | Status = STATUS_INSUFFICIENT_RESOURCES; 81 | goto end; 82 | } 83 | 84 | PIO_STACK_LOCATION IrpSp2 = IoGetNextIrpStackLocation(ctxs[i].Irp); 85 | IrpSp2->MajorFunction = IRP_MJ_WRITE; 86 | IrpSp2->FileObject = pdo->child_list[i]->fileobj; 87 | 88 | ctxs[i].Irp->MdlAddress = Irp->MdlAddress; 89 | 90 | IrpSp2->Parameters.Write.Length = IrpSp->Parameters.Write.Length; 91 | IrpSp2->Parameters.Write.ByteOffset.QuadPart = IrpSp->Parameters.Write.ByteOffset.QuadPart + (pdo->child_list[i]->disk_info.data_offset * 512); 92 | 93 | ctxs[i].Irp->UserIosb = &ctxs[i].iosb; 94 | 95 | KeInitializeEvent(&ctxs[i].Event, NotificationEvent, false); 96 | ctxs[i].Irp->UserEvent = &ctxs[i].Event; 97 | 98 | IoSetCompletionRoutine(ctxs[i].Irp, io_completion_raid1, &ctxs[i], true, true, true); 99 | } 100 | 101 | for (unsigned int i = 0; i < pdo->array_info.raid_disks; i++) { 102 | ctxs[i].Status = IoCallDriver(pdo->child_list[i]->device, ctxs[i].Irp); 103 | if (!NT_SUCCESS(ctxs[i].Status)) 104 | ERR("IoCallDriver returned %08x\n", ctxs[i].Status); 105 | } 106 | 107 | Status = STATUS_SUCCESS; 108 | 109 | for (unsigned int i = 0; i < pdo->array_info.raid_disks; i++) { 110 | if (ctxs[i].Status == STATUS_PENDING) { 111 | KeWaitForSingleObject(&ctxs[i].Event, Executive, KernelMode, false, NULL); 112 | ctxs[i].Status = ctxs[i].iosb.Status; 113 | } 114 | 115 | if (!NT_SUCCESS(ctxs[i].Status)) 116 | Status = ctxs[i].Status; 117 | } 118 | 119 | end: 120 | for (unsigned int i = 0; i < pdo->array_info.raid_disks; i++) { 121 | if (ctxs[i].Irp) 122 | IoFreeIrp(ctxs[i].Irp); 123 | } 124 | 125 | ExFreePool(ctxs); 126 | 127 | return Status; 128 | } 129 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | project(winmd VERSION 0.1.0) 4 | 5 | # winmd.sys 6 | 7 | set(SRC_FILES src/winmd.c 8 | src/io.c 9 | src/linear.c 10 | src/logger.c 11 | src/mountmgr.c 12 | src/pnp.c 13 | src/raid0.c 14 | src/raid10.c 15 | src/raid1.c 16 | src/raid45.c 17 | src/raid6.c 18 | ${CMAKE_CURRENT_BINARY_DIR}/winmd.rc) 19 | 20 | set(CMAKE_C_STANDARD 11) 21 | set(CMAKE_C_STANDARD_REQUIRED ON) 22 | 23 | if(MSVC) # cmake bug 15170 24 | if(MSVC_C_ARCHITECTURE_ID STREQUAL "X86") 25 | set(CMAKE_SYSTEM_PROCESSOR "x86") 26 | elseif(MSVC_C_ARCHITECTURE_ID STREQUAL "x64") 27 | set(CMAKE_SYSTEM_PROCESSOR "x86_64") 28 | elseif(MSVC_C_ARCHITECTURE_ID STREQUAL "ARMV7") 29 | set(CMAKE_SYSTEM_PROCESSOR "arm") 30 | elseif(MSVC_C_ARCHITECTURE_ID STREQUAL "ARM64") 31 | set(CMAKE_SYSTEM_PROCESSOR "aarch64") 32 | endif() 33 | endif() 34 | 35 | configure_file(src/winmd.rc.in winmd.rc) 36 | 37 | add_library(winmd SHARED ${SRC_FILES}) 38 | 39 | if(MSVC) 40 | include_directories("$ENV{WindowsSdkDir}Include\\$ENV{WindowsSDKLibVersion}km") 41 | elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND WIN32) 42 | include_directories("${CMAKE_FIND_ROOT_PATH}/usr/include/ddk") 43 | endif() 44 | 45 | if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") 46 | add_definitions(-D_AMD64_) 47 | set(MS_ARCH "x64") 48 | elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86") 49 | add_definitions(-D_X86_) 50 | set(MS_ARCH "x86") 51 | elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm") 52 | add_definitions(-D_ARM_) 53 | set(MS_ARCH "arm") 54 | elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") 55 | add_definitions(-D_ARM64_) 56 | set(MS_ARCH "arm64") 57 | endif() 58 | 59 | if(CMAKE_BUILD_TYPE STREQUAL "Debug") 60 | add_definitions(-D_DEBUG) 61 | endif() 62 | 63 | if(NOT MSVC) 64 | target_compile_options(winmd PUBLIC -U__NO_INLINE__) 65 | add_definitions(-D__USE_MINGW_ANSI_STDIO=0) 66 | add_definitions(-D__INTRINSIC_DEFINED_InterlockedBitTestAndSet) 67 | add_definitions(-D__INTRINSIC_DEFINED_InterlockedBitTestAndReset) 68 | endif() 69 | 70 | add_definitions(-DNTDDI_VERSION=0x0A000007) 71 | 72 | target_compile_definitions(winmd PUBLIC _KERNEL_MODE WIN9X_COMPAT_SPINLOCK _NO_CRT_STDIO_INLINE) 73 | 74 | if(MSVC) 75 | if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86") 76 | target_compile_options(winmd PUBLIC /Gz) # stdcall 77 | endif() 78 | 79 | target_link_libraries(winmd "$ENV{WindowsSdkDir}Lib\\$ENV{WindowsSDKLibVersion}km\\${MS_ARCH}\\ntoskrnl.lib") 80 | target_link_libraries(winmd "$ENV{WindowsSdkDir}Lib\\$ENV{WindowsSDKLibVersion}km\\${MS_ARCH}\\hal.lib") 81 | 82 | if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") 83 | target_link_libraries(winmd "$ENV{WindowsSdkDir}Lib\\$ENV{WindowsSDKLibVersion}km\\${MS_ARCH}\\bufferoverflowfastfailk.lib") 84 | else() 85 | target_link_libraries(winmd "$ENV{WindowsSdkDir}Lib\\$ENV{WindowsSDKLibVersion}km\\${MS_ARCH}\\BufferOverflowK.lib") 86 | endif() 87 | 88 | if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm") 89 | target_link_libraries(winmd "$ENV{WindowsSdkDir}Lib\\$ENV{WindowsSDKLibVersion}um\\${MS_ARCH}\\armrt.lib") 90 | elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") 91 | target_link_libraries(winmd "$ENV{WindowsSdkDir}Lib\\$ENV{WindowsSDKLibVersion}um\\${MS_ARCH}\\arm64rt.lib") 92 | endif() 93 | 94 | target_link_libraries(winmd "$ENV{WindowsSdkDir}Lib\\$ENV{WindowsSDKLibVersion}km\\${MS_ARCH}\\rtlver.lib") 95 | target_link_options(winmd PUBLIC /SUBSYSTEM:NATIVE /NODEFAULTLIB /MANIFEST:NO /Driver /ENTRY:DriverEntry) 96 | 97 | # strip out flags for MSVC's runtime checks 98 | string(REGEX REPLACE "/RTC(su|[1su])" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") 99 | string(REGEX REPLACE "/RTC(su|[1su])" "" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}") 100 | else() 101 | target_compile_options(winmd PUBLIC -Wall -Wno-expansion-to-defined -Wunused-parameter -Wtype-limits -Wextra -Wno-unknown-pragmas -msse4.2) 102 | 103 | if (CMAKE_C_COMPILER_ID STREQUAL "GNU") 104 | target_compile_options(winmd PUBLIC -Werror=cast-function-type) 105 | elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang") 106 | target_compile_options(winmd PUBLIC -Wno-pragma-pack) # ignore warning in mingw headers 107 | endif() 108 | 109 | target_link_libraries(winmd ntoskrnl hal gcc) 110 | target_link_options(winmd PUBLIC -nostdlib -Wl,--subsystem,native -Wl,--file-alignment,0x1000 -Wl,--section-alignment,0x1000 -Wl,--exclude-all-symbols) 111 | 112 | if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86") 113 | target_link_options(winmd PUBLIC -Wl,--entry,_DriverEntry@8) 114 | else() 115 | target_link_options(winmd PUBLIC -Wl,--entry,DriverEntry) 116 | endif() 117 | endif() 118 | 119 | set_target_properties(winmd PROPERTIES PREFIX "") 120 | set_target_properties(winmd PROPERTIES SUFFIX ".sys") 121 | 122 | # -------------------------------------- 123 | 124 | # install 125 | 126 | install(TARGETS winmd DESTINATION bin) 127 | -------------------------------------------------------------------------------- /src/mountmgr.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Mark Harmstone 2019 2 | * 3 | * This file is part of WinMD. 4 | * 5 | * WinMD is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public Licence as published by 7 | * the Free Software Foundation, either version 3 of the Licence, or 8 | * (at your option) any later version. 9 | * 10 | * WinBtrfs is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Lesser General Public Licence for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public Licence 16 | * along with WinMD. If not, see . */ 17 | 18 | #include "winmd.h" 19 | #include 20 | #include 21 | #include 22 | 23 | static const WCHAR drive_letter_prefix[] = L"\\DosDevices\\"; 24 | 25 | static char get_drive_letter2(const MOUNTMGR_MOUNT_POINTS* points) { 26 | TRACE("%u points\n", points->NumberOfMountPoints); 27 | 28 | for (ULONG i = 0; i < points->NumberOfMountPoints; i++) { 29 | const MOUNTMGR_MOUNT_POINT* mmp = &points->MountPoints[i]; 30 | WCHAR* symlink = (WCHAR*)((uint8_t*)points + mmp->SymbolicLinkNameOffset); 31 | 32 | TRACE("point %u\n", i); 33 | TRACE("symbolic link %.*S\n", mmp->SymbolicLinkNameLength / sizeof(WCHAR), symlink); 34 | 35 | if (mmp->SymbolicLinkNameLength == sizeof(drive_letter_prefix) + sizeof(WCHAR) && 36 | RtlCompareMemory(symlink, drive_letter_prefix, sizeof(drive_letter_prefix) - sizeof(WCHAR)) == sizeof(drive_letter_prefix) - sizeof(WCHAR) && 37 | symlink[sizeof(drive_letter_prefix) / sizeof(WCHAR)] == ':') 38 | return (char)symlink[(sizeof(drive_letter_prefix) / sizeof(WCHAR)) - 1]; 39 | } 40 | 41 | return 0; 42 | } 43 | 44 | char get_drive_letter(HANDLE h, const UNICODE_STRING* name) { 45 | NTSTATUS Status; 46 | USHORT mmmp_len = sizeof(MOUNTMGR_MOUNT_POINT) + name->Length; 47 | char ret; 48 | 49 | MOUNTMGR_MOUNT_POINT* mmmp = ExAllocatePoolWithTag(NonPagedPool, mmmp_len, ALLOC_TAG); 50 | 51 | if (!mmmp) { 52 | ERR("out of memory\n"); 53 | return 0; 54 | } 55 | 56 | RtlZeroMemory(mmmp, mmmp_len); 57 | 58 | mmmp->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT); 59 | mmmp->DeviceNameLength = name->Length; 60 | 61 | RtlCopyMemory((uint8_t*)mmmp + mmmp->DeviceNameOffset, name->Buffer, name->Length); 62 | 63 | MOUNTMGR_MOUNT_POINTS points; 64 | IO_STATUS_BLOCK iosb; 65 | 66 | Status = NtDeviceIoControlFile(h, NULL, NULL, NULL, &iosb, IOCTL_MOUNTMGR_QUERY_POINTS, 67 | mmmp, mmmp_len, &points, sizeof(points)); 68 | 69 | if (Status == STATUS_BUFFER_OVERFLOW && points.Size > 0) { 70 | MOUNTMGR_MOUNT_POINTS* points2 = ExAllocatePoolWithTag(NonPagedPool, points.Size, ALLOC_TAG); 71 | 72 | if (!points2) { 73 | ERR("out of memory\n"); 74 | ret = 0; 75 | goto end; 76 | } 77 | 78 | Status = NtDeviceIoControlFile(h, NULL, NULL, NULL, &iosb, IOCTL_MOUNTMGR_QUERY_POINTS, 79 | mmmp, mmmp_len, points2, points.Size); 80 | 81 | if (!NT_SUCCESS(Status)) { 82 | ERR("IOCTL_MOUNTMGR_QUERY_POINTS returned %08x\n", Status); 83 | ExFreePool(points2); 84 | ret = 0; 85 | goto end; 86 | } 87 | 88 | ret = get_drive_letter2(points2); 89 | 90 | ExFreePool(points2); 91 | } else if (!NT_SUCCESS(Status)) { 92 | ERR("IOCTL_MOUNTMGR_QUERY_POINTS returned %08x\n", Status); 93 | ret = 0; 94 | } else 95 | ret = get_drive_letter2(&points); 96 | 97 | end: 98 | ExFreePool(mmmp); 99 | 100 | return ret; 101 | } 102 | 103 | NTSTATUS remove_drive_letter(HANDLE h, char c) { 104 | NTSTATUS Status; 105 | USHORT mmmp_len = sizeof(MOUNTMGR_MOUNT_POINT) + sizeof(drive_letter_prefix) + sizeof(WCHAR); 106 | 107 | MOUNTMGR_MOUNT_POINT* mmmp = ExAllocatePoolWithTag(NonPagedPool, mmmp_len, ALLOC_TAG); 108 | 109 | if (!mmmp) { 110 | ERR("out of memory\n"); 111 | return STATUS_INSUFFICIENT_RESOURCES; 112 | } 113 | 114 | RtlZeroMemory(mmmp, mmmp_len); 115 | 116 | mmmp->SymbolicLinkNameOffset = sizeof(MOUNTMGR_MOUNT_POINT); 117 | mmmp->SymbolicLinkNameLength = sizeof(drive_letter_prefix) + sizeof(WCHAR); 118 | 119 | WCHAR* symlink = (WCHAR*)((uint8_t*)mmmp + mmmp->SymbolicLinkNameOffset); 120 | 121 | RtlCopyMemory(symlink, drive_letter_prefix, sizeof(drive_letter_prefix) - sizeof(WCHAR)); 122 | symlink[(sizeof(drive_letter_prefix) / sizeof(WCHAR)) - 1] = c; 123 | symlink[sizeof(drive_letter_prefix) / sizeof(WCHAR)] = ':'; 124 | 125 | MOUNTMGR_MOUNT_POINTS points; 126 | IO_STATUS_BLOCK iosb; 127 | 128 | Status = NtDeviceIoControlFile(h, NULL, NULL, NULL, &iosb, IOCTL_MOUNTMGR_DELETE_POINTS, 129 | mmmp, mmmp_len, &points, sizeof(points)); 130 | 131 | if (Status == STATUS_BUFFER_OVERFLOW && points.Size > 0) { 132 | MOUNTMGR_MOUNT_POINTS* points2 = ExAllocatePoolWithTag(NonPagedPool, points.Size, ALLOC_TAG); 133 | if (!points2) { 134 | ERR("out of memory\n"); 135 | ExFreePool(mmmp); 136 | return STATUS_INSUFFICIENT_RESOURCES; 137 | } 138 | 139 | Status = NtDeviceIoControlFile(h, NULL, NULL, NULL, &iosb, IOCTL_MOUNTMGR_DELETE_POINTS, 140 | mmmp, mmmp_len, points2, points.Size); 141 | 142 | ExFreePool(points2); 143 | } 144 | 145 | if (!NT_SUCCESS(Status)) 146 | ERR("IOCTL_MOUNTMGR_DELETE_POINTS returned %08x\n", Status); 147 | 148 | ExFreePool(mmmp); 149 | 150 | return Status; 151 | } 152 | -------------------------------------------------------------------------------- /src/logger.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Mark Harmstone 2019 2 | * 3 | * This file is part of WinMD. 4 | * 5 | * WinMD is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public Licence as published by 7 | * the Free Software Foundation, either version 3 of the Licence, or 8 | * (at your option) any later version. 9 | * 10 | * WinBtrfs is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Lesser General Public Licence for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public Licence 16 | * along with WinMD. If not, see . */ 17 | 18 | #include "winmd.h" 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #ifdef _DEBUG 25 | typedef struct { 26 | KEVENT Event; 27 | IO_STATUS_BLOCK iosb; 28 | } logger_context; 29 | 30 | static const WCHAR log_device[] = L"\\Device\\Serial0"; 31 | 32 | _Function_class_(KSTART_ROUTINE) 33 | static void __stdcall serial_thread(void* context) { 34 | LARGE_INTEGER due_time; 35 | KTIMER timer; 36 | 37 | KeInitializeTimer(&timer); 38 | 39 | due_time.QuadPart = (UINT64)-10000000; 40 | 41 | KeSetTimer(&timer, due_time, NULL); 42 | 43 | while (true) { 44 | KeWaitForSingleObject(&timer, Executive, KernelMode, false, NULL); 45 | 46 | { 47 | UNICODE_STRING us; 48 | 49 | us.Buffer = (WCHAR*)log_device; 50 | us.Length = us.MaximumLength = sizeof(log_device) - sizeof(WCHAR); 51 | 52 | NTSTATUS Status = IoGetDeviceObjectPointer(&us, FILE_WRITE_DATA, &logger->comfo, &logger->comdo); 53 | if (!NT_SUCCESS(Status)) 54 | ERR("IoGetDeviceObjectPointer returned %08x\n", Status); 55 | } 56 | 57 | if (logger->comdo) 58 | break; 59 | 60 | KeSetTimer(&timer, due_time, NULL); 61 | } 62 | 63 | KeCancelTimer(&timer); 64 | 65 | logger->serial_thread_handle = NULL; 66 | 67 | PsTerminateSystemThread(STATUS_SUCCESS); 68 | } 69 | 70 | void init_serial_logger() { 71 | NTSTATUS Status; 72 | UNICODE_STRING us; 73 | 74 | logger->unloading = false; 75 | logger->serial_thread_handle = NULL; 76 | 77 | ExInitializeResourceLite(&logger->log_lock); 78 | 79 | us.Buffer = (WCHAR*)log_device; 80 | us.Length = us.MaximumLength = sizeof(log_device) - sizeof(WCHAR); 81 | 82 | Status = IoGetDeviceObjectPointer(&us, FILE_WRITE_DATA, &logger->comfo, &logger->comdo); 83 | if (!NT_SUCCESS(Status)) { 84 | ERR("IoGetDeviceObjectPointer returned %08x\n", Status); 85 | 86 | Status = PsCreateSystemThread(&logger->serial_thread_handle, 0, NULL, NULL, NULL, serial_thread, logger); 87 | 88 | if (!NT_SUCCESS(Status)) { 89 | ERR("PsCreateSystemThread returned %08x\n", Status); 90 | return; 91 | } 92 | } 93 | } 94 | 95 | void stop_serial_logger() { 96 | logger->unloading = true; 97 | 98 | // sync 99 | ExAcquireResourceExclusiveLite(&logger->log_lock, TRUE); 100 | ExReleaseResourceLite(&logger->log_lock); 101 | 102 | if (logger->comfo) 103 | ObDereferenceObject(logger->comfo); 104 | 105 | if (logger->serial_thread_handle) 106 | NtClose(logger->serial_thread_handle); 107 | 108 | ExDeleteResourceLite(&logger->log_lock); 109 | } 110 | 111 | _Function_class_(IO_COMPLETION_ROUTINE) 112 | static NTSTATUS __stdcall dbg_completion(PDEVICE_OBJECT devobj, PIRP Irp, PVOID ctx) { 113 | logger_context* context = ctx; 114 | 115 | context->iosb = Irp->IoStatus; 116 | KeSetEvent(&context->Event, 0, FALSE); 117 | 118 | return STATUS_MORE_PROCESSING_REQUIRED; 119 | } 120 | 121 | void do_log(const char* func, const char* msg, ...) { 122 | NTSTATUS Status; 123 | PIRP Irp; 124 | PIO_STACK_LOCATION IrpSp; 125 | 126 | static const size_t buf_size = 1024; 127 | char* buf2 = ExAllocatePoolWithTag(NonPagedPool, buf_size, ALLOC_TAG); 128 | 129 | if (!buf2) { 130 | DbgPrint("Couldn't allocate buffer in debug_message\n"); 131 | return; 132 | } 133 | 134 | sprintf(buf2, "%p:%s:", PsGetCurrentThread(), func); 135 | size_t prefix_size = strlen(buf2); 136 | char* buf = &buf2[prefix_size]; 137 | 138 | va_list ap; 139 | va_start(ap, msg); 140 | 141 | RtlStringCbVPrintfA(buf, buf_size - strlen(buf2), msg, ap); 142 | 143 | if (logger->unloading || !logger->comfo) { 144 | DbgPrint(buf2); 145 | 146 | va_end(ap); 147 | 148 | ExFreePool(buf2); 149 | 150 | return; 151 | } 152 | 153 | ExAcquireResourceSharedLite(&logger->log_lock, true); 154 | 155 | uint32_t length = (uint32_t)strlen(buf2); 156 | 157 | LARGE_INTEGER offset; 158 | offset.u.LowPart = 0; 159 | offset.u.HighPart = 0; 160 | 161 | logger_context* context = ExAllocatePoolWithTag(NonPagedPool, sizeof(logger_context), ALLOC_TAG); 162 | if (!context) { 163 | DbgPrint("out of memory\n"); 164 | goto exit2; 165 | } 166 | 167 | RtlZeroMemory(context, sizeof(logger_context)); 168 | 169 | KeInitializeEvent(&context->Event, NotificationEvent, false); 170 | 171 | Irp = IoAllocateIrp(logger->comdo->StackSize, false); 172 | 173 | if (!Irp) { 174 | DbgPrint("IoAllocateIrp failed\n"); 175 | ExFreePool(context); 176 | goto exit2; 177 | } 178 | 179 | IrpSp = IoGetNextIrpStackLocation(Irp); 180 | IrpSp->MajorFunction = IRP_MJ_WRITE; 181 | 182 | if (logger->comdo->Flags & DO_BUFFERED_IO) { 183 | Irp->AssociatedIrp.SystemBuffer = (void*)buf2; 184 | 185 | Irp->Flags = IRP_BUFFERED_IO; 186 | } else if (logger->comdo->Flags & DO_DIRECT_IO) { 187 | Irp->MdlAddress = IoAllocateMdl((void*)buf2, length, false, false, NULL); 188 | if (!Irp->MdlAddress) { 189 | DbgPrint("IoAllocateMdl failed\n"); 190 | goto exit; 191 | } 192 | 193 | MmBuildMdlForNonPagedPool(Irp->MdlAddress); 194 | } else 195 | Irp->UserBuffer = (void*)buf2; 196 | 197 | IrpSp->Parameters.Write.Length = length; 198 | IrpSp->Parameters.Write.ByteOffset = offset; 199 | 200 | Irp->UserIosb = &context->iosb; 201 | 202 | Irp->UserEvent = &context->Event; 203 | 204 | IoSetCompletionRoutine(Irp, dbg_completion, context, TRUE, TRUE, TRUE); 205 | 206 | Status = IoCallDriver(logger->comdo, Irp); 207 | 208 | if (Status == STATUS_PENDING) { 209 | LARGE_INTEGER timeout; 210 | 211 | timeout.QuadPart = -10000000ll; // 1 second 212 | 213 | KeWaitForSingleObject(&context->Event, Executive, KernelMode, false, &timeout); 214 | Status = context->iosb.Status; 215 | } 216 | 217 | if (logger->comdo->Flags & DO_DIRECT_IO) 218 | IoFreeMdl(Irp->MdlAddress); 219 | 220 | if (!NT_SUCCESS(Status)) { 221 | DbgPrint("failed to write to COM1 - error %08x\n", Status); 222 | goto exit; 223 | } 224 | 225 | exit: 226 | IoFreeIrp(Irp); 227 | ExFreePool(context); 228 | 229 | exit2: 230 | ExReleaseResourceLite(&logger->log_lock); 231 | 232 | va_end(ap); 233 | 234 | if (buf2) 235 | ExFreePool(buf2); 236 | } 237 | #endif 238 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /src/linear.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Mark Harmstone 2019 2 | * 3 | * This file is part of WinMD. 4 | * 5 | * WinMD is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public Licence as published by 7 | * the Free Software Foundation, either version 3 of the Licence, or 8 | * (at your option) any later version. 9 | * 10 | * WinBtrfs is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Lesser General Public Licence for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public Licence 16 | * along with WinMD. If not, see . */ 17 | 18 | #include "winmd.h" 19 | 20 | typedef struct { 21 | PIRP Irp; 22 | KEVENT Event; 23 | IO_STATUS_BLOCK iosb; 24 | NTSTATUS Status; 25 | PMDL mdl; 26 | LIST_ENTRY list_entry; 27 | } io_context_linear; 28 | 29 | _Function_class_(IO_COMPLETION_ROUTINE) 30 | static NTSTATUS __stdcall io_completion_linear(PDEVICE_OBJECT devobj, PIRP Irp, PVOID ctx) { 31 | io_context_linear* context = ctx; 32 | 33 | context->iosb = Irp->IoStatus; 34 | KeSetEvent(&context->Event, 0, FALSE); 35 | 36 | return STATUS_MORE_PROCESSING_REQUIRED; 37 | } 38 | 39 | static NTSTATUS io_linear2(set_pdo* pdo, PIRP Irp, uint64_t offset, uint32_t start_disk, bool write) { 40 | NTSTATUS Status; 41 | PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 42 | uint32_t length = write ? IrpSp->Parameters.Write.Length : IrpSp->Parameters.Read.Length; 43 | LIST_ENTRY ctxs; 44 | uint8_t* va = (uint8_t*)MmGetMdlVirtualAddress(Irp->MdlAddress); 45 | 46 | InitializeListHead(&ctxs); 47 | 48 | for (uint32_t i = start_disk; i < pdo->array_info.raid_disks; i++) { 49 | uint32_t io_length = (uint32_t)min(length, (pdo->child_list[i]->disk_info.data_size * 512) - offset); 50 | 51 | io_context_linear* last = ExAllocatePoolWithTag(NonPagedPool, sizeof(io_context_linear), ALLOC_TAG); 52 | if (!last) { 53 | Status = STATUS_INSUFFICIENT_RESOURCES; 54 | goto fail; 55 | } 56 | 57 | last->Irp = IoAllocateIrp(pdo->child_list[i]->device->StackSize, false); 58 | if (!last->Irp) { 59 | ERR("out of memory\n"); 60 | Status = STATUS_INSUFFICIENT_RESOURCES; 61 | ExFreePool(last); 62 | goto fail; 63 | } 64 | 65 | last->Irp->UserIosb = &last->iosb; 66 | 67 | KeInitializeEvent(&last->Event, NotificationEvent, false); 68 | last->Irp->UserEvent = &last->Event; 69 | 70 | IoSetCompletionRoutine(last->Irp, io_completion_linear, last, true, true, true); 71 | 72 | last->Status = STATUS_SUCCESS; 73 | 74 | last->mdl = NULL; 75 | 76 | InsertTailList(&ctxs, &last->list_entry); 77 | 78 | if (!NT_SUCCESS(last->Status)) { 79 | ERR("io_context_linear constructor returned %08x\n", last->Status); 80 | Status = last->Status; 81 | goto fail; 82 | } 83 | 84 | last->mdl = IoAllocateMdl(va, io_length, false, false, NULL); 85 | if (!last->mdl) { 86 | ERR("IoAllocateMdl failed\n"); 87 | Status = STATUS_INSUFFICIENT_RESOURCES; 88 | goto fail; 89 | } 90 | 91 | last->Irp->MdlAddress = last->mdl; 92 | 93 | IoBuildPartialMdl(Irp->MdlAddress, last->mdl, va, io_length); 94 | 95 | PIO_STACK_LOCATION IrpSp2 = IoGetNextIrpStackLocation(last->Irp); 96 | 97 | IrpSp2->FileObject = pdo->child_list[i]->fileobj; 98 | 99 | if (write) { 100 | IrpSp2->MajorFunction = IRP_MJ_WRITE; 101 | IrpSp2->Parameters.Write.ByteOffset.QuadPart = offset + (pdo->child_list[i]->disk_info.data_offset * 512); 102 | IrpSp2->Parameters.Write.Length = io_length; 103 | } else { 104 | IrpSp2->MajorFunction = IRP_MJ_READ; 105 | IrpSp2->Parameters.Read.ByteOffset.QuadPart = offset + (pdo->child_list[i]->disk_info.data_offset * 512); 106 | IrpSp2->Parameters.Read.Length = io_length; 107 | } 108 | 109 | last->Status = IoCallDriver(pdo->child_list[i]->device, last->Irp); 110 | 111 | length -= io_length; 112 | 113 | if (length == 0) 114 | break; 115 | 116 | offset = 0; 117 | va += io_length; 118 | } 119 | 120 | Status = STATUS_SUCCESS; 121 | 122 | while (!IsListEmpty(&ctxs)) { 123 | io_context_linear* ctx = CONTAINING_RECORD(RemoveHeadList(&ctxs), io_context_linear, list_entry); 124 | 125 | if (ctx->Status == STATUS_PENDING) { 126 | KeWaitForSingleObject(&ctx->Event, Executive, KernelMode, false, NULL); 127 | ctx->Status = ctx->iosb.Status; 128 | } 129 | 130 | if (!NT_SUCCESS(ctx->Status)) { 131 | ERR("device returned %08x\n", ctx->Status); 132 | Status = ctx->Status; 133 | } 134 | 135 | if (ctx->mdl) 136 | IoFreeMdl(ctx->mdl); 137 | 138 | if (ctx->Irp) 139 | IoFreeIrp(ctx->Irp); 140 | 141 | ExFreePool(ctx); 142 | } 143 | 144 | return Status; 145 | 146 | fail: 147 | while (!IsListEmpty(&ctxs)) { 148 | io_context_linear* ctx = CONTAINING_RECORD(RemoveHeadList(&ctxs), io_context_linear, list_entry); 149 | 150 | if (ctx->mdl) 151 | IoFreeMdl(ctx->mdl); 152 | 153 | if (ctx->Irp) 154 | IoFreeIrp(ctx->Irp); 155 | 156 | ExFreePool(ctx); 157 | } 158 | 159 | return Status; 160 | } 161 | 162 | NTSTATUS read_linear(set_pdo* pdo, PIRP Irp, bool* no_complete) { 163 | PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 164 | uint64_t offset = IrpSp->Parameters.Read.ByteOffset.QuadPart; 165 | uint32_t length = IrpSp->Parameters.Read.Length; 166 | 167 | ExAcquireResourceSharedLite(&pdo->lock, true); 168 | 169 | for (uint32_t i = 0; i < pdo->array_info.raid_disks; i++) { 170 | if (offset < (pdo->child_list[i]->disk_info.data_size * 512)) { 171 | NTSTATUS Status; 172 | 173 | if (offset + length < (pdo->child_list[i]->disk_info.data_size * 512) || i == pdo->array_info.raid_disks - 1) { 174 | set_child* c = pdo->child_list[i]; 175 | 176 | IoCopyCurrentIrpStackLocationToNext(Irp); 177 | 178 | PIO_STACK_LOCATION IrpSp2 = IoGetNextIrpStackLocation(Irp); 179 | 180 | IrpSp2->FileObject = pdo->child_list[i]->fileobj; 181 | IrpSp2->Parameters.Read.ByteOffset.QuadPart = offset + (c->disk_info.data_offset * 512); 182 | 183 | if (i == pdo->array_info.raid_disks - 1) 184 | IrpSp2->Parameters.Read.Length = (uint32_t)min(IrpSp2->Parameters.Read.Length, ((pdo->child_list[i]->disk_info.data_size * 512) - offset)); 185 | 186 | *no_complete = true; 187 | 188 | Status = IoCallDriver(c->device, Irp); 189 | } else 190 | Status = io_linear2(pdo, Irp, offset, i, false); 191 | 192 | ExReleaseResourceLite(&pdo->lock); 193 | 194 | return Status; 195 | } 196 | 197 | offset -= pdo->child_list[i]->disk_info.data_size * 512; 198 | } 199 | 200 | ExReleaseResourceLite(&pdo->lock); 201 | 202 | return STATUS_INVALID_PARAMETER; 203 | } 204 | 205 | NTSTATUS write_linear(set_pdo* pdo, PIRP Irp, bool* no_complete) { 206 | PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 207 | uint64_t offset = IrpSp->Parameters.Write.ByteOffset.QuadPart; 208 | uint32_t length = IrpSp->Parameters.Write.Length; 209 | 210 | for (uint32_t i = 0; i < pdo->array_info.raid_disks; i++) { 211 | if (offset < (pdo->child_list[i]->disk_info.data_size * 512)) { 212 | if (offset + length < (pdo->child_list[i]->disk_info.data_size * 512) || i == pdo->array_info.raid_disks - 1) { 213 | set_child* c = pdo->child_list[i]; 214 | 215 | IoCopyCurrentIrpStackLocationToNext(Irp); 216 | 217 | PIO_STACK_LOCATION IrpSp2 = IoGetNextIrpStackLocation(Irp); 218 | 219 | IrpSp2->FileObject = pdo->child_list[i]->fileobj; 220 | IrpSp2->Parameters.Write.ByteOffset.QuadPart = offset + (c->disk_info.data_offset * 512); 221 | 222 | if (i == pdo->array_info.raid_disks - 1) 223 | IrpSp2->Parameters.Write.Length = (uint32_t)min(IrpSp2->Parameters.Write.Length, ((pdo->child_list[i]->disk_info.data_size * 512) - offset)); 224 | 225 | *no_complete = true; 226 | 227 | return IoCallDriver(c->device, Irp); 228 | } else 229 | return io_linear2(pdo, Irp, offset, i, true); 230 | } 231 | 232 | offset -= pdo->child_list[i]->disk_info.data_size * 512; 233 | } 234 | 235 | return STATUS_INVALID_PARAMETER; 236 | } 237 | -------------------------------------------------------------------------------- /src/winmd.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Mark Harmstone 2019 2 | * 3 | * This file is part of WinMD. 4 | * 5 | * WinMD is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public Licence as published by 7 | * the Free Software Foundation, either version 3 of the Licence, or 8 | * (at your option) any later version. 9 | * 10 | * WinBtrfs is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Lesser General Public Licence for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public Licence 16 | * along with WinMD. If not, see . */ 17 | 18 | #pragma once 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #define DEBUG_PARANOID 27 | 28 | #define ALLOC_TAG 0x6472444d // 'MDrd' 29 | 30 | static const char16_t device_prefix[] = u"\\Device\\WinMD{"; 31 | 32 | #ifdef _DEBUG 33 | typedef struct { 34 | PFILE_OBJECT comfo; 35 | PDEVICE_OBJECT comdo; 36 | ERESOURCE log_lock; 37 | bool unloading; 38 | HANDLE serial_thread_handle; 39 | } serial_logger; 40 | 41 | extern serial_logger* logger; 42 | 43 | // logger.cpp 44 | void do_log(const char* func, const char* msg, ...); 45 | void init_serial_logger(); 46 | void stop_serial_logger(); 47 | #endif 48 | 49 | #ifdef _MSC_VER 50 | #define funcname __FUNCTION__ 51 | #else 52 | #define funcname __func__ 53 | #endif 54 | 55 | #ifndef min 56 | #define min(a, b) (((a)<(b)) ? (a) : (b)) 57 | #endif 58 | 59 | #ifndef max 60 | #define max(a, b) (((a)>(b)) ? (a) : (b)) 61 | #endif 62 | 63 | #ifndef FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL 64 | #define FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL 0x00020000 65 | #endif 66 | 67 | #ifndef _MSC_VER 68 | #define _Dispatch_type_(a) 69 | #endif 70 | 71 | extern uint32_t debug_log_level; 72 | extern bool have_sse2; 73 | 74 | #ifdef _DEBUG 75 | #define ERR(s, ...) do { if (logger && debug_log_level > 0) { do_log(funcname, s, ##__VA_ARGS__); } } while (0); 76 | #define FIXME(s, ...) do { if (logger && debug_log_level > 0) { do_log(funcname, s, ##__VA_ARGS__); } } while (0); 77 | #define WARN(s, ...) do { if (logger && debug_log_level > 1) { do_log(funcname, s, ##__VA_ARGS__); } } while (0); 78 | #define TRACE(s, ...) do { if (logger && debug_log_level > 2) { do_log(funcname, s, ##__VA_ARGS__); } } while (0); 79 | #else 80 | #define ERR(s, ...) do { } while (0); 81 | #define FIXME(s, ...) do { } while (0); 82 | #define WARN(s, ...) do { } while (0); 83 | #define TRACE(s, ...) do { } while (0); 84 | #endif 85 | 86 | #if defined(_MSC_VER) || defined(__clang__) 87 | #define try __try 88 | #define except __except 89 | #define finally __finally 90 | #define leave __leave 91 | #else 92 | #define try if (1) 93 | #define except(x) if (0 && (x)) 94 | #define finally if (1) 95 | #define leave 96 | #endif 97 | 98 | enum device_type { 99 | device_type_control, 100 | device_type_set, 101 | device_type_pdo 102 | }; 103 | 104 | typedef struct { 105 | enum device_type type; 106 | PDEVICE_OBJECT buspdo; 107 | PDEVICE_OBJECT attached_device; 108 | UNICODE_STRING bus_name; 109 | } control_device; 110 | 111 | __inline static uint64_t sector_align64(uint64_t n, uint64_t a) { 112 | if (n & (a - 1)) 113 | n = (n + a) & ~(a - 1); 114 | 115 | return n; 116 | } 117 | 118 | __inline static uint32_t sector_align32(uint32_t n, uint32_t a) { 119 | if (n & (a - 1)) 120 | n = (n + a) & ~(a - 1); 121 | 122 | return n; 123 | } 124 | 125 | #define RAID_12_OFFSET 0x1000 126 | 127 | #define RAID_MAGIC 0xa92b4efc 128 | 129 | #define RAID_LEVEL_MULTI_PATH 0xfffffffc 130 | #define RAID_LEVEL_LINEAR 0xffffffff 131 | #define RAID_LEVEL_0 0 132 | #define RAID_LEVEL_1 1 133 | #define RAID_LEVEL_4 4 134 | #define RAID_LEVEL_5 5 135 | #define RAID_LEVEL_6 6 136 | #define RAID_LEVEL_10 10 137 | 138 | #define RAID_LAYOUT_LEFT_ASYMMETRIC 0 139 | #define RAID_LAYOUT_RIGHT_ASYMMETRIC 1 140 | #define RAID_LAYOUT_LEFT_SYMMETRIC 2 141 | #define RAID_LAYOUT_RIGHT_SYMMETRIC 3 142 | 143 | #pragma pack(push,1) 144 | 145 | typedef struct { 146 | uint64_t data_offset; 147 | uint64_t data_size; 148 | uint64_t super_offset; 149 | uint64_t recovery_offset; 150 | uint32_t dev_number; 151 | uint32_t cnt_correct_read; 152 | uint8_t device_uuid[16]; 153 | uint8_t devflags; 154 | } mdraid_disk_info; 155 | 156 | typedef struct { 157 | uint8_t set_uuid[16]; 158 | char set_name[32]; 159 | uint64_t ctime; 160 | uint32_t level; 161 | uint32_t layout; 162 | uint64_t size; 163 | uint32_t chunksize; 164 | uint32_t raid_disks; 165 | uint32_t bitmap_offset; 166 | } mdraid_array_info; 167 | 168 | typedef struct { 169 | uint64_t utime; 170 | uint64_t events; 171 | uint64_t resync_offset; 172 | uint32_t sb_csum; 173 | uint32_t max_dev; 174 | } mdraid_array_state; 175 | 176 | typedef struct { 177 | uint16_t dev_roles[256]; // FIXME - is this the right maximum size? 178 | } mdraid_roles; 179 | 180 | typedef struct { 181 | uint32_t magic; 182 | uint32_t major_version; 183 | uint32_t feature_map; 184 | uint32_t pad0; 185 | mdraid_array_info array_info; 186 | uint32_t new_level; 187 | uint64_t reshape_position; 188 | uint32_t delta_disks; 189 | uint32_t new_layout; 190 | uint32_t new_chunk; 191 | uint32_t pad1; 192 | mdraid_disk_info disk_info; 193 | uint8_t pad2[7]; 194 | mdraid_array_state array_state; 195 | uint8_t pad3[32]; 196 | mdraid_roles roles; 197 | } mdraid_superblock; 198 | 199 | #pragma pack(pop) 200 | 201 | typedef struct { 202 | PDEVICE_OBJECT device; 203 | PFILE_OBJECT fileobj; 204 | mdraid_disk_info disk_info; 205 | UNICODE_STRING devpath; 206 | LIST_ENTRY list_entry; 207 | } set_child; 208 | 209 | typedef struct { 210 | LIST_ENTRY list_entry; 211 | uint64_t offset; 212 | RTL_BITMAP bmp; 213 | #ifdef _MSC_VER 214 | __declspec(align(16)) uint8_t data[1]; 215 | #else 216 | _Alignas(16) uint8_t data[1]; 217 | #endif 218 | } partial_chunk; 219 | 220 | typedef struct _set_pdo set_pdo; 221 | 222 | typedef struct { 223 | enum device_type type; 224 | set_pdo* pdo; 225 | PDEVICE_OBJECT devobj; 226 | PDEVICE_OBJECT attached_device; 227 | LONG open_count; 228 | ERESOURCE lock; 229 | } set_device; 230 | 231 | typedef struct _set_pdo { 232 | enum device_type type; 233 | ERESOURCE lock; 234 | mdraid_array_info array_info; 235 | mdraid_array_state array_state; 236 | mdraid_roles roles; 237 | uint64_t array_size; 238 | set_child** child_list; 239 | LONG read_device; 240 | ULONG found_devices; 241 | bool loaded; 242 | PDEVICE_OBJECT pdo; 243 | set_device* dev; 244 | uint8_t stack_size; 245 | uint16_t dev_sector_size; 246 | LIST_ENTRY children; 247 | ERESOURCE partial_chunks_lock; 248 | LIST_ENTRY partial_chunks; 249 | LIST_ENTRY list_entry; 250 | HANDLE flush_thread_handle; 251 | KTIMER flush_thread_timer; 252 | KEVENT flush_thread_finished; 253 | bool readonly; 254 | UNICODE_STRING bus_name; 255 | } set_pdo; 256 | 257 | static __inline void get_raid0_offset(uint64_t off, uint64_t stripe_length, uint32_t num_stripes, uint64_t* stripeoff, uint32_t* stripe) { 258 | uint64_t initoff, startoff; 259 | 260 | startoff = off % (num_stripes * stripe_length); 261 | initoff = (off / (num_stripes * stripe_length)) * stripe_length; 262 | 263 | *stripe = (uint32_t)(startoff / stripe_length); 264 | *stripeoff = initoff + startoff - (*stripe * stripe_length); 265 | } 266 | 267 | // winmd.cpp 268 | bool is_top_level(PIRP Irp); 269 | 270 | // io.cpp 271 | _Dispatch_type_(IRP_MJ_READ) 272 | _Function_class_(DRIVER_DISPATCH) 273 | NTSTATUS drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp); 274 | 275 | _Dispatch_type_(IRP_MJ_WRITE) 276 | _Function_class_(DRIVER_DISPATCH) 277 | NTSTATUS drv_write(PDEVICE_OBJECT DeviceObject, PIRP Irp); 278 | 279 | _Function_class_(KSTART_ROUTINE) 280 | void __stdcall flush_thread(void* context); 281 | 282 | void do_xor(uint8_t* buf1, uint8_t* buf2, uint32_t len); 283 | NTSTATUS add_partial_chunk(set_pdo* pdo, uint64_t offset, uint32_t length, void* data); 284 | void flush_chunks(set_pdo* pdo); 285 | uint32_t get_parity_volume(set_pdo* pdo, uint64_t offset); 286 | uint32_t get_physical_stripe(set_pdo* pdo, uint32_t stripe, uint32_t parity); 287 | 288 | // pnp.cpp 289 | _Dispatch_type_(IRP_MJ_PNP) 290 | _Function_class_(DRIVER_DISPATCH) 291 | NTSTATUS drv_pnp(PDEVICE_OBJECT DeviceObject, PIRP Irp); 292 | 293 | _Function_class_(DRIVER_ADD_DEVICE) 294 | NTSTATUS __stdcall AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT PhysicalDeviceObject); 295 | 296 | // raid0.cpp 297 | NTSTATUS read_raid0(set_pdo* pdo, PIRP Irp, bool* no_complete); 298 | NTSTATUS write_raid0(set_pdo* pdo, PIRP Irp, bool* no_complete); 299 | 300 | // raid1.cpp 301 | NTSTATUS read_raid1(set_pdo* pdo, PIRP Irp, bool* no_complete); 302 | NTSTATUS write_raid1(set_pdo* pdo, PIRP Irp); 303 | 304 | // raid45.cpp 305 | NTSTATUS read_raid45(set_pdo* pdo, PIRP Irp, bool* no_complete); 306 | NTSTATUS write_raid45(set_pdo* pdo, PIRP Irp, bool* no_complete); 307 | NTSTATUS flush_partial_chunk_raid45(set_pdo* pdo, partial_chunk* pc, RTL_BITMAP* valid_bmp); 308 | 309 | // raid6.cpp 310 | NTSTATUS read_raid6(set_pdo* pdo, PIRP Irp, bool* no_complete); 311 | NTSTATUS write_raid6(set_pdo* pdo, PIRP Irp, bool* no_complete); 312 | NTSTATUS flush_partial_chunk_raid6(set_pdo* pdo, partial_chunk* pc, RTL_BITMAP* valid_bmp); 313 | 314 | // raid10.cpp 315 | NTSTATUS read_raid10(set_pdo* pdo, PIRP Irp, bool* no_complete); 316 | NTSTATUS write_raid10(set_pdo* pdo, PIRP Irp); 317 | 318 | // linear.cpp 319 | NTSTATUS read_linear(set_pdo* pdo, PIRP Irp, bool* no_complete); 320 | NTSTATUS write_linear(set_pdo* pdo, PIRP Irp, bool* no_complete); 321 | 322 | // mountmgr.cpp 323 | char get_drive_letter(HANDLE h, const UNICODE_STRING* name); 324 | NTSTATUS remove_drive_letter(HANDLE h, char c); 325 | -------------------------------------------------------------------------------- /src/pnp.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Mark Harmstone 2019 2 | * 3 | * This file is part of WinMD. 4 | * 5 | * WinMD is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public Licence as published by 7 | * the Free Software Foundation, either version 3 of the Licence, or 8 | * (at your option) any later version. 9 | * 10 | * WinBtrfs is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Lesser General Public Licence for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public Licence 16 | * along with WinMD. If not, see . */ 17 | 18 | #include "winmd.h" 19 | #include 20 | #include 21 | 22 | bool no_pnp = false; 23 | 24 | extern ERESOURCE dev_lock; 25 | extern LIST_ENTRY dev_list; 26 | extern PDRIVER_OBJECT drvobj; 27 | extern bool is_windows_8; 28 | 29 | static NTSTATUS set_query_hardware_ids(PIRP Irp) { 30 | static const char16_t ids[] = u"WinMDVolume\0"; 31 | 32 | char16_t* out = ExAllocatePoolWithTag(PagedPool, sizeof(ids), ALLOC_TAG); 33 | if (!out) { 34 | ERR("out of memory\n"); 35 | return STATUS_INSUFFICIENT_RESOURCES; 36 | } 37 | 38 | RtlCopyMemory(out, ids, sizeof(ids)); 39 | 40 | Irp->IoStatus.Information = (ULONG_PTR)out; 41 | 42 | return STATUS_SUCCESS; 43 | } 44 | 45 | static char16_t hex_digit(uint8_t c) { 46 | if (c < 10) 47 | return c + u'0'; 48 | 49 | return c - 10 + u'a'; 50 | } 51 | 52 | static NTSTATUS query_device_ids(mdraid_array_info* array_info, PIRP Irp) { 53 | char16_t name[100]; 54 | 55 | static const char16_t pref[] = u"WinMD\\"; 56 | 57 | RtlCopyMemory(name, pref, sizeof(pref) - sizeof(char16_t)); 58 | 59 | char16_t* noff = &name[(sizeof(pref) / sizeof(char16_t)) - 1]; 60 | for (unsigned int i = 0; i < 16; i++) { 61 | *noff = hex_digit(array_info->set_uuid[i] >> 4); noff++; 62 | *noff = hex_digit(array_info->set_uuid[i] & 0xf); noff++; 63 | 64 | if (i == 3 || i == 5 || i == 7 || i == 9) { 65 | *noff = '-'; 66 | noff++; 67 | } 68 | } 69 | *noff = 0; 70 | 71 | char16_t* out = ExAllocatePoolWithTag(PagedPool, sizeof(pref) + (36 * sizeof(char16_t)), ALLOC_TAG); 72 | if (!out) { 73 | ERR("out of memory\n"); 74 | return STATUS_INSUFFICIENT_RESOURCES; 75 | } 76 | 77 | RtlCopyMemory(out, name, sizeof(pref) + (36 * sizeof(char16_t))); 78 | 79 | Irp->IoStatus.Information = (ULONG_PTR)out; 80 | 81 | return STATUS_SUCCESS; 82 | } 83 | 84 | static NTSTATUS pdo_pnp(set_pdo* pdo, PIRP Irp) { 85 | TRACE("(%p, %p)\n", pdo, Irp); 86 | 87 | PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 88 | 89 | TRACE("PNP message %x\n", IrpSp->MinorFunction); 90 | 91 | switch (IrpSp->MinorFunction) { 92 | case IRP_MN_SURPRISE_REMOVAL: 93 | case IRP_MN_CANCEL_REMOVE_DEVICE: 94 | case IRP_MN_CANCEL_STOP_DEVICE: 95 | case IRP_MN_REMOVE_DEVICE: 96 | return STATUS_SUCCESS; 97 | 98 | case IRP_MN_QUERY_ID: 99 | switch (IrpSp->Parameters.QueryId.IdType) { 100 | case BusQueryHardwareIDs: 101 | return set_query_hardware_ids(Irp); 102 | 103 | case BusQueryDeviceID: 104 | return query_device_ids(&pdo->array_info, Irp); 105 | 106 | default: 107 | return Irp->IoStatus.Status; 108 | } 109 | 110 | } 111 | 112 | return Irp->IoStatus.Status; 113 | } 114 | 115 | static NTSTATUS query_capabilities(PIRP Irp) { 116 | PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 117 | PDEVICE_CAPABILITIES dc = IrpSp->Parameters.DeviceCapabilities.Capabilities; 118 | 119 | dc->UniqueID = true; 120 | dc->SilentInstall = true; 121 | 122 | return STATUS_SUCCESS; 123 | } 124 | 125 | static NTSTATUS query_hardware_ids(PIRP Irp) { 126 | static const char16_t ids[] = u"ROOT\\winmd\0"; 127 | 128 | char16_t* out = ExAllocatePoolWithTag(PagedPool, sizeof(ids), ALLOC_TAG); 129 | if (!out) { 130 | ERR("out of memory\n"); 131 | return STATUS_INSUFFICIENT_RESOURCES; 132 | } 133 | 134 | RtlCopyMemory(out, ids, sizeof(ids)); 135 | 136 | Irp->IoStatus.Information = (ULONG_PTR)out; 137 | 138 | return STATUS_SUCCESS; 139 | } 140 | 141 | static NTSTATUS query_device_relations(PIRP Irp) { 142 | ExAcquireResourceSharedLite(&dev_lock, true); 143 | 144 | unsigned int num_children = 0; 145 | 146 | { 147 | LIST_ENTRY* le = dev_list.Flink; 148 | 149 | while (le != &dev_list) { 150 | num_children++; 151 | 152 | le = le->Flink; 153 | } 154 | } 155 | 156 | ULONG drsize = offsetof(DEVICE_RELATIONS, Objects[0]) + (num_children * sizeof(PDEVICE_OBJECT)); 157 | DEVICE_RELATIONS* dr = ExAllocatePoolWithTag(PagedPool, drsize, ALLOC_TAG); 158 | 159 | if (!dr) { 160 | ERR("out of memory\n"); 161 | ExReleaseResourceLite(&dev_lock); 162 | return STATUS_INSUFFICIENT_RESOURCES; 163 | } 164 | 165 | dr->Count = num_children; 166 | 167 | { 168 | unsigned int i = 0; 169 | LIST_ENTRY* le = dev_list.Flink; 170 | 171 | while (le != &dev_list) { 172 | set_pdo* sd = CONTAINING_RECORD(le, set_pdo, list_entry); 173 | 174 | ObReferenceObject(sd->pdo); 175 | dr->Objects[i] = sd->pdo; 176 | i++; 177 | 178 | le = le->Flink; 179 | } 180 | } 181 | 182 | Irp->IoStatus.Information = (ULONG_PTR)dr; 183 | 184 | ExReleaseResourceLite(&dev_lock); 185 | 186 | return STATUS_SUCCESS; 187 | } 188 | 189 | static NTSTATUS control_pnp(control_device* control, PIRP Irp, bool* no_complete) { 190 | PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 191 | 192 | TRACE("(%p, %p)\n", control, Irp); 193 | TRACE("IrpSp->MinorFunction = %x\n", IrpSp->MinorFunction); 194 | 195 | switch (IrpSp->MinorFunction) { 196 | case IRP_MN_QUERY_CAPABILITIES: 197 | Irp->IoStatus.Status = query_capabilities(Irp); 198 | break; 199 | 200 | case IRP_MN_QUERY_DEVICE_RELATIONS: 201 | if (IrpSp->Parameters.QueryDeviceRelations.Type != BusRelations || no_pnp) 202 | break; 203 | 204 | Irp->IoStatus.Status = query_device_relations(Irp); 205 | break; 206 | 207 | case IRP_MN_QUERY_ID: 208 | { 209 | if (IrpSp->Parameters.QueryId.IdType != BusQueryHardwareIDs) 210 | break; 211 | 212 | Irp->IoStatus.Status = query_hardware_ids(Irp); 213 | break; 214 | } 215 | 216 | case IRP_MN_START_DEVICE: 217 | case IRP_MN_CANCEL_REMOVE_DEVICE: 218 | case IRP_MN_SURPRISE_REMOVAL: 219 | case IRP_MN_REMOVE_DEVICE: 220 | case IRP_MN_QUERY_PNP_DEVICE_STATE: 221 | Irp->IoStatus.Status = STATUS_SUCCESS; 222 | break; 223 | 224 | case IRP_MN_QUERY_REMOVE_DEVICE: 225 | Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; 226 | break; 227 | } 228 | 229 | if (!NT_SUCCESS(Irp->IoStatus.Status) && Irp->IoStatus.Status != STATUS_NOT_SUPPORTED) 230 | return Irp->IoStatus.Status; 231 | 232 | *no_complete = true; 233 | 234 | IoSkipCurrentIrpStackLocation(Irp); 235 | return IoCallDriver(control->attached_device, Irp); 236 | } 237 | 238 | static NTSTATUS set_pnp(set_device* set, PIRP Irp, bool* no_complete) { 239 | PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 240 | 241 | TRACE("(%p, %p)\n", set, Irp); 242 | 243 | TRACE("PNP message %x\n", IrpSp->MinorFunction); 244 | 245 | switch (IrpSp->MinorFunction) { 246 | case IRP_MN_START_DEVICE: 247 | case IRP_MN_CANCEL_REMOVE_DEVICE: 248 | case IRP_MN_REMOVE_DEVICE: 249 | case IRP_MN_QUERY_PNP_DEVICE_STATE: 250 | Irp->IoStatus.Status = STATUS_SUCCESS; 251 | break; 252 | 253 | case IRP_MN_QUERY_REMOVE_DEVICE: 254 | Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; 255 | break; 256 | 257 | case IRP_MN_SURPRISE_REMOVAL: 258 | if (set->open_count == 0) { 259 | PDEVICE_OBJECT devobj = set->devobj; 260 | 261 | IoDetachDevice(set->attached_device); 262 | 263 | ExDeleteResourceLite(&set->lock); 264 | IoDeleteDevice(devobj); 265 | } 266 | 267 | Irp->IoStatus.Status = STATUS_SUCCESS; 268 | 269 | break; 270 | } 271 | 272 | if (!NT_SUCCESS(Irp->IoStatus.Status) && Irp->IoStatus.Status != STATUS_NOT_SUPPORTED) 273 | return Irp->IoStatus.Status; 274 | 275 | *no_complete = true; 276 | 277 | IoSkipCurrentIrpStackLocation(Irp); 278 | return IoCallDriver(set->attached_device, Irp); 279 | } 280 | 281 | _Dispatch_type_(IRP_MJ_PNP) 282 | _Function_class_(DRIVER_DISPATCH) 283 | NTSTATUS drv_pnp(PDEVICE_OBJECT DeviceObject, PIRP Irp) { 284 | NTSTATUS Status; 285 | bool top_level; 286 | 287 | FsRtlEnterFileSystem(); 288 | 289 | top_level = is_top_level(Irp); 290 | 291 | bool no_complete = false; 292 | 293 | switch (*(enum device_type*)DeviceObject->DeviceExtension) { 294 | case device_type_control: 295 | Status = control_pnp((control_device*)(DeviceObject->DeviceExtension), Irp, &no_complete); 296 | break; 297 | 298 | case device_type_set: 299 | Status = set_pnp((set_device*)(DeviceObject->DeviceExtension), Irp, &no_complete); 300 | break; 301 | 302 | case device_type_pdo: 303 | Status = pdo_pnp((set_pdo*)(DeviceObject->DeviceExtension), Irp); 304 | break; 305 | 306 | default: { 307 | PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 308 | 309 | switch (IrpSp->MinorFunction) { 310 | case IRP_MN_SURPRISE_REMOVAL: 311 | case IRP_MN_CANCEL_REMOVE_DEVICE: 312 | case IRP_MN_CANCEL_STOP_DEVICE: 313 | case IRP_MN_REMOVE_DEVICE: 314 | Status = STATUS_SUCCESS; 315 | break; 316 | 317 | default: 318 | Status = Irp->IoStatus.Status; 319 | } 320 | } 321 | } 322 | 323 | if (!no_complete) { 324 | Irp->IoStatus.Status = Status; 325 | IoCompleteRequest(Irp, IO_NO_INCREMENT); 326 | } 327 | 328 | if (top_level) 329 | IoSetTopLevelIrp(NULL); 330 | 331 | FsRtlExitFileSystem(); 332 | 333 | return Status; 334 | } 335 | 336 | static NTSTATUS add_set_device(set_pdo* pdo) { 337 | NTSTATUS Status; 338 | UNICODE_STRING volname; 339 | PDEVICE_OBJECT voldev; 340 | set_device* sd; 341 | 342 | ExAcquireResourceExclusiveLite(&pdo->lock, true); 343 | 344 | if (pdo->dev) { 345 | ERR("AddDevice called for already-created device\n"); 346 | Status = STATUS_INTERNAL_ERROR; 347 | goto end; 348 | } 349 | 350 | volname.Length = volname.MaximumLength = sizeof(device_prefix) + (36 * sizeof(char16_t)); 351 | 352 | volname.Buffer = ExAllocatePoolWithTag(NonPagedPool, volname.Length, ALLOC_TAG); 353 | if (!volname.Buffer) { 354 | ERR("out of memory\n"); 355 | Status = STATUS_INSUFFICIENT_RESOURCES; 356 | goto end; 357 | } 358 | 359 | RtlCopyMemory(volname.Buffer, device_prefix, sizeof(device_prefix) - sizeof(char16_t)); 360 | 361 | { 362 | WCHAR* p = &volname.Buffer[(sizeof(device_prefix) / sizeof(char16_t)) - 1]; 363 | 364 | for (uint8_t i = 0; i < 16; i++) { 365 | *p = hex_digit((pdo->array_info.set_uuid[i] & 0xf0) >> 4); p++; 366 | *p = hex_digit(pdo->array_info.set_uuid[i] & 0xf); p++; 367 | 368 | if (i == 3 || i == 5 || i == 7 || i == 9) { 369 | *p = u'-'; p++; 370 | } 371 | } 372 | 373 | *p = u'}'; 374 | } 375 | 376 | Status = IoCreateDevice(drvobj, sizeof(set_device), &volname, FILE_DEVICE_DISK, 377 | is_windows_8 ? FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL : 0, false, &voldev); 378 | 379 | ExFreePool(volname.Buffer); 380 | 381 | if (!NT_SUCCESS(Status)) { 382 | ERR("IoCreateDevice returned %08x\n", Status); 383 | goto end; 384 | } 385 | 386 | voldev->Flags |= DO_DIRECT_IO; 387 | 388 | sd = (set_device*)voldev->DeviceExtension; 389 | sd->type = device_type_set; 390 | sd->pdo = pdo; 391 | sd->devobj = voldev; 392 | sd->open_count = 0; 393 | 394 | ExInitializeResourceLite(&sd->lock); 395 | 396 | Status = IoRegisterDeviceInterface(pdo->pdo, &GUID_DEVINTERFACE_VOLUME, NULL, &pdo->bus_name); 397 | if (!NT_SUCCESS(Status)) 398 | WARN("IoRegisterDeviceInterface returned %08x\n", Status); 399 | 400 | sd->attached_device = IoAttachDeviceToDeviceStack(voldev, pdo->pdo); 401 | 402 | voldev->StackSize = pdo->stack_size; 403 | voldev->SectorSize = pdo->dev_sector_size; 404 | 405 | pdo->dev = sd; 406 | 407 | voldev->Flags &= ~DO_DEVICE_INITIALIZING; 408 | 409 | Status = IoSetDeviceInterfaceState(&pdo->bus_name, true); 410 | if (!NT_SUCCESS(Status)) 411 | WARN("IoSetDeviceInterfaceState returned %08x\n", Status); 412 | 413 | Status = STATUS_SUCCESS; 414 | 415 | end: 416 | ExReleaseResourceLite(&pdo->lock); 417 | 418 | return Status; 419 | } 420 | 421 | _Function_class_(DRIVER_ADD_DEVICE) 422 | NTSTATUS __stdcall AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT PhysicalDeviceObject) { 423 | TRACE("(%p, %p)\n", DriverObject, PhysicalDeviceObject); 424 | 425 | set_pdo* sd = NULL; 426 | 427 | ExAcquireResourceSharedLite(&dev_lock, true); 428 | 429 | LIST_ENTRY* le = dev_list.Flink; 430 | 431 | while (le != &dev_list) { 432 | set_pdo* sd2 = CONTAINING_RECORD(le, set_pdo, list_entry); 433 | 434 | if (sd2->pdo == PhysicalDeviceObject) { 435 | sd = sd2; 436 | break; 437 | } 438 | 439 | le = le->Flink; 440 | } 441 | 442 | ExReleaseResourceLite(&dev_lock); 443 | 444 | if (!sd) { 445 | WARN("unrecognized PDO %p\n", PhysicalDeviceObject); 446 | return STATUS_NOT_SUPPORTED; 447 | } 448 | 449 | return add_set_device(sd); 450 | } 451 | -------------------------------------------------------------------------------- /src/raid0.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Mark Harmstone 2019 2 | * 3 | * This file is part of WinMD. 4 | * 5 | * WinMD is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public Licence as published by 7 | * the Free Software Foundation, either version 3 of the Licence, or 8 | * (at your option) any later version. 9 | * 10 | * WinBtrfs is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Lesser General Public Licence for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public Licence 16 | * along with WinMD. If not, see . */ 17 | 18 | #include "winmd.h" 19 | 20 | typedef struct { 21 | PIRP Irp; 22 | KEVENT Event; 23 | IO_STATUS_BLOCK iosb; 24 | NTSTATUS Status; 25 | set_child* sc; 26 | uint64_t stripe_start; 27 | uint64_t stripe_end; 28 | PMDL mdl; 29 | PFN_NUMBER* pfns; 30 | PFN_NUMBER* pfnp; 31 | } io_context_raid0; 32 | 33 | _Function_class_(IO_COMPLETION_ROUTINE) 34 | static NTSTATUS __stdcall io_completion_raid0(PDEVICE_OBJECT devobj, PIRP Irp, PVOID ctx) { 35 | io_context_raid0* context = ctx; 36 | 37 | context->iosb = Irp->IoStatus; 38 | KeSetEvent(&context->Event, 0, FALSE); 39 | 40 | return STATUS_MORE_PROCESSING_REQUIRED; 41 | } 42 | 43 | NTSTATUS read_raid0(set_pdo* pdo, PIRP Irp, bool* no_complete) { 44 | PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 45 | uint64_t offset = IrpSp->Parameters.Read.ByteOffset.QuadPart; 46 | uint32_t length = IrpSp->Parameters.Read.Length; 47 | bool mdl_locked = true; 48 | uint8_t* tmpbuf = NULL; 49 | PMDL tmpmdl = NULL; 50 | 51 | if (pdo->array_info.chunksize == 0 || (pdo->array_info.chunksize * 512) % PAGE_SIZE != 0) 52 | return STATUS_INTERNAL_ERROR; 53 | 54 | uint64_t start_chunk = offset / (pdo->array_info.chunksize * 512); 55 | uint64_t end_chunk = (offset + length - 1) / (pdo->array_info.chunksize * 512); 56 | 57 | if (start_chunk == end_chunk) { // small reads, on one device 58 | set_child* c = pdo->child_list[start_chunk % pdo->array_info.raid_disks]; 59 | 60 | IoCopyCurrentIrpStackLocationToNext(Irp); 61 | 62 | PIO_STACK_LOCATION IrpSp2 = IoGetNextIrpStackLocation(Irp); 63 | 64 | uint64_t start = (start_chunk / pdo->array_info.raid_disks) * (pdo->array_info.chunksize * 512); 65 | 66 | start += offset % (pdo->array_info.chunksize * 512); 67 | start += c->disk_info.data_offset * 512; 68 | 69 | IrpSp2->FileObject = c->fileobj; 70 | IrpSp2->Parameters.Read.ByteOffset.QuadPart = start; 71 | 72 | *no_complete = true; 73 | 74 | return IoCallDriver(c->device, Irp); 75 | } 76 | 77 | uint64_t startoff, endoff; 78 | uint32_t startoffstripe, endoffstripe; 79 | 80 | uint32_t stripe_length = pdo->array_info.chunksize * 512; 81 | 82 | uint32_t skip_first = offset % PAGE_SIZE; 83 | 84 | offset -= skip_first; 85 | length += skip_first; 86 | 87 | get_raid0_offset(offset, stripe_length, pdo->array_info.raid_disks, &startoff, &startoffstripe); 88 | get_raid0_offset(offset + length - 1, stripe_length, pdo->array_info.raid_disks, &endoff, &endoffstripe); 89 | 90 | io_context_raid0* ctxs = ExAllocatePoolWithTag(NonPagedPool, sizeof(io_context_raid0) * pdo->array_info.raid_disks, ALLOC_TAG); 91 | if (!ctxs) { 92 | ERR("out of memory\n"); 93 | return STATUS_INSUFFICIENT_RESOURCES; 94 | } 95 | 96 | RtlZeroMemory(ctxs, sizeof(io_context_raid0) * pdo->array_info.raid_disks); 97 | 98 | for (unsigned int i = 0; i < pdo->array_info.raid_disks; i++) { 99 | if (startoffstripe > i) 100 | ctxs[i].stripe_start = startoff - (startoff % stripe_length) + stripe_length; 101 | else if (startoffstripe == i) 102 | ctxs[i].stripe_start = startoff; 103 | else 104 | ctxs[i].stripe_start = startoff - (startoff % stripe_length); 105 | 106 | if (endoffstripe > i) 107 | ctxs[i].stripe_end = endoff - (endoff % stripe_length) + stripe_length; 108 | else if (endoffstripe == i) 109 | ctxs[i].stripe_end = endoff + 1; 110 | else 111 | ctxs[i].stripe_end = endoff - (endoff % stripe_length); 112 | } 113 | 114 | NTSTATUS Status; 115 | 116 | for (unsigned int i = 0; i < pdo->array_info.raid_disks; i++) { 117 | if (ctxs[i].stripe_end != ctxs[i].stripe_start) { 118 | ctxs[i].Irp = IoAllocateIrp(pdo->child_list[i]->device->StackSize, false); 119 | 120 | if (!ctxs[i].Irp) { 121 | ERR("IoAllocateIrp failed\n"); 122 | Status = STATUS_INSUFFICIENT_RESOURCES; 123 | goto end; 124 | } 125 | 126 | PIO_STACK_LOCATION IrpSp2 = IoGetNextIrpStackLocation(ctxs[i].Irp); 127 | IrpSp2->MajorFunction = IRP_MJ_READ; 128 | 129 | ctxs[i].mdl = IoAllocateMdl(NULL, (ULONG)(ctxs[i].stripe_end - ctxs[i].stripe_start), false, false, NULL); 130 | if (!ctxs[i].mdl) { 131 | ERR("IoAllocateMdl failed\n"); 132 | Status = STATUS_INSUFFICIENT_RESOURCES; 133 | goto end; 134 | } 135 | 136 | ctxs[i].mdl->MdlFlags |= MDL_PARTIAL; 137 | 138 | ctxs[i].Irp->MdlAddress = ctxs[i].mdl; 139 | 140 | IrpSp2->FileObject = pdo->child_list[i]->fileobj; 141 | IrpSp2->Parameters.Read.Length = (ULONG)(ctxs[i].stripe_end - ctxs[i].stripe_start); 142 | IrpSp2->Parameters.Read.ByteOffset.QuadPart = ctxs[i].stripe_start + (pdo->child_list[i]->disk_info.data_offset * 512); 143 | 144 | ctxs[i].Irp->UserIosb = &ctxs[i].iosb; 145 | 146 | KeInitializeEvent(&ctxs[i].Event, NotificationEvent, false); 147 | ctxs[i].Irp->UserEvent = &ctxs[i].Event; 148 | 149 | IoSetCompletionRoutine(ctxs[i].Irp, io_completion_raid0, &ctxs[i], true, true, true); 150 | } else 151 | ctxs[i].Status = STATUS_SUCCESS; 152 | } 153 | 154 | mdl_locked = Irp->MdlAddress->MdlFlags & (MDL_PAGES_LOCKED | MDL_PARTIAL); 155 | 156 | if (!mdl_locked) { 157 | Status = STATUS_SUCCESS; 158 | 159 | try { 160 | MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess); 161 | } except (EXCEPTION_EXECUTE_HANDLER) { 162 | Status = GetExceptionCode(); 163 | } 164 | 165 | if (!NT_SUCCESS(Status)) { 166 | ERR("MmProbeAndLockPages threw exception %08x\n", Status); 167 | mdl_locked = true; 168 | goto end; 169 | } 170 | } 171 | 172 | if (Irp->MdlAddress->ByteOffset != 0 || skip_first != 0) { 173 | tmpbuf = ExAllocatePoolWithTag(NonPagedPool, length, ALLOC_TAG); 174 | if (!tmpbuf) { 175 | ERR("out of memory\n"); 176 | Status = STATUS_INSUFFICIENT_RESOURCES; 177 | goto end; 178 | } 179 | 180 | tmpmdl = IoAllocateMdl(tmpbuf, length, false, false, NULL); 181 | if (!tmpmdl) { 182 | ERR("IoAllocateMdl failed\n"); 183 | Status = STATUS_INSUFFICIENT_RESOURCES; 184 | goto end; 185 | } 186 | 187 | MmBuildMdlForNonPagedPool(tmpmdl); 188 | } 189 | 190 | { 191 | uint32_t pos = 0; 192 | uint32_t stripe = startoffstripe; 193 | MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); 194 | 195 | for (unsigned int i = 0; i < pdo->array_info.raid_disks; i++) { 196 | if (ctxs[i].mdl) 197 | ctxs[i].pfnp = ctxs[i].pfns = MmGetMdlPfnArray(ctxs[i].mdl); 198 | } 199 | 200 | PPFN_NUMBER src_pfns = MmGetMdlPfnArray((tmpmdl ? tmpmdl : Irp->MdlAddress)); 201 | 202 | while (pos < length) { 203 | uint32_t len, pages; 204 | 205 | if (pos == 0) { 206 | len = stripe_length - (startoff % stripe_length); 207 | 208 | if (len % PAGE_SIZE != 0) { 209 | pages = len / PAGE_SIZE; 210 | pages++; 211 | } else 212 | pages = len / PAGE_SIZE; 213 | } else { 214 | len = stripe_length; 215 | pages = len / PAGE_SIZE; 216 | } 217 | 218 | if (pos + len > length) { 219 | len = length - pos; 220 | 221 | if (len % PAGE_SIZE != 0) { 222 | pages = len / PAGE_SIZE; 223 | pages++; 224 | } else 225 | pages = len / PAGE_SIZE; 226 | } 227 | 228 | RtlCopyMemory(ctxs[stripe].pfnp, src_pfns, sizeof(PFN_NUMBER) * pages); 229 | src_pfns = &src_pfns[pages]; 230 | ctxs[stripe].pfnp = &ctxs[stripe].pfnp[pages]; 231 | 232 | pos += len; 233 | 234 | stripe = (stripe + 1) % pdo->array_info.raid_disks; 235 | } 236 | } 237 | 238 | for (unsigned int i = 0; i < pdo->array_info.raid_disks; i++) { 239 | if (ctxs[i].Irp) { 240 | ctxs[i].Status = IoCallDriver(pdo->child_list[i]->device, ctxs[i].Irp); 241 | if (!NT_SUCCESS(ctxs[i].Status)) 242 | ERR("IoCallDriver returned %08x\n", ctxs[i].Status); 243 | } 244 | } 245 | 246 | Status = STATUS_SUCCESS; 247 | 248 | for (unsigned int i = 0; i < pdo->array_info.raid_disks; i++) { 249 | if (ctxs[i].Status == STATUS_PENDING) { 250 | KeWaitForSingleObject(&ctxs[i].Event, Executive, KernelMode, false, NULL); 251 | ctxs[i].Status = ctxs[i].iosb.Status; 252 | } 253 | 254 | if (!NT_SUCCESS(ctxs[i].Status)) 255 | Status = ctxs[i].Status; 256 | } 257 | 258 | if (tmpbuf) { 259 | PVOID dest = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); 260 | 261 | RtlCopyMemory(dest, tmpbuf + skip_first, length - skip_first); 262 | } 263 | 264 | end: 265 | if (!mdl_locked) 266 | MmUnlockPages(Irp->MdlAddress); 267 | 268 | for (unsigned int i = 0; i < pdo->array_info.raid_disks; i++) { 269 | if (ctxs[i].mdl) 270 | IoFreeMdl(ctxs[i].mdl); 271 | 272 | if (ctxs[i].Irp) 273 | IoFreeIrp(ctxs[i].Irp); 274 | } 275 | 276 | ExFreePool(ctxs); 277 | 278 | if (tmpmdl) 279 | IoFreeMdl(tmpmdl); 280 | 281 | if (tmpbuf) 282 | ExFreePool(tmpbuf); 283 | 284 | return Status; 285 | } 286 | 287 | NTSTATUS write_raid0(set_pdo* pdo, PIRP Irp, bool* no_complete) { 288 | PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 289 | uint64_t offset = IrpSp->Parameters.Write.ByteOffset.QuadPart; 290 | uint32_t length = IrpSp->Parameters.Write.Length; 291 | bool mdl_locked = true; 292 | uint8_t* tmpbuf = NULL; 293 | PMDL tmpmdl = NULL; 294 | io_context_raid0* ctxs = NULL; 295 | io_context_raid0 first_bit; 296 | 297 | if (pdo->array_info.chunksize == 0 || (pdo->array_info.chunksize * 512) % PAGE_SIZE != 0) 298 | return STATUS_INTERNAL_ERROR; 299 | 300 | uint64_t start_chunk = offset / (pdo->array_info.chunksize * 512); 301 | uint64_t end_chunk = (offset + length - 1) / (pdo->array_info.chunksize * 512); 302 | 303 | if (start_chunk == end_chunk) { // small write, on one device 304 | set_child* c = pdo->child_list[start_chunk % pdo->array_info.raid_disks]; 305 | 306 | IoCopyCurrentIrpStackLocationToNext(Irp); 307 | 308 | PIO_STACK_LOCATION IrpSp2 = IoGetNextIrpStackLocation(Irp); 309 | 310 | uint64_t start = (start_chunk / pdo->array_info.raid_disks) * (pdo->array_info.chunksize * 512); 311 | 312 | start += offset % (pdo->array_info.chunksize * 512); 313 | start += c->disk_info.data_offset * 512; 314 | 315 | IrpSp2->FileObject = c->fileobj; 316 | IrpSp2->Parameters.Write.ByteOffset.QuadPart = start; 317 | 318 | *no_complete = true; 319 | 320 | return IoCallDriver(c->device, Irp); 321 | } 322 | 323 | uint64_t startoff, endoff; 324 | uint32_t startoffstripe, endoffstripe; 325 | uint32_t stripe_length = pdo->array_info.chunksize * 512; 326 | 327 | uint32_t skip_first = offset % PAGE_SIZE ? (PAGE_SIZE - (offset % PAGE_SIZE)) : 0; 328 | NTSTATUS Status; 329 | 330 | mdl_locked = Irp->MdlAddress->MdlFlags & (MDL_PAGES_LOCKED | MDL_PARTIAL); 331 | 332 | if (!mdl_locked) { 333 | Status = STATUS_SUCCESS; 334 | 335 | try { 336 | MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoReadAccess); 337 | } except (EXCEPTION_EXECUTE_HANDLER) { 338 | Status = GetExceptionCode(); 339 | } 340 | 341 | if (!NT_SUCCESS(Status)) { 342 | ERR("MmProbeAndLockPages threw exception %08x\n", Status); 343 | return Status; 344 | } 345 | } 346 | 347 | first_bit.Irp = NULL; 348 | first_bit.mdl = NULL; 349 | 350 | if (skip_first != 0) { 351 | first_bit.sc = pdo->child_list[start_chunk % pdo->array_info.raid_disks]; 352 | first_bit.Irp = IoAllocateIrp(first_bit.sc->device->StackSize, false); 353 | 354 | if (!first_bit.Irp) { 355 | ERR("IoAllocateIrp failed\n"); 356 | Status = STATUS_INSUFFICIENT_RESOURCES; 357 | goto end2; 358 | } 359 | 360 | PIO_STACK_LOCATION IrpSp2 = IoGetNextIrpStackLocation(first_bit.Irp); 361 | IrpSp2->MajorFunction = IRP_MJ_WRITE; 362 | 363 | PVOID addr = MmGetMdlVirtualAddress(Irp->MdlAddress); 364 | 365 | first_bit.mdl = IoAllocateMdl(addr, skip_first, false, false, NULL); 366 | if (!first_bit.mdl) { 367 | ERR("IoAllocateMdl failed\n"); 368 | Status = STATUS_INSUFFICIENT_RESOURCES; 369 | goto end2; 370 | } 371 | 372 | IoBuildPartialMdl(Irp->MdlAddress, first_bit.mdl, addr, skip_first); 373 | 374 | first_bit.Irp->MdlAddress = first_bit.mdl; 375 | 376 | uint64_t start = (start_chunk / pdo->array_info.raid_disks) * (pdo->array_info.chunksize * 512); 377 | 378 | start += offset % (pdo->array_info.chunksize * 512); 379 | start += first_bit.sc->disk_info.data_offset * 512; 380 | 381 | IrpSp2->FileObject = first_bit.sc->fileobj; 382 | IrpSp2->Parameters.Write.Length = skip_first; 383 | IrpSp2->Parameters.Write.ByteOffset.QuadPart = start; 384 | 385 | first_bit.Irp->UserIosb = &first_bit.iosb; 386 | 387 | KeInitializeEvent(&first_bit.Event, NotificationEvent, false); 388 | first_bit.Irp->UserEvent = &first_bit.Event; 389 | 390 | IoSetCompletionRoutine(first_bit.Irp, io_completion_raid0, &first_bit, true, true, true); 391 | 392 | offset += skip_first; 393 | length -= skip_first; 394 | } 395 | 396 | get_raid0_offset(offset, stripe_length, pdo->array_info.raid_disks, &startoff, &startoffstripe); 397 | get_raid0_offset(offset + length - 1, stripe_length, pdo->array_info.raid_disks, &endoff, &endoffstripe); 398 | 399 | ctxs = ExAllocatePoolWithTag(NonPagedPool, sizeof(io_context_raid0) * pdo->array_info.raid_disks, ALLOC_TAG); 400 | if (!ctxs) { 401 | ERR("out of memory\n"); 402 | Status = STATUS_INSUFFICIENT_RESOURCES; 403 | goto end2; 404 | } 405 | 406 | RtlZeroMemory(ctxs, sizeof(io_context_raid0) * pdo->array_info.raid_disks); 407 | 408 | for (unsigned int i = 0; i < pdo->array_info.raid_disks; i++) { 409 | if (startoffstripe > i) 410 | ctxs[i].stripe_start = startoff - (startoff % stripe_length) + stripe_length; 411 | else if (startoffstripe == i) 412 | ctxs[i].stripe_start = startoff; 413 | else 414 | ctxs[i].stripe_start = startoff - (startoff % stripe_length); 415 | 416 | if (endoffstripe > i) 417 | ctxs[i].stripe_end = endoff - (endoff % stripe_length) + stripe_length; 418 | else if (endoffstripe == i) 419 | ctxs[i].stripe_end = endoff + 1; 420 | else 421 | ctxs[i].stripe_end = endoff - (endoff % stripe_length); 422 | } 423 | 424 | for (unsigned int i = 0; i < pdo->array_info.raid_disks; i++) { 425 | if (ctxs[i].stripe_end != ctxs[i].stripe_start) { 426 | ctxs[i].Irp = IoAllocateIrp(pdo->child_list[i]->device->StackSize, false); 427 | 428 | if (!ctxs[i].Irp) { 429 | ERR("IoAllocateIrp failed\n"); 430 | Status = STATUS_INSUFFICIENT_RESOURCES; 431 | goto end; 432 | } 433 | 434 | PIO_STACK_LOCATION IrpSp2 = IoGetNextIrpStackLocation(ctxs[i].Irp); 435 | IrpSp2->MajorFunction = IRP_MJ_WRITE; 436 | 437 | ctxs[i].mdl = IoAllocateMdl(NULL, (ULONG)(ctxs[i].stripe_end - ctxs[i].stripe_start), false, false, NULL); 438 | if (!ctxs[i].mdl) { 439 | ERR("IoAllocateMdl failed\n"); 440 | Status = STATUS_INSUFFICIENT_RESOURCES; 441 | goto end; 442 | } 443 | 444 | ctxs[i].mdl->MdlFlags |= MDL_PARTIAL; 445 | 446 | ctxs[i].Irp->MdlAddress = ctxs[i].mdl; 447 | 448 | IrpSp2->FileObject = pdo->child_list[i]->fileobj; 449 | IrpSp2->Parameters.Write.Length = (ULONG)(ctxs[i].stripe_end - ctxs[i].stripe_start); 450 | IrpSp2->Parameters.Write.ByteOffset.QuadPart = ctxs[i].stripe_start + (pdo->child_list[i]->disk_info.data_offset * 512); 451 | 452 | ctxs[i].Irp->UserIosb = &ctxs[i].iosb; 453 | 454 | KeInitializeEvent(&ctxs[i].Event, NotificationEvent, false); 455 | ctxs[i].Irp->UserEvent = &ctxs[i].Event; 456 | 457 | IoSetCompletionRoutine(ctxs[i].Irp, io_completion_raid0, &ctxs[i], true, true, true); 458 | } else 459 | ctxs[i].Status = STATUS_SUCCESS; 460 | } 461 | 462 | if (Irp->MdlAddress->ByteOffset != 0 || skip_first != 0) { 463 | tmpbuf = ExAllocatePoolWithTag(NonPagedPool, length, ALLOC_TAG); 464 | if (!tmpbuf) { 465 | ERR("out of memory\n"); 466 | Status = STATUS_INSUFFICIENT_RESOURCES; 467 | goto end; 468 | } 469 | 470 | tmpmdl = IoAllocateMdl(tmpbuf, length, false, false, NULL); 471 | if (!tmpmdl) { 472 | ERR("IoAllocateMdl failed\n"); 473 | Status = STATUS_INSUFFICIENT_RESOURCES; 474 | goto end; 475 | } 476 | 477 | MmBuildMdlForNonPagedPool(tmpmdl); 478 | 479 | PVOID src = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); 480 | 481 | RtlCopyMemory(tmpbuf, (uint8_t*)src + skip_first, length); 482 | } 483 | 484 | { 485 | uint32_t pos = 0; 486 | uint32_t stripe = startoffstripe; 487 | MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); 488 | 489 | for (unsigned int i = 0; i < pdo->array_info.raid_disks; i++) { 490 | if (ctxs[i].mdl) 491 | ctxs[i].pfnp = ctxs[i].pfns = MmGetMdlPfnArray(ctxs[i].mdl); 492 | } 493 | 494 | PPFN_NUMBER src_pfns = MmGetMdlPfnArray((tmpmdl ? tmpmdl : Irp->MdlAddress)); 495 | 496 | while (pos < length) { 497 | uint32_t len, pages; 498 | 499 | if (pos == 0) { 500 | len = stripe_length - (startoff % stripe_length); 501 | 502 | if (len % PAGE_SIZE != 0) { 503 | pages = len / PAGE_SIZE; 504 | pages++; 505 | } else 506 | pages = len / PAGE_SIZE; 507 | } else { 508 | len = stripe_length; 509 | pages = len / PAGE_SIZE; 510 | } 511 | 512 | if (pos + len > length) { 513 | len = length - pos; 514 | 515 | if (len % PAGE_SIZE != 0) { 516 | pages = len / PAGE_SIZE; 517 | pages++; 518 | } else 519 | pages = len / PAGE_SIZE; 520 | } 521 | 522 | RtlCopyMemory(ctxs[stripe].pfnp, src_pfns, sizeof(PFN_NUMBER) * pages); 523 | src_pfns = &src_pfns[pages]; 524 | ctxs[stripe].pfnp = &ctxs[stripe].pfnp[pages]; 525 | 526 | pos += len; 527 | 528 | stripe = (stripe + 1) % pdo->array_info.raid_disks; 529 | } 530 | } 531 | 532 | for (unsigned int i = 0; i < pdo->array_info.raid_disks; i++) { 533 | if (ctxs[i].Irp) { 534 | ctxs[i].Status = IoCallDriver(pdo->child_list[i]->device, ctxs[i].Irp); 535 | if (!NT_SUCCESS(ctxs[i].Status)) 536 | ERR("IoCallDriver returned %08x\n", ctxs[i].Status); 537 | } 538 | } 539 | 540 | if (skip_first != 0) { 541 | first_bit.Status = IoCallDriver(first_bit.sc->device, first_bit.Irp); 542 | if (!NT_SUCCESS(first_bit.Status)) 543 | ERR("IoCallDriver returned %08x\n", first_bit.Status); 544 | } 545 | 546 | Status = STATUS_SUCCESS; 547 | 548 | for (unsigned int i = 0; i < pdo->array_info.raid_disks; i++) { 549 | if (ctxs[i].Status == STATUS_PENDING) { 550 | KeWaitForSingleObject(&ctxs[i].Event, Executive, KernelMode, false, NULL); 551 | ctxs[i].Status = ctxs[i].iosb.Status; 552 | } 553 | 554 | if (!NT_SUCCESS(ctxs[i].Status)) 555 | Status = ctxs[i].Status; 556 | } 557 | 558 | if (skip_first != 0) { 559 | if (first_bit.Status == STATUS_PENDING) { 560 | KeWaitForSingleObject(&first_bit.Event, Executive, KernelMode, false, NULL); 561 | first_bit.Status = first_bit.iosb.Status; 562 | } 563 | 564 | if (!NT_SUCCESS(first_bit.Status)) 565 | Status = first_bit.Status; 566 | } 567 | 568 | end: 569 | if (!mdl_locked) 570 | MmUnlockPages(Irp->MdlAddress); 571 | 572 | for (unsigned int i = 0; i < pdo->array_info.raid_disks; i++) { 573 | if (ctxs[i].mdl) 574 | IoFreeMdl(ctxs[i].mdl); 575 | 576 | if (ctxs[i].Irp) 577 | IoFreeIrp(ctxs[i].Irp); 578 | } 579 | 580 | ExFreePool(ctxs); 581 | 582 | end2: 583 | if (tmpmdl) 584 | IoFreeMdl(tmpmdl); 585 | 586 | if (tmpbuf) 587 | ExFreePool(tmpbuf); 588 | 589 | if (first_bit.mdl) 590 | IoFreeMdl(first_bit.mdl); 591 | 592 | if (first_bit.Irp) 593 | IoFreeIrp(first_bit.Irp); 594 | 595 | return Status; 596 | } 597 | -------------------------------------------------------------------------------- /src/io.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Mark Harmstone 2019 2 | * 3 | * This file is part of WinMD. 4 | * 5 | * WinMD is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public Licence as published by 7 | * the Free Software Foundation, either version 3 of the Licence, or 8 | * (at your option) any later version. 9 | * 10 | * WinBtrfs is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Lesser General Public Licence for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public Licence 16 | * along with WinMD. If not, see . */ 17 | 18 | #include "winmd.h" 19 | #include 20 | #include 21 | 22 | static const int64_t flush_interval = 5; 23 | 24 | typedef struct { 25 | PIRP Irp; 26 | KEVENT Event; 27 | IO_STATUS_BLOCK iosb; 28 | NTSTATUS Status; 29 | set_child* sc; 30 | uint64_t stripe_start; 31 | uint64_t stripe_end; 32 | void* va2; 33 | PMDL mdl; 34 | LIST_ENTRY list_entry; 35 | } io_context; 36 | 37 | _Function_class_(IO_COMPLETION_ROUTINE) 38 | static NTSTATUS __stdcall io_completion(PDEVICE_OBJECT devobj, PIRP Irp, PVOID ctx) { 39 | io_context* context = ctx; 40 | 41 | context->iosb = Irp->IoStatus; 42 | KeSetEvent(&context->Event, 0, FALSE); 43 | 44 | return STATUS_MORE_PROCESSING_REQUIRED; 45 | } 46 | 47 | void do_xor(uint8_t* buf1, uint8_t* buf2, uint32_t len) { 48 | uint32_t j; 49 | __m128i x1, x2; 50 | 51 | if (have_sse2 && ((uintptr_t)buf1 & 0xf) == 0 && ((uintptr_t)buf2 & 0xf) == 0) { 52 | while (len >= 16) { 53 | x1 = _mm_load_si128((__m128i*)buf1); 54 | x2 = _mm_load_si128((__m128i*)buf2); 55 | x1 = _mm_xor_si128(x1, x2); 56 | _mm_store_si128((__m128i*)buf1, x1); 57 | 58 | buf1 += 16; 59 | buf2 += 16; 60 | len -= 16; 61 | } 62 | } 63 | 64 | while (len >= 4) { 65 | *(uint32_t*)buf1 ^= *(uint32_t*)buf2; 66 | buf1 += 4; 67 | buf2 += 4; 68 | len -= 4; 69 | } 70 | 71 | for (j = 0; j < len; j++) { 72 | *buf1 ^= *buf2; 73 | buf1++; 74 | buf2++; 75 | } 76 | } 77 | 78 | static void do_and(uint8_t* buf1, uint8_t* buf2, uint32_t len) { 79 | uint32_t j; 80 | __m128i x1, x2; 81 | 82 | if (have_sse2 && ((uintptr_t)buf1 & 0xf) == 0 && ((uintptr_t)buf2 & 0xf) == 0) { 83 | while (len >= 16) { 84 | x1 = _mm_load_si128((__m128i*)buf1); 85 | x2 = _mm_load_si128((__m128i*)buf2); 86 | x1 = _mm_and_si128(x1, x2); 87 | _mm_store_si128((__m128i*)buf1, x1); 88 | 89 | buf1 += 16; 90 | buf2 += 16; 91 | len -= 16; 92 | } 93 | } 94 | 95 | while (len >= 4) { 96 | *(uint32_t*)buf1 &= *(uint32_t*)buf2; 97 | buf1 += 4; 98 | buf2 += 4; 99 | len -= 4; 100 | } 101 | 102 | for (j = 0; j < len; j++) { 103 | *buf1 &= *buf2; 104 | buf1++; 105 | buf2++; 106 | } 107 | } 108 | 109 | uint32_t get_parity_volume(set_pdo* pdo, uint64_t offset) { 110 | switch (pdo->array_info.level) { 111 | case RAID_LEVEL_4: 112 | return pdo->array_info.raid_disks - 1; 113 | 114 | case RAID_LEVEL_5: 115 | offset /= (pdo->array_info.raid_disks - 1) * pdo->array_info.chunksize * 512; 116 | offset %= pdo->array_info.raid_disks; 117 | 118 | if (pdo->array_info.layout == RAID_LAYOUT_RIGHT_ASYMMETRIC || pdo->array_info.layout == RAID_LAYOUT_RIGHT_SYMMETRIC) 119 | return (uint32_t)offset; 120 | else 121 | return pdo->array_info.raid_disks - (uint32_t)offset - 1; 122 | 123 | case RAID_LEVEL_6: 124 | offset /= (pdo->array_info.raid_disks - 2) * pdo->array_info.chunksize * 512; 125 | offset %= pdo->array_info.raid_disks; 126 | 127 | if (pdo->array_info.layout == RAID_LAYOUT_RIGHT_ASYMMETRIC || pdo->array_info.layout == RAID_LAYOUT_RIGHT_SYMMETRIC) 128 | return (uint32_t)offset; 129 | else 130 | return pdo->array_info.raid_disks - (uint32_t)offset - 1; 131 | 132 | default: 133 | return 0; 134 | } 135 | } 136 | 137 | uint32_t get_physical_stripe(set_pdo* pdo, uint32_t stripe, uint32_t parity) { 138 | if (pdo->array_info.level == RAID_LEVEL_6) { 139 | uint32_t q = (parity + 1) % pdo->array_info.raid_disks; 140 | 141 | if (pdo->array_info.layout == RAID_LAYOUT_LEFT_ASYMMETRIC || pdo->array_info.layout == RAID_LAYOUT_RIGHT_ASYMMETRIC) 142 | return stripe + (q == 0 ? 1 : (stripe >= parity ? 2 : 0)); 143 | else 144 | return (parity + stripe + 2) % pdo->array_info.raid_disks; 145 | } else { 146 | if (pdo->array_info.level == RAID_LEVEL_5 && (pdo->array_info.layout == RAID_LAYOUT_LEFT_ASYMMETRIC || pdo->array_info.layout == RAID_LAYOUT_RIGHT_ASYMMETRIC)) 147 | return stripe + (stripe >= parity ? 1 : 0); 148 | else 149 | return (parity + stripe + 1) % pdo->array_info.raid_disks; 150 | } 151 | } 152 | 153 | static NTSTATUS set_read(set_device* set, PIRP Irp, bool* no_complete) { 154 | TRACE("(%p, %p)\n", set, Irp); 155 | 156 | if (!set->pdo) 157 | return STATUS_INVALID_DEVICE_REQUEST; 158 | 159 | if (!set->pdo->loaded) 160 | return STATUS_DEVICE_NOT_READY; 161 | 162 | PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 163 | 164 | if (IrpSp->Parameters.Read.ByteOffset.QuadPart < 0) { 165 | WARN("read start is negative\n"); 166 | return STATUS_INVALID_PARAMETER; 167 | } 168 | 169 | if ((uint64_t)IrpSp->Parameters.Read.ByteOffset.QuadPart >= set->pdo->array_size) { 170 | WARN("trying to read past end of device\n"); 171 | return STATUS_INVALID_PARAMETER; 172 | } 173 | 174 | if ((uint64_t)IrpSp->Parameters.Read.ByteOffset.QuadPart + IrpSp->Parameters.Read.Length > set->pdo->array_size) 175 | IrpSp->Parameters.Read.Length = (ULONG)(set->pdo->array_size - IrpSp->Parameters.Read.ByteOffset.QuadPart); 176 | 177 | if (IrpSp->Parameters.Read.ByteOffset.QuadPart % set->devobj->SectorSize || IrpSp->Parameters.Read.Length % set->devobj->SectorSize) 178 | return STATUS_INVALID_PARAMETER; 179 | 180 | Irp->IoStatus.Information = IrpSp->Parameters.Read.Length; 181 | 182 | if (IrpSp->Parameters.Read.Length == 0) 183 | return STATUS_SUCCESS; 184 | 185 | switch (set->pdo->array_info.level) { 186 | case RAID_LEVEL_0: 187 | return read_raid0(set->pdo, Irp, no_complete); 188 | 189 | case RAID_LEVEL_1: 190 | return read_raid1(set->pdo, Irp, no_complete); 191 | 192 | case RAID_LEVEL_4: 193 | case RAID_LEVEL_5: 194 | return read_raid45(set->pdo, Irp, no_complete); 195 | 196 | case RAID_LEVEL_6: 197 | return read_raid6(set->pdo, Irp, no_complete); 198 | 199 | case RAID_LEVEL_10: 200 | return read_raid10(set->pdo, Irp, no_complete); 201 | 202 | case RAID_LEVEL_LINEAR: 203 | return read_linear(set->pdo, Irp, no_complete); 204 | 205 | default: 206 | return STATUS_INVALID_DEVICE_REQUEST; 207 | } 208 | } 209 | 210 | _Dispatch_type_(IRP_MJ_READ) 211 | _Function_class_(DRIVER_DISPATCH) 212 | NTSTATUS drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp) { 213 | NTSTATUS Status; 214 | bool top_level; 215 | 216 | FsRtlEnterFileSystem(); 217 | 218 | top_level = is_top_level(Irp); 219 | 220 | bool no_complete = false; 221 | 222 | switch (*(enum device_type*)DeviceObject->DeviceExtension) { 223 | case device_type_set: 224 | Status = set_read((set_device*)(DeviceObject->DeviceExtension), Irp, &no_complete); 225 | break; 226 | 227 | default: 228 | Status = STATUS_INVALID_DEVICE_REQUEST; 229 | } 230 | 231 | if (!no_complete) { 232 | Irp->IoStatus.Status = Status; 233 | IoCompleteRequest(Irp, IO_NO_INCREMENT); 234 | } 235 | 236 | if (top_level) 237 | IoSetTopLevelIrp(NULL); 238 | 239 | FsRtlExitFileSystem(); 240 | 241 | return Status; 242 | } 243 | 244 | static NTSTATUS flush_partial_chunk(set_pdo* pdo, partial_chunk* pc) { 245 | NTSTATUS Status; 246 | LIST_ENTRY ctxs; 247 | 248 | TRACE("(%llx)\n", pc->offset); 249 | 250 | uint32_t data_disks = pdo->array_info.raid_disks - (pdo->array_info.level == RAID_LEVEL_6 ? 2 : 1); 251 | uint32_t chunk_size = pdo->array_info.chunksize * 512; 252 | bool asymmetric = pdo->array_info.layout == RAID_LAYOUT_LEFT_ASYMMETRIC || pdo->array_info.layout == RAID_LAYOUT_RIGHT_ASYMMETRIC; 253 | 254 | InitializeListHead(&ctxs); 255 | 256 | uint8_t* valid = ExAllocatePoolWithTag(NonPagedPool, sector_align32(pdo->array_info.chunksize, 32) / 8, ALLOC_TAG); 257 | if (!valid) { 258 | ERR("out of memory\n"); 259 | return STATUS_INSUFFICIENT_RESOURCES; 260 | } 261 | 262 | RTL_BITMAP valid_bmp; 263 | 264 | RtlInitializeBitMap(&valid_bmp, (ULONG*)valid, pdo->array_info.chunksize); 265 | 266 | // FIXME - what if array_info.chunksize not multiple of 8? 267 | RtlCopyMemory(valid, pc->bmp.Buffer, pdo->array_info.chunksize / 8); 268 | 269 | for (uint32_t i = 1; i < data_disks; i++) { 270 | do_and(valid, (uint8_t*)pc->bmp.Buffer + (i * pdo->array_info.chunksize / 8), pdo->array_info.chunksize / 8); 271 | } 272 | 273 | { 274 | uint32_t parity = get_parity_volume(pdo, pc->offset); 275 | uint32_t stripe = get_physical_stripe(pdo, 0, parity); 276 | 277 | for (uint32_t i = 0; i < data_disks; i++) { 278 | ULONG index; 279 | io_context* last = NULL; 280 | ULONG runlength = RtlFindFirstRunClear(&valid_bmp, &index); 281 | 282 | while (runlength != 0) { 283 | for (uint32_t j = index; j < index + runlength; j++) { 284 | if (RtlCheckBit(&pc->bmp, (i * pdo->array_info.chunksize) + j)) { 285 | uint64_t stripe_start = (pc->offset / data_disks) + (j * 512) + (pdo->child_list[stripe]->disk_info.data_offset * 512); 286 | 287 | if (last && last->stripe_end == stripe_start) 288 | last->stripe_end += 512; 289 | else { 290 | io_context* last = ExAllocatePoolWithTag(NonPagedPool, sizeof(io_context), ALLOC_TAG); 291 | if (!last) { 292 | Status = STATUS_INSUFFICIENT_RESOURCES; 293 | goto end; 294 | } 295 | 296 | last->sc = pdo->child_list[stripe]; 297 | last->stripe_start = stripe_start; 298 | last->stripe_end = stripe_start + 512; 299 | 300 | last->Irp = IoAllocateIrp(pdo->child_list[stripe]->device->StackSize, false); 301 | if (!last->Irp) { 302 | ERR("out of memory\n"); 303 | ExFreePool(last); 304 | Status = STATUS_INSUFFICIENT_RESOURCES; 305 | goto end; 306 | } 307 | 308 | last->Irp->UserIosb = &last->iosb; 309 | 310 | KeInitializeEvent(&last->Event, NotificationEvent, false); 311 | last->Irp->UserEvent = &last->Event; 312 | 313 | IoSetCompletionRoutine(last->Irp, io_completion, last, true, true, true); 314 | 315 | last->Status = STATUS_SUCCESS; 316 | 317 | last->mdl = NULL; 318 | 319 | InsertTailList(&ctxs, &last->list_entry); 320 | 321 | last->va2 = pc->data + (i * chunk_size) + (j * 512); 322 | } 323 | } 324 | } 325 | 326 | runlength = RtlFindNextForwardRunClear(&valid_bmp, index + runlength, &index); 327 | } 328 | 329 | if (asymmetric) { 330 | stripe++; 331 | 332 | if (stripe == parity) { 333 | if (pdo->array_info.level == RAID_LEVEL_6) 334 | stripe += 2; 335 | else 336 | stripe++; 337 | } 338 | } else 339 | stripe = (stripe + 1) % pdo->array_info.raid_disks; 340 | } 341 | 342 | if (!IsListEmpty(&ctxs)) { 343 | LIST_ENTRY* le = ctxs.Flink; 344 | while (le != &ctxs) { 345 | io_context* ctx = CONTAINING_RECORD(le, io_context, list_entry); 346 | 347 | PIO_STACK_LOCATION IrpSp = IoGetNextIrpStackLocation(ctx->Irp); 348 | IrpSp->MajorFunction = IRP_MJ_READ; 349 | 350 | ctx->mdl = IoAllocateMdl(ctx->va2, (ULONG)(ctx->stripe_end - ctx->stripe_start), false, false, NULL); 351 | if (!ctx->mdl) { 352 | ERR("IoAllocateMdl failed\n"); 353 | Status = STATUS_INSUFFICIENT_RESOURCES; 354 | goto end; 355 | } 356 | 357 | MmBuildMdlForNonPagedPool(ctx->mdl); 358 | 359 | ctx->Irp->MdlAddress = ctx->mdl; 360 | 361 | IrpSp->FileObject = ctx->sc->fileobj; 362 | IrpSp->Parameters.Read.ByteOffset.QuadPart = ctx->stripe_start; 363 | IrpSp->Parameters.Read.Length = (ULONG)(ctx->stripe_end - ctx->stripe_start); 364 | 365 | ctx->Status = IoCallDriver(ctx->sc->device, ctx->Irp); 366 | 367 | le = le->Flink; 368 | } 369 | 370 | Status = STATUS_SUCCESS; 371 | 372 | while (!IsListEmpty(&ctxs)) { 373 | io_context* ctx = CONTAINING_RECORD(RemoveHeadList(&ctxs), io_context, list_entry); 374 | 375 | if (ctx->Status == STATUS_PENDING) { 376 | KeWaitForSingleObject(&ctx->Event, Executive, KernelMode, false, NULL); 377 | ctx->Status = ctx->iosb.Status; 378 | } 379 | 380 | if (!NT_SUCCESS(ctx->Status)) { 381 | ERR("reading returned %08x\n", ctx->Status); 382 | Status = ctx->Status; 383 | } 384 | 385 | if (ctx->mdl) 386 | IoFreeMdl(ctx->mdl); 387 | 388 | if (ctx->Irp) 389 | IoFreeIrp(ctx->Irp); 390 | 391 | ExFreePool(ctx); 392 | } 393 | 394 | if (!NT_SUCCESS(Status)) 395 | goto end; 396 | } 397 | } 398 | 399 | if (pdo->array_info.level == RAID_LEVEL_6) 400 | Status = flush_partial_chunk_raid6(pdo, pc, &valid_bmp); 401 | else 402 | Status = flush_partial_chunk_raid45(pdo, pc, &valid_bmp); 403 | 404 | end: 405 | while (!IsListEmpty(&ctxs)) { 406 | io_context* ctx = CONTAINING_RECORD(RemoveHeadList(&ctxs), io_context, list_entry); 407 | 408 | if (ctx->mdl) 409 | IoFreeMdl(ctx->mdl); 410 | 411 | if (ctx->Irp) 412 | IoFreeIrp(ctx->Irp); 413 | 414 | ExFreePool(ctx); 415 | } 416 | 417 | ExFreePool(valid); 418 | 419 | return Status; 420 | } 421 | 422 | void flush_chunks(set_pdo* pdo) { 423 | ExAcquireResourceExclusiveLite(&pdo->partial_chunks_lock, true); 424 | 425 | while (!IsListEmpty(&pdo->partial_chunks)) { 426 | partial_chunk* pc = CONTAINING_RECORD(RemoveHeadList(&pdo->partial_chunks), partial_chunk, list_entry); 427 | 428 | flush_partial_chunk(pdo, pc); 429 | 430 | ExFreePool(pc); 431 | } 432 | 433 | ExReleaseResourceLite(&pdo->partial_chunks_lock); 434 | } 435 | 436 | _Function_class_(KSTART_ROUTINE) 437 | void __stdcall flush_thread(void* context) { 438 | set_pdo* sd = (set_pdo*)context; 439 | 440 | LARGE_INTEGER due_time; 441 | 442 | ObReferenceObject(sd->pdo); 443 | 444 | KeInitializeTimer(&sd->flush_thread_timer); 445 | 446 | due_time.QuadPart = flush_interval * -10000000ll; 447 | 448 | KeSetTimer(&sd->flush_thread_timer, due_time, NULL); 449 | 450 | while (true) { 451 | KeWaitForSingleObject(&sd->flush_thread_timer, Executive, KernelMode, false, NULL); 452 | 453 | if (sd->loaded) 454 | flush_chunks(sd); 455 | 456 | if (sd->readonly) 457 | break; 458 | 459 | KeSetTimer(&sd->flush_thread_timer, due_time, NULL); 460 | } 461 | 462 | ObDereferenceObject(sd->pdo); 463 | KeCancelTimer(&sd->flush_thread_timer); 464 | 465 | KeSetEvent(&sd->flush_thread_finished, 0, false); 466 | 467 | PsTerminateSystemThread(STATUS_SUCCESS); 468 | } 469 | 470 | NTSTATUS add_partial_chunk(set_pdo* pdo, uint64_t offset, uint32_t length, void* data) { 471 | NTSTATUS Status; 472 | uint32_t data_disks = pdo->array_info.raid_disks - (pdo->array_info.level == RAID_LEVEL_6 ? 2 : 1); 473 | uint32_t full_chunk = pdo->array_info.chunksize * 512 * data_disks; 474 | partial_chunk* pc; 475 | uint32_t pclen; 476 | 477 | uint64_t chunk_offset = offset - (offset % full_chunk); 478 | 479 | ExAcquireResourceExclusiveLite(&pdo->partial_chunks_lock, true); 480 | 481 | LIST_ENTRY* le = pdo->partial_chunks.Flink; 482 | 483 | while (le != &pdo->partial_chunks) { 484 | partial_chunk* pc = CONTAINING_RECORD(le, partial_chunk, list_entry); 485 | 486 | if (pc->offset == chunk_offset) { 487 | RtlCopyMemory(pc->data + offset - chunk_offset, data, length); 488 | 489 | RtlClearBits(&pc->bmp, (ULONG)((offset - chunk_offset) / 512), length / 512); 490 | 491 | if (RtlAreBitsClear(&pc->bmp, 0, pdo->array_info.chunksize * data_disks)) { 492 | Status = flush_partial_chunk(pdo, pc); 493 | if (!NT_SUCCESS(Status)) { 494 | ERR("flush_partial_chunk returned %08x\n", Status); 495 | goto end; 496 | } 497 | 498 | RemoveEntryList(&pc->list_entry); 499 | ExFreePool(pc); 500 | } 501 | 502 | Status = STATUS_SUCCESS; 503 | goto end; 504 | } else if (pc->offset > chunk_offset) 505 | break; 506 | 507 | le = le->Flink; 508 | } 509 | 510 | pclen = offsetof(partial_chunk, data[0]); 511 | pclen += full_chunk; // data length 512 | pclen += sector_align32(pdo->array_info.chunksize * data_disks, 32) / 8; // bitmap length 513 | 514 | pc = ExAllocatePoolWithTag(NonPagedPool/*FIXME - ?*/, pclen, ALLOC_TAG); 515 | if (!pc) { 516 | ERR("out of memory\n"); 517 | Status = STATUS_INSUFFICIENT_RESOURCES; 518 | goto end; 519 | } 520 | 521 | pc->offset = chunk_offset; 522 | 523 | RtlInitializeBitMap(&pc->bmp, (ULONG*)(pc->data + full_chunk), pdo->array_info.chunksize * data_disks); 524 | RtlSetBits(&pc->bmp, 0, pdo->array_info.chunksize * data_disks); 525 | 526 | RtlCopyMemory(pc->data + offset - chunk_offset, data, length); 527 | 528 | RtlClearBits(&pc->bmp, (ULONG)((offset - chunk_offset) / 512), length / 512); 529 | 530 | InsertHeadList(le->Blink, &pc->list_entry); 531 | 532 | Status = STATUS_SUCCESS; 533 | 534 | end: 535 | ExReleaseResourceLite(&pdo->partial_chunks_lock); 536 | 537 | return Status; 538 | } 539 | 540 | static NTSTATUS set_write(set_device* set, PIRP Irp, bool* no_complete) { 541 | NTSTATUS Status; 542 | PIO_STACK_LOCATION IrpSp; 543 | 544 | TRACE("(%p, %p)\n", set, Irp); 545 | 546 | if (!set->pdo) 547 | return STATUS_INVALID_DEVICE_REQUEST; 548 | 549 | ExAcquireResourceSharedLite(&set->pdo->lock, true); 550 | 551 | if (!set->pdo->loaded) { 552 | Status = STATUS_DEVICE_NOT_READY; 553 | goto end; 554 | } 555 | 556 | if (set->pdo->readonly) { 557 | Status = STATUS_MEDIA_WRITE_PROTECTED; 558 | goto end; 559 | } 560 | 561 | IrpSp = IoGetCurrentIrpStackLocation(Irp); 562 | 563 | if (IrpSp->Parameters.Write.ByteOffset.QuadPart < 0) { 564 | WARN("write start is negative\n"); 565 | Status = STATUS_INVALID_PARAMETER; 566 | goto end; 567 | } 568 | 569 | if ((uint64_t)IrpSp->Parameters.Write.ByteOffset.QuadPart >= set->pdo->array_size) { 570 | WARN("trying to write past end of device\n"); 571 | Status = STATUS_INVALID_PARAMETER; 572 | goto end; 573 | } 574 | 575 | if ((uint64_t)IrpSp->Parameters.Write.ByteOffset.QuadPart + IrpSp->Parameters.Write.Length > set->pdo->array_size) 576 | IrpSp->Parameters.Write.Length = (ULONG)(set->pdo->array_size - IrpSp->Parameters.Write.ByteOffset.QuadPart); 577 | 578 | if (IrpSp->Parameters.Write.ByteOffset.QuadPart % set->devobj->SectorSize || IrpSp->Parameters.Write.Length % set->devobj->SectorSize) { 579 | Status = STATUS_INVALID_PARAMETER; 580 | goto end; 581 | } 582 | 583 | Irp->IoStatus.Information = IrpSp->Parameters.Write.Length; 584 | 585 | if (IrpSp->Parameters.Write.Length == 0) { 586 | Status = STATUS_SUCCESS; 587 | goto end; 588 | } 589 | 590 | switch (set->pdo->array_info.level) { 591 | case RAID_LEVEL_0: 592 | Status = write_raid0(set->pdo, Irp, no_complete); 593 | break; 594 | 595 | case RAID_LEVEL_1: 596 | Status = write_raid1(set->pdo, Irp); 597 | break; 598 | 599 | case RAID_LEVEL_4: 600 | case RAID_LEVEL_5: 601 | Status = write_raid45(set->pdo, Irp, no_complete); 602 | break; 603 | 604 | case RAID_LEVEL_6: 605 | Status = write_raid6(set->pdo, Irp, no_complete); 606 | break; 607 | 608 | case RAID_LEVEL_10: 609 | Status = write_raid10(set->pdo, Irp); 610 | break; 611 | 612 | case RAID_LEVEL_LINEAR: 613 | Status = write_linear(set->pdo, Irp, no_complete); 614 | break; 615 | 616 | default: 617 | Status = STATUS_INVALID_DEVICE_REQUEST; 618 | break; 619 | } 620 | 621 | end: 622 | ExReleaseResourceLite(&set->pdo->lock); 623 | 624 | return Status; 625 | } 626 | 627 | _Dispatch_type_(IRP_MJ_WRITE) 628 | _Function_class_(DRIVER_DISPATCH) 629 | NTSTATUS drv_write(PDEVICE_OBJECT DeviceObject, PIRP Irp) { 630 | NTSTATUS Status; 631 | bool top_level; 632 | 633 | FsRtlEnterFileSystem(); 634 | 635 | top_level = is_top_level(Irp); 636 | 637 | bool no_complete = false; 638 | 639 | switch (*(enum device_type*)DeviceObject->DeviceExtension) { 640 | case device_type_set: 641 | Status = set_write((set_device*)(DeviceObject->DeviceExtension), Irp, &no_complete); 642 | break; 643 | 644 | default: 645 | Status = STATUS_INVALID_DEVICE_REQUEST; 646 | } 647 | 648 | if (!no_complete) { 649 | Irp->IoStatus.Status = Status; 650 | IoCompleteRequest(Irp, IO_NO_INCREMENT); 651 | } 652 | 653 | if (top_level) 654 | IoSetTopLevelIrp(NULL); 655 | 656 | FsRtlExitFileSystem(); 657 | 658 | return Status; 659 | } 660 | -------------------------------------------------------------------------------- /src/raid45.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Mark Harmstone 2019 2 | * 3 | * This file is part of WinMD. 4 | * 5 | * WinMD is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public Licence as published by 7 | * the Free Software Foundation, either version 3 of the Licence, or 8 | * (at your option) any later version. 9 | * 10 | * WinBtrfs is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Lesser General Public Licence for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public Licence 16 | * along with WinMD. If not, see . */ 17 | 18 | #include "winmd.h" 19 | 20 | typedef struct { 21 | PIRP Irp; 22 | KEVENT Event; 23 | IO_STATUS_BLOCK iosb; 24 | NTSTATUS Status; 25 | set_child* sc; 26 | uint64_t stripe_start; 27 | uint64_t stripe_end; 28 | void* va; 29 | void* va2; 30 | PMDL mdl; 31 | PFN_NUMBER* pfns; 32 | PFN_NUMBER* pfnp; 33 | bool first; 34 | LIST_ENTRY list_entry; 35 | } io_context_raid45; 36 | 37 | _Function_class_(IO_COMPLETION_ROUTINE) 38 | static NTSTATUS __stdcall io_completion_raid45(PDEVICE_OBJECT devobj, PIRP Irp, PVOID ctx) { 39 | io_context_raid45* context = ctx; 40 | 41 | context->iosb = Irp->IoStatus; 42 | KeSetEvent(&context->Event, 0, FALSE); 43 | 44 | return STATUS_MORE_PROCESSING_REQUIRED; 45 | } 46 | 47 | NTSTATUS read_raid45(set_pdo* pdo, PIRP Irp, bool* no_complete) { 48 | PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 49 | bool mdl_locked = true; 50 | uint64_t offset = IrpSp->Parameters.Read.ByteOffset.QuadPart; 51 | uint32_t length = IrpSp->Parameters.Read.Length; 52 | void* dummypage = NULL; 53 | PMDL dummy_mdl = NULL; 54 | uint8_t* tmpbuf = NULL; 55 | PMDL tmpmdl = NULL; 56 | NTSTATUS Status; 57 | bool asymmetric; 58 | uint64_t startoff, endoff; 59 | uint32_t startoffstripe, endoffstripe, stripe_length; 60 | uint64_t start_chunk, end_chunk; 61 | uint32_t skip_first; 62 | io_context_raid45* ctxs; 63 | bool need_dummy; 64 | uint32_t pos; 65 | 66 | ExAcquireResourceSharedLite(&pdo->lock, true); 67 | 68 | if (pdo->array_info.level == RAID_LEVEL_5 && pdo->array_info.layout != RAID_LAYOUT_LEFT_SYMMETRIC && 69 | pdo->array_info.layout != RAID_LAYOUT_RIGHT_SYMMETRIC && pdo->array_info.layout != RAID_LAYOUT_LEFT_ASYMMETRIC && 70 | pdo->array_info.layout != RAID_LAYOUT_RIGHT_ASYMMETRIC) { 71 | Status = STATUS_INVALID_DEVICE_REQUEST; 72 | goto end2; 73 | } 74 | 75 | asymmetric = pdo->array_info.level == RAID_LEVEL_5 && (pdo->array_info.layout == RAID_LAYOUT_LEFT_ASYMMETRIC || pdo->array_info.layout == RAID_LAYOUT_RIGHT_ASYMMETRIC); 76 | 77 | if (pdo->array_info.chunksize == 0 || (pdo->array_info.chunksize * 512) % PAGE_SIZE != 0) { 78 | Status = STATUS_INTERNAL_ERROR; 79 | goto end2; 80 | } 81 | 82 | stripe_length = pdo->array_info.chunksize * 512; 83 | 84 | get_raid0_offset(offset, stripe_length, pdo->array_info.raid_disks - 1, &startoff, &startoffstripe); 85 | get_raid0_offset(offset + length - 1, stripe_length, pdo->array_info.raid_disks - 1, &endoff, &endoffstripe); 86 | 87 | start_chunk = offset / stripe_length; 88 | end_chunk = (offset + length - 1) / stripe_length; 89 | 90 | if (start_chunk == end_chunk) { // small reads, on one device 91 | uint32_t parity = get_parity_volume(pdo, offset); 92 | uint32_t disk_num = get_physical_stripe(pdo, startoffstripe, parity); 93 | 94 | set_child* c = pdo->child_list[disk_num]; 95 | 96 | IoCopyCurrentIrpStackLocationToNext(Irp); 97 | 98 | PIO_STACK_LOCATION IrpSp2 = IoGetNextIrpStackLocation(Irp); 99 | 100 | uint64_t start = (start_chunk / (pdo->array_info.raid_disks - 1)) * stripe_length; 101 | 102 | start += offset % stripe_length; 103 | start += c->disk_info.data_offset * 512; 104 | 105 | IrpSp2->FileObject = c->fileobj; 106 | IrpSp2->Parameters.Read.ByteOffset.QuadPart = start; 107 | 108 | *no_complete = true; 109 | 110 | Status = IoCallDriver(c->device, Irp); 111 | goto end2; 112 | } 113 | 114 | skip_first = offset % PAGE_SIZE; 115 | 116 | startoff -= skip_first; 117 | offset -= skip_first; 118 | length += skip_first; 119 | 120 | ctxs = ExAllocatePoolWithTag(NonPagedPool, sizeof(io_context_raid45) * pdo->array_info.raid_disks, ALLOC_TAG); 121 | if (!ctxs) { 122 | ERR("out of memory\n"); 123 | Status = STATUS_INSUFFICIENT_RESOURCES; 124 | goto end2; 125 | } 126 | 127 | RtlZeroMemory(ctxs, sizeof(io_context_raid45) * pdo->array_info.raid_disks); 128 | 129 | need_dummy = false; 130 | 131 | pos = 0; 132 | while (pos < length) { 133 | uint32_t parity = get_parity_volume(pdo, offset + pos); 134 | 135 | if (pos == 0) { 136 | uint32_t stripe = get_physical_stripe(pdo, startoffstripe, parity); 137 | 138 | for (uint32_t i = startoffstripe; i < pdo->array_info.raid_disks - 1; i++) { 139 | if (i == startoffstripe) { 140 | uint32_t readlen = min(length, (uint32_t)(stripe_length - (startoff % stripe_length))); 141 | 142 | ctxs[stripe].stripe_start = startoff; 143 | ctxs[stripe].stripe_end = startoff + readlen; 144 | 145 | pos += readlen; 146 | 147 | if (pos == length) 148 | break; 149 | } else { 150 | uint32_t readlen = min(length - pos, (uint32_t)stripe_length); 151 | 152 | ctxs[stripe].stripe_start = startoff - (startoff % stripe_length); 153 | ctxs[stripe].stripe_end = ctxs[stripe].stripe_start + readlen; 154 | 155 | pos += readlen; 156 | 157 | if (pos == length) 158 | break; 159 | } 160 | 161 | if (asymmetric) { 162 | stripe++; 163 | 164 | if (stripe == parity) 165 | stripe++; 166 | } else 167 | stripe = (stripe + 1) % pdo->array_info.raid_disks; 168 | } 169 | 170 | if (pos == length) 171 | break; 172 | 173 | for (uint32_t i = 0; i < startoffstripe; i++) { 174 | uint32_t stripe2 = get_physical_stripe(pdo, i, parity); 175 | 176 | ctxs[stripe2].stripe_start = ctxs[stripe2].stripe_end = startoff - (startoff % stripe_length) + stripe_length; 177 | } 178 | 179 | ctxs[parity].stripe_start = ctxs[parity].stripe_end = startoff - (startoff % stripe_length) + stripe_length; 180 | 181 | if (length - pos > pdo->array_info.raid_disks * (pdo->array_info.raid_disks - 1) * stripe_length) { 182 | uint32_t skip = (uint32_t)(((length - pos) / (pdo->array_info.raid_disks * (pdo->array_info.raid_disks - 1) * stripe_length)) - 1); 183 | 184 | for (uint32_t i = 0; i < pdo->array_info.raid_disks; i++) { 185 | ctxs[i].stripe_end += skip * pdo->array_info.raid_disks * stripe_length; 186 | } 187 | 188 | pos += (uint32_t)(skip * (pdo->array_info.raid_disks - 1) * pdo->array_info.raid_disks * stripe_length); 189 | need_dummy = true; 190 | } 191 | } else if (length - pos >= stripe_length * (pdo->array_info.raid_disks - 1)) { 192 | for (uint32_t i = 0; i < pdo->array_info.raid_disks; i++) { 193 | ctxs[i].stripe_end += stripe_length; 194 | } 195 | 196 | pos += (uint32_t)(stripe_length * (pdo->array_info.raid_disks - 1)); 197 | need_dummy = true; 198 | } else { 199 | uint32_t stripe = get_physical_stripe(pdo, 0, parity); 200 | 201 | for (uint32_t i = 0; i < pdo->array_info.raid_disks - 1; i++) { 202 | if (endoffstripe == i) { 203 | ctxs[stripe].stripe_end = endoff + 1; 204 | break; 205 | } else if (endoffstripe > i) 206 | ctxs[stripe].stripe_end = endoff - (endoff % stripe_length) + stripe_length; 207 | 208 | if (asymmetric) { 209 | stripe++; 210 | 211 | if (stripe == parity) 212 | stripe++; 213 | } else 214 | stripe = (stripe + 1) % pdo->array_info.raid_disks; 215 | } 216 | 217 | break; 218 | } 219 | } 220 | 221 | for (unsigned int i = 0; i < pdo->array_info.raid_disks; i++) { 222 | if (ctxs[i].stripe_end != ctxs[i].stripe_start) { 223 | ctxs[i].Irp = IoAllocateIrp(pdo->child_list[i]->device->StackSize, false); 224 | 225 | if (!ctxs[i].Irp) { 226 | ERR("IoAllocateIrp failed\n"); 227 | Status = STATUS_INSUFFICIENT_RESOURCES; 228 | goto end; 229 | } 230 | 231 | PIO_STACK_LOCATION IrpSp2 = IoGetNextIrpStackLocation(ctxs[i].Irp); 232 | IrpSp2->MajorFunction = IRP_MJ_READ; 233 | 234 | ctxs[i].mdl = IoAllocateMdl(NULL, (ULONG)(ctxs[i].stripe_end - ctxs[i].stripe_start), false, false, NULL); 235 | if (!ctxs[i].mdl) { 236 | ERR("IoAllocateMdl failed\n"); 237 | Status = STATUS_INSUFFICIENT_RESOURCES; 238 | goto end; 239 | } 240 | 241 | ctxs[i].mdl->MdlFlags |= MDL_PARTIAL; 242 | 243 | ctxs[i].Irp->MdlAddress = ctxs[i].mdl; 244 | 245 | IrpSp2->FileObject = pdo->child_list[i]->fileobj; 246 | IrpSp2->Parameters.Read.Length = (ULONG)(ctxs[i].stripe_end - ctxs[i].stripe_start); 247 | IrpSp2->Parameters.Read.ByteOffset.QuadPart = ctxs[i].stripe_start + (pdo->child_list[i]->disk_info.data_offset * 512); 248 | 249 | ctxs[i].Irp->UserIosb = &ctxs[i].iosb; 250 | 251 | KeInitializeEvent(&ctxs[i].Event, NotificationEvent, false); 252 | ctxs[i].Irp->UserEvent = &ctxs[i].Event; 253 | 254 | IoSetCompletionRoutine(ctxs[i].Irp, io_completion_raid45, &ctxs[i], true, true, true); 255 | } else 256 | ctxs[i].Status = STATUS_SUCCESS; 257 | } 258 | 259 | mdl_locked = Irp->MdlAddress->MdlFlags & (MDL_PAGES_LOCKED | MDL_PARTIAL); 260 | 261 | if (!mdl_locked) { 262 | Status = STATUS_SUCCESS; 263 | 264 | try { 265 | MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess); 266 | } except (EXCEPTION_EXECUTE_HANDLER) { 267 | Status = GetExceptionCode(); 268 | } 269 | 270 | if (!NT_SUCCESS(Status)) { 271 | ERR("MmProbeAndLockPages threw exception %08x\n", Status); 272 | mdl_locked = true; 273 | goto end; 274 | } 275 | } 276 | 277 | if (Irp->MdlAddress->ByteOffset != 0 || skip_first != 0) { 278 | tmpbuf = ExAllocatePoolWithTag(NonPagedPool, length, ALLOC_TAG); 279 | if (!tmpbuf) { 280 | ERR("out of memory\n"); 281 | Status = STATUS_INSUFFICIENT_RESOURCES; 282 | goto end; 283 | } 284 | 285 | tmpmdl = IoAllocateMdl(tmpbuf, length, false, false, NULL); 286 | if (!tmpmdl) { 287 | ERR("IoAllocateMdl failed\n"); 288 | Status = STATUS_INSUFFICIENT_RESOURCES; 289 | goto end; 290 | } 291 | 292 | MmBuildMdlForNonPagedPool(tmpmdl); 293 | } 294 | 295 | { 296 | uint32_t pos = 0; 297 | PFN_NUMBER dummy; 298 | 299 | if (need_dummy) { 300 | dummypage = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, ALLOC_TAG); 301 | if (!dummypage) { 302 | ERR("out of memory\n"); 303 | Status = STATUS_INSUFFICIENT_RESOURCES; 304 | goto end; 305 | } 306 | 307 | dummy_mdl = IoAllocateMdl(dummypage, PAGE_SIZE, FALSE, FALSE, NULL); 308 | if (!dummy_mdl) { 309 | ERR("IoAllocateMdl failed\n"); 310 | Status = STATUS_INSUFFICIENT_RESOURCES; 311 | goto end; 312 | } 313 | 314 | MmBuildMdlForNonPagedPool(dummy_mdl); 315 | 316 | dummy = *(PFN_NUMBER*)(dummy_mdl + 1); 317 | } 318 | 319 | MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); 320 | 321 | for (unsigned int i = 0; i < pdo->array_info.raid_disks; i++) { 322 | if (ctxs[i].mdl) 323 | ctxs[i].pfnp = ctxs[i].pfns = MmGetMdlPfnArray(ctxs[i].mdl); 324 | } 325 | 326 | PPFN_NUMBER src_pfns = MmGetMdlPfnArray((tmpmdl ? tmpmdl : Irp->MdlAddress)); 327 | 328 | while (pos < length) { 329 | uint32_t parity = get_parity_volume(pdo, offset + pos); 330 | 331 | if (pos == 0) { 332 | uint32_t stripe = get_physical_stripe(pdo, startoffstripe, parity); 333 | 334 | for (uint32_t i = startoffstripe; i < pdo->array_info.raid_disks - 1; i++) { 335 | uint32_t len, pages; 336 | 337 | if (pos == 0) { 338 | len = stripe_length - (startoff % stripe_length); 339 | 340 | if (len % PAGE_SIZE != 0) { 341 | pages = len / PAGE_SIZE; 342 | pages++; 343 | } else 344 | pages = len / PAGE_SIZE; 345 | } else { 346 | len = stripe_length; 347 | pages = len / PAGE_SIZE; 348 | } 349 | 350 | if (pos + len > length) { 351 | len = length - pos; 352 | 353 | if (len % PAGE_SIZE != 0) { 354 | pages = len / PAGE_SIZE; 355 | pages++; 356 | } else 357 | pages = len / PAGE_SIZE; 358 | } 359 | 360 | RtlCopyMemory(ctxs[stripe].pfnp, src_pfns, sizeof(PFN_NUMBER) * pages); 361 | src_pfns = &src_pfns[pages]; 362 | ctxs[stripe].pfnp = &ctxs[stripe].pfnp[pages]; 363 | 364 | pos += len; 365 | 366 | if (asymmetric) { 367 | stripe++; 368 | 369 | if (stripe == parity) 370 | stripe++; 371 | } else 372 | stripe = (stripe + 1) % pdo->array_info.raid_disks; 373 | } 374 | } else if (length - pos >= stripe_length * (pdo->array_info.raid_disks - 1)) { 375 | uint32_t stripe = get_physical_stripe(pdo, 0, parity); 376 | uint32_t pages = stripe_length / PAGE_SIZE; 377 | 378 | for (uint32_t i = 0; i < pdo->array_info.raid_disks - 1; i++) { 379 | RtlCopyMemory(ctxs[stripe].pfnp, src_pfns, sizeof(PFN_NUMBER) * pages); 380 | src_pfns = &src_pfns[pages]; 381 | ctxs[stripe].pfnp = &ctxs[stripe].pfnp[pages]; 382 | 383 | pos += stripe_length; 384 | 385 | if (asymmetric) { 386 | stripe++; 387 | 388 | if (stripe == parity) 389 | stripe++; 390 | } else 391 | stripe = (stripe + 1) % pdo->array_info.raid_disks; 392 | } 393 | 394 | for (uint32_t k = 0; k < stripe_length / PAGE_SIZE; k++) { 395 | ctxs[parity].pfnp[0] = dummy; 396 | ctxs[parity].pfnp = &ctxs[parity].pfnp[1]; 397 | } 398 | } else { 399 | uint32_t stripe = get_physical_stripe(pdo, 0, parity); 400 | 401 | for (uint32_t i = 0; i < pdo->array_info.raid_disks - 1; i++) { 402 | uint32_t readlen, pages; 403 | 404 | if (length - pos < stripe_length) { 405 | readlen = length - pos; 406 | pages = readlen / PAGE_SIZE; 407 | 408 | if ((length - pos) % PAGE_SIZE != 0) 409 | pages++; 410 | } else { 411 | readlen = stripe_length; 412 | pages = stripe_length / PAGE_SIZE; 413 | } 414 | 415 | RtlCopyMemory(ctxs[stripe].pfnp, src_pfns, sizeof(PFN_NUMBER) * pages); 416 | src_pfns = &src_pfns[pages]; 417 | ctxs[stripe].pfnp = &ctxs[stripe].pfnp[pages]; 418 | 419 | pos += readlen; 420 | 421 | if (pos == length) 422 | break; 423 | 424 | if (asymmetric) { 425 | stripe++; 426 | 427 | if (stripe == parity) 428 | stripe++; 429 | } else 430 | stripe = (stripe + 1) % pdo->array_info.raid_disks; 431 | } 432 | } 433 | } 434 | } 435 | 436 | for (unsigned int i = 0; i < pdo->array_info.raid_disks; i++) { 437 | if (ctxs[i].Irp) { 438 | ctxs[i].Status = IoCallDriver(pdo->child_list[i]->device, ctxs[i].Irp); 439 | if (!NT_SUCCESS(ctxs[i].Status)) 440 | ERR("IoCallDriver returned %08x\n", ctxs[i].Status); 441 | } 442 | } 443 | 444 | Status = STATUS_SUCCESS; 445 | 446 | for (unsigned int i = 0; i < pdo->array_info.raid_disks; i++) { 447 | if (ctxs[i].Status == STATUS_PENDING) { 448 | KeWaitForSingleObject(&ctxs[i].Event, Executive, KernelMode, false, NULL); 449 | ctxs[i].Status = ctxs[i].iosb.Status; 450 | } 451 | 452 | if (!NT_SUCCESS(ctxs[i].Status)) 453 | Status = ctxs[i].Status; 454 | } 455 | 456 | if (tmpbuf) { 457 | PVOID dest = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); 458 | 459 | RtlCopyMemory(dest, tmpbuf + skip_first, length - skip_first); 460 | } 461 | 462 | end: 463 | if (!mdl_locked) 464 | MmUnlockPages(Irp->MdlAddress); 465 | 466 | if (dummy_mdl) 467 | IoFreeMdl(dummy_mdl); 468 | 469 | if (dummypage) 470 | ExFreePool(dummypage); 471 | 472 | for (unsigned int i = 0; i < pdo->array_info.raid_disks; i++) { 473 | if (ctxs[i].mdl) 474 | IoFreeMdl(ctxs[i].mdl); 475 | 476 | if (ctxs[i].va) 477 | ExFreePool(ctxs[i].va); 478 | 479 | if (ctxs[i].Irp) 480 | IoFreeIrp(ctxs[i].Irp); 481 | } 482 | 483 | ExFreePool(ctxs); 484 | 485 | if (tmpmdl) 486 | IoFreeMdl(tmpmdl); 487 | 488 | if (tmpbuf) 489 | ExFreePool(tmpbuf); 490 | 491 | end2: 492 | ExReleaseResourceLite(&pdo->lock); 493 | 494 | return Status; 495 | } 496 | 497 | #ifdef DEBUG_PARANOID 498 | static void paranoid_raid5_check(set_pdo* pdo, uint64_t parity_offset, uint32_t parity_length) { 499 | uint32_t data_disks = pdo->array_info.raid_disks - 1; 500 | uint64_t read_offset = parity_offset / data_disks; 501 | LIST_ENTRY ctxs; 502 | LIST_ENTRY* le; 503 | io_context_raid45* first; 504 | 505 | parity_length /= data_disks; 506 | 507 | InitializeListHead(&ctxs); 508 | 509 | for (uint32_t i = 0; i < pdo->array_info.raid_disks; i++) { 510 | io_context_raid45* last = ExAllocatePoolWithTag(NonPagedPool, sizeof(io_context_raid45), ALLOC_TAG); 511 | if (!last) { 512 | ERR("out of memory\n"); 513 | goto end; 514 | } 515 | 516 | last->sc = pdo->child_list[i]; 517 | last->stripe_start = read_offset + (pdo->child_list[i]->disk_info.data_offset * 512); 518 | last->stripe_end = parity_length; 519 | 520 | last->Irp = IoAllocateIrp(last->sc->device->StackSize, false); 521 | if (!last->Irp) { 522 | ERR("out of memory\n"); 523 | ExFreePool(last); 524 | goto end; 525 | } 526 | 527 | last->Irp->UserIosb = &last->iosb; 528 | 529 | KeInitializeEvent(&last->Event, NotificationEvent, false); 530 | last->Irp->UserEvent = &last->Event; 531 | 532 | IoSetCompletionRoutine(last->Irp, io_completion_raid45, last, true, true, true); 533 | 534 | last->Status = STATUS_SUCCESS; 535 | 536 | last->va = NULL; 537 | last->mdl = NULL; 538 | 539 | InsertTailList(&ctxs, &last->list_entry); 540 | 541 | last->va = ExAllocatePoolWithTag(NonPagedPool, parity_length, ALLOC_TAG); 542 | if (!last->va) { 543 | ERR("out of memory\n"); 544 | goto end; 545 | } 546 | } 547 | 548 | le = ctxs.Flink; 549 | while (le != &ctxs) { 550 | io_context_raid45* ctx = CONTAINING_RECORD(le, io_context_raid45, list_entry); 551 | 552 | PIO_STACK_LOCATION IrpSp = IoGetNextIrpStackLocation(ctx->Irp); 553 | IrpSp->MajorFunction = IRP_MJ_READ; 554 | 555 | ctx->mdl = IoAllocateMdl(ctx->va, parity_length, false, false, NULL); 556 | if (!ctx->mdl) { 557 | ERR("IoAllocateMdl failed\n"); 558 | goto end; 559 | } 560 | 561 | MmBuildMdlForNonPagedPool(ctx->mdl); 562 | 563 | ctx->Irp->MdlAddress = ctx->mdl; 564 | 565 | IrpSp->FileObject = ctx->sc->fileobj; 566 | IrpSp->Parameters.Read.ByteOffset.QuadPart = ctx->stripe_start; 567 | IrpSp->Parameters.Read.Length = parity_length; 568 | 569 | ctx->Status = IoCallDriver(ctx->sc->device, ctx->Irp); 570 | 571 | le = le->Flink; 572 | } 573 | 574 | le = ctxs.Flink; 575 | while (le != &ctxs) { 576 | io_context_raid45* ctx = CONTAINING_RECORD(le, io_context_raid45, list_entry); 577 | 578 | if (ctx->Status == STATUS_PENDING) { 579 | KeWaitForSingleObject(&ctx->Event, Executive, KernelMode, false, NULL); 580 | ctx->Status = ctx->iosb.Status; 581 | } 582 | 583 | if (!NT_SUCCESS(ctx->Status)) 584 | ERR("writing returned %08x\n", ctx->Status); 585 | 586 | le = le->Flink; 587 | } 588 | 589 | le = ctxs.Flink; 590 | 591 | first = CONTAINING_RECORD(le, io_context_raid45, list_entry); 592 | 593 | le = le->Flink; 594 | while (le != &ctxs) { 595 | io_context_raid45* ctx = CONTAINING_RECORD(le, io_context_raid45, list_entry); 596 | 597 | do_xor((uint8_t*)first->va, (uint8_t*)ctx->va, parity_length); 598 | 599 | le = le->Flink; 600 | } 601 | 602 | for (unsigned int i = 0; i < parity_length; i++) { 603 | if (((uint8_t*)first->va)[i] != 0) { 604 | ERR("parity error\n"); 605 | __debugbreak(); 606 | } 607 | } 608 | 609 | end: 610 | while (!IsListEmpty(&ctxs)) { 611 | io_context_raid45* ctx = CONTAINING_RECORD(RemoveHeadList(&ctxs), io_context_raid45, list_entry); 612 | 613 | if (ctx->mdl) 614 | IoFreeMdl(ctx->mdl); 615 | 616 | if (ctx->va) 617 | ExFreePool(ctx->va); 618 | 619 | if (ctx->Irp) 620 | IoFreeIrp(ctx->Irp); 621 | 622 | ExFreePool(ctx); 623 | } 624 | } 625 | #endif 626 | 627 | NTSTATUS write_raid45(set_pdo* pdo, PIRP Irp, bool* no_complete) { 628 | PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 629 | NTSTATUS Status; 630 | uint64_t offset = IrpSp->Parameters.Write.ByteOffset.QuadPart, parity_offset = offset; 631 | uint32_t length = IrpSp->Parameters.Write.Length, parity_length = length; 632 | uint8_t* data; 633 | uint8_t* parity_data = NULL; 634 | PMDL parity_mdl = NULL; 635 | uint8_t* tmpbuf = NULL; 636 | PMDL tmpmdl = NULL; 637 | 638 | if (pdo->array_info.level == RAID_LEVEL_5 && pdo->array_info.layout != RAID_LAYOUT_LEFT_SYMMETRIC && 639 | pdo->array_info.layout != RAID_LAYOUT_RIGHT_SYMMETRIC && pdo->array_info.layout != RAID_LAYOUT_LEFT_ASYMMETRIC && 640 | pdo->array_info.layout != RAID_LAYOUT_RIGHT_ASYMMETRIC) 641 | return STATUS_INVALID_DEVICE_REQUEST; 642 | 643 | bool asymmetric = pdo->array_info.level == RAID_LEVEL_5 && (pdo->array_info.layout == RAID_LAYOUT_LEFT_ASYMMETRIC || pdo->array_info.layout == RAID_LAYOUT_RIGHT_ASYMMETRIC); 644 | 645 | if (pdo->array_info.chunksize == 0 || (pdo->array_info.chunksize * 512) % PAGE_SIZE != 0) 646 | return STATUS_INTERNAL_ERROR; 647 | 648 | if ((offset % 512) != 0 || (length % 512) != 0) 649 | return STATUS_INVALID_PARAMETER; 650 | 651 | uint32_t full_chunk = pdo->array_info.chunksize * 512 * (pdo->array_info.raid_disks - 1); 652 | bool mdl_locked = Irp->MdlAddress->MdlFlags & (MDL_PAGES_LOCKED | MDL_PARTIAL); 653 | io_context_raid45* ctxs = NULL; 654 | uint64_t startoff, endoff, start_chunk, end_chunk; 655 | uint32_t startoffstripe, endoffstripe, stripe_length, pos; 656 | uint32_t skip_first = offset % PAGE_SIZE ? (PAGE_SIZE - (offset % PAGE_SIZE)) : 0; 657 | io_context_raid45 first_bit; 658 | 659 | first_bit.Irp = NULL; 660 | first_bit.va = NULL; 661 | first_bit.mdl = NULL; 662 | 663 | if (!mdl_locked) { 664 | Status = STATUS_SUCCESS; 665 | 666 | try { 667 | MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoReadAccess); 668 | } except (EXCEPTION_EXECUTE_HANDLER) { 669 | Status = GetExceptionCode(); 670 | } 671 | 672 | if (!NT_SUCCESS(Status)) { 673 | ERR("MmProbeAndLockPages threw exception %08x\n", Status); 674 | mdl_locked = true; 675 | goto end; 676 | } 677 | } 678 | 679 | data = (uint8_t*)MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); 680 | 681 | if (offset % full_chunk != 0) { 682 | Status = add_partial_chunk(pdo, offset, min(length, full_chunk - (offset % full_chunk)), data); 683 | if (!NT_SUCCESS(Status)) 684 | goto end; 685 | 686 | uint32_t skip_start = min(length, full_chunk - (offset % full_chunk)); 687 | parity_offset += skip_start; 688 | parity_length -= skip_start; 689 | } 690 | 691 | if (parity_length % full_chunk != 0) { 692 | // FIXME - don't call if covered by previous add_partial_chunk 693 | Status = add_partial_chunk(pdo, parity_offset + parity_length - (parity_length % full_chunk), parity_length % full_chunk, 694 | data + parity_offset - offset + parity_length - (parity_length % full_chunk)); 695 | if (!NT_SUCCESS(Status)) 696 | goto end; 697 | 698 | parity_length -= parity_length % full_chunk; 699 | } 700 | 701 | stripe_length = pdo->array_info.chunksize * 512; 702 | 703 | get_raid0_offset(offset, stripe_length, pdo->array_info.raid_disks - 1, &startoff, &startoffstripe); 704 | get_raid0_offset(offset + length - 1, stripe_length, pdo->array_info.raid_disks - 1, &endoff, &endoffstripe); 705 | 706 | start_chunk = offset / stripe_length; 707 | end_chunk = (offset + length - 1) / stripe_length; 708 | 709 | if (start_chunk == end_chunk) { // small write, on one device 710 | uint32_t parity = get_parity_volume(pdo, offset); 711 | uint32_t disk_num = get_physical_stripe(pdo, startoffstripe, parity); 712 | 713 | set_child* c = pdo->child_list[disk_num]; 714 | 715 | IoCopyCurrentIrpStackLocationToNext(Irp); 716 | 717 | PIO_STACK_LOCATION IrpSp2 = IoGetNextIrpStackLocation(Irp); 718 | 719 | uint64_t start = (start_chunk / (pdo->array_info.raid_disks - 1)) * stripe_length; 720 | 721 | start += offset % stripe_length; 722 | start += c->disk_info.data_offset * 512; 723 | 724 | IrpSp2->FileObject = c->fileobj; 725 | IrpSp2->Parameters.Write.ByteOffset.QuadPart = start; 726 | 727 | *no_complete = true; 728 | 729 | return IoCallDriver(c->device, Irp); 730 | } 731 | 732 | if (skip_first != 0) { 733 | uint32_t parity = get_parity_volume(pdo, offset); 734 | uint32_t disk_num = get_physical_stripe(pdo, startoffstripe, parity); 735 | first_bit.sc = pdo->child_list[disk_num]; 736 | first_bit.Irp = IoAllocateIrp(first_bit.sc->device->StackSize, false); 737 | 738 | if (!first_bit.Irp) { 739 | ERR("IoAllocateIrp failed\n"); 740 | Status = STATUS_INSUFFICIENT_RESOURCES; 741 | goto end; 742 | } 743 | 744 | PIO_STACK_LOCATION IrpSp2 = IoGetNextIrpStackLocation(first_bit.Irp); 745 | IrpSp2->MajorFunction = IRP_MJ_WRITE; 746 | 747 | PVOID addr = MmGetMdlVirtualAddress(Irp->MdlAddress); 748 | 749 | first_bit.mdl = IoAllocateMdl(addr, skip_first, false, false, NULL); 750 | if (!first_bit.mdl) { 751 | ERR("IoAllocateMdl failed\n"); 752 | Status = STATUS_INSUFFICIENT_RESOURCES; 753 | goto end; 754 | } 755 | 756 | IoBuildPartialMdl(Irp->MdlAddress, first_bit.mdl, addr, skip_first); 757 | 758 | first_bit.Irp->MdlAddress = first_bit.mdl; 759 | 760 | uint64_t start = (start_chunk / (pdo->array_info.raid_disks - 1)) * stripe_length; 761 | 762 | start += offset % stripe_length; 763 | start += first_bit.sc->disk_info.data_offset * 512; 764 | 765 | IrpSp2->FileObject = first_bit.sc->fileobj; 766 | IrpSp2->Parameters.Write.Length = skip_first; 767 | IrpSp2->Parameters.Write.ByteOffset.QuadPart = start; 768 | 769 | first_bit.Irp->UserIosb = &first_bit.iosb; 770 | 771 | KeInitializeEvent(&first_bit.Event, NotificationEvent, false); 772 | first_bit.Irp->UserEvent = &first_bit.Event; 773 | 774 | IoSetCompletionRoutine(first_bit.Irp, io_completion_raid45, &first_bit, true, true, true); 775 | 776 | offset += skip_first; 777 | length -= skip_first; 778 | 779 | get_raid0_offset(offset, stripe_length, pdo->array_info.raid_disks - 1, &startoff, &startoffstripe); 780 | } 781 | 782 | ctxs = ExAllocatePoolWithTag(NonPagedPool, sizeof(io_context_raid45) * pdo->array_info.raid_disks, ALLOC_TAG); 783 | if (!ctxs) { 784 | ERR("out of memory\n"); 785 | Status = STATUS_INSUFFICIENT_RESOURCES; 786 | goto end; 787 | } 788 | 789 | RtlZeroMemory(ctxs, sizeof(io_context_raid45) * pdo->array_info.raid_disks); 790 | 791 | pos = 0; 792 | while (pos < length) { 793 | uint32_t parity = get_parity_volume(pdo, offset + pos); 794 | 795 | if (pos == 0) { 796 | uint32_t stripe = get_physical_stripe(pdo, startoffstripe, parity); 797 | 798 | ctxs[stripe].first = true; 799 | 800 | for (uint32_t i = startoffstripe; i < pdo->array_info.raid_disks - 1; i++) { 801 | if (i == startoffstripe) { 802 | uint32_t readlen = min(length, (uint32_t)(stripe_length - (startoff % stripe_length))); 803 | 804 | ctxs[stripe].stripe_start = startoff; 805 | ctxs[stripe].stripe_end = startoff + readlen; 806 | 807 | pos += readlen; 808 | } else { 809 | uint32_t readlen = min(length - pos, (uint32_t)stripe_length); 810 | 811 | ctxs[stripe].stripe_start = startoff - (startoff % stripe_length); 812 | ctxs[stripe].stripe_end = ctxs[stripe].stripe_start + readlen; 813 | 814 | pos += readlen; 815 | } 816 | 817 | if (pos == length) 818 | break; 819 | 820 | if (asymmetric) { 821 | stripe++; 822 | 823 | if (stripe == parity) 824 | stripe++; 825 | } else 826 | stripe = (stripe + 1) % pdo->array_info.raid_disks; 827 | } 828 | 829 | for (uint32_t i = 0; i < startoffstripe; i++) { 830 | uint32_t stripe2 = get_physical_stripe(pdo, i, parity); 831 | 832 | ctxs[stripe2].stripe_start = ctxs[stripe2].stripe_end = startoff - (startoff % stripe_length) + stripe_length; 833 | } 834 | 835 | { 836 | uint64_t v = parity_offset / (pdo->array_info.raid_disks - 1); 837 | 838 | if (v % stripe_length != 0) { 839 | v += stripe_length - (startoff % stripe_length); 840 | ctxs[parity].stripe_start = ctxs[parity].stripe_end = v; 841 | } else { 842 | ctxs[parity].stripe_start = v; 843 | ctxs[parity].stripe_end = v + min(parity_length, stripe_length); 844 | } 845 | } 846 | 847 | if (length - pos > pdo->array_info.raid_disks * (pdo->array_info.raid_disks - 1) * stripe_length) { 848 | uint32_t skip = (uint32_t)(((length - pos) / (pdo->array_info.raid_disks * (pdo->array_info.raid_disks - 1) * stripe_length)) - 1); 849 | 850 | for (uint32_t i = 0; i < pdo->array_info.raid_disks; i++) { 851 | ctxs[i].stripe_end += skip * pdo->array_info.raid_disks * stripe_length; 852 | } 853 | 854 | pos += (uint32_t)(skip * (pdo->array_info.raid_disks - 1) * pdo->array_info.raid_disks * stripe_length); 855 | } 856 | } else if (length - pos >= stripe_length * (pdo->array_info.raid_disks - 1)) { 857 | for (uint32_t i = 0; i < pdo->array_info.raid_disks; i++) { 858 | ctxs[i].stripe_end += stripe_length; 859 | } 860 | 861 | pos += (uint32_t)(stripe_length * (pdo->array_info.raid_disks - 1)); 862 | } else { 863 | uint32_t stripe = get_physical_stripe(pdo, 0, parity); 864 | 865 | for (uint32_t i = 0; i < pdo->array_info.raid_disks - 1; i++) { 866 | if (endoffstripe == i) { 867 | ctxs[stripe].stripe_end = endoff + 1; 868 | break; 869 | } else if (endoffstripe > i) 870 | ctxs[stripe].stripe_end = endoff - (endoff % stripe_length) + stripe_length; 871 | 872 | if (asymmetric) { 873 | stripe++; 874 | 875 | if (stripe == parity) 876 | stripe++; 877 | } else 878 | stripe = (stripe + 1) % pdo->array_info.raid_disks; 879 | } 880 | 881 | break; 882 | } 883 | } 884 | 885 | for (unsigned int i = 0; i < pdo->array_info.raid_disks; i++) { 886 | if (ctxs[i].stripe_end != ctxs[i].stripe_start) { 887 | ctxs[i].Irp = IoAllocateIrp(pdo->child_list[i]->device->StackSize, false); 888 | 889 | if (!ctxs[i].Irp) { 890 | ERR("IoAllocateIrp failed\n"); 891 | Status = STATUS_INSUFFICIENT_RESOURCES; 892 | goto end; 893 | } 894 | 895 | PIO_STACK_LOCATION IrpSp2 = IoGetNextIrpStackLocation(ctxs[i].Irp); 896 | IrpSp2->MajorFunction = IRP_MJ_WRITE; 897 | 898 | ULONG mdl_length = (ULONG)(ctxs[i].stripe_end - ctxs[i].stripe_start); 899 | 900 | if (ctxs[i].first) 901 | mdl_length += startoff % PAGE_SIZE; 902 | 903 | ctxs[i].mdl = IoAllocateMdl(NULL, (ULONG)mdl_length, false, false, NULL); 904 | if (!ctxs[i].mdl) { 905 | ERR("IoAllocateMdl failed\n"); 906 | Status = STATUS_INSUFFICIENT_RESOURCES; 907 | goto end; 908 | } 909 | 910 | ctxs[i].mdl->MdlFlags |= MDL_PARTIAL; 911 | 912 | ctxs[i].Irp->MdlAddress = ctxs[i].mdl; 913 | 914 | IrpSp2->FileObject = pdo->child_list[i]->fileobj; 915 | IrpSp2->Parameters.Write.Length = (ULONG)(ctxs[i].stripe_end - ctxs[i].stripe_start); 916 | IrpSp2->Parameters.Write.ByteOffset.QuadPart = ctxs[i].stripe_start + (pdo->child_list[i]->disk_info.data_offset * 512); 917 | 918 | ctxs[i].Irp->UserIosb = &ctxs[i].iosb; 919 | 920 | KeInitializeEvent(&ctxs[i].Event, NotificationEvent, false); 921 | ctxs[i].Irp->UserEvent = &ctxs[i].Event; 922 | 923 | IoSetCompletionRoutine(ctxs[i].Irp, io_completion_raid45, &ctxs[i], true, true, true); 924 | } else 925 | ctxs[i].Status = STATUS_SUCCESS; 926 | } 927 | 928 | if (Irp->MdlAddress->ByteOffset != 0 || skip_first != 0) { 929 | tmpbuf = ExAllocatePoolWithTag(NonPagedPool, length, ALLOC_TAG); 930 | if (!tmpbuf) { 931 | ERR("out of memory\n"); 932 | Status = STATUS_INSUFFICIENT_RESOURCES; 933 | goto end; 934 | } 935 | 936 | tmpmdl = IoAllocateMdl(tmpbuf, length, false, false, NULL); 937 | if (!tmpmdl) { 938 | ERR("IoAllocateMdl failed\n"); 939 | Status = STATUS_INSUFFICIENT_RESOURCES; 940 | goto end; 941 | } 942 | 943 | MmBuildMdlForNonPagedPool(tmpmdl); 944 | 945 | RtlCopyMemory(tmpbuf, (uint8_t*)data + skip_first, length); 946 | } 947 | 948 | { 949 | pos = 0; 950 | 951 | uint8_t* pp = NULL; 952 | PFN_NUMBER* parity_pfns = NULL; 953 | 954 | if (parity_length > 0) { 955 | parity_data = ExAllocatePoolWithTag(NonPagedPool, parity_length, ALLOC_TAG); 956 | if (!parity_data) { 957 | ERR("out of memory\n"); 958 | Status = STATUS_INSUFFICIENT_RESOURCES; 959 | goto end; 960 | } 961 | 962 | parity_mdl = IoAllocateMdl(parity_data, parity_length, false, false, NULL); 963 | if (!parity_mdl) { 964 | ERR("IoAllocateMdl failed\n"); 965 | Status = STATUS_INSUFFICIENT_RESOURCES; 966 | goto end; 967 | } 968 | 969 | MmBuildMdlForNonPagedPool(parity_mdl); 970 | 971 | pp = parity_data; 972 | parity_pfns = MmGetMdlPfnArray(parity_mdl); 973 | } 974 | 975 | for (unsigned int i = 0; i < pdo->array_info.raid_disks; i++) { 976 | if (ctxs[i].mdl) 977 | ctxs[i].pfnp = ctxs[i].pfns = MmGetMdlPfnArray(ctxs[i].mdl); 978 | } 979 | 980 | uint8_t* addr = data; 981 | PPFN_NUMBER src_pfns = MmGetMdlPfnArray((tmpmdl ? tmpmdl : Irp->MdlAddress)); 982 | 983 | while (pos < length) { 984 | uint32_t parity = get_parity_volume(pdo, offset + pos); 985 | 986 | if (pos == 0 && offset != parity_offset) { 987 | uint32_t stripe = get_physical_stripe(pdo, startoffstripe, parity); 988 | 989 | for (uint32_t i = startoffstripe; i < pdo->array_info.raid_disks - 1; i++) { 990 | uint32_t writelen, pages; 991 | 992 | if (i == startoffstripe) 993 | writelen = min(length, (uint32_t)(stripe_length - (startoff % stripe_length))); 994 | else 995 | writelen = min(length - pos, (uint32_t)stripe_length); 996 | 997 | if (writelen % PAGE_SIZE != 0) { 998 | pages = writelen / PAGE_SIZE; 999 | pages++; 1000 | } else 1001 | pages = writelen / PAGE_SIZE; 1002 | 1003 | RtlCopyMemory(ctxs[stripe].pfnp, src_pfns, sizeof(PFN_NUMBER) * pages); 1004 | src_pfns = &src_pfns[pages]; 1005 | ctxs[stripe].pfnp = &ctxs[stripe].pfnp[pages]; 1006 | 1007 | pos += writelen; 1008 | addr += writelen; 1009 | 1010 | if (pos == length) 1011 | break; 1012 | 1013 | if (asymmetric) { 1014 | stripe++; 1015 | 1016 | if (stripe == parity) 1017 | stripe++; 1018 | } else 1019 | stripe = (stripe + 1) % pdo->array_info.raid_disks; 1020 | } 1021 | } else if (length - pos >= stripe_length * (pdo->array_info.raid_disks - 1)) { 1022 | uint32_t stripe = get_physical_stripe(pdo, 0, parity); 1023 | uint32_t pages = stripe_length / PAGE_SIZE; 1024 | bool first = true; 1025 | 1026 | for (uint32_t i = 0; i < pdo->array_info.raid_disks - 1; i++) { 1027 | if (first) { 1028 | RtlCopyMemory(pp, addr, stripe_length); 1029 | first = false; 1030 | } else 1031 | do_xor(pp, addr, stripe_length); 1032 | 1033 | pos += stripe_length; 1034 | addr += stripe_length; 1035 | 1036 | RtlCopyMemory(ctxs[stripe].pfnp, src_pfns, sizeof(PFN_NUMBER) * pages); 1037 | src_pfns = &src_pfns[pages]; 1038 | ctxs[stripe].pfnp = &ctxs[stripe].pfnp[pages]; 1039 | 1040 | if (asymmetric) { 1041 | stripe++; 1042 | 1043 | if (stripe == parity) 1044 | stripe++; 1045 | } else 1046 | stripe = (stripe + 1) % pdo->array_info.raid_disks; 1047 | } 1048 | 1049 | pp = &pp[stripe_length]; 1050 | 1051 | RtlCopyMemory(ctxs[parity].pfnp, parity_pfns, sizeof(PFN_NUMBER) * pages); 1052 | parity_pfns = &parity_pfns[pages]; 1053 | ctxs[parity].pfnp = &ctxs[parity].pfnp[pages]; 1054 | } else { 1055 | uint32_t stripe = get_physical_stripe(pdo, 0, parity); 1056 | 1057 | for (uint32_t i = 0; i < pdo->array_info.raid_disks - 1; i++) { 1058 | uint32_t writelen = min(length - pos, (uint32_t)stripe_length); 1059 | uint32_t pages = writelen / PAGE_SIZE; 1060 | 1061 | if (writelen % PAGE_SIZE != 0) 1062 | pages++; 1063 | 1064 | RtlCopyMemory(ctxs[stripe].pfnp, src_pfns, sizeof(PFN_NUMBER) * pages); 1065 | src_pfns = &src_pfns[pages]; 1066 | ctxs[stripe].pfnp = &ctxs[stripe].pfnp[pages]; 1067 | 1068 | pos += writelen; 1069 | 1070 | if (pos == length) 1071 | break; 1072 | 1073 | if (asymmetric) { 1074 | stripe++; 1075 | 1076 | if (stripe == parity) 1077 | stripe++; 1078 | } else 1079 | stripe = (stripe + 1) % pdo->array_info.raid_disks; 1080 | } 1081 | } 1082 | } 1083 | } 1084 | 1085 | for (unsigned int i = 0; i < pdo->array_info.raid_disks; i++) { 1086 | if (ctxs[i].Irp) { 1087 | ctxs[i].Status = IoCallDriver(pdo->child_list[i]->device, ctxs[i].Irp); 1088 | if (!NT_SUCCESS(ctxs[i].Status)) 1089 | ERR("IoCallDriver returned %08x\n", ctxs[i].Status); 1090 | } 1091 | } 1092 | 1093 | if (skip_first != 0) { 1094 | first_bit.Status = IoCallDriver(first_bit.sc->device, first_bit.Irp); 1095 | if (!NT_SUCCESS(first_bit.Status)) 1096 | ERR("IoCallDriver returned %08x\n", first_bit.Status); 1097 | } 1098 | 1099 | Status = STATUS_SUCCESS; 1100 | 1101 | for (unsigned int i = 0; i < pdo->array_info.raid_disks; i++) { 1102 | if (ctxs[i].Status == STATUS_PENDING) { 1103 | KeWaitForSingleObject(&ctxs[i].Event, Executive, KernelMode, false, NULL); 1104 | ctxs[i].Status = ctxs[i].iosb.Status; 1105 | } 1106 | 1107 | if (!NT_SUCCESS(ctxs[i].Status)) 1108 | Status = ctxs[i].Status; 1109 | } 1110 | 1111 | if (skip_first != 0) { 1112 | if (first_bit.Status == STATUS_PENDING) { 1113 | KeWaitForSingleObject(&first_bit.Event, Executive, KernelMode, false, NULL); 1114 | first_bit.Status = first_bit.iosb.Status; 1115 | } 1116 | 1117 | if (!NT_SUCCESS(first_bit.Status)) 1118 | Status = first_bit.Status; 1119 | } 1120 | 1121 | #ifdef DEBUG_PARANOID 1122 | if (parity_length != 0) 1123 | paranoid_raid5_check(pdo, parity_offset, parity_length); 1124 | #endif 1125 | 1126 | end: 1127 | if (!mdl_locked) 1128 | MmUnlockPages(Irp->MdlAddress); 1129 | 1130 | if (parity_mdl) 1131 | IoFreeMdl(parity_mdl); 1132 | 1133 | if (parity_data) 1134 | ExFreePool(parity_data); 1135 | 1136 | if (ctxs) { 1137 | for (unsigned int i = 0; i < pdo->array_info.raid_disks; i++) { 1138 | if (ctxs[i].mdl) 1139 | IoFreeMdl(ctxs[i].mdl); 1140 | 1141 | if (ctxs[i].va) 1142 | ExFreePool(ctxs[i].va); 1143 | 1144 | if (ctxs[i].Irp) 1145 | IoFreeIrp(ctxs[i].Irp); 1146 | } 1147 | 1148 | ExFreePool(ctxs); 1149 | } 1150 | 1151 | if (tmpmdl) 1152 | IoFreeMdl(tmpmdl); 1153 | 1154 | if (tmpbuf) 1155 | ExFreePool(tmpbuf); 1156 | 1157 | if (first_bit.mdl) 1158 | IoFreeMdl(first_bit.mdl); 1159 | 1160 | if (first_bit.va) 1161 | ExFreePool(first_bit.va); 1162 | 1163 | if (first_bit.Irp) 1164 | IoFreeIrp(first_bit.Irp); 1165 | 1166 | return Status; 1167 | } 1168 | 1169 | NTSTATUS flush_partial_chunk_raid45(set_pdo* pdo, partial_chunk* pc, RTL_BITMAP* valid_bmp) { 1170 | NTSTATUS Status; 1171 | LIST_ENTRY ctxs; 1172 | ULONG index; 1173 | ULONG runlength = RtlFindFirstRunClear(valid_bmp, &index); 1174 | uint32_t parity = get_parity_volume(pdo, pc->offset); 1175 | set_child* parity_dev = pdo->child_list[parity]; 1176 | uint32_t data_disks = pdo->array_info.raid_disks - 1; 1177 | uint32_t chunk_size = pdo->array_info.chunksize * 512; 1178 | 1179 | InitializeListHead(&ctxs); 1180 | 1181 | while (runlength != 0) { 1182 | for (uint32_t i = 1; i < data_disks; i++) { 1183 | do_xor(pc->data + (index * 512), pc->data + (i * chunk_size) + (index * 512), runlength * 512); 1184 | } 1185 | 1186 | uint64_t stripe_start = (pc->offset / data_disks) + (index * 512) + (parity_dev->disk_info.data_offset * 512); 1187 | 1188 | io_context_raid45* last = ExAllocatePoolWithTag(NonPagedPool, sizeof(io_context_raid45), ALLOC_TAG); 1189 | if (!last) { 1190 | ERR("out of memory\n"); 1191 | Status = STATUS_INSUFFICIENT_RESOURCES; 1192 | goto fail; 1193 | } 1194 | 1195 | last->sc = parity_dev; 1196 | last->stripe_start = stripe_start; 1197 | last->stripe_end = stripe_start + (runlength * 512); 1198 | 1199 | last->Irp = IoAllocateIrp(parity_dev->device->StackSize, false); 1200 | if (!last->Irp) { 1201 | ERR("out of memory\n"); 1202 | Status = STATUS_INSUFFICIENT_RESOURCES; 1203 | ExFreePool(last); 1204 | goto fail; 1205 | } 1206 | 1207 | last->Irp->UserIosb = &last->iosb; 1208 | 1209 | KeInitializeEvent(&last->Event, NotificationEvent, false); 1210 | last->Irp->UserEvent = &last->Event; 1211 | 1212 | IoSetCompletionRoutine(last->Irp, io_completion_raid45, last, true, true, true); 1213 | 1214 | last->Status = STATUS_SUCCESS; 1215 | 1216 | last->va = NULL; 1217 | last->mdl = NULL; 1218 | 1219 | InsertTailList(&ctxs, &last->list_entry); 1220 | 1221 | last->va2 = pc->data + (index * 512); 1222 | 1223 | runlength = RtlFindNextForwardRunClear(valid_bmp, index + runlength, &index); 1224 | } 1225 | 1226 | if (!IsListEmpty(&ctxs)) { 1227 | LIST_ENTRY* le = ctxs.Flink; 1228 | while (le != &ctxs) { 1229 | io_context_raid45* ctx = CONTAINING_RECORD(le, io_context_raid45, list_entry); 1230 | 1231 | PIO_STACK_LOCATION IrpSp = IoGetNextIrpStackLocation(ctx->Irp); 1232 | IrpSp->MajorFunction = IRP_MJ_WRITE; 1233 | 1234 | ctx->mdl = IoAllocateMdl(ctx->va2, (ULONG)(ctx->stripe_end - ctx->stripe_start), false, false, NULL); 1235 | if (!ctx->mdl) { 1236 | ERR("IoAllocateMdl failed\n"); 1237 | Status = STATUS_INSUFFICIENT_RESOURCES; 1238 | goto fail; 1239 | } 1240 | 1241 | MmBuildMdlForNonPagedPool(ctx->mdl); 1242 | 1243 | ctx->Irp->MdlAddress = ctx->mdl; 1244 | 1245 | IrpSp->FileObject = ctx->sc->fileobj; 1246 | IrpSp->Parameters.Write.ByteOffset.QuadPart = ctx->stripe_start; 1247 | IrpSp->Parameters.Write.Length = (ULONG)(ctx->stripe_end - ctx->stripe_start); 1248 | 1249 | ctx->Status = IoCallDriver(ctx->sc->device, ctx->Irp); 1250 | 1251 | le = le->Flink; 1252 | } 1253 | 1254 | Status = STATUS_SUCCESS; 1255 | 1256 | while (!IsListEmpty(&ctxs)) { 1257 | io_context_raid45* ctx = CONTAINING_RECORD(RemoveHeadList(&ctxs), io_context_raid45, list_entry); 1258 | 1259 | if (ctx->Status == STATUS_PENDING) { 1260 | KeWaitForSingleObject(&ctx->Event, Executive, KernelMode, false, NULL); 1261 | ctx->Status = ctx->iosb.Status; 1262 | } 1263 | 1264 | if (!NT_SUCCESS(ctx->Status)) { 1265 | ERR("writing returned %08x\n", ctx->Status); 1266 | Status = ctx->Status; 1267 | } 1268 | 1269 | if (ctx->mdl) 1270 | IoFreeMdl(ctx->mdl); 1271 | 1272 | if (ctx->va) 1273 | ExFreePool(ctx->va); 1274 | 1275 | if (ctx->Irp) 1276 | IoFreeIrp(ctx->Irp); 1277 | 1278 | ExFreePool(ctx); 1279 | } 1280 | 1281 | if (!NT_SUCCESS(Status)) 1282 | goto fail; 1283 | } 1284 | 1285 | return STATUS_SUCCESS; 1286 | 1287 | fail: 1288 | while (!IsListEmpty(&ctxs)) { 1289 | io_context_raid45* ctx = CONTAINING_RECORD(RemoveHeadList(&ctxs), io_context_raid45, list_entry); 1290 | 1291 | if (ctx->mdl) 1292 | IoFreeMdl(ctx->mdl); 1293 | 1294 | if (ctx->va) 1295 | ExFreePool(ctx->va); 1296 | 1297 | if (ctx->Irp) 1298 | IoFreeIrp(ctx->Irp); 1299 | 1300 | ExFreePool(ctx); 1301 | } 1302 | 1303 | return Status; 1304 | } 1305 | -------------------------------------------------------------------------------- /src/winmd.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Mark Harmstone 2019 2 | * 3 | * This file is part of WinMD. 4 | * 5 | * WinMD is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public Licence as published by 7 | * the Free Software Foundation, either version 3 of the Licence, or 8 | * (at your option) any later version. 9 | * 10 | * WinBtrfs is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Lesser General Public Licence for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public Licence 16 | * along with WinMD. If not, see . */ 17 | 18 | #include "winmd.h" 19 | #include 20 | 21 | #ifndef _MSC_VER 22 | #include 23 | #include 24 | #undef INITGUID 25 | #endif 26 | 27 | #include 28 | #include 29 | 30 | #ifdef _MSC_VER 31 | #include 32 | #include 33 | #undef INITGUID 34 | #endif 35 | 36 | #include 37 | #include 38 | #include 39 | 40 | #ifdef _MSC_VER 41 | #include 42 | #endif 43 | 44 | #include 45 | 46 | static const WCHAR device_name[] = L"\\WinMD"; 47 | 48 | DEFINE_GUID(WinMDBusInterface, 0x034d566e, 0x836b, 0x4e79, 0x96, 0x17, 0x60, 0x23, 0x58, 0x74, 0xc9, 0x08); 49 | 50 | #ifdef _DEBUG 51 | serial_logger* logger = NULL; 52 | #endif 53 | void *notification_entry = NULL, *notification_entry2 = NULL, *notification_entry3 = NULL; 54 | PDRIVER_OBJECT drvobj = NULL; 55 | PDEVICE_OBJECT master_devobj = NULL; 56 | bool have_sse2 = false; 57 | #ifdef _DEBUG 58 | uint32_t debug_log_level = 0; 59 | #endif 60 | 61 | ERESOURCE dev_lock; 62 | LIST_ENTRY dev_list; 63 | bool is_windows_8; 64 | 65 | extern bool no_pnp; 66 | 67 | typedef void (*pnp_callback)(PDRIVER_OBJECT DriverObject, PUNICODE_STRING devpath); 68 | static NTSTATUS dev_ioctl(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, ULONG ControlCode, PVOID InputBuffer, 69 | ULONG InputBufferSize, PVOID OutputBuffer, ULONG OutputBufferSize, bool Override, IO_STATUS_BLOCK* iosb); 70 | 71 | typedef struct { 72 | PDRIVER_OBJECT DriverObject; 73 | UNICODE_STRING name; 74 | pnp_callback func; 75 | PIO_WORKITEM work_item; 76 | } pnp_callback_context; 77 | 78 | _Function_class_(IO_WORKITEM_ROUTINE) 79 | static void __stdcall do_pnp_callback(PDEVICE_OBJECT devobj, PVOID con) { 80 | pnp_callback_context* context = (pnp_callback_context*)con; 81 | 82 | context->func(context->DriverObject, &context->name); 83 | 84 | if (context->name.Buffer) 85 | ExFreePool(context->name.Buffer); 86 | 87 | IoFreeWorkItem(context->work_item); 88 | } 89 | 90 | static void enqueue_pnp_callback(PDRIVER_OBJECT DriverObject, PUNICODE_STRING name, pnp_callback func) { 91 | PIO_WORKITEM work_item = IoAllocateWorkItem(master_devobj); 92 | 93 | pnp_callback_context* context = ExAllocatePoolWithTag(PagedPool, sizeof(pnp_callback_context), ALLOC_TAG); 94 | 95 | if (!context) { 96 | ERR("out of memory\n"); 97 | IoFreeWorkItem(work_item); 98 | return; 99 | } 100 | 101 | context->DriverObject = DriverObject; 102 | 103 | if (name->Length > 0) { 104 | context->name.Buffer = ExAllocatePoolWithTag(PagedPool, name->Length, ALLOC_TAG); 105 | if (!context->name.Buffer) { 106 | ERR("out of memory\n"); 107 | ExFreePool(context); 108 | IoFreeWorkItem(work_item); 109 | return; 110 | } 111 | 112 | RtlCopyMemory(context->name.Buffer, name->Buffer, name->Length); 113 | context->name.Length = context->name.MaximumLength = name->Length; 114 | } else { 115 | context->name.Length = context->name.MaximumLength = 0; 116 | context->name.Buffer = NULL; 117 | } 118 | 119 | context->func = func; 120 | context->work_item = work_item; 121 | 122 | IoQueueWorkItem(work_item, do_pnp_callback, DelayedWorkQueue, context); 123 | } 124 | 125 | typedef struct { 126 | KEVENT Event; 127 | IO_STATUS_BLOCK iosb; 128 | } read_context; 129 | 130 | _Function_class_(IO_COMPLETION_ROUTINE) 131 | static NTSTATUS __stdcall read_completion(PDEVICE_OBJECT devobj, PIRP Irp, PVOID conptr) { 132 | read_context* context = (read_context*)conptr; 133 | 134 | context->iosb = Irp->IoStatus; 135 | KeSetEvent(&context->Event, 0, FALSE); 136 | 137 | return STATUS_MORE_PROCESSING_REQUIRED; 138 | } 139 | 140 | static NTSTATUS sync_read_phys(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, uint64_t StartingOffset, uint32_t Length, 141 | uint8_t* Buffer, bool override) { 142 | IO_STATUS_BLOCK IoStatus; 143 | LARGE_INTEGER Offset; 144 | PIRP Irp; 145 | PIO_STACK_LOCATION IrpSp; 146 | NTSTATUS Status; 147 | read_context context; 148 | 149 | RtlZeroMemory(&context, sizeof(read_context)); 150 | KeInitializeEvent(&context.Event, NotificationEvent, FALSE); 151 | 152 | Offset.QuadPart = (LONGLONG)StartingOffset; 153 | 154 | Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE); 155 | 156 | if (!Irp) { 157 | ERR("IoAllocateIrp failed\n"); 158 | return STATUS_INSUFFICIENT_RESOURCES; 159 | } 160 | 161 | Irp->Flags |= IRP_NOCACHE; 162 | IrpSp = IoGetNextIrpStackLocation(Irp); 163 | IrpSp->FileObject = FileObject; 164 | IrpSp->MajorFunction = IRP_MJ_READ; 165 | 166 | if (override) 167 | IrpSp->Flags |= SL_OVERRIDE_VERIFY_VOLUME; 168 | 169 | if (DeviceObject->Flags & DO_BUFFERED_IO) { 170 | Irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag(NonPagedPool, Length, ALLOC_TAG); 171 | if (!Irp->AssociatedIrp.SystemBuffer) { 172 | ERR("out of memory\n"); 173 | Status = STATUS_INSUFFICIENT_RESOURCES; 174 | goto exit; 175 | } 176 | 177 | Irp->Flags |= IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER | IRP_INPUT_OPERATION; 178 | 179 | Irp->UserBuffer = Buffer; 180 | } else if (DeviceObject->Flags & DO_DIRECT_IO) { 181 | Irp->MdlAddress = IoAllocateMdl(Buffer, Length, FALSE, FALSE, NULL); 182 | if (!Irp->MdlAddress) { 183 | ERR("IoAllocateMdl failed\n"); 184 | Status = STATUS_INSUFFICIENT_RESOURCES; 185 | goto exit; 186 | } 187 | 188 | Status = STATUS_SUCCESS; 189 | 190 | try { 191 | MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoWriteAccess); 192 | } except (EXCEPTION_EXECUTE_HANDLER) { 193 | Status = GetExceptionCode(); 194 | } 195 | 196 | if (!NT_SUCCESS(Status)) { 197 | ERR("MmProbeAndLockPages threw exception %08x\n", Status); 198 | IoFreeMdl(Irp->MdlAddress); 199 | goto exit; 200 | } 201 | } else 202 | Irp->UserBuffer = Buffer; 203 | 204 | IrpSp->Parameters.Read.Length = Length; 205 | IrpSp->Parameters.Read.ByteOffset = Offset; 206 | 207 | Irp->UserIosb = &IoStatus; 208 | 209 | Irp->UserEvent = &context.Event; 210 | 211 | IoSetCompletionRoutine(Irp, read_completion, &context, TRUE, TRUE, TRUE); 212 | 213 | Status = IoCallDriver(DeviceObject, Irp); 214 | 215 | if (Status == STATUS_PENDING) { 216 | KeWaitForSingleObject(&context.Event, Executive, KernelMode, FALSE, NULL); 217 | Status = context.iosb.Status; 218 | } 219 | 220 | if (DeviceObject->Flags & DO_DIRECT_IO) { 221 | MmUnlockPages(Irp->MdlAddress); 222 | IoFreeMdl(Irp->MdlAddress); 223 | } 224 | 225 | exit: 226 | IoFreeIrp(Irp); 227 | 228 | return Status; 229 | } 230 | 231 | static WCHAR hex_digit(uint8_t c) { 232 | if (c < 10) 233 | return c + '0'; 234 | 235 | return c - 10 + 'a'; 236 | } 237 | 238 | // FIXME - make sure this gets called 239 | void unit_set_pdo(set_pdo* pdo) { 240 | if (pdo->child_list) 241 | ExFreePool(pdo->child_list); 242 | 243 | while (!IsListEmpty(&pdo->children)) { 244 | set_child* c = CONTAINING_RECORD(RemoveHeadList(&pdo->children), set_child, list_entry); 245 | 246 | ObDereferenceObject(c->fileobj); 247 | 248 | if (c->devpath.Buffer) 249 | ExFreePool(c->devpath.Buffer); 250 | 251 | ExFreePool(c); 252 | } 253 | 254 | if (pdo->bus_name.Buffer) 255 | ExFreePool(pdo->bus_name.Buffer); 256 | 257 | // FIXME - make sure partial chunks list is empty 258 | 259 | ExDeleteResourceLite(&pdo->lock); 260 | ExDeleteResourceLite(&pdo->partial_chunks_lock); 261 | } 262 | 263 | static void device_found(PDEVICE_OBJECT devobj, PFILE_OBJECT fileobj, PUNICODE_STRING devpath, mdraid_superblock* sb) { 264 | set_pdo* sd; 265 | 266 | set_child* c = ExAllocatePoolWithTag(NonPagedPool, sizeof(set_child), ALLOC_TAG); 267 | if (!c) { 268 | ERR("out of memory\n"); 269 | return; 270 | } 271 | 272 | c->device = devobj; 273 | c->fileobj = fileobj; 274 | 275 | ObReferenceObject(fileobj); 276 | 277 | RtlCopyMemory(&c->disk_info, &sb->disk_info, sizeof(mdraid_disk_info)); 278 | 279 | c->devpath.Length = c->devpath.MaximumLength = devpath->Length; 280 | 281 | if (devpath->Length > 0) { 282 | c->devpath.Buffer = ExAllocatePoolWithTag(NonPagedPool, c->devpath.Length, ALLOC_TAG); 283 | 284 | if (c->devpath.Buffer) 285 | RtlCopyMemory(c->devpath.Buffer, devpath->Buffer, c->devpath.Length); 286 | else { 287 | ERR("out of memory\n"); 288 | 289 | ObDereferenceObject(c->fileobj); 290 | 291 | if (c->devpath.Buffer) 292 | ExFreePool(c->devpath.Buffer); 293 | 294 | ExFreePool(c); 295 | return; 296 | } 297 | } else 298 | c->devpath.Buffer = NULL; 299 | 300 | { 301 | UNICODE_STRING us; 302 | OBJECT_ATTRIBUTES attr; 303 | IO_STATUS_BLOCK iosb; 304 | NTSTATUS Status; 305 | HANDLE h; 306 | 307 | RtlInitUnicodeString(&us, MOUNTMGR_DEVICE_NAME); 308 | InitializeObjectAttributes(&attr, &us, 0, NULL, NULL); 309 | 310 | Status = NtOpenFile(&h, FILE_GENERIC_READ | FILE_GENERIC_WRITE, &attr, &iosb, 311 | FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_ALERT); 312 | 313 | if (NT_SUCCESS(Status)) { 314 | char c = get_drive_letter(h, devpath); 315 | 316 | TRACE("get_drive_letter returned %u\n", c); 317 | 318 | if (c != 0) { 319 | NTSTATUS Status = remove_drive_letter(h, c); 320 | if (!NT_SUCCESS(Status)) 321 | ERR("remove_drive_letter returned %08x\n", Status); 322 | } 323 | } 324 | 325 | if (h) 326 | NtClose(h); 327 | } 328 | 329 | ExAcquireResourceExclusiveLite(&dev_lock, true); 330 | 331 | LIST_ENTRY* le = dev_list.Flink; 332 | 333 | while (le != &dev_list) { 334 | sd = CONTAINING_RECORD(le, set_pdo, list_entry); 335 | 336 | if (RtlCompareMemory(sd->array_info.set_uuid, sb->array_info.set_uuid, sizeof(sb->array_info.set_uuid)) == sizeof(sb->array_info.set_uuid)) { 337 | ExAcquireResourceExclusiveLite(&sd->lock, true); 338 | 339 | if (sd->array_state.events != sb->array_state.events) { 340 | WARN("device events count is out of sync (%llu, other device has %llu)\n", sb->array_state.events, sd->array_state.events); 341 | ExReleaseResourceLite(&sd->lock); 342 | goto fail; 343 | } 344 | 345 | InsertTailList(&sd->children, &c->list_entry); 346 | 347 | if (sd->stack_size <= (unsigned int)devobj->StackSize) { 348 | sd->stack_size = devobj->StackSize + 1; 349 | 350 | if (sd->dev) 351 | sd->dev->devobj->StackSize = sd->stack_size; 352 | } 353 | 354 | if (sd->dev_sector_size < devobj->SectorSize) { 355 | sd->dev_sector_size = devobj->SectorSize; 356 | 357 | if (sd->dev) 358 | sd->dev->devobj->SectorSize = sd->dev_sector_size; 359 | } 360 | 361 | if (sb->disk_info.dev_number < sd->array_state.max_dev && sd->roles.dev_roles[sb->disk_info.dev_number] < sd->array_info.raid_disks && 362 | !sd->child_list[sd->roles.dev_roles[sb->disk_info.dev_number]]) { 363 | sd->found_devices++; 364 | sd->child_list[sd->roles.dev_roles[sb->disk_info.dev_number]] = c; 365 | 366 | if (sd->array_info.level == RAID_LEVEL_0 || sd->array_info.level == RAID_LEVEL_LINEAR) 367 | sd->array_size += sb->disk_info.data_size * 512; 368 | 369 | if (sd->found_devices == sd->array_info.raid_disks) 370 | sd->loaded = true; 371 | } 372 | 373 | ExReleaseResourceLite(&sd->lock); 374 | ExReleaseResourceLite(&dev_lock); 375 | return; 376 | } 377 | 378 | le = le->Flink; 379 | } 380 | 381 | if (sb->array_info.level == RAID_LEVEL_0 || sb->array_info.level == RAID_LEVEL_4 || sb->array_info.level == RAID_LEVEL_5 || 382 | sb->array_info.level == RAID_LEVEL_6 || sb->array_info.level == RAID_LEVEL_10) { 383 | if (sb->array_info.chunksize == 0) { 384 | ERR("invalid value for chunk size: cannot be 0\n"); 385 | goto fail; 386 | } 387 | 388 | if (((sb->array_info.chunksize * 512) % PAGE_SIZE) != 0) { 389 | ERR("invalid value for chunk size (%u): must be multiple of 4096\n", sb->array_info.chunksize * 512); 390 | goto fail; 391 | } 392 | } 393 | 394 | if (sb->array_info.level == RAID_LEVEL_10) { 395 | uint8_t near = sb->array_info.layout & 0xff; 396 | uint8_t far = (sb->array_info.layout >> 8) & 0xff; 397 | bool offset = sb->array_info.layout & 0x10000; 398 | 399 | if (near == 0 || near > sb->array_info.raid_disks) { 400 | ERR("invalid near value %u, expected between 1 and %u\n", sb->array_info.raid_disks); 401 | goto fail; 402 | } 403 | 404 | if (far == 0 || far > sb->array_info.raid_disks) { 405 | ERR("invalid far value %u, expected between 1 and %u\n", sb->array_info.raid_disks); 406 | goto fail; 407 | } 408 | 409 | if (near > 1 && far > 1) { 410 | ERR("at least one of near and far needs to be 1 (near = %u, far = %u)\n", near, far); 411 | goto fail; 412 | } 413 | 414 | if (offset && near > 1) { 415 | ERR("invalid value for near (%u) when offset set\n", near); 416 | goto fail; 417 | } 418 | } 419 | 420 | if (sb->array_info.level == RAID_LEVEL_0 && (sb->array_info.size * 512) % PAGE_SIZE != 0) { 421 | ERR("invalid value for array size (%llu): must be multiple of 4096\n"); 422 | goto fail; 423 | } 424 | 425 | if (sb->feature_map != 0) { 426 | ERR("unsupported features %x\n", sb->feature_map); 427 | goto fail; 428 | } 429 | 430 | PDEVICE_OBJECT newdev; 431 | NTSTATUS Status; 432 | 433 | Status = IoCreateDevice(drvobj, sizeof(set_pdo), NULL, FILE_DEVICE_DISK, 434 | FILE_AUTOGENERATED_DEVICE_NAME | FILE_DEVICE_SECURE_OPEN, false, &newdev); 435 | if (!NT_SUCCESS(Status)) { 436 | ERR("IoCreateDevice returned %08x\n", Status); 437 | goto fail; 438 | } 439 | 440 | newdev->Flags |= DO_BUS_ENUMERATED_DEVICE; 441 | 442 | sd = (set_pdo*)newdev->DeviceExtension; 443 | sd->type = device_type_pdo; 444 | sd->pdo = newdev; 445 | sd->stack_size = devobj->StackSize + 1; 446 | sd->dev_sector_size = devobj->SectorSize == 0 ? 512 : devobj->SectorSize; 447 | sd->array_size = 0; 448 | sd->read_device = 0; 449 | sd->found_devices = 0; 450 | sd->loaded = false; 451 | sd->dev = NULL; 452 | sd->flush_thread_handle = NULL; 453 | sd->readonly = false; 454 | 455 | ExInitializeResourceLite(&sd->lock); 456 | 457 | InitializeListHead(&sd->children); 458 | 459 | ExInitializeResourceLite(&sd->partial_chunks_lock); 460 | 461 | InitializeListHead(&sd->partial_chunks); 462 | 463 | sd->child_list = NULL; 464 | sd->bus_name.Buffer = NULL; 465 | 466 | KeInitializeEvent(&sd->flush_thread_finished, NotificationEvent, false); 467 | 468 | RtlCopyMemory(&sd->array_info, &sb->array_info, sizeof(sb->array_info)); 469 | RtlCopyMemory(&sd->array_state, &sb->array_state, sizeof(sb->array_state)); 470 | RtlCopyMemory(&sd->roles, &sb->roles, sizeof(sb->roles)); 471 | 472 | if (sb->array_info.level == RAID_LEVEL_4 || sb->array_info.level == RAID_LEVEL_5 || sb->array_info.level == RAID_LEVEL_6) { 473 | Status = PsCreateSystemThread(&sd->flush_thread_handle, 0, NULL, NULL, NULL, flush_thread, sd); 474 | if (!NT_SUCCESS(Status)) { 475 | ERR("PsCreateSystemThread returned %08x\n", Status); 476 | IoDeleteDevice(newdev); 477 | goto fail; 478 | } 479 | } 480 | 481 | if (sd->array_info.raid_disks > 0) { 482 | sd->child_list = ExAllocatePoolWithTag(PagedPool, sizeof(set_child*) * sd->array_info.raid_disks, ALLOC_TAG); 483 | if (!sd->child_list) { 484 | ERR("out of memory\n"); 485 | IoDeleteDevice(newdev); 486 | goto fail; 487 | } 488 | 489 | RtlZeroMemory(sd->child_list, sizeof(set_child*) * sd->array_info.raid_disks); 490 | 491 | if (sb->disk_info.dev_number < sd->array_state.max_dev && sd->roles.dev_roles[sb->disk_info.dev_number] < sd->array_info.raid_disks && 492 | !sd->child_list[sd->roles.dev_roles[sb->disk_info.dev_number]]) { 493 | sd->found_devices++; 494 | sd->child_list[sd->roles.dev_roles[sb->disk_info.dev_number]] = c; 495 | 496 | if (sd->found_devices == sd->array_info.raid_disks) 497 | sd->loaded = true; 498 | } 499 | } 500 | 501 | InsertTailList(&sd->children, &c->list_entry); 502 | 503 | newdev->Flags &= ~DO_DEVICE_INITIALIZING; 504 | 505 | InsertTailList(&dev_list, &sd->list_entry); 506 | 507 | if (sb->array_info.level == RAID_LEVEL_0 || sd->array_info.level == RAID_LEVEL_LINEAR) 508 | sd->array_size = sb->disk_info.data_size * 512; 509 | else 510 | sd->array_size = sb->array_info.size * 512; 511 | 512 | Status = IoRegisterLastChanceShutdownNotification(newdev); 513 | if (!NT_SUCCESS(Status)) 514 | ERR("IoRegisterLastChanceShutdownNotification returned %08x\n", Status); 515 | 516 | if (!no_pnp) { 517 | control_device* cde = (control_device*)master_devobj->DeviceExtension; 518 | IoInvalidateDeviceRelations(cde->buspdo, BusRelations); 519 | } 520 | 521 | ExReleaseResourceLite(&dev_lock); 522 | 523 | return; 524 | 525 | fail: 526 | ObDereferenceObject(c->fileobj); 527 | 528 | if (c->devpath.Buffer) 529 | ExFreePool(c->devpath.Buffer); 530 | 531 | ExFreePool(c); 532 | 533 | ExReleaseResourceLite(&dev_lock); 534 | } 535 | 536 | static uint32_t calc_csum(mdraid_superblock* sb) { 537 | uint32_t* buf = (uint32_t*)sb; 538 | uint64_t v = 0; 539 | 540 | uint32_t size = (offsetof(mdraid_superblock, roles) + (2 * sb->array_state.max_dev)) / 4; 541 | 542 | for (uint32_t i = 0; i < size; i++) { 543 | v += buf[i]; 544 | } 545 | 546 | v -= sb->array_state.sb_csum; 547 | 548 | return (v & 0xffffffff) + (v >> 32); 549 | } 550 | 551 | static bool volume_arrival2(PDEVICE_OBJECT devobj, PFILE_OBJECT fileobj, uint64_t offset, uint32_t buflen, mdraid_superblock* sb) { 552 | NTSTATUS Status; 553 | uint32_t expected_csum; 554 | 555 | Status = sync_read_phys(devobj, fileobj, offset, buflen, (uint8_t*)sb, true); 556 | if (!NT_SUCCESS(Status)) { 557 | ERR("sync_read_phys returned %08x\n", Status); 558 | return false; 559 | } 560 | 561 | TRACE("magic: %08x\n", sb->magic); 562 | 563 | if (sb->magic != RAID_MAGIC) 564 | return false; 565 | 566 | TRACE("RAID device found\n"); 567 | 568 | expected_csum = calc_csum(sb); 569 | 570 | if (expected_csum != sb->array_state.sb_csum) { 571 | WARN("invalid checksum: expected %08x, found %08x\n", expected_csum, sb->array_state.sb_csum); 572 | return false; 573 | } 574 | 575 | return true; 576 | } 577 | 578 | void volume_arrival(PDRIVER_OBJECT DriverObject, PUNICODE_STRING devpath) { 579 | NTSTATUS Status; 580 | PFILE_OBJECT fileobj; 581 | PDEVICE_OBJECT devobj; 582 | 583 | TRACE("(%p, %.*S)\n", DriverObject, devpath->Length / sizeof(WCHAR), devpath->Buffer); 584 | 585 | Status = IoGetDeviceObjectPointer(devpath, FILE_READ_DATA, &fileobj, &devobj); 586 | if (!NT_SUCCESS(Status)) { 587 | ERR("IoGetDeviceObjectPointer returned %08x\n", Status); 588 | return; 589 | } 590 | 591 | TRACE("devobj = %p, SectorSize = %lx\n", devobj, devobj->SectorSize); 592 | 593 | uint32_t sector_size = devobj->SectorSize; 594 | 595 | if (sector_size < 4096) 596 | sector_size = 4096; 597 | 598 | uint32_t buflen = sector_align32((uint32_t)sizeof(mdraid_superblock), sector_size); 599 | 600 | mdraid_superblock* sb = ExAllocatePoolWithTag(PagedPool, buflen, ALLOC_TAG); 601 | if (!sb) { 602 | ERR("out of memory\n"); 603 | ObDereferenceObject(fileobj); 604 | return; 605 | } 606 | 607 | // version 1.2 608 | if (volume_arrival2(devobj, fileobj, RAID_12_OFFSET, buflen, sb)) { 609 | device_found(devobj, fileobj, devpath, sb); 610 | goto end; 611 | } 612 | 613 | // version 1.1 614 | if (volume_arrival2(devobj, fileobj, 0, buflen, sb)) { 615 | device_found(devobj, fileobj, devpath, sb); 616 | goto end; 617 | } 618 | 619 | // version 1.0 620 | 621 | GET_LENGTH_INFORMATION gli; 622 | 623 | Status = dev_ioctl(devobj, fileobj, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &gli, sizeof(GET_LENGTH_INFORMATION), false, NULL); 624 | if (!NT_SUCCESS(Status)) { 625 | ERR("IOCTL_DISK_GET_LENGTH_INFO returned %08x\n", Status); 626 | } else { 627 | uint64_t offset = gli.Length.QuadPart - 0x2000; 628 | offset &= ~0xfff; 629 | 630 | if (volume_arrival2(devobj, fileobj, offset, buflen, sb)) 631 | device_found(devobj, fileobj, devpath, sb); 632 | } 633 | 634 | end: 635 | ObDereferenceObject(fileobj); 636 | 637 | ExFreePool(sb); 638 | } 639 | 640 | static NTSTATUS set_close(set_device* set) { 641 | if (InterlockedDecrement(&set->open_count) == 0 && set->pdo->found_devices == 0) { 642 | PDEVICE_OBJECT devobj = set->devobj; 643 | 644 | IoDetachDevice(set->attached_device); 645 | 646 | ExDeleteResourceLite(&set->lock); 647 | IoDeleteDevice(devobj); 648 | } 649 | 650 | return STATUS_SUCCESS; 651 | } 652 | 653 | static void child_removed(set_pdo* pdo, set_child* sc) { 654 | TRACE("(%p)\n", sc); 655 | 656 | if (sc->disk_info.dev_number < pdo->array_state.max_dev && pdo->roles.dev_roles[sc->disk_info.dev_number] < pdo->array_info.raid_disks && 657 | pdo->child_list[pdo->roles.dev_roles[sc->disk_info.dev_number]] == sc) { 658 | pdo->child_list[pdo->roles.dev_roles[sc->disk_info.dev_number]] = NULL; 659 | pdo->found_devices--; 660 | pdo->loaded = false; 661 | } 662 | 663 | RemoveEntryList(&sc->list_entry); 664 | 665 | ObDereferenceObject(sc->fileobj); 666 | 667 | if (sc->devpath.Buffer) 668 | ExFreePool(sc->devpath.Buffer); 669 | 670 | ExFreePool(sc); 671 | 672 | // FIXME - send PNP messages(?) 673 | 674 | if (pdo->found_devices == 0) { 675 | RemoveEntryList(&pdo->list_entry); 676 | 677 | pdo->readonly = true; 678 | 679 | if (pdo->flush_thread_handle) { 680 | LARGE_INTEGER due_time; 681 | 682 | KeCancelTimer(&pdo->flush_thread_timer); 683 | 684 | due_time.QuadPart = 0; 685 | KeSetTimer(&pdo->flush_thread_timer, due_time, NULL); 686 | 687 | KeWaitForSingleObject(&pdo->flush_thread_finished, Executive, KernelMode, false, NULL); 688 | 689 | NtClose(pdo->flush_thread_handle); 690 | pdo->flush_thread_handle = NULL; 691 | } 692 | 693 | NTSTATUS Status = IoSetDeviceInterfaceState(&pdo->bus_name, false); 694 | if (!NT_SUCCESS(Status)) 695 | WARN("IoSetDeviceInterfaceState returned %08x\n", Status); 696 | 697 | control_device* cde = (control_device*)master_devobj->DeviceExtension; 698 | IoInvalidateDeviceRelations(cde->buspdo, BusRelations); 699 | } 700 | } 701 | 702 | void volume_removal(PDRIVER_OBJECT DriverObject, PUNICODE_STRING devpath) { 703 | TRACE("(%p, %.*S)\n", DriverObject, devpath->Length / sizeof(WCHAR), devpath->Buffer); 704 | 705 | ExAcquireResourceExclusiveLite(&dev_lock, true); 706 | 707 | LIST_ENTRY* le = dev_list.Flink; 708 | 709 | while (le != &dev_list) { 710 | set_pdo* sd = CONTAINING_RECORD(le, set_pdo, list_entry); 711 | 712 | ExAcquireResourceExclusiveLite(&sd->lock, true); 713 | 714 | bool found = false; 715 | 716 | LIST_ENTRY* le2 = sd->children.Flink; 717 | while (le2 != &sd->children) { 718 | set_child* sc = CONTAINING_RECORD(le2, set_child, list_entry); 719 | 720 | if (sc->devpath.Length == devpath->Length && RtlCompareMemory(sc->devpath.Buffer, devpath->Buffer, devpath->Length) == devpath->Length) { 721 | child_removed(sd, sc); 722 | ExReleaseResourceLite(&sd->lock); 723 | found = true; 724 | break; 725 | } 726 | 727 | le2 = le2->Flink; 728 | } 729 | 730 | if (found) 731 | break; 732 | else 733 | ExReleaseResourceLite(&sd->lock); 734 | 735 | le = le->Flink; 736 | } 737 | 738 | ExReleaseResourceLite(&dev_lock); 739 | } 740 | 741 | _Function_class_(DRIVER_NOTIFICATION_CALLBACK_ROUTINE) 742 | static NTSTATUS __stdcall volume_notification(PVOID NotificationStructure, PVOID Context) { 743 | DEVICE_INTERFACE_CHANGE_NOTIFICATION* dicn = (DEVICE_INTERFACE_CHANGE_NOTIFICATION*)NotificationStructure; 744 | PDRIVER_OBJECT DriverObject = (PDRIVER_OBJECT)Context; 745 | 746 | TRACE("(%p, %p)\n", NotificationStructure, Context); 747 | 748 | if (RtlCompareMemory(&dicn->Event, &GUID_DEVICE_INTERFACE_ARRIVAL, sizeof(GUID)) == sizeof(GUID)) 749 | enqueue_pnp_callback(DriverObject, dicn->SymbolicLinkName, volume_arrival); 750 | else if (RtlCompareMemory(&dicn->Event, &GUID_DEVICE_INTERFACE_REMOVAL, sizeof(GUID)) == sizeof(GUID)) 751 | enqueue_pnp_callback(DriverObject, dicn->SymbolicLinkName, volume_removal); 752 | 753 | return STATUS_SUCCESS; 754 | } 755 | 756 | _Function_class_(DRIVER_UNLOAD) 757 | static void __stdcall DriverUnload(PDRIVER_OBJECT DriverObject) { 758 | TRACE("(%p)\n", DriverObject); 759 | 760 | if (notification_entry3) 761 | IoUnregisterPlugPlayNotificationEx(notification_entry3); 762 | 763 | if (notification_entry2) 764 | IoUnregisterPlugPlayNotificationEx(notification_entry2); 765 | 766 | if (notification_entry) 767 | IoUnregisterPlugPlayNotificationEx(notification_entry); 768 | 769 | if (master_devobj) 770 | IoDeleteDevice(master_devobj); 771 | 772 | ExAcquireResourceExclusiveLite(&dev_lock, true); 773 | 774 | while (!IsListEmpty(&dev_list)) { 775 | set_pdo* sd = CONTAINING_RECORD(RemoveHeadList(&dev_list), set_pdo, list_entry); 776 | 777 | sd->readonly = true; 778 | 779 | if (sd->flush_thread_handle) { 780 | LARGE_INTEGER due_time; 781 | 782 | KeCancelTimer(&sd->flush_thread_timer); 783 | 784 | due_time.QuadPart = 0; 785 | KeSetTimer(&sd->flush_thread_timer, due_time, NULL); 786 | 787 | KeWaitForSingleObject(&sd->flush_thread_finished, Executive, KernelMode, false, NULL); 788 | 789 | NtClose(sd->flush_thread_handle); 790 | sd->flush_thread_handle = NULL; 791 | } 792 | } 793 | 794 | ExReleaseResourceLite(&dev_lock); 795 | 796 | if (master_devobj) { 797 | control_device* cde = (control_device*)master_devobj->DeviceExtension; 798 | IoInvalidateDeviceRelations(cde->buspdo, BusRelations); 799 | } 800 | 801 | ExDeleteResourceLite(&dev_lock); 802 | 803 | #ifdef _DEBUG 804 | if (logger) { 805 | stop_serial_logger(); 806 | ExFreePool(logger); 807 | } 808 | #endif 809 | } 810 | 811 | static NTSTATUS set_create(set_device* set, PIRP Irp) { 812 | if (set->pdo->found_devices == 0) 813 | return STATUS_DEVICE_NOT_READY; 814 | 815 | Irp->IoStatus.Information = FILE_OPENED; 816 | 817 | InterlockedIncrement(&set->open_count); 818 | 819 | return STATUS_SUCCESS; 820 | } 821 | 822 | static NTSTATUS mountdev_query_device_name(mdraid_array_info* array_info, PIRP Irp) { 823 | PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 824 | 825 | if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_NAME)) { 826 | Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME); 827 | return STATUS_BUFFER_TOO_SMALL; 828 | } 829 | 830 | MOUNTDEV_NAME* name = (MOUNTDEV_NAME*)Irp->AssociatedIrp.SystemBuffer; 831 | 832 | name->NameLength = sizeof(device_prefix) + (36 * sizeof(char16_t)); 833 | 834 | if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < offsetof(MOUNTDEV_NAME, Name[0]) + name->NameLength) { 835 | Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME); 836 | return STATUS_BUFFER_OVERFLOW; 837 | } 838 | 839 | RtlCopyMemory(name->Name, device_prefix, sizeof(device_prefix) - sizeof(char16_t)); 840 | 841 | WCHAR* p = &name->Name[(sizeof(device_prefix) / sizeof(char16_t)) - 1]; 842 | for (uint8_t i = 0; i < 16; i++) { 843 | *p = hex_digit((array_info->set_uuid[i] & 0xf0) >> 4); p++; 844 | *p = hex_digit(array_info->set_uuid[i] & 0xf); p++; 845 | 846 | if (i == 3 || i == 5 || i == 7 || i == 9) { 847 | *p = u'-'; p++; 848 | } 849 | } 850 | 851 | *p = '}'; 852 | 853 | Irp->IoStatus.Information = offsetof(MOUNTDEV_NAME, Name[0]) + name->NameLength; 854 | 855 | return STATUS_SUCCESS; 856 | } 857 | 858 | static NTSTATUS mountdev_query_unique_id(mdraid_array_info* array_info, PIRP Irp) { 859 | PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 860 | 861 | if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_UNIQUE_ID)) { 862 | Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID); 863 | return STATUS_BUFFER_TOO_SMALL; 864 | } 865 | 866 | MOUNTDEV_UNIQUE_ID* mduid = (MOUNTDEV_UNIQUE_ID*)Irp->AssociatedIrp.SystemBuffer; 867 | mduid->UniqueIdLength = sizeof(array_info->set_uuid); 868 | 869 | if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < offsetof(MOUNTDEV_UNIQUE_ID, UniqueId[0]) + mduid->UniqueIdLength) { 870 | Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID); 871 | return STATUS_BUFFER_OVERFLOW; 872 | } 873 | 874 | RtlCopyMemory(mduid->UniqueId, array_info->set_uuid, sizeof(array_info->set_uuid)); 875 | 876 | Irp->IoStatus.Information = offsetof(MOUNTDEV_UNIQUE_ID, UniqueId[0]) + mduid->UniqueIdLength; 877 | 878 | return STATUS_SUCCESS; 879 | } 880 | 881 | static NTSTATUS dev_ioctl(PDEVICE_OBJECT DeviceObject, PFILE_OBJECT FileObject, ULONG ControlCode, PVOID InputBuffer, 882 | ULONG InputBufferSize, PVOID OutputBuffer, ULONG OutputBufferSize, bool Override, IO_STATUS_BLOCK* iosb) { 883 | PIRP Irp; 884 | KEVENT Event; 885 | NTSTATUS Status; 886 | IO_STATUS_BLOCK IoStatus; 887 | 888 | KeInitializeEvent(&Event, NotificationEvent, FALSE); 889 | 890 | Irp = IoBuildDeviceIoControlRequest(ControlCode, DeviceObject, InputBuffer, InputBufferSize, OutputBuffer, OutputBufferSize, 891 | false, &Event, &IoStatus); 892 | 893 | if (!Irp) 894 | return STATUS_INSUFFICIENT_RESOURCES; 895 | 896 | PIO_STACK_LOCATION IrpSp = IoGetNextIrpStackLocation(Irp); 897 | 898 | IrpSp->FileObject = FileObject; 899 | 900 | if (Override) 901 | IrpSp->Flags |= SL_OVERRIDE_VERIFY_VOLUME; 902 | 903 | Status = IoCallDriver(DeviceObject, Irp); 904 | 905 | if (Status == STATUS_PENDING) { 906 | KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); 907 | Status = IoStatus.Status; 908 | } 909 | 910 | if (iosb) 911 | *iosb = IoStatus; 912 | 913 | return Status; 914 | } 915 | 916 | static NTSTATUS check_verify(set_pdo* pdo) { 917 | ExAcquireResourceSharedLite(&pdo->lock, true); 918 | 919 | LIST_ENTRY* le = pdo->children.Flink; 920 | while (le != &pdo->children) { 921 | set_child* c = CONTAINING_RECORD(le, set_child, list_entry); 922 | 923 | NTSTATUS Status = dev_ioctl(c->device, c->fileobj, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, NULL, 0, false, NULL); 924 | if (!NT_SUCCESS(Status)) { 925 | ExReleaseResourceLite(&pdo->lock); 926 | return Status; 927 | } 928 | 929 | le = le->Flink; 930 | } 931 | 932 | ExReleaseResourceLite(&pdo->lock); 933 | 934 | return STATUS_SUCCESS; 935 | } 936 | 937 | static NTSTATUS disk_get_drive_geometry(uint64_t array_size, PIRP Irp, PDEVICE_OBJECT devobj) { 938 | PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 939 | 940 | if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DISK_GEOMETRY)) 941 | return STATUS_BUFFER_TOO_SMALL; 942 | 943 | DISK_GEOMETRY* geom = (DISK_GEOMETRY*)Irp->AssociatedIrp.SystemBuffer; 944 | 945 | geom->BytesPerSector = devobj->SectorSize == 0 ? 0x200 : devobj->SectorSize; 946 | geom->SectorsPerTrack = 0x3f; 947 | geom->TracksPerCylinder = 0xff; 948 | geom->Cylinders.QuadPart = array_size / (UInt32x32To64(geom->TracksPerCylinder, geom->SectorsPerTrack) * geom->BytesPerSector); 949 | geom->MediaType = devobj->Characteristics & FILE_REMOVABLE_MEDIA ? RemovableMedia : FixedMedia; 950 | 951 | Irp->IoStatus.Information = sizeof(DISK_GEOMETRY); 952 | 953 | return STATUS_SUCCESS; 954 | } 955 | 956 | static NTSTATUS disk_get_length_info(uint64_t array_size, PIRP Irp) { 957 | PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 958 | 959 | if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(GET_LENGTH_INFORMATION)) 960 | return STATUS_BUFFER_TOO_SMALL; 961 | 962 | GET_LENGTH_INFORMATION* gli = (GET_LENGTH_INFORMATION*)Irp->AssociatedIrp.SystemBuffer; 963 | 964 | gli->Length.QuadPart = array_size; 965 | 966 | Irp->IoStatus.Information = sizeof(GET_LENGTH_INFORMATION); 967 | 968 | return STATUS_SUCCESS; 969 | } 970 | 971 | static NTSTATUS set_device_control(set_device* set, PIRP Irp) { 972 | PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 973 | 974 | if (!set->pdo) 975 | return STATUS_INVALID_DEVICE_REQUEST; 976 | 977 | switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) { 978 | case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME: 979 | return mountdev_query_device_name(&set->pdo->array_info, Irp); 980 | 981 | case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID: 982 | return mountdev_query_unique_id(&set->pdo->array_info, Irp); 983 | 984 | case IOCTL_STORAGE_CHECK_VERIFY: 985 | case IOCTL_DISK_CHECK_VERIFY: 986 | return check_verify(set->pdo); 987 | 988 | case IOCTL_DISK_GET_DRIVE_GEOMETRY: 989 | return disk_get_drive_geometry(set->pdo->array_size, Irp, set->devobj); 990 | 991 | case IOCTL_DISK_GET_LENGTH_INFO: 992 | return disk_get_length_info(set->pdo->array_size, Irp); 993 | 994 | default: 995 | ERR("ioctl %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode); 996 | return STATUS_INVALID_DEVICE_REQUEST; 997 | } 998 | } 999 | 1000 | static NTSTATUS pdo_shutdown(set_pdo* pdo, PIRP Irp) { 1001 | TRACE("(%p, %p)\n", pdo, Irp); 1002 | 1003 | ExAcquireResourceExclusiveLite(&pdo->lock, true); 1004 | 1005 | if (pdo->readonly) 1006 | goto end; 1007 | 1008 | pdo->readonly = true; 1009 | 1010 | if (pdo->flush_thread_handle) { 1011 | LARGE_INTEGER due_time; 1012 | 1013 | KeCancelTimer(&pdo->flush_thread_timer); 1014 | 1015 | due_time.QuadPart = 0; 1016 | KeSetTimer(&pdo->flush_thread_timer, due_time, NULL); 1017 | 1018 | KeWaitForSingleObject(&pdo->flush_thread_finished, Executive, KernelMode, false, NULL); 1019 | 1020 | NtClose(pdo->flush_thread_handle); 1021 | pdo->flush_thread_handle = NULL; 1022 | 1023 | if (pdo->loaded) 1024 | flush_chunks(pdo); 1025 | } 1026 | 1027 | // FIXME - mark superblocks as clean(?) 1028 | 1029 | end: 1030 | ExReleaseResourceLite(&pdo->lock); 1031 | 1032 | return STATUS_SUCCESS; 1033 | } 1034 | 1035 | static NTSTATUS control_power(PIRP Irp) { 1036 | NTSTATUS Status; 1037 | PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 1038 | 1039 | if (IrpSp->MinorFunction == IRP_MN_SET_POWER || IrpSp->MinorFunction == IRP_MN_QUERY_POWER) 1040 | Irp->IoStatus.Status = STATUS_SUCCESS; 1041 | 1042 | Status = Irp->IoStatus.Status; 1043 | PoStartNextPowerIrp(Irp); 1044 | 1045 | return Status; 1046 | } 1047 | 1048 | bool is_top_level(PIRP Irp) { 1049 | if (!IoGetTopLevelIrp()) { 1050 | IoSetTopLevelIrp(Irp); 1051 | return true; 1052 | } 1053 | 1054 | return false; 1055 | } 1056 | 1057 | _Dispatch_type_(IRP_MJ_CREATE) 1058 | _Function_class_(DRIVER_DISPATCH) 1059 | NTSTATUS drv_create(PDEVICE_OBJECT DeviceObject, PIRP Irp) { 1060 | NTSTATUS Status; 1061 | bool top_level; 1062 | 1063 | FsRtlEnterFileSystem(); 1064 | 1065 | top_level = is_top_level(Irp); 1066 | 1067 | switch (*(enum device_type*)DeviceObject->DeviceExtension) { 1068 | case device_type_control: 1069 | case device_type_pdo: 1070 | Irp->IoStatus.Information = FILE_OPENED; 1071 | Status = STATUS_SUCCESS; 1072 | break; 1073 | 1074 | case device_type_set: 1075 | Status = set_create((set_device*)(DeviceObject->DeviceExtension), Irp); 1076 | break; 1077 | 1078 | default: 1079 | Status = STATUS_INVALID_DEVICE_REQUEST; 1080 | } 1081 | 1082 | Irp->IoStatus.Status = Status; 1083 | IoCompleteRequest(Irp, IO_NO_INCREMENT); 1084 | 1085 | if (top_level) 1086 | IoSetTopLevelIrp(NULL); 1087 | 1088 | FsRtlExitFileSystem(); 1089 | 1090 | return Status; 1091 | } 1092 | 1093 | _Dispatch_type_(IRP_MJ_DEVICE_CONTROL) 1094 | _Function_class_(DRIVER_DISPATCH) 1095 | NTSTATUS drv_device_control(PDEVICE_OBJECT DeviceObject, PIRP Irp) { 1096 | NTSTATUS Status; 1097 | bool top_level; 1098 | 1099 | FsRtlEnterFileSystem(); 1100 | 1101 | top_level = is_top_level(Irp); 1102 | 1103 | switch (*(enum device_type*)DeviceObject->DeviceExtension) { 1104 | case device_type_set: 1105 | Status = set_device_control((set_device*)(DeviceObject->DeviceExtension), Irp); 1106 | break; 1107 | 1108 | default: 1109 | Status = STATUS_INVALID_DEVICE_REQUEST; 1110 | } 1111 | 1112 | Irp->IoStatus.Status = Status; 1113 | IoCompleteRequest(Irp, IO_NO_INCREMENT); 1114 | 1115 | if (top_level) 1116 | IoSetTopLevelIrp(NULL); 1117 | 1118 | FsRtlExitFileSystem(); 1119 | 1120 | return Status; 1121 | } 1122 | 1123 | _Dispatch_type_(IRP_MJ_SHUTDOWN) 1124 | _Function_class_(DRIVER_DISPATCH) 1125 | NTSTATUS drv_shutdown(PDEVICE_OBJECT DeviceObject, PIRP Irp) { 1126 | NTSTATUS Status; 1127 | bool top_level; 1128 | 1129 | FsRtlEnterFileSystem(); 1130 | 1131 | top_level = is_top_level(Irp); 1132 | 1133 | switch (*(enum device_type*)DeviceObject->DeviceExtension) { 1134 | case device_type_pdo: 1135 | Status = pdo_shutdown((set_pdo*)(DeviceObject->DeviceExtension), Irp); 1136 | break; 1137 | 1138 | default: 1139 | Status = STATUS_INVALID_DEVICE_REQUEST; 1140 | } 1141 | 1142 | Irp->IoStatus.Status = Status; 1143 | IoCompleteRequest(Irp, IO_NO_INCREMENT); 1144 | 1145 | if (top_level) 1146 | IoSetTopLevelIrp(NULL); 1147 | 1148 | FsRtlExitFileSystem(); 1149 | 1150 | return Status; 1151 | } 1152 | 1153 | _Dispatch_type_(IRP_MJ_POWER) 1154 | _Function_class_(DRIVER_DISPATCH) 1155 | NTSTATUS drv_power(PDEVICE_OBJECT DeviceObject, PIRP Irp) { 1156 | NTSTATUS Status; 1157 | bool top_level; 1158 | 1159 | FsRtlEnterFileSystem(); 1160 | 1161 | top_level = is_top_level(Irp); 1162 | 1163 | switch (*(enum device_type*)DeviceObject->DeviceExtension) { 1164 | case device_type_control: 1165 | Status = control_power(Irp); 1166 | break; 1167 | 1168 | default: 1169 | Status = Irp->IoStatus.Status; 1170 | PoStartNextPowerIrp(Irp); 1171 | break; 1172 | } 1173 | 1174 | Irp->IoStatus.Status = Status; 1175 | IoCompleteRequest(Irp, IO_NO_INCREMENT); 1176 | 1177 | if (top_level) 1178 | IoSetTopLevelIrp(NULL); 1179 | 1180 | FsRtlExitFileSystem(); 1181 | 1182 | return Status; 1183 | } 1184 | 1185 | static void check_cpu() { 1186 | int cpuInfo[4]; 1187 | __cpuid(cpuInfo, 1); 1188 | have_sse2 = cpuInfo[3] & (1 << 26); 1189 | } 1190 | 1191 | _Dispatch_type_(IRP_MJ_CLOSE) 1192 | _Function_class_(DRIVER_DISPATCH) 1193 | static NTSTATUS drv_close(PDEVICE_OBJECT DeviceObject, PIRP Irp) { 1194 | NTSTATUS Status; 1195 | bool top_level; 1196 | 1197 | FsRtlEnterFileSystem(); 1198 | 1199 | top_level = is_top_level(Irp); 1200 | 1201 | switch (*(enum device_type*)DeviceObject->DeviceExtension) { 1202 | case device_type_set: 1203 | Status = set_close((set_device*)(DeviceObject->DeviceExtension)); 1204 | break; 1205 | 1206 | case device_type_pdo: 1207 | Status = STATUS_SUCCESS; 1208 | break; 1209 | 1210 | default: 1211 | Status = STATUS_INVALID_DEVICE_REQUEST; 1212 | } 1213 | 1214 | Irp->IoStatus.Status = Status; 1215 | IoCompleteRequest(Irp, IO_NO_INCREMENT); 1216 | 1217 | if (top_level) 1218 | IoSetTopLevelIrp(NULL); 1219 | 1220 | FsRtlExitFileSystem(); 1221 | 1222 | return Status; 1223 | } 1224 | 1225 | #ifdef _DEBUG 1226 | static void get_registry_value(HANDLE h, const WCHAR* string, ULONG type, void* val, ULONG size) { 1227 | ULONG kvfilen; 1228 | UNICODE_STRING us; 1229 | NTSTATUS Status; 1230 | 1231 | RtlInitUnicodeString(&us, string); 1232 | 1233 | Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, NULL, 0, &kvfilen); 1234 | 1235 | if ((Status == STATUS_BUFFER_TOO_SMALL || Status == STATUS_BUFFER_OVERFLOW) && kvfilen > 0) { 1236 | KEY_VALUE_FULL_INFORMATION* kvfi = ExAllocatePoolWithTag(PagedPool, kvfilen, ALLOC_TAG); 1237 | 1238 | if (!kvfi) { 1239 | ERR("out of memory\n"); 1240 | ZwClose(h); 1241 | return; 1242 | } 1243 | 1244 | Status = ZwQueryValueKey(h, &us, KeyValueFullInformation, kvfi, kvfilen, &kvfilen); 1245 | 1246 | if (NT_SUCCESS(Status)) { 1247 | if (kvfi->Type == type && kvfi->DataLength >= size) { 1248 | RtlCopyMemory(val, ((UINT8*)kvfi) + kvfi->DataOffset, size); 1249 | } else { 1250 | Status = ZwDeleteValueKey(h, &us); 1251 | if (!NT_SUCCESS(Status)) 1252 | ERR("ZwDeleteValueKey returned %08x\n", Status); 1253 | 1254 | Status = ZwSetValueKey(h, &us, 0, type, val, size); 1255 | if (!NT_SUCCESS(Status)) 1256 | ERR("ZwSetValueKey returned %08x\n", Status); 1257 | } 1258 | } 1259 | 1260 | ExFreePool(kvfi); 1261 | } else if (Status == STATUS_OBJECT_NAME_NOT_FOUND) { 1262 | Status = ZwSetValueKey(h, &us, 0, type, val, size); 1263 | 1264 | if (!NT_SUCCESS(Status)) 1265 | ERR("ZwSetValueKey returned %08x\n", Status); 1266 | } else 1267 | ERR("ZwQueryValueKey returned %08x\n", Status); 1268 | } 1269 | 1270 | void read_registry(PUNICODE_STRING regpath) { 1271 | OBJECT_ATTRIBUTES oa; 1272 | NTSTATUS Status; 1273 | HANDLE h; 1274 | ULONG dispos; 1275 | 1276 | InitializeObjectAttributes(&oa, regpath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); 1277 | 1278 | Status = ZwCreateKey(&h, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &oa, 0, NULL, REG_OPTION_NON_VOLATILE, &dispos); 1279 | 1280 | if (!NT_SUCCESS(Status)) { 1281 | ERR("ZwCreateKey returned %08x\n", Status); 1282 | return; 1283 | } 1284 | 1285 | get_registry_value(h, L"DebugLogLevel", REG_DWORD, &debug_log_level, sizeof(debug_log_level)); 1286 | 1287 | ZwClose(h); 1288 | } 1289 | #endif 1290 | 1291 | static NTSTATUS set_system_control(set_device* set, PIRP Irp, bool* no_complete) { 1292 | *no_complete = true; 1293 | 1294 | IoSkipCurrentIrpStackLocation(Irp); 1295 | return IoCallDriver(set->attached_device, Irp); 1296 | } 1297 | 1298 | static NTSTATUS control_system_control(control_device* control, PIRP Irp, bool* no_complete) { 1299 | *no_complete = true; 1300 | 1301 | IoSkipCurrentIrpStackLocation(Irp); 1302 | return IoCallDriver(control->attached_device, Irp); 1303 | } 1304 | 1305 | _Dispatch_type_(IRP_MJ_SYSTEM_CONTROL) 1306 | _Function_class_(DRIVER_DISPATCH) 1307 | static NTSTATUS drv_system_control(PDEVICE_OBJECT DeviceObject, PIRP Irp) { 1308 | NTSTATUS Status; 1309 | bool top_level; 1310 | 1311 | FsRtlEnterFileSystem(); 1312 | 1313 | top_level = is_top_level(Irp); 1314 | 1315 | bool no_complete = false; 1316 | 1317 | switch (*(enum device_type*)DeviceObject->DeviceExtension) { 1318 | case device_type_control: 1319 | Status = control_system_control((control_device*)(DeviceObject->DeviceExtension), Irp, &no_complete); 1320 | break; 1321 | 1322 | case device_type_set: 1323 | Status = set_system_control((set_device*)(DeviceObject->DeviceExtension), Irp, &no_complete); 1324 | break; 1325 | 1326 | default: 1327 | Status = Irp->IoStatus.Status; 1328 | } 1329 | 1330 | if (!no_complete) { 1331 | Irp->IoStatus.Status = Status; 1332 | IoCompleteRequest(Irp, IO_NO_INCREMENT); 1333 | } 1334 | 1335 | if (top_level) 1336 | IoSetTopLevelIrp(NULL); 1337 | 1338 | FsRtlExitFileSystem(); 1339 | 1340 | return Status; 1341 | } 1342 | 1343 | NTSTATUS __stdcall DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { 1344 | NTSTATUS Status; 1345 | RTL_OSVERSIONINFOW ver; 1346 | 1347 | ver.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOW); 1348 | 1349 | Status = RtlGetVersion(&ver); 1350 | if (!NT_SUCCESS(Status)) { 1351 | ERR("RtlGetVersion returned %08lx\n", Status); 1352 | return Status; 1353 | } 1354 | 1355 | is_windows_8 = ver.dwMajorVersion > 6 || (ver.dwMajorVersion == 6 && ver.dwMinorVersion >= 2); 1356 | 1357 | drvobj = DriverObject; 1358 | 1359 | DriverObject->DriverUnload = DriverUnload; 1360 | DriverObject->DriverExtension->AddDevice = AddDevice; 1361 | 1362 | DriverObject->MajorFunction[IRP_MJ_CREATE] = (PDRIVER_DISPATCH)drv_create; 1363 | DriverObject->MajorFunction[IRP_MJ_CLOSE] = (PDRIVER_DISPATCH)drv_close; 1364 | DriverObject->MajorFunction[IRP_MJ_READ] = (PDRIVER_DISPATCH)drv_read; 1365 | DriverObject->MajorFunction[IRP_MJ_WRITE] = (PDRIVER_DISPATCH)drv_write; 1366 | DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = (PDRIVER_DISPATCH)drv_device_control; 1367 | DriverObject->MajorFunction[IRP_MJ_PNP] = (PDRIVER_DISPATCH)drv_pnp; 1368 | DriverObject->MajorFunction[IRP_MJ_POWER] = (PDRIVER_DISPATCH)drv_power; 1369 | DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = (PDRIVER_DISPATCH)drv_shutdown; 1370 | DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = (PDRIVER_DISPATCH)drv_system_control; 1371 | 1372 | #ifdef _DEBUG 1373 | read_registry(RegistryPath); 1374 | 1375 | if (debug_log_level > 0) { 1376 | NTSTATUS Status; 1377 | UNICODE_STRING us; 1378 | 1379 | logger = ExAllocatePoolWithTag(NonPagedPool, sizeof(serial_logger), ALLOC_TAG); 1380 | if (!logger) { 1381 | ERR("out of memory\n"); 1382 | return STATUS_INSUFFICIENT_RESOURCES; 1383 | } 1384 | 1385 | init_serial_logger(); 1386 | } 1387 | #endif 1388 | 1389 | TRACE("(%p, %.*S)\n", DriverObject, RegistryPath->Length / sizeof(WCHAR), RegistryPath->Buffer); 1390 | 1391 | check_cpu(); 1392 | 1393 | UNICODE_STRING device_nameW; 1394 | 1395 | device_nameW.Buffer = (WCHAR*)device_name; 1396 | device_nameW.Length = device_nameW.MaximumLength = sizeof(device_name) - sizeof(WCHAR); 1397 | 1398 | Status = IoCreateDevice(DriverObject, sizeof(control_device), &device_nameW, FILE_DEVICE_DISK, 1399 | FILE_DEVICE_SECURE_OPEN, false, &master_devobj); 1400 | if (!NT_SUCCESS(Status)) { 1401 | ERR("IoCreateDevice returned %08x\n", Status); 1402 | return Status; 1403 | } 1404 | 1405 | control_device* cde = (control_device*)master_devobj->DeviceExtension; 1406 | cde->type = device_type_control; 1407 | 1408 | ExInitializeResourceLite(&dev_lock); 1409 | 1410 | InitializeListHead(&dev_list); 1411 | 1412 | master_devobj->Flags &= ~DO_DEVICE_INITIALIZING; 1413 | 1414 | Status = IoReportDetectedDevice(drvobj, InterfaceTypeUndefined, 0xFFFFFFFF, 0xFFFFFFFF, 1415 | NULL, NULL, 0, &cde->buspdo); 1416 | if (!NT_SUCCESS(Status)) { 1417 | ERR("IoReportDetectedDevice returned %08x\n", Status); 1418 | IoDeleteDevice(master_devobj); 1419 | return Status; 1420 | } 1421 | 1422 | Status = IoRegisterDeviceInterface(cde->buspdo, &WinMDBusInterface, NULL, &cde->bus_name); 1423 | if (!NT_SUCCESS(Status)) 1424 | WARN("IoRegisterDeviceInterface returned %08x\n", Status); 1425 | 1426 | cde->attached_device = IoAttachDeviceToDeviceStack(master_devobj, cde->buspdo); 1427 | 1428 | Status = IoSetDeviceInterfaceState(&cde->bus_name, true); 1429 | if (!NT_SUCCESS(Status)) 1430 | WARN("IoSetDeviceInterfaceState returned %08x\n", Status); 1431 | 1432 | IoInvalidateDeviceRelations(cde->buspdo, BusRelations); 1433 | 1434 | Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, 1435 | (PVOID)&GUID_DEVINTERFACE_VOLUME, DriverObject, volume_notification, DriverObject, ¬ification_entry); 1436 | if (!NT_SUCCESS(Status)) { 1437 | ERR("IoRegisterPlugPlayNotification returned %08x\n", Status); 1438 | IoDeleteDevice(master_devobj); 1439 | return Status; 1440 | } 1441 | 1442 | Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, 1443 | (PVOID)&GUID_DEVINTERFACE_HIDDEN_VOLUME, DriverObject, volume_notification, DriverObject, ¬ification_entry2); 1444 | if (!NT_SUCCESS(Status)) { 1445 | ERR("IoRegisterPlugPlayNotification returned %08x\n", Status); 1446 | IoUnregisterPlugPlayNotificationEx(notification_entry); 1447 | IoDeleteDevice(master_devobj); 1448 | return Status; 1449 | } 1450 | 1451 | Status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, 1452 | (PVOID)&GUID_DEVINTERFACE_DISK, DriverObject, volume_notification, DriverObject, ¬ification_entry3); 1453 | if (!NT_SUCCESS(Status)) { 1454 | ERR("IoRegisterPlugPlayNotification returned %08x\n", Status); 1455 | IoUnregisterPlugPlayNotificationEx(notification_entry2); 1456 | IoUnregisterPlugPlayNotificationEx(notification_entry); 1457 | IoDeleteDevice(master_devobj); 1458 | return Status; 1459 | } 1460 | 1461 | return STATUS_SUCCESS; 1462 | } 1463 | --------------------------------------------------------------------------------