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