com_android_mtp_AppFuse.cpp revision f4e7fa80384ac72d0228ca5de6e949a9162cefbf
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 jmethodID app_fuse_flush_file_handle; 56static jmethodID app_fuse_close_file_handle; 57static jfieldID app_fuse_buffer; 58 59// NOTE: 60// FuseRequest and FuseResponse shares the same buffer to save memory usage, so the handlers must 61// not access input buffer after writing data to output buffer. 62struct FuseRequest { 63 char buffer[MAX_REQUEST_SIZE]; 64 FuseRequest() {} 65 const struct fuse_in_header& header() const { 66 return *(const struct fuse_in_header*) buffer; 67 } 68 void* data() { 69 return (buffer + sizeof(struct fuse_in_header)); 70 } 71 size_t data_length() const { 72 return header().len - sizeof(struct fuse_in_header); 73 } 74}; 75 76template<typename T> 77class FuseResponse { 78 size_t size_; 79 T* const buffer_; 80public: 81 FuseResponse(void* buffer) : size_(0), buffer_(static_cast<T*>(buffer)) {} 82 83 void prepare_buffer(size_t size = sizeof(T)) { 84 memset(buffer_, 0, size); 85 size_ = size; 86 } 87 88 void set_size(size_t size) { 89 size_ = size; 90 } 91 92 size_t size() const { return size_; } 93 T* data() const { return buffer_; } 94}; 95 96class ScopedFd { 97 int mFd; 98 99public: 100 explicit ScopedFd(int fd) : mFd(fd) {} 101 ~ScopedFd() { 102 close(mFd); 103 } 104 operator int() { 105 return mFd; 106 } 107}; 108 109/** 110 * Fuse implementation consists of handlers parsing FUSE commands. 111 */ 112class AppFuse { 113 JNIEnv* env_; 114 jobject self_; 115 116 // Map between file handle and inode. 117 std::map<uint32_t, uint64_t> handles_; 118 uint32_t handle_counter_; 119 120public: 121 AppFuse(JNIEnv* env, jobject self) : 122 env_(env), self_(self), handle_counter_(0) {} 123 124 bool handle_fuse_request(int fd, FuseRequest* req) { 125 ALOGV("Request op=%d", req->header().opcode); 126 switch (req->header().opcode) { 127 // TODO: Handle more operations that are enough to provide seekable 128 // FD. 129 case FUSE_LOOKUP: 130 invoke_handler(fd, req, &AppFuse::handle_fuse_lookup); 131 return true; 132 case FUSE_INIT: 133 invoke_handler(fd, req, &AppFuse::handle_fuse_init); 134 return true; 135 case FUSE_GETATTR: 136 invoke_handler(fd, req, &AppFuse::handle_fuse_getattr); 137 return true; 138 case FUSE_FORGET: 139 return false; 140 case FUSE_OPEN: 141 invoke_handler(fd, req, &AppFuse::handle_fuse_open); 142 return true; 143 case FUSE_READ: 144 invoke_handler(fd, req, &AppFuse::handle_fuse_read); 145 return true; 146 case FUSE_WRITE: 147 invoke_handler(fd, req, &AppFuse::handle_fuse_write); 148 return true; 149 case FUSE_RELEASE: 150 invoke_handler(fd, req, &AppFuse::handle_fuse_release); 151 return true; 152 case FUSE_FLUSH: 153 invoke_handler(fd, req, &AppFuse::handle_fuse_flush); 154 return true; 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 true; 162 } 163 } 164 } 165 166private: 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 449jboolean com_android_mtp_AppFuse_start_app_fuse_loop( 450 JNIEnv* env, jobject self, jint jfd) { 451 ScopedFd fd(static_cast<int>(jfd)); 452 AppFuse appfuse(env, self); 453 454 ALOGD("Start fuse loop."); 455 while (true) { 456 FuseRequest request; 457 458 const ssize_t result = TEMP_FAILURE_RETRY( 459 read(fd, request.buffer, sizeof(request.buffer))); 460 if (result < 0) { 461 if (errno == ENODEV) { 462 ALOGE("Someone stole our marbles!\n"); 463 return JNI_FALSE; 464 } 465 ALOGE("Failed to read bytes from FD: errno=%d\n", errno); 466 continue; 467 } 468 469 const size_t length = static_cast<size_t>(result); 470 if (length < sizeof(struct fuse_in_header)) { 471 ALOGE("request too short: len=%zu\n", length); 472 continue; 473 } 474 475 if (request.header().len != length) { 476 ALOGE("malformed header: len=%zu, hdr->len=%u\n", 477 length, request.header().len); 478 continue; 479 } 480 481 if (!appfuse.handle_fuse_request(fd, &request)) { 482 return JNI_TRUE; 483 } 484 } 485} 486 487static const JNINativeMethod gMethods[] = { 488 { 489 "native_start_app_fuse_loop", 490 "(I)Z", 491 (void *) com_android_mtp_AppFuse_start_app_fuse_loop 492 } 493}; 494 495} 496 497jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) { 498 JNIEnv* env = nullptr; 499 if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { 500 ALOGE("ERROR: GetEnv failed\n"); 501 return -1; 502 503 } 504 assert(env != nullptr); 505 506 jclass clazz = env->FindClass("com/android/mtp/AppFuse"); 507 if (clazz == nullptr) { 508 ALOGE("Can't find com/android/mtp/AppFuse"); 509 return -1; 510 } 511 512 app_fuse_class = static_cast<jclass>(env->NewGlobalRef(clazz)); 513 if (app_fuse_class == nullptr) { 514 ALOGE("Can't obtain global reference for com/android/mtp/AppFuse"); 515 return -1; 516 } 517 518 app_fuse_get_file_size = env->GetMethodID( 519 app_fuse_class, "getFileSize", "(I)J"); 520 if (app_fuse_get_file_size == nullptr) { 521 ALOGE("Can't find getFileSize"); 522 return -1; 523 } 524 525 app_fuse_read_object_bytes = env->GetMethodID( 526 app_fuse_class, "readObjectBytes", "(IJJ)J"); 527 if (app_fuse_read_object_bytes == nullptr) { 528 ALOGE("Can't find readObjectBytes"); 529 return -1; 530 } 531 532 app_fuse_write_object_bytes = env->GetMethodID(app_fuse_class, "writeObjectBytes", "(JIJI[B)I"); 533 if (app_fuse_write_object_bytes == nullptr) { 534 ALOGE("Can't find writeObjectBytes"); 535 return -1; 536 } 537 538 app_fuse_flush_file_handle = env->GetMethodID(app_fuse_class, "flushFileHandle", "(J)I"); 539 if (app_fuse_flush_file_handle == nullptr) { 540 ALOGE("Can't find flushFileHandle"); 541 return -1; 542 } 543 544 app_fuse_close_file_handle = env->GetMethodID(app_fuse_class, "closeFileHandle", "(J)I"); 545 if (app_fuse_close_file_handle == nullptr) { 546 ALOGE("Can't find closeFileHandle"); 547 return -1; 548 } 549 550 app_fuse_buffer = env->GetFieldID(app_fuse_class, "mBuffer", "[B"); 551 if (app_fuse_buffer == nullptr) { 552 ALOGE("Can't find mBuffer"); 553 return -1; 554 } 555 556 const jfieldID read_max_fied = env->GetStaticFieldID(app_fuse_class, "MAX_READ", "I"); 557 if (static_cast<int>(env->GetStaticIntField(app_fuse_class, read_max_fied)) != MAX_READ) { 558 return -1; 559 } 560 561 const jfieldID write_max_fied = env->GetStaticFieldID(app_fuse_class, "MAX_WRITE", "I"); 562 if (static_cast<int>(env->GetStaticIntField(app_fuse_class, write_max_fied)) != MAX_WRITE) { 563 return -1; 564 } 565 566 const int result = android::AndroidRuntime::registerNativeMethods( 567 env, "com/android/mtp/AppFuse", gMethods, NELEM(gMethods)); 568 if (result < 0) { 569 return -1; 570 } 571 572 return JNI_VERSION_1_4; 573} 574