blob: 3b4febbf786c0cf91fa70fb7dcb0900a9a038d8b [file] [log] [blame]
Victor Hsieh045f1e62021-08-03 12:04:34 -07001/*
2 * Copyright (C) 2021 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
17use anyhow::{bail, Context, Result};
18use log::{debug, error, warn};
19use nix::mount::{umount2, MntFlags};
20use nix::sys::statfs::{statfs, FsType};
21use shared_child::SharedChild;
22use std::ffi::{OsStr, OsString};
23use std::fs::{remove_dir, OpenOptions};
24use std::path::PathBuf;
25use std::process::Command;
26use std::thread::sleep;
27use std::time::{Duration, Instant};
28
29use crate::common::new_binder_exception;
30use authfs_aidl_interface::aidl::com::android::virt::fs::IAuthFs::{BnAuthFs, IAuthFs};
31use authfs_aidl_interface::aidl::com::android::virt::fs::{
32 AuthFsConfig::AuthFsConfig, InputFdAnnotation::InputFdAnnotation,
33 OutputFdAnnotation::OutputFdAnnotation,
34};
35use authfs_aidl_interface::binder::{
36 self, BinderFeatures, ExceptionCode, Interface, ParcelFileDescriptor, Strong,
37};
38
39const AUTHFS_BIN: &str = "/system/bin/authfs";
40const AUTHFS_SETUP_POLL_INTERVAL_MS: Duration = Duration::from_millis(50);
41const AUTHFS_SETUP_TIMEOUT_SEC: Duration = Duration::from_secs(10);
42const FUSE_SUPER_MAGIC: FsType = FsType(0x65735546);
43
44/// An `AuthFs` instance is supposed to be backed by an `authfs` process. When the lifetime of the
45/// instance is over, it should leave no trace on the system: the process should be terminated, the
46/// FUSE should be unmounted, and the mount directory should be deleted.
47pub struct AuthFs {
48 mountpoint: OsString,
49 process: SharedChild,
50}
51
52impl Interface for AuthFs {}
53
54impl IAuthFs for AuthFs {
55 fn openFile(
56 &self,
57 remote_fd_name: i64,
58 writable: bool,
59 ) -> binder::Result<ParcelFileDescriptor> {
60 let mut path = PathBuf::from(&self.mountpoint);
61 path.push(remote_fd_name.to_string());
62 let file = OpenOptions::new().read(true).write(writable).open(&path).map_err(|e| {
63 new_binder_exception(
64 ExceptionCode::SERVICE_SPECIFIC,
65 format!("failed to open {:?} on authfs: {}", &path, e),
66 )
67 })?;
68 Ok(ParcelFileDescriptor::new(file))
69 }
70}
71
72impl AuthFs {
73 /// Mount an authfs at `mountpoint` with specified FD annotations.
74 pub fn mount_and_wait(
75 mountpoint: OsString,
76 config: &AuthFsConfig,
77 debuggable: bool,
78 ) -> Result<Strong<dyn IAuthFs>> {
79 let child = run_authfs(
80 &mountpoint,
81 &config.inputFdAnnotations,
82 &config.outputFdAnnotations,
83 debuggable,
84 )?;
85 wait_until_authfs_ready(&mountpoint).map_err(|e| {
86 debug!("Wait for authfs: {:?}", child.wait());
87 e
88 })?;
89
90 let authfs = AuthFs { mountpoint, process: child };
91 Ok(BnAuthFs::new_binder(authfs, BinderFeatures::default()))
92 }
93}
94
95impl Drop for AuthFs {
96 /// On drop, try to erase all the traces for this authfs mount.
97 fn drop(&mut self) {
98 debug!("Dropping AuthFs instance at mountpoint {:?}", &self.mountpoint);
99 if let Err(e) = self.process.kill() {
100 error!("Failed to kill authfs: {}", e);
101 }
102 match self.process.wait() {
103 Ok(status) => debug!("authfs exit code: {}", status),
104 Err(e) => warn!("Failed to wait for authfs: {}", e),
105 }
106 // The client may still hold the file descriptors that refer to this filesystem. Use
107 // MNT_DETACH to detach the mountpoint, and automatically unmount when there is no more
108 // reference.
109 if let Err(e) = umount2(self.mountpoint.as_os_str(), MntFlags::MNT_DETACH) {
110 error!("Failed to umount authfs at {:?}: {}", &self.mountpoint, e)
111 }
112
113 if let Err(e) = remove_dir(&self.mountpoint) {
114 error!("Failed to clean up mount directory {:?}: {}", &self.mountpoint, e)
115 }
116 }
117}
118
119fn run_authfs(
120 mountpoint: &OsStr,
121 in_fds: &[InputFdAnnotation],
122 out_fds: &[OutputFdAnnotation],
123 debuggable: bool,
124) -> Result<SharedChild> {
125 let mut args = vec![mountpoint.to_owned(), OsString::from("--cid=2")];
Victor Hsieh8bb67b62021-08-04 12:10:58 -0700126 args.push(OsString::from("-o"));
127 args.push(OsString::from("fscontext=u:object_r:authfs_fuse:s0"));
Victor Hsieh045f1e62021-08-03 12:04:34 -0700128 for conf in in_fds {
129 // TODO(b/185178698): Many input files need to be signed and verified.
130 // or can we use debug cert for now, which is better than nothing?
131 args.push(OsString::from("--remote-ro-file-unverified"));
132 args.push(OsString::from(format!("{}:{}:{}", conf.fd, conf.fd, conf.fileSize)));
133 }
134 for conf in out_fds {
135 args.push(OsString::from("--remote-new-rw-file"));
136 args.push(OsString::from(format!("{}:{}", conf.fd, conf.fd)));
137 }
138 if debuggable {
139 args.push(OsString::from("--debug"));
140 }
141
142 let mut command = Command::new(AUTHFS_BIN);
143 command.args(&args);
144 SharedChild::spawn(&mut command).context("Spawn authfs")
145}
146
147fn wait_until_authfs_ready(mountpoint: &OsStr) -> Result<()> {
148 let start_time = Instant::now();
149 loop {
150 if is_fuse(mountpoint)? {
151 break;
152 }
153 if start_time.elapsed() > AUTHFS_SETUP_TIMEOUT_SEC {
154 bail!("Time out mounting authfs");
155 }
156 sleep(AUTHFS_SETUP_POLL_INTERVAL_MS);
157 }
158 Ok(())
159}
160
161fn is_fuse(path: &OsStr) -> Result<bool> {
162 Ok(statfs(path)?.filesystem_type() == FUSE_SUPER_MAGIC)
163}