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