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