├── .gitignore ├── .idea └── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── COPYRIGHT ├── LICENSE ├── MAPPING.md ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src ├── main └── java │ └── dev │ └── dokan │ └── dokan_java │ ├── AbstractDokanFileSystem.java │ ├── Byteable.java │ ├── DokanCallback.java │ ├── DokanException.java │ ├── DokanFileSystem.java │ ├── DokanFileSystemStub.java │ ├── DokanNativeMethods.java │ ├── DokanOperations.java │ ├── DokanUtils.java │ ├── FileSystemInformation.java │ ├── LibraryNotFoundException.java │ ├── MountFailedException.java │ ├── Mountable.java │ ├── NotImplemented.java │ ├── UnmountFailedException.java │ ├── Unsigned.java │ ├── UnsignedNumbers.java │ ├── Win32FindStreamData.java │ ├── constants │ ├── dokany │ │ ├── MountError.java │ │ └── MountOption.java │ └── microsoft │ │ ├── CreateDisposition.java │ │ ├── CreateDispositions.java │ │ ├── CreateOption.java │ │ ├── CreateOptions.java │ │ ├── CreationDisposition.java │ │ ├── FileAttribute.java │ │ ├── FileSystemFlag.java │ │ ├── NtStatus.java │ │ ├── NtStatuses.java │ │ ├── Win32ErrorCodes.java │ │ ├── accessmaskflags │ │ ├── BasicAccessMaskFlag.java │ │ ├── DirectoryAccessMaskFlag.java │ │ └── FileAccessMaskFlag.java │ │ └── filesecurity │ │ ├── AccessControlEntryFlag.java │ │ ├── AccessControlEntryType.java │ │ ├── SecurityDescriptorControlFlag.java │ │ └── SidIdentifierAuthority.java │ ├── examples │ ├── DirListingFileSystem.java │ └── MirrorFileSystem.java │ ├── masking │ ├── EnumInteger.java │ ├── IntegerConvertible.java │ ├── MaskValueEnum.java │ ├── MaskValueSet.java │ └── MaskValueSetImpl.java │ └── structure │ ├── ByHandleFileInformation.java │ ├── DokanAccessState.java │ ├── DokanControl.java │ ├── DokanFileInfo.java │ ├── DokanIOSecurityContext.java │ ├── DokanOptions.java │ ├── UnicodeString.java │ └── filesecurity │ ├── AccessAllowedACE.java │ ├── AccessControlEntry.java │ ├── AccessControlList.java │ ├── SecurityIdentifier.java │ └── SelfRelativeSecurityDescriptor.java └── test └── java └── dev └── dokan └── dokan_java ├── examples └── DirListingFSTest.java ├── masking ├── EnumIntegerTest.java └── MaskValueSetTest.java └── structure └── filesecurity └── SelfRelativeSecurityDescriptorTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | #build directory 2 | build/ 3 | 4 | # IntelliJ IDEA 5 | # User-specific stuff 6 | .idea/**/workspace.xml 7 | .idea/**/tasks.xml 8 | .idea/**/usage.statistics.xml 9 | .idea/**/dictionaries 10 | .idea/**/shelf 11 | 12 | ## Generated files 13 | .idea/**/contentModel.xml 14 | 15 | ## Gradle 16 | .idea/**/gradle.xml 17 | .idea/**/libraries 18 | 19 | # Gradle and Maven with auto-import 20 | # When using Gradle or Maven with auto-import, you should exclude module files, 21 | # since they will be recreated, and may cause churn. Uncomment if using 22 | # auto-import. 23 | .idea/modules.xml 24 | .idea/*.iml 25 | .idea/modules 26 | 27 | # Java 28 | ## Compiled class file 29 | *.class 30 | 31 | ## virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 32 | hs_err_pid* 33 | 34 | ## Package Files # 35 | *.jar 36 | *.war 37 | *.nar 38 | *.ear 39 | *.zip 40 | *.tar.gz 41 | *.rar 42 | 43 | # Misc 44 | ## Log file 45 | *.log 46 | 47 | 48 | # Gradle 49 | # gradle directory 50 | .gradle/* 51 | !gradle/ 52 | 53 | # Mac metadata 54 | .DS_Store -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Dokany-Java is a Java (JNA) wrapper for Dokany 1.x+ releases. 2 | Copyright (C) 2008 Yu Kobayashi http://yukoba.accelart.jp/ 3 | 2015 Simon Herter 4 | 2016 Carlos Ballesteros Velasco http://cballesterosvelasco.es/ 5 | 2016-2017 Jonathan Hult https://jonathanhult.com/ 6 | 2018-2019 Armin Schrenk 7 | 8 | This program is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU Lesser General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU Lesser General Public License for more details. 17 | 18 | You should have received a copy of the GNU Lesser General Public License 19 | along with this program. If not, see . 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /MAPPING.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | | C Data Type | Windows Data Type - Macro | Size (Bytes/bits) | java/JNA Data Type | 3 | |---------------------|---------------------------|-------------------|-----------------------------------| 4 | | char/byte | UCHAR | 1B/8b | byte | 5 | | unsigned short | USHORT | 2B/16b | short (@Unsigned)/WinDef.USHORT | 6 | | unsigned long (int) | ULONG | 4B/32b | int (@Unsigned)/WinDef.ULONG | 7 | | unsigned long (int) | DWORD/AccessMask | 4B/32b | int (@Unsigned)/WinDef.DWORD | 8 | | unsigned __int64 | ULONG64 | 8B/64b | long (@Unsigned)/WinDef.ULONGLONG | 9 | # References 10 | Basic Types/Size: [Web](https://docs.microsoft.com/en-us/cpp/c-language/storage-of-basic-types?view=vs-2019) | [Permalink](https://web.archive.org/web/20200616151823/https://docs.microsoft.com/en-us/cpp/c-language/storage-of-basic-types?view=vs-2019)
11 | Windows Data Types/Mapped C Data Types: [Web](https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types) | [Permalink](https://web.archive.org/web/20200616152122/https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types)
12 | JNA Data Types: [Web](https://java-native-access.github.io/jna/5.5.0/javadoc/com/sun/jna/platform/win32/WinDef.html) | [Permalink](https://web.archive.org/web/20200616212719/https://java-native-access.github.io/jna/5.5.0/javadoc/com/sun/jna/platform/win32/WinDef.html)
-------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Dokan-Java 2 | ====== 3 | 4 | Dokan-Java is a Java wrapper for [Dokany 1.3.0 releases](https://github.com/dokan-dev/dokany/releases) and above. 5 | 6 | ## Introduction 7 | [Dokany](https://github.com/dokan-dev/dokany) is a device driver providing an interface to develop and run your own filesystem on Windows (similar to [FUSE](https://github.com/libfuse/libfuse)). 8 | It is natively written in C/C++. Usually a Java developer, who would like to utilize Dokany would need to write time-consuming and error-prone JNI-Code. Dokan-java closes this gap and provides a Java interface to Dokany's API with additional convenience methods using [JNA](https://github.com/java-native-access/jna) to access Dokany. 9 | Using this project you are able to implement your own filesystem on Windows... in Java! 10 | 11 | ## Runtime Dependencies 12 | dokan-java requires [Java JRE 11](https://jdk.java.net/11/). 13 | 14 | All dependencies can be seen in the dependecies section of [the build file](build.gradle). 15 | The following are the primary dependencies: 16 | 17 | - [JNA](https://github.com/java-native-access/jna) - provides access to [native Dokany functions](https://dokan-dev.github.io/dokany-doc/html/struct_d_o_k_a_n___o_p_e_r_a_t_i_o_n_s.html) 18 | - [JUnit 5](https://junit.org/junit5/) 19 | 20 | ## How to Use 21 | There are 2 ways to directly use dokan-java: 22 | 1. Use a pre-built version 23 | 2. Build it yourself 24 | 25 | ### Pre-Built Version 26 | Release can be found on [bintray](https://bintray.com/infeo/maven/dokan-java). 27 | 28 | The project publishs to jcenter for easy integration with maven or gradle projects. 29 | 30 | #### Maven 31 | 32 | In order to include dokan-java, add jcenter as an additional repository in your `pom.xml` 33 | ```xml 34 | 35 | 36 | jcenter 37 | https://jcenter.bintray.com/ 38 | 39 | 40 | ``` 41 | and add the project as a dependency: 42 | ```xml 43 | 44 | dev.dokan 45 | dokan-java 46 | 1.1.0 47 | 48 | ``` 49 | 50 | #### Gradle 51 | 52 | In order to use dokan-java, add jcenter to your `build.gradle` 53 | ```groovy 54 | repositories { 55 | jcenter() 56 | } 57 | ``` 58 | and add the project as a dependency: 59 | ```groovy 60 | dependencies { 61 | implementation (group:'dev.dokan', name:'dokan-java', version:'1.1.1') 62 | } 63 | ``` 64 | 65 | ### Build Instructions 66 | Prerequisite: [JDK 11](https://jdk.java.net/11/) 67 | 68 | Building a jar using [Gradle](https://gradle.org/): 69 | 1. Open Terminal 70 | 2. Navigate to the root folder of the project 71 | 3. Execute `./gradlew.bat jar` 72 | 73 | To publish to your local Maven repository, execute as the third step `./gradlew.bat publishToMavenLocal` 74 | 75 | ## Examples 76 | An example user filesystems using this library can be found in the examples package [dev.dokan.dokan_java.examples](https://github.com/dokan-dev/dokan-java/tree/develop/src/main/java/dev/dokan/dokan_java/examples). 77 | 78 | ## Contributing 79 | You're encouraged to contribute. 80 | Fork the code and then submit a pull request. 81 | 82 | ## License 83 | This library is licensed under [GNU LGPLv3](LICENSE). 84 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | * 4 | * It describes how to build the dokan-java project. 5 | * 6 | * To publish to the local Maven repository, execute 'gradle publishToMavenLocal' 7 | */ 8 | 9 | plugins { 10 | // Apply the java-library plugin to add support for Java Library 11 | id 'java-library' 12 | id 'maven-publish' 13 | id 'com.jfrog.bintray' version '1.8.4' 14 | } 15 | 16 | tasks.withType(JavaCompile) { 17 | options.incremental = true 18 | options.encoding = 'UTF-8' 19 | sourceCompatibility = JavaVersion.VERSION_11 20 | targetCompatibility = JavaVersion.VERSION_11 21 | options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" 22 | } 23 | 24 | dependencies { 25 | implementation (group:'net.java.dev.jna', name:'jna', version:jnaVersion) 26 | api (group:'net.java.dev.jna', name:'jna-platform', version:jnaVersion) 27 | testImplementation (group:'org.slf4j', name:'slf4j-simple', version:'1.7.25') 28 | testImplementation (group:'org.junit.jupiter', name:'junit-jupiter-api', version:junitVersion) 29 | testRuntimeOnly (group:'org.junit.jupiter', name:'junit-jupiter-engine', version:junitVersion) 30 | } 31 | 32 | // In this section you declare where to find the dependencies of your project 33 | repositories { 34 | mavenCentral() 35 | jcenter() 36 | } 37 | 38 | //Check if the bintray credentials are defined, otherwise set them to dummy values 39 | ext { 40 | if(project.hasProperty('bintrayUsername')){ 41 | bintrayUser = bintrayUsername 42 | } else { 43 | bintrayUser = 'NOT DEFINED' 44 | } 45 | if(project.hasProperty('bintrayApiKey')){ 46 | bintrayKey = bintrayApiKey 47 | } else { 48 | bintrayKey = 'NOT DEFINED' 49 | } 50 | } 51 | 52 | bintray{ 53 | user = bintrayUser 54 | key = bintrayKey 55 | publications = ['release'] 56 | pkg { 57 | version { 58 | name = project.version 59 | desc = project.description 60 | released = new Date() 61 | vcsTag = project.version 62 | gpg { 63 | sign = true 64 | passphrase = System.getenv("PWD") 65 | } 66 | } 67 | repo = 'maven' 68 | name = project.name 69 | licenses = ['LGPL-3.0'] 70 | vcsUrl = scmUrl 71 | } 72 | } 73 | 74 | test { 75 | useJUnitPlatform() 76 | } 77 | 78 | task sourcesJar(type: Jar) { 79 | archiveClassifier = 'sources' 80 | from sourceSets.main.allJava 81 | } 82 | 83 | task javadocJar(type: Jar) { 84 | archiveClassifier = 'javadoc' 85 | from javadoc.destinationDir 86 | } 87 | 88 | task showBintrayInfo() { 89 | description 'Shows your system bintray credentials, if defined, otherwise NOT DEFINED ' 90 | group 'bintray' 91 | doLast() { 92 | println bintrayUser 93 | print bintrayKey 94 | } 95 | } 96 | 97 | task showProjectInfo(){ 98 | doLast() { 99 | println project.name 100 | println project.version 101 | println project.description 102 | } 103 | } 104 | 105 | tasks { 106 | jar { 107 | manifest { 108 | attributes( 109 | 'Implementation-Title': project.name, 110 | 'Implementation-Version': project.version, 111 | 'Specification-Title': project.description, 112 | 'Specification-Version': project.version, 113 | ) 114 | } 115 | } 116 | } 117 | 118 | publishing { 119 | publications { 120 | release(MavenPublication) { 121 | from components.java 122 | groupId group 123 | artifactId artifact 124 | artifact sourcesJar 125 | artifact javadocJar 126 | pom { 127 | name = project.name 128 | description = project.description 129 | url = scmUrl 130 | licenses { 131 | license { 132 | name = licenseName 133 | url = licenseUrl 134 | distribution = "repo" 135 | } 136 | } 137 | developers { 138 | developer { 139 | id = maintainerId 140 | name = maintainer 141 | email = maintainerMail 142 | timezone = '+1' 143 | } 144 | } 145 | scm { 146 | connection = 'scm:git:git://github.com/dokan-dev/dokan-java.git' 147 | url = scmUrl 148 | } 149 | } 150 | } 151 | } 152 | } 153 | 154 | javadoc { 155 | if(JavaVersion.current().isJava9Compatible()) { 156 | options.addBooleanOption('html5', true) 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | name=dokan-java 2 | version=1.2.0-SNAPSHOT 3 | group=dev.dokan 4 | artifact=dokan-java 5 | description=A Java wrapper for Dokany (https://dokan-dev.github.io/) 6 | 7 | scmUrl=https://github.com/dokan-dev/dokan-java 8 | 9 | #maintainer 10 | maintainer=Armin Schrenk 11 | maintainerId=infeo 12 | maintainerMail=armin.schrenk@zoho.eu 13 | 14 | 15 | #license 16 | licenseName=GNU Lesser General Public License, Version 3.0 17 | licenseUrl=https://www.gnu.org/licenses/lgpl-3.0.en.html 18 | 19 | #lib versions 20 | jnaVersion=5.5.0 21 | slf4jVersion=1.7.30 22 | junitVersion =5.6.0 23 | 24 | #using the build cache 25 | org.gradle.caching=true 26 | 27 | #more than one jvm is spawned to build several projects in parallel 28 | org.gradle.parallel=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dokan-dev/dokan-java/26b96fe7fe0622e6da801c731a6496ef8a3c4b83/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Jan 22 15:25:48 PST 2020 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin or MSYS, switch paths to Windows format before running java 129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=$((i+1)) 158 | done 159 | case $i in 160 | (0) set -- ;; 161 | (1) set -- "$args0" ;; 162 | (2) set -- "$args0" "$args1" ;; 163 | (3) set -- "$args0" "$args1" "$args2" ;; 164 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=$(save "$@") 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 184 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 185 | cd "$(dirname "$0")" 186 | fi 187 | 188 | exec "$JAVACMD" "$@" 189 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 34 | 35 | @rem Find java.exe 36 | if defined JAVA_HOME goto findJavaFromJavaHome 37 | 38 | set JAVA_EXE=java.exe 39 | %JAVA_EXE% -version >NUL 2>&1 40 | if "%ERRORLEVEL%" == "0" goto init 41 | 42 | echo. 43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 44 | echo. 45 | echo Please set the JAVA_HOME variable in your environment to match the 46 | echo location of your Java installation. 47 | 48 | goto fail 49 | 50 | :findJavaFromJavaHome 51 | set JAVA_HOME=%JAVA_HOME:"=% 52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 53 | 54 | if exist "%JAVA_EXE%" goto init 55 | 56 | echo. 57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 58 | echo. 59 | echo Please set the JAVA_HOME variable in your environment to match the 60 | echo location of your Java installation. 61 | 62 | goto fail 63 | 64 | :init 65 | @rem Get command-line arguments, handling Windows variants 66 | 67 | if not "%OS%" == "Windows_NT" goto win9xME_args 68 | 69 | :win9xME_args 70 | @rem Slurp the command line arguments. 71 | set CMD_LINE_ARGS= 72 | set _SKIP=2 73 | 74 | :win9xME_args_slurp 75 | if "x%~1" == "x" goto execute 76 | 77 | set CMD_LINE_ARGS=%* 78 | 79 | :execute 80 | @rem Setup the command line 81 | 82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 83 | 84 | @rem Execute Gradle 85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 86 | 87 | :end 88 | @rem End local scope for the variables with windows NT shell 89 | if "%ERRORLEVEL%"=="0" goto mainEnd 90 | 91 | :fail 92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 93 | rem the _cmd.exe /c_ return code! 94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 95 | exit /b 1 96 | 97 | :mainEnd 98 | if "%OS%"=="Windows_NT" endlocal 99 | 100 | :omega 101 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* rootProject.name = 'dokan-java' 2 | * This file was generated by the Gradle 'init' task. 3 | * 4 | * The settings file is used to specify which projects to include in your build. 5 | * 6 | * Detailed information about configuring a multi-project build in Gradle can be found 7 | * in the user guide at https://docs.gradle.org/4.10.2/userguide/multi_project_builds.html 8 | */ 9 | 10 | rootProject.name = 'dokan-java' -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/AbstractDokanFileSystem.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java; 2 | 3 | 4 | import com.sun.jna.CallbackThreadInitializer; 5 | import com.sun.jna.Native; 6 | import com.sun.jna.Pointer; 7 | import com.sun.jna.WString; 8 | import com.sun.jna.ptr.IntByReference; 9 | import dev.dokan.dokan_java.constants.dokany.MountError; 10 | import dev.dokan.dokan_java.constants.dokany.MountOption; 11 | import dev.dokan.dokan_java.masking.MaskValueSet; 12 | import dev.dokan.dokan_java.structure.DokanControl; 13 | import dev.dokan.dokan_java.structure.DokanOptions; 14 | 15 | import java.lang.reflect.Method; 16 | import java.nio.file.Path; 17 | import java.util.Arrays; 18 | import java.util.List; 19 | import java.util.Set; 20 | import java.util.concurrent.CompletableFuture; 21 | import java.util.concurrent.TimeUnit; 22 | import java.util.concurrent.TimeoutException; 23 | import java.util.concurrent.atomic.AtomicBoolean; 24 | import java.util.stream.Collectors; 25 | 26 | 27 | /** 28 | * TODO: Add Description to this class 29 | */ 30 | public abstract class AbstractDokanFileSystem implements DokanFileSystem { 31 | 32 | private static final int TIMEOUT = 3000; 33 | private static final CallbackThreadInitializer DEFAULT_CALLBACK_THREAD_INITIALIZER = new CallbackThreadInitializer(); 34 | 35 | protected final FileSystemInformation fileSystemInformation; 36 | protected final DokanOperations dokanOperations; 37 | protected Path mountPoint; 38 | protected String volumeName; 39 | protected int volumeSerialnumber; 40 | protected DokanOptions dokanOptions; 41 | 42 | private final AtomicBoolean isMounted; 43 | private Set notImplementedMethods; 44 | 45 | public AbstractDokanFileSystem(FileSystemInformation fileSystemInformation) { 46 | this.fileSystemInformation = fileSystemInformation; 47 | this.isMounted = new AtomicBoolean(false); 48 | this.dokanOperations = new DokanOperations(); 49 | init(dokanOperations); 50 | } 51 | 52 | private void init(DokanOperations dokanOperations) { 53 | notImplementedMethods = Arrays.stream(getClass().getMethods()) 54 | .filter(method -> method.getAnnotation(NotImplemented.class) != null) 55 | .map(Method::getName) 56 | .collect(Collectors.toSet()); 57 | 58 | if (isImplemented("zwCreateFile")) { 59 | dokanOperations.setZwCreateFile(this::zwCreateFile); 60 | Native.setCallbackThreadInitializer(dokanOperations.ZwCreateFile, DEFAULT_CALLBACK_THREAD_INITIALIZER); 61 | } 62 | if (isImplemented("cleanup")) { 63 | dokanOperations.setCleanup(this::cleanup); 64 | Native.setCallbackThreadInitializer(dokanOperations.Cleanup, DEFAULT_CALLBACK_THREAD_INITIALIZER); 65 | } 66 | if (isImplemented("closeFile")) { 67 | dokanOperations.setCloseFile(this::closeFile); 68 | Native.setCallbackThreadInitializer(dokanOperations.CloseFile, DEFAULT_CALLBACK_THREAD_INITIALIZER); 69 | } 70 | if (isImplemented("readFile")) { 71 | dokanOperations.setReadFile(this::readFile); 72 | Native.setCallbackThreadInitializer(dokanOperations.ReadFile, DEFAULT_CALLBACK_THREAD_INITIALIZER); 73 | } 74 | if (isImplemented("writeFile")) { 75 | dokanOperations.setWriteFile(this::writeFile); 76 | Native.setCallbackThreadInitializer(dokanOperations.WriteFile, DEFAULT_CALLBACK_THREAD_INITIALIZER); 77 | } 78 | if (isImplemented("flushFileBuffer")) { 79 | dokanOperations.setFlushFileBuffers(this::flushFileBuffers); 80 | Native.setCallbackThreadInitializer(dokanOperations.FlushFileBuffers, DEFAULT_CALLBACK_THREAD_INITIALIZER); 81 | } 82 | if (isImplemented("getFileInformation")) { 83 | dokanOperations.setGetFileInformation(this::getFileInformation); 84 | Native.setCallbackThreadInitializer(dokanOperations.GetFileInformation, DEFAULT_CALLBACK_THREAD_INITIALIZER); 85 | } 86 | if (isImplemented("findFiles")) { 87 | dokanOperations.setFindFiles(this::findFiles); 88 | Native.setCallbackThreadInitializer(dokanOperations.FindFiles, DEFAULT_CALLBACK_THREAD_INITIALIZER); 89 | } 90 | if (isImplemented("findFilesWithPattern")) { 91 | dokanOperations.setFindFilesWithPattern(this::findFilesWithPattern); 92 | Native.setCallbackThreadInitializer(dokanOperations.FindFilesWithPattern, DEFAULT_CALLBACK_THREAD_INITIALIZER); 93 | } 94 | if (isImplemented("setFileAttributes")) { 95 | dokanOperations.setSetFileAttributes(this::setFileAttributes); 96 | Native.setCallbackThreadInitializer(dokanOperations.SetFileAttributes, DEFAULT_CALLBACK_THREAD_INITIALIZER); 97 | } 98 | if (isImplemented("setFileTime")) { 99 | dokanOperations.setSetFileTime(this::setFileTime); 100 | Native.setCallbackThreadInitializer(dokanOperations.SetFileTime, DEFAULT_CALLBACK_THREAD_INITIALIZER); 101 | } 102 | if (isImplemented("deleteFile")) { 103 | dokanOperations.setDeleteFile(this::deleteFile); 104 | Native.setCallbackThreadInitializer(dokanOperations.DeleteFile, DEFAULT_CALLBACK_THREAD_INITIALIZER); 105 | } 106 | if (isImplemented("deleteDirectory")) { 107 | dokanOperations.setDeleteDirectory(this::deleteDirectory); 108 | Native.setCallbackThreadInitializer(dokanOperations.DeleteDirectory, DEFAULT_CALLBACK_THREAD_INITIALIZER); 109 | } 110 | if (isImplemented("moveFile")) { 111 | dokanOperations.setMoveFile(this::moveFile); 112 | Native.setCallbackThreadInitializer(dokanOperations.MoveFile, DEFAULT_CALLBACK_THREAD_INITIALIZER); 113 | } 114 | if (isImplemented("setEndOfFile")) { 115 | dokanOperations.setSetEndOfFile(this::setEndOfFile); 116 | Native.setCallbackThreadInitializer(dokanOperations.SetEndOfFile, DEFAULT_CALLBACK_THREAD_INITIALIZER); 117 | } 118 | if (isImplemented("setAllocationSize")) { 119 | dokanOperations.setSetAllocationSize(this::setAllocationSize); 120 | Native.setCallbackThreadInitializer(dokanOperations.SetAllocationSize, DEFAULT_CALLBACK_THREAD_INITIALIZER); 121 | } 122 | if (isImplemented("lockFile")) { 123 | dokanOperations.setLockFile(this::lockFile); 124 | Native.setCallbackThreadInitializer(dokanOperations.LockFile, DEFAULT_CALLBACK_THREAD_INITIALIZER); 125 | } 126 | if (isImplemented("unlockFile")) { 127 | dokanOperations.setUnlockFile(this::unlockFile); 128 | Native.setCallbackThreadInitializer(dokanOperations.UnlockFile, DEFAULT_CALLBACK_THREAD_INITIALIZER); 129 | } 130 | if (isImplemented("getDiskFreeSpace")) { 131 | dokanOperations.setGetDiskFreeSpace(this::getDiskFreeSpace); 132 | Native.setCallbackThreadInitializer(dokanOperations.GetDiskFreeSpace, DEFAULT_CALLBACK_THREAD_INITIALIZER); 133 | } 134 | if (isImplemented("getVolumeInformation")) { 135 | dokanOperations.setGetVolumeInformation(this::getVolumeInformation); 136 | Native.setCallbackThreadInitializer(dokanOperations.GetVolumeInformation, DEFAULT_CALLBACK_THREAD_INITIALIZER); 137 | } 138 | if (isImplemented("mounted")) { 139 | dokanOperations.setMounted(this::mounted); 140 | Native.setCallbackThreadInitializer(dokanOperations.Mounted, DEFAULT_CALLBACK_THREAD_INITIALIZER); 141 | } 142 | if (isImplemented("unmounted")) { 143 | dokanOperations.setUnmounted(this::unmounted); 144 | Native.setCallbackThreadInitializer(dokanOperations.Unmounted, DEFAULT_CALLBACK_THREAD_INITIALIZER); 145 | } 146 | if (isImplemented("getFileSecurity")) { 147 | dokanOperations.setGetFileSecurity(this::getFileSecurity); 148 | Native.setCallbackThreadInitializer(dokanOperations.GetFileSecurity, DEFAULT_CALLBACK_THREAD_INITIALIZER); 149 | } 150 | if (isImplemented("setFileSecurity")) { 151 | dokanOperations.setSetFileSecurity(this::setFileSecurity); 152 | Native.setCallbackThreadInitializer(dokanOperations.SetFileSecurity, DEFAULT_CALLBACK_THREAD_INITIALIZER); 153 | } 154 | if (isImplemented("findStreams")) { 155 | dokanOperations.setFindStreams(this::findStreams); 156 | Native.setCallbackThreadInitializer(dokanOperations.FindStreams, DEFAULT_CALLBACK_THREAD_INITIALIZER); 157 | } 158 | } 159 | 160 | private boolean isImplemented(String funcName) { 161 | return !notImplementedMethods.contains(funcName); 162 | } 163 | 164 | /** 165 | * The general mount method. If the underlying system supports shutdown hooks, one is installed in case the JVM is shutting down and the filesystem is still mounted. 166 | * 167 | * @param mountPoint path pointing to an empty directory or unused drive letter 168 | * @param volumeName the displayed name of the volume (only important when a drive letter is used as a mount point) 169 | * @param volumeSerialnumber the serial number of the volume (only important when a drive letter is used as a mount point) 170 | * @param blocking if true the mount and further file system calls are foreground operations and thus will block this thread. To unmount the device you have to use the dokanctl.exe tool. 171 | * @param timeout timeout after which a not processed file system call is canceled and the volume is unmounted 172 | * @param allocationUnitSize the size of the smallest allocatable space in bytes 173 | * @param sectorSize the sector size 174 | * @param UNCName 175 | * @param threadCount the number of threads spawned for processing filesystem calls 176 | * @param options an {@link MaskValueSet} containing {@link MountOption}s 177 | */ 178 | @Override 179 | public final synchronized void mount(Path mountPoint, String volumeName, int volumeSerialnumber, boolean blocking, @Unsigned int timeout, @Unsigned int allocationUnitSize, @Unsigned int sectorSize, String UNCName, @Unsigned short threadCount, MaskValueSet options) { 180 | this.dokanOptions = new DokanOptions(mountPoint.toString(), threadCount, options, UNCName, timeout, allocationUnitSize, sectorSize); 181 | this.mountPoint = mountPoint; 182 | this.volumeName = volumeName; 183 | this.volumeSerialnumber = volumeSerialnumber; 184 | 185 | try { 186 | int mountStatus; 187 | 188 | if (DokanUtils.canHandleShutdownHooks()) { 189 | Runtime.getRuntime().addShutdownHook(new Thread(this::unmount)); 190 | } 191 | 192 | if (blocking) { 193 | mountStatus = execMount(dokanOptions); 194 | } else { 195 | try { 196 | mountStatus = CompletableFuture 197 | .supplyAsync(() -> execMount(dokanOptions)) 198 | .get(TIMEOUT, TimeUnit.MILLISECONDS); 199 | } catch (TimeoutException e) { 200 | // ok 201 | mountStatus = 0; 202 | } 203 | isMounted.set(true); 204 | } 205 | if (mountStatus < 0) { 206 | throw new RuntimeException("Negative result of mount operation. Code" + mountStatus + " -- " + MountError.fromInt(mountStatus).getDescription()); 207 | } 208 | } catch (UnsatisfiedLinkError | Exception e) { 209 | throw new MountFailedException("Unable to mount filesystem.", e); 210 | } 211 | } 212 | 213 | /** 214 | * Additional method for easy mounting with a lot of default values 215 | * 216 | * @param mountPoint 217 | * @param mountOptions 218 | */ 219 | public void mount(Path mountPoint, MaskValueSet mountOptions) { 220 | String uncName = null; 221 | @Unsigned short threadCount = 5; 222 | @Unsigned int timeout = 3000; 223 | @Unsigned int allocationUnitSize = 4096; 224 | @Unsigned int sectorsize = 512; 225 | String volumeName = "DOKAN"; 226 | int volumeSerialnumber = 30975; 227 | mount(mountPoint, volumeName, volumeSerialnumber, false, timeout, allocationUnitSize, sectorsize, uncName, threadCount, mountOptions); 228 | } 229 | 230 | private int execMount(DokanOptions dokanOptions) { 231 | return DokanNativeMethods.DokanMain(dokanOptions, this.dokanOperations); 232 | } 233 | 234 | @Override 235 | public final synchronized void unmount() { 236 | if (!volumeIsStillMounted()) { 237 | isMounted.set(false); 238 | } 239 | 240 | if (isMounted.get()) { 241 | if (DokanNativeMethods.DokanRemoveMountPoint(new WString(mountPoint.toAbsolutePath().toString()))) { 242 | isMounted.set(false); 243 | } else { 244 | throw new UnmountFailedException("Unmount of " + volumeName + "(" + mountPoint + ") failed. Try again, shut down JVM or use `dokanctl.exe to unmount manually."); 245 | } 246 | } 247 | } 248 | 249 | private boolean volumeIsStillMounted() { 250 | char[] mntPtCharArray = mountPoint.toAbsolutePath().toString().toCharArray(); 251 | IntByReference lengthPointer = new IntByReference(); 252 | Pointer startOfList = DokanNativeMethods.DokanGetMountPointList(false, lengthPointer); 253 | 254 | @Unsigned int length = lengthPointer.getValue(); 255 | List list = DokanControl.getDokanControlList(startOfList, length); 256 | // It is not enough that the entry.MountPoint contains the actual mount point. It also has to ends afterwards. 257 | boolean mountPointInList = list.stream().anyMatch(entry -> 258 | Arrays.equals(entry.MountPoint, 12, 12 + mntPtCharArray.length, mntPtCharArray, 0, mntPtCharArray.length) 259 | && (entry.MountPoint.length == 12 + mntPtCharArray.length || entry.MountPoint[12 + mntPtCharArray.length] == '\0')); 260 | DokanNativeMethods.DokanReleaseMountPointList(startOfList); 261 | return mountPointInList; 262 | } 263 | 264 | 265 | @Override 266 | public void close() { 267 | unmount(); 268 | } 269 | 270 | } 271 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/Byteable.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java; 2 | 3 | /** 4 | * A byteable object can be converted into an array of Bytes. 5 | */ 6 | public interface Byteable { 7 | 8 | /** 9 | * Computes the byte representation of the object. 10 | * 11 | * @return array of bytes 12 | */ 13 | byte[] toByteArray(); 14 | 15 | /** 16 | * Function to retrieve the length of the byte array needed to represent the object. 17 | * 18 | * @return the size of byte array 19 | */ 20 | int sizeOfByteArray(); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/DokanCallback.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java; 2 | 3 | 4 | import com.sun.jna.win32.StdCallLibrary; 5 | 6 | 7 | public interface DokanCallback extends StdCallLibrary.StdCallCallback { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/DokanException.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java; 2 | 3 | public final class DokanException extends RuntimeException { 4 | 5 | private final int errorCode; 6 | 7 | public DokanException(Exception e) { 8 | super(e); 9 | this.errorCode = Integer.MIN_VALUE; 10 | } 11 | 12 | public DokanException(String message) { 13 | super(message); 14 | this.errorCode = Integer.MIN_VALUE; 15 | } 16 | public DokanException(String message, int errorCode) { 17 | super(message); 18 | this.errorCode = errorCode; 19 | } 20 | 21 | public DokanException(Throwable cause, int errorCode) { 22 | super(cause); 23 | this.errorCode = errorCode; 24 | } 25 | 26 | public DokanException(String message, Throwable cause) { 27 | super(message, cause); 28 | this.errorCode = Integer.MIN_VALUE; 29 | } 30 | 31 | public DokanException(String message, Throwable cause, int errorCode) { 32 | super(message, cause); 33 | this.errorCode = errorCode; 34 | } 35 | 36 | public int getErrorCode() { 37 | return errorCode; 38 | } 39 | 40 | /** 41 | * Use #getErrorCode() instead. 42 | * @return error code. 43 | */ 44 | @Deprecated 45 | public int getValue() { 46 | return errorCode; 47 | } 48 | 49 | 50 | } -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/DokanFileSystemStub.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java; 2 | 3 | import dev.dokan.dokan_java.constants.microsoft.accessmaskflags.BasicAccessMaskFlag; 4 | import dev.dokan.dokan_java.structure.ByHandleFileInformation; 5 | import dev.dokan.dokan_java.structure.DokanFileInfo; 6 | import com.sun.jna.Pointer; 7 | import com.sun.jna.WString; 8 | import com.sun.jna.platform.win32.WinBase; 9 | import com.sun.jna.ptr.IntByReference; 10 | import com.sun.jna.ptr.LongByReference; 11 | import dev.dokan.dokan_java.structure.DokanIOSecurityContext; 12 | 13 | 14 | public class DokanFileSystemStub extends AbstractDokanFileSystem { 15 | 16 | public DokanFileSystemStub(FileSystemInformation fileSystemInformation) { 17 | super(fileSystemInformation); 18 | } 19 | 20 | /** 21 | * {@inheritDoc} 22 | * 23 | *

24 | * Remark: 25 | * When your filesystem uses the win32 codes (i.e. setting usesKernelFlagsAndCodes to false), some of the parameters are different form the description: 26 | *

27 | *
    28 | *
  1. rawDesiredAccess represents the generic access mask of {@link BasicAccessMaskFlag} with specific one
  2. 29 | *
  3. rawFileAttributes contains besides the file attributes also file flags (see Microsoft documentation of CreateFile)
  4. 30 | *
  5. rawCreateDisposition represents an {@link dev.dokan.dokan_java.constants.microsoft.CreationDisposition} value
  6. 31 | *
32 | */ 33 | @Override 34 | @NotImplemented 35 | public int zwCreateFile(WString rawPath, DokanIOSecurityContext securityContext, int rawDesiredAccess, int rawFileAttributes, int rawShareAccess, int rawCreateDisposition, int rawCreateOptions, DokanFileInfo dokanFileInfo) { 36 | return 0; 37 | } 38 | 39 | @Override 40 | @NotImplemented 41 | public void cleanup(WString rawPath, DokanFileInfo dokanFileInfo) { 42 | 43 | } 44 | 45 | @Override 46 | @NotImplemented 47 | public void closeFile(WString rawPath, DokanFileInfo dokanFileInfo) { 48 | 49 | } 50 | 51 | @Override 52 | @NotImplemented 53 | public int readFile(WString rawPath, Pointer rawBuffer, int rawBufferLength, IntByReference rawReadLength, long rawOffset, DokanFileInfo dokanFileInfo) { 54 | return 0; 55 | } 56 | 57 | @Override 58 | @NotImplemented 59 | public int writeFile(WString rawPath, Pointer rawBuffer, int rawNumberOfBytesToWrite, IntByReference rawNumberOfBytesWritten, long rawOffset, DokanFileInfo dokanFileInfo) { 60 | return 0; 61 | } 62 | 63 | @Override 64 | @NotImplemented 65 | public int flushFileBuffers(WString rawPath, DokanFileInfo dokanFileInfo) { 66 | return 0; 67 | } 68 | 69 | @Override 70 | @NotImplemented 71 | public int getFileInformation(WString fileName, ByHandleFileInformation handleFileInfo, DokanFileInfo dokanFileInfo) { 72 | return 0; 73 | } 74 | 75 | @Override 76 | @NotImplemented 77 | public int findFiles(WString rawPath, DokanOperations.FillWin32FindData rawFillFindData, DokanFileInfo dokanFileInfo) { 78 | return 0; 79 | } 80 | 81 | @Override 82 | @NotImplemented 83 | public int findFilesWithPattern(WString fileName, WString searchPattern, DokanOperations.FillWin32FindData rawFillFindData, DokanFileInfo dokanFileInfo) { 84 | return 0; 85 | } 86 | 87 | @Override 88 | @NotImplemented 89 | public int setFileAttributes(WString rawPath, int rawAttributes, DokanFileInfo dokanFileInfo) { 90 | return 0; 91 | } 92 | 93 | @Override 94 | @NotImplemented 95 | public int setFileTime(WString rawPath, WinBase.FILETIME rawCreationTime, WinBase.FILETIME rawLastAccessTime, WinBase.FILETIME rawLastWriteTime, DokanFileInfo dokanFileInfo) { 96 | return 0; 97 | } 98 | 99 | @Override 100 | @NotImplemented 101 | public int deleteFile(WString rawPath, DokanFileInfo dokanFileInfo) { 102 | return 0; 103 | } 104 | 105 | @Override 106 | @NotImplemented 107 | public int deleteDirectory(WString rawPath, DokanFileInfo dokanFileInfo) { 108 | return 0; 109 | } 110 | 111 | @Override 112 | @NotImplemented 113 | public int moveFile(WString rawPath, WString rawNewFileName, boolean rawReplaceIfExisting, DokanFileInfo dokanFileInfo) { 114 | return 0; 115 | } 116 | 117 | @Override 118 | @NotImplemented 119 | public int setEndOfFile(WString rawPath, long rawByteOffset, DokanFileInfo dokanFileInfo) { 120 | return 0; 121 | } 122 | 123 | @Override 124 | @NotImplemented 125 | public int setAllocationSize(WString rawPath, long rawLength, DokanFileInfo dokanFileInfo) { 126 | return 0; 127 | } 128 | 129 | @Override 130 | @NotImplemented 131 | public int lockFile(WString rawPath, long rawByteOffset, long rawLength, DokanFileInfo dokanFileInfo) { 132 | return 0; 133 | } 134 | 135 | @Override 136 | @NotImplemented 137 | public int unlockFile(WString rawPath, long rawByteOffset, long rawLength, DokanFileInfo dokanFileInfo) { 138 | return 0; 139 | } 140 | 141 | @Override 142 | @NotImplemented 143 | public int getDiskFreeSpace(LongByReference freeBytesAvailable, LongByReference totalNumberOfBytes, LongByReference totalNumberOfFreeBytes, DokanFileInfo dokanFileInfo) { 144 | return 0; 145 | } 146 | 147 | @Override 148 | @NotImplemented 149 | public int getVolumeInformation(Pointer rawVolumeNameBuffer, int rawVolumeNameSize, IntByReference rawVolumeSerialNumber, IntByReference rawMaximumComponentLength, IntByReference rawFileSystemFlags, Pointer rawFileSystemNameBuffer, int rawFileSystemNameSize, DokanFileInfo dokanFileInfo) { 150 | return 0; 151 | } 152 | 153 | @Override 154 | @NotImplemented 155 | public int mounted(DokanFileInfo dokanFileInfo) { 156 | return 0; 157 | } 158 | 159 | @Override 160 | @NotImplemented 161 | public int unmounted(DokanFileInfo dokanFileInfo) { 162 | return 0; 163 | } 164 | 165 | @Override 166 | @NotImplemented 167 | public int getFileSecurity(WString rawPath, int rawSecurityInformation, Pointer rawSecurityDescriptor, int rawSecurityDescriptorLength, IntByReference rawSecurityDescriptorLengthNeeded, DokanFileInfo dokanFileInfo) { 168 | return 0; 169 | } 170 | 171 | @Override 172 | @NotImplemented 173 | public int setFileSecurity(WString rawPath, int rawSecurityInformation, Pointer rawSecurityDescriptor, int rawSecurityDescriptorLength, DokanFileInfo dokanFileInfo) { 174 | return 0; 175 | } 176 | 177 | @Override 178 | @NotImplemented 179 | public void fillWin32FindData(WinBase.WIN32_FIND_DATA rawFillFindData, DokanFileInfo dokanFileInfo) { 180 | 181 | } 182 | 183 | @Override 184 | @NotImplemented 185 | public int findStreams(WString rawPath, DokanOperations.FillWin32FindStreamData rawFillFindData, DokanFileInfo dokanFileInfo) { 186 | return 0; 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/DokanNativeMethods.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java; 2 | 3 | 4 | import com.sun.jna.Native; 5 | import com.sun.jna.Pointer; 6 | import com.sun.jna.WString; 7 | import com.sun.jna.platform.win32.WinNT; 8 | import com.sun.jna.ptr.IntByReference; 9 | import com.sun.jna.win32.StdCallLibrary; 10 | import dev.dokan.dokan_java.constants.dokany.MountError; 11 | import dev.dokan.dokan_java.structure.DokanControl; 12 | import dev.dokan.dokan_java.structure.DokanFileInfo; 13 | import dev.dokan.dokan_java.structure.DokanOptions; 14 | 15 | 16 | /** 17 | * Native API to the kernel Dokan driver. 18 | */ 19 | public class DokanNativeMethods implements StdCallLibrary { 20 | 21 | private static final String DOKAN_DLL = "dokan1"; 22 | 23 | private static final short MINIMUM_REQUIRED_DOKAN_VERSION = 130; 24 | 25 | 26 | static { 27 | Native.register(DOKAN_DLL); 28 | } 29 | 30 | private DokanNativeMethods() { 31 | 32 | } 33 | 34 | /** 35 | * Returns the hard coded minimum required dokan driver version that is needed by this library. 36 | *

37 | * The version is returned in "Dokan" style, i.e. without any dots. 38 | * 39 | * @return the minimum required dokan version without dots. 40 | */ 41 | public static short getMinimumRequiredDokanVersion() { 42 | return MINIMUM_REQUIRED_DOKAN_VERSION; 43 | } 44 | 45 | /** 46 | * Mount a new Dokan Volume. This function blocks until the device is unmounted. 47 | * If the mount fails, it will directly return {@link MountError}. 48 | * 49 | * @param options A {@link DokanOptions} object that describes the mount. 50 | * @param operations Instance of {@link DokanOperations} that will be called for each file system request made by the kernel. 51 | * @return a status code indicating the outcome. For the possible values, see {@link MountError}. 52 | */ 53 | static native int DokanMain(DokanOptions options, DokanOperations operations); 54 | 55 | /** 56 | * Get the version of Dokan. 57 | * 58 | *

The returned long value is the version number without the dots.

59 | * 60 | * @return The version of Dokan 61 | */ 62 | static native long DokanVersion(); 63 | 64 | /** 65 | * Get the version of the Dokan driver. 66 | * 67 | *

The returned long value is the version number without the dots.

68 | * 69 | * @return The version of Dokan driver. 70 | */ 71 | static native long DokanDriverVersion(); 72 | 73 | /** 74 | * Unmount a Dokan device from a driver letter. 75 | * 76 | * @param driveLetter Driver letter to unmount. 77 | * @return {@code true} if device was unmounted or {@code false} in case of failure or device not found. 78 | */ 79 | static native boolean DokanUnmount(char driveLetter); 80 | 81 | /** 82 | * Unmount a Dokan device from a mount point 83 | * 84 | * @param mountPoint Mount point to unmount 85 | *
    86 | *
  • Z
  • 87 | *
  • Z:
  • 88 | *
  • Z:\\
  • 89 | *
  • Z:\MyMountPoint
  • 90 | *
91 | * @return {@code true} if device was unmounted or {@code false} in case of failure or device not found. 92 | */ 93 | static native boolean DokanRemoveMountPoint(WString mountPoint); 94 | 95 | 96 | /** 97 | * TODO: Does not work. Why? 98 | * static native boolean DokanRemoveMountPointEx(WString mountPoint, boolean safe); 99 | */ 100 | 101 | /** 102 | * Extends the time out of the current IO operation in driver. 103 | * 104 | * @param timeout Extended time in milliseconds requested. 105 | * @param dokanFileInfo {@link DokanFileInfo} of the operation to extend. 106 | * @return {@code true} if the operation was successful, otherwise false. 107 | */ 108 | static native boolean DokanResetTimeout(long timeout, DokanFileInfo dokanFileInfo); 109 | 110 | /** 111 | * Get the handle to Access Token. 112 | * 113 | *

114 | * This method needs be called in CreateFile callback. 115 | * The caller must call CloseHandle for the returned handle. 116 | *

117 | * 118 | * @param dokanFileInfo {@link DokanFileInfo} of the operation to extend. 119 | * @return A handle to the account token for the user on whose behalf the code is running. 120 | */ 121 | static native WinNT.HANDLE DokanOpenRequestorToken(DokanFileInfo dokanFileInfo); 122 | 123 | /** 124 | * Convert {@link DokanOperations.ZwCreateFile} parameters to CreateFile parameters. 125 | * TODO: Improve documentation 126 | * 127 | * @param desiredAccess 128 | * @param fileAttributes FileAttributes 129 | * @param createOptions CreateOptions 130 | * @param createDisposition CreateDisposition 131 | * @param genericDesiredAccess 132 | * @param outFileAttributesAndFlags 133 | * @param outCreationDisposition 134 | */ 135 | static native void DokanMapKernelToUserCreateFileFlags( 136 | long desiredAccess, 137 | long fileAttributes, 138 | long createOptions, 139 | long createDisposition, 140 | IntByReference genericDesiredAccess, 141 | IntByReference outFileAttributesAndFlags, 142 | IntByReference outCreationDisposition); 143 | 144 | /** 145 | * Checks whether name matches Expression. 146 | *

147 | * Behave like FsRtlIsNameInExpression routine from Microsoft\n 148 | *

149 | * 150 | * 151 | * 152 | * 153 | * 154 | * 155 | * 156 | * 157 | * 158 | * 159 | * 160 | * 163 | * 164 | * 165 | * 168 | * 169 | * 170 | * 173 | * 174 | * 175 | * 178 | * 179 | * 180 | * 183 | * 184 | * 185 | *
Special character handling
CharacterCharacter descriptionEffect
* 161 | * asterisk 162 | * Matches zero or more characters.
? 166 | * question mark 167 | * Matches a single character.
" 171 | * quotation mark, DOS_DOT 172 | * Matches either a period or zero characters beyond the name string.
{@literal >} 176 | * greater than, DOS_QM 177 | * Matches any single character or, upon encountering a period or end of name string, advances the expression to the end of the set of contiguous DOS_QMs.
{@literal <} 181 | * less than, DOS_STAR 182 | * Matches zero or more characters until encountering and matching the final . in the name.
186 | * 187 | * @param expression - Expression, possibly containing any of the above characters 188 | * @param name - Name to check 189 | * @param ignoreCase - Case sensitive or not 190 | * @return {@code true} if name matches the expression, otherwise false 191 | */ 192 | static native boolean DokanIsNameInExpression(WString expression, WString name, boolean ignoreCase); 193 | 194 | /** 195 | * Get active Dokan mount points. 196 | * 197 | *

198 | * Returned array need to be released by calling {@link #DokanReleaseMountPointList}. 199 | *

200 | * 201 | * @param uncOnly - Get only instances that have UNC Name. 202 | * @param nbRead - {@link Unsigned} Number of instances successfully retrieved 203 | * @return a pointer to the start of the allocated array of {@link DokanControl} elemets. 204 | */ 205 | static native Pointer DokanGetMountPointList(boolean uncOnly, @Unsigned IntByReference nbRead); 206 | 207 | /** 208 | * Release Mount point list resources from {@link #DokanGetMountPointList}. 209 | * 210 | *

211 | * After {@link #DokanGetMountPointList} call you will receive a dynamically allocated array of {@link DokanControl}. 212 | * This array needs to be released when no longer needed by calling this function. 213 | *

214 | * 215 | * @param startOfList Pointer to the start of the {@link DokanControl} list. 216 | */ 217 | static native void DokanReleaseMountPointList(Pointer startOfList); 218 | 219 | /** 220 | * Convert Win32 error to NtStatus 221 | * 222 | * @param error - Win32 error to convert 223 | * @return NtStatus associated to the error 224 | * @see kb113996 225 | */ 226 | static native long DokanNtStatusFromWin32(int error); 227 | 228 | /** 229 | * Notify Dokan that a file or a directory has been created. 230 | * 231 | * @param FilePath Absolute path to the file or directory, including the mount-point of the file system. 232 | * @param IsDirectory Indicates if the path is a directory. 233 | * @return {@code true} if notification succeeded. 234 | */ 235 | static native boolean DokanNotifyCreate(WString FilePath, boolean IsDirectory); 236 | 237 | /** 238 | * Notify Dokan that a file or a directory has been deleted. 239 | * 240 | * @param FilePath Absolute path to the file or directory, including the mount-point of the file system. 241 | * @param IsDirectory Indicates if the path was a directory. 242 | * @return {@code true} if notification succeeded. 243 | */ 244 | static native boolean DokanNotifyDelete(WString FilePath, boolean IsDirectory); 245 | 246 | /** 247 | * Notify Dokan that file or directory attributes have changed. 248 | * 249 | * @param FilePath Absolute path to the file or directory, including the mount-point of the file system. 250 | * @return {@code true} if notification succeeded. 251 | */ 252 | static native boolean DokanNotifyUpdate(WString FilePath); 253 | 254 | /** 255 | * Notify Dokan that file or directory extended attributes have changed. 256 | * 257 | * @param FilePath Absolute path to the file or directory, including the mount-point of the file system. 258 | * @return {@code true} if notification succeeded. 259 | */ 260 | static native boolean DokanNotifyXAttrUpdate(WString FilePath); 261 | 262 | /** 263 | * Notify Dokan that a file or a directory has been renamed. 264 | *

265 | * This method supports in-place rename for file/directory within the same parent. 266 | * 267 | * @param OldPath Old, absolute path to the file or directory, including the mount-point of the file system. 268 | * @param NewPath New, absolute path to the file or directory, including the mount-point of the file system. 269 | * @param IsDirectory Indicates if the path is a directory. 270 | * @param IsInSameDirectory Indicates if the file or directory have the same parent directory. 271 | * @return {@code true} if notification succeeded. 272 | */ 273 | static native boolean DokanNotifyRename(WString OldPath, WString NewPath, 274 | boolean IsDirectory, boolean IsInSameDirectory); 275 | } -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/DokanUtils.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java; 2 | 3 | import com.sun.jna.platform.win32.WinBase.FILETIME; 4 | import com.sun.jna.platform.win32.WinNT; 5 | import dev.dokan.dokan_java.constants.microsoft.CreationDisposition; 6 | 7 | import java.util.Date; 8 | 9 | import static dev.dokan.dokan_java.constants.microsoft.CreateDispositions.*; 10 | import static dev.dokan.dokan_java.constants.microsoft.CreateOptions.*; 11 | import static dev.dokan.dokan_java.constants.microsoft.CreationDisposition.*; 12 | 13 | /** 14 | * Utilities to do various operations. 15 | */ 16 | public class DokanUtils { 17 | 18 | private static final boolean winVersionAtLeast8; 19 | 20 | static { 21 | boolean tmp = false; 22 | try { 23 | String s = System.getProperty("os.version"); 24 | if (s.startsWith("10") || s.startsWith("8")) { 25 | tmp = true; 26 | } 27 | } catch (Exception e) { 28 | //NO-OP 29 | } 30 | winVersionAtLeast8 = tmp; 31 | } 32 | 33 | private DokanUtils() { 34 | 35 | } 36 | 37 | public static String trimStrToSize(final String str, final int len) { 38 | return str.substring(0, Math.min(str.length(), len)); 39 | } 40 | 41 | public static FILETIME getTime(final Date date) { 42 | return new FILETIME(date); 43 | } 44 | 45 | public static FILETIME getTime(final long time) { 46 | return getTime(new Date(time)); 47 | } 48 | 49 | public static boolean canHandleShutdownHooks() { 50 | SecurityManager security = System.getSecurityManager(); 51 | if (security == null) { 52 | return true; 53 | } 54 | try { 55 | security.checkPermission(new RuntimePermission("shutdownHooks")); 56 | return true; 57 | } catch (final SecurityException e) { 58 | return false; 59 | } 60 | } 61 | 62 | /** 63 | * Extends the file attributes number with the file flags given in the createOptions. 64 | * 65 | * @param fileAttributes 66 | * @param createOptions 67 | * @return fileAttributes extended with the file flags 68 | */ 69 | public static int addFileFlagsToFileAttributes(int fileAttributes, int createOptions) { 70 | int outFileAttributesAndFlags = fileAttributes; 71 | outFileAttributesAndFlags = DokanMapKernelBit(outFileAttributesAndFlags, createOptions, WinNT.FILE_FLAG_WRITE_THROUGH, FILE_WRITE_THROUGH); 72 | outFileAttributesAndFlags = DokanMapKernelBit(outFileAttributesAndFlags, createOptions, WinNT.FILE_FLAG_SEQUENTIAL_SCAN, FILE_SEQUENTIAL_ONLY); 73 | outFileAttributesAndFlags = DokanMapKernelBit(outFileAttributesAndFlags, createOptions, WinNT.FILE_FLAG_RANDOM_ACCESS, FILE_RANDOM_ACCESS); 74 | outFileAttributesAndFlags = DokanMapKernelBit(outFileAttributesAndFlags, createOptions, WinNT.FILE_FLAG_NO_BUFFERING, FILE_NO_INTERMEDIATE_BUFFERING); 75 | outFileAttributesAndFlags = DokanMapKernelBit(outFileAttributesAndFlags, createOptions, WinNT.FILE_FLAG_OPEN_REPARSE_POINT, FILE_OPEN_REPARSE_POINT); 76 | outFileAttributesAndFlags = DokanMapKernelBit(outFileAttributesAndFlags, createOptions, WinNT.FILE_FLAG_DELETE_ON_CLOSE, FILE_DELETE_ON_CLOSE); 77 | outFileAttributesAndFlags = DokanMapKernelBit(outFileAttributesAndFlags, createOptions, WinNT.FILE_FLAG_BACKUP_SEMANTICS, FILE_OPEN_FOR_BACKUP_INTENT); 78 | 79 | if (winVersionAtLeast8) { 80 | outFileAttributesAndFlags = DokanMapKernelBit(outFileAttributesAndFlags, createOptions, 0x00800000, FILE_SESSION_AWARE); //FILE_FLAG_SESSION_AWARE == 0x00800000 81 | } 82 | 83 | return outFileAttributesAndFlags; 84 | } 85 | 86 | private static int DokanMapKernelBit(int dest, int src, int userBit, int kernelBit) { 87 | if (((src) & (kernelBit)) == (kernelBit)) 88 | return (dest |= userBit); 89 | else 90 | return dest; 91 | } 92 | 93 | /** 94 | * Maps the generic file accesses specific to files and directories to the general generic accesses. 95 | * Copied from dokan.c. 96 | * 97 | * @param fileAccess the 98 | * @return 99 | */ 100 | public static int mapFileGenericAccessToGenericAccess(int fileAccess) { 101 | boolean genericRead = false, genericWrite = false, genericExecute = false, genericAll = false; 102 | int outDesiredAccess = fileAccess; 103 | 104 | if ((outDesiredAccess & WinNT.FILE_GENERIC_READ) == WinNT.FILE_GENERIC_READ) { 105 | outDesiredAccess |= WinNT.GENERIC_READ; 106 | genericRead = true; 107 | } 108 | if ((outDesiredAccess & WinNT.FILE_GENERIC_WRITE) == WinNT.FILE_GENERIC_WRITE) { 109 | outDesiredAccess |= WinNT.GENERIC_WRITE; 110 | genericWrite = true; 111 | } 112 | if ((outDesiredAccess & WinNT.FILE_GENERIC_EXECUTE) == WinNT.FILE_GENERIC_EXECUTE) { 113 | outDesiredAccess |= WinNT.GENERIC_EXECUTE; 114 | genericExecute = true; 115 | } 116 | if ((outDesiredAccess & WinNT.FILE_ALL_ACCESS) == WinNT.FILE_ALL_ACCESS) { 117 | outDesiredAccess |= WinNT.GENERIC_ALL; 118 | genericAll = true; 119 | } 120 | 121 | if (genericRead) 122 | outDesiredAccess &= ~WinNT.FILE_GENERIC_READ; 123 | if (genericWrite) 124 | outDesiredAccess &= ~WinNT.FILE_GENERIC_WRITE; 125 | if (genericExecute) 126 | outDesiredAccess &= ~WinNT.FILE_GENERIC_EXECUTE; 127 | if (genericAll) 128 | outDesiredAccess &= ~WinNT.FILE_ALL_ACCESS; 129 | 130 | return outDesiredAccess; 131 | } 132 | 133 | /** 134 | * Converts the kernel file creation flags to the win32 flags. 135 | * Copied from dokan.c. 136 | * 137 | * @param createDisposition 138 | * @return integer corresponding to an enum in {@link CreationDisposition} 139 | */ 140 | public static int convertCreateDispositionToCreationDispostion(int createDisposition) { 141 | switch (createDisposition) { 142 | case FILE_CREATE: 143 | return CREATE_NEW.intValue(); 144 | case FILE_OPEN: 145 | return OPEN_EXISTING.intValue(); 146 | case FILE_OPEN_IF: 147 | return OPEN_ALWAYS.intValue(); 148 | case FILE_OVERWRITE: 149 | return TRUNCATE_EXISTING.intValue(); 150 | case FILE_SUPERSEDE: 151 | // The documentation isn't clear on the difference between replacing a file 152 | // and truncating it. 153 | // For now we just map it to create/truncate 154 | case FILE_OVERWRITE_IF: 155 | return CREATE_ALWAYS.intValue(); 156 | default: 157 | //TODO: maybe throw an exception 158 | return 0; 159 | } 160 | } 161 | 162 | /** 163 | * Maps Win32 error codes to their respective NTStatus accordingly to ntstatus.i 164 | * 165 | * @param win32Errorcode a valid win32 error code 166 | * @return the corresponding NTStatus value 167 | */ 168 | public static int ntStatusFromWin32ErrorCode(int win32Errorcode) { //FIXME 169 | return (int) DokanNativeMethods.DokanNtStatusFromWin32(win32Errorcode); 170 | } 171 | 172 | } 173 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/FileSystemInformation.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java; 2 | 3 | import dev.dokan.dokan_java.constants.microsoft.FileSystemFlag; 4 | import dev.dokan.dokan_java.masking.MaskValueSet; 5 | 6 | /** 7 | * Supplementary class to bundle information of the filesystem. 8 | *

Mainly used for {@link DokanOperations#GetVolumeInformation} function to have all needed information at one place.

9 | */ 10 | public final class FileSystemInformation { 11 | 12 | public static final int DEFAULT_MAX_COMPONENT_LENGTH = 256; 13 | public static final String DEFAULT_FS_NAME = "NTFS"; 14 | 15 | private final int maxComponentLength; 16 | private final String fileSystemName; 17 | private final MaskValueSet fileSystemFeatures; 18 | 19 | /** 20 | * Provides default values for maxComponentLength and Filesystem name. 21 | * 22 | * @param fileSystemFlags An {@link MaskValueSet} of features the file system supports. For possible values, see the {@link FileSystemFlag} enum. 23 | */ 24 | public FileSystemInformation(MaskValueSet fileSystemFlags) { 25 | this(DEFAULT_MAX_COMPONENT_LENGTH, DEFAULT_FS_NAME, fileSystemFlags); 26 | } 27 | 28 | public FileSystemInformation(final int maxComponentLength, final String fileSystemName, final MaskValueSet fileSystemFeatures) { 29 | this.maxComponentLength = maxComponentLength; 30 | this.fileSystemName = fileSystemName; 31 | this.fileSystemFeatures = fileSystemFeatures; 32 | } 33 | 34 | public int getMaxComponentLength() { 35 | return this.maxComponentLength; 36 | } 37 | 38 | public String getFileSystemName() { 39 | return this.fileSystemName; 40 | } 41 | 42 | public MaskValueSet getFileSystemFeatures() { 43 | return this.fileSystemFeatures; 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | return "VolumeInformation(maxComponentLength=" + this.getMaxComponentLength() + ", fileSystemName=" + this.getFileSystemName() + ", fileSystemFeatures=" + this.getFileSystemFeatures() + ")"; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/LibraryNotFoundException.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java; 2 | 3 | public class LibraryNotFoundException extends RuntimeException { 4 | 5 | public LibraryNotFoundException(String message) { 6 | super(message); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/MountFailedException.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java; 2 | 3 | public class MountFailedException extends RuntimeException { 4 | 5 | public MountFailedException(String msg) { 6 | super(msg); 7 | } 8 | 9 | public MountFailedException(String msg, Throwable cause) { 10 | super(msg, cause); 11 | } 12 | 13 | public MountFailedException(Throwable cause) { 14 | super(cause); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/Mountable.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java; 2 | 3 | import dev.dokan.dokan_java.constants.dokany.MountOption; 4 | import dev.dokan.dokan_java.masking.MaskValueSet; 5 | 6 | import java.nio.file.Path; 7 | 8 | /** 9 | * An object which can be mounted in a filesystem. 10 | * 11 | * @author Armin Schrnek 12 | * @since 2.0 13 | */ 14 | public interface Mountable extends AutoCloseable { 15 | 16 | /** 17 | * Mount this object on a mount point with the given options. 18 | * 19 | * @param mountPoint Path pointing to an empty Directory or unused drive letter 20 | * @param volumeName The displayed name of the volume (only important in combination with a drive letter) 21 | * @param volumeSerialnumber the serial number of the volume 22 | * @param blocking If true the mount and further file system calls are foreground operations and thus will block this thread 23 | * @param timeout Timeout after which a not processed file system call is canceled 24 | * @param allocationUnitSize the size of the smallest allocatable space in bytes 25 | * @param sectorSize the sector size 26 | * @param UNCName 27 | * @param threadCount the number of threads spawned for processing filesystem calls 28 | * @param options an {@link MaskValueSet} containing {@link MountOption}s 29 | */ 30 | void mount(Path mountPoint, String volumeName, int volumeSerialnumber, boolean blocking, @Unsigned int timeout, @Unsigned int allocationUnitSize, @Unsigned int sectorSize, String UNCName, @Unsigned short threadCount, MaskValueSet options); 31 | 32 | /** 33 | * Unmount this object. 34 | */ 35 | void unmount(); 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/NotImplemented.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | import static java.lang.annotation.ElementType.METHOD; 9 | 10 | /** 11 | * If a method is marked with this annotation it won't be registered in DokanOperations struct. 12 | *

13 | * This annotation is not inheritable, so all overridden method will be registered. 14 | *

15 | * The goal of this annotation is performance, if the method is not registered in the DokanOperations struct 16 | * then the native → java call will not be performed. 17 | * 18 | *

This class is a copy of ru.serce.jnrfuse.NotImplemented.

19 | * 20 | * @author Sergey Tselovalnikov 21 | * @since 2.0 22 | */ 23 | @Documented 24 | @Retention(RetentionPolicy.RUNTIME) 25 | @Target(value = METHOD) 26 | public @interface NotImplemented { 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/UnmountFailedException.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java; 2 | 3 | public class UnmountFailedException extends RuntimeException { 4 | 5 | public UnmountFailedException(String msg) { 6 | super(msg); 7 | } 8 | 9 | public UnmountFailedException(String msg, Throwable cause) { 10 | super(msg, cause); 11 | } 12 | 13 | public UnmountFailedException(Throwable cause) { 14 | super(cause); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/Unsigned.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java; 2 | 3 | 4 | import java.lang.annotation.Documented; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | import static java.lang.annotation.ElementType.TYPE_PARAMETER; 10 | import static java.lang.annotation.ElementType.TYPE_USE; 11 | 12 | 13 | /** 14 | * This annotation is used used to indicate that a value with an integer type or an integer type pointer refers to an unsigned value.
15 | * In this case integers are numbers without positions after decimal point. 16 | * (Java's Default integer types being {@code byte, short, int, long} and their corresponding wrappers.) 17 | *

Introduction

18 | * Java stores integer types in Two's complement-Representation. 19 | * Usually numbers represented as Two's complement use the Most-Significant-Bit (MSB) to store the sign of the number. 20 | * If a field is annotated with @Unsigned this rule does not apply! 21 | * Instead the MSB should be considered as part of the number itself, resulting in a doubled storage capacity of the field 22 | * while removing the support for signed values (the field becomes unsigned and the value should only be interpreted as positive.) 23 | * See this table as reference for the resulting differences when interpreting numbers: 24 | * 25 | * 26 | * 27 | * 28 | * 29 | * 30 | * 31 | * 32 | * 33 | * 34 | * 35 | * 36 | * 37 | * 38 | * 39 | * 40 | * 41 | * 42 | * 43 | * 44 | * 45 | * 46 | * 47 | * 48 | * 49 | * 50 | * 51 | * 52 | * 53 | * 54 | * 55 | * 56 | * 57 | * 58 | * 59 | * 60 | * 61 | * 62 | * 63 | * 64 | * 65 | * 66 | * 67 | * 68 | * 69 | *
Signed value (Java Default)Unsigned values (@Unsigned)Bitmask (for a byte)
000000 0000
110000 0001
220000 0010
64640100 0000
-1281281000 0000
-1271291000 0001
-12551111 1111
70 | *

Usage

71 | * As Java only supports signed numbers, the correct interpretation and handling of unsigned numbers must be dealt with by the developer. 72 | * Because of this it's strongly recommended to tag all values that are unsigned as such by using this annotation.
73 | * @Unsigned is defined to {@link Target target} {@link java.lang.annotation.ElementType#TYPE_PARAMETER} and 74 | * {@link java.lang.annotation.ElementType#TYPE_USE} and can therefore be applied to all usages of types (type contexts) 75 | * and parameterized types respectively. See §9.6.4.1 76 | * and §4.11 of "The Java® Language Specification" for 77 | * further reference.
78 | *
79 | * At least the following cases ("minimum usage") should be annotated with @Unsigned to 80 | * guarantee the best quality of code: 81 | *
    82 | *
  • Field declarations: {@code @Unsigned private final int index;} 83 | *
  • Local variable declarations: {@code @Unsigned int i;} 84 | *
  • Parameters: {@code public void remove(@Unsigned int index) {...}} 85 | *
  • Method return types: {@code public @Unsigned int getIndex() {...}} 86 | *

87 | * Additionally it's recommended to annotate any other usage of unsigned types ("advanced usage"), 88 | * especially (but not limited to): 89 | *
    90 | *
  • Parameterized types: {@code List<@Unsigned Integer> indices = new ArrayList<>();} 91 | *
  • Casts: {@code Integer i = (@Unsigned Integer) uInt;} 92 | *
  • Type declarations: {@code public class @Unsigned UInt {...}} 93 | *
  • Extension/Implementation of unsigned types: {@code public class //@Unsigned// Index extends @Unsigned UInt {...}} 94 | *
95 | * Note: Contributions to the dokan-java project must annotate all usages of unsigned types (Minimum usage and advanced usage) 96 | * with @Unsigned. 97 | *

Pitfalls

98 | * If a field is annotated with @Unsigned developers should take extra care handling it as some unsafe operations may lead 99 | * to unexpected results. 100 | * An operation is considered safe if using it with unsigned values yields the same results as using it with signed numbers. 101 | * ("An operation is safe if it can be used the same way when dealing with unsigned values as one would use it with signed numbers.") 102 | * The following (inconclusive) table shows which operations are safe or unsafe and how operations can be dealt with alternatively. 103 | * 104 | * 105 | * 106 | * 107 | * 108 | * 109 | * 110 | * 111 | * 112 | * 113 | * 114 | * 115 | * 116 | * 117 | * 118 | * 119 | * 120 | * 121 | * 122 | * 123 | * 124 | * 125 | * 126 | * 127 | * 128 | * 129 | * 130 | * 131 | * 132 | * 133 | * 134 | * 135 | * 136 | * 137 | * 138 | * 139 | * 140 | * 141 | * 142 | * 143 | * 144 | * 145 | * 146 | * 147 | * 148 | * 149 | * 150 | * 151 | * 152 | * 153 | * 154 | * 155 | * 156 | * 157 | * 158 | *
OperationSafe/UnsafeRecommended course of action
Adding (+)Safe
Subtracting (-)Safe
Multiplication (*)Safe
Division (/)Unsafe{@link UnsignedNumbers#divideUnsigned(int, int)}
Remainder/ModuloUnsafe{@link UnsignedNumbers#remainderUnsigned(int, int)}
Comparision (%)Unsafe{@link UnsignedNumbers#compareUnsigned(int, int)}
PrintingUnsafe{@link UnsignedNumbers#toUnsignedString(int)}
DowncastingSafe{@link UnsignedNumbers#toUnsignedInt(long)}
UpcastingUnsafe{@link UnsignedNumbers#toUnsignedInt(short)}
159 | * Note: Operations that require two numbers (e.g. addition, subtraction) should never be used with a signed 160 | * and an unsigned number as arguments, even if the operation is usually considered safe. 161 | * Doing so can result in heavy computational errors, as seen here:
162 | *
163 | * {@code //Don't do this:}
164 | * {@code byte a = 64; //0100 0000}
165 | * {@code @Unsigned byte b = 64; //0100 0000}
166 | * {@code byte c = a + b; //0100 0000 + 0100 0000}
167 | * {@code //--> c = -128; //1000 0000}
168 | *
169 | * This is a problem for the following reason: Before the addition the status (signed/unsigned) of 170 | * a and b didn't matter (in fact they were equal). As soon as the value exceeds 127 the developer needs to decide 171 | * whether c is signed or unsigned.
172 | * If c is unsigned all future users of c must take care that they 173 | * interpret it correctly. Also a comes from a signed context and could be negative. 174 | * If a's MSB is set to indicate a negative value any computation that considers a unsigned must be wrong 175 | * as it would interpret a as a big positive number instead of a negative number.
176 | * If c is interpreted as signed, the computation is plain wrong (64 + 64 is not -128) because it leads to a number-overflow.
177 | *
178 | * {@code //Instead do this:}
179 | * {@code byte a = 64;}
180 | * {@code @Unsigned byte b = 64;}
181 | * {@code short s = UnsignedNumbers.toUnsignedShort(b);}
182 | * {@code short c = s + a;}
183 | * {@code //--> c = 128;}
184 | * 185 | * @author JaniruTEC 186 | * @see UnsignedNumbers 187 | * @since 2.0 188 | */ 189 | @Documented 190 | @Retention(RetentionPolicy.RUNTIME) 191 | //In Theory TYPE_USE should contain TYPE_PARAMETER, 192 | //but the documentation is so vague that I'm really not sure to be honest 193 | @Target(value = {TYPE_PARAMETER, TYPE_USE}) 194 | public @interface Unsigned { 195 | 196 | } 197 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/UnsignedNumbers.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java; 2 | 3 | 4 | public class UnsignedNumbers { 5 | 6 | private static final short BYTE_MASK = 0xff; 7 | private static final int SHORT_MASK = 0xffff; 8 | private static final long INT_MASK = 0xffffff; 9 | 10 | public static String toUnsignedString(@Unsigned byte value) { 11 | return toUnsignedString(Byte.toUnsignedInt(value)); 12 | } 13 | 14 | public static String toUnsignedString(@Unsigned short value) { 15 | return toUnsignedString(Short.toUnsignedInt(value)); 16 | } 17 | 18 | public static String toUnsignedString(@Unsigned int value) { 19 | return Integer.toUnsignedString(value); 20 | } 21 | 22 | public static String toUnsignedString(@Unsigned long value) { 23 | return Long.toUnsignedString(value); 24 | } 25 | 26 | /** 27 | * Convenience method that stands in for the missing method {@code Byte#divideUnsigned(byte, byte)}.
28 | * This method was inspired by {@link Integer#divideUnsigned(int, int) the method of the same in Integer}
29 | *
30 | * Description copied from {@link Integer#divideUnsigned(int, int)}
31 | * Returns the unsigned quotient of dividing the first argument by 32 | * the second where each argument and the result is interpreted as 33 | * an unsigned value. 34 | * 35 | *

Note that in two's complement arithmetic, the three other 36 | * basic arithmetic operations of add, subtract, and multiply are 37 | * bit-wise identical if the two operands are regarded as both 38 | * being signed or both being unsigned. Therefore separate {@code 39 | * addUnsigned}, etc. methods are not provided. 40 | * 41 | * @param dividend the value to be divided 42 | * @param divisor the value doing the dividing 43 | * @return the unsigned quotient of the first argument divided by 44 | * the second argument 45 | * @see #remainderUnsigned 46 | * @since 1.8 47 | * 48 | * @see Integer#divideUnsigned(int, int) 49 | */ 50 | public static @Unsigned byte divideUnsigned(@Unsigned byte dividend, @Unsigned byte divisor) { 51 | return (byte) (Byte.toUnsignedInt(dividend) / Byte.toUnsignedInt(divisor)); 52 | } 53 | 54 | /** 55 | * Convenience method that stands in for the missing method {@code Short#divideUnsigned(short, short)}.
56 | * This method was inspired by {@link Integer#divideUnsigned(int, int) the method of the same in Integer}
57 | *
58 | * Description copied from {@link Integer#divideUnsigned(int, int)}
59 | * Returns the unsigned quotient of dividing the first argument by 60 | * the second where each argument and the result is interpreted as 61 | * an unsigned value. 62 | * 63 | *

Note that in two's complement arithmetic, the three other 64 | * basic arithmetic operations of add, subtract, and multiply are 65 | * bit-wise identical if the two operands are regarded as both 66 | * being signed or both being unsigned. Therefore separate {@code 67 | * addUnsigned}, etc. methods are not provided. 68 | * 69 | * @param dividend the value to be divided 70 | * @param divisor the value doing the dividing 71 | * @return the unsigned quotient of the first argument divided by 72 | * the second argument 73 | * @see #remainderUnsigned 74 | * @since 1.8 75 | * 76 | * @see Integer#divideUnsigned(int, int) 77 | */ 78 | public static @Unsigned short divideUnsigned(@Unsigned short dividend, @Unsigned short divisor) { 79 | return (short) (Short.toUnsignedInt(dividend) / Short.toUnsignedInt(divisor)); 80 | } 81 | 82 | /** 83 | * Convenience method that delegates to {@link Integer#divideUnsigned(int, int)}.
84 | *
85 | * Description copied from {@link Integer#divideUnsigned(int, int)}
86 | * Returns the unsigned quotient of dividing the first argument by 87 | * the second where each argument and the result is interpreted as 88 | * an unsigned value. 89 | * 90 | *

Note that in two's complement arithmetic, the three other 91 | * basic arithmetic operations of add, subtract, and multiply are 92 | * bit-wise identical if the two operands are regarded as both 93 | * being signed or both being unsigned. Therefore separate {@code 94 | * addUnsigned}, etc. methods are not provided. 95 | * 96 | * @param dividend the value to be divided 97 | * @param divisor the value doing the dividing 98 | * @return the unsigned quotient of the first argument divided by 99 | * the second argument 100 | * @see #remainderUnsigned 101 | * @since 1.8 102 | * 103 | * @see Integer#divideUnsigned(int, int) 104 | */ 105 | public static @Unsigned int divideUnsigned(@Unsigned int dividend, @Unsigned int divisor) { 106 | return Integer.divideUnsigned(dividend, divisor); 107 | } 108 | 109 | /** 110 | * Convenience method that delegates to {@link Long#divideUnsigned(long, long)}.
111 | *
112 | * Description copied from {@link Long#divideUnsigned(long, long)}
113 | * Returns the unsigned quotient of dividing the first argument by 114 | * the second where each argument and the result is interpreted as 115 | * an unsigned value. 116 | * 117 | *

Note that in two's complement arithmetic, the three other 118 | * basic arithmetic operations of add, subtract, and multiply are 119 | * bit-wise identical if the two operands are regarded as both 120 | * being signed or both being unsigned. Therefore separate {@code 121 | * addUnsigned}, etc. methods are not provided. 122 | * 123 | * @param dividend the value to be divided 124 | * @param divisor the value doing the dividing 125 | * @return the unsigned quotient of the first argument divided by 126 | * the second argument 127 | * @see #remainderUnsigned 128 | * @since 1.8 129 | * 130 | * @see Long#divideUnsigned(long, long) 131 | */ 132 | public static @Unsigned long divideUnsigned(@Unsigned long dividend, @Unsigned long divisor) { 133 | return Long.divideUnsigned(dividend, divisor); 134 | } 135 | 136 | public static @Unsigned byte remainderUnsigned(@Unsigned byte dividend, @Unsigned byte divisor) { 137 | return (byte) (Byte.toUnsignedInt(dividend) % Byte.toUnsignedInt(divisor)); 138 | } 139 | 140 | public static @Unsigned short remainderUnsigned(@Unsigned short dividend, @Unsigned short divisor) { 141 | return (short) (Short.toUnsignedInt(dividend) % Short.toUnsignedInt(divisor)); 142 | } 143 | 144 | public static @Unsigned int remainderUnsigned(@Unsigned int dividend, @Unsigned int divisor) { 145 | return Integer.remainderUnsigned(dividend, divisor); 146 | } 147 | 148 | public static @Unsigned long remainderUnsigned(@Unsigned long dividend, @Unsigned long divisor) { 149 | return Long.remainderUnsigned(dividend, divisor); 150 | } 151 | 152 | public static int compareUnsigned(byte x, byte y) { 153 | return Byte.compareUnsigned(x, y); 154 | } 155 | 156 | public static int compareUnsigned(short x, short y) { 157 | return Short.compareUnsigned(x, y); 158 | } 159 | 160 | public static int compareUnsigned(int x, int y) { 161 | return Integer.compareUnsigned(x, y); 162 | } 163 | 164 | public static int compareUnsigned(long x, long y) { 165 | return Long.compareUnsigned(x, y); 166 | } 167 | 168 | ///////////////////////////////////////////////////////// 169 | ////////////////////// Conversions ////////////////////// 170 | ///////////////////////////////////////////////////////// 171 | 172 | ////////////////////// byte ////////////////////// 173 | 174 | public static @Unsigned byte toUnsignedByte(@Unsigned short value) { 175 | return (byte) value; 176 | } 177 | 178 | public static @Unsigned byte toUnsignedByte(@Unsigned int value) { 179 | return (byte) value; 180 | } 181 | 182 | public static @Unsigned byte toUnsignedByte(@Unsigned long value) { 183 | return (byte) value; 184 | } 185 | 186 | ////////////////////// short ////////////////////// 187 | 188 | public static @Unsigned short toUnsignedShort(@Unsigned byte value) { 189 | return (short) (((short) value) & BYTE_MASK); 190 | } 191 | 192 | public static @Unsigned short toUnsignedShort(@Unsigned int value) { 193 | return (short) value; 194 | } 195 | 196 | public static @Unsigned short toUnsignedShort(@Unsigned long value) { 197 | return (short) value; 198 | } 199 | 200 | ////////////////////// int ////////////////////// 201 | 202 | public static @Unsigned int toUnsignedInt(@Unsigned byte value) { 203 | return ((int) value) & BYTE_MASK; 204 | } 205 | 206 | public static @Unsigned int toUnsignedInt(@Unsigned short value) { 207 | return ((int) value) & SHORT_MASK; 208 | } 209 | 210 | public static @Unsigned int toUnsignedInt(@Unsigned long value) { 211 | return (int) value; 212 | } 213 | 214 | ////////////////////// long ////////////////////// 215 | 216 | public static @Unsigned long toUnsignedLong(@Unsigned byte value) { 217 | return ((long) value) & BYTE_MASK; 218 | } 219 | 220 | public static @Unsigned long toUnsignedLong(@Unsigned short value) { 221 | return ((long) value) & SHORT_MASK; 222 | } 223 | 224 | public static @Unsigned long toUnsignedLong(@Unsigned int value) { 225 | return ((long) value) & INT_MASK; 226 | } 227 | } -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/Win32FindStreamData.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | import com.sun.jna.Structure; 7 | 8 | public class Win32FindStreamData extends Structure implements DokanOperations.Win32FindStreamDataInterface { 9 | public long length; 10 | //max path = 260 under windows 11 | public char[] cFileName = new char[260 + 36]; 12 | 13 | @Override 14 | public void length(final long val) { 15 | length = val; 16 | } 17 | 18 | @Override 19 | public char[] cFileName() { 20 | return cFileName; 21 | } 22 | 23 | @Override 24 | protected List getFieldOrder() { 25 | return Arrays.asList( 26 | "length", 27 | "cFileName"); 28 | } 29 | } -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/constants/dokany/MountError.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.constants.dokany; 2 | 3 | import dev.dokan.dokan_java.masking.EnumInteger; 4 | 5 | /** 6 | * Return values of com.dokan.java.NativeMethods#DokanMain(DokanOptions, DokanOperations) 7 | * 8 | * @see Dokany documentation 9 | */ 10 | public enum MountError implements EnumInteger { 11 | SUCCESS(0, "Mount succeed."), 12 | MOUNT_ERROR(-1, "Mount error."), 13 | DRIVE_LETTER_ERROR(-2, "Mount failed: Bad drive letter."), 14 | DRIVER_INSTALL_ERROR(-3, "Mount failed: Can't install driver."), 15 | START_ERROR(-4, "Mount failed: Driver answer that something is wrong."), 16 | CANNOT_ASSIGN(-5, "Mount failed: Cannot assign a drive letter or mount point. Probably already used by another volume."), 17 | MOUNT_POINT_ERROR(-6, "Mount failed: Mount point is invalid."), 18 | VERSION_ERROR(-7, "Mount failed: Requested an incompatible version."); 19 | 20 | private final int intValue; 21 | private final String description; 22 | 23 | public static MountError fromInt(final int value) { 24 | return EnumInteger.enumFromInt(value, values()); 25 | } 26 | 27 | MountError(final int intValue, final String description) { 28 | this.intValue = intValue; 29 | this.description = description; 30 | } 31 | 32 | @Override 33 | public int intValue() { 34 | return this.intValue; 35 | } 36 | 37 | public String getDescription() { 38 | return this.description; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/constants/dokany/MountOption.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.constants.dokany; 2 | 3 | import dev.dokan.dokan_java.masking.MaskValueSet; 4 | import dev.dokan.dokan_java.masking.MaskValueEnum; 5 | import dev.dokan.dokan_java.structure.DokanOptions; 6 | 7 | /** 8 | * Enumeration of features that can be enabled for a mount. Part of {@link DokanOptions} 9 | * 10 | * @see Dokany documentation 11 | */ 12 | public enum MountOption implements MaskValueEnum { 13 | DEBUG_MODE(1, "Enable output debug message."), 14 | STD_ERR_OUTPUT(2, "Enable output debug message to stderr."), 15 | ALT_STREAM(4, "Use alternate stream."), 16 | WRITE_PROTECTION(8, "Enable mount drive as write-protected."), 17 | NETWORK_DRIVE(16, "Use network drive - Dokan network provider need to be installed."), 18 | REMOVABLE(32, "Use removable drive."), 19 | MOUNT_MANAGER(64, "Use mount manager."), 20 | CURRENT_SESSION(128, "Mount the drive on current session only."), 21 | FILELOCK_USER_MODE(256, "Enable Lockfile/Unlockfile operations. Otherwise Dokan will take care of it."); 22 | 23 | private final int maskingValue; 24 | private final String description; 25 | 26 | MountOption(final int maskingValue, final String desc) { 27 | this.maskingValue = maskingValue; 28 | this.description = desc; 29 | } 30 | 31 | public static MaskValueSet maskValueSet(final int mask) { 32 | return MaskValueSet.maskValueSet(mask, values()); 33 | } 34 | 35 | @Override 36 | public int intValue() { 37 | return this.maskingValue; 38 | } 39 | 40 | public String getDescription() { 41 | return this.description; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/constants/microsoft/CreateDisposition.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.constants.microsoft; 2 | 3 | import dev.dokan.dokan_java.DokanNativeMethods; 4 | import dev.dokan.dokan_java.DokanOperations; 5 | import dev.dokan.dokan_java.masking.EnumInteger; 6 | import com.sun.jna.ptr.IntByReference; 7 | 8 | /** 9 | * Enum of possible actions to perform on a file when the function {@link DokanOperations#ZwCreateFile} is called. 10 | * 11 | *

12 | * Attention! 13 | * The members of this enum are the kernel flags, not to be confused with the user flags in {@link CreationDisposition}. To convert them, use {@link DokanNativeMethods#DokanMapKernelToUserCreateFileFlags(long, long, long, long, IntByReference, IntByReference, IntByReference)}. The relation between this two is also 14 | * descriped under this link. 15 | *

16 | * 17 | *

18 | * The following table shows the actions performed on a file for the given flag distinguishing if the file already exists or not: 19 | *

20 | * 21 | * 22 | * 23 | * 24 | * 25 | * 26 | * 27 | * 28 | * 29 | * 30 | * 31 | * 32 | * 33 | * 34 | * 35 | * 36 | * 37 | * 38 | * 39 | * 40 | * 41 | * 42 | * 43 | * 44 | * 45 | * 46 | * 47 | * 48 | * 49 | * 50 | * 51 | * 52 | * 53 | * 54 | * 55 | * 56 | * 57 | * 58 | * 59 | * 60 | * 61 | * 62 | *
additional options
OptionAction if file existsAction if file does not exist
FILE_SUPERSEDEReplace the file.Create the file.
FILE_CREATEReturn an error.Create the file.
FILE_OPENOpen the file.Return an error.
FILE_OPEN_IFOpen the file.Create the file.
FILE_OVERWRITEOpen the file, and overwrite it.Return an error.
FILE_OVERWRITE_IFOpen the file, and overwrite it.Create the file.
63 | * 64 | * 65 | * @see Microsoft documentation of ZwCreateFile 66 | * @see Dokany documentation of ZwCreateFile 67 | */ 68 | public enum CreateDisposition implements EnumInteger { 69 | 70 | FILE_SUPERSEDE(CreateDispositions.FILE_SUPERSEDE), 71 | FILE_CREATE(CreateDispositions.FILE_CREATE), 72 | FILE_OPEN(CreateDispositions.FILE_OPEN), 73 | FILE_OPEN_IF(CreateDispositions.FILE_OPEN_IF), 74 | FILE_OVERWRITE(CreateDispositions.FILE_OVERWRITE), 75 | FILE_OVERWRITE_IF(CreateDispositions.FILE_OVERWRITE_IF); 76 | 77 | private final int intValue; 78 | 79 | CreateDisposition(int intValue) { 80 | this.intValue = intValue; 81 | } 82 | 83 | public static CreateDisposition fromInt(final int value) { 84 | return EnumInteger.enumFromInt(value, values()); 85 | } 86 | 87 | @Override 88 | public int intValue() { 89 | return this.intValue; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/constants/microsoft/CreateDispositions.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.constants.microsoft; 2 | 3 | public final class CreateDispositions { 4 | 5 | public final static int FILE_SUPERSEDE = 0; 6 | public final static int FILE_OPEN = 1; 7 | public final static int FILE_CREATE = 2; 8 | public final static int FILE_OPEN_IF = 3; 9 | public final static int FILE_OVERWRITE = 4; 10 | public final static int FILE_OVERWRITE_IF = 5; 11 | 12 | private CreateDispositions() { 13 | 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/constants/microsoft/CreateOption.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.constants.microsoft; 2 | 3 | import dev.dokan.dokan_java.masking.MaskValueSet; 4 | import dev.dokan.dokan_java.masking.MaskValueEnum; 5 | 6 | /** 7 | * Enum of flags specifying the options to apply when the driver creates or opens the file. 8 | * 9 | * @see Microsofts documentation of ZwCreateFile 10 | * @see Dokany documentation of ZwCreateFile 11 | */ 12 | public enum CreateOption implements MaskValueEnum { 13 | FILE_DIRECTORY_FILE(CreateOptions.FILE_DIRECTORY_FILE), 14 | FILE_WRITE_THROUGH(CreateOptions.FILE_WRITE_THROUGH), 15 | FILE_SEQUENTIAL_ONLY(CreateOptions.FILE_SEQUENTIAL_ONLY), 16 | FILE_NO_INTERMEDIATE_BUFFERING(CreateOptions.FILE_NO_INTERMEDIATE_BUFFERING), 17 | FILE_SYNCHRONOUS_IO_ALERT(CreateOptions.FILE_SYNCHRONOUS_IO_ALERT), 18 | FILE_SYNCHRONOUS_IO_NONALERT(CreateOptions.FILE_SYNCHRONOUS_IO_NONALERT), 19 | FILE_NON_DIRECTORY_FILE(CreateOptions.FILE_NON_DIRECTORY_FILE), 20 | FILE_CREATE_TREE_CONNECTION(CreateOptions.FILE_CREATE_TREE_CONNECTION), 21 | FILE_COMPLETE_IF_OPLOCKED(CreateOptions.FILE_COMPLETE_IF_OPLOCKED), 22 | FILE_NO_EA_KNOWLEDGE(CreateOptions.FILE_NO_EA_KNOWLEDGE), 23 | FILE_OPEN_REMOTE_INSTANCE(CreateOptions.FILE_OPEN_REMOTE_INSTANCE), 24 | FILE_RANDOM_ACCESS(CreateOptions.FILE_RANDOM_ACCESS), 25 | FILE_OPEN_REPARSE_POINT(CreateOptions.FILE_OPEN_REPARSE_POINT), 26 | FILE_DELETE_ON_CLOSE(CreateOptions.FILE_DELETE_ON_CLOSE), 27 | FILE_OPEN_BY_FILE_ID(CreateOptions.FILE_OPEN_BY_FILE_ID), 28 | FILE_OPEN_FOR_BACKUP_INTENT(CreateOptions.FILE_OPEN_FOR_BACKUP_INTENT), 29 | FILE_NO_COMPRESSION(CreateOptions.FILE_NO_COMPRESSION), 30 | FILE_SESSION_AWARE(CreateOptions.FILE_SESSION_AWARE); 31 | 32 | private final int maskingValue; 33 | 34 | CreateOption(final int maskingValue) { 35 | this.maskingValue = maskingValue; 36 | } 37 | 38 | public static MaskValueSet maskValueSet(final int mask) { 39 | return MaskValueSet.maskValueSet(mask, values()); 40 | } 41 | 42 | @Override 43 | public int intValue() { 44 | return this.maskingValue; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/constants/microsoft/CreateOptions.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.constants.microsoft; 2 | 3 | public final class CreateOptions { 4 | 5 | public static final int FILE_DIRECTORY_FILE = 0x00000001; 6 | public static final int FILE_WRITE_THROUGH = 0x00000002; 7 | public static final int FILE_SEQUENTIAL_ONLY = 0x00000004; 8 | public static final int FILE_NO_INTERMEDIATE_BUFFERING = 0x00000008; 9 | public static final int FILE_SYNCHRONOUS_IO_ALERT = 0x00000010; 10 | public static final int FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020; 11 | public static final int FILE_NON_DIRECTORY_FILE = 0x00000040; 12 | public static final int FILE_CREATE_TREE_CONNECTION = 0x00000080; 13 | public static final int FILE_COMPLETE_IF_OPLOCKED = 0x00000100; 14 | public static final int FILE_NO_EA_KNOWLEDGE = 0x00000200; 15 | public static final int FILE_OPEN_REMOTE_INSTANCE = 0x00000400; 16 | public static final int FILE_RANDOM_ACCESS = 0x00000800; 17 | public static final int FILE_OPEN_REPARSE_POINT = 0x00200000; 18 | public static final int FILE_DELETE_ON_CLOSE = 0x00001000; 19 | public static final int FILE_OPEN_BY_FILE_ID = 0x00002000; 20 | public static final int FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000; 21 | public static final int FILE_NO_COMPRESSION = 0x00008000; 22 | public static final int FILE_SESSION_AWARE = 0x00040000; 23 | 24 | private CreateOptions() { 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/constants/microsoft/CreationDisposition.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.constants.microsoft; 2 | 3 | import com.sun.jna.platform.win32.WinNT; 4 | import dev.dokan.dokan_java.DokanNativeMethods; 5 | import dev.dokan.dokan_java.masking.EnumInteger; 6 | 7 | /** 8 | * Enum of actions to take on a not-necessarily existing file or device. 9 | *

These values are the userspace equivalent of {@link CreateDisposition}. 10 | * For a given CreateDisposition value the corresponding CreationDisposition value can be computed via the {@link DokanNativeMethods#DokanMapKernelToUserCreateFileFlags(long, long, long, long, 11 | * IntByReference, IntByReference, IntByReference)} 12 | *

13 | * 14 | *

15 | * The following table shows the actions performed on a file for the given flag distinguishing if the file already exists or not: 16 | *

17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | * 24 | * 25 | * 26 | * 27 | * 28 | * 29 | * 30 | * 31 | * 32 | * 33 | * 34 | * 35 | * 36 | * 37 | * 38 | * 39 | * 40 | * 41 | * 42 | * 43 | * 44 | * 45 | * 46 | * 47 | * 48 | * 49 | * 50 | * 51 | * 52 | * 53 | * 54 | *
additional options
OptionAction if file existsAction if file does not exist
CREATE_ALWAYSOverwrite the file and return {@link Win32ErrorCodes#ERROR_ALREADY_EXISTS}.Create the file.
CREATE_NEWFail and return {@link Win32ErrorCodes#ERROR_ALREADY_EXISTS}.Create the file.
OPEN_ALWAYSOpen the file and return {@link Win32ErrorCodes#ERROR_ALREADY_EXISTS}.Create the file.
OPEN_EXISTINGOpen the file.Fail and return {@link Win32ErrorCodes#ERROR_FILE_NOT_FOUND}.
TRUNCATE_EXISTINGTruncate the file, and overwrite it.Fail.
55 | * 56 | * @see The Microsoft Documentation of CreateFile 57 | * @see StackOverFlow Answer summing up the above table. 58 | */ 59 | public enum CreationDisposition implements EnumInteger { 60 | CREATE_NEW(WinNT.CREATE_NEW), 61 | CREATE_ALWAYS(WinNT.CREATE_ALWAYS), 62 | OPEN_EXISTING(WinNT.OPEN_EXISTING), 63 | OPEN_ALWAYS(WinNT.OPEN_ALWAYS), 64 | TRUNCATE_EXISTING(WinNT.TRUNCATE_EXISTING); 65 | 66 | private final int intValue; 67 | 68 | CreationDisposition(final int intValue) { 69 | this.intValue = intValue; 70 | } 71 | 72 | public static CreationDisposition fromInt(final int value) { 73 | return EnumInteger.enumFromInt(value, values()); 74 | } 75 | 76 | @Override 77 | public int intValue() { 78 | return this.intValue; 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/constants/microsoft/FileAttribute.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.constants.microsoft; 2 | 3 | 4 | import com.sun.jna.platform.win32.WinNT; 5 | import dev.dokan.dokan_java.masking.MaskValueSet; 6 | import dev.dokan.dokan_java.masking.MaskValueEnum; 7 | 8 | 9 | /** 10 | * File attribute flags. They are metadata values stored on the disk by the filesystem to be used by the system. 11 | * 12 | * @see Microsoft documentation of file attribute constants 13 | * @see Microsoft documentation of CreateFileA function including the list of valid file attributes 14 | */ 15 | public enum FileAttribute implements MaskValueEnum { 16 | ARCHIVE(WinNT.FILE_ATTRIBUTE_ARCHIVE), 17 | COMPRESSED(WinNT.FILE_ATTRIBUTE_COMPRESSED), 18 | DEVICE(WinNT.FILE_ATTRIBUTE_DEVICE), 19 | DIRECTORY(WinNT.FILE_ATTRIBUTE_DIRECTORY), 20 | ENCRYPTED(WinNT.FILE_ATTRIBUTE_ENCRYPTED), 21 | HIDDEN(WinNT.FILE_ATTRIBUTE_HIDDEN), 22 | INTEGRITY_STREAM(32768), 23 | NORMAL(WinNT.FILE_ATTRIBUTE_NORMAL), 24 | NOT_CONTENT_INDEXED(WinNT.FILE_ATTRIBUTE_NOT_CONTENT_INDEXED), 25 | NO_SCRUB_DATA(131072), 26 | OFFLINE(WinNT.FILE_ATTRIBUTE_OFFLINE), 27 | READONLY(WinNT.FILE_ATTRIBUTE_READONLY), 28 | REPARSE_POINT(WinNT.FILE_ATTRIBUTE_REPARSE_POINT), 29 | SPARSE_FILE(WinNT.FILE_ATTRIBUTE_SPARSE_FILE), 30 | SYSTEM(WinNT.FILE_ATTRIBUTE_SYSTEM), 31 | TEMPORARY(WinNT.FILE_ATTRIBUTE_TEMPORARY), 32 | VIRTUAL(WinNT.FILE_ATTRIBUTE_VIRTUAL), 33 | RECALL_ON_DATA_ACCESS(4194394), 34 | RECALL_ON_OPEN(262144); 35 | 36 | private final int maskingValue; 37 | 38 | public static MaskValueSet maskValueSet(final int mask) { 39 | return MaskValueSet.maskValueSet(mask, values()); 40 | } 41 | 42 | FileAttribute(final int maskingValue) { 43 | this.maskingValue = maskingValue; 44 | } 45 | 46 | @Override 47 | public int intValue() { 48 | return this.maskingValue; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/constants/microsoft/FileSystemFlag.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.constants.microsoft; 2 | 3 | import com.sun.jna.platform.win32.WinNT; 4 | import dev.dokan.dokan_java.DokanOperations; 5 | import dev.dokan.dokan_java.masking.MaskValueSet; 6 | import dev.dokan.dokan_java.masking.MaskValueEnum; 7 | 8 | /** 9 | * Properties which the implemented filesystem supports. 10 | * 11 | *

12 | * Returned in {@link DokanOperations#GetVolumeInformation} to the kernel layer indicating what properties your file system implementation supports. 13 | *

14 | * 15 | *

They can be arbitrary combined within an {@link MaskValueSet}. However FILE_FILE_COMPRESSION and FILE_VOL_IS_COMPRESSED are mutually exclusive.

16 | * 17 | * @see Microsoft Documentation of function GetVolumeInformation, Parameter {@code lpFileSystemFlags} 18 | * @see Listing of possible values 19 | */ 20 | public enum FileSystemFlag implements MaskValueEnum { 21 | NONE(0), 22 | CASE_PRESERVED_NAMES(WinNT.FILE_CASE_PRESERVED_NAMES), 23 | CASE_SENSITIVE_SEARCH(WinNT.FILE_CASE_SENSITIVE_SEARCH), 24 | DAX_VOLUME(0x20000000), 25 | FILE_COMPRESSION(WinNT.FILE_FILE_COMPRESSION), 26 | NAMED_STREAMS(WinNT.FILE_NAMED_STREAMS), 27 | PERSISTENT_ACLS(WinNT.FILE_PERSISTENT_ACLS), 28 | READ_ONLY_VOLUME(WinNT.FILE_READ_ONLY_VOLUME), 29 | SEQUENTIAL_WRITE_ONCE(WinNT.FILE_SEQUENTIAL_WRITE_ONCE), 30 | SUPPORTS_ENCRYPTION(WinNT.FILE_SUPPORTS_ENCRYPTION), 31 | SUPPORTS_EXTENDED_ATTRIBUTES(WinNT.FILE_SUPPORTS_EXTENDED_ATTRIBUTES), 32 | SUPPORTS_HARD_LINKS(WinNT.FILE_SUPPORTS_HARD_LINKS), 33 | SUPPORTS_OBJECT_IDS(WinNT.FILE_SUPPORTS_OBJECT_IDS), 34 | SUPPORTS_OPEN_BY_FILE_ID(WinNT.FILE_SUPPORTS_OPEN_BY_FILE_ID), 35 | SUPPORTS_REPARSE_POINTS(WinNT.FILE_SUPPORTS_REPARSE_POINTS), 36 | SUPPORTS_SPARSE_FILES(WinNT.FILE_SUPPORTS_SPARSE_FILES), 37 | SUPPORTS_TRANSACTIONS(WinNT.FILE_SUPPORTS_TRANSACTIONS), 38 | SUPPORTS_USN_JOURNAL(WinNT.FILE_SUPPORTS_USN_JOURNAL), 39 | UNICODE_ON_DISK(WinNT.FILE_UNICODE_ON_DISK), 40 | SUPPORTS_REMOTE_STORAGE(WinNT.FILE_SUPPORTS_REMOTE_STORAGE), 41 | VOLUME_IS_COMPRESSED(WinNT.FILE_VOLUME_IS_COMPRESSED), 42 | VOLUME_QUOTAS(WinNT.FILE_VOLUME_QUOTAS); 43 | 44 | private final int maskingValue; 45 | 46 | public static MaskValueSet maskValueSet(final int mask) { 47 | return MaskValueSet.maskValueSet(mask, values()); 48 | } 49 | 50 | FileSystemFlag(final int maskingValue) { 51 | this.maskingValue = maskingValue; 52 | } 53 | 54 | @Override 55 | public int intValue() { 56 | return this.maskingValue; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/constants/microsoft/accessmaskflags/BasicAccessMaskFlag.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.constants.microsoft.accessmaskflags; 2 | 3 | import com.sun.jna.platform.win32.WinNT; 4 | import dev.dokan.dokan_java.masking.MaskValueEnum; 5 | import dev.dokan.dokan_java.masking.MaskValueSet; 6 | 7 | /** 8 | * Enumeration of the possible AccessMask options. 9 | * For more info see the Microsoft Developer Documentation or the normal documentation. 10 | */ 11 | public enum BasicAccessMaskFlag implements MaskValueEnum { 12 | /** 13 | * GENERIC_READ 14 | * When used in an Access Request operation: When read access to an object is requested, this bit is translated to a combination of bits. These are most often set in the lower 16 bits of the ACCESS_MASK. (Individual protocol specifications MAY specify a different configuration.) The bits that are set are implementation dependent. During this translation, the GENERIC_READ bit is cleared. The resulting ACCESS_MASK bits are the actual permissions that are checked against the ACE structures in the security descriptor that attached to the object. 15 | *

16 | * When used to set the Security Descriptor on an object: When the GENERIC_READ bit is set in an ACE that is to be attached to an object, it is translated into a combination of bits, which are usually set in the lower 16 bits of the ACCESS_MASK. (Individual protocol specifications MAY specify a different configuration.) The bits that are set are implementation dependent. During this translation, the GENERIC_READ bit is cleared. The resulting ACCESS_MASK bits are the actual permissions that are granted by this ACE. 17 | */ 18 | GENERIC_READ(WinNT.GENERIC_READ), 19 | 20 | 21 | /** 22 | * GENERIC_WRITE 23 | * When used in an Access Request operation: When write access to an object is requested, this bit is translated to a combination of bits, which are usually set in the lower 16 bits of the ACCESS_MASK. (Individual protocol specifications MAY specify a different configuration.) The bits that are set are implementation dependent. During this translation, the GENERIC_WRITE bit is cleared. The resulting ACCESS_MASK bits are the actual permissions that are checked against the ACE structures in the security descriptor that attached to the object. 24 | *

25 | * When used to set the Security Descriptor on an object: When the GENERIC_WRITE bit is set in an ACE that is to be attached to an object, it is translated into a combination of bits, which are usually set in the lower 16 bits of the ACCESS_MASK. (Individual protocol specifications MAY specify a different configuration.) The bits that are set are implementation dependent. During this translation, the GENERIC_WRITE bit is cleared. The resulting ACCESS_MASK bits are the actual permissions that are granted by this ACE. 26 | */ 27 | GENERIC_WRITE(WinNT.GENERIC_WRITE), 28 | 29 | 30 | /** 31 | * GENERIC_EXECUTE 32 | *

33 | * When used in an Access Request operation: When execute access to an object is requested, this bit is translated to a combination of bits, which are usually set in the lower 16 bits of the ACCESS_MASK. (Individual protocol specifications MAY specify a different configuration.) The bits that are set are implementation dependent. During this translation, the GENERIC_EXECUTE bit is cleared. The resulting ACCESS_MASK bits are the actual permissions that are checked against the ACE structures in the security descriptor that attached to the object. 34 | *

35 | * When used to set the Security Descriptor on an object: When the GENERIC_EXECUTE bit is set in an ACE that is to be attached to an object, it is translated into a combination of bits, which are usually set in the lower 16 bits of the ACCESS_MASK. (Individual protocol specifications MAY specify a different configuration.) The bits that are set are implementation dependent. During this translation, the GENERIC_EXECUTE bit is cleared. The resulting ACCESS_MASK bits are the actual permissions that are granted by this ACE. 36 | */ 37 | GENERIC_EXECUTE(WinNT.GENERIC_EXECUTE), 38 | 39 | 40 | /** 41 | * GENERIC_ALL 42 | * When used in an Access Request operation: When all access permissions to an object are requested, this bit is translated to a combination of bits, which are usually set in the lower 16 bits of the ACCESS_MASK. (Individual protocol specifications MAY specify a different configuration.) Objects are free to include bits from the upper 16 bits in that translation as required by the objects semantics. The bits that are set are implementation dependent. During this translation, the GENERIC_ALL bit is cleared. The resulting ACCESS_MASK bits are the actual permissions that are checked against the ACE structures in the security descriptor that attached to the object. 43 | *

44 | * When used to set the Security Descriptor on an object: When the GENERIC_ALL bit is set in an ACE that is to be attached to an object, it is translated into a combination of bits, which are usually set in the lower 16 bits of the ACCESS_MASK. (Individual protocol specifications MAY specify a different configuration.) Objects are free to include bits from the upper 16 bits in that translation, if required by the objects semantics. The bits that are set are implementation dependent. During this translation, the GENERIC_ALL bit is cleared. The resulting ACCESS_MASK bits are the actual permissions that are granted by this ACE. 45 | */ 46 | GENERIC_ALL(WinNT.GENERIC_ALL), 47 | 48 | 49 | /** 50 | * MAXIMUM_ALLOWED 51 | *

52 | * When used in an Access Request operation: When requested, this bit grants the requestor the maximum permissions allowed to the object through the Access Check Algorithm. This bit can only be requested; it cannot be set in an ACE. 53 | *

54 | * When used to set the Security Descriptor on an object: Specifying the Maximum Allowed bit in the SECURITY_DESCRIPTOR has no meaning. The MAXIMUM_ALLOWED bit SHOULD NOT be set and SHOULD be ignored when part of a SECURITY_DESCRIPTOR structure. 55 | */ 56 | MAXIMUM_ALLOWED(0x02000000L), 57 | 58 | 59 | /** 60 | * ACCESS_SYSTEM_SECURITY 61 | * When used in an Access Request operation: When requested, this bit grants the requestor the right to change the SACL of an object. This bit MUST NOT be set in an ACE that is part of a DACL. When set in an ACE that is part of a SACL, this bit controls auditing of accesses to the SACL itself. 62 | */ 63 | ACCESS_SYSTEM_SECURITY(WinNT.ACCESS_SYSTEM_SECURITY), 64 | 65 | 66 | /** 67 | * SYNCHRONIZE 68 | *

69 | * Specifies access to the object sufficient to synchronize or wait on the object. 70 | */ 71 | SYNCHRONIZE(WinNT.SYNCHRONIZE), 72 | 73 | 74 | /** 75 | * WRITE_OWNER 76 | * Specifies access to change the owner of the object as listed in the security descriptor. 77 | */ 78 | WRITE_OWNER(WinNT.WRITE_OWNER), 79 | 80 | 81 | /** 82 | * WRITE_DAC 83 | * Specifies access to change the discretionary access control list of the security descriptor of an object. 84 | */ 85 | WRITE_DAC(WinNT.WRITE_DAC), 86 | 87 | 88 | /** 89 | * READ_CONTROL 90 | * Specifies access to read the security descriptor of an object. 91 | */ 92 | READ_CONTROL(WinNT.READ_CONTROL), 93 | 94 | 95 | /** 96 | * DELETE 97 | * Specifies access to delete an object. 98 | */ 99 | DELETE(WinNT.DELETE); 100 | 101 | private int maskingValue; 102 | 103 | BasicAccessMaskFlag(long maskingValue) { 104 | this.maskingValue = (int) maskingValue; 105 | } 106 | 107 | public static MaskValueSet maskValueSet(final int mask) { 108 | return MaskValueSet.maskValueSet(mask, values()); 109 | } 110 | 111 | @Override 112 | public int intValue() { 113 | return this.maskingValue; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/constants/microsoft/accessmaskflags/DirectoryAccessMaskFlag.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.constants.microsoft.accessmaskflags; 2 | 3 | import com.sun.jna.platform.win32.WinNT; 4 | import dev.dokan.dokan_java.masking.MaskValueEnum; 5 | import dev.dokan.dokan_java.masking.MaskValueSet; 6 | 7 | /** 8 | * Additional {@link BasicAccessMaskFlag} values specific to directories. 9 | * 10 | * @see Microsoft documentation of ZwCreateFile, Section Parameters, Parameter {@code DesiredAccess} 11 | */ 12 | public enum DirectoryAccessMaskFlag implements MaskValueEnum { 13 | LIST_DIRECTORY(WinNT.FILE_LIST_DIRECTORY), 14 | TRAVERSE(WinNT.FILE_TRAVERSE); 15 | 16 | private final int maskingValue; 17 | 18 | DirectoryAccessMaskFlag(int maskingValue) { 19 | this.maskingValue = maskingValue; 20 | } 21 | 22 | public static MaskValueSet maskValueSet(final int mask) { 23 | return MaskValueSet.maskValueSet(mask, values()); 24 | } 25 | 26 | @Override 27 | public int intValue() { 28 | return this.maskingValue; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/constants/microsoft/accessmaskflags/FileAccessMaskFlag.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.constants.microsoft.accessmaskflags; 2 | 3 | import com.sun.jna.platform.win32.WinNT; 4 | import dev.dokan.dokan_java.masking.MaskValueEnum; 5 | import dev.dokan.dokan_java.masking.MaskValueSet; 6 | 7 | /** 8 | * Additional {@link BasicAccessMaskFlag} values specific to files. 9 | * 10 | * @see Microsoft documentation of ZwCreateFile, Section Parameters, Parameter {@code DesiredAccess} 11 | */ 12 | public enum FileAccessMaskFlag implements MaskValueEnum { 13 | READ_DATA(WinNT.FILE_READ_DATA), 14 | READ_ATTRIBUTES(WinNT.FILE_READ_ATTRIBUTES), 15 | READ_EA(WinNT.FILE_READ_EA), 16 | WRITE_DATA(WinNT.FILE_WRITE_DATA), 17 | WRITE_ATTRIBUTES(WinNT.FILE_WRITE_ATTRIBUTES), 18 | WRITE_EA(WinNT.FILE_WRITE_EA), 19 | APPEND_DATA(WinNT.FILE_APPEND_DATA), 20 | EXECUTE(WinNT.FILE_EXECUTE); 21 | 22 | private final int maskingValue; 23 | 24 | FileAccessMaskFlag(int maskingValue) { 25 | this.maskingValue = maskingValue; 26 | } 27 | 28 | public static MaskValueSet maskValueSet(final int mask) { 29 | return MaskValueSet.maskValueSet(mask, values()); 30 | } 31 | 32 | @Override 33 | public int intValue() { 34 | return this.maskingValue; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/constants/microsoft/filesecurity/AccessControlEntryFlag.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.constants.microsoft.filesecurity; 2 | 3 | import dev.dokan.dokan_java.masking.MaskValueEnum; 4 | import dev.dokan.dokan_java.masking.MaskValueSet; 5 | 6 | /** 7 | * Enumeration of the different ACE control flags. 8 | *

9 | * From the Microsoft documentation: An unsigned 8-bit integer that specifies a set of ACE type-specific control flags. This field can be a combination of the following values. 10 | */ 11 | public enum AccessControlEntryFlag implements MaskValueEnum { 12 | 13 | /** 14 | * Child objects that are containers, such as directories, inherit the ACE as an effective ACE. The inherited ACE is inheritable unless the NO_PROPAGATE_INHERIT_ACE bit flag is also set. 15 | */ 16 | CONTAINER_INHERIT_ACE(0x02), 17 | 18 | 19 | /** 20 | * Used with system-audit ACEs in a system access control list (SACL) to generate audit messages for failed access attempts. 21 | */ 22 | FAILED_ACCESS_ACE_FLAG(0x80), 23 | 24 | 25 | /** 26 | * Indicates an inherit-only ACE, which does not control access to the object to which it is attached. If this flag is not set, the ACE is an effective ACE that controls access to the object to which it is attached. 27 | * Both effective and inherit-only ACEs can be inherited depending on the state of the other inheritance flags. 28 | */ 29 | INHERIT_ONLY_ACE(0x08), 30 | 31 | 32 | /** 33 | * Indicates that the ACE was inherited. The system sets this bit when it propagates an inherited ACE to a child object. 34 | */ 35 | INHERITED_ACE(0x10), 36 | 37 | 38 | /** 39 | * If the ACE is inherited by a child object, the system clears the OBJECT_INHERIT_ACE and CONTAINER_INHERIT_ACE flags in the inherited ACE. This prevents the ACE from being inherited by subsequent generations of objects. 40 | */ 41 | NO_PROPAGATE_INHERIT_ACE(0x04), 42 | 43 | 44 | /** 45 | * Noncontainer child objects inherit the ACE as an effective ACE. 46 | * For child objects that are containers, the ACE is inherited as an inherit-only ACE unless the NO_PROPAGATE_INHERIT_ACE bit flag is also set. 47 | */ 48 | OBJECT_INHERIT_ACE(0x01), 49 | 50 | 51 | /** 52 | * Used with system-audit ACEs in a SACL to generate audit messages for successful access attempts. 53 | */ 54 | SUCCESSFUL_ACCESS_ACE_FLAG(0x40); 55 | 56 | 57 | private final int maskingValue; 58 | 59 | AccessControlEntryFlag(int maskingValue) { 60 | this.maskingValue = maskingValue; 61 | } 62 | 63 | public static MaskValueSet maskValueSet(final int mask) { 64 | return MaskValueSet.maskValueSet(mask, values()); 65 | } 66 | 67 | @Override 68 | public int intValue() { 69 | return this.maskingValue; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/constants/microsoft/filesecurity/AccessControlEntryType.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.constants.microsoft.filesecurity; 2 | 3 | import dev.dokan.dokan_java.Byteable; 4 | 5 | /** 6 | * Enumeration of possible ACE-Types. See also in the Microsoft documentation. 7 | */ 8 | public enum AccessControlEntryType implements Byteable { 9 | /** 10 | * Access-allowed ACE that uses the ACCESS_ALLOWED_ACE (section 2.4.4.2) structure. 11 | */ 12 | ACCESS_ALLOWED_ACE_TYPE((byte) 0x00), 13 | 14 | /** 15 | * Access-denied ACE that uses the ACCESS_DENIED_ACE (section 2.4.4.4) structure. 16 | */ 17 | ACCESS_DENIED_ACE_TYPE((byte) 0x01), 18 | 19 | /** 20 | * System-audit ACE that uses the SYSTEM_AUDIT_ACE (section 2.4.4.10) structure. 21 | */ 22 | SYSTEM_AUDIT_ACE_TYPE((byte) 0x02), 23 | 24 | /** 25 | * Reserved for future use. 26 | */ 27 | SYSTEM_ALARM_ACE_TYPE((byte) 0x03), 28 | 29 | /** 30 | * Reserved for future use. 31 | */ 32 | ACCESS_ALLOWED_COMPOUND_ACE_TYPE((byte) 0x04), 33 | 34 | /** 35 | * Object-specific access-allowed ACE that uses the ACCESS_ALLOWED_OBJECT_ACE (section 2.4.4.3) structure. 36 | */ 37 | ACCESS_ALLOWED_OBJECT_ACE_TYPE((byte) 0x05), 38 | 39 | /** 40 | * Object-specific access-denied ACE that uses the ACCESS_DENIED_OBJECT_ACE (section 2.4.4.5) structure. 41 | */ 42 | ACCESS_DENIED_OBJECT_ACE_TYPE((byte) 0x06), 43 | 44 | /** 45 | * Object-specific system-audit ACE that uses the SYSTEM_AUDIT_OBJECT_ACE (section 2.4.4.11) structure. 46 | */ 47 | SYSTEM_AUDIT_OBJECT_ACE_TYPE((byte) 0x07), 48 | 49 | /** 50 | * Reserved for future use. 51 | */ 52 | SYSTEM_ALARM_OBJECT_ACE_TYPE((byte) 0x08), 53 | 54 | /** 55 | * Access-allowed callback ACE that uses the ACCESS_ALLOWED_CALLBACK_ACE (section 2.4.4.6) structure. 56 | */ 57 | ACCESS_ALLOWED_CALLBACK_ACE_TYPE((byte) 0x09), 58 | 59 | /** 60 | * Access-denied callback ACE that uses the ACCESS_DENIED_CALLBACK_ACE (section 2.4.4.7) structure. 61 | */ 62 | ACCESS_DENIED_CALLBACK_ACE_TYPE((byte) 0x0A), 63 | 64 | /** 65 | * Object-specific access-allowed callback ACE that uses the ACCESS_ALLOWED_CALLBACK_OBJECT_ACE (section 2.4.4.8) structure. 66 | */ 67 | ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE((byte) 0x0B), 68 | 69 | /** 70 | * Object-specific access-denied callback ACE that uses the ACCESS_DENIED_CALLBACK_OBJECT_ACE (section 2.4.4.9) structure. 71 | */ 72 | ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE((byte) 0x0C), 73 | 74 | /** 75 | * System-audit callback ACE that uses the SYSTEM_AUDIT_CALLBACK_ACE (section 2.4.4.12) structure. 76 | */ 77 | SYSTEM_AUDIT_CALLBACK_ACE_TYPE((byte) 0x0D), 78 | 79 | /** 80 | * Reserved for future use. 81 | */ 82 | SYSTEM_ALARM_CALLBACK_ACE_TYPE((byte) 0x0E), 83 | 84 | /** 85 | * Object-specific system-audit callback ACE that uses the SYSTEM_AUDIT_CALLBACK_OBJECT_ACE (section 2.4.4.14) structure. 86 | */ 87 | SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE((byte) 0x0F), 88 | 89 | /** 90 | * Reserved for future use. 91 | */ 92 | SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE((byte) 0x10), 93 | 94 | /** 95 | * Mandatory label ACE that uses the SYSTEM_MANDATORY_LABEL_ACE (section 2.4.4.13) structure. 96 | */ 97 | SYSTEM_MANDATORY_LABEL_ACE_TYPE((byte) 0x11), 98 | 99 | /** 100 | * Resource attribute ACE that uses the SYSTEM_RESOURCE_ATTRIBUTE_ACE (section 2.4.4.15) 101 | */ 102 | SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE((byte) 0x12), 103 | 104 | /** 105 | * A central policy ID ACE that uses the SYSTEM_SCOPED_POLICY_ID_ACE (section 2.4.4.16) 106 | */ 107 | SYSTEM_SCOPED_POLICY_ID_ACE_TYPE((byte) 0x13); 108 | 109 | private final byte mask; 110 | 111 | AccessControlEntryType(byte mask) { 112 | this.mask = mask; 113 | } 114 | 115 | @Override 116 | public byte[] toByteArray() { 117 | return new byte[]{mask}; 118 | } 119 | 120 | @Override 121 | public int sizeOfByteArray() { 122 | return 1; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/constants/microsoft/filesecurity/SecurityDescriptorControlFlag.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.constants.microsoft.filesecurity; 2 | 3 | import dev.dokan.dokan_java.masking.MaskValueEnum; 4 | import dev.dokan.dokan_java.masking.MaskValueSet; 5 | 6 | /** 7 | * Enumeration of the different control flags that can be set in the header of a security descriptor 8 | * The documentation is taken from the official Microsoft doc. 9 | */ 10 | public enum SecurityDescriptorControlFlag implements MaskValueEnum { 11 | 12 | /** 13 | * Self-Relative 14 | * Set when the security descriptor is in self-relative format. Cleared when the security descriptor is in absolute format. 15 | */ 16 | SR(1 << 15), 17 | 18 | /** 19 | * RM Control Valid 20 | * Set to 0x1 when the Sbz1 field is to be interpreted as resource manager control bits. 21 | */ 22 | RM(1 << 14), 23 | 24 | /** 25 | * SACL Protected 26 | * Set when the SACL will be protected from inherit operations. 27 | */ 28 | PS(1 << 13), 29 | 30 | /** 31 | * DACL Protected 32 | * Set when the DACL will be protected from inherit operations. 33 | */ 34 | PD(1 << 12), 35 | 36 | /** 37 | * SACL Auto-Inherited 38 | * Set when the SACL was created through inheritance. 39 | */ 40 | SI(1 << 11), 41 | 42 | /** 43 | * DACL Auto-Inherited 44 | * Set when the DACL was created through inheritance. 45 | */ 46 | DI(1 << 10), 47 | 48 | /** 49 | * SACL Computed Inheritance Required 50 | * Set when the SACL is to be computed through inheritance. When both SC and SI are set, the resulting security descriptor sets SI; the SC setting is not preserved. 51 | */ 52 | SC(1 << 9), 53 | 54 | /** 55 | * DACL Computed Inheritance Required 56 | * Set when the DACL is to be computed through inheritance. When both DC and DI are set, the resulting security descriptor sets DI; the DC setting is not preserved. 57 | */ 58 | DC(1 << 8), 59 | 60 | /** 61 | * Server Security 62 | * Set when the caller wants the system to create a Server ACL based on the input ACL, regardless of its source (explicit or defaulting). 63 | */ 64 | SS(1 << 7), 65 | 66 | /** 67 | * DACL Trusted 68 | * Set when the ACL that is pointed to by the DACL field was provided by a trusted source and does not require any editing of compound ACEs. 69 | */ 70 | DT(1 << 6), 71 | 72 | /** 73 | * SACL Defaulted 74 | * Set when the SACL was established by default means. 75 | */ 76 | SD(1 << 5), 77 | 78 | /** 79 | * SACL Present 80 | * Set when the SACL is present on the object. 81 | */ 82 | SP(1 << 4), 83 | 84 | /** 85 | * DACL Defaulted 86 | * Set when the DACL was established by default means. 87 | */ 88 | DD(1 << 3), 89 | 90 | /** 91 | * DACL Present 92 | * Set when the DACL is present on the object. 93 | */ 94 | DP(1 << 2), 95 | 96 | /** 97 | * Group Defaulted 98 | * Set when the group was established by default means. 99 | */ 100 | GD(1 << 1), 101 | 102 | /** 103 | * Owner Defaulted 104 | * Set when the owner was established by default means. 105 | */ 106 | OD(1 << 0); 107 | 108 | private final int maskingValue; 109 | 110 | SecurityDescriptorControlFlag(int maskingValue) { 111 | this.maskingValue = maskingValue; 112 | } 113 | 114 | public static MaskValueSet maskValueSet(final int mask) { 115 | return MaskValueSet.maskValueSet(mask, values()); 116 | } 117 | @Override 118 | public int intValue() { 119 | return this.maskingValue; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/constants/microsoft/filesecurity/SidIdentifierAuthority.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.constants.microsoft.filesecurity; 2 | 3 | import dev.dokan.dokan_java.Byteable; 4 | 5 | /** 6 | * Enumeration of Well known SidIdentifierAuthorities 7 | */ 8 | public enum SidIdentifierAuthority implements Byteable { 9 | 10 | /** 11 | * Specifies the NULL SID authority. It defines only the NULL well-known-SID: S-1-0-0. 12 | */ 13 | NULL_SID_AUTHORITY(0), 14 | 15 | /** 16 | * Specifies the World SID authority. It only defines the Everyone well-known-SID: S-1-1-0. 17 | */ 18 | WORLD_SID_AUTHORITY(1), 19 | 20 | /** 21 | * Specifies the Local SID authority. It defines only the Local well-known-SID: S-1-2-0. 22 | */ 23 | LOCAL_SID_AUTHORITY(2), 24 | 25 | /** 26 | * Specifies the Creator SID authority. It defines the Creator Owner, Creator Group, and Creator Owner Server well-known-SIDs: S-1-3-0, S-1-3-1, and S-1-3-2. These SIDs are used as placeholders in an access control list (ACL) and are replaced by the user, group, and machine SIDs of the security principal. 27 | */ 28 | CREATOR_SID_AUTHORITY(3), 29 | 30 | /** 31 | * Not used. 32 | */ 33 | NON_UNIQUE_AUTHORITY(4), 34 | 35 | /** 36 | * Specifies the Windows NT operating system security subsystem SID authority. It defines all other SIDs in the forest. 37 | */ 38 | SECURITY_NT_AUTHORITY(5), 39 | 40 | /** 41 | * Specifies the application package authority. It defines application capability SIDs. 42 | */ 43 | SECURITY_APP_PACKAGE_AUTHORITY(15), 44 | 45 | /** 46 | * Specifies the Mandatory label authority. It defines the integrity level SIDs. 47 | */ 48 | SECURITY_MANDATORY_LABEL_AUTHORITY(16), 49 | 50 | /** 51 | * Specifies the Scoped Policy Authority. It defines all other scoped policy SIDs in the forest. 52 | */ 53 | SECURITY_SCOPED_POLICY_ID_AUTHORITY(17), 54 | 55 | /** 56 | * Specifies the authentication authority asserting the client’s identity. It defines only the following well-known SIDs: S-1-18-1, and S-1-18-2. 57 | */ 58 | SECURITY_AUTHENTICATION_AUTHORITY(18); 59 | 60 | private int id; 61 | 62 | SidIdentifierAuthority(int id) { 63 | this.id = id; 64 | } 65 | 66 | @Override 67 | public byte[] toByteArray() { 68 | return new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, (byte) id}; 69 | } 70 | 71 | @Override 72 | public int sizeOfByteArray() { 73 | return 6; 74 | } 75 | 76 | /** 77 | * TODO: can be improved by exception handling 78 | * 79 | * @param id 80 | * @return 81 | */ 82 | public static SidIdentifierAuthority fromInt(int id) { 83 | for (SidIdentifierAuthority idAuth : values()) { 84 | if (idAuth.id == id) { 85 | return idAuth; 86 | } 87 | } 88 | return null; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/examples/DirListingFileSystem.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.examples; 2 | 3 | 4 | import com.sun.jna.Pointer; 5 | import com.sun.jna.WString; 6 | import com.sun.jna.platform.win32.WinNT; 7 | import com.sun.jna.ptr.IntByReference; 8 | import com.sun.jna.ptr.LongByReference; 9 | import dev.dokan.dokan_java.DokanFileSystemStub; 10 | import dev.dokan.dokan_java.DokanOperations; 11 | import dev.dokan.dokan_java.DokanUtils; 12 | import dev.dokan.dokan_java.FileSystemInformation; 13 | import dev.dokan.dokan_java.Unsigned; 14 | import dev.dokan.dokan_java.constants.microsoft.CreateDisposition; 15 | import dev.dokan.dokan_java.constants.microsoft.CreateOption; 16 | import dev.dokan.dokan_java.constants.microsoft.NtStatuses; 17 | import dev.dokan.dokan_java.masking.EnumInteger; 18 | import dev.dokan.dokan_java.masking.MaskValueSet; 19 | import dev.dokan.dokan_java.structure.ByHandleFileInformation; 20 | import dev.dokan.dokan_java.structure.DokanFileInfo; 21 | import dev.dokan.dokan_java.structure.DokanIOSecurityContext; 22 | 23 | import java.io.IOException; 24 | import java.nio.file.FileStore; 25 | import java.nio.file.Files; 26 | import java.nio.file.Path; 27 | import java.nio.file.attribute.DosFileAttributes; 28 | import java.util.concurrent.atomic.AtomicLong; 29 | import java.util.stream.Stream; 30 | 31 | /** 32 | * This filesystem shows the content of a given directory and it sub directories 33 | */ 34 | public class DirListingFileSystem extends DokanFileSystemStub { 35 | 36 | private final AtomicLong handleHandler; 37 | private final FileStore fileStore; 38 | private final Path root; 39 | 40 | public DirListingFileSystem(Path root, FileSystemInformation fileSystemInformation) { 41 | super(fileSystemInformation); 42 | this.root = root; 43 | this.handleHandler = new AtomicLong(0); 44 | FileStore tmp = null; 45 | try { 46 | tmp = Files.getFileStore(this.root); 47 | } catch (IOException e) { 48 | //Log message 49 | } 50 | this.fileStore = tmp; 51 | } 52 | 53 | @Override 54 | public int zwCreateFile(WString rawPath, DokanIOSecurityContext securityContext, int rawDesiredAccess, int rawFileAttributes, int rawShareAccess, int rawCreateDisposition, int rawCreateOptions, DokanFileInfo dokanFileInfo) { 55 | Path p = getrootedPath(rawPath); 56 | 57 | //the files must exist and we are read only here 58 | CreateDisposition openOption = EnumInteger.enumFromInt(rawCreateDisposition, CreateDisposition.values()); 59 | if (Files.exists(p)) { 60 | switch (openOption) { 61 | case FILE_CREATE: 62 | return NtStatuses.STATUS_OBJECT_NAME_COLLISION; 63 | case FILE_OPEN: 64 | case FILE_OPEN_IF: 65 | break; 66 | case FILE_OVERWRITE: 67 | case FILE_OVERWRITE_IF: 68 | case FILE_SUPERSEDE: 69 | return NtStatuses.STATUS_ACCESS_DENIED; 70 | default: 71 | return NtStatuses.STATUS_UNSUCCESSFUL; 72 | } 73 | } else { 74 | switch (openOption) { 75 | case FILE_CREATE: 76 | case FILE_OPEN_IF: 77 | case FILE_OVERWRITE_IF: 78 | case FILE_SUPERSEDE: 79 | return NtStatuses.STATUS_ACCESS_DENIED; 80 | case FILE_OPEN: 81 | case FILE_OVERWRITE: 82 | return NtStatuses.STATUS_OBJECT_NAME_NOT_FOUND; 83 | default: 84 | return NtStatuses.STATUS_UNSUCCESSFUL; 85 | } 86 | 87 | } 88 | 89 | if (Files.isDirectory(p)) { 90 | if (MaskValueSet.maskValueSet(rawCreateOptions, CreateOption.values()).contains(CreateOption.FILE_NON_DIRECTORY_FILE)) { 91 | return NtStatuses.STATUS_FILE_IS_A_DIRECTORY; 92 | } else { 93 | dokanFileInfo.IsDirectory = 1; 94 | } 95 | } 96 | 97 | @Unsigned long val = this.handleHandler.incrementAndGet(); 98 | if (val == 0) { 99 | val = this.handleHandler.incrementAndGet(); 100 | } 101 | 102 | dokanFileInfo.Context = val; 103 | 104 | return NtStatuses.STATUS_SUCCESS; 105 | } 106 | 107 | @Override 108 | public void cleanup(WString rawPath, DokanFileInfo dokanFileInfo) { 109 | Path p = getrootedPath(rawPath); 110 | //nothing to do 111 | } 112 | 113 | @Override 114 | public void closeFile(WString rawPath, DokanFileInfo dokanFileInfo) { 115 | Path p = getrootedPath(rawPath); 116 | dokanFileInfo.Context = 0; 117 | } 118 | 119 | @Override 120 | public int getFileInformation(WString rawPath, ByHandleFileInformation handleFileInfo, DokanFileInfo dokanFileInfo) { 121 | Path p = getrootedPath(rawPath); 122 | if (dokanFileInfo.Context == 0) { 123 | return NtStatuses.STATUS_INVALID_HANDLE; 124 | } 125 | try { 126 | getFileInformation(p).copyTo(handleFileInfo); 127 | return NtStatuses.STATUS_SUCCESS; 128 | } catch (IOException e) { 129 | return NtStatuses.STATUS_IO_DEVICE_ERROR; 130 | } 131 | } 132 | 133 | private ByHandleFileInformation getFileInformation(Path p) throws IOException { 134 | DosFileAttributes attr = Files.readAttributes(p, DosFileAttributes.class); 135 | long index = 0; 136 | if (attr.fileKey() != null) { 137 | index = (long) attr.fileKey(); 138 | } 139 | @Unsigned int fileAttr = 0; 140 | fileAttr |= attr.isArchive() ? WinNT.FILE_ATTRIBUTE_ARCHIVE : 0; 141 | fileAttr |= attr.isSystem() ? WinNT.FILE_ATTRIBUTE_SYSTEM : 0; 142 | fileAttr |= attr.isHidden() ? WinNT.FILE_ATTRIBUTE_HIDDEN : 0; 143 | fileAttr |= attr.isReadOnly() ? WinNT.FILE_ATTRIBUTE_READONLY : 0; 144 | fileAttr |= attr.isDirectory() ? WinNT.FILE_ATTRIBUTE_DIRECTORY : 0; 145 | fileAttr |= attr.isSymbolicLink() ? WinNT.FILE_ATTRIBUTE_REPARSE_POINT : 0; 146 | 147 | if (fileAttr == 0) { 148 | fileAttr |= WinNT.FILE_ATTRIBUTE_NORMAL; 149 | } 150 | 151 | return new ByHandleFileInformation(p.getFileName(), fileAttr, attr.creationTime(), attr.lastAccessTime(), attr.lastModifiedTime(), this.volumeSerialnumber, attr.size(), index); 152 | } 153 | 154 | @Override 155 | public int findFiles(WString rawPath, DokanOperations.FillWin32FindData rawFillFindData, DokanFileInfo dokanFileInfo) { 156 | Path path = getrootedPath(rawPath); 157 | if (dokanFileInfo.Context == 0) { 158 | return NtStatuses.STATUS_INVALID_HANDLE; 159 | } 160 | try (Stream stream = Files.list(path)) { 161 | stream.map(p -> { 162 | try { 163 | return getFileInformation(path.resolve(p)).toWin32FindData(); 164 | } catch (IOException e) { 165 | return null; 166 | } 167 | }).forEach(file -> { 168 | if (file != null) { 169 | rawFillFindData.fillWin32FindData(file, dokanFileInfo); 170 | } 171 | }); 172 | return NtStatuses.STATUS_SUCCESS; 173 | } catch (IOException e) { 174 | return NtStatuses.STATUS_IO_DEVICE_ERROR; 175 | } 176 | } 177 | 178 | @Override 179 | public int getDiskFreeSpace(LongByReference freeBytesAvailable, LongByReference totalNumberOfBytes, LongByReference totalNumberOfFreeBytes, DokanFileInfo dokanFileInfo) { 180 | if (this.fileStore == null) { 181 | return NtStatuses.STATUS_UNSUCCESSFUL; 182 | } else { 183 | try { 184 | freeBytesAvailable.setValue(fileStore.getUsableSpace()); 185 | totalNumberOfBytes.setValue(fileStore.getTotalSpace()); 186 | totalNumberOfFreeBytes.setValue(fileStore.getUnallocatedSpace()); 187 | return NtStatuses.STATUS_SUCCESS; 188 | } catch (IOException e) { 189 | return NtStatuses.STATUS_IO_DEVICE_ERROR; 190 | } 191 | } 192 | } 193 | 194 | @Override 195 | public int getVolumeInformation(Pointer rawVolumeNameBuffer, int rawVolumeNameSize, IntByReference rawVolumeSerialNumber, IntByReference rawMaximumComponentLength, IntByReference rawFileSystemFlags, Pointer rawFileSystemNameBuffer, int rawFileSystemNameSize, DokanFileInfo dokanFileInfo) { 196 | rawVolumeNameBuffer.setWideString(0L, DokanUtils.trimStrToSize(this.volumeName, rawVolumeNameSize)); 197 | rawVolumeSerialNumber.setValue(this.volumeSerialnumber); 198 | rawMaximumComponentLength.setValue(this.fileSystemInformation.getMaxComponentLength()); 199 | rawFileSystemFlags.setValue(this.fileSystemInformation.getFileSystemFeatures().intValue()); 200 | rawFileSystemNameBuffer.setWideString(0L, DokanUtils.trimStrToSize(this.fileSystemInformation.getFileSystemName(), rawFileSystemNameSize)); 201 | return NtStatuses.STATUS_SUCCESS; 202 | } 203 | 204 | private Path getrootedPath(WString rawPath) { 205 | String unixPath = rawPath.toString().replace('\\', '/'); 206 | String relativeUnixPath = unixPath; 207 | if(unixPath.startsWith("/")) 208 | relativeUnixPath = unixPath.length()==1?"":unixPath.substring(1); // if it is already the root, we return the empty string 209 | return root.resolve(relativeUnixPath); 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/examples/MirrorFileSystem.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.examples; 2 | 3 | /** 4 | * Mirrors a given directory. 5 | * 6 | * This class is currently a stub. Help to implement it (; 7 | */ 8 | public class MirrorFileSystem { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/masking/EnumInteger.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.masking; 2 | 3 | /** 4 | * An EnumInteger is an enum that is represented by an 32bit integer value. 5 | */ 6 | public interface EnumInteger extends IntegerConvertible { 7 | 8 | /** 9 | * Converts an 32bit integer into an object. 10 | *

11 | * The class of the object implements this interface and all whished/possible values to check for a match are given as parameter. 12 | * 13 | * @param value The 32bit integer value to be converted 14 | * @param enumValues Array of possible objects which can be represented as a 32bit integer 15 | * @param Class which implements the EnumInteger interface. 16 | * @return Object which has the same bitmask as value and is a subclass of EnumInteger 17 | * 18 | * @throws IllegalArgumentException if none of the EnumIntegers equals the given value 19 | */ 20 | static & EnumInteger> T enumFromInt(final int value, final T[] enumValues) { 21 | for (final T current : enumValues) { 22 | if (value == current.intValue()) { 23 | return current; 24 | } 25 | } 26 | 27 | throw new IllegalArgumentException("Invalid int value: " + value); 28 | } 29 | 30 | static & EnumInteger> T enumFromInt(final int value, final Class type) { 31 | return enumFromInt(value, type.getEnumConstants()); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/masking/IntegerConvertible.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.masking; 2 | 3 | /** 4 | * An IntegerConvertible is an object that is represented by an 32bit integer value. 5 | */ 6 | public interface IntegerConvertible { 7 | 8 | /** 9 | * Returns the 32bit integer value which represents this object. 10 | * 11 | * @return the value representing this object. 12 | */ 13 | int intValue(); 14 | 15 | } -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/masking/MaskValueEnum.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.masking; 2 | 3 | 4 | public interface MaskValueEnum extends EnumInteger { 5 | 6 | default int maskingValue() { 7 | return intValue(); 8 | } 9 | } -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/masking/MaskValueSet.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.masking; 2 | 3 | import dev.dokan.dokan_java.constants.dokany.MountOption; 4 | import dev.dokan.dokan_java.constants.microsoft.FileSystemFlag; 5 | 6 | import java.util.Collection; 7 | import java.util.EnumSet; 8 | import java.util.Set; 9 | 10 | /** 11 | * Used to store multiple {@link MaskValueEnum} values such as {@link FileSystemFlag} and {@link MountOption}. 12 | * 13 | * @param Type of {@link EnumInteger} 14 | */ 15 | public interface MaskValueSet & MaskValueEnum> extends Set, IntegerConvertible { 16 | 17 | static & MaskValueEnum> MaskValueSet emptySet(Class clazz) { 18 | return new MaskValueSetImpl<>(clazz); 19 | } 20 | 21 | static & MaskValueEnum> MaskValueSet allOf(Class clazz) { 22 | return new MaskValueSetImpl(clazz.getEnumConstants()); 23 | } 24 | 25 | static & MaskValueEnum> MaskValueSet copyOf(Collection collection) { 26 | if(collection instanceof MaskValueSet) { 27 | return new MaskValueSetImpl((MaskValueSet) collection); 28 | } 29 | return new MaskValueSetImpl(collection); 30 | } 31 | 32 | static & MaskValueEnum> MaskValueSet of(T[] values) { 33 | return new MaskValueSetImpl(values); 34 | } 35 | 36 | @SafeVarargs 37 | static & MaskValueEnum> MaskValueSet of(T first, T... others) { 38 | return new MaskValueSetImpl<>(first, others); 39 | } 40 | 41 | /** 42 | * Creates a set of MaskValueEnums which corresponds to the bit flag given as an 32bit integer. 43 | *

44 | * The type of the set is the enum class of the input array 45 | * 46 | * @param intValue the integer value of the combined bitflag 47 | * @param allEnumValues all possible values of this MaskValueEnum 48 | * @param enum type of the array implementing the MaskValueEnum interface 49 | * @return a set of MaskValueEnum values whose mask were set in the intValue 50 | */ 51 | static & MaskValueEnum> MaskValueSet maskValueSet(final int intValue, final T[] allEnumValues) { 52 | MaskValueSet elements = new MaskValueSetImpl<>(allEnumValues[0].getDeclaringClass()); 53 | int remainingValues = intValue; 54 | for (T current : allEnumValues) { 55 | int mask = current.intValue(); 56 | 57 | if ((remainingValues & mask) == mask) { 58 | elements.add(current); 59 | remainingValues -= mask; 60 | } 61 | } 62 | return elements; 63 | } 64 | 65 | static & MaskValueEnum> MaskValueSet maskValueSet(final int intValue, final Class type) { 66 | return maskValueSet(intValue, type.getEnumConstants()); 67 | } 68 | 69 | void add(T... items); 70 | 71 | EnumSet elements(); 72 | 73 | MaskValueSet clone(); 74 | 75 | } -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/masking/MaskValueSetImpl.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.masking; 2 | 3 | 4 | import java.util.AbstractSet; 5 | import java.util.Arrays; 6 | import java.util.Collection; 7 | import java.util.EnumSet; 8 | import java.util.Iterator; 9 | import java.util.Objects; 10 | 11 | 12 | public class MaskValueSetImpl & MaskValueEnum> extends AbstractSet implements MaskValueSet, Cloneable { 13 | 14 | private final EnumSet elements; 15 | 16 | public MaskValueSetImpl(Class clazz) { 17 | this.elements = EnumSet.noneOf(clazz); 18 | } 19 | 20 | public MaskValueSetImpl(MaskValueSet set) { 21 | this.elements = EnumSet.copyOf(set.elements()); 22 | } 23 | 24 | public MaskValueSetImpl(Collection collection) { 25 | this.elements = EnumSet.copyOf(collection); 26 | } 27 | 28 | @SuppressWarnings("unchecked") 29 | public MaskValueSetImpl(T[] values) { 30 | this((Class) values.getClass().getComponentType()); 31 | 32 | add(values); 33 | } 34 | 35 | @SafeVarargs 36 | public MaskValueSetImpl(T first, T... others) { 37 | this.elements = EnumSet.of(first, others); 38 | } 39 | 40 | @SafeVarargs 41 | @Override 42 | public final void add(T... items) { 43 | if (items == null) { 44 | throw new IllegalArgumentException("Adding null is not allowed."); 45 | } 46 | 47 | Arrays.stream(items).filter(Objects::nonNull).forEach(this::add); 48 | } 49 | 50 | @Override 51 | public EnumSet elements() { 52 | return this.elements.clone(); 53 | } 54 | 55 | @SuppressWarnings("MethodDoesntCallSuperMethod") //Yes, that's okay here 56 | @Override 57 | public MaskValueSet clone() { 58 | return new MaskValueSetImpl<>(this.elements); 59 | } 60 | 61 | @Override 62 | public int intValue() { 63 | int toReturn = 0; 64 | for (final T current : this.elements) { 65 | toReturn |= current.intValue(); 66 | } 67 | return toReturn; 68 | } 69 | 70 | @Override 71 | public boolean add(final T e) { 72 | return this.elements.add(e); 73 | } 74 | 75 | @Override 76 | public Iterator iterator() { 77 | return this.elements.iterator(); 78 | } 79 | 80 | @Override 81 | public int size() { 82 | return this.elements.size(); 83 | } 84 | 85 | @Override 86 | public String toString() { 87 | return "EnumIntegerSet(elements=" + this.elements + ")"; 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/structure/DokanAccessState.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.structure; 2 | 3 | 4 | import com.sun.jna.Structure; 5 | import com.sun.jna.platform.win32.WinNT; 6 | import dev.dokan.dokan_java.Unsigned; 7 | 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | 12 | /** 13 | * This is a Dokan specific implementation of the ACCESS_STATE structure of the windows kernel. 14 | * 15 | * @see Microsoft Documentation 16 | * @see Check for Traverse Privilege on IRP_MJ_CREATE. 48 | * A driver can also check for the TOKEN_IS_RESTRICTED flag. 49 | * These flags are defined in ntifs.h. 50 | */ 51 | @Unsigned 52 | public int Flags; 53 | 54 | /** 55 | * An ACCESS_MASK type that describes the access rights that have not yet been granted to the caller. 56 | * A driver uses this member to determine if the Windows security system can grant access. 57 | * If access can be granted, the driver updates the PreviouslyGrantedAccess and RemainingDesiredAccess members accordingly. 58 | */ 59 | @Unsigned 60 | public int RemainingDesiredAccess; 61 | 62 | /** 63 | * An ACCESS_MASK type that specifies the information about access that has already been granted to the caller of one of the Security Reference Monitor Routines 64 | * The Windows security system grants certain rights based on the privileges of the caller, such as traverse right (the ability to traverse through a directory as part of opening a subdirectory or file). 65 | */ 66 | @Unsigned 67 | public int PreviouslyGrantedAccess; 68 | 69 | /** 70 | * An ACCESS_MASK type that contains the original access rights that were requested by the caller. 71 | */ 72 | @Unsigned 73 | public int OriginalDesiredAccess; 74 | 75 | /** 76 | * A self relative security descriptor that contains security information for the object that this access relates to. 77 | */ 78 | public WinNT.SECURITY_DESCRIPTOR_RELATIVE SecurityDescriptor; 79 | 80 | /** 81 | * A UNICODE_STRING structure that contains the object name string for the access. This member is used for auditing. 82 | */ 83 | public UnicodeString ObjectName; 84 | 85 | /** 86 | * A UNICODE_STRING structure that contains the object type name string for the access. This member is used for auditing. 87 | */ 88 | public UnicodeString ObjectType; 89 | 90 | @Override 91 | protected List getFieldOrder() { 92 | return Arrays.asList(new String[]{"SecurityEvaluated", 93 | "GenerateAudit", 94 | "GenerateOnClose", 95 | "AuditPrivileges", 96 | "Flags", 97 | "RemainingDesiredAccess", 98 | "PreviouslyGrantedAccess", 99 | "OriginalDesiredAccess", 100 | "SecurityDescriptor", 101 | "ObjectName", 102 | "ObjectType"}); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/structure/DokanControl.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.structure; 2 | 3 | 4 | import com.sun.jna.Native; 5 | import com.sun.jna.NativeLong; 6 | import com.sun.jna.Pointer; 7 | import com.sun.jna.Structure; 8 | import com.sun.jna.platform.win32.WinNT; 9 | import dev.dokan.dokan_java.Unsigned; 10 | 11 | import java.util.ArrayList; 12 | import java.util.Arrays; 13 | import java.util.List; 14 | 15 | /** 16 | * Structure containing information for a single Dokan device. 17 | *

18 | * Used by dev.dokan.dokan_java.NativeMethods#DokanGetMountPointList(boolean, LongByReference)} and dev.dokan.dokan_java.NativeMethods#DokanReleaseMountPointList(Pointer). 19 | */ 20 | public class DokanControl extends Structure implements Structure.ByReference { 21 | 22 | /** 23 | * File System Type 24 | */ 25 | @Unsigned 26 | public int Type; 27 | 28 | /** 29 | * Mount point. Can be "M:\" (drive letter) or "C:\mount\dokan" (path in NTFS) 30 | */ 31 | public char[] MountPoint = new char[WinNT.MAX_PATH]; 32 | 33 | /** 34 | * UNC name used for network volume 35 | */ 36 | public char[] UNCName = new char[64]; 37 | 38 | /** 39 | * Disk Device Name 40 | */ 41 | public char[] DeviceName = new char[64]; 42 | 43 | /** 44 | * Volume Device Object 45 | */ 46 | public Pointer DeviceObject; 47 | 48 | /** 49 | * Session ID of calling process 50 | */ 51 | @Unsigned 52 | public int SessionId; 53 | 54 | public DokanControl(Pointer p) { 55 | this(p, 0); 56 | } 57 | 58 | public DokanControl(Pointer p, long currentOffset) { 59 | super(p); 60 | this.Type = p.getInt(currentOffset); 61 | currentOffset += NativeLong.SIZE; 62 | this.MountPoint = p.getCharArray(currentOffset, WinNT.MAX_PATH); 63 | currentOffset += WinNT.MAX_PATH * 2; 64 | this.UNCName = p.getCharArray(currentOffset, 64); 65 | currentOffset += 64 * 2; 66 | this.DeviceName = p.getCharArray(currentOffset, 64); 67 | currentOffset += 64 * 2; 68 | this.DeviceObject = new Pointer(p.getLong(currentOffset)); 69 | currentOffset += Native.POINTER_SIZE; 70 | this.SessionId = p.getInt(currentOffset); 71 | } 72 | 73 | 74 | @Override 75 | protected List getFieldOrder() { 76 | return Arrays.asList("Type", "MountPoint", "UNCName", "DeviceName", "DeviceObject", "SessionId"); 77 | } 78 | 79 | /** 80 | * Creates a java {@link List} of {@link DokanControl} structures given the pointer returned by NativeMethods#DokanGetMountPointList(boolean, LongByReference).
81 | *
82 | * Implementation note:
83 | * Length is an unsigned 32-bit int. Java only supports arrays and lists up to an index size of 231-1 ({@link Integer#MAX_VALUE Integer.MAX_VALUE}). 84 | * A list that exceeds this size is unrealistic (it would need at least 2 GB of space, even if it only stored unique Byte-Objects). 85 | * Any value that exceeds {@link Integer#MAX_VALUE Integer.MAX_VALUE}, has it's 32nd bit set (at least when using Two's complement for storing it). 86 | * The 32nd bit defines the sign of a java int, therefore a {@code length < 0} indicates that this threshold has been reached. 87 | * In this case the application crashes ("fail-fast") to allow someone to take care of this issue. 88 | * 89 | * @param start the initial pointer returned by the native method 90 | * @param length the number of elements in the array. Also acquired with the native method call. 91 | * @return a list of DokanControl structures 92 | */ 93 | public static List getDokanControlList(Pointer start, @Unsigned int length) { //TODO Relocate 94 | List list = new ArrayList<>(); 95 | 96 | if(length < 0) { 97 | throw new AssertionError(String.format("Illegal length: %s (%d)", Integer.toUnsignedString(length), length)); 98 | } 99 | if (length != 0) { 100 | long offset = 0; 101 | for(int i = 0; i < length; i++) { 102 | DokanControl control = new DokanControl(start, offset); 103 | list.add(control); 104 | offset += control.size(); 105 | } 106 | } 107 | return list; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/structure/DokanFileInfo.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.structure; 2 | 3 | 4 | import com.sun.jna.Structure; 5 | import dev.dokan.dokan_java.DokanNativeMethods; 6 | import dev.dokan.dokan_java.DokanOperations; 7 | import dev.dokan.dokan_java.Unsigned; 8 | import dev.dokan.dokan_java.UnsignedNumbers; 9 | 10 | import java.util.Arrays; 11 | import java.util.List; 12 | 13 | /** 14 | * Dokan file information on the current operation. 15 | * 16 | * @see Dokany Documentation of PDOKAN_FILE_INFO 17 | */ 18 | public class DokanFileInfo extends Structure implements Structure.ByReference { 19 | 20 | /** 21 | * Context that can be used to carry information between operation. The context can carry whatever type like {@link com.sun.jna.platform.win32.WinNT.HANDLE}, {@link Structure}, {@link com.sun.jna.ptr.IntByReference}, 22 | * {@link com.sun.jna.Pointer} that will help the implementation understand the request context of the event. 23 | */ 24 | @Unsigned 25 | public long Context; 26 | 27 | /** 28 | * Flag if the file has to be delete during {@link DokanOperations#Cleanup} event. 29 | */ 30 | public byte DeleteOnClose; 31 | 32 | /** 33 | * Reserved. Used internally by Dokan library. Never modify. 34 | */ 35 | @Unsigned 36 | public long DokanContext; 37 | 38 | /** 39 | * A pointer to {@link DokanOptions} which was passed to {@link DokanNativeMethods#DokanMain}. 40 | */ 41 | public DokanOptions DokanOpts; 42 | 43 | /** 44 | * Requesting a directory file. Must be set in {@link DokanOperations#ZwCreateFile} if the file object appears to be a directory. 45 | */ 46 | public byte IsDirectory; 47 | 48 | /** 49 | * Read or write directly from data source without cache. 50 | */ 51 | public byte Nocache; 52 | 53 | /** 54 | * Read or write is paging IO. 55 | */ 56 | public byte PagingIo; 57 | 58 | /** 59 | * Process ID for the thread that originally requested a given I/O operation. 60 | */ 61 | @Unsigned 62 | public int ProcessId; 63 | 64 | /** 65 | * Read or write is synchronous IO. 66 | */ 67 | public byte SynchronousIo; 68 | 69 | /** 70 | * If true, write to the current end of file instead of using the Offset parameter. 71 | */ 72 | public byte WriteToEndOfFile; 73 | 74 | public DokanFileInfo() { 75 | } 76 | 77 | @Override 78 | protected List getFieldOrder() { 79 | return Arrays.asList("Context", "DokanContext", "DokanOpts", "ProcessId", "IsDirectory", "DeleteOnClose", "PagingIo", "SynchronousIo", "Nocache", "WriteToEndOfFile"); 80 | } 81 | 82 | public final boolean isDirectory() { 83 | return IsDirectory != 0; 84 | } 85 | 86 | public final boolean deleteOnClose() { 87 | return DeleteOnClose != 0; 88 | } 89 | 90 | public final boolean pagingIo() { 91 | return PagingIo != 0; 92 | } 93 | 94 | public final boolean synchronousIo() { 95 | return SynchronousIo != 0; 96 | } 97 | 98 | public final boolean noCache() { 99 | return Nocache != 0; 100 | } 101 | 102 | public final boolean writeToEndOfFile() { 103 | return WriteToEndOfFile != 0; 104 | } 105 | 106 | @Override 107 | public String toString() { 108 | return String.format("DokanFileInfo(Context=%s, DokanContext=%s, DokanOpts=%s, ProcessId=%s, IsDirectory=%s/%s, DeleteOnClose=%s/%s, PagingIo=%s/%s, SynchronousIo=%s/%s, Nocache=%s/%s, WriteToEndOfFile=%s/%s)", 109 | UnsignedNumbers.toUnsignedString(this.Context), 110 | UnsignedNumbers.toUnsignedString(this.DokanContext), 111 | this.DokanOpts, 112 | UnsignedNumbers.toUnsignedString(this.ProcessId), 113 | this.IsDirectory, isDirectory(), 114 | this.DeleteOnClose, deleteOnClose(), 115 | this.PagingIo, pagingIo(), 116 | this.SynchronousIo, synchronousIo(), 117 | this.Nocache, noCache(), 118 | this.WriteToEndOfFile, writeToEndOfFile()); 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/structure/DokanIOSecurityContext.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.structure; 2 | 3 | 4 | import com.sun.jna.Structure; 5 | import com.sun.jna.WString; 6 | import dev.dokan.dokan_java.Unsigned; 7 | 8 | 9 | /** 10 | * The DokanIOSecurityContext contains the Dokan specific security context of the Windows kernel create request. 11 | * It is a parameter in the {@link dev.dokan.dokan_java.DokanFileSystem#zwCreateFile(WString, DokanIOSecurityContext, int, int, int, int, int, DokanFileInfo)} function. 12 | * 13 | * @see Microsoft documentation of the original structure 14 | * @see Dokany Documentation of PDOKAN_OPTIONS 20 | */ 21 | public class DokanOptions extends Structure implements Structure.ByReference { 22 | 23 | /** 24 | * Version of the Dokan features requested (version "123" is equal to Dokan version 1.2.3). 25 | */ 26 | @Unsigned 27 | public short Version = DokanNativeMethods.getMinimumRequiredDokanVersion(); 28 | 29 | /** 30 | * Number of threads to be used internally by Dokan library. More thread will handle more events at the same time. 31 | */ 32 | @Unsigned 33 | public short ThreadCount; 34 | 35 | /** 36 | * Features enable for the mount. It is a combination of {@link MountOption} masks. 37 | */ 38 | @Unsigned 39 | public int Options; 40 | 41 | /** 42 | * FileSystem can store anything here 43 | */ 44 | @Unsigned 45 | public long GlobalContext = 0L; 46 | 47 | /** 48 | * Mount point. It can be a drive letter like \"M:\\\" or a folder path \"C:\\mount\\dokany\" on a NTFS partition. 49 | */ 50 | public WString MountPoint; 51 | 52 | /** 53 | * UNC name used for the Network Redirector. 54 | * 55 | * @see Support for UNC Naming 56 | */ 57 | public WString UNCName; 58 | 59 | /** 60 | * Max timeout in milliseconds of each request before Dokan gives up to wait events to complete. 61 | */ 62 | @Unsigned 63 | public int Timeout; 64 | 65 | /** 66 | * Allocation Unit Size of the volume. This will affect the file size. 67 | */ 68 | @Unsigned 69 | public int AllocationUnitSize; 70 | 71 | /** 72 | * Sector Size of the volume. This will affect then file size. 73 | */ 74 | @Unsigned 75 | public int SectorSize; 76 | 77 | public DokanOptions() { 78 | 79 | } 80 | 81 | public DokanOptions(String mountPoint, @Unsigned short threadCount, MaskValueSet mountOptions, String uncName, @Unsigned int timeout, @Unsigned int allocationUnitSize, @Unsigned int sectorSize) { 82 | MountPoint = new WString(mountPoint); 83 | ThreadCount = threadCount; 84 | Options = mountOptions.intValue(); 85 | if (uncName != null) { 86 | UNCName = new WString(uncName); 87 | } else { 88 | UNCName = null; 89 | } 90 | Timeout = timeout; 91 | AllocationUnitSize = allocationUnitSize; 92 | SectorSize = sectorSize; 93 | } 94 | 95 | public MaskValueSet getMountOptions() { 96 | return MaskValueSet.maskValueSet(this.Options, MountOption.values()); 97 | } 98 | 99 | @Override 100 | protected List getFieldOrder() { 101 | return Arrays.asList("Version", "ThreadCount", "Options", "GlobalContext", "MountPoint", "UNCName", "Timeout", "AllocationUnitSize", "SectorSize"); 102 | } 103 | 104 | @Override 105 | public String toString() { 106 | return String.format("DeviceOptions(Version=%s, ThreadCount=%s, Options=%s, mountOptions=%s, GlobalContext=%s, MountPoint=%s, UNCName=%s, Timeout=%s, AllocationUnitSize=%s, SectorSize=%s)", 107 | UnsignedNumbers.toUnsignedString(this.Version), 108 | UnsignedNumbers.toUnsignedString(this.ThreadCount), 109 | UnsignedNumbers.toUnsignedString(this.Options), 110 | this.getMountOptions(), 111 | UnsignedNumbers.toUnsignedString(this.GlobalContext), 112 | this.MountPoint, 113 | this.UNCName, 114 | UnsignedNumbers.toUnsignedString(this.Timeout), 115 | UnsignedNumbers.toUnsignedString(this.AllocationUnitSize), 116 | UnsignedNumbers.toUnsignedString(this.SectorSize)); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/structure/UnicodeString.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.structure; 2 | 3 | 4 | import com.sun.jna.Pointer; 5 | import com.sun.jna.Structure; 6 | import dev.dokan.dokan_java.Unsigned; 7 | 8 | 9 | /** 10 | * Supplemental class used to define Unicode Strings. 11 | *

12 | * This class is needed to fully implement {@link DokanAccessState}. 13 | * It is defined in fileinfo.h in the dokan module of the Dokany project. 14 | */ 15 | @Structure.FieldOrder({"Length", "MaximumLength", "Buffer"}) 16 | public class UnicodeString extends Structure { 17 | 18 | /** 19 | * The length, in bytes, of the string stored in {@link UnicodeString#Buffer}. 20 | */ 21 | @Unsigned 22 | public short Length; 23 | 24 | /** 25 | * The length, in bytes, of {@link UnicodeString#Buffer}. 26 | */ 27 | @Unsigned 28 | public short MaximumLength; 29 | 30 | /** 31 | * Pointer to a buffer used to contain a string of wide characters. 32 | */ 33 | public Pointer Buffer; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/structure/filesecurity/AccessAllowedACE.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.structure.filesecurity; 2 | 3 | import dev.dokan.dokan_java.masking.MaskValueSet; 4 | import dev.dokan.dokan_java.constants.microsoft.accessmaskflags.BasicAccessMaskFlag; 5 | import dev.dokan.dokan_java.constants.microsoft.filesecurity.AccessControlEntryFlag; 6 | import dev.dokan.dokan_java.constants.microsoft.filesecurity.AccessControlEntryType; 7 | 8 | import java.nio.ByteBuffer; 9 | 10 | public class AccessAllowedACE extends AccessControlEntry { 11 | 12 | MaskValueSet rights; 13 | 14 | SecurityIdentifier sid; 15 | 16 | public AccessAllowedACE(MaskValueSet flags, SecurityIdentifier sid, MaskValueSet rights) { 17 | super(AccessControlEntryType.ACCESS_ALLOWED_ACE_TYPE, flags); 18 | this.rights = rights; 19 | this.sid = sid; 20 | } 21 | 22 | @Override 23 | public byte[] toByteArray() { 24 | ByteBuffer buf = ByteBuffer.allocate(sizeOfByteArray()); 25 | buf.put(type.toByteArray()); 26 | buf.put((byte) flags.intValue()); 27 | buf.putShort(Short.reverseBytes((short) sizeOfByteArray())); 28 | buf.putInt(Integer.reverseBytes(rights.intValue())); 29 | buf.put(sid.toByteArray()); 30 | return buf.array(); 31 | } 32 | 33 | @Override 34 | public int sizeOfByteArray() { 35 | return 1 //type 36 | + 1 //flags 37 | + 2 //size 38 | + 4 //mask 39 | + sid.sizeOfByteArray(); //size of sid 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/structure/filesecurity/AccessControlEntry.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.structure.filesecurity; 2 | 3 | import dev.dokan.dokan_java.Byteable; 4 | import dev.dokan.dokan_java.constants.microsoft.filesecurity.AccessControlEntryFlag; 5 | import dev.dokan.dokan_java.constants.microsoft.filesecurity.AccessControlEntryType; 6 | import dev.dokan.dokan_java.masking.MaskValueSet; 7 | 8 | public abstract class AccessControlEntry implements Byteable { 9 | 10 | protected final AccessControlEntryType type; 11 | 12 | protected final MaskValueSet flags; 13 | 14 | protected AccessControlEntry(AccessControlEntryType type, MaskValueSet flags) { 15 | this.type = type; 16 | this.flags = flags; 17 | } 18 | 19 | @Override 20 | public abstract byte[] toByteArray(); 21 | 22 | @Override 23 | public abstract int sizeOfByteArray(); 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/structure/filesecurity/AccessControlList.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.structure.filesecurity; 2 | 3 | import dev.dokan.dokan_java.Byteable; 4 | import dev.dokan.dokan_java.constants.microsoft.filesecurity.AccessControlEntryType; 5 | 6 | import java.nio.ByteBuffer; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | /** 11 | * Object-oriented implementation of the ACL-structure used in a {@link SelfRelativeSecurityDescriptor}. 12 | * For more information, please read the official Microsoft documentation. 13 | */ 14 | public class AccessControlList implements Byteable { 15 | 16 | private enum ACLType { 17 | DACL, 18 | SACL; 19 | } 20 | 21 | private final ACLType aclType; 22 | 23 | private static final AccessControlEntryType[] allowedACEsForDaclRev2 = new AccessControlEntryType[]{ 24 | AccessControlEntryType.ACCESS_ALLOWED_ACE_TYPE, AccessControlEntryType.ACCESS_DENIED_ACE_TYPE 25 | }; 26 | 27 | private static final AccessControlEntryType[] allowedACEsForDaclRev4 = new AccessControlEntryType[]{ 28 | AccessControlEntryType.ACCESS_ALLOWED_COMPOUND_ACE_TYPE, AccessControlEntryType.ACCESS_ALLOWED_OBJECT_ACE_TYPE, AccessControlEntryType.ACCESS_DENIED_OBJECT_ACE_TYPE 29 | }; 30 | 31 | private static final AccessControlEntryType[] allowedACEsForSaclRev2 = new AccessControlEntryType[]{ 32 | AccessControlEntryType.SYSTEM_AUDIT_ACE_TYPE, AccessControlEntryType.SYSTEM_ALARM_ACE_TYPE, AccessControlEntryType.SYSTEM_MANDATORY_LABEL_ACE_TYPE, AccessControlEntryType.SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE, AccessControlEntryType.SYSTEM_SCOPED_POLICY_ID_ACE_TYPE 33 | }; 34 | 35 | private static final AccessControlEntryType[] allowedACEsForSaclRev4 = new AccessControlEntryType[]{ 36 | AccessControlEntryType.SYSTEM_AUDIT_OBJECT_ACE_TYPE, AccessControlEntryType.SYSTEM_ALARM_OBJECT_ACE_TYPE, AccessControlEntryType.SYSTEM_MANDATORY_LABEL_ACE_TYPE 37 | }; 38 | 39 | /** 40 | * AclRevision 41 | * An unsigned 8-bit value that specifies the revision of the ACL. The only two legitimate forms of ACLs supported for on-the-wire management or manipulation are type 2 and type 4. No other form is valid for manipulation on the wire. 42 | */ 43 | private final byte aclRevision; 44 | 45 | /** 46 | * Sbz1 47 | * An unsigned 8-bit value. This field is reserved and MUST be set to zero. 48 | */ 49 | private final byte sbz1 = 0; 50 | 51 | /** 52 | * Sbz2 53 | * An unsigned 16-bit integer. This field is reserved and MUST be set to zero. 54 | */ 55 | private final short sbz2 = 0; 56 | 57 | /** 58 | * List of AccessControlEntries in this ACL 59 | */ 60 | private List aces; 61 | 62 | private AccessControlList(ACLType aclType, byte aclRevision, List aces) { 63 | this.aclType = aclType; 64 | this.aclRevision = aclRevision; 65 | this.aces = new ArrayList<>(15); 66 | this.aces.addAll(aces); 67 | } 68 | 69 | 70 | @Override 71 | public byte[] toByteArray() { 72 | ByteBuffer buf = ByteBuffer.allocate(sizeOfByteArray()); 73 | buf.put(aclRevision); 74 | buf.put(sbz1); 75 | buf.putShort(Short.reverseBytes((short) sizeOfByteArray())); 76 | buf.putShort(Short.reverseBytes((short) aces.size())); 77 | buf.putShort(Short.reverseBytes(sbz2)); 78 | for (AccessControlEntry ace : aces) { 79 | buf.put(ace.toByteArray()); 80 | } 81 | return buf.array(); 82 | } 83 | 84 | @Override 85 | public int sizeOfByteArray() { 86 | return 1 // aclRevision 87 | + 1 //sbz1 88 | + 2 // aclSize 89 | + 2 //ace count 90 | + 2 //sbz2 91 | + aces.stream().reduce(0, (sum, ace) -> sum + ace.sizeOfByteArray(), Integer::sum); 92 | } 93 | 94 | public static AccessControlList createDaclRevision2(List aces) { 95 | for (AccessControlEntry ace : aces) { 96 | if (!isValidAce(ace.type, allowedACEsForDaclRev2)) { 97 | //we found an invalid ace 98 | //aborting 99 | return null; 100 | } 101 | } 102 | return new AccessControlList(ACLType.DACL, (byte) 0x02, aces); 103 | } 104 | 105 | public static AccessControlList createDaclRevision4(List aces) { 106 | for (AccessControlEntry ace : aces) { 107 | if (!isValidAce(ace.type, allowedACEsForDaclRev4)) { 108 | //we found an invalid ace 109 | //aborting 110 | return null; 111 | } 112 | } 113 | return new AccessControlList(ACLType.DACL, (byte) 0x04, aces); 114 | } 115 | 116 | public static AccessControlList createSaclRevision2(List aces) { 117 | for (AccessControlEntry ace : aces) { 118 | if (!isValidAce(ace.type, allowedACEsForSaclRev2)) { 119 | //we found an invalid ace 120 | //aborting 121 | return null; 122 | } 123 | } 124 | return new AccessControlList(ACLType.SACL, (byte) 0x02, aces); 125 | } 126 | 127 | public static AccessControlList createSaclRevision4(List aces) { 128 | for (AccessControlEntry ace : aces) { 129 | if (!isValidAce(ace.type, allowedACEsForSaclRev4)) { 130 | //we found an invalid ace 131 | //aborting 132 | return null; 133 | } 134 | } 135 | return new AccessControlList(ACLType.SACL, (byte) 0x04, aces); 136 | } 137 | 138 | private static boolean isValidAce(AccessControlEntryType aceType, AccessControlEntryType[] validACEs) { 139 | for (AccessControlEntryType validAceType : validACEs) { 140 | if (aceType.ordinal() == validAceType.ordinal()) { 141 | return true; 142 | } 143 | } 144 | return false; 145 | } 146 | 147 | } 148 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/structure/filesecurity/SecurityIdentifier.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.structure.filesecurity; 2 | 3 | import dev.dokan.dokan_java.Byteable; 4 | import dev.dokan.dokan_java.DokanException; 5 | import dev.dokan.dokan_java.constants.microsoft.filesecurity.SidIdentifierAuthority; 6 | 7 | import java.nio.ByteBuffer; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * Object-oriented implementation of the SID structure. 13 | * See also the microsoft doc. 14 | * TODO: implement add() method to add subAuthorities 15 | */ 16 | public class SecurityIdentifier implements Byteable { 17 | 18 | private final byte revision = 0x01; 19 | 20 | private final static int MAX_SUB_AUTHORITIES =15 ; 21 | 22 | private SidIdentifierAuthority sidAuth; 23 | 24 | private List subAuthorities; 25 | 26 | /** 27 | * Creates a SecurityIdentifier with the given Authority and Subauthorities. 28 | * The number of Subauthorities is bounded from above by 15 and if the List of subauthorities exceeds this limit only the first 15 will be taken. 29 | * 30 | * @param sidAuth 31 | * @param subAuthorities 32 | */ 33 | public SecurityIdentifier(SidIdentifierAuthority sidAuth, List subAuthorities) { 34 | this.sidAuth = sidAuth; 35 | this.subAuthorities = new ArrayList<>(MAX_SUB_AUTHORITIES); 36 | if (subAuthorities != null) { 37 | if (subAuthorities.size() <= MAX_SUB_AUTHORITIES) { 38 | this.subAuthorities.addAll(subAuthorities); 39 | } else { 40 | throw new DokanException("Number of sub-authorities exceeds the limit of "+MAX_SUB_AUTHORITIES+", it is: "+subAuthorities.size()); 41 | } 42 | } 43 | } 44 | 45 | @Override 46 | public byte[] toByteArray() { 47 | ByteBuffer buf = ByteBuffer.allocate(sizeOfByteArray()); 48 | buf.put(revision); 49 | buf.put((byte) subAuthorities.size()); 50 | //dont reverse the authority 51 | buf.put(sidAuth.toByteArray()); 52 | for (Integer subAuth : subAuthorities) { 53 | //but reverse the subauthorities 54 | buf.putInt(Integer.reverseBytes(subAuth)); 55 | } 56 | return buf.array(); 57 | } 58 | 59 | @Override 60 | public int sizeOfByteArray() { 61 | return 1 //revision 62 | + 1 //subauthority count 63 | + 6 //6 bytes of of the id authority 64 | + 4 * subAuthorities.size();//each subauthority consists of 4 bytes 65 | } 66 | 67 | /** 68 | * @param stringSid 69 | * @return 70 | */ 71 | public static SecurityIdentifier fromString(String stringSid) { 72 | String[] sidTokenized = stringSid.split("-"); 73 | List subAuths = new ArrayList<>(sidTokenized.length - 3); // the first tokens are S-[Revision]-[IdAuthority]-... 74 | SidIdentifierAuthority idAuth = SidIdentifierAuthority.fromInt(Integer.parseUnsignedInt(sidTokenized[2])); 75 | for (int i = 3; i < sidTokenized.length; i++) { 76 | subAuths.add(Integer.parseUnsignedInt(sidTokenized[i])); 77 | } 78 | return new SecurityIdentifier(idAuth, subAuths); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/dev/dokan/dokan_java/structure/filesecurity/SelfRelativeSecurityDescriptor.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.structure.filesecurity; 2 | 3 | import dev.dokan.dokan_java.Byteable; 4 | import dev.dokan.dokan_java.constants.microsoft.filesecurity.SecurityDescriptorControlFlag; 5 | import dev.dokan.dokan_java.masking.MaskValueSet; 6 | 7 | import java.nio.ByteBuffer; 8 | import java.util.Optional; 9 | 10 | /** 11 | * Class implementing the self-relative SelfRelativeSecurityDescriptor structure described in the Microsoft documentation. The following is a copy of the documentation: 12 | *

13 | * The SECURITY_DESCRIPTOR structure defines the security attributes of an object. These attributes specify who owns the object; who can access the object and what they can do with it; what level of audit logging can be applied to the object; and what kind of restrictions apply to the use of the security descriptor. 14 | *

15 | * Security descriptors appear in one of two forms, absolute or self-relative. 16 | *

17 | * A security descriptor is said to be in absolute format if it stores all of its security information via pointer fields, as specified in the RPC representation in section 2.4.6.1. 18 | *

19 | * A security descriptor is said to be in self-relative format if it stores all of its security information in a contiguous block of memory and expresses all of its pointer fields as offsets from its beginning. The order of appearance of pointer target fields is not required to be in any particular order; the location of the OwnerSid, GroupSid, Sacl, and/or Dacl is only based on OffsetOwner, OffsetGroup, OffsetSacl, and/or OffsetDacl pointers found in the fixed portion of the relative security descriptor. 20 | *

21 | * The self-relative form of the security descriptor is required if one wants to transmit the SECURITY_DESCRIPTOR structure as an opaque data structure for transmission in communication protocols over a wire, or for storage on secondary media; the absolute form cannot be transmitted because it contains pointers to objects that are generally not accessible to the recipient. 22 | *

23 | * When a self-relative security descriptor is transmitted over a wire, it is sent in little-endian format and requires no padding. 24 | */ 25 | public class SelfRelativeSecurityDescriptor implements Byteable { 26 | 27 | private static final byte[] EMPTY = new byte[0]; 28 | 29 | /** 30 | * Revision 31 | * An unsigned 8-bit value that specifies the revision of the SECURITY_DESCRIPTOR structure. This field MUST be set to one. 32 | */ 33 | private final byte revision = (byte) 0x01; 34 | 35 | /** 36 | * Sbz1 37 | * An unsigned 8-bit value with no meaning unless the Control RM bit is set to 0x1. If the RM bit is set to 0x1, Sbz1 is interpreted as the resource manager control bits that contain specific information<70> for the specific resource manager that is accessing the structure. The permissible values and meanings of these bits are determined by the implementation of the resource manager. 38 | */ 39 | private final byte sbz1 = (byte) 0x00; 40 | 41 | /** 42 | * Control 43 | * An unsigned 16-bit field that specifies control access bit flags. The Self Relative (SR) bit MUST be set when the security descriptor is in self-relative format, represented by a EnumIntegerSet of ControlFlags 44 | * 45 | * @see MaskValueSet 46 | * @see SecurityDescriptorControlFlag 47 | */ 48 | private MaskValueSet control; 49 | 50 | /** 51 | * OwnerSid 52 | * The SID of the owner of the object. The length of the SID MUST be a multiple of 4. This field MUST be present if the OffsetOwner field is not zero. 53 | *

54 | * This implementation guarantees the existence of a SID if the offset is not zero by only writing an offset greater zero if this structure is present. 55 | * 56 | * @see SecurityIdentifier 57 | */ 58 | private Optional ownerSid; 59 | 60 | 61 | /** 62 | * GroupSid 63 | * The SID of the group of the object. The length of the SID MUST be a multiple of 4. This field MUST be present if the OffsetOwner field is not zero. 64 | *

65 | * This implementation guarantees the existence of a SID if the offset is not zero by only writing an offset greater zero if this structure is present. 66 | * 67 | * @see SecurityIdentifier 68 | */ 69 | private Optional groupSid; 70 | 71 | /** 72 | * Sacl 73 | * The SACL of the object. The length of the SID MUST be a multiple of 4. This field MUST be present if the SP flag is set. 74 | *

75 | * This implementation guarantees the existence of a SACL if SP-flag is set by only writing the flag if this structure is present. 76 | */ 77 | private Optional sacl; 78 | 79 | /** 80 | * Dacl 81 | * The DACL of the object. The length of the SID MUST be a multiple of 4. This field MUST be present if the DP flag is set. 82 | *

83 | * This implementation guarantees the existence of a DACL if DP-flag is set by only writing the flag if this structure is present. 84 | */ 85 | private Optional dacl; 86 | 87 | /** 88 | * Creates an empty SecurityDescriptor. 89 | * 90 | * @param control 91 | */ 92 | private SelfRelativeSecurityDescriptor(MaskValueSet control) { 93 | this.control = control; 94 | this.ownerSid = Optional.empty(); 95 | this.groupSid = Optional.empty(); 96 | this.sacl = Optional.empty(); 97 | this.dacl = Optional.empty(); 98 | } 99 | 100 | private SelfRelativeSecurityDescriptor(MaskValueSet control, SecurityIdentifier ownerSid, SecurityIdentifier groupSid, AccessControlList sacl, AccessControlList dacl) { 101 | this.control = control; 102 | if (ownerSid != null) { 103 | this.ownerSid = Optional.of(ownerSid); 104 | } else { 105 | this.ownerSid = Optional.empty(); 106 | } 107 | if (groupSid != null) { 108 | this.groupSid = Optional.of(groupSid); 109 | } else { 110 | this.groupSid = Optional.empty(); 111 | } 112 | if (sacl != null) { 113 | this.sacl = Optional.of(sacl); 114 | } else { 115 | this.sacl = Optional.empty(); 116 | } 117 | if (dacl != null) { 118 | this.dacl = Optional.of(dacl); 119 | } else { 120 | this.dacl = Optional.empty(); 121 | } 122 | } 123 | 124 | @Override 125 | public byte[] toByteArray() { 126 | int offset = 20; // basic header offset: revision, sbz1, controlmask and the four offsets 127 | int offsetOwner = 0, offsetGroup = 0, offsetSacl = 0, offsetDacl = 0; 128 | if (ownerSid.isPresent()) { 129 | offsetOwner = offset; 130 | offset += ownerSid.get().sizeOfByteArray(); 131 | } 132 | if (groupSid.isPresent()) { 133 | offsetGroup = offset; 134 | offset += groupSid.get().sizeOfByteArray(); 135 | } 136 | if (sacl.isPresent()) { 137 | offsetSacl = offset; 138 | offset += sacl.get().sizeOfByteArray(); 139 | } 140 | if (dacl.isPresent()) { 141 | offsetDacl = offset; 142 | offset += dacl.get().sizeOfByteArray(); // not really necessary 143 | } 144 | //do some computations of the size 145 | ByteBuffer buf = ByteBuffer.allocate(sizeOfByteArray()); 146 | buf.put(revision); 147 | buf.put(sbz1); 148 | buf.putShort(Short.reverseBytes((short) control.intValue())); 149 | buf.putInt(Integer.reverseBytes(offsetOwner)); 150 | buf.putInt(Integer.reverseBytes(offsetGroup)); 151 | buf.putInt(Integer.reverseBytes(offsetSacl)); 152 | buf.putInt(Integer.reverseBytes(offsetDacl)); 153 | buf.put(ownerSid.map(SecurityIdentifier::toByteArray).orElse(EMPTY)); 154 | buf.put(groupSid.map(SecurityIdentifier::toByteArray).orElse(EMPTY)); 155 | buf.put(sacl.map(AccessControlList::toByteArray).orElse(EMPTY)); 156 | buf.put(dacl.map(AccessControlList::toByteArray).orElse(EMPTY)); 157 | return buf.array(); 158 | } 159 | 160 | @Override 161 | public int sizeOfByteArray() { 162 | return 2 // the first fixed bytes (revision and sbz1) 163 | + 2 // the 16bit big control mask 164 | + 4 * 4 // the 4 32bit integer offset values indicating the offset to the following variable length data fields 165 | + ownerSid.map(SecurityIdentifier::sizeOfByteArray).orElse(0) 166 | + groupSid.map(SecurityIdentifier::sizeOfByteArray).orElse(0) 167 | + sacl.map(AccessControlList::sizeOfByteArray).orElse(0) 168 | + dacl.map(AccessControlList::sizeOfByteArray).orElse(0); 169 | } 170 | 171 | public static SelfRelativeSecurityDescriptor createEmptySD(MaskValueSet flags) { 172 | if ((flags.intValue() & (SecurityDescriptorControlFlag.DP.intValue() | SecurityDescriptorControlFlag.SP.intValue())) == 0) { 173 | flags.add(SecurityDescriptorControlFlag.SR); 174 | return new SelfRelativeSecurityDescriptor(flags); 175 | } else { 176 | //wrong control flags, abort 177 | return null; 178 | } 179 | } 180 | 181 | public static SelfRelativeSecurityDescriptor createSD(MaskValueSet flags, SecurityIdentifier owner, SecurityIdentifier group, AccessControlList sacl, AccessControlList dacl) { 182 | int controlMask = flags.intValue(); 183 | if ((controlMask & SecurityDescriptorControlFlag.DP.intValue()) != 0 && dacl == null) { 184 | //abort 185 | return null; 186 | } 187 | if ((controlMask & SecurityDescriptorControlFlag.SP.intValue()) != 0 && sacl == null) { 188 | //abort 189 | return null; 190 | } 191 | flags.add(SecurityDescriptorControlFlag.SR); 192 | return new SelfRelativeSecurityDescriptor(flags, owner, group, sacl, dacl); 193 | 194 | 195 | } 196 | 197 | } 198 | -------------------------------------------------------------------------------- /src/test/java/dev/dokan/dokan_java/examples/DirListingFSTest.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.examples; 2 | 3 | import dev.dokan.dokan_java.FileSystemInformation; 4 | import dev.dokan.dokan_java.constants.dokany.MountOption; 5 | import dev.dokan.dokan_java.constants.microsoft.FileSystemFlag; 6 | import dev.dokan.dokan_java.masking.MaskValueSet; 7 | 8 | 9 | import java.io.IOException; 10 | import java.nio.file.Path; 11 | import java.nio.file.Paths; 12 | 13 | public class DirListingFSTest { 14 | 15 | 16 | public static void main(String[] args) { 17 | System.out.println("Starting Dokany MirrorFS"); 18 | 19 | Path mountPoint = Path.of("M:\\mnt\\"); 20 | MaskValueSet mountOptions = MaskValueSet.of(MountOption.STD_ERR_OUTPUT, MountOption.WRITE_PROTECTION, MountOption.CURRENT_SESSION); 21 | 22 | MaskValueSet fsFeatures = MaskValueSet.of(FileSystemFlag.READ_ONLY_VOLUME, FileSystemFlag.CASE_PRESERVED_NAMES); 23 | FileSystemInformation fsInfo = new FileSystemInformation(fsFeatures); 24 | try (DirListingFileSystem fs = new DirListingFileSystem(Paths.get("M:\\test"), fsInfo)) { 25 | fs.mount(mountPoint, mountOptions); 26 | System.in.read(); 27 | } catch (IOException e) { 28 | e.printStackTrace(); 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/dev/dokan/dokan_java/masking/EnumIntegerTest.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.masking; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | public class EnumIntegerTest { 7 | 8 | @Test 9 | void FromIntegerOverEnumToInteger() { 10 | int val = 4096; 11 | Assertions.assertEquals(val, EnumInteger.enumFromInt(val, TestEnum.values()).intValue()); 12 | } 13 | 14 | @Test 15 | void NotExistingIntThrowsException() { 16 | int val = 2; 17 | Assertions.assertThrows(IllegalArgumentException.class, () -> EnumInteger.enumFromInt(val, TestEnum.values())); 18 | } 19 | 20 | enum TestEnum implements EnumInteger { 21 | A(1), 22 | B(4096); 23 | 24 | private final int val; 25 | 26 | TestEnum(int val) { 27 | this.val = val; 28 | } 29 | 30 | @Override 31 | public int intValue() { 32 | return val; 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/dev/dokan/dokan_java/masking/MaskValueSetTest.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.masking; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | 6 | public class MaskValueSetTest { 7 | 8 | @Test 9 | void fromIntOverEnumIntegerSetToInt() { 10 | int val = TestEnum.A.intValue() | TestEnum.B.intValue() | TestEnum.C.intValue(); 11 | MaskValueSet testSet = MaskValueSet.maskValueSet(val, TestEnum.values()); 12 | Assertions.assertFalse(testSet.contains(TestEnum.D)); 13 | Assertions.assertTrue(testSet.contains(TestEnum.A)); 14 | Assertions.assertTrue(testSet.contains(TestEnum.B)); 15 | Assertions.assertTrue(testSet.contains(TestEnum.C)); 16 | Assertions.assertEquals(val, testSet.intValue()); 17 | } 18 | 19 | enum TestEnum implements MaskValueEnum { 20 | A(0x01), 21 | B(0x08), 22 | C(0x110), 23 | D(0x200); 24 | 25 | private final int val; 26 | 27 | TestEnum(int val) { 28 | this.val = val; 29 | } 30 | 31 | @Override 32 | public int intValue() { 33 | return val; 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/dev/dokan/dokan_java/structure/filesecurity/SelfRelativeSecurityDescriptorTest.java: -------------------------------------------------------------------------------- 1 | package dev.dokan.dokan_java.structure.filesecurity; 2 | 3 | 4 | import dev.dokan.dokan_java.constants.microsoft.accessmaskflags.BasicAccessMaskFlag; 5 | import dev.dokan.dokan_java.constants.microsoft.filesecurity.AccessControlEntryFlag; 6 | import dev.dokan.dokan_java.constants.microsoft.filesecurity.SecurityDescriptorControlFlag; 7 | import dev.dokan.dokan_java.constants.microsoft.filesecurity.SidIdentifierAuthority; 8 | import dev.dokan.dokan_java.masking.MaskValueSet; 9 | import org.junit.jupiter.api.Assertions; 10 | import org.junit.jupiter.api.Test; 11 | 12 | import java.nio.ByteBuffer; 13 | import java.util.ArrayList; 14 | import java.util.Arrays; 15 | import java.util.Collections; 16 | 17 | /** 18 | * TODO: the harcoded byte arrays are not directly bind to the methods, so changing some sid constant or something like this can lead to failures! 19 | * TODO: combine the selection of sid/whatever and the hardcoded array in one method 20 | *

21 | * TODO: refactor in suhc a way, that all test methods for a specific class has an own test class 22 | */ 23 | public class SelfRelativeSecurityDescriptorTest { 24 | 25 | @Test 26 | public void testControlField() { 27 | MaskValueSet control = MaskValueSet.of(SecurityDescriptorControlFlag.GD, SecurityDescriptorControlFlag.OD, SecurityDescriptorControlFlag.DD, SecurityDescriptorControlFlag.SD); 28 | ByteBuffer buf = ByteBuffer.allocate(2); 29 | 30 | Assertions.assertEquals((43 << 8 + 0) << 16, Integer.reverseBytes(control.intValue())); 31 | Assertions.assertEquals((43 << 8 + 0), Short.reverseBytes((short) control.intValue())); 32 | Assertions.assertArrayEquals(new byte[]{43, 0}, buf.putShort(Short.reverseBytes((short) control.intValue())).array()); 33 | } 34 | 35 | @Test 36 | public void testSidWithoutSubAuthorities() { 37 | SecurityIdentifier sid = new SecurityIdentifier(SidIdentifierAuthority.WORLD_SID_AUTHORITY, null); 38 | byte[] expected = new byte[]{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; 39 | Assertions.assertArrayEquals(expected, sid.toByteArray()); 40 | } 41 | 42 | @Test 43 | public void testValidEveryoneSid() { 44 | SecurityIdentifier sid = new SecurityIdentifier(SidIdentifierAuthority.WORLD_SID_AUTHORITY, Collections.singletonList(0)); 45 | Assertions.assertArrayEquals(getEveryoneSid(), sid.toByteArray()); 46 | } 47 | 48 | @Test 49 | public void testValidBuiltinAdminitstratorsSid() { 50 | ArrayList subAuths = new ArrayList<>(2); 51 | subAuths.add(32); 52 | subAuths.add(544); 53 | SecurityIdentifier sid = new SecurityIdentifier(SidIdentifierAuthority.SECURITY_NT_AUTHORITY, subAuths); 54 | Assertions.assertArrayEquals(new byte[]{0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00}, sid.toByteArray()); 55 | } 56 | 57 | @Test 58 | public void testSidFromString() { 59 | SecurityIdentifier sid = SecurityIdentifier.fromString("S-1-5-32-544"); 60 | Assertions.assertArrayEquals(new byte[]{0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00}, sid.toByteArray()); 61 | } 62 | 63 | @Test 64 | public void testAccessAllowedACE() { 65 | //set the flag 66 | MaskValueSet flags = MaskValueSet.of(AccessControlEntryFlag.CONTAINER_INHERIT_ACE, AccessControlEntryFlag.OBJECT_INHERIT_ACE); 67 | //set the mask 68 | MaskValueSet mask = MaskValueSet.of(BasicAccessMaskFlag.GENERIC_ALL); 69 | //set the sid to world sid resp. everyone 70 | SecurityIdentifier sid = SecurityIdentifier.fromString("S-1-1-0");// everyone sid 71 | //create ace 72 | AccessAllowedACE allowedACE = new AccessAllowedACE(flags, sid, mask); 73 | Assertions.assertArrayEquals(getAllowedAccessACE(), allowedACE.toByteArray()); 74 | } 75 | 76 | @Test 77 | public void testACL() { 78 | //test empty DACL rev2 79 | AccessControlList emptyDaclRev2 = AccessControlList.createDaclRevision2(new ArrayList<>(0)); 80 | Assertions.assertArrayEquals(new byte[]{0x02, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}, emptyDaclRev2.toByteArray()); 81 | //test empty DACL rev4 82 | AccessControlList emptyDaclRev4 = AccessControlList.createDaclRevision4(new ArrayList<>(0)); 83 | Assertions.assertArrayEquals(new byte[]{0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}, emptyDaclRev4.toByteArray()); 84 | //test empty SACL rev2 85 | AccessControlList emptySaclRev2 = AccessControlList.createSaclRevision2(new ArrayList<>(0)); 86 | Assertions.assertArrayEquals(new byte[]{0x02, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}, emptySaclRev2.toByteArray()); 87 | //test empty SACL rev4 88 | AccessControlList emptySaclRev4 = AccessControlList.createSaclRevision4(new ArrayList<>(0)); 89 | Assertions.assertArrayEquals(new byte[]{0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}, emptySaclRev4.toByteArray()); 90 | 91 | //test DACL rev2 with accessAllowACE 92 | MaskValueSet flags = MaskValueSet.of(AccessControlEntryFlag.CONTAINER_INHERIT_ACE, AccessControlEntryFlag.OBJECT_INHERIT_ACE); 93 | //set the mask 94 | MaskValueSet mask = MaskValueSet.of(BasicAccessMaskFlag.GENERIC_ALL); 95 | //set the sid to world sid resp. everyone 96 | SecurityIdentifier sid = SecurityIdentifier.fromString("S-1-1-0"); 97 | //create ace 98 | AccessControlList daclRev2WithAccessAllow = AccessControlList.createDaclRevision2(Collections.singletonList(new AccessAllowedACE(flags, sid, mask))); 99 | 100 | Assertions.assertArrayEquals(getAclWithAAAce(), daclRev2WithAccessAllow.toByteArray()); 101 | } 102 | 103 | @Test 104 | public void testEmptySecurityDescriptor() { 105 | MaskValueSet flags = MaskValueSet.of(SecurityDescriptorControlFlag.GD, SecurityDescriptorControlFlag.OD, SecurityDescriptorControlFlag.DD, SecurityDescriptorControlFlag.SD); 106 | byte[] expected = getEmptySelfRelativeSecurityDescriptorWithEmptyFlags(); 107 | expected[2] = 43; 108 | Assertions.assertArrayEquals(expected, SelfRelativeSecurityDescriptor.createEmptySD(flags).toByteArray()); 109 | } 110 | 111 | @Test 112 | public void testOwnerAndGroupSD() { 113 | //control 114 | MaskValueSet control = MaskValueSet.of(SecurityDescriptorControlFlag.SR, SecurityDescriptorControlFlag.SD, SecurityDescriptorControlFlag.DD); 115 | //owner 116 | SecurityIdentifier oSid = SecurityIdentifier.fromString("S-1-1-0"); 117 | //group 118 | SecurityIdentifier gSid = SecurityIdentifier.fromString("S-1-1-0"); 119 | //security descriptor 120 | SelfRelativeSecurityDescriptor sd = SelfRelativeSecurityDescriptor.createSD(control, oSid, gSid, null, null); 121 | 122 | //our expected stuff 123 | byte[] emptySD = getEmptySelfRelativeSecurityDescriptorWithEmptyFlags(); 124 | emptySD[2] = 40; //we defaulting only sacl and dacl 125 | emptySD[4] = 20; //offsets 126 | emptySD[8] = 32; 127 | byte[] sid = getEveryoneSid(); 128 | byte[] expected = concat(concat(emptySD, sid), sid); 129 | 130 | Assertions.assertArrayEquals(expected, sd.toByteArray()); 131 | } 132 | 133 | @Test 134 | public void testSDWithOwnerGroupAndDacl() { 135 | //control 136 | MaskValueSet control = MaskValueSet.of(SecurityDescriptorControlFlag.SR, SecurityDescriptorControlFlag.DP, SecurityDescriptorControlFlag.SD); 137 | //owner 138 | SecurityIdentifier oSid = SecurityIdentifier.fromString("S-1-1-0"); 139 | SecurityIdentifier gSid = SecurityIdentifier.fromString("S-1-1-0"); 140 | //ace 141 | //ace control 142 | MaskValueSet flags = MaskValueSet.of(AccessControlEntryFlag.CONTAINER_INHERIT_ACE, AccessControlEntryFlag.OBJECT_INHERIT_ACE); 143 | //set the mask 144 | MaskValueSet mask = MaskValueSet.of(BasicAccessMaskFlag.GENERIC_ALL); 145 | //create ace 146 | AccessControlList daclRev2WithAccessAllow = AccessControlList.createDaclRevision2(Collections.singletonList(new AccessAllowedACE(flags, oSid, mask))); 147 | 148 | SelfRelativeSecurityDescriptor sd = SelfRelativeSecurityDescriptor.createSD(control, oSid, gSid, null, daclRev2WithAccessAllow); 149 | 150 | //our expected stuff 151 | byte[] emptySD = getEmptySelfRelativeSecurityDescriptorWithEmptyFlags(); 152 | emptySD[2] = 36; //we defaulting only sacl and dacl is present! 153 | emptySD[4] = 20; //offsets 154 | emptySD[8] = 32; 155 | emptySD[16] = 44; 156 | byte[] sid = getEveryoneSid(); 157 | byte[] acl = new byte[]{ 158 | 0x02, //revision 159 | 0x00, //sbz1 160 | 0x1C, 161 | 0x00, //size 162 | 0x01, 0x00, //count 163 | 0x00, 0x00, //sbz2 164 | 0x00, // ace Type 165 | 0x03, //ace flags 166 | 0x14, 0x00, //ace size 167 | 0x00, 0x00, 0x00, 0x10, //access mask 168 | 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00}; 169 | byte[] expected = concat(concat(concat(emptySD, sid), sid), acl); 170 | Assertions.assertArrayEquals(expected, sd.toByteArray()); 171 | } 172 | 173 | private static byte[] getEmptySelfRelativeSecurityDescriptorWithEmptyFlags() { 174 | return new byte[]{ 175 | 0x01, //revision 176 | 0x00, //sbz1 177 | 0x00,// second half of control flag 178 | -128, //first half indicating a self relative sec. desc. 179 | 0x00, 180 | 0x00, 181 | 0x00, 182 | 0x00, //owner offset 183 | 0x00, 184 | 0x00, 185 | 0x00, 186 | 0x00, //group offset 187 | 0x00, 188 | 0x00, 189 | 0x00, 190 | 0x00, //sacl offset 191 | 0x00, 192 | 0x00, 193 | 0x00, 194 | 0x00, //dacl offset 195 | }; 196 | } 197 | 198 | private static byte[] getEveryoneSid() { 199 | return new byte[]{0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00}; 200 | } 201 | 202 | private static byte[] getAllowedAccessACE() { 203 | return new byte[]{ 204 | 0x00, // ace Type 205 | 0x03, //ace flags 206 | 0x14, 0x00, //ace size 207 | 0x00, 0x00, 0x00, 0x10, //access mask 208 | 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00}; 209 | } 210 | 211 | private static byte[] getAclWithAAAce() { 212 | return concat(new byte[]{ 213 | 0x02, //revision 214 | 0x00, //sbz1 215 | 0x1C, 216 | 0x00, //size 217 | 0x01, 0x00, //count 218 | 0x00, 0x00 //sbz2 219 | }, getAllowedAccessACE()); 220 | } 221 | 222 | private static byte[] getSDWithDaclWithAAAce() { 223 | byte[] tmp = concat(new byte[]{ 224 | 0x01, //revision 225 | 0x00, //sbz1 226 | 0x01,// first half of control flag indicating a self relative sec. desc. 227 | 36, //second half indicating sacl defaulted and dacl present 228 | 0x10, 229 | 0x00, 230 | 0x00, 231 | 0x00, //owner offset (this header = 16 Byte) 232 | 0x18, 233 | 0x00, 234 | 0x00, 235 | 0x00, //group offset (header + owner = 16 + 8) 236 | 0x00, 237 | 0x00, 238 | 0x00, 239 | 0x00, //sacl offset (zero) 240 | 0x20, 241 | 0x00, 242 | 0x00, 243 | 0x00, //dacl offset (header +owner +group = 16+8+8) 244 | }, getEveryoneSid()); 245 | tmp = concat(tmp, getEveryoneSid()); 246 | return concat(tmp, getAclWithAAAce()); 247 | } 248 | 249 | private static byte[] concat(byte[] arr1, byte[] arr2) { 250 | byte[] arr3 = Arrays.copyOf(arr1, arr1.length + arr2.length); 251 | for (int i = 0; i < arr2.length; i++) { 252 | arr3[i + arr1.length] = arr2[i]; 253 | } 254 | return arr3; 255 | } 256 | 257 | } 258 | --------------------------------------------------------------------------------