blob: 5a1f473b5ea282e3b9d9a898cc7e8a003f3ff35d [file] [log] [blame]
Daichi Hirono3ff1c012016-10-28 12:53:44 +09001/*
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
17package com.android.server.storage;
18
Daichi Hirono3ff1c012016-10-28 12:53:44 +090019import android.os.ParcelFileDescriptor;
20import android.system.ErrnoException;
21import android.system.Os;
Daichi Hirono9fb00182016-11-08 14:12:17 +090022import com.android.internal.util.Preconditions;
Daichi Hirono3ff1c012016-10-28 12:53:44 +090023import libcore.io.IoUtils;
Daichi Hirono3ff1c012016-10-28 12:53:44 +090024import java.io.File;
Daichi Hirono3ff1c012016-10-28 12:53:44 +090025import java.io.FileNotFoundException;
Daichi Hirono9fb00182016-11-08 14:12:17 +090026import java.util.concurrent.BlockingQueue;
Daichi Hirono3ff1c012016-10-28 12:53:44 +090027
Daichi Hirono9fb00182016-11-08 14:12:17 +090028/**
29 * Runnable that delegates FUSE command from the kernel to application.
30 * run() blocks until all opened files on the FUSE mount point are closed. So this should be run in
31 * a separated thread.
32 */
33public class AppFuseBridge implements Runnable, AutoCloseable {
34 public static final String TAG = "AppFuseBridge";
Daichi Hirono3ff1c012016-10-28 12:53:44 +090035
36 /**
Daichi Hirono9fb00182016-11-08 14:12:17 +090037 * The path AppFuse is mounted to.
38 * The first number is UID who is mounting the FUSE.
39 * THe second number is mount ID.
40 * The path must be sync with vold.
Daichi Hirono3ff1c012016-10-28 12:53:44 +090041 */
Daichi Hirono9fb00182016-11-08 14:12:17 +090042 private static final String APPFUSE_MOUNT_NAME_TEMPLATE = "/mnt/appfuse/%d_%d";
43
44 private final IMountScope mMountScope;
45 private final ParcelFileDescriptor mProxyFd;
46 private final BlockingQueue<Boolean> mChannel;
47
48 /**
49 * @param mountScope Listener to unmount mount point.
50 * @param proxyFd FD of socket pair. Ownership of FD is taken by AppFuseBridge.
51 * @param channel Channel that the runnable send mount result to.
52 */
53 public AppFuseBridge(
54 IMountScope mountScope, ParcelFileDescriptor proxyFd, BlockingQueue<Boolean> channel) {
55 Preconditions.checkNotNull(mountScope);
56 Preconditions.checkNotNull(proxyFd);
57 Preconditions.checkNotNull(channel);
58 mMountScope = mountScope;
Daichi Hirono3ff1c012016-10-28 12:53:44 +090059 mProxyFd = proxyFd;
Daichi Hirono9fb00182016-11-08 14:12:17 +090060 mChannel = channel;
Daichi Hirono3ff1c012016-10-28 12:53:44 +090061 }
62
63 @Override
64 public void run() {
Daichi Hirono9fb00182016-11-08 14:12:17 +090065 try {
66 // deviceFd and proxyFd must be closed in native_start_loop.
67 native_start_loop(
68 mMountScope.getDeviceFileDescriptor().detachFd(),
69 mProxyFd.detachFd());
70 } finally {
71 close();
72 }
73 }
74
75 public static ParcelFileDescriptor openFile(int uid, int mountId, int fileId, int mode)
76 throws FileNotFoundException {
77 final File mountPoint = getMountPoint(uid, mountId);
78 try {
79 if (Os.stat(mountPoint.getPath()).st_ino != 1) {
80 throw new FileNotFoundException("Could not find bridge mount point.");
81 }
82 } catch (ErrnoException e) {
83 throw new FileNotFoundException(
84 "Failed to stat mount point: " + mountPoint.getParent());
85 }
86 return ParcelFileDescriptor.open(new File(mountPoint, String.valueOf(fileId)), mode);
87 }
88
89 private static File getMountPoint(int uid, int mountId) {
90 return new File(String.format(APPFUSE_MOUNT_NAME_TEMPLATE, uid, mountId));
91 }
92
93 @Override
94 public void close() {
95 IoUtils.closeQuietly(mMountScope);
96 IoUtils.closeQuietly(mProxyFd);
97 // Invoke countDown here in case where close is invoked before mount.
98 mChannel.offer(false);
Daichi Hirono3ff1c012016-10-28 12:53:44 +090099 }
100
101 // Used by com_android_server_storage_AppFuse.cpp.
102 private void onMount() {
Daichi Hirono9fb00182016-11-08 14:12:17 +0900103 mChannel.offer(true);
104 }
105
106 public static interface IMountScope extends AutoCloseable {
107 ParcelFileDescriptor getDeviceFileDescriptor();
Daichi Hirono3ff1c012016-10-28 12:53:44 +0900108 }
109
110 private native boolean native_start_loop(int deviceFd, int proxyFd);
111}