com_android_internal_os_FuseAppLoop.cpp revision 9fb00183a04036a58ee208f5bfd6c9768982f0aa
1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "FuseAppLoopJNI"
18#define LOG_NDEBUG 0
19
20#include <stdlib.h>
21#include <sys/stat.h>
22
23#include <android_runtime/Log.h>
24#include <android-base/logging.h>
25#include <android-base/unique_fd.h>
26#include <jni.h>
27#include <libappfuse/FuseAppLoop.h>
28#include <nativehelper/ScopedLocalRef.h>
29
30#include "core_jni_helpers.h"
31
32namespace android {
33
34namespace {
35
36constexpr const char* CLASS_NAME = "com/android/internal/os/FuseAppLoop";
37
38jclass gFuseAppLoopClass;
39jmethodID gOnGetSizeMethod;
40jmethodID gOnOpenMethod;
41jmethodID gOnFsyncMethod;
42jmethodID gOnReleaseMethod;
43jmethodID gOnReadMethod;
44jmethodID gOnWriteMethod;
45
46class Callback : public fuse::FuseAppLoopCallback {
47private:
48    static constexpr size_t kBufferSize = std::max(fuse::kFuseMaxWrite, fuse::kFuseMaxRead);
49    static_assert(kBufferSize <= INT32_MAX, "kBufferSize should be fit in int32_t.");
50
51    JNIEnv* const mEnv;
52    jobject const mSelf;
53    ScopedLocalRef<jbyteArray> mJniBuffer;
54
55    template <typename T>
56    T checkException(T result) const {
57        if (mEnv->ExceptionCheck()) {
58            LOGE_EX(mEnv, nullptr);
59            mEnv->ExceptionClear();
60            return -EIO;
61        }
62        return result;
63    }
64
65public:
66    Callback(JNIEnv* env, jobject self) :
67        mEnv(env),
68        mSelf(self),
69        mJniBuffer(env, nullptr) {}
70
71    bool Init() {
72        mJniBuffer.reset(mEnv->NewByteArray(kBufferSize));
73        return mJniBuffer.get();
74    }
75
76    bool IsActive() override {
77        return true;
78    }
79
80    int64_t OnGetSize(uint64_t inode) override {
81        return checkException(mEnv->CallLongMethod(mSelf, gOnGetSizeMethod, inode));
82    }
83
84    int32_t OnOpen(uint64_t inode) override {
85        return checkException(mEnv->CallIntMethod(mSelf, gOnOpenMethod, inode));
86    }
87
88    int32_t OnFsync(uint64_t inode) override {
89        return checkException(mEnv->CallIntMethod(mSelf, gOnFsyncMethod, inode));
90    }
91
92    int32_t OnRelease(uint64_t inode) override {
93        return checkException(mEnv->CallIntMethod(mSelf, gOnReleaseMethod, inode));
94    }
95
96    int32_t OnRead(uint64_t inode, uint64_t offset, uint32_t size, void* buffer) override {
97        CHECK_LE(size, static_cast<uint32_t>(kBufferSize));
98        const int32_t result = checkException(mEnv->CallIntMethod(
99                mSelf, gOnReadMethod, inode, offset, size, mJniBuffer.get()));
100        if (result <= 0) {
101            return result;
102        }
103        if (result > static_cast<int32_t>(size)) {
104            LOG(ERROR) << "Returned size is too large.";
105            return -EIO;
106        }
107
108        mEnv->GetByteArrayRegion(mJniBuffer.get(), 0, result, static_cast<jbyte*>(buffer));
109        CHECK(!mEnv->ExceptionCheck());
110
111        return checkException(result);
112    }
113
114    int32_t OnWrite(uint64_t inode, uint64_t offset, uint32_t size, const void* buffer) override {
115        CHECK_LE(size, static_cast<uint32_t>(kBufferSize));
116
117        mEnv->SetByteArrayRegion(mJniBuffer.get(), 0, size, static_cast<const jbyte*>(buffer));
118        CHECK(!mEnv->ExceptionCheck());
119
120        return checkException(mEnv->CallIntMethod(
121                mSelf, gOnWriteMethod, inode, offset, size, mJniBuffer.get()));
122    }
123};
124
125jboolean com_android_internal_os_FuseAppLoop_start_loop(JNIEnv* env, jobject self, jint jfd) {
126    base::unique_fd fd(jfd);
127    Callback callback(env, self);
128
129    if (!callback.Init()) {
130        LOG(ERROR) << "Failed to init callback";
131        return JNI_FALSE;
132    }
133
134    return fuse::StartFuseAppLoop(fd.release(), &callback);
135}
136
137const JNINativeMethod methods[] = {
138    {
139        "native_start_loop",
140        "(I)Z",
141        (void *) com_android_internal_os_FuseAppLoop_start_loop
142    }
143};
144
145}  // namespace
146
147int register_com_android_internal_os_FuseAppLoop(JNIEnv* env) {
148    gFuseAppLoopClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, CLASS_NAME));
149    gOnGetSizeMethod = GetMethodIDOrDie(env, gFuseAppLoopClass, "onGetSize", "(J)J");
150    gOnOpenMethod = GetMethodIDOrDie(env, gFuseAppLoopClass, "onOpen", "(J)I");
151    gOnFsyncMethod = GetMethodIDOrDie(env, gFuseAppLoopClass, "onFsync", "(J)I");
152    gOnReleaseMethod = GetMethodIDOrDie(env, gFuseAppLoopClass, "onRelease", "(J)I");
153    gOnReadMethod = GetMethodIDOrDie(env, gFuseAppLoopClass, "onRead", "(JJI[B)I");
154    gOnWriteMethod = GetMethodIDOrDie(env, gFuseAppLoopClass, "onWrite", "(JJI[B)I");
155    RegisterMethodsOrDie(env, CLASS_NAME, methods, NELEM(methods));
156    return 0;
157}
158
159}  // namespace android
160