~xdavidwu/saf-cephfs

023f06863ccf958db0713a81f0d704ef31a26515 — xdavidwu 3 years ago
initial commit
42 files changed, 2620 insertions(+), 0 deletions(-)

A .gitignore
A COMPILING-CEPH.md
A LICENSE
A README.md
A build.gradle
A ceph.patch
A gradle/wrapper/gradle-wrapper.jar
A gradle/wrapper/gradle-wrapper.properties
A gradlew
A gradlew.bat
A src/main/AndroidManifest.xml
A src/main/java/com/ceph/crush/Bucket.java
A src/main/java/com/ceph/fs/CephAlreadyMountedException.java
A src/main/java/com/ceph/fs/CephFileAlreadyExistsException.java
A src/main/java/com/ceph/fs/CephFileExtent.java
A src/main/java/com/ceph/fs/CephMount.java
A src/main/java/com/ceph/fs/CephNativeLoader.java
A src/main/java/com/ceph/fs/CephNotDirectoryException.java
A src/main/java/com/ceph/fs/CephNotMountedException.java
A src/main/java/com/ceph/fs/CephPoolException.java
A src/main/java/com/ceph/fs/CephStat.java
A src/main/java/com/ceph/fs/CephStatVFS.java
A src/main/java/org/safcephfs/CephFSDocumentsProvider.java
A src/main/java/org/safcephfs/MainActivity.java
A src/main/java/org/safcephfs/ToastThread.java
A src/main/jniLibs/arm64-v8a/libboost_filesystem.so
A src/main/jniLibs/arm64-v8a/libboost_iostreams.so
A src/main/jniLibs/arm64-v8a/libboost_thread.so
A src/main/jniLibs/arm64-v8a/libc++_shared.so
A src/main/jniLibs/arm64-v8a/libcephfs.so
A src/main/jniLibs/arm64-v8a/libcephfs_jni.so
A src/main/jniLibs/arm64-v8a/libcrc32.so
A src/main/jniLibs/arm64-v8a/libcrypto_1_1.so
A src/main/jniLibs/arm64-v8a/libfmt.so
A src/main/res/mipmap-hdpi/sym_def_app_icon.png
A src/main/res/mipmap-ldpi/sym_def_app_icon.png
A src/main/res/mipmap-mdpi/sym_def_app_icon.png
A src/main/res/mipmap-xhdpi/sym_def_app_icon.png
A src/main/res/mipmap-xxhdpi/sym_def_app_icon.png
A src/main/res/mipmap-xxxhdpi/sym_def_app_icon.png
A src/main/res/values/strings.xml
A src/main/res/xml/main_pre.xml
A  => .gitignore +84 -0
@@ 1,84 @@
# Built application files
*.apk
*.ap_
*.aab

# Files for the ART/Dalvik VM
*.dex

# Java class files
*.class

# Generated files
bin/
gen/
out/
release/

# Gradle files
.gradle/
build/

# Local configuration file (sdk path, etc)
local.properties

# Proguard folder generated by Eclipse
proguard/

# Log Files
*.log

# Android Studio Navigation editor temp files
.navigation/

# Android Studio captures folder
captures/

# IntelliJ
*.iml
.idea/workspace.xml
.idea/tasks.xml
.idea/gradle.xml
.idea/assetWizardSettings.xml
.idea/dictionaries
.idea/libraries
# Android Studio 3 in .gitignore file.
.idea/caches
.idea/modules.xml
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
.idea/navEditor.xml

# Keystore files
# Uncomment the following lines if you do not want to check your keystore files in.
#*.jks
#*.keystore

# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild

# Google Services (e.g. APIs or Firebase)
# google-services.json

# Freeline
freeline.py
freeline/
freeline_project_description.json

# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md

# Version control
vcs.xml

# lint
lint/intermediates/
lint/generated/
lint/outputs/
lint/tmp/
# lint/reports/

*.args

A  => COMPILING-CEPH.md +15 -0
@@ 1,15 @@
# Compile instruction for Ceph libraries used in this project

Ceph libraries in this project is built by:

Install android-ndk android-cmake android-aarch64-boost android-aarch64-openssl AUR packages on Archlinux.

Under Ceph 15.2.5 source with ceph.patch applied:

```sh
mkdir build && cd build
android-aarch64-cmake -DWITH_MANPAGE=OFF -DWITH_RDMA=OFF -DWITH_LEVELDB=OFF -DWITH_KVS=OFF -DWITH_FUSE=OFF -DWITH_BLUESTORE=OFF -DWITH_XFS=OFF -DWITH_RBD=OFF -DWITH_OPENLDAP=OFF -DWITH_RADOSGW=OFF -DWITH_LZ4=OFF -DWITH_KRBD=OFF -DWITH_LTTNG=OFF -DWITH_MGR=OFF -DWITH_BABELTRACE=OFF -DWITH_CEPHFS=OFF -DWITH_LIBRADOSSTRIPER=OFF -DWITH_TESTS=OFF -DWITH_REENTRANT_STRSIGNAL=ON -DWITH_SYSTEMD=OFF -DWITH_MGR_DASHBOARD_FRONTEND=OFF -DWITH_RADOSGW_KAFKA_ENDPOINT=OFF -D WITH_RADOSGW_AMQP_ENDPOINT=OFF -DWITH_RADOSGW_BEAST_OPENSSL=OFF -DWITH_RADOSGW_BEAST_FRONTEND=OFF -DDEBUG_GATHER=OFF -DWITH_CEPHFS_JAVA=ON -DOPENSSL_INCLUDE_DIR:FILEPATH=/opt/android-libs/aarch64/include/ -DOPENSSL_CRYPTO_LIBRARY=/opt/android-libs/aarch64/lib/libcrypto.so -DWITH_SYSTEM_BOOST=ON -DBoost_INCLUDE_DIR=/opt/android-libs/aarch64/include/ -DBoost_LIBRARY_DIR=/opt/android-libs/aarch64/lib/ -DWITH_BOOST_CONTEXT=OFF ..
make java
```

ceph.patch contains some hacks to avoid dependency of unused targets, to use boost::filesystem instead of std::filesystem as needed library is not in NDK yet, and to fix build with Android bionic libc.

A  => LICENSE +21 -0
@@ 1,21 @@
MIT License

Copyright (c) 2020 xdavidwu

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.

A  => README.md +29 -0
@@ 1,29 @@
# saf-cephfs

[WIP] Access CephFS from Android Storage Access Framework (SAF), with libcephfs-jni

Currently only for arm64-v8a

## Libraries sources

Native libraries bundled are built from:

* libboost_\*.so: Archlinx User Repository (AUR) package `android-aarch64-boost` (1.71.0-1)
* libcrypto\_1\_1.so: AUR package `android-aarch64-openssl` (1.1.1.g-1)
* libc++\_shared.so: copied from NDK r21.d
* libcrc32.so, libcephfs.so, libceph-common.so, libcephfs\_jni.so, libfmt.so: built from Ceph 15.2.5, see [COMPILING-CEPH.md](COMPILING-CEPH.md) for how I built it

Java libraries com.ceph.\* is copied from Ceph 15.2.5 source code.

## Current features

* Traverse the tree under specified path

## Notes

This is a WIP, and the code is dirty and full of debugging lines.
Many things still don't work.

### What's tested and worked so far

* Listing files, changing directories

A  => build.gradle +37 -0
@@ 1,37 @@
buildscript {
	repositories {
		google()
		jcenter()
		maven {
			url 'https://oss.jfrog.org/artifactory/oss-snapshot-local/'
		}
	}
	dependencies {
		classpath 'com.android.tools.build:gradle:3.4.1'
	}
}
apply plugin: 'com.android.application'

android {
	compileSdkVersion 'android-30'
	buildToolsVersion '30.0.2'

	defaultConfig {
		minSdkVersion 19
		targetSdkVersion 30
	}

	buildTypes {
		release {
			minifyEnabled false
			proguardFile getDefaultProguardFile('proguard-android.txt')
		}
	}
}

allprojects {
	repositories {
		google()
		jcenter()
	}
}

A  => ceph.patch +242 -0
@@ 1,242 @@
diff '--color=auto' -Naur '--exclude=build' '--exclude=build-android-aarch64' '--exclude=boost' '--exclude=qa' '--exclude=90-ceph-osd.conf' ceph-15.2.5/cmake/modules/CephChecks.cmake ceph-15.2.5-android/cmake/modules/CephChecks.cmake
--- ceph-15.2.5/cmake/modules/CephChecks.cmake	2020-09-16 02:57:02.000000000 +0800
+++ ceph-15.2.5-android/cmake/modules/CephChecks.cmake	2020-10-09 15:54:41.488684672 +0800
@@ -74,7 +74,7 @@
 if(HAVE_POSIX_TIMERS)
   find_library(RT_LIBRARY NAMES rt)
 endif()
-check_symbol_exists(res_nquery "resolv.h" HAVE_RES_NQUERY)
+#check_symbol_exists(res_nquery "resolv.h" HAVE_RES_NQUERY)
 check_symbol_exists(F_SETPIPE_SZ "linux/fcntl.h" CEPH_HAVE_SETPIPE_SZ)
 check_symbol_exists(__func__ "" HAVE_FUNC)
 check_symbol_exists(__PRETTY_FUNCTION__ "" HAVE_PRETTY_FUNC)
diff '--color=auto' -Naur '--exclude=build' '--exclude=build-android-aarch64' '--exclude=boost' '--exclude=qa' '--exclude=90-ceph-osd.conf' ceph-15.2.5/CMakeLists.txt ceph-15.2.5-android/CMakeLists.txt
--- ceph-15.2.5/CMakeLists.txt	2020-09-16 02:57:02.000000000 +0800
+++ ceph-15.2.5-android/CMakeLists.txt	2020-10-09 17:40:39.627422756 +0800
@@ -260,7 +260,7 @@
   find_file(HAVE_LEVELDB_FILTER_POLICY leveldb/filter_policy.h PATHS ${LEVELDB_INCLUDE_DIR})
 endif(WITH_LEVELDB)
 
-find_package(snappy REQUIRED)
+#find_package(snappy REQUIRED)
 
 option(WITH_BROTLI "Brotli compression support" OFF)
 if(WITH_BROTLI)
@@ -319,10 +319,10 @@
   set(LIBTCMALLOC_MISSING_ALIGNED_ALLOC ON)
 endif()
 
-find_package(CURL REQUIRED)
-set(CMAKE_REQUIRED_INCLUDES ${CURL_INCLUDE_DIRS})
-set(CMAKE_REQUIRED_LIBRARIES ${CURL_LIBRARIES})
-CHECK_SYMBOL_EXISTS(curl_multi_wait curl/curl.h HAVE_CURL_MULTI_WAIT)
+#find_package(CURL REQUIRED)
+#set(CMAKE_REQUIRED_INCLUDES ${CURL_INCLUDE_DIRS})
+#set(CMAKE_REQUIRED_LIBRARIES ${CURL_LIBRARIES})
+#CHECK_SYMBOL_EXISTS(curl_multi_wait curl/curl.h HAVE_CURL_MULTI_WAIT)
 
 find_package(OpenSSL REQUIRED)
 set(CRYPTO_LIBS OpenSSL::Crypto)
@@ -429,8 +429,8 @@
 elseif(NOT WITH_PYTHON3 STREQUAL "3")
   set(find_python3_exact "EXACT")
 endif()
-find_package(Python3 ${WITH_PYTHON3} ${find_python3_exact} REQUIRED
-  COMPONENTS Interpreter Development)
+#find_package(Python3 ${WITH_PYTHON3} ${find_python3_exact} REQUIRED
+#  COMPONENTS Interpreter Development)
 unset(find_python3_exact)
 
 option(WITH_MGR "ceph-mgr is enabled" ON)
@@ -556,7 +556,7 @@
 # Boost::thread depends on Boost::atomic, so list it explicitly.
 set(BOOST_COMPONENTS
   atomic chrono thread system regex random program_options date_time
-  iostreams)
+  iostreams filesystem)
 set(BOOST_HEADER_COMPONENTS container)
 
 if(WITH_MGR)
diff '--color=auto' -Naur '--exclude=build' '--exclude=build-android-aarch64' '--exclude=boost' '--exclude=qa' '--exclude=90-ceph-osd.conf' ceph-15.2.5/src/client/Client.cc ceph-15.2.5-android/src/client/Client.cc
--- ceph-15.2.5/src/client/Client.cc	2020-09-16 02:57:02.000000000 +0800
+++ ceph-15.2.5-android/src/client/Client.cc	2020-10-09 15:42:09.299181519 +0800
@@ -1,3 +1,4 @@
+#define IFTODT(x) ((x) >> 12 & 017)
 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
 // vim: ts=8 sw=2 smarttab
 /*
diff '--color=auto' -Naur '--exclude=build' '--exclude=build-android-aarch64' '--exclude=boost' '--exclude=qa' '--exclude=90-ceph-osd.conf' ceph-15.2.5/src/CMakeLists.txt ceph-15.2.5-android/src/CMakeLists.txt
--- ceph-15.2.5/src/CMakeLists.txt	2020-09-16 02:57:02.000000000 +0800
+++ ceph-15.2.5-android/src/CMakeLists.txt	2020-10-09 17:39:35.463005918 +0800
@@ -374,7 +374,7 @@
   Boost::program_options
   Boost::date_time
   Boost::iostreams
-  StdFilesystem::filesystem
+  Boost::filesystem
   fmt::fmt
   ${BLKID_LIBRARIES}
   ${Backtrace_LIBRARIES}
@@ -505,7 +505,7 @@
   set(CEPH_BUILD_VIRTUALENV ${CMAKE_BINARY_DIR})
 endif()
 
-add_subdirectory(pybind)
+#add_subdirectory(pybind)
 add_subdirectory(ceph-volume)
 add_subdirectory(python-common)
 add_subdirectory(cephadm)
diff '--color=auto' -Naur '--exclude=build' '--exclude=build-android-aarch64' '--exclude=boost' '--exclude=qa' '--exclude=90-ceph-osd.conf' ceph-15.2.5/src/common/addr_parsing.c ceph-15.2.5-android/src/common/addr_parsing.c
--- ceph-15.2.5/src/common/addr_parsing.c	2020-09-16 02:57:02.000000000 +0800
+++ ceph-15.2.5-android/src/common/addr_parsing.c	2020-10-09 15:45:32.350180015 +0800
@@ -1,3 +1,4 @@
+#include <linux/in.h>
 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 
 // vim: ts=8 sw=2 smarttab
 /*
diff '--color=auto' -Naur '--exclude=build' '--exclude=build-android-aarch64' '--exclude=boost' '--exclude=qa' '--exclude=90-ceph-osd.conf' ceph-15.2.5/src/common/blkdev.cc ceph-15.2.5-android/src/common/blkdev.cc
--- ceph-15.2.5/src/common/blkdev.cc	2020-09-16 02:57:02.000000000 +0800
+++ ceph-15.2.5-android/src/common/blkdev.cc	2020-10-09 16:31:04.381488339 +0800
@@ -61,7 +61,7 @@
 
 #include "common/blkdev.h"
 
-#ifdef __linux__
+#if defined(__linux__) && !defined(__ANDROID__)
 #include <libudev.h>
 #include <linux/fs.h>
 #include <linux/kdev_t.h>
@@ -104,7 +104,7 @@
   return 0;
 }
 
-#ifdef __linux__
+#if defined(__linux__) && !defined(__ANDROID__)
 
 const char *BlkDev::sysfsdir() const {
   return "/sys";
@@ -1168,12 +1168,12 @@
   return false;
 }
 
-int BlkDev::discard(int fd, int64_t offset, int64_t len) const
+int BlkDev::discard(int64_t offset, int64_t len) const
 {
   return -EOPNOTSUPP;
 }
 
-bool BlkDev::is_rotational(const char *devname) const
+bool BlkDev::is_rotational() const
 {
   return false;
 }
diff '--color=auto' -Naur '--exclude=build' '--exclude=build-android-aarch64' '--exclude=boost' '--exclude=qa' '--exclude=90-ceph-osd.conf' ceph-15.2.5/src/common/ConfUtils.cc ceph-15.2.5-android/src/common/ConfUtils.cc
--- ceph-15.2.5/src/common/ConfUtils.cc	2020-09-16 02:57:02.000000000 +0800
+++ ceph-15.2.5-android/src/common/ConfUtils.cc	2020-10-09 17:38:43.806680865 +0800
@@ -21,7 +21,7 @@
 #include <map>
 #include <sstream>
 
-#if __has_include(<filesystem>)
+/*#if __has_include(<filesystem>)
 #include <filesystem>
 namespace fs = std::filesystem;
 #elif __has_include(<experimental/filesystem>)
@@ -29,7 +29,10 @@
 namespace fs = std::experimental::filesystem;
 #else
 #error std::filesystem not available!
-#endif
+#endif*/
+
+#include <boost/filesystem.hpp>
+namespace fs = boost::filesystem;
 
 #include <boost/algorithm/string.hpp>
 #include <boost/algorithm/string/trim_all.hpp>
@@ -122,7 +125,7 @@
       return -EINVAL;
     }
   } catch (const fs::filesystem_error& e) {
-    std::error_code ec;
+    boost::system::error_code ec;
     auto is_other = fs::is_other(fname, ec);
     if (!ec && is_other) {
       // /dev/null?
diff '--color=auto' -Naur '--exclude=build' '--exclude=build-android-aarch64' '--exclude=boost' '--exclude=qa' '--exclude=90-ceph-osd.conf' ceph-15.2.5/src/common/dns_resolve.cc ceph-15.2.5-android/src/common/dns_resolve.cc
--- ceph-15.2.5/src/common/dns_resolve.cc	2020-09-16 02:57:02.000000000 +0800
+++ ceph-15.2.5-android/src/common/dns_resolve.cc	2020-10-09 16:20:06.701513735 +0800
@@ -206,8 +206,13 @@
 
 }
 
+#ifdef HAVE_RES_NQUERY
 int DNSResolver::resolve_ip_addr(CephContext *cct, res_state *res, const string& hostname, 
     entity_addr_t *addr) {
+#else
+int DNSResolver::resolve_ip_addr(CephContext *cct, void *res, const string& hostname, 
+    entity_addr_t *addr) {
+#endif
 
   u_char nsbuf[NS_PACKETSZ];
   int len;
diff '--color=auto' -Naur '--exclude=build' '--exclude=build-android-aarch64' '--exclude=boost' '--exclude=qa' '--exclude=90-ceph-osd.conf' ceph-15.2.5/src/common/dns_resolve.h ceph-15.2.5-android/src/common/dns_resolve.h
--- ceph-15.2.5/src/common/dns_resolve.h	2020-09-16 02:57:02.000000000 +0800
+++ ceph-15.2.5-android/src/common/dns_resolve.h	2020-10-09 16:19:12.409285141 +0800
@@ -142,8 +142,13 @@
     /* this private function allows to reuse the res_state structure used
      * by other function of this class
      */
+#ifdef HAVE_RES_NQUERY
     int resolve_ip_addr(CephContext *cct, res_state *res,
         const std::string& hostname, entity_addr_t *addr);
+#else
+    int resolve_ip_addr(CephContext *cct, void *res,
+        const std::string& hostname, entity_addr_t *addr);
+#endif
 
     std::string srv_protocol_to_str(SRV_Protocol proto) {
       switch (proto) {
diff '--color=auto' -Naur '--exclude=build' '--exclude=build-android-aarch64' '--exclude=boost' '--exclude=qa' '--exclude=90-ceph-osd.conf' ceph-15.2.5/src/java/CMakeLists.txt ceph-15.2.5-android/src/java/CMakeLists.txt
--- ceph-15.2.5/src/java/CMakeLists.txt	2020-09-16 02:57:02.000000000 +0800
+++ ceph-15.2.5-android/src/java/CMakeLists.txt	2020-10-09 15:15:57.392846740 +0800
@@ -1,5 +1,5 @@
 find_package(Java COMPONENTS Development REQUIRED)
-find_package(JNI REQUIRED)
+#find_package(JNI REQUIRED)
 include(UseJava)
 
 set(java_srcs
@@ -58,4 +58,4 @@
 
 add_custom_target(java DEPENDS
   libcephfs.jar
-  libcephfs_jni)
+  cephfs_jni)
diff '--color=auto' -Naur '--exclude=build' '--exclude=build-android-aarch64' '--exclude=boost' '--exclude=qa' '--exclude=90-ceph-osd.conf' ceph-15.2.5/src/mds/CInode.h ceph-15.2.5-android/src/mds/CInode.h
--- ceph-15.2.5/src/mds/CInode.h	2020-09-16 02:57:02.000000000 +0800
+++ ceph-15.2.5-android/src/mds/CInode.h	2020-10-09 15:47:19.231593102 +0800
@@ -1,3 +1,4 @@
+#define IFTODT(x) ((x) >> 12 & 017)
 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 
 // vim: ts=8 sw=2 smarttab
 /*
diff '--color=auto' -Naur '--exclude=build' '--exclude=build-android-aarch64' '--exclude=boost' '--exclude=qa' '--exclude=90-ceph-osd.conf' ceph-15.2.5/src/msg/CMakeLists.txt ceph-15.2.5-android/src/msg/CMakeLists.txt
--- ceph-15.2.5/src/msg/CMakeLists.txt	2020-09-16 02:57:02.000000000 +0800
+++ ceph-15.2.5-android/src/msg/CMakeLists.txt	2020-10-09 18:04:20.807630744 +0800
@@ -20,13 +20,13 @@
   async/frames_v2.cc
   async/net_handler.cc)
 
-if(LINUX)
+if(LINUX OR ANDROID)
   list(APPEND msg_srcs
     async/EventEpoll.cc)
 elseif(FREEBSD OR APPLE)
   list(APPEND msg_srcs
     async/EventKqueue.cc)
-endif(LINUX)
+endif(LINUX OR ANDROID)
 
 if(HAVE_RDMA)
   list(APPEND msg_srcs

A  => gradle/wrapper/gradle-wrapper.jar +0 -0
A  => gradle/wrapper/gradle-wrapper.properties +6 -0
@@ 1,6 @@
#Wed Apr 10 15:27:10 PDT 2013
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip

A  => gradlew +164 -0
@@ 1,164 @@
#!/usr/bin/env bash

##############################################################################
##
##  Gradle start up script for UN*X
##
##############################################################################

# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""

APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"

warn ( ) {
    echo "$*"
}

die ( ) {
    echo
    echo "$*"
    echo
    exit 1
}

# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
  CYGWIN* )
    cygwin=true
    ;;
  Darwin* )
    darwin=true
    ;;
  MINGW* )
    msys=true
    ;;
esac

# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi

# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
    ls=`ls -ld "$PRG"`
    link=`expr "$ls" : '.*-> \(.*\)$'`
    if expr "$link" : '/.*' > /dev/null; then
        PRG="$link"
    else
        PRG=`dirname "$PRG"`"/$link"
    fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar

# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
        # IBM's JDK on AIX uses strange locations for the executables
        JAVACMD="$JAVA_HOME/jre/sh/java"
    else
        JAVACMD="$JAVA_HOME/bin/java"
    fi
    if [ ! -x "$JAVACMD" ] ; then
        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
    fi
else
    JAVACMD="java"
    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi

# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
    MAX_FD_LIMIT=`ulimit -H -n`
    if [ $? -eq 0 ] ; then
        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
            MAX_FD="$MAX_FD_LIMIT"
        fi
        ulimit -n $MAX_FD
        if [ $? -ne 0 ] ; then
            warn "Could not set maximum file descriptor limit: $MAX_FD"
        fi
    else
        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
    fi
fi

# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi

# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`

    # We build the pattern for arguments to be converted via cygpath
    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
    SEP=""
    for dir in $ROOTDIRSRAW ; do
        ROOTDIRS="$ROOTDIRS$SEP$dir"
        SEP="|"
    done
    OURCYGPATTERN="(^($ROOTDIRS))"
    # Add a user-defined pattern to the cygpath arguments
    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
    fi
    # Now convert the arguments - kludge to limit ourselves to /bin/sh
    i=0
    for arg in "$@" ; do
        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option

        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
        else
            eval `echo args$i`="\"$arg\""
        fi
        i=$((i+1))
    done
    case $i in
        (0) set -- ;;
        (1) set -- "$args0" ;;
        (2) set -- "$args0" "$args1" ;;
        (3) set -- "$args0" "$args1" "$args2" ;;
        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
    esac
fi

# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
    JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"

exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

A  => gradlew.bat +90 -0
@@ 1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem  Gradle startup script for Windows
@rem
@rem ##########################################################################

@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal

@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=

set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto init

echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:init
@rem Get command-line arguments, handling Windowz variants

if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args

:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2

:win9xME_args_slurp
if "x%~1" == "x" goto execute

set CMD_LINE_ARGS=%*
goto execute

:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$

:execute
@rem Setup the command line

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd

:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1

:mainEnd
if "%OS%"=="Windows_NT" endlocal

:omega

A  => src/main/AndroidManifest.xml +30 -0
@@ 1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
	package="org.safcephfs"
	android:versionCode="1"
	android:versionName="1.0">

	<uses-permission android:name="android.permission.INTERNET" />

	<application android:label="@string/app_name"
		  android:icon="@mipmap/sym_def_app_icon"
		  android:allowBackup="false">
		<activity android:name="MainActivity"
			android:label="@string/app_name"
			android:theme="@android:style/Theme.DeviceDefault">
			<intent-filter>
				<action android:name="android.intent.action.MAIN" />
				<category android:name="android.intent.category.LAUNCHER" />
			</intent-filter>
		</activity>
		<provider android:name=".CephFSDocumentsProvider"
			android:authorities="org.safcephfs"
			android:permission="android.permission.MANAGE_DOCUMENTS"
			android:grantUriPermissions="true"
			android:exported="true">
			<intent-filter>
				<action android:name="android.content.action.DOCUMENTS_PROVIDER" />
			</intent-filter>
		</provider>
	</application>
</manifest>

A  => src/main/java/com/ceph/crush/Bucket.java +42 -0
@@ 1,42 @@
/*
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
package com.ceph.crush;

public class Bucket {
  private String type;
  private String name;

  public Bucket(String type, String name) {
    this.type = type;
    this.name = name;
  }

  public String getType() {
    return type;
  }

  public String getName() {
    return name;
  }

  public String toString() {
    return "bucket[" + type + "," + name + "]";
  }
}

A  => src/main/java/com/ceph/fs/CephAlreadyMountedException.java +44 -0
@@ 1,44 @@
/*
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
package com.ceph.fs;

import java.io.IOException;

/**
 * Ceph is already mounted.
 */
public class CephAlreadyMountedException extends IOException {

  private static final long serialVersionUID = 1L;

  /**
   * Construct CephAlreadyMountedException.
   */
  public CephAlreadyMountedException() {
    super();
  }

  /**
   * Construct CephAlreadyMountedException with message.
   */
  public CephAlreadyMountedException(String s) {
    super(s);
  }
}

A  => src/main/java/com/ceph/fs/CephFileAlreadyExistsException.java +44 -0
@@ 1,44 @@
/*
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
package com.ceph.fs;

import java.io.IOException;

/**
 * Ceph file/directory already exists.
 */
public class CephFileAlreadyExistsException extends IOException {

  private static final long serialVersionUID = 1L;

  /**
   * Construct CephFileAlreadyExistsException.
   */
  public CephFileAlreadyExistsException() {
    super();
  }

  /**
   * Construct CephFileAlreadyExistsException with message.
   */
  public CephFileAlreadyExistsException(String s) {
    super(s);
  }
}

A  => src/main/java/com/ceph/fs/CephFileExtent.java +66 -0
@@ 1,66 @@
/*
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
package com.ceph.fs;

import java.util.Arrays;

/**
 * Holds information about a file extent in CephFS.
 */
public class CephFileExtent {
  private long offset;
  private long length;
  private int[] osds;

  CephFileExtent(long offset, long length, int[] osds) {
    this.offset = offset;
    this.length = length;
    this.osds = osds;
  }

  /**
   * Get starting offset of extent.
   */
  public long getOffset() {
    return offset;
  }

  /**
   * Get length of extent.
   */
  public long getLength() {
    return length;
  }

  /**
   * Get list of OSDs with this extent.
   */
  public int[] getOSDs() {
    return osds;
  }

  /**
   * Pretty print.
   */
  public String toString() {
    return "extent[" + offset + "," + length + ","
      + Arrays.toString(osds) + "]";
  }
}

A  => src/main/java/com/ceph/fs/CephMount.java +1103 -0
@@ 1,1103 @@
/*
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
package com.ceph.fs;

import java.io.IOException;
import java.io.FileNotFoundException;
import java.net.InetAddress;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.lang.String;

import com.ceph.crush.Bucket;

public class CephMount {

  /*
   * Set via JNI callback in native_ceph_create
   *
   * Do not touch!
   */
  private long instance_ptr;

  /*
   * Flags for open().
   *
   * Must be synchronized with JNI if changed.
   */
  public static final int O_RDONLY    = 1;
  public static final int O_RDWR      = 2;
  public static final int O_APPEND    = 4;
  public static final int O_CREAT     = 8;
  public static final int O_TRUNC     = 16;
  public static final int O_EXCL      = 32;
  public static final int O_WRONLY    = 64;
  public static final int O_DIRECTORY = 128;

  /*
   * Whence flags for seek().
   *
   * Must be synchronized with JNI if changed.
   */
  public static final int SEEK_SET = 1;
  public static final int SEEK_CUR = 2;
  public static final int SEEK_END = 3;

  /*
   * Attribute flags for setattr().
   *
   * Must be synchronized with JNI if changed.
   */
  public static final int SETATTR_MODE  = 1;
  public static final int SETATTR_UID   = 2;
  public static final int SETATTR_GID   = 4;
  public static final int SETATTR_MTIME = 8;
  public static final int SETATTR_ATIME = 16;

  /*
   * Flags for setxattr();
   *
   * Must be synchronized with JNI if changed.
   */
  public static final int XATTR_CREATE  = 1;
  public static final int XATTR_REPLACE = 2;
  public static final int XATTR_NONE    = 3;

  /*
   * Flags for flock();
   *
   * Must be synchronized with JNI if changed.
   */
  public static final int LOCK_SH       = 1;
  public static final int LOCK_EX       = 2;
  public static final int LOCK_NB       = 4;
  public static final int LOCK_UN       = 8;

  /*
   * This is run by the class loader and will report early any problems
   * finding or linking in the shared JNI library.
   */
  static {
    loadLibrary();
  }

  static synchronized void loadLibrary() {
    CephNativeLoader.getInstance().loadLibrary();
  }

  /*
   * Package-private: called from CephNativeLoader
   */
  static native void native_initialize();

  /*
   * RW lock used for fine grained synchronization to native
   */
  private final ReentrantReadWriteLock rwlock = new ReentrantReadWriteLock();
  private final Lock rlock = rwlock.readLock();
  private final Lock wlock = rwlock.writeLock();

  /*
   * Controls clean-up synchronization between the constructor and finalize().
   * If native_ceph_create fails, then we want a call to finalize() to not
   * attempt to clean-up native context, because there is none.
   */
  private boolean initialized = false;

  /*
   * Try to clean-up. First, unmount() will catch users who forget to do the
   * unmount manually. Second, release() will destroy the entire context. It
   * is safe to call release after a failure in unmount.
   */
  protected void finalize() throws Throwable {
    if (initialized) {
      try {
        unmount();
      } catch (Exception e) {}
      try {
        native_ceph_release(instance_ptr);
      } catch (Exception e) {}
    }
    super.finalize();
  }

  /**
   * Create a new CephMount with specific client id.
   *
   * @param id client id.
   */
  public CephMount(String id) {
    native_ceph_create(this, id);
    initialized = true;
  }

  private static native int native_ceph_create(CephMount mount, String id);

  /**
   * Create a new CephMount with default client id.
   */
  public CephMount() {
    this(null);
  }

  /**
   * Activate the mount with a given root path.
   *
   * @param root The path to use as the root (pass null for "/").
   */
  public void mount(String root) {
    wlock.lock();
    try {
      native_ceph_mount(instance_ptr, root);
    } finally {
      wlock.unlock();
    }
  }

  private static native int native_ceph_mount(long mountp, String root);

  /**
   * Deactivate the mount.
   *
   * The mount can be reactivated using mount(). Configuration parameters
   * previously set are not reset.
   */
  public void unmount() {
    wlock.lock();
    try {
      native_ceph_unmount(instance_ptr);
    } finally {
      wlock.unlock();
    }
  }

  private static native int native_ceph_unmount(long mountp);

  /*
   * Private access to low-level ceph_release.
   */
  private static native int native_ceph_release(long mountp);

  /**
   * Load configuration from a file.
   *
   * @param path The path to the configuration file.
   */
  public void conf_read_file(String path) throws FileNotFoundException {
    rlock.lock();
    try {
      native_ceph_conf_read_file(instance_ptr, path);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_conf_read_file(long mountp, String path);

  /**
   * Set the value of a configuration option.
   *
   * @param option The configuration option to modify.
   * @param value The new value of the option.
   */
  public void conf_set(String option, String value) {
    rlock.lock();
    try {
      native_ceph_conf_set(instance_ptr, option, value);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_conf_set(long mountp, String option, String value);

  /**
   * Get the value of a configuration option.
   *
   * @param option The name of the configuration option.
   * @return The value of the option or null if option not found
   */
  public String conf_get(String option) {
    rlock.lock();
    try {
      return native_ceph_conf_get(instance_ptr, option);
    } finally {
      rlock.unlock();
    }
  }

  private static native String native_ceph_conf_get(long mountp, String option);

  /**
   * Get file system status.
   *
   * @param path Path to file in file system.
   * @param statvfs CephStatVFS structure to hold status.
   */
  public void statfs(String path, CephStatVFS statvfs) throws FileNotFoundException {
    rlock.lock();
    try {
      native_ceph_statfs(instance_ptr, path, statvfs);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_statfs(long mountp, String path, CephStatVFS statvfs);

  /**
   * Get the current working directory.
   *
   * @return The current working directory in Ceph.
   */
  public String getcwd() {
    rlock.lock();
    try {
      return native_ceph_getcwd(instance_ptr);
    } finally {
      rlock.unlock();
    }
  }

  private static native String native_ceph_getcwd(long mountp);

  /**
   * Set the current working directory.
   *
   * @param path The directory set as the cwd.
   */
  public void chdir(String path) throws FileNotFoundException {
    rlock.lock();
    try {
      native_ceph_chdir(instance_ptr, path);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_chdir(long mountp, String cwd);

  /**
   * List the contents of a directory.
   *
   * @param dir The directory.
   * @return List of files and directories excluding "." and "..".
   */
  public String[] listdir(String dir) throws FileNotFoundException {
    rlock.lock();
    try {
      return native_ceph_listdir(instance_ptr, dir);
    } finally {
      rlock.unlock();
    }
  }

  private static native String[] native_ceph_listdir(long mountp, String path);

  /**
   * Create a hard link to an existing file.
   *
   * @param oldpath The target path of the link.
   * @param newpath The name of the link.
   */
  public void link(String oldpath, String newpath) throws FileNotFoundException {
    rlock.lock();
    try {
      native_ceph_link(instance_ptr, oldpath, newpath);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_link(long mountp, String existing, String newname);

  /**
   * Unlink/delete a name from the file system.
   *
   * @param path The name to unlink/delete.
   */
  public void unlink(String path) throws FileNotFoundException {
    rlock.lock();
    try {
      native_ceph_unlink(instance_ptr, path);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_unlink(long mountp, String path);

  /**
   * Rename a file or directory.
   *
   * @param from The current path.
   * @param to The new path.
   */
  public void rename(String from, String to) throws FileNotFoundException {
    rlock.lock();
    try {
      native_ceph_rename(instance_ptr, from, to);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_rename(long mountp, String from, String to);

  /**
   * Create a directory.
   *
   * @param path The directory to create.
   * @param mode The mode of the new directory.
   */
  public void mkdir(String path, int mode) {
    rlock.lock();
    try {
      native_ceph_mkdir(instance_ptr, path, mode);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_mkdir(long mountp, String path, int mode);

  /**
   * Create a directory and all parents.
   *
   * @param path The directory to create.
   * @param mode The mode of the new directory.
   */
  public void mkdirs(String path, int mode) throws IOException {
    rlock.lock();
    try {
      native_ceph_mkdirs(instance_ptr, path, mode);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_mkdirs(long mountp, String path, int mode);

  /**
   * Delete a directory.
   *
   * @param path The directory to delete.
   */
  public void rmdir(String path) throws FileNotFoundException {
    rlock.lock();
    try {
      native_ceph_rmdir(instance_ptr, path);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_rmdir(long mountp, String path);

  /**
   * Read the value of a symbolic link.
   */
  public String readlink(String path) throws FileNotFoundException {
    rlock.lock();
    try {
      return native_ceph_readlink(instance_ptr, path);
    } finally {
      rlock.unlock();
    }
  }

  private static native String native_ceph_readlink(long mountp, String path);

  /**
   * Create a symbolic link.
   *
   * @param oldpath Target of the symbolic link.
   * @param newpath Name of the link.
   */
  public void symlink(String oldpath, String newpath) {
    rlock.lock();
    try {
      native_ceph_symlink(instance_ptr, oldpath, newpath);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_symlink(long mountp, String existing, String newname);

  /**
   * Get file status.
   *
   * @param path Path of file to stat.
   * @param stat CephStat structure to hold file status.
   */
  public void stat(String path, CephStat stat) throws FileNotFoundException, CephNotDirectoryException {
    rlock.lock();
    try {
      native_ceph_stat(instance_ptr, path, stat);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_stat(long mountp, String path, CephStat stat);

  /**
   * Get file status, without following symlinks.
   *
   * @param path Path of file to stat.
   * @param stat CephStat structure to hold file status.
   */
  public void lstat(String path, CephStat stat) throws FileNotFoundException, CephNotDirectoryException {
    rlock.lock();
    try {
      native_ceph_lstat(instance_ptr, path, stat);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_lstat(long mountp, String path, CephStat stat);

  /**
   * Set file attributes.
   *
   * @param path Path to file.
   * @param stat CephStat structure holding attributes.
   * @param mask Mask specifying which attributes to set.
   */
  public void setattr(String path, CephStat stat, int mask) throws FileNotFoundException {
    rlock.lock();
    try {
      native_ceph_setattr(instance_ptr, path, stat, mask);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_setattr(long mountp, String relpath, CephStat stat, int mask);

  /**
   * Change file mode.
   * 
   * @param path Path to file.
   * @param mode New mode bits.
   */
  public void chmod(String path, int mode) throws FileNotFoundException {
    rlock.lock();
    try {
      native_ceph_chmod(instance_ptr, path, mode);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_chmod(long mountp, String path, int mode);

  /**
   * Change file mode of an open file.
   *
   * @param fd The open file descriptor to change the mode bits on.
   * @param mode New mode bits.
   */
  public void fchmod(int fd, int mode) {
    rlock.lock();
    try {
      native_ceph_fchmod(instance_ptr, fd, mode);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_fchmod(long mountp, int fd, int mode);

  /**
   * Truncate a file to a specified length.
   *
   * @param path Path of the file.
   * @param size New file length.
   */
  public void truncate(String path, long size) throws FileNotFoundException {
    rlock.lock();
    try {
      native_ceph_truncate(instance_ptr, path, size);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_truncate(long mountp, String path, long size);

  /**
   * Open a file.
   *
   * @param path Path of file to open or create.
   * @param flags Open flags.
   * @param mode Permission mode.
   * @return File descriptor.
   */
  public int open(String path, int flags, int mode) throws FileNotFoundException {
    rlock.lock();
    try {
      return native_ceph_open(instance_ptr, path, flags, mode);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_open(long mountp, String path, int flags, int mode);

  /**
   * Open a file with a specific file layout.
   *
   * @param path Path of file to open or create.
   * @param flags Open flags.
   * @param mode Permission mode.
   * @param stripe_unit File layout stripe unit size.
   * @param stripe_count File layout stripe count.
   * @param object_size Size of each object.
   * @param data_pool The target data pool.
   * @return File descriptor.
   */
  public int open(String path, int flags, int mode, int stripe_unit, int stripe_count,
      int object_size, String data_pool) throws FileNotFoundException {
    rlock.lock();
    try {
      return native_ceph_open_layout(instance_ptr, path, flags, mode, stripe_unit,
          stripe_count, object_size, data_pool);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_open_layout(long mountp, String path,
      int flags, int mode, int stripe_unit, int stripe_count, int object_size, String data_pool);

  /**
   * Close an open file.
   *
   * @param fd The file descriptor.
   */
  public void close(int fd) {
    rlock.lock();
    try {
      native_ceph_close(instance_ptr, fd);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_close(long mountp, int fd);

  /**
   * Seek to a position in a file.
   *
   * @param fd File descriptor.
   * @param offset New offset.
   * @param whence Whence value.
   * @return The new offset.
   */
  public long lseek(int fd, long offset, int whence) {
    rlock.lock();
    try {
      return native_ceph_lseek(instance_ptr, fd, offset, whence);
    } finally {
      rlock.unlock();
    }
  }

  private static native long native_ceph_lseek(long mountp, int fd, long offset, int whence);

  /**
   * Read from a file.
   *
   * @param fd The file descriptor.
   * @param buf Buffer to for data read.
   * @param size Amount of data to read into the buffer.
   * @param offset Offset to read from (-1 for current position).
   * @return The number of bytes read.
   */
  public long read(int fd, byte[] buf, long size, long offset) {
    rlock.lock();
    try {
      return native_ceph_read(instance_ptr, fd, buf, size, offset);
    } finally {
      rlock.unlock();
    }
  }

  private static native long native_ceph_read(long mountp, int fd, byte[] buf, long size, long offset);

  /**
   * Write to a file at a specific offset.
   *
   * @param fd The file descriptor.
   * @param buf Buffer to write.
   * @param size Amount of data to write.
   * @param offset Offset to write from (-1 for current position).
   * @return The number of bytes written.
   */
  public long write(int fd, byte[] buf, long size, long offset) {
    rlock.lock();
    try {
      return native_ceph_write(instance_ptr, fd, buf, size, offset);
    } finally {
      rlock.unlock();
    }
  }

  private static native long native_ceph_write(long mountp, int fd, byte[] buf, long size, long offset);

  /**
   * Truncate a file.
   *
   * @param fd File descriptor of the file to truncate.
   * @param size New file size.
   */
  public void ftruncate(int fd, long size) {
    rlock.lock();
    try {
      native_ceph_ftruncate(instance_ptr, fd, size);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_ftruncate(long mountp, int fd, long size);

  /**
   * Synchronize a file with the file system.
   *
   * @param fd File descriptor to synchronize.
   * @param dataonly Synchronize only data.
   */
  public void fsync(int fd, boolean dataonly) {
    rlock.lock();
    try {
      native_ceph_fsync(instance_ptr, fd, dataonly);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_fsync(long mountp, int fd, boolean dataonly);

  /**
   * Apply or remove an advisory lock.
   *
   * @param fd File descriptor to lock or unlock.
   * @param operation the advisory lock operation to be performed on the file
   * descriptor among LOCK_SH (shared lock), LOCK_EX (exclusive lock),
   * or LOCK_UN (remove lock). The LOCK_NB value can be ORed to perform a
   * non-blocking operation.
   * @param owner the user-supplied owner identifier (an arbitrary integer)
   */
  public void flock(int fd, int operation, long owner) throws IOException {
    rlock.lock();
    try {
      native_ceph_flock(instance_ptr, fd, operation, owner);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_flock(long mountp, int fd, int operation, long owner);

  /**
   * Get file status.
   *
   * @param fd The file descriptor.
   * @param stat The object in which to store the status.
   */
  public void fstat(int fd, CephStat stat) {
    rlock.lock();
    try {
      native_ceph_fstat(instance_ptr, fd, stat);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_fstat(long mountp, int fd, CephStat stat);

  /**
   * Synchronize the client with the file system.
   */
  public void sync_fs() {
    rlock.lock();
    try {
      native_ceph_sync_fs(instance_ptr);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_sync_fs(long mountp);

  /**
   * Get an extended attribute value.
   *
   * If the buffer is large enough to hold the entire attribute value, or
   * buf is null, the size of the value is returned.
   *
   * @param path File path.
   * @param name Name of the attribute.
   * @param buf Buffer to store attribute value.
   * @return The length of the attribute value. See description for more
   * details.
   */
  public long getxattr(String path, String name, byte[] buf) throws FileNotFoundException {
    rlock.lock();
    try {
      return native_ceph_getxattr(instance_ptr, path, name, buf);
    } finally {
      rlock.unlock();
    }
  }

  private static native long native_ceph_getxattr(long mountp, String path, String name, byte[] buf);

  /**
   * Get an extended attribute value of a symbolic link.
   *
   * If the buffer is large enough to hold the entire attribute value, or
   * buf is null, the size of the value is returned.
   *
   * @param path File path.
   * @param name Name of attribute.
   * @param buf Buffer to store attribute value.
   * @return The length of the attribute value. See description for more
   * details.
   */
  public long lgetxattr(String path, String name, byte[] buf) throws FileNotFoundException {
    rlock.lock();
    try {
      return native_ceph_lgetxattr(instance_ptr, path, name, buf);
    } finally {
      rlock.unlock();
    }
  }

  private static native long native_ceph_lgetxattr(long mountp, String path, String name, byte[] buf);

  /**
   * List extended attributes.
   *
   * @param path File path.
   * @return List of attribute names.
   */
  public String[] listxattr(String path) throws FileNotFoundException {
    rlock.lock();
    try {
      return native_ceph_listxattr(instance_ptr, path);
    } finally {
      rlock.unlock();
    }
  }

  private static native String[] native_ceph_listxattr(long mountp, String path);

  /**
   * List extended attributes of a symbolic link.
   *
   * @param path File path.
   * @return List of attribute names.
   */
  public String[] llistxattr(String path) throws FileNotFoundException {
    rlock.lock();
    try {
      return native_ceph_llistxattr(instance_ptr, path);
    } finally {
      rlock.unlock();
    }
  }

  private static native String[] native_ceph_llistxattr(long mountp, String path);

  /**
   * Remove an extended attribute.
   *
   * @param path File path.
   * @param name Name of attribute.
   */
  public void removexattr(String path, String name) throws FileNotFoundException {
    rlock.lock();
    try {
      native_ceph_removexattr(instance_ptr, path, name);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_removexattr(long mountp, String path, String name);

  /**
   * Remove an extended attribute from a symbolic link.
   *
   * @param path File path.
   * @param name Name of attribute.
   */
  public void lremovexattr(String path, String name) throws FileNotFoundException {
    rlock.lock();
    try {
      native_ceph_lremovexattr(instance_ptr, path, name);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_lremovexattr(long mountp, String path, String name);

  /**
   * Set the value of an extended attribute.
   *
   * @param path The file path.
   * @param name The attribute name.
   * @param buf The attribute value.
   * @param size The size of the attribute value.
   * @param flags Flag controlling behavior (XATTR_CREATE/REPLACE/NONE).
   */
  public void setxattr(String path, String name, byte[] buf, long size, int flags) throws FileNotFoundException {
    rlock.lock();
    try {
      native_ceph_setxattr(instance_ptr, path, name, buf, size, flags);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_setxattr(long mountp, String path, String name, byte[] buf, long size, int flags);

  /**
   * Set the value of an extended attribute on a symbolic link.
   *
   * @param path The file path.
   * @param name The attribute name.
   * @param buf The attribute value.
   * @param size The size of the attribute value.
   * @param flags Flag controlling behavior (XATTR_CREATE/REPLACE/NONE).
   */
  public void lsetxattr(String path, String name, byte[] buf, long size, int flags) throws FileNotFoundException {
    rlock.lock();
    try {
      native_ceph_lsetxattr(instance_ptr, path, name, buf, size, flags);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_lsetxattr(long mountp, String path, String name, byte[] buf, long size, int flags);

  /**
   * Get the stripe unit of a file.
   *
   * @param fd The file descriptor.
   * @return The stripe unit.
   */
  public int get_file_stripe_unit(int fd) {
    rlock.lock();
    try {
      return native_ceph_get_file_stripe_unit(instance_ptr, fd);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_get_file_stripe_unit(long mountp, int fd);

  /**
   * Get the name of the pool a file is stored in.
   *
   * @param fd An open file descriptor.
   * @return The pool name.
   */
  public String get_file_pool_name(int fd) {
    rlock.lock();
    try {
      return native_ceph_get_file_pool_name(instance_ptr, fd);
    } finally {
      rlock.unlock();
    }
  }

  private static native String native_ceph_get_file_pool_name(long mountp, int fd);
  
  /**
   * Get the default data pool of cephfs.
   * 
   * @return The pool name.
   */ 
  public String get_default_data_pool_name() {
    rlock.lock();
    try {
      return native_ceph_get_default_data_pool_name(instance_ptr);
    } finally {
      rlock.unlock();
    }
  }
  
  private static native String native_ceph_get_default_data_pool_name(long mountp);

  /**
   * Get the replication of a file.
   *
   * @param fd The file descriptor.
   * @return The file replication.
   */
  public int get_file_replication(int fd) {
    rlock.lock();
    try {
      return native_ceph_get_file_replication(instance_ptr, fd);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_get_file_replication(long mountp, int fd);

  /**
   * Favor reading from local replicas when possible.
   *
   * @param state Enable or disable localized reads.
   */
  public void localize_reads(boolean state) {
    rlock.lock();
    try {
      native_ceph_localize_reads(instance_ptr, state);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_localize_reads(long mountp, boolean on);

  /**
   * Get file layout stripe unit granularity.
   *
   * @return Stripe unit granularity.
   */
  public int get_stripe_unit_granularity() {
    rlock.lock();
    try {
      return native_ceph_get_stripe_unit_granularity(instance_ptr);
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_get_stripe_unit_granularity(long mountp);

  /**
   * Get the pool id for the named pool.
   *
   * @param name The pool name.
   * @return The pool id.
   */
  public int get_pool_id(String name) throws CephPoolException {
    rlock.lock();
    try {
      return native_ceph_get_pool_id(instance_ptr, name);
    } catch (FileNotFoundException e) {
      throw new CephPoolException("pool name " + name + " not found");
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_get_pool_id(long mountp, String name) throws FileNotFoundException;

  /**
   * Get the pool replication factor.
   *
   * @param pool_id The pool id.
   * @return Number of replicas stored in the pool.
   */
  public int get_pool_replication(int pool_id) throws CephPoolException {
    rlock.lock();
    try {
      return native_ceph_get_pool_replication(instance_ptr, pool_id);
    } catch (FileNotFoundException e) {
      throw new CephPoolException("pool id " + pool_id + " not found");
    } finally {
      rlock.unlock();
    }
  }

  private static native int native_ceph_get_pool_replication(long mountp, int pool_id) throws FileNotFoundException;

  /**
   * Get file extent containing a given offset.
   *
   * @param fd The file descriptor.
   * @param offset Offset in file.
   * @return A CephFileExtent object.
   */
  public CephFileExtent get_file_extent(int fd, long offset) {
    rlock.lock();
    try {
      return native_ceph_get_file_extent_osds(instance_ptr, fd, offset);
    } finally {
      rlock.unlock();
    }
  }

  private static native CephFileExtent native_ceph_get_file_extent_osds(long mountp, int fd, long offset);

  /**
   * Get the fully qualified CRUSH location of an OSD.
   *
   * Returns (type, name) string pairs for each device in the CRUSH bucket
   * hierarchy starting from the given OSD to the root.
   *
   * @param osd The OSD device id.
   * @return List of pairs.
   */
  public Bucket[] get_osd_crush_location(int osd) {
    rlock.lock();
    try {
      String[] parts = native_ceph_get_osd_crush_location(instance_ptr, osd);
      Bucket[] path = new Bucket[parts.length / 2];
      for (int i = 0; i < path.length; i++)
        path[i] = new Bucket(parts[i*2], parts[i*2+1]);
      return path;
    } finally {
      rlock.unlock();
    }
  }

  private static native String[] native_ceph_get_osd_crush_location(long mountp, int osd);

  /**
   * Get the network address of an OSD.
   *
   * @param osd The OSD device id.
   * @return The network address.
   */
  public InetAddress get_osd_address(int osd) {
    rlock.lock();
    try {
      return native_ceph_get_osd_addr(instance_ptr, osd);
    } finally {
      rlock.unlock();
    }
  }

  private static native InetAddress native_ceph_get_osd_addr(long mountp, int osd);
}

A  => src/main/java/com/ceph/fs/CephNativeLoader.java +93 -0
@@ 1,93 @@
/*
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
package com.ceph.fs;

class CephNativeLoader {
  private static final CephNativeLoader instance = new CephNativeLoader();
  private static boolean initialized = false;

  private static final String JNI_PATH_ENV_VAR = "CEPH_JNI_PATH";
  private static final String LIBRARY_NAME = "cephfs_jni";
  private static final String LIBRARY_FILE = "libcephfs_jni.so";

  private CephNativeLoader() {}

  public static CephNativeLoader getInstance() {
    return instance;
  }

  public synchronized void loadLibrary() {
    if (initialized)
      return;

    boolean success = false;

    /*
     * Allow a Ceph specific environment variable to force
     * the loading path.
     */
    String path = System.getenv(JNI_PATH_ENV_VAR);
    try {
      if (path != null) {
        System.out.println("Loading libcephfs-jni: " + path);
        System.load(path);
        success = true;
      } else {
        try {
          /*
           * Try default Java loading path(s)
           */
          System.out.println("Loading libcephfs-jni from default path: " +
              System.getProperty("java.library.path"));
          System.loadLibrary(LIBRARY_NAME);
          success = true;
        } catch (final UnsatisfiedLinkError ule1) {
          try {
            /*
             * Try RHEL/CentOS default path
             */
            path = "/usr/lib64/" + LIBRARY_FILE;
            System.out.println("Loading libcephfs-jni: " + path);
            System.load(path);
            success = true;
          } catch (final UnsatisfiedLinkError ule2) {
            /*
             * Try Ubuntu default path
             */
            path = "/usr/lib/jni/" + LIBRARY_FILE;
            System.out.println("Loading libcephfs-jni: " + path);
            System.load(path);
            success = true;
          }
        }
      }
    } finally {
      System.out.println("Loading libcephfs-jni: " +
          (success ? "Success!" : "Failure!"));
    }

    /*
     * Finish initialization
     */
    CephMount.native_initialize();
    initialized = true;
  }

}

A  => src/main/java/com/ceph/fs/CephNotDirectoryException.java +44 -0
@@ 1,44 @@
/*
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
package com.ceph.fs;

import java.io.IOException;

/**
 * Component of path is not a directory.
 */
public class CephNotDirectoryException extends IOException {

  private static final long serialVersionUID = 1L;

  /**
   * Construct CephNotDirectoryException.
   */
  public CephNotDirectoryException() {
    super();
  }

  /**
   * Construct CephNotDirectoryException with message.
   */
  public CephNotDirectoryException(String s) {
    super(s);
  }
}

A  => src/main/java/com/ceph/fs/CephNotMountedException.java +44 -0
@@ 1,44 @@
/*
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
package com.ceph.fs;

import java.io.IOException;

/**
 * Ceph is not mounted.
 */
public class CephNotMountedException extends IOException {

  private static final long serialVersionUID = 1L;

  /**
   * Construct CephNotMountedException.
   */
  public CephNotMountedException() {
    super();
  }

  /**
   * Construct CephNotMountedException with message.
   */
  public CephNotMountedException(String s) {
    super(s);
  }
}

A  => src/main/java/com/ceph/fs/CephPoolException.java +44 -0
@@ 1,44 @@
/*
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
package com.ceph.fs;

import java.io.IOException;

/**
 * Exception related to Ceph pool.
 */
public class CephPoolException extends IOException {

  private static final long serialVersionUID = 1L;

  /**
   * Construct CephPoolException.
   */
  public CephPoolException() {
    super();
  }

  /**
   * Construct CephPoolException with message.
   */
  public CephPoolException(String s) {
    super(s);
  }
}

A  => src/main/java/com/ceph/fs/CephStat.java +53 -0
@@ 1,53 @@
/*
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
package com.ceph.fs;

/**
 * Holds struct stat fields.
 */
public class CephStat {

  /* Set from native */
  private boolean is_file;       /* S_ISREG */
  private boolean is_directory;  /* S_ISDIR */
  private boolean is_symlink;    /* S_ISLNK */

  public int mode;
  public int uid;
  public int gid;
  public long size;
  public long blksize;
  public long blocks;
  public long a_time;
  public long m_time;

  public boolean isFile() {
    return is_file;
  }

  public boolean isDir() {
    return is_directory;
  }

  public boolean isSymlink() {
    return is_symlink;
  }

}

A  => src/main/java/com/ceph/fs/CephStatVFS.java +33 -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;
}

A  => src/main/java/org/safcephfs/CephFSDocumentsProvider.java +154 -0
@@ 1,154 @@
package org.safcephfs;

import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.StrictMode;
import android.preference.PreferenceManager;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
import android.provider.DocumentsProvider;
import android.webkit.MimeTypeMap;
import android.widget.Toast;
import android.util.Log;

import java.io.IOException;
import java.util.Vector;

import com.ceph.fs.CephMount;
import com.ceph.fs.CephStat;

public class CephFSDocumentsProvider extends DocumentsProvider {
	private String id, mon, path, key;
	private ToastThread lthread;
	
	private static final String[] DEFAULT_ROOT_PROJECTION = new String[]{
		Root.COLUMN_ROOT_ID,
		Root.COLUMN_FLAGS,
		Root.COLUMN_ICON,
		Root.COLUMN_TITLE,
		Root.COLUMN_DOCUMENT_ID,
	};

	private static final String[] DEFAULT_DOC_PROJECTION = new String[]{
		Document.COLUMN_DOCUMENT_ID,
		Document.COLUMN_DISPLAY_NAME,
		Document.COLUMN_MIME_TYPE,
		Document.COLUMN_LAST_MODIFIED,
		Document.COLUMN_SIZE
	};

	private static String getMime(String filename) {
		int idx = filename.lastIndexOf(".");
		if (idx > 0) {
			String mime = MimeTypeMap.getSingleton()
				.getMimeTypeFromExtension(
					filename.substring(idx + 1).toLowerCase()
				);
			if (mime != null) return mime;
		}
		return "application/octet-stream";
	}

	@Override
	public boolean onCreate() {
		lthread = new ToastThread(getContext());
		lthread.start();
		return true;
	}

	public ParcelFileDescriptor openDocument(String documentId,
			String mode,CancellationSignal cancellationSignal) {
		throw new UnsupportedOperationException("TODO");
	}

	public Cursor queryChildDocuments(String parentDocumentId,
			String[] projection,String sortOrder) {
		MatrixCursor result=new MatrixCursor(projection != null ? projection : DEFAULT_DOC_PROJECTION);
		Log.v("CephFS","qcf " + parentDocumentId);
		String filename = parentDocumentId.substring(parentDocumentId.indexOf("/") + 1);
		CephMount cm = new CephMount(id);
		cm.conf_set("mon_host", mon);
		cm.conf_set("key", key);
		cm.mount(path);
		try {
			String[] res = cm.listdir(filename);
			for (String entry : res) {
				CephStat cs = new CephStat();
				Log.v("CephFS","qcf " + parentDocumentId + " " + entry);
				cm.lstat(filename + "/" + entry, cs);
				MatrixCursor.RowBuilder row = result.newRow();
				row.add(Document.COLUMN_DOCUMENT_ID, parentDocumentId + '/' + entry);
				row.add(Document.COLUMN_DISPLAY_NAME, entry);
				if (cs.isDir()) {
					row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
				} else if (cs.isFile()) {
					row.add(Document.COLUMN_MIME_TYPE, getMime(entry));
				}
				row.add(Document.COLUMN_SIZE, cs.size);
				row.add(Document.COLUMN_LAST_MODIFIED, cs.m_time);
			}
		} catch (Exception e) {
			Log.e("CephFS","qcf " + parentDocumentId + " " + e.toString());
			Message msg = lthread.handler.obtainMessage();
			msg.obj = e.toString();
			lthread.handler.sendMessage(msg);
		}
		cm.unmount();
		return result;
	}

	public Cursor queryDocument(String documentId, String[] projection) {
		MatrixCursor result = new MatrixCursor(projection != null ? projection : DEFAULT_DOC_PROJECTION);
		String filename = documentId.substring(documentId.indexOf("/") + 1);
		CephMount cm = new CephMount(id);
		cm.conf_set("mon_host", mon);
		cm.conf_set("key", key);
		cm.mount(path);
		try {
			CephStat cs = new CephStat();
			Log.v("CephFS","qf " + documentId);
			cm.lstat(filename, cs);
			MatrixCursor.RowBuilder row = result.newRow();
			row.add(Document.COLUMN_DOCUMENT_ID, documentId);
			row.add(Document.COLUMN_DISPLAY_NAME, filename);
			if (cs.isDir()) {
				row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
			} else if (cs.isFile()) {
				row.add(Document.COLUMN_MIME_TYPE, getMime(filename));
			}
			row.add(Document.COLUMN_SIZE, cs.size);
			row.add(Document.COLUMN_LAST_MODIFIED, cs.m_time);
		} catch(Exception e){
			Log.e("CephFS","qf " + documentId + " " + e.toString());
			Message msg = lthread.handler.obtainMessage();
			msg.obj = e.toString();
			lthread.handler.sendMessage(msg);
		}
		cm.unmount();
		return result;
	}

	public Cursor queryRoots(String[] projection) {
		MatrixCursor result=new MatrixCursor(projection != null ? projection : DEFAULT_ROOT_PROJECTION);
		SharedPreferences settings=PreferenceManager
			.getDefaultSharedPreferences(getContext());
		mon = settings.getString("mon", "");
		key = settings.getString("key", "");
		id = settings.getString("id", "");
		path = settings.getString("path", "");
		MatrixCursor.RowBuilder row=result.newRow();
		row.add(Root.COLUMN_ROOT_ID, id + "@" + mon + ":" + path);
		row.add(Root.COLUMN_DOCUMENT_ID, "root/");
		row.add(Root.COLUMN_FLAGS, 0);
		row.add(Root.COLUMN_TITLE,"CephFS " + mon + ":" + path);
		row.add(Root.COLUMN_ICON, R.mipmap.sym_def_app_icon);
		return result;
	}
}


A  => src/main/java/org/safcephfs/MainActivity.java +76 -0
@@ 1,76 @@
package org.safcephfs;

import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.net.Uri;
import android.preference.EditTextPreference;
import android.preference.PreferenceActivity;
import android.provider.DocumentsContract;
import android.os.Bundle;
import android.util.Log;

import java.util.Arrays;

public class MainActivity extends PreferenceActivity
	implements OnSharedPreferenceChangeListener {
	private EditTextPreference monText, pathText, idText, keyText;

	private void notifyRootChanges(){
		Uri uri = DocumentsContract.buildRootsUri("org.safcephfs");
		getContentResolver().notifyChange(uri, null);
	}
	
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		addPreferencesFromResource(R.xml.main_pre);

		monText = (EditTextPreference) findPreference("mon");
		pathText = (EditTextPreference) findPreference("path");
		idText = (EditTextPreference) findPreference("id");
		keyText = (EditTextPreference) findPreference("key");

		SharedPreferences settings=getPreferenceScreen().getSharedPreferences();
		settings.registerOnSharedPreferenceChangeListener(this);
		if (!settings.getString("mon", "").equals(""))
			monText.setSummary(settings.getString("mon", ""));
		if (!settings.getString("path", "").equals(""))
			pathText.setSummary(settings.getString("path", ""));
		if (!settings.getString("id", "").equals(""))
			idText.setSummary(settings.getString("id", ""));
		if (!settings.getString("key", "").equals(""))
			keyText.setSummary(getString(R.string.key_filled));
	}

	@Override
	public void onSharedPreferenceChanged(SharedPreferences settings,
			String key) {
		notifyRootChanges();
		switch (key) {
		case "mon":
			if (settings.getString("mon", "").equals(""))
				monText.setSummary(getString(R.string.mon_summary));
			else
				monText.setSummary(settings.getString("mon", ""));
			break;
		case "path":
			if (settings.getString("path", "").equals(""))
				pathText.setSummary(getString(R.string.path_summary));
			else
				pathText.setSummary(settings.getString("path", ""));
			break;
		case "id":
			if (settings.getString("id", "").equals(""))
				idText.setSummary(getString(R.string.id_summary));
			else
				idText.setSummary(settings.getString("id", ""));
			break;
		case "key":
			if (settings.getString("key", "").equals(""))
				keyText.setSummary(getString(R.string.key_summary));
			else
				keyText.setSummary(getString(R.string.key_filled));
			break;
		}
	}
}

A  => src/main/java/org/safcephfs/ToastThread.java +27 -0
@@ 1,27 @@
package org.safcephfs;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.widget.Toast;

public class ToastThread extends Thread {
	public Handler handler;
	private Context context;

	public ToastThread(Context context){
		this.context=context;
	}

	public void run() {
		Looper.prepare();
		handler=new Handler() {
			public void handleMessage(Message msg) {
				Toast.makeText(context,(String)msg.obj,Toast.LENGTH_SHORT).show();
			}
		};
		Looper.loop();
	}
}


A  => src/main/jniLibs/arm64-v8a/libboost_filesystem.so +0 -0
A  => src/main/jniLibs/arm64-v8a/libboost_iostreams.so +0 -0
A  => src/main/jniLibs/arm64-v8a/libboost_thread.so +0 -0
A  => src/main/jniLibs/arm64-v8a/libc++_shared.so +0 -0
A  => src/main/jniLibs/arm64-v8a/libcephfs.so +0 -0
A  => src/main/jniLibs/arm64-v8a/libcephfs_jni.so +0 -0
A  => src/main/jniLibs/arm64-v8a/libcrc32.so +0 -0
A  => src/main/jniLibs/arm64-v8a/libcrypto_1_1.so +0 -0
A  => src/main/jniLibs/arm64-v8a/libfmt.so +0 -0
A  => src/main/res/mipmap-hdpi/sym_def_app_icon.png +0 -0
A  => src/main/res/mipmap-ldpi/sym_def_app_icon.png +0 -0
A  => src/main/res/mipmap-mdpi/sym_def_app_icon.png +0 -0
A  => src/main/res/mipmap-xhdpi/sym_def_app_icon.png +0 -0
A  => src/main/res/mipmap-xxhdpi/sym_def_app_icon.png +0 -0
A  => src/main/res/mipmap-xxxhdpi/sym_def_app_icon.png +0 -0
A  => src/main/res/values/strings.xml +15 -0
@@ 1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
	<string name="app_name">SAF CephFS</string>
	<string name="conn_det">Connection Details</string>
	<string name="mon">Monitors</string>
	<string name="mon_summary">Monitors to connect.</string>
	<string name="path">Path to mount</string>
	<string name="path_summary">Path on CephFS to mount.</string>
	<string name="auth">Authentication Details</string>
	<string name="id">CephX user</string>
	<string name="id_summary">CephX user to authenticate.</string>
	<string name="key">Secret key</string>
	<string name="key_summary">Secret key of CephX user.</string>
	<string name="key_filled">(filled)</string>
</resources>

A  => src/main/res/xml/main_pre.xml +20 -0
@@ 1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
	<PreferenceCategory android:title="@string/conn_det">
		<EditTextPreference android:key="mon"
			android:summary="@string/mon_summary"
			android:title="@string/mon" />
		<EditTextPreference android:key="path"
			android:summary="@string/path_summary"
			android:title="@string/path" />
	</PreferenceCategory>
	<PreferenceCategory android:title="@string/auth">
		<EditTextPreference android:key="id"
			android:summary="@string/id_summary"
			android:title="@string/id" />
		<EditTextPreference android:title="@string/key"
			android:summary="@string/key_summary"
			android:inputType="textPassword"
			android:key="key" />
	</PreferenceCategory>
</PreferenceScreen>