• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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];
FuseRequest__anon5b7878130111::FuseRequest63     FuseRequest() {}
header__anon5b7878130111::FuseRequest64     const struct fuse_in_header& header() const {
65         return *(const struct fuse_in_header*) buffer;
66     }
data__anon5b7878130111::FuseRequest67     void* data() {
68         return (buffer + sizeof(struct fuse_in_header));
69     }
data_length__anon5b7878130111::FuseRequest70     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:
FuseResponse(void * buffer)80    FuseResponse(void* buffer) : size_(0), buffer_(static_cast<T*>(buffer)) {}
81 
prepare_buffer(size_t size=sizeof (T))82    void prepare_buffer(size_t size = sizeof(T)) {
83        memset(buffer_, 0, size);
84        size_ = size;
85    }
86 
set_size(size_t size)87    void set_size(size_t size) {
88        size_ = size;
89    }
90 
size() const91    size_t size() const { return size_; }
data() const92    T* data() const { return buffer_; }
93 };
94 
95 class ScopedFd {
96     int mFd;
97 
98 public:
ScopedFd(int fd)99     explicit ScopedFd(int fd) : mFd(fd) {}
~ScopedFd()100     ~ScopedFd() {
101         close(mFd);
102     }
operator int()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:
AppFuse(JNIEnv * env,jobject self)120     AppFuse(JNIEnv* env, jobject self) :
121         env_(env), self_(self), handle_counter_(0) {}
122 
handle_fuse_request(int fd,FuseRequest * req)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:
handle_fuse_lookup(const fuse_in_header & header,const char * name,FuseResponse<fuse_entry_out> * out)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 
handle_fuse_init(const fuse_in_header &,const fuse_init_in * in,FuseResponse<fuse_init_out> * out)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 
handle_fuse_getattr(const fuse_in_header & header,const fuse_getattr_in *,FuseResponse<fuse_attr_out> * out)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 
handle_fuse_open(const fuse_in_header & header,const fuse_open_in *,FuseResponse<fuse_open_out> * out)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 
handle_fuse_read(const fuse_in_header &,const fuse_read_in * in,FuseResponse<void> * out)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 
handle_fuse_write(const fuse_in_header &,const fuse_write_in * in,FuseResponse<fuse_write_out> * out)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 
handle_fuse_release(const fuse_in_header &,const fuse_release_in * in,FuseResponse<void> *)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 
handle_fuse_flush(const fuse_in_header &,const fuse_flush_in * in,FuseResponse<void> *)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>
invoke_handler(int fd,FuseRequest * request,int (AppFuse::* handler)(const fuse_in_header &,const T *,FuseResponse<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 
get_file_size(int inode)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 
get_object_bytes(int inode,uint64_t offset,uint32_t size,void * buf)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 
write_object_bytes(uint64_t handle,int inode,uint64_t offset,uint32_t size,const void * buffer,uint32_t * written_size)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 
file_handle_to_jlong(uint64_t handle)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 
fuse_reply(int fd,int unique,int reply_code,void * reply_data,size_t reply_size)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 
com_android_mtp_AppFuse_start_app_fuse_loop(JNIEnv * env,jobject self,jint jfd)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 
JNI_OnLoad(JavaVM * vm,void *)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