com_android_mtp_AppFuse.cpp revision 09ece6c68bdaf3a04b517f04dff6a3272b54b2b2
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#include "nativehelper/ScopedPrimitiveArray.h" 34#include "nativehelper/ScopedLocalRef.h" 35 36namespace { 37 38// The numbers came from sdcard.c. 39// Maximum number of bytes to write/read in one request/one reply. 40constexpr size_t MAX_WRITE = 256 * 1024; 41constexpr size_t MAX_READ = 128 * 1024; 42 43constexpr size_t NUM_MAX_HANDLES = 1024; 44 45// Largest possible request. 46// The request size is bounded by the maximum size of a FUSE_WRITE request 47// because it has the largest possible data payload. 48constexpr size_t MAX_REQUEST_SIZE = sizeof(struct fuse_in_header) + 49 sizeof(struct fuse_write_in) + (MAX_WRITE > MAX_READ ? MAX_WRITE : MAX_READ); 50 51static jclass app_fuse_class; 52static jmethodID app_fuse_get_file_size; 53static jmethodID app_fuse_read_object_bytes; 54static jmethodID app_fuse_write_object_bytes; 55static jfieldID app_fuse_buffer; 56 57// NOTE: 58// FuseRequest and FuseResponse shares the same buffer to save memory usage, so the handlers must 59// not access input buffer after writing data to output buffer. 60struct FuseRequest { 61 char buffer[MAX_REQUEST_SIZE]; 62 FuseRequest() {} 63 const struct fuse_in_header& header() const { 64 return *(const struct fuse_in_header*) buffer; 65 } 66 void* data() { 67 return (buffer + sizeof(struct fuse_in_header)); 68 } 69 size_t data_length() const { 70 return header().len - sizeof(struct fuse_in_header); 71 } 72}; 73 74template<typename T> 75class FuseResponse { 76 size_t size_; 77 T* const buffer_; 78public: 79 FuseResponse(void* buffer) : size_(0), buffer_(static_cast<T*>(buffer)) {} 80 81 void prepare_buffer(size_t size = sizeof(T)) { 82 memset(buffer_, 0, size); 83 size_ = size; 84 } 85 86 void set_size(size_t size) { 87 size_ = size; 88 } 89 90 size_t size() const { return size_; } 91 T* data() const { return buffer_; } 92}; 93 94class ScopedFd { 95 int mFd; 96 97public: 98 explicit ScopedFd(int fd) : mFd(fd) {} 99 ~ScopedFd() { 100 close(mFd); 101 } 102 operator int() { 103 return mFd; 104 } 105}; 106 107/** 108 * Fuse implementation consists of handlers parsing FUSE commands. 109 */ 110class AppFuse { 111 JNIEnv* env_; 112 jobject self_; 113 114 // Map between file handle and inode. 115 std::map<uint32_t, uint64_t> handles_; 116 uint32_t handle_counter_; 117 118public: 119 AppFuse(JNIEnv* env, jobject self) : 120 env_(env), self_(self), handle_counter_(0) {} 121 122 bool handle_fuse_request(int fd, FuseRequest* req) { 123 ALOGV("Request op=%d", req->header().opcode); 124 switch (req->header().opcode) { 125 // TODO: Handle more operations that are enough to provide seekable 126 // FD. 127 case FUSE_LOOKUP: 128 invoke_handler(fd, req, &AppFuse::handle_fuse_lookup); 129 return true; 130 case FUSE_INIT: 131 invoke_handler(fd, req, &AppFuse::handle_fuse_init); 132 return true; 133 case FUSE_GETATTR: 134 invoke_handler(fd, req, &AppFuse::handle_fuse_getattr); 135 return true; 136 case FUSE_FORGET: 137 return false; 138 case FUSE_OPEN: 139 invoke_handler(fd, req, &AppFuse::handle_fuse_open); 140 return true; 141 case FUSE_READ: 142 invoke_handler(fd, req, &AppFuse::handle_fuse_read); 143 return true; 144 case FUSE_WRITE: 145 invoke_handler(fd, req, &AppFuse::handle_fuse_write); 146 return true; 147 case FUSE_RELEASE: 148 invoke_handler(fd, req, &AppFuse::handle_fuse_release); 149 return true; 150 case FUSE_FLUSH: 151 invoke_handler(fd, req, &AppFuse::handle_fuse_flush); 152 return true; 153 default: { 154 ALOGV("NOTIMPL op=%d uniq=%" PRIx64 " nid=%" PRIx64 "\n", 155 req->header().opcode, 156 req->header().unique, 157 req->header().nodeid); 158 fuse_reply(fd, req->header().unique, -ENOSYS, NULL, 0); 159 return true; 160 } 161 } 162 } 163 164private: 165 int handle_fuse_lookup(const fuse_in_header& header, 166 const char* name, 167 FuseResponse<fuse_entry_out>* out) { 168 if (header.nodeid != 1) { 169 return -ENOENT; 170 } 171 172 const int n = atoi(name); 173 if (n == 0) { 174 return -ENOENT; 175 } 176 177 int64_t size = get_file_size(n); 178 if (size < 0) { 179 return -ENOENT; 180 } 181 182 out->prepare_buffer(); 183 out->data()->nodeid = n; 184 out->data()->attr_valid = 10; 185 out->data()->entry_valid = 10; 186 out->data()->attr.ino = n; 187 out->data()->attr.mode = S_IFREG | 0777; 188 out->data()->attr.size = size; 189 return 0; 190 } 191 192 int handle_fuse_init(const fuse_in_header&, 193 const fuse_init_in* in, 194 FuseResponse<fuse_init_out>* out) { 195 // Kernel 2.6.16 is the first stable kernel with struct fuse_init_out 196 // defined (fuse version 7.6). The structure is the same from 7.6 through 197 // 7.22. Beginning with 7.23, the structure increased in size and added 198 // new parameters. 199 if (in->major != FUSE_KERNEL_VERSION || in->minor < 6) { 200 ALOGE("Fuse kernel version mismatch: Kernel version %d.%d, " 201 "Expected at least %d.6", 202 in->major, in->minor, FUSE_KERNEL_VERSION); 203 return -1; 204 } 205 206 // Before writing |out|, we need to copy data from |in|. 207 const uint32_t minor = in->minor; 208 const uint32_t max_readahead = in->max_readahead; 209 210 // We limit ourselves to 15 because we don't handle BATCH_FORGET yet 211 size_t response_size = sizeof(fuse_init_out); 212#if defined(FUSE_COMPAT_22_INIT_OUT_SIZE) 213 // FUSE_KERNEL_VERSION >= 23. 214 215 // If the kernel only works on minor revs older than or equal to 22, 216 // then use the older structure size since this code only uses the 7.22 217 // version of the structure. 218 if (minor <= 22) { 219 response_size = FUSE_COMPAT_22_INIT_OUT_SIZE; 220 } 221#endif 222 out->prepare_buffer(response_size); 223 out->data()->major = FUSE_KERNEL_VERSION; 224 out->data()->minor = std::min(minor, 15u); 225 out->data()->max_readahead = max_readahead; 226 out->data()->flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES; 227 out->data()->max_background = 32; 228 out->data()->congestion_threshold = 32; 229 out->data()->max_write = MAX_WRITE; 230 231 return 0; 232 } 233 234 int handle_fuse_getattr(const fuse_in_header& header, 235 const fuse_getattr_in* /* in */, 236 FuseResponse<fuse_attr_out>* out) { 237 out->prepare_buffer(); 238 out->data()->attr_valid = 10; 239 out->data()->attr.ino = header.nodeid; 240 if (header.nodeid == 1) { 241 out->data()->attr.mode = S_IFDIR | 0777; 242 out->data()->attr.size = 0; 243 } else { 244 int64_t size = get_file_size(header.nodeid); 245 if (size < 0) { 246 return -ENOENT; 247 } 248 out->data()->attr.mode = S_IFREG | 0777; 249 out->data()->attr.size = size; 250 } 251 252 return 0; 253 } 254 255 int handle_fuse_open(const fuse_in_header& header, 256 const fuse_open_in* /* in */, 257 FuseResponse<fuse_open_out>* out) { 258 if (handles_.size() >= NUM_MAX_HANDLES) { 259 // Too many open files. 260 return -EMFILE; 261 } 262 uint32_t handle; 263 do { 264 handle = handle_counter_++; 265 } while (handles_.count(handle) != 0); 266 handles_.insert(std::make_pair(handle, header.nodeid)); 267 268 out->prepare_buffer(); 269 out->data()->fh = handle; 270 return 0; 271 } 272 273 int handle_fuse_read(const fuse_in_header& /* header */, 274 const fuse_read_in* in, 275 FuseResponse<void>* out) { 276 if (in->size > MAX_READ) { 277 return -EINVAL; 278 } 279 const std::map<uint32_t, uint64_t>::iterator it = handles_.find(in->fh); 280 if (it == handles_.end()) { 281 return -EBADF; 282 } 283 uint64_t offset = in->offset; 284 uint32_t size = in->size; 285 286 // Overwrite the size after writing data. 287 out->prepare_buffer(0); 288 const int64_t result = get_object_bytes(it->second, offset, size, out->data()); 289 if (result < 0) { 290 return result; 291 } 292 out->set_size(result); 293 return 0; 294 } 295 296 int handle_fuse_write(const fuse_in_header& /* header */, 297 const fuse_write_in* in, 298 FuseResponse<fuse_write_out>* out) { 299 if (in->size > MAX_WRITE) { 300 return -EINVAL; 301 } 302 const std::map<uint32_t, uint64_t>::iterator it = handles_.find(in->fh); 303 if (it == handles_.end()) { 304 return -EBADF; 305 } 306 const uint64_t offset = in->offset; 307 const uint32_t size = in->size; 308 const void* const buffer = reinterpret_cast<const uint8_t*>(in) + sizeof(fuse_write_in); 309 uint32_t written_size; 310 const int result = write_object_bytes(it->second, offset, size, buffer, &written_size); 311 if (result < 0) { 312 return result; 313 } 314 out->prepare_buffer(); 315 out->data()->size = written_size; 316 return 0; 317 } 318 319 int handle_fuse_release(const fuse_in_header& /* header */, 320 const fuse_release_in* in, 321 FuseResponse<void>* /* out */) { 322 handles_.erase(in->fh); 323 return 0; 324 } 325 326 int handle_fuse_flush(const fuse_in_header& /* header */, 327 const void* /* in */, 328 FuseResponse<void>* /* out */) { 329 return 0; 330 } 331 332 template <typename T, typename S> 333 void invoke_handler(int fd, 334 FuseRequest* request, 335 int (AppFuse::*handler)(const fuse_in_header&, 336 const T*, 337 FuseResponse<S>*)) { 338 FuseResponse<S> response(request->data()); 339 const int reply_code = (this->*handler)( 340 request->header(), 341 static_cast<const T*>(request->data()), 342 &response); 343 fuse_reply( 344 fd, 345 request->header().unique, 346 reply_code, 347 request->data(), 348 response.size()); 349 } 350 351 int64_t get_file_size(int inode) { 352 return static_cast<int64_t>(env_->CallLongMethod( 353 self_, 354 app_fuse_get_file_size, 355 static_cast<int>(inode))); 356 } 357 358 int64_t get_object_bytes( 359 int inode, 360 uint64_t offset, 361 uint32_t size, 362 void* buf) { 363 const jlong read_size = env_->CallLongMethod( 364 self_, 365 app_fuse_read_object_bytes, 366 static_cast<jint>(inode), 367 static_cast<jlong>(offset), 368 static_cast<jlong>(size)); 369 if (read_size <= 0) { 370 return read_size; 371 } 372 ScopedLocalRef<jbyteArray> array( 373 env_, static_cast<jbyteArray>(env_->GetObjectField(self_, app_fuse_buffer))); 374 if (array.get() == nullptr) { 375 return -EFAULT; 376 } 377 ScopedByteArrayRO bytes(env_, array.get()); 378 if (bytes.get() == nullptr) { 379 return -ENOMEM; 380 } 381 memcpy(buf, bytes.get(), read_size); 382 return read_size; 383 } 384 385 int write_object_bytes(int inode, uint64_t offset, uint32_t size, const void* buffer, 386 uint32_t* written_size) { 387 ScopedLocalRef<jbyteArray> array( 388 env_, 389 static_cast<jbyteArray>(env_->GetObjectField(self_, app_fuse_buffer))); 390 { 391 ScopedByteArrayRW bytes(env_, array.get()); 392 if (bytes.get() == nullptr) { 393 return -EIO; 394 } 395 memcpy(bytes.get(), buffer, size); 396 } 397 *written_size = env_->CallIntMethod( 398 self_, app_fuse_write_object_bytes, inode, offset, size, array.get()); 399 if (env_->ExceptionCheck()) { 400 env_->ExceptionClear(); 401 return -EIO; 402 } 403 return 0; 404 } 405 406 static void fuse_reply(int fd, int unique, int reply_code, void* reply_data, 407 size_t reply_size) { 408 // Don't send any data for error case. 409 if (reply_code != 0) { 410 reply_size = 0; 411 } 412 413 struct fuse_out_header hdr; 414 hdr.len = reply_size + sizeof(hdr); 415 hdr.error = reply_code; 416 hdr.unique = unique; 417 418 struct iovec vec[2]; 419 vec[0].iov_base = &hdr; 420 vec[0].iov_len = sizeof(hdr); 421 vec[1].iov_base = reply_data; 422 vec[1].iov_len = reply_size; 423 424 const int res = writev(fd, vec, reply_size != 0 ? 2 : 1); 425 if (res < 0) { 426 ALOGE("*** REPLY FAILED *** %d\n", errno); 427 } 428 } 429}; 430 431jboolean com_android_mtp_AppFuse_start_app_fuse_loop( 432 JNIEnv* env, jobject self, jint jfd) { 433 ScopedFd fd(static_cast<int>(jfd)); 434 AppFuse appfuse(env, self); 435 436 ALOGD("Start fuse loop."); 437 while (true) { 438 FuseRequest request; 439 440 const ssize_t result = TEMP_FAILURE_RETRY( 441 read(fd, request.buffer, sizeof(request.buffer))); 442 if (result < 0) { 443 if (errno == ENODEV) { 444 ALOGE("Someone stole our marbles!\n"); 445 return JNI_FALSE; 446 } 447 ALOGE("Failed to read bytes from FD: errno=%d\n", errno); 448 continue; 449 } 450 451 const size_t length = static_cast<size_t>(result); 452 if (length < sizeof(struct fuse_in_header)) { 453 ALOGE("request too short: len=%zu\n", length); 454 continue; 455 } 456 457 if (request.header().len != length) { 458 ALOGE("malformed header: len=%zu, hdr->len=%u\n", 459 length, request.header().len); 460 continue; 461 } 462 463 if (!appfuse.handle_fuse_request(fd, &request)) { 464 return JNI_TRUE; 465 } 466 } 467} 468 469static const JNINativeMethod gMethods[] = { 470 { 471 "native_start_app_fuse_loop", 472 "(I)Z", 473 (void *) com_android_mtp_AppFuse_start_app_fuse_loop 474 } 475}; 476 477} 478 479jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) { 480 JNIEnv* env = nullptr; 481 if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { 482 ALOGE("ERROR: GetEnv failed\n"); 483 return -1; 484 485 } 486 assert(env != nullptr); 487 488 jclass clazz = env->FindClass("com/android/mtp/AppFuse"); 489 if (clazz == nullptr) { 490 ALOGE("Can't find com/android/mtp/AppFuse"); 491 return -1; 492 } 493 494 app_fuse_class = static_cast<jclass>(env->NewGlobalRef(clazz)); 495 if (app_fuse_class == nullptr) { 496 ALOGE("Can't obtain global reference for com/android/mtp/AppFuse"); 497 return -1; 498 } 499 500 app_fuse_get_file_size = env->GetMethodID( 501 app_fuse_class, "getFileSize", "(I)J"); 502 if (app_fuse_get_file_size == nullptr) { 503 ALOGE("Can't find getFileSize"); 504 return -1; 505 } 506 507 app_fuse_read_object_bytes = env->GetMethodID( 508 app_fuse_class, "readObjectBytes", "(IJJ)J"); 509 if (app_fuse_read_object_bytes == nullptr) { 510 ALOGE("Can't find readObjectBytes"); 511 return -1; 512 } 513 514 app_fuse_buffer = env->GetFieldID(app_fuse_class, "mBuffer", "[B"); 515 if (app_fuse_buffer == nullptr) { 516 ALOGE("Can't find mBuffer"); 517 return -1; 518 } 519 520 app_fuse_write_object_bytes = env->GetMethodID(app_fuse_class, "writeObjectBytes", "(IJI[B)I"); 521 if (app_fuse_write_object_bytes == nullptr) { 522 ALOGE("Can't find getWriteObjectBytes"); 523 return -1; 524 } 525 526 app_fuse_buffer = env->GetFieldID(app_fuse_class, "mBuffer", "[B"); 527 if (app_fuse_buffer == nullptr) { 528 ALOGE("Can't find mBuffer"); 529 return -1; 530 } 531 532 const jfieldID read_max_fied = env->GetStaticFieldID(app_fuse_class, "MAX_READ", "I"); 533 if (static_cast<int>(env->GetStaticIntField(app_fuse_class, read_max_fied)) != MAX_READ) { 534 return -1; 535 } 536 537 const jfieldID write_max_fied = env->GetStaticFieldID(app_fuse_class, "MAX_WRITE", "I"); 538 if (static_cast<int>(env->GetStaticIntField(app_fuse_class, write_max_fied)) != MAX_WRITE) { 539 return -1; 540 } 541 542 const int result = android::AndroidRuntime::registerNativeMethods( 543 env, "com/android/mtp/AppFuse", gMethods, NELEM(gMethods)); 544 if (result < 0) { 545 return -1; 546 } 547 548 return JNI_VERSION_1_4; 549} 550