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