├── FastStrings.pas ├── FastStringFuncs.pas ├── dipfs.cfg ├── README.md ├── dipfs.dof ├── dipfs.dsk ├── dipfs.drc ├── Dokan.pas ├── HashMap.pas └── dipfs.dpr /FastStrings.pas: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexpmorris/dipfs/HEAD/FastStrings.pas -------------------------------------------------------------------------------- /FastStringFuncs.pas: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexpmorris/dipfs/HEAD/FastStringFuncs.pas -------------------------------------------------------------------------------- /dipfs.cfg: -------------------------------------------------------------------------------- 1 | -$A8 2 | -$B- 3 | -$C+ 4 | -$D+ 5 | -$E- 6 | -$F- 7 | -$G+ 8 | -$H+ 9 | -$I+ 10 | -$J+ 11 | -$K- 12 | -$L+ 13 | -$M- 14 | -$N+ 15 | -$O+ 16 | -$P+ 17 | -$Q- 18 | -$R- 19 | -$S- 20 | -$T- 21 | -$U- 22 | -$V+ 23 | -$W+ 24 | -$X+ 25 | -$YD 26 | -$Z1 27 | -cg 28 | -AWinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; 29 | -H- 30 | -W- 31 | -M 32 | -$M16384,1048576 33 | -K$00400000 34 | -LE"c:\delphi7\Projects\Bpl" 35 | -LN"c:\delphi7\Projects\Bpl" 36 | -U"c:\delphi7\ics\delphi\vc32;c:\delphi7\emagic\unit\d5\gui;c:\delphi7\emagic\source\package;c:\delphi7\iabsocket\API_all_env;c:\delphi7\fastcode;c:\delphi7\synapse\source\lib;c:\delphi7\delphizlib" 37 | -O"c:\delphi7\ics\delphi\vc32;c:\delphi7\emagic\unit\d5\gui;c:\delphi7\emagic\source\package;c:\delphi7\iabsocket\API_all_env;c:\delphi7\fastcode;c:\delphi7\synapse\source\lib;c:\delphi7\delphizlib" 38 | -I"c:\delphi7\ics\delphi\vc32;c:\delphi7\emagic\unit\d5\gui;c:\delphi7\emagic\source\package;c:\delphi7\iabsocket\API_all_env;c:\delphi7\fastcode;c:\delphi7\synapse\source\lib;c:\delphi7\delphizlib" 39 | -R"c:\delphi7\ics\delphi\vc32;c:\delphi7\emagic\unit\d5\gui;c:\delphi7\emagic\source\package;c:\delphi7\iabsocket\API_all_env;c:\delphi7\fastcode;c:\delphi7\synapse\source\lib;c:\delphi7\delphizlib" 40 | -w-UNSAFE_TYPE 41 | -w-UNSAFE_CODE 42 | -w-UNSAFE_CAST 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dipfs 2 | mount the [ipfs distributed file system](https://github.com/ipfs/go-ipfs) to a Windows drive via dokan 3 | 4 | basic read-only interface between ipfs and dokan, works by connecting to the local ipfs REST endpoint at http://127.0.0.1:5001/api/ 5 | to mount the ipfs distributed file system to a Windows drive via dokan (like Fuse for Linux). 6 | 7 | The code is in Delphi 7, and should be relatively easily adaptable to FreePascal or later version of Delphi. 8 | 9 | usage (*make sure ipfs is already running via "ipfs daemon"*): 10 | 11 | ``` 12 | dipfs /l z <-- will link \ipfs\ and \ipns\ to drive z: 13 | dipfs /l z /d <-- same as above, with additional output for debugging 14 | ``` 15 | 16 | from Windows Command Prompt, try the following: 17 | 18 | ``` 19 | z: 20 | cd \ipfs\QmPXME1oRtoT627YKaDPDQ3PwA8tdP9rWuAAweLzqSwAWT 21 | dir 22 | type readme 23 | ``` 24 | 25 | for a node with a subdirectory structure, try the following: 26 | 27 | ``` 28 | cd \ipfs\QmRCJXG7HSmprrYwDrK1GctXHgbV7EYpVcJPQPwevoQuqF 29 | ``` 30 | 31 | Additional Source Code Dependencies: HTTPSend (Synapse) 32 | 33 | See releases for a compiled version. 34 | 35 | Required Driver/DLL Install File: 36 | 37 | * For new versions of Windows (Windows 10, Windows 8.1, Windows Server 2012 R2, Windows 8, Windows Server 2012, Windows 7) use dokany release v0.7.4 (later versions have breaking changes): https://github.com/dokan-dev/dokany/releases/tag/v0.7.4 38 | 39 | * for 32-bit/64-bit Windows 7 or below, you can use the version of dokan under releases: **DokanInstall_0.6.0.exe** 40 | -------------------------------------------------------------------------------- /dipfs.dof: -------------------------------------------------------------------------------- 1 | [FileVersion] 2 | Version=7.0 3 | [Compiler] 4 | A=8 5 | B=0 6 | C=1 7 | D=1 8 | E=0 9 | F=0 10 | G=1 11 | H=1 12 | I=1 13 | J=1 14 | K=0 15 | L=1 16 | M=0 17 | N=1 18 | O=1 19 | P=1 20 | Q=0 21 | R=0 22 | S=0 23 | T=0 24 | U=0 25 | V=1 26 | W=1 27 | X=1 28 | Y=1 29 | Z=1 30 | ShowHints=0 31 | ShowWarnings=0 32 | UnitAliases=WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; 33 | NamespacePrefix= 34 | SymbolDeprecated=1 35 | SymbolLibrary=1 36 | SymbolPlatform=1 37 | UnitLibrary=1 38 | UnitPlatform=1 39 | UnitDeprecated=1 40 | HResultCompat=1 41 | HidingMember=1 42 | HiddenVirtual=1 43 | Garbage=1 44 | BoundsError=1 45 | ZeroNilCompat=1 46 | StringConstTruncated=1 47 | ForLoopVarVarPar=1 48 | TypedConstVarPar=1 49 | AsgToTypedConst=1 50 | CaseLabelRange=1 51 | ForVariable=1 52 | ConstructingAbstract=1 53 | ComparisonFalse=1 54 | ComparisonTrue=1 55 | ComparingSignedUnsigned=1 56 | CombiningSignedUnsigned=1 57 | UnsupportedConstruct=1 58 | FileOpen=1 59 | FileOpenUnitSrc=1 60 | BadGlobalSymbol=1 61 | DuplicateConstructorDestructor=1 62 | InvalidDirective=1 63 | PackageNoLink=1 64 | PackageThreadVar=1 65 | ImplicitImport=1 66 | HPPEMITIgnored=1 67 | NoRetVal=1 68 | UseBeforeDef=1 69 | ForLoopVarUndef=1 70 | UnitNameMismatch=1 71 | NoCFGFileFound=1 72 | MessageDirective=1 73 | ImplicitVariants=1 74 | UnicodeToLocale=1 75 | LocaleToUnicode=1 76 | ImagebaseMultiple=1 77 | SuspiciousTypecast=1 78 | PrivatePropAccessor=1 79 | UnsafeType=0 80 | UnsafeCode=0 81 | UnsafeCast=0 82 | [Linker] 83 | MapFile=0 84 | OutputObjs=0 85 | ConsoleApp=1 86 | DebugInfo=0 87 | RemoteSymbols=0 88 | MinStackSize=16384 89 | MaxStackSize=1048576 90 | ImageBase=4194304 91 | ExeDescription= 92 | [Directories] 93 | OutputDir= 94 | UnitOutputDir= 95 | PackageDLLOutputDir= 96 | PackageDCPOutputDir= 97 | SearchPath=$(DELPHI)\ics\delphi\vc32;$(DELPHI)\emagic\unit\d5\gui;$(DELPHI)\emagic\source\package;c:\delphi7\iabsocket\API_all_env;c:\delphi7\fastcode;c:\delphi7\synapse\source\lib;c:\delphi7\delphizlib 98 | Packages=Vcl40;Vclx40;Vcldb40;vcldbx40;ibevnt40;NMFast40;Vclmid40;Inet40;Inetdb40;WinshoesPkgD4;VclSmp40;Qrpt40;TeeUI40;teedb40;tee40;Dss40;Icsdel40 99 | Conditionals= 100 | DebugSourceDirs= 101 | UsePackages=0 102 | [Parameters] 103 | RunParams= 104 | HostApplication= 105 | Launcher= 106 | UseLauncher=0 107 | DebugCWD= 108 | [Language] 109 | ActiveLang= 110 | ProjectLang= 111 | RootDir=C:\Delphi7\Bin\ 112 | [Version Info] 113 | IncludeVerInfo=0 114 | AutoIncBuild=0 115 | MajorVer=1 116 | MinorVer=0 117 | Release=0 118 | Build=0 119 | Debug=0 120 | PreRelease=0 121 | Special=0 122 | Private=0 123 | DLL=0 124 | Locale=1033 125 | CodePage=1252 126 | [Version Info Keys] 127 | CompanyName= 128 | FileDescription= 129 | FileVersion=1.0.0.0 130 | InternalName= 131 | LegalCopyright= 132 | LegalTrademarks= 133 | OriginalFilename= 134 | ProductName= 135 | ProductVersion=1.0.0.0 136 | Comments= 137 | [Excluded Packages] 138 | c:\delphi7\Projects\Bpl\DCPdelphi6.bpl=DCPcrypt cryptographic component library v2 BETA 3 139 | c:\delphi7\Projects\Bpl\curlpkg.bpl=(untitled) 140 | [HistoryLists\hlUnitAliases] 141 | Count=1 142 | Item0=WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; 143 | [HistoryLists\hlSearchPath] 144 | Count=1 145 | Item0=$(DELPHI)\ics\delphi\vc32;$(DELPHI)\emagic\unit\d5\gui;$(DELPHI)\emagic\source\package;c:\delphi7\iabsocket\API_all_env;c:\delphi7\fastcode;c:\delphi7\synapse\source\lib;c:\delphi7\delphizlib 146 | -------------------------------------------------------------------------------- /dipfs.dsk: -------------------------------------------------------------------------------- 1 | [Closed Files] 2 | File_0=SourceModule,'C:\Delphi7\TYM\rest\rshndlr1.pas',0,1,1027,1,1051,1,0 3 | File_1=SourceModule,'Q:\Workspace\dokan-delphi-master\superobject.pas',0,1,2552,71,2571,0,0 4 | File_2=SourceModule,'C:\Delphi7\TYM\FastMove.pas',0,1,331,1,350,0,0 5 | File_3=SourceModule,'Q:\Workspace\dokan-delphi-master\Mirror.dpr',0,1,789,1,789,0,0 6 | File_4=SourceModule,'C:\Delphi7\iocpengine\examples\tls\MainUnit.pas',0,1,39,25,47,1,0 7 | File_5=SourceModule,'C:\Delphi7\TYM\rest\rshndlr2.PAS',0,1,1,1,1,1,0 8 | File_6=SourceModule,'C:\delphi7\tym\TYMTRDR9.pas',0,1,123,21,142,0,0 9 | File_7=SourceModule,'C:\Delphi7\TYM\TDATypes.pas',0,1,623,39,626,0,0 10 | File_8=SourceModule,'C:\Delphi7\TYM\TDADLLHeaders.pas',0,1,1,1,1,0,0 11 | File_9=SourceModule,'C:\Delphi7\TYM\eshndlr1.pas',0,1,5481,29,5500,1,0 12 | 13 | [Modules] 14 | Module0=Q:\Workspace\dokan-delphi-master\dipfs.dpr 15 | Module1=Q:\Workspace\dokan-delphi-master\Dokan.pas 16 | Count=2 17 | EditWindowCount=1 18 | 19 | [Q:\Workspace\dokan-delphi-master\dipfs.dpr] 20 | ModuleType=SourceModule 21 | FormState=0 22 | FormOnTop=0 23 | 24 | [Q:\Workspace\dokan-delphi-master\Dokan.pas] 25 | ModuleType=SourceModule 26 | FormState=0 27 | FormOnTop=0 28 | 29 | [C:\Delphi7\Projects\ProjectGroup1.bpg] 30 | FormState=0 31 | FormOnTop=0 32 | 33 | [EditWindow0] 34 | ViewCount=2 35 | CurrentView=0 36 | View0=0 37 | View1=1 38 | CodeExplorer=CodeExplorer@EditWindow0 39 | MessageView=MessageView@EditWindow0 40 | Create=1 41 | Visible=1 42 | State=0 43 | Left=246 44 | Top=126 45 | Width=1085 46 | Height=758 47 | MaxLeft=-1 48 | MaxTop=-1 49 | ClientWidth=1077 50 | ClientHeight=731 51 | LeftPanelSize=140 52 | LeftPanelClients=CodeExplorer@EditWindow0 53 | LeftPanelData=000004000000000000000000000000000000000000000000000100000000000000000C000000436F64654578706C6F726572FFFFFFFF 54 | RightPanelSize=0 55 | BottomPanelSize=0 56 | BottomPanelClients=MessageView@EditWindow0 57 | BottomPanelData=00000400010000000B0000004D65737361676556696577350400000000000002550000000000000001000000000000000000000000010000000035040000000000000100000000350400000000000001000000003504000000000000010000000035040000000000000100000000350400000000000001000000003504000000000000FFFFFFFF 58 | 59 | [View0] 60 | Module=Q:\Workspace\dokan-delphi-master\dipfs.dpr 61 | CursorX=54 62 | CursorY=23 63 | TopLine=1 64 | LeftCol=1 65 | 66 | [View1] 67 | Module=Q:\Workspace\dokan-delphi-master\Dokan.pas 68 | CursorX=1 69 | CursorY=1 70 | TopLine=1 71 | LeftCol=1 72 | 73 | [Watches] 74 | Count=0 75 | 76 | [WatchWindow] 77 | WatchColumnWidth=100 78 | WatchShowColumnHeaders=1 79 | Create=1 80 | Visible=0 81 | State=0 82 | Left=335 83 | Top=895 84 | Width=539 85 | Height=187 86 | MaxLeft=-1 87 | MaxTop=-1 88 | ClientWidth=531 89 | ClientHeight=161 90 | TBDockHeight=149 91 | LRDockWidth=531 92 | Dockable=1 93 | 94 | [Breakpoints] 95 | Count=0 96 | 97 | [AddressBreakpoints] 98 | Count=0 99 | 100 | [Main Window] 101 | Create=1 102 | Visible=1 103 | State=0 104 | Left=0 105 | Top=0 106 | Width=1600 107 | Height=105 108 | MaxLeft=-1 109 | MaxTop=-1 110 | ClientWidth=1592 111 | ClientHeight=78 112 | 113 | [ProjectManager] 114 | Create=1 115 | Visible=0 116 | State=0 117 | Left=369 118 | Top=372 119 | Width=537 120 | Height=370 121 | MaxLeft=-1 122 | MaxTop=-1 123 | ClientWidth=529 124 | ClientHeight=344 125 | TBDockHeight=370 126 | LRDockWidth=537 127 | Dockable=1 128 | 129 | [Components] 130 | Left=240 131 | Top=237 132 | Width=183 133 | Height=235 134 | Create=1 135 | Visible=0 136 | State=0 137 | MaxLeft=-1 138 | MaxTop=-1 139 | ClientWidth=175 140 | ClientHeight=209 141 | TBDockHeight=235 142 | LRDockWidth=183 143 | Dockable=1 144 | 145 | [CPUWindow] 146 | Create=1 147 | Visible=0 148 | State=0 149 | Left=78 150 | Top=46 151 | Width=1072 152 | Height=410 153 | MaxLeft=-1 154 | MaxTop=-1 155 | ClientWidth=1064 156 | ClientHeight=383 157 | DumpPane=101 158 | DisassemblyPane=240 159 | RegisterPane=297 160 | FlagPane=82 161 | 162 | [AlignmentPalette] 163 | Create=1 164 | Visible=0 165 | State=0 166 | Left=200 167 | Top=116 168 | Width=156 169 | Height=84 170 | MaxLeft=-1 171 | MaxTop=-1 172 | ClientWidth=150 173 | ClientHeight=60 174 | 175 | [PropertyInspector] 176 | Create=1 177 | Visible=1 178 | State=0 179 | Left=3 180 | Top=127 181 | Width=233 182 | Height=696 183 | MaxLeft=-1 184 | MaxTop=-1 185 | ClientWidth=225 186 | ClientHeight=670 187 | TBDockHeight=774 188 | LRDockWidth=232 189 | Dockable=1 190 | SplitPos=141 191 | ArrangeBy=Name 192 | SelectedItem= 193 | ExpandedItems= 194 | HiddenCategories=Legacy 195 | 196 | [BreakpointWindow] 197 | Create=1 198 | Visible=1 199 | State=0 200 | Left=431 201 | Top=501 202 | Width=737 203 | Height=197 204 | MaxLeft=-1 205 | MaxTop=-1 206 | ClientWidth=729 207 | ClientHeight=171 208 | TBDockHeight=197 209 | LRDockWidth=737 210 | Dockable=1 211 | Column0Width=123 212 | Column1Width=92 213 | Column2Width=246 214 | Column3Width=246 215 | Column4Width=92 216 | Column5Width=92 217 | 218 | [CallStackWindow] 219 | Create=1 220 | Visible=1 221 | State=0 222 | Left=653 223 | Top=519 224 | Width=294 225 | Height=161 226 | MaxLeft=-1 227 | MaxTop=-1 228 | ClientWidth=286 229 | ClientHeight=135 230 | TBDockHeight=161 231 | LRDockWidth=294 232 | Dockable=1 233 | 234 | [ThreadStatusWindow] 235 | Create=1 236 | Visible=0 237 | State=0 238 | Left=193 239 | Top=107 240 | Width=624 241 | Height=152 242 | MaxLeft=-1 243 | MaxTop=-1 244 | ClientWidth=616 245 | ClientHeight=126 246 | TBDockHeight=152 247 | LRDockWidth=624 248 | Dockable=1 249 | Column0Width=178 250 | Column1Width=123 251 | Column2Width=142 252 | Column3Width=308 253 | 254 | [ObjectTree] 255 | Create=1 256 | Visible=0 257 | State=0 258 | Left=0 259 | Top=122 260 | Width=190 261 | Height=418 262 | MaxLeft=-1 263 | MaxTop=-1 264 | ClientWidth=182 265 | ClientHeight=392 266 | TBDockHeight=418 267 | LRDockWidth=190 268 | Dockable=1 269 | 270 | [DebugLogView] 271 | Create=1 272 | Visible=1 273 | State=0 274 | Left=591 275 | Top=454 276 | Width=417 277 | Height=291 278 | MaxLeft=-1 279 | MaxTop=-1 280 | ClientWidth=409 281 | ClientHeight=265 282 | TBDockHeight=291 283 | LRDockWidth=417 284 | Dockable=1 285 | 286 | [LocalVarsWindow] 287 | Create=1 288 | Visible=0 289 | State=0 290 | Left=273 291 | Top=197 292 | Width=421 293 | Height=192 294 | MaxLeft=-1 295 | MaxTop=-1 296 | ClientWidth=413 297 | ClientHeight=166 298 | TBDockHeight=192 299 | LRDockWidth=421 300 | Dockable=1 301 | 302 | [ToDo List] 303 | Create=1 304 | Visible=0 305 | State=0 306 | Left=341 307 | Top=307 308 | Width=470 309 | Height=250 310 | MaxLeft=-1 311 | MaxTop=-1 312 | ClientWidth=462 313 | ClientHeight=224 314 | TBDockHeight=250 315 | LRDockWidth=470 316 | Dockable=0 317 | Column0Width=200 318 | Column1Width=30 319 | Column2Width=100 320 | Column3Width=70 321 | Column4Width=70 322 | SortOrder=4 323 | ShowHints=1 324 | ShowChecked=1 325 | 326 | [FPUWindow] 327 | Create=1 328 | Visible=0 329 | State=0 330 | Left=271 331 | Top=248 332 | Width=457 333 | Height=250 334 | MaxLeft=-1 335 | MaxTop=-1 336 | ClientWidth=449 337 | ClientHeight=223 338 | RegisterPane=147 339 | FlagPane=72 340 | 341 | [ModuleWindow] 342 | Create=1 343 | Visible=0 344 | State=0 345 | Left=197 346 | Top=130 347 | Width=638 348 | Height=355 349 | MaxLeft=-1 350 | MaxTop=-1 351 | ClientWidth=630 352 | ClientHeight=329 353 | TBDockHeight=355 354 | LRDockWidth=638 355 | Dockable=1 356 | Column0Width=125 357 | Column1Width=100 358 | Column2Width=155 359 | EntryPointPane=225 360 | CompUnitPane=104 361 | 362 | [MessageHintFrm] 363 | Create=1 364 | Visible=0 365 | State=0 366 | Left=268 367 | Top=963 368 | Width=383 369 | Height=195 370 | MaxLeft=-1 371 | MaxTop=-1 372 | ClientWidth=375 373 | ClientHeight=169 374 | TBDockHeight=195 375 | LRDockWidth=383 376 | Dockable=1 377 | 378 | [CnAsciiForm] 379 | Create=1 380 | Visible=0 381 | State=0 382 | Left=504 383 | Top=457 384 | Width=220 385 | Height=417 386 | MaxLeft=-1 387 | MaxTop=-1 388 | ClientWidth=212 389 | ClientHeight=391 390 | TBDockHeight=417 391 | LRDockWidth=220 392 | Dockable=1 393 | 394 | [CodeExplorer@EditWindow0] 395 | Create=1 396 | Visible=1 397 | State=0 398 | Left=0 399 | Top=12 400 | Width=140 401 | Height=719 402 | MaxLeft=-1 403 | MaxTop=-1 404 | ClientWidth=140 405 | ClientHeight=719 406 | TBDockHeight=305 407 | LRDockWidth=140 408 | Dockable=1 409 | 410 | [MessageView@EditWindow0] 411 | Create=1 412 | Visible=0 413 | State=0 414 | Left=12 415 | Top=0 416 | Width=1065 417 | Height=85 418 | MaxLeft=-1 419 | MaxTop=-1 420 | ClientWidth=1065 421 | ClientHeight=85 422 | TBDockHeight=85 423 | LRDockWidth=443 424 | Dockable=1 425 | 426 | [DockHosts] 427 | DockHostCount=0 428 | 429 | -------------------------------------------------------------------------------- /dipfs.drc: -------------------------------------------------------------------------------- 1 | /* VER150 2 | Generated by the Borland Delphi Pascal Compiler 3 | because -GD or --drc was supplied to the compiler. 4 | 5 | This file contains compiler-generated resources that 6 | were bound to the executable. 7 | If this file is empty, then no compiler-generated 8 | resources were bound to the produced executable. 9 | */ 10 | 11 | #define RTLConsts_SFOpenErrorEx 65424 12 | #define RTLConsts_SInvalidPropertyValue 65425 13 | #define RTLConsts_SListCapacityError 65426 14 | #define RTLConsts_SListCountError 65427 15 | #define RTLConsts_SListIndexError 65428 16 | #define RTLConsts_SMemoryStreamError 65429 17 | #define RTLConsts_SReadError 65430 18 | #define RTLConsts_SSeekNotImplemented 65431 19 | #define RTLConsts_SSortedListError 65432 20 | #define RTLConsts_SWriteError 65433 21 | #define SysConst_SShortDayNameMon 65440 22 | #define SysConst_SShortDayNameTue 65441 23 | #define SysConst_SShortDayNameWed 65442 24 | #define SysConst_SShortDayNameThu 65443 25 | #define SysConst_SShortDayNameFri 65444 26 | #define SysConst_SShortDayNameSat 65445 27 | #define SysConst_SLongDayNameSun 65446 28 | #define SysConst_SLongDayNameMon 65447 29 | #define SysConst_SLongDayNameTue 65448 30 | #define SysConst_SLongDayNameWed 65449 31 | #define SysConst_SLongDayNameThu 65450 32 | #define SysConst_SLongDayNameFri 65451 33 | #define SysConst_SLongDayNameSat 65452 34 | #define RTLConsts_SAssignError 65453 35 | #define RTLConsts_SDuplicateString 65454 36 | #define RTLConsts_SFCreateErrorEx 65455 37 | #define SysConst_SShortMonthNameOct 65456 38 | #define SysConst_SShortMonthNameNov 65457 39 | #define SysConst_SShortMonthNameDec 65458 40 | #define SysConst_SLongMonthNameJan 65459 41 | #define SysConst_SLongMonthNameFeb 65460 42 | #define SysConst_SLongMonthNameMar 65461 43 | #define SysConst_SLongMonthNameApr 65462 44 | #define SysConst_SLongMonthNameMay 65463 45 | #define SysConst_SLongMonthNameJun 65464 46 | #define SysConst_SLongMonthNameJul 65465 47 | #define SysConst_SLongMonthNameAug 65466 48 | #define SysConst_SLongMonthNameSep 65467 49 | #define SysConst_SLongMonthNameOct 65468 50 | #define SysConst_SLongMonthNameNov 65469 51 | #define SysConst_SLongMonthNameDec 65470 52 | #define SysConst_SShortDayNameSun 65471 53 | #define SysConst_SIntfCastError 65472 54 | #define SysConst_SSafecallException 65473 55 | #define SysConst_SAssertError 65474 56 | #define SysConst_SAbstractError 65475 57 | #define SysConst_SModuleAccessViolation 65476 58 | #define SysConst_SOSError 65477 59 | #define SysConst_SUnkOSError 65478 60 | #define SysConst_SShortMonthNameJan 65479 61 | #define SysConst_SShortMonthNameFeb 65480 62 | #define SysConst_SShortMonthNameMar 65481 63 | #define SysConst_SShortMonthNameApr 65482 64 | #define SysConst_SShortMonthNameMay 65483 65 | #define SysConst_SShortMonthNameJun 65484 66 | #define SysConst_SShortMonthNameJul 65485 67 | #define SysConst_SShortMonthNameAug 65486 68 | #define SysConst_SShortMonthNameSep 65487 69 | #define SysConst_SWriteAccess 65488 70 | #define SysConst_SVarArrayCreate 65489 71 | #define SysConst_SVarArrayBounds 65490 72 | #define SysConst_SVarArrayLocked 65491 73 | #define SysConst_SInvalidVarCast 65492 74 | #define SysConst_SInvalidVarOp 65493 75 | #define SysConst_SInvalidVarOpWithHResultWithPrefix 65494 76 | #define SysConst_SVarTypeCouldNotConvert 65495 77 | #define SysConst_SVarTypeConvertOverflow 65496 78 | #define SysConst_SVarOverflow 65497 79 | #define SysConst_SVarInvalid 65498 80 | #define SysConst_SVarBadType 65499 81 | #define SysConst_SVarNotImplemented 65500 82 | #define SysConst_SVarUnexpected 65501 83 | #define SysConst_SExternalException 65502 84 | #define SysConst_SAssertionFailed 65503 85 | #define SysConst_SZeroDivide 65504 86 | #define SysConst_SOverflow 65505 87 | #define SysConst_SUnderflow 65506 88 | #define SysConst_SInvalidPointer 65507 89 | #define SysConst_SInvalidCast 65508 90 | #define SysConst_SAccessViolationArg3 65509 91 | #define SysConst_SAccessViolationNoArg 65510 92 | #define SysConst_SStackOverflow 65511 93 | #define SysConst_SControlC 65512 94 | #define SysConst_SPrivilege 65513 95 | #define SysConst_SException 65514 96 | #define SysConst_SExceptTitle 65515 97 | #define SysConst_SInvalidFormat 65516 98 | #define SysConst_SArgumentMissing 65517 99 | #define SysConst_SDispatchError 65518 100 | #define SysConst_SReadAccess 65519 101 | #define SysConst_SInvalidInteger 65520 102 | #define SysConst_STimeEncodeError 65521 103 | #define SysConst_SDateEncodeError 65522 104 | #define SysConst_SOutOfMemory 65523 105 | #define SysConst_SInOutError 65524 106 | #define SysConst_SFileNotFound 65525 107 | #define SysConst_SInvalidFilename 65526 108 | #define SysConst_STooManyOpenFiles 65527 109 | #define SysConst_SAccessDenied 65528 110 | #define SysConst_SEndOfFile 65529 111 | #define SysConst_SDiskFull 65530 112 | #define SysConst_SInvalidInput 65531 113 | #define SysConst_SDivByZero 65532 114 | #define SysConst_SRangeError 65533 115 | #define SysConst_SIntOverflow 65534 116 | #define SysConst_SInvalidOp 65535 117 | STRINGTABLE 118 | BEGIN 119 | RTLConsts_SFOpenErrorEx, "Cannot open file \"%s\". %s" 120 | RTLConsts_SInvalidPropertyValue, "Invalid property value" 121 | RTLConsts_SListCapacityError, "List capacity out of bounds (%d)" 122 | RTLConsts_SListCountError, "List count out of bounds (%d)" 123 | RTLConsts_SListIndexError, "List index out of bounds (%d)" 124 | RTLConsts_SMemoryStreamError, "Out of memory while expanding memory stream" 125 | RTLConsts_SReadError, "Stream read error" 126 | RTLConsts_SSeekNotImplemented, "%s.Seek not implemented" 127 | RTLConsts_SSortedListError, "Operation not allowed on sorted list" 128 | RTLConsts_SWriteError, "Stream write error" 129 | SysConst_SShortDayNameMon, "Mon" 130 | SysConst_SShortDayNameTue, "Tue" 131 | SysConst_SShortDayNameWed, "Wed" 132 | SysConst_SShortDayNameThu, "Thu" 133 | SysConst_SShortDayNameFri, "Fri" 134 | SysConst_SShortDayNameSat, "Sat" 135 | SysConst_SLongDayNameSun, "Sunday" 136 | SysConst_SLongDayNameMon, "Monday" 137 | SysConst_SLongDayNameTue, "Tuesday" 138 | SysConst_SLongDayNameWed, "Wednesday" 139 | SysConst_SLongDayNameThu, "Thursday" 140 | SysConst_SLongDayNameFri, "Friday" 141 | SysConst_SLongDayNameSat, "Saturday" 142 | RTLConsts_SAssignError, "Cannot assign a %s to a %s" 143 | RTLConsts_SDuplicateString, "String list does not allow duplicates" 144 | RTLConsts_SFCreateErrorEx, "Cannot create file \"%s\". %s" 145 | SysConst_SShortMonthNameOct, "Oct" 146 | SysConst_SShortMonthNameNov, "Nov" 147 | SysConst_SShortMonthNameDec, "Dec" 148 | SysConst_SLongMonthNameJan, "January" 149 | SysConst_SLongMonthNameFeb, "February" 150 | SysConst_SLongMonthNameMar, "March" 151 | SysConst_SLongMonthNameApr, "April" 152 | SysConst_SLongMonthNameMay, "May" 153 | SysConst_SLongMonthNameJun, "June" 154 | SysConst_SLongMonthNameJul, "July" 155 | SysConst_SLongMonthNameAug, "August" 156 | SysConst_SLongMonthNameSep, "September" 157 | SysConst_SLongMonthNameOct, "October" 158 | SysConst_SLongMonthNameNov, "November" 159 | SysConst_SLongMonthNameDec, "December" 160 | SysConst_SShortDayNameSun, "Sun" 161 | SysConst_SIntfCastError, "Interface not supported" 162 | SysConst_SSafecallException, "Exception in safecall method" 163 | SysConst_SAssertError, "%s (%s, line %d)" 164 | SysConst_SAbstractError, "Abstract Error" 165 | SysConst_SModuleAccessViolation, "Access violation at address %p in module '%s'. %s of address %p" 166 | SysConst_SOSError, "System Error. Code: %d.\r\n%s" 167 | SysConst_SUnkOSError, "A call to an OS function failed" 168 | SysConst_SShortMonthNameJan, "Jan" 169 | SysConst_SShortMonthNameFeb, "Feb" 170 | SysConst_SShortMonthNameMar, "Mar" 171 | SysConst_SShortMonthNameApr, "Apr" 172 | SysConst_SShortMonthNameMay, "May" 173 | SysConst_SShortMonthNameJun, "Jun" 174 | SysConst_SShortMonthNameJul, "Jul" 175 | SysConst_SShortMonthNameAug, "Aug" 176 | SysConst_SShortMonthNameSep, "Sep" 177 | SysConst_SWriteAccess, "Write" 178 | SysConst_SVarArrayCreate, "Error creating variant or safe array" 179 | SysConst_SVarArrayBounds, "Variant or safe array index out of bounds" 180 | SysConst_SVarArrayLocked, "Variant or safe array is locked" 181 | SysConst_SInvalidVarCast, "Invalid variant type conversion" 182 | SysConst_SInvalidVarOp, "Invalid variant operation" 183 | SysConst_SInvalidVarOpWithHResultWithPrefix, "Invalid variant operation (%s%.8x)\n%s" 184 | SysConst_SVarTypeCouldNotConvert, "Could not convert variant of type (%s) into type (%s)" 185 | SysConst_SVarTypeConvertOverflow, "Overflow while converting variant of type (%s) into type (%s)" 186 | SysConst_SVarOverflow, "Variant overflow" 187 | SysConst_SVarInvalid, "Invalid argument" 188 | SysConst_SVarBadType, "Invalid variant type" 189 | SysConst_SVarNotImplemented, "Operation not supported" 190 | SysConst_SVarUnexpected, "Unexpected variant error" 191 | SysConst_SExternalException, "External exception %x" 192 | SysConst_SAssertionFailed, "Assertion failed" 193 | SysConst_SZeroDivide, "Floating point division by zero" 194 | SysConst_SOverflow, "Floating point overflow" 195 | SysConst_SUnderflow, "Floating point underflow" 196 | SysConst_SInvalidPointer, "Invalid pointer operation" 197 | SysConst_SInvalidCast, "Invalid class typecast" 198 | SysConst_SAccessViolationArg3, "Access violation at address %p. %s of address %p" 199 | SysConst_SAccessViolationNoArg, "Access violation" 200 | SysConst_SStackOverflow, "Stack overflow" 201 | SysConst_SControlC, "Control-C hit" 202 | SysConst_SPrivilege, "Privileged instruction" 203 | SysConst_SException, "Exception %s in module %s at %p.\r\n%s%s\r\n" 204 | SysConst_SExceptTitle, "Application Error" 205 | SysConst_SInvalidFormat, "Format '%s' invalid or incompatible with argument" 206 | SysConst_SArgumentMissing, "No argument for format '%s'" 207 | SysConst_SDispatchError, "Variant method calls not supported" 208 | SysConst_SReadAccess, "Read" 209 | SysConst_SInvalidInteger, "'%s' is not a valid integer value" 210 | SysConst_STimeEncodeError, "Invalid argument to time encode" 211 | SysConst_SDateEncodeError, "Invalid argument to date encode" 212 | SysConst_SOutOfMemory, "Out of memory" 213 | SysConst_SInOutError, "I/O error %d" 214 | SysConst_SFileNotFound, "File not found" 215 | SysConst_SInvalidFilename, "Invalid filename" 216 | SysConst_STooManyOpenFiles, "Too many open files" 217 | SysConst_SAccessDenied, "File access denied" 218 | SysConst_SEndOfFile, "Read beyond end of file" 219 | SysConst_SDiskFull, "Disk full" 220 | SysConst_SInvalidInput, "Invalid numeric input" 221 | SysConst_SDivByZero, "Division by zero" 222 | SysConst_SRangeError, "Range check error" 223 | SysConst_SIntOverflow, "Integer overflow" 224 | SysConst_SInvalidOp, "Invalid floating point operation" 225 | END 226 | 227 | -------------------------------------------------------------------------------- /Dokan.pas: -------------------------------------------------------------------------------- 1 | (******************************************************************************* 2 | * 3 | * Dokan : user-mode file system library for Windows 4 | * 5 | * Copyright (C) 2008 Hiroki Asakawa info@dokan-dev.net 6 | * 7 | * http://dokan-dev.net/en 8 | * 9 | * Delphi header translation by Vincent Forman (vincent.forman@gmail.com) 10 | * 11 | * This program is free software; you can redistribute it and/or modify it under 12 | * the terms of the GNU Lesser General Public License as published by the Free 13 | * Software Foundation; either version 3 of the License, or (at your option) any 14 | * later version. 15 | 16 | * This program is distributed in the hope that it will be useful, but WITHOUT 17 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 18 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 19 | * details. 20 | * 21 | * You should have received a copy of the GNU Lesser General Public License 22 | * along with this program. If not, see . 23 | * 24 | *******************************************************************************) 25 | 26 | unit Dokan; 27 | 28 | interface 29 | 30 | uses 31 | Windows; 32 | 33 | const 34 | DokanLibrary = 'dokan.dll'; 35 | 36 | DOKAN_VERSION = 600{730}; 37 | 38 | DOKAN_OPTION_DEBUG = 1; // ouput debug message 39 | DOKAN_OPTION_STDERR = 2; // ouput debug message to stderr 40 | DOKAN_OPTION_ALT_STREAM = 4; // use alternate stream 41 | DOKAN_OPTION_KEEP_ALIVE = 8; // use auto unmount 42 | DOKAN_OPTION_NETWORK =16; // use network drive, you need to install Dokan network provider. 43 | DOKAN_OPTION_REMOVABLE =32; // use removable drive 44 | 45 | type 46 | _DOKAN_OPTIONS = packed record 47 | Version : Word; 48 | ThreadCount: Word; // Number of threads to be used 49 | Options: UInt; // Ouput debug message 50 | GlobalContext: UInt64; // User-mode filesystem can use this variable 51 | MountPoint: LPCWSTR; // Drive letter to be mounted 52 | end; 53 | PDOKAN_OPTIONS = ^_DOKAN_OPTIONS; 54 | DOKAN_OPTIONS = _DOKAN_OPTIONS; 55 | 56 | TDokanOptions = _DOKAN_OPTIONS; 57 | PDokanOptions = PDOKAN_OPTIONS; 58 | 59 | _DOKAN_FILE_INFO = packed record 60 | Context: UInt64; // User-mode filesystem can use this variable 61 | DokanContext: UInt64; // Reserved. Don't touch this! 62 | DokanOptions : PDOKAN_OPTIONS; // A pointer to DOKAN_OPTIONS which was passed to DokanMain. 63 | ProcessId: ULONG; // process id for the thread that originally requested a given I/O operation 64 | IsDirectory: Boolean; // requesting a directory file 65 | DeleteOnClose: Boolean; // Delete on when "cleanup" is called 66 | PagingIo: Boolean; // Read or write is paging IO. 67 | SynchronousIo: Boolean; // Read or write is synchronous IO. 68 | Nocache: Boolean; 69 | WriteToEndOfFile: Boolean; // If true, write to the current end of file instead of Offset parameter. 70 | end; 71 | PDOKAN_FILE_INFO = ^_DOKAN_FILE_INFO; 72 | DOKAN_FILE_INFO = _DOKAN_FILE_INFO; 73 | 74 | TDokanFileInfo = _DOKAN_FILE_INFO; 75 | PDokanFileInfo = PDOKAN_FILE_INFO; 76 | 77 | type 78 | // When an error occurs, the following user-mode callbacks should return 79 | // negative values. Usually, you should return GetLastError() * -1. 80 | 81 | // FillFileData is a client-side callback to enumerate a file list when 82 | // FindFiles is called. It is supposed to return 1 if buffer is full, 0 83 | // otherwise (currently it never returns 1) 84 | TDokanFillFindData = function(var FindData: WIN32_FIND_DATAW; 85 | var DokanFileInfo: DOKAN_FILE_INFO): Integer; stdcall; 86 | 87 | // CreateFile may be called when FileName is the name of an existing directory. 88 | // In that case, CreateFile should return 0 if that directory can be opened and 89 | // you should set DokanFileInfo.IsDirectory to True. 90 | // When CreationDisposition is CREATE_ALWAYS or OPEN_ALWAYS and a file with the 91 | // same name already exists, you should return ERROR_ALREADY_EXISTS (183) (not 92 | // the negative value) 93 | TDokanCreateFile = function(FileName: LPCWSTR; 94 | DesiredAccess, ShareMode, CreationDisposition, FlagsAndAttributes: DWORD; 95 | var DokanFileInfo: DOKAN_FILE_INFO): Integer; stdcall; 96 | 97 | TDokanOpenDirectory = function(FileName: LPCWSTR; 98 | var DokanFileInfo: DOKAN_FILE_INFO): Integer; stdcall; 99 | 100 | TDokanCreateDirectory = function(FileName: LPCWSTR; 101 | var DokanFileInfo: DOKAN_FILE_INFO): Integer; stdcall; 102 | 103 | // When FileInfo.DeleteOnClose is set to True, you must delete the file during 104 | // Cleanup. 105 | TDokanCleanup = function(FileName: LPCWSTR; 106 | var DokanFileInfo: DOKAN_FILE_INFO): Integer; stdcall; 107 | 108 | TDokanCloseFile = function(FileName: LPCWSTR; 109 | var DokanFileInfo: DOKAN_FILE_INFO): Integer; stdcall; 110 | 111 | TDokanReadFile = function(FileName: LPCWSTR; 112 | var Buffer; 113 | NumberOfBytesToRead: DWORD; 114 | var NumberOfBytesRead: DWORD; 115 | Offset: LONGLONG; 116 | var DokanFileInfo: DOKAN_FILE_INFO): Integer; stdcall; 117 | 118 | TDokanWriteFile = function(FileName: LPCWSTR; 119 | var Buffer; 120 | NumberOfBytesToWrite: DWORD; 121 | var NumberOfBytesWritten: DWORD; 122 | Offset: LONGLONG; 123 | var DokanFileInfo: DOKAN_FILE_INFO): Integer; stdcall; 124 | 125 | TDokanFlushFileBuffers = function(FileName: LPCWSTR; 126 | var DokanFileInfo: DOKAN_FILE_INFO): Integer; stdcall; 127 | 128 | TDokanGetFileInformation = function(FileName: LPCWSTR; 129 | FileInformation: PByHandleFileInformation; 130 | var DokanFileInfo: DOKAN_FILE_INFO): Integer; stdcall; 131 | 132 | TDokanFindFiles = function(PathName: LPCWSTR; 133 | // For each matched file, call this function with a filled PWIN32_FIND_DATAW structure 134 | FillFindDataCallback: TDokanFillFindData; 135 | var DokanFileInfo: DOKAN_FILE_INFO): Integer; stdcall; 136 | 137 | // You should implement either FindFiles or FindFilesWithPattern 138 | TDokanFindFilesWithPattern = function(PathName, SearchPattern: LPCWSTR; 139 | // For each matched file, call this function with a filled PWIN32_FIND_DATAW structure 140 | FillFindDataCallback: TDokanFillFindData; 141 | var DokanFileInfo: DOKAN_FILE_INFO): Integer; stdcall; 142 | 143 | TDokanSetFileAttributes = function(FileName: LPCWSTR; 144 | FileAttributes: DWORD; 145 | var DokanFileInfo: DOKAN_FILE_INFO): Integer; stdcall; 146 | 147 | TDokanSetFileTime = function(FileName: LPCWSTR; 148 | CreationTime, LastAccessTime, LastWriteTime: PFileTime; 149 | var DokanFileInfo: DOKAN_FILE_INFO): Integer; stdcall; 150 | 151 | // You should not delete any file or directory when DeleteFile or 152 | // DeleteDirectory is called. Instead, you must check whether it can be deleted 153 | // or not, return 0 (ERROR_SUCCESS) if yes, or appropriate error codes such as 154 | // -ERROR_DIR_NOT_EMPTY, -ERROR_SHARING_VIOLATION... otherwise. 155 | // Returning 0 ensures that the Cleanup callback will be called later with 156 | // FileInfo.DeleteOnClose set to True, and you will be able to safely delete 157 | // the file at that time. 158 | TDokanDeleteFile = function(FileName: LPCWSTR; 159 | var DokanFileInfo: DOKAN_FILE_INFO): Integer; stdcall; 160 | 161 | TDokanDeleteDirectory = function(FileName: LPCWSTR; 162 | var DokanFileInfo: DOKAN_FILE_INFO): Integer; stdcall; 163 | 164 | TDokanMoveFile = function(ExistingFileName, NewFileName: LPCWSTR; 165 | ReplaceExisiting: BOOL; 166 | var DokanFileInfo: DOKAN_FILE_INFO): Integer; stdcall; 167 | 168 | TDokanSetEndOfFile = function(FileName: LPCWSTR; 169 | Length: LONGLONG; 170 | var DokanFileInfo: DOKAN_FILE_INFO): Integer; stdcall; 171 | 172 | TDokanSetAllocationSize = function(FileName: LPCWSTR; 173 | Length: LONGLONG; 174 | var DokanFileInfo: DOKAN_FILE_INFO): Integer; stdcall; 175 | 176 | TDokanLockFile = function(FileName: LPCWSTR; 177 | ByteOffset, Length: LONGLONG; 178 | var DokanFileInfo: DOKAN_FILE_INFO): Integer; stdcall; 179 | 180 | TDokanUnlockFile = function(FileName: LPCWSTR; 181 | ByteOffset, Length: LONGLONG; 182 | var DokanFileInfo: DOKAN_FILE_INFO): Integer; stdcall; 183 | 184 | // Neither GetDiskFreeSpace nor GetVolumeInformation will save the value of 185 | // DokanFileContext.Context. 186 | 187 | ULONGLONG = ULARGE_INTEGER; 188 | 189 | // See Win32 API GetDiskFreeSpaceEx 190 | TDokanGetDiskFreeSpace = function(var FreeBytesAvailable, TotalNumberOfBytes, TotalNumberOfFreeBytes: ULONGLONG; 191 | var DokanFileInfo: DOKAN_FILE_INFO): Integer; stdcall; 192 | 193 | // See Win32 API GetVolumeInformation 194 | TDokanGetVolumeInformation = function(VolumeNameBuffer: LPWSTR; 195 | VolumeNameSize: DWORD; 196 | var VolumeSerialNumber, MaximumComponentLength, FileSystemFlags: DWORD; 197 | FileSystemNameBuffer: LPWSTR; 198 | FileSystemNameSize: DWORD; 199 | var DokanFileInfo: DOKAN_FILE_INFO): Integer; stdcall; 200 | 201 | TDokanUnmount = function(var DokanFileInfo: DOKAN_FILE_INFO): Integer; stdcall; 202 | 203 | TDokanGetFileSecurity = function(FileName: LPCWSTR; 204 | var SecurityInformation : SECURITY_INFORMATION; 205 | var SecurityDescriptor : SECURITY_DESCRIPTOR; 206 | LengthOfSecurityDescriptorBuffer : ULONG; 207 | var LengthNeeded : ULONG; 208 | var DokanFileInfo: DOKAN_FILE_INFO): Integer; stdcall; 209 | 210 | TDokanSetFileSecurity = function(FileName: LPCWSTR; 211 | var SecurityInformation : SECURITY_INFORMATION; 212 | var SecurityDescriptor : SECURITY_DESCRIPTOR; 213 | SecurityDescriptorLength : ULONG; 214 | var DokanFileInfo: DOKAN_FILE_INFO): Integer; stdcall; 215 | 216 | _DOKAN_OPERATIONS = packed record 217 | CreateFile: TDokanCreateFile; 218 | OpenDirectory: TDokanOpenDirectory; 219 | CreateDirectory: TDokanCreateDirectory; 220 | Cleanup: TDokanCleanup; 221 | CloseFile: TDokanCloseFile; 222 | ReadFile: TDokanReadFile; 223 | WriteFile: TDokanWriteFile; 224 | FlushFileBuffers: TDokanFlushFileBuffers; 225 | GetFileInformation: TDokanGetFileInformation; 226 | FindFiles: TDokanFindFiles; 227 | FindFilesWithPattern: TDokanFindFilesWithPattern; 228 | SetFileAttributes: TDokanSetFileAttributes; 229 | SetFileTime: TDokanSetFileTime; 230 | DeleteFile: TDokanDeleteFile; 231 | DeleteDirectory: TDokanDeleteDirectory; 232 | MoveFile: TDokanMoveFile; 233 | SetEndOfFile: TDokanSetEndOfFile; 234 | SetAllocationSize : TDokanSetAllocationSize; 235 | LockFile: TDokanLockFile; 236 | UnlockFile: TDOkanUnlockFile; 237 | GetFileSecurity: TDokanGetFileSecurity; 238 | SetFileSecurity: TDokanSetFileSecurity; 239 | GetDiskFreeSpace: TDokanGetDiskFreeSpace; 240 | GetVolumeInformation:TDokanGetVolumeInformation; 241 | Unmount: TDokanUnmount; 242 | end; 243 | PDOKAN_OPERATIONS = ^_DOKAN_OPERATIONS; 244 | DOKAN_OPERATIONS = _DOKAN_OPERATIONS; 245 | 246 | TDokanOperations = _DOKAN_OPERATIONS; 247 | PDokanOperations = PDOKAN_OPERATIONS; 248 | 249 | const 250 | DOKAN_SUCCESS = 0; 251 | DOKAN_ERROR = -1; // General error 252 | DOKAN_DRIVE_LETTER_ERROR = -2; // Bad drive letter 253 | DOKAN_DRIVER_INSTALL_ERROR = -3; // Cannot install driver 254 | DOKAN_START_ERROR = -4; // Something is wrong with the driver 255 | DOKAN_MOUNT_ERROR = -5; // Cannot assign the drive letter 256 | DOKAN_MOUNT_POINT_ERROR = -6; // Mountpoint is invalid 257 | 258 | function DokanMain(var Options: DOKAN_OPTIONS; var Operations: DOKAN_OPERATIONS): Integer; stdcall; 259 | function DokanUnmount(DriveLetter: WCHAR): BOOL; stdcall; 260 | function DokanRemoveMountPoint(MountPoint : LPCWSTR): BOOL; stdcall; 261 | // DokanIsNameInExpression 262 | // check whether Name can match Expression 263 | // Expression can contain wildcard characters (? and *) 264 | function DokanIsNameInExpression(Expression, Name: LPCWSTR; IgnoreCase: BOOL): Bool; stdcall; 265 | function DokanVersion: ULONG; stdcall; 266 | function DokanDriverVersion: ULONG; stdcall; 267 | // DokanResetTimeout 268 | // extends the time out of the current IO operation in driver. 269 | function DokanResetTimeout(Timeout : ULONG;var DokanFileInfo: DOKAN_FILE_INFO): Bool; stdcall; 270 | // Get the handle to Access Token 271 | // This method needs be called in CreateFile, OpenDirectory or CreateDirectly callback. 272 | // The caller must call CloseHandle for the returned handle. 273 | function DokanOpenRequestorToken(var DokanFileInfo: DOKAN_FILE_INFO): THandle; stdcall; 274 | 275 | // For internal use only. Do not call these! 276 | function DokanServiceInstall(ServiceName: LPCWSTR; ServiceType: DWORD; ServiceFullPath: LPCWSTR): Bool; stdcall; 277 | function DokanServiceDelete(ServiceName: LPCWSTR): Bool; stdcall; 278 | 279 | implementation 280 | 281 | function DokanMain; external DokanLibrary; 282 | function DokanUnmount; external DokanLibrary; 283 | function DokanRemoveMountPoint; external DokanLibrary; 284 | function DokanIsNameInExpression; external DokanLibrary; 285 | function DokanVersion; external DokanLibrary; 286 | function DokanDriverVersion; external DokanLibrary; 287 | function DokanResetTimeout; external DokanLibrary; 288 | function DokanOpenRequestorToken; external DokanLibrary; 289 | function DokanServiceInstall; external DokanLibrary; 290 | function DokanServiceDelete; external DokanLibrary; 291 | 292 | end. 293 | -------------------------------------------------------------------------------- /HashMap.pas: -------------------------------------------------------------------------------- 1 | unit HashMap; 2 | //////////////////////////////////////////////////////////////////////////////// 3 | // 4 | // Unit : THashMap 5 | // Author : rllibby 6 | // Date : 06.16.2008 7 | // Description : Object hash that allows for fast lookups, as well as indexed 8 | // traversal of the items. 9 | // http://planetmath.org/encyclopedia/GoodHashTablePrimes.html 10 | // APM 2009 - added Dynamic Array for adjustable BucketSize at Runtime 11 | // - added FastCode/FastMove 12 | // - added Hash/Key compare for faster bucket traversals 13 | //////////////////////////////////////////////////////////////////////////////// 14 | interface 15 | 16 | //////////////////////////////////////////////////////////////////////////////// 17 | // Include units 18 | //////////////////////////////////////////////////////////////////////////////// 19 | uses 20 | FastCode, FastMove, Windows, SysUtils, Classes; 21 | 22 | //////////////////////////////////////////////////////////////////////////////// 23 | // Hash constansts 24 | //////////////////////////////////////////////////////////////////////////////// 25 | //const 26 | // HASH_SIZE = 1543{521}; // Can be changed, but should always be a prime number 27 | 28 | //////////////////////////////////////////////////////////////////////////////// 29 | // THashMapNode 30 | //////////////////////////////////////////////////////////////////////////////// 31 | type 32 | PHashMapNode = ^THashMapNode; 33 | THashMapNode = packed record 34 | Key: PChar; 35 | Hash: LongWord; 36 | Data: TObject; 37 | Next: PHashMapNode; 38 | end; 39 | 40 | //////////////////////////////////////////////////////////////////////////////// 41 | // String compare prototype 42 | //////////////////////////////////////////////////////////////////////////////// 43 | type 44 | TStrCompare = function(const Str1, Str2: PChar): Integer; 45 | 46 | //////////////////////////////////////////////////////////////////////////////// 47 | // THashMap 48 | //////////////////////////////////////////////////////////////////////////////// 49 | type 50 | THashMap = class(TObject) 51 | private 52 | // Private declarations 53 | FHash: Array of PHashMapNode; 54 | FList: TList; 55 | FCompare: TStrCompare; 56 | FCaseSensitive:Boolean; 57 | FOwnsObjects: Boolean; 58 | HASH_SIZE: Integer; 59 | protected 60 | // Protected declarations 61 | function GetCount: Integer; 62 | function GetItems(Index: Integer): TObject; 63 | function GetKeys(Index: Integer): String; 64 | function HashFunc(Key: PChar): LongWord; 65 | function HashCompare(Item1, Item2: PChar): Boolean; 66 | function NewNode(Key: PChar; Data: TObject; Hash: LongWord): PHashMapNode; 67 | procedure FreeNode(Node: PHashMapNode); 68 | procedure SetCaseSensitive(Value: Boolean); 69 | public 70 | // Public declarations 71 | constructor Create(ACaseSensitive: Boolean = False; AOwnsObjects: Boolean = True; AHash_Size: Integer = 521); 72 | destructor Destroy; override; 73 | procedure Clear; 74 | function Delete(Key: String): Boolean; 75 | function Add(Key: String; Data: TObject): Boolean; 76 | function Get(Key: String): TObject; 77 | function Find(Key: String; out Data): Boolean; 78 | function Extract(Key: String; out Data): Boolean; 79 | public 80 | // Public properties 81 | property CaseSensitive: Boolean read FCaseSensitive write SetCaseSensitive; 82 | property OwnsObjects: Boolean read FOwnsObjects write FOwnsObjects; 83 | property Count: Integer read GetCount; 84 | property Items[Index: Integer]: TObject read GetItems; 85 | property Keys[Index: Integer]: String read GetKeys; 86 | end; 87 | 88 | implementation 89 | 90 | //// THashMap ////////////////////////////////////////////////////////////////// 91 | constructor THashMap.Create(ACaseSensitive: Boolean = False; AOwnsObjects: Boolean = True; AHash_Size: Integer = 521); 92 | begin 93 | 94 | // Perform inherited 95 | inherited Create; 96 | 97 | // Initial values 98 | SetLength(FHash,AHash_Size); 99 | HASH_SIZE := AHash_Size; 100 | //FillChar(FHash, SizeOf(FHash), 0); 101 | FList:=TList.Create; 102 | FCaseSensitive:=ACaseSensitive; 103 | FOwnsObjects:=AOwnsObjects; 104 | 105 | // Determine the compare function to use 106 | if FCaseSensitive then 107 | // Use StrComp 108 | @FCompare:=@StrComp 109 | else 110 | // Use StrIComp 111 | @FCompare:=@StrIComp; 112 | 113 | end; 114 | 115 | destructor THashMap.Destroy; 116 | begin 117 | 118 | // Resource protection 119 | try 120 | // Clear the hash 121 | Clear; 122 | // Free the list 123 | FList.Free; 124 | // Free the dynamic array 125 | SetLength(FHash, 0); 126 | finally 127 | // Perform inherited 128 | inherited Destroy; 129 | end; 130 | 131 | end; 132 | 133 | procedure THashMap.Clear; 134 | var lpNode: PHashMapNode; 135 | lpNext: PHashMapNode; 136 | dwIndex: Integer; 137 | begin 138 | 139 | // Resource protection 140 | try 141 | // Resource protection 142 | try 143 | // Iterate the array and clear the hash nodes 144 | for dwIndex:=0 to Pred(HASH_SIZE) do 145 | begin 146 | // Get bucket node 147 | lpNode:=FHash[dwIndex]; 148 | // Walk the nodes 149 | while Assigned(lpNode) do 150 | begin 151 | // Get pointer to next item 152 | lpNext:=lpNode^.Next; 153 | // Free node 154 | FreeNode(lpNode); 155 | // Set iterator to next item 156 | lpNode:=lpNext; 157 | end; 158 | // Clear hash bucket pointer in dynamic array 159 | FHash[dwIndex] := nil; 160 | end; 161 | finally 162 | // Clear all hash buckets 163 | //FillChar(FHash, SizeOf(FHash), 0); 164 | end; 165 | finally 166 | // Clear the list 167 | FList.Clear; 168 | end; 169 | 170 | end; 171 | 172 | {$OVERFLOWCHECKS OFF} 173 | function THashMap.HashFunc(Key: PChar): LongWord; 174 | var bChar: Byte; 175 | begin 176 | 177 | // Set starting result 178 | result:=0; 179 | 180 | // Check key pointer 181 | if Assigned(Key) then 182 | begin 183 | // Generate hash index for key 184 | while (Key^ > #0) do 185 | begin 186 | // Check ascii char 187 | if (Key^ in ['A'..'Z']) then 188 | // Lowercase the value 189 | bChar:=Byte(Key^) + 32 190 | else 191 | // Keep value as is 192 | bChar:=Byte(Key^); 193 | // Update hash value 194 | Inc(result, (result shl 3) + bChar); 195 | // Next char 196 | Inc(Key); 197 | end; 198 | end; 199 | 200 | // Keep result in bounds of array 201 | //result:=result mod HASH_SIZE; 202 | 203 | end; 204 | {$OVERFLOWCHECKS ON} 205 | 206 | function THashMap.HashCompare(Item1, Item2: PChar): Boolean; 207 | begin 208 | 209 | // Check item1 for null 210 | if (Item1 = nil) then 211 | // Check for Item2 being nil 212 | result:=(Item2 = nil) 213 | // Check item2 for null 214 | else if (Item2 = nil) then 215 | // Item1 is not null, so no possible match 216 | result:=False 217 | else 218 | // Compare the strings 219 | result:=(FCompare(Item1, Item2) = 0); 220 | 221 | end; 222 | 223 | function THashMap.NewNode(Key: PChar; Data: TObject; Hash: LongWord): PHashMapNode; 224 | begin 225 | 226 | // Get memory for new node 227 | GetMem(result, SizeOf(THashMapNode)); 228 | 229 | // Resource protection 230 | try 231 | // Resource protection 232 | try 233 | // Check key 234 | if Assigned(Key) then 235 | // Set key and data fields 236 | result^.Key:=StrCopy(AllocMem(Succ(StrLen(Key))), Key) 237 | else 238 | // Allocate byte for null terminator 239 | result^.Key:=AllocMem(SizeOf(Char)); 240 | // Set data field 241 | result^.Data:=Data; 242 | // Assign full hash 243 | result^.Hash:=Hash; 244 | finally 245 | // Make sure the next node link is cleared 246 | result^.Next:=nil; 247 | end; 248 | finally 249 | // Add item to list 250 | FList.Add(result); 251 | end; 252 | 253 | end; 254 | 255 | procedure THashMap.FreeNode(Node: PHashMapNode); 256 | begin 257 | 258 | // Remove the node from the list 259 | FList.Remove(Node); 260 | 261 | // Resource protection 262 | try 263 | // Free node key 264 | FreeMem(Node^.Key); 265 | // If owns object is true, then free the object 266 | if FOwnsObjects then Node^.Data.Free; 267 | finally 268 | // Free node memory 269 | FreeMem(Node); 270 | end; 271 | 272 | end; 273 | 274 | function THashMap.Extract(Key: String; out Data): Boolean; 275 | var lpNode: PHashMapNode; 276 | lpIter: PHashMapNode; 277 | dwIndex,Hash: LongWord; 278 | begin 279 | 280 | // Set default result 281 | result:=False; 282 | 283 | // Get the hash index 284 | Hash:=HashFunc(Pointer(Key)); 285 | dwIndex:=Hash mod HASH_SIZE; 286 | 287 | // Check top level bucket 288 | if Assigned(FHash[dwIndex]) then 289 | begin 290 | // Prepare for node iteration 291 | lpNode:=FHash[dwIndex]; 292 | lpIter:=lpNode; 293 | // Walk the nodes 294 | while Assigned(lpIter) do 295 | begin 296 | // Match key 297 | if (lpIter^.Hash = Hash) and HashCompare(lpIter^.Key, Pointer(Key)) then break; 298 | // Save current node 299 | lpNode:=lpIter; 300 | // Move to the next node in the chain 301 | lpIter:=lpNode^.Next; 302 | end; 303 | // Check to see if the node is still set 304 | if Assigned(lpIter) then 305 | begin 306 | // Check to see if this is the top level item 307 | if (lpIter = lpNode) then 308 | // Link next node into the bucket 309 | FHash[dwIndex]:=lpIter^.Next 310 | else 311 | // Link over this node 312 | lpNode^.Next:=lpIter^.Next; 313 | // Set the outbound data 314 | TObject(Data):=lpIter^.Data; 315 | // Clear the node data (extract will not free the object) 316 | lpIter^.Data:=nil; 317 | // Free the node 318 | FreeNode(lpIter); 319 | // Success 320 | result:=True; 321 | end; 322 | end; 323 | 324 | end; 325 | 326 | function THashMap.Delete(Key: String): Boolean; 327 | var lpNode: PHashMapNode; 328 | lpIter: PHashMapNode; 329 | dwIndex,Hash: LongWord; 330 | begin 331 | 332 | // Set default result 333 | result:=False; 334 | 335 | // Get the hash index 336 | Hash:=HashFunc(Pointer(Key)); 337 | dwIndex:=Hash mod HASH_SIZE; 338 | 339 | // Check top level bucket 340 | if Assigned(FHash[dwIndex]) then 341 | begin 342 | // Prepare for node iteration 343 | lpNode:=FHash[dwIndex]; 344 | lpIter:=lpNode; 345 | // Walk the nodes 346 | while Assigned(lpIter) do 347 | begin 348 | // Match key/hash 349 | if (lpIter^.Hash = Hash) and HashCompare(lpIter^.Key, Pointer(Key)) then break; 350 | // Save current node 351 | lpNode:=lpIter; 352 | // Move to the next node in the chain 353 | lpIter:=lpNode^.Next; 354 | end; 355 | // Check to see if the node is still set 356 | if Assigned(lpIter) then 357 | begin 358 | // Check to see if this is the top level item 359 | if (lpIter = lpNode) then 360 | // Link next node into the bucket 361 | FHash[dwIndex]:=lpIter^.Next 362 | else 363 | // Link over this node 364 | lpNode^.Next:=lpIter^.Next; 365 | // Free the node 366 | FreeNode(lpIter); 367 | // Success 368 | result:=True; 369 | end; 370 | end; 371 | 372 | end; 373 | 374 | function THashMap.Add(Key: String; Data: TObject): Boolean; 375 | var lpNode: PHashMapNode; 376 | lpIter: PHashMapNode; 377 | dwIndex,Hash: LongWord; 378 | begin 379 | 380 | // Get the hash bucket item index 381 | Hash:=HashFunc(Pointer(Key)); 382 | dwIndex:=Hash mod HASH_SIZE; 383 | 384 | // Resource protection 385 | try 386 | // Get the hash bucket item 387 | lpNode:=FHash[dwIndex]; 388 | // Is the bucket empty? 389 | if (lpNode = nil) then 390 | // Add new node into bucket 391 | FHash[dwIndex]:=NewNode(Pointer(Key), Data, Hash) 392 | else 393 | begin 394 | // Save current node 395 | lpIter:=lpNode; 396 | // Walk nodes 397 | while Assigned(lpIter) do 398 | begin 399 | // Match the key 400 | if (lpIter^.Hash = Hash) and HashCompare(lpIter^.Key, Pointer(Key)) then 401 | begin 402 | // Check for same object 403 | if not(lpIter^.Data = Data) then 404 | begin 405 | // Not same object, do we own the original (and should we free it) 406 | if FOwnsObjects then lpIter^.Data.Free; 407 | // Assign new object 408 | lpIter^.Data:=Data; 409 | end; 410 | // Done processing 411 | break; 412 | end; 413 | // Save current node 414 | lpNode:=lpIter; 415 | // Walk next node 416 | lpIter:=lpNode^.Next; 417 | end; 418 | // Do we need to add a new item to the end of the chain? 419 | if not(Assigned(lpIter)) then 420 | begin 421 | // Create new hash node and add to end of the chain 422 | lpNode^.Next:=NewNode(Pointer(Key), Data, Hash); 423 | end; 424 | end; 425 | finally 426 | // Always success 427 | result:=True; 428 | end; 429 | 430 | end; 431 | 432 | function THashMap.Get(Key: String): TObject; 433 | begin 434 | 435 | // Just another way of calling Find(...) 436 | if not(Find(Key, result)) then result:=nil; 437 | 438 | end; 439 | 440 | function THashMap.Find(Key: String; out Data): Boolean; 441 | var lpNode: PHashMapNode; 442 | dwIndex,Hash: LongWord; 443 | begin 444 | 445 | // Get the hash bucket item 446 | Hash:=HashFunc(Pointer(Key)); 447 | dwIndex:=Hash mod HASH_SIZE; 448 | lpNode:=FHash[dwIndex]; 449 | 450 | // Resource protection 451 | try 452 | // Walk the items 453 | while Assigned(lpNode) do 454 | begin 455 | // Compare the key/hash 456 | if (lpNode^.Hash = Hash) and HashCompare(lpNode^.Key, Pointer(Key)) then 457 | begin 458 | // Key exists, set out return data 459 | TObject(Data):=lpNode^.Data; 460 | // Done processing 461 | break; 462 | end; 463 | // Walk the next item 464 | lpNode:=lpNode^.Next; 465 | end; 466 | finally 467 | // Success if node is assigned 468 | result:=Assigned(lpNode); 469 | end; 470 | 471 | end; 472 | 473 | function THashMap.GetCount: Integer; 474 | begin 475 | 476 | // Return the count of items 477 | result:=FList.Count; 478 | 479 | end; 480 | 481 | function THashMap.GetItems(Index: Integer): TObject; 482 | begin 483 | 484 | // Return the data object for the indexed item 485 | result:=PHashMapNode(FList[Index])^.Data; 486 | 487 | end; 488 | 489 | function THashMap.GetKeys(Index: Integer): String; 490 | begin 491 | 492 | // Return the Key for the indexed item 493 | result:=PHashMapNode(FList[Index])^.Key; 494 | 495 | end; 496 | 497 | procedure THashMap.SetCaseSensitive(Value: Boolean); 498 | begin 499 | 500 | // Set case sensitivity 501 | if not(FCaseSensitive = Value) then 502 | begin 503 | // Change 504 | FCaseSensitive:=Value; 505 | // Update the compare function 506 | if FCaseSensitive then 507 | // Use StrComp 508 | @FCompare:=@StrComp 509 | else 510 | // Use StrIComp 511 | @FCompare:=@StrIComp; 512 | end; 513 | 514 | end; 515 | 516 | end. 517 | 518 | 519 | { //HashMap appears to be over 5x faster than TStringHashTrie!!! 520 | // uses HashMap 521 | var objScreen: TScreen; 522 | objMouse: TMouse; 523 | dwIndex: Integer; 524 | begin 525 | 526 | // Create with case insensitivty / does not own objects 527 | with THashMap.Create(False, False) do 528 | begin 529 | // Adding items to hash map 530 | Add('Mouse', Mouse); 531 | Add('Screen', Screen); 532 | Add('Form1', Self); 533 | Add('Application', Application); 534 | 535 | // Using the Find accessor 536 | if Find('Screen', objScreen) then ShowMessage(objScreen.ClassName); 537 | 538 | // Using the Get accessor 539 | objMouse:=TMouse(Get('Mouse')); 540 | // Need to check assignment 541 | if Assigned(objMouse) then ShowMessage(objMouse.ClassName); 542 | 543 | // Example using iteration 544 | for dwIndex:=0 to Pred(Count) do 545 | ShowMessage(Keys[dwIndex] + '=' + Items[dwIndex].ClassName); 546 | 547 | // Free the hash map 548 | Free; 549 | end; 550 | } 551 | 552 | { 553 | In the course of designing a good hashing configuration, it is helpful to have a list of prime numbers for the hash table size. 554 | 555 | The following is such a list. It has the properties that: 556 | 557 | 1. each number in the list is prime (as you no doubt expected by now) 558 | 2. each number is slightly less than twice the size of the previous 559 | 3. each number is as far as possible from the nearest two powers of two 560 | 561 | Using primes for hash tables is a good idea because it minimizes clustering in the hashed table. Item (2) is nice because it is convenient for growing a hash table in the face of expanding data. Item (3) has, allegedly, been shown to yield especially good results in practice. 562 | 563 | And here is the list: 564 | lwr upr % err prime 565 | 2^5 2^6 10.41667 53 566 | 2^6 2^7 1.041667 97 567 | 2^7 2^8 0.520833 193 568 | 2^8 2^9 1.302083 389 569 | 2^9 2^10 0.130208 769 570 | 2^10 2^11 0.455729 1543 571 | 2^11 2^12 0.227865 3079 572 | 2^12 2^13 0.113932 6151 573 | 2^13 2^14 0.008138 12289 574 | 2^14 2^15 0.069173 24593 575 | 2^15 2^16 0.010173 49157 576 | 2^16 2^17 0.013224 98317 577 | 2^17 2^18 0.002543 196613 578 | 2^18 2^19 0.006358 393241 579 | 2^19 2^20 0.000127 786433 580 | 2^20 2^21 0.000318 1572869 581 | 2^21 2^22 0.000350 3145739 582 | 2^22 2^23 0.000207 6291469 583 | 2^23 2^24 0.000040 12582917 584 | 2^24 2^25 0.000075 25165843 585 | 2^25 2^26 0.000010 50331653 586 | 2^26 2^27 0.000023 100663319 587 | 2^27 2^28 0.000009 201326611 588 | 2^28 2^29 0.000001 402653189 589 | 2^29 2^30 0.000011 805306457 590 | 2^30 2^31 0.000000 1610612741 591 | 592 | The columns are, in order, the lower bounding power of two, the upper bounding power of two, the relative deviation (in percent) of the prime number from the optimal middle of the first two, and finally the prime itself. 593 | } 594 | -------------------------------------------------------------------------------- /dipfs.dpr: -------------------------------------------------------------------------------- 1 | program dipfs; 2 | 3 | (******************************************************************************* 4 | * 5 | * dipfs - basic read-only interface between ipfs and dokan, by Alexander Paul Morris 6 | * works by connecting to the local ipfs REST endpoint at http://127.0.0.1:5001/api/ 7 | * to mount the ipfs distributed file system to a Windows drive (like Fuse for Linux) 8 | * 9 | * Confirmed to work with Windows 32-bit and 64-bit installs. 10 | * For Windows 7 to Windows 10, use latest dokany release at https://github.com/dokan-dev/dokany 11 | * 12 | * 13 | * v0.12, 2015-07-22, Added access to \ipns\ paths as well (an ipns path is a mutable pointer to an ipfs path) 14 | * 15 | * v0.11, 2015-07-17, Cleaned up a few things, and fixed error if no drive id was provided 16 | * Confirmed that DokanInstall_0.6.0.exe and dipfs also work on a 17 | * 64-bit Windows7 system, as well as on older Windows 32-bit setups 18 | * 19 | * v0.10, 2015-07-16 Initial release 20 | * 21 | * 22 | * usage: 23 | * dipfs /l z <-- will link \ipfs\ and \ipns\ to drive z: 24 | * dipfs /l z /d <-- same as above, with additional output for debugging 25 | * 26 | * from Windows Command Prompt, try the following: 27 | * 28 | * z: 29 | * cd \ipfs\QmPXME1oRtoT627YKaDPDQ3PwA8tdP9rWuAAweLzqSwAWT 30 | * dir 31 | * type readme 32 | * 33 | * for a node with a subdirectory structure, try the following: 34 | * 35 | * cd \ipfs\QmRCJXG7HSmprrYwDrK1GctXHgbV7EYpVcJPQPwevoQuqF 36 | * 37 | * 38 | * Additional Source Code Dependencies: HTTPSend (Synapse) 39 | * 40 | * 41 | * Copyright (c) 2007, 2008 Hiroki Asakawa info@dokan-dev.net 42 | * 43 | * Delphi translation by Vincent Forman (vincent.forman@gmail.com) 44 | * 45 | * Permission is hereby granted, free of charge, to any person obtaining a copy 46 | * of this software and associated documentation files (the "Software"), to deal 47 | * in the Software without restriction, including without limitation the rights 48 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 49 | * copies of the Software, and to permit persons to whom the Software is 50 | * furnished to do so, subject to the following conditions: 51 | * 52 | * The above copyright notice and this permission notice shall be included in 53 | * all copies or substantial portions of the Software. 54 | * 55 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 56 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 57 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 58 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 59 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 60 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 61 | * THE SOFTWARE. 62 | * 63 | *******************************************************************************) 64 | 65 | {$APPTYPE CONSOLE} 66 | 67 | uses 68 | Windows, 69 | SysUtils, 70 | Classes, 71 | SuperObject, HashMap, FastStrings, 72 | HTTPSend, //ssl_openssl, ssl_openssl_lib, ZLibExGz, 73 | Dokan in 'Dokan.pas'; 74 | 75 | const dipfsVersion = '0.12'; 76 | 77 | // Not available in Windows.pas 78 | function SetFilePointerEx(hFile: THandle; lDistanceToMove: LARGE_INTEGER; lpNewFilePointer: Pointer; dwMoveMethod: DWORD): BOOL; stdcall; external kernel32; 79 | 80 | // Some additional Win32 flags 81 | const 82 | FILE_READ_DATA = $00000001; 83 | FILE_WRITE_DATA = $00000002; 84 | FILE_APPEND_DATA = $00000004; 85 | FILE_READ_EA = $00000008; 86 | FILE_WRITE_EA = $00000010; 87 | FILE_EXECUTE = $00000020; 88 | FILE_READ_ATTRIBUTES = $00000080; 89 | FILE_WRITE_ATTRIBUTES = $00000100; 90 | 91 | FILE_ATTRIBUTE_ENCRYPTED = $00000040; 92 | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = $00002000; 93 | FILE_FLAG_OPEN_NO_RECALL = $00100000; 94 | FILE_FLAG_OPEN_REPARSE_POINT = $00200000; 95 | 96 | STATUS_DIRECTORY_NOT_EMPTY = $C0000101; 97 | 98 | INVALID_SET_FILE_POINTER = $FFFFFFFF; 99 | 100 | 101 | var HTFileMap: THashMap; 102 | 103 | 104 | // Utilities routines, to be defined later 105 | procedure DbgPrint(const Message: string); overload; forward; 106 | procedure DbgPrint(const Format: string; const Args: array of const); overload; forward; 107 | 108 | 109 | 110 | procedure GetUrlHtmlWithComp(UrlS: string; var TmpL: TStringList); 111 | var HTTP: THTTPSend; 112 | //DeCompStream: TGZDecompressionStream; 113 | TmpStream: TMemoryStream; 114 | err: boolean; 115 | begin 116 | TmpL.Clear; 117 | 118 | HTTP := THTTPSend.Create; 119 | HTTP.Clear; 120 | HTTP.Timeout := 2500; 121 | HTTP.UserAgent := 'Mozilla/5.0 (compatible)'; //Mozilla 4.0 disables many gzips! oy 122 | HTTP.Headers.Add('Accept-Encoding: gzip'); 123 | HTTP.Protocol := '1.1'; 124 | try 125 | //if (copy(urls,1,5)='https') then begin 126 | // HTTP.Sock.CreateWithSSL(TSSLOpenSSL); 127 | // HTTP.Sock.SSLDoConnect; 128 | // end; 129 | HTTP.HTTPMethod('GET',urls); 130 | except end; 131 | 132 | TmpStream := TMemoryStream.Create; 133 | err := false; 134 | //HTTP.Document.Seek(0,soFromBeginning); 135 | //try GZDecompressStream(HTTP.Document,TmpStream); except err := true; end; 136 | HTTP.Document.Seek(0,soFromBeginning); 137 | TmpStream.Seek(0,soFromBeginning); 138 | //if (not err) then TmpL.LoadFromStream(TmpStream) else 139 | TmpL.LoadFromStream(HTTP.Document); 140 | TmpStream.Free; 141 | HTTP.Free; 142 | 143 | end; 144 | 145 | 146 | procedure GetUrlHtmlWithCompAsStream(UrlS: string; var TmpStream: TMemoryStream); 147 | var HTTP: THTTPSend; 148 | //DeCompStream: TGZDecompressionStream; 149 | err: boolean; 150 | begin 151 | TmpStream := nil; 152 | 153 | HTTP := THTTPSend.Create; 154 | HTTP.Clear; 155 | HTTP.Timeout := 2500; 156 | HTTP.UserAgent := 'Mozilla/5.0 (compatible)'; //Mozilla 4.0 disables many gzips! oy 157 | HTTP.Headers.Add('Accept-Encoding: gzip'); 158 | HTTP.Protocol := '1.1'; 159 | try 160 | //if (copy(urls,1,5)='https') then begin 161 | // HTTP.Sock.CreateWithSSL(TSSLOpenSSL); 162 | // HTTP.Sock.SSLDoConnect; 163 | // end; 164 | HTTP.HTTPMethod('GET',urls); 165 | except end; 166 | 167 | TmpStream := TMemoryStream.Create; 168 | err := false; 169 | //HTTP.Document.Seek(0,soFromBeginning); 170 | //try GZDecompressStream(HTTP.Document,TmpStream); except err := true; end; 171 | HTTP.Document.Seek(0,soFromBeginning); 172 | TmpStream.Seek(0,soFromBeginning); 173 | //if (not err) then TmpStream.LoadFromStream(TmpStream) else 174 | TmpStream.LoadFromStream(HTTP.Document); 175 | HTTP.Free; 176 | 177 | end; 178 | 179 | 180 | Function MyPos(const Srch,Strg: String): Integer; 181 | Begin 182 | Result := FastPos(Strg,Srch,Length(Strg),Length(Srch),1); 183 | End; 184 | 185 | Function MyPosBack(const Srch,Strg: String): Integer; 186 | Begin 187 | Result := FastPosBack(Strg,Srch,Length(Strg),Length(Srch),length(Strg)); 188 | End; 189 | 190 | Function LongHandStrNxt(Strg,Srch,Repl: String): String; 191 | {Finds an occurance and copies off - does not consider new changes in search} 192 | var i: integer; 193 | DumS: String; 194 | Begin 195 | i := FastPos(Strg,Srch,Length(Strg),Length(Srch),1); 196 | DumS := ''; 197 | While (i <> 0) Do Begin 198 | DumS := DumS + Copy(Strg,1,i-1) + Repl; 199 | Delete(Strg,1,i+Length(Srch)-1); 200 | i := FastPos(Strg,Srch,Length(Strg),Length(Srch),1); 201 | End; 202 | DumS := DumS + Strg; 203 | Result := DumS; 204 | End; 205 | 206 | 207 | // Output the value of a flag by searching amongst an array of value/name pairs 208 | procedure CheckFlag(const Flag: Cardinal; 209 | Values: array of Cardinal; 210 | Names: array of string); 211 | var 212 | i:Integer; 213 | begin 214 | for i:=Low(Values) to High(Values) do 215 | if Values[i]=Flag then 216 | DbgPrint(' %s',[Names[i]]); 217 | end; 218 | 219 | type 220 | EDokanMainError = class(Exception) 221 | public 222 | constructor Create(DokanErrorCode: Integer); 223 | end; 224 | 225 | constructor EDokanMainError.Create(DokanErrorCode: Integer); 226 | var 227 | s:string; 228 | begin 229 | case DokanErrorCode of 230 | DOKAN_SUCCESS: s := 'Success'; 231 | DOKAN_ERROR: s := 'Generic error'; 232 | DOKAN_DRIVE_LETTER_ERROR: s := 'Bad drive letter'; 233 | DOKAN_DRIVER_INSTALL_ERROR: s := 'Cannot install driver'; 234 | DOKAN_START_ERROR: s := 'Cannot start driver'; 235 | DOKAN_MOUNT_ERROR: s := 'Cannot mount on the specified drive letter'; 236 | DOKAN_MOUNT_POINT_ERROR : s := 'Mount point error'; 237 | else 238 | s := 'Unknown error'; 239 | end; 240 | inherited CreateFmt('Dokan Error. Code: %d.'+sLineBreak+'%s',[DokanErrorCode,s]); 241 | end; 242 | 243 | // Dokan callbacks 244 | function MirrorCreateFile(FileName: PWideChar; 245 | AccessMode, ShareMode, CreationDisposition, FlagsAndAttributes: Cardinal; 246 | var DokanFileInfo: TDokanFileInfo): Integer; stdcall; 247 | begin 248 | result := 0; 249 | end; 250 | 251 | function MirrorOpenDirectory(FileName: PWideChar; 252 | var DokanFileInfo: TDokanFileInfo): Integer; stdcall; 253 | begin 254 | result := 0; 255 | end; 256 | 257 | function MirrorCreateDirectory(FileName: PWideChar; 258 | var DokanFileInfo: TDokanFileInfo): Integer; stdcall; 259 | begin 260 | result := -1; 261 | end; 262 | 263 | function MirrorCleanup(FileName: PWideChar; 264 | var DokanFileInfo: TDokanFileInfo): Integer; stdcall; 265 | begin 266 | result := 0; 267 | end; 268 | 269 | function MirrorCloseFile(FileName: PWideChar; 270 | var DokanFileInfo: TDokanFileInfo): Integer; stdcall; 271 | var 272 | FilePath: string; 273 | TmpStream: TMemoryStream; 274 | Data: TObject; 275 | begin 276 | result := 0; 277 | 278 | FilePath := FileName; 279 | if (Copy(FilePath,Length(FilePath)-1,2)='\*') then Delete(FilePath,length(FilePath)-1,2); 280 | FilePath := LongHandStrNxt(FilePath,'\','/'); 281 | 282 | if HTFileMap.Find(FilePath,Data) then begin 283 | DbgPrint('CloseFile: %s', [FilePath]); 284 | TmpStream := TMemoryStream(Data); 285 | TmpStream.Free; 286 | HTFileMap.Delete(FilePath); 287 | end; 288 | end; 289 | 290 | function MirrorReadFile(FileName: PWideChar; 291 | var Buffer; 292 | NumberOfBytesToRead: Cardinal; 293 | var NumberOfBytesRead: Cardinal; 294 | Offset: Int64; 295 | var DokanFileInfo: TDokanFileInfo): Integer; stdcall; 296 | var 297 | FilePath: string; 298 | Opened: Boolean; 299 | Data: TObject; 300 | TmpS: string; 301 | obj: ISuperObject; 302 | hash: string; 303 | TmpStream: TMemoryStream; 304 | i: Integer; 305 | begin 306 | FilePath := FileName; 307 | if (Copy(FilePath,Length(FilePath)-1,2)='\*') then Delete(FilePath,length(FilePath)-1,2); 308 | FilePath := LongHandStrNxt(FilePath,'\','/'); 309 | 310 | DbgPrint('ReadFile: %s (Offset: %d, Length: %d)', [FilePath, Offset, NumberOfBytesToRead]); 311 | 312 | if HTFileMap.Find('isDir:'+FilePath,Data) then begin 313 | result := -1; 314 | exit; 315 | end; 316 | 317 | FillChar(Buffer,NumberOfBytesToRead,0); //clear dokan data buffer 318 | 319 | if HTFileMap.Find(FilePath,Data) then begin 320 | TmpStream := TMemoryStream(Data); 321 | TmpStream.Seek(Offset,soFromBeginning); 322 | if (TmpStream.Size-TmpStream.Position < NumberOfBytesToRead) then 323 | NumberOfBytesToRead := TmpStream.Size-TmpStream.Position; 324 | TmpStream.ReadBuffer(Buffer,NumberOfBytesToRead); 325 | NumberOfBytesRead := NumberOfBytesToRead; 326 | end else begin 327 | GetUrlHtmlWithCompAsStream('http://127.0.0.1:5001/api/v0/cat?arg='+FilePath,TmpStream); 328 | if (TmpStream = nil) then begin 329 | result := -1; 330 | exit; 331 | end; 332 | TmpStream.Seek(0,soFromBeginning); 333 | if (TmpStream.Size-TmpStream.Position < NumberOfBytesToRead) then 334 | NumberOfBytesToRead := TmpStream.Size-TmpStream.Position; 335 | TmpStream.ReadBuffer(Buffer,NumberOfBytesToRead); 336 | if (TmpStream.Position < TmpStream.Size-1) then begin 337 | NumberOfBytesRead := NumberOfBytesToRead; 338 | end else NumberOfBytesRead := TmpStream.Size; 339 | HTFileMap.Add(FilePath,Pointer(TmpStream)); 340 | end; 341 | 342 | end; 343 | 344 | function MirrorWriteFile(FileName: PWideChar; 345 | var Buffer; 346 | NumberOfBytesToWrite: Cardinal; 347 | var NumberOfBytesWritten: Cardinal; 348 | Offset: Int64; 349 | var DokanFileInfo: TDokanFileInfo): Integer; stdcall; 350 | begin 351 | result := -1; 352 | end; 353 | 354 | function MirrorFlushFileBuffers(FileName: PWideChar; 355 | var DokanFileInfo: TDokanFileInfo): Integer; stdcall; 356 | begin 357 | result := -1; 358 | end; 359 | 360 | 361 | //this part was tricky and very hacky, because we need to get the fileSize (if we are requesting a file), 362 | //so that dokan knows how many times to call ReadFile() versus its 512 byte buffer 363 | function MirrorGetFileInformation(FileName: PWideChar; 364 | FileInformation: PByHandleFileInformation; 365 | var DokanFileInfo: TDokanFileInfo): Integer; stdcall; 366 | var 367 | FilePath: string; 368 | Opened: Boolean; 369 | FindData: WIN32_FIND_DATAA; 370 | FindHandle: THandle; 371 | i,tmpDate,entries,entries2: integer; 372 | TmpL: TStringList; 373 | TmpS,fName,fNameTemp: string; 374 | obj: ISuperObject; 375 | arr: TSuperArray; 376 | hash: string; 377 | fSize: DWORD; 378 | begin 379 | FilePath := FileName; 380 | if (LowerCase(FilePath)='autorun.inf') then begin 381 | result := -1; 382 | exit; 383 | end; 384 | 385 | DbgPrint('GetFileInformation: %s', [FilePath]); 386 | 387 | fSize := 0; 388 | if (Copy(FilePath,1,6) = '\ipfs\') or (Copy(FilePath,1,6) = '\ipns\') then begin 389 | if (Copy(FilePath,Length(FilePath)-1,2)='\*') then Delete(FilePath,length(FilePath)-1,2); 390 | FilePath := LongHandStrNxt(FilePath,'\','/'); 391 | TmpL := TStringList.Create; 392 | GetUrlHtmlWithComp('http://127.0.0.1:5001/api/v0/ls?arg='+FilePath,TmpL); 393 | TmpS := TmpL.Text; 394 | TmpL.Free; 395 | try 396 | obj := SO(TmpS); 397 | hash := obj.a['Objects'].o[0].s['Hash']; 398 | entries := 0; 399 | arr := obj.a['Objects'].o[0].o['Links'].AsArray; 400 | if assigned(arr) then entries := arr.Length; 401 | except result := -1; exit; end; 402 | fName := FilePath; 403 | fNameTemp := ''; 404 | i := MyPosBack('/',fName); 405 | if (i<>0) then begin 406 | fNameTemp := lowercase(Copy(fName,i+1,length(fName))); 407 | delete(fName,i,length(fName)); 408 | end else fName := ''; 409 | entries2 := 0; 410 | while (entries2 = 0) and (fName <> '') do begin 411 | //writeln('finding size for: ',fName); 412 | TmpL := TStringList.Create; 413 | GetUrlHtmlWithComp('http://127.0.0.1:5001/api/v0/ls?arg='+fName,TmpL); 414 | TmpS := TmpL.Text; 415 | TmpL.Free; 416 | try 417 | obj := SO(TmpS); 418 | hash := obj.a['Objects'].o[0].s['Hash']; 419 | arr := obj.a['Objects'].o[0].o['Links'].AsArray; 420 | if assigned(arr) then entries2 := arr.Length; 421 | if (entries2 > 0) then begin 422 | for i := 0 to entries2-1 do begin 423 | if (lowercase(arr[i].s['Name']) = fNameTemp) then begin 424 | if (arr[i].I['Type'] = 1{directory}) then begin 425 | HTFileMap.Add('isDir:'+FilePath,Pointer(1)); 426 | entries := 1; //set as a directory later in FileInformation 427 | end; 428 | fSize := arr[i].i['Size']; 429 | //writeln(fName,' gotSize:',fSize,' type:',arr[i].I['Type']); 430 | break; 431 | end; 432 | end; 433 | end; 434 | except end; 435 | i := MyPosBack('/',fName); 436 | if (i<>0) then begin 437 | fNameTemp := lowercase(Copy(fName,i+1,length(fName))); 438 | delete(fName,i,length(fName)); 439 | end else fName := ''; 440 | end; 441 | end else entries := 1; 442 | 443 | FillChar(FileInformation^,SizeOf(FileInformation),0); 444 | if (entries > 0) then FileInformation.dwFileAttributes := FILE_ATTRIBUTE_DIRECTORY; 445 | FileInformation.nFileSizeLow := fSize; 446 | //BOOL DosDateTimeToFileTime(WORD wDOSDate, WORD wDOSTime, LPFILETIME lpft) 447 | tmpDate := DateTimeToFileDate(now); 448 | DosDateTimeToFileTime(LongRec(tmpDate).Hi,LongRec(tmpDate).Lo,FileInformation.ftCreationTime); 449 | DosDateTimeToFileTime(LongRec(tmpDate).Hi,LongRec(tmpDate).Lo,FileInformation.ftLastAccessTime); 450 | DosDateTimeToFileTime(LongRec(tmpDate).Hi,LongRec(tmpDate).Lo,FileInformation.ftLastWriteTime); 451 | Result := 0; 452 | end; 453 | 454 | procedure MakeDirectoryEntry(var FindData: WIN32_FIND_DATAW; fName: string; fType: Integer; fSize: DWORD); 455 | var tmpDate: integer; 456 | begin 457 | FillChar(FindData,sizeof(FindData),0); 458 | if (fType = 1) then FindData.dwFileAttributes := FILE_ATTRIBUTE_DIRECTORY else 459 | FindData.dwFileAttributes := 0; 460 | StringToWideChar(fName,FindData.cFileName,length(fName)*2); 461 | FindData.nFileSizeLow := fSize; 462 | //BOOL DosDateTimeToFileTime(WORD wDOSDate, WORD wDOSTime, LPFILETIME lpft) 463 | tmpDate := DateTimeToFileDate(now); 464 | DosDateTimeToFileTime(LongRec(tmpDate).Hi,LongRec(tmpDate).Lo,FindData.ftCreationTime); 465 | DosDateTimeToFileTime(LongRec(tmpDate).Hi,LongRec(tmpDate).Lo,FindData.ftLastAccessTime); 466 | DosDateTimeToFileTime(LongRec(tmpDate).Hi,LongRec(tmpDate).Lo,FindData.ftLastWriteTime); 467 | end; 468 | 469 | function MirrorFindFiles(PathName: PWideChar; 470 | FillFindDataCallback: TDokanFillFindData; 471 | var DokanFileInfo: TDokanFileInfo): Integer; stdcall; 472 | var 473 | FilePath: WideString; 474 | FindData: WIN32_FIND_DATAW; 475 | FindHandle: THandle; 476 | TmpL: TStringList; 477 | TmpS,fName: string; 478 | obj: ISuperObject; 479 | arr: TSuperArray; 480 | hash: string; 481 | i,j,entries: Integer; 482 | begin 483 | FilePath := PathName; 484 | if (Copy(FilePath,Length(FilePath)-1,2)='\*') then Delete(FilePath,length(FilePath)-1,2); 485 | FilePath := LongHandStrNxt(FilePath,'\','/'); 486 | 487 | DbgPrint('FindFiles: %s', [FilePath]); 488 | 489 | if (FilePath = '') or (FilePath = '/') then begin 490 | MakeDirectoryEntry(FindData,'ipfs',1{directory},0); 491 | FillFindDataCallback(FindData, DokanFileInfo); 492 | MakeDirectoryEntry(FindData,'ipns',1{directory},0); 493 | FillFindDataCallback(FindData, DokanFileInfo); 494 | result := 0; 495 | exit; 496 | end; 497 | 498 | MakeDirectoryEntry(FindData,'.',1{directory},0); 499 | FillFindDataCallback(FindData, DokanFileInfo); 500 | MakeDirectoryEntry(FindData,'..',1{directory},0); 501 | FillFindDataCallback(FindData, DokanFileInfo); 502 | if (FilePath = '/ipfs') or (FilePath = '/ipns') then begin 503 | result := 0; 504 | exit; 505 | end; 506 | 507 | TmpL := TStringList.Create; 508 | GetUrlHtmlWithComp('http://127.0.0.1:5001/api/v0/ls?arg='+FilePath,TmpL); 509 | TmpS := TmpL.Text; 510 | TmpL.Free; 511 | try 512 | obj := SO(TmpS); 513 | hash := obj.a['Objects'].o[0].s['Hash']; 514 | entries := 0; 515 | arr := obj.a['Objects'].o[0].o['Links'].AsArray; 516 | if assigned(arr) then entries := arr.Length; 517 | if (hash = '') then begin 518 | Result := -1; 519 | DbgPrint('FindFirstFile failed, error code = %s', [TmpS]); 520 | end else begin 521 | for i := 0 to entries-1 do begin 522 | fName := arr[i].s['Name'];// + ' ['+ arr[i].S['Hash'] + ']'; 523 | MakeDirectoryEntry(FindData,fName,arr[i].I['Type'],arr[i].I['Size']); 524 | FillFindDataCallback(FindData, DokanFileInfo); 525 | end; 526 | end; 527 | except 528 | Result := -1; 529 | if (TmpS='') then TmpS := 'cannotConnect?'; 530 | DbgPrint('FindFirstFile failed, error code = %s', [TmpS]); 531 | end; 532 | end; 533 | 534 | function MirrorSetFileAttributes(FileName: PWideChar; 535 | FileAttributes: Cardinal; 536 | var DokanFileInfo: TDokanFileInfo): Integer; stdcall; 537 | begin 538 | result := -1; 539 | end; 540 | 541 | function MirrorSetFileTime(FileName: PWideChar; 542 | CreationTime, LastAccessTime, LastWriteTime: PFileTime; 543 | var DokanFileInfo: TDokanFileInfo): Integer; stdcall; 544 | begin 545 | result := -1; 546 | end; 547 | 548 | function MirrorDeleteFile(FileName: PWideChar; 549 | var DokanFileInfo: TDokanFileInfo): Integer; stdcall; 550 | begin 551 | result := 0; 552 | end; 553 | 554 | function MirrorDeleteDirectory(FileName: PWideChar; 555 | var DokanFileInfo: TDokanFileInfo): Integer; stdcall; 556 | begin 557 | result := -1; 558 | end; 559 | 560 | function MirrorMoveFile(ExistingFileName, NewFileName: PWideChar; 561 | ReplaceExisiting: LongBool; 562 | var DokanFileInfo: TDokanFileInfo): Integer; stdcall; 563 | begin 564 | result := -1; 565 | end; 566 | 567 | function MirrorSetEndOfFile(FileName: PWideChar; 568 | Length: Int64; 569 | var DokanFileInfo: TDokanFileInfo): Integer; stdcall; 570 | begin 571 | result := -1; 572 | end; 573 | 574 | function MirrorLockFile(FileName: PWideChar; 575 | Offset, Length: Int64; 576 | var DokanFileInfo: TDokanFileInfo): Integer; stdcall; 577 | begin 578 | result := -1; 579 | end; 580 | 581 | function MirrorUnlockFile(FileName: PWideChar; 582 | Offset, Length: Int64; 583 | var DokanFileInfo: TDokanFileInfo): Integer; stdcall; 584 | begin 585 | result := -1; 586 | end; 587 | 588 | function MirrorUnmount(var DokanFileInfo: TDokanFileInfo): Integer; stdcall; 589 | begin 590 | result := 0; 591 | DbgPrint('Unmount'); 592 | end; 593 | 594 | // Global vars 595 | var 596 | g_DokanOperations: TDokanOperations = ( 597 | CreateFile: MirrorCreateFile; 598 | OpenDirectory: MirrorOpenDirectory; 599 | CreateDirectory: MirrorCreateDirectory; 600 | Cleanup: MirrorCleanup; 601 | CloseFile: MirrorCloseFile; 602 | ReadFile: MirrorReadFile; 603 | WriteFile: MirrorWriteFile; 604 | FlushFileBuffers: MirrorFlushFileBuffers; 605 | GetFileInformation: MirrorGetFileInformation; 606 | FindFiles: MirrorFindFiles; 607 | FindFilesWithPattern: nil; 608 | SetFileAttributes: MirrorSetFileAttributes; 609 | SetFileTime: MirrorSetFileTime; 610 | DeleteFile: MirrorDeleteFile; 611 | DeleteDirectory: MirrorDeleteDirectory; 612 | MoveFile: MirrorMoveFile; 613 | SetEndOfFile: MirrorSetEndOfFile; 614 | SetAllocationSize: nil; 615 | LockFile: MirrorLockFile; 616 | UnlockFile: MirrorUnlockFile; 617 | GetFileSecurity: nil; 618 | SetFileSecurity: nil; 619 | GetDiskFreeSpace: nil; 620 | GetVolumeInformation: nil; 621 | Unmount: MirrorUnmount 622 | ); 623 | 624 | g_DokanOptions: TDokanOptions = ( 625 | Version : 0; 626 | ThreadCount: 0; 627 | Options: 0; 628 | GlobalContext: 0; 629 | MountPoint: #0; 630 | ); 631 | 632 | // Utilities routines 633 | procedure DbgPrint(const Message: string); overload; 634 | begin 635 | if (g_DokanOptions.Options and DOKAN_OPTION_DEBUG) = DOKAN_OPTION_DEBUG then 636 | begin 637 | if (g_DokanOptions.Options and DOKAN_OPTION_STDERR) = DOKAN_OPTION_STDERR then 638 | Writeln(ErrOutput,Message) 639 | else 640 | Writeln(Message) 641 | end; 642 | end; 643 | 644 | procedure DbgPrint(const Format: string; const Args: array of const); overload; 645 | begin 646 | if (g_DokanOptions.Options and DOKAN_OPTION_DEBUG) = DOKAN_OPTION_DEBUG then 647 | begin 648 | if (g_DokanOptions.Options and DOKAN_OPTION_STDERR) = DOKAN_OPTION_STDERR then 649 | Writeln(ErrOutput,SysUtils.Format(Format,Args)) 650 | else 651 | Writeln(SysUtils.Format(Format,Args)) 652 | end; 653 | end; 654 | 655 | function CharInSet(C: Char; const CharSet: TSysCharSet): Boolean; 656 | begin 657 | Result := c in CharSet 658 | end; 659 | 660 | // Main procedure 661 | procedure Main; 662 | var 663 | i: Integer; 664 | 665 | function FindSwitch(const s: string; t: array of Char): Integer; 666 | var 667 | i: Integer; 668 | c: Char; 669 | begin 670 | if (Length(s) = 2) and CharInSet(s[1],['/','-','\']) then 671 | begin 672 | c := UpCase(s[2]); 673 | for i:=Low(t) to High(t) do 674 | if t[i] = c then 675 | begin 676 | Result := i; 677 | Exit; 678 | end; 679 | end; 680 | Result := Low(t) - 1; 681 | end; 682 | 683 | begin 684 | IsMultiThread := True; 685 | i := 1; 686 | g_DokanOptions.Version := DOKAN_VERSION; 687 | g_DokanOptions.ThreadCount := 0; 688 | 689 | while i <= ParamCount do 690 | begin 691 | case FindSwitch(ParamStr(i), ['R','L','T','D','S','N','M','K','A']) of 692 | 0: begin 693 | if (i = ParamCount) or (ParamStr(i+1) = '') then 694 | raise Exception.Create('Missing root directory after /R'); 695 | Inc(i); 696 | end; 697 | 1: begin 698 | if (i = ParamCount) or (Length(ParamStr(i+1)) <> 1) then 699 | raise Exception.Create('Missing drive letter after /L'); 700 | Inc(i); 701 | g_DokanOptions.MountPoint := PWideChar(WideString(ParamStr(i))); 702 | end; 703 | 2: begin 704 | if (i = ParamCount) or (ParamStr(i+1) = '') then 705 | raise Exception.Create('Missing thread count after /T'); 706 | Inc(i); 707 | g_DokanOptions.ThreadCount := StrToInt(ParamStr(i)); 708 | end; 709 | 3: g_DokanOptions.Options := g_DokanOptions.Options or DOKAN_OPTION_DEBUG; 710 | 4: g_DokanOptions.Options := g_DokanOptions.Options or DOKAN_OPTION_STDERR; 711 | 5: g_DokanOptions.Options := g_DokanOptions.Options or DOKAN_OPTION_NETWORK; 712 | 6: g_DokanOptions.Options := g_DokanOptions.Options or DOKAN_OPTION_REMOVABLE; 713 | 7: g_DokanOptions.Options := g_DokanOptions.Options or DOKAN_OPTION_KEEP_ALIVE; 714 | 8: g_DokanOptions.Options := g_DokanOptions.Options or DOKAN_OPTION_ALT_STREAM; 715 | end; 716 | Inc(i); 717 | end; 718 | if (g_DokanOptions.MountPoint = WideString('')) then 719 | begin 720 | WriteLn('dipfs v',dipfsVersion,' - mount ipfs to a Windows drive via Dokan'); 721 | WriteLn('Usage: ',ExtractFileName(ParamStr(0))); 722 | WriteLn(' /L DriveLetter (e.g. /L m)'); 723 | WriteLn(' /D (optional, enable debug output)'); 724 | //WriteLn(' /R RootDirectory (e.g. /R C:\test)'); 725 | //WriteLn(' /T ThreadCount (optional, e.g. /T 5)'); 726 | //WriteLn(' /S (optional, use stderr for output)'); 727 | //WriteLn(' /N (optional, use network drive)'); 728 | //WriteLn(' /M (optional, use removable drive)'); 729 | //WriteLn(' /K (optional, keep alive)'); 730 | //WriteLn(' /A (optional, use alternate stream)'); 731 | end else 732 | begin 733 | Writeln('dipfs v',dipfsVersion,': Mounting ipfs ['+g_DokanOptions.MountPoint+':\ipfs\, '+g_DokanOptions.MountPoint+':\ipns\] via http://127.0.0.1:5001/api'); 734 | i := DokanMain(g_DokanOptions, g_DokanOperations); 735 | if i <> DOKAN_SUCCESS then 736 | raise EDokanMainError.Create(i); 737 | end; 738 | end; 739 | 740 | 741 | begin 742 | HTFileMap := THashMap.Create(True,false); 743 | try 744 | Main; 745 | except 746 | on e: Exception do 747 | WriteLn('Error (',e.ClassName,'): ',e.Message); 748 | else 749 | WriteLn('Unspecified error'); 750 | end; 751 | HTFileMap.Free; 752 | end. 753 | 754 | --------------------------------------------------------------------------------