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