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