]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
java: add Java and C++ source files
authorNoah Watkins <noahwatkins@gmail.com>
Sat, 1 Sep 2012 17:26:41 +0000 (10:26 -0700)
committerNoah Watkins <noahwatkins@gmail.com>
Fri, 19 Oct 2012 16:59:10 +0000 (09:59 -0700)
This adds all of the Java and C++ source files that make up the
libcephfs Java wrappers package.

Signed-off-by: Noah Watkins <noahwatkins@gmail.com>
13 files changed:
src/common/config_opts.h
src/java/README [new file with mode: 0644]
src/java/build.xml [new file with mode: 0644]
src/java/java/com/ceph/fs/CephException.java [new file with mode: 0644]
src/java/java/com/ceph/fs/CephMount.java [new file with mode: 0644]
src/java/java/com/ceph/fs/CephNativeLoader.java [new file with mode: 0644]
src/java/java/com/ceph/fs/CephNotMountedException.java [new file with mode: 0644]
src/java/java/com/ceph/fs/CephStat.java [new file with mode: 0644]
src/java/java/com/ceph/fs/CephStatVFS.java [new file with mode: 0644]
src/java/native/libcephfs_jni.cc [new file with mode: 0644]
src/java/test/CephMountCreateTest.java [new file with mode: 0644]
src/java/test/CephMountTest.java [new file with mode: 0644]
src/java/test/CephUnmountedTest.java [new file with mode: 0644]

index 29e3b74766d328de98086e8e362be8646821083f..706dfd5383272601ae2939b4f7916f1806a4a19d 100644 (file)
@@ -82,6 +82,7 @@ SUBSYS(heartbeatmap, 1, 5)
 SUBSYS(perfcounter, 1, 5)
 SUBSYS(rgw, 1, 5)                 // log level for the Rados gateway
 SUBSYS(hadoop, 1, 5)
+SUBSYS(javaclient, 1, 5)
 SUBSYS(asok, 1, 5)
 SUBSYS(throttle, 1, 5)
 
diff --git a/src/java/README b/src/java/README
new file mode 100644 (file)
index 0000000..ca39a44
--- /dev/null
@@ -0,0 +1,48 @@
+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 JUnit and Ant.
+
+To run the tests make sure that the JUnit JAR is available in the lib/
+directory. For example:
+
+  $ mkdir lib
+  $ cd lib
+  $ curl -O https://github.com/downloads/KentBeck/junit/junit-4.8.2.jar
+
+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 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 in ../.libs 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 (file)
index 0000000..f846ca4
--- /dev/null
@@ -0,0 +1,67 @@
+<?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="lib.dir" location="lib" />
+       <property name="test.src.dir" location="test" />
+    <property name="test.build.dir" location="test_build" />
+
+    <property environment="env"/>
+
+       <target name="clean">
+               <delete dir="${doc.dir}" />
+               <delete dir="${test.build.dir}" />
+       </target>
+
+       <target name="makedir">
+               <mkdir dir="${doc.dir}" />
+               <mkdir dir="${test.build.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" depends="makedir">
+        <javac srcdir="${test.src.dir}" destdir="${test.build.dir}"
+            includeantruntime="false">
+            <classpath>
+                               <pathelement location="${env.CEPHFS_JAR}"/>
+                               <pathelement location="libcephfs.jar"/>
+                <fileset dir="${lib.dir}">
+                    <include name="**/*.jar"/>
+                </fileset>
+            </classpath>
+               </javac>
+       </target>
+
+       <target name="test" depends="compile-tests">
+        <junit printsummary="yes" haltonfailure="yes" showoutput="yes">
+                       <sysproperty key="java.library.path" path="${env.CEPHFS_JNI_LIB}:../.libs/"/>
+            <sysproperty key="CEPH_CONF_FILE" path="${env.CEPHFS_CONF}"/>
+            <classpath>
+                <fileset dir="${lib.dir}">
+                    <include name="**/*.jar"/>
+                </fileset>
+                               <pathelement location="${env.CEPHFS_JAR}"/>
+                               <pathelement location="libcephfs.jar"/>
+                <pathelement path="${test.build.dir}"/>
+                <pathelement path="${test.src.dir}"/>
+            </classpath>
+            <formatter type="plain"/>
+            <batchtest fork="yes">
+                <fileset dir="${test.src.dir}">
+                    <include name="**/*Test.java"/>
+                </fileset>
+            </batchtest>
+        </junit>
+    </target>
+
+</project>
diff --git a/src/java/java/com/ceph/fs/CephException.java b/src/java/java/com/ceph/fs/CephException.java
new file mode 100644 (file)
index 0000000..2fe2137
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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 that represents generic Ceph exception.
+ */
+public class CephException extends Exception {
+
+  /**
+   * Create basic CephException.
+   */
+  public CephException() {
+    super();
+  }
+
+  /**
+   * Create CephException with message.
+   */
+  public CephException(String s) {
+    super(s);
+  }
+}
diff --git a/src/java/java/com/ceph/fs/CephMount.java b/src/java/java/com/ceph/fs/CephMount.java
new file mode 100644 (file)
index 0000000..0211f67
--- /dev/null
@@ -0,0 +1,645 @@
+/*
+ * 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.util.Arrays;
+import java.lang.String;
+
+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;
+
+  /*
+   * 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;
+
+
+  /*
+   * This is run by the class loader and will report early any problems
+   * finding or linking in the shared JNI library.
+   */
+  static {
+    CephNativeLoader.checkLoaded();
+  }
+
+  /*
+   * Package-private: called from CephNativeLoader
+   */
+  static native void native_initialize();
+
+  /**
+   * Create a new CephMount with specific client id.
+   *
+   * @param id client id.
+   */
+  public CephMount(String id) {
+    native_ceph_create(this, id);
+  }
+
+  private      static synchronized 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) {
+    native_ceph_mount(instance_ptr, root);
+  }
+
+  private      static synchronized native int native_ceph_mount(long mountp, String root);
+
+  /**
+   * Shutdown the mount.
+   */
+  public void shutdown() {
+    native_ceph_shutdown(instance_ptr);
+  }
+
+  private      static synchronized native void native_ceph_shutdown(long mountp);
+
+  /**
+   * Load configuration from a file.
+   *
+   * @param path The path to the configuration file.
+   */
+  public void conf_read_file(String path) throws FileNotFoundException {
+    native_ceph_conf_read_file(instance_ptr, path);
+  }
+
+  private      static synchronized 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) {
+    native_ceph_conf_set(instance_ptr, option, value);
+  }
+
+  private      static synchronized 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) {
+    return native_ceph_conf_get(instance_ptr, option);
+  }
+
+  private static synchronized 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 {
+    native_ceph_statfs(instance_ptr, path, statvfs);
+  }
+
+  private      static synchronized 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() {
+    return native_ceph_getcwd(instance_ptr);
+  }
+
+  private      static synchronized 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 {
+    native_ceph_chdir(instance_ptr, path);
+  }
+
+  private      static synchronized 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 {
+    return native_ceph_listdir(instance_ptr, dir);
+  }
+
+  private static synchronized 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 {
+    native_ceph_link(instance_ptr, oldpath, newpath);
+  }
+
+  private      static synchronized 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 {
+    native_ceph_unlink(instance_ptr, path);
+  }
+
+  private      static synchronized 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 {
+    native_ceph_rename(instance_ptr, from, to);
+  }
+
+  private      static synchronized 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) {
+    native_ceph_mkdir(instance_ptr, path, mode);
+  }
+
+  private      static synchronized 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) {
+    native_ceph_mkdirs(instance_ptr, path, mode);
+  }
+
+  private      static synchronized 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 {
+    native_ceph_rmdir(instance_ptr, path);
+  }
+
+  private      static synchronized native int native_ceph_rmdir(long mountp, String path);
+
+  /**
+   * Read the value of a symbolic link.
+   */
+  public String readlink(String path) throws FileNotFoundException {
+    return native_ceph_readlink(instance_ptr, path);
+  }
+
+  private static synchronized 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) {
+    native_ceph_symlink(instance_ptr, oldpath, newpath);
+  }
+
+  private static synchronized 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 lstat(String path, CephStat stat) throws FileNotFoundException {
+    native_ceph_lstat(instance_ptr, path, stat);
+  }
+
+  private      static synchronized 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 {
+    native_ceph_setattr(instance_ptr, path, stat, mask);
+  }
+
+  private      static synchronized 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 {
+    native_ceph_chmod(instance_ptr, path, mode);
+  }
+
+  private      static synchronized native int native_ceph_chmod(long mountp, String path, 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 {
+    native_ceph_truncate(instance_ptr, path, size);
+  }
+
+  private      static synchronized 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 {
+    return native_ceph_open(instance_ptr, path, flags, mode);
+  }
+
+  private      static synchronized native int native_ceph_open(long mountp, String path, int flags, int mode);
+
+  /**
+   * Close an open file.
+   *
+   * @param fd The file descriptor.
+   */
+  public void close(int fd) {
+    native_ceph_close(instance_ptr, fd);
+  }
+
+  private      static synchronized 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) {
+    return native_ceph_lseek(instance_ptr, fd, offset, whence);
+  }
+
+  private      static synchronized 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) {
+    return native_ceph_read(instance_ptr, fd, buf, size, offset);
+  }
+
+  private      static synchronized 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) {
+    return native_ceph_write(instance_ptr, fd, buf, size, offset);
+  }
+
+  private      static synchronized 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) {
+    native_ceph_ftruncate(instance_ptr, fd, size);
+  }
+
+  private      static synchronized 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) {
+    native_ceph_fsync(instance_ptr, fd, dataonly);
+  }
+
+  private      static synchronized native int native_ceph_fsync(long mountp, int fd, boolean dataonly);
+
+  /**
+   * 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) {
+    native_ceph_fstat(instance_ptr, fd, stat);
+  }
+
+  private      static synchronized native int native_ceph_fstat(long mountp, int fd, CephStat stat);
+
+  /**
+   * Synchronize the client with the file system.
+   */
+  public void sync_fs() {
+    native_ceph_sync_fs(instance_ptr);
+  }
+
+  private      static synchronized 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.
+   * @param The length of the attribute value. See description for more
+   * details.
+   */
+  public long getxattr(String path, String name, byte[] buf) throws FileNotFoundException {
+    return native_ceph_getxattr(instance_ptr, path, name, buf);
+  }
+
+  private static synchronized 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 nuf 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 {
+    return native_ceph_lgetxattr(instance_ptr, path, name, buf);
+  }
+
+  private static synchronized 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 {
+    return native_ceph_listxattr(instance_ptr, path);
+  }
+
+  private static synchronized 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 {
+    return native_ceph_llistxattr(instance_ptr, path);
+  }
+
+  private static synchronized 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 {
+    native_ceph_removexattr(instance_ptr, path, name);
+  }
+
+  private static synchronized 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 {
+    native_ceph_lremovexattr(instance_ptr, path, name);
+  }
+
+  private static synchronized 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 {
+    native_ceph_setxattr(instance_ptr, path, name, buf, size, flags);
+  }
+
+  private static synchronized 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 {
+    native_ceph_lsetxattr(instance_ptr, path, name, buf, size, flags);
+  }
+
+  private static synchronized 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) {
+    return native_ceph_get_file_stripe_unit(instance_ptr, fd);
+  }
+
+  private      static synchronized native int native_ceph_get_file_stripe_unit(long mountp, int fd);
+
+  /**
+   * Get the replication of a file.
+   *
+   * @param fd The file descriptor.
+   * @return The file replication.
+   */
+  public int get_file_replication(int fd) {
+    return native_ceph_get_file_replication(instance_ptr, fd);
+  }
+
+  private      static synchronized native int native_ceph_get_file_replication(long mountp, int fd);
+
+  /**
+   * Set the default file stripe unit.
+   *
+   * @param stripe_unit The stripe unit.
+   */
+  public void set_default_file_stripe_unit(int stripe_unit) {
+    native_ceph_set_default_file_stripe_unit(instance_ptr, stripe_unit);
+  }
+
+  private      static synchronized native int native_ceph_set_default_file_stripe_unit(long mountp, int stripe_unit);
+
+  /**
+   * Set the default file stripe count.
+   *
+   * @param stripe_count The stripe count.
+   */
+  public void set_default_file_stripe_count(int stripe_count) {
+    native_ceph_set_default_file_stripe_count(instance_ptr, stripe_count);
+  }
+
+  private      static synchronized native int native_ceph_set_default_file_stripe_count(long mountp, int stripe_count);
+
+  /**
+   * Set the default object size.
+   *
+   * @param object_size The object size.
+   */
+  public void set_default_object_size(int object_size) {
+    native_ceph_set_default_object_size(instance_ptr, object_size);
+  }
+
+  private      static synchronized native int native_ceph_set_default_object_size(long mountp, int object_size);
+
+  /**
+   * Favor reading from local replicas when possible.
+   *
+   * @param state Enable or disable localized reads.
+   */
+  public void localize_reads(boolean state) {
+    native_ceph_localize_reads(instance_ptr, state);
+  }
+
+  private static synchronized native int native_ceph_localize_reads(long mountp, boolean on);
+}
diff --git a/src/java/java/com/ceph/fs/CephNativeLoader.java b/src/java/java/com/ceph/fs/CephNativeLoader.java
new file mode 100644 (file)
index 0000000..21a54c3
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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 boolean loaded = false;
+
+  static {
+    if (!loaded) {
+      System.loadLibrary("cephfs_jni");
+      CephMount.native_initialize();
+      loaded = true;
+    }
+  }
+
+  static void checkLoaded() { assert(loaded); }
+}
diff --git a/src/java/java/com/ceph/fs/CephNotMountedException.java b/src/java/java/com/ceph/fs/CephNotMountedException.java
new file mode 100644 (file)
index 0000000..3e43a75
--- /dev/null
@@ -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.fs;
+
+import java.io.IOException;
+
+/**
+ * Ceph is not mounted.
+ */
+public class CephNotMountedException extends IOException {
+
+  /**
+   * Construct CephNotMountedException.
+   */
+  public CephNotMountedException() {
+    super();
+  }
+
+  /**
+   * Construct CephNotMountedException with message.
+   */
+  public CephNotMountedException(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 (file)
index 0000000..a2f5a74
--- /dev/null
@@ -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;
+
+/**
+ * Holds struct stat fields.
+ */
+public class CephStat {
+  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;
+
+  /*
+   * Results from the following tests:
+   *
+   *   - S_ISREG
+   *   - S_ISDIR
+   *   - S_ISLNK
+   */
+  public boolean is_file;
+  public boolean is_directory;
+  public boolean 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 (file)
index 0000000..4a37a28
--- /dev/null
@@ -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/libcephfs_jni.cc b/src/java/native/libcephfs_jni.cc
new file mode 100644 (file)
index 0000000..fe5ce75
--- /dev/null
@@ -0,0 +1,2383 @@
+/*
+ * 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 <jni.h>
+
+#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_MOUNT_CP "com/ceph/fs/CephMount"
+#define CEPH_NOTMOUNTED_CP "com/ceph/fs/CephNotMountedException"
+
+/*
+ * 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
+
+/*
+ * 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
+
+/* 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)
+
+#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;
+
+/*
+ * 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/OutOfMemoryException", 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 handle_error(JNIEnv *env, int rc)
+{
+  switch (rc) {
+  case -ENOENT:
+    cephThrowFNF(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)
+
+#define CHECK_MOUNTED_NORV(_c) do { \
+       if (!ceph_is_mounted((_c))) { \
+               THROW(env, CEPH_NOTMOUNTED_CP, "not mounted"); \
+               return; \
+       } } 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 retreive 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;
+
+/*
+ * 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);
+
+#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;
+
+       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_shutdown
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1shutdown
+       (JNIEnv *env, jclass clz, jlong j_mntp)
+{
+       struct ceph_mount_info *cmount = get_ceph_mount(j_mntp);
+       CephContext *cct = ceph_get_mount_context(cmount);
+
+       CHECK_MOUNTED_NORV(cmount);
+
+       ldout(cct, 10) << "jni: ceph_shutdown: enter" << dendl;
+
+       ceph_shutdown(cmount);
+
+       ldout(cct, 10) << "jni: ceph_shutdown: exit" << dendl;
+}
+
+/*
+ * 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);
+
+                       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 stat st;
+  jstring j_linkname;
+       int ret;
+
+       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;
+    ret = ceph_lstat(cmount, c_path, &st);
+    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[st.st_size + 1];
+    if (!linkname) {
+      env->ReleaseStringUTFChars(j_path, c_path);
+      cephThrowOutOfMemory(env, "head allocation failed");
+      return NULL;
+    }
+
+    ldout(cct, 10) << "jni: readlink: size " << st.st_size << " path " << c_path << dendl;
+
+    ret = ceph_readlink(cmount, c_path, linkname, st.st_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 > st.st_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;
+}
+
+/*
+ * 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;
+       long long time;
+       struct stat st;
+       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_lstat(cmount, c_path, &st);
+
+       ldout(cct, 10) << "jni: lstat exit ret " << ret << dendl;
+
+       env->ReleaseStringUTFChars(j_path, c_path);
+
+  if (ret) {
+    handle_error(env, ret);
+    return ret;
+  }
+
+       env->SetIntField(j_cephstat, cephstat_mode_fid, st.st_mode);
+       env->SetIntField(j_cephstat, cephstat_uid_fid, st.st_uid);
+       env->SetIntField(j_cephstat, cephstat_gid_fid, st.st_gid);
+       env->SetLongField(j_cephstat, cephstat_size_fid, st.st_size);
+       env->SetLongField(j_cephstat, cephstat_blksize_fid, st.st_blksize);
+  env->SetLongField(j_cephstat, cephstat_blocks_fid, st.st_blocks);
+
+       time = st.st_mtim.tv_sec;
+       time *= 1000;
+       time += st.st_mtim.tv_nsec / 1000000;
+       env->SetLongField(j_cephstat, cephstat_m_time_fid, time);
+
+       time = st.st_atim.tv_sec;
+       time *= 1000;
+       time += st.st_atim.tv_nsec / 1000000;
+       env->SetLongField(j_cephstat, cephstat_a_time_fid, time);
+
+       env->SetBooleanField(j_cephstat, cephstat_is_file_fid,
+                       S_ISREG(st.st_mode) ? JNI_TRUE : JNI_FALSE);
+
+       env->SetBooleanField(j_cephstat, cephstat_is_directory_fid,
+                       S_ISDIR(st.st_mode) ? JNI_TRUE : JNI_FALSE);
+
+       env->SetBooleanField(j_cephstat, cephstat_is_symlink_fid,
+                       S_ISLNK(st.st_mode) ? JNI_TRUE : JNI_FALSE);
+
+       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 stat st;
+       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(&st, 0, sizeof(st));
+
+       st.st_mode = env->GetIntField(j_cephstat, cephstat_mode_fid);
+       st.st_uid = env->GetIntField(j_cephstat, cephstat_uid_fid);
+       st.st_gid = env->GetIntField(j_cephstat, cephstat_gid_fid);
+       st.st_mtime = env->GetIntField(j_cephstat, cephstat_m_time_fid);
+       st.st_atime = env->GetIntField(j_cephstat, cephstat_a_time_fid);
+
+       ldout(cct, 10) << "jni: setattr: path " << c_path << " mask " << mask << dendl;
+
+       ret = ceph_setattr(cmount, c_path, &st, mask);
+
+       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_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_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 " << (int)j_size <<
+               " offset " << (int)j_offset << dendl;
+
+       ret = ceph_read(cmount, (int)j_fd, (char*)c_buf, (int)j_size, (int)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 " << (int)j_size <<
+               " offset " << (int)j_offset << dendl;
+
+       ret = ceph_write(cmount, (int)j_fd, (char*)c_buf, (int)j_size, (int)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_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);
+       long long time;
+       struct stat st;
+       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_fstat(cmount, (int)j_fd, &st);
+
+       ldout(cct, 10) << "jni: fstat exit ret " << ret << dendl;
+
+       if (ret) {
+    handle_error(env, ret);
+               return ret;
+  }
+
+       env->SetIntField(j_cephstat, cephstat_mode_fid, st.st_mode);
+       env->SetIntField(j_cephstat, cephstat_uid_fid, st.st_uid);
+       env->SetIntField(j_cephstat, cephstat_gid_fid, st.st_gid);
+       env->SetLongField(j_cephstat, cephstat_size_fid, st.st_size);
+       env->SetLongField(j_cephstat, cephstat_blksize_fid, st.st_blksize);
+       env->SetLongField(j_cephstat, cephstat_blocks_fid, st.st_blocks);
+
+       time = st.st_mtim.tv_sec;
+       time *= 1000;
+       time += st.st_mtim.tv_nsec / 1000;
+       env->SetLongField(j_cephstat, cephstat_m_time_fid, time);
+
+       time = st.st_atim.tv_sec;
+       time *= 1000;
+       time += st.st_atim.tv_nsec / 1000;
+       env->SetLongField(j_cephstat, cephstat_a_time_fid, time);
+
+       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 long 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_set_default_file_stripe_unit
+ * Signature: (JI)I
+ */
+JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1set_1default_1file_1stripe_1unit
+       (JNIEnv *env, jclass clz, jlong j_mntp, jint j_stripe_unit)
+{
+       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: set_default_file_stripe_unit: " << (int)j_stripe_unit << dendl;
+
+       ret = ceph_set_default_file_stripe_unit(cmount, (int)j_stripe_unit);
+
+       ldout(cct, 10) << "jni: set_default_file_stripe_unit: exit ret " << ret << dendl;
+
+  if (ret)
+    handle_error(env, ret);
+
+       return ret;
+}
+
+/*
+ * Class:     com_ceph_fs_CephMount
+ * Method:    native_ceph_set_default_file_stripe_count
+ * Signature: (JI)I
+ */
+JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1set_1default_1file_1stripe_1count
+       (JNIEnv *env, jclass clz, jlong j_mntp, jint j_stripe_count)
+{
+       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: set_default_file_stripe_count: " << (int)j_stripe_count << dendl;
+
+       ret = ceph_set_default_file_stripe_count(cmount, (int)j_stripe_count);
+
+       ldout(cct, 10) << "jni: set_default_file_stripe_count: exit ret " << ret << dendl;
+
+  if (ret)
+    handle_error(env, ret);
+
+       return ret;
+}
+
+/*
+ * Class:     com_ceph_fs_CephMount
+ * Method:    native_ceph_set_default_object_size
+ * Signature: (JI)I
+ */
+JNIEXPORT jint JNICALL Java_com_ceph_fs_CephMount_native_1ceph_1set_1default_1object_1size
+       (JNIEnv *env, jclass clz, jlong j_mntp, jint j_object_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: set_default_object_size: " << (int)j_object_size << dendl;
+
+       ret = ceph_set_default_object_size(cmount, (int)j_object_size);
+
+       ldout(cct, 10) << "jni: set_default_object_size: exit ret " << ret << dendl;
+
+  if (ret)
+    handle_error(env, ret);
+
+       return ret;
+}
+
+/*
+ * 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;
+}
diff --git a/src/java/test/CephMountCreateTest.java b/src/java/test/CephMountCreateTest.java
new file mode 100644 (file)
index 0000000..1a9c488
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+import java.io.FileNotFoundException;
+import org.junit.*;
+import static org.junit.Assert.*;
+
+import com.ceph.fs.*;
+
+/*
+ * 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.mount(root);
+    return mount;
+  }
+
+  @Test
+  public void test_CephMountCreate() throws Exception {
+    CephMount mount;
+    boolean found;
+
+    /* root dir has more than one dir */
+    mount = setupMount(null);
+
+    try {
+      mount.rmdir("/libcephfs_java_test_dir");
+    } catch (FileNotFoundException e) {}
+    mount.mkdirs("/libcephfs_java_test_dir", 777);
+    String[] subdirs = mount.listdir("/");
+    found = false;
+    for (String d : subdirs) {
+      if (d.compareTo("libcephfs_java_test_dir") == 0)
+        found = true;
+    }
+    assertTrue(found);
+    mount.shutdown();
+
+    /* changing root to empty dir */
+    mount = setupMount("/libcephfs_java_test_dir");
+
+    subdirs = mount.listdir("/");
+    found = false;
+    for (String d : subdirs) {
+      if (d.compareTo(".") != 0 && d.compareTo("..") != 0)
+        found = true;
+    }
+    assertFalse(found);
+    mount.shutdown();
+
+    /* cleanup */
+    mount = setupMount(null);
+    mount.rmdir("/libcephfs_java_test_dir");
+    mount.shutdown();
+  }
+}
diff --git a/src/java/test/CephMountTest.java b/src/java/test/CephMountTest.java
new file mode 100644 (file)
index 0000000..4f5a3e0
--- /dev/null
@@ -0,0 +1,797 @@
+/*
+ * 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.
+ */
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.UUID;
+import org.junit.*;
+import static org.junit.Assert.*;
+
+import com.ceph.fs.*;
+
+/*
+ * Coverage
+ *  - Everything is covered in at least success cases.
+ *  - link/symlink/readlink/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.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.shutdown();
+  }
+
+  /*
+   * Helper function to construct a unique path.
+   */
+  public String makePath() {
+    String path = basedir + "/" + UUID.randomUUID();
+    return path;
+  }
+
+  /*
+   * 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 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.is_directory);
+    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.is_directory);
+
+    mount.lstat(path + "/x", st);
+    assertTrue(st.is_directory);
+
+    mount.lstat(path + "/x/y", st);
+    assertTrue(st.is_directory);
+
+    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.is_directory);
+    /* remove it */
+    mount.rmdir(path);
+    /* should not exist now */
+    mount.lstat(path, st);
+  }
+
+  /*
+   * Missing
+   *
+   * readlink
+   * symlink
+   */
+
+  /*
+   * 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
+   */
+
+  @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 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);
+
+    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);
+  }
+
+  /*
+   * 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);
+  }
+
+  /*
+   * 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);
+  }
+
+  /*
+   * 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);
+    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);
+  }
+
+  /*
+   * 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, "attr1", buf1, buf1.length, mount.XATTR_CREATE);
+    mount.setxattr(path, "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("attr1") == 0) {
+        found++;
+        continue;
+      }
+      if (xattr.compareTo("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, "attr1", null);
+    byte[] out = new byte[(int)attr1_len];
+    mount.getxattr(path, "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, "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);
+  }
+
+  @Test
+  public void test_set_def_obj_size() throws Exception {
+    mount.set_default_object_size(1 << 21);
+  }
+
+  @Test
+  public void test_set_def_file_stripe_count() throws Exception {
+    mount.set_default_file_stripe_count(2);
+  }
+
+  @Test
+  public void test_set_def_file_stripe_unit() throws Exception {
+    mount.set_default_file_stripe_unit(1 << 10);
+  }
+
+}
diff --git a/src/java/test/CephUnmountedTest.java b/src/java/test/CephUnmountedTest.java
new file mode 100644 (file)
index 0000000..7b8b7cd
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * 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.
+ */
+import org.junit.*;
+import static org.junit.Assert.*;
+
+import com.ceph.fs.*;
+
+public class CephUnmountedTest {
+
+  private CephMount mount;
+
+  @Before
+  public void setup() throws Exception {
+    mount = new CephMount("admin");
+  }
+
+  @Test(expected=CephNotMountedException.class)
+  public void test_shutdown() throws Exception {
+    mount.shutdown();
+  }
+
+  @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_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_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_set_def_stripe_unit() throws Exception {
+    mount.set_default_file_stripe_unit(1);
+  }
+
+  @Test(expected=CephNotMountedException.class)
+  public void test_set_def_stripe_count() throws Exception {
+    mount.set_default_file_stripe_count(1);
+  }
+
+  @Test(expected=CephNotMountedException.class)
+  public void test_set_def_obj_size() throws Exception {
+    mount.set_default_object_size(1);
+  }
+}