├── .gitignore ├── .project ├── LICENSE ├── README.md ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── run.sh ├── settings.gradle └── src ├── main └── java │ ├── co │ └── paralleluniverse │ │ ├── filesystem │ │ ├── FileSystemAdapter.java │ │ └── FileSystemProviderAdapter.java │ │ ├── fuse │ │ ├── AbstractFuseFilesystem.java │ │ ├── AccessConstants.java │ │ ├── DirectoryFiller.java │ │ ├── DirectoryFillerImpl.java │ │ ├── ErrorCodes.java │ │ ├── Filesystem.java │ │ ├── Fuse.java │ │ ├── FuseBufFlags.java │ │ ├── FuseException.java │ │ ├── FuseFilesystem.java │ │ ├── IoctlFlags.java │ │ ├── JNRUtil.java │ │ ├── LibDl.java │ │ ├── LibFuse.java │ │ ├── LoggedFuseFilesystem.java │ │ ├── Platform.java │ │ ├── ProcessGobbler.java │ │ ├── StructFlock.java │ │ ├── StructFuseBuf.java │ │ ├── StructFuseBufvec.java │ │ ├── StructFuseConnInfo.java │ │ ├── StructFuseContext.java │ │ ├── StructFuseFileInfo.java │ │ ├── StructFuseOperations.java │ │ ├── StructFuseOperationsIfaces.java │ │ ├── StructFusePollHandle.java │ │ ├── StructStat.java │ │ ├── StructStatvfs.java │ │ ├── StructTimeBuffer.java │ │ ├── StructTimespec.java │ │ ├── TypeMode.java │ │ ├── XAttrConstants.java │ │ ├── XattrFiller.java │ │ └── XattrListFiller.java │ │ └── javafs │ │ ├── FuseFileSystemProvider.java │ │ ├── JavaFS.java │ │ ├── ReadOnlyFileSystem.java │ │ └── ReadOnlyFileSystemProvider.java │ └── jnr │ └── ffi │ └── provider │ └── jffi │ └── ClosureHelper.java └── test └── java └── co └── paralleluniverse └── javafs ├── JFSTest.java ├── Main.java └── ZipFS.java /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /dist/ 3 | /out/ 4 | _site/ 5 | /docs/javadoc/ 6 | .idea/ 7 | *.iml 8 | /artifacts 9 | /nbproject/private/ 10 | /.nb-gradle 11 | /build 12 | /*/build/ 13 | .gradle/ 14 | .nb-gradle-properties 15 | Gemfile.lock 16 | /push_docs.sh 17 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | fuse-jna 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Parallel Universe 2 | Copyright (c) 2012-2015 Etienne Perot. All rights reserved. 3 | Copyright (c) 2015 Sergey Tselovalnikov 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are 6 | permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this list of 9 | conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, this list 12 | of conditions and the following disclaimer in the documentation and/or other materials 13 | provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED THE COPYRIGHT HOLDERS AND CONTRIBUTORS ''AS IS'' AND ANY EXPRESS OR IMPLIED 16 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 17 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR 18 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 21 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 22 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 23 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | --- 26 | 27 | Redistribution and use in source and binary forms, with or without modification, are 28 | permitted provided that the following conditions are met: 29 | 30 | 1. Redistributions of source code must retain the above copyright notice, this list of 31 | conditions and the following disclaimer. 32 | 33 | 2. Redistributions in binary form must reproduce the above copyright notice, this list 34 | of conditions and the following disclaimer in the documentation and/or other materials 35 | provided with the distribution. 36 | 37 | THIS SOFTWARE IS PROVIDED BY ETIENNE PEROT ''AS IS'' AND ANY EXPRESS OR IMPLIED 38 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 39 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR 40 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 41 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 42 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 43 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 44 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 45 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 46 | 47 | The views and conclusions contained in the software and documentation are those of the 48 | authors and should not be interpreted as representing official policies, either expressed 49 | or implied, of Etienne Perot. 50 | 51 | Copyright 2012 Etienne Perot. All rights reserved. 52 | 53 | Redistribution and use in source and binary forms, with or without modification, are 54 | permitted provided that the following conditions are met: 55 | 56 | 1. Redistributions of source code must retain the above copyright notice, this list of 57 | conditions and the following disclaimer. 58 | 59 | 2. Redistributions in binary form must reproduce the above copyright notice, this list 60 | of conditions and the following disclaimer in the documentation and/or other materials 61 | provided with the distribution. 62 | 63 | THIS SOFTWARE IS PROVIDED BY ETIENNE PEROT ''AS IS'' AND ANY EXPRESS OR IMPLIED 64 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 65 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR 66 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 67 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 68 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 69 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 70 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 71 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 72 | 73 | The views and conclusions contained in the software and documentation are those of the 74 | authors and should not be interpreted as representing official policies, either expressed 75 | or implied, of Etienne Perot. 76 | 77 | --- 78 | 79 | The MIT License (MIT) 80 | 81 | Copyright (c) 2015 Sergey Tselovalnikov 82 | 83 | Permission is hereby granted, free of charge, to any person obtaining a copy 84 | of this software and associated documentation files (the "Software"), to deal 85 | in the Software without restriction, including without limitation the rights 86 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 87 | copies of the Software, and to permit persons to whom the Software is 88 | furnished to do so, subject to the following conditions: 89 | 90 | The above copyright notice and this permission notice shall be included in all 91 | copies or substantial portions of the Software. 92 | 93 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 94 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 95 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 96 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 97 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 98 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 99 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JavaFS [![Version](http://img.shields.io/badge/version-0.1.0-blue.svg?style=flat)](https://github.com/puniverse/javafs/releases) 2 | ## Java filesystems as FUSE 3 | 4 | ## Requirements 5 | 6 | Java 7 and up. 7 | 8 | Your OS must support FUSE or have it installed. 9 | 10 | ## Usage 11 | 12 | The API consists of a single class with two methods: 13 | 14 | JavaFS.mount, which mounts a Java `FileSystem` as FUSE filesystem, and 15 | 16 | JavaFS.unmount, which unmounts a FUSE filesystem 17 | 18 | ## Test 19 | 20 | ``` 21 | ./run.sh [-r] [] 22 | ``` 23 | 24 | #### Compatibility 25 | 26 | * OS X with [MacFUSE]/[fuse4x]/[OSXFUSE] on Intel architectures 27 | * Linux with [FUSE][Linux-Fuse] on Intel, PowerPC and ARM architectures 28 | * FreeBSD with [FUSE][FreeBSD-Fuse] on Intel architectures 29 | 30 | ## Project Information 31 | 32 | This is essentially a port of [fuse-jna], by Etienne Perot, from [JNA] to [JNR], 33 | with some code copied from [jnr-fuse], by Sergey Tselovalnikov, made to work with the standard JDK [FileSystem] API. 34 | 35 | * Differences from [fuse-jna]: this project uses [JNR] rather than [JNA]. 36 | * Differences from [jnr-fuse]: this project supports Java 7 (jnr-fuse supports only Java 8), and more platforms (like Mac). 37 | * Differences from both: rather than exposing a new, specific, Java FUSE API, this project uses the standard [FileSystem] API. 38 | 39 | ## License 40 | 41 | ``` 42 | Copyright (c) 2015 Parallel Universe 43 | Copyright (c) 2012-2015 Etienne Perot 44 | Copyright (c) 2015 Sergey Tselovalnikov 45 | 46 | Redistribution and use in source and binary forms, with or without modification, are 47 | permitted provided that the following conditions are met: 48 | 49 | 1. Redistributions of source code must retain the above copyright notice, this list of 50 | conditions and the following disclaimer. 51 | 52 | 2. Redistributions in binary form must reproduce the above copyright notice, this list 53 | of conditions and the following disclaimer in the documentation and/or other materials 54 | provided with the distribution. 55 | 56 | THIS SOFTWARE IS PROVIDED THE COPYRIGHT HOLDERS AND CONTRIBUTORS ''AS IS'' AND ANY EXPRESS OR IMPLIED 57 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 58 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR 59 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 60 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 61 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 62 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 63 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 64 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 65 | ``` 66 | 67 | This is the 2-clause BSD license. 68 | 69 | [fuse-jna] is licensed under the [BSD 2-Clause License]. 70 | 71 | [jnr-fuse] is licensed under the [MIT License]. 72 | 73 | 74 | [fuse-jna]: https://github.com/EtiennePerot/fuse-jna 75 | [jnr-fuse]: https://github.com/SerCeMan/jnr-fuse 76 | [FileSystem]: http://docs.oracle.com/javase/7/docs/api/java/nio/file/FileSystems.html 77 | [JNA]: https://github.com/java-native-access/jna 78 | [JNR]: https://github.com/jnr 79 | [MacFUSE]: http://code.google.com/p/macfuse/ 80 | [fuse4x]: http://fuse4x.org/ 81 | [OSXFUSE]: http://osxfuse.github.com/ 82 | [Linux-FUSE]: http://fuse.sourceforge.net/ 83 | [FreeBSD-FUSE]: http://wiki.freebsd.org/FuseFilesystem 84 | [BSD 2-Clause License]: http://www.opensource.org/licenses/bsd-license 85 | [MIT License]: http://opensource.org/licenses/MIT 86 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'maven' 4 | id 'signing' 5 | } 6 | 7 | sourceCompatibility = '1.7' 8 | targetCompatibility = '1.7' 9 | 10 | [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' 11 | 12 | group = "co.paralleluniverse" 13 | version = "0.1.0" 14 | status = "integration" 15 | description = "Java FileSystem as FUSE" 16 | ext.url = "http://puniverse.github.com/javafs" 17 | ext.vendor = "Parallel Universe Software Co." 18 | ext.licenseName = "GNU General Public License, version 2, with the Classpath Exception" 19 | ext.licenseUrl = "http://openjdk.java.net/legal/gplv2+ce.html" 20 | ext.scmUrl = "https://github.com/puniverse/javafs" 21 | ext.scmConnection = "https://github.com/puniverse/javafs.git" 22 | 23 | ext.distDir = "$buildDir/dist" 24 | ext.isReleaseVersion = !version.endsWith("SNAPSHOT") 25 | 26 | repositories { 27 | mavenCentral() 28 | } 29 | 30 | dependencies { 31 | compile 'com.github.jnr:jnr-ffi:2.0.3' 32 | compile 'com.github.jnr:jnr-posix:3.0.17' 33 | 34 | testCompile 'junit:junit:4.12' 35 | testCompile 'org.truth0:truth:0.23' 36 | testCompile 'com.google.jimfs:jimfs:1.0' 37 | } 38 | 39 | tasks.withType(JavaExec) { 40 | classpath += sourceSets.test.runtimeClasspath 41 | } 42 | 43 | javadoc { 44 | options { 45 | links = [ "http://docs.oracle.com/javase/7/docs/api/" ] 46 | noDeprecated = true 47 | addStringOption('public', '-quiet') 48 | } 49 | excludes = [ 50 | "co/paralleluniverse/fuse/**", 51 | "co/paralleluniverse/filesystem/**", 52 | "jnr/**", 53 | ] 54 | } 55 | 56 | task sourcesJar(type: Jar, dependsOn: classes) { 57 | classifier = 'sources' 58 | from sourceSets.main.allSource 59 | } 60 | 61 | task javadocJar(type: Jar, dependsOn: javadoc) { 62 | classifier = 'javadoc' 63 | from javadoc.destinationDir 64 | } 65 | 66 | artifacts { 67 | archives jar 68 | archives sourcesJar 69 | archives javadocJar 70 | } 71 | 72 | signing { 73 | required { isReleaseVersion && gradle.taskGraph.hasTask("uploadArchives") } 74 | sign configurations.archives 75 | } 76 | 77 | if (!project.hasProperty("sonatypeUsername") || !project.hasProperty("sonatypePassword")) { 78 | println "sonatype username or password not set" 79 | ext.sonatypeUsername = "" 80 | ext.sonatypePassword = "" 81 | } 82 | 83 | uploadArchives { 84 | repositories { 85 | mavenDeployer { 86 | beforeDeployment { deployment -> signing.signPom(deployment) } 87 | 88 | repository( 89 | url: (isReleaseVersion ? 90 | "https://oss.sonatype.org/service/local/staging/deploy/maven2" : 91 | "https://oss.sonatype.org/content/repositories/snapshots")) { 92 | // User and Password are taken from ~/.gradle/gradle.properties 93 | authentication(userName: project.sonatypeUsername, password: project.sonatypePassword) 94 | } 95 | pom.project { 96 | name project.name 97 | packaging 'jar' 98 | description project.description 99 | url project.url 100 | scm { 101 | url project.scmUrl 102 | connection project.scmConnection 103 | developerConnection project.scmConnection 104 | } 105 | licenses { 106 | license { 107 | name project.licenseName 108 | url project.licenseUrl 109 | distribution 'repo' 110 | } 111 | } 112 | developers { 113 | developer { 114 | id 'pron' 115 | name 'Ron Pressler' 116 | } 117 | } 118 | } 119 | } 120 | } 121 | } 122 | 123 | task wrapper(type: Wrapper) { 124 | gradleVersion = '2.4' 125 | } 126 | 127 | 128 | task('run', type: JavaExec, dependsOn:[testClasses]) { 129 | classpath = sourceSets.main.runtimeClasspath + sourceSets.test.runtimeClasspath 130 | 131 | main = 'co.paralleluniverse.javafs.Main' 132 | 133 | if(project.hasProperty('args')){ 134 | args project.args.split('\\s+') 135 | } 136 | 137 | // systemProperty 'jnr.ffi.compile.dump', 'true' 138 | } 139 | 140 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/puniverse/javafs/ed62bcb24ff06332e0c692bb10fb34f455e441c2/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Sep 04 17:18:36 IDT 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ "$#" == "0" ]; then 3 | ./gradlew run 4 | else 5 | ./gradlew run -Pargs="$*" 6 | fi 7 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'javafs' 2 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/filesystem/FileSystemAdapter.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.filesystem; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.FileStore; 5 | import java.nio.file.FileSystem; 6 | import java.nio.file.Path; 7 | import java.nio.file.PathMatcher; 8 | import java.nio.file.WatchService; 9 | import java.nio.file.attribute.UserPrincipalLookupService; 10 | import java.nio.file.spi.FileSystemProvider; 11 | import java.util.Set; 12 | 13 | public abstract class FileSystemAdapter extends FileSystem { 14 | private final FileSystem fs; 15 | private final FileSystemProvider fsp; 16 | 17 | protected FileSystemAdapter(FileSystem fs, FileSystemProvider fsp) { 18 | this.fs = fs; 19 | this.fsp = fsp; 20 | } 21 | 22 | // protected FileSystemAdapter(FileSystem fs) { 23 | // this.fs = fs; 24 | // this.fsp = wrapFileSystemProvider(fs.provider()); 25 | // } 26 | // protected FileSystemProvider wrapFileSystemProvider(FileSystemProvider fsp) { 27 | // return fsp; 28 | // } 29 | @Override 30 | public String toString() { 31 | return fs.toString(); 32 | } 33 | 34 | @Override 35 | public FileSystemProvider provider() { 36 | return fsp; 37 | } 38 | 39 | @Override 40 | public void close() throws IOException { 41 | fs.close(); 42 | } 43 | 44 | @Override 45 | public boolean isOpen() { 46 | return fs.isOpen(); 47 | } 48 | 49 | @Override 50 | public boolean isReadOnly() { 51 | return fs.isReadOnly(); 52 | } 53 | 54 | @Override 55 | public String getSeparator() { 56 | return fs.getSeparator(); 57 | } 58 | 59 | @Override 60 | public Iterable getRootDirectories() { 61 | return fs.getRootDirectories(); 62 | } 63 | 64 | @Override 65 | public Iterable getFileStores() { 66 | return fs.getFileStores(); 67 | } 68 | 69 | @Override 70 | public Set supportedFileAttributeViews() { 71 | return fs.supportedFileAttributeViews(); 72 | } 73 | 74 | @Override 75 | public Path getPath(String first, String... more) { 76 | return fs.getPath(first, more); 77 | } 78 | 79 | @Override 80 | public PathMatcher getPathMatcher(String syntaxAndPattern) { 81 | return fs.getPathMatcher(syntaxAndPattern); 82 | } 83 | 84 | @Override 85 | public UserPrincipalLookupService getUserPrincipalLookupService() { 86 | return fs.getUserPrincipalLookupService(); 87 | } 88 | 89 | @Override 90 | public WatchService newWatchService() throws IOException { 91 | return fs.newWatchService(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/filesystem/FileSystemProviderAdapter.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.filesystem; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | import java.net.URI; 7 | import java.nio.channels.AsynchronousFileChannel; 8 | import java.nio.channels.FileChannel; 9 | import java.nio.channels.SeekableByteChannel; 10 | import java.nio.file.AccessMode; 11 | import java.nio.file.CopyOption; 12 | import java.nio.file.DirectoryStream; 13 | import java.nio.file.FileStore; 14 | import java.nio.file.FileSystem; 15 | import java.nio.file.LinkOption; 16 | import java.nio.file.OpenOption; 17 | import java.nio.file.Path; 18 | import java.nio.file.attribute.BasicFileAttributes; 19 | import java.nio.file.attribute.FileAttribute; 20 | import java.nio.file.attribute.FileAttributeView; 21 | import java.nio.file.spi.FileSystemProvider; 22 | import java.util.Map; 23 | import java.util.Set; 24 | import java.util.concurrent.ExecutorService; 25 | 26 | public class FileSystemProviderAdapter extends FileSystemProvider { 27 | private final FileSystemProvider fsp; 28 | 29 | protected FileSystemProviderAdapter(FileSystemProvider fsp) { 30 | this.fsp = fsp; 31 | } 32 | 33 | protected FileSystem wrapFileSystem(FileSystem fs) { 34 | return fs; 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return fsp.toString(); 40 | } 41 | 42 | @Override 43 | public String getScheme() { 44 | return fsp.getScheme(); 45 | } 46 | 47 | @Override 48 | public FileSystem newFileSystem(URI uri, Map env) throws IOException { 49 | return wrapFileSystem(fsp.newFileSystem(uri, env)); 50 | } 51 | 52 | @Override 53 | public FileSystem newFileSystem(Path path, Map env) throws IOException { 54 | return wrapFileSystem(fsp.newFileSystem(path, env)); 55 | } 56 | 57 | @Override 58 | public FileSystem getFileSystem(URI uri) { 59 | return wrapFileSystem(fsp.getFileSystem(uri)); 60 | } 61 | 62 | @Override 63 | public Path getPath(URI uri) { 64 | return fsp.getPath(uri); 65 | } 66 | 67 | @Override 68 | public InputStream newInputStream(Path path, OpenOption... options) throws IOException { 69 | return fsp.newInputStream(path, options); 70 | } 71 | 72 | @Override 73 | public OutputStream newOutputStream(Path path, OpenOption... options) throws IOException { 74 | return fsp.newOutputStream(path, options); 75 | } 76 | 77 | @Override 78 | public FileChannel newFileChannel(Path path, Set options, FileAttribute... attrs) throws IOException { 79 | return fsp.newFileChannel(path, options, attrs); 80 | } 81 | 82 | @Override 83 | public AsynchronousFileChannel newAsynchronousFileChannel(Path path, Set options, ExecutorService executor, FileAttribute... attrs) throws IOException { 84 | return fsp.newAsynchronousFileChannel(path, options, executor, attrs); 85 | } 86 | 87 | @Override 88 | public SeekableByteChannel newByteChannel(Path path, Set options, FileAttribute... attrs) throws IOException { 89 | return fsp.newByteChannel(path, options, attrs); 90 | } 91 | 92 | @Override 93 | public DirectoryStream newDirectoryStream(Path dir, DirectoryStream.Filter filter) throws IOException { 94 | return fsp.newDirectoryStream(dir, filter); 95 | } 96 | 97 | @Override 98 | public void createDirectory(Path dir, FileAttribute... attrs) throws IOException { 99 | fsp.createDirectory(dir, attrs); 100 | } 101 | 102 | @Override 103 | public void createSymbolicLink(Path link, Path target, FileAttribute... attrs) throws IOException { 104 | fsp.createSymbolicLink(link, target, attrs); 105 | } 106 | 107 | @Override 108 | public void createLink(Path link, Path existing) throws IOException { 109 | fsp.createLink(link, existing); 110 | } 111 | 112 | @Override 113 | public void delete(Path path) throws IOException { 114 | fsp.delete(path); 115 | } 116 | 117 | @Override 118 | public boolean deleteIfExists(Path path) throws IOException { 119 | return fsp.deleteIfExists(path); 120 | } 121 | 122 | @Override 123 | public Path readSymbolicLink(Path link) throws IOException { 124 | return fsp.readSymbolicLink(link); 125 | } 126 | 127 | @Override 128 | public void copy(Path source, Path target, CopyOption... options) throws IOException { 129 | fsp.copy(source, target, options); 130 | } 131 | 132 | @Override 133 | public void move(Path source, Path target, CopyOption... options) throws IOException { 134 | fsp.move(source, target, options); 135 | } 136 | 137 | @Override 138 | public boolean isSameFile(Path path, Path path2) throws IOException { 139 | return fsp.isSameFile(path, path2); 140 | } 141 | 142 | @Override 143 | public boolean isHidden(Path path) throws IOException { 144 | return fsp.isHidden(path); 145 | } 146 | 147 | @Override 148 | public FileStore getFileStore(Path path) throws IOException { 149 | return fsp.getFileStore(path); 150 | } 151 | 152 | @Override 153 | public void checkAccess(Path path, AccessMode... modes) throws IOException { 154 | fsp.checkAccess(path, modes); 155 | } 156 | 157 | @Override 158 | public V getFileAttributeView(Path path, Class type, LinkOption... options) { 159 | return fsp.getFileAttributeView(path, type, options); 160 | } 161 | 162 | @Override 163 | public A readAttributes(Path path, Class type, LinkOption... options) throws IOException { 164 | return fsp.readAttributes(path, type, options); 165 | } 166 | 167 | @Override 168 | public Map readAttributes(Path path, String attributes, LinkOption... options) throws IOException { 169 | return fsp.readAttributes(path, attributes, options); 170 | } 171 | 172 | @Override 173 | public void setAttribute(Path path, String attribute, Object value, LinkOption... options) throws IOException { 174 | fsp.setAttribute(path, attribute, value, options); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/AbstractFuseFilesystem.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.nio.file.Path; 5 | import jnr.constants.platform.Errno; 6 | import jnr.ffi.Pointer; 7 | 8 | /** 9 | * An adapter that tries to put sane defaults for default return values. Will silently pretend that most non-critical operations 10 | * have succeeded, but return ENOSYS on non-implemented important operations. 11 | */ 12 | public abstract class AbstractFuseFilesystem extends FuseFilesystem { 13 | @Override 14 | protected String getName() { 15 | return null; 16 | } 17 | 18 | @Override 19 | protected String[] getOptions() { 20 | return null; 21 | } 22 | 23 | @Override 24 | protected void afterUnmount(Path mountPoint) { 25 | } 26 | 27 | @Override 28 | protected void beforeMount(Path mountPoint) { 29 | } 30 | 31 | @Override 32 | protected int getattr(String path, StructStat stat) { 33 | return -Errno.ENOSYS.intValue(); 34 | } 35 | 36 | @Override 37 | protected int readlink(String path, ByteBuffer buffer, long size) { 38 | return 0; 39 | } 40 | 41 | @Override 42 | protected int mknod(String path, long mode, long dev) { 43 | return create(path, mode, null); 44 | } 45 | 46 | @Override 47 | protected int mkdir(String path, long mode) { 48 | return 0; 49 | } 50 | 51 | @Override 52 | protected int unlink(String path) { 53 | return 0; 54 | } 55 | 56 | @Override 57 | protected int rmdir(String path) { 58 | return 0; 59 | } 60 | 61 | @Override 62 | protected int symlink(String path, String target) { 63 | return 0; 64 | } 65 | 66 | @Override 67 | protected int rename(String path, String newName) { 68 | return 0; 69 | } 70 | 71 | @Override 72 | protected int link(String path, String target) { 73 | return 0; 74 | } 75 | 76 | @Override 77 | protected int chmod(String path, long mode) { 78 | return 0; 79 | } 80 | 81 | @Override 82 | protected int chown(String path, long uid, long gid) { 83 | return 0; 84 | } 85 | 86 | @Override 87 | protected int truncate(String path, long offset) { 88 | return 0; 89 | } 90 | 91 | @Override 92 | protected int open(String path, StructFuseFileInfo info) { 93 | return 0; 94 | } 95 | 96 | @Override 97 | protected int read(String path, ByteBuffer buffer, long size, long offset, StructFuseFileInfo info) { 98 | return 0; 99 | } 100 | 101 | @Override 102 | protected int write(String path, ByteBuffer buf, long bufSize, long writeOffset, StructFuseFileInfo wrapper) { 103 | return 0; 104 | } 105 | 106 | @Override 107 | protected int statfs(String path, StructStatvfs stratvfs) { 108 | return 0; 109 | } 110 | 111 | @Override 112 | protected int flush(String path, StructFuseFileInfo info) { 113 | return 0; 114 | } 115 | 116 | @Override 117 | protected int release(String path, StructFuseFileInfo info) { 118 | return 0; 119 | } 120 | 121 | @Override 122 | protected int fsync(String path, int datasync, StructFuseFileInfo info) { 123 | return 0; 124 | } 125 | 126 | @Override 127 | protected int setxattr(String path, String xattr, ByteBuffer buf, long size, int flags, int position) { 128 | return -Errno.ENOSYS.intValue(); 129 | } 130 | 131 | @Override 132 | protected int getxattr(String path, String xattr, XattrFiller filler, long size, long position) { 133 | return -Errno.ENOSYS.intValue(); 134 | } 135 | 136 | @Override 137 | protected int listxattr(String path, XattrListFiller filler) { 138 | return -Errno.ENOSYS.intValue(); 139 | } 140 | 141 | @Override 142 | protected int removexattr(String path, String xattr) { 143 | return -Errno.ENOSYS.intValue(); 144 | } 145 | 146 | @Override 147 | protected int opendir(String path, StructFuseFileInfo info) { 148 | return 0; 149 | } 150 | 151 | @Override 152 | protected int readdir(String path, StructFuseFileInfo info, DirectoryFiller filler) { 153 | return 0; 154 | } 155 | 156 | @Override 157 | protected int releasedir(String path, StructFuseFileInfo info) { 158 | return 0; 159 | } 160 | 161 | @Override 162 | protected int fsyncdir(String path, int datasync, StructFuseFileInfo info) { 163 | return 0; 164 | } 165 | 166 | @Override 167 | protected void init() { 168 | } 169 | 170 | @Override 171 | protected void destroy() { 172 | } 173 | 174 | @Override 175 | protected int access(String path, int access) { 176 | return -Errno.ENOSYS.intValue(); 177 | } 178 | 179 | @Override 180 | protected int create(String path, long mode, StructFuseFileInfo info) { 181 | return -Errno.ENOSYS.intValue(); 182 | } 183 | 184 | @Override 185 | public int ftruncate(String path, long offset, StructFuseFileInfo info) { 186 | return truncate(path, offset); 187 | } 188 | 189 | @Override 190 | protected int fgetattr(String path, StructStat stat, StructFuseFileInfo info) { 191 | return getattr(path, stat); 192 | } 193 | 194 | @Override 195 | protected int lock(String path, StructFuseFileInfo info, int command, StructFlock flock) { 196 | return -Errno.ENOSYS.intValue(); 197 | } 198 | 199 | @Override 200 | protected int utimens(String path, StructTimeBuffer timeBuffer) { 201 | return -Errno.ENOSYS.intValue(); 202 | } 203 | 204 | @Override 205 | protected int bmap(String path, StructFuseFileInfo info) { 206 | return 0; 207 | } 208 | 209 | @Override 210 | public int ioctl(String path, int cmd, Pointer arg, StructFuseFileInfo fi, long flags, Pointer data) { 211 | return -ErrorCodes.ENOSYS(); 212 | } 213 | 214 | @Override 215 | public int poll(String path, StructFuseFileInfo fi, StructFusePollHandle ph, Pointer reventsp) { 216 | return -ErrorCodes.ENOSYS(); 217 | } 218 | 219 | @Override 220 | protected int write_buf(String path, StructFuseBufvec buf, long off, StructFuseFileInfo fi) { 221 | return -ErrorCodes.ENOSYS(); 222 | } 223 | 224 | @Override 225 | protected int read_buf(String path, Pointer bufp, long size, long off, StructFuseFileInfo fi) { 226 | return -ErrorCodes.ENOSYS(); 227 | } 228 | 229 | @Override 230 | public int flock(String path, StructFuseFileInfo fi, int op) { 231 | return -ErrorCodes.ENOSYS(); 232 | } 233 | 234 | @Override 235 | public int fallocate(String path, int mode, long off, long length, StructFuseFileInfo fi) { 236 | return -ErrorCodes.ENOSYS(); 237 | } 238 | 239 | } 240 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/AccessConstants.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import jnr.constants.platform.Access; 4 | 5 | /** 6 | * Values for the second argument to access. These may be OR'd together. 7 | *

8 | * 9 | * @author Sergey Tselovalnikov 10 | * @since 05.06.15 11 | */ 12 | public final class AccessConstants { 13 | public static final int R_OK = Access.R_OK.intValue(); /* Test for read permission. */ 14 | public static final int W_OK = Access.W_OK.intValue(); /* Test for write permission. */ 15 | public static final int X_OK = Access.X_OK.intValue(); /* Test for execute permission. */ 16 | public static final int F_OK = Access.F_OK.intValue(); /* Test for existence. */ 17 | 18 | private AccessConstants() {} 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/DirectoryFiller.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | /** 4 | * Base interface for the transfer-object for the readdir() call. 5 | */ 6 | public interface DirectoryFiller { 7 | /** 8 | * Pass the given files to the FUSE interfaces. 9 | * 10 | * @param files A list of filenames without directory. 11 | * @return true if the operation succeeds, false if a problem happens when passing any of the files to FUSE. 12 | */ 13 | public boolean add(Iterable files); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/DirectoryFillerImpl.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.nio.file.Paths; 5 | import java.util.Arrays; 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | import jnr.ffi.Pointer; 9 | import jnr.ffi.annotations.Delegate; 10 | import jnr.ffi.types.off_t; 11 | 12 | /** 13 | * A class which provides functionality to pass filenames back to FUSE as part of a readdir() call. 14 | */ 15 | final class DirectoryFillerImpl implements DirectoryFiller { 16 | private static final String currentDirectory = "."; 17 | private static final String parentDirectory = ".."; 18 | private final Pointer buf; 19 | private final fuse_fill_dir_t nativeFunction; 20 | private final Set addedFiles = new HashSet(); 21 | 22 | DirectoryFillerImpl(Pointer buf, fuse_fill_dir_t nativeFunction) { 23 | this.buf = buf; 24 | this.nativeFunction = nativeFunction; 25 | 26 | add(Arrays.asList(currentDirectory, parentDirectory)); 27 | } 28 | 29 | public static interface fuse_fill_dir_t { 30 | @Delegate 31 | int invoke(Pointer buf, ByteBuffer name, Pointer stat, @off_t long off); 32 | } 33 | 34 | @Override 35 | public final boolean add(Iterable files) { 36 | int result; 37 | for (String file : files) { 38 | if (file == null) 39 | continue; 40 | 41 | file = Paths.get(file).getFileName().toString(); // Keep only the name component 42 | 43 | if (addedFiles.add(file)) { 44 | result = nativeFunction.invoke(buf, ByteBuffer.wrap(file.getBytes()), null, 0); 45 | if (result != 0) 46 | return false; 47 | } 48 | } 49 | return true; 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | final StringBuilder output = new StringBuilder(); 55 | int count = 0; 56 | for (final String file : addedFiles) { 57 | output.append(file); 58 | if (count < addedFiles.size() - 1) 59 | output.append(", "); 60 | count++; 61 | } 62 | return output.toString(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/Filesystem.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import jnr.ffi.provider.jffi.ClosureHelper; 4 | import java.nio.BufferOverflowException; 5 | import java.nio.ByteBuffer; 6 | import jnr.ffi.Pointer; 7 | import jnr.ffi.annotations.In; 8 | import jnr.ffi.annotations.Out; 9 | import jnr.ffi.types.dev_t; 10 | import jnr.ffi.types.gid_t; 11 | import jnr.ffi.types.mode_t; 12 | import jnr.ffi.types.off_t; 13 | import jnr.ffi.types.size_t; 14 | import jnr.ffi.types.u_int32_t; 15 | import jnr.ffi.types.uid_t; 16 | import static co.paralleluniverse.fuse.JNRUtil.toByteBuffer; 17 | import co.paralleluniverse.fuse.StructFuseOperationsIfaces.*; 18 | 19 | class Filesystem implements 20 | _readlink, _mknod, _mkdir, _unlink, _rmdir, _symlink, _rename, _link, _chmod, _chown, _truncate, 21 | _open, _read, _write, _flush, _release, _fsync, _statfs, _lock, _getattr, _fgetattr, 22 | _setxattr_MAC, _setxattr_NOT_MAC, 23 | _getxattr_MAC, _getxattr_NOT_MAC, 24 | _listxattr, _removexattr, 25 | _opendir, _readdir, _releasedir, _fsyncdir, 26 | _init, _destroy, _access, _create, _ftruncate, _utimens, _bmap, 27 | _ioctl, _poll, _write_buf, _read_buf, _flock, _fallocate { 28 | private final FuseFilesystem fs; 29 | 30 | public Filesystem(FuseFilesystem fs) { 31 | this.fs = fs; 32 | } 33 | 34 | @Override 35 | public final int _getattr(String path, Pointer stat) { 36 | return fs.getattr(path, fs.defaultStat(new StructStat(stat, path))); 37 | } 38 | 39 | @Override 40 | public final int _readlink(String path, Pointer buffer, @size_t long size) { 41 | final ByteBuffer buf = toByteBuffer(buffer, size); 42 | final int result = fs.readlink(path, buf, size); 43 | if (result == 0) { 44 | try { 45 | buf.put((byte) 0); 46 | } catch (final BufferOverflowException e) { 47 | ((ByteBuffer) buf.position(buf.limit() - 1)).put((byte) 0); 48 | } 49 | } 50 | return result; 51 | } 52 | 53 | @Override 54 | public final int _mknod(String path, @mode_t long mode, @dev_t long dev) { 55 | return fs.mknod(path, mode, dev); 56 | } 57 | 58 | @Override 59 | public final int _mkdir(String path, @mode_t long mode) { 60 | return fs.mkdir(path, mode); 61 | } 62 | 63 | @Override 64 | public final int _unlink(String path) { 65 | return fs.unlink(path); 66 | } 67 | 68 | @Override 69 | public final int _rmdir(String path) { 70 | return fs.rmdir(path); 71 | } 72 | 73 | @Override 74 | public final int _symlink(String path, String target) { 75 | return fs.symlink(path, target); 76 | } 77 | 78 | @Override 79 | public final int _rename(String path, String newName) { 80 | return fs.rename(path, newName); 81 | } 82 | 83 | @Override 84 | public final int _link(String path, String target) { 85 | return fs.link(path, target); 86 | } 87 | 88 | @Override 89 | public final int _chmod(String path, @mode_t long mode) { 90 | return fs.chmod(path, mode); 91 | } 92 | 93 | @Override 94 | public final int _chown(String path, @uid_t long uid, @gid_t long gid) { 95 | return fs.chown(path, uid, gid); 96 | } 97 | 98 | @Override 99 | public final int _truncate(String path, @off_t long offset) { 100 | return fs.truncate(path, offset); 101 | } 102 | 103 | @Override 104 | public final int _open(String path, Pointer info) { 105 | return fs.open(path, new StructFuseFileInfo(info, path)); 106 | } 107 | 108 | @Override 109 | public final int _read(String path, @Out Pointer buffer, @size_t long size, @off_t long offset, Pointer info) { 110 | final ByteBuffer buf = toByteBuffer(buffer, size); 111 | return fs.read(path, buf, size, offset, new StructFuseFileInfo(info, path)); 112 | } 113 | 114 | @Override 115 | public final int _write(String path, @In Pointer buffer, @size_t long size, @off_t long offset, Pointer info) { 116 | final ByteBuffer buf = toByteBuffer(buffer, size); 117 | return fs.write(path, buf, size, offset, new StructFuseFileInfo(info, path)); 118 | } 119 | 120 | @Override 121 | public final int _statfs(String path, Pointer statsvfs) { 122 | return fs.statfs(path, new StructStatvfs(statsvfs, path)); 123 | } 124 | 125 | @Override 126 | public final int _flush(String path, Pointer info) { 127 | return fs.flush(path, new StructFuseFileInfo(info, path)); 128 | } 129 | 130 | @Override 131 | public final int _release(String path, Pointer info) { 132 | return fs.release(path, new StructFuseFileInfo(info, path)); 133 | } 134 | 135 | @Override 136 | public final int _fsync(String path, int datasync, @In Pointer info) { 137 | return fs.fsync(path, datasync, new StructFuseFileInfo(info, path)); 138 | } 139 | 140 | @Override 141 | public final int _setxattr(String path, String xattr, Pointer value, @size_t long size, int flags) { 142 | return _setxattr(path, xattr, value, size, flags, 0); 143 | } 144 | 145 | @Override 146 | public final int _setxattr(String path, String xattr, Pointer value, @size_t long size, int flags, int position) { 147 | final ByteBuffer val = toByteBuffer(value, size); 148 | return fs.setxattr(path, xattr, val, size, flags, position); 149 | } 150 | 151 | @Override 152 | public final int _getxattr(String path, String xattr, Pointer buffer, @size_t long size) { 153 | return _getxattr(path, xattr, buffer, size, 0); 154 | } 155 | 156 | @Override 157 | public final int _getxattr(String path, String xattr, Pointer buffer, @size_t long size, @u_int32_t long position) { 158 | final XattrFiller filler = new XattrFiller(buffer == null ? null : toByteBuffer(buffer, size), size, (int) position); 159 | final int result = fs.getxattr(path, xattr, filler, size, position); 160 | return result < 0 ? result : (int) filler.getSize(); 161 | } 162 | 163 | @Override 164 | public final int _listxattr(String path, Pointer buffer, @size_t long size) { 165 | final XattrListFiller filler = new XattrListFiller(buffer == null ? null : toByteBuffer(buffer, size), size); 166 | final int result = fs.listxattr(path, filler); 167 | return result < 0 ? result : (int) filler.requiredSize(); 168 | } 169 | 170 | @Override 171 | public final int _removexattr(String path, String xattr) { 172 | return fs.removexattr(path, xattr); 173 | } 174 | 175 | @Override 176 | public final int _opendir(String path, Pointer info) { 177 | return fs.opendir(path, new StructFuseFileInfo(info, path)); 178 | } 179 | 180 | @Override 181 | public final int _readdir(String path, Pointer buf, Pointer fillFunction, @off_t long offset, @In Pointer info) { 182 | return fs.readdir(path, 183 | new StructFuseFileInfo(info, path), 184 | new DirectoryFillerImpl(buf, ClosureHelper.getInstance().fromNative(fillFunction, DirectoryFillerImpl.fuse_fill_dir_t.class))); 185 | } 186 | 187 | @Override 188 | public final int _releasedir(String path, Pointer info) { 189 | return fs.releasedir(path, new StructFuseFileInfo(info, path)); 190 | } 191 | 192 | @Override 193 | public final int _fsyncdir(String path, int datasync, @In Pointer info) { 194 | return fs.fsyncdir(path, datasync, new StructFuseFileInfo(info, path)); 195 | } 196 | 197 | @Override 198 | public final void _init(Pointer conn) { 199 | fs.init(); 200 | } 201 | 202 | @Override 203 | public final void _destroy() { 204 | fs.destroy(); 205 | fs._destroy(); 206 | } 207 | 208 | @Override 209 | public final int _access(String path, int access) { 210 | return fs.access(path, access); 211 | } 212 | 213 | @Override 214 | public final int _create(String path, @mode_t long mode, Pointer info) { 215 | return fs.create(path, mode, new StructFuseFileInfo(info, path)); 216 | } 217 | 218 | @Override 219 | public final int _ftruncate(String path, @off_t long offset, @In Pointer info) { 220 | return fs.ftruncate(path, offset, new StructFuseFileInfo(info, path)); 221 | } 222 | 223 | @Override 224 | public final int _fgetattr(String path, Pointer stat, Pointer info) { 225 | return fs.fgetattr(path, 226 | fs.defaultStat(new StructStat(stat, path)), 227 | new StructFuseFileInfo(info, path)); 228 | } 229 | 230 | @Override 231 | public final int _lock(String path, Pointer info, int cmd, Pointer flock) { 232 | final StructFuseFileInfo fileWrapper = new StructFuseFileInfo(info, path); 233 | final StructFlock flockWrapper = new StructFlock(flock, path); 234 | final int result = fs.lock(path, fileWrapper, cmd, flockWrapper); 235 | return result; 236 | } 237 | 238 | @Override 239 | public final int _utimens(String path, Pointer timebuffer) { 240 | return fs.utimens(path, new StructTimeBuffer(timebuffer)); 241 | } 242 | 243 | @Override 244 | public final int _bmap(String path, Pointer info) { 245 | return fs.bmap(path, new StructFuseFileInfo(info, path)); 246 | } 247 | 248 | @Override 249 | public void _ioctl(String path, int cmd, Pointer arg, Pointer fi, long flags, Pointer data) { 250 | fs.ioctl(path, cmd, arg, new StructFuseFileInfo(fi, path), flags, data); 251 | } 252 | 253 | @Override 254 | public void _poll(String path, Pointer fi, Pointer ph, Pointer reventsp) { 255 | fs.poll(path, new StructFuseFileInfo(fi, path), new StructFusePollHandle(ph), reventsp); 256 | } 257 | 258 | @Override 259 | public void _write_buf(String path, Pointer buf, long off, Pointer fi) { 260 | fs.write_buf(path, new StructFuseBufvec(buf), off, new StructFuseFileInfo(fi, path)); 261 | } 262 | 263 | @Override 264 | public void _read_buf(String path, Pointer bufp, long size, long off, Pointer fi) { 265 | fs.read_buf(path, bufp, size, off, new StructFuseFileInfo(fi, path)); 266 | } 267 | 268 | @Override 269 | public void _flock(String path, Pointer fi, int op) { 270 | fs.flock(path, new StructFuseFileInfo(fi, path), op); 271 | } 272 | 273 | @Override 274 | public void _fallocate(String path, int mode, long off, long length, Pointer fi) { 275 | fs.fallocate(path, mode, off, length, new StructFuseFileInfo(fi, path)); 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/Fuse.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.AccessDeniedException; 5 | import java.nio.file.Files; 6 | import java.nio.file.NotDirectoryException; 7 | import java.nio.file.Path; 8 | import java.util.Map; 9 | import java.util.Random; 10 | import java.util.Map.Entry; 11 | import java.util.concurrent.ConcurrentHashMap; 12 | import java.util.concurrent.ConcurrentMap; 13 | import java.util.concurrent.locks.Lock; 14 | import java.util.concurrent.locks.ReentrantLock; 15 | import java.util.logging.Logger; 16 | 17 | import jnr.ffi.Struct; 18 | 19 | public final class Fuse { 20 | private static final class MountThread extends Thread { 21 | private Integer result = null; 22 | private final String[] args; 23 | private final StructFuseOperations operations; 24 | private final LibFuse fuse; 25 | private final Path mountPoint; 26 | 27 | private MountThread(String filesystemName, LibFuse fuse, String[] args, Path mountPoint, StructFuseOperations operations) { 28 | super(filesystemName + "-fuse"); 29 | this.fuse = fuse; 30 | this.args = args; 31 | this.mountPoint = mountPoint; 32 | this.operations = operations; 33 | } 34 | 35 | private Integer getResult() { 36 | return result; 37 | } 38 | 39 | @Override 40 | public void run() { 41 | result = fuse.fuse_main_real(args.length, args, operations, Struct.size(operations), null); 42 | } 43 | } 44 | 45 | private static LibFuse libFuse = null; 46 | private static long initTime; 47 | private static final Lock initLock = new ReentrantLock(); 48 | private static final Random defaultFilesystemRandom = new Random(); 49 | private static final long errorSleepDuration = 750; 50 | private static String fusermount = "fusermount"; 51 | private static String umount = "umount"; 52 | private static int currentUid = 0; 53 | private static int currentGid = 0; 54 | private static final ConcurrentMap mountedFs = new ConcurrentHashMap<>(); 55 | 56 | static void destroyed(FuseFilesystem fuseFilesystem) { 57 | if (handleShutdownHooks()) { 58 | try { 59 | Runtime.getRuntime().removeShutdownHook(fuseFilesystem.getUnmountHook()); 60 | } catch (IllegalStateException e) { 61 | // Already shutting down; this is fine and expected, ignore the exception. 62 | } 63 | } 64 | } 65 | 66 | static StructFuseContext getFuseContext() { 67 | return init().fuse_get_context(); 68 | } 69 | 70 | static int getGid() { 71 | return currentGid; 72 | } 73 | 74 | static long getInitTime() { 75 | init(); 76 | return initTime; 77 | } 78 | 79 | static int getUid() { 80 | return currentUid; 81 | } 82 | 83 | private static boolean handleShutdownHooks() { 84 | final SecurityManager security = System.getSecurityManager(); 85 | if (security == null) { 86 | return true; 87 | } 88 | try { 89 | security.checkPermission(new RuntimePermission("shutdownHooks")); 90 | return true; 91 | } catch (SecurityException e) { 92 | return false; 93 | } 94 | } 95 | 96 | static LibFuse init() throws UnsatisfiedLinkError { 97 | if (libFuse != null) 98 | return libFuse; // No need to lock if everything is fine already 99 | 100 | initLock.lock(); 101 | try { 102 | if (libFuse == null) { 103 | libFuse = Platform.fuse(); 104 | initTime = System.currentTimeMillis(); 105 | } 106 | try { 107 | currentUid = Integer.parseInt(new ProcessGobbler("id", "-u").getStdout()); 108 | currentGid = Integer.parseInt(new ProcessGobbler("id", "-g").getStdout()); 109 | } catch (Exception e) { 110 | // Oh well, keep default values 111 | } 112 | return libFuse; 113 | } finally { 114 | initLock.unlock(); 115 | } 116 | } 117 | 118 | public static void mount(FuseFilesystem filesystem, Path mountPoint, boolean blocking, boolean debug, Map mountOptions) throws IOException { 119 | mountPoint = mountPoint.toAbsolutePath().normalize().toRealPath(); 120 | if (!Files.isDirectory(mountPoint)) 121 | throw new NotDirectoryException(mountPoint.toString()); 122 | 123 | if (!Files.isReadable(mountPoint) || !Files.isWritable(mountPoint) || !Files.isExecutable(mountPoint)) 124 | throw new AccessDeniedException(mountPoint.toString()); 125 | 126 | final Logger logger = filesystem.getLogger(); 127 | if (logger != null) 128 | filesystem = new LoggedFuseFilesystem(filesystem, logger); 129 | 130 | filesystem.mount(mountPoint, blocking); 131 | 132 | final String filesystemName = filesystem.getFuseName(); 133 | final String[] options = toOptionsArray(mountOptions); 134 | final String[] argv; 135 | if (options == null) 136 | argv = new String[debug ? 4 : 3]; 137 | else { 138 | argv = new String[(debug ? 4 : 3) + options.length]; 139 | System.arraycopy(options, 0, argv, (debug ? 3 : 2), options.length); 140 | } 141 | 142 | argv[0] = filesystemName; 143 | argv[1] = "-f"; 144 | if (debug) 145 | argv[2] = "-d"; 146 | argv[argv.length - 1] = mountPoint.toString(); 147 | 148 | final LibFuse fuse = init(); 149 | final StructFuseOperations operations = new StructFuseOperations(jnr.ffi.Runtime.getRuntime(fuse), filesystem); 150 | 151 | if (handleShutdownHooks()) 152 | Runtime.getRuntime().addShutdownHook(filesystem.getUnmountHook()); 153 | 154 | mountedFs.put(mountPoint, filesystem); 155 | 156 | final Integer result; 157 | if (blocking) 158 | result = fuse.fuse_main_real(argv.length, argv, operations, Struct.size(operations), null); 159 | else { 160 | final MountThread mountThread = new MountThread(filesystemName, fuse, argv, mountPoint, operations); 161 | mountThread.start(); 162 | try { 163 | mountThread.join(errorSleepDuration); 164 | } catch (final InterruptedException e) { 165 | } 166 | result = mountThread.getResult(); 167 | } 168 | if (result != null && result != 0) 169 | throw new FuseException(result); 170 | } 171 | 172 | static void unmount(FuseFilesystem fuseFilesystem) throws IOException { 173 | fuseFilesystem.unmount(); 174 | final Path mountPoint = fuseFilesystem.getMountPoint(); 175 | 176 | final FuseFilesystem fs = mountedFs.remove(mountPoint); 177 | assert fs == null || fs == fuseFilesystem; 178 | 179 | unmount(mountPoint); 180 | } 181 | 182 | public static void setFusermount(String fusermount) { 183 | Fuse.fusermount = fusermount; 184 | } 185 | 186 | public static void setUmount(String umount) { 187 | Fuse.umount = umount; 188 | } 189 | 190 | /** 191 | * Try to unmount an existing FUSE mountpoint. NOTE: You should use {@link FuseFilesystem#unmount FuseFilesystem.unmount()} 192 | * for unmounting the FuseFilesystem (or let the shutdown hook take care unmounting during shutdown of the application). 193 | * This method is available for special cases, e.g. where mountpoints were left over from previous invocations and need to 194 | * be unmounted before the filesystem can be mounted again. 195 | * 196 | * @param mountPoint The location where the filesystem is mounted. 197 | * @throws IOException thrown if an error occurs while starting the external process. 198 | */ 199 | public static void unmount(Path mountPoint) throws IOException { 200 | final FuseFilesystem fs = mountedFs.get(mountPoint); 201 | if (fs != null) { 202 | unmount(fs); 203 | return; 204 | } 205 | ProcessGobbler process; 206 | try { 207 | process = new ProcessGobbler(Fuse.fusermount, "-z", "-u", mountPoint.toString()); 208 | } catch (IOException e) { 209 | process = new ProcessGobbler(Fuse.umount, mountPoint.toString()); 210 | } 211 | final int res = process.getReturnCode(); 212 | if (res != 0) 213 | throw new FuseException(res); 214 | } 215 | 216 | private static String[] toOptionsArray(Map options) { 217 | if (options == null) { 218 | return null; 219 | } 220 | 221 | int i = 0; 222 | String[] values = new String[options.size() * 2]; 223 | for (Entry entry : options.entrySet()) { 224 | values[i++] = "-o"; 225 | values[i++] = entry.getKey() 226 | + (entry.getValue() == null ? "" : "=" + entry.getValue()); 227 | } 228 | 229 | return values; 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/FuseBufFlags.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import jnr.ffi.util.EnumMapper; 4 | 5 | /** 6 | * Buffer flags 7 | * 8 | * @author Sergey Tselovalnikov 9 | * @since 02.06.15 10 | */ 11 | enum FuseBufFlags implements EnumMapper.IntegerEnum { 12 | /** 13 | * Buffer contains a file descriptor 14 | *

15 | * If this flag is set, the .fd field is valid, otherwise the 16 | * .mem fields is valid. 17 | */ 18 | FUSE_BUF_IS_FD(1 << 1), 19 | 20 | /** 21 | * Seek on the file descriptor 22 | *

23 | * If this flag is set then the .pos field is valid and is 24 | * used to seek to the given offset before performing 25 | * operation on file descriptor. 26 | */ 27 | FUSE_BUF_FD_SEEK(1 << 2), 28 | 29 | /** 30 | * Retry operation on file descriptor 31 | *

32 | * If this flag is set then retry operation on file descriptor 33 | * until .size bytes have been copied or an error or EOF is 34 | * detected. 35 | */ 36 | FUSE_BUF_FD_RETRY(1 << 3), 37 | 38 | /** 39 | * JNR does not work without null value enum 40 | */ 41 | NULL_VALUE(0); 42 | 43 | private final int value; 44 | 45 | FuseBufFlags(int value) { 46 | this.value = value; 47 | } 48 | 49 | /** 50 | * Special JNR method, see jnr.ffi.util.EnumMapper#getNumberValueMethod(java.lang.Class, java.lang.Class) 51 | */ 52 | @Override 53 | public int intValue() { 54 | return value; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/FuseException.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import java.io.IOException; 4 | 5 | final class FuseException extends IOException { 6 | private static long serialVersionUID = -3323428017667312747L; 7 | 8 | FuseException(int returnCode) { 9 | super("FUSE returned error code " + returnCode); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/FuseFilesystem.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import java.io.IOException; 4 | import java.nio.ByteBuffer; 5 | import java.nio.file.Path; 6 | import java.util.concurrent.locks.ReentrantLock; 7 | import java.util.logging.Logger; 8 | import java.util.regex.Pattern; 9 | import jnr.ffi.Pointer; 10 | import jnr.ffi.types.gid_t; 11 | import jnr.ffi.types.pid_t; 12 | import jnr.ffi.types.uid_t; 13 | 14 | /** 15 | * Fuse file system. 16 | * All documentation from "fuse.h" 17 | * 18 | * @see http://fuse.sourceforge.net/doxygen/index.html 19 | * @see http://fuse.sourceforge.net/wiki/ 20 | */ 21 | public abstract class FuseFilesystem { 22 | private static final String defaultFilesystemName = "userfs-"; 23 | private static final Pattern regexNormalizeFilesystemName = Pattern.compile("[^a-zA-Z]"); 24 | 25 | /** 26 | * Perform destroy-time cleanup. Takes two {@link FuseFilesystem}s arguments which should be equal in most cases, but may 27 | * not in the case of a wrapped filesystem object for logging ({@link LoggedFuseFilesystem}). 28 | * 29 | * @param mountedFilesystem The {@link FuseFilesystem} object that is actually mounted (the one receiving the destroy call) 30 | * @param userFilesystem The {@link FuseFilesystem} that the user believes is mounted (the one that the user called .mount on) 31 | */ 32 | final static void _destroy(FuseFilesystem mountedFilesystem, FuseFilesystem userFilesystem) { 33 | final Path oldMountPoint; 34 | mountedFilesystem.mountLock.lock(); 35 | userFilesystem.mountLock.lock(); 36 | try { 37 | if (!mountedFilesystem.isMounted()) 38 | throw new IllegalStateException("destroy called on a non-mounted filesystem"); 39 | 40 | oldMountPoint = mountedFilesystem.mountPoint; 41 | Fuse.destroyed(mountedFilesystem); 42 | userFilesystem.mountPoint = null; 43 | mountedFilesystem.mountPoint = null; 44 | } finally { 45 | userFilesystem.mountLock.unlock(); 46 | mountedFilesystem.mountLock.unlock(); 47 | } 48 | mountedFilesystem.afterUnmount(oldMountPoint); 49 | } 50 | 51 | private final ReentrantLock mountLock = new ReentrantLock(); 52 | private final AutoUnmountHook unmountHook = new AutoUnmountHook(); 53 | private volatile Path mountPoint = null; 54 | private Logger logger = null; 55 | 56 | protected abstract void afterUnmount(Path mountPoint); 57 | 58 | protected abstract void beforeMount(Path mountPoint); 59 | 60 | public final boolean isMounted() { 61 | return getMountPoint() != null; 62 | } 63 | 64 | void _destroy() { 65 | _destroy(this, this); 66 | } 67 | 68 | /** 69 | * Returns the raw fuse_context structure. Only valid when called while a filesystem operation is taking place. 70 | * 71 | * @return The fuse_context structure by reference. 72 | */ 73 | protected final StructFuseContext getFuseContext() { 74 | if (!isMounted()) 75 | throw new IllegalStateException("Cannot get FUSE context if the filesystem is not mounted."); 76 | 77 | return Fuse.getFuseContext(); 78 | } 79 | 80 | /** 81 | * Returns the gid field of the fuse context. Only valid when called while a filesystem operation is taking place. 82 | * 83 | * @return The group ID of the process executing an operation on this filesystem. 84 | */ 85 | protected @gid_t long getFuseContextGid() { 86 | return getFuseContext().gid.get(); 87 | } 88 | 89 | /** 90 | * Returns the pid field of the fuse context. Only valid when called while a filesystem operation is taking place. 91 | * 92 | * @return The process ID of the process executing an operation on this filesystem. 93 | */ 94 | protected @pid_t long getFuseContextPid() { 95 | return getFuseContext().pid.get(); 96 | } 97 | 98 | /** 99 | * Returns the uid field of the fuse context. Only valid when called while a filesystem operation is taking place. 100 | * 101 | * @return The user ID of the user running the process executing an operation of this filesystem. 102 | */ 103 | protected @uid_t long getFuseContextUid() { 104 | return getFuseContext().uid.get(); 105 | } 106 | 107 | final String getFuseName() { 108 | String name = getName(); 109 | if (name == null) 110 | return defaultFilesystemName; 111 | 112 | name = regexNormalizeFilesystemName.matcher(name).replaceAll(""); 113 | if (name.isEmpty()) 114 | return defaultFilesystemName; 115 | 116 | return name.toLowerCase(); 117 | } 118 | 119 | protected final Logger getLogger() { 120 | return logger; 121 | } 122 | 123 | public final Path getMountPoint() { 124 | return this.mountPoint; 125 | } 126 | 127 | protected abstract String getName(); 128 | 129 | protected abstract String[] getOptions(); 130 | 131 | final AutoUnmountHook getUnmountHook() { 132 | return unmountHook; 133 | } 134 | 135 | /** 136 | * Populates a {@link StatWrapper} with somewhat-sane, usually-better-than-zero values. Subclasses may override this to 137 | * customize the default parameters applied to the stat structure, or to prevent such behavior in the first place (by 138 | * overriding this method with an empty one). 139 | * 140 | * @param stat The StructStat object to write to. 141 | * @param uid The UID under which the JVM is running. 142 | * @param gid The GID under which the JVM is running. 143 | */ 144 | protected StructStat defaultStat(StructStat stat) { 145 | // Set some sensible defaults 146 | stat.mode(0).setAllTimesMillis(Fuse.getInitTime()).nlink(1).uid(Fuse.getUid()).gid(Fuse.getGid()); 147 | return stat; 148 | } 149 | 150 | public final FuseFilesystem log(boolean logging) { 151 | return log(logging ? Logger.getLogger(getClass().getCanonicalName()) : null); 152 | } 153 | 154 | public final FuseFilesystem log(Logger logger) { 155 | mountLock.lock(); 156 | try { 157 | if (mountPoint != null) 158 | throw new IllegalStateException("Cannot turn logging on/orr when filesystem is already mounted."); 159 | this.logger = logger; 160 | } finally { 161 | mountLock.unlock(); 162 | } 163 | return this; 164 | } 165 | 166 | final void mount(Path mountPoint, boolean blocking) throws IOException { 167 | mountLock.lock(); 168 | try { 169 | if (isMounted()) { 170 | mountLock.unlock(); 171 | throw new IllegalStateException(getFuseName() + " is already mounted at " + this.mountPoint); 172 | } 173 | this.mountPoint = mountPoint; 174 | } finally { 175 | mountLock.unlock(); 176 | } 177 | beforeMount(mountPoint); 178 | } 179 | 180 | final void unmount() throws IOException { 181 | if (!isMounted()) 182 | throw new IllegalStateException("Tried to unmount a filesystem which is not mounted"); 183 | } 184 | 185 | final class AutoUnmountHook extends Thread { 186 | @Override 187 | public final void run() { 188 | try { 189 | Fuse.unmount(FuseFilesystem.this); 190 | } catch (final Exception e) { 191 | // Can't do much here in a shutdown hook. Silently ignore. 192 | } 193 | } 194 | } 195 | 196 | //////////////////////////////// 197 | /** 198 | * Get file attributes. 199 | * 200 | * Similar to stat(). The 'st_dev' and 'st_blksize' fields are ignored. 201 | * The 'st_ino' field is ignored except if the 'use_ino' mount option is given. 202 | * 203 | * @param path 204 | * @param stat 205 | * @return 206 | */ 207 | protected abstract int getattr(String path, StructStat stat); 208 | 209 | /** 210 | * Read the target of a symbolic link. 211 | * 212 | * The buffer should be filled with a null terminated string. 213 | * The buffer size argument includes the space for the terminating null character. 214 | * If the linkname is too long to fit in the buffer, it should be truncated. The return value should be 0 for success. 215 | * 216 | * @param path 217 | * @param buffer 218 | * @param size 219 | * @return 220 | */ 221 | protected abstract int readlink(String path, ByteBuffer buffer, long size); 222 | 223 | /** 224 | * Create a file node. 225 | * 226 | * This is called for creation of all non-directory, non-symlink nodes. 227 | * If the filesystem defines a create() method, then for regular files that will be called instead. 228 | * 229 | * @param path 230 | * @param mode 231 | * @param dev 232 | * @return 233 | */ 234 | protected abstract int mknod(String path, long mode, long dev); 235 | 236 | /** 237 | * Create a directory. 238 | * 239 | * Note that the mode argument may not have the type specification bits set, i.e. S_ISDIR(mode) can be false. 240 | * To obtain the correct directory type bits use mode|S_IFDIR 241 | * 242 | * @param path 243 | * @param mode 244 | * @return 245 | */ 246 | protected abstract int mkdir(String path, long mode); 247 | 248 | /** 249 | * Remove a file. 250 | * 251 | * @param path 252 | * @return 253 | */ 254 | protected abstract int unlink(String path); 255 | 256 | /** 257 | * Remove a directory. 258 | * 259 | * @param path 260 | * @return 261 | */ 262 | protected abstract int rmdir(String path); 263 | 264 | /** 265 | * Create a symbolic link. 266 | * 267 | * @param path 268 | * @param target 269 | * @return 270 | */ 271 | protected abstract int symlink(String path, String target); 272 | 273 | /** 274 | * Rename a file. 275 | * 276 | * @param path 277 | * @param newName 278 | * @return 279 | */ 280 | protected abstract int rename(String path, String newName); 281 | 282 | /** 283 | * Create a hard link to a file. 284 | * 285 | * @param path 286 | * @param target 287 | * @return 288 | */ 289 | protected abstract int link(String path, String target); 290 | 291 | /** 292 | * Change the permission bits of a file. 293 | * 294 | * @param path 295 | * @param mode 296 | * @return 297 | */ 298 | protected abstract int chmod(String path, long mode); 299 | 300 | /** 301 | * Change the owner and group of a file. 302 | * 303 | * @param path 304 | * @param uid 305 | * @param gid 306 | * @return 307 | */ 308 | protected abstract int chown(String path, long uid, long gid); 309 | 310 | /** 311 | * Change the size of a file. 312 | * 313 | * @param path 314 | * @param offset 315 | * @return 316 | */ 317 | protected abstract int truncate(String path, long offset); 318 | 319 | /** 320 | * File open operation. 321 | *

322 | * No creation (O_CREAT, O_EXCL) and by default also no truncation (O_TRUNC) flags will be passed to open(). 323 | * If an application specifies O_TRUNC, fuse first calls truncate() and then open(). 324 | * Only if 'atomic_o_trunc' has been specified and kernel version is 2.6.24 or later, O_TRUNC is passed on to open. 325 | *

326 | * Unless the 'default_permissions' mount option is given, open should check if the operation is permitted for the given flags. 327 | * Optionally open may also return an arbitrary filehandle in the fuse_file_info structure, which will be passed to all file operations. 328 | *

329 | * Changed in version 2.2 330 | * 331 | * @param path 332 | * @param info 333 | * @return 334 | */ 335 | protected abstract int open(String path, StructFuseFileInfo info); 336 | 337 | /** 338 | * Read data from an open file. 339 | *

340 | * Read should return exactly the number of bytes requested except on EOF or error, 341 | * otherwise the rest of the data will be substituted with zeroes. 342 | * An exception to this is when the 'direct_io' mount option is specified, 343 | * in which case the return value of the read system call will reflect the return value of this operation. 344 | *

345 | * Changed in version 2.2 346 | * 347 | * @param path 348 | * @param buffer 349 | * @param size 350 | * @param offset 351 | * @param info 352 | * @return 353 | */ 354 | protected abstract int read(String path, ByteBuffer buffer, long size, long offset, StructFuseFileInfo info); 355 | 356 | /** 357 | * Write data to an open file. 358 | *

359 | * Write should return exactly the number of bytes requested except on error. 360 | * An exception to this is when the 'direct_io' mount option is specified (see read operation). 361 | *

362 | * Changed in version 2.2 363 | * 364 | * @param path 365 | * @param buf 366 | * @param bufSize 367 | * @param writeOffset 368 | * @param info 369 | * @return 370 | */ 371 | protected abstract int write(String path, ByteBuffer buf, long bufSize, long writeOffset, StructFuseFileInfo info); 372 | 373 | /** 374 | * Get file system statistics. 375 | *

376 | * The 'f_frsize', 'f_favail', 'f_fsid' and 'f_flag' fields are ignored 377 | *

378 | * Replaced 'struct statfs' parameter with 'struct statvfs' in version 2.5 379 | * 380 | * @param path 381 | * @param statvfs 382 | * @return 383 | */ 384 | protected abstract int statfs(String path, StructStatvfs statvfs); 385 | 386 | /** 387 | * Possibly flush cached data. 388 | *

389 | * BIG NOTE: This is not equivalent to fsync(). It's not a request to sync dirty data. 390 | *

391 | * Flush is called on each close() of a file descriptor. 392 | * So if a filesystem wants to return write errors in close() and the file has cached dirty data, 393 | * this is a good place to write back data and return any errors. Since many applications ignore close() errors this is not always useful. 394 | *

395 | * NOTE: The flush() method may be called more than once for each open(). This happens if more than one file descriptor refers to an opened file due to dup(), dup2() or fork() calls. It is not possible to determine if a flush is final, so each flush should be treated equally. Multiple write-flush sequences are relatively rare, so this shouldn't be a problem. 396 | *

397 | * Filesystems shouldn't assume that flush will always be called after some writes, or that if will be called at all. 398 | *

399 | * Changed in version 2.2 400 | * 401 | * @param path 402 | * @param info 403 | * @return 404 | */ 405 | protected abstract int flush(String path, StructFuseFileInfo info); 406 | 407 | /** 408 | * Release an open file. 409 | *

410 | * Release is called when there are no more references to an open file: all file descriptors are closed and all memory mappings are unmapped. 411 | *

412 | * For every open() call there will be exactly one release() call with the same flags and file descriptor. 413 | * It is possible to have a file opened more than once, in which case only the last release will mean, that no more reads/writes will happen on the file. 414 | * The return value of release is ignored. 415 | *

416 | * Changed in version 2.2 417 | * 418 | * @param path 419 | * @param info 420 | * @return 421 | */ 422 | protected abstract int release(String path, StructFuseFileInfo info); 423 | 424 | /** 425 | * Synchronize file contents. 426 | *

427 | * If the datasync parameter is non-zero, then only the user data should be flushed, not the meta data. 428 | *

429 | * Changed in version 2.2 430 | * 431 | * @param path 432 | * @param datasync 433 | * @param info 434 | * @return 435 | */ 436 | protected abstract int fsync(String path, int datasync, StructFuseFileInfo info); 437 | 438 | /** 439 | * Set extended attribute. 440 | * 441 | * Set the attribute NAME of the file pointed to by PATH to VALUE (which 442 | * is SIZE bytes long). 443 | * 444 | * @param flags see {@link XAttrConstants} 445 | * @return Return 0 on success, -1 for errors. 446 | */ 447 | protected abstract int setxattr(String path, String xattr, ByteBuffer value, long size, int flags, int position); 448 | 449 | /** 450 | * Get extended attribute. 451 | * 452 | * Get the attribute NAME of the file pointed to by PATH to VALUE (which is 453 | * SIZE bytes long). 454 | * 455 | * @return Return 0 on success, -1 for errors. 456 | */ 457 | protected abstract int getxattr(String path, String xattr, XattrFiller filler, long size, long position); 458 | 459 | /** 460 | * List extended attributes. 461 | *

462 | * The retrieved list is placed 463 | * in list, a caller-allocated buffer whose size (in bytes) is specified 464 | * in the argument size. The list is the set of (null-terminated) 465 | * names, one after the other. Names of extended attributes to which 466 | * the calling process does not have access may be omitted from the 467 | * list. The length of the attribute name list is returned 468 | * 469 | * @param path 470 | * @param filler 471 | * @return 472 | */ 473 | protected abstract int listxattr(String path, XattrListFiller filler); 474 | 475 | /** 476 | * Remove extended attributes. 477 | * Remove the attribute NAME from the file pointed to by PATH. 478 | * 479 | * @param path 480 | * @param xattr 481 | * @return 0 on success, -1 for errors. 482 | */ 483 | protected abstract int removexattr(String path, String xattr); 484 | 485 | /** 486 | * Open directory. 487 | *

488 | * Unless the 'default_permissions' mount option is given, this method should check if opendir is permitted for this directory. 489 | * Optionally opendir may also return an arbitrary filehandle in the fuse_file_info structure, 490 | * which will be passed to readdir, closedir and fsyncdir. 491 | *

492 | * Introduced in version 2.3 493 | * 494 | * @param path 495 | * @param info 496 | * @return 497 | */ 498 | protected abstract int opendir(String path, StructFuseFileInfo info); 499 | 500 | /** 501 | * Read directory. 502 | * 503 | * The filesystem may choose between two modes of operation: 504 | *

505 | *

    506 | *
  1. The readdir implementation ignores the offset parameter, and passes zero to the filler function's offset. 507 | * The filler function will not return '1' (unless an error happens), so the whole directory is read in a single readdir operation.
  2. 508 | * 509 | *
  3. The readdir implementation keeps track of the offsets of the directory entries. 510 | * It uses the offset parameter and always passes non-zero offset to the filler function. 511 | * When the buffer is full (or an error happens) the filler function will return '1'.
  4. 512 | *
513 | * Introduced in version 2.3 514 | * 515 | * @param path 516 | * @param filler 517 | * @return 518 | */ 519 | protected abstract int readdir(String path, StructFuseFileInfo info, DirectoryFiller filler); 520 | 521 | /** 522 | * Release directory. 523 | *

524 | * Introduced in version 2.3 525 | * 526 | * @param path 527 | * @param info 528 | * @return 529 | */ 530 | protected abstract int releasedir(String path, StructFuseFileInfo info); 531 | 532 | /** 533 | * Synchronize directory contents. 534 | *

535 | * If the datasync parameter is non-zero, then only the user data should be flushed, not the meta data 536 | *

537 | * Introduced in version 2.3 538 | * 539 | * @param path 540 | * @param datasync 541 | * @param info 542 | * @return 543 | */ 544 | protected abstract int fsyncdir(String path, int datasync, StructFuseFileInfo info); 545 | 546 | /** 547 | * Initialize filesystem. 548 | *

549 | * The return value will passed in the private_data field of fuse_context to all file operations and as a parameter to the destroy() method. 550 | *

551 | * Introduced in version 2.3 Changed in version 2.6 552 | */ 553 | protected abstract void init(); 554 | 555 | /** 556 | * Clean up filesystem 557 | *

558 | * Called on filesystem exit. 559 | *

560 | * Introduced in version 2.3 561 | */ 562 | protected abstract void destroy(); 563 | 564 | /** 565 | * Check file access permissions. 566 | *

567 | * This will be called for the access() system call. 568 | * If the 'default_permissions' mount option is given, this method is not called. 569 | *

570 | * This method is not called under Linux kernel versions 2.4.x 571 | *

572 | * Introduced in version 2.5 573 | * 574 | * @param mask see {@link AccessConstants} 575 | * @return -ENOENT if the path doesn't exist, -EACCESS if the requested permission isn't available, or 0 for success 576 | * @return 577 | */ 578 | protected abstract int access(String path, int access); 579 | 580 | /** 581 | * Create and open a file. 582 | *

583 | * If the file does not exist, first create it with the specified mode, and then open it. 584 | *

585 | * If this method is not implemented or under Linux kernel versions earlier than 2.6.15, 586 | * the mknod() and open() methods will be called instead. 587 | *

588 | * Introduced in version 2.5 589 | * 590 | * @param path 591 | * @param mode 592 | * @param info 593 | * @return 594 | */ 595 | protected abstract int create(String path, long mode, StructFuseFileInfo info); 596 | 597 | /** 598 | * Change the size of an open file. 599 | *

600 | * This method is called instead of the truncate() method if the truncation was invoked from an ftruncate() system call. 601 | *

602 | * If this method is not implemented or under Linux kernel versions earlier than 2.6.15, the truncate() method will be called instead. 603 | *

604 | * Introduced in version 2.5 605 | * 606 | * @param path 607 | * @param offset 608 | * @param info 609 | * @return 610 | */ 611 | protected abstract int ftruncate(String path, long offset, StructFuseFileInfo info); 612 | 613 | /** 614 | * Get attributes from an open file. 615 | *

616 | * This method is called instead of the getattr() method if the file information is available. 617 | *

618 | * Currently this is only called after the create() method if that is implemented (see above). 619 | * Later it may be called for invocations of fstat() too. 620 | *

621 | * Introduced in version 2.5 622 | * 623 | * @param path 624 | * @param stat 625 | * @param info 626 | * @return 627 | */ 628 | protected abstract int fgetattr(String path, StructStat stat, StructFuseFileInfo info); 629 | 630 | /** 631 | * Perform POSIX file locking operation. 632 | *

633 | * The cmd argument will be either F_GETLK, F_SETLK or F_SETLKW. 634 | *

635 | * For the meaning of fields in 'struct flock' see the man page for fcntl(2). The l_whence field will always be set to SEEK_SET. 636 | *

637 | * For checking lock ownership, the 'fuse_file_info->owner' argument must be used. 638 | *

639 | * For F_GETLK operation, the library will first check currently held locks, 640 | * and if a conflicting lock is found it will return information without calling this method. 641 | * This ensures, that for local locks the l_pid field is correctly filled in. 642 | * The results may not be accurate in case of race conditions and in the presence of hard links, 643 | * but it's unlikely that an application would rely on accurate GETLK results in these cases. 644 | * If a conflicting lock is not found, this method will be called, and the filesystem may fill out l_pid by a meaningful value, 645 | * or it may leave this field zero. 646 | *

647 | * For F_SETLK and F_SETLKW the l_pid field will be set to the pid of the process performing the locking operation. 648 | *

649 | * Note: if this method is not implemented, the kernel will still allow file locking to work locally. 650 | * Hence it is only interesting for network filesystems and similar. 651 | *

652 | * Introduced in version 2.6 653 | * 654 | * @param path 655 | * @param info 656 | * @param command 657 | * @param flock 658 | * @return 659 | */ 660 | protected abstract int lock(String path, StructFuseFileInfo info, int command, StructFlock flock); 661 | 662 | /** 663 | * Change the access and modification times of a file with nanosecond resolution. 664 | *

665 | * This supersedes the old utime() interface. New applications should use this. 666 | *

667 | * See the utimensat(2) man page for details. 668 | *

669 | * Introduced in version 2.6 670 | * 671 | * @param path 672 | * @param timeBuffer 673 | * @return 674 | */ 675 | protected abstract int utimens(String path, StructTimeBuffer timeBuffer); 676 | 677 | /** 678 | * Map block index within file to block index within device/ 679 | *

680 | * Note: This makes sense only for block device backed filesystems mounted with the 'blkdev' option 681 | *

682 | * Introduced in version 2.6 683 | * 684 | * @param path 685 | * @param info 686 | * @return 687 | */ 688 | protected abstract int bmap(String path, StructFuseFileInfo info); 689 | 690 | /** 691 | * Ioctl. 692 | *

693 | * flags will have FUSE_IOCTL_COMPAT set for 32bit ioctls in 64bit environment. 694 | * The size and direction of data is determined by _IOC_*() decoding of cmd. 695 | * For _IOC_NONE, data will be NULL, for _IOC_WRITE data is out area, for _IOC_READ in area and if both are set in/out area. 696 | * In all non-NULL cases, the area is of _IOC_SIZE(cmd) bytes. 697 | * 698 | * @param flags See { 699 | * @IoctlFlags} 700 | */ 701 | protected abstract int ioctl(String path, int cmd, Pointer arg, StructFuseFileInfo fi, long flags, Pointer data); 702 | 703 | /** 704 | * Poll for IO readiness events. 705 | *

706 | * Note: If ph is non-NULL, the client should notify when IO readiness events occur by calling 707 | * fuse_notify_poll() with the specified ph. 708 | *

709 | * Regardless of the number of times poll with a non-NULL ph is received, single notification is enough to clear all. 710 | * Notifying more times incurs overhead but doesn't harm correctness. 711 | *

712 | * The callee is responsible for destroying ph with fuse_pollhandle_destroy() when no longer in use. 713 | * 714 | * @param reventsp A pointer to a bitmask of the returned events satisfied. 715 | */ 716 | protected abstract int poll(String path, StructFuseFileInfo fi, StructFusePollHandle ph, Pointer reventsp); 717 | 718 | /** 719 | * Write contents of buffer to an open file. 720 | *

721 | * Similar to the write() method, but data is supplied in a generic buffer. 722 | * Use fuse_buf_copy() to transfer data to the destination. 723 | */ 724 | protected abstract int write_buf(String path, StructFuseBufvec buf, long off, StructFuseFileInfo fi); 725 | 726 | /** 727 | * Store data from an open file in a buffer. 728 | *

729 | * Similar to the read() method, but data is stored and returned in a generic buffer. 730 | *

731 | * No actual copying of data has to take place, the source file descriptor may simply be stored in the buffer for later data transfer. 732 | *

733 | * The buffer must be allocated dynamically and stored at the location pointed to by bufp. 734 | * If the buffer contains memory regions, they too must be allocated using malloc(). 735 | * The allocated memory will be freed by the caller. 736 | */ 737 | protected abstract int read_buf(String path, Pointer bufp, long size, long off, StructFuseFileInfo fi); 738 | 739 | /** 740 | * Perform BSD file locking operation 741 | *

742 | * The op argument will be either LOCK_SH, LOCK_EX or LOCK_UN 743 | *

744 | * Nonblocking requests will be indicated by ORing LOCK_NB to 745 | * the above operations 746 | *

747 | * For more information see the flock(2) manual page. 748 | *

749 | * Additionally fi->owner will be set to a value unique to this open file. 750 | * This same value will be supplied to ->release() when the file is released. 751 | *

752 | * Note: if this method is not implemented, the kernel will still allow file locking to work locally. 753 | * Hence it is only interesting for network filesystems and similar. 754 | * 755 | * @param op see {@link ru.serce.jnrfuse.struct.Flock} 756 | */ 757 | protected abstract int flock(String path, StructFuseFileInfo fi, int op); 758 | 759 | /** 760 | * Allocates space for an open file. 761 | *

762 | * This function ensures that required space is allocated for specified file. 763 | * If this function returns success then any subsequent write request to specified range is guaranteed not to fail because of lack 764 | * of space on the file system media. 765 | */ 766 | protected abstract int fallocate(String path, int mode, long off, long length, StructFuseFileInfo fi); 767 | } 768 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/IoctlFlags.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | /** 4 | * @author Sergey Tselovalnikov 5 | * @since 05.06.15 6 | */ 7 | final class IoctlFlags { 8 | 9 | // flags 10 | public static final int _IOC_NONE = 0; 11 | public static final int _IOC_READ = 2; 12 | public static final int _IOC_WRITE = 1; 13 | 14 | 15 | // macros 16 | public static final int _IOC_SIZEBITS = 14; 17 | public static final int _IOC_TYPEBITS = 8; 18 | public static final int _IOC_NRBITS = 8; 19 | public static final int _IOC_NRSHIFT = 0; 20 | 21 | public static int _IOC_TYPESHIFT() { 22 | return (_IOC_NRSHIFT + _IOC_NRBITS); 23 | } 24 | 25 | public static int _IOC_SIZESHIFT() { 26 | return (_IOC_TYPESHIFT() + _IOC_TYPEBITS); 27 | } 28 | 29 | public static int _IOC_SIZEMASK() { 30 | return ((1 << _IOC_SIZEBITS) - 1); 31 | } 32 | 33 | public static int _IOC_SIZE(int nr) { 34 | return (((nr) >> _IOC_SIZESHIFT()) & _IOC_SIZEMASK()); 35 | } 36 | 37 | private IoctlFlags() { 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/JNRUtil.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import com.kenai.jffi.MemoryIO; 4 | import java.lang.reflect.Field; 5 | import java.nio.ByteBuffer; 6 | import jnr.ffi.Pointer; 7 | import jnr.ffi.StructLayout; 8 | 9 | final class JNRUtil { 10 | public static ByteBuffer toByteBuffer(Pointer pointer, long capacity) { 11 | if (capacity > Integer.MAX_VALUE) 12 | throw new IllegalArgumentException("Capacity too big: " + capacity); 13 | return MemoryIO.getInstance().newDirectByteBuffer(pointer.address(), (int) capacity); 14 | } 15 | 16 | public static String toString(StructLayout layout, Pointer p) { 17 | StringBuilder sb = new StringBuilder(); 18 | Field[] fields = layout.getClass().getDeclaredFields(); 19 | sb.append(layout.getClass().getSimpleName()).append(" { \n"); 20 | final String fieldPrefix = " "; 21 | for (Field field : fields) { 22 | try { 23 | sb.append(fieldPrefix) 24 | .append(field.getType().getSimpleName()).append(' ').append(field.getName()).append(": ") 25 | .append(field.getType().getMethod("toString", Pointer.class).invoke(p)) 26 | .append('\n'); 27 | } catch (Throwable ex) { 28 | throw new RuntimeException(ex); 29 | } 30 | } 31 | sb.append("}\n"); 32 | 33 | return sb.toString(); 34 | } 35 | 36 | private JNRUtil() { 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/LibDl.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | public interface LibDl { 4 | public static final int RTLD_LAZY = 0x1; 5 | public static final int RTLD_NOW = 0x2; 6 | public static final int RTLD_GLOBAL = 0x100; 7 | 8 | public void dlopen(String file, int mode); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/LibFuse.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import jnr.ffi.Pointer; 4 | import jnr.ffi.types.size_t; 5 | import jnr.ffi.types.ssize_t; 6 | 7 | public interface LibFuse { 8 | static interface LibFuseProbe { 9 | } 10 | 11 | static interface LibMacFuseProbe extends LibFuseProbe { 12 | String macfuse_version(); 13 | } 14 | 15 | StructFuseContext fuse_get_context(); 16 | 17 | /** 18 | * Main function of FUSE. 19 | *

20 | * This function does the following: 21 | * - parses command line options (-d -s and -h) 22 | * - passes relevant mount options to the fuse_mount() 23 | * - installs signal handlers for INT, HUP, TERM and PIPE 24 | * - registers an exit handler to unmount the filesystem on program exit 25 | * - creates a fuse handle 26 | * - registers the operations 27 | * - calls either the single-threaded or the multi-threaded event loop 28 | * 29 | * @param argc the argument counter passed to the main() function 30 | * @param argv the argument vector passed to the main() function 31 | * @param op the file system operation 32 | * @param user_data user data supplied in the context during the init() method 33 | * @return 0 on success, nonzero on failure 34 | */ 35 | int fuse_main_real(int argc, String[] argv, StructFuseOperations op, @size_t int size, Pointer user_data); 36 | 37 | @size_t long fuse_buf_size(Pointer bufv); // StructFuseBufvec 38 | 39 | @ssize_t long fuse_buf_copy(Pointer dstv, Pointer srcv, int flags); // StructFuseBufvec 40 | 41 | void fuse_pollhandle_destroy(Pointer ph); // StructFusePollhandle 42 | 43 | int fuse_notify_poll(Pointer ph); // StructFusePollhandle 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/LoggedFuseFilesystem.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.nio.file.Path; 5 | import java.util.logging.Level; 6 | import java.util.logging.Logger; 7 | import jnr.ffi.Pointer; 8 | 9 | final class LoggedFuseFilesystem extends FuseFilesystem { 10 | private static interface LoggedMethod { 11 | public T invoke(); 12 | } 13 | 14 | private static interface LoggedVoidMethod { 15 | public void invoke(); 16 | } 17 | 18 | private static final String methodSuccess = "Method succeeded."; 19 | private static final String methodFailure = "Exception thrown: "; 20 | private static final String methodResult = " Result: "; 21 | private final String className; 22 | private final Logger logger; 23 | private final FuseFilesystem filesystem; 24 | 25 | LoggedFuseFilesystem(FuseFilesystem filesystem, Logger logger) { 26 | this.filesystem = filesystem; 27 | this.logger = logger; 28 | className = filesystem.getClass().getName(); 29 | } 30 | 31 | private void log(final String methodName, final LoggedVoidMethod method) { 32 | log(methodName, method, null, (Object[]) null); 33 | } 34 | 35 | private void log(final String methodName, final LoggedVoidMethod method, final String path, final Object... args) { 36 | try { 37 | logger.entering(className, methodName, args); 38 | method.invoke(); 39 | logger.logp(Level.INFO, className, methodName, (path == null ? "" : "[" + path + "] ") + methodSuccess, args); 40 | logger.exiting(className, methodName, args); 41 | } catch (Throwable e) { 42 | logException(e, methodName, null, args); 43 | } 44 | } 45 | 46 | private T log(String methodName, T defaultValue, LoggedMethod method) { 47 | return log(methodName, defaultValue, method, null, (Object[]) null); 48 | } 49 | 50 | private T log(String methodName, T defaultValue, LoggedMethod method, String path, Object... args) { 51 | try { 52 | logger.entering(className, methodName, args); 53 | final T result = method.invoke(); 54 | logger.logp(Level.INFO, className, methodName, (path == null ? "" : "[" + path + "] ") + methodSuccess + methodResult + result, args); 55 | logger.exiting(className, methodName, args); 56 | return result; 57 | } catch (Throwable e) { 58 | return logException(e, methodName, defaultValue, args); 59 | } 60 | } 61 | 62 | private T logException(Throwable e, String methodName, T defaultValue, Object... args) { 63 | final StackTraceElement[] stack = e.getStackTrace(); 64 | final StringBuilder builder = new StringBuilder(); 65 | for (StackTraceElement element : stack) 66 | builder.append('\n').append(element); 67 | 68 | logger.logp(Level.SEVERE, className, methodName, methodFailure + e + builder.toString(), args); 69 | return defaultValue; 70 | } 71 | 72 | @Override 73 | public final void _destroy() { 74 | destroy(); 75 | _destroy(this, filesystem); 76 | } 77 | 78 | @Override 79 | public int access(final String path, final int access) { 80 | return log("access", 0, new LoggedMethod() { 81 | @Override 82 | public Integer invoke() { 83 | return filesystem.access(path, access); 84 | } 85 | }, path, access); 86 | } 87 | 88 | @Override 89 | public void afterUnmount(final Path mountPoint) { 90 | log("afterUnmount", new LoggedVoidMethod() { 91 | @Override 92 | public void invoke() { 93 | filesystem.afterUnmount(mountPoint); 94 | } 95 | }, mountPoint.toString()); 96 | } 97 | 98 | @Override 99 | public void beforeMount(final Path mountPoint) { 100 | log("beforeMount", new LoggedVoidMethod() { 101 | @Override 102 | public void invoke() { 103 | filesystem.beforeMount(mountPoint); 104 | } 105 | }, mountPoint.toString()); 106 | } 107 | 108 | @Override 109 | public int bmap(final String path, final StructFuseFileInfo info) { 110 | return log("bmap", 0, new LoggedMethod() { 111 | @Override 112 | public Integer invoke() { 113 | return filesystem.bmap(path, info); 114 | } 115 | }, path, info); 116 | } 117 | 118 | @Override 119 | public int chmod(final String path, final long mode) { 120 | return log("chmod", 0, new LoggedMethod() { 121 | @Override 122 | public Integer invoke() { 123 | return filesystem.chmod(path, mode); 124 | } 125 | }, path, mode); 126 | } 127 | 128 | @Override 129 | public int chown(final String path, final long uid, final long gid) { 130 | return log("chown", 0, new LoggedMethod() { 131 | @Override 132 | public Integer invoke() { 133 | return filesystem.chown(path, uid, gid); 134 | } 135 | }, path, uid, gid); 136 | } 137 | 138 | @Override 139 | public int create(final String path, final long mode, final StructFuseFileInfo info) { 140 | return log("create", 0, new LoggedMethod() { 141 | @Override 142 | public Integer invoke() { 143 | return filesystem.create(path, mode, info); 144 | } 145 | }, path, mode, info); 146 | } 147 | 148 | @Override 149 | public void destroy() { 150 | log("destroy", new LoggedVoidMethod() { 151 | @Override 152 | public void invoke() { 153 | filesystem.destroy(); 154 | } 155 | }); 156 | } 157 | 158 | @Override 159 | public int fgetattr(final String path, final StructStat stat, final StructFuseFileInfo info) { 160 | return log("fgetattr", 0, new LoggedMethod() { 161 | @Override 162 | public Integer invoke() { 163 | return filesystem.fgetattr(path, stat, info); 164 | } 165 | }, path, stat); 166 | } 167 | 168 | @Override 169 | public int flush(final String path, final StructFuseFileInfo info) { 170 | return log("flush", 0, new LoggedMethod() { 171 | @Override 172 | public Integer invoke() { 173 | return filesystem.flush(path, info); 174 | } 175 | }, path, info); 176 | } 177 | 178 | @Override 179 | public int fsync(final String path, final int datasync, final StructFuseFileInfo info) { 180 | return log("fsync", 0, new LoggedMethod() { 181 | @Override 182 | public Integer invoke() { 183 | return filesystem.fsync(path, datasync, info); 184 | } 185 | }, path, info); 186 | } 187 | 188 | @Override 189 | public int fsyncdir(final String path, final int datasync, final StructFuseFileInfo info) { 190 | return log("fsyncdir", 0, new LoggedMethod() { 191 | @Override 192 | public Integer invoke() { 193 | return filesystem.fsyncdir(path, datasync, info); 194 | } 195 | }, path, info); 196 | } 197 | 198 | @Override 199 | public int ftruncate(final String path, final long offset, final StructFuseFileInfo info) { 200 | return log("ftruncate", 0, new LoggedMethod() { 201 | @Override 202 | public Integer invoke() { 203 | return filesystem.ftruncate(path, offset, info); 204 | } 205 | }, path, offset, info); 206 | } 207 | 208 | @Override 209 | public int getattr(final String path, final StructStat stat) { 210 | return log("getattr", 0, new LoggedMethod() { 211 | @Override 212 | public Integer invoke() { 213 | return filesystem.getattr(path, stat); 214 | } 215 | }, path, stat); 216 | } 217 | 218 | @Override 219 | protected String getName() { 220 | return log("getName", null, new LoggedMethod() { 221 | @Override 222 | public String invoke() { 223 | return filesystem.getName(); 224 | } 225 | }); 226 | } 227 | 228 | @Override 229 | protected String[] getOptions() { 230 | return log("getOptions", null, new LoggedMethod() { 231 | @Override 232 | public String[] invoke() { 233 | return filesystem.getOptions(); 234 | } 235 | }); 236 | } 237 | 238 | @Override 239 | public int getxattr(final String path, final String xattr, final XattrFiller filler, final long size, final long position) { 240 | return log("getxattr", 0, new LoggedMethod() { 241 | @Override 242 | public Integer invoke() { 243 | return filesystem.getxattr(path, xattr, filler, size, position); 244 | } 245 | }, path, xattr, filler, size, position); 246 | } 247 | 248 | @Override 249 | public void init() { 250 | log("init", new LoggedVoidMethod() { 251 | @Override 252 | public void invoke() { 253 | filesystem.init(); 254 | } 255 | }); 256 | } 257 | 258 | @Override 259 | public int link(final String path, final String target) { 260 | return log("link", 0, new LoggedMethod() { 261 | @Override 262 | public Integer invoke() { 263 | return filesystem.link(path, target); 264 | } 265 | }, path, target); 266 | } 267 | 268 | @Override 269 | public int listxattr(final String path, final XattrListFiller filler) { 270 | return log("listxattr", 0, new LoggedMethod() { 271 | @Override 272 | public Integer invoke() { 273 | return filesystem.listxattr(path, filler); 274 | } 275 | }, path, filler); 276 | } 277 | 278 | @Override 279 | public int lock(final String path, final StructFuseFileInfo info, final int command, final StructFlock flock) { 280 | return log("lock", 0, new LoggedMethod() { 281 | @Override 282 | public Integer invoke() { 283 | return filesystem.lock(path, info, command, flock); 284 | } 285 | }, path, info, command, flock); 286 | } 287 | 288 | @Override 289 | public int mkdir(final String path, final long mode) { 290 | return log("mkdir", 0, new LoggedMethod() { 291 | @Override 292 | public Integer invoke() { 293 | return filesystem.mkdir(path, mode); 294 | } 295 | }, path, mode); 296 | } 297 | 298 | @Override 299 | public int mknod(final String path, final long mode, final long dev) { 300 | return log("mknod", 0, new LoggedMethod() { 301 | @Override 302 | public Integer invoke() { 303 | return filesystem.mknod(path, mode, dev); 304 | } 305 | }, path, mode, dev); 306 | } 307 | 308 | @Override 309 | public int open(final String path, final StructFuseFileInfo info) { 310 | return log("open", 0, new LoggedMethod() { 311 | @Override 312 | public Integer invoke() { 313 | return filesystem.open(path, info); 314 | } 315 | }, path, info); 316 | } 317 | 318 | @Override 319 | public int opendir(final String path, final StructFuseFileInfo info) { 320 | return log("opendir", 0, new LoggedMethod() { 321 | @Override 322 | public Integer invoke() { 323 | return filesystem.opendir(path, info); 324 | } 325 | }, path, info); 326 | } 327 | 328 | @Override 329 | public int read(final String path, final ByteBuffer buffer, final long size, final long offset, final StructFuseFileInfo info) { 330 | return log("read", 0, new LoggedMethod() { 331 | @Override 332 | public Integer invoke() { 333 | return filesystem.read(path, buffer, size, offset, info); 334 | } 335 | }, path, buffer, size, offset, info); 336 | } 337 | 338 | @Override 339 | public int readdir(final String path, final StructFuseFileInfo info, final DirectoryFiller filler) { 340 | return log("readdir", 0, new LoggedMethod() { 341 | @Override 342 | public Integer invoke() { 343 | return filesystem.readdir(path, info, filler); 344 | } 345 | }, path, filler); 346 | } 347 | 348 | @Override 349 | public int readlink(final String path, final ByteBuffer buffer, final long size) { 350 | return log("readlink", 0, new LoggedMethod() { 351 | @Override 352 | public Integer invoke() { 353 | return filesystem.readlink(path, buffer, size); 354 | } 355 | }, path, buffer, size); 356 | } 357 | 358 | @Override 359 | public int release(final String path, final StructFuseFileInfo info) { 360 | return log("release", 0, new LoggedMethod() { 361 | @Override 362 | public Integer invoke() { 363 | return filesystem.release(path, info); 364 | } 365 | }, path, info); 366 | } 367 | 368 | @Override 369 | public int releasedir(final String path, final StructFuseFileInfo info) { 370 | return log("releasedir", 0, new LoggedMethod() { 371 | @Override 372 | public Integer invoke() { 373 | return filesystem.releasedir(path, info); 374 | } 375 | }, path, info); 376 | } 377 | 378 | @Override 379 | public int removexattr(final String path, final String xattr) { 380 | return log("removexattr", 0, new LoggedMethod() { 381 | @Override 382 | public Integer invoke() { 383 | return filesystem.removexattr(path, xattr); 384 | } 385 | }, path, xattr); 386 | } 387 | 388 | @Override 389 | public int rename(final String path, final String newName) { 390 | return log("rename", 0, new LoggedMethod() { 391 | @Override 392 | public Integer invoke() { 393 | return filesystem.rename(path, newName); 394 | } 395 | }); 396 | } 397 | 398 | @Override 399 | public int rmdir(final String path) { 400 | return log("rmdir", 0, new LoggedMethod() { 401 | @Override 402 | public Integer invoke() { 403 | return filesystem.rmdir(path); 404 | } 405 | }, path); 406 | } 407 | 408 | @Override 409 | public int setxattr(final String path, final String name, final ByteBuffer buf, final long size, final int flags, final int position) { 410 | return log("setxattr", 0, new LoggedMethod() { 411 | @Override 412 | public Integer invoke() { 413 | return filesystem.setxattr(path, name, buf, size, flags, position); 414 | } 415 | }, path, name, buf, size, flags, position); 416 | } 417 | 418 | @Override 419 | public int statfs(final String path, final StructStatvfs statvfs) { 420 | return log("statfs", 0, new LoggedMethod() { 421 | @Override 422 | public Integer invoke() { 423 | return filesystem.statfs(path, statvfs); 424 | } 425 | }, path, statvfs); 426 | } 427 | 428 | @Override 429 | public int symlink(final String path, final String target) { 430 | return log("symlink", 0, new LoggedMethod() { 431 | @Override 432 | public Integer invoke() { 433 | return filesystem.symlink(path, target); 434 | } 435 | }, path, target); 436 | } 437 | 438 | @Override 439 | public int truncate(final String path, final long offset) { 440 | return log("truncate", 0, new LoggedMethod() { 441 | @Override 442 | public Integer invoke() { 443 | return filesystem.truncate(path, offset); 444 | } 445 | }, path, offset); 446 | } 447 | 448 | @Override 449 | public int unlink(final String path) { 450 | return log("unlink", 0, new LoggedMethod() { 451 | @Override 452 | public Integer invoke() { 453 | return filesystem.unlink(path); 454 | } 455 | }, path); 456 | } 457 | 458 | @Override 459 | public int utimens(final String path, final StructTimeBuffer timeBuffer) { 460 | return log("utimens", 0, new LoggedMethod() { 461 | @Override 462 | public Integer invoke() { 463 | return filesystem.utimens(path, timeBuffer); 464 | } 465 | }, path, timeBuffer); 466 | } 467 | 468 | @Override 469 | public int write(final String path, final ByteBuffer buf, final long bufSize, final long writeOffset, final StructFuseFileInfo wrapper) { 470 | return log("write", 0, new LoggedMethod() { 471 | @Override 472 | public Integer invoke() { 473 | return filesystem.write(path, buf, bufSize, writeOffset, wrapper); 474 | } 475 | }, path, buf, bufSize, writeOffset, wrapper); 476 | } 477 | 478 | @Override 479 | protected int ioctl(final String path, final int cmd, final Pointer arg, final StructFuseFileInfo fi, final long flags, final Pointer data) { 480 | return log("ioctl", 0, new LoggedMethod() { 481 | @Override 482 | public Integer invoke() { 483 | return filesystem.ioctl(path, cmd, arg, fi, flags, data); 484 | } 485 | }, path, cmd, arg, fi, flags, data); 486 | } 487 | 488 | @Override 489 | protected int poll(final String path, final StructFuseFileInfo fi, final StructFusePollHandle ph, final Pointer reventsp) { 490 | return log("poll", 0, new LoggedMethod() { 491 | @Override 492 | public Integer invoke() { 493 | return filesystem.poll(path, fi, ph, reventsp); 494 | } 495 | }, path, fi, ph, reventsp); 496 | } 497 | 498 | @Override 499 | protected int write_buf(final String path, final StructFuseBufvec buf, final long off, final StructFuseFileInfo fi) { 500 | return log("write_buf", 0, new LoggedMethod() { 501 | @Override 502 | public Integer invoke() { 503 | return filesystem.write_buf(path, buf, off, fi); 504 | } 505 | }, path, buf, off, fi); 506 | } 507 | 508 | @Override 509 | protected int read_buf(final String path, final Pointer bufp, final long size, final long off, final StructFuseFileInfo fi) { 510 | return log("read_buf", 0, new LoggedMethod() { 511 | @Override 512 | public Integer invoke() { 513 | return filesystem.read_buf(path, bufp, size, off, fi); 514 | } 515 | }, path, bufp, size, off, fi); 516 | } 517 | 518 | @Override 519 | protected int flock(final String path, final StructFuseFileInfo fi, final int op) { 520 | return log("flock", 0, new LoggedMethod() { 521 | @Override 522 | public Integer invoke() { 523 | return filesystem.flock(path, fi, op); 524 | } 525 | }, path, fi, op); 526 | } 527 | 528 | @Override 529 | protected int fallocate(final String path, final int mode, final long off, final long length, final StructFuseFileInfo fi) { 530 | return log("fallocate", 0, new LoggedMethod() { 531 | @Override 532 | public Integer invoke() { 533 | return filesystem.fallocate(path, mode, off, length, fi); 534 | } 535 | }, path, mode, off, length, fi); 536 | } 537 | 538 | } 539 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/Platform.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import java.util.concurrent.locks.Lock; 4 | import java.util.concurrent.locks.ReentrantLock; 5 | import jnr.ffi.LibraryLoader; 6 | 7 | import co.paralleluniverse.fuse.LibFuse.LibFuseProbe; 8 | import co.paralleluniverse.fuse.LibFuse.LibMacFuseProbe; 9 | 10 | import jnr.ffi.Platform.CPU; 11 | 12 | final class Platform { 13 | public static enum PlatformEnum { 14 | LINUX_X86_64, LINUX_I686, LINUX_PPC, MAC, MAC_MACFUSE, FREEBSD, LINUX_ARM 15 | } 16 | 17 | private static final String[] osxFuseLibraries = {"fuse4x", "osxfuse", "macfuse", "fuse"}; 18 | private static PlatformEnum platform = null; 19 | private static LibFuse libFuse = null; 20 | private static final Lock initLock = new ReentrantLock(); 21 | 22 | static final LibFuse fuse() { 23 | if (libFuse == null) 24 | init(); 25 | return libFuse; 26 | } 27 | 28 | private static void init() { 29 | if (libFuse != null) 30 | return; 31 | initLock.lock(); 32 | try { 33 | final jnr.ffi.Platform p = jnr.ffi.Platform.getNativePlatform(); 34 | // Need to recheck 35 | if (libFuse == null) { 36 | switch (p.getOS()) { 37 | case FREEBSD: 38 | platform = PlatformEnum.FREEBSD; 39 | libFuse = LibraryLoader.create(LibFuse.class).failImmediately().load("fuse"); 40 | break; 41 | case DARWIN: 42 | // First, need to load iconv 43 | final LibDl dl = LibraryLoader.create(LibDl.class).failImmediately().load("iconv"); 44 | dl.dlopen("iconv", LibDl.RTLD_LAZY | LibDl.RTLD_GLOBAL); 45 | libFuse = null; 46 | LibFuseProbe probe; 47 | for (String library : osxFuseLibraries) { 48 | try { 49 | // Regular FUSE-compatible fuse library 50 | platform = PlatformEnum.MAC; 51 | libFuse = LibraryLoader.create(LibFuse.class).failImmediately().load(library); 52 | break; 53 | } catch (Throwable e) { 54 | // Carry on 55 | } 56 | try { 57 | probe = LibraryLoader.create(LibMacFuseProbe.class).failImmediately().load(library); 58 | ((LibMacFuseProbe) probe).macfuse_version(); 59 | // MacFUSE-compatible fuse library 60 | platform = PlatformEnum.MAC_MACFUSE; 61 | libFuse = LibraryLoader.create(LibFuse.class).failImmediately().load("fuse"); 62 | break; 63 | } catch (Throwable e) { 64 | // Carry on 65 | } 66 | } 67 | if (libFuse == null) { 68 | // Everything failed. Do a last-ditch attempt. 69 | // Worst-case scenario, this causes an exception 70 | // which will be more meaningful to the user than a NullPointerException on libFuse. 71 | libFuse = LibraryLoader.create(LibFuse.class).failImmediately().load("fuse"); 72 | } 73 | break; 74 | 75 | case LINUX: 76 | LibraryLoader libraryLoader = LibraryLoader.create(LibFuse.class); 77 | if (p.getCPU() == CPU.X86_64) { 78 | libraryLoader.search("/lib/x86_64-linux-gnu"); 79 | platform = PlatformEnum.LINUX_X86_64; 80 | } else if(p.getCPU() == CPU.I386) { 81 | libraryLoader.search("/lib/i386-linux-gnu"); 82 | platform = PlatformEnum.LINUX_I686; 83 | } else if(p.getCPU() == CPU.ARM) { 84 | platform = PlatformEnum.LINUX_ARM; 85 | } else { 86 | platform = PlatformEnum.LINUX_PPC; 87 | } 88 | 89 | libFuse = libraryLoader.failImmediately().load("fuse"); 90 | break; 91 | 92 | default: 93 | throw new RuntimeException("Unsupported Platform"); 94 | } 95 | } 96 | } finally { 97 | initLock.unlock(); 98 | } 99 | } 100 | 101 | public static final PlatformEnum platform() { 102 | if (platform == null) 103 | init(); 104 | 105 | return platform; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/ProcessGobbler.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.InputStreamReader; 7 | 8 | /** 9 | * Run a process, grab all from stdout, return that. 10 | */ 11 | final class ProcessGobbler { 12 | private static final class Gobbler extends Thread { 13 | private final InputStream stream; 14 | private String contents = null; 15 | private boolean failed = false; 16 | 17 | Gobbler(InputStream stream) { 18 | this.stream = stream; 19 | start(); 20 | } 21 | 22 | private String getContents() { 23 | if (failed) 24 | return null; 25 | 26 | return contents; 27 | } 28 | 29 | @Override 30 | public final void run() { 31 | final BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); 32 | final StringBuilder contents = new StringBuilder(); 33 | String line; 34 | try { 35 | while ((line = reader.readLine()) != null) { 36 | contents.append(line); 37 | } 38 | } catch (IOException e) { 39 | failed = true; 40 | return; 41 | } 42 | this.contents = contents.toString(); 43 | } 44 | } 45 | 46 | private final Process process; 47 | private final Gobbler stdout; 48 | private final Gobbler stderr; 49 | private Integer returnCode = null; 50 | 51 | ProcessGobbler(String... args) throws IOException { 52 | process = new ProcessBuilder(args).start(); 53 | stdout = new Gobbler(process.getInputStream()); 54 | stderr = new Gobbler(process.getErrorStream()); 55 | } 56 | 57 | int getReturnCode() { 58 | try { 59 | returnCode = process.waitFor(); 60 | } catch (InterruptedException e) { 61 | // Too bad 62 | } 63 | return returnCode; 64 | } 65 | 66 | String getStderr() { 67 | try { 68 | stderr.join(); 69 | } catch (InterruptedException e) { 70 | return null; 71 | } 72 | return stderr.getContents(); 73 | } 74 | 75 | String getStdout() { 76 | try { 77 | stdout.join(); 78 | } catch (InterruptedException e) { 79 | return null; 80 | } 81 | return stdout.getContents(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/StructFlock.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import jnr.ffi.Pointer; 4 | import jnr.ffi.Runtime; 5 | import jnr.ffi.StructLayout; 6 | 7 | public class StructFlock { 8 | public static final int CMD_GETLK = 5; 9 | public static final int CMD_SETLK = 6; 10 | public static final int CMD_SETLKW = 7; // set lock write 11 | 12 | public static final int LOCK_SH = 1; // Shared lock. 13 | public static final int LOCK_EX = 2; // Exclusive lock. 14 | public static final int LOCK_UN = 8; // Unlock. 15 | 16 | // lock types 17 | public static final int F_RDLCK = 0; // Read lock. 18 | public static final int F_WRLCK = 1; // Write lock 19 | public static final int F_UNLCK = 2; // Remove lock 20 | 21 | public static final int SEEK_SET = 0; // Offset is calculated from the start of the file. 22 | public static final int SEEK_CUR = 1; // Offset is calculated from the current position in the file. 23 | public static final int SEEK_END = 2; // Offset is calculated from the end of the file. 24 | 25 | private static final class Layout extends StructLayout { 26 | public final int16_t l_type; // Type of lock: F_RDLCK, F_WRLCK, or F_UNLCK. 27 | public final int16_t l_whence; // Where `l_start' is relative to (like `lseek') -- SEEK_SET/CUR/END 28 | public final off_t l_start; // Offset where the lock begins. 29 | public final off_t l_len; // Size of the locked area; zero means until EOF. 30 | public final pid_t l_pid; // Process holding the lock 31 | public final int32_t l_sysid; 32 | 33 | private Layout(Runtime runtime) { 34 | super(runtime); 35 | 36 | switch (Platform.platform()) { 37 | case FREEBSD: { 38 | this.l_start = new off_t(); 39 | this.l_len = new off_t(); 40 | this.l_pid = new pid_t(); 41 | this.l_type = new int16_t(); 42 | this.l_whence = new int16_t(); 43 | this.l_sysid = new int32_t(); 44 | break; 45 | } 46 | default: { 47 | this.l_type = new int16_t(); 48 | this.l_whence = new int16_t(); 49 | this.l_start = new off_t(); 50 | this.l_len = new off_t(); 51 | this.l_pid = new pid_t(); 52 | this.l_sysid = null; 53 | } 54 | } 55 | } 56 | } 57 | 58 | private static final Layout layout = new Layout(Runtime.getSystemRuntime()); 59 | 60 | private final Pointer p; 61 | private final String path; 62 | 63 | StructFlock(Pointer p, String path) { 64 | this.p = p; 65 | this.path = path; 66 | } 67 | 68 | public final int type() { 69 | return (int) layout.l_type.get(p); 70 | } 71 | 72 | public final StructFlock type(int l_type) { 73 | layout.l_type.set(p, l_type); 74 | return this; 75 | } 76 | 77 | public final long len() { 78 | return layout.l_len.get(p); 79 | } 80 | 81 | public final StructFlock len(long l_len) { 82 | layout.l_len.set(p, l_len); 83 | return this; 84 | } 85 | 86 | public final long pid() { 87 | return layout.l_pid.get(p); 88 | } 89 | 90 | public final StructFlock pid(long l_pid) { 91 | layout.l_pid.set(p, l_pid); 92 | return this; 93 | } 94 | 95 | public final long start() { 96 | return layout.l_start.get(p); 97 | } 98 | 99 | public final StructFlock start(long l_start) { 100 | layout.l_start.set(p, l_start); 101 | return this; 102 | } 103 | 104 | public final long sysid() { 105 | return layout.l_sysid != null ? layout.l_sysid.get(p) : -1L; 106 | } 107 | 108 | public final StructFlock sysid(long l_sysid) { 109 | layout.l_sysid.set(p, l_sysid); 110 | return this; 111 | } 112 | 113 | public final long whence() { 114 | return layout.l_whence.get(p); 115 | } 116 | 117 | public final StructFlock whence(long l_whence) { 118 | layout.l_whence.set(p, l_whence); 119 | return this; 120 | } 121 | 122 | @Override 123 | public final java.lang.String toString() { 124 | if (path != null) 125 | return path + "\n" + JNRUtil.toString(layout, p); 126 | return JNRUtil.toString(layout, p); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/StructFuseBuf.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import jnr.ffi.Pointer; 4 | import jnr.ffi.StructLayout; 5 | import jnr.ffi.Runtime; 6 | 7 | /** 8 | * Single data buffer 9 | *

10 | * Generic data buffer for I/O, extended attributes, etc... Data may 11 | * be supplied as a memory pointer or as a file descriptor 12 | */ 13 | class StructFuseBuf { 14 | static final class Layout extends StructLayout { 15 | /** 16 | * Size of data in bytes 17 | */ 18 | public final size_t size = new size_t(); 19 | 20 | /** 21 | * Buffer flags 22 | */ 23 | public final Enum flags = new Enum<>(FuseBufFlags.class); 24 | 25 | /** 26 | * Memory pointer 27 | *

28 | * Used unless FUSE_BUF_IS_FD flag is set. 29 | */ 30 | public final Pointer mem = new Pointer(); 31 | 32 | /** 33 | * File descriptor 34 | *

35 | * Used if FUSE_BUF_IS_FD flag is set. 36 | */ 37 | public final Signed32 fd = new Signed32(); 38 | 39 | /** 40 | * File position 41 | *

42 | * Used if FUSE_BUF_FD_SEEK flag is set. 43 | */ 44 | public final off_t pos = new off_t(); 45 | 46 | private Layout(Runtime runtime) { 47 | super(runtime); 48 | } 49 | } 50 | static final Layout layout = new Layout(Runtime.getSystemRuntime()); 51 | 52 | private final Pointer p; 53 | 54 | public StructFuseBuf(Pointer p) { 55 | this.p = p; 56 | } 57 | 58 | public final long size() { 59 | return layout.size.get(p); 60 | } 61 | 62 | public final FuseBufFlags flags() { 63 | return layout.flags.get(p); 64 | } 65 | 66 | public final int fd() { 67 | return layout.fd.get(p); 68 | } 69 | 70 | public final long pos() { 71 | return layout.pos.get(p); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/StructFuseBufvec.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import jnr.ffi.*; 4 | import jnr.ffi.Runtime; 5 | 6 | /** 7 | * Data buffer vector 8 | *

9 | * An array of data buffers, each containing a memory pointer or a 10 | * file descriptor. 11 | *

12 | * Allocate dynamically to add more than one buffer. 13 | * 14 | * @author Sergey Tselovalnikov 15 | * @since 02.06.15 16 | */ 17 | public class StructFuseBufvec { 18 | private static final class Layout extends StructLayout { 19 | public final size_t count = new size_t(); // Number of buffers in the array 20 | public final size_t idx = new size_t(); // Index of current buffer within the array 21 | public final size_t off = new size_t(); // Current offset within the current buffer 22 | public final StructFuseBuf.Layout buf = inner(StructFuseBuf.layout); // Array of buffers 23 | 24 | private Layout(Runtime runtime) { 25 | super(runtime); 26 | } 27 | } 28 | 29 | static final Layout layout = new Layout(Runtime.getSystemRuntime()); 30 | 31 | private final Pointer p; 32 | 33 | public StructFuseBufvec(Pointer p) { 34 | this.p = p; 35 | } 36 | 37 | public final long cout() { 38 | return layout.count.get(p); 39 | } 40 | 41 | public final long idx() { 42 | return layout.idx.get(p); 43 | } 44 | 45 | public final long off() { 46 | return layout.off.get(p); 47 | } 48 | 49 | public final StructFuseBuf buf() { 50 | return new StructFuseBuf(p.getPointer(layout.buf.offset())); 51 | } 52 | 53 | /** 54 | * Similar to FUSE_BUFVEC_INIT macros 55 | */ 56 | public static void init(StructFuseBufvec buf, long size) { 57 | layout.count.set(buf.p, 1); 58 | layout.idx.set(buf.p, 0); 59 | layout.off.set(buf.p, 0); 60 | layout.buf.size.set(buf.p, size); 61 | layout.buf.flags.set(buf.p, 0); 62 | layout.buf.mem.set(buf.p, 0); 63 | layout.buf.fd.set(buf.p, -1); 64 | layout.buf.pos.set(buf.p, 0); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/StructFuseConnInfo.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import jnr.ffi.Pointer; 4 | import jnr.ffi.Runtime; 5 | import jnr.ffi.StructLayout; 6 | import jnr.ffi.TypeAlias; 7 | 8 | class StructFuseConnInfo { 9 | static final class Layout extends StructLayout { 10 | Layout(Runtime runtime) { 11 | super(runtime); 12 | } 13 | public final int32_t proto_major = new int32_t(); 14 | public final int32_t proto_minor = new int32_t(); 15 | public final int32_t async_read = new int32_t(); 16 | public final int32_t max_write = new int32_t(); 17 | public final int32_t max_readahead = new int32_t(); 18 | public final int32_t enable = new int32_t(); 19 | public final int32_t want = new int32_t(); 20 | private final Padding reserved = new Padding(getRuntime().findType(TypeAlias.int32_t), 25); 21 | } 22 | 23 | static final Layout layout = new Layout(Runtime.getSystemRuntime()); 24 | private final Pointer p; 25 | 26 | public StructFuseConnInfo(Pointer p) { 27 | this.p = p; 28 | } 29 | 30 | public final void setOptions(final boolean setVolumeName, final boolean caseInsensitive) { 31 | layout.want.set(p, (setVolumeName ? 0x2 : 0x0) | (caseInsensitive ? 0x1 : 0x0)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/StructFuseContext.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import jnr.ffi.Runtime; 4 | import jnr.ffi.Struct; 5 | 6 | abstract class StructFuseContext extends Struct { 7 | public final Pointer fuse = new Pointer(); 8 | public final uid_t uid = new uid_t(); 9 | public final gid_t gid = new gid_t(); 10 | public final pid_t pid = new pid_t(); 11 | public final Pointer private_data = new Pointer(); 12 | 13 | public StructFuseContext(Runtime runtime) { 14 | super(runtime); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/StructFuseFileInfo.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import jnr.ffi.Pointer; 4 | import jnr.ffi.Runtime; 5 | import jnr.ffi.StructLayout; 6 | 7 | public class StructFuseFileInfo { 8 | private static final class Layout extends StructLayout { 9 | 10 | private Layout(Runtime runtime) { 11 | super(runtime); 12 | } 13 | 14 | public final Signed32 flags = new Signed32(); 15 | public final SignedLong fh_old = new SignedLong(); 16 | public final Signed32 writepage = new Signed32(); 17 | public final Signed32 flags_bitfield = new Signed32(); 18 | public final Unsigned64 fh = new Unsigned64(); 19 | public final Unsigned64 lock_owner = new Unsigned64(); 20 | } 21 | private static final Layout layout = new Layout(Runtime.getSystemRuntime()); 22 | 23 | private final Pointer p; 24 | private final String path; 25 | 26 | StructFuseFileInfo(Pointer p, String path) { 27 | this.p = p; 28 | this.path = path; 29 | } 30 | 31 | @Override 32 | public final java.lang.String toString() { 33 | if (path != null) 34 | return path + "\n" + JNRUtil.toString(layout, p); 35 | return JNRUtil.toString(layout, p); 36 | } 37 | 38 | // FuseFileInfo(final FuseFileInfo fileinfo) { 39 | // this(null, fileinfo); 40 | // } 41 | public final boolean append() { 42 | return (layout.flags.intValue(p) & O_APPEND) != 0; 43 | } 44 | 45 | public final StructFuseFileInfo append(boolean append) { 46 | layout.flags.set(p, (layout.flags.get(p) & ~O_APPEND) | (append ? O_APPEND : 0)); 47 | return this; 48 | } 49 | 50 | public final boolean create() { 51 | return (layout.flags.get(p) & O_CREAT) != 0; 52 | } 53 | 54 | public final StructFuseFileInfo create(boolean create) { 55 | layout.flags.set(p, (layout.flags.get(p) & ~O_CREAT) | (create ? O_CREAT : 0)); 56 | return this; 57 | } 58 | 59 | public final boolean noblock() { 60 | return (layout.flags.get(p) & O_NONBLOCK) != 0; 61 | } 62 | 63 | public final StructFuseFileInfo noblock(boolean value) { 64 | layout.flags.set(p, (layout.flags.get(p) & ~O_NONBLOCK) | (value ? O_NONBLOCK : 0)); 65 | return this; 66 | } 67 | 68 | public final boolean direct_io() { 69 | return (layout.flags_bitfield.get(p) & BIT_DIRECT_IO) != 0; 70 | } 71 | 72 | public final StructFuseFileInfo direct_io(boolean direct_io) { 73 | if (direct_io) 74 | layout.flags_bitfield.set(p, layout.flags_bitfield.get(p) | BIT_DIRECT_IO); 75 | else 76 | layout.flags_bitfield.set(p, layout.flags_bitfield.get(p) & ~BIT_DIRECT_IO); 77 | return this; 78 | } 79 | 80 | public final long fh() { 81 | return layout.fh.get(p); 82 | } 83 | 84 | public final StructFuseFileInfo fh(long fh) { 85 | layout.fh.set(p, fh); 86 | return this; 87 | } 88 | 89 | public final long fh_old() { 90 | return layout.fh_old.get(p); 91 | } 92 | 93 | public final StructFuseFileInfo fh_old(long fh_old) { 94 | layout.fh_old.set(p, fh_old); 95 | return this; 96 | } 97 | 98 | public final int flags() { 99 | return layout.flags.intValue(p); 100 | } 101 | 102 | public final StructFuseFileInfo flags(int flags) { 103 | layout.flags.set(p, flags); 104 | return this; 105 | } 106 | 107 | public final boolean flockrelease() { 108 | return (layout.flags_bitfield.get(p) & BIT_FLOCKRELEASE) != 0; 109 | } 110 | 111 | public final StructFuseFileInfo flockrelease(boolean flockrelease) { 112 | if (flockrelease) 113 | layout.flags_bitfield.set(p, layout.flags_bitfield.get(p) | BIT_FLOCKRELEASE); 114 | else 115 | layout.flags_bitfield.set(p, layout.flags_bitfield.get(p) & ~BIT_FLOCKRELEASE); 116 | return this; 117 | } 118 | 119 | public final boolean flush() { 120 | return (layout.flags_bitfield.get(p) & BIT_FLUSH) != 0; 121 | } 122 | 123 | public final StructFuseFileInfo flush(boolean flush) { 124 | if (flush) 125 | layout.flags_bitfield.set(p, layout.flags_bitfield.get(p) | BIT_FLUSH); 126 | else 127 | layout.flags_bitfield.set(p, layout.flags_bitfield.get(p) & ~BIT_FLUSH); 128 | 129 | return this; 130 | } 131 | 132 | public final boolean keep_cache() { 133 | return (layout.flags_bitfield.get(p) & BIT_KEEP_CACHE) != 0; 134 | } 135 | 136 | public final StructFuseFileInfo keep_cache(boolean keep_cache) { 137 | if (keep_cache) 138 | layout.flags_bitfield.set(p, layout.flags_bitfield.get(p) | BIT_KEEP_CACHE); 139 | else 140 | layout.flags_bitfield.set(p, layout.flags_bitfield.get(p) & ~BIT_KEEP_CACHE); 141 | 142 | return this; 143 | } 144 | 145 | public final long lock_owner() { 146 | return layout.lock_owner.longValue(p); 147 | } 148 | 149 | public final StructFuseFileInfo lock_owner(long lock_owner) { 150 | layout.lock_owner.set(p, lock_owner); 151 | return this; 152 | } 153 | 154 | public final boolean nonseekable() { 155 | return (layout.flags_bitfield.get(p) & BIT_NONSEEKABLE) != 0; 156 | } 157 | 158 | public final StructFuseFileInfo nonseekable(boolean nonseekable) { 159 | if (nonseekable) 160 | layout.flags_bitfield.set(p, layout.flags_bitfield.get(p) | BIT_NONSEEKABLE); 161 | else 162 | layout.flags_bitfield.set(p, layout.flags_bitfield.get(p) & ~BIT_NONSEEKABLE); 163 | 164 | return this; 165 | } 166 | 167 | public final int openMode() { 168 | return layout.flags.intValue(p) & openMask; 169 | } 170 | 171 | public final StructFuseFileInfo openMode(int openMode) { 172 | layout.flags.set(p, (layout.flags.get(p) & ~openMask) | openMode); 173 | return this; 174 | } 175 | 176 | public final boolean truncate() { 177 | return (layout.flags.get(p) & O_TRUNC) != 0; 178 | } 179 | 180 | public final StructFuseFileInfo truncate(boolean truncate) { 181 | layout.flags.set(p, (layout.flags.get(p) & ~O_TRUNC) | (truncate ? O_TRUNC : 0)); 182 | return this; 183 | } 184 | 185 | public final boolean writepage() { 186 | return layout.writepage.get(p) != 0; 187 | } 188 | 189 | public final StructFuseFileInfo writepage(boolean writepage) { 190 | layout.writepage.set(p, writepage ? 1 : 0); 191 | return this; 192 | } 193 | 194 | public static final int openMask = 03; 195 | public static final int O_RDONLY = 00; 196 | public static final int O_WRONLY = 01; 197 | public static final int O_RDWR = 02; 198 | public static final int O_CREAT = 0100; 199 | public static final int O_EXCL = 0200; 200 | public static final int O_NOCTTY = 0400; 201 | public static final int O_TRUNC = 01000; 202 | public static final int O_APPEND = 02000; 203 | public static final int O_NONBLOCK = 04000; 204 | public static final int O_NDELAY = O_NONBLOCK; 205 | public static final int O_SYNC = 010000; 206 | public static final int O_ASYNC = 020000; 207 | public static final int O_DIRECT = 040000; 208 | public static final int O_DIRECTORY = 0200000; 209 | public static final int O_NOFOLLOW = 0400000; 210 | public static final int O_NOATIME = 01000000; 211 | public static final int O_CLOEXEC = 02000000; 212 | 213 | private static final int BIT_DIRECT_IO = 1 << 0; 214 | private static final int BIT_KEEP_CACHE = 1 << 1; 215 | private static final int BIT_FLUSH = 1 << 2; 216 | private static final int BIT_NONSEEKABLE = 1 << 3; 217 | private static final int BIT_FLOCKRELEASE = 1 << 4; 218 | } 219 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/StructFuseOperations.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import jnr.ffi.NativeType; 4 | import jnr.ffi.Struct; 5 | import co.paralleluniverse.fuse.StructFuseOperationsIfaces.*; 6 | import static jnr.ffi.provider.jffi.ClosureHelper.toNative; 7 | 8 | class StructFuseOperations extends Struct { 9 | private final Function<_getattr> getattr = function(_getattr.class); 10 | private final Function<_readlink> readlink = function(_readlink.class); 11 | private final Pointer getdir = new Pointer(); 12 | private final Function<_mknod> mknod = function(_mknod.class); 13 | private final Function<_mkdir> mkdir = function(_mkdir.class); 14 | private final Function<_unlink> unlink = function(_unlink.class); 15 | private final Function<_rmdir> rmdir = function(_rmdir.class); 16 | private final Function<_symlink> symlink = function(_symlink.class); 17 | private final Function<_rename> rename = function(_rename.class); 18 | private final Function<_link> link = function(_link.class); 19 | private final Function<_chmod> chmod = function(_chmod.class); 20 | private final Function<_chown> chown = function(_chown.class); 21 | private final Function<_truncate> truncate = function(_truncate.class); 22 | private final Pointer utime = new Pointer(); 23 | private final Function<_open> open = function(_open.class); 24 | private final Function<_read> read = function(_read.class); 25 | private final Function<_write> write = function(_write.class); 26 | private final Function<_statfs> statfs = function(_statfs.class); 27 | private final Function<_flush> flush = function(_flush.class); 28 | private final Function<_release> release = function(_release.class); 29 | private final Function<_fsync> fsync = function(_fsync.class); 30 | private final Pointer setxattr = new Pointer(); 31 | private final Pointer getxattr = new Pointer(); 32 | private final Function<_listxattr> listxattr = function(_listxattr.class); 33 | private final Function<_removexattr> removexattr = function(_removexattr.class); 34 | private final Function<_opendir> opendir = function(_opendir.class); 35 | private final Function<_readdir> readdir = function(_readdir.class); 36 | private final Function<_releasedir> releasedir = function(_releasedir.class); 37 | private final Function<_fsyncdir> fsyncdir = function(_fsyncdir.class); 38 | private final Function<_init> init = function(_init.class); 39 | private final Function<_destroy> destroy = function(_destroy.class); 40 | private final Function<_access> access = function(_access.class); 41 | private final Function<_create> create = function(_create.class); 42 | private final Function<_ftruncate> ftruncate = function(_ftruncate.class); 43 | private final Function<_fgetattr> fgetattr = function(_fgetattr.class); 44 | private final Function<_lock> lock = function(_lock.class); 45 | private final Function<_utimens> utimens = function(_utimens.class); 46 | private final Function<_bmap> bmap = function(_bmap.class); 47 | /** 48 | * Flag indicating that the filesystem can accept a NULL path 49 | * as the first argument for the following operations: 50 | * 51 | * read, write, flush, release, fsync, readdir, releasedir, 52 | * fsyncdir, ftruncate, fgetattr, lock, ioctl and poll 53 | * 54 | * If this flag is set these operations continue to work on 55 | * unlinked files even if "-ohard_remove" option was specified. 56 | */ 57 | private final Padding flag_nullpath_ok = new Padding(NativeType.UCHAR, 1); 58 | /** 59 | * Flag indicating that the path need not be calculated for 60 | * the following operations: 61 | * 62 | * read, write, flush, release, fsync, readdir, releasedir, 63 | * fsyncdir, ftruncate, fgetattr, lock, ioctl and poll 64 | * 65 | * Closely related to flag_nullpath_ok, but if this flag is 66 | * set then the path will not be calculaged even if the file 67 | * wasn't unlinked. However the path can still be non-NULL if 68 | * it needs to be calculated for some other reason. 69 | */ 70 | private final Padding flag_nopath = new Padding(NativeType.UCHAR, 1); 71 | /** 72 | * Flag indicating that the filesystem accepts special 73 | * UTIME_NOW and UTIME_OMIT values in its utimens operation. 74 | */ 75 | private final Padding flag_utime_omit_ok = new Padding(NativeType.UCHAR, 1); 76 | /** 77 | * Reserved flags, don't set 78 | */ 79 | private final Padding flag_reserved = new Padding(NativeType.UCHAR, 1); 80 | private final Function<_ioctl> ioctl = function(_ioctl.class); 81 | private final Function<_poll> poll = function(_poll.class); 82 | private final Pointer write_buf = new Pointer(); 83 | private final Pointer read_buf = new Pointer(); 84 | private final Function<_flock> flock = function(_flock.class); 85 | private final Function<_fallocate> fallocate = function(_fallocate.class); 86 | 87 | @SuppressWarnings("unused") 88 | public StructFuseOperations(jnr.ffi.Runtime runtime, FuseFilesystem fs) { 89 | super(runtime); 90 | final Filesystem filesystem = new Filesystem(fs); 91 | getattr.set(filesystem); 92 | readlink.set(filesystem); 93 | mknod.set(filesystem); 94 | mkdir.set(filesystem); 95 | unlink.set(filesystem); 96 | rmdir.set(filesystem); 97 | symlink.set(filesystem); 98 | rename.set(filesystem); 99 | link.set(filesystem); 100 | chmod.set(filesystem); 101 | chown.set(filesystem); 102 | truncate.set(filesystem); 103 | utime.set((jnr.ffi.Pointer) null); 104 | open.set(filesystem); 105 | read.set(filesystem); 106 | write.set(filesystem); 107 | statfs.set(filesystem); 108 | flush.set(filesystem); 109 | release.set(filesystem); 110 | fsync.set(filesystem); 111 | switch (Platform.platform()) { 112 | case MAC: 113 | case MAC_MACFUSE: 114 | setxattr.set(toNative(_setxattr_MAC.class, filesystem)); 115 | getxattr.set(toNative(_getxattr_MAC.class, filesystem)); 116 | break; 117 | default: 118 | setxattr.set(toNative(_setxattr_NOT_MAC.class, filesystem)); 119 | getxattr.set(toNative(_getxattr_NOT_MAC.class, filesystem)); 120 | } 121 | listxattr.set(filesystem); 122 | removexattr.set(filesystem); 123 | opendir.set(filesystem); 124 | readdir.set(filesystem); 125 | releasedir.set(filesystem); 126 | fsyncdir.set(filesystem); 127 | init.set(filesystem); 128 | destroy.set(filesystem); 129 | access.set(filesystem); 130 | create.set(filesystem); 131 | ftruncate.set(filesystem); 132 | fgetattr.set(filesystem); 133 | lock.set(filesystem); 134 | utimens.set(filesystem); 135 | bmap.set(filesystem); 136 | ioctl.set(filesystem); 137 | poll.set(filesystem); 138 | flock.set(filesystem); 139 | fallocate.set(filesystem); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/StructFuseOperationsIfaces.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import jnr.ffi.Pointer; 4 | import jnr.ffi.annotations.Delegate; 5 | import jnr.ffi.annotations.In; 6 | import jnr.ffi.annotations.Out; 7 | import jnr.ffi.types.dev_t; 8 | import jnr.ffi.types.gid_t; 9 | import jnr.ffi.types.mode_t; 10 | import jnr.ffi.types.off_t; 11 | import jnr.ffi.types.size_t; 12 | import jnr.ffi.types.u_int32_t; 13 | import jnr.ffi.types.uid_t; 14 | 15 | final class StructFuseOperationsIfaces { 16 | public static interface _init { @Delegate void _init(Pointer conn); } 17 | public static interface _destroy { @Delegate void _destroy(); } 18 | public static interface _readlink { @Delegate int _readlink(String path, Pointer buffer, @size_t long size); } 19 | public static interface _mknod { @Delegate int _mknod(String path, @mode_t long mode, @dev_t long dev); } 20 | public static interface _mkdir { @Delegate int _mkdir(String path, @mode_t long mode); } 21 | public static interface _unlink { @Delegate int _unlink(String path); } 22 | public static interface _rmdir { @Delegate int _rmdir(String path); } 23 | public static interface _symlink { @Delegate int _symlink(String path, String target); } 24 | public static interface _rename { @Delegate int _rename(String path, String newName); } 25 | public static interface _link { @Delegate int _link(String path, String target); } 26 | public static interface _chmod { @Delegate int _chmod(String path, @mode_t long mode); } 27 | public static interface _chown { @Delegate int _chown(String path, @uid_t long uid, @gid_t long gid); } 28 | public static interface _truncate { @Delegate int _truncate(String path, @off_t long offset); } 29 | public static interface _open { @Delegate int _open(String path, Pointer info); } 30 | public static interface _read { @Delegate int _read(String path, @Out Pointer buffer, @size_t long size, @off_t long offset, Pointer info); } 31 | public static interface _write { @Delegate int _write(String path, @In Pointer buffer, @size_t long size, @off_t long offset, Pointer info); } 32 | public static interface _flush { @Delegate int _flush(String path, Pointer info); } 33 | public static interface _fsync { @Delegate int _fsync(String path, int datasync, @In Pointer info); } 34 | public static interface _release { @Delegate int _release(String path, Pointer info); } 35 | public static interface _opendir { @Delegate int _opendir(String path, Pointer info); } 36 | public static interface _readdir { @Delegate int _readdir(String path, Pointer buf, Pointer fillFunction, @off_t long offset, @In Pointer info); } 37 | public static interface _releasedir { @Delegate int _releasedir(String path, Pointer info); } 38 | public static interface _fsyncdir { @Delegate int _fsyncdir(String path, int datasync, @In Pointer info); } 39 | public static interface _access { @Delegate int _access(String path, int access); } 40 | public static interface _create { @Delegate int _create(String path, @mode_t long mode, Pointer info); } 41 | public static interface _ftruncate { @Delegate int _ftruncate(String path, @off_t long offset, @In Pointer info); } 42 | public static interface _utimens { @Delegate int _utimens(String path, Pointer timebuffer); } 43 | public static interface _bmap { @Delegate int _bmap(String path, Pointer info); } 44 | public static interface _statfs { @Delegate int _statfs(String path, @Out Pointer statsvfs); } 45 | public static interface _lock { @Delegate int _lock(String path, Pointer info, int cmd, Pointer flock); } 46 | public static interface _getattr { @Delegate int _getattr(String path, Pointer stat); } 47 | public static interface _fgetattr { @Delegate int _fgetattr(String path, Pointer stat, Pointer info); } 48 | 49 | public static interface _listxattr { @Delegate int _listxattr(String path, Pointer buffer, @size_t long size); } 50 | public static interface _removexattr { @Delegate int _removexattr(String path, String xattr); } 51 | 52 | public static interface _getxattr_MAC { @Delegate int _getxattr(String path, String xattr, Pointer buffer, @size_t long size, @u_int32_t long position); } 53 | public static interface _setxattr_MAC { @Delegate int _setxattr(String path, String xattr, Pointer value, @size_t long size, int flags, int position); } 54 | 55 | public static interface _getxattr_NOT_MAC { @Delegate int _getxattr(String path, String xattr, Pointer buffer, @size_t long size); } 56 | public static interface _setxattr_NOT_MAC { @Delegate int _setxattr(String path, String xattr, Pointer value, @size_t long size, int flags); } 57 | 58 | public static interface _ioctl { @Delegate void _ioctl(String path, int cmd, Pointer arg, Pointer fi, @u_int32_t long flags, Pointer data); } 59 | public static interface _poll { @Delegate void _poll(String path, Pointer fi, Pointer ph, Pointer reventsp); } 60 | public static interface _write_buf { @Delegate void _write_buf(String path, Pointer buf, @off_t long off, Pointer fi); } 61 | public static interface _read_buf { @Delegate void _read_buf(String path, Pointer bufp, @size_t long size, @off_t long off, Pointer fi); } 62 | public static interface _flock { @Delegate void _flock(String path, Pointer fi, int op); } 63 | public static interface _fallocate { @Delegate void _fallocate(String path, int mode, @off_t long off, @off_t long length, Pointer fi); } 64 | 65 | private StructFuseOperationsIfaces() { 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/StructFusePollHandle.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import jnr.ffi.Pointer; 4 | import jnr.ffi.StructLayout; 5 | import jnr.ffi.Runtime; 6 | 7 | /** 8 | * @see "fuse_lowlevel.c" 9 | * 10 | *

11 |  * struct fuse_pollhandle {
12 |  *   uint64_t kh;
13 |  *   struct fuse_chan *ch;
14 |  *   struct fuse_ll *f;
15 |  * };
16 |  * 
17 | * 18 | * @author Sergey Tselovalnikov 19 | * @since 02.06.15 20 | */ 21 | public class StructFusePollHandle { 22 | private static final class Layout extends StructLayout { 23 | public final Unsigned64 kh = new Unsigned64(); 24 | // TODO struct fuse_chan *ch; 25 | public final Pointer ch = new Pointer(); 26 | // TODO struct fuse_ll *f; 27 | public final Pointer f = new Pointer(); 28 | 29 | protected Layout(Runtime runtime) { 30 | super(runtime); 31 | } 32 | } 33 | 34 | private static final Layout layout = new Layout(Runtime.getSystemRuntime()); 35 | private final Pointer p; 36 | 37 | public StructFusePollHandle(Pointer p) { 38 | this.p = p; 39 | } 40 | 41 | public long kh() { 42 | return layout.kh.get(p); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/StructStat.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import jnr.ffi.Pointer; 4 | import jnr.ffi.Runtime; 5 | import jnr.ffi.StructLayout; 6 | 7 | public class StructStat { 8 | private static final class Layout extends StructLayout { 9 | public final dev_t st_dev; // Device. 10 | public final ino_t st_ino; // File serial number. 11 | public final mode_t st_mode; // File mode. 12 | public final nlink_t st_nlink; // Link count. 13 | public final uid_t st_uid; // User ID of the file's owner. 14 | public final gid_t st_gid; // Group ID of the file's group. 15 | public final dev_t st_rdev; // 16 | public final StructTimespec.Layout st_atime; // Time of last access. 17 | public final StructTimespec.Layout st_mtime; // Time of last modification. 18 | public final StructTimespec.Layout st_ctime; // Time of last status change. 19 | public final StructTimespec.Layout st_birthtime; 20 | public final off_t st_size; // Size of file, in bytes. 21 | public final blkcnt_t st_blocks; // Number 512-byte blocks allocated. 22 | public final blksize_t st_blksize; // Optimal block size for I/O. 23 | public final int32_t st_gen; 24 | public final int32_t st_lspare; 25 | public final int64_t st_qspare; 26 | 27 | private Layout(Runtime runtime) { 28 | super(runtime); 29 | 30 | switch (Platform.platform()) { 31 | case LINUX_X86_64: { 32 | this.st_dev = new dev_t(); 33 | this.st_ino = new ino_t(); 34 | this.st_nlink = new nlink_t(); 35 | this.st_mode = new mode_t(); 36 | this.st_uid = new uid_t(); 37 | this.st_gid = new gid_t(); 38 | int32_t __pad0 = new int32_t(); 39 | this.st_rdev = new dev_t(); 40 | this.st_size = new off_t(); 41 | this.st_blksize = new blksize_t(); 42 | this.st_blocks = new blkcnt_t(); 43 | this.st_atime = inner(new StructTimespec.Layout(getRuntime())); 44 | this.st_mtime = inner(new StructTimespec.Layout(getRuntime())); 45 | this.st_ctime = inner(new StructTimespec.Layout(getRuntime())); 46 | this.st_birthtime = null; 47 | this.st_gen = null; 48 | this.st_lspare = null; 49 | this.st_qspare = null; 50 | break; 51 | } 52 | case LINUX_ARM: 53 | case LINUX_I686: { 54 | this.st_dev = new dev_t(); 55 | int16_t __pad1 = new int16_t(); 56 | u_int32_t __st_ino = new u_int32_t(); 57 | this.st_mode = new mode_t(); 58 | this.st_nlink = new nlink_t(); 59 | this.st_uid = new uid_t(); 60 | this.st_gid = new gid_t(); 61 | this.st_rdev = new dev_t(); 62 | int16_t __pad2 = new int16_t(); 63 | this.st_size = new off_t(); 64 | this.st_blksize = new blksize_t(); 65 | this.st_blocks = new blkcnt_t(); 66 | this.st_atime = inner(new StructTimespec.Layout(getRuntime())); 67 | this.st_mtime = inner(new StructTimespec.Layout(getRuntime())); 68 | this.st_ctime = inner(new StructTimespec.Layout(getRuntime())); 69 | this.st_ino = new ino_t(); 70 | this.st_birthtime = null; 71 | this.st_gen = null; 72 | this.st_lspare = null; 73 | this.st_qspare = null; 74 | break; 75 | } 76 | case LINUX_PPC: { 77 | this.st_dev = new dev_t(); 78 | this.st_ino = new ino_t(); 79 | this.st_mode = new mode_t(); 80 | this.st_nlink = new nlink_t(); 81 | this.st_uid = new uid_t(); 82 | this.st_gid = new gid_t(); 83 | this.st_rdev = new dev_t(); 84 | int16_t __pad0 = new int16_t(); 85 | this.st_size = new off_t(); 86 | this.st_blksize = new blksize_t(); 87 | this.st_blocks = new blkcnt_t(); 88 | this.st_atime = inner(new StructTimespec.Layout(getRuntime())); 89 | this.st_mtime = inner(new StructTimespec.Layout(getRuntime())); 90 | this.st_ctime = inner(new StructTimespec.Layout(getRuntime())); 91 | this.st_birthtime = null; 92 | this.st_gen = null; 93 | this.st_lspare = null; 94 | this.st_qspare = null; 95 | break; 96 | } 97 | case MAC: { 98 | this.st_dev = new dev_t(); 99 | this.st_mode = new mode_t(); 100 | this.st_nlink = new nlink_t(); 101 | this.st_ino = new ino_t(); 102 | this.st_uid = new uid_t(); 103 | this.st_gid = new gid_t(); 104 | this.st_rdev = new dev_t(); 105 | this.st_atime = inner(new StructTimespec.Layout(getRuntime())); 106 | this.st_mtime = inner(new StructTimespec.Layout(getRuntime())); 107 | this.st_ctime = inner(new StructTimespec.Layout(getRuntime())); 108 | this.st_birthtime = inner(new StructTimespec.Layout(getRuntime())); 109 | this.st_size = new off_t(); 110 | this.st_blocks = new blkcnt_t(); 111 | this.st_blksize = new blksize_t(); 112 | this.st_gen = new int32_t(); 113 | this.st_lspare = new int32_t(); 114 | this.st_qspare = new int64_t(); 115 | break; 116 | } 117 | case FREEBSD: 118 | case MAC_MACFUSE: { 119 | this.st_dev = new dev_t(); 120 | this.st_ino = new ino_t(); 121 | this.st_mode = new mode_t(); 122 | this.st_nlink = new nlink_t(); 123 | this.st_uid = new uid_t(); 124 | this.st_gid = new gid_t(); 125 | this.st_rdev = new dev_t(); 126 | this.st_atime = inner(new StructTimespec.Layout(getRuntime())); 127 | this.st_mtime = inner(new StructTimespec.Layout(getRuntime())); 128 | this.st_ctime = inner(new StructTimespec.Layout(getRuntime())); 129 | this.st_size = new off_t(); 130 | this.st_blocks = new blkcnt_t(); 131 | this.st_blksize = new blksize_t(); 132 | this.st_birthtime = null; 133 | this.st_gen = null; 134 | this.st_lspare = null; 135 | this.st_qspare = null; 136 | break; 137 | } 138 | default: 139 | throw new AssertionError(); 140 | } 141 | } 142 | 143 | } 144 | 145 | private static final Layout layout = new Layout(Runtime.getSystemRuntime()); 146 | 147 | private final Pointer p; 148 | final String path; 149 | 150 | public StructStat(Pointer p, String path) { 151 | this.p = p; 152 | this.path = path; 153 | } 154 | 155 | private void setTime(StructTimespec.Layout layout, long sec, long nsec) { 156 | StructTimespec.set(layout, p, sec, nsec); 157 | } 158 | 159 | public StructStat atime(long sec) { 160 | return atime(sec, 0); 161 | } 162 | 163 | public StructStat atime(long sec, long nsec) { 164 | setTime(layout.st_atime, sec, nsec); 165 | return this; 166 | } 167 | 168 | public StructStat blksize(long blksize) { 169 | layout.st_blksize.set(p, blksize); 170 | return this; 171 | } 172 | 173 | public StructStat blocks(long blocks) { 174 | layout.st_blocks.set(p, blocks); 175 | return this; 176 | } 177 | 178 | public StructStat ctime(long sec) { 179 | return ctime(sec, 0); 180 | } 181 | 182 | public StructStat ctime(long sec, long nsec) { 183 | setTime(layout.st_ctime, sec, nsec); 184 | return this; 185 | } 186 | 187 | public StructStat dev(long dev) { 188 | layout.st_dev.set(p, dev); 189 | return this; 190 | } 191 | 192 | public StructStat gen(long gen) { 193 | if (layout.st_gen != null) 194 | layout.st_gen.set(p, gen); 195 | return this; 196 | } 197 | 198 | public StructStat gid(long gid) { 199 | layout.st_gid.set(p, gid); 200 | return this; 201 | } 202 | 203 | public StructStat ino(long ino) { 204 | layout.st_ino.set(p, ino); 205 | return this; 206 | } 207 | 208 | public StructStat lspare(long lspare) { 209 | if (layout.st_lspare != null) 210 | layout.st_lspare.set(p, lspare); 211 | return this; 212 | } 213 | 214 | public long mode() { 215 | return layout.st_mode.get(p); 216 | } 217 | 218 | public StructStat mode(long bits) { 219 | layout.st_mode.set(p, bits); 220 | return this; 221 | } 222 | 223 | public StructStat mtime(long sec) { 224 | return mtime(sec, 0); 225 | } 226 | 227 | public StructStat mtime(long sec, long nsec) { 228 | setTime(layout.st_mtime, sec, nsec); 229 | return this; 230 | } 231 | 232 | public StructStat nlink(long nlink) { 233 | layout.st_nlink.set(p, nlink); 234 | return this; 235 | } 236 | 237 | public StructStat qspare(long qspare) { 238 | if (layout.st_qspare != null) 239 | layout.st_qspare.set(p, qspare); 240 | return this; 241 | } 242 | 243 | public StructStat rdev(long rdev) { 244 | layout.st_rdev.set(p, rdev); 245 | return this; 246 | } 247 | 248 | public StructStat setAllTimes(long sec, long nsec) { 249 | return setTimes(sec, nsec, sec, nsec, sec, nsec); 250 | } 251 | 252 | public StructStat setAllTimesMillis(long millis) { 253 | final long sec = millis / 1000L; 254 | final long nsec = (millis % 1000L) * 1000000L; 255 | return setAllTimes(sec, nsec); 256 | } 257 | 258 | public StructStat setAllTimesSec(long sec) { 259 | return setAllTimesSec(sec, sec, sec); 260 | } 261 | 262 | public StructStat setAllTimesSec(long atime, long mtime, long ctime) { 263 | return setAllTimesSec(atime, mtime, ctime, ctime); 264 | } 265 | 266 | public StructStat setAllTimesSec(long atime, long mtime, long ctime, long birthtime) { 267 | return setTimes(atime, 0, mtime, 0, ctime, 0); 268 | } 269 | 270 | public StructStat setTimes(long atime_sec, long atime_nsec, long mtime_sec, 271 | long mtime_nsec, long ctime_sec, long ctime_nsec) { 272 | return setTimes(atime_sec, atime_nsec, mtime_sec, mtime_nsec, ctime_sec, ctime_nsec, ctime_sec, ctime_nsec); 273 | } 274 | 275 | public StructStat setTimes(long atime_sec, long atime_nsec, long mtime_sec, long mtime_nsec, 276 | long ctime_sec, long ctime_nsec, long birthtime_sec, long birthtime_nsec) { 277 | setTime(layout.st_atime, atime_sec, atime_nsec); 278 | setTime(layout.st_mtime, mtime_sec, mtime_nsec); 279 | setTime(layout.st_ctime, ctime_sec, ctime_nsec); 280 | if (layout.st_birthtime != null) 281 | setTime(layout.st_birthtime, birthtime_sec, birthtime_nsec); 282 | return this; 283 | } 284 | 285 | public StructStat size(long size) { 286 | layout.st_size.set(p, size); 287 | return this; 288 | } 289 | 290 | public StructStat uid(long uid) { 291 | layout.st_uid.set(p, uid); 292 | return this; 293 | } 294 | 295 | @Override 296 | public java.lang.String toString() { 297 | if (path != null) 298 | return path + "\n" + JNRUtil.toString(layout, p); 299 | return JNRUtil.toString(layout, p); 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/StructStatvfs.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import jnr.ffi.Pointer; 4 | import jnr.ffi.Runtime; 5 | import jnr.ffi.StructLayout; 6 | import jnr.ffi.Platform.CPU; 7 | 8 | public class StructStatvfs { 9 | /* Definitions for the flag in `f_flag'.*/ 10 | public static final int ST_RDONLY = 1; // Mount read-only. 11 | public static final int ST_NOSUID = 2; // Ignore suid and sgid bits. 12 | public static final int ST_NODEV = 4; // Disallow access to device special files. 13 | public static final int ST_NOEXEC = 8; // Disallow program execution. 14 | public static final int ST_SYNCHRONOUS = 16; // Writes are synced at once. 15 | public static final int ST_MANDLOCK = 64; // Allow mandatory locks on an FS. 16 | public static final int ST_WRITE = 128; // Write on file/directory/symlink. 17 | public static final int ST_APPEND = 256; // Append-only file. 18 | public static final int ST_IMMUTABLE = 512; // Immutable file. 19 | public static final int ST_NOATIME = 1024; // Do not update access times. 20 | public static final int ST_NODIRATIME = 2048;// Do not update directory access times. 21 | public static final int ST_RELATIME = 4096; // Update atime relative to mtime/ctime. 22 | 23 | private static final class Layout extends StructLayout { 24 | public final SignedLong f_bsize; // file system block size 25 | public final SignedLong f_frsize; // fragment size 26 | public final blkcnt_t f_blocks; // size of fs in f_frsize units 27 | public final blkcnt_t f_bfree; // # free blocks 28 | public final blkcnt_t f_bavail; // # free blocks for non-root 29 | public final fsfilcnt_t f_files; // # inodes 30 | public final fsfilcnt_t f_ffree; // # free inodes 31 | public final fsfilcnt_t f_favail; // # free inodes for non-root 32 | public final Signed32 f_unused; 33 | public final UnsignedLong f_flag; // mount flags 34 | public final UnsignedLong f_namemax;// maximum filename length 35 | public final Signed32[] __f_spare; 36 | 37 | private Layout(Runtime runtime) { 38 | super(runtime); 39 | 40 | switch (Platform.platform()) { 41 | case FREEBSD: { 42 | this.f_bavail = new blkcnt_t(); 43 | this.f_bfree = new blkcnt_t(); 44 | this.f_blocks = new blkcnt_t(); 45 | this.f_ffree = new fsfilcnt_t(); 46 | this.f_favail = new fsfilcnt_t(); 47 | this.f_files = new fsfilcnt_t(); 48 | this.f_bsize = new SignedLong(); 49 | SignedLong __pad0 = new SignedLong(); 50 | this.f_frsize = new SignedLong(); 51 | break; 52 | } 53 | default: { 54 | this.f_bsize = new SignedLong(); 55 | this.f_frsize = new SignedLong(); 56 | this.f_blocks = new blkcnt_t(); 57 | this.f_bfree = new blkcnt_t(); 58 | this.f_bavail = new blkcnt_t(); 59 | this.f_files = new fsfilcnt_t(); 60 | this.f_ffree = new fsfilcnt_t(); 61 | this.f_favail = new fsfilcnt_t(); 62 | } 63 | 64 | } 65 | final boolean is32bit = (jnr.ffi.Platform.getNativePlatform().getCPU() == CPU.I386 || jnr.ffi.Platform.getNativePlatform().getCPU() == CPU.ARM); 66 | this.f_unused = is32bit ? new Signed32() : null; 67 | this.f_flag = new UnsignedLong(); 68 | this.f_namemax = new UnsignedLong(); 69 | this.__f_spare = array(new Signed32[6]); 70 | } 71 | } 72 | 73 | private static final Layout layout = new Layout(Runtime.getSystemRuntime()); 74 | private final Pointer p; 75 | private final String path; 76 | 77 | public StructStatvfs(Pointer p, String path) { 78 | this.p = p; 79 | this.path = path; 80 | } 81 | 82 | public final long bavail() { 83 | return layout.f_bavail.get(p); 84 | } 85 | 86 | public final StructStatvfs bavail(long f_bavail) { 87 | layout.f_bavail.set(p, f_bavail); 88 | return this; 89 | } 90 | 91 | public final long bfree() { 92 | return layout.f_bfree.get(p); 93 | } 94 | 95 | public final StructStatvfs bfree(long f_bfree) { 96 | layout.f_bfree.set(p, f_bfree); 97 | return this; 98 | } 99 | 100 | public final long blocks() { 101 | return layout.f_blocks.get(p); 102 | } 103 | 104 | public final StructStatvfs blocks(long f_blocks) { 105 | layout.f_blocks.set(p, f_blocks); 106 | return this; 107 | } 108 | 109 | public final long bsize() { 110 | return layout.f_bsize.get(p); 111 | } 112 | 113 | public final StructStatvfs bsize(long f_bsize) { 114 | layout.f_bsize.set(p, f_bsize); 115 | return this; 116 | } 117 | 118 | public final long favail() { 119 | return layout.f_favail.get(p); 120 | } 121 | 122 | public final StructStatvfs favail(long f_favail) { 123 | layout.f_favail.set(p, f_favail); 124 | return this; 125 | } 126 | 127 | public final long ffree() { 128 | return layout.f_ffree.get(p); 129 | } 130 | 131 | public final StructStatvfs ffree(long f_ffree) { 132 | layout.f_ffree.set(p, f_ffree); 133 | return this; 134 | } 135 | 136 | public final long files() { 137 | return layout.f_files.get(p); 138 | } 139 | 140 | public final StructStatvfs files(long f_files) { 141 | layout.f_files.set(p, f_files); 142 | return this; 143 | } 144 | 145 | public final long frsize() { 146 | return layout.f_frsize.get(p); 147 | } 148 | 149 | public final StructStatvfs frsize(long f_frsize) { 150 | layout.f_frsize.set(p, f_frsize); 151 | return this; 152 | } 153 | 154 | public final long flags() { 155 | return layout.f_flag.get(p); 156 | } 157 | 158 | public final StructStatvfs flags(long f_flags) { 159 | layout.f_flag.set(p, f_flags); 160 | return this; 161 | } 162 | 163 | public final StructStatvfs set(long blockSize, long fragmentSize, long freeBlocks, long availBlocks, long totalBlocks, 164 | long freeFiles, long availFiles, long totalFiles) { 165 | return setSizes(blockSize, fragmentSize).setBlockInfo(freeBlocks, availBlocks, totalBlocks).setFileInfo(freeFiles, 166 | availFiles, totalFiles); 167 | } 168 | 169 | public final StructStatvfs setBlockInfo(long freeBlocks, long availBlocks, long totalBlocks) { 170 | return bfree(freeBlocks).bavail(availBlocks).blocks(totalBlocks); 171 | } 172 | 173 | public final StructStatvfs setFileInfo(long freeFiles, long availFiles, long totalFiles) { 174 | return ffree(freeFiles).favail(availFiles).files(totalFiles); 175 | } 176 | 177 | public final StructStatvfs setSizes(long blockSize, long fragmentSize) { 178 | return bsize(blockSize).frsize(fragmentSize); 179 | } 180 | 181 | @Override 182 | public final java.lang.String toString() { 183 | if (path != null) 184 | return path + "\n" + JNRUtil.toString(layout, p); 185 | return JNRUtil.toString(layout, p); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/StructTimeBuffer.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import jnr.ffi.Pointer; 4 | import jnr.ffi.Runtime; 5 | import jnr.ffi.StructLayout; 6 | 7 | public final class StructTimeBuffer { 8 | static final class Layout extends StructLayout { 9 | Layout(Runtime runtime) { 10 | super(runtime); 11 | } 12 | public final StructTimespec.Layout actime = inner(new StructTimespec.Layout(getRuntime())); 13 | public final StructTimespec.Layout modtime = inner(new StructTimespec.Layout(getRuntime())); 14 | } 15 | static final Layout layout = new Layout(Runtime.getSystemRuntime()); 16 | 17 | private final Pointer p; 18 | 19 | StructTimeBuffer(Pointer p) { 20 | this.p = p; 21 | } 22 | 23 | public long ac_nsec() { 24 | return layout.actime.tv_nsec.get(p); 25 | } 26 | 27 | public long ac_sec() { 28 | return layout.actime.tv_sec.get(p); 29 | } 30 | 31 | public StructTimeBuffer ac_set(double time) { 32 | StructTimespec.set(layout.actime, p, time); 33 | return this; 34 | } 35 | 36 | public StructTimeBuffer ac_set(long sec, long nsec) { 37 | StructTimespec.set(layout.actime, p, sec, nsec); 38 | return this; 39 | } 40 | 41 | public StructTimeBuffer ac_setMillis(long millis) { 42 | StructTimespec.setMillis(layout.actime, p, millis); 43 | return this; 44 | } 45 | 46 | public StructTimeBuffer ac_setSeconds(long seconds) { 47 | StructTimespec.setSeconds(layout.actime, p, seconds); 48 | return this; 49 | } 50 | 51 | public long mod_nsec() { 52 | return layout.modtime.tv_nsec.get(p); 53 | } 54 | 55 | public long mod_sec() { 56 | return layout.modtime.tv_sec.get(p); 57 | } 58 | 59 | public final StructTimeBuffer mod_set(double time) { 60 | StructTimespec.set(layout.modtime, p, time); 61 | return this; 62 | } 63 | 64 | public StructTimeBuffer mod_set(long sec, final long nsec) { 65 | StructTimespec.set(layout.modtime, p, sec, nsec); 66 | return this; 67 | } 68 | 69 | public StructTimeBuffer mod_setMillis(long millis) { 70 | StructTimespec.setMillis(layout.modtime, p, millis); 71 | return this; 72 | } 73 | 74 | public StructTimeBuffer mod_setSeconds(long seconds) { 75 | StructTimespec.setSeconds(layout.modtime, p, seconds); 76 | return this; 77 | } 78 | 79 | public StructTimeBuffer both_set(double time) { 80 | ac_set(time); 81 | mod_set(time); 82 | return this; 83 | } 84 | 85 | public StructTimeBuffer both_set(long sec, long nsec) { 86 | ac_set(sec, nsec); 87 | mod_set(sec, nsec); 88 | return this; 89 | } 90 | 91 | public StructTimeBuffer both_setMillis(long millis) { 92 | ac_setMillis(millis); 93 | mod_setMillis(millis); 94 | return this; 95 | } 96 | 97 | public StructTimeBuffer both_setSeconds(long seconds) { 98 | ac_setSeconds(seconds); 99 | mod_setSeconds(seconds); 100 | return this; 101 | } 102 | 103 | @Override 104 | public java.lang.String toString() { 105 | return layout.toString(); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/StructTimespec.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import jnr.ffi.Pointer; 4 | import jnr.ffi.Runtime; 5 | import jnr.ffi.Struct; 6 | import jnr.ffi.StructLayout; 7 | 8 | final class StructTimespec { 9 | static final class Layout extends StructLayout { 10 | Layout(Runtime runtime) { 11 | super(runtime); 12 | } 13 | public final SignedLong tv_sec = new SignedLong(); 14 | public final SignedLong tv_nsec = new SignedLong(); 15 | } 16 | static final Layout layout = new Layout(Runtime.getSystemRuntime()); 17 | 18 | private final Pointer p; 19 | 20 | public StructTimespec(Pointer p) { 21 | this.p = p; 22 | } 23 | 24 | public long nsec() { 25 | return layout.tv_nsec.longValue(p); 26 | } 27 | 28 | public long sec() { 29 | return layout.tv_sec.longValue(p); 30 | } 31 | 32 | public void set(double time) { 33 | set(layout, p, time); 34 | } 35 | 36 | public void set(long sec, long nsec) { 37 | set(layout, p, sec, nsec); 38 | } 39 | 40 | public void setMillis(long millis) { 41 | set(millis / 1000L, (millis % 1000L) * 1000000L); 42 | } 43 | 44 | public void setSeconds(long seconds) { 45 | set(seconds); 46 | } 47 | 48 | static void set(StructTimespec.Layout layout, Pointer p, double time) { 49 | set(layout, p, (long) time, (long) (time * 1000000000d)); 50 | } 51 | 52 | static void set(StructTimespec.Layout layout, Pointer p, long sec, long nsec) { 53 | layout.tv_sec.set(p, sec); 54 | layout.tv_nsec.set(p, nsec); 55 | } 56 | 57 | static void setMillis(StructTimespec.Layout layout, Pointer p, long millis) { 58 | set(layout, p, millis / 1000L, (millis % 1000L) * 1000000L); 59 | } 60 | 61 | static void setSeconds(StructTimespec.Layout layout, Pointer p, long seconds) { 62 | set(layout, p, seconds); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/TypeMode.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | public class TypeMode { 4 | public static final int S_IFIFO = 0010000; // named pipe (fifo) 5 | public static final int S_IFCHR = 0020000; // character special 6 | public static final int S_IFDIR = 0040000; // directory 7 | public static final int S_IFBLK = 0060000; // block special 8 | public static final int S_IFREG = 0100000; // regular 9 | public static final int S_IFLNK = 0120000; // symbolic link 10 | public static final int S_IFSOCK = 0140000; // socket 11 | public static final int S_IFMT = 0170000; // file mask for type checks 12 | public static final int S_ISUID = 0004000; // set user id on execution 13 | public static final int S_ISGID = 0002000; // set group id on execution 14 | public static final int S_ISVTX = 0001000; // save swapped text even after use 15 | public static final int S_IRUSR = 0000400; // read permission, owner 16 | public static final int S_IWUSR = 0000200; // write permission, owner 17 | public static final int S_IXUSR = 0000100; // execute/search permission, owner 18 | public static final int S_IRGRP = 0000040; // read permission, group 19 | public static final int S_IWGRP = 0000020; // write permission, group 20 | public static final int S_IXGRP = 0000010; // execute/search permission, group 21 | public static final int S_IROTH = 0000004; // read permission, other 22 | public static final int S_IWOTH = 0000002; // write permission, other 23 | public static final int S_IXOTH = 0000001; // execute permission, other 24 | 25 | public static final int ALL_READ = S_IRUSR | S_IRGRP | S_IROTH; 26 | public static final int ALL_WRITE = S_IWUSR | S_IWGRP | S_IWOTH; 27 | public static final int S_IXUGO = S_IXUSR | S_IXGRP | S_IXOTH; 28 | 29 | public static boolean S_ISTYPE(int mode, int mask) { 30 | return (mode & S_IFMT) == mask; 31 | } 32 | 33 | public static boolean S_ISDIR(int mode) { 34 | return S_ISTYPE(mode, S_IFDIR); 35 | } 36 | 37 | public static boolean S_ISCHR(int mode) { 38 | return S_ISTYPE(mode, S_IFCHR); 39 | } 40 | 41 | public static boolean S_ISBLK(int mode) { 42 | return S_ISTYPE(mode, S_IFBLK); 43 | } 44 | 45 | public static boolean S_ISREG(int mode) { 46 | return S_ISTYPE(mode, S_IFREG); 47 | } 48 | 49 | public static boolean S_ISFIFO(int mode) { 50 | return S_ISTYPE(mode, S_IFIFO); 51 | } 52 | 53 | public static boolean S_ISLNK(int mode) { 54 | return S_ISTYPE(mode, S_IFLNK); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/XAttrConstants.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | /** 4 | * The following constants should be used for the fifth parameter of 5 | * `*setxattr'. 6 | * 7 | * @author Sergey Tselovalnikov 8 | * @since 05.06.15 9 | */ 10 | final class XAttrConstants { 11 | public static final int XATTR_CREATE = 1; /* set value, fail if attr already exists. */ 12 | public static final int XATTR_REPLACE = 2; /* set value, fail if attr does not exist. */ 13 | 14 | private XAttrConstants() { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/XattrFiller.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.Arrays; 5 | 6 | public final class XattrFiller { 7 | private final ByteBuffer buffer; 8 | private final long maxSize; 9 | private final int position; 10 | private byte[] value = null; 11 | private boolean isSet = false; 12 | 13 | XattrFiller(ByteBuffer buffer, long size, int position) { 14 | this.buffer = buffer; 15 | maxSize = size; 16 | this.position = position; 17 | } 18 | 19 | long getSize() { 20 | return value == null ? 0 : value.length; 21 | } 22 | 23 | public final void set(byte[] value) { 24 | if (buffer != null && value != null) { 25 | if (isSet) 26 | throw new IllegalStateException("Cannot set the xattr twice."); 27 | 28 | isSet = true; 29 | if (value.length > position + maxSize) 30 | value = Arrays.copyOf(value, position + (int) maxSize); 31 | 32 | buffer.put(value, position, value.length); 33 | } 34 | this.value = value; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/fuse/XattrListFiller.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.fuse; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.util.Arrays; 5 | import java.util.HashSet; 6 | import java.util.Set; 7 | 8 | public final class XattrListFiller { 9 | private final ByteBuffer buffer; 10 | private final long maxSize; 11 | private long currentSize = 0; 12 | private final Set addedXattrs = new HashSet(); 13 | 14 | XattrListFiller(ByteBuffer buffer, long size) { 15 | this.buffer = buffer; 16 | maxSize = size; 17 | } 18 | 19 | public final boolean add(Iterable xattrs) { 20 | byte[] bytes; 21 | int size; 22 | boolean hasNullByte; 23 | for (final String xattr : xattrs) { 24 | if (addedXattrs.contains(xattr)) 25 | continue; 26 | 27 | if (currentSize >= maxSize && buffer != null) 28 | return false; 29 | 30 | bytes = xattr.getBytes(); 31 | hasNullByte = bytes[bytes.length - 1] == 0; 32 | size = bytes.length + (hasNullByte ? 0 : 1); 33 | if (currentSize + size > maxSize && buffer != null) 34 | return false; 35 | 36 | addedXattrs.add(xattr); 37 | if (buffer != null) { 38 | buffer.put(bytes); 39 | if (!hasNullByte) 40 | buffer.put((byte) 0); 41 | } 42 | currentSize += size; 43 | } 44 | return true; 45 | } 46 | 47 | public final boolean add(String... xattrs) { 48 | return add(Arrays.asList(xattrs)); 49 | } 50 | 51 | public final long requiredSize() { 52 | return currentSize; 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | final StringBuilder output = new StringBuilder(); 58 | int count = 0; 59 | for (final String xattr : addedXattrs) { 60 | output.append(xattr); 61 | if (count < addedXattrs.size() - 1) { 62 | output.append(", "); 63 | } 64 | count++; 65 | } 66 | return output.toString(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/javafs/JavaFS.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.javafs; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.FileSystem; 5 | import java.nio.file.Path; 6 | import java.util.Map; 7 | 8 | import co.paralleluniverse.fuse.Fuse; 9 | 10 | /** 11 | * Mounts Java {@link FileSystem}s as a FUSE filesystems. 12 | * 13 | * @author pron 14 | */ 15 | public final class JavaFS { 16 | 17 | /** 18 | * Mounts a filesystem. 19 | * 20 | * @param fs the filesystem 21 | * @param mountPoint the path of the mount point 22 | * @param readonly if {@code true}, mounts the filesystem as read-only 23 | * @param log if {@code true}, all filesystem calls will be logged with juc logging. 24 | * @param mountOptions the platform specific mount options (e.g. {@code ro}, {@code rw}, etc.). {@code null} for value-less options. 25 | */ 26 | public static void mount(FileSystem fs, Path mountPoint, boolean readonly, boolean log, Map mountOptions) throws IOException { 27 | if (readonly) 28 | fs = new ReadOnlyFileSystem(fs); 29 | Fuse.mount(new FuseFileSystemProvider(fs, log).log(log), mountPoint, false, log, mountOptions); 30 | } 31 | 32 | /** 33 | * Mounts a filesystem. 34 | * 35 | * @param fs the filesystem 36 | * @param mountPoint the path of the mount point 37 | * @param readonly if {@code true}, mounts the filesystem as read-only 38 | * @param log if {@code true}, all filesystem calls will be logged with juc logging. 39 | */ 40 | public static void mount(FileSystem fs, Path mountPoint, boolean readonly, boolean log) throws IOException { 41 | mount(fs,mountPoint, readonly, log, null); 42 | } 43 | 44 | /** 45 | * Mounts a filesystem. 46 | * 47 | * @param fs the filesystem 48 | * @param mountPoint the path of the mount point 49 | * @param mountOptions the platform specific mount options (e.g. {@code ro}, {@code rw}, etc.). {@code null} for value-less options. 50 | */ 51 | public static void mount(FileSystem fs, Path mountPoint, Map mountOptions) throws IOException { 52 | mount(fs, mountPoint, false, false, mountOptions); 53 | } 54 | 55 | /** 56 | * Mounts a filesystem. 57 | * 58 | * @param fs the filesystem 59 | * @param mountPoint the path of the mount point 60 | */ 61 | public static void mount(FileSystem fs, Path mountPoint) throws IOException { 62 | mount(fs, mountPoint, false, false, null); 63 | } 64 | 65 | /** 66 | * Try to unmount an existing mountpoint. 67 | * 68 | * @param mountPoint The location where the filesystem is mounted. 69 | * @throws IOException thrown if an error occurs while starting the external process. 70 | */ 71 | public static void unmount(Path mountPoint) throws IOException { 72 | Fuse.unmount(mountPoint); 73 | } 74 | 75 | private JavaFS() { 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/javafs/ReadOnlyFileSystem.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.javafs; 2 | 3 | import co.paralleluniverse.filesystem.FileSystemAdapter; 4 | import java.io.IOException; 5 | import java.nio.file.FileStore; 6 | import java.nio.file.FileSystem; 7 | import java.nio.file.Path; 8 | import java.nio.file.PathMatcher; 9 | import java.nio.file.WatchService; 10 | import java.nio.file.attribute.UserPrincipalLookupService; 11 | import java.nio.file.spi.FileSystemProvider; 12 | import java.util.Set; 13 | 14 | /** 15 | * 16 | * @author pron 17 | */ 18 | class ReadOnlyFileSystem extends FileSystemAdapter { 19 | public ReadOnlyFileSystem(FileSystem fs) { 20 | super(fs, new ReadOnlyFileSystemProvider(fs.provider())); 21 | } 22 | 23 | ReadOnlyFileSystem(FileSystem fs, FileSystemProvider fsp) { 24 | super(fs, fsp); 25 | } 26 | 27 | @Override 28 | public boolean isReadOnly() { 29 | return true; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/co/paralleluniverse/javafs/ReadOnlyFileSystemProvider.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.javafs; 2 | 3 | import co.paralleluniverse.filesystem.FileSystemProviderAdapter; 4 | import java.io.IOException; 5 | import java.io.OutputStream; 6 | import java.nio.channels.AsynchronousFileChannel; 7 | import java.nio.channels.FileChannel; 8 | import java.nio.channels.SeekableByteChannel; 9 | import java.nio.file.AccessDeniedException; 10 | import java.nio.file.AccessMode; 11 | import java.nio.file.CopyOption; 12 | import java.nio.file.FileSystem; 13 | import java.nio.file.LinkOption; 14 | import java.nio.file.OpenOption; 15 | import java.nio.file.Path; 16 | import java.nio.file.ReadOnlyFileSystemException; 17 | import java.nio.file.StandardOpenOption; 18 | import java.nio.file.attribute.FileAttribute; 19 | import java.nio.file.spi.FileSystemProvider; 20 | import java.util.Set; 21 | import java.util.concurrent.ExecutorService; 22 | 23 | /** 24 | * 25 | * @author pron 26 | */ 27 | class ReadOnlyFileSystemProvider extends FileSystemProviderAdapter { 28 | public ReadOnlyFileSystemProvider(FileSystemProvider fsp) { 29 | super(fsp); 30 | } 31 | 32 | @Override 33 | protected FileSystem wrapFileSystem(FileSystem fs) { 34 | return new ReadOnlyFileSystem(fs, this); 35 | } 36 | 37 | @Override 38 | public OutputStream newOutputStream(Path path, OpenOption... options) throws IOException { 39 | throw new ReadOnlyFileSystemException(); 40 | } 41 | 42 | @Override 43 | public FileChannel newFileChannel(Path path, Set options, FileAttribute... attrs) throws IOException { 44 | checkOpenOptions(options); 45 | return super.newFileChannel(path, options, attrs); 46 | } 47 | 48 | @Override 49 | public AsynchronousFileChannel newAsynchronousFileChannel(Path path, Set options, ExecutorService executor, FileAttribute... attrs) throws IOException { 50 | checkOpenOptions(options); 51 | return super.newAsynchronousFileChannel(path, options, executor, attrs); 52 | } 53 | 54 | @Override 55 | public SeekableByteChannel newByteChannel(Path path, Set options, FileAttribute... attrs) throws IOException { 56 | checkOpenOptions(options); 57 | return super.newByteChannel(path, options, attrs); 58 | } 59 | 60 | @Override 61 | public void checkAccess(Path path, AccessMode... modes) throws IOException { 62 | for (AccessMode mode : modes) { 63 | if (mode == AccessMode.WRITE) 64 | throw new AccessDeniedException(path.toString()); 65 | } 66 | super.checkAccess(path, modes); 67 | } 68 | 69 | private void checkOpenOptions(Set options) { 70 | if (options.contains(StandardOpenOption.CREATE) 71 | || options.contains(StandardOpenOption.CREATE_NEW) 72 | || options.contains(StandardOpenOption.APPEND) 73 | || options.contains(StandardOpenOption.WRITE) 74 | || options.contains(StandardOpenOption.DELETE_ON_CLOSE)) 75 | throw new ReadOnlyFileSystemException(); 76 | } 77 | 78 | @Override 79 | public void createDirectory(Path dir, FileAttribute... attrs) throws IOException { 80 | throw new ReadOnlyFileSystemException(); 81 | } 82 | 83 | @Override 84 | public void createSymbolicLink(Path link, Path target, FileAttribute... attrs) throws IOException { 85 | throw new ReadOnlyFileSystemException(); 86 | } 87 | 88 | @Override 89 | public void createLink(Path link, Path existing) throws IOException { 90 | throw new ReadOnlyFileSystemException(); 91 | } 92 | 93 | @Override 94 | public void delete(Path path) throws IOException { 95 | throw new ReadOnlyFileSystemException(); 96 | } 97 | 98 | @Override 99 | public boolean deleteIfExists(Path path) throws IOException { 100 | throw new ReadOnlyFileSystemException(); 101 | } 102 | 103 | @Override 104 | public void setAttribute(Path path, String attribute, Object value, LinkOption... options) throws IOException { 105 | throw new ReadOnlyFileSystemException(); 106 | } 107 | 108 | @Override 109 | public void copy(Path source, Path target, CopyOption... options) throws IOException { 110 | throw new ReadOnlyFileSystemException(); 111 | } 112 | 113 | @Override 114 | public void move(Path source, Path target, CopyOption... options) throws IOException { 115 | throw new ReadOnlyFileSystemException(); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/jnr/ffi/provider/jffi/ClosureHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Sergey Tselovalnikov 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package jnr.ffi.provider.jffi; 25 | 26 | import java.lang.annotation.Annotation; 27 | import java.lang.reflect.AccessibleObject; 28 | import java.util.Collection; 29 | import jnr.ffi.Pointer; 30 | import jnr.ffi.Runtime; 31 | import jnr.ffi.mapper.CompositeTypeMapper; 32 | import jnr.ffi.mapper.DefaultSignatureType; 33 | import jnr.ffi.mapper.FromNativeContext; 34 | import jnr.ffi.mapper.FromNativeConverter; 35 | import jnr.ffi.provider.ClosureManager; 36 | import java.util.Collections; 37 | 38 | public class ClosureHelper { 39 | public static ClosureHelper getInstance() { 40 | return SingletonHolder.INSTANCE; 41 | } 42 | 43 | private static class SingletonHolder { 44 | private static final ClosureHelper INSTANCE = new ClosureHelper(); 45 | } 46 | 47 | private final SimpleNativeContext ctx; 48 | private final ClassValue> cache; 49 | 50 | private ClosureHelper() { 51 | try { 52 | final ClosureManager closureManager = Runtime.getSystemRuntime().getClosureManager(); 53 | 54 | final AsmClassLoader cl = (AsmClassLoader) accessible(NativeClosureManager.class.getDeclaredField("classLoader")).get(closureManager); 55 | final CompositeTypeMapper ctm = (CompositeTypeMapper) accessible(NativeClosureManager.class.getDeclaredField("typeMapper")).get(closureManager); 56 | this.ctx = new SimpleNativeContext(Runtime.getSystemRuntime(), (Collection) Collections.EMPTY_LIST); 57 | this.cache = new ClassValue>() { 58 | @Override 59 | protected FromNativeConverter computeValue(Class closureClass) { 60 | return ClosureFromNativeConverter. 61 | getInstance(Runtime.getSystemRuntime(), DefaultSignatureType.create(closureClass, (FromNativeContext) ctx), cl, ctm); 62 | } 63 | }; 64 | } catch (Exception e) { 65 | throw new RuntimeException("Unable to create helper", e); 66 | } 67 | } 68 | 69 | public T fromNative(Pointer nativeValue, Class closureClass) { 70 | return (T) cache.get(closureClass).fromNative(nativeValue, ctx); 71 | } 72 | 73 | public static Pointer toNative(Class closureClass, T instance) { 74 | return Runtime.getSystemRuntime().getClosureManager().getClosurePointer(closureClass, instance); 75 | } 76 | 77 | private static T accessible(T obj) { 78 | obj.setAccessible(true); 79 | return obj; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/test/java/co/paralleluniverse/javafs/JFSTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package co.paralleluniverse.javafs; 7 | 8 | import co.paralleluniverse.javafs.JavaFS; 9 | import com.google.common.jimfs.Jimfs; 10 | import static com.google.common.truth.Truth.assert_; 11 | import java.io.DataInputStream; 12 | import java.io.DataOutputStream; 13 | import java.io.File; 14 | import java.io.FileInputStream; 15 | import java.io.FileOutputStream; 16 | import java.nio.file.FileSystem; 17 | import java.nio.file.Files; 18 | import java.nio.file.Path; 19 | import org.junit.Test; 20 | import static org.junit.Assert.*; 21 | 22 | /** 23 | * 24 | * @author pron 25 | */ 26 | public class JFSTest { 27 | 28 | public JFSTest() { 29 | } 30 | 31 | @Test 32 | public void test() throws Exception { 33 | FileSystem fs = Jimfs.newFileSystem(); 34 | try (DataOutputStream os = new DataOutputStream(Files.newOutputStream(fs.getPath("/jimfs.txt")))) { 35 | os.writeUTF("JIMFS"); 36 | } 37 | 38 | final Path mnt = Files.createTempDirectory("jfsmnt"); 39 | try { 40 | JavaFS.mount(fs, mnt, false, false); 41 | 42 | // From this point on we use the old file IO 43 | File root = mnt.toFile(); 44 | 45 | // verify that we are, in fact, in Jimfs 46 | try (DataInputStream is = new DataInputStream(new FileInputStream(new File(root, "jimfs.txt")))) { 47 | assertEquals("JIMFS", is.readUTF()); 48 | } 49 | 50 | try (DataOutputStream os = new DataOutputStream(new FileOutputStream(new File(root, "a.txt")))) { 51 | os.writeUTF("hello!"); 52 | } 53 | try (DataOutputStream os = new DataOutputStream(new FileOutputStream(new File(root, "b.txt")))) { 54 | os.writeUTF("wha?"); 55 | } 56 | try (DataOutputStream os = new DataOutputStream(new FileOutputStream(new File(root, "c.txt")))) { 57 | os.writeUTF("goodbye!"); 58 | } 59 | 60 | assert_().that(root.list()).asList().has().allOf("a.txt", "b.txt", "c.txt", "jimfs.txt"); 61 | 62 | try (DataInputStream is = new DataInputStream(new FileInputStream(new File(root, "a.txt")))) { 63 | assertEquals("hello!", is.readUTF()); 64 | } 65 | try (DataInputStream is = new DataInputStream(new FileInputStream(new File(root, "b.txt")))) { 66 | assertEquals("wha?", is.readUTF()); 67 | } 68 | try (DataInputStream is = new DataInputStream(new FileInputStream(new File(root, "c.txt")))) { 69 | assertEquals("goodbye!", is.readUTF()); 70 | } 71 | } finally { 72 | JavaFS.unmount(mnt); 73 | Files.delete(mnt); 74 | } 75 | 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/test/java/co/paralleluniverse/javafs/Main.java: -------------------------------------------------------------------------------- 1 | package co.paralleluniverse.javafs; 2 | 3 | import com.google.common.jimfs.Jimfs; 4 | 5 | import java.nio.file.FileSystem; 6 | import java.nio.file.Paths; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | public class Main { 11 | public static void main(final String... args) throws Exception { 12 | try { 13 | if (args.length < 1 || args.length > 3) 14 | throw new IllegalArgumentException(); 15 | 16 | int i = 0; 17 | boolean readonly = false; 18 | if ("-r".equals(args[i])) { 19 | readonly = true; 20 | i++; 21 | } 22 | final String mountPoint = args[i++]; 23 | final FileSystem fs = i >= args.length ? Jimfs.newFileSystem() : ZipFS.newZipFileSystem(Paths.get(args[i++])); 24 | 25 | System.out.println("========================"); 26 | System.out.println("Mounting filesystem " + fs + " at " + mountPoint + (readonly ? " READONLY" : "")); 27 | System.out.println("========================"); 28 | 29 | Map options = new HashMap<>(); 30 | options.put("fsname", fs.getClass().getSimpleName() + "@" + System.currentTimeMillis()); 31 | 32 | JavaFS.mount(fs, Paths.get(mountPoint), readonly, true, options); 33 | Thread.sleep(Long.MAX_VALUE); 34 | } catch (IllegalArgumentException e) { 35 | System.err.println("Usage: JavaFS [-r] []"); 36 | System.exit(1); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/co/paralleluniverse/javafs/ZipFS.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Capsule 3 | * Copyright (c) 2014-2015, Parallel Universe Software Co. All rights reserved. 4 | * 5 | * This program and the accompanying materials are licensed under the terms 6 | * of the Eclipse Public License v1.0, available at 7 | * http://www.eclipse.org/legal/epl-v10.html 8 | */ 9 | package co.paralleluniverse.javafs; 10 | 11 | import com.sun.nio.zipfs.ZipFileSystem; 12 | import com.sun.nio.zipfs.ZipFileSystemProvider; 13 | import java.io.IOException; 14 | import java.lang.reflect.Constructor; 15 | import java.lang.reflect.InvocationTargetException; 16 | import java.nio.file.FileSystem; 17 | import java.nio.file.Path; 18 | import java.nio.file.spi.FileSystemProvider; 19 | import java.util.Collections; 20 | import java.util.Map; 21 | 22 | /** 23 | * Bypasses the check in ZipFileSystemProvider.newFileSystem that verifies that the given path is in the default FileSystem. 24 | * This is a JDK bug, fixed in JDK 8. See https://bugs.openjdk.java.net/browse/JDK-8004789 25 | */ 26 | public final class ZipFS { 27 | private static final ZipFileSystemProvider ZIP_FILE_SYSTEM_PROVIDER = getZipFileSystemProvider(); 28 | private static final Constructor ZIP_FILE_SYSTEM_CONSTRUCTOR; 29 | 30 | static { 31 | try { 32 | Constructor c = ZipFileSystem.class.getDeclaredConstructor(ZipFileSystemProvider.class, Path.class, Map.class); 33 | c.setAccessible(true); 34 | ZIP_FILE_SYSTEM_CONSTRUCTOR = c; 35 | } catch (NoSuchMethodException e) { 36 | throw new AssertionError(e); 37 | } 38 | } 39 | 40 | private static ZipFileSystemProvider getZipFileSystemProvider() { 41 | for (FileSystemProvider fsr : FileSystemProvider.installedProviders()) { 42 | if (fsr instanceof ZipFileSystemProvider) 43 | return (ZipFileSystemProvider) fsr; 44 | } 45 | throw new AssertionError("Zip file system not installed!"); 46 | } 47 | 48 | public static FileSystem newZipFileSystem(Path path) throws IOException { 49 | // return FileSystems.newFileSystem(path, null); 50 | if (path.getFileSystem() instanceof ZipFileSystem) 51 | throw new IllegalArgumentException("Can't create a ZIP file system nested in a ZIP file system. (" + path + " is nested in " + path.getFileSystem() + ")"); 52 | try { 53 | return (ZipFileSystem) ZIP_FILE_SYSTEM_CONSTRUCTOR.newInstance(ZIP_FILE_SYSTEM_PROVIDER, path, Collections.emptyMap()); 54 | } catch (ReflectiveOperationException e) { 55 | throw new AssertionError(e); 56 | } catch (Exception e) { 57 | throw rethrow(e); 58 | } 59 | } 60 | 61 | public static RuntimeException rethrow(Throwable t) { 62 | while (t instanceof InvocationTargetException) 63 | t = ((InvocationTargetException) t).getTargetException(); 64 | if (t instanceof RuntimeException) 65 | throw (RuntimeException) t; 66 | if (t instanceof Error) 67 | throw (Error) t; 68 | throw new RuntimeException(t); 69 | } 70 | 71 | private ZipFS() { 72 | } 73 | } 74 | --------------------------------------------------------------------------------