13ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono/*
23ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono * Copyright (C) 2016 The Android Open Source Project
33ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono *
43ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono * Licensed under the Apache License, Version 2.0 (the "License");
53ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono * you may not use this file except in compliance with the License.
63ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono * You may obtain a copy of the License at
73ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono *
83ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono *      http://www.apache.org/licenses/LICENSE-2.0
93ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono *
103ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono * Unless required by applicable law or agreed to in writing, software
113ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono * distributed under the License is distributed on an "AS IS" BASIS,
123ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono * See the License for the specific language governing permissions and
143ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono * limitations under the License.
153ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono */
163ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono
173ff1c01cae0b654acd53088634e07e26557edd99Daichi Hironopackage com.android.server.storage;
183ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono
193ff1c01cae0b654acd53088634e07e26557edd99Daichi Hironoimport android.os.ParcelFileDescriptor;
203ff1c01cae0b654acd53088634e07e26557edd99Daichi Hironoimport android.system.ErrnoException;
213ff1c01cae0b654acd53088634e07e26557edd99Daichi Hironoimport android.system.Os;
22e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hironoimport android.util.SparseArray;
23e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hironoimport com.android.internal.annotations.GuardedBy;
24812c95d37dccf8a1fcef55c6999c6d69ecbac400Daichi Hironoimport com.android.internal.os.FuseUnavailableMountException;
259fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hironoimport com.android.internal.util.Preconditions;
26812c95d37dccf8a1fcef55c6999c6d69ecbac400Daichi Hironoimport com.android.server.NativeDaemonConnectorException;
273ff1c01cae0b654acd53088634e07e26557edd99Daichi Hironoimport libcore.io.IoUtils;
283ff1c01cae0b654acd53088634e07e26557edd99Daichi Hironoimport java.io.File;
293ff1c01cae0b654acd53088634e07e26557edd99Daichi Hironoimport java.io.FileNotFoundException;
30e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hironoimport java.util.concurrent.CountDownLatch;
319fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono
329fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono/**
339fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono * Runnable that delegates FUSE command from the kernel to application.
349fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono * run() blocks until all opened files on the FUSE mount point are closed. So this should be run in
359fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono * a separated thread.
369fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono */
37e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hironopublic class AppFuseBridge implements Runnable {
389fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono    public static final String TAG = "AppFuseBridge";
393ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono
409fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono    /**
419fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono     * The path AppFuse is mounted to.
429fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono     * The first number is UID who is mounting the FUSE.
439fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono     * THe second number is mount ID.
449fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono     * The path must be sync with vold.
459fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono     */
469fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono    private static final String APPFUSE_MOUNT_NAME_TEMPLATE = "/mnt/appfuse/%d_%d";
473ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono
48e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono    @GuardedBy("this")
49e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono    private final SparseArray<MountScope> mScopes = new SparseArray<>();
503ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono
51e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono    @GuardedBy("this")
52e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono    private long mNativeLoop;
53e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono
54e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono    public AppFuseBridge() {
55e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        mNativeLoop = native_new();
563ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono    }
573ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono
58e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono    public ParcelFileDescriptor addBridge(MountScope mountScope)
59812c95d37dccf8a1fcef55c6999c6d69ecbac400Daichi Hirono            throws FuseUnavailableMountException, NativeDaemonConnectorException {
609fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono        try {
61e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono            synchronized (this) {
62e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono                Preconditions.checkArgument(mScopes.indexOfKey(mountScope.mountId) < 0);
63e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono                if (mNativeLoop == 0) {
64812c95d37dccf8a1fcef55c6999c6d69ecbac400Daichi Hirono                    throw new FuseUnavailableMountException(mountScope.mountId);
65e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono                }
66e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono                final int fd = native_add_bridge(
67812c95d37dccf8a1fcef55c6999c6d69ecbac400Daichi Hirono                        mNativeLoop, mountScope.mountId, mountScope.open().detachFd());
68e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono                if (fd == -1) {
69812c95d37dccf8a1fcef55c6999c6d69ecbac400Daichi Hirono                    throw new FuseUnavailableMountException(mountScope.mountId);
70e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono                }
71e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono                final ParcelFileDescriptor result = ParcelFileDescriptor.adoptFd(fd);
72e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono                mScopes.put(mountScope.mountId, mountScope);
73e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono                mountScope = null;
74e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono                return result;
75e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono            }
769fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono        } finally {
77e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono            IoUtils.closeQuietly(mountScope);
78e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        }
79e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono    }
80e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono
81e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono    @Override
82e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono    public void run() {
83e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        native_start_loop(mNativeLoop);
84e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        synchronized (this) {
85e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono            native_delete(mNativeLoop);
86e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono            mNativeLoop = 0;
879fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono        }
889fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono    }
893ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono
90e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono    public ParcelFileDescriptor openFile(int pid, int mountId, int fileId, int mode)
91812c95d37dccf8a1fcef55c6999c6d69ecbac400Daichi Hirono            throws FuseUnavailableMountException, InterruptedException {
92e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        final MountScope scope;
93e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        synchronized (this) {
94e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono            scope = mScopes.get(mountId);
95e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono            if (scope == null) {
96812c95d37dccf8a1fcef55c6999c6d69ecbac400Daichi Hirono                throw new FuseUnavailableMountException(mountId);
97e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono            }
98e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        }
99e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        if (scope.pid != pid) {
100e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono            throw new SecurityException("PID does not match");
101e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        }
102e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        final boolean result = scope.waitForMount();
103e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        if (result == false) {
104812c95d37dccf8a1fcef55c6999c6d69ecbac400Daichi Hirono            throw new FuseUnavailableMountException(mountId);
105e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        }
1063ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono        try {
107812c95d37dccf8a1fcef55c6999c6d69ecbac400Daichi Hirono            return ParcelFileDescriptor.open(
108812c95d37dccf8a1fcef55c6999c6d69ecbac400Daichi Hirono                    new File(scope.mountPoint, String.valueOf(fileId)), mode);
109812c95d37dccf8a1fcef55c6999c6d69ecbac400Daichi Hirono        } catch (FileNotFoundException error) {
110812c95d37dccf8a1fcef55c6999c6d69ecbac400Daichi Hirono            throw new FuseUnavailableMountException(mountId);
1113ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono        }
1129fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono    }
1139fb00183a04036a58ee208f5bfd6c9768982f0aaDaichi Hirono
114e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono    // Used by com_android_server_storage_AppFuse.cpp.
115e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono    synchronized private void onMount(int mountId) {
116e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        final MountScope scope = mScopes.get(mountId);
117e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        if (scope != null) {
118e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono            scope.setMountResultLocked(true);
119e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        }
1203ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono    }
1213ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono
122e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono    // Used by com_android_server_storage_AppFuse.cpp.
123e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono    synchronized private void onClosed(int mountId) {
124e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        final MountScope scope = mScopes.get(mountId);
125e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        if (scope != null) {
126e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono            scope.setMountResultLocked(false);
127e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono            IoUtils.closeQuietly(scope);
128e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono            mScopes.remove(mountId);
129e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        }
1303ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono    }
1313ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono
132812c95d37dccf8a1fcef55c6999c6d69ecbac400Daichi Hirono    public static abstract class MountScope implements AutoCloseable {
133e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        public final int uid;
134e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        public final int pid;
135e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        public final int mountId;
136e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        public final File mountPoint;
137e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        private final CountDownLatch mMounted = new CountDownLatch(1);
138e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        private boolean mMountResult = false;
139e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono
140812c95d37dccf8a1fcef55c6999c6d69ecbac400Daichi Hirono        public MountScope(int uid, int pid, int mountId) {
141e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono            this.uid = uid;
142e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono            this.pid = pid;
143e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono            this.mountId = mountId;
144e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono            this.mountPoint = new File(String.format(APPFUSE_MOUNT_NAME_TEMPLATE,  uid, mountId));
145e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        }
146e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono
147e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        @GuardedBy("AppFuseBridge.this")
148e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        void setMountResultLocked(boolean result) {
149e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono            if (mMounted.getCount() == 0) {
150e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono                return;
151e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono            }
152e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono            mMountResult = result;
153e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono            mMounted.countDown();
154e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        }
155e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono
156e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        boolean waitForMount() throws InterruptedException {
157e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono            mMounted.await();
158e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono            return mMountResult;
159e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono        }
160e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono
161812c95d37dccf8a1fcef55c6999c6d69ecbac400Daichi Hirono        public abstract ParcelFileDescriptor open() throws NativeDaemonConnectorException;
1623ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono    }
1633ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono
164e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono    private native long native_new();
165e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono    private native void native_delete(long loop);
166e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono    private native void native_start_loop(long loop);
167e56740dc081cd3024fa2a0c72689febd49ee1c9fDaichi Hirono    private native int native_add_bridge(long loop, int mountId, int deviceId);
1683ff1c01cae0b654acd53088634e07e26557edd99Daichi Hirono}
169