com_android_mtp_AppFuse.cpp revision bee50c05439191d88df37f20749fff1c700d9684
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 65/** 66 * The class is used to access AppFuse class in Java from fuse handlers. 67 */ 68class AppFuse { 69public: 70 AppFuse(JNIEnv* /*env*/, jobject /*self*/) { 71 } 72 73 void handle_fuse_request(int fd, const FuseRequest& req) { 74 ALOGV("Request op=%d", req.header().opcode); 75 switch (req.header().opcode) { 76 // TODO: Handle more operations that are enough to provide seekable 77 // FD. 78 case FUSE_INIT: 79 invoke_handler(fd, req, &AppFuse::handle_fuse_init); 80 break; 81 case FUSE_GETATTR: 82 invoke_handler(fd, req, &AppFuse::handle_fuse_getattr); 83 break; 84 default: { 85 ALOGV("NOTIMPL op=%d uniq=%" PRIx64 " nid=%" PRIx64 "\n", 86 req.header().opcode, 87 req.header().unique, 88 req.header().nodeid); 89 fuse_reply(fd, req.header().unique, -ENOSYS, NULL, 0); 90 break; 91 } 92 } 93 } 94 95private: 96 int handle_fuse_init(const fuse_in_header&, 97 const fuse_init_in* in, 98 fuse_init_out* out, 99 size_t* reply_size) { 100 // Kernel 2.6.16 is the first stable kernel with struct fuse_init_out 101 // defined (fuse version 7.6). The structure is the same from 7.6 through 102 // 7.22. Beginning with 7.23, the structure increased in size and added 103 // new parameters. 104 if (in->major != FUSE_KERNEL_VERSION || in->minor < 6) { 105 ALOGE("Fuse kernel version mismatch: Kernel version %d.%d, " 106 "Expected at least %d.6", 107 in->major, in->minor, FUSE_KERNEL_VERSION); 108 return -1; 109 } 110 111 // We limit ourselves to 15 because we don't handle BATCH_FORGET yet 112 out->minor = min(in->minor, 15); 113#if defined(FUSE_COMPAT_22_INIT_OUT_SIZE) 114 // FUSE_KERNEL_VERSION >= 23. 115 116 // If the kernel only works on minor revs older than or equal to 22, 117 // then use the older structure size since this code only uses the 7.22 118 // version of the structure. 119 if (in->minor <= 22) { 120 *reply_size = FUSE_COMPAT_22_INIT_OUT_SIZE; 121 } 122#endif 123 124 out->major = FUSE_KERNEL_VERSION; 125 out->max_readahead = in->max_readahead; 126 out->flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES; 127 out->max_background = 32; 128 out->congestion_threshold = 32; 129 out->max_write = MAX_WRITE; 130 131 return 0; 132 } 133 134 int handle_fuse_getattr(const fuse_in_header& header, 135 const fuse_getattr_in* /* in */, 136 fuse_attr_out* out, 137 size_t* /*unused*/) { 138 if (header.nodeid != 1) { 139 return -ENOENT; 140 } 141 out->attr_valid = 1000 * 60 * 10; 142 out->attr.ino = header.nodeid; 143 out->attr.mode = S_IFDIR | 0777; 144 out->attr.size = 0; 145 return 0; 146 } 147 148 template <typename T, typename S> 149 void invoke_handler(int fd, 150 const FuseRequest& request, 151 int (AppFuse::*handler)(const fuse_in_header&, 152 const T*, 153 S*, 154 size_t*), 155 size_t reply_size = sizeof(S)) { 156 char reply_data[reply_size]; 157 memset(reply_data, 0, reply_size); 158 const int reply_code = (this->*handler)( 159 request.header(), 160 static_cast<const T*>(request.data()), 161 reinterpret_cast<S*>(reply_data), 162 &reply_size); 163 fuse_reply( 164 fd, 165 request.header().unique, 166 reply_code, 167 reply_data, 168 reply_size); 169 } 170 171 static void fuse_reply(int fd, int unique, int reply_code, void* reply_data, 172 size_t reply_size) { 173 // Don't send any data for error case. 174 if (reply_code != 0) { 175 reply_size = 0; 176 } 177 178 struct fuse_out_header hdr; 179 hdr.len = reply_size + sizeof(hdr); 180 hdr.error = reply_code; 181 hdr.unique = unique; 182 183 struct iovec vec[2]; 184 vec[0].iov_base = &hdr; 185 vec[0].iov_len = sizeof(hdr); 186 vec[1].iov_base = reply_data; 187 vec[1].iov_len = reply_size; 188 189 const int res = writev(fd, vec, reply_size != 0 ? 2 : 1); 190 if (res < 0) { 191 ALOGE("*** REPLY FAILED *** %d\n", errno); 192 } 193 } 194}; 195 196jboolean com_android_mtp_AppFuse_start_app_fuse_loop( 197 JNIEnv* env, jobject self, jint jfd) { 198 const int fd = static_cast<int>(jfd); 199 AppFuse appfuse(env, self); 200 201 ALOGD("Start fuse loop."); 202 while (true) { 203 FuseRequest request; 204 const ssize_t result = TEMP_FAILURE_RETRY( 205 read(fd, request.buffer, sizeof(request.buffer))); 206 if (result < 0) { 207 if (errno == ENODEV) { 208 ALOGE("Someone stole our marbles!\n"); 209 return false; 210 } 211 ALOGE("Failed to read bytes from FD: errno=%d\n", errno); 212 continue; 213 } 214 215 const size_t length = static_cast<size_t>(result); 216 if (length < sizeof(struct fuse_in_header)) { 217 ALOGE("request too short: len=%zu\n", length); 218 continue; 219 } 220 221 if (request.header().len != length) { 222 ALOGE("malformed header: len=%zu, hdr->len=%u\n", 223 length, request.header().len); 224 continue; 225 } 226 227 appfuse.handle_fuse_request(fd, request); 228 } 229} 230 231static const JNINativeMethod gMethods[] = { 232 { 233 "native_start_app_fuse_loop", 234 "(I)Z", 235 (void *) com_android_mtp_AppFuse_start_app_fuse_loop 236 } 237}; 238 239} 240 241jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) { 242 JNIEnv* env = nullptr; 243 if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { 244 ALOGE("ERROR: GetEnv failed\n"); 245 return -1; 246 247 } 248 assert(env != nullptr); 249 250 jclass clazz = env->FindClass("com/android/mtp/AppFuse"); 251 if (clazz == nullptr) { 252 ALOGE("Can't find com/android/mtp/AppFuse"); 253 return -1; 254 } 255 app_fuse_class = static_cast<jclass>(env->NewGlobalRef(clazz)); 256 if (app_fuse_class == nullptr) { 257 ALOGE("Can't obtain global reference for com/android/mtp/AppFuse"); 258 return -1; 259 } 260 261 const int result = android::AndroidRuntime::registerNativeMethods( 262 env, "com/android/mtp/AppFuse", gMethods, NELEM(gMethods)); 263 if (result < 0) { 264 return -1; 265 } 266 267 return JNI_VERSION_1_4; 268} 269