diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 18:24:20 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 18:24:20 +0000 |
commit | 483eb2f56657e8e7f419ab1a4fab8dce9ade8609 (patch) | |
tree | e5d88d25d870d5dedacb6bbdbe2a966086a0a5cf /src/java | |
parent | Initial commit. (diff) | |
download | ceph-483eb2f56657e8e7f419ab1a4fab8dce9ade8609.tar.xz ceph-483eb2f56657e8e7f419ab1a4fab8dce9ade8609.zip |
Adding upstream version 14.2.21.upstream/14.2.21upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/java')
25 files changed, 6392 insertions, 0 deletions
diff --git a/src/java/.gitignore b/src/java/.gitignore new file mode 100644 index 00000000..6aa4d620 --- /dev/null +++ b/src/java/.gitignore @@ -0,0 +1,6 @@ +*.class +libcephfs.jar +libcephfs-test.jar +native/com_ceph_fs_CephMount.h +TEST-*.txt +doc diff --git a/src/java/CMakeLists.txt b/src/java/CMakeLists.txt new file mode 100644 index 00000000..62b5259c --- /dev/null +++ b/src/java/CMakeLists.txt @@ -0,0 +1,61 @@ +find_package(Java COMPONENTS Development REQUIRED) +find_package(JNI REQUIRED) +include(UseJava) + +set(java_srcs + java/com/ceph/crush/Bucket.java + java/com/ceph/fs/CephAlreadyMountedException.java + java/com/ceph/fs/CephFileAlreadyExistsException.java + java/com/ceph/fs/CephFileExtent.java + java/com/ceph/fs/CephMount.java + java/com/ceph/fs/CephNativeLoader.java + java/com/ceph/fs/CephNotDirectoryException.java + java/com/ceph/fs/CephNotMountedException.java + java/com/ceph/fs/CephPoolException.java + java/com/ceph/fs/CephStat.java + java/com/ceph/fs/CephStatVFS.java) + +# note: for the -source 1.7 builds, we add +# -Xlint:-options +# to get rid of the warning +# warning: [options] bootstrap class path not set in conjunction with -source 1.7 +# as per +# https://blogs.oracle.com/darcy/entry/bootclasspath_older_source +set(CMAKE_JAVA_COMPILE_FLAGS "-source" "1.8" "-target" "1.8" "-Xlint:-options") +set(jni_header_dir "${CMAKE_CURRENT_BINARY_DIR}/native") +if(CMAKE_VERSION VERSION_LESS 3.11) + set(CMAKE_JAVA_COMPILE_FLAGS ${CMAKE_JAVA_COMPILE_FLAGS} "-h" ${jni_header_dir}) + add_jar(libcephfs ${java_srcs}) + add_custom_target( + jni-header + DEPENDS libcephfs) + add_dependencies(jni-header libcephfs) +else() + add_jar(libcephfs ${java_srcs} + GENERATE_NATIVE_HEADERS jni-header + DESTINATION ${jni_header_dir}) +endif() +get_property(libcephfs_jar TARGET libcephfs PROPERTY JAR_FILE) +install_jar(libcephfs share/java) + +find_jar(JUNIT_JAR + NAMES junit4 junit + PATHS "/usr/share/java") +if(JUNIT_JAR) + set(CMAKE_JAVA_INCLUDE_PATH ${JUNIT_JAR} ${libcephfs_jar}) + set(java_test_srcs + test/com/ceph/fs/CephAllTests.java + test/com/ceph/fs/CephDoubleMountTest.java + test/com/ceph/fs/CephMountCreateTest.java + test/com/ceph/fs/CephMountTest.java + test/com/ceph/fs/CephUnmountedTest.java) + add_jar(libcephfs-test ${java_test_srcs}) + add_dependencies(libcephfs-test libcephfs) + install_jar(libcephfs-test share/java) +endif(JUNIT_JAR) + +add_subdirectory(native) + +add_custom_target(java DEPENDS + libcephfs.jar + libcephfs_jni) diff --git a/src/java/README b/src/java/README new file mode 100644 index 00000000..5077bb30 --- /dev/null +++ b/src/java/README @@ -0,0 +1,54 @@ +libcephfs Java wrappers +======================= + +- native/: C++ +- java/: Java +- test/: JUnit tests +- lib/: JUnit library +- build.xml: Test runner + +Building +-------- + +Autotools handles the build using the configure flag --enable-cephfs-java + +Testing +------- + +These tests assume a live cluster, and depend on JUnit4 and Ant. + +To run the tests make sure that the JUnit4 JAR is installed. +Install it via a package manager or like this: + + $ mkdir lib + $ cd lib + $ wget https://github.com/downloads/KentBeck/junit/junit-4.8.2.jar + +And then add the jar to the CLASSPATH. +*NOTE* for now, configure is only looking for this jar in the +/usr/share/java directory as junit4.jar. So create a softlink +to that location from wherever the junit jar is installed. + +Ant is used to run the unit test (apt-get install ant). For example: + + $ cd src/ + $ ./vstart -d -n --localhost + $ cd java + $ CEPHFS_CONF=../ceph.conf CLASSPATH=/usr/share/java/junit4.jar ant test + +1. The tests depend on the compiled wrappers. If the wrappers are installed as +part of a package (e.g. Debian package) then this should 'just work'. Ant will +also look in the current directory for 'libcephfs.jar' and 'libcephfs-test.jar'; +and in ../build/lib for the +JNI library. If all else fails, set the environment variables CEPHFS_JAR, and +CEPHFS_JNI_LIB accordingly. + +2. Set CEPHFS_CONF environment variable to point to a ceph.conf. This can be +omitted if the desired configuration file can be found in a default location. + +Documentation +------------- + +Ant is used to build the Javadocs: + + $ ant docs diff --git a/src/java/build.xml b/src/java/build.xml new file mode 100644 index 00000000..49509adb --- /dev/null +++ b/src/java/build.xml @@ -0,0 +1,75 @@ +<?xml version="1.0"?> +<project name="cephfs-java" default="main" basedir="."> + + <description>CephFS Java Bindings</description> + + <property name="src.dir" location="java" /> + <property name="doc.dir" location="doc" /> + <property name="test.dir" location="test" /> + + <property environment="env"/> + + <target name="clean"> + <delete dir="${doc.dir}" /> + <delete> + <fileset dir="${test.dir}" includes="**/*.class" /> + </delete> + <delete file="./libcephfs-test.jar" /> + </target> + + <target name="makedir"> + <mkdir dir="${doc.dir}" /> + </target> + + <target name="docs" depends="makedir"> + <javadoc packagenames="src" sourcepath="${src.dir}" destdir="${doc.dir}"> + <fileset dir="${src.dir}"> + <include name="**/*.java" /> + </fileset> + </javadoc> + </target> + + <target name="compile-tests-jar"> + <javac srcdir="${test.dir}" destdir="${test.dir}" + includeantruntime="false" source="1.5" target="1.5"> + <classpath> + <pathelement location="${env.CEPHFS_JAR}"/> + <pathelement location="libcephfs.jar"/> + <pathelement location="${env.CLASSPATH}" /> + </classpath> + </javac> + <jar destfile="./libcephfs-test.jar"> + <fileset dir="${test.dir}" includes="**/*.class"/> + </jar> + </target> + + <target name="test" depends="compile-tests-jar"> + <junit printsummary="yes" haltonfailure="yes" showoutput="yes" fork="true"> + <sysproperty key="java.library.path" path="${env.CEPHFS_JNI_LIB}:../../build/lib/"/> + <sysproperty key="CEPH_CONF_FILE" path="${env.CEPHFS_CONF}"/> + <jvmarg value="-Xcheck:jni"/> + <classpath> + <pathelement location="${env.CEPHFS_JAR}"/> + <pathelement location="libcephfs.jar"/> + <pathelement location="libcephfs-test.jar"/> + </classpath> + <formatter type="plain"/> + <test name="com.ceph.fs.CephAllTests" haltonerror="true" /> + </junit> + </target> + + <target name="test-compat" depends="compile-tests-jar"> + <junit printsummary="yes" haltonfailure="yes" showoutput="yes" fork="true"> + <sysproperty key="java.library.path" path="${env.CEPHFS_JNI_LIB}:../../build/lib/"/> + <sysproperty key="CEPH_CONF_FILE" path="${env.CEPHFS_CONF}"/> + <classpath> + <pathelement location="${env.CEPHFS_JAR}"/> + <pathelement location="libcephfs.jar"/> + <pathelement location="libcephfs-test.jar"/> + </classpath> + <formatter type="plain"/> + <test name="com.ceph.fs.CephAllTests" haltonerror="true" /> + </junit> + </target> + +</project> diff --git a/src/java/java/com/ceph/crush/Bucket.java b/src/java/java/com/ceph/crush/Bucket.java new file mode 100644 index 00000000..989f6198 --- /dev/null +++ b/src/java/java/com/ceph/crush/Bucket.java @@ -0,0 +1,42 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +package com.ceph.crush; + +public class Bucket { + private String type; + private String name; + + public Bucket(String type, String name) { + this.type = type; + this.name = name; + } + + public String getType() { + return type; + } + + public String getName() { + return name; + } + + public String toString() { + return "bucket[" + type + "," + name + "]"; + } +} diff --git a/src/java/java/com/ceph/fs/CephAlreadyMountedException.java b/src/java/java/com/ceph/fs/CephAlreadyMountedException.java new file mode 100644 index 00000000..23b93e9a --- /dev/null +++ b/src/java/java/com/ceph/fs/CephAlreadyMountedException.java @@ -0,0 +1,44 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +package com.ceph.fs; + +import java.io.IOException; + +/** + * Ceph is already mounted. + */ +public class CephAlreadyMountedException extends IOException { + + private static final long serialVersionUID = 1L; + + /** + * Construct CephAlreadyMountedException. + */ + public CephAlreadyMountedException() { + super(); + } + + /** + * Construct CephAlreadyMountedException with message. + */ + public CephAlreadyMountedException(String s) { + super(s); + } +} diff --git a/src/java/java/com/ceph/fs/CephFileAlreadyExistsException.java b/src/java/java/com/ceph/fs/CephFileAlreadyExistsException.java new file mode 100644 index 00000000..12cdcb2b --- /dev/null +++ b/src/java/java/com/ceph/fs/CephFileAlreadyExistsException.java @@ -0,0 +1,44 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +package com.ceph.fs; + +import java.io.IOException; + +/** + * Ceph file/directory already exists. + */ +public class CephFileAlreadyExistsException extends IOException { + + private static final long serialVersionUID = 1L; + + /** + * Construct CephFileAlreadyExistsException. + */ + public CephFileAlreadyExistsException() { + super(); + } + + /** + * Construct CephFileAlreadyExistsException with message. + */ + public CephFileAlreadyExistsException(String s) { + super(s); + } +} diff --git a/src/java/java/com/ceph/fs/CephFileExtent.java b/src/java/java/com/ceph/fs/CephFileExtent.java new file mode 100644 index 00000000..909ff138 --- /dev/null +++ b/src/java/java/com/ceph/fs/CephFileExtent.java @@ -0,0 +1,66 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +package com.ceph.fs; + +import java.util.Arrays; + +/** + * Holds information about a file extent in CephFS. + */ +public class CephFileExtent { + private long offset; + private long length; + private int[] osds; + + CephFileExtent(long offset, long length, int[] osds) { + this.offset = offset; + this.length = length; + this.osds = osds; + } + + /** + * Get starting offset of extent. + */ + public long getOffset() { + return offset; + } + + /** + * Get length of extent. + */ + public long getLength() { + return length; + } + + /** + * Get list of OSDs with this extent. + */ + public int[] getOSDs() { + return osds; + } + + /** + * Pretty print. + */ + public String toString() { + return "extent[" + offset + "," + length + "," + + Arrays.toString(osds) + "]"; + } +} diff --git a/src/java/java/com/ceph/fs/CephMount.java b/src/java/java/com/ceph/fs/CephMount.java new file mode 100644 index 00000000..786a6ece --- /dev/null +++ b/src/java/java/com/ceph/fs/CephMount.java @@ -0,0 +1,1103 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +package com.ceph.fs; + +import java.io.IOException; +import java.io.FileNotFoundException; +import java.net.InetAddress; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.lang.String; + +import com.ceph.crush.Bucket; + +public class CephMount { + + /* + * Set via JNI callback in native_ceph_create + * + * Do not touch! + */ + private long instance_ptr; + + /* + * Flags for open(). + * + * Must be synchronized with JNI if changed. + */ + public static final int O_RDONLY = 1; + public static final int O_RDWR = 2; + public static final int O_APPEND = 4; + public static final int O_CREAT = 8; + public static final int O_TRUNC = 16; + public static final int O_EXCL = 32; + public static final int O_WRONLY = 64; + public static final int O_DIRECTORY = 128; + + /* + * Whence flags for seek(). + * + * Must be synchronized with JNI if changed. + */ + public static final int SEEK_SET = 1; + public static final int SEEK_CUR = 2; + public static final int SEEK_END = 3; + + /* + * Attribute flags for setattr(). + * + * Must be synchronized with JNI if changed. + */ + public static final int SETATTR_MODE = 1; + public static final int SETATTR_UID = 2; + public static final int SETATTR_GID = 4; + public static final int SETATTR_MTIME = 8; + public static final int SETATTR_ATIME = 16; + + /* + * Flags for setxattr(); + * + * Must be synchronized with JNI if changed. + */ + public static final int XATTR_CREATE = 1; + public static final int XATTR_REPLACE = 2; + public static final int XATTR_NONE = 3; + + /* + * Flags for flock(); + * + * Must be synchronized with JNI if changed. + */ + public static final int LOCK_SH = 1; + public static final int LOCK_EX = 2; + public static final int LOCK_NB = 4; + public static final int LOCK_UN = 8; + + /* + * This is run by the class loader and will report early any problems + * finding or linking in the shared JNI library. + */ + static { + loadLibrary(); + } + + static synchronized void loadLibrary() { + CephNativeLoader.getInstance().loadLibrary(); + } + + /* + * Package-private: called from CephNativeLoader + */ + static native void native_initialize(); + + /* + * RW lock used for fine grained synchronization to native + */ + private final ReentrantReadWriteLock rwlock = new ReentrantReadWriteLock(); + private final Lock rlock = rwlock.readLock(); + private final Lock wlock = rwlock.writeLock(); + + /* + * Controls clean-up synchronization between the constructor and finalize(). + * If native_ceph_create fails, then we want a call to finalize() to not + * attempt to clean-up native context, because there is none. + */ + private boolean initialized = false; + + /* + * Try to clean-up. First, unmount() will catch users who forget to do the + * unmount manually. Second, release() will destroy the entire context. It + * is safe to call release after a failure in unmount. + */ + protected void finalize() throws Throwable { + if (initialized) { + try { + unmount(); + } catch (Exception e) {} + try { + native_ceph_release(instance_ptr); + } catch (Exception e) {} + } + super.finalize(); + } + + /** + * Create a new CephMount with specific client id. + * + * @param id client id. + */ + public CephMount(String id) { + native_ceph_create(this, id); + initialized = true; + } + + private static native int native_ceph_create(CephMount mount, String id); + + /** + * Create a new CephMount with default client id. + */ + public CephMount() { + this(null); + } + + /** + * Activate the mount with a given root path. + * + * @param root The path to use as the root (pass null for "/"). + */ + public void mount(String root) { + wlock.lock(); + try { + native_ceph_mount(instance_ptr, root); + } finally { + wlock.unlock(); + } + } + + private static native int native_ceph_mount(long mountp, String root); + + /** + * Deactivate the mount. + * + * The mount can be reactivated using mount(). Configuration parameters + * previously set are not reset. + */ + public void unmount() { + wlock.lock(); + try { + native_ceph_unmount(instance_ptr); + } finally { + wlock.unlock(); + } + } + + private static native int native_ceph_unmount(long mountp); + + /* + * Private access to low-level ceph_release. + */ + private static native int native_ceph_release(long mountp); + + /** + * Load configuration from a file. + * + * @param path The path to the configuration file. + */ + public void conf_read_file(String path) throws FileNotFoundException { + rlock.lock(); + try { + native_ceph_conf_read_file(instance_ptr, path); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_conf_read_file(long mountp, String path); + + /** + * Set the value of a configuration option. + * + * @param option The configuration option to modify. + * @param value The new value of the option. + */ + public void conf_set(String option, String value) { + rlock.lock(); + try { + native_ceph_conf_set(instance_ptr, option, value); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_conf_set(long mountp, String option, String value); + + /** + * Get the value of a configuration option. + * + * @param option The name of the configuration option. + * @return The value of the option or null if option not found + */ + public String conf_get(String option) { + rlock.lock(); + try { + return native_ceph_conf_get(instance_ptr, option); + } finally { + rlock.unlock(); + } + } + + private static native String native_ceph_conf_get(long mountp, String option); + + /** + * Get file system status. + * + * @param path Path to file in file system. + * @param statvfs CephStatVFS structure to hold status. + */ + public void statfs(String path, CephStatVFS statvfs) throws FileNotFoundException { + rlock.lock(); + try { + native_ceph_statfs(instance_ptr, path, statvfs); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_statfs(long mountp, String path, CephStatVFS statvfs); + + /** + * Get the current working directory. + * + * @return The current working directory in Ceph. + */ + public String getcwd() { + rlock.lock(); + try { + return native_ceph_getcwd(instance_ptr); + } finally { + rlock.unlock(); + } + } + + private static native String native_ceph_getcwd(long mountp); + + /** + * Set the current working directory. + * + * @param path The directory set as the cwd. + */ + public void chdir(String path) throws FileNotFoundException { + rlock.lock(); + try { + native_ceph_chdir(instance_ptr, path); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_chdir(long mountp, String cwd); + + /** + * List the contents of a directory. + * + * @param dir The directory. + * @return List of files and directories excluding "." and "..". + */ + public String[] listdir(String dir) throws FileNotFoundException { + rlock.lock(); + try { + return native_ceph_listdir(instance_ptr, dir); + } finally { + rlock.unlock(); + } + } + + private static native String[] native_ceph_listdir(long mountp, String path); + + /** + * Create a hard link to an existing file. + * + * @param oldpath The target path of the link. + * @param newpath The name of the link. + */ + public void link(String oldpath, String newpath) throws FileNotFoundException { + rlock.lock(); + try { + native_ceph_link(instance_ptr, oldpath, newpath); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_link(long mountp, String existing, String newname); + + /** + * Unlink/delete a name from the file system. + * + * @param path The name to unlink/delete. + */ + public void unlink(String path) throws FileNotFoundException { + rlock.lock(); + try { + native_ceph_unlink(instance_ptr, path); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_unlink(long mountp, String path); + + /** + * Rename a file or directory. + * + * @param from The current path. + * @param to The new path. + */ + public void rename(String from, String to) throws FileNotFoundException { + rlock.lock(); + try { + native_ceph_rename(instance_ptr, from, to); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_rename(long mountp, String from, String to); + + /** + * Create a directory. + * + * @param path The directory to create. + * @param mode The mode of the new directory. + */ + public void mkdir(String path, int mode) { + rlock.lock(); + try { + native_ceph_mkdir(instance_ptr, path, mode); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_mkdir(long mountp, String path, int mode); + + /** + * Create a directory and all parents. + * + * @param path The directory to create. + * @param mode The mode of the new directory. + */ + public void mkdirs(String path, int mode) throws IOException { + rlock.lock(); + try { + native_ceph_mkdirs(instance_ptr, path, mode); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_mkdirs(long mountp, String path, int mode); + + /** + * Delete a directory. + * + * @param path The directory to delete. + */ + public void rmdir(String path) throws FileNotFoundException { + rlock.lock(); + try { + native_ceph_rmdir(instance_ptr, path); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_rmdir(long mountp, String path); + + /** + * Read the value of a symbolic link. + */ + public String readlink(String path) throws FileNotFoundException { + rlock.lock(); + try { + return native_ceph_readlink(instance_ptr, path); + } finally { + rlock.unlock(); + } + } + + private static native String native_ceph_readlink(long mountp, String path); + + /** + * Create a symbolic link. + * + * @param oldpath Target of the symbolic link. + * @param newpath Name of the link. + */ + public void symlink(String oldpath, String newpath) { + rlock.lock(); + try { + native_ceph_symlink(instance_ptr, oldpath, newpath); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_symlink(long mountp, String existing, String newname); + + /** + * Get file status. + * + * @param path Path of file to stat. + * @param stat CephStat structure to hold file status. + */ + public void stat(String path, CephStat stat) throws FileNotFoundException, CephNotDirectoryException { + rlock.lock(); + try { + native_ceph_stat(instance_ptr, path, stat); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_stat(long mountp, String path, CephStat stat); + + /** + * Get file status, without following symlinks. + * + * @param path Path of file to stat. + * @param stat CephStat structure to hold file status. + */ + public void lstat(String path, CephStat stat) throws FileNotFoundException, CephNotDirectoryException { + rlock.lock(); + try { + native_ceph_lstat(instance_ptr, path, stat); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_lstat(long mountp, String path, CephStat stat); + + /** + * Set file attributes. + * + * @param path Path to file. + * @param stat CephStat structure holding attributes. + * @param mask Mask specifying which attributes to set. + */ + public void setattr(String path, CephStat stat, int mask) throws FileNotFoundException { + rlock.lock(); + try { + native_ceph_setattr(instance_ptr, path, stat, mask); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_setattr(long mountp, String relpath, CephStat stat, int mask); + + /** + * Change file mode. + * + * @param path Path to file. + * @param mode New mode bits. + */ + public void chmod(String path, int mode) throws FileNotFoundException { + rlock.lock(); + try { + native_ceph_chmod(instance_ptr, path, mode); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_chmod(long mountp, String path, int mode); + + /** + * Change file mode of an open file. + * + * @param fd The open file descriptor to change the mode bits on. + * @param mode New mode bits. + */ + public void fchmod(int fd, int mode) { + rlock.lock(); + try { + native_ceph_fchmod(instance_ptr, fd, mode); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_fchmod(long mountp, int fd, int mode); + + /** + * Truncate a file to a specified length. + * + * @param path Path of the file. + * @param size New file length. + */ + public void truncate(String path, long size) throws FileNotFoundException { + rlock.lock(); + try { + native_ceph_truncate(instance_ptr, path, size); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_truncate(long mountp, String path, long size); + + /** + * Open a file. + * + * @param path Path of file to open or create. + * @param flags Open flags. + * @param mode Permission mode. + * @return File descriptor. + */ + public int open(String path, int flags, int mode) throws FileNotFoundException { + rlock.lock(); + try { + return native_ceph_open(instance_ptr, path, flags, mode); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_open(long mountp, String path, int flags, int mode); + + /** + * Open a file with a specific file layout. + * + * @param path Path of file to open or create. + * @param flags Open flags. + * @param mode Permission mode. + * @param stripe_unit File layout stripe unit size. + * @param stripe_count File layout stripe count. + * @param object_size Size of each object. + * @param data_pool The target data pool. + * @return File descriptor. + */ + public int open(String path, int flags, int mode, int stripe_unit, int stripe_count, + int object_size, String data_pool) throws FileNotFoundException { + rlock.lock(); + try { + return native_ceph_open_layout(instance_ptr, path, flags, mode, stripe_unit, + stripe_count, object_size, data_pool); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_open_layout(long mountp, String path, + int flags, int mode, int stripe_unit, int stripe_count, int object_size, String data_pool); + + /** + * Close an open file. + * + * @param fd The file descriptor. + */ + public void close(int fd) { + rlock.lock(); + try { + native_ceph_close(instance_ptr, fd); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_close(long mountp, int fd); + + /** + * Seek to a position in a file. + * + * @param fd File descriptor. + * @param offset New offset. + * @param whence Whence value. + * @return The new offset. + */ + public long lseek(int fd, long offset, int whence) { + rlock.lock(); + try { + return native_ceph_lseek(instance_ptr, fd, offset, whence); + } finally { + rlock.unlock(); + } + } + + private static native long native_ceph_lseek(long mountp, int fd, long offset, int whence); + + /** + * Read from a file. + * + * @param fd The file descriptor. + * @param buf Buffer to for data read. + * @param size Amount of data to read into the buffer. + * @param offset Offset to read from (-1 for current position). + * @return The number of bytes read. + */ + public long read(int fd, byte[] buf, long size, long offset) { + rlock.lock(); + try { + return native_ceph_read(instance_ptr, fd, buf, size, offset); + } finally { + rlock.unlock(); + } + } + + private static native long native_ceph_read(long mountp, int fd, byte[] buf, long size, long offset); + + /** + * Write to a file at a specific offset. + * + * @param fd The file descriptor. + * @param buf Buffer to write. + * @param size Amount of data to write. + * @param offset Offset to write from (-1 for current position). + * @return The number of bytes written. + */ + public long write(int fd, byte[] buf, long size, long offset) { + rlock.lock(); + try { + return native_ceph_write(instance_ptr, fd, buf, size, offset); + } finally { + rlock.unlock(); + } + } + + private static native long native_ceph_write(long mountp, int fd, byte[] buf, long size, long offset); + + /** + * Truncate a file. + * + * @param fd File descriptor of the file to truncate. + * @param size New file size. + */ + public void ftruncate(int fd, long size) { + rlock.lock(); + try { + native_ceph_ftruncate(instance_ptr, fd, size); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_ftruncate(long mountp, int fd, long size); + + /** + * Synchronize a file with the file system. + * + * @param fd File descriptor to synchronize. + * @param dataonly Synchronize only data. + */ + public void fsync(int fd, boolean dataonly) { + rlock.lock(); + try { + native_ceph_fsync(instance_ptr, fd, dataonly); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_fsync(long mountp, int fd, boolean dataonly); + + /** + * Apply or remove an advisory lock. + * + * @param fd File descriptor to lock or unlock. + * @param operation the advisory lock operation to be performed on the file + * descriptor among LOCK_SH (shared lock), LOCK_EX (exclusive lock), + * or LOCK_UN (remove lock). The LOCK_NB value can be ORed to perform a + * non-blocking operation. + * @param owner the user-supplied owner identifier (an arbitrary integer) + */ + public void flock(int fd, int operation, long owner) throws IOException { + rlock.lock(); + try { + native_ceph_flock(instance_ptr, fd, operation, owner); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_flock(long mountp, int fd, int operation, long owner); + + /** + * Get file status. + * + * @param fd The file descriptor. + * @param stat The object in which to store the status. + */ + public void fstat(int fd, CephStat stat) { + rlock.lock(); + try { + native_ceph_fstat(instance_ptr, fd, stat); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_fstat(long mountp, int fd, CephStat stat); + + /** + * Synchronize the client with the file system. + */ + public void sync_fs() { + rlock.lock(); + try { + native_ceph_sync_fs(instance_ptr); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_sync_fs(long mountp); + + /** + * Get an extended attribute value. + * + * If the buffer is large enough to hold the entire attribute value, or + * buf is null, the size of the value is returned. + * + * @param path File path. + * @param name Name of the attribute. + * @param buf Buffer to store attribute value. + * @return The length of the attribute value. See description for more + * details. + */ + public long getxattr(String path, String name, byte[] buf) throws FileNotFoundException { + rlock.lock(); + try { + return native_ceph_getxattr(instance_ptr, path, name, buf); + } finally { + rlock.unlock(); + } + } + + private static native long native_ceph_getxattr(long mountp, String path, String name, byte[] buf); + + /** + * Get an extended attribute value of a symbolic link. + * + * If the buffer is large enough to hold the entire attribute value, or + * buf is null, the size of the value is returned. + * + * @param path File path. + * @param name Name of attribute. + * @param buf Buffer to store attribute value. + * @return The length of the attribute value. See description for more + * details. + */ + public long lgetxattr(String path, String name, byte[] buf) throws FileNotFoundException { + rlock.lock(); + try { + return native_ceph_lgetxattr(instance_ptr, path, name, buf); + } finally { + rlock.unlock(); + } + } + + private static native long native_ceph_lgetxattr(long mountp, String path, String name, byte[] buf); + + /** + * List extended attributes. + * + * @param path File path. + * @return List of attribute names. + */ + public String[] listxattr(String path) throws FileNotFoundException { + rlock.lock(); + try { + return native_ceph_listxattr(instance_ptr, path); + } finally { + rlock.unlock(); + } + } + + private static native String[] native_ceph_listxattr(long mountp, String path); + + /** + * List extended attributes of a symbolic link. + * + * @param path File path. + * @return List of attribute names. + */ + public String[] llistxattr(String path) throws FileNotFoundException { + rlock.lock(); + try { + return native_ceph_llistxattr(instance_ptr, path); + } finally { + rlock.unlock(); + } + } + + private static native String[] native_ceph_llistxattr(long mountp, String path); + + /** + * Remove an extended attribute. + * + * @param path File path. + * @param name Name of attribute. + */ + public void removexattr(String path, String name) throws FileNotFoundException { + rlock.lock(); + try { + native_ceph_removexattr(instance_ptr, path, name); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_removexattr(long mountp, String path, String name); + + /** + * Remove an extended attribute from a symbolic link. + * + * @param path File path. + * @param name Name of attribute. + */ + public void lremovexattr(String path, String name) throws FileNotFoundException { + rlock.lock(); + try { + native_ceph_lremovexattr(instance_ptr, path, name); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_lremovexattr(long mountp, String path, String name); + + /** + * Set the value of an extended attribute. + * + * @param path The file path. + * @param name The attribute name. + * @param buf The attribute value. + * @param size The size of the attribute value. + * @param flags Flag controlling behavior (XATTR_CREATE/REPLACE/NONE). + */ + public void setxattr(String path, String name, byte[] buf, long size, int flags) throws FileNotFoundException { + rlock.lock(); + try { + native_ceph_setxattr(instance_ptr, path, name, buf, size, flags); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_setxattr(long mountp, String path, String name, byte[] buf, long size, int flags); + + /** + * Set the value of an extended attribute on a symbolic link. + * + * @param path The file path. + * @param name The attribute name. + * @param buf The attribute value. + * @param size The size of the attribute value. + * @param flags Flag controlling behavior (XATTR_CREATE/REPLACE/NONE). + */ + public void lsetxattr(String path, String name, byte[] buf, long size, int flags) throws FileNotFoundException { + rlock.lock(); + try { + native_ceph_lsetxattr(instance_ptr, path, name, buf, size, flags); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_lsetxattr(long mountp, String path, String name, byte[] buf, long size, int flags); + + /** + * Get the stripe unit of a file. + * + * @param fd The file descriptor. + * @return The stripe unit. + */ + public int get_file_stripe_unit(int fd) { + rlock.lock(); + try { + return native_ceph_get_file_stripe_unit(instance_ptr, fd); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_get_file_stripe_unit(long mountp, int fd); + + /** + * Get the name of the pool a file is stored in. + * + * @param fd An open file descriptor. + * @return The pool name. + */ + public String get_file_pool_name(int fd) { + rlock.lock(); + try { + return native_ceph_get_file_pool_name(instance_ptr, fd); + } finally { + rlock.unlock(); + } + } + + private static native String native_ceph_get_file_pool_name(long mountp, int fd); + + /** + * Get the default data pool of cephfs. + * + * @return The pool name. + */ + public String get_default_data_pool_name() { + rlock.lock(); + try { + return native_ceph_get_default_data_pool_name(instance_ptr); + } finally { + rlock.unlock(); + } + } + + private static native String native_ceph_get_default_data_pool_name(long mountp); + + /** + * Get the replication of a file. + * + * @param fd The file descriptor. + * @return The file replication. + */ + public int get_file_replication(int fd) { + rlock.lock(); + try { + return native_ceph_get_file_replication(instance_ptr, fd); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_get_file_replication(long mountp, int fd); + + /** + * Favor reading from local replicas when possible. + * + * @param state Enable or disable localized reads. + */ + public void localize_reads(boolean state) { + rlock.lock(); + try { + native_ceph_localize_reads(instance_ptr, state); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_localize_reads(long mountp, boolean on); + + /** + * Get file layout stripe unit granularity. + * + * @return Stripe unit granularity. + */ + public int get_stripe_unit_granularity() { + rlock.lock(); + try { + return native_ceph_get_stripe_unit_granularity(instance_ptr); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_get_stripe_unit_granularity(long mountp); + + /** + * Get the pool id for the named pool. + * + * @param name The pool name. + * @return The pool id. + */ + public int get_pool_id(String name) throws CephPoolException { + rlock.lock(); + try { + return native_ceph_get_pool_id(instance_ptr, name); + } catch (FileNotFoundException e) { + throw new CephPoolException("pool name " + name + " not found"); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_get_pool_id(long mountp, String name) throws FileNotFoundException; + + /** + * Get the pool replication factor. + * + * @param pool_id The pool id. + * @return Number of replicas stored in the pool. + */ + public int get_pool_replication(int pool_id) throws CephPoolException { + rlock.lock(); + try { + return native_ceph_get_pool_replication(instance_ptr, pool_id); + } catch (FileNotFoundException e) { + throw new CephPoolException("pool id " + pool_id + " not found"); + } finally { + rlock.unlock(); + } + } + + private static native int native_ceph_get_pool_replication(long mountp, int pool_id) throws FileNotFoundException; + + /** + * Get file extent containing a given offset. + * + * @param fd The file descriptor. + * @param offset Offset in file. + * @return A CephFileExtent object. + */ + public CephFileExtent get_file_extent(int fd, long offset) { + rlock.lock(); + try { + return native_ceph_get_file_extent_osds(instance_ptr, fd, offset); + } finally { + rlock.unlock(); + } + } + + private static native CephFileExtent native_ceph_get_file_extent_osds(long mountp, int fd, long offset); + + /** + * Get the fully qualified CRUSH location of an OSD. + * + * Returns (type, name) string pairs for each device in the CRUSH bucket + * hierarchy starting from the given OSD to the root. + * + * @param osd The OSD device id. + * @return List of pairs. + */ + public Bucket[] get_osd_crush_location(int osd) { + rlock.lock(); + try { + String[] parts = native_ceph_get_osd_crush_location(instance_ptr, osd); + Bucket[] path = new Bucket[parts.length / 2]; + for (int i = 0; i < path.length; i++) + path[i] = new Bucket(parts[i*2], parts[i*2+1]); + return path; + } finally { + rlock.unlock(); + } + } + + private static native String[] native_ceph_get_osd_crush_location(long mountp, int osd); + + /** + * Get the network address of an OSD. + * + * @param osd The OSD device id. + * @return The network address. + */ + public InetAddress get_osd_address(int osd) { + rlock.lock(); + try { + return native_ceph_get_osd_addr(instance_ptr, osd); + } finally { + rlock.unlock(); + } + } + + private static native InetAddress native_ceph_get_osd_addr(long mountp, int osd); +} diff --git a/src/java/java/com/ceph/fs/CephNativeLoader.java b/src/java/java/com/ceph/fs/CephNativeLoader.java new file mode 100644 index 00000000..358ca243 --- /dev/null +++ b/src/java/java/com/ceph/fs/CephNativeLoader.java @@ -0,0 +1,93 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +package com.ceph.fs; + +class CephNativeLoader { + private static final CephNativeLoader instance = new CephNativeLoader(); + private static boolean initialized = false; + + private static final String JNI_PATH_ENV_VAR = "CEPH_JNI_PATH"; + private static final String LIBRARY_NAME = "cephfs_jni"; + private static final String LIBRARY_FILE = "libcephfs_jni.so"; + + private CephNativeLoader() {} + + public static CephNativeLoader getInstance() { + return instance; + } + + public synchronized void loadLibrary() { + if (initialized) + return; + + boolean success = false; + + /* + * Allow a Ceph specific environment variable to force + * the loading path. + */ + String path = System.getenv(JNI_PATH_ENV_VAR); + try { + if (path != null) { + System.out.println("Loading libcephfs-jni: " + path); + System.load(path); + success = true; + } else { + try { + /* + * Try default Java loading path(s) + */ + System.out.println("Loading libcephfs-jni from default path: " + + System.getProperty("java.library.path")); + System.loadLibrary(LIBRARY_NAME); + success = true; + } catch (final UnsatisfiedLinkError ule1) { + try { + /* + * Try RHEL/CentOS default path + */ + path = "/usr/lib64/" + LIBRARY_FILE; + System.out.println("Loading libcephfs-jni: " + path); + System.load(path); + success = true; + } catch (final UnsatisfiedLinkError ule2) { + /* + * Try Ubuntu default path + */ + path = "/usr/lib/jni/" + LIBRARY_FILE; + System.out.println("Loading libcephfs-jni: " + path); + System.load(path); + success = true; + } + } + } + } finally { + System.out.println("Loading libcephfs-jni: " + + (success ? "Success!" : "Failure!")); + } + + /* + * Finish initialization + */ + CephMount.native_initialize(); + initialized = true; + } + +} diff --git a/src/java/java/com/ceph/fs/CephNotDirectoryException.java b/src/java/java/com/ceph/fs/CephNotDirectoryException.java new file mode 100644 index 00000000..3fb77c77 --- /dev/null +++ b/src/java/java/com/ceph/fs/CephNotDirectoryException.java @@ -0,0 +1,44 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +package com.ceph.fs; + +import java.io.IOException; + +/** + * Component of path is not a directory. + */ +public class CephNotDirectoryException extends IOException { + + private static final long serialVersionUID = 1L; + + /** + * Construct CephNotDirectoryException. + */ + public CephNotDirectoryException() { + super(); + } + + /** + * Construct CephNotDirectoryException with message. + */ + public CephNotDirectoryException(String s) { + super(s); + } +} diff --git a/src/java/java/com/ceph/fs/CephNotMountedException.java b/src/java/java/com/ceph/fs/CephNotMountedException.java new file mode 100644 index 00000000..5a479b5e --- /dev/null +++ b/src/java/java/com/ceph/fs/CephNotMountedException.java @@ -0,0 +1,44 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +package com.ceph.fs; + +import java.io.IOException; + +/** + * Ceph is not mounted. + */ +public class CephNotMountedException extends IOException { + + private static final long serialVersionUID = 1L; + + /** + * Construct CephNotMountedException. + */ + public CephNotMountedException() { + super(); + } + + /** + * Construct CephNotMountedException with message. + */ + public CephNotMountedException(String s) { + super(s); + } +} diff --git a/src/java/java/com/ceph/fs/CephPoolException.java b/src/java/java/com/ceph/fs/CephPoolException.java new file mode 100644 index 00000000..c5092e6c --- /dev/null +++ b/src/java/java/com/ceph/fs/CephPoolException.java @@ -0,0 +1,44 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +package com.ceph.fs; + +import java.io.IOException; + +/** + * Exception related to Ceph pool. + */ +public class CephPoolException extends IOException { + + private static final long serialVersionUID = 1L; + + /** + * Construct CephPoolException. + */ + public CephPoolException() { + super(); + } + + /** + * Construct CephPoolException with message. + */ + public CephPoolException(String s) { + super(s); + } +} diff --git a/src/java/java/com/ceph/fs/CephStat.java b/src/java/java/com/ceph/fs/CephStat.java new file mode 100644 index 00000000..12aa8c98 --- /dev/null +++ b/src/java/java/com/ceph/fs/CephStat.java @@ -0,0 +1,53 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +package com.ceph.fs; + +/** + * Holds struct stat fields. + */ +public class CephStat { + + /* Set from native */ + private boolean is_file; /* S_ISREG */ + private boolean is_directory; /* S_ISDIR */ + private boolean is_symlink; /* S_ISLNK */ + + public int mode; + public int uid; + public int gid; + public long size; + public long blksize; + public long blocks; + public long a_time; + public long m_time; + + public boolean isFile() { + return is_file; + } + + public boolean isDir() { + return is_directory; + } + + public boolean isSymlink() { + return is_symlink; + } + +} diff --git a/src/java/java/com/ceph/fs/CephStatVFS.java b/src/java/java/com/ceph/fs/CephStatVFS.java new file mode 100644 index 00000000..4a37a28d --- /dev/null +++ b/src/java/java/com/ceph/fs/CephStatVFS.java @@ -0,0 +1,33 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +package com.ceph.fs; + +/** + * Holds struct statvfs fields. + */ +public class CephStatVFS { + public long bsize; + public long frsize; + public long blocks; + public long bavail; + public long files; + public long fsid; + public long namemax; +} diff --git a/src/java/native/CMakeLists.txt b/src/java/native/CMakeLists.txt new file mode 100644 index 00000000..2c6067b6 --- /dev/null +++ b/src/java/native/CMakeLists.txt @@ -0,0 +1,14 @@ +add_library(cephfs_jni SHARED + libcephfs_jni.cc + ScopedLocalRef.h + JniConstants.cpp + JniConstants.h) +set_target_properties(cephfs_jni PROPERTIES + VERSION 1.0.0 + SOVERSION 1) +add_dependencies(cephfs_jni jni-header) +include_directories(${JNI_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR}) +target_link_libraries(cephfs_jni PRIVATE cephfs ceph-common + ${EXTRALIBS} ${JNI_LIBRARIES}) +install(TARGETS cephfs_jni + DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/src/java/native/JniConstants.cpp b/src/java/native/JniConstants.cpp new file mode 100644 index 00000000..cd54c99b --- /dev/null +++ b/src/java/native/JniConstants.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "JniConstants.h" +#include "ScopedLocalRef.h" + +#include <stdlib.h> + +jclass JniConstants::inet6AddressClass; +jclass JniConstants::inetAddressClass; +jclass JniConstants::inetSocketAddressClass; +jclass JniConstants::stringClass; + +static jclass findClass(JNIEnv* env, const char* name) { + ScopedLocalRef<jclass> localClass(env, env->FindClass(name)); + jclass result = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get())); + if (result == NULL) { + fprintf(stderr, "failed to find class '%s'", name); + abort(); + } + return result; +} + +void JniConstants::init(JNIEnv* env) { + inet6AddressClass = findClass(env, "java/net/Inet6Address"); + inetAddressClass = findClass(env, "java/net/InetAddress"); + inetSocketAddressClass = findClass(env, "java/net/InetSocketAddress"); + stringClass = findClass(env, "java/lang/String"); +} diff --git a/src/java/native/JniConstants.h b/src/java/native/JniConstants.h new file mode 100644 index 00000000..acee8670 --- /dev/null +++ b/src/java/native/JniConstants.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef JNI_CONSTANTS_H_included +#define JNI_CONSTANTS_H_included + +#include <jni.h> + +/** + * A cache to avoid calling FindClass at runtime. + * + * Class lookup is relatively expensive (2.5us on passion-eng at the time of writing), so we do + * all such lookups eagerly at VM startup. This means that code that never uses, say, + * java.util.zip.Deflater still has to pay for the lookup, but it means that on a device the cost + * is definitely paid during boot and amortized. A central cache also removes the temptation to + * dynamically call FindClass rather than add a small cache to each file that needs one. Another + * cost is that each class cached here requires a global reference, though in practice we save + * enough by not having a global reference for each file that uses a class such as java.lang.String + * which is used in several files. + * + * FindClass is still called in a couple of situations: when throwing exceptions, and in some of + * the serialization code. The former is clearly not a performance case, and we're currently + * assuming that neither is the latter. + * + * TODO: similar arguments hold for field and method IDs; we should cache them centrally too. + */ +struct JniConstants { + static void init(JNIEnv* env); + + static jclass inet6AddressClass; + static jclass inetAddressClass; + static jclass inetSocketAddressClass; + static jclass stringClass; +}; + +#define NATIVE_METHOD(className, functionName, signature) \ + { #functionName, signature, reinterpret_cast<void*>(className ## _ ## functionName) } + +#endif // JNI_CONSTANTS_H_included diff --git a/src/java/native/ScopedLocalRef.h b/src/java/native/ScopedLocalRef.h new file mode 100644 index 00000000..71d57767 --- /dev/null +++ b/src/java/native/ScopedLocalRef.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SCOPED_LOCAL_REF_H_included +#define SCOPED_LOCAL_REF_H_included + +#include "jni.h" + +#include <stddef.h> + +// A smart pointer that deletes a JNI local reference when it goes out of scope. +template<typename T> +class ScopedLocalRef { +public: + ScopedLocalRef(JNIEnv* env, T localRef) : mEnv(env), mLocalRef(localRef) { + } + + ~ScopedLocalRef() { + reset(); + } + + void reset(T ptr = NULL) { + if (ptr != mLocalRef) { + if (mLocalRef != NULL) { + mEnv->DeleteLocalRef(mLocalRef); + } + mLocalRef = ptr; + } + } + + T release() __attribute__((warn_unused_result)) { + T localRef = mLocalRef; + mLocalRef = NULL; + return localRef; + } + + T get() const { + return mLocalRef; + } + +private: + JNIEnv* mEnv; + T mLocalRef; + + // Disallow copy and assignment. + ScopedLocalRef(const ScopedLocalRef&); + void operator=(const ScopedLocalRef&); +}; + +#endif // SCOPED_LOCAL_REF_H_included diff --git a/src/java/native/libcephfs_jni.cc b/src/java/native/libcephfs_jni.cc new file mode 100644 index 00000000..74ef8dcd --- /dev/null +++ b/src/java/native/libcephfs_jni.cc @@ -0,0 +1,3031 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/un.h> +#include <jni.h> + +#include "ScopedLocalRef.h" +#include "JniConstants.h" + +#include "include/cephfs/libcephfs.h" +#include "common/dout.h" + +#define dout_subsys ceph_subsys_javaclient + +#include "com_ceph_fs_CephMount.h" + +#define CEPH_STAT_CP "com/ceph/fs/CephStat" +#define CEPH_STAT_VFS_CP "com/ceph/fs/CephStatVFS" +#define CEPH_FILE_EXTENT_CP "com/ceph/fs/CephFileExtent" +#define CEPH_MOUNT_CP "com/ceph/fs/CephMount" +#define CEPH_NOTMOUNTED_CP "com/ceph/fs/CephNotMountedException" +#define CEPH_FILEEXISTS_CP "com/ceph/fs/CephFileAlreadyExistsException" +#define CEPH_ALREADYMOUNTED_CP "com/ceph/fs/CephAlreadyMountedException" +#define CEPH_NOTDIR_CP "com/ceph/fs/CephNotDirectoryException" + +/* + * Flags to open(). must be synchronized with CephMount.java + * + * There are two versions of flags: the version in Java and the version in the + * target library (e.g. libc or libcephfs). We control the Java values and map + * to the target value with fixup_* functions below. This is much faster than + * keeping the values in Java and making a cross-JNI up-call to retrieve them, + * and makes it easy to keep any platform specific value changes in this file. + */ +#define JAVA_O_RDONLY 1 +#define JAVA_O_RDWR 2 +#define JAVA_O_APPEND 4 +#define JAVA_O_CREAT 8 +#define JAVA_O_TRUNC 16 +#define JAVA_O_EXCL 32 +#define JAVA_O_WRONLY 64 +#define JAVA_O_DIRECTORY 128 + +/* + * Whence flags for seek(). sync with CephMount.java if changed. + * + * Mapping of SEEK_* done in seek function. + */ +#define JAVA_SEEK_SET 1 +#define JAVA_SEEK_CUR 2 +#define JAVA_SEEK_END 3 + +/* + * File attribute flags. sync with CephMount.java if changed. + */ +#define JAVA_SETATTR_MODE 1 +#define JAVA_SETATTR_UID 2 +#define JAVA_SETATTR_GID 4 +#define JAVA_SETATTR_MTIME 8 +#define JAVA_SETATTR_ATIME 16 + +/* + * Setxattr flags. sync with CephMount.java if changed. + */ +#define JAVA_XATTR_CREATE 1 +#define JAVA_XATTR_REPLACE 2 +#define JAVA_XATTR_NONE 3 + +/* + * flock flags. sync with CephMount.java if changed. + */ +#define JAVA_LOCK_SH 1 +#define JAVA_LOCK_EX 2 +#define JAVA_LOCK_NB 4 +#define JAVA_LOCK_UN 8 + +/* Map JAVA_O_* open flags to values in libc */ +static inline int fixup_open_flags(jint jflags) +{ + int ret = 0; + +#define FIXUP_OPEN_FLAG(name) \ + if (jflags & JAVA_##name) \ + ret |= name; + + FIXUP_OPEN_FLAG(O_RDONLY) + FIXUP_OPEN_FLAG(O_RDWR) + FIXUP_OPEN_FLAG(O_APPEND) + FIXUP_OPEN_FLAG(O_CREAT) + FIXUP_OPEN_FLAG(O_TRUNC) + FIXUP_OPEN_FLAG(O_EXCL) + FIXUP_OPEN_FLAG(O_WRONLY) + FIXUP_OPEN_FLAG(O_DIRECTORY) + +#undef FIXUP_OPEN_FLAG + + return ret; +} + +/* Map JAVA_SETATTR_* to values in ceph lib */ +static inline int fixup_attr_mask(jint jmask) +{ + int mask = 0; + +#define FIXUP_ATTR_MASK(name) \ + if (jmask & JAVA_##name) \ + mask |= CEPH_##name; + + FIXUP_ATTR_MASK(SETATTR_MODE) + FIXUP_ATTR_MASK(SETATTR_UID) + FIXUP_ATTR_MASK(SETATTR_GID) + FIXUP_ATTR_MASK(SETATTR_MTIME) + FIXUP_ATTR_MASK(SETATTR_ATIME) + +#undef FIXUP_ATTR_MASK + + return mask; +} + +/* Cached field IDs for com.ceph.fs.CephStat */ +static jfieldID cephstat_mode_fid; +static jfieldID cephstat_uid_fid; +static jfieldID cephstat_gid_fid; +static jfieldID cephstat_size_fid; +static jfieldID cephstat_blksize_fid; +static jfieldID cephstat_blocks_fid; +static jfieldID cephstat_a_time_fid; +static jfieldID cephstat_m_time_fid; +static jfieldID cephstat_is_file_fid; +static jfieldID cephstat_is_directory_fid; +static jfieldID cephstat_is_symlink_fid; + +/* Cached field IDs for com.ceph.fs.CephStatVFS */ +static jfieldID cephstatvfs_bsize_fid; +static jfieldID cephstatvfs_frsize_fid; +static jfieldID cephstatvfs_blocks_fid; +static jfieldID cephstatvfs_bavail_fid; +static jfieldID cephstatvfs_files_fid; +static jfieldID cephstatvfs_fsid_fid; +static jfieldID cephstatvfs_namemax_fid; + +/* Cached field IDs for com.ceph.fs.CephMount */ +static jfieldID cephmount_instance_ptr_fid; + +/* Cached field IDs for com.ceph.fs.CephFileExtent */ +static jclass cephfileextent_cls; +static jmethodID cephfileextent_ctor_fid; + +/* + * Exception throwing helper. Adapted from Apache Hadoop header + * org_apache_hadoop.h by adding the do {} while (0) construct. + */ +#define THROW(env, exception_name, message) \ + do { \ + jclass ecls = env->FindClass(exception_name); \ + if (ecls) { \ + int ret = env->ThrowNew(ecls, message); \ + if (ret < 0) { \ + printf("(CephFS) Fatal Error\n"); \ + } \ + env->DeleteLocalRef(ecls); \ + } \ + } while (0) + + +static void cephThrowNullArg(JNIEnv *env, const char *msg) +{ + THROW(env, "java/lang/NullPointerException", msg); +} + +static void cephThrowOutOfMemory(JNIEnv *env, const char *msg) +{ + THROW(env, "java/lang/OutOfMemoryError", msg); +} + +static void cephThrowInternal(JNIEnv *env, const char *msg) +{ + THROW(env, "java/lang/InternalError", msg); +} + +static void cephThrowIndexBounds(JNIEnv *env, const char *msg) +{ + THROW(env, "java/lang/IndexOutOfBoundsException", msg); +} + +static void cephThrowIllegalArg(JNIEnv *env, const char *msg) +{ + THROW(env, "java/lang/IllegalArgumentException", msg); +} + +static void cephThrowFNF(JNIEnv *env, const char *msg) +{ + THROW(env, "java/io/FileNotFoundException", msg); +} + +static void cephThrowFileExists(JNIEnv *env, const char *msg) +{ + THROW(env, CEPH_FILEEXISTS_CP, msg); +} + +static void cephThrowNotDir(JNIEnv *env, const char *msg) +{ + THROW(env, CEPH_NOTDIR_CP, msg); +} + +static void handle_error(JNIEnv *env, int rc) +{ + switch (rc) { + case -ENOENT: + cephThrowFNF(env, ""); + return; + case -EEXIST: + cephThrowFileExists(env, ""); + return; + case -ENOTDIR: + cephThrowNotDir(env, ""); + return; + default: + break; + } + + THROW(env, "java/io/IOException", strerror(-rc)); +} + +#define CHECK_ARG_NULL(v, m, r) do { \ + if (!(v)) { \ + cephThrowNullArg(env, (m)); \ + return (r); \ + } } while (0) + +#define CHECK_ARG_BOUNDS(c, m, r) do { \ + if ((c)) { \ + cephThrowIndexBounds(env, (m)); \ + return (r); \ + } } while (0) + +#define CHECK_MOUNTED(_c, _r) do { \ + if (!ceph_is_mounted((_c))) { \ + THROW(env, CEPH_NOTMOUNTED_CP, "not mounted"); \ + return (_r); \ + } } while (0) + +/* + * Cast a jlong to ceph_mount_info. Each JNI function is expected to pass in + * the class instance variable instance_ptr. Passing a parameter is faster + * than reaching back into Java via an upcall to retrieve this pointer. + */ +static inline struct ceph_mount_info *get_ceph_mount(jlong j_mntp) +{ + return (struct ceph_mount_info *)j_mntp; +} + +/* + * Setup cached field IDs + */ +static void setup_field_ids(JNIEnv *env, jclass clz) +{ + jclass cephstat_cls; + jclass cephstatvfs_cls; + jclass tmp_cephfileextent_cls; + +/* + * Get a fieldID from a class with a specific type + * + * clz: jclass + * field: field in clz + * type: integer, long, etc.. + * + * This macro assumes some naming convention that is used + * only in this file: + * + * GETFID(cephstat, mode, I) gets translated into + * cephstat_mode_fid = env->GetFieldID(cephstat_cls, "mode", "I"); + */ +#define GETFID(clz, field, type) do { \ + clz ## _ ## field ## _fid = env->GetFieldID(clz ## _cls, #field, #type); \ + if ( ! clz ## _ ## field ## _fid ) \ + return; \ + } while (0) + + /* Cache CephStat fields */ + + cephstat_cls = env->FindClass(CEPH_STAT_CP); + if (!cephstat_cls) + return; + + GETFID(cephstat, mode, I); + GETFID(cephstat, uid, I); + GETFID(cephstat, gid, I); + GETFID(cephstat, size, J); + GETFID(cephstat, blksize, J); + GETFID(cephstat, blocks, J); + GETFID(cephstat, a_time, J); + GETFID(cephstat, m_time, J); + GETFID(cephstat, is_file, Z); + GETFID(cephstat, is_directory, Z); + GETFID(cephstat, is_symlink, Z); + + /* Cache CephStatVFS fields */ + + cephstatvfs_cls = env->FindClass(CEPH_STAT_VFS_CP); + if (!cephstatvfs_cls) + return; + + GETFID(cephstatvfs, bsize, J); + GETFID(cephstatvfs, frsize, J); + GETFID(cephstatvfs, blocks, J); + GETFID(cephstatvfs, bavail, J); + GETFID(cephstatvfs, files, J); + GETFID(cephstatvfs, fsid, J); + GETFID(cephstatvfs, namemax, J); + + /* Cache CephFileExtent fields */ + + tmp_cephfileextent_cls = env->FindClass(CEPH_FILE_EXTENT_CP); + if (!tmp_cephfileextent_cls) + return; + + cephfileextent_cls = (jclass)env->NewGlobalRef(tmp_cephfileextent_cls); + env->DeleteLocalRef(tmp_cephfileextent_cls); + + cephfileextent_ctor_fid = env->GetMethodID(cephfileextent_cls, "<init>", "(JJ[I)V"); + if (!cephfileextent_ctor_fid) + return; + + JniConstants::init(env); + +#undef GETFID + + cephmount_instance_ptr_fid = env->GetFieldID(clz, "instance_ptr", "J"); +} + + +/* + * Class: com_ceph_fs_CephMount + * Method: native_initialize + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_ceph_fs_CephMount_native_1initialize + (JNIEnv *env, jclass clz) +{ + setup_field_ids(env, clz); +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_create + * Signature: (Lcom/ceph/fs/CephMount;Ljava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1create + (JNIEnv *env, jclass clz, jobject j_cephmount, jstring j_id) +{ + struct ceph_mount_info *cmount; + const char *c_id = NULL; + int ret; + + CHECK_ARG_NULL(j_cephmount, "@mount is null", -1); + + if (j_id) { + c_id = env->GetStringUTFChars(j_id, NULL); + if (!c_id) { + cephThrowInternal(env, "Failed to pin memory"); + return -1; + } + } + + ret = ceph_create(&cmount, c_id); + + if (c_id) + env->ReleaseStringUTFChars(j_id, c_id); + + if (ret) { + THROW(env, "java/lang/RuntimeException", "failed to create Ceph mount object"); + return ret; + } + + env->SetLongField(j_cephmount, cephmount_instance_ptr_fid, (long)cmount); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_mount + * Signature: (JLjava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1mount + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_root) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_root = NULL; + int ret; + + /* + * Toss a message up if we are already mounted. + */ + if (ceph_is_mounted(cmount)) { + THROW(env, CEPH_ALREADYMOUNTED_CP, ""); + return -1; + } + + if (j_root) { + c_root = env->GetStringUTFChars(j_root, NULL); + if (!c_root) { + cephThrowInternal(env, "Failed to pin memory"); + return -1; + } + } + + ldout(cct, 10) << "jni: ceph_mount: " << (c_root ? c_root : "<NULL>") << dendl; + + ret = ceph_mount(cmount, c_root); + + ldout(cct, 10) << "jni: ceph_mount: exit ret " << ret << dendl; + + if (c_root) + env->ReleaseStringUTFChars(j_root, c_root); + + if (ret) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_unmount + * Signature: (J)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1unmount + (JNIEnv *env, jclass clz, jlong j_mntp) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + int ret; + + ldout(cct, 10) << "jni: ceph_unmount enter" << dendl; + + CHECK_MOUNTED(cmount, -1); + + ret = ceph_unmount(cmount); + + ldout(cct, 10) << "jni: ceph_unmount exit ret " << ret << dendl; + + if (ret) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_release + * Signature: (J)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1release + (JNIEnv *env, jclass clz, jlong j_mntp) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + int ret; + + ldout(cct, 10) << "jni: ceph_release called" << dendl; + + ret = ceph_release(cmount); + + if (ret) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_conf_set + * Signature: (JLjava/lang/String;Ljava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1conf_1set + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_opt, jstring j_val) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_opt, *c_val; + int ret; + + CHECK_ARG_NULL(j_opt, "@option is null", -1); + CHECK_ARG_NULL(j_val, "@value is null", -1); + + c_opt = env->GetStringUTFChars(j_opt, NULL); + if (!c_opt) { + cephThrowInternal(env, "failed to pin memory"); + return -1; + } + + c_val = env->GetStringUTFChars(j_val, NULL); + if (!c_val) { + env->ReleaseStringUTFChars(j_opt, c_opt); + cephThrowInternal(env, "failed to pin memory"); + return -1; + } + + ldout(cct, 10) << "jni: conf_set: opt " << c_opt << " val " << c_val << dendl; + + ret = ceph_conf_set(cmount, c_opt, c_val); + + ldout(cct, 10) << "jni: conf_set: exit ret " << ret << dendl; + + env->ReleaseStringUTFChars(j_opt, c_opt); + env->ReleaseStringUTFChars(j_val, c_val); + + if (ret) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_conf_get + * Signature: (JLjava/lang/String;)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1conf_1get + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_opt) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_opt; + jstring value = NULL; + int ret, buflen; + char *buf; + + CHECK_ARG_NULL(j_opt, "@option is null", NULL); + + c_opt = env->GetStringUTFChars(j_opt, NULL); + if (!c_opt) { + cephThrowInternal(env, "failed to pin memory"); + return NULL; + } + + buflen = 128; + buf = new (std::nothrow) char[buflen]; + if (!buf) { + cephThrowOutOfMemory(env, "head allocation failed"); + goto out; + } + + while (1) { + memset(buf, 0, sizeof(char)*buflen); + ldout(cct, 10) << "jni: conf_get: opt " << c_opt << " len " << buflen << dendl; + ret = ceph_conf_get(cmount, c_opt, buf, buflen); + if (ret == -ENAMETOOLONG) { + buflen *= 2; + delete [] buf; + buf = new (std::nothrow) char[buflen]; + if (!buf) { + cephThrowOutOfMemory(env, "head allocation failed"); + goto out; + } + } else + break; + } + + ldout(cct, 10) << "jni: conf_get: ret " << ret << dendl; + + if (ret == 0) + value = env->NewStringUTF(buf); + else if (ret != -ENOENT) + handle_error(env, ret); + + delete [] buf; + +out: + env->ReleaseStringUTFChars(j_opt, c_opt); + return value; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_conf_read_file + * Signature: (JLjava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1conf_1read_1file + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_path) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_path; + int ret; + + CHECK_ARG_NULL(j_path, "@path is null", -1); + + c_path = env->GetStringUTFChars(j_path, NULL); + if (!c_path) { + cephThrowInternal(env, "failed to pin memory"); + return -1; + } + + ldout(cct, 10) << "jni: conf_read_file: path " << c_path << dendl; + + ret = ceph_conf_read_file(cmount, c_path); + + ldout(cct, 10) << "jni: conf_read_file: exit ret " << ret << dendl; + + env->ReleaseStringUTFChars(j_path, c_path); + + if (ret) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_statfs + * Signature: (JLjava/lang/String;Lcom/ceph/fs/CephStatVFS;)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1statfs + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_path, jobject j_cephstatvfs) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_path; + struct statvfs st; + int ret; + + CHECK_ARG_NULL(j_path, "@path is null", -1); + CHECK_ARG_NULL(j_cephstatvfs, "@stat is null", -1); + CHECK_MOUNTED(cmount, -1); + + c_path = env->GetStringUTFChars(j_path, NULL); + if (!c_path) { + cephThrowInternal(env, "Failed to pin memory"); + return -1; + } + + ldout(cct, 10) << "jni: statfs: path " << c_path << dendl; + + ret = ceph_statfs(cmount, c_path, &st); + + ldout(cct, 10) << "jni: statfs: exit ret " << ret << dendl; + + env->ReleaseStringUTFChars(j_path, c_path); + + if (ret) { + handle_error(env, ret); + return ret; + } + + env->SetLongField(j_cephstatvfs, cephstatvfs_bsize_fid, st.f_bsize); + env->SetLongField(j_cephstatvfs, cephstatvfs_frsize_fid, st.f_frsize); + env->SetLongField(j_cephstatvfs, cephstatvfs_blocks_fid, st.f_blocks); + env->SetLongField(j_cephstatvfs, cephstatvfs_bavail_fid, st.f_bavail); + env->SetLongField(j_cephstatvfs, cephstatvfs_files_fid, st.f_files); + env->SetLongField(j_cephstatvfs, cephstatvfs_fsid_fid, st.f_fsid); + env->SetLongField(j_cephstatvfs, cephstatvfs_namemax_fid, st.f_namemax); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_getcwd + * Signature: (J)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1getcwd + (JNIEnv *env, jclass clz, jlong j_mntp) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_cwd; + + CHECK_MOUNTED(cmount, NULL); + + ldout(cct, 10) << "jni: getcwd: enter" << dendl; + + c_cwd = ceph_getcwd(cmount); + if (!c_cwd) { + cephThrowOutOfMemory(env, "ceph_getcwd"); + return NULL; + } + + ldout(cct, 10) << "jni: getcwd: exit ret " << c_cwd << dendl; + + return env->NewStringUTF(c_cwd); +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_chdir + * Signature: (JLjava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1chdir + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_path) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_path; + int ret; + + CHECK_ARG_NULL(j_path, "@path is null", -1); + CHECK_MOUNTED(cmount, -1); + + c_path = env->GetStringUTFChars(j_path, NULL); + if (!c_path) { + cephThrowInternal(env, "failed to pin memory"); + return -1; + } + + ldout(cct, 10) << "jni: chdir: path " << c_path << dendl; + + ret = ceph_chdir(cmount, c_path); + + ldout(cct, 10) << "jni: chdir: exit ret " << ret << dendl; + + env->ReleaseStringUTFChars(j_path, c_path); + + if (ret) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_listdir + * Signature: (JLjava/lang/String;)[Ljava/lang/String; + */ +JNIEXPORT jobjectArray JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1listdir + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_path) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + struct ceph_dir_result *dirp; + list<string>::iterator it; + list<string> contents; + const char *c_path; + jobjectArray dirlist; + string *ent; + int ret, buflen, bufpos, i; + jstring name; + char *buf; + + CHECK_ARG_NULL(j_path, "@path is null", NULL); + CHECK_MOUNTED(cmount, NULL); + + c_path = env->GetStringUTFChars(j_path, NULL); + if (!c_path) { + cephThrowInternal(env, "failed to pin memory"); + return NULL; + } + + ldout(cct, 10) << "jni: listdir: opendir: path " << c_path << dendl; + + /* ret < 0 also includes -ENOTDIR which should return NULL */ + ret = ceph_opendir(cmount, c_path, &dirp); + if (ret) { + env->ReleaseStringUTFChars(j_path, c_path); + handle_error(env, ret); + return NULL; + } + + ldout(cct, 10) << "jni: listdir: opendir: exit ret " << ret << dendl; + + /* buffer for ceph_getdnames() results */ + buflen = 256; + buf = new (std::nothrow) char[buflen]; + if (!buf) { + cephThrowOutOfMemory(env, "heap allocation failed"); + goto out; + } + + while (1) { + ldout(cct, 10) << "jni: listdir: getdnames: enter" << dendl; + ret = ceph_getdnames(cmount, dirp, buf, buflen); + if (ret == -ERANGE) { + delete [] buf; + buflen *= 2; + buf = new (std::nothrow) char[buflen]; + if (!buf) { + cephThrowOutOfMemory(env, "heap allocation failed"); + goto out; + } + continue; + } + + ldout(cct, 10) << "jni: listdir: getdnames: exit ret " << ret << dendl; + + if (ret <= 0) + break; + + /* got at least one name */ + bufpos = 0; + while (bufpos < ret) { + ent = new (std::nothrow) string(buf + bufpos); + if (!ent) { + delete [] buf; + cephThrowOutOfMemory(env, "heap allocation failed"); + goto out; + } + + /* filter out dot files: xref: java.io.File::list() */ + if (ent->compare(".") && ent->compare("..")) { + contents.push_back(*ent); + ldout(cct, 20) << "jni: listdir: take path " << *ent << dendl; + } + + bufpos += ent->size() + 1; + delete ent; + } + } + + delete [] buf; + + if (ret < 0) { + handle_error(env, ret); + goto out; + } + + /* directory list */ + dirlist = env->NewObjectArray(contents.size(), env->FindClass("java/lang/String"), NULL); + if (!dirlist) + goto out; + + /* + * Fill directory listing array. + * + * FIXME: how should a partially filled array be cleaned-up properly? + */ + for (i = 0, it = contents.begin(); it != contents.end(); ++it) { + name = env->NewStringUTF(it->c_str()); + if (!name) + goto out; + env->SetObjectArrayElement(dirlist, i++, name); + if (env->ExceptionOccurred()) + goto out; + env->DeleteLocalRef(name); + } + + env->ReleaseStringUTFChars(j_path, c_path); + ceph_closedir(cmount, dirp); + + return dirlist; + +out: + env->ReleaseStringUTFChars(j_path, c_path); + ceph_closedir(cmount, dirp); + return NULL; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_link + * Signature: (JLjava/lang/String;Ljava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1link + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_oldpath, jstring j_newpath) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_oldpath, *c_newpath; + int ret; + + CHECK_ARG_NULL(j_oldpath, "@oldpath is null", -1); + CHECK_ARG_NULL(j_newpath, "@newpath is null", -1); + CHECK_MOUNTED(cmount, -1); + + c_oldpath = env->GetStringUTFChars(j_oldpath, NULL); + if (!c_oldpath) { + cephThrowInternal(env, "failed to pin memory"); + return -1; + } + + c_newpath = env->GetStringUTFChars(j_newpath, NULL); + if (!c_newpath) { + env->ReleaseStringUTFChars(j_oldpath, c_oldpath); + cephThrowInternal(env, "failed to pin memory"); + return -1; + } + + ldout(cct, 10) << "jni: link: oldpath " << c_oldpath << + " newpath " << c_newpath << dendl; + + ret = ceph_link(cmount, c_oldpath, c_newpath); + + ldout(cct, 10) << "jni: link: exit ret " << ret << dendl; + + env->ReleaseStringUTFChars(j_oldpath, c_oldpath); + env->ReleaseStringUTFChars(j_newpath, c_newpath); + + if (ret) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_unlink + * Signature: (JLjava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1unlink + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_path) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_path; + int ret; + + CHECK_ARG_NULL(j_path, "@path is null", -1); + CHECK_MOUNTED(cmount, -1); + + c_path = env->GetStringUTFChars(j_path, NULL); + if (!c_path) { + cephThrowInternal(env, "failed to pin memory"); + return -1; + } + + ldout(cct, 10) << "jni: unlink: path " << c_path << dendl; + + ret = ceph_unlink(cmount, c_path); + + ldout(cct, 10) << "jni: unlink: exit ret " << ret << dendl; + + env->ReleaseStringUTFChars(j_path, c_path); + + if (ret) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_rename + * Signature: (JLjava/lang/String;Ljava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1rename + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_from, jstring j_to) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_from, *c_to; + int ret; + + CHECK_ARG_NULL(j_from, "@from is null", -1); + CHECK_ARG_NULL(j_to, "@to is null", -1); + CHECK_MOUNTED(cmount, -1); + + c_from = env->GetStringUTFChars(j_from, NULL); + if (!c_from) { + cephThrowInternal(env, "Failed to pin memory!"); + return -1; + } + + c_to = env->GetStringUTFChars(j_to, NULL); + if (!c_to) { + env->ReleaseStringUTFChars(j_from, c_from); + cephThrowInternal(env, "Failed to pin memory."); + return -1; + } + + ldout(cct, 10) << "jni: rename: from " << c_from << " to " << c_to << dendl; + + ret = ceph_rename(cmount, c_from, c_to); + + ldout(cct, 10) << "jni: rename: exit ret " << ret << dendl; + + env->ReleaseStringUTFChars(j_from, c_from); + env->ReleaseStringUTFChars(j_to, c_to); + + if (ret) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_mkdir + * Signature: (JLjava/lang/String;I)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1mkdir + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_path, jint j_mode) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_path; + int ret; + + CHECK_ARG_NULL(j_path, "@path is null", -1); + CHECK_MOUNTED(cmount, -1); + + c_path = env->GetStringUTFChars(j_path, NULL); + if (!c_path) { + cephThrowInternal(env, "failed to pin memory"); + return -1; + } + + ldout(cct, 10) << "jni: mkdir: path " << c_path << " mode " << (int)j_mode << dendl; + + ret = ceph_mkdir(cmount, c_path, (int)j_mode); + + ldout(cct, 10) << "jni: mkdir: exit ret " << ret << dendl; + + env->ReleaseStringUTFChars(j_path, c_path); + + if (ret) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_mkdirs + * Signature: (JLjava/lang/String;I)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1mkdirs + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_path, jint j_mode) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_path; + int ret; + + CHECK_ARG_NULL(j_path, "@path is null", -1); + CHECK_MOUNTED(cmount, -1); + + c_path = env->GetStringUTFChars(j_path, NULL); + if (!c_path) { + cephThrowInternal(env, "failed to pin memory"); + return -1; + } + + ldout(cct, 10) << "jni: mkdirs: path " << c_path << " mode " << (int)j_mode << dendl; + + ret = ceph_mkdirs(cmount, c_path, (int)j_mode); + + ldout(cct, 10) << "jni: mkdirs: exit ret " << ret << dendl; + + env->ReleaseStringUTFChars(j_path, c_path); + + if (ret) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_rmdir + * Signature: (JLjava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1rmdir + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_path) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_path; + int ret; + + CHECK_ARG_NULL(j_path, "@path is null", -1); + CHECK_MOUNTED(cmount, -1); + + c_path = env->GetStringUTFChars(j_path, NULL); + if (!c_path) { + cephThrowInternal(env, "failed to pin memory"); + return -1; + } + + ldout(cct, 10) << "jni: rmdir: path " << c_path << dendl; + + ret = ceph_rmdir(cmount, c_path); + + ldout(cct, 10) << "jni: rmdir: exit ret " << ret << dendl; + + env->ReleaseStringUTFChars(j_path, c_path); + + if (ret) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_readlink + * Signature: (JLjava/lang/String;)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1readlink + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_path) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_path; + char *linkname; + struct ceph_statx stx; + jstring j_linkname; + + CHECK_ARG_NULL(j_path, "@path is null", NULL); + CHECK_MOUNTED(cmount, NULL); + + c_path = env->GetStringUTFChars(j_path, NULL); + if (!c_path) { + cephThrowInternal(env, "failed to pin memory"); + return NULL; + } + + for (;;) { + ldout(cct, 10) << "jni: readlink: lstatx " << c_path << dendl; + int ret = ceph_statx(cmount, c_path, &stx, CEPH_STATX_SIZE, + AT_SYMLINK_NOFOLLOW); + ldout(cct, 10) << "jni: readlink: lstat exit ret " << ret << dendl; + if (ret) { + env->ReleaseStringUTFChars(j_path, c_path); + handle_error(env, ret); + return NULL; + } + + linkname = new (std::nothrow) char[stx.stx_size + 1]; + if (!linkname) { + env->ReleaseStringUTFChars(j_path, c_path); + cephThrowOutOfMemory(env, "head allocation failed"); + return NULL; + } + + ldout(cct, 10) << "jni: readlink: size " << stx.stx_size << " path " << c_path << dendl; + + ret = ceph_readlink(cmount, c_path, linkname, stx.stx_size + 1); + + ldout(cct, 10) << "jni: readlink: exit ret " << ret << dendl; + + if (ret < 0) { + delete [] linkname; + env->ReleaseStringUTFChars(j_path, c_path); + handle_error(env, ret); + return NULL; + } + + /* re-stat and try again */ + if (ret > (int)stx.stx_size) { + delete [] linkname; + continue; + } + + linkname[ret] = '\0'; + break; + } + + env->ReleaseStringUTFChars(j_path, c_path); + + j_linkname = env->NewStringUTF(linkname); + delete [] linkname; + + return j_linkname; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_symlink + * Signature: (JLjava/lang/String;Ljava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1symlink + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_oldpath, jstring j_newpath) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_oldpath, *c_newpath; + int ret; + + CHECK_ARG_NULL(j_oldpath, "@oldpath is null", -1); + CHECK_ARG_NULL(j_newpath, "@newpath is null", -1); + CHECK_MOUNTED(cmount, -1); + + c_oldpath = env->GetStringUTFChars(j_oldpath, NULL); + if (!c_oldpath) { + cephThrowInternal(env, "failed to pin memory"); + return -1; + } + + c_newpath = env->GetStringUTFChars(j_newpath, NULL); + if (!c_newpath) { + env->ReleaseStringUTFChars(j_oldpath, c_oldpath); + cephThrowInternal(env, "failed to pin memory"); + return -1; + } + + ldout(cct, 10) << "jni: symlink: oldpath " << c_oldpath << + " newpath " << c_newpath << dendl; + + ret = ceph_symlink(cmount, c_oldpath, c_newpath); + + ldout(cct, 10) << "jni: symlink: exit ret " << ret << dendl; + + env->ReleaseStringUTFChars(j_oldpath, c_oldpath); + env->ReleaseStringUTFChars(j_newpath, c_newpath); + + if (ret) + handle_error(env, ret); + + return ret; +} + +#define CEPH_J_CEPHSTAT_MASK (CEPH_STATX_UID|CEPH_STATX_GID|CEPH_STATX_SIZE|CEPH_STATX_BLOCKS|CEPH_STATX_MTIME|CEPH_STATX_ATIME) + +static void fill_cephstat(JNIEnv *env, jobject j_cephstat, struct ceph_statx *stx) +{ + env->SetIntField(j_cephstat, cephstat_mode_fid, stx->stx_mode); + env->SetIntField(j_cephstat, cephstat_uid_fid, stx->stx_uid); + env->SetIntField(j_cephstat, cephstat_gid_fid, stx->stx_gid); + env->SetLongField(j_cephstat, cephstat_size_fid, stx->stx_size); + env->SetLongField(j_cephstat, cephstat_blksize_fid, stx->stx_blksize); + env->SetLongField(j_cephstat, cephstat_blocks_fid, stx->stx_blocks); + + long long time = stx->stx_mtime.tv_sec; + time *= 1000; + time += stx->stx_mtime.tv_nsec / 1000000; + env->SetLongField(j_cephstat, cephstat_m_time_fid, time); + + time = stx->stx_atime.tv_sec; + time *= 1000; + time += stx->stx_atime.tv_nsec / 1000000; + env->SetLongField(j_cephstat, cephstat_a_time_fid, time); + + env->SetBooleanField(j_cephstat, cephstat_is_file_fid, + S_ISREG(stx->stx_mode) ? JNI_TRUE : JNI_FALSE); + + env->SetBooleanField(j_cephstat, cephstat_is_directory_fid, + S_ISDIR(stx->stx_mode) ? JNI_TRUE : JNI_FALSE); + + env->SetBooleanField(j_cephstat, cephstat_is_symlink_fid, + S_ISLNK(stx->stx_mode) ? JNI_TRUE : JNI_FALSE); +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_lstat + * Signature: (JLjava/lang/String;Lcom/ceph/fs/CephStat;)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1lstat + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_path, jobject j_cephstat) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_path; + struct ceph_statx stx; + int ret; + + CHECK_ARG_NULL(j_path, "@path is null", -1); + CHECK_ARG_NULL(j_cephstat, "@stat is null", -1); + CHECK_MOUNTED(cmount, -1); + + c_path = env->GetStringUTFChars(j_path, NULL); + if (!c_path) { + cephThrowInternal(env, "Failed to pin memory"); + return -1; + } + + ldout(cct, 10) << "jni: lstat: path " << c_path << dendl; + + ret = ceph_statx(cmount, c_path, &stx, CEPH_J_CEPHSTAT_MASK, AT_SYMLINK_NOFOLLOW); + + ldout(cct, 10) << "jni: lstat exit ret " << ret << dendl; + + env->ReleaseStringUTFChars(j_path, c_path); + + if (ret) { + handle_error(env, ret); + return ret; + } + + fill_cephstat(env, j_cephstat, &stx); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_stat + * Signature: (JLjava/lang/String;Lcom/ceph/fs/CephStat;)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1stat + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_path, jobject j_cephstat) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_path; + struct ceph_statx stx; + int ret; + + CHECK_ARG_NULL(j_path, "@path is null", -1); + CHECK_ARG_NULL(j_cephstat, "@stat is null", -1); + CHECK_MOUNTED(cmount, -1); + + c_path = env->GetStringUTFChars(j_path, NULL); + if (!c_path) { + cephThrowInternal(env, "Failed to pin memory"); + return -1; + } + + ldout(cct, 10) << "jni: stat: path " << c_path << dendl; + + ret = ceph_statx(cmount, c_path, &stx, CEPH_J_CEPHSTAT_MASK, 0); + + ldout(cct, 10) << "jni: stat exit ret " << ret << dendl; + + env->ReleaseStringUTFChars(j_path, c_path); + + if (ret) { + handle_error(env, ret); + return ret; + } + + fill_cephstat(env, j_cephstat, &stx); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_setattr + * Signature: (JLjava/lang/String;Lcom/ceph/fs/CephStat;I)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1setattr + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_path, jobject j_cephstat, jint j_mask) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_path; + struct ceph_statx stx; + int ret, mask = fixup_attr_mask(j_mask); + + CHECK_ARG_NULL(j_path, "@path is null", -1); + CHECK_ARG_NULL(j_cephstat, "@stat is null", -1); + CHECK_MOUNTED(cmount, -1); + + c_path = env->GetStringUTFChars(j_path, NULL); + if (!c_path) { + cephThrowInternal(env, "Failed to pin memory"); + return -1; + } + + memset(&stx, 0, sizeof(stx)); + + stx.stx_mode = env->GetIntField(j_cephstat, cephstat_mode_fid); + stx.stx_uid = env->GetIntField(j_cephstat, cephstat_uid_fid); + stx.stx_gid = env->GetIntField(j_cephstat, cephstat_gid_fid); + long mtime_msec = env->GetLongField(j_cephstat, cephstat_m_time_fid); + long atime_msec = env->GetLongField(j_cephstat, cephstat_a_time_fid); + stx.stx_mtime.tv_sec = mtime_msec / 1000; + stx.stx_mtime.tv_nsec = (mtime_msec % 1000) * 1000000; + stx.stx_atime.tv_sec = atime_msec / 1000; + stx.stx_atime.tv_nsec = (atime_msec % 1000) * 1000000; + + ldout(cct, 10) << "jni: setattr: path " << c_path << " mask " << mask << dendl; + + ret = ceph_setattrx(cmount, c_path, &stx, mask, 0); + + ldout(cct, 10) << "jni: setattr: exit ret " << ret << dendl; + + env->ReleaseStringUTFChars(j_path, c_path); + + if (ret) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_chmod + * Signature: (JLjava/lang/String;I)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1chmod + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_path, jint j_mode) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_path; + int ret; + + CHECK_ARG_NULL(j_path, "@path is null", -1); + CHECK_MOUNTED(cmount, -1); + + c_path = env->GetStringUTFChars(j_path, NULL); + if (!c_path) { + cephThrowInternal(env, "Failed to pin memory"); + return -1; + } + + ldout(cct, 10) << "jni: chmod: path " << c_path << " mode " << (int)j_mode << dendl; + + ret = ceph_chmod(cmount, c_path, (int)j_mode); + + ldout(cct, 10) << "jni: chmod: exit ret " << ret << dendl; + + env->ReleaseStringUTFChars(j_path, c_path); + + if (ret) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_fchmod + * Signature: (JII)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1fchmod + (JNIEnv *env, jclass clz, jlong j_mntp, jint j_fd, jint j_mode) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + int ret; + + CHECK_MOUNTED(cmount, -1); + + ldout(cct, 10) << "jni: fchmod: fd " << (int)j_fd << " mode " << (int)j_mode << dendl; + + ret = ceph_fchmod(cmount, (int)j_fd, (int)j_mode); + + ldout(cct, 10) << "jni: fchmod: exit ret " << ret << dendl; + + if (ret) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_truncate + * Signature: (JLjava/lang/String;J)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1truncate + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_path, jlong j_size) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_path; + int ret; + + CHECK_ARG_NULL(j_path, "@path is null", -1); + CHECK_MOUNTED(cmount, -1); + + c_path = env->GetStringUTFChars(j_path, NULL); + if (!c_path) { + cephThrowInternal(env, "Failed to pin memory"); + return -1; + } + + ldout(cct, 10) << "jni: truncate: path " << c_path << " size " << (loff_t)j_size << dendl; + + ret = ceph_truncate(cmount, c_path, (loff_t)j_size); + + ldout(cct, 10) << "jni: truncate: exit ret " << ret << dendl; + + env->ReleaseStringUTFChars(j_path, c_path); + + if (ret) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_open + * Signature: (JLjava/lang/String;II)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1open + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_path, jint j_flags, jint j_mode) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_path; + int ret, flags = fixup_open_flags(j_flags); + + CHECK_ARG_NULL(j_path, "@path is null", -1); + CHECK_MOUNTED(cmount, -1); + + c_path = env->GetStringUTFChars(j_path, NULL); + if (!c_path) { + cephThrowInternal(env, "Failed to pin memory"); + return -1; + } + + ldout(cct, 10) << "jni: open: path " << c_path << " flags " << flags + << " mode " << (int)j_mode << dendl; + + ret = ceph_open(cmount, c_path, flags, (int)j_mode); + + ldout(cct, 10) << "jni: open: exit ret " << ret << dendl; + + env->ReleaseStringUTFChars(j_path, c_path); + + if (ret < 0) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_open_layout + * Signature: (JLjava/lang/String;IIIIILjava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1open_1layout + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_path, jint j_flags, jint j_mode, + jint stripe_unit, jint stripe_count, jint object_size, jstring j_data_pool) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_path, *c_data_pool = NULL; + int ret, flags = fixup_open_flags(j_flags); + + CHECK_ARG_NULL(j_path, "@path is null", -1); + CHECK_MOUNTED(cmount, -1); + + c_path = env->GetStringUTFChars(j_path, NULL); + if (!c_path) { + cephThrowInternal(env, "Failed to pin memory"); + return -1; + } + + if (j_data_pool) { + c_data_pool = env->GetStringUTFChars(j_data_pool, NULL); + if (!c_data_pool) { + env->ReleaseStringUTFChars(j_path, c_path); + cephThrowInternal(env, "Failed to pin memory"); + return -1; + } + } + + ldout(cct, 10) << "jni: open_layout: path " << c_path << " flags " << flags + << " mode " << (int)j_mode << " stripe_unit " << stripe_unit + << " stripe_count " << stripe_count << " object_size " << object_size + << " data_pool " << (c_data_pool ? c_data_pool : "<NULL>") << dendl; + + ret = ceph_open_layout(cmount, c_path, flags, (int)j_mode, + (int)stripe_unit, (int)stripe_count, (int)object_size, c_data_pool); + + ldout(cct, 10) << "jni: open_layout: exit ret " << ret << dendl; + + env->ReleaseStringUTFChars(j_path, c_path); + if (j_data_pool) + env->ReleaseStringUTFChars(j_data_pool, c_data_pool); + + if (ret < 0) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_close + * Signature: (JI)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1close + (JNIEnv *env, jclass clz, jlong j_mntp, jint j_fd) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + int ret; + + CHECK_MOUNTED(cmount, -1); + + ldout(cct, 10) << "jni: close: fd " << (int)j_fd << dendl; + + ret = ceph_close(cmount, (int)j_fd); + + ldout(cct, 10) << "jni: close: ret " << ret << dendl; + + if (ret) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_lseek + * Signature: (JIJI)J + */ +JNIEXPORT jlong JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1lseek + (JNIEnv *env, jclass clz, jlong j_mntp, jint j_fd, jlong j_offset, jint j_whence) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + int whence; + jlong ret; + + CHECK_MOUNTED(cmount, -1); + + switch (j_whence) { + case JAVA_SEEK_SET: + whence = SEEK_SET; + break; + case JAVA_SEEK_CUR: + whence = SEEK_CUR; + break; + case JAVA_SEEK_END: + whence = SEEK_END; + break; + default: + cephThrowIllegalArg(env, "Unknown whence value"); + return -1; + } + + ldout(cct, 10) << "jni: lseek: fd " << (int)j_fd << " offset " + << (long)j_offset << " whence " << whence << dendl; + + ret = ceph_lseek(cmount, (int)j_fd, (long)j_offset, whence); + + ldout(cct, 10) << "jni: lseek: exit ret " << ret << dendl; + + if (ret < 0) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_read + * Signature: (JI[BJJ)J + */ +JNIEXPORT jlong JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1read + (JNIEnv *env, jclass clz, jlong j_mntp, jint j_fd, jbyteArray j_buf, jlong j_size, jlong j_offset) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + jsize buf_size; + jbyte *c_buf; + long ret; + + CHECK_ARG_NULL(j_buf, "@buf is null", -1); + CHECK_ARG_BOUNDS(j_size < 0, "@size is negative", -1); + CHECK_MOUNTED(cmount, -1); + + buf_size = env->GetArrayLength(j_buf); + CHECK_ARG_BOUNDS(j_size > buf_size, "@size > @buf.length", -1); + + c_buf = env->GetByteArrayElements(j_buf, NULL); + if (!c_buf) { + cephThrowInternal(env, "failed to pin memory"); + return -1; + } + + ldout(cct, 10) << "jni: read: fd " << (int)j_fd << " len " << (long)j_size << + " offset " << (long)j_offset << dendl; + + ret = ceph_read(cmount, (int)j_fd, (char*)c_buf, (long)j_size, (long)j_offset); + + ldout(cct, 10) << "jni: read: exit ret " << ret << dendl; + + if (ret < 0) + handle_error(env, (int)ret); + else + env->ReleaseByteArrayElements(j_buf, c_buf, 0); + + return (jlong)ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_write + * Signature: (JI[BJJ)J + */ +JNIEXPORT jlong JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1write + (JNIEnv *env, jclass clz, jlong j_mntp, jint j_fd, jbyteArray j_buf, jlong j_size, jlong j_offset) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + jsize buf_size; + jbyte *c_buf; + long ret; + + CHECK_ARG_NULL(j_buf, "@buf is null", -1); + CHECK_ARG_BOUNDS(j_size < 0, "@size is negative", -1); + CHECK_MOUNTED(cmount, -1); + + buf_size = env->GetArrayLength(j_buf); + CHECK_ARG_BOUNDS(j_size > buf_size, "@size > @buf.length", -1); + + c_buf = env->GetByteArrayElements(j_buf, NULL); + if (!c_buf) { + cephThrowInternal(env, "failed to pin memory"); + return -1; + } + + ldout(cct, 10) << "jni: write: fd " << (int)j_fd << " len " << (long)j_size << + " offset " << (long)j_offset << dendl; + + ret = ceph_write(cmount, (int)j_fd, (char*)c_buf, (long)j_size, (long)j_offset); + + ldout(cct, 10) << "jni: write: exit ret " << ret << dendl; + + if (ret < 0) + handle_error(env, (int)ret); + else + env->ReleaseByteArrayElements(j_buf, c_buf, JNI_ABORT); + + return ret; +} + + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_ftruncate + * Signature: (JIJ)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1ftruncate + (JNIEnv *env, jclass clz, jlong j_mntp, jint j_fd, jlong j_size) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + int ret; + + CHECK_MOUNTED(cmount, -1); + + ldout(cct, 10) << "jni: ftruncate: fd " << (int)j_fd << + " size " << (loff_t)j_size << dendl; + + ret = ceph_ftruncate(cmount, (int)j_fd, (loff_t)j_size); + + ldout(cct, 10) << "jni: ftruncate: exit ret " << ret << dendl; + + if (ret) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_fsync + * Signature: (JIZ)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1fsync + (JNIEnv *env, jclass clz, jlong j_mntp, jint j_fd, jboolean j_dataonly) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + int ret; + + ldout(cct, 10) << "jni: fsync: fd " << (int)j_fd << + " dataonly " << (j_dataonly ? 1 : 0) << dendl; + + ret = ceph_fsync(cmount, (int)j_fd, j_dataonly ? 1 : 0); + + ldout(cct, 10) << "jni: fsync: exit ret " << ret << dendl; + + if (ret) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_flock + * Signature: (JIZ)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1flock + (JNIEnv *env, jclass clz, jlong j_mntp, jint j_fd, jint j_operation, jlong j_owner) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + int ret; + + ldout(cct, 10) << "jni: flock: fd " << (int)j_fd << + " operation " << j_operation << " owner " << j_owner << dendl; + + int operation = 0; + +#define MAP_FLOCK_FLAG(JNI_MASK, NATIVE_MASK) do { \ + if ((j_operation & JNI_MASK) != 0) { \ + operation |= NATIVE_MASK; \ + j_operation &= ~JNI_MASK; \ + } \ + } while(0) + MAP_FLOCK_FLAG(JAVA_LOCK_SH, LOCK_SH); + MAP_FLOCK_FLAG(JAVA_LOCK_EX, LOCK_EX); + MAP_FLOCK_FLAG(JAVA_LOCK_NB, LOCK_NB); + MAP_FLOCK_FLAG(JAVA_LOCK_UN, LOCK_UN); + if (j_operation != 0) { + cephThrowIllegalArg(env, "flock flags"); + return -EINVAL; + } +#undef MAP_FLOCK_FLAG + + ret = ceph_flock(cmount, (int)j_fd, operation, (uint64_t) j_owner); + + ldout(cct, 10) << "jni: flock: exit ret " << ret << dendl; + + if (ret) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_fstat + * Signature: (JILcom/ceph/fs/CephStat;)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1fstat + (JNIEnv *env, jclass clz, jlong j_mntp, jint j_fd, jobject j_cephstat) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + struct ceph_statx stx; + int ret; + + CHECK_ARG_NULL(j_cephstat, "@stat is null", -1); + CHECK_MOUNTED(cmount, -1); + + ldout(cct, 10) << "jni: fstat: fd " << (int)j_fd << dendl; + + ret = ceph_fstatx(cmount, (int)j_fd, &stx, CEPH_J_CEPHSTAT_MASK, 0); + + ldout(cct, 10) << "jni: fstat exit ret " << ret << dendl; + + if (ret) { + handle_error(env, ret); + return ret; + } + + fill_cephstat(env, j_cephstat, &stx); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_sync_fs + * Signature: (J)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1sync_1fs + (JNIEnv *env, jclass clz, jlong j_mntp) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + int ret; + + ldout(cct, 10) << "jni: sync_fs: enter" << dendl; + + ret = ceph_sync_fs(cmount); + + ldout(cct, 10) << "jni: sync_fs: exit ret " << ret << dendl; + + if (ret) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_getxattr + * Signature: (JLjava/lang/String;Ljava/lang/String;[B)J + */ +JNIEXPORT jlong JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1getxattr + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_path, jstring j_name, jbyteArray j_buf) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_path; + const char *c_name; + jsize buf_size; + jbyte *c_buf = NULL; /* please gcc with goto */ + long ret; + + CHECK_ARG_NULL(j_path, "@path is null", -1); + CHECK_ARG_NULL(j_name, "@name is null", -1); + CHECK_MOUNTED(cmount, -1); + + c_path = env->GetStringUTFChars(j_path, NULL); + if (!c_path) { + cephThrowInternal(env, "Failed to pin memory"); + return -1; + } + + c_name = env->GetStringUTFChars(j_name, NULL); + if (!c_name) { + env->ReleaseStringUTFChars(j_path, c_path); + cephThrowInternal(env, "Failed to pin memory"); + return -1; + } + + /* just lookup the size if buf is null */ + if (!j_buf) { + buf_size = 0; + goto do_getxattr; + } + + c_buf = env->GetByteArrayElements(j_buf, NULL); + if (!c_buf) { + env->ReleaseStringUTFChars(j_path, c_path); + env->ReleaseStringUTFChars(j_name, c_name); + cephThrowInternal(env, "failed to pin memory"); + return -1; + } + + buf_size = env->GetArrayLength(j_buf); + +do_getxattr: + + ldout(cct, 10) << "jni: getxattr: path " << c_path << " name " << c_name << + " len " << buf_size << dendl; + + ret = ceph_getxattr(cmount, c_path, c_name, c_buf, buf_size); + if (ret == -ERANGE) + ret = ceph_getxattr(cmount, c_path, c_name, c_buf, 0); + + ldout(cct, 10) << "jni: getxattr: exit ret " << ret << dendl; + + env->ReleaseStringUTFChars(j_path, c_path); + env->ReleaseStringUTFChars(j_name, c_name); + if (j_buf) + env->ReleaseByteArrayElements(j_buf, c_buf, 0); + + if (ret < 0) + handle_error(env, (int)ret); + + return (jlong)ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_lgetxattr + * Signature: (JLjava/lang/String;Ljava/lang/String;[B)I + */ +JNIEXPORT jlong JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1lgetxattr + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_path, jstring j_name, jbyteArray j_buf) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_path; + const char *c_name; + jsize buf_size; + jbyte *c_buf = NULL; /* please gcc with goto */ + long ret; + + CHECK_ARG_NULL(j_path, "@path is null", -1); + CHECK_ARG_NULL(j_name, "@name is null", -1); + CHECK_MOUNTED(cmount, -1); + + c_path = env->GetStringUTFChars(j_path, NULL); + if (!c_path) { + cephThrowInternal(env, "Failed to pin memory"); + return -1; + } + + c_name = env->GetStringUTFChars(j_name, NULL); + if (!c_name) { + env->ReleaseStringUTFChars(j_path, c_path); + cephThrowInternal(env, "Failed to pin memory"); + return -1; + } + + /* just lookup the size if buf is null */ + if (!j_buf) { + buf_size = 0; + goto do_lgetxattr; + } + + c_buf = env->GetByteArrayElements(j_buf, NULL); + if (!c_buf) { + env->ReleaseStringUTFChars(j_path, c_path); + env->ReleaseStringUTFChars(j_name, c_name); + cephThrowInternal(env, "failed to pin memory"); + return -1; + } + + buf_size = env->GetArrayLength(j_buf); + +do_lgetxattr: + + ldout(cct, 10) << "jni: lgetxattr: path " << c_path << " name " << c_name << + " len " << buf_size << dendl; + + ret = ceph_lgetxattr(cmount, c_path, c_name, c_buf, buf_size); + if (ret == -ERANGE) + ret = ceph_lgetxattr(cmount, c_path, c_name, c_buf, 0); + + ldout(cct, 10) << "jni: lgetxattr: exit ret " << ret << dendl; + + env->ReleaseStringUTFChars(j_path, c_path); + env->ReleaseStringUTFChars(j_name, c_name); + if (j_buf) + env->ReleaseByteArrayElements(j_buf, c_buf, 0); + + if (ret < 0) + handle_error(env, (int)ret); + + return (jlong)ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_listxattr + * Signature: (JLjava/lang/String;)[Ljava/lang/String; + */ +JNIEXPORT jobjectArray JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1listxattr + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_path) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + jobjectArray xattrlist; + const char *c_path; + string *ent; + jstring name; + list<string>::iterator it; + list<string> contents; + int ret, buflen, bufpos, i; + char *buf; + + CHECK_ARG_NULL(j_path, "@path is null", NULL); + CHECK_MOUNTED(cmount, NULL); + + c_path = env->GetStringUTFChars(j_path, NULL); + if (!c_path) { + cephThrowInternal(env, "Failed to pin memory"); + return NULL; + } + + buflen = 1024; + buf = new (std::nothrow) char[buflen]; + if (!buf) { + cephThrowOutOfMemory(env, "head allocation failed"); + goto out; + } + + while (1) { + ldout(cct, 10) << "jni: listxattr: path " << c_path << " len " << buflen << dendl; + ret = ceph_listxattr(cmount, c_path, buf, buflen); + if (ret == -ERANGE) { + delete [] buf; + buflen *= 2; + buf = new (std::nothrow) char[buflen]; + if (!buf) { + cephThrowOutOfMemory(env, "heap allocation failed"); + goto out; + } + continue; + } + break; + } + + ldout(cct, 10) << "jni: listxattr: ret " << ret << dendl; + + if (ret < 0) { + delete [] buf; + handle_error(env, ret); + goto out; + } + + bufpos = 0; + while (bufpos < ret) { + ent = new (std::nothrow) string(buf + bufpos); + if (!ent) { + delete [] buf; + cephThrowOutOfMemory(env, "heap allocation failed"); + goto out; + } + contents.push_back(*ent); + bufpos += ent->size() + 1; + delete ent; + } + + delete [] buf; + + xattrlist = env->NewObjectArray(contents.size(), env->FindClass("java/lang/String"), NULL); + if (!xattrlist) + goto out; + + for (i = 0, it = contents.begin(); it != contents.end(); ++it) { + name = env->NewStringUTF(it->c_str()); + if (!name) + goto out; + env->SetObjectArrayElement(xattrlist, i++, name); + if (env->ExceptionOccurred()) + goto out; + env->DeleteLocalRef(name); + } + + env->ReleaseStringUTFChars(j_path, c_path); + return xattrlist; + +out: + env->ReleaseStringUTFChars(j_path, c_path); + return NULL; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_llistxattr + * Signature: (JLjava/lang/String;)[Ljava/lang/String; + */ +JNIEXPORT jobjectArray JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1llistxattr + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_path) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + jobjectArray xattrlist; + const char *c_path; + string *ent; + jstring name; + list<string>::iterator it; + list<string> contents; + int ret, buflen, bufpos, i; + char *buf; + + CHECK_ARG_NULL(j_path, "@path is null", NULL); + CHECK_MOUNTED(cmount, NULL); + + c_path = env->GetStringUTFChars(j_path, NULL); + if (!c_path) { + cephThrowInternal(env, "Failed to pin memory"); + return NULL; + } + + buflen = 1024; + buf = new (std::nothrow) char[buflen]; + if (!buf) { + cephThrowOutOfMemory(env, "head allocation failed"); + goto out; + } + + while (1) { + ldout(cct, 10) << "jni: llistxattr: path " << c_path << " len " << buflen << dendl; + ret = ceph_llistxattr(cmount, c_path, buf, buflen); + if (ret == -ERANGE) { + delete [] buf; + buflen *= 2; + buf = new (std::nothrow) char[buflen]; + if (!buf) { + cephThrowOutOfMemory(env, "heap allocation failed"); + goto out; + } + continue; + } + break; + } + + ldout(cct, 10) << "jni: llistxattr: ret " << ret << dendl; + + if (ret < 0) { + delete [] buf; + handle_error(env, ret); + goto out; + } + + bufpos = 0; + while (bufpos < ret) { + ent = new (std::nothrow) string(buf + bufpos); + if (!ent) { + delete [] buf; + cephThrowOutOfMemory(env, "heap allocation failed"); + goto out; + } + contents.push_back(*ent); + bufpos += ent->size() + 1; + delete ent; + } + + delete [] buf; + + xattrlist = env->NewObjectArray(contents.size(), env->FindClass("java/lang/String"), NULL); + if (!xattrlist) + goto out; + + for (i = 0, it = contents.begin(); it != contents.end(); ++it) { + name = env->NewStringUTF(it->c_str()); + if (!name) + goto out; + env->SetObjectArrayElement(xattrlist, i++, name); + if (env->ExceptionOccurred()) + goto out; + env->DeleteLocalRef(name); + } + + env->ReleaseStringUTFChars(j_path, c_path); + return xattrlist; + +out: + env->ReleaseStringUTFChars(j_path, c_path); + return NULL; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_removexattr + * Signature: (JLjava/lang/String;Ljava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1removexattr + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_path, jstring j_name) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_path; + const char *c_name; + int ret; + + CHECK_ARG_NULL(j_path, "@path is null", -1); + CHECK_ARG_NULL(j_name, "@name is null", -1); + CHECK_MOUNTED(cmount, -1); + + c_path = env->GetStringUTFChars(j_path, NULL); + if (!c_path) { + cephThrowInternal(env, "Failed to pin memory"); + return -1; + } + + c_name = env->GetStringUTFChars(j_name, NULL); + if (!c_name) { + env->ReleaseStringUTFChars(j_path, c_path); + cephThrowInternal(env, "Failed to pin memory"); + return -1; + } + + ldout(cct, 10) << "jni: removexattr: path " << c_path << " name " << c_name << dendl; + + ret = ceph_removexattr(cmount, c_path, c_name); + + ldout(cct, 10) << "jni: removexattr: exit ret " << ret << dendl; + + env->ReleaseStringUTFChars(j_path, c_path); + env->ReleaseStringUTFChars(j_name, c_name); + + if (ret) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_lremovexattr + * Signature: (JLjava/lang/String;Ljava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1lremovexattr + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_path, jstring j_name) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_path; + const char *c_name; + int ret; + + CHECK_ARG_NULL(j_path, "@path is null", -1); + CHECK_ARG_NULL(j_name, "@name is null", -1); + CHECK_MOUNTED(cmount, -1); + + c_path = env->GetStringUTFChars(j_path, NULL); + if (!c_path) { + cephThrowInternal(env, "Failed to pin memory"); + return -1; + } + + c_name = env->GetStringUTFChars(j_name, NULL); + if (!c_name) { + env->ReleaseStringUTFChars(j_path, c_path); + cephThrowInternal(env, "Failed to pin memory"); + return -1; + } + + ldout(cct, 10) << "jni: lremovexattr: path " << c_path << " name " << c_name << dendl; + + ret = ceph_lremovexattr(cmount, c_path, c_name); + + ldout(cct, 10) << "jni: lremovexattr: exit ret " << ret << dendl; + + env->ReleaseStringUTFChars(j_path, c_path); + env->ReleaseStringUTFChars(j_name, c_name); + + if (ret) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_setxattr + * Signature: (JLjava/lang/String;Ljava/lang/String;[BJI)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1setxattr + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_path, jstring j_name, + jbyteArray j_buf, jlong j_size, jint j_flags) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_path; + const char *c_name; + jsize buf_size; + jbyte *c_buf; + int ret, flags; + + CHECK_ARG_NULL(j_path, "@path is null", -1); + CHECK_ARG_NULL(j_name, "@name is null", -1); + CHECK_ARG_NULL(j_buf, "@buf is null", -1); + CHECK_ARG_BOUNDS(j_size < 0, "@size is negative", -1); + CHECK_MOUNTED(cmount, -1); + + buf_size = env->GetArrayLength(j_buf); + CHECK_ARG_BOUNDS(j_size > buf_size, "@size > @buf.length", -1); + + c_path = env->GetStringUTFChars(j_path, NULL); + if (!c_path) { + cephThrowInternal(env, "Failed to pin memory"); + return -1; + } + + c_name = env->GetStringUTFChars(j_name, NULL); + if (!c_name) { + env->ReleaseStringUTFChars(j_path, c_path); + cephThrowInternal(env, "Failed to pin memory"); + return -1; + } + + c_buf = env->GetByteArrayElements(j_buf, NULL); + if (!c_buf) { + env->ReleaseStringUTFChars(j_path, c_path); + env->ReleaseStringUTFChars(j_name, c_name); + cephThrowInternal(env, "failed to pin memory"); + return -1; + } + + switch (j_flags) { + case JAVA_XATTR_CREATE: + flags = CEPH_XATTR_CREATE; + break; + case JAVA_XATTR_REPLACE: + flags = CEPH_XATTR_REPLACE; + break; + case JAVA_XATTR_NONE: + flags = 0; + break; + default: + env->ReleaseStringUTFChars(j_path, c_path); + env->ReleaseStringUTFChars(j_name, c_name); + env->ReleaseByteArrayElements(j_buf, c_buf, JNI_ABORT); + cephThrowIllegalArg(env, "setxattr flag"); + return -1; + } + + ldout(cct, 10) << "jni: setxattr: path " << c_path << " name " << c_name + << " len " << j_size << " flags " << flags << dendl; + + ret = ceph_setxattr(cmount, c_path, c_name, c_buf, j_size, flags); + + ldout(cct, 10) << "jni: setxattr: exit ret " << ret << dendl; + + env->ReleaseStringUTFChars(j_path, c_path); + env->ReleaseStringUTFChars(j_name, c_name); + env->ReleaseByteArrayElements(j_buf, c_buf, JNI_ABORT); + + if (ret) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_lsetxattr + * Signature: (JLjava/lang/String;Ljava/lang/String;[BJI)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1lsetxattr + (JNIEnv *env, jclass clz, jlong j_mntp, jstring j_path, jstring j_name, + jbyteArray j_buf, jlong j_size, jint j_flags) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_path; + const char *c_name; + jsize buf_size; + jbyte *c_buf; + int ret, flags; + + CHECK_ARG_NULL(j_path, "@path is null", -1); + CHECK_ARG_NULL(j_name, "@name is null", -1); + CHECK_ARG_NULL(j_buf, "@buf is null", -1); + CHECK_ARG_BOUNDS(j_size < 0, "@size is negative", -1); + CHECK_MOUNTED(cmount, -1); + + buf_size = env->GetArrayLength(j_buf); + CHECK_ARG_BOUNDS(j_size > buf_size, "@size > @buf.length", -1); + + c_path = env->GetStringUTFChars(j_path, NULL); + if (!c_path) { + cephThrowInternal(env, "Failed to pin memory"); + return -1; + } + + c_name = env->GetStringUTFChars(j_name, NULL); + if (!c_name) { + env->ReleaseStringUTFChars(j_path, c_path); + cephThrowInternal(env, "Failed to pin memory"); + return -1; + } + + c_buf = env->GetByteArrayElements(j_buf, NULL); + if (!c_buf) { + env->ReleaseStringUTFChars(j_path, c_path); + env->ReleaseStringUTFChars(j_name, c_name); + cephThrowInternal(env, "failed to pin memory"); + return -1; + } + + switch (j_flags) { + case JAVA_XATTR_CREATE: + flags = CEPH_XATTR_CREATE; + break; + case JAVA_XATTR_REPLACE: + flags = CEPH_XATTR_REPLACE; + break; + case JAVA_XATTR_NONE: + flags = 0; + break; + default: + env->ReleaseStringUTFChars(j_path, c_path); + env->ReleaseStringUTFChars(j_name, c_name); + env->ReleaseByteArrayElements(j_buf, c_buf, JNI_ABORT); + cephThrowIllegalArg(env, "lsetxattr flag"); + return -1; + } + + ldout(cct, 10) << "jni: lsetxattr: path " << c_path << " name " << c_name + << " len " << j_size << " flags " << flags << dendl; + + ret = ceph_lsetxattr(cmount, c_path, c_name, c_buf, j_size, flags); + + ldout(cct, 10) << "jni: lsetxattr: exit ret " << ret << dendl; + + env->ReleaseStringUTFChars(j_path, c_path); + env->ReleaseStringUTFChars(j_name, c_name); + env->ReleaseByteArrayElements(j_buf, c_buf, JNI_ABORT); + + if (ret) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_get_file_stripe_unit + * Signature: (JI)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1get_1file_1stripe_1unit + (JNIEnv *env, jclass clz, jlong j_mntp, jint j_fd) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + int ret; + + CHECK_MOUNTED(cmount, -1); + + ldout(cct, 10) << "jni: get_file_stripe_unit: fd " << (int)j_fd << dendl; + + ret = ceph_get_file_stripe_unit(cmount, (int)j_fd); + + ldout(cct, 10) << "jni: get_file_stripe_unit: exit ret " << ret << dendl; + + if (ret < 0) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_get_file_replication + * Signature: (JI)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1get_1file_1replication + (JNIEnv *env, jclass clz, jlong j_mntp, jint j_fd) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + int ret; + + CHECK_MOUNTED(cmount, -1); + + ldout(cct, 10) << "jni: get_file_replication: fd " << (int)j_fd << dendl; + + ret = ceph_get_file_replication(cmount, (int)j_fd); + + ldout(cct, 10) << "jni: get_file_replication: exit ret " << ret << dendl; + + if (ret < 0) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_get_file_pool_name + * Signature: (JI)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1get_1file_1pool_1name + (JNIEnv *env, jclass clz, jlong j_mntp, jint j_fd) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + jstring pool = NULL; + int ret, buflen = 0; + char *buf = NULL; + + CHECK_MOUNTED(cmount, NULL); + + ldout(cct, 10) << "jni: get_file_pool_name: fd " << (int)j_fd << dendl; + + for (;;) { + /* get pool name length (len==0) */ + ret = ceph_get_file_pool_name(cmount, (int)j_fd, NULL, 0); + if (ret < 0) + break; + + /* allocate buffer */ + if (buf) + delete [] buf; + buflen = ret; + buf = new (std::nothrow) char[buflen+1]; /* +1 for '\0' */ + if (!buf) { + cephThrowOutOfMemory(env, "head allocation failed"); + goto out; + } + memset(buf, 0, (buflen+1)*sizeof(*buf)); + + /* handle zero-length pool name!? */ + if (buflen == 0) + break; + + /* fill buffer */ + ret = ceph_get_file_pool_name(cmount, (int)j_fd, buf, buflen); + if (ret == -ERANGE) /* size changed! */ + continue; + else + break; + } + + ldout(cct, 10) << "jni: get_file_pool_name: ret " << ret << dendl; + + if (ret < 0) + handle_error(env, ret); + else + pool = env->NewStringUTF(buf); + +out: + if (buf) + delete [] buf; + + return pool; +} + +/** + * Class: com_ceph_fs_CephMount + * Method: native_ceph_get_default_data_pool_name + * Signature: (J)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1get_1default_1data_1pool_1name + (JNIEnv *env, jclass clz, jlong j_mntp) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + jstring pool = NULL; + int ret, buflen = 0; + char *buf = NULL; + + CHECK_MOUNTED(cmount, NULL); + + ldout(cct, 10) << "jni: get_default_data_pool_name" << dendl; + + ret = ceph_get_default_data_pool_name(cmount, NULL, 0); + if (ret < 0) + goto out; + buflen = ret; + buf = new (std::nothrow) char[buflen+1]; /* +1 for '\0' */ + if (!buf) { + cephThrowOutOfMemory(env, "head allocation failed"); + goto out; + } + memset(buf, 0, (buflen+1)*sizeof(*buf)); + ret = ceph_get_default_data_pool_name(cmount, buf, buflen); + + ldout(cct, 10) << "jni: get_default_data_pool_name: ret " << ret << dendl; + + if (ret < 0) + handle_error(env, ret); + else + pool = env->NewStringUTF(buf); + +out: + if (buf) + delete [] buf; + + return pool; +} + + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_localize_reads + * Signature: (JZ)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1localize_1reads + (JNIEnv *env, jclass clz, jlong j_mntp, jboolean j_on) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + int ret, val = j_on ? 1 : 0; + + CHECK_MOUNTED(cmount, -1); + + ldout(cct, 10) << "jni: localize_reads: val " << val << dendl; + + ret = ceph_localize_reads(cmount, val); + + ldout(cct, 10) << "jni: localize_reads: exit ret " << ret << dendl; + + if (ret) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_get_stripe_unit_granularity + * Signature: (J)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1get_1stripe_1unit_1granularity + (JNIEnv *env, jclass clz, jlong j_mntp) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + int ret; + + CHECK_MOUNTED(cmount, -1); + + ldout(cct, 10) << "jni: get_stripe_unit_granularity" << dendl; + + ret = ceph_get_stripe_unit_granularity(cmount); + + ldout(cct, 10) << "jni: get_stripe_unit_granularity: exit ret " << ret << dendl; + + if (ret < 0) + handle_error(env, ret); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_get_pool_id + * Signature: (JLjava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1get_1pool_1id + (JNIEnv *env, jclass clz, jlong j_mntp, jstring jname) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + const char *c_name; + int ret; + + CHECK_MOUNTED(cmount, -1); + CHECK_ARG_NULL(jname, "@name is null", -1); + + c_name = env->GetStringUTFChars(jname, NULL); + if (!c_name) { + cephThrowInternal(env, "failed to pin memory"); + return -1; + } + + ldout(cct, 10) << "jni: get_pool_id: name " << c_name << dendl; + + ret = ceph_get_pool_id(cmount, c_name); + if (ret < 0) + handle_error(env, ret); + + ldout(cct, 10) << "jni: get_pool_id: ret " << ret << dendl; + + env->ReleaseStringUTFChars(jname, c_name); + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_get_pool_replication + * Signature: (JI)I + */ +JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1get_1pool_1replication + (JNIEnv *env, jclass clz, jlong j_mntp, jint jpoolid) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + int ret; + + CHECK_MOUNTED(cmount, -1); + + ldout(cct, 10) << "jni: get_pool_replication: poolid " << jpoolid << dendl; + + ret = ceph_get_pool_replication(cmount, jpoolid); + if (ret < 0) + handle_error(env, ret); + + ldout(cct, 10) << "jni: get_pool_replication: ret " << ret << dendl; + + return ret; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_get_file_extent_osds + * Signature: (JIJ)Lcom/ceph/fs/CephFileExtent; + */ +JNIEXPORT jobject JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1get_1file_1extent_1osds + (JNIEnv *env, jclass clz, jlong mntp, jint fd, jlong off) +{ + struct ceph_mount_info *cmount = get_ceph_mount(mntp); + CephContext *cct = ceph_get_mount_context(cmount); + jobject extent = NULL; + int ret, nosds, *osds = NULL; + jintArray osd_array; + loff_t len; + + CHECK_MOUNTED(cmount, NULL); + + ldout(cct, 10) << "jni: get_file_extent_osds: fd " << fd << " off " << off << dendl; + + for (;;) { + /* get pg size */ + ret = ceph_get_file_extent_osds(cmount, fd, off, NULL, NULL, 0); + if (ret < 0) + break; + + /* alloc osd id array */ + if (osds) + delete [] osds; + nosds = ret; + osds = new int[nosds]; + + /* get osd ids */ + ret = ceph_get_file_extent_osds(cmount, fd, off, &len, osds, nosds); + if (ret == -ERANGE) + continue; + else + break; + } + + ldout(cct, 10) << "jni: get_file_extent_osds: ret " << ret << dendl; + + if (ret < 0) { + handle_error(env, ret); + goto out; + } + + nosds = ret; + + osd_array = env->NewIntArray(nosds); + if (!osd_array) + goto out; + + env->SetIntArrayRegion(osd_array, 0, nosds, osds); + if (env->ExceptionOccurred()) + goto out; + + extent = env->NewObject(cephfileextent_cls, cephfileextent_ctor_fid, off, len, osd_array); + if (!extent) + goto out; + +out: + if (osds) + delete [] osds; + + return extent; +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_get_osd_crush_location + * Signature: (JI)[Ljava/lang/String; + */ +JNIEXPORT jobjectArray JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1get_1osd_1crush_1location + (JNIEnv *env, jclass clz, jlong j_mntp, jint osdid) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + jobjectArray path = NULL; + vector<string> str_path; + int ret, bufpos, buflen = 0; + char *buf = NULL; + + CHECK_MOUNTED(cmount, NULL); + + ldout(cct, 10) << "jni: osd loc: osd " << osdid << dendl; + + for (;;) { + /* get length of the location path */ + ret = ceph_get_osd_crush_location(cmount, osdid, NULL, 0); + if (ret < 0) + break; + + /* alloc path buffer */ + if (buf) + delete [] buf; + buflen = ret; + buf = new char[buflen+1]; + memset(buf, 0, buflen*sizeof(*buf)); + + /* empty path */ + if (buflen == 0) + break; + + /* get the path */ + ret = ceph_get_osd_crush_location(cmount, osdid, buf, buflen); + if (ret == -ERANGE) + continue; + else + break; + } + + ldout(cct, 10) << "jni: osd loc: osd " << osdid << " ret " << ret << dendl; + + if (ret < 0) { + handle_error(env, ret); + goto out; + } + + bufpos = 0; + while (bufpos < ret) { + string type(buf + bufpos); + bufpos += type.size() + 1; + string name(buf + bufpos); + bufpos += name.size() + 1; + str_path.push_back(type); + str_path.push_back(name); + } + + path = env->NewObjectArray(str_path.size(), env->FindClass("java/lang/String"), NULL); + if (!path) + goto out; + + for (unsigned i = 0; i < str_path.size(); i++) { + jstring ent = env->NewStringUTF(str_path[i].c_str()); + if (!ent) + goto out; + env->SetObjectArrayElement(path, i, ent); + if (env->ExceptionOccurred()) + goto out; + env->DeleteLocalRef(ent); + } + +out: + if (buf) + delete [] buf; + + return path; +} + +/* + * sockaddrToInetAddress uses with the following license, and is adapted for + * use in this project by using Ceph JNI exception utilities. + * + * ---- + * + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +jobject sockaddrToInetAddress(JNIEnv* env, const sockaddr_storage& ss, jint* port) { + // Convert IPv4-mapped IPv6 addresses to IPv4 addresses. + // The RI states "Java will never return an IPv4-mapped address". + const sockaddr_in6& sin6 = reinterpret_cast<const sockaddr_in6&>(ss); + if (ss.ss_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&sin6.sin6_addr)) { + // Copy the IPv6 address into the temporary sockaddr_storage. + sockaddr_storage tmp; + memset(&tmp, 0, sizeof(tmp)); + memcpy(&tmp, &ss, sizeof(sockaddr_in6)); + // Unmap it into an IPv4 address. + sockaddr_in& sin = reinterpret_cast<sockaddr_in&>(tmp); + sin.sin_family = AF_INET; + sin.sin_port = sin6.sin6_port; + memcpy(&sin.sin_addr.s_addr, &sin6.sin6_addr.s6_addr[12], 4); + // Do the regular conversion using the unmapped address. + return sockaddrToInetAddress(env, tmp, port); + } + + const void* rawAddress; + size_t addressLength; + int sin_port = 0; + int scope_id = 0; + if (ss.ss_family == AF_INET) { + const sockaddr_in& sin = reinterpret_cast<const sockaddr_in&>(ss); + rawAddress = &sin.sin_addr.s_addr; + addressLength = 4; + sin_port = ntohs(sin.sin_port); + } else if (ss.ss_family == AF_INET6) { + const sockaddr_in6& sin6 = reinterpret_cast<const sockaddr_in6&>(ss); + rawAddress = &sin6.sin6_addr.s6_addr; + addressLength = 16; + sin_port = ntohs(sin6.sin6_port); + scope_id = sin6.sin6_scope_id; + } else if (ss.ss_family == AF_UNIX) { + const sockaddr_un& sun = reinterpret_cast<const sockaddr_un&>(ss); + rawAddress = &sun.sun_path; + addressLength = strlen(sun.sun_path); + } else { + // We can't throw SocketException. We aren't meant to see bad addresses, so seeing one + // really does imply an internal error. + //jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", + // "sockaddrToInetAddress unsupported ss_family: %i", ss.ss_family); + cephThrowIllegalArg(env, "sockaddrToInetAddress unsupposed ss_family"); + return NULL; + } + if (port != NULL) { + *port = sin_port; + } + + ScopedLocalRef<jbyteArray> byteArray(env, env->NewByteArray(addressLength)); + if (byteArray.get() == NULL) { + return NULL; + } + env->SetByteArrayRegion(byteArray.get(), 0, addressLength, + reinterpret_cast<jbyte*>(const_cast<void*>(rawAddress))); + + if (ss.ss_family == AF_UNIX) { + // Note that we get here for AF_UNIX sockets on accept(2). The unix(7) man page claims + // that the peer's sun_path will contain the path, but in practice it doesn't, and the + // peer length is returned as 2 (meaning only the sun_family field was set). + // + // Ceph Note: this isn't supported. inetUnixAddress appears to just be + // something in Dalvik/Android stuff. + cephThrowInternal(env, "OSD address should never be a UNIX socket"); + return NULL; + //static jmethodID ctor = env->GetMethodID(JniConstants::inetUnixAddressClass, "<init>", "([B)V"); + //return env->NewObject(JniConstants::inetUnixAddressClass, ctor, byteArray.get()); + } + + if (addressLength == 4) { + static jmethodID getByAddressMethod = env->GetStaticMethodID(JniConstants::inetAddressClass, + "getByAddress", "(Ljava/lang/String;[B)Ljava/net/InetAddress;"); + if (getByAddressMethod == NULL) { + return NULL; + } + return env->CallStaticObjectMethod(JniConstants::inetAddressClass, getByAddressMethod, + NULL, byteArray.get()); + } else if (addressLength == 16) { + static jmethodID getByAddressMethod = env->GetStaticMethodID(JniConstants::inet6AddressClass, + "getByAddress", "(Ljava/lang/String;[BI)Ljava/net/Inet6Address;"); + if (getByAddressMethod == NULL) { + return NULL; + } + return env->CallStaticObjectMethod(JniConstants::inet6AddressClass, getByAddressMethod, + NULL, byteArray.get(), scope_id); + } else { + abort(); + return NULL; + } +} + +/* + * Class: com_ceph_fs_CephMount + * Method: native_ceph_get_osd_addr + * Signature: (JI)Ljava/net/InetAddress; + */ +JNIEXPORT jobject JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1get_1osd_1addr + (JNIEnv *env, jclass clz, jlong j_mntp, jint osd) +{ + struct ceph_mount_info *cmount = get_ceph_mount(j_mntp); + CephContext *cct = ceph_get_mount_context(cmount); + struct sockaddr_storage addr; + int ret; + + CHECK_MOUNTED(cmount, NULL); + + ldout(cct, 10) << "jni: get_osd_addr: osd " << osd << dendl; + + ret = ceph_get_osd_addr(cmount, osd, &addr); + + ldout(cct, 10) << "jni: get_osd_addr: ret " << ret << dendl; + + if (ret < 0) { + handle_error(env, ret); + return NULL; + } + + return sockaddrToInetAddress(env, addr, NULL); +} diff --git a/src/java/test/com/ceph/fs/CephAllTests.java b/src/java/test/com/ceph/fs/CephAllTests.java new file mode 100644 index 00000000..039ad6da --- /dev/null +++ b/src/java/test/com/ceph/fs/CephAllTests.java @@ -0,0 +1,44 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +package com.ceph.fs; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.UUID; +import org.junit.*; +import org.junit.runners.Suite; +import org.junit.runner.RunWith; +import static org.junit.Assert.*; + + +@RunWith( Suite.class ) +@Suite.SuiteClasses( { + CephDoubleMountTest.class, + CephMountCreateTest.class, + CephMountTest.class, + CephUnmountedTest.class, +}) + +/** + * Every Java test class must be added to this list in order to be executed with 'ant test' + */ +public class CephAllTests{ + +} diff --git a/src/java/test/com/ceph/fs/CephDoubleMountTest.java b/src/java/test/com/ceph/fs/CephDoubleMountTest.java new file mode 100644 index 00000000..62ca1cb8 --- /dev/null +++ b/src/java/test/com/ceph/fs/CephDoubleMountTest.java @@ -0,0 +1,45 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +package com.ceph.fs; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.UUID; +import org.junit.*; +import static org.junit.Assert.*; + + +public class CephDoubleMountTest { + + @Test(expected=CephAlreadyMountedException.class) + public void test_double_mount() throws Exception { + CephMount mount = new CephMount("admin"); + String conf_file = System.getProperty("CEPH_CONF_FILE"); + if (conf_file != null) + mount.conf_read_file(conf_file); + mount.mount(null); + try { + mount.mount(null); + } finally { + mount.unmount(); + } + } + +} diff --git a/src/java/test/com/ceph/fs/CephMountCreateTest.java b/src/java/test/com/ceph/fs/CephMountCreateTest.java new file mode 100644 index 00000000..fc2bafd6 --- /dev/null +++ b/src/java/test/com/ceph/fs/CephMountCreateTest.java @@ -0,0 +1,91 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +package com.ceph.fs; + +import java.io.FileNotFoundException; +import org.junit.*; +import java.util.UUID; +import static org.junit.Assert.*; + +/* + * This tests the mount root dir functionality. It creates an empty + * directory in the real root, then it re-mounts the file system + * with the empty directory specified as the root. Assertions are + * that the "/" in the normal mount is non-empty, and that "/" is + * empty in the mount with the empty directory as the root. + */ +public class CephMountCreateTest { + + private static String conf_file; + + @BeforeClass + public static void class_setup() throws Exception { + conf_file = System.getProperty("CEPH_CONF_FILE"); + } + + private CephMount setupMount(String root) throws Exception { + CephMount mount = new CephMount("admin"); + if (conf_file != null) + mount.conf_read_file(conf_file); + mount.conf_set("client_permissions", "0"); + mount.mount(root); + return mount; + } + + @Test + public void test_CephMountCreate() throws Exception { + CephMount mount; + boolean found; + + String dir = "libcephfs_junit_" + UUID.randomUUID(); + + /* root dir has more than one dir */ + mount = setupMount("/"); + + try { + mount.rmdir("/" + dir); + } catch (FileNotFoundException e) {} + mount.mkdirs("/" + dir, 777); + String[] subdirs = mount.listdir("/"); + found = false; + for (String d : subdirs) { + if (d.compareTo(dir) == 0) + found = true; + } + assertTrue(found); + mount.unmount(); + + /* changing root to empty dir */ + mount = setupMount("/" + dir); + + subdirs = mount.listdir("/"); + found = false; + for (String d : subdirs) { + found = true; + } + assertFalse(found); + mount.unmount(); + + /* cleanup */ + mount = setupMount("/"); + mount.rmdir("/" + dir); + mount.unmount(); + } +} diff --git a/src/java/test/com/ceph/fs/CephMountTest.java b/src/java/test/com/ceph/fs/CephMountTest.java new file mode 100644 index 00000000..fcc54d8c --- /dev/null +++ b/src/java/test/com/ceph/fs/CephMountTest.java @@ -0,0 +1,1040 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package com.ceph.fs; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.InetAddress; +import java.util.UUID; +import org.junit.*; +import static org.junit.Assert.*; + +import com.ceph.crush.Bucket; + +/* + * Coverage + * - Everything is covered in at least success cases. + * - l[set,get,remove]xattr are not working + */ + +public class CephMountTest { + + private static CephMount mount; + private static String basedir = null; + + @BeforeClass + public static void setup() throws Exception { + mount = new CephMount("admin"); + + String conf_file = System.getProperty("CEPH_CONF_FILE"); + if (conf_file != null) + mount.conf_read_file(conf_file); + mount.conf_set("client_permissions", "0"); + + mount.mount(null); + + basedir = "/libcephfs_junit_" + UUID.randomUUID(); + mount.mkdir(basedir, 0777); + } + + @AfterClass + public static void destroy() throws Exception { + String[] list = mount.listdir(basedir); + for (String l : list) + System.out.println(l); + mount.rmdir(basedir); + mount.unmount(); + } + + /* + * Helper function to construct a unique path. + */ + public String makePath() { + String path = basedir + "/" + UUID.randomUUID(); + return path; + } + + /* + * Helper to learn the data pool name, by reading it + * from the '/' dir inode. + */ + public String getRootPoolName() throws Exception + { + int fd = mount.open("/", CephMount.O_DIRECTORY, 0600); + String pool = mount.get_file_pool_name(fd); + mount.close(fd); + return pool; + } + + /* + * Helper function to create a file with the given path and size. The file + * is filled with size bytes and the file descriptor is returned. + */ + public int createFile(String path, int size) throws Exception { + int fd = mount.open(path, CephMount.O_RDWR|CephMount.O_CREAT, 0600); + byte[] buf = new byte[4096]; + int left = size; + while (left > 0) { + size = Math.min(buf.length, left); + long ret = mount.write(fd, buf, size, -1); + left -= ret; + } + return fd; + } + + /* + * Helper function to create a unique file and fill it with size bytes. The + * file descriptor is returned. + */ + public int createFile(int size) throws Exception { + return createFile(makePath(), size); + } + + @Test(expected=FileNotFoundException.class) + public void test_mount_dne() throws Exception { + CephMount mount2 = new CephMount("admin"); + String conf_file = System.getProperty("CEPH_CONF_FILE"); + if (conf_file != null) + mount2.conf_read_file(conf_file); + mount2.mount("/wlfkjwlekfjwlejfwe"); + mount2.unmount(); + } + + /* + * Test loading of conf file that doesn't exist. + * + * FIXME: + * Ceph returns -ENOSYS rather than -ENOENT. Correct? + */ + //@Test(expected=FileNotFoundException.class) + @Test + public void test_conf_read_file_dne() throws Exception { + //mount.conf_read_file("/this_file_does_not_exist"); + } + + /* + * Test loading of conf file that isn't valid + * + * FIXME: implement + */ + @Test + public void test_conf_read_file_invalid() throws Exception { + } + + @Test(expected=NullPointerException.class) + public void test_conf_read_file_null() throws Exception { + mount.conf_read_file(null); + } + + /* + * conf_set/conf_get + */ + + @Test(expected=NullPointerException.class) + public void test_conf_set_null_opt() throws Exception { + mount.conf_set(null, "value"); + } + + @Test(expected=NullPointerException.class) + public void test_conf_set_null_val() throws Exception { + mount.conf_set("option", null); + } + + @Test(expected=NullPointerException.class) + public void test_conf_get_null_opt() throws Exception { + mount.conf_get(null); + } + + @Test + public void test_conf() throws Exception { + String opt = "log to stderr"; + String val1, val2, val3; + + /* get the current value */ + val1 = mount.conf_get(opt); + + /* + * flip the value. this may make some debug information be dumped to the + * console when the value becomes true. TODO: find a better config option + * to toggle. + */ + if (val1.compareTo("true") == 0) + val2 = "false"; + else + val2 = "true"; + mount.conf_set(opt, val2); + + /* verify the change */ + val3 = mount.conf_get(opt); + assertTrue(val3.compareTo(val2) == 0); + + /* reset to original value */ + mount.conf_set(opt, val1); + val3 = mount.conf_get(opt); + assertTrue(val3.compareTo(val1) == 0); + } + + /* + * statfs + */ + + @Test + public void test_statfs() throws Exception { + CephStatVFS st1 = new CephStatVFS(); + mount.statfs("/", st1); + + /* + * FIXME: a better test here is to see if changes to the file system are + * reflected through statfs (e.g. increasing number of files). However, it + * appears that the updates aren't immediately visible. + */ + assertTrue(st1.bsize > 0); + assertTrue(st1.frsize > 0); + assertTrue(st1.blocks > 0); + assertTrue(st1.bavail > 0); + assertTrue(st1.namemax > 0); + } + + /* + * getcwd/chdir + */ + + @Test + public void test_getcwd() throws Exception { + mount.chdir(basedir); + String cwd = mount.getcwd(); + assertTrue(cwd.compareTo(basedir) == 0); + + /* Make sure to reset cwd to root */ + mount.chdir("/"); + cwd = mount.getcwd(); + assertTrue(cwd.compareTo("/") == 0); + } + + @Test(expected=NullPointerException.class) + public void test_chdir_null() throws Exception { + mount.chdir(null); + } + + @Test(expected=FileNotFoundException.class) + public void test_chdir_dne() throws Exception { + mount.chdir("/this/path/does/not/exist/"); + } + + /* + * FIXME: this test should throw an error (but does not)? + */ + //@Test(expected=IOException.class) + @Test + public void test_chdir_not_dir() throws Exception { + String path = makePath(); + int fd = createFile(path, 1); + mount.close(fd); + //mount.chdir(path); shouldn't be able to do this? + mount.unlink(path); + + /* + * Switch back. Other tests seem to be sensitive to the current directory + * being something other than "/". This shouldn't happen once this tests + * passes and the call to chdir fails anyway. + */ + mount.chdir("/"); + } + + /* + * listdir + */ + + @Test(expected=NullPointerException.class) + public void test_listdir_null() throws Exception { + mount.listdir(null); + } + + @Test(expected=FileNotFoundException.class) + public void test_listdir_dne() throws Exception { + mount.listdir("/this/path/does/not/exist/"); + } + + @Test(expected=IOException.class) + public void test_listdir_not_dir() throws Exception { + String path = makePath(); + int fd = createFile(path, 1); + mount.close(fd); + try { + mount.listdir(path); + } finally { + mount.unlink(path); + } + } + + @Test + public void test_listdir() throws Exception { + String dir = makePath(); + mount.mkdir(dir, 0777); + /* test that new directory is empty */ + String[] list = mount.listdir(dir); + assertTrue(list.length == 0); + /* test that new directories are seen */ + for (int i = 0; i < 3; i++) + mount.mkdir(dir + "/" + i, 777); + list = mount.listdir(dir); + assertTrue(list.length == 3); + /* test that more new directories are seen */ + for (int i = 0; i < 30; i++) + mount.mkdir(dir + "/x" + i, 777); + list = mount.listdir(dir); + assertTrue(list.length == 33); + + /* remove */ + for (int i = 0; i < 30; i++) + mount.rmdir(dir + "/x" + i); + for (int i = 0; i < 3; i++) + mount.rmdir(dir + "/" + i); + mount.rmdir(dir); + } + + /* + * Missing + * + * ceph_link + * ceph_unlink + */ + + /* + * rename + */ + + @Test(expected=NullPointerException.class) + public void test_rename_null_from() throws Exception { + mount.rename(null, "to"); + } + + @Test(expected=NullPointerException.class) + public void test_rename_null_to() throws Exception { + mount.rename("from", null); + } + + @Test(expected=FileNotFoundException.class) + public void test_rename_dne() throws Exception { + mount.rename("/this/doesnt/exist", "/this/neither"); + } + + @Test + public void test_rename() throws Exception { + /* create a file */ + String path = makePath(); + int fd = createFile(path, 1); + mount.close(fd); + + /* move it to a new name */ + String newpath = makePath(); + mount.rename(path, newpath); + + /* verfiy the sizes are the same */ + CephStat st = new CephStat(); + mount.lstat(newpath, st); + assertTrue(st.size == 1); + + /* remove the file */ + mount.unlink(newpath); + } + + /* + * mkdir/mkdirs/rmdir + */ + + @Test(expected=IOException.class) + public void test_mkdir_exists() throws Exception { + String path = makePath(); + mount.mkdir(path, 0777); + try { + mount.mkdir(path, 0777); + } finally { + mount.rmdir(path); + } + } + + @Test(expected=IOException.class) + public void test_mkdirs_exists() throws Exception { + String path = makePath(); + mount.mkdirs(path, 0777); + try { + mount.mkdirs(path, 0777); + } finally { + mount.rmdir(path); + } + } + + @Test + public void test_mkdir() throws Exception { + String path = makePath(); + mount.mkdir(path, 0777); + CephStat st = new CephStat(); + mount.lstat(path, st); + assertTrue(st.isDir()); + mount.rmdir(path); + } + + @Test + public void test_mkdirs() throws Exception { + String path = makePath(); + mount.mkdirs(path + "/x/y", 0777); + + CephStat st = new CephStat(); + mount.lstat(path, st); + assertTrue(st.isDir()); + + mount.lstat(path + "/x", st); + assertTrue(st.isDir()); + + mount.lstat(path + "/x/y", st); + assertTrue(st.isDir()); + + mount.rmdir(path + "/x/y"); + mount.rmdir(path + "/x"); + mount.rmdir(path); + } + + @Test(expected=FileNotFoundException.class) + public void test_rmdir() throws Exception { + /* make a new directory */ + String path = makePath(); + mount.mkdir(path, 0777); + CephStat st = new CephStat(); + mount.lstat(path, st); + assertTrue(st.isDir()); + /* remove it */ + mount.rmdir(path); + /* should not exist now */ + mount.lstat(path, st); + } + + /* + * readlink + * symlink + */ + @Test + public void test_symlink() throws Exception { + String oldpath = makePath(); + String newpath = makePath(); + + mount.symlink(oldpath, newpath); + CephStat stat = new CephStat(); + mount.lstat(newpath, stat); + assertTrue(stat.isSymlink()); + + String symlink = mount.readlink(newpath); + assertTrue(symlink.compareTo(oldpath) == 0); + + mount.unlink(newpath); + } + + /* + * lstat + */ + + @Test(expected=NullPointerException.class) + public void test_lstat_null_path() throws Exception { + mount.lstat(null, new CephStat()); + } + + @Test(expected=NullPointerException.class) + public void test_lstat_null_stat() throws Exception { + mount.lstat("/path", null); + } + + @Test(expected=FileNotFoundException.class) + public void test_lstat_null_dne() throws Exception { + mount.lstat("/path/does/not/exist", new CephStat()); + } + + /* + * test_stat covers lstat and fstat and stat. + * + * TODO: create test that for lstat vs stat with symlink follow/nofollow. + */ + + @Test + public void test_stat() throws Exception { + /* create a new file */ + String path = makePath(); + int size = 12345; + int fd = createFile(path, size); + mount.close(fd); + + /* test some basic info about the new file */ + CephStat orig_st = new CephStat(); + mount.lstat(path, orig_st); + assertTrue(orig_st.size == size); + assertTrue(orig_st.blksize > 0); + assertTrue(orig_st.blocks > 0); + + /* now try stat */ + CephStat stat_st = new CephStat(); + mount.stat(path, stat_st); + + /* now try fstat */ + CephStat other_st = new CephStat(); + fd = mount.open(path, CephMount.O_RDWR, 0); + mount.fstat(fd, other_st); + mount.close(fd); + + mount.unlink(path); + + /* compare to fstat results */ + assertTrue(orig_st.mode == other_st.mode); + assertTrue(orig_st.uid == other_st.uid); + assertTrue(orig_st.gid == other_st.gid); + assertTrue(orig_st.size == other_st.size); + assertTrue(orig_st.blksize == other_st.blksize); + assertTrue(orig_st.blocks == other_st.blocks); + + /* compare to stat results */ + assertTrue(orig_st.mode == stat_st.mode); + assertTrue(orig_st.uid == stat_st.uid); + assertTrue(orig_st.gid == stat_st.gid); + assertTrue(orig_st.size == stat_st.size); + assertTrue(orig_st.blksize == stat_st.blksize); + assertTrue(orig_st.blocks == stat_st.blocks); + } + + /* + * stat + */ + + @Test(expected=NullPointerException.class) + public void test_stat_null_path() throws Exception { + mount.stat(null, new CephStat()); + } + + @Test(expected=NullPointerException.class) + public void test_stat_null_stat() throws Exception { + mount.stat("/path", null); + } + + @Test(expected=FileNotFoundException.class) + public void test_stat_null_dne() throws Exception { + mount.stat("/path/does/not/exist", new CephStat()); + } + + @Test(expected=CephNotDirectoryException.class) + public void test_enotdir() throws Exception { + String path = makePath(); + int fd = createFile(path, 1); + mount.close(fd); + + try { + CephStat stat = new CephStat(); + mount.lstat(path + "/blah", stat); + } finally { + mount.unlink(path); + } + } + + /* + * setattr + */ + + @Test(expected=NullPointerException.class) + public void test_setattr_null_path() throws Exception { + mount.setattr(null, new CephStat(), 0); + } + + @Test(expected=NullPointerException.class) + public void test_setattr_null_stat() throws Exception { + mount.setattr("/path", null, 0); + } + + @Test(expected=FileNotFoundException.class) + public void test_setattr_dne() throws Exception { + mount.setattr("/path/does/not/exist", new CephStat(), 0); + } + + @Test + public void test_setattr() throws Exception { + /* create a file */ + String path = makePath(); + int fd = createFile(path, 1); + mount.close(fd); + + CephStat st1 = new CephStat(); + mount.lstat(path, st1); + + st1.uid += 1; + st1.gid += 1; + mount.setattr(path, st1, mount.SETATTR_UID|mount.SETATTR_GID); + + CephStat st2 = new CephStat(); + mount.lstat(path, st2); + + assertTrue(st2.uid == st1.uid); + assertTrue(st2.gid == st1.gid); + + /* remove the file */ + mount.unlink(path); + } + + /* + * chmod + */ + + @Test(expected=NullPointerException.class) + public void test_chmod_null_path() throws Exception { + mount.chmod(null, 0); + } + + @Test(expected=FileNotFoundException.class) + public void test_chmod_dne() throws Exception { + mount.chmod("/path/does/not/exist", 0); + } + + @Test + public void test_chmod() throws Exception { + /* create a file */ + String path = makePath(); + int fd = createFile(path, 1); + mount.close(fd); + + CephStat st = new CephStat(); + mount.lstat(path, st); + + /* flip a bit */ + int mode = st.mode; + if ((mode & 1) != 0) + mode -= 1; + else + mode += 1; + + mount.chmod(path, mode); + CephStat st2 = new CephStat(); + mount.lstat(path, st2); + assertTrue(st2.mode == mode); + + mount.unlink(path); + } + + /* + * fchmod + */ + + @Test + public void test_fchmod() throws Exception { + /* create a file */ + String path = makePath(); + int fd = createFile(path, 1); + + CephStat st = new CephStat(); + mount.lstat(path, st); + + /* flip a bit */ + int mode = st.mode; + if ((mode & 1) != 0) + mode -= 1; + else + mode += 1; + + mount.fchmod(fd, mode); + mount.close(fd); + + CephStat st2 = new CephStat(); + mount.lstat(path, st2); + assertTrue(st2.mode == mode); + + mount.unlink(path); + } + + /* + * truncate + */ + + @Test(expected=FileNotFoundException.class) + public void test_truncate_dne() throws Exception { + mount.truncate("/path/does/not/exist", 0); + } + + @Test(expected=NullPointerException.class) + public void test_truncate_null_path() throws Exception { + mount.truncate(null, 0); + } + + @Test + public void test_truncate() throws Exception { + // make file + String path = makePath(); + int orig_size = 1398331; + int fd = createFile(path, orig_size); + mount.close(fd); + + // check file size + CephStat st = new CephStat(); + mount.lstat(path, st); + assertTrue(st.size == orig_size); + + // truncate and check + int crop_size = 333333; + mount.truncate(path, crop_size); + mount.lstat(path, st); + assertTrue(st.size == crop_size); + + // check after re-open + fd = mount.open(path, CephMount.O_RDWR, 0); + mount.fstat(fd, st); + assertTrue(st.size == crop_size); + mount.close(fd); + + mount.unlink(path); + } + + @Test + public void test_open_layout() throws Exception { + String path = makePath(); + int fd = mount.open(path, CephMount.O_WRONLY|CephMount.O_CREAT, 0, + (1<<20), 1, (1<<20), null); + mount.close(fd); + mount.unlink(path); + } + + /* + * open/close + */ + + @Test(expected=FileNotFoundException.class) + public void test_open_dne() throws Exception { + mount.open("/path/doesnt/exist", 0, 0); + } + + /* + * lseek + */ + + @Test + public void test_lseek() throws Exception { + /* create a new file */ + String path = makePath(); + int size = 12345; + int fd = createFile(path, size); + mount.close(fd); + + /* open and check size */ + fd = mount.open(path, CephMount.O_RDWR, 0); + long end = mount.lseek(fd, 0, CephMount.SEEK_END); + mount.close(fd); + + mount.unlink(path); + + assertTrue(size == (int)end); + } + + /* + * read/write + */ + + @Test + public void test_read() throws Exception { + String path = makePath(); + int fd = createFile(path, 1500); + byte[] buf = new byte[1500]; + long ret = mount.read(fd, buf, 1500, 0); + assertTrue(ret == 1500); + mount.unlink(path); + } + + /* + * ftruncate + */ + + @Test + public void test_ftruncate() throws Exception { + // make file + String path = makePath(); + int orig_size = 1398331; + int fd = createFile(path, orig_size); + + // check file size + CephStat st = new CephStat(); + mount.fstat(fd, st); + assertTrue(st.size == orig_size); + + // truncate and check + int crop_size = 333333; + mount.ftruncate(fd, crop_size); + mount.fstat(fd, st); + if (st.size != crop_size) { + System.err.println("ftruncate error: st.size=" + st.size + " crop_size=" + crop_size); + assertTrue(false); + } + assertTrue(st.size == crop_size); + mount.close(fd); + + // check after re-open + fd = mount.open(path, CephMount.O_RDWR, 0); + mount.fstat(fd, st); + assertTrue(st.size == crop_size); + mount.close(fd); + + mount.unlink(path); + } + + /* + * fsync + */ + + @Test + public void test_fsync() throws Exception { + String path = makePath(); + int fd = createFile(path, 123); + mount.fsync(fd, false); + mount.fsync(fd, true); + mount.close(fd); + mount.unlink(path); + } + + /* + * flock + */ + + @Test + public void test_flock() throws Exception { + String path = makePath(); + int fd = createFile(path, 123); + mount.flock(fd, CephMount.LOCK_SH | CephMount.LOCK_NB, 42); + mount.flock(fd, CephMount.LOCK_SH | CephMount.LOCK_NB, 43); + mount.flock(fd, CephMount.LOCK_UN, 42); + mount.flock(fd, CephMount.LOCK_UN, 43); + mount.flock(fd, CephMount.LOCK_EX | CephMount.LOCK_NB, 42); + try { + mount.flock(fd, CephMount.LOCK_SH | CephMount.LOCK_NB, 43); + assertTrue(false); + } catch(IOException io) {} + try { + mount.flock(fd, CephMount.LOCK_EX | CephMount.LOCK_NB, 43); + assertTrue(false); + } catch(IOException io) {} + mount.flock(fd, CephMount.LOCK_SH, 42); // downgrade + mount.flock(fd, CephMount.LOCK_SH, 43); + mount.flock(fd, CephMount.LOCK_UN, 42); + mount.flock(fd, CephMount.LOCK_UN, 43); + mount.close(fd); + mount.unlink(path); + } + + /* + * fstat + * + * success case is handled in test_stat along with lstat. + */ + + /* + * sync_fs + */ + + @Test + public void test_sync_fs() throws Exception { + mount.sync_fs(); + } + + /* + * get/set/list/remove xattr + */ + + @Test + public void test_xattr() throws Exception { + /* make file */ + String path = makePath(); + int fd = createFile(path, 123); + mount.close(fd); + + /* make xattrs */ + String val1 = "This is a new xattr"; + String val2 = "This is a different xattr"; + byte[] buf1 = val1.getBytes(); + byte[] buf2 = val2.getBytes(); + mount.setxattr(path, "user.attr1", buf1, buf1.length, mount.XATTR_CREATE); + mount.setxattr(path, "user.attr2", buf2, buf2.length, mount.XATTR_CREATE); + + /* list xattrs */ + String[] xattrs = mount.listxattr(path); + assertTrue(xattrs.length == 2); + int found = 0; + for (String xattr : xattrs) { + if (xattr.compareTo("user.attr1") == 0) { + found++; + continue; + } + if (xattr.compareTo("user.attr2") == 0) { + found++; + continue; + } + System.out.println("found unwanted xattr: " + xattr); + } + assertTrue(found == 2); + + /* get first xattr by looking up length */ + long attr1_len = mount.getxattr(path, "user.attr1", null); + byte[] out = new byte[(int)attr1_len]; + mount.getxattr(path, "user.attr1", out); + String outStr = new String(out); + assertTrue(outStr.compareTo(val1) == 0); + + /* get second xattr assuming original length */ + out = new byte[buf2.length]; + mount.getxattr(path, "user.attr2", out); + outStr = new String(out); + assertTrue(outStr.compareTo(val2) == 0); + + /* remove the attributes */ + /* FIXME: the MDS returns ENODATA for removexattr */ + /* + mount.removexattr(path, "attr1"); + xattrs = mount.listxattr(path); + assertTrue(xattrs.length == 1); + mount.removexattr(path, "attr2"); + xattrs = mount.listxattr(path); + assertTrue(xattrs.length == 0); + */ + + mount.unlink(path); + } + + /* + * get/set/list/remove symlink xattr + * + * Currently not working. Code is the same as for regular xattrs, so there + * might be a deeper issue. + */ + + @Test + public void test_get_stripe_unit() throws Exception { + String path = makePath(); + int fd = createFile(path, 1); + assertTrue(mount.get_file_stripe_unit(fd) > 0); + mount.close(fd); + mount.unlink(path); + } + + @Test + public void test_get_repl() throws Exception { + String path = makePath(); + int fd = createFile(path, 1); + assertTrue(mount.get_file_replication(fd) > 0); + mount.close(fd); + mount.unlink(path); + } + + /* + * stripe unit granularity + */ + + @Test + public void test_get_stripe_unit_gran() throws Exception { + assertTrue(mount.get_stripe_unit_granularity() > 0); + } + + @Test + public void test_get_pool_id() throws Exception { + String data_pool_name = getRootPoolName(); + /* returns valid pool id */ + assertTrue(mount.get_pool_id(data_pool_name) >= 0); + + /* test non-existent pool name */ + try { + mount.get_pool_id("asdlfkjlsejflkjef"); + assertTrue(false); + } catch (CephPoolException e) {} + } + + @Test + public void test_get_pool_replication() throws Exception { + /* test invalid pool id */ + try { + mount.get_pool_replication(-1); + assertTrue(false); + } catch (CephPoolException e) {} + + /* test valid pool id */ + String data_pool_name = getRootPoolName(); + int poolid = mount.get_pool_id(data_pool_name); + assertTrue(poolid >= 0); + assertTrue(mount.get_pool_replication(poolid) > 0); + } + + @Test + public void test_get_file_pool_name() throws Exception { + String data_pool_name = getRootPoolName(); + String path = makePath(); + int fd = createFile(path, 1); + String pool = mount.get_file_pool_name(fd); + mount.close(fd); + assertTrue(pool != null); + /* assumes using default data pool */ + assertTrue(pool.compareTo(data_pool_name) == 0); + mount.unlink(path); + } + + @Test(expected=IOException.class) + public void test_get_file_pool_name_ebadf() throws Exception { + String pool = mount.get_file_pool_name(-40); + } + + @Test + public void test_get_file_extent() throws Exception { + int stripe_unit = 1<<18; + String path = makePath(); + int fd = mount.open(path, CephMount.O_WRONLY|CephMount.O_CREAT, 0, + stripe_unit, 2, stripe_unit*2, null); + + CephFileExtent e = mount.get_file_extent(fd, 0); + assertTrue(e.getOSDs().length > 0); + + assertTrue(e.getOffset() == 0); + assertTrue(e.getLength() == stripe_unit); + + e = mount.get_file_extent(fd, stripe_unit/2); + assertTrue(e.getOffset() == stripe_unit/2); + assertTrue(e.getLength() == stripe_unit/2); + + e = mount.get_file_extent(fd, 3*stripe_unit/2-1); + assertTrue(e.getOffset() == 3*stripe_unit/2-1); + assertTrue(e.getLength() == stripe_unit/2+1); + + e = mount.get_file_extent(fd, 3*stripe_unit/2+1); + assertTrue(e.getLength() == stripe_unit/2-1); + + mount.close(fd); + mount.unlink(path); + } + + @Test + public void test_get_osd_crush_location() throws Exception { + Bucket[] path = mount.get_osd_crush_location(0); + assertTrue(path.length > 0); + for (Bucket b : path) { + assertTrue(b.getType().length() > 0); + assertTrue(b.getName().length() > 0); + } + } + + @Test + public void test_get_osd_address() throws Exception { + InetAddress addr = mount.get_osd_address(0); + assertTrue(addr.getHostAddress().length() > 0); + } +} diff --git a/src/java/test/com/ceph/fs/CephUnmountedTest.java b/src/java/test/com/ceph/fs/CephUnmountedTest.java new file mode 100644 index 00000000..eb95e69f --- /dev/null +++ b/src/java/test/com/ceph/fs/CephUnmountedTest.java @@ -0,0 +1,164 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +package com.ceph.fs; + +import org.junit.*; +import static org.junit.Assert.*; + +public class CephUnmountedTest { + + private CephMount mount; + + @Before + public void setup() throws Exception { + mount = new CephMount("admin"); + } + + @Test(expected=CephNotMountedException.class) + public void test_unmount() throws Exception { + mount.unmount(); + } + + @Test(expected=CephNotMountedException.class) + public void test_statfs() throws Exception { + CephStatVFS stat = new CephStatVFS(); + mount.statfs("/a/path", stat); + } + + @Test(expected=CephNotMountedException.class) + public void test_getcwd() throws Exception { + mount.getcwd(); + } + + @Test(expected=CephNotMountedException.class) + public void test_chdir() throws Exception { + mount.chdir("/a/path"); + } + + @Test(expected=CephNotMountedException.class) + public void test_listdir() throws Exception { + mount.listdir("/a/path"); + } + + @Test(expected=CephNotMountedException.class) + public void test_unlink() throws Exception { + mount.unlink("/a/path"); + } + + @Test(expected=CephNotMountedException.class) + public void test_rename() throws Exception { + mount.rename("/a/path", "/another/path"); + } + + @Test(expected=CephNotMountedException.class) + public void test_mkdirs() throws Exception { + mount.mkdirs("/a/path", 0); + } + + @Test(expected=CephNotMountedException.class) + public void test_rmdir() throws Exception { + mount.rmdir("/a/path"); + } + + @Test(expected=CephNotMountedException.class) + public void test_stat() throws Exception { + CephStat stat = new CephStat(); + mount.stat("/a/path", stat); + } + + @Test(expected=CephNotMountedException.class) + public void test_lstat() throws Exception { + CephStat stat = new CephStat(); + mount.lstat("/a/path", stat); + } + + @Test(expected=CephNotMountedException.class) + public void test_setattr() throws Exception { + CephStat stat = new CephStat(); + mount.setattr("/a/path", stat, 0); + } + + @Test(expected=CephNotMountedException.class) + public void test_open() throws Exception { + mount.open("/a/path", 0, 0); + } + + @Test(expected=CephNotMountedException.class) + public void test_open_layout() throws Exception { + mount.open("/a/path", 0, 0, 0, 0, 0, null); + } + + @Test(expected=CephNotMountedException.class) + public void test_close() throws Exception { + mount.close(0); + } + + @Test(expected=CephNotMountedException.class) + public void test_lseek() throws Exception { + mount.lseek(0, 0, CephMount.SEEK_CUR); + } + + @Test(expected=CephNotMountedException.class) + public void test_read() throws Exception { + byte[] buf = new byte[1]; + mount.read(0, buf, 1, 0); + } + + @Test(expected=CephNotMountedException.class) + public void test_write() throws Exception { + byte[] buf = new byte[1]; + mount.write(0, buf, 1, 0); + } + + @Test(expected=CephNotMountedException.class) + public void test_get_stripe_unit() throws Exception { + mount.get_file_stripe_unit(0); + } + + @Test(expected=CephNotMountedException.class) + public void test_get_repl() throws Exception { + mount.get_file_replication(0); + } + + @Test(expected=CephNotMountedException.class) + public void test_get_stripe_unit_gran() throws Exception { + mount.get_stripe_unit_granularity(); + } + + @Test(expected=CephNotMountedException.class) + public void test_get_pool_id() throws Exception { + mount.get_pool_id("data"); + } + + @Test(expected=CephNotMountedException.class) + public void test_get_pool_replication() throws Exception { + mount.get_pool_replication(1); + } + + @Test(expected=CephNotMountedException.class) + public void test_fchmod() throws Exception { + mount.fchmod(1, 0); + } + + @Test(expected=CephNotMountedException.class) + public void test_chmod() throws Exception { + mount.chmod("/foo", 0); + } +} |