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 <map>
24 #include <memory>
25
26 #include <android_runtime/Log.h>
27 #include <android-base/logging.h>
28 #include <android-base/unique_fd.h>
29 #include <jni.h>
30 #include <libappfuse/FuseAppLoop.h>
31 #include <nativehelper/ScopedLocalRef.h>
32 #include <nativehelper/ScopedPrimitiveArray.h>
33
34 #include "core_jni_helpers.h"
35
36 namespace android {
37 namespace {
38 constexpr const char* CLASS_NAME = "com/android/internal/os/FuseAppLoop";
39
40 jclass gFuseAppLoopClass;
41 jmethodID gOnCommandMethod;
42 jmethodID gOnOpenMethod;
43
44 class Callback : public fuse::FuseAppLoopCallback {
45 private:
46 typedef ScopedLocalRef<jbyteArray> LocalBytes;
47 JNIEnv* const mEnv;
48 jobject const mSelf;
49 std::map<uint64_t, std::unique_ptr<LocalBytes>> mBuffers;
50
51 public:
Callback(JNIEnv * env,jobject self)52 Callback(JNIEnv* env, jobject self) :
53 mEnv(env), mSelf(self) {}
54
OnLookup(uint64_t unique,uint64_t inode)55 void OnLookup(uint64_t unique, uint64_t inode) override {
56 CallOnCommand(FUSE_LOOKUP, unique, inode, 0, 0, nullptr);
57 }
58
OnGetAttr(uint64_t unique,uint64_t inode)59 void OnGetAttr(uint64_t unique, uint64_t inode) override {
60 CallOnCommand(FUSE_GETATTR, unique, inode, 0, 0, nullptr);
61 }
62
OnOpen(uint64_t unique,uint64_t inode)63 void OnOpen(uint64_t unique, uint64_t inode) override {
64 const jbyteArray buffer = static_cast<jbyteArray>(mEnv->CallObjectMethod(
65 mSelf, gOnOpenMethod, unique, inode));
66 CHECK(!mEnv->ExceptionCheck());
67 if (buffer == nullptr) {
68 return;
69 }
70
71 mBuffers.insert(std::make_pair(inode, std::unique_ptr<LocalBytes>(
72 new LocalBytes(mEnv, buffer))));
73 }
74
OnFsync(uint64_t unique,uint64_t inode)75 void OnFsync(uint64_t unique, uint64_t inode) override {
76 CallOnCommand(FUSE_FSYNC, unique, inode, 0, 0, nullptr);
77 }
78
OnRelease(uint64_t unique,uint64_t inode)79 void OnRelease(uint64_t unique, uint64_t inode) override {
80 mBuffers.erase(inode);
81 CallOnCommand(FUSE_RELEASE, unique, inode, 0, 0, nullptr);
82 }
83
OnRead(uint64_t unique,uint64_t inode,uint64_t offset,uint32_t size)84 void OnRead(uint64_t unique, uint64_t inode, uint64_t offset, uint32_t size) override {
85 CHECK_LE(size, static_cast<uint32_t>(fuse::kFuseMaxRead));
86
87 auto it = mBuffers.find(inode);
88 CHECK(it != mBuffers.end());
89
90 CallOnCommand(FUSE_READ, unique, inode, offset, size, it->second->get());
91 }
92
OnWrite(uint64_t unique,uint64_t inode,uint64_t offset,uint32_t size,const void * buffer)93 void OnWrite(uint64_t unique, uint64_t inode, uint64_t offset, uint32_t size,
94 const void* buffer) override {
95 CHECK_LE(size, static_cast<uint32_t>(fuse::kFuseMaxWrite));
96
97 auto it = mBuffers.find(inode);
98 CHECK(it != mBuffers.end());
99
100 jbyteArray const javaBuffer = it->second->get();
101
102 mEnv->SetByteArrayRegion(javaBuffer, 0, size, static_cast<const jbyte*>(buffer));
103 CHECK(!mEnv->ExceptionCheck());
104
105 CallOnCommand(FUSE_WRITE, unique, inode, offset, size, javaBuffer);
106 }
107
108 private:
109 // Helper function to make sure we invoke CallVoidMethod with correct size of integer arguments.
CallOnCommand(jint command,jlong unique,jlong inode,jlong offset,jint size,jobject bytes)110 void CallOnCommand(jint command, jlong unique, jlong inode, jlong offset, jint size,
111 jobject bytes) {
112 mEnv->CallVoidMethod(mSelf, gOnCommandMethod, command, unique, inode, offset, size, bytes);
113 CHECK(!mEnv->ExceptionCheck());
114 }
115 };
116
com_android_internal_os_FuseAppLoop_new(JNIEnv * env,jobject self,jint jfd)117 jlong com_android_internal_os_FuseAppLoop_new(JNIEnv* env, jobject self, jint jfd) {
118 return reinterpret_cast<jlong>(new fuse::FuseAppLoop(base::unique_fd(jfd)));
119 }
120
com_android_internal_os_FuseAppLoop_delete(JNIEnv * env,jobject self,jlong ptr)121 void com_android_internal_os_FuseAppLoop_delete(JNIEnv* env, jobject self, jlong ptr) {
122 delete reinterpret_cast<fuse::FuseAppLoop*>(ptr);
123 }
124
com_android_internal_os_FuseAppLoop_start(JNIEnv * env,jobject self,jlong ptr)125 void com_android_internal_os_FuseAppLoop_start(JNIEnv* env, jobject self, jlong ptr) {
126 Callback callback(env, self);
127 reinterpret_cast<fuse::FuseAppLoop*>(ptr)->Start(&callback);
128 }
129
com_android_internal_os_FuseAppLoop_replySimple(JNIEnv * env,jobject self,jlong ptr,jlong unique,jint result)130 void com_android_internal_os_FuseAppLoop_replySimple(
131 JNIEnv* env, jobject self, jlong ptr, jlong unique, jint result) {
132 if (!reinterpret_cast<fuse::FuseAppLoop*>(ptr)->ReplySimple(unique, result)) {
133 reinterpret_cast<fuse::FuseAppLoop*>(ptr)->Break();
134 }
135 }
136
com_android_internal_os_FuseAppLoop_replyOpen(JNIEnv * env,jobject self,jlong ptr,jlong unique,jlong fh)137 void com_android_internal_os_FuseAppLoop_replyOpen(
138 JNIEnv* env, jobject self, jlong ptr, jlong unique, jlong fh) {
139 if (!reinterpret_cast<fuse::FuseAppLoop*>(ptr)->ReplyOpen(unique, fh)) {
140 reinterpret_cast<fuse::FuseAppLoop*>(ptr)->Break();
141 }
142 }
143
com_android_internal_os_FuseAppLoop_replyLookup(JNIEnv * env,jobject self,jlong ptr,jlong unique,jlong inode,jlong size)144 void com_android_internal_os_FuseAppLoop_replyLookup(
145 JNIEnv* env, jobject self, jlong ptr, jlong unique, jlong inode, jlong size) {
146 if (!reinterpret_cast<fuse::FuseAppLoop*>(ptr)->ReplyLookup(unique, inode, size)) {
147 reinterpret_cast<fuse::FuseAppLoop*>(ptr)->Break();
148 }
149 }
150
com_android_internal_os_FuseAppLoop_replyGetAttr(JNIEnv * env,jobject self,jlong ptr,jlong unique,jlong inode,jlong size)151 void com_android_internal_os_FuseAppLoop_replyGetAttr(
152 JNIEnv* env, jobject self, jlong ptr, jlong unique, jlong inode, jlong size) {
153 if (!reinterpret_cast<fuse::FuseAppLoop*>(ptr)->ReplyGetAttr(
154 unique, inode, size, S_IFREG | 0777)) {
155 reinterpret_cast<fuse::FuseAppLoop*>(ptr)->Break();
156 }
157 }
158
com_android_internal_os_FuseAppLoop_replyWrite(JNIEnv * env,jobject self,jlong ptr,jlong unique,jint size)159 void com_android_internal_os_FuseAppLoop_replyWrite(
160 JNIEnv* env, jobject self, jlong ptr, jlong unique, jint size) {
161 if (!reinterpret_cast<fuse::FuseAppLoop*>(ptr)->ReplyWrite(unique, size)) {
162 reinterpret_cast<fuse::FuseAppLoop*>(ptr)->Break();
163 }
164 }
165
com_android_internal_os_FuseAppLoop_replyRead(JNIEnv * env,jobject self,jlong ptr,jlong unique,jint size,jbyteArray data)166 void com_android_internal_os_FuseAppLoop_replyRead(
167 JNIEnv* env, jobject self, jlong ptr, jlong unique, jint size, jbyteArray data) {
168 ScopedByteArrayRO array(env, data);
169 CHECK(size >= 0);
170 CHECK(static_cast<size_t>(size) < array.size());
171 if (!reinterpret_cast<fuse::FuseAppLoop*>(ptr)->ReplyRead(unique, size, array.get())) {
172 reinterpret_cast<fuse::FuseAppLoop*>(ptr)->Break();
173 }
174 }
175
176 const JNINativeMethod methods[] = {
177 {
178 "native_new",
179 "(I)J",
180 reinterpret_cast<void*>(com_android_internal_os_FuseAppLoop_new)
181 },
182 {
183 "native_delete",
184 "(J)V",
185 reinterpret_cast<void*>(com_android_internal_os_FuseAppLoop_delete)
186 },
187 {
188 "native_start",
189 "(J)V",
190 reinterpret_cast<void*>(com_android_internal_os_FuseAppLoop_start)
191 },
192 {
193 "native_replySimple",
194 "(JJI)V",
195 reinterpret_cast<void*>(com_android_internal_os_FuseAppLoop_replySimple)
196 },
197 {
198 "native_replyOpen",
199 "(JJJ)V",
200 reinterpret_cast<void*>(com_android_internal_os_FuseAppLoop_replyOpen)
201 },
202 {
203 "native_replyLookup",
204 "(JJJJ)V",
205 reinterpret_cast<void*>(com_android_internal_os_FuseAppLoop_replyLookup)
206 },
207 {
208 "native_replyGetAttr",
209 "(JJJJ)V",
210 reinterpret_cast<void*>(com_android_internal_os_FuseAppLoop_replyGetAttr)
211 },
212 {
213 "native_replyRead",
214 "(JJI[B)V",
215 reinterpret_cast<void*>(com_android_internal_os_FuseAppLoop_replyRead)
216 },
217 {
218 "native_replyWrite",
219 "(JJI)V",
220 reinterpret_cast<void*>(com_android_internal_os_FuseAppLoop_replyWrite)
221 },
222 };
223 } // namespace
224
register_com_android_internal_os_FuseAppLoop(JNIEnv * env)225 int register_com_android_internal_os_FuseAppLoop(JNIEnv* env) {
226 gFuseAppLoopClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, CLASS_NAME));
227 gOnCommandMethod = GetMethodIDOrDie(env, gFuseAppLoopClass, "onCommand", "(IJJJI[B)V");
228 gOnOpenMethod = GetMethodIDOrDie(env, gFuseAppLoopClass, "onOpen", "(JJ)[B");
229 RegisterMethodsOrDie(env, CLASS_NAME, methods, NELEM(methods));
230 return 0;
231 }
232 } // namespace android
233