Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e442872e authored by Daichi Hirono's avatar Daichi Hirono Committed by Android (Google) Code Review
Browse files

Merge "Unmount appfuse when the device FD is closed."

parents bdb5a45f 91e3b506
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -233,6 +233,19 @@ public class ParcelFileDescriptor implements Parcelable, Closeable {
        final FileDescriptor fd = openInternal(file, mode);
        if (fd == null) return null;

        return fromFd(fd, handler, listener);
    }

    /** {@hide} */
    public static ParcelFileDescriptor fromFd(
            FileDescriptor fd, Handler handler, final OnCloseListener listener) throws IOException {
        if (handler == null) {
            throw new IllegalArgumentException("Handler must not be null");
        }
        if (listener == null) {
            throw new IllegalArgumentException("Listener must not be null");
        }

        final FileDescriptor[] comm = createCommSocketPair();
        final ParcelFileDescriptor pfd = new ParcelFileDescriptor(fd, comm[0]);
        final MessageQueue queue = handler.getLooper().getQueue();
+24 −7
Original line number Diff line number Diff line
@@ -62,6 +62,19 @@ struct FuseRequest {
    }
};

class ScopedFd {
    int mFd;

public:
    explicit ScopedFd(int fd) : mFd(fd) {}
    ~ScopedFd() {
        close(mFd);
    }
    operator int() {
        return mFd;
    }
};

/**
 * The class is used to access AppFuse class in Java from fuse handlers.
 */
@@ -70,24 +83,26 @@ public:
    AppFuse(JNIEnv* /*env*/, jobject /*self*/) {
    }

    void handle_fuse_request(int fd, const FuseRequest& req) {
    bool handle_fuse_request(int fd, const FuseRequest& req) {
        ALOGV("Request op=%d", req.header().opcode);
        switch (req.header().opcode) {
            // TODO: Handle more operations that are enough to provide seekable
            // FD.
            case FUSE_INIT:
                invoke_handler(fd, req, &AppFuse::handle_fuse_init);
                break;
                return true;
            case FUSE_GETATTR:
                invoke_handler(fd, req, &AppFuse::handle_fuse_getattr);
                break;
                return true;
            case FUSE_FORGET:
                return false;
            default: {
                ALOGV("NOTIMPL op=%d uniq=%" PRIx64 " nid=%" PRIx64 "\n",
                      req.header().opcode,
                      req.header().unique,
                      req.header().nodeid);
                fuse_reply(fd, req.header().unique, -ENOSYS, NULL, 0);
                break;
                return true;
            }
        }
    }
@@ -198,7 +213,7 @@ private:

jboolean com_android_mtp_AppFuse_start_app_fuse_loop(
        JNIEnv* env, jobject self, jint jfd) {
    const int fd = static_cast<int>(jfd);
    ScopedFd fd(dup(static_cast<int>(jfd)));
    AppFuse appfuse(env, self);

    ALOGD("Start fuse loop.");
@@ -209,7 +224,7 @@ jboolean com_android_mtp_AppFuse_start_app_fuse_loop(
        if (result < 0) {
            if (errno == ENODEV) {
                ALOGE("Someone stole our marbles!\n");
                return false;
                return JNI_FALSE;
            }
            ALOGE("Failed to read bytes from FD: errno=%d\n", errno);
            continue;
@@ -227,7 +242,9 @@ jboolean com_android_mtp_AppFuse_start_app_fuse_loop(
            continue;
        }

        appfuse.handle_fuse_request(fd, request);
        if (!appfuse.handle_fuse_request(fd, request)) {
            return JNI_TRUE;
        }
    }
}

+18 −0
Original line number Diff line number Diff line
@@ -18,10 +18,13 @@ package com.android.mtp;

import android.os.ParcelFileDescriptor;
import android.os.storage.StorageManager;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;

import java.io.File;
import java.io.IOException;

import android.os.Process;

/**
@@ -54,6 +57,21 @@ public class AppFuse {
        mMessageThread.start();
    }

    @VisibleForTesting
    void close() {
        try {
            // Remote side of ParcelFileDescriptor is tracking the close of mDeviceFd, and unmount
            // the corresponding fuse file system. The mMessageThread will receive FUSE_FORGET, and
            // then terminate itself.
            mDeviceFd.close();
            mMessageThread.join();
        } catch (IOException exp) {
            Log.e(MtpDocumentsProvider.TAG, "Failed to close device FD.", exp);
        } catch (InterruptedException exp) {
            Log.e(MtpDocumentsProvider.TAG, "Failed to terminate message thread.", exp);
        }
    }

    @VisibleForTesting
    File getMountPoint() {
        return new File("/mnt/appfuse/" + Process.myUid() + "_" + mName);
+8 −1
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
package com.android.mtp;

import android.os.storage.StorageManager;
import android.system.ErrnoException;
import android.system.Os;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;

@@ -26,12 +28,17 @@ import java.io.File;
public class AppFuseTest extends AndroidTestCase {
    /**
     * TODO: Enable this test after adding SELinux policies for appfuse.
     * @throws ErrnoException
     * @throws InterruptedException
     */
    public void testBasic() {
    public void disabled_testBasic() throws ErrnoException, InterruptedException {
        final StorageManager storageManager = getContext().getSystemService(StorageManager.class);
        final AppFuse appFuse = new AppFuse("test");
        appFuse.mount(storageManager);
        final File file = appFuse.getMountPoint();
        assertTrue(file.isDirectory());
        assertEquals(1, Os.stat(file.getPath()).st_ino);
        appFuse.close();
        assertTrue(1 != Os.stat(file.getPath()).st_ino);
    }
}
+21 −6
Original line number Diff line number Diff line
@@ -2813,17 +2813,32 @@ class MountService extends IMountService.Stub
    }

    @Override
    public ParcelFileDescriptor mountAppFuse(String name) throws RemoteException {
    public ParcelFileDescriptor mountAppFuse(final String name) throws RemoteException {
        try {
            final int uid = Binder.getCallingUid();
            final NativeDaemonEvent event =
                    mConnector.execute("appfuse", "mount", Binder.getCallingUid(), name);
                    mConnector.execute("appfuse", "mount", uid, name);
            if (event.getFileDescriptors() == null) {
                Log.e(TAG, "AppFuse FD from vold is null.");
                return null;
                throw new RemoteException("AppFuse FD from vold is null.");
            }
            return ParcelFileDescriptor.fromFd(
                    event.getFileDescriptors()[0],
                    mHandler,
                    new ParcelFileDescriptor.OnCloseListener() {
                        @Override
                        public void onClose(IOException e) {
                            try {
                                final NativeDaemonEvent event = mConnector.execute(
                                        "appfuse", "unmount", uid, name);
                            } catch (NativeDaemonConnectorException unmountException) {
                                Log.e(TAG, "Failed to unmount appfuse.");
                            }
                        }
            return new ParcelFileDescriptor(event.getFileDescriptors()[0]);
                    });
        } catch (NativeDaemonConnectorException e) {
            throw e.rethrowAsParcelableException();
        } catch (IOException e) {
            throw new RemoteException(e.getMessage());
        }
    }