1 /* 2 * Copyright (C) 2016 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 specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.storage; 18 19 import android.os.FileUtils; 20 import android.os.ParcelFileDescriptor; 21 import android.system.ErrnoException; 22 import android.system.Os; 23 import android.util.SparseArray; 24 import com.android.internal.annotations.GuardedBy; 25 import com.android.internal.os.FuseUnavailableMountException; 26 import com.android.internal.util.Preconditions; 27 import com.android.server.NativeDaemonConnectorException; 28 import libcore.io.IoUtils; 29 import java.util.concurrent.CountDownLatch; 30 31 /** 32 * Runnable that delegates FUSE command from the kernel to application. 33 * run() blocks until all opened files on the FUSE mount point are closed. So this should be run in 34 * a separated thread. 35 */ 36 public class AppFuseBridge implements Runnable { 37 public static final String TAG = "AppFuseBridge"; 38 39 /** 40 * The path AppFuse is mounted to. 41 * The first number is UID who is mounting the FUSE. 42 * THe second number is mount ID. 43 * The path must be sync with vold. 44 */ 45 private static final String APPFUSE_MOUNT_NAME_TEMPLATE = "/mnt/appfuse/%d_%d"; 46 47 @GuardedBy("this") 48 private final SparseArray<MountScope> mScopes = new SparseArray<>(); 49 50 @GuardedBy("this") 51 private long mNativeLoop; 52 AppFuseBridge()53 public AppFuseBridge() { 54 mNativeLoop = native_new(); 55 } 56 addBridge(MountScope mountScope)57 public ParcelFileDescriptor addBridge(MountScope mountScope) 58 throws FuseUnavailableMountException, NativeDaemonConnectorException { 59 /* 60 ** Dead Lock between Java lock (AppFuseBridge.java) and Native lock (FuseBridgeLoop.cc) 61 ** 62 ** (Thread A) Got Java lock (addBrdige) -> Try to get Native lock (native_add_brdige) 63 ** (Thread B) Got Native lock (FuseBrdigeLoop.start) -> Try to get Java lock (onClosed) 64 ** 65 ** Guarantee the lock order (native lock -> java lock) when adding Bridge. 66 */ 67 native_lock(); 68 try { 69 synchronized (this) { 70 Preconditions.checkArgument(mScopes.indexOfKey(mountScope.mountId) < 0); 71 if (mNativeLoop == 0) { 72 throw new FuseUnavailableMountException(mountScope.mountId); 73 } 74 final int fd = native_add_bridge( 75 mNativeLoop, mountScope.mountId, mountScope.open().detachFd()); 76 if (fd == -1) { 77 throw new FuseUnavailableMountException(mountScope.mountId); 78 } 79 final ParcelFileDescriptor result = ParcelFileDescriptor.adoptFd(fd); 80 mScopes.put(mountScope.mountId, mountScope); 81 mountScope = null; 82 return result; 83 } 84 } finally { 85 native_unlock(); 86 IoUtils.closeQuietly(mountScope); 87 } 88 } 89 90 @Override 91 public void run() { 92 native_start_loop(mNativeLoop); 93 synchronized (this) { 94 native_delete(mNativeLoop); 95 mNativeLoop = 0; 96 } 97 } 98 99 public ParcelFileDescriptor openFile(int mountId, int fileId, int mode) 100 throws FuseUnavailableMountException, InterruptedException { 101 final MountScope scope; 102 synchronized (this) { 103 scope = mScopes.get(mountId); 104 if (scope == null) { 105 throw new FuseUnavailableMountException(mountId); 106 } 107 } 108 final boolean result = scope.waitForMount(); 109 if (result == false) { 110 throw new FuseUnavailableMountException(mountId); 111 } 112 try { 113 int flags = FileUtils.translateModePfdToPosix(mode); 114 return scope.openFile(mountId, fileId, flags); 115 } catch (NativeDaemonConnectorException error) { 116 throw new FuseUnavailableMountException(mountId); 117 } 118 } 119 120 // Used by com_android_server_storage_AppFuse.cpp. 121 synchronized private void onMount(int mountId) { 122 final MountScope scope = mScopes.get(mountId); 123 if (scope != null) { 124 scope.setMountResultLocked(true); 125 } 126 } 127 128 // Used by com_android_server_storage_AppFuse.cpp. 129 synchronized private void onClosed(int mountId) { 130 final MountScope scope = mScopes.get(mountId); 131 if (scope != null) { 132 scope.setMountResultLocked(false); 133 IoUtils.closeQuietly(scope); 134 mScopes.remove(mountId); 135 } 136 } 137 138 public static abstract class MountScope implements AutoCloseable { 139 public final int uid; 140 public final int mountId; 141 private final CountDownLatch mMounted = new CountDownLatch(1); 142 private boolean mMountResult = false; 143 144 public MountScope(int uid, int mountId) { 145 this.uid = uid; 146 this.mountId = mountId; 147 } 148 149 @GuardedBy("AppFuseBridge.this") 150 void setMountResultLocked(boolean result) { 151 if (mMounted.getCount() == 0) { 152 return; 153 } 154 mMountResult = result; 155 mMounted.countDown(); 156 } 157 158 boolean waitForMount() throws InterruptedException { 159 mMounted.await(); 160 return mMountResult; 161 } 162 163 public abstract ParcelFileDescriptor open() throws NativeDaemonConnectorException; 164 public abstract ParcelFileDescriptor openFile(int mountId, int fileId, int flags) 165 throws NativeDaemonConnectorException; 166 } 167 168 private native long native_new(); 169 private native void native_delete(long loop); 170 private native void native_start_loop(long loop); 171 private native int native_add_bridge(long loop, int mountId, int deviceId); 172 private native void native_lock(); 173 private native void native_unlock(); 174 } 175