com_android_mtp_AppFuse.cpp revision 91e3b50636f48f0860fa7576f185fb36ec4e6dc7
1/* 2 * Copyright (C) 2015 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 specic language governing permissions and 14 * limitations under the License. 15 */ 16 17#define LOG_NDEBUG 0 18#define LOG_TAG "AppFuseJNI" 19#include "utils/Log.h" 20 21#include <assert.h> 22#include <dirent.h> 23#include <inttypes.h> 24 25#include <linux/fuse.h> 26#include <sys/stat.h> 27 28#include <map> 29 30#include "jni.h" 31#include "JNIHelp.h" 32#include "android_runtime/AndroidRuntime.h" 33 34namespace { 35 36constexpr int min(int a, int b) { 37 return a < b ? a : b; 38} 39 40// Maximum number of bytes to write in one request. 41constexpr size_t MAX_WRITE = 256 * 1024; 42 43// Largest possible request. 44// The request size is bounded by the maximum size of a FUSE_WRITE request 45// because it has the largest possible data payload. 46constexpr size_t MAX_REQUEST_SIZE = sizeof(struct fuse_in_header) + 47 sizeof(struct fuse_write_in) + MAX_WRITE; 48 49static jclass app_fuse_class; 50 51struct FuseRequest { 52 char buffer[MAX_REQUEST_SIZE]; 53 FuseRequest() {} 54 const struct fuse_in_header& header() const { 55 return *(const struct fuse_in_header*) buffer; 56 } 57 const void* data() const { 58 return (buffer + sizeof(struct fuse_in_header)); 59 } 60 size_t data_length() const { 61 return header().len - sizeof(struct fuse_in_header); 62 } 63}; 64 65class ScopedFd { 66 int mFd; 67 68public: 69 explicit ScopedFd(int fd) : mFd(fd) {} 70 ~ScopedFd() { 71 close(mFd); 72 } 73 operator int() { 74 return mFd; 75 } 76}; 77 78/** 79 * The class is used to access AppFuse class in Java from fuse handlers. 80 */ 81class AppFuse { 82public: 83 AppFuse(JNIEnv* /*env*/, jobject /*self*/) { 84 } 85 86 bool handle_fuse_request(int fd, const FuseRequest& req) { 87 ALOGV("Request op=%d", req.header().opcode); 88 switch (req.header().opcode) { 89 // TODO: Handle more operations that are enough to provide seekable 90 // FD. 91 case FUSE_INIT: 92 invoke_handler(fd, req, &AppFuse::handle_fuse_init); 93 return true; 94 case FUSE_GETATTR: 95 invoke_handler(fd, req, &AppFuse::handle_fuse_getattr); 96 return true; 97 case FUSE_FORGET: 98 return false; 99 default: { 100 ALOGV("NOTIMPL op=%d uniq=%" PRIx64 " nid=%" PRIx64 "\n", 101 req.header().opcode, 102 req.header().unique, 103 req.header().nodeid); 104 fuse_reply(fd, req.header().unique, -ENOSYS, NULL, 0); 105 return true; 106 } 107 } 108 } 109 110private: 111 int handle_fuse_init(const fuse_in_header&, 112 const fuse_init_in* in, 113 fuse_init_out* out, 114 size_t* reply_size) { 115 // Kernel 2.6.16 is the first stable kernel with struct fuse_init_out 116 // defined (fuse version 7.6). The structure is the same from 7.6 through 117 // 7.22. Beginning with 7.23, the structure increased in size and added 118 // new parameters. 119 if (in->major != FUSE_KERNEL_VERSION || in->minor < 6) { 120 ALOGE("Fuse kernel version mismatch: Kernel version %d.%d, " 121 "Expected at least %d.6", 122 in->major, in->minor, FUSE_KERNEL_VERSION); 123 return -1; 124 } 125 126 // We limit ourselves to 15 because we don't handle BATCH_FORGET yet 127 out->minor = min(in->minor, 15); 128#if defined(FUSE_COMPAT_22_INIT_OUT_SIZE) 129 // FUSE_KERNEL_VERSION >= 23. 130 131 // If the kernel only works on minor revs older than or equal to 22, 132 // then use the older structure size since this code only uses the 7.22 133 // version of the structure. 134 if (in->minor <= 22) { 135 *reply_size = FUSE_COMPAT_22_INIT_OUT_SIZE; 136 } 137#else 138 // Don't drop this line to prevent an 'unused' compile error. 139 *reply_size = sizeof(fuse_init_out); 140#endif 141 142 out->major = FUSE_KERNEL_VERSION; 143 out->max_readahead = in->max_readahead; 144 out->flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES; 145 out->max_background = 32; 146 out->congestion_threshold = 32; 147 out->max_write = MAX_WRITE; 148 149 return 0; 150 } 151 152 int handle_fuse_getattr(const fuse_in_header& header, 153 const fuse_getattr_in* /* in */, 154 fuse_attr_out* out, 155 size_t* /*unused*/) { 156 if (header.nodeid != 1) { 157 return -ENOENT; 158 } 159 out->attr_valid = 1000 * 60 * 10; 160 out->attr.ino = header.nodeid; 161 out->attr.mode = S_IFDIR | 0777; 162 out->attr.size = 0; 163 return 0; 164 } 165 166 template <typename T, typename S> 167 void invoke_handler(int fd, 168 const FuseRequest& request, 169 int (AppFuse::*handler)(const fuse_in_header&, 170 const T*, 171 S*, 172 size_t*), 173 size_t reply_size = sizeof(S)) { 174 char reply_data[reply_size]; 175 memset(reply_data, 0, reply_size); 176 const int reply_code = (this->*handler)( 177 request.header(), 178 static_cast<const T*>(request.data()), 179 reinterpret_cast<S*>(reply_data), 180 &reply_size); 181 fuse_reply( 182 fd, 183 request.header().unique, 184 reply_code, 185 reply_data, 186 reply_size); 187 } 188 189 static void fuse_reply(int fd, int unique, int reply_code, void* reply_data, 190 size_t reply_size) { 191 // Don't send any data for error case. 192 if (reply_code != 0) { 193 reply_size = 0; 194 } 195 196 struct fuse_out_header hdr; 197 hdr.len = reply_size + sizeof(hdr); 198 hdr.error = reply_code; 199 hdr.unique = unique; 200 201 struct iovec vec[2]; 202 vec[0].iov_base = &hdr; 203 vec[0].iov_len = sizeof(hdr); 204 vec[1].iov_base = reply_data; 205 vec[1].iov_len = reply_size; 206 207 const int res = writev(fd, vec, reply_size != 0 ? 2 : 1); 208 if (res < 0) { 209 ALOGE("*** REPLY FAILED *** %d\n", errno); 210 } 211 } 212}; 213 214jboolean com_android_mtp_AppFuse_start_app_fuse_loop( 215 JNIEnv* env, jobject self, jint jfd) { 216 ScopedFd fd(dup(static_cast<int>(jfd))); 217 AppFuse appfuse(env, self); 218 219 ALOGD("Start fuse loop."); 220 while (true) { 221 FuseRequest request; 222 const ssize_t result = TEMP_FAILURE_RETRY( 223 read(fd, request.buffer, sizeof(request.buffer))); 224 if (result < 0) { 225 if (errno == ENODEV) { 226 ALOGE("Someone stole our marbles!\n"); 227 return JNI_FALSE; 228 } 229 ALOGE("Failed to read bytes from FD: errno=%d\n", errno); 230 continue; 231 } 232 233 const size_t length = static_cast<size_t>(result); 234 if (length < sizeof(struct fuse_in_header)) { 235 ALOGE("request too short: len=%zu\n", length); 236 continue; 237 } 238 239 if (request.header().len != length) { 240 ALOGE("malformed header: len=%zu, hdr->len=%u\n", 241 length, request.header().len); 242 continue; 243 } 244 245 if (!appfuse.handle_fuse_request(fd, request)) { 246 return JNI_TRUE; 247 } 248 } 249} 250 251static const JNINativeMethod gMethods[] = { 252 { 253 "native_start_app_fuse_loop", 254 "(I)Z", 255 (void *) com_android_mtp_AppFuse_start_app_fuse_loop 256 } 257}; 258 259} 260 261jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) { 262 JNIEnv* env = nullptr; 263 if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { 264 ALOGE("ERROR: GetEnv failed\n"); 265 return -1; 266 267 } 268 assert(env != nullptr); 269 270 jclass clazz = env->FindClass("com/android/mtp/AppFuse"); 271 if (clazz == nullptr) { 272 ALOGE("Can't find com/android/mtp/AppFuse"); 273 return -1; 274 } 275 app_fuse_class = static_cast<jclass>(env->NewGlobalRef(clazz)); 276 if (app_fuse_class == nullptr) { 277 ALOGE("Can't obtain global reference for com/android/mtp/AppFuse"); 278 return -1; 279 } 280 281 const int result = android::AndroidRuntime::registerNativeMethods( 282 env, "com/android/mtp/AppFuse", gMethods, NELEM(gMethods)); 283 if (result < 0) { 284 return -1; 285 } 286 287 return JNI_VERSION_1_4; 288} 289