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 try { 60 synchronized (this) { 61 Preconditions.checkArgument(mScopes.indexOfKey(mountScope.mountId) < 0); 62 if (mNativeLoop == 0) { 63 throw new FuseUnavailableMountException(mountScope.mountId); 64 } 65 final int fd = native_add_bridge( 66 mNativeLoop, mountScope.mountId, mountScope.open().detachFd()); 67 if (fd == -1) { 68 throw new FuseUnavailableMountException(mountScope.mountId); 69 } 70 final ParcelFileDescriptor result = ParcelFileDescriptor.adoptFd(fd); 71 mScopes.put(mountScope.mountId, mountScope); 72 mountScope = null; 73 return result; 74 } 75 } finally { 76 IoUtils.closeQuietly(mountScope); 77 } 78 } 79 80 @Override 81 public void run() { 82 native_start_loop(mNativeLoop); 83 synchronized (this) { 84 native_delete(mNativeLoop); 85 mNativeLoop = 0; 86 } 87 } 88 89 public ParcelFileDescriptor openFile(int mountId, int fileId, int mode) 90 throws FuseUnavailableMountException, InterruptedException { 91 final MountScope scope; 92 synchronized (this) { 93 scope = mScopes.get(mountId); 94 if (scope == null) { 95 throw new FuseUnavailableMountException(mountId); 96 } 97 } 98 final boolean result = scope.waitForMount(); 99 if (result == false) { 100 throw new FuseUnavailableMountException(mountId); 101 } 102 try { 103 int flags = FileUtils.translateModePfdToPosix(mode); 104 return scope.openFile(mountId, fileId, flags); 105 } catch (NativeDaemonConnectorException error) { 106 throw new FuseUnavailableMountException(mountId); 107 } 108 } 109 110 // Used by com_android_server_storage_AppFuse.cpp. 111 synchronized private void onMount(int mountId) { 112 final MountScope scope = mScopes.get(mountId); 113 if (scope != null) { 114 scope.setMountResultLocked(true); 115 } 116 } 117 118 // Used by com_android_server_storage_AppFuse.cpp. 119 synchronized private void onClosed(int mountId) { 120 final MountScope scope = mScopes.get(mountId); 121 if (scope != null) { 122 scope.setMountResultLocked(false); 123 IoUtils.closeQuietly(scope); 124 mScopes.remove(mountId); 125 } 126 } 127 128 public static abstract class MountScope implements AutoCloseable { 129 public final int uid; 130 public final int mountId; 131 private final CountDownLatch mMounted = new CountDownLatch(1); 132 private boolean mMountResult = false; 133 134 public MountScope(int uid, int mountId) { 135 this.uid = uid; 136 this.mountId = mountId; 137 } 138 139 @GuardedBy("AppFuseBridge.this") 140 void setMountResultLocked(boolean result) { 141 if (mMounted.getCount() == 0) { 142 return; 143 } 144 mMountResult = result; 145 mMounted.countDown(); 146 } 147 148 boolean waitForMount() throws InterruptedException { 149 mMounted.await(); 150 return mMountResult; 151 } 152 153 public abstract ParcelFileDescriptor open() throws NativeDaemonConnectorException; 154 public abstract ParcelFileDescriptor openFile(int mountId, int fileId, int flags) 155 throws NativeDaemonConnectorException; 156 } 157 158 private native long native_new(); 159 private native void native_delete(long loop); 160 private native void native_start_loop(long loop); 161 private native int native_add_bridge(long loop, int mountId, int deviceId); 162 } 163