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