• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 authfs_aidl_interface::binder::{
35     self, BinderFeatures, ExceptionCode, Interface, ParcelFileDescriptor, Strong,
36 };
37 use binder_common::new_binder_exception;
38 
39 const AUTHFS_BIN: &str = "/system/bin/authfs";
40 const AUTHFS_SETUP_POLL_INTERVAL_MS: Duration = Duration::from_millis(50);
41 const AUTHFS_SETUP_TIMEOUT_SEC: Duration = Duration::from_secs(10);
42 const 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.
47 pub struct AuthFs {
48     mountpoint: OsString,
49     process: SharedChild,
50 }
51 
52 impl Interface for AuthFs {}
53 
54 impl IAuthFs for AuthFs {
openFile( &self, remote_fd_name: i32, writable: bool, ) -> binder::Result<ParcelFileDescriptor>55     fn openFile(
56         &self,
57         remote_fd_name: i32,
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 
getMountPoint(&self) -> binder::Result<String>71     fn getMountPoint(&self) -> binder::Result<String> {
72         if let Some(s) = self.mountpoint.to_str() {
73             Ok(s.to_string())
74         } else {
75             Err(new_binder_exception(ExceptionCode::SERVICE_SPECIFIC, "Bad string encoding"))
76         }
77     }
78 }
79 
80 impl AuthFs {
81     /// Mount an authfs at `mountpoint` with specified FD annotations.
mount_and_wait( mountpoint: OsString, config: &AuthFsConfig, debuggable: bool, ) -> Result<Strong<dyn IAuthFs>>82     pub fn mount_and_wait(
83         mountpoint: OsString,
84         config: &AuthFsConfig,
85         debuggable: bool,
86     ) -> Result<Strong<dyn IAuthFs>> {
87         let child = run_authfs(
88             &mountpoint,
89             &config.inputFdAnnotations,
90             &config.outputFdAnnotations,
91             &config.inputDirFdAnnotations,
92             &config.outputDirFdAnnotations,
93             debuggable,
94         )?;
95         wait_until_authfs_ready(&child, &mountpoint).map_err(|e| {
96             match child.wait() {
97                 Ok(status) => debug!("Wait for authfs: {}", status),
98                 Err(e) => warn!("Failed to wait for child: {}", e),
99             }
100             e
101         })?;
102 
103         let authfs = AuthFs { mountpoint, process: child };
104         Ok(BnAuthFs::new_binder(authfs, BinderFeatures::default()))
105     }
106 }
107 
108 impl Drop for AuthFs {
109     /// On drop, try to erase all the traces for this authfs mount.
drop(&mut self)110     fn drop(&mut self) {
111         debug!("Dropping AuthFs instance at mountpoint {:?}", &self.mountpoint);
112         if let Err(e) = self.process.kill() {
113             error!("Failed to kill authfs: {}", e);
114         }
115         match self.process.wait() {
116             Ok(status) => debug!("authfs exit code: {}", status),
117             Err(e) => warn!("Failed to wait for authfs: {}", e),
118         }
119         // The client may still hold the file descriptors that refer to this filesystem. Use
120         // MNT_DETACH to detach the mountpoint, and automatically unmount when there is no more
121         // reference.
122         if let Err(e) = umount2(self.mountpoint.as_os_str(), MntFlags::MNT_DETACH) {
123             error!("Failed to umount authfs at {:?}: {}", &self.mountpoint, e)
124         }
125 
126         if let Err(e) = remove_dir(&self.mountpoint) {
127             error!("Failed to clean up mount directory {:?}: {}", &self.mountpoint, e)
128         }
129     }
130 }
131 
run_authfs( mountpoint: &OsStr, in_file_fds: &[InputFdAnnotation], out_file_fds: &[OutputFdAnnotation], in_dir_fds: &[InputDirFdAnnotation], out_dir_fds: &[OutputDirFdAnnotation], debuggable: bool, ) -> Result<SharedChild>132 fn run_authfs(
133     mountpoint: &OsStr,
134     in_file_fds: &[InputFdAnnotation],
135     out_file_fds: &[OutputFdAnnotation],
136     in_dir_fds: &[InputDirFdAnnotation],
137     out_dir_fds: &[OutputDirFdAnnotation],
138     debuggable: bool,
139 ) -> Result<SharedChild> {
140     let mut args = vec![mountpoint.to_owned(), OsString::from("--cid=2")];
141     args.push(OsString::from("-o"));
142     args.push(OsString::from("fscontext=u:object_r:authfs_fuse:s0"));
143     for conf in in_file_fds {
144         // TODO(b/185178698): Many input files need to be signed and verified.
145         // or can we use debug cert for now, which is better than nothing?
146         args.push(OsString::from("--remote-ro-file-unverified"));
147         args.push(OsString::from(conf.fd.to_string()));
148     }
149     for conf in out_file_fds {
150         args.push(OsString::from("--remote-new-rw-file"));
151         args.push(OsString::from(conf.fd.to_string()));
152     }
153     for conf in in_dir_fds {
154         args.push(OsString::from("--remote-ro-dir"));
155         args.push(OsString::from(format!("{}:{}:{}", conf.fd, conf.manifestPath, conf.prefix)));
156     }
157     for conf in out_dir_fds {
158         args.push(OsString::from("--remote-new-rw-dir"));
159         args.push(OsString::from(conf.fd.to_string()));
160     }
161     if debuggable {
162         args.push(OsString::from("--debug"));
163     }
164 
165     let mut command = Command::new(AUTHFS_BIN);
166     command.args(&args);
167     debug!("Spawn authfs: {:?}", command);
168     SharedChild::spawn(&mut command).context("Spawn authfs")
169 }
170 
wait_until_authfs_ready(child: &SharedChild, mountpoint: &OsStr) -> Result<()>171 fn wait_until_authfs_ready(child: &SharedChild, mountpoint: &OsStr) -> Result<()> {
172     let start_time = Instant::now();
173     loop {
174         if is_fuse(mountpoint)? {
175             break;
176         }
177         if let Some(exit_status) = child.try_wait()? {
178             // If the child has exited, we will never become ready.
179             bail!("Child has exited: {}", exit_status);
180         }
181         if start_time.elapsed() > AUTHFS_SETUP_TIMEOUT_SEC {
182             let _ = child.kill();
183             bail!("Time out mounting authfs");
184         }
185         sleep(AUTHFS_SETUP_POLL_INTERVAL_MS);
186     }
187     Ok(())
188 }
189 
is_fuse(path: &OsStr) -> Result<bool>190 fn is_fuse(path: &OsStr) -> Result<bool> {
191     Ok(statfs(path)?.filesystem_type() == FUSE_SUPER_MAGIC)
192 }
193