├── Tools └── enableDokanDebugMode.bat ├── Samples └── Mirror │ ├── Mirror.res │ ├── Mirror.lpi │ ├── Mirror.dproj │ └── Mirror.dpr ├── clean.bat ├── README.md ├── LICENSE.md ├── .gitignore ├── DokanWin.pas └── Dokan.pas /Tools/enableDokanDebugMode.bat: -------------------------------------------------------------------------------- 1 | "C:\Program Files\Dokan\Dokan Library-2.0.6\dokanctl.exe" /d 7 2 | pause -------------------------------------------------------------------------------- /Samples/Mirror/Mirror.res: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dokan-dev/dokan-delphi/HEAD/Samples/Mirror/Mirror.res -------------------------------------------------------------------------------- /clean.bat: -------------------------------------------------------------------------------- 1 | del /q /s *.dcu 2 | del /q /s *.exe 3 | del /q /s *.dll 4 | del /q /s *.cbk 5 | del /q /s *.drc 6 | del /q /s *.dsk 7 | del /q /s *.dsm 8 | del /q /s *.rsm 9 | del /q /s *.identcache 10 | del /q /s *.local 11 | del /q /s *.map 12 | del /q /s *.ico 13 | del /q /s *.otares 14 | del /q /s *.stat 15 | rmdir /Q /S .\Samples\Mirror\__history 16 | rmdir /Q /S .\Samples\Mirror\__recovery 17 | rmdir /Q /S .\Samples\Mirror\Win32 18 | rmdir /Q /S .\Samples\Mirror\Win64 19 | rmdir /Q /S .\__history 20 | rmdir /Q /S .\__recovery -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dokan Delphi/FreePascal Binding 2 | 3 | ## What is Dokan Delphi/FreePascal Binding 4 | By using Dokan library, you can create your own file systems very easily 5 | without writing device driver. Dokan Delphi/FreePascal Binding is a library that allows 6 | you to make a file system on Win32/Win64 environment. 7 | 8 | ## Licensing 9 | Dokan Delphi/FreePascal Binding is distributed under a version of the "MIT License", 10 | which is a BSD-like license. See the 'LICENSE.md' file for details. 11 | 12 | ## Environment 13 | Delphi/FreePascal and Dokan library 14 | 15 | ## Supported Dokan Version 16 | https://github.com/dokan-dev/dokany 17 | 18 | 2.2.0.1000 19 | 20 | https://github.com/dokan-dev/dokany/releases/tag/v2.2.0.1000 21 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (C) 2015 - 2022 Adrien J. and Maxime C. 2 | Copyright (C) 2007 - 2011 Hiroki Asakawa 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Uncomment these types if you want even more clean repository. But be careful. 2 | # It can make harm to an existing project source. Read explanations below. 3 | # 4 | # Resource files are binaries containing manifest, project icon and version info. 5 | # They can not be viewed as text or compared by diff-tools. Consider replacing them with .rc files. 6 | #*.res 7 | # 8 | # Type library file (binary). In old Delphi versions it should be stored. 9 | # Since Delphi 2009 it is produced from .ridl file and can safely be ignored. 10 | #*.tlb 11 | # 12 | # Diagram Portfolio file. Used by the diagram editor up to Delphi 7. 13 | # Uncomment this if you are not using diagrams or use newer Delphi version. 14 | #*.ddp 15 | # 16 | # Visual LiveBindings file. Added in Delphi XE2. 17 | # Uncomment this if you are not using LiveBindings Designer. 18 | #*.vlb 19 | # 20 | # Deployment Manager configuration file for your project. Added in Delphi XE2. 21 | # Uncomment this if it is not mobile development and you do not use remote debug feature. 22 | #*.deployproj 23 | # 24 | # C++ object files produced when C/C++ Output file generation is configured. 25 | # Uncomment this if you are not using external objects (zlib library for example). 26 | #*.obj 27 | # 28 | 29 | Win32 30 | Win64 31 | 32 | # Delphi compiler-generated binaries (safe to delete) 33 | *.exe 34 | *.dll 35 | *.bpl 36 | *.bpi 37 | *.dcp 38 | *.so 39 | *.apk 40 | *.drc 41 | *.map 42 | *.dres 43 | *.rsm 44 | *.tds 45 | *.dcu 46 | *.lib 47 | *.a 48 | *.o 49 | *.ocx 50 | 51 | # Delphi autogenerated files (duplicated info) 52 | *.cfg 53 | *.hpp 54 | *Resource.rc 55 | 56 | # Delphi local files (user-specific info) 57 | *.local 58 | *.identcache 59 | *.projdata 60 | *.tvsconfig 61 | *.dsk 62 | 63 | # Delphi history and backups 64 | __history/ 65 | __recovery/ 66 | *.~* 67 | 68 | # Castalia statistics file (since XE7 Castalia is distributed with Delphi) 69 | *.stat 70 | 71 | Dokan-Releases -------------------------------------------------------------------------------- /Samples/Mirror/Mirror.lpi: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | <UseAppBundle Value="False"/> 15 | <ResourceType Value="res"/> 16 | </General> 17 | <i18n> 18 | <EnableI18N LFM="False"/> 19 | </i18n> 20 | <VersionInfo> 21 | <StringTable ProductVersion=""/> 22 | </VersionInfo> 23 | <BuildModes Count="3"> 24 | <Item1 Name="Default" Default="True"/> 25 | <Item2 Name="Debug"> 26 | <CompilerOptions> 27 | <Version Value="11"/> 28 | <PathDelim Value="\"/> 29 | <Target> 30 | <Filename Value="Mirror"/> 31 | </Target> 32 | <SearchPaths> 33 | <IncludeFiles Value="$(ProjOutDir)"/> 34 | <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> 35 | </SearchPaths> 36 | <Parsing> 37 | <SyntaxOptions> 38 | <IncludeAssertionCode Value="True"/> 39 | </SyntaxOptions> 40 | </Parsing> 41 | <CodeGeneration> 42 | <Checks> 43 | <IOChecks Value="True"/> 44 | <RangeChecks Value="True"/> 45 | <OverflowChecks Value="True"/> 46 | <StackChecks Value="True"/> 47 | </Checks> 48 | </CodeGeneration> 49 | <Linking> 50 | <Debugging> 51 | <DebugInfoType Value="dsDwarf2Set"/> 52 | <UseHeaptrc Value="True"/> 53 | <UseExternalDbgSyms Value="True"/> 54 | </Debugging> 55 | </Linking> 56 | </CompilerOptions> 57 | </Item2> 58 | <Item3 Name="Release"> 59 | <CompilerOptions> 60 | <Version Value="11"/> 61 | <PathDelim Value="\"/> 62 | <Target> 63 | <Filename Value="Mirror"/> 64 | </Target> 65 | <SearchPaths> 66 | <IncludeFiles Value="$(ProjOutDir)"/> 67 | <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> 68 | </SearchPaths> 69 | <CodeGeneration> 70 | <SmartLinkUnit Value="True"/> 71 | <Optimizations> 72 | <OptimizationLevel Value="3"/> 73 | </Optimizations> 74 | </CodeGeneration> 75 | <Linking> 76 | <Debugging> 77 | <GenerateDebugInfo Value="False"/> 78 | </Debugging> 79 | <LinkSmart Value="True"/> 80 | </Linking> 81 | </CompilerOptions> 82 | </Item3> 83 | </BuildModes> 84 | <PublishOptions> 85 | <Version Value="2"/> 86 | </PublishOptions> 87 | <RunParams> 88 | <local> 89 | <FormatVersion Value="1"/> 90 | <CommandLineParams Value="/R d:\test /L k /D /S"/> 91 | </local> 92 | </RunParams> 93 | <Units Count="1"> 94 | <Unit0> 95 | <Filename Value="Mirror.dpr"/> 96 | <IsPartOfProject Value="True"/> 97 | </Unit0> 98 | </Units> 99 | </ProjectOptions> 100 | <CompilerOptions> 101 | <Version Value="11"/> 102 | <PathDelim Value="\"/> 103 | <Target> 104 | <Filename Value="Mirror"/> 105 | </Target> 106 | <SearchPaths> 107 | <IncludeFiles Value="$(ProjOutDir)"/> 108 | <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> 109 | </SearchPaths> 110 | </CompilerOptions> 111 | <Debugging> 112 | <Exceptions Count="3"> 113 | <Item1> 114 | <Name Value="EAbort"/> 115 | </Item1> 116 | <Item2> 117 | <Name Value="ECodetoolError"/> 118 | </Item2> 119 | <Item3> 120 | <Name Value="EFOpenError"/> 121 | </Item3> 122 | </Exceptions> 123 | </Debugging> 124 | </CONFIG> 125 | -------------------------------------------------------------------------------- /DokanWin.pas: -------------------------------------------------------------------------------- 1 | (* 2 | Dokan API wrapper for Delphi based on Release 2.2.0.1000 3 | https://github.com/dokan-dev/dokany/releases/tag/v2.2.0.1000 4 | Copyright (C) 2019 - 2024 Sven Harazim 5 | 6 | Dokan : user-mode file system library for Windows 7 | 8 | Copyright (C) 2015 - 2019 Adrien J. <liryna.stark@gmail.com> and Maxime C. <maxime@islog.com> 9 | Copyright (C) 2020 Google, Inc. 10 | Copyright (C) 2007 - 2011 Hiroki Asakawa <info@dokan-dev.net> 11 | 12 | http://dokan-dev.github.io 13 | 14 | This program is free software; you can redistribute it and/or modify it under 15 | the terms of the GNU Lesser General Public License as published by the Free 16 | Software Foundation; either version 3 of the License, or (at your option) any 17 | later version. 18 | 19 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 20 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 21 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 22 | 23 | You should have received a copy of the GNU Lesser General Public License along 24 | with this program. If not, see <http://www.gnu.org/licenses/>. 25 | *) 26 | 27 | unit DokanWin; 28 | 29 | {$ifdef FPC} 30 | {$mode delphi} 31 | {$endif FPC} 32 | 33 | {$align 8} 34 | {$minenumsize 4} 35 | 36 | interface 37 | 38 | uses 39 | Windows; 40 | 41 | type 42 | USHORT = Word; 43 | ULONG64 = UInt64; 44 | PACCESS_MASK = ^ACCESS_MASK; 45 | 46 | LPVOID = Pointer; 47 | PVOID = Pointer; 48 | 49 | _UNICODE_STRING = record 50 | Length: USHORT; 51 | MaximumLength: USHORT; 52 | Buffer: LPWSTR; 53 | end; 54 | UNICODE_STRING = _UNICODE_STRING; 55 | PUNICODE_STRING = ^_UNICODE_STRING; 56 | TUnicodeString = UNICODE_STRING; 57 | PUnicodeString = PUNICODE_STRING; 58 | 59 | _WIN32_FIND_STREAM_DATA = record 60 | StreamSize: LARGE_INTEGER; 61 | cStreamName: array [0 .. (MAX_PATH+36) - 1] of WCHAR; 62 | end; 63 | WIN32_FIND_STREAM_DATA = _WIN32_FIND_STREAM_DATA; 64 | PWIN32_FIND_STREAM_DATA = ^_WIN32_FIND_STREAM_DATA; 65 | TWin32FindStreamData = WIN32_FIND_STREAM_DATA; 66 | PWin32FindStreamData = PWIN32_FIND_STREAM_DATA; 67 | 68 | NTSTATUS = LongInt; 69 | TNtStatus = NTSTATUS; 70 | 71 | const 72 | // DesiredAccess 73 | 74 | FILE_READ_DATA = $0001; 75 | FILE_LIST_DIRECTORY = $0001; 76 | FILE_WRITE_DATA = $0002; 77 | FILE_ADD_FILE = $0002; 78 | FILE_APPEND_DATA = $0004; 79 | FILE_ADD_SUBDIRECTORY = $0004; 80 | FILE_CREATE_PIPE_INSTANCE = $0004; 81 | FILE_READ_EA = $0008; 82 | FILE_WRITE_EA = $0010; 83 | FILE_EXECUTE = $0020; 84 | FILE_TRAVERSE = $0020; 85 | FILE_DELETE_CHILD = $0040; 86 | FILE_READ_ATTRIBUTES = $0080; 87 | FILE_WRITE_ATTRIBUTES = $0100; 88 | 89 | DELETE = $00010000; 90 | READ_CONTROL = $00020000; 91 | WRITE_DAC = $00040000; 92 | WRITE_OWNER = $00080000; 93 | SYNCHRONIZE = $00100000; 94 | ACCESS_SYSTEM_SECURITY = $01000000; 95 | MAXIMUM_ALLOWED = $02000000; 96 | GENERIC_ALL = $10000000; 97 | GENERIC_EXECUTE = $20000000; 98 | GENERIC_WRITE = $40000000; 99 | GENERIC_READ = $80000000; 100 | 101 | FILE_GENERIC_READ = STANDARD_RIGHTS_READ 102 | or FILE_READ_DATA 103 | or FILE_READ_ATTRIBUTES 104 | or FILE_READ_EA 105 | or SYNCHRONIZE; 106 | 107 | FILE_GENERIC_WRITE = STANDARD_RIGHTS_WRITE 108 | or FILE_WRITE_DATA 109 | or FILE_WRITE_ATTRIBUTES 110 | or FILE_WRITE_EA 111 | or FILE_APPEND_DATA 112 | or SYNCHRONIZE; 113 | 114 | FILE_GENERIC_EXECUTE = STANDARD_RIGHTS_EXECUTE 115 | or FILE_READ_ATTRIBUTES 116 | or FILE_EXECUTE 117 | or SYNCHRONIZE; 118 | 119 | // FileAttributes 120 | 121 | FILE_ATTRIBUTE_READONLY = $00000001; 122 | FILE_ATTRIBUTE_HIDDEN = $00000002; 123 | FILE_ATTRIBUTE_SYSTEM = $00000004; 124 | FILE_ATTRIBUTE_DIRECTORY = $00000010; 125 | FILE_ATTRIBUTE_ARCHIVE = $00000020; 126 | FILE_ATTRIBUTE_DEVICE = $00000040; 127 | FILE_ATTRIBUTE_NORMAL = $00000080; 128 | FILE_ATTRIBUTE_TEMPORARY = $00000100; 129 | FILE_ATTRIBUTE_SPARSE_FILE = $00000200; 130 | FILE_ATTRIBUTE_REPARSE_POINT = $00000400; 131 | FILE_ATTRIBUTE_COMPRESSED = $00000800; 132 | FILE_ATTRIBUTE_OFFLINE = $00001000; 133 | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = $00002000; 134 | FILE_ATTRIBUTE_ENCRYPTED = $00004000; 135 | FILE_ATTRIBUTE_INTEGRITY_STREAM = $00008000; 136 | FILE_ATTRIBUTE_VIRTUAL = $00010000; 137 | FILE_ATTRIBUTE_NO_SCRUB_DATA = $00020000; 138 | FILE_ATTRIBUTE_RECALL_ON_OPEN = $00040000; 139 | FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = $00400000; 140 | INVALID_FILE_ATTRIBUTES = $FFFFFFFF; 141 | 142 | // FileFlags 143 | 144 | FILE_FLAG_OPEN_REQUIRING_OPLOCK = $00040000; 145 | FILE_FLAG_FIRST_PIPE_INSTANCE = $00080000; 146 | FILE_FLAG_OPEN_NO_RECALL = $00100000; 147 | FILE_FLAG_OPEN_REPARSE_POINT = $00200000; 148 | FILE_FLAG_SESSION_AWARE = $00800000; 149 | FILE_FLAG_POSIX_SEMANTICS = $01000000; 150 | FILE_FLAG_BACKUP_SEMANTICS = $02000000; 151 | FILE_FLAG_DELETE_ON_CLOSE = $04000000; 152 | FILE_FLAG_SEQUENTIAL_SCAN = $08000000; 153 | FILE_FLAG_RANDOM_ACCESS = $10000000; 154 | FILE_FLAG_NO_BUFFERING = $20000000; 155 | FILE_FLAG_OVERLAPPED = $40000000; 156 | FILE_FLAG_WRITE_THROUGH = $80000000; 157 | 158 | // CreateDisposition 159 | 160 | FILE_SUPERSEDE = $00000000; 161 | FILE_OPEN = $00000001; 162 | FILE_CREATE = $00000002; 163 | FILE_OPEN_IF = $00000003; 164 | FILE_OVERWRITE = $00000004; 165 | FILE_OVERWRITE_IF = $00000005; 166 | 167 | // CreateOptions 168 | 169 | FILE_DIRECTORY_FILE = $00000001; 170 | FILE_WRITE_THROUGH = $00000002; 171 | FILE_SEQUENTIAL_ONLY = $00000004; 172 | FILE_NO_INTERMEDIATE_BUFFERING = $00000008; 173 | FILE_SYNCHRONOUS_IO_ALERT = $00000010; 174 | FILE_SYNCHRONOUS_IO_NONALERT = $00000020; 175 | FILE_NON_DIRECTORY_FILE = $00000040; 176 | FILE_CREATE_TREE_CONNECTION = $00000080; 177 | FILE_COMPLETE_IF_OPLOCKED = $00000100; 178 | FILE_NO_EA_KNOWLEDGE = $00000200; 179 | FILE_OPEN_FOR_RECOVERY = $00000400; 180 | FILE_RANDOM_ACCESS = $00000800; 181 | FILE_DELETE_ON_CLOSE = $00001000; 182 | FILE_OPEN_BY_FILE_ID = $00002000; 183 | FILE_OPEN_FOR_BACKUP_INTENT = $00004000; 184 | FILE_NO_COMPRESSION = $00008000; 185 | FILE_OPEN_REQUIRING_OPLOCK = $00010000; 186 | FILE_DISALLOW_EXCLUSIVE = $00020000; 187 | FILE_SESSION_AWARE = $00040000; 188 | FILE_RESERVE_OPFILTER = $00100000; 189 | FILE_OPEN_REPARSE_POINT = $00200000; 190 | FILE_OPEN_NO_RECALL = $00400000; 191 | FILE_OPEN_FOR_FREE_SPACE_QUERY = $00800000; 192 | 193 | // SecurityInformation 194 | 195 | OWNER_SECURITY_INFORMATION = $00000001; 196 | GROUP_SECURITY_INFORMATION = $00000002; 197 | DACL_SECURITY_INFORMATION = $00000004; 198 | SACL_SECURITY_INFORMATION = $00000008; 199 | LABEL_SECURITY_INFORMATION = $00000010; 200 | ATTRIBUTE_SECURITY_INFORMATION = $00000020; 201 | SCOPE_SECURITY_INFORMATION = $00000040; 202 | PROCESS_TRUST_LABEL_SECURITY_INFORMATION = $00000080; 203 | BACKUP_SECURITY_INFORMATION = $00010000; 204 | UNPROTECTED_SACL_SECURITY_INFORMATION = $10000000; 205 | UNPROTECTED_DACL_SECURITY_INFORMATION = $20000000; 206 | PROTECTED_SACL_SECURITY_INFORMATION = $40000000; 207 | PROTECTED_DACL_SECURITY_INFORMATION = $80000000; 208 | 209 | // FileSystemFlags 210 | 211 | FILE_CASE_SENSITIVE_SEARCH = $00000001; 212 | FILE_CASE_PRESERVED_NAMES = $00000002; 213 | FILE_UNICODE_ON_DISK = $00000004; 214 | FILE_PERSISTENT_ACLS = $00000008; 215 | FILE_FILE_COMPRESSION = $00000010; 216 | FILE_VOLUME_QUOTAS = $00000020; 217 | FILE_SUPPORTS_SPARSE_FILES = $00000040; 218 | FILE_SUPPORTS_REPARSE_POINTS = $00000080; 219 | FILE_SUPPORTS_REMOTE_STORAGE = $00000100; 220 | FILE_VOLUME_IS_COMPRESSED = $00008000; 221 | FILE_SUPPORTS_OBJECT_IDS = $00010000; 222 | FILE_SUPPORTS_ENCRYPTION = $00020000; 223 | FILE_NAMED_STREAMS = $00040000; 224 | FILE_READ_ONLY_VOLUME = $00080000; 225 | FILE_SEQUENTIAL_WRITE_ONCE = $00100000; 226 | FILE_SUPPORTS_TRANSACTIONS = $00200000; 227 | FILE_SUPPORTS_HARD_LINKS = $00400000; 228 | FILE_SUPPORTS_EXTENDED_ATTRIBUTES = $00800000; 229 | FILE_SUPPORTS_OPEN_BY_FILE_ID = $01000000; 230 | FILE_SUPPORTS_USN_JOURNAL = $02000000; 231 | 232 | // AccessState Flags 233 | 234 | TOKEN_HAS_TRAVERSE_PRIVILEGE = $01; 235 | TOKEN_HAS_BACKUP_PRIVILEGE = $02; 236 | TOKEN_HAS_RESTORE_PRIVILEGE = $04; 237 | TOKEN_HAS_ADMIN_GROUP = $08; 238 | TOKEN_IS_RESTRICTED = $10; 239 | 240 | // NTSTATUS 241 | 242 | STATUS_SUCCESS = NTSTATUS($00000000); 243 | STATUS_BUFFER_OVERFLOW = NTSTATUS($80000005); 244 | STATUS_NO_MORE_FILES = NTSTATUS($80000006); 245 | STATUS_UNSUCCESSFUL = NTSTATUS($C0000001); 246 | STATUS_NOT_IMPLEMENTED = NTSTATUS($C0000002); 247 | STATUS_INVALID_HANDLE = NTSTATUS($C0000008); 248 | STATUS_INVALID_PARAMETER = NTSTATUS($C000000D); 249 | STATUS_NO_SUCH_FILE = NTSTATUS($C000000F); 250 | STATUS_END_OF_FILE = NTSTATUS($C0000011); 251 | STATUS_NO_MEMORY = NTSTATUS($C0000017); 252 | STATUS_ACCESS_DENIED = NTSTATUS($C0000022); 253 | STATUS_BUFFER_TOO_SMALL = NTSTATUS($C0000023); 254 | STATUS_OBJECT_NAME_INVALID = NTSTATUS($C0000033); 255 | STATUS_OBJECT_NAME_NOT_FOUND = NTSTATUS($C0000034); 256 | STATUS_OBJECT_NAME_COLLISION = NTSTATUS($C0000035); 257 | STATUS_OBJECT_PATH_NOT_FOUND = NTSTATUS($C000003A); 258 | STATUS_SHARING_VIOLATION = NTSTATUS($C0000043); 259 | STATUS_LOCK_NOT_GRANTED = NTSTATUS($C0000055); 260 | STATUS_PRIVILEGE_NOT_HELD = NTSTATUS($C0000061); 261 | STATUS_DISK_FULL = NTSTATUS($C000007F); 262 | STATUS_INSUFFICIENT_RESOURCES = NTSTATUS($C000009A); 263 | STATUS_DEVICE_NOT_READY = NTSTATUS($C00000A3); 264 | STATUS_FILE_IS_A_DIRECTORY = NTSTATUS($C00000BA); 265 | STATUS_NOT_SUPPORTED = NTSTATUS($C00000BB); 266 | STATUS_INTERNAL_ERROR = NTSTATUS($C00000E5); 267 | STATUS_DIRECTORY_NOT_EMPTY = NTSTATUS($C0000101); 268 | STATUS_NOT_A_DIRECTORY = NTSTATUS($C0000103); 269 | STATUS_NAME_TOO_LONG = NTSTATUS($C0000106); 270 | STATUS_CANNOT_DELETE = NTSTATUS($C0000121); 271 | STATUS_NOT_FOUND = NTSTATUS($C0000225); 272 | 273 | VOLUME_SECURITY_DESCRIPTOR_MAX_SIZE = 1024 * 16; 274 | 275 | implementation 276 | 277 | end. 278 | -------------------------------------------------------------------------------- /Samples/Mirror/Mirror.dproj: -------------------------------------------------------------------------------- 1 | <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> 2 | <PropertyGroup> 3 | <ProjectGuid>{3A07D74D-B1FE-4345-9C95-2226092B7136}</ProjectGuid> 4 | <MainSource>Mirror.dpr</MainSource> 5 | <Base>True</Base> 6 | <Config Condition="'$(Config)'==''">Debug</Config> 7 | <TargetedPlatforms>3</TargetedPlatforms> 8 | <AppType>Console</AppType> 9 | <FrameworkType>None</FrameworkType> 10 | <ProjectVersion>20.1</ProjectVersion> 11 | <Platform Condition="'$(Platform)'==''">Win64</Platform> 12 | <ProjectName Condition="'$(ProjectName)'==''">Mirror</ProjectName> 13 | </PropertyGroup> 14 | <PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''"> 15 | <Base>true</Base> 16 | </PropertyGroup> 17 | <PropertyGroup Condition="('$(Platform)'=='OSX64' and '$(Base)'=='true') or '$(Base_OSX64)'!=''"> 18 | <Base_OSX64>true</Base_OSX64> 19 | <CfgParent>Base</CfgParent> 20 | <Base>true</Base> 21 | </PropertyGroup> 22 | <PropertyGroup Condition="('$(Platform)'=='OSXARM64' and '$(Base)'=='true') or '$(Base_OSXARM64)'!=''"> 23 | <Base_OSXARM64>true</Base_OSXARM64> 24 | <CfgParent>Base</CfgParent> 25 | <Base>true</Base> 26 | </PropertyGroup> 27 | <PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Base)'=='true') or '$(Base_Win32)'!=''"> 28 | <Base_Win32>true</Base_Win32> 29 | <CfgParent>Base</CfgParent> 30 | <Base>true</Base> 31 | </PropertyGroup> 32 | <PropertyGroup Condition="('$(Platform)'=='Win64' and '$(Base)'=='true') or '$(Base_Win64)'!=''"> 33 | <Base_Win64>true</Base_Win64> 34 | <CfgParent>Base</CfgParent> 35 | <Base>true</Base> 36 | </PropertyGroup> 37 | <PropertyGroup Condition="'$(Config)'=='Debug' or '$(Cfg_2)'!=''"> 38 | <Cfg_2>true</Cfg_2> 39 | <CfgParent>Base</CfgParent> 40 | <Base>true</Base> 41 | </PropertyGroup> 42 | <PropertyGroup Condition="('$(Platform)'=='OSX64' and '$(Cfg_2)'=='true') or '$(Cfg_2_OSX64)'!=''"> 43 | <Cfg_2_OSX64>true</Cfg_2_OSX64> 44 | <CfgParent>Cfg_2</CfgParent> 45 | <Cfg_2>true</Cfg_2> 46 | <Base>true</Base> 47 | </PropertyGroup> 48 | <PropertyGroup Condition="('$(Platform)'=='OSXARM64' and '$(Cfg_2)'=='true') or '$(Cfg_2_OSXARM64)'!=''"> 49 | <Cfg_2_OSXARM64>true</Cfg_2_OSXARM64> 50 | <CfgParent>Cfg_2</CfgParent> 51 | <Cfg_2>true</Cfg_2> 52 | <Base>true</Base> 53 | </PropertyGroup> 54 | <PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_2)'=='true') or '$(Cfg_2_Win32)'!=''"> 55 | <Cfg_2_Win32>true</Cfg_2_Win32> 56 | <CfgParent>Cfg_2</CfgParent> 57 | <Cfg_2>true</Cfg_2> 58 | <Base>true</Base> 59 | </PropertyGroup> 60 | <PropertyGroup Condition="('$(Platform)'=='Win64' and '$(Cfg_2)'=='true') or '$(Cfg_2_Win64)'!=''"> 61 | <Cfg_2_Win64>true</Cfg_2_Win64> 62 | <CfgParent>Cfg_2</CfgParent> 63 | <Cfg_2>true</Cfg_2> 64 | <Base>true</Base> 65 | </PropertyGroup> 66 | <PropertyGroup Condition="'$(Base)'!=''"> 67 | <DCC_K>false</DCC_K> 68 | <DCC_ImageBase>00400000</DCC_ImageBase> 69 | <DCC_Namespace>System;Xml;Data;Datasnap;Web;Soap;Winapi;$(DCC_Namespace)</DCC_Namespace> 70 | <DCC_S>false</DCC_S> 71 | <DCC_N>false</DCC_N> 72 | <VerInfo_Locale>1031</VerInfo_Locale> 73 | <DCC_E>false</DCC_E> 74 | <DCC_F>false</DCC_F> 75 | <VerInfo_Keys>CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName=;CFBundleDisplayName=;UIDeviceFamily=;CFBundleIdentifier=;CFBundleVersion=;CFBundlePackageType=;CFBundleSignature=;CFBundleAllowMixedLocalizations=;UISupportedInterfaceOrientations=;CFBundleExecutable=;CFBundleResourceSpecification=;LSRequiresIPhoneOS=;CFBundleInfoDictionaryVersion=;CFBundleDevelopmentRegion=</VerInfo_Keys> 76 | <DCC_ExeOutput>.\$(Platform)</DCC_ExeOutput> 77 | <DCC_DcuOutput>.\$(Platform)</DCC_DcuOutput> 78 | <Manifest_File>None</Manifest_File> 79 | <SanitizedProjectName>Mirror</SanitizedProjectName> 80 | <Icon_MainIcon>$(BDS)\bin\delphi_PROJECTICON.ico</Icon_MainIcon> 81 | <Icns_MainIcns>$(BDS)\bin\delphi_PROJECTICNS.icns</Icns_MainIcns> 82 | </PropertyGroup> 83 | <PropertyGroup Condition="'$(Base_OSX64)'!=''"> 84 | <VerInfo_Keys>CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSCameraUsageDescription=The reason for accessing the camera;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSMotionUsageDescription=The reason for accessing the accelerometer;NSDesktopFolderUsageDescription=The reason for accessing the Desktop folder;NSDocumentsFolderUsageDescription=The reason for accessing the Documents folder;NSDownloadsFolderUsageDescription=The reason for accessing the Downloads folder;NSNetworkVolumesUsageDescription=The reason for accessing files on a network volume;NSRemovableVolumesUsageDescription=The reason for accessing files on a removable volume;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers</VerInfo_Keys> 85 | <BT_BuildType>Debug</BT_BuildType> 86 | </PropertyGroup> 87 | <PropertyGroup Condition="'$(Base_OSXARM64)'!=''"> 88 | <VerInfo_Keys>CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSCameraUsageDescription=The reason for accessing the camera;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSMotionUsageDescription=The reason for accessing the accelerometer;NSDesktopFolderUsageDescription=The reason for accessing the Desktop folder;NSDocumentsFolderUsageDescription=The reason for accessing the Documents folder;NSDownloadsFolderUsageDescription=The reason for accessing the Downloads folder;NSNetworkVolumesUsageDescription=The reason for accessing files on a network volume;NSRemovableVolumesUsageDescription=The reason for accessing files on a removable volume;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers</VerInfo_Keys> 89 | <BT_BuildType>Debug</BT_BuildType> 90 | </PropertyGroup> 91 | <PropertyGroup Condition="'$(Base_Win32)'!=''"> 92 | <DCC_Namespace>System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)</DCC_Namespace> 93 | <VerInfo_Locale>1033</VerInfo_Locale> 94 | <VerInfo_Keys>CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName)</VerInfo_Keys> 95 | </PropertyGroup> 96 | <PropertyGroup Condition="'$(Base_Win64)'!=''"> 97 | <DCC_Namespace>System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace)</DCC_Namespace> 98 | <VerInfo_Locale>1033</VerInfo_Locale> 99 | <VerInfo_Keys>CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName)</VerInfo_Keys> 100 | </PropertyGroup> 101 | <PropertyGroup Condition="'$(Cfg_2)'!=''"> 102 | <DCC_GenerateStackFrames>true</DCC_GenerateStackFrames> 103 | <DCC_Define>DEBUG;$(DCC_Define)</DCC_Define> 104 | <DCC_Optimize>false</DCC_Optimize> 105 | </PropertyGroup> 106 | <PropertyGroup Condition="'$(Cfg_2_OSX64)'!=''"> 107 | <DCC_RemoteDebug>true</DCC_RemoteDebug> 108 | </PropertyGroup> 109 | <PropertyGroup Condition="'$(Cfg_2_OSXARM64)'!=''"> 110 | <DCC_RemoteDebug>true</DCC_RemoteDebug> 111 | </PropertyGroup> 112 | <PropertyGroup Condition="'$(Cfg_2_Win32)'!=''"> 113 | <Debugger_RunParams>/R d:\test /L k:\ /D /S /O</Debugger_RunParams> 114 | </PropertyGroup> 115 | <PropertyGroup Condition="'$(Cfg_2_Win64)'!=''"> 116 | <VerInfo_Locale>1033</VerInfo_Locale> 117 | <Manifest_File>None</Manifest_File> 118 | <VerInfo_Keys>CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName)</VerInfo_Keys> 119 | <Debugger_RunParams>/R d:\test /L k:\ /D /S /O</Debugger_RunParams> 120 | </PropertyGroup> 121 | <ItemGroup> 122 | <DelphiCompile Include="$(MainSource)"> 123 | <MainSource>MainSource</MainSource> 124 | </DelphiCompile> 125 | <DCCReference Include="..\..\Dokan.pas"/> 126 | <DCCReference Include="..\..\DokanWin.pas"/> 127 | <BuildConfiguration Include="Base"> 128 | <Key>Base</Key> 129 | </BuildConfiguration> 130 | <BuildConfiguration Include="Debug"> 131 | <Key>Cfg_2</Key> 132 | <CfgParent>Base</CfgParent> 133 | </BuildConfiguration> 134 | </ItemGroup> 135 | <ProjectExtensions> 136 | <Borland.Personality>Delphi.Personality.12</Borland.Personality> 137 | <Borland.ProjectType/> 138 | <BorlandProject> 139 | <Delphi.Personality> 140 | <Source> 141 | <Source Name="MainSource">Mirror.dpr</Source> 142 | </Source> 143 | <VersionInfo> 144 | <VersionInfo Name="IncludeVerInfo">False</VersionInfo> 145 | <VersionInfo Name="AutoIncBuild">False</VersionInfo> 146 | <VersionInfo Name="MajorVer">1</VersionInfo> 147 | <VersionInfo Name="MinorVer">0</VersionInfo> 148 | <VersionInfo Name="Release">0</VersionInfo> 149 | <VersionInfo Name="Build">0</VersionInfo> 150 | <VersionInfo Name="Debug">False</VersionInfo> 151 | <VersionInfo Name="PreRelease">False</VersionInfo> 152 | <VersionInfo Name="Special">False</VersionInfo> 153 | <VersionInfo Name="Private">False</VersionInfo> 154 | <VersionInfo Name="DLL">False</VersionInfo> 155 | <VersionInfo Name="Locale">1031</VersionInfo> 156 | <VersionInfo Name="CodePage">1252</VersionInfo> 157 | </VersionInfo> 158 | <VersionInfoKeys> 159 | <VersionInfoKeys Name="CompanyName"/> 160 | <VersionInfoKeys Name="FileDescription"/> 161 | <VersionInfoKeys Name="FileVersion">1.0.0.0</VersionInfoKeys> 162 | <VersionInfoKeys Name="InternalName"/> 163 | <VersionInfoKeys Name="LegalCopyright"/> 164 | <VersionInfoKeys Name="LegalTrademarks"/> 165 | <VersionInfoKeys Name="OriginalFilename"/> 166 | <VersionInfoKeys Name="ProductName"/> 167 | <VersionInfoKeys Name="ProductVersion">1.0.0.0</VersionInfoKeys> 168 | <VersionInfoKeys Name="Comments"/> 169 | <VersionInfoKeys Name="CFBundleName"/> 170 | <VersionInfoKeys Name="CFBundleDisplayName"/> 171 | <VersionInfoKeys Name="UIDeviceFamily"/> 172 | <VersionInfoKeys Name="CFBundleIdentifier"/> 173 | <VersionInfoKeys Name="CFBundleVersion"/> 174 | <VersionInfoKeys Name="CFBundlePackageType"/> 175 | <VersionInfoKeys Name="CFBundleSignature"/> 176 | <VersionInfoKeys Name="CFBundleAllowMixedLocalizations"/> 177 | <VersionInfoKeys Name="UISupportedInterfaceOrientations"/> 178 | <VersionInfoKeys Name="CFBundleExecutable"/> 179 | <VersionInfoKeys Name="CFBundleResourceSpecification"/> 180 | <VersionInfoKeys Name="LSRequiresIPhoneOS"/> 181 | <VersionInfoKeys Name="CFBundleInfoDictionaryVersion"/> 182 | <VersionInfoKeys Name="CFBundleDevelopmentRegion"/> 183 | </VersionInfoKeys> 184 | <Excluded_Packages> 185 | <Excluded_Packages Name="$(BDSBIN)\dclMetropolisUILiveTile180.bpl">Embarcadero Metropolis-UI Standardkomponenten für Live-Kacheln</Excluded_Packages> 186 | <Excluded_Packages Name="$(BDSBIN)\dcloffice2k180.bpl">Microsoft Office 2000 Beispiele für gekapselte Komponenten für Automatisierungsserver</Excluded_Packages> 187 | <Excluded_Packages Name="$(BDSBIN)\dclofficexp180.bpl">Microsoft Office XP Beispiele für gekapselte Komponenten für Automation Server</Excluded_Packages> 188 | </Excluded_Packages> 189 | </Delphi.Personality> 190 | <Platforms> 191 | <Platform value="OSX64">False</Platform> 192 | <Platform value="OSXARM64">False</Platform> 193 | <Platform value="Win32">True</Platform> 194 | <Platform value="Win64">True</Platform> 195 | </Platforms> 196 | </BorlandProject> 197 | <ProjectFileVersion>12</ProjectFileVersion> 198 | </ProjectExtensions> 199 | <Import Project="$(BDS)\Bin\CodeGear.Delphi.Targets" Condition="Exists('$(BDS)\Bin\CodeGear.Delphi.Targets')"/> 200 | <Import Project="$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj" Condition="Exists('$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj')"/> 201 | </Project> 202 | -------------------------------------------------------------------------------- /Dokan.pas: -------------------------------------------------------------------------------- 1 | (* 2 | Dokan API wrapper for Delphi based on Release 2.2.0.1000 3 | https://github.com/dokan-dev/dokany/releases/tag/v2.2.0.1000 4 | Copyright (C) 2019 - 2024 Sven Harazim 5 | 6 | Dokan : user-mode file system library for Windows 7 | 8 | Copyright (C) 2015 - 2019 Adrien J. <liryna.stark@gmail.com> and Maxime C. <maxime@islog.com> 9 | Copyright (C) 2020 - 2022 Google, Inc. 10 | Copyright (C) 2007 - 2011 Hiroki Asakawa <info@dokan-dev.net> 11 | 12 | http://dokan-dev.github.io 13 | 14 | This program is free software; you can redistribute it and/or modify it under 15 | the terms of the GNU Lesser General Public License as published by the Free 16 | Software Foundation; either version 3 of the License, or (at your option) any 17 | later version. 18 | 19 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 20 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 21 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 22 | 23 | You should have received a copy of the GNU Lesser General Public License along 24 | with this program. If not, see <http://www.gnu.org/licenses/>. 25 | *) 26 | 27 | unit Dokan; 28 | 29 | {.$DEFINE DOKAN_EXPLICIT_LINK} 30 | 31 | {$ifdef FPC} 32 | {$mode delphi} 33 | {$endif FPC} 34 | 35 | {$align 8} 36 | {$minenumsize 4} 37 | 38 | interface 39 | 40 | uses 41 | Windows, DokanWin; 42 | 43 | const 44 | DokanLibrary = 'dokan2.dll'; 45 | 46 | //The current Dokan version (200 means ver 2.0.0). 47 | DOKAN_VERSION = 220; 48 | //Minimum Dokan version (ver 2.0.0) accepted 49 | DOKAN_MINIMUM_COMPATIBLE_VERSION = 200; 50 | 51 | //Enable ouput debug message 52 | DOKAN_OPTION_DEBUG = 1; 53 | //Enable ouput debug message to stderr 54 | DOKAN_OPTION_STDERR = 2; 55 | //Enable the use of alternate stream paths in the form 56 | //<file-name>:<stream-name>. If this is not specified then the driver will 57 | //fail any attempt to access a path with a colon. 58 | DOKAN_OPTION_ALT_STREAM = 4; 59 | //Enable mount drive as write-protected 60 | DOKAN_OPTION_WRITE_PROTECT = 8; 61 | //Use network drive - Dokan network provider needs to be installed and a DOKAN_OPTIONS.UNCName provided 62 | DOKAN_OPTION_NETWORK = 16; 63 | //Use removable drive 64 | //Be aware that on some environments, the userland application will be denied 65 | //to communicate with the drive which will result in a unwanted unmount. 66 | //see <a href="https://github.com/dokan-dev/dokany/issues/843">Issue #843</a> 67 | DOKAN_OPTION_REMOVABLE = 32; 68 | //Use mount manager 69 | DOKAN_OPTION_MOUNT_MANAGER = 64; 70 | //Mount the drive on current session only 71 | //Note: As Windows process only have on sessionID which is here used to define what is the current session, 72 | //impersonation will not work if someone attend to mount for a user from another one (like system service). 73 | //See issue #1196 74 | DOKAN_OPTION_CURRENT_SESSION = 128; 75 | //Enable Lockfile/Unlockfile operations. Otherwise Dokan will take care of it 76 | DOKAN_OPTION_FILELOCK_USER_MODE = 256; 77 | //Enable Case sensitive path. 78 | //By default all path are case insensitive. 79 | //For case sensitive: \dir\File & \diR\\file are different files 80 | //but for case insensitive they are the same. 81 | DOKAN_OPTION_CASE_SENSITIVE = 512; 82 | //Allows unmounting of network drive via explorer */ 83 | DOKAN_OPTION_ENABLE_UNMOUNT_NETWORK_DRIVE = 1024; 84 | //Forward the kernel driver global and volume logs to the userland. 85 | //Can be very slow if single thread is enabled. 86 | DOKAN_OPTION_DISPATCH_DRIVER_LOGS = 2048; 87 | //Pull batches of events from the driver instead of a single one and execute them parallelly. 88 | //This option should only be used on computers with low cpu count 89 | //and userland filesystem taking time to process requests (like remote storage). 90 | DOKAN_OPTION_ALLOW_IPC_BATCHING = 4096; 91 | 92 | type 93 | DOKAN_HANDLE = THandle; 94 | HANDLE = THandle; 95 | 96 | //Dokan mount options used to describe Dokan device behavior. 97 | _DOKAN_OPTIONS = record 98 | Version: USHORT; //Version of the Dokan features requested without dots (version "123" is equal to Dokan version 1.2.3). 99 | SingleThread: ByteBool; //Only use a single thread to process events. This is highly not recommended as can easily create a bottleneck. 100 | Options: ULONG; //Features enabled for the mount. See \ref DOKAN_OPTION. 101 | GlobalContext: ULONG64; //FileSystem can store anything here. 102 | MountPoint: LPCWSTR; //Mount point. It can be a driver letter like "M:\" or an existing empty folder path "C:\mount\dokan" on a NTFS partition. */ 103 | UNCName: LPCWSTR; //UNC Name for the Network Redirector 104 | Timeout: ULONG; //Max timeout in milliseconds of each request before Dokan gives up to wait events to complete. The default timeout value is 15 seconds. 105 | AllocationUnitSize: ULONG;//Allocation Unit Size of the volume. This will affect the file size. 106 | SectorSize: ULONG; //Sector Size of the volume. This will affect the file size. 107 | VolumeSecurityDescriptorLength : ULONG; //Length of the optional VolumeSecurityDescriptor provided. Set 0 will disable the option. 108 | VolumeSecurityDescriptor : array [0..VOLUME_SECURITY_DESCRIPTOR_MAX_SIZE-1] of AnsiChar;//Optional Volume Security descriptor. See <a href="https://docs.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-initializesecuritydescriptor">InitializeSecurityDescriptor</a> 109 | end; 110 | DOKAN_OPTIONS = _DOKAN_OPTIONS; 111 | PDOKAN_OPTIONS = ^_DOKAN_OPTIONS; 112 | TDokanOptions = DOKAN_OPTIONS; 113 | PDokanOptions = PDOKAN_OPTIONS; 114 | 115 | //Dokan file information on the current operation. 116 | _DOKAN_FILE_INFO = record 117 | Context: ULONG64; 118 | DokanContext: ULONG64; 119 | DokanOptions: PDOKAN_OPTIONS; 120 | ProcessingContext : PVOID; 121 | ProcessId: ULONG; 122 | IsDirectory: ByteBool; 123 | DeleteOnClose: ByteBool; 124 | PagingIo: ByteBool; 125 | SynchronousIo: ByteBool; 126 | Nocache: ByteBool; 127 | WriteToEndOfFile: ByteBool; 128 | end; 129 | DOKAN_FILE_INFO = _DOKAN_FILE_INFO; 130 | PDOKAN_FILE_INFO = ^_DOKAN_FILE_INFO; 131 | TDokanFileInfo = DOKAN_FILE_INFO; 132 | PDokanFileInfo = PDOKAN_FILE_INFO; 133 | 134 | const 135 | DOKAN_EXCEPTION_NOT_INITIALIZED = $0f0ff0ff; 136 | DOKAN_EXCEPTION_INITIALIZATION_FAILED = $0fbadbad; 137 | DOKAN_EXCEPTION_SHUTDOWN_FAILED = $0fbadf00; 138 | 139 | type 140 | //FillFindData Used to add an entry in FindFiles operation 141 | //return 1 if buffer is full, otherwise 0 (currently it never returns 1) 142 | TDokanFillFindData = function ( 143 | FindData: PWin32FindData; 144 | DokanFileInfo: PDOKAN_FILE_INFO 145 | ): Integer; stdcall; 146 | 147 | //FillFindStreamData Used to add an entry in FindStreams 148 | //return FALSE if the buffer is full, otherwise TRUE 149 | TDokanFillFindStreamData = function ( 150 | FindStreamData: PWIN32_FIND_STREAM_DATA; 151 | FindStreamContext: PVOID 152 | ): BOOL; stdcall; 153 | 154 | _DOKAN_ACCESS_STATE = record 155 | SecurityEvaluated: ByteBool; 156 | GenerateAudit: ByteBool; 157 | GenerateOnClose: ByteBool; 158 | AuditPrivileges: ByteBool; 159 | Flags: ULONG; 160 | RemainingDesiredAccess: ACCESS_MASK; 161 | PreviouslyGrantedAccess: ACCESS_MASK; 162 | OriginalDesiredAccess: ACCESS_MASK; 163 | SecurityDescriptor: PSECURITY_DESCRIPTOR; 164 | ObjectName: UNICODE_STRING; 165 | ObjectType: UNICODE_STRING; 166 | end; 167 | DOKAN_ACCESS_STATE = _DOKAN_ACCESS_STATE; 168 | PDOKAN_ACCESS_STATE = ^_DOKAN_ACCESS_STATE; 169 | TDokanAccessState = DOKAN_ACCESS_STATE; 170 | PDokanAccessState = PDOKAN_ACCESS_STATE; 171 | 172 | _DOKAN_IO_SECURITY_CONTEXT = record 173 | AccessState: DOKAN_ACCESS_STATE; 174 | DesiredAccess: ACCESS_MASK; 175 | end; 176 | DOKAN_IO_SECURITY_CONTEXT = _DOKAN_IO_SECURITY_CONTEXT; 177 | PDOKAN_IO_SECURITY_CONTEXT = ^_DOKAN_IO_SECURITY_CONTEXT; 178 | TDokanIOSecurityContext = DOKAN_IO_SECURITY_CONTEXT; 179 | PDokanIOSecurityContext = PDOKAN_IO_SECURITY_CONTEXT; 180 | 181 | TDokanZwCreateFile = function ( 182 | FileName: LPCWSTR; 183 | SecurityContext: PDOKAN_IO_SECURITY_CONTEXT; 184 | DesiredAccess: ACCESS_MASK; 185 | FileAttributes: ULONG; 186 | ShareAccess: ULONG; 187 | CreateDisposition: ULONG; 188 | CreateOptions: ULONG; 189 | DokanFileInfo: PDOKAN_FILE_INFO 190 | ): NTSTATUS; stdcall; 191 | 192 | TDokanCleanup = procedure ( 193 | FileName: LPCWSTR; 194 | DokanFileInfo: PDOKAN_FILE_INFO 195 | ); stdcall; 196 | 197 | TDokanCloseFile = procedure ( 198 | FileName: LPCWSTR; 199 | DokanFileInfo: PDOKAN_FILE_INFO 200 | ); stdcall; 201 | 202 | TDokanReadFile = function ( 203 | FileName: LPCWSTR; 204 | var Buffer; 205 | BufferLength: DWORD; 206 | ReadLength: PDWORD; 207 | Offset: LONGLONG; 208 | DokanFileInfo: PDOKAN_FILE_INFO 209 | ): NTSTATUS; stdcall; 210 | 211 | TDokanWriteFile = function ( 212 | FileName: LPCWSTR; 213 | const Buffer; 214 | NumberOfBytesToWrite: DWORD; 215 | NumberOfBytesWritten: PDWORD; 216 | Offset: LONGLONG; 217 | DokanFileInfo: PDOKAN_FILE_INFO 218 | ): NTSTATUS; stdcall; 219 | 220 | TDokanFlushFileBuffers = function ( 221 | FileName: LPCWSTR; 222 | DokanFileInfo: PDOKAN_FILE_INFO 223 | ): NTSTATUS; stdcall; 224 | 225 | TDokanGetFileInformation = function ( 226 | FileName: LPCWSTR; 227 | Buffer: PByHandleFileInformation; 228 | DokanFileInfo: PDOKAN_FILE_INFO 229 | ): NTSTATUS; stdcall; 230 | 231 | TDokanFindFiles = function ( 232 | PathName: LPCWSTR; 233 | FillFindData: TDokanFillFindData; 234 | DokanFileInfo: PDOKAN_FILE_INFO 235 | ): NTSTATUS; stdcall; 236 | 237 | TDokanFindFilesWithPattern = function ( 238 | PathName: LPCWSTR; 239 | SearchPattern: LPCWSTR; 240 | FillFindData: TDokanFillFindData; 241 | DokanFileInfo: PDOKAN_FILE_INFO 242 | ): NTSTATUS; stdcall; 243 | 244 | TDokanSetFileAttributes = function ( 245 | FileName: LPCWSTR; 246 | FileAttributes: DWORD; 247 | DokanFileInfo: PDOKAN_FILE_INFO 248 | ): NTSTATUS; stdcall; 249 | 250 | TDokanSetFileTime = function ( 251 | FileName: LPCWSTR; 252 | var CreationTime: FILETIME; 253 | var LastAccessTime: FILETIME; 254 | var LastWriteTime: FILETIME; 255 | DokanFileInfo: PDOKAN_FILE_INFO 256 | ): NTSTATUS; stdcall; 257 | 258 | TDokanDeleteFile = function ( 259 | FileName: LPCWSTR; 260 | DokanFileInfo: PDOKAN_FILE_INFO 261 | ): NTSTATUS; stdcall; 262 | 263 | TDokanDeleteDirectory = function ( 264 | FileName: LPCWSTR; 265 | DokanFileInfo: PDOKAN_FILE_INFO 266 | ): NTSTATUS; stdcall; 267 | 268 | TDokanMoveFile = function ( 269 | FileName: LPCWSTR; 270 | NewFileName: LPCWSTR; 271 | ReplaceIfExisting: BOOL; 272 | DokanFileInfo: PDOKAN_FILE_INFO 273 | ): NTSTATUS; stdcall; 274 | 275 | TDokanSetEndOfFile = function ( 276 | FileName: LPCWSTR; 277 | ByteOffset: LONGLONG; 278 | DokanFileInfo: PDOKAN_FILE_INFO 279 | ): NTSTATUS; stdcall; 280 | 281 | TDokanSetAllocationSize = function ( 282 | FileName: LPCWSTR; 283 | AllocSize: LONGLONG; 284 | DokanFileInfo: PDOKAN_FILE_INFO 285 | ): NTSTATUS; stdcall; 286 | 287 | TDokanLockFile = function ( 288 | FileName: LPCWSTR; 289 | ByteOffset: LONGLONG; 290 | Length: LONGLONG; 291 | DokanFileInfo: PDOKAN_FILE_INFO 292 | ): NTSTATUS; stdcall; 293 | 294 | TDokanUnlockFile = function ( 295 | FileName: LPCWSTR; 296 | ByteOffset: LONGLONG; 297 | Length: LONGLONG; 298 | DokanFileInfo: PDOKAN_FILE_INFO 299 | ): NTSTATUS; stdcall; 300 | 301 | TDokanGetDiskFreeSpace = function ( 302 | FreeBytesAvailable: PULONGLONG; 303 | TotalNumberOfBytes: PULONGLONG; 304 | TotalNumberOfFreeBytes: PULONGLONG; 305 | DokanFileInfo: PDOKAN_FILE_INFO 306 | ): NTSTATUS; stdcall; 307 | 308 | TDokanGetVolumeInformation = function ( 309 | VolumeNameBuffer: LPWSTR; 310 | VolumeNameSize: DWORD; 311 | VolumeSerialNumber: PDWORD; 312 | MaximumComponentLength: PDWORD; 313 | FileSystemFlags: PDWORD; 314 | FileSystemNameBuffer: LPWSTR; 315 | FileSystemNameSize: DWORD; 316 | DokanFileInfo: PDOKAN_FILE_INFO 317 | ): NTSTATUS; stdcall; 318 | 319 | TDokanMounted = function ( 320 | MountPoint: LPCWSTR; 321 | DokanFileInfo: PDOKAN_FILE_INFO 322 | ): NTSTATUS; stdcall; 323 | 324 | TDokanUnmounted = function ( 325 | DokanFileInfo: PDOKAN_FILE_INFO 326 | ): NTSTATUS; stdcall; 327 | 328 | TDokanGetFileSecurity = function ( 329 | FileName: LPCWSTR; 330 | SecurityInformation: PSECURITY_INFORMATION; 331 | SecurityDescriptor: PSECURITY_DESCRIPTOR; 332 | BufferLength: ULONG; 333 | LengthNeeded: PULONG; 334 | DokanFileInfo: PDOKAN_FILE_INFO 335 | ): NTSTATUS; stdcall; 336 | 337 | TDokanSetFileSecurity = function ( 338 | FileName: LPCWSTR; 339 | SecurityInformation: PSECURITY_INFORMATION; 340 | SecurityDescriptor: PSECURITY_DESCRIPTOR; 341 | BufferLength: ULONG; 342 | DokanFileInfo: PDOKAN_FILE_INFO 343 | ): NTSTATUS; stdcall; 344 | 345 | TDokanFindStreams = function ( 346 | FileName: LPCWSTR; 347 | FillFindStreamData: TDokanFillFindStreamData; 348 | FindStreamContext : PVOID; 349 | DokanFileInfo: PDOKAN_FILE_INFO 350 | ): NTSTATUS; stdcall; 351 | 352 | _DOKAN_OPERATIONS = record 353 | ZwCreateFile: TDokanZwCreateFile; 354 | Cleanup: TDokanCleanup; 355 | CloseFile: TDokanCloseFile; 356 | ReadFile: TDokanReadFile; 357 | WriteFile: TDokanWriteFile; 358 | FlushFileBuffers: TDokanFlushFileBuffers; 359 | GetFileInformation: TDokanGetFileInformation; 360 | FindFiles: TDokanFindFiles; 361 | FindFilesWithPattern: TDokanFindFilesWithPattern; 362 | SetFileAttributes: TDokanSetFileAttributes; 363 | SetFileTime: TDokanSetFileTime; 364 | DeleteFile: TDokanDeleteFile; 365 | DeleteDirectory: TDokanDeleteDirectory; 366 | MoveFile: TDokanMoveFile; 367 | SetEndOfFile: TDokanSetEndOfFile; 368 | SetAllocationSize: TDokanSetAllocationSize; 369 | LockFile: TDokanLockFile; 370 | UnlockFile: TDokanUnlockFile; 371 | GetDiskFreeSpace: TDokanGetDiskFreeSpace; 372 | GetVolumeInformation: TDokanGetVolumeInformation; 373 | Mounted: TDokanMounted; 374 | Unmounted: TDokanUnmounted; 375 | GetFileSecurity: TDokanGetFileSecurity; 376 | SetFileSecurity: TDokanSetFileSecurity; 377 | FindStreams: TDokanFindStreams; 378 | end; 379 | DOKAN_OPERATIONS = _DOKAN_OPERATIONS; 380 | PDOKAN_OPERATIONS = ^_DOKAN_OPERATIONS; 381 | TDokanOperations = DOKAN_OPERATIONS; 382 | PDokanOperations = PDOKAN_OPERATIONS; 383 | 384 | _DOKAN_MOUNT_POINT_INFO = record 385 | Type_ : ULONG; 386 | MountPoint: array [0 .. MAX_PATH - 1] of WCHAR; 387 | UNCName: array [0 .. 63] of WCHAR; 388 | DeviceName: array [0 .. 63] of WCHAR; 389 | SessionId : ULONG; 390 | MountOptions : ULONG; 391 | end; 392 | DOKAN_MOUNT_POINT_INFO = _DOKAN_MOUNT_POINT_INFO; 393 | PDOKAN_MOUNT_POINT_INFO = ^DOKAN_MOUNT_POINT_INFO; 394 | TDokanMountPointInfo = _DOKAN_MOUNT_POINT_INFO; 395 | PDokanMountPointInfo = PDOKAN_MOUNT_POINT_INFO; 396 | 397 | const 398 | DOKAN_SUCCESS = 0; 399 | DOKAN_ERROR = -1; 400 | DOKAN_DRIVE_LETTER_ERROR = -2; 401 | DOKAN_DRIVER_INSTALL_ERROR = -3; 402 | DOKAN_START_ERROR = -4; 403 | DOKAN_MOUNT_ERROR = -5; 404 | DOKAN_MOUNT_POINT_ERROR = -6; 405 | DOKAN_VERSION_ERROR = -7; 406 | 407 | {$ifdef DOKAN_EXPLICIT_LINK} 408 | 409 | var 410 | DokanLibHandle: HMODULE = 0; 411 | DokanInit: procedure; stdcall = nil; 412 | DokanShutdown: procedure; stdcall = nil; 413 | DokanMain: function (Options: PDOKAN_OPTIONS; Operations: PDOKAN_OPERATIONS): Integer; stdcall = nil; 414 | DokanCreateFileSystem: function (DokanOptions : PDOKAN_OPTIONS; DokanOperations : PDOKAN_OPERATIONS; var DokanInstance : DOKAN_HANDLE) : Integer; stdcall = nil; 415 | DokanIsFileSystemRunning: function (DokanInstance : DOKAN_HANDLE) : BOOL; stdcall = nil; 416 | DokanWaitForFileSystemClosed: function (DokanInstance : DOKAN_HANDLE; dwMilliseconds : DWORD) : DWORD; stdcall = nil; 417 | DokanRegisterWaitForFileSystemClosed: function (DokanInstance : DOKAN_HANDLE; var WaitHandle : PHANDLE; Callback : TWaitOrTimerCallback; Context : PVOID; dwMilliseconds : DWORD): BOOL; stdcall = nil; 418 | DokanUnregisterWaitForFileSystemClosed: function (WaitHandle : HANDLE; WaitForCallbacks : BOOL): BOOL; stdcall = nil; 419 | DokanCloseHandle: procedure (DokanInstance : DOKAN_HANDLE); stdcall = nil; 420 | DokanUnmount: function (DriveLetter: WCHAR): BOOL; stdcall = nil; 421 | DokanRemoveMountPoint: function (MountPoint: LPCWSTR): BOOL; stdcall = nil; 422 | DokanIsNameInExpression: function (Expression, Name: LPCWSTR; IgnoreCase: BOOL): BOOL; stdcall = nil; 423 | DokanVersion: function (): ULONG; stdcall = nil; 424 | DokanDriverVersion: function (): ULONG; stdcall = nil; 425 | DokanResetTimeout: function (Timeout: ULONG; DokanFileInfo: PDOKAN_FILE_INFO): BOOL; stdcall = nil; 426 | DokanOpenRequestorToken: function (DokanFileInfo: PDOKAN_FILE_INFO): THandle; stdcall = nil; 427 | DokanGetMountPointList: function (uncOnly: BOOL; var nbRead: ULONG): PDOKAN_MOUNT_POINT_INFO; stdcall = nil; 428 | DokanReleaseMountPointList: procedure (list: PDOKAN_MOUNT_POINT_INFO); stdcall = nil; 429 | DokanMapKernelToUserCreateFileFlags: procedure (DesiredAccess: ACCESS_MASK; FileAttributes, CreateOptions, 430 | CreateDisposition: ULONG;outDesiredAccess: PACCESS_MASK; outFileAttributesAndFlags, 431 | outCreationDisposition: PDWORD); stdcall = nil; 432 | DokanNotifyCreate: function (DokanInstance : DOKAN_HANDLE; FilePath: LPCWSTR; IsDirectory : BOOL) : BOOL; stdcall = nil; 433 | DokanNotifyDelete: function (DokanInstance : DOKAN_HANDLE; FilePath : LPCWSTR; IsDirectory : BOOL) : BOOL; stdcall = nil; 434 | DokanNotifyUpdate: function (DokanInstance : DOKAN_HANDLE; FilePath : LPCWSTR) : BOOL; stdcall = nil; 435 | DokanNotifyXAttrUpdate: function (DokanInstance : DOKAN_HANDLE; FilePath : LPCWSTR) : BOOL; stdcall = nil; 436 | DokanNotifyRename: function (DokanInstance : DOKAN_HANDLE; OldPath: LPCWSTR; NewPath : LPCWSTR; IsDirectory : BOOL; 437 | IsInSameDirectory: BOOL) : BOOL; stdcall = nil; 438 | DokanNtStatusFromWin32: function (Error: DWORD): NTSTATUS; stdcall = nil; 439 | 440 | function DokanLoad(const LibFileName: string = DokanLibrary): Boolean; 441 | procedure DokanFree(); 442 | 443 | {$else DOKAN_EXPLICIT_LINK} 444 | 445 | procedure DokanInit; stdcall; 446 | procedure DokanShutdown; stdcall; 447 | function DokanMain(Options: PDOKAN_OPTIONS; Operations: PDOKAN_OPERATIONS): Integer; stdcall; 448 | function DokanCreateFileSystem(DokanOptions : PDOKAN_OPTIONS; DokanOperations : PDOKAN_OPERATIONS; var DokanInstance : DOKAN_HANDLE) : Integer; stdcall; 449 | function DokanIsFileSystemRunning(DokanInstance : DOKAN_HANDLE) : BOOL; stdcall; 450 | function DokanWaitForFileSystemClosed(DokanInstance : DOKAN_HANDLE; dwMilliseconds : DWORD) : DWORD; stdcall; 451 | function DokanRegisterWaitForFileSystemClosed(DokanInstance : DOKAN_HANDLE; var WaitHandle : PHANDLE; Callback : TWaitOrTimerCallback; Context : PVOID; dwMilliseconds : DWORD): BOOL; stdcall; 452 | function DokanUnregisterWaitForFileSystemClosed(WaitHandle : HANDLE; WaitForCallbacks : BOOL): BOOL; stdcall; 453 | procedure DokanCloseHandle(DokanInstance : DOKAN_HANDLE); stdcall; 454 | function DokanUnmount(DriveLetter: WCHAR): BOOL; stdcall; 455 | function DokanRemoveMountPoint(MountPoint: LPCWSTR): BOOL; stdcall; 456 | function DokanIsNameInExpression(Expression, Name: LPCWSTR; IgnoreCase: BOOL): BOOL; stdcall; 457 | function DokanVersion(): ULONG; stdcall; 458 | function DokanDriverVersion(): ULONG; stdcall; 459 | function DokanResetTimeout(Timeout: ULONG; DokanFileInfo: PDOKAN_FILE_INFO): BOOL; stdcall; 460 | function DokanOpenRequestorToken(DokanFileInfo: PDOKAN_FILE_INFO): THandle; stdcall; 461 | function DokanGetMountPointList(uncOnly: BOOL; var nbRead: ULONG): PDOKAN_MOUNT_POINT_INFO; stdcall; 462 | procedure DokanReleaseMountPointList(list: PDOKAN_MOUNT_POINT_INFO); stdcall; 463 | procedure DokanMapKernelToUserCreateFileFlags(DesiredAccess: ACCESS_MASK; FileAttributes, CreateOptions, 464 | CreateDisposition: ULONG; outDesiredAccess: PACCESS_MASK; outFileAttributesAndFlags, 465 | outCreationDisposition: PDWORD); stdcall; 466 | function DokanNotifyCreate(DokanInstance : DOKAN_HANDLE; FilePath: LPCWSTR; IsDirectory : BOOL) : BOOL; stdcall; 467 | function DokanNotifyDelete(DokanInstance : DOKAN_HANDLE; FilePath : LPCWSTR; IsDirectory : BOOL) : BOOL; stdcall; 468 | function DokanNotifyUpdate(DokanInstance : DOKAN_HANDLE; FilePath : LPCWSTR) : BOOL; stdcall; 469 | function DokanNotifyXAttrUpdate(DokanInstance : DOKAN_HANDLE; FilePath : LPCWSTR) : BOOL; stdcall; 470 | function DokanNotifyRename(DokanInstance : DOKAN_HANDLE; OldPath: LPCWSTR; NewPath : LPCWSTR; IsDirectory : BOOL; 471 | IsInSameDirectory: BOOL) : BOOL; stdcall; 472 | function DokanNtStatusFromWin32(Error: DWORD): NTSTATUS; stdcall; 473 | 474 | {$endif DOKAN_EXPLICIT_LINK} 475 | 476 | implementation 477 | 478 | {$ifdef DOKAN_EXPLICIT_LINK} 479 | 480 | function DokanLoad(const LibFileName: string = DokanLibrary): Boolean; 481 | function GetProc(const ProcName: string): Pointer; 482 | begin 483 | Result := GetProcAddress(DokanLibHandle, PChar(ProcName)); 484 | if Result = nil then 485 | DokanLoad := False; 486 | end; 487 | begin 488 | if DokanLibHandle <> 0 then begin 489 | Result := True; 490 | Exit; 491 | end; 492 | 493 | DokanLibHandle := LoadLibrary(PChar(LibFileName)); 494 | if DokanLibHandle = 0 then begin 495 | Result := False; 496 | Exit; 497 | end; 498 | 499 | Result := True; 500 | 501 | DokanInit := GetProc('DokanInit'); 502 | DokanShutdown := GetProc('DokanShutdown'); 503 | DokanMain := GetProc('DokanMain'); 504 | DokanCreateFileSystem := GetProc('DokanCreateFileSystem'); 505 | DokanIsFileSystemRunning := GetProc('DokanIsFileSystemRunning'); 506 | DokanWaitForFileSystemClosed := GetProc('DokanWaitForFileSystemClosed'); 507 | DokanRegisterWaitForFileSystemClosed := GetProc('DokanRegisterWaitForFileSystemClosed'); 508 | DokanUnregisterWaitForFileSystemClosed := GetProc('DokanUnregisterWaitForFileSystemClosed'); 509 | DokanCloseHandle := GetProc('DokanCloseHandle'); 510 | DokanUnmount := GetProc('DokanUnmount'); 511 | DokanRemoveMountPoint := GetProc('DokanRemoveMountPoint'); 512 | //DokanRemoveMountPointEx := GetProc('DokanRemoveMountPointEx'); 513 | DokanIsNameInExpression := GetProc('DokanIsNameInExpression'); 514 | DokanVersion := GetProc('DokanVersion'); 515 | DokanDriverVersion := GetProc('DokanDriverVersion'); 516 | DokanResetTimeout := GetProc('DokanResetTimeout'); 517 | DokanOpenRequestorToken := GetProc('DokanOpenRequestorToken'); 518 | DokanGetMountPointList := GetProc('DokanGetMountPointList'); 519 | DokanReleaseMountPointList := GetProc('DokanReleaseMountPointList'); 520 | DokanMapKernelToUserCreateFileFlags := GetProc('DokanMapKernelToUserCreateFileFlags'); 521 | DokanNotifyCreate := GetProc('DokanNotifyCreate'); 522 | DokanNotifyDelete := GetProc('DokanNotifyDelete'); 523 | DokanNotifyUpdate := GetProc('DokanNotifyUpdate'); 524 | DokanNotifyXAttrUpdate := GetProc('DokanNotifyXAttrUpdate'); 525 | DokanNotifyRename := GetProc('DokanNotifyRename'); 526 | DokanNtStatusFromWin32 := GetProc('DokanNtStatusFromWin32'); 527 | 528 | if not Result then 529 | DokanFree(); 530 | end; 531 | 532 | procedure DokanFree(); 533 | begin 534 | if DokanLibHandle = 0 then 535 | Exit; 536 | 537 | DokanInit := nil; 538 | DokanShutdown := nil; 539 | DokanMain := nil; 540 | DokanCreateFileSystem := nil; 541 | DokanIsFileSystemRunning := nil; 542 | DokanWaitForFileSystemClosed := nil; 543 | DokanRegisterWaitForFileSystemClosed := nil; 544 | DokanUnregisterWaitForFileSystemClosed := nil; 545 | DokanCloseHandle := nil; 546 | DokanUnmount := nil; 547 | DokanRemoveMountPoint := nil; 548 | //DokanRemoveMountPointEx := nil; 549 | DokanIsNameInExpression := nil; 550 | DokanVersion := nil; 551 | DokanDriverVersion := nil; 552 | DokanResetTimeout := nil; 553 | DokanOpenRequestorToken := nil; 554 | DokanGetMountPointList := nil; 555 | DokanReleaseMountPointList := nil; 556 | DokanMapKernelToUserCreateFileFlags := nil; 557 | DokanNotifyCreate := nil; 558 | DokanNotifyDelete := nil; 559 | DokanNotifyUpdate := nil; 560 | DokanNotifyXAttrUpdate := nil; 561 | DokanNotifyRename := nil; 562 | DokanNtStatusFromWin32 := nil; 563 | 564 | FreeLibrary(DokanLibHandle); 565 | DokanLibHandle := 0; 566 | end; 567 | 568 | {$else DOKAN_EXPLICIT_LINK} 569 | 570 | procedure DokanInit; external DokanLibrary; 571 | procedure DokanShutdown; external DokanLibrary; 572 | function DokanMain; external DokanLibrary; 573 | function DokanCreateFileSystem; external DokanLibrary; 574 | function DokanIsFileSystemRunning; external DokanLibrary; 575 | function DokanWaitForFileSystemClosed; external DokanLibrary; 576 | function DokanRegisterWaitForFileSystemClosed; external DokanLibrary; 577 | function DokanUnregisterWaitForFileSystemClosed; external DokanLibrary; 578 | procedure DokanCloseHandle; external DokanLibrary; 579 | function DokanUnmount; external DokanLibrary; 580 | function DokanRemoveMountPoint; external DokanLibrary; 581 | //function DokanRemoveMountPointEx; external DokanLibrary; 582 | function DokanIsNameInExpression; external DokanLibrary; 583 | function DokanVersion; external DokanLibrary; 584 | function DokanDriverVersion; external DokanLibrary; 585 | function DokanResetTimeout; external DokanLibrary; 586 | function DokanOpenRequestorToken; external DokanLibrary; 587 | function DokanGetMountPointList; external DokanLibrary; 588 | procedure DokanReleaseMountPointList; external DokanLibrary; 589 | procedure DokanMapKernelToUserCreateFileFlags; external DokanLibrary; 590 | function DokanNotifyCreate; external DokanLibrary; 591 | function DokanNotifyDelete; external DokanLibrary; 592 | function DokanNotifyUpdate; external DokanLibrary; 593 | function DokanNotifyXAttrUpdate; external DokanLibrary; 594 | function DokanNotifyRename; external DokanLibrary; 595 | function DokanNtStatusFromWin32; external DokanLibrary; 596 | 597 | {$endif DOKAN_EXPLICIT_LINK} 598 | 599 | initialization 600 | 601 | finalization 602 | 603 | {$ifdef DOKAN_EXPLICIT_LINK} 604 | DokanFree(); 605 | {$endif DOKAN_EXPLICIT_LINK} 606 | 607 | end. 608 | -------------------------------------------------------------------------------- /Samples/Mirror/Mirror.dpr: -------------------------------------------------------------------------------- 1 | (* 2 | Dokan API wrapper for Delphi based on Release 2.2.0.1000 3 | https://github.com/dokan-dev/dokany/releases/tag/v2.2.0.1000 4 | Copyright (C) 2019 - 2024 Sven Harazim 5 | 6 | Dokan : user-mode file system library for Windows 7 | 8 | Copyright (C) 2015 - 2019 Adrien J. <liryna.stark@gmail.com> and Maxime C. <maxime@islog.com> 9 | Copyright (C) 2020 - 2022 Google, Inc. 10 | Copyright (C) 2007 - 2011 Hiroki Asakawa <info@dokan-dev.net> 11 | 12 | http://dokan-dev.github.io 13 | 14 | This program is free software; you can redistribute it and/or modify it under 15 | the terms of the GNU Lesser General Public License as published by the Free 16 | Software Foundation; either version 3 of the License, or (at your option) any 17 | later version. 18 | 19 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 20 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 21 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 22 | 23 | You should have received a copy of the GNU Lesser General Public License along 24 | with this program. If not, see <http://www.gnu.org/licenses/>. 25 | *) 26 | 27 | program Mirror; 28 | 29 | {$ifdef FPC} 30 | {$mode delphi} 31 | {$endif FPC} 32 | 33 | {$align 8} 34 | {$minenumsize 4} 35 | {$apptype console} 36 | 37 | uses 38 | Windows, SysUtils, Math, 39 | Dokan in '..\..\Dokan.pas', 40 | DokanWin in '..\..\DokanWin.pas'; 41 | 42 | const 43 | EXIT_SUCCESS = 0; 44 | EXIT_FAILURE = 1; 45 | 46 | CSTR_EQUAL = 2; 47 | LOCALE_NAME_SYSTEM_DEFAULT = '!x-sys-default-locale'; 48 | 49 | type 50 | size_t = NativeUInt; 51 | 52 | _TOKEN_USER = record 53 | User : TSIDAndAttributes; 54 | end; 55 | TOKEN_USER = _TOKEN_USER; 56 | PTOKEN_USER = ^_TOKEN_USER; 57 | 58 | STREAM_INFO_LEVELS = (FindStreamInfoStandard = 0); 59 | 60 | FILE_INFO_BY_HANDLE_CLASS = (FileRenameInfo = 3, FileDispositionInfo = 4); 61 | 62 | _FILE_RENAME_INFO = record 63 | ReplaceIfExists: ByteBool; 64 | RootDirectory: THandle; 65 | FileNameLength: DWORD; 66 | FileName: array [0 .. 0] of WCHAR; 67 | end; 68 | FILE_RENAME_INFO = _FILE_RENAME_INFO; 69 | PFILE_RENAME_INFO = ^_FILE_RENAME_INFO; 70 | 71 | _FILE_DISPOSITION_INFO = record 72 | DeleteFile: ByteBool; 73 | end; 74 | FILE_DISPOSITION_INFO = _FILE_DISPOSITION_INFO; 75 | PFILE_DISPOSITION_INFO = ^_FILE_DISPOSITION_INFO; 76 | 77 | function GetFileSizeEx(hFile: THandle; 78 | var lpFileSize: LARGE_INTEGER): BOOL; stdcall; external kernel32; 79 | 80 | function SetFilePointerEx(hFile: THandle; liDistanceToMove: LARGE_INTEGER; 81 | lpNewFilePointer: PLargeInteger; dwMoveMethod: DWORD): BOOL; stdcall; external kernel32; 82 | 83 | function FindFirstStreamW(lpFileName: LPCWSTR; InfoLevel: STREAM_INFO_LEVELS; 84 | lpFindStreamData: LPVOID; dwFlags: DWORD): THandle; stdcall; external kernel32; 85 | 86 | function FindNextStreamW(hFindStream: THandle; 87 | lpFindStreamData: LPVOID): BOOL; stdcall; external kernel32; 88 | 89 | function CompareStringEx(lpLocaleName: LPCWSTR; dwCmpFlags: DWORD; 90 | lpString1: LPCWSTR; cchCount1: Integer; 91 | lpString2: LPCWSTR; cchCount2: Integer; 92 | lpVersionInformation: Pointer; lpReserved: LPVOID; 93 | lParam: LPARAM): Integer; stdcall; external kernel32; 94 | 95 | function SetFileInformationByHandle(hFile: THandle; 96 | FileInformationClass: FILE_INFO_BY_HANDLE_CLASS; lpFileInformation: LPVOID; 97 | dwBufferSize: DWORD): BOOL; stdcall; external kernel32; 98 | 99 | procedure wcsncat_s(dst: PWCHAR; dst_len: size_t; src: PWCHAR; src_len: size_t); 100 | begin 101 | while (dst^ <> #0) and (dst_len > 1) do begin 102 | Inc(dst); 103 | Dec(dst_len); 104 | end; 105 | while (dst_len > 1) and (src^ <> #0) and (src_len > 0) do begin 106 | dst^ := src^; 107 | Inc(dst); 108 | Dec(dst_len); 109 | Inc(src); 110 | Dec(src_len); 111 | end; 112 | if (dst_len > 0) then 113 | dst^ := #0 114 | end; 115 | 116 | function _wcsnicmp(str1, str2: PWCHAR; len: Integer): Integer; 117 | begin 118 | Result := CompareStringEx( 119 | LOCALE_NAME_SYSTEM_DEFAULT, 120 | NORM_IGNORECASE, 121 | str1, Math.Min(lstrlenW(str1), len), 122 | str2, Math.Min(lstrlenW(str2), len), 123 | nil, nil, 0 124 | ) - CSTR_EQUAL; 125 | end; 126 | 127 | function escape_replace(const esc: string): string; 128 | var 129 | i, j, len: Integer; 130 | begin 131 | i := 1; 132 | j := 1; 133 | len:=Length(esc); 134 | SetLength(Result, len); 135 | while (i <= len) do begin 136 | if (esc[i] = '\') then begin 137 | Inc(i); 138 | case (esc[i]) of 139 | 't': Result[j] := #09; 140 | 'n': Result[j] := #10; 141 | else 142 | Result[j] := esc[i]; 143 | end; 144 | end else 145 | Result[j] := esc[i]; 146 | Inc(i); 147 | Inc(j); 148 | end; 149 | if (i <> j) then 150 | SetLength(Result, j - 1); 151 | end; 152 | 153 | {.$define WIN10_ENABLE_LONG_PATH} 154 | {$ifdef WIN10_ENABLE_LONG_PATH} 155 | //dirty but should be enough 156 | const 157 | DOKAN_MAX_PATH = 32768; 158 | {$else} 159 | const 160 | DOKAN_MAX_PATH = MAX_PATH; 161 | {$endif} // DEBUG 162 | 163 | type 164 | WCHAR_PATH = array [0..DOKAN_MAX_PATH-1] of WCHAR; 165 | 166 | var 167 | g_UseStdErr: Boolean; 168 | g_DebugMode: Boolean; 169 | g_CaseSensitive: Boolean; 170 | g_HasSeSecurityPrivilege: Boolean; 171 | g_ImpersonateCallerUser: Boolean; 172 | 173 | procedure DbgPrint(format: string; const args: array of const); overload; 174 | var 175 | outputString: string; 176 | begin 177 | if (g_DebugMode) then begin 178 | outputString := SysUtils.Format(escape_replace(format), args); 179 | if (g_UseStdErr) then begin 180 | Write(ErrOutput, outputString); 181 | Flush(ErrOutput); 182 | end else 183 | OutputDebugString(PChar(outputString)); 184 | end; 185 | end; 186 | 187 | procedure DbgPrint(fmt: string); overload; 188 | begin 189 | DbgPrint(fmt, []); 190 | end; 191 | 192 | var 193 | gRootDirectory: WCHAR_PATH; 194 | gMountPoint: WCHAR_PATH; 195 | gUNCName: WCHAR_PATH; 196 | gVolumeName: WCHAR_PATH; 197 | 198 | procedure GetFilePath(filePath: PWCHAR; numberOfElements: ULONG; 199 | const FileName: LPCWSTR); 200 | var 201 | unclen: size_t; 202 | begin 203 | lstrcpynW(filePath, gRootDirectory, numberOfElements); 204 | unclen := lstrlenW(gUNCName); 205 | if (unclen > 0) and (_wcsnicmp(FileName, gUNCName, unclen) = 0) then begin 206 | if (_wcsnicmp(FileName + unclen, '.', 1) <> 0) then begin 207 | wcsncat_s(filePath, numberOfElements, FileName + unclen, 208 | size_t(lstrlenW(FileName)) - unclen); 209 | end; 210 | end else begin 211 | wcsncat_s(filePath, numberOfElements, FileName, lstrlenW(FileName)); 212 | end; 213 | end; 214 | 215 | procedure PrintUserName(DokanFileInfo: PDOKAN_FILE_INFO); 216 | var 217 | handle: THandle; 218 | buffer: array [0 .. 1023] of UCHAR; 219 | returnLength: DWORD; 220 | accountName: array [0 .. 255] of WCHAR; 221 | domainName: array [0 .. 255] of WCHAR; 222 | accountLength: DWORD; 223 | domainLength: DWORD; 224 | tokenUser_: PTOKEN_USER; 225 | snu: SID_NAME_USE; 226 | begin 227 | accountLength := SizeOf(accountName) div SizeOf(WCHAR); 228 | domainLength := SizeOf(domainName) div SizeOf(WCHAR); 229 | 230 | if (not g_DebugMode) then begin 231 | Exit; 232 | end; 233 | 234 | handle := DokanOpenRequestorToken(DokanFileInfo); 235 | if (handle = INVALID_HANDLE_VALUE) then begin 236 | DbgPrint(' DokanOpenRequestorToken failed\n'); 237 | Exit; 238 | end; 239 | 240 | if (not GetTokenInformation(handle, TokenUser, @buffer, SizeOf(buffer), 241 | returnLength)) then begin 242 | DbgPrint(' GetTokenInformaiton failed: %d\n', [GetLastError()]); 243 | CloseHandle(handle); 244 | Exit; 245 | end; 246 | 247 | CloseHandle(handle); 248 | 249 | tokenUser_ := PTOKEN_USER(@buffer); 250 | if (not LookupAccountSidW(nil, tokenUser_^.User.Sid, accountName, accountLength, 251 | domainName, domainLength, snu)) then begin 252 | DbgPrint(' LookupAccountSid failed: %d\n', [GetLastError()]); 253 | Exit; 254 | end; 255 | 256 | DbgPrint(' AccountName: %s, DomainName: %s\n', [accountName, domainName]); 257 | end; 258 | 259 | function AddSeSecurityNamePrivilege(): Boolean; 260 | var 261 | token: THandle; 262 | err: DWORD; 263 | luid: TLargeInteger; 264 | attr: LUID_AND_ATTRIBUTES; 265 | priv: TOKEN_PRIVILEGES; 266 | oldPriv: TOKEN_PRIVILEGES; 267 | retSize: DWORD; 268 | privAlreadyPresent: Boolean; 269 | i: Integer; 270 | begin 271 | token := 0; 272 | DbgPrint( 273 | '## Attempting to add SE_SECURITY_NAME privilege to process token ##\n'); 274 | if (not LookupPrivilegeValueW(nil, SE_SECURITY_NAME, luid)) then begin 275 | err := GetLastError(); 276 | if (err <> ERROR_SUCCESS) then begin 277 | DbgPrint(' failed: Unable to lookup privilege value. error = %u\n', 278 | [err]); 279 | Result := False; Exit; 280 | end; 281 | end; 282 | 283 | attr.Attributes := SE_PRIVILEGE_ENABLED; 284 | attr.Luid := luid; 285 | 286 | priv.PrivilegeCount := 1; 287 | priv.Privileges[0] := attr; 288 | 289 | if (not OpenProcessToken(GetCurrentProcess(), 290 | TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, token)) then begin 291 | err := GetLastError(); 292 | if (err <> ERROR_SUCCESS) then begin 293 | DbgPrint(' failed: Unable obtain process token. error = %u\n', [err]); 294 | Result := False; Exit; 295 | end; 296 | end; 297 | 298 | AdjustTokenPrivileges(token, False, priv, SizeOf(TOKEN_PRIVILEGES), oldPriv, 299 | retSize); 300 | err := GetLastError(); 301 | if (err <> ERROR_SUCCESS) then begin 302 | DbgPrint(' failed: Unable to adjust token privileges: %u\n', [err]); 303 | CloseHandle(token); 304 | Result := False; Exit; 305 | end; 306 | 307 | privAlreadyPresent := False; 308 | for i := 0 to oldPriv.PrivilegeCount - 1 do begin 309 | if (oldPriv.Privileges[i].Luid = luid) then begin 310 | privAlreadyPresent := True; 311 | Break; 312 | end; 313 | end; 314 | if (privAlreadyPresent) then 315 | DbgPrint(' success: privilege already present\n') 316 | else 317 | DbgPrint(' success: privilege added\n'); 318 | if (token <> 0) then 319 | CloseHandle(token); 320 | Result := True; Exit; 321 | end; 322 | 323 | procedure MirrorCheckFlag(const val: DWORD; const flag: DWORD; const flagname: string); 324 | begin 325 | if (val and flag <> 0) then 326 | DbgPrint('\t%s\n', [flagname]); 327 | end; 328 | 329 | function MirrorCreateFile(FileName: LPCWSTR; SecurityContext: PDOKAN_IO_SECURITY_CONTEXT; 330 | DesiredAccess: ACCESS_MASK; FileAttributes: ULONG; 331 | ShareAccess: ULONG; CreateDisposition: ULONG; 332 | CreateOptions: ULONG; DokanFileInfo: PDOKAN_FILE_INFO): NTSTATUS; stdcall; 333 | var 334 | filePath: WCHAR_PATH; 335 | handle: THandle; 336 | fileAttr: DWORD; 337 | status: NTSTATUS; 338 | creationDisposition: DWORD; 339 | fileAttributesAndFlags: DWORD; 340 | error: DWORD; 341 | securityAttrib: SECURITY_ATTRIBUTES; 342 | genericDesiredAccess: ACCESS_MASK; 343 | // userTokenHandle is for Impersonate Caller User Option 344 | userTokenHandle: THandle; 345 | begin 346 | status := STATUS_SUCCESS; 347 | 348 | securityAttrib.nLength := SizeOf(securityAttrib); 349 | securityAttrib.lpSecurityDescriptor := 350 | SecurityContext^.AccessState.SecurityDescriptor; 351 | securityAttrib.bInheritHandle := False; 352 | 353 | DokanMapKernelToUserCreateFileFlags( 354 | DesiredAccess, FileAttributes, CreateOptions, CreateDisposition, 355 | @genericDesiredAccess, @fileAttributesAndFlags, @creationDisposition); 356 | 357 | GetFilePath(filePath, DOKAN_MAX_PATH, FileName); 358 | 359 | DbgPrint('CreateFile: %s\n', [filePath]); 360 | 361 | PrintUserName(DokanFileInfo); 362 | 363 | (* 364 | if (ShareMode = 0) and (AccessMode and FILE_WRITE_DATA <> 0) then 365 | ShareMode := FILE_SHARE_WRITE 366 | else if (ShareMode = 0) then 367 | ShareMode := FILE_SHARE_READ; 368 | *) 369 | 370 | DbgPrint('\tShareMode = 0x%x\n', [ShareAccess]); 371 | 372 | MirrorCheckFlag(ShareAccess, FILE_SHARE_READ, 'FILE_SHARE_READ'); 373 | MirrorCheckFlag(ShareAccess, FILE_SHARE_WRITE, 'FILE_SHARE_WRITE'); 374 | MirrorCheckFlag(ShareAccess, FILE_SHARE_DELETE, 'FILE_SHARE_DELETE'); 375 | 376 | DbgPrint('\tDesiredAccess = 0x%x\n', [DesiredAccess]); 377 | 378 | MirrorCheckFlag(DesiredAccess, GENERIC_READ, 'GENERIC_READ'); 379 | MirrorCheckFlag(DesiredAccess, GENERIC_WRITE, 'GENERIC_WRITE'); 380 | MirrorCheckFlag(DesiredAccess, GENERIC_EXECUTE, 'GENERIC_EXECUTE'); 381 | 382 | MirrorCheckFlag(DesiredAccess, DELETE, 'DELETE'); 383 | MirrorCheckFlag(DesiredAccess, FILE_READ_DATA, 'FILE_READ_DATA'); 384 | MirrorCheckFlag(DesiredAccess, FILE_READ_ATTRIBUTES, 'FILE_READ_ATTRIBUTES'); 385 | MirrorCheckFlag(DesiredAccess, FILE_READ_EA, 'FILE_READ_EA'); 386 | MirrorCheckFlag(DesiredAccess, READ_CONTROL, 'READ_CONTROL'); 387 | MirrorCheckFlag(DesiredAccess, FILE_WRITE_DATA, 'FILE_WRITE_DATA'); 388 | MirrorCheckFlag(DesiredAccess, FILE_WRITE_ATTRIBUTES, 'FILE_WRITE_ATTRIBUTES'); 389 | MirrorCheckFlag(DesiredAccess, FILE_WRITE_EA, 'FILE_WRITE_EA'); 390 | MirrorCheckFlag(DesiredAccess, FILE_APPEND_DATA, 'FILE_APPEND_DATA'); 391 | MirrorCheckFlag(DesiredAccess, WRITE_DAC, 'WRITE_DAC'); 392 | MirrorCheckFlag(DesiredAccess, WRITE_OWNER, 'WRITE_OWNER'); 393 | MirrorCheckFlag(DesiredAccess, SYNCHRONIZE, 'SYNCHRONIZE'); 394 | MirrorCheckFlag(DesiredAccess, FILE_EXECUTE, 'FILE_EXECUTE'); 395 | MirrorCheckFlag(DesiredAccess, STANDARD_RIGHTS_READ, 'STANDARD_RIGHTS_READ'); 396 | MirrorCheckFlag(DesiredAccess, STANDARD_RIGHTS_WRITE, 'STANDARD_RIGHTS_WRITE'); 397 | MirrorCheckFlag(DesiredAccess, STANDARD_RIGHTS_EXECUTE, 'STANDARD_RIGHTS_EXECUTE'); 398 | 399 | // When filePath is a directory, needs to change the flag so that the file can 400 | // be opened. 401 | fileAttr := GetFileAttributesW(filePath); 402 | 403 | if (fileAttr <> INVALID_FILE_ATTRIBUTES) then begin 404 | if (fileAttr and FILE_ATTRIBUTE_DIRECTORY <> 0) then begin 405 | if (CreateOptions and FILE_NON_DIRECTORY_FILE = 0) then begin 406 | DokanFileInfo^.IsDirectory := true; 407 | // Needed by FindFirstFile to list files in it 408 | // TODO: use ReOpenFile in MirrorFindFiles to set share read temporary 409 | ShareAccess := ShareAccess or FILE_SHARE_READ; 410 | end else begin // FILE_NON_DIRECTORY_FILE - Cannot open a dir as a file 411 | DbgPrint('\tCannot open a dir as a file\n'); 412 | Result := STATUS_FILE_IS_A_DIRECTORY; Exit; 413 | end; 414 | end; 415 | end; 416 | 417 | DbgPrint('\tFlagsAndAttributes = 0x%x\n', [fileAttributesAndFlags]); 418 | 419 | MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_ARCHIVE, 'FILE_ATTRIBUTE_ARCHIVE'); 420 | MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_COMPRESSED, 'FILE_ATTRIBUTE_COMPRESSED'); 421 | MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_DEVICE, 'FILE_ATTRIBUTE_DEVICE'); 422 | MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_DIRECTORY, 'FILE_ATTRIBUTE_DIRECTORY'); 423 | MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_ENCRYPTED, 'FILE_ATTRIBUTE_ENCRYPTED'); 424 | MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_HIDDEN, 'FILE_ATTRIBUTE_HIDDEN'); 425 | MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_INTEGRITY_STREAM, 'FILE_ATTRIBUTE_INTEGRITY_STREAM'); 426 | MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_NORMAL, 'FILE_ATTRIBUTE_NORMAL'); 427 | MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED, 'FILE_ATTRIBUTE_NOT_CONTENT_INDEXED'); 428 | MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_NO_SCRUB_DATA, 'FILE_ATTRIBUTE_NO_SCRUB_DATA'); 429 | MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_OFFLINE, 'FILE_ATTRIBUTE_OFFLINE'); 430 | MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_READONLY, 'FILE_ATTRIBUTE_READONLY'); 431 | MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_REPARSE_POINT, 'FILE_ATTRIBUTE_REPARSE_POINT'); 432 | MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_SPARSE_FILE, 'FILE_ATTRIBUTE_SPARSE_FILE'); 433 | MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_SYSTEM, 'FILE_ATTRIBUTE_SYSTEM'); 434 | MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_TEMPORARY, 'FILE_ATTRIBUTE_TEMPORARY'); 435 | MirrorCheckFlag(fileAttributesAndFlags, FILE_ATTRIBUTE_VIRTUAL, 'FILE_ATTRIBUTE_VIRTUAL'); 436 | MirrorCheckFlag(fileAttributesAndFlags, FILE_FLAG_WRITE_THROUGH, 'FILE_FLAG_WRITE_THROUGH'); 437 | MirrorCheckFlag(fileAttributesAndFlags, FILE_FLAG_OVERLAPPED, 'FILE_FLAG_OVERLAPPED'); 438 | MirrorCheckFlag(fileAttributesAndFlags, FILE_FLAG_NO_BUFFERING, 'FILE_FLAG_NO_BUFFERING'); 439 | MirrorCheckFlag(fileAttributesAndFlags, FILE_FLAG_RANDOM_ACCESS, 'FILE_FLAG_RANDOM_ACCESS'); 440 | MirrorCheckFlag(fileAttributesAndFlags, FILE_FLAG_SEQUENTIAL_SCAN, 'FILE_FLAG_SEQUENTIAL_SCAN'); 441 | MirrorCheckFlag(fileAttributesAndFlags, FILE_FLAG_DELETE_ON_CLOSE, 'FILE_FLAG_DELETE_ON_CLOSE'); 442 | MirrorCheckFlag(fileAttributesAndFlags, FILE_FLAG_BACKUP_SEMANTICS, 'FILE_FLAG_BACKUP_SEMANTICS'); 443 | MirrorCheckFlag(fileAttributesAndFlags, FILE_FLAG_POSIX_SEMANTICS, 'FILE_FLAG_POSIX_SEMANTICS'); 444 | MirrorCheckFlag(fileAttributesAndFlags, FILE_FLAG_OPEN_REPARSE_POINT, 'FILE_FLAG_OPEN_REPARSE_POINT'); 445 | MirrorCheckFlag(fileAttributesAndFlags, FILE_FLAG_OPEN_NO_RECALL, 'FILE_FLAG_OPEN_NO_RECALL'); 446 | MirrorCheckFlag(fileAttributesAndFlags, SECURITY_ANONYMOUS, 'SECURITY_ANONYMOUS'); 447 | MirrorCheckFlag(fileAttributesAndFlags, SECURITY_IDENTIFICATION, 'SECURITY_IDENTIFICATION'); 448 | MirrorCheckFlag(fileAttributesAndFlags, SECURITY_IMPERSONATION, 'SECURITY_IMPERSONATION'); 449 | MirrorCheckFlag(fileAttributesAndFlags, SECURITY_DELEGATION, 'SECURITY_DELEGATION'); 450 | MirrorCheckFlag(fileAttributesAndFlags, SECURITY_CONTEXT_TRACKING, 'SECURITY_CONTEXT_TRACKING'); 451 | MirrorCheckFlag(fileAttributesAndFlags, SECURITY_EFFECTIVE_ONLY, 'SECURITY_EFFECTIVE_ONLY'); 452 | MirrorCheckFlag(fileAttributesAndFlags, SECURITY_SQOS_PRESENT, 'SECURITY_SQOS_PRESENT'); 453 | 454 | if (g_CaseSensitive) then 455 | fileAttributesAndFlags := fileAttributesAndFlags or FILE_FLAG_POSIX_SEMANTICS; 456 | 457 | if (creationDisposition = CREATE_NEW) then begin 458 | DbgPrint('\tCREATE_NEW\n'); 459 | end else if (creationDisposition = OPEN_ALWAYS) then begin 460 | DbgPrint('\tOPEN_ALWAYS\n'); 461 | end else if (creationDisposition = CREATE_ALWAYS) then begin 462 | DbgPrint('\tCREATE_ALWAYS\n'); 463 | end else if (creationDisposition = OPEN_EXISTING) then begin 464 | DbgPrint('\tOPEN_EXISTING\n'); 465 | end else if (creationDisposition = TRUNCATE_EXISTING) then begin 466 | DbgPrint('\tTRUNCATE_EXISTING\n'); 467 | end else begin 468 | DbgPrint('\tUNKNOWN creationDisposition!\n'); 469 | end; 470 | 471 | if (g_ImpersonateCallerUser) then begin 472 | userTokenHandle := DokanOpenRequestorToken(DokanFileInfo); 473 | 474 | if (userTokenHandle = INVALID_HANDLE_VALUE) then begin 475 | DbgPrint(' DokanOpenRequestorToken failed\n'); 476 | // Should we return some error? 477 | end; 478 | end else 479 | userTokenHandle := INVALID_HANDLE_VALUE; //to prevent compiler-warning 480 | 481 | if (DokanFileInfo^.IsDirectory) then begin 482 | // It is a create directory request 483 | 484 | if (creationDisposition = CREATE_NEW) or 485 | (creationDisposition = OPEN_ALWAYS) then begin 486 | 487 | if (g_ImpersonateCallerUser) then begin 488 | // if g_ImpersonateCallerUser option is on, call the ImpersonateLoggedOnUser function. 489 | if (not ImpersonateLoggedOnUser(userTokenHandle)) then begin 490 | // handle the error if failed to impersonate 491 | DbgPrint('\tImpersonateLoggedOnUser failed.\n'); 492 | end; 493 | end; 494 | 495 | //We create folder 496 | if (not CreateDirectoryW(filePath, @securityAttrib)) then begin 497 | error := GetLastError(); 498 | // Fail to create folder for OPEN_ALWAYS is not an error 499 | if (error <> ERROR_ALREADY_EXISTS) or 500 | (creationDisposition = CREATE_NEW) then begin 501 | DbgPrint('\terror code = %d\n\n', [error]); 502 | status := DokanNtStatusFromWin32(error); 503 | end; 504 | end; 505 | 506 | if (g_ImpersonateCallerUser) then begin 507 | // Clean Up operation for impersonate 508 | RevertToSelf(); 509 | end; 510 | end; 511 | 512 | if (status = STATUS_SUCCESS) then begin 513 | 514 | //Check first if we're trying to open a file as a directory. 515 | if (fileAttr <> INVALID_FILE_ATTRIBUTES) and 516 | (fileAttr and FILE_ATTRIBUTE_DIRECTORY = 0) and 517 | (CreateOptions and FILE_DIRECTORY_FILE <> 0) then begin 518 | Result := STATUS_NOT_A_DIRECTORY; Exit; 519 | end; 520 | 521 | if (g_ImpersonateCallerUser) then begin 522 | // if g_ImpersonateCallerUser option is on, call the ImpersonateLoggedOnUser function. 523 | if (not ImpersonateLoggedOnUser(userTokenHandle)) then begin 524 | // handle the error if failed to impersonate 525 | DbgPrint('\tImpersonateLoggedOnUser failed.\n'); 526 | end; 527 | end; 528 | 529 | // FILE_FLAG_BACKUP_SEMANTICS is required for opening directory handles 530 | handle := 531 | CreateFileW(filePath, genericDesiredAccess, ShareAccess, 532 | @securityAttrib, OPEN_EXISTING, 533 | fileAttributesAndFlags or FILE_FLAG_BACKUP_SEMANTICS, 0); 534 | 535 | if (g_ImpersonateCallerUser) then begin 536 | // Clean Up operation for impersonate 537 | RevertToSelf(); 538 | end; 539 | 540 | if (handle = INVALID_HANDLE_VALUE) then begin 541 | error := GetLastError(); 542 | DbgPrint('\terror code = %d\n\n', [error]); 543 | 544 | status := DokanNtStatusFromWin32(error); 545 | end else begin 546 | DokanFileInfo^.Context := 547 | ULONG64(handle); // save the file handle in Context 548 | 549 | // Open succeed but we need to inform the driver 550 | // that the dir open and not created by returning STATUS_OBJECT_NAME_COLLISION 551 | if (creationDisposition = OPEN_ALWAYS) and 552 | (fileAttr <> INVALID_FILE_ATTRIBUTES) then begin 553 | Result := STATUS_OBJECT_NAME_COLLISION; Exit; 554 | end; 555 | end; 556 | end; 557 | end else begin 558 | // It is a create file request 559 | 560 | // Cannot overwrite a hidden or system file if flag not set 561 | if (fileAttr <> INVALID_FILE_ATTRIBUTES) and 562 | (((fileAttributesAndFlags and FILE_ATTRIBUTE_HIDDEN = 0) and 563 | (fileAttr and FILE_ATTRIBUTE_HIDDEN <> 0)) or 564 | ((fileAttributesAndFlags and FILE_ATTRIBUTE_SYSTEM = 0) and 565 | (fileAttr and FILE_ATTRIBUTE_SYSTEM <> 0))) and 566 | ((creationDisposition = TRUNCATE_EXISTING) or 567 | (creationDisposition = CREATE_ALWAYS)) then begin 568 | Result := STATUS_ACCESS_DENIED; Exit; 569 | end; 570 | 571 | // Cannot delete a read only file 572 | if (((fileAttr <> INVALID_FILE_ATTRIBUTES) and 573 | (fileAttr and FILE_ATTRIBUTE_READONLY <> 0) or 574 | (fileAttributesAndFlags and FILE_ATTRIBUTE_READONLY <> 0)) and 575 | (fileAttributesAndFlags and FILE_FLAG_DELETE_ON_CLOSE <> 0)) then begin 576 | Result := STATUS_CANNOT_DELETE; Exit; 577 | end; 578 | 579 | // Truncate should always be used with write access 580 | if (creationDisposition = TRUNCATE_EXISTING) then 581 | genericDesiredAccess := genericDesiredAccess or GENERIC_WRITE; 582 | 583 | if (g_ImpersonateCallerUser) then begin 584 | // if g_ImpersonateCallerUser option is on, call the ImpersonateLoggedOnUser function. 585 | if (not ImpersonateLoggedOnUser(userTokenHandle)) then begin 586 | // handle the error if failed to impersonate 587 | DbgPrint('\tImpersonateLoggedOnUser failed.\n'); 588 | end; 589 | end; 590 | 591 | handle := CreateFileW( 592 | filePath, 593 | genericDesiredAccess, // GENERIC_READ or GENERIC_WRITE or GENERIC_EXECUTE, 594 | ShareAccess, 595 | @securityAttrib, // security attribute 596 | creationDisposition, 597 | fileAttributesAndFlags, // or FILE_FLAG_NO_BUFFERING, 598 | 0); // template file handle 599 | 600 | if (g_ImpersonateCallerUser) then begin 601 | // Clean Up operation for impersonate 602 | RevertToSelf(); 603 | end; 604 | 605 | if (handle = INVALID_HANDLE_VALUE) then begin 606 | error := GetLastError(); 607 | DbgPrint('\terror code = %d\n\n', [error]); 608 | 609 | status := DokanNtStatusFromWin32(error); 610 | end else begin 611 | 612 | //Need to update FileAttributes with previous when Overwrite file 613 | if (fileAttr <> INVALID_FILE_ATTRIBUTES) and 614 | (creationDisposition = TRUNCATE_EXISTING) then begin 615 | SetFileAttributesW(filePath, fileAttributesAndFlags or fileAttr); 616 | end; 617 | 618 | DokanFileInfo^.Context := 619 | ULONG64(handle); // save the file handle in Context 620 | 621 | if (creationDisposition = OPEN_ALWAYS) or 622 | (creationDisposition = CREATE_ALWAYS) then begin 623 | error := GetLastError(); 624 | if (error = ERROR_ALREADY_EXISTS) then begin 625 | DbgPrint('\tOpen an already existing file\n'); 626 | // Open succeed but we need to inform the driver 627 | // that the file open and not created by returning STATUS_OBJECT_NAME_COLLISION 628 | status := STATUS_OBJECT_NAME_COLLISION; 629 | end; 630 | end; 631 | end; 632 | end; 633 | 634 | DbgPrint('\n'); 635 | Result := status; Exit; 636 | end; 637 | 638 | procedure MirrorCloseFile(FileName: LPCWSTR; 639 | DokanFileInfo: PDOKAN_FILE_INFO); stdcall; 640 | var 641 | filePath: WCHAR_PATH; 642 | begin 643 | GetFilePath(filePath, DOKAN_MAX_PATH, FileName); 644 | 645 | if (DokanFileInfo^.Context <> 0) then begin 646 | DbgPrint('CloseFile: %s\n', [filePath]); 647 | DbgPrint('\terror : not cleanuped file\n\n'); 648 | CloseHandle(THandle(DokanFileInfo^.Context)); 649 | DokanFileInfo^.Context := 0; 650 | end else begin 651 | DbgPrint('Close: %s\n\n', [filePath]); 652 | end; 653 | end; 654 | 655 | procedure MirrorCleanup(FileName: LPCWSTR; 656 | DokanFileInfo: PDOKAN_FILE_INFO); stdcall; 657 | var 658 | filePath: WCHAR_PATH; 659 | begin 660 | GetFilePath(filePath, DOKAN_MAX_PATH, FileName); 661 | 662 | if (DokanFileInfo^.Context <> 0) then begin 663 | DbgPrint('Cleanup: %s\n\n', [filePath]); 664 | CloseHandle(THandle(DokanFileInfo^.Context)); 665 | DokanFileInfo^.Context := 0; 666 | end else begin 667 | DbgPrint('Cleanup: %s\n\tinvalid handle\n\n', [filePath]); 668 | end; 669 | 670 | if (DokanFileInfo^.DeleteOnClose) then begin 671 | // Should already be deleted by CloseHandle 672 | // if open with FILE_FLAG_DELETE_ON_CLOSE 673 | DbgPrint('\tDeleteOnClose\n'); 674 | if (DokanFileInfo^.IsDirectory) then begin 675 | DbgPrint(' DeleteDirectory '); 676 | if (not RemoveDirectoryW(filePath)) then begin 677 | DbgPrint('error code = %d\n\n', [GetLastError()]); 678 | end else begin 679 | DbgPrint('success\n\n'); 680 | end; 681 | end else begin 682 | DbgPrint(' DeleteFile '); 683 | if (DeleteFileW(filePath) = False) then begin 684 | DbgPrint(' error code = %d\n\n', [GetLastError()]); 685 | end else begin 686 | DbgPrint('success\n\n'); 687 | end; 688 | end; 689 | end; 690 | end; 691 | 692 | function MirrorReadFile(FileName: LPCWSTR; var Buffer; 693 | BufferLength: DWORD; 694 | ReadLength: PDWORD; 695 | Offset: LONGLONG; 696 | DokanFileInfo: PDOKAN_FILE_INFO): NTSTATUS; stdcall; 697 | var 698 | filePath: WCHAR_PATH; 699 | handle: THandle; 700 | offset_: ULONG; 701 | opened: Boolean; 702 | error: DWORD; 703 | distanceToMove: LARGE_INTEGER; 704 | begin 705 | handle := THandle(DokanFileInfo^.Context); 706 | offset_ := ULONG(Offset); 707 | opened := False; 708 | 709 | GetFilePath(filePath, DOKAN_MAX_PATH, FileName); 710 | 711 | DbgPrint('ReadFile : %s\n', [filePath]); 712 | 713 | if (handle = 0) or (handle = INVALID_HANDLE_VALUE) then begin 714 | DbgPrint('\tinvalid handle, cleanuped?\n'); 715 | handle := CreateFileW(filePath, GENERIC_READ, FILE_SHARE_READ, nil, 716 | OPEN_EXISTING, 0, 0); 717 | if (handle = INVALID_HANDLE_VALUE) then begin 718 | error := GetLastError(); 719 | DbgPrint('\tCreateFile error : %d\n\n', [error]); 720 | Result := DokanNtStatusFromWin32(error); Exit; 721 | end; 722 | opened := True; 723 | end; 724 | 725 | distanceToMove.QuadPart := Offset; 726 | if (not SetFilePointerEx(handle, distanceToMove, nil, FILE_BEGIN)) then begin 727 | error := GetLastError(); 728 | DbgPrint('\tseek error, offset = %d\n\n', [offset_]); 729 | if (opened) then 730 | CloseHandle(handle); 731 | Result := DokanNtStatusFromWin32(error); Exit; 732 | end; 733 | 734 | if (not ReadFile(handle, Buffer, BufferLength, ReadLength^, nil)) then begin 735 | error := GetLastError(); 736 | DbgPrint('\tread error = %u, buffer length = %d, read length = %d\n\n', 737 | [error, BufferLength, ReadLength^]); 738 | if (opened) then 739 | CloseHandle(handle); 740 | Result := DokanNtStatusFromWin32(error); Exit; 741 | 742 | end else begin 743 | DbgPrint('\tByte to read: %d, Byte read %d, offset %d\n\n', [BufferLength, 744 | ReadLength^, offset_]); 745 | end; 746 | 747 | if (opened) then 748 | CloseHandle(handle); 749 | 750 | Result := STATUS_SUCCESS; Exit; 751 | end; 752 | 753 | function MirrorWriteFile(FileName: LPCWSTR; const Buffer; 754 | NumberOfBytesToWrite: DWORD; 755 | NumberOfBytesWritten: PDWORD; 756 | Offset: LONGLONG; 757 | DokanFileInfo: PDOKAN_FILE_INFO): NTSTATUS; stdcall; 758 | var 759 | filePath: WCHAR_PATH; 760 | handle: THandle; 761 | opened: Boolean; 762 | error: DWORD; 763 | fileSize: UINT64; 764 | fileSizeLow: DWORD; 765 | fileSizeHigh: DWORD; 766 | z: LARGE_INTEGER; 767 | bytes: UINT64; 768 | distanceToMove: LARGE_INTEGER; 769 | begin 770 | handle := THandle(DokanFileInfo^.Context); 771 | opened := False; 772 | 773 | GetFilePath(filePath, DOKAN_MAX_PATH, FileName); 774 | 775 | DbgPrint('WriteFile : %s, offset %d, length %d\n', [filePath, Offset, 776 | NumberOfBytesToWrite]); 777 | 778 | // reopen the file 779 | if (handle = 0) or (handle = INVALID_HANDLE_VALUE) then begin 780 | DbgPrint('\tinvalid handle, cleanuped?\n'); 781 | handle := CreateFileW(filePath, GENERIC_WRITE, FILE_SHARE_WRITE, nil, 782 | OPEN_EXISTING, 0, 0); 783 | if (handle = INVALID_HANDLE_VALUE) then begin 784 | error := GetLastError(); 785 | DbgPrint('\tCreateFile error : %d\n\n', [error]); 786 | Result := DokanNtStatusFromWin32(error); Exit; 787 | end; 788 | opened := True; 789 | end; 790 | 791 | fileSizeHigh := 0; 792 | fileSizeLow := GetFileSize(handle, @fileSizeHigh); 793 | if (fileSizeLow = INVALID_FILE_SIZE) then begin 794 | error := GetLastError(); 795 | DbgPrint('\tcan not get a file size error = %d\n', [error]); 796 | if (opened) then 797 | CloseHandle(handle); 798 | Result := DokanNtStatusFromWin32(error); Exit; 799 | end; 800 | 801 | fileSize := (UINT64(fileSizeHigh) shl 32) or fileSizeLow; 802 | 803 | if (DokanFileInfo^.WriteToEndOfFile) then begin 804 | z.QuadPart := 0; 805 | if (not SetFilePointerEx(handle, z, nil, FILE_END)) then begin 806 | error := GetLastError(); 807 | DbgPrint('\tseek error, offset = EOF, error = %d\n', [error]); 808 | if (opened) then 809 | CloseHandle(handle); 810 | Result := DokanNtStatusFromWin32(error); Exit; 811 | end; 812 | end else begin 813 | // Paging IO cannot write after allocate file size. 814 | if (DokanFileInfo^.PagingIo) then begin 815 | if (UINT64(Offset) >= fileSize) then begin 816 | NumberOfBytesWritten^ := 0; 817 | if (opened) then 818 | CloseHandle(handle); 819 | Result := STATUS_SUCCESS; Exit; 820 | end; 821 | 822 | if ((UINT64(Offset) + NumberOfBytesToWrite) > fileSize) then begin 823 | bytes := fileSize - UINT64(Offset); 824 | if (bytes shr 32 <> 0) then begin 825 | NumberOfBytesToWrite := DWORD(bytes and $FFFFFFFF); 826 | end else begin 827 | NumberOfBytesToWrite := DWORD(bytes); 828 | end; 829 | end; 830 | end; 831 | 832 | if (UINT64(Offset) > fileSize) then begin 833 | // In the mirror sample helperZeroFileData is not necessary. NTFS will 834 | // zero a hole. 835 | // But if user's file system is different from NTFS( or other Windows's 836 | // file systems ) then users will have to zero the hole themselves. 837 | end; 838 | 839 | distanceToMove.QuadPart := Offset; 840 | if (not SetFilePointerEx(handle, distanceToMove, nil, FILE_BEGIN)) then begin 841 | error := GetLastError(); 842 | DbgPrint('\tseek error, offset = %d, error = %d\n', [Offset, error]); 843 | if (opened) then 844 | CloseHandle(handle); 845 | Result := DokanNtStatusFromWin32(error); Exit; 846 | end; 847 | end; 848 | 849 | if (not WriteFile(handle, Buffer, NumberOfBytesToWrite, NumberOfBytesWritten^, 850 | nil)) then begin 851 | error := GetLastError(); 852 | DbgPrint('\twrite error = %u, buffer length = %d, write length = %d\n', 853 | [error, NumberOfBytesToWrite, NumberOfBytesWritten^]); 854 | if (opened) then 855 | CloseHandle(handle); 856 | Result := DokanNtStatusFromWin32(error); Exit; 857 | 858 | end else begin 859 | DbgPrint('\twrite %d, offset %d\n\n', [NumberOfBytesWritten^, Offset]); 860 | end; 861 | 862 | // close the file when it is reopened 863 | if (opened) then 864 | CloseHandle(handle); 865 | 866 | Result := STATUS_SUCCESS; Exit; 867 | end; 868 | 869 | function MirrorFlushFileBuffers(FileName: LPCWSTR; DokanFileInfo: PDOKAN_FILE_INFO): NTSTATUS; stdcall; 870 | var 871 | filePath: WCHAR_PATH; 872 | handle: THandle; 873 | error: DWORD; 874 | begin 875 | handle := THandle(DokanFileInfo^.Context); 876 | 877 | GetFilePath(filePath, DOKAN_MAX_PATH, FileName); 878 | 879 | DbgPrint('FlushFileBuffers : %s\n', [filePath]); 880 | 881 | if (handle = 0) or (handle = INVALID_HANDLE_VALUE) then begin 882 | DbgPrint('\tinvalid handle\n\n'); 883 | Result := STATUS_SUCCESS; Exit; 884 | end; 885 | 886 | if (FlushFileBuffers(handle)) then begin 887 | Result := STATUS_SUCCESS; Exit; 888 | end else begin 889 | error := GetLastError(); 890 | DbgPrint('\tflush error code = %d\n', [error]); 891 | Result := DokanNtStatusFromWin32(error); Exit; 892 | end; 893 | end; 894 | 895 | function MirrorGetFileInformation( 896 | FileName: LPCWSTR; HandleFileInformation: PByHandleFileInformation; 897 | DokanFileInfo: PDOKAN_FILE_INFO): NTSTATUS; stdcall; 898 | var 899 | filePath: WCHAR_PATH; 900 | handle: THandle; 901 | error: DWORD; 902 | find: WIN32_FIND_DATAW; 903 | findHandle: THandle; 904 | opened: Boolean; 905 | begin 906 | handle := THandle(DokanFileInfo^.Context); 907 | opened := False; 908 | 909 | GetFilePath(filePath, DOKAN_MAX_PATH, FileName); 910 | 911 | DbgPrint('GetFileInfo : %s\n', [filePath]); 912 | 913 | if (handle = 0) or (handle = INVALID_HANDLE_VALUE) then begin 914 | DbgPrint('\tinvalid handle, cleanuped?\n'); 915 | handle := CreateFileW(filePath, GENERIC_READ, FILE_SHARE_READ, nil, 916 | OPEN_EXISTING, 0, 0); 917 | if (handle = INVALID_HANDLE_VALUE) then begin 918 | error := GetLastError(); 919 | DbgPrint('\tCreateFile error : %d\n\n', [error]); 920 | Result := DokanNtStatusFromWin32(error); Exit; 921 | end; 922 | opened := True; 923 | end; 924 | 925 | if (not GetFileInformationByHandle(handle, HandleFileInformation^)) then begin 926 | DbgPrint('\terror code = %d\n', [GetLastError()]); 927 | 928 | // FileName is a root directory 929 | // in this case, FindFirstFile can't get directory information 930 | if (lstrlenW(FileName) = 1) then begin 931 | DbgPrint(' root dir\n'); 932 | HandleFileInformation^.dwFileAttributes := GetFileAttributesW(filePath); 933 | 934 | end else begin 935 | ZeroMemory(@find, SizeOf(WIN32_FIND_DATAW)); 936 | findHandle := FindFirstFileW(filePath, find); 937 | if (findHandle = INVALID_HANDLE_VALUE) then begin 938 | error := GetLastError(); 939 | DbgPrint('\tFindFirstFile error code = %d\n\n', [error]); 940 | if (opened) then 941 | CloseHandle(handle); 942 | Result := DokanNtStatusFromWin32(error); Exit; 943 | end; 944 | HandleFileInformation^.dwFileAttributes := find.dwFileAttributes; 945 | HandleFileInformation^.ftCreationTime := find.ftCreationTime; 946 | HandleFileInformation^.ftLastAccessTime := find.ftLastAccessTime; 947 | HandleFileInformation^.ftLastWriteTime := find.ftLastWriteTime; 948 | HandleFileInformation^.nFileSizeHigh := find.nFileSizeHigh; 949 | HandleFileInformation^.nFileSizeLow := find.nFileSizeLow; 950 | DbgPrint('\tFindFiles OK, file size = %d\n', [find.nFileSizeLow]); 951 | Windows.FindClose(findHandle); 952 | end; 953 | end else begin 954 | DbgPrint('\tGetFileInformationByHandle success, file size = %d\n', 955 | [HandleFileInformation^.nFileSizeLow]); 956 | end; 957 | 958 | DbgPrint('FILE ATTRIBUTE = %d\n', [HandleFileInformation^.dwFileAttributes]); 959 | 960 | if (opened) then 961 | CloseHandle(handle); 962 | 963 | Result := STATUS_SUCCESS; Exit; 964 | end; 965 | 966 | function MirrorFindFiles(FileName: LPCWSTR; 967 | FillFindData: TDokanFillFindData; // function pointer 968 | DokanFileInfo: PDOKAN_FILE_INFO): NTSTATUS; stdcall; 969 | var 970 | filePath: WCHAR_PATH; 971 | fileLen: size_t; 972 | hFind: THandle; 973 | findData: WIN32_FIND_DATAW; 974 | error: DWORD; 975 | count: Integer; 976 | rootFolder: Boolean; 977 | begin 978 | count := 0; 979 | 980 | GetFilePath(filePath, DOKAN_MAX_PATH, FileName); 981 | 982 | DbgPrint('FindFiles : %s\n', [filePath]); 983 | 984 | fileLen := lstrlenW(filePath); 985 | if (filePath[fileLen - 1] <> '\') then begin 986 | filePath[fileLen] := '\'; 987 | Inc(fileLen); 988 | end; 989 | filePath[fileLen] := '*'; 990 | filePath[fileLen + 1] := #0; 991 | 992 | hFind := FindFirstFileW(filePath, findData); 993 | 994 | if (hFind = INVALID_HANDLE_VALUE) then begin 995 | error := GetLastError(); 996 | DbgPrint('\tinvalid file handle. Error is %u\n\n', [error]); 997 | Result := DokanNtStatusFromWin32(error); Exit; 998 | end; 999 | 1000 | // Root folder does not have . and .. folder - we remove them 1001 | rootFolder := (lstrcmpW(FileName, '\') = 0); 1002 | repeat 1003 | if (not rootFolder) or ((lstrcmpW(findData.cFileName, '.') <> 0) and 1004 | (lstrcmpW(findData.cFileName, '..') <> 0)) then 1005 | FillFindData(@findData, DokanFileInfo); 1006 | Inc(count); 1007 | until( FindNextFileW(hFind, findData) = False); 1008 | 1009 | error := GetLastError(); 1010 | Windows.FindClose(hFind); 1011 | 1012 | if (error <> ERROR_NO_MORE_FILES) then begin 1013 | DbgPrint('\tFindNextFile error. Error is %u\n\n', [error]); 1014 | Result := DokanNtStatusFromWin32(error); Exit; 1015 | end; 1016 | 1017 | DbgPrint('\tFindFiles return %d entries in %s\n\n', [count, filePath]); 1018 | 1019 | Result := STATUS_SUCCESS; Exit; 1020 | end; 1021 | 1022 | function MirrorDeleteFile(FileName: LPCWSTR; DokanFileInfo: PDOKAN_FILE_INFO): NTSTATUS; stdcall; 1023 | var 1024 | filePath: WCHAR_PATH; 1025 | handle: THandle; 1026 | dwAttrib: DWORD; 1027 | fdi: FILE_DISPOSITION_INFO; 1028 | begin 1029 | handle := THandle(DokanFileInfo^.Context); 1030 | 1031 | GetFilePath(filePath, DOKAN_MAX_PATH, FileName); 1032 | DbgPrint('DeleteFile %s - %d\n', [filePath, Byte(DokanFileInfo^.DeleteOnClose)]); 1033 | 1034 | dwAttrib := GetFileAttributesW(filePath); 1035 | 1036 | if (dwAttrib <> INVALID_FILE_ATTRIBUTES) and 1037 | (dwAttrib and FILE_ATTRIBUTE_DIRECTORY <> 0) then begin 1038 | Result := STATUS_ACCESS_DENIED; Exit; 1039 | end; 1040 | 1041 | if (handle <> 0) and (handle <> INVALID_HANDLE_VALUE) then begin 1042 | fdi.DeleteFile := DokanFileInfo^.DeleteOnClose; 1043 | if (not SetFileInformationByHandle(handle, FileDispositionInfo, @fdi, 1044 | sizeof(FILE_DISPOSITION_INFO))) then begin 1045 | Result := DokanNtStatusFromWin32(GetLastError()); Exit; 1046 | end; 1047 | end; 1048 | 1049 | Result := STATUS_SUCCESS; Exit; 1050 | end; 1051 | 1052 | function MirrorDeleteDirectory(FileName: LPCWSTR; DokanFileInfo: PDOKAN_FILE_INFO): NTSTATUS; stdcall; 1053 | var 1054 | filePath: WCHAR_PATH; 1055 | hFind: THandle; 1056 | findData: WIN32_FIND_DATAW; 1057 | fileLen: size_t; 1058 | error: DWORD; 1059 | begin 1060 | ZeroMemory(@filePath[0], SizeOf(filePath)); 1061 | GetFilePath(filePath, DOKAN_MAX_PATH, FileName); 1062 | 1063 | DbgPrint('DeleteDirectory %s - %d\n', [filePath, 1064 | Byte(DokanFileInfo^.DeleteOnClose)]); 1065 | 1066 | if (not DokanFileInfo^.DeleteOnClose) then begin 1067 | //Dokan notify that the file is requested not to be deleted. 1068 | Result := STATUS_SUCCESS; Exit; 1069 | end; 1070 | 1071 | fileLen := lstrlenW(filePath); 1072 | if (filePath[fileLen - 1] <> '\') then begin 1073 | filePath[fileLen] := '\'; 1074 | Inc(fileLen); 1075 | end; 1076 | filePath[fileLen] := '*'; 1077 | filePath[fileLen + 1] := #0; 1078 | 1079 | hFind := FindFirstFileW(filePath, findData); 1080 | 1081 | if (hFind = INVALID_HANDLE_VALUE) then begin 1082 | error := GetLastError(); 1083 | DbgPrint('\tDeleteDirectory error code = %d\n\n', [error]); 1084 | Result := DokanNtStatusFromWin32(error); Exit; 1085 | end; 1086 | 1087 | repeat 1088 | if (lstrcmpW(findData.cFileName, '..') <> 0) and 1089 | (lstrcmpW(findData.cFileName, '.') <> 0) then begin 1090 | Windows.FindClose(hFind); 1091 | DbgPrint('\tDirectory is not empty: %s\n', [findData.cFileName]); 1092 | Result := STATUS_DIRECTORY_NOT_EMPTY; Exit; 1093 | end; 1094 | until (FindNextFileW(hFind, findData) = False); 1095 | 1096 | error := GetLastError(); 1097 | 1098 | Windows.FindClose(hFind); 1099 | 1100 | if (error <> ERROR_NO_MORE_FILES) then begin 1101 | DbgPrint('\tDeleteDirectory error code = %d\n\n', [error]); 1102 | Result := DokanNtStatusFromWin32(error); Exit; 1103 | end; 1104 | 1105 | Result := STATUS_SUCCESS; Exit; 1106 | end; 1107 | 1108 | function MirrorMoveFile(FileName: LPCWSTR; // existing file name 1109 | NewFileName: LPCWSTR; ReplaceIfExisting: BOOL; 1110 | DokanFileInfo: PDOKAN_FILE_INFO): NTSTATUS; stdcall; 1111 | var 1112 | filePath: WCHAR_PATH; 1113 | newFilePath: WCHAR_PATH; 1114 | handle: THandle; 1115 | bufferSize: DWORD; 1116 | result_: Boolean; 1117 | newFilePathLen: size_t; 1118 | renameInfo: PFILE_RENAME_INFO; 1119 | error: DWORD; 1120 | begin 1121 | GetFilePath(filePath, DOKAN_MAX_PATH, FileName); 1122 | GetFilePath(newFilePath, DOKAN_MAX_PATH, NewFileName); 1123 | 1124 | DbgPrint('MoveFile %s -> %s\n\n', [filePath, newFilePath]); 1125 | handle := THandle(DokanFileInfo^.Context); 1126 | if (handle = 0) or (handle = INVALID_HANDLE_VALUE) then begin 1127 | DbgPrint('\tinvalid handle\n\n'); 1128 | Result := STATUS_INVALID_HANDLE; Exit; 1129 | end; 1130 | 1131 | newFilePathLen := lstrlenW(newFilePath); 1132 | 1133 | // the PFILE_RENAME_INFO struct has space for one WCHAR for the name at 1134 | // the end;, so that 1135 | // accounts for the null terminator 1136 | 1137 | bufferSize := DWORD(SizeOf(FILE_RENAME_INFO) + 1138 | newFilePathLen * SizeOf(newFilePath[0])); 1139 | 1140 | GetMem(renameInfo, bufferSize); 1141 | if (renameInfo = nil) then begin 1142 | Result := STATUS_BUFFER_OVERFLOW; Exit; 1143 | end; 1144 | ZeroMemory(renameInfo, bufferSize); 1145 | 1146 | renameInfo^.ReplaceIfExists := ReplaceIfExisting; 1147 | renameInfo^.RootDirectory := 0; // hope it is never needed, shouldn't be 1148 | renameInfo^.FileNameLength := 1149 | DWORD(newFilePathLen) * 1150 | SizeOf(newFilePath[0]); // they want length in bytes 1151 | 1152 | lstrcpynW(renameInfo^.FileName, newFilePath, newFilePathLen + 1); 1153 | 1154 | result_ := SetFileInformationByHandle(handle, FileRenameInfo, renameInfo, 1155 | bufferSize); 1156 | 1157 | FreeMem(renameInfo); 1158 | 1159 | if (result_) then begin 1160 | Result := STATUS_SUCCESS; Exit; 1161 | end else begin 1162 | error := GetLastError(); 1163 | DbgPrint('\tMoveFile error = %u\n', [error]); 1164 | Result := DokanNtStatusFromWin32(error); Exit; 1165 | end; 1166 | end; 1167 | 1168 | function MirrorLockFile(FileName: LPCWSTR; 1169 | ByteOffset: LONGLONG; 1170 | Length: LONGLONG; 1171 | DokanFileInfo: PDOKAN_FILE_INFO): NTSTATUS; stdcall; 1172 | var 1173 | filePath: WCHAR_PATH; 1174 | handle: THandle; 1175 | offset: LARGE_INTEGER; 1176 | length_: LARGE_INTEGER; 1177 | error: DWORD; 1178 | begin 1179 | GetFilePath(filePath, DOKAN_MAX_PATH, FileName); 1180 | 1181 | DbgPrint('LockFile %s\n', [filePath]); 1182 | 1183 | handle := THandle(DokanFileInfo^.Context); 1184 | if (handle = 0) or (handle = INVALID_HANDLE_VALUE) then begin 1185 | DbgPrint('\tinvalid handle\n\n'); 1186 | Result := STATUS_INVALID_HANDLE; Exit; 1187 | end; 1188 | 1189 | length_.QuadPart := Length; 1190 | offset.QuadPart := ByteOffset; 1191 | 1192 | if (not LockFile(handle, offset.LowPart, offset.HighPart, length_.LowPart, 1193 | length_.HighPart)) then begin 1194 | error := GetLastError(); 1195 | DbgPrint('\terror code = %d\n\n', [error]); 1196 | Result := DokanNtStatusFromWin32(error); Exit; 1197 | end; 1198 | 1199 | DbgPrint('\tsuccess\n\n'); 1200 | Result := STATUS_SUCCESS; Exit; 1201 | end; 1202 | 1203 | function MirrorSetEndOfFile( 1204 | FileName: LPCWSTR; ByteOffset: LONGLONG; DokanFileInfo: PDOKAN_FILE_INFO): NTSTATUS; stdcall; 1205 | var 1206 | filePath: WCHAR_PATH; 1207 | handle: THandle; 1208 | offset: LARGE_INTEGER; 1209 | error: DWORD; 1210 | begin 1211 | GetFilePath(filePath, DOKAN_MAX_PATH, FileName); 1212 | 1213 | DbgPrint('SetEndOfFile %s, %d\n', [filePath, ByteOffset]); 1214 | 1215 | handle := THandle(DokanFileInfo^.Context); 1216 | if (handle = 0) or (handle = INVALID_HANDLE_VALUE) then begin 1217 | DbgPrint('\tinvalid handle\n\n'); 1218 | Result := STATUS_INVALID_HANDLE; Exit; 1219 | end; 1220 | 1221 | offset.QuadPart := ByteOffset; 1222 | if (not SetFilePointerEx(handle, offset, nil, FILE_BEGIN)) then begin 1223 | error := GetLastError(); 1224 | DbgPrint('\tSetFilePointer error: %d, offset = %d\n\n', [error, 1225 | ByteOffset]); 1226 | Result := DokanNtStatusFromWin32(error); Exit; 1227 | end; 1228 | 1229 | if (not SetEndOfFile(handle)) then begin 1230 | error := GetLastError(); 1231 | DbgPrint('\tSetEndOfFile error code = %d\n\n', [error]); 1232 | Result := DokanNtStatusFromWin32(error); Exit; 1233 | end; 1234 | 1235 | Result := STATUS_SUCCESS; Exit; 1236 | end; 1237 | 1238 | function MirrorSetAllocationSize( 1239 | FileName: LPCWSTR; AllocSize: LONGLONG; DokanFileInfo: PDOKAN_FILE_INFO): NTSTATUS; stdcall; 1240 | var 1241 | filePath: WCHAR_PATH; 1242 | handle: THandle; 1243 | fileSize: LARGE_INTEGER; 1244 | error: DWORD; 1245 | begin 1246 | GetFilePath(filePath, DOKAN_MAX_PATH, FileName); 1247 | 1248 | DbgPrint('SetAllocationSize %s, %d\n', [filePath, AllocSize]); 1249 | 1250 | handle := THandle(DokanFileInfo^.Context); 1251 | if (handle = 0) or (handle = INVALID_HANDLE_VALUE) then begin 1252 | DbgPrint('\tinvalid handle\n\n'); 1253 | Result := STATUS_INVALID_HANDLE; Exit; 1254 | end; 1255 | 1256 | if (GetFileSizeEx(handle, fileSize)) then begin 1257 | if (AllocSize < fileSize.QuadPart) then begin 1258 | fileSize.QuadPart := AllocSize; 1259 | if (not SetFilePointerEx(handle, fileSize, nil, FILE_BEGIN)) then begin 1260 | error := GetLastError(); 1261 | DbgPrint('\tSetAllocationSize: SetFilePointer eror: %d, ' + 1262 | 'offset = %d\n\n', 1263 | [error, AllocSize]); 1264 | Result := DokanNtStatusFromWin32(error); Exit; 1265 | end; 1266 | if (not SetEndOfFile(handle)) then begin 1267 | error := GetLastError(); 1268 | DbgPrint('\tSetEndOfFile error code = %d\n\n', [error]); 1269 | Result := DokanNtStatusFromWin32(error); Exit; 1270 | end; 1271 | end; 1272 | end else begin 1273 | error := GetLastError(); 1274 | DbgPrint('\terror code = %d\n\n', [error]); 1275 | Result := DokanNtStatusFromWin32(error); Exit; 1276 | end; 1277 | Result := STATUS_SUCCESS; Exit; 1278 | end; 1279 | 1280 | function MirrorSetFileAttributes( 1281 | FileName: LPCWSTR; FileAttributes: DWORD; DokanFileInfo: PDOKAN_FILE_INFO): NTSTATUS; stdcall; 1282 | var 1283 | filePath: WCHAR_PATH; 1284 | error: DWORD; 1285 | begin 1286 | GetFilePath(filePath, DOKAN_MAX_PATH, FileName); 1287 | 1288 | DbgPrint('SetFileAttributes %s 0x%x\n', [filePath, FileAttributes]); 1289 | 1290 | if (FileAttributes <> 0) then begin 1291 | if (not SetFileAttributesW(filePath, FileAttributes)) then begin 1292 | error := GetLastError(); 1293 | DbgPrint('\terror code = %d\n\n', [error]); 1294 | Result := DokanNtStatusFromWin32(error); Exit; 1295 | end; 1296 | end else begin 1297 | // case FileAttributes == 0 : 1298 | // MS-FSCC 2.6 File Attributes : There is no file attribute with the value 0x00000000 1299 | // because a value of 0x00000000 in the FileAttributes field means that the file attributes for this file MUST NOT be changed when setting basic information for the file 1300 | DbgPrint('Set 0 to FileAttributes means MUST NOT be changed. Didn''t call ' + 1301 | 'SetFileAttributes function. \n'); 1302 | end; 1303 | 1304 | DbgPrint('\n'); 1305 | Result := STATUS_SUCCESS; Exit; 1306 | end; 1307 | 1308 | function MirrorSetFileTime(FileName: LPCWSTR; var CreationTime: FILETIME; 1309 | var LastAccessTime: FILETIME; var LastWriteTime: FILETIME; 1310 | DokanFileInfo: PDOKAN_FILE_INFO): NTSTATUS; stdcall; 1311 | var 1312 | filePath: WCHAR_PATH; 1313 | handle: THandle; 1314 | error: DWORD; 1315 | begin 1316 | GetFilePath(filePath, DOKAN_MAX_PATH, FileName); 1317 | 1318 | DbgPrint('SetFileTime %s\n', [filePath]); 1319 | 1320 | handle := THandle(DokanFileInfo^.Context); 1321 | 1322 | if (handle = 0) or (handle = INVALID_HANDLE_VALUE) then begin 1323 | DbgPrint('\tinvalid handle\n\n'); 1324 | Result := STATUS_INVALID_HANDLE; Exit; 1325 | end; 1326 | 1327 | if (not SetFileTime(handle, @CreationTime, @LastAccessTime, @LastWriteTime)) then begin 1328 | error := GetLastError(); 1329 | DbgPrint('\terror code = %d\n\n', [error]); 1330 | Result := DokanNtStatusFromWin32(error); Exit; 1331 | end; 1332 | 1333 | DbgPrint('\n'); 1334 | Result := STATUS_SUCCESS; Exit; 1335 | end; 1336 | 1337 | function MirrorUnlockFile(FileName: LPCWSTR; ByteOffset: LONGLONG; Length: LONGLONG; 1338 | DokanFileInfo: PDOKAN_FILE_INFO): NTSTATUS; stdcall; 1339 | var 1340 | filePath: WCHAR_PATH; 1341 | handle: THandle; 1342 | length_: LARGE_INTEGER; 1343 | offset: LARGE_INTEGER; 1344 | error: DWORD; 1345 | begin 1346 | GetFilePath(filePath, DOKAN_MAX_PATH, FileName); 1347 | 1348 | DbgPrint('UnlockFile %s\n', [filePath]); 1349 | 1350 | handle := THandle(DokanFileInfo^.Context); 1351 | if (handle = 0) or (handle = INVALID_HANDLE_VALUE) then begin 1352 | DbgPrint('\tinvalid handle\n\n'); 1353 | Result := STATUS_INVALID_HANDLE; Exit; 1354 | end; 1355 | 1356 | length_.QuadPart := Length; 1357 | offset.QuadPart := ByteOffset; 1358 | 1359 | if (not UnlockFile(handle, offset.LowPart, offset.HighPart, length_.LowPart, 1360 | length_.HighPart)) then begin 1361 | error := GetLastError(); 1362 | DbgPrint('\terror code = %d\n\n', [error]); 1363 | Result := DokanNtStatusFromWin32(error); Exit; 1364 | end; 1365 | 1366 | DbgPrint('\tsuccess\n\n'); 1367 | Result := STATUS_SUCCESS; Exit; 1368 | end; 1369 | 1370 | function MirrorGetFileSecurity( 1371 | FileName: LPCWSTR; SecurityInformation: PSECURITY_INFORMATION; 1372 | SecurityDescriptor: PSECURITY_DESCRIPTOR; BufferLength: ULONG; 1373 | LengthNeeded: PULONG; DokanFileInfo: PDOKAN_FILE_INFO): NTSTATUS; stdcall; 1374 | var 1375 | filePath: WCHAR_PATH; 1376 | requestingSaclInfo: Boolean; 1377 | handle: THandle; 1378 | DesiredAccess: DWORD; 1379 | error: DWORD; 1380 | securityDescriptorLength: DWORD; 1381 | begin 1382 | GetFilePath(filePath, DOKAN_MAX_PATH, FileName); 1383 | 1384 | DbgPrint('GetFileSecurity %s\n', [filePath]); 1385 | 1386 | MirrorCheckFlag(SecurityInformation^, FILE_SHARE_READ, 'FILE_SHARE_READ'); 1387 | MirrorCheckFlag(SecurityInformation^, OWNER_SECURITY_INFORMATION, 'OWNER_SECURITY_INFORMATION'); 1388 | MirrorCheckFlag(SecurityInformation^, GROUP_SECURITY_INFORMATION, 'GROUP_SECURITY_INFORMATION'); 1389 | MirrorCheckFlag(SecurityInformation^, DACL_SECURITY_INFORMATION, 'DACL_SECURITY_INFORMATION'); 1390 | MirrorCheckFlag(SecurityInformation^, SACL_SECURITY_INFORMATION, 'SACL_SECURITY_INFORMATION'); 1391 | MirrorCheckFlag(SecurityInformation^, LABEL_SECURITY_INFORMATION, 'LABEL_SECURITY_INFORMATION'); 1392 | MirrorCheckFlag(SecurityInformation^, ATTRIBUTE_SECURITY_INFORMATION, 'ATTRIBUTE_SECURITY_INFORMATION'); 1393 | MirrorCheckFlag(SecurityInformation^, SCOPE_SECURITY_INFORMATION, 'SCOPE_SECURITY_INFORMATION'); 1394 | MirrorCheckFlag(SecurityInformation^, PROCESS_TRUST_LABEL_SECURITY_INFORMATION, 'PROCESS_TRUST_LABEL_SECURITY_INFORMATION'); 1395 | MirrorCheckFlag(SecurityInformation^, BACKUP_SECURITY_INFORMATION, 'BACKUP_SECURITY_INFORMATION'); 1396 | MirrorCheckFlag(SecurityInformation^, PROTECTED_DACL_SECURITY_INFORMATION, 'PROTECTED_DACL_SECURITY_INFORMATION'); 1397 | MirrorCheckFlag(SecurityInformation^, PROTECTED_SACL_SECURITY_INFORMATION, 'PROTECTED_SACL_SECURITY_INFORMATION'); 1398 | MirrorCheckFlag(SecurityInformation^, UNPROTECTED_DACL_SECURITY_INFORMATION, 'UNPROTECTED_DACL_SECURITY_INFORMATION'); 1399 | MirrorCheckFlag(SecurityInformation^, UNPROTECTED_SACL_SECURITY_INFORMATION, 'UNPROTECTED_SACL_SECURITY_INFORMATION'); 1400 | 1401 | requestingSaclInfo := ((SecurityInformation^ and SACL_SECURITY_INFORMATION) <> 0) or 1402 | ((SecurityInformation^ and BACKUP_SECURITY_INFORMATION) <> 0); 1403 | 1404 | if (not g_HasSeSecurityPrivilege) then begin 1405 | SecurityInformation^ := SecurityInformation^ and not SACL_SECURITY_INFORMATION; 1406 | SecurityInformation^ := SecurityInformation^ and not BACKUP_SECURITY_INFORMATION; 1407 | end; 1408 | 1409 | DesiredAccess := READ_CONTROL; 1410 | if (requestingSaclInfo and g_HasSeSecurityPrivilege) then begin 1411 | DesiredAccess := DesiredAccess or ACCESS_SYSTEM_SECURITY; 1412 | end; 1413 | DbgPrint(' Opening new handle with READ_CONTROL access\n'); 1414 | handle := CreateFileW( 1415 | filePath, 1416 | DesiredAccess, 1417 | FILE_SHARE_WRITE or FILE_SHARE_READ or FILE_SHARE_DELETE, 1418 | nil, // security attribute 1419 | OPEN_EXISTING, 1420 | FILE_FLAG_BACKUP_SEMANTICS, // or FILE_FLAG_NO_BUFFERING, 1421 | 0); 1422 | 1423 | if (handle = 0) or (handle = INVALID_HANDLE_VALUE) then begin 1424 | DbgPrint('\tinvalid handle\n\n'); 1425 | error := GetLastError(); 1426 | Result := DokanNtStatusFromWin32(error); Exit; 1427 | end; 1428 | 1429 | if (not GetUserObjectSecurity(handle, SecurityInformation^, SecurityDescriptor, 1430 | BufferLength, LengthNeeded^)) then begin 1431 | error := GetLastError(); 1432 | if (error = ERROR_INSUFFICIENT_BUFFER) then begin 1433 | DbgPrint(' GetUserObjectSecurity error: ERROR_INSUFFICIENT_BUFFER\n'); 1434 | CloseHandle(handle); 1435 | Result := STATUS_BUFFER_OVERFLOW; Exit; 1436 | end else begin 1437 | DbgPrint(' GetUserObjectSecurity error: %d\n', [error]); 1438 | CloseHandle(handle); 1439 | Result := DokanNtStatusFromWin32(error); Exit; 1440 | end; 1441 | end; 1442 | 1443 | // Ensure the Security Descriptor Length is set 1444 | securityDescriptorLength := 1445 | GetSecurityDescriptorLength(SecurityDescriptor); 1446 | DbgPrint(' GetUserObjectSecurity return true, *LengthNeeded = ' + 1447 | 'securityDescriptorLength \n'); 1448 | LengthNeeded^ := securityDescriptorLength; 1449 | 1450 | CloseHandle(handle); 1451 | 1452 | Result := STATUS_SUCCESS; Exit; 1453 | end; 1454 | 1455 | function MirrorSetFileSecurity( 1456 | FileName: LPCWSTR; SecurityInformation: PSECURITY_INFORMATION; 1457 | SecurityDescriptor: PSECURITY_DESCRIPTOR; SecurityDescriptorLength: ULONG; 1458 | DokanFileInfo: PDOKAN_FILE_INFO): NTSTATUS; stdcall; 1459 | var 1460 | handle: THandle; 1461 | filePath: WCHAR_PATH; 1462 | error: DWORD; 1463 | begin 1464 | GetFilePath(filePath, DOKAN_MAX_PATH, FileName); 1465 | 1466 | DbgPrint('SetFileSecurity %s\n', [filePath]); 1467 | 1468 | handle := THandle(DokanFileInfo^.Context); 1469 | if (handle = 0) or (handle = INVALID_HANDLE_VALUE) then begin 1470 | DbgPrint('\tinvalid handle\n\n'); 1471 | Result := STATUS_INVALID_HANDLE; Exit; 1472 | end; 1473 | 1474 | if (not SetUserObjectSecurity(handle, SecurityInformation^, SecurityDescriptor)) then begin 1475 | error := GetLastError(); 1476 | DbgPrint(' SetUserObjectSecurity error: %d\n', [error]); 1477 | Result := DokanNtStatusFromWin32(error); Exit; 1478 | end; 1479 | 1480 | Result := STATUS_SUCCESS; Exit; 1481 | end; 1482 | 1483 | function MirrorGetVolumeInformation( 1484 | VolumeNameBuffer: LPWSTR; VolumeNameSize: DWORD; VolumeSerialNumber: PDWORD; 1485 | MaximumComponentLength: PDWORD; FileSystemFlags: PDWORD; 1486 | FileSystemNameBuffer: LPWSTR; FileSystemNameSize: DWORD; 1487 | DokanFileInfo: PDOKAN_FILE_INFO): NTSTATUS; stdcall; 1488 | var 1489 | volumeRoot: array [0 .. 3] of WCHAR; 1490 | fsFlags: DWORD; 1491 | begin 1492 | fsFlags := 0; 1493 | 1494 | lstrcpynW(VolumeNameBuffer, gVolumeName, VolumeNameSize); 1495 | if (VolumeSerialNumber <> nil) then 1496 | VolumeSerialNumber^ := $19831116; 1497 | if (MaximumComponentLength <> nil) then 1498 | MaximumComponentLength^ := 255; 1499 | if (FileSystemFlags <> nil) then 1500 | begin 1501 | FileSystemFlags^ := FILE_SUPPORTS_REMOTE_STORAGE or FILE_UNICODE_ON_DISK or 1502 | FILE_PERSISTENT_ACLS or FILE_NAMED_STREAMS; 1503 | if (g_CaseSensitive) then 1504 | FileSystemFlags^ := FILE_CASE_SENSITIVE_SEARCH or FILE_CASE_PRESERVED_NAMES; 1505 | end; 1506 | 1507 | volumeRoot[0] := gRootDirectory[0]; 1508 | volumeRoot[1] := ':'; 1509 | volumeRoot[2] := '\'; 1510 | volumeRoot[3] := #0; 1511 | 1512 | if (GetVolumeInformationW(@volumeRoot[0], nil, 0, nil, MaximumComponentLength^, 1513 | fsFlags, FileSystemNameBuffer, 1514 | FileSystemNameSize)) then begin 1515 | 1516 | if (FileSystemFlags <> nil) then 1517 | FileSystemFlags^ := FileSystemFlags^ and fsFlags; 1518 | 1519 | if (MaximumComponentLength <> nil) then begin 1520 | DbgPrint('GetVolumeInformation: max component length %u\n', 1521 | [MaximumComponentLength^]); 1522 | end; 1523 | if (FileSystemNameBuffer <> nil) then begin 1524 | DbgPrint('GetVolumeInformation: file system name %s\n', 1525 | [FileSystemNameBuffer^]); 1526 | end; 1527 | if (FileSystemFlags <> nil) then begin 1528 | DbgPrint('GetVolumeInformation: got file system flags 0x%08x,' + 1529 | ' returning 0x%08x\n', 1530 | [fsFlags, FileSystemFlags^]); 1531 | end; 1532 | end else begin 1533 | 1534 | DbgPrint('GetVolumeInformation: unable to query underlying fs,' + 1535 | ' using defaults. Last error = %u\n', 1536 | [GetLastError()]); 1537 | 1538 | // File system name could be anything up to 10 characters. 1539 | // But Windows check few feature availability based on file system name. 1540 | // For this, it is recommended to set NTFS or FAT here. 1541 | lstrcpynW(FileSystemNameBuffer, 'NTFS', FileSystemNameSize); 1542 | end; 1543 | 1544 | Result := STATUS_SUCCESS; Exit; 1545 | end; 1546 | 1547 | function MirrorDokanGetDiskFreeSpace( 1548 | FreeBytesAvailable: PULONGLONG; TotalNumberOfBytes: PULONGLONG; 1549 | TotalNumberOfFreeBytes: PULONGLONG; DokanFileInfo: PDOKAN_FILE_INFO 1550 | ): NTSTATUS; stdcall; 1551 | begin 1552 | FreeBytesAvailable^ := (512 * 1024 * 1024); 1553 | TotalNumberOfBytes^ := 9223372036854775807; 1554 | TotalNumberOfFreeBytes^ := 9223372036854775807; 1555 | 1556 | Result := STATUS_SUCCESS; Exit; 1557 | end; 1558 | 1559 | (** 1560 | * Avoid #include <winternl.h> which as conflict with FILE_INFORMATION_CLASS 1561 | * definition. 1562 | * This only for MirrorFindStreams. Link with ntdll.lib still required. 1563 | * 1564 | * Not needed if you're not using NtQueryInformationFile! 1565 | * 1566 | * BEGIN 1567 | */ 1568 | typedef struct _IO_STATUS_BLOCK { 1569 | union { 1570 | NTSTATUS Status; 1571 | PVOID Pointer; 1572 | } DUMMYUNIONNAME; 1573 | 1574 | ULONG_PTR Information; 1575 | } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; 1576 | 1577 | NTSYSCALLAPI NTSTATUS NTAPI NtQueryInformationFile( 1578 | _In_ HANDLE FileHandle, _Out_ PIO_STATUS_BLOCK IoStatusBlock, 1579 | _Out_writes_bytes_(Length) PVOID FileInformation, _In_ ULONG Length, 1580 | _In_ FILE_INFORMATION_CLASS FileInformationClass); 1581 | /** 1582 | * END 1583 | *) 1584 | 1585 | function MirrorFindStreams(FileName: LPCWSTR; FillFindStreamData: TDokanFillFindStreamData; 1586 | FindStreamContext: PVOID;DokanFileInfo: PDOKAN_FILE_INFO): NTSTATUS; stdcall; 1587 | var 1588 | filePath: WCHAR_PATH; 1589 | hFind: THandle; 1590 | findData: WIN32_FIND_STREAM_DATA; 1591 | error: DWORD; 1592 | bufferFull: Boolean; 1593 | count : Integer; 1594 | begin 1595 | count := 0; 1596 | 1597 | GetFilePath(filePath, DOKAN_MAX_PATH, FileName); 1598 | 1599 | DbgPrint('FindStreams :%s\n', [filePath]); 1600 | 1601 | hFind := FindFirstStreamW(filePath, FindStreamInfoStandard, @findData, 0); 1602 | 1603 | if (hFind = INVALID_HANDLE_VALUE) then begin 1604 | error := GetLastError(); 1605 | DbgPrint('\tinvalid file handle. Error is %u\n\n', [error]); 1606 | Result := DokanNtStatusFromWin32(error); Exit; 1607 | end; 1608 | 1609 | bufferFull := FillFindStreamData(@findData, FindStreamContext); 1610 | if bufferFull then 1611 | begin 1612 | Inc(count); 1613 | while (FindNextStreamW(hFind, @findData) <> False) do begin 1614 | bufferFull := FillFindStreamData(@findData, FindStreamContext); 1615 | if not bufferFull then 1616 | break; 1617 | Inc(count); 1618 | end; 1619 | end; 1620 | 1621 | error := GetLastError(); 1622 | Windows.FindClose(hFind); 1623 | 1624 | if not bufferFull then 1625 | begin 1626 | DbgPrint('\tFindStreams returned %d\n\n entries in %s with ', 1627 | [STATUS_BUFFER_OVERFLOW,count, filePath]); 1628 | // https://msdn.microsoft.com/en-us/library/windows/hardware/ff540364(v=vs.85).aspx 1629 | Result := STATUS_BUFFER_OVERFLOW; 1630 | exit; 1631 | end; 1632 | 1633 | if (error <> ERROR_HANDLE_EOF) then begin 1634 | DbgPrint('\tFindNextStreamW error. Error is %u\n\n', [error]); 1635 | Result := DokanNtStatusFromWin32(error); Exit; 1636 | end; 1637 | 1638 | DbgPrint('\tFindStreams return %d entries in %s\n\n', [count, filePath]); 1639 | 1640 | Result := STATUS_SUCCESS; Exit; 1641 | end; 1642 | 1643 | function MirrorMounted(MountPoint: LPCWSTR; DokanFileInfo: PDOKAN_FILE_INFO): NTSTATUS; stdcall; 1644 | begin 1645 | DbgPrint('Mounted as %s\n',[MountPoint]); 1646 | Result := STATUS_SUCCESS; Exit; 1647 | end; 1648 | 1649 | function MirrorUnmounted(DokanFileInfo: PDOKAN_FILE_INFO): NTSTATUS; stdcall; 1650 | begin 1651 | DbgPrint('Unmounted\n'); 1652 | Result := STATUS_SUCCESS; Exit; 1653 | end; 1654 | 1655 | function CtrlHandler(dwCtrlType: DWORD): BOOL; stdcall; 1656 | begin 1657 | case (dwCtrlType) of 1658 | CTRL_C_EVENT, 1659 | CTRL_BREAK_EVENT, 1660 | CTRL_CLOSE_EVENT, 1661 | CTRL_LOGOFF_EVENT, 1662 | CTRL_SHUTDOWN_EVENT: begin 1663 | SetConsoleCtrlHandler(@CtrlHandler, False); 1664 | DokanRemoveMountPoint(gMountPoint); 1665 | Result := True; 1666 | end; 1667 | else 1668 | Result := False; 1669 | end; 1670 | end; 1671 | 1672 | procedure ShowUsage(); 1673 | begin 1674 | Write(ErrOutput, escape_replace('mirror.exe\n' + 1675 | ' /r RootDirectory (ex. /r c:\\test)\t\t Directory source to mirror.\n' + 1676 | ' /l MountPoint (ex. /l m)\t\t\t Mount point. Can be M:\\ (drive letter) or empty NTFS folder C:\\mount\\dokan .\n' + 1677 | ' /t Single thread\t\t\t\t Only use a single thread to process events.\n\t\t\t\t\t\t This is highly not recommended as can easily create a bottleneck.\n' + 1678 | ' /d (enable debug output)\t\t\t Enable debug output to an attached debugger.\n' + 1679 | ' /s (use stderr for output)\t\t\t Enable debug output to stderr.\n' + 1680 | ' /m (use removable drive)\t\t\t Show device as removable media.\n' + 1681 | ' /w (write-protect drive)\t\t\t Read only filesystem.\n' + 1682 | ' /b (case sensitive drive)\t\t\t Supports case-sensitive file names.\n'+ 1683 | ' /o (use mount manager)\t\t\t Register device to Windows mount manager.\n\t\t\t\t\t\t This enables advanced Windows features like recycle bin and more...\n' + 1684 | ' /c (mount for current session only)\t\t Device only visible for current user session.\n' + 1685 | ' /n (Network drive with UNC name ex. \\myfs\\fs1) Show device as network device with a UNC name.\n' + 1686 | ' /p (Impersonate Caller User)\t\t\t Impersonate Caller User when getting the handle in CreateFile for operations.\n\t\t\t\t\t\t This option requires administrator right to work properly.\n' + 1687 | ' /a Allocation unit size (ex. /a 512)\t\t Allocation Unit Size of the volume. This will behave on the disk file size.\n' + 1688 | ' /k Sector size (ex. /k 512)\t\t\t Sector Size of the volume. This will behave on the disk file size.\n' + 1689 | ' /f User mode Lock\t\t\t\t Enable Lockfile/Unlockfile operations. Otherwise Dokan will take care of it.\n' + 1690 | ' /i Timeout in Milliseconds (ex. /i 30000)\t Timeout until a running operation is aborted and the device is unmounted.\n' + 1691 | ' /z Enabled FCB GCt\t\t\t\t Might speed up on env with filter drivers (Anti-virus) slowing down the system.\n' + 1692 | ' /x Network unmount\t\t\t\t Allows unmounting network drive from file explorer.\n' + 1693 | ' /e Enable Driver Logs\t\t\t\t Forward Driver logs to userland.\n' + 1694 | ' /v Volume name\t\t\t\t Personalize the volume name.\n\n' + 1695 | 'Examples:\n' + 1696 | '\tmirror.exe /r C:\\Users /l M:\t\t\t# Mirror C:\\Users as RootDirectory into a drive of letter M:\\.\n' + 1697 | '\tmirror.exe /r C:\\Users /l C:\\mount\\dokan\t# Mirror C:\\Users as RootDirectory into NTFS folder C:\\mount\\dokan.\n' + 1698 | '\tmirror.exe /r C:\\Users /l M: /n /u \\myfs\\myfs1\t# Mirror C:\\Users as RootDirectory into a network drive M:\\. with UNC \\\\myfs\\myfs1\n\n' + 1699 | 'Unmount the drive with CTRL + C in the console or alternatively via ''dokanctl /u MountPoint''.\n')); 1700 | end; 1701 | 1702 | function wmain(argc: ULONG; argv: array of string): Integer; 1703 | var 1704 | status: Integer; 1705 | command: ULONG; 1706 | dokanOperations: DOKAN_OPERATIONS; 1707 | dokanOptions: DOKAN_OPTIONS; 1708 | begin 1709 | if (argc < 3) then begin 1710 | ShowUsage(); 1711 | Result := EXIT_FAILURE; Exit; 1712 | end; 1713 | 1714 | g_DebugMode := False; 1715 | g_UseStdErr := False; 1716 | g_CaseSensitive := False; 1717 | 1718 | ZeroMemory(@dokanOptions, SizeOf(DOKAN_OPTIONS)); 1719 | dokanOptions.Version := DOKAN_VERSION; 1720 | dokanOptions.SingleThread:= false; 1721 | dokanOptions.Options:= 0; 1722 | //dokanOptions.GlobalContext: ULONG64; //FileSystem can store anything here. 1723 | //dokanOptions.MountPoint: LPCWSTR; //Mount point. It can be a driver letter like "M:\" or a folder path "C:\mount\dokan" on a NTFS partition. 1724 | //dokanOptions.UNCName: LPCWSTR; //UNC Name for the Network Redirector 1725 | dokanOptions.Timeout:= 15;// 1726 | //dokanOptions.AllocationUnitSize: ULONG;//Allocation Unit Size of the volume. This will affect the file size. 1727 | //dokanOptions.SectorSize: ULONG; //Sector Size of the volume. This will affect the file size. 1728 | dokanOptions.VolumeSecurityDescriptorLength := 0; 1729 | //dokanOptions.VolumeSecurityDescriptor : array [0..VOLUME_SECURITY_DESCRIPTOR_MAX_SIZE-1] of AnsiChar;//Optional Volume Security descriptor. See <a href="https://docs.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-initializesecuritydescriptor">InitializeSecurityDescriptor</a> 1730 | 1731 | command := 1; 1732 | while (command < argc) do begin 1733 | case (UpCase(argv[command][2])) of 1734 | 'R': begin 1735 | Inc(command); 1736 | lstrcpynW(gRootDirectory, PWideChar(WideString(argv[command])), DOKAN_MAX_PATH); 1737 | DbgPrint('RootDirectory: %s\n', [gRootDirectory]); 1738 | end; 1739 | 'L': begin 1740 | Inc(command); 1741 | lstrcpynW(gMountPoint, PWideChar(WideString(argv[command])), DOKAN_MAX_PATH); 1742 | dokanOptions.MountPoint := gMountPoint; 1743 | end; 1744 | 'T': begin 1745 | dokanOptions.SingleThread := true; 1746 | end; 1747 | 'D': begin 1748 | g_DebugMode := True; 1749 | end; 1750 | 'S': begin 1751 | g_UseStdErr := True; 1752 | end; 1753 | 'M': begin 1754 | dokanOptions.Options := dokanOptions.Options or DOKAN_OPTION_REMOVABLE; 1755 | end; 1756 | 'W': begin 1757 | dokanOptions.Options := dokanOptions.Options or DOKAN_OPTION_WRITE_PROTECT; 1758 | end; 1759 | 'O': begin 1760 | dokanOptions.Options := dokanOptions.Options or DOKAN_OPTION_MOUNT_MANAGER; 1761 | end; 1762 | 'C': begin 1763 | dokanOptions.Options := dokanOptions.Options or DOKAN_OPTION_CURRENT_SESSION; 1764 | end; 1765 | 'F': begin 1766 | dokanOptions.Options := dokanOptions.Options or DOKAN_OPTION_FILELOCK_USER_MODE; 1767 | end; 1768 | 'X': begin 1769 | dokanOptions.Options := dokanOptions.Options or DOKAN_OPTION_ENABLE_UNMOUNT_NETWORK_DRIVE; 1770 | end; 1771 | 'E': begin 1772 | dokanOptions.Options := dokanOptions.Options or DOKAN_OPTION_DISPATCH_DRIVER_LOGS; 1773 | end; 1774 | 'B': begin 1775 | dokanOptions.Options := dokanOptions.Options or DOKAN_OPTION_CASE_SENSITIVE; 1776 | g_CaseSensitive := true; 1777 | end; 1778 | 'N': begin 1779 | Inc(command); 1780 | dokanOptions.Options := dokanOptions.Options or DOKAN_OPTION_NETWORK; 1781 | lstrcpynW(gUNCName, PWideChar(WideString(argv[command])), DOKAN_MAX_PATH); 1782 | dokanOptions.UNCName := gUNCName; 1783 | DbgPrint('UNC Name: %s\n', [gUNCName]); 1784 | end; 1785 | 'V': begin 1786 | Inc(command); 1787 | lstrcpynW(gVolumeName, PWideChar(WideString(argv[command])), DOKAN_MAX_PATH); 1788 | DbgPrint('Volume Name: %s\n', [gVolumeName]); 1789 | end; 1790 | 'P': begin 1791 | g_ImpersonateCallerUser := True; 1792 | end; 1793 | 'I': begin 1794 | Inc(command); 1795 | dokanOptions.Timeout := StrToInt(argv[command]); 1796 | end; 1797 | 'A': begin 1798 | Inc(command); 1799 | dokanOptions.AllocationUnitSize := StrToInt(argv[command]); 1800 | end; 1801 | 'K': begin 1802 | Inc(command); 1803 | dokanOptions.SectorSize := StrToInt(argv[command]); 1804 | end; 1805 | else 1806 | Writeln(ErrOutput, 'unknown command: ', argv[command]); 1807 | Result := EXIT_FAILURE; Exit; 1808 | end; 1809 | Inc(command); 1810 | end; 1811 | 1812 | if (gUNCName <> '') and 1813 | (dokanOptions.Options and DOKAN_OPTION_NETWORK = 0) then begin 1814 | Writeln( 1815 | ErrOutput, 1816 | ' Warning: UNC provider name should be set on network drive only.'); 1817 | end; 1818 | 1819 | if (dokanOptions.Options and DOKAN_OPTION_NETWORK <> 0) and 1820 | (dokanOptions.Options and DOKAN_OPTION_MOUNT_MANAGER <> 0) then begin 1821 | Writeln(ErrOutput, 'Mount manager cannot be used on network drive.'); 1822 | Result := EXIT_FAILURE; Exit; 1823 | end; 1824 | 1825 | if (dokanOptions.Options and DOKAN_OPTION_MOUNT_MANAGER = 0) and 1826 | (gMountPoint = '') then begin 1827 | Writeln(ErrOutput, 'Mount Point required.'); 1828 | Result := EXIT_FAILURE; Exit; 1829 | end; 1830 | 1831 | if (dokanOptions.Options and DOKAN_OPTION_MOUNT_MANAGER <> 0) and 1832 | (dokanOptions.Options and DOKAN_OPTION_CURRENT_SESSION <> 0) then begin 1833 | Writeln(ErrOutput, 1834 | 'Mount Manager always mount the drive for all user sessions.'); 1835 | Result := EXIT_FAILURE; Exit; 1836 | end; 1837 | 1838 | if (not SetConsoleCtrlHandler(@CtrlHandler, True)) then begin 1839 | Writeln(ErrOutput, 'Control Handler is not set.'); 1840 | end; 1841 | 1842 | // Add security name privilege. Required here to handle GetFileSecurity 1843 | // properly. 1844 | g_HasSeSecurityPrivilege := AddSeSecurityNamePrivilege(); 1845 | if (not g_HasSeSecurityPrivilege) then begin 1846 | Writeln(ErrOutput, 'Failed to add security privilege to process'); 1847 | Writeln(ErrOutput, 1848 | #09'=> GetFileSecurity/SetFileSecurity may not work properly'); 1849 | Writeln(ErrOutput, #09'=> Please restart mirror sample with administrator ' + 1850 | 'rights to fix it'); 1851 | end; 1852 | 1853 | if (g_ImpersonateCallerUser and not g_HasSeSecurityPrivilege) then begin 1854 | Writeln(ErrOutput, 'Impersonate Caller User requires administrator right to ' + 1855 | 'work properly\n'); 1856 | Writeln(ErrOutput, #09'=> Other users may not use the drive properly\n'); 1857 | Writeln(ErrOutput, #09'=> Please restart mirror sample with administrator ' + 1858 | 'rights to fix it\n'); 1859 | end; 1860 | 1861 | if (g_DebugMode) then begin 1862 | dokanOptions.Options := dokanOptions.Options or DOKAN_OPTION_DEBUG; 1863 | end; 1864 | if (g_UseStdErr) then begin 1865 | dokanOptions.Options := dokanOptions.Options or DOKAN_OPTION_STDERR; 1866 | end; 1867 | 1868 | dokanOptions.Options := dokanOptions.Options or DOKAN_OPTION_ALT_STREAM; 1869 | 1870 | ZeroMemory(@dokanOperations, SizeOf(DOKAN_OPERATIONS)); 1871 | dokanOperations.ZwCreateFile := MirrorCreateFile; 1872 | dokanOperations.Cleanup := MirrorCleanup; 1873 | dokanOperations.CloseFile := MirrorCloseFile; 1874 | dokanOperations.ReadFile := MirrorReadFile; 1875 | dokanOperations.WriteFile := MirrorWriteFile; 1876 | dokanOperations.FlushFileBuffers := MirrorFlushFileBuffers; 1877 | dokanOperations.GetFileInformation := MirrorGetFileInformation; 1878 | dokanOperations.FindFiles := MirrorFindFiles; 1879 | dokanOperations.FindFilesWithPattern := nil; 1880 | dokanOperations.SetFileAttributes := MirrorSetFileAttributes; 1881 | dokanOperations.SetFileTime := MirrorSetFileTime; 1882 | dokanOperations.DeleteFile := MirrorDeleteFile; 1883 | dokanOperations.DeleteDirectory := MirrorDeleteDirectory; 1884 | dokanOperations.MoveFile := MirrorMoveFile; 1885 | dokanOperations.SetEndOfFile := MirrorSetEndOfFile; 1886 | dokanOperations.SetAllocationSize := MirrorSetAllocationSize; 1887 | dokanOperations.LockFile := MirrorLockFile; 1888 | dokanOperations.UnlockFile := MirrorUnlockFile; 1889 | dokanOperations.GetFileSecurity := MirrorGetFileSecurity; 1890 | dokanOperations.SetFileSecurity := MirrorSetFileSecurity; 1891 | dokanOperations.GetDiskFreeSpace := MirrorDokanGetDiskFreeSpace; 1892 | dokanOperations.GetVolumeInformation := MirrorGetVolumeInformation; 1893 | dokanOperations.Unmounted := MirrorUnmounted; 1894 | dokanOperations.FindStreams := MirrorFindStreams; 1895 | dokanOperations.Mounted := MirrorMounted; 1896 | 1897 | //if defined Dokan.pas DOKAN_EXPLICIT_LINK then call DokanLoad; 1898 | DokanInit; 1899 | status := DokanMain(@dokanOptions, @dokanOperations); 1900 | DokanShutdown; 1901 | //if defined Dokan.pas DOKAN_EXPLICIT_LINK then call DokanFree 1902 | case (status) of 1903 | DOKAN_SUCCESS: 1904 | Writeln(ErrOutput, 'Success'); 1905 | DOKAN_ERROR: 1906 | Writeln(ErrOutput, 'Error'); 1907 | DOKAN_DRIVE_LETTER_ERROR: 1908 | Writeln(ErrOutput, 'Bad Drive letter'); 1909 | DOKAN_DRIVER_INSTALL_ERROR: 1910 | Writeln(ErrOutput, 'Can''t install driver'); 1911 | DOKAN_START_ERROR: 1912 | Writeln(ErrOutput, 'Driver something wrong'); 1913 | DOKAN_MOUNT_ERROR: 1914 | Writeln(ErrOutput, 'Can''t assign a drive letter'); 1915 | DOKAN_MOUNT_POINT_ERROR: 1916 | Writeln(ErrOutput, 'Mount point error'); 1917 | DOKAN_VERSION_ERROR: 1918 | Writeln(ErrOutput, 'Version error'); 1919 | else 1920 | Writeln(ErrOutput, 'Unknown error: ', status); 1921 | end; 1922 | 1923 | Result := EXIT_SUCCESS; Exit; 1924 | end; 1925 | 1926 | var 1927 | i: Integer; 1928 | argc: ULONG; 1929 | argv: array of string; 1930 | 1931 | begin 1932 | IsMultiThread := True; 1933 | 1934 | lstrcpyW(gRootDirectory, 'C:'); 1935 | lstrcpyW(gMountPoint, 'M:\'); 1936 | lstrcpyW(gUNCName, ''); 1937 | lstrcpyW(gVolumeName, 'DOKAN'); 1938 | 1939 | argc := 1 + ParamCount(); 1940 | SetLength(argv, argc); 1941 | for i := 0 to argc - 1 do 1942 | argv[i] := ParamStr(i); 1943 | 1944 | try 1945 | ExitCode := wmain(argc, argv); 1946 | except 1947 | ExitCode := EXIT_FAILURE; 1948 | end; 1949 | end. 1950 | --------------------------------------------------------------------------------