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