com_android_mtp_AppFuse.cpp revision d0ef1394d295209b6693c1a8ced1a70cef0082eb
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 35namespace { 36 37// The numbers came from sdcard.c. 38// Maximum number of bytes to write/read in one request/one reply. 39constexpr size_t MAX_WRITE = 256 * 1024; 40constexpr size_t MAX_READ = 128 * 1024; 41 42constexpr 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. 47constexpr 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 50static jclass app_fuse_class; 51static jmethodID app_fuse_get_file_size; 52static jmethodID app_fuse_read_object_bytes; 53static jmethodID app_fuse_write_object_bytes; 54static jmethodID app_fuse_flush_file_handle; 55static jmethodID app_fuse_close_file_handle; 56static 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. 61struct 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 75template<typename T> 76class FuseResponse { 77 size_t size_; 78 T* const buffer_; 79public: 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 95class ScopedFd { 96 int mFd; 97 98public: 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 */ 111class 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 119public: 120 AppFuse(JNIEnv* env, jobject self) : 121 env_(env), self_(self), handle_counter_(0) {} 122 123 bool 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 true; 131 case FUSE_INIT: 132 invoke_handler(fd, req, &AppFuse::handle_fuse_init); 133 return true; 134 case FUSE_GETATTR: 135 invoke_handler(fd, req, &AppFuse::handle_fuse_getattr); 136 return true; 137 case FUSE_FORGET: 138 return false; 139 case FUSE_OPEN: 140 invoke_handler(fd, req, &AppFuse::handle_fuse_open); 141 return true; 142 case FUSE_READ: 143 invoke_handler(fd, req, &AppFuse::handle_fuse_read); 144 return true; 145 case FUSE_WRITE: 146 invoke_handler(fd, req, &AppFuse::handle_fuse_write); 147 return true; 148 case FUSE_RELEASE: 149 invoke_handler(fd, req, &AppFuse::handle_fuse_release); 150 return true; 151 case FUSE_FLUSH: 152 invoke_handler(fd, req, &AppFuse::handle_fuse_flush); 153 return true; 154 default: { 155 ALOGV("NOTIMPL op=%d uniq=%" PRIx64 " nid=%" PRIx64 "\n", 156 req->header().opcode, 157 req->header().unique, 158 req->header().nodeid); 159 fuse_reply(fd, req->header().unique, -ENOSYS, NULL, 0); 160 return true; 161 } 162 } 163 } 164 165private: 166 int handle_fuse_lookup(const fuse_in_header& header, 167 const char* name, 168 FuseResponse<fuse_entry_out>* out) { 169 if (header.nodeid != 1) { 170 return -ENOENT; 171 } 172 173 const int n = atoi(name); 174 if (n == 0) { 175 return -ENOENT; 176 } 177 178 int64_t size = get_file_size(n); 179 if (size < 0) { 180 return -ENOENT; 181 } 182 183 out->prepare_buffer(); 184 out->data()->nodeid = n; 185 out->data()->attr_valid = 10; 186 out->data()->entry_valid = 10; 187 out->data()->attr.ino = n; 188 out->data()->attr.mode = S_IFREG | 0777; 189 out->data()->attr.size = size; 190 return 0; 191 } 192 193 int handle_fuse_init(const fuse_in_header&, 194 const fuse_init_in* in, 195 FuseResponse<fuse_init_out>* out) { 196 // Kernel 2.6.16 is the first stable kernel with struct fuse_init_out 197 // defined (fuse version 7.6). The structure is the same from 7.6 through 198 // 7.22. Beginning with 7.23, the structure increased in size and added 199 // new parameters. 200 if (in->major != FUSE_KERNEL_VERSION || in->minor < 6) { 201 ALOGE("Fuse kernel version mismatch: Kernel version %d.%d, " 202 "Expected at least %d.6", 203 in->major, in->minor, FUSE_KERNEL_VERSION); 204 return -1; 205 } 206 207 // Before writing |out|, we need to copy data from |in|. 208 const uint32_t minor = in->minor; 209 const uint32_t max_readahead = in->max_readahead; 210 211 // We limit ourselves to 15 because we don't handle BATCH_FORGET yet 212 size_t response_size = sizeof(fuse_init_out); 213#if defined(FUSE_COMPAT_22_INIT_OUT_SIZE) 214 // FUSE_KERNEL_VERSION >= 23. 215 216 // If the kernel only works on minor revs older than or equal to 22, 217 // then use the older structure size since this code only uses the 7.22 218 // version of the structure. 219 if (minor <= 22) { 220 response_size = FUSE_COMPAT_22_INIT_OUT_SIZE; 221 } 222#endif 223 out->prepare_buffer(response_size); 224 out->data()->major = FUSE_KERNEL_VERSION; 225 out->data()->minor = std::min(minor, 15u); 226 out->data()->max_readahead = max_readahead; 227 out->data()->flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES; 228 out->data()->max_background = 32; 229 out->data()->congestion_threshold = 32; 230 out->data()->max_write = MAX_WRITE; 231 232 return 0; 233 } 234 235 int handle_fuse_getattr(const fuse_in_header& header, 236 const fuse_getattr_in* /* in */, 237 FuseResponse<fuse_attr_out>* out) { 238 out->prepare_buffer(); 239 out->data()->attr_valid = 10; 240 out->data()->attr.ino = header.nodeid; 241 if (header.nodeid == 1) { 242 out->data()->attr.mode = S_IFDIR | 0777; 243 out->data()->attr.size = 0; 244 } else { 245 int64_t size = get_file_size(header.nodeid); 246 if (size < 0) { 247 return -ENOENT; 248 } 249 out->data()->attr.mode = S_IFREG | 0777; 250 out->data()->attr.size = size; 251 } 252 253 return 0; 254 } 255 256 int handle_fuse_open(const fuse_in_header& header, 257 const fuse_open_in* /* in */, 258 FuseResponse<fuse_open_out>* out) { 259 if (handles_.size() >= NUM_MAX_HANDLES) { 260 // Too many open files. 261 return -EMFILE; 262 } 263 uint32_t handle; 264 do { 265 handle = handle_counter_++; 266 } while (handles_.count(handle) != 0); 267 handles_.insert(std::make_pair(handle, header.nodeid)); 268 269 out->prepare_buffer(); 270 out->data()->fh = handle; 271 return 0; 272 } 273 274 int handle_fuse_read(const fuse_in_header& /* header */, 275 const fuse_read_in* in, 276 FuseResponse<void>* out) { 277 if (in->size > MAX_READ) { 278 return -EINVAL; 279 } 280 const std::map<uint32_t, uint64_t>::iterator it = handles_.find(in->fh); 281 if (it == handles_.end()) { 282 return -EBADF; 283 } 284 uint64_t offset = in->offset; 285 uint32_t size = in->size; 286 287 // Overwrite the size after writing data. 288 out->prepare_buffer(0); 289 const int64_t result = get_object_bytes(it->second, offset, size, out->data()); 290 if (result < 0) { 291 return result; 292 } 293 out->set_size(result); 294 return 0; 295 } 296 297 int handle_fuse_write(const fuse_in_header& /* header */, 298 const fuse_write_in* in, 299 FuseResponse<fuse_write_out>* out) { 300 if (in->size > MAX_WRITE) { 301 return -EINVAL; 302 } 303 const std::map<uint32_t, uint64_t>::iterator it = handles_.find(in->fh); 304 if (it == handles_.end()) { 305 return -EBADF; 306 } 307 const uint64_t offset = in->offset; 308 const uint32_t size = in->size; 309 const void* const buffer = reinterpret_cast<const uint8_t*>(in) + sizeof(fuse_write_in); 310 uint32_t written_size; 311 const int result = write_object_bytes( 312 in->fh, it->second, offset, size, buffer, &written_size); 313 if (result < 0) { 314 return result; 315 } 316 out->prepare_buffer(); 317 out->data()->size = written_size; 318 return 0; 319 } 320 321 int handle_fuse_release(const fuse_in_header& /* header */, 322 const fuse_release_in* in, 323 FuseResponse<void>* /* out */) { 324 handles_.erase(in->fh); 325 return env_->CallIntMethod(self_, app_fuse_close_file_handle, file_handle_to_jlong(in->fh)); 326 } 327 328 int handle_fuse_flush(const fuse_in_header& /* header */, 329 const fuse_flush_in* in, 330 FuseResponse<void>* /* out */) { 331 return env_->CallIntMethod(self_, app_fuse_flush_file_handle, file_handle_to_jlong(in->fh)); 332 } 333 334 template <typename T, typename S> 335 void invoke_handler(int fd, 336 FuseRequest* request, 337 int (AppFuse::*handler)(const fuse_in_header&, 338 const T*, 339 FuseResponse<S>*)) { 340 FuseResponse<S> response(request->data()); 341 const int reply_code = (this->*handler)( 342 request->header(), 343 static_cast<const T*>(request->data()), 344 &response); 345 fuse_reply( 346 fd, 347 request->header().unique, 348 reply_code, 349 request->data(), 350 response.size()); 351 } 352 353 int64_t get_file_size(int inode) { 354 return static_cast<int64_t>(env_->CallLongMethod( 355 self_, 356 app_fuse_get_file_size, 357 static_cast<int>(inode))); 358 } 359 360 int64_t get_object_bytes( 361 int inode, 362 uint64_t offset, 363 uint32_t size, 364 void* buf) { 365 const jlong read_size = env_->CallLongMethod( 366 self_, 367 app_fuse_read_object_bytes, 368 static_cast<jint>(inode), 369 static_cast<jlong>(offset), 370 static_cast<jlong>(size)); 371 if (read_size <= 0) { 372 return read_size; 373 } 374 ScopedLocalRef<jbyteArray> array( 375 env_, static_cast<jbyteArray>(env_->GetObjectField(self_, app_fuse_buffer))); 376 if (array.get() == nullptr) { 377 return -EFAULT; 378 } 379 ScopedByteArrayRO bytes(env_, array.get()); 380 if (bytes.get() == nullptr) { 381 return -ENOMEM; 382 } 383 memcpy(buf, bytes.get(), read_size); 384 return read_size; 385 } 386 387 int write_object_bytes(uint64_t handle, int inode, uint64_t offset, uint32_t size, 388 const void* buffer, uint32_t* written_size) { 389 static_assert(sizeof(uint64_t) <= sizeof(jlong), 390 "jlong must be able to express any uint64_t values"); 391 ScopedLocalRef<jbyteArray> array( 392 env_, 393 static_cast<jbyteArray>(env_->GetObjectField(self_, app_fuse_buffer))); 394 { 395 ScopedByteArrayRW bytes(env_, array.get()); 396 if (bytes.get() == nullptr) { 397 return -EIO; 398 } 399 memcpy(bytes.get(), buffer, size); 400 } 401 const int result = env_->CallIntMethod( 402 self_, 403 app_fuse_write_object_bytes, 404 file_handle_to_jlong(handle), 405 inode, 406 offset, 407 size, 408 array.get()); 409 if (result < 0) { 410 return result; 411 } 412 *written_size = result; 413 return 0; 414 } 415 416 static jlong file_handle_to_jlong(uint64_t handle) { 417 static_assert( 418 sizeof(uint64_t) <= sizeof(jlong), 419 "jlong must be able to express any uint64_t values"); 420 return static_cast<jlong>(handle); 421 } 422 423 static void fuse_reply(int fd, int unique, int reply_code, void* reply_data, 424 size_t reply_size) { 425 // Don't send any data for error case. 426 if (reply_code != 0) { 427 reply_size = 0; 428 } 429 430 struct fuse_out_header hdr; 431 hdr.len = reply_size + sizeof(hdr); 432 hdr.error = reply_code; 433 hdr.unique = unique; 434 435 struct iovec vec[2]; 436 vec[0].iov_base = &hdr; 437 vec[0].iov_len = sizeof(hdr); 438 vec[1].iov_base = reply_data; 439 vec[1].iov_len = reply_size; 440 441 const int res = writev(fd, vec, reply_size != 0 ? 2 : 1); 442 if (res < 0) { 443 ALOGE("*** REPLY FAILED *** %d\n", errno); 444 } 445 } 446}; 447 448jboolean com_android_mtp_AppFuse_start_app_fuse_loop( 449 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 ALOGE("Someone stole our marbles!\n"); 462 return JNI_FALSE; 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 if (!appfuse.handle_fuse_request(fd, &request)) { 481 return JNI_TRUE; 482 } 483 } 484} 485 486static const JNINativeMethod gMethods[] = { 487 { 488 "native_start_app_fuse_loop", 489 "(I)Z", 490 (void *) com_android_mtp_AppFuse_start_app_fuse_loop 491 } 492}; 493 494} 495 496jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) { 497 JNIEnv* env = nullptr; 498 if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { 499 ALOGE("ERROR: GetEnv failed\n"); 500 return -1; 501 502 } 503 assert(env != nullptr); 504 505 jclass clazz = env->FindClass("com/android/mtp/AppFuse"); 506 if (clazz == nullptr) { 507 ALOGE("Can't find com/android/mtp/AppFuse"); 508 return -1; 509 } 510 511 app_fuse_class = static_cast<jclass>(env->NewGlobalRef(clazz)); 512 if (app_fuse_class == nullptr) { 513 ALOGE("Can't obtain global reference for com/android/mtp/AppFuse"); 514 return -1; 515 } 516 517 app_fuse_get_file_size = env->GetMethodID( 518 app_fuse_class, "getFileSize", "(I)J"); 519 if (app_fuse_get_file_size == nullptr) { 520 ALOGE("Can't find getFileSize"); 521 return -1; 522 } 523 524 app_fuse_read_object_bytes = env->GetMethodID( 525 app_fuse_class, "readObjectBytes", "(IJJ)J"); 526 if (app_fuse_read_object_bytes == nullptr) { 527 ALOGE("Can't find readObjectBytes"); 528 return -1; 529 } 530 531 app_fuse_write_object_bytes = env->GetMethodID(app_fuse_class, "writeObjectBytes", "(JIJI[B)I"); 532 if (app_fuse_write_object_bytes == nullptr) { 533 ALOGE("Can't find writeObjectBytes"); 534 return -1; 535 } 536 537 app_fuse_flush_file_handle = env->GetMethodID(app_fuse_class, "flushFileHandle", "(J)I"); 538 if (app_fuse_flush_file_handle == nullptr) { 539 ALOGE("Can't find flushFileHandle"); 540 return -1; 541 } 542 543 app_fuse_close_file_handle = env->GetMethodID(app_fuse_class, "closeFileHandle", "(J)I"); 544 if (app_fuse_close_file_handle == nullptr) { 545 ALOGE("Can't find closeFileHandle"); 546 return -1; 547 } 548 549 app_fuse_buffer = env->GetFieldID(app_fuse_class, "mBuffer", "[B"); 550 if (app_fuse_buffer == nullptr) { 551 ALOGE("Can't find mBuffer"); 552 return -1; 553 } 554 555 const jfieldID read_max_fied = env->GetStaticFieldID(app_fuse_class, "MAX_READ", "I"); 556 if (static_cast<int>(env->GetStaticIntField(app_fuse_class, read_max_fied)) != MAX_READ) { 557 return -1; 558 } 559 560 const jfieldID write_max_fied = env->GetStaticFieldID(app_fuse_class, "MAX_WRITE", "I"); 561 if (static_cast<int>(env->GetStaticIntField(app_fuse_class, write_max_fied)) != MAX_WRITE) { 562 return -1; 563 } 564 565 const int result = android::AndroidRuntime::registerNativeMethods( 566 env, "com/android/mtp/AppFuse", gMethods, NELEM(gMethods)); 567 if (result < 0) { 568 return -1; 569 } 570 571 return JNI_VERSION_1_4; 572} 573