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 //! A helper library to start a fd_server.
18 
19 use anyhow::{Context, Result};
20 use log::{debug, warn};
21 use minijail::Minijail;
22 use nix::fcntl::OFlag;
23 use nix::unistd::pipe2;
24 use std::fs::File;
25 use std::io::Read;
26 use std::os::unix::io::{AsRawFd, OwnedFd};
27 use std::path::Path;
28 
29 const FD_SERVER_BIN: &str = "/apex/com.android.virt/bin/fd_server";
30 
31 /// Config for starting a `FdServer`
32 #[derive(Default)]
33 pub struct FdServerConfig {
34     /// List of file FDs exposed for read-only operations.
35     pub ro_file_fds: Vec<OwnedFd>,
36     /// List of file FDs exposed for read-write operations.
37     pub rw_file_fds: Vec<OwnedFd>,
38     /// List of directory FDs exposed for read-only operations.
39     pub ro_dir_fds: Vec<OwnedFd>,
40     /// List of directory FDs exposed for read-write operations.
41     pub rw_dir_fds: Vec<OwnedFd>,
42 }
43 
44 impl FdServerConfig {
45     /// Creates a `FdServer` based on the current config.
into_fd_server(self) -> Result<FdServer>46     pub fn into_fd_server(self) -> Result<FdServer> {
47         let (ready_read_fd, ready_write_fd) = create_pipe()?;
48         let fd_server_jail = self.do_spawn_fd_server(ready_write_fd)?;
49         wait_for_fd_server_ready(ready_read_fd)?;
50         Ok(FdServer { jailed_process: fd_server_jail })
51     }
52 
do_spawn_fd_server(self, ready_file: File) -> Result<Minijail>53     fn do_spawn_fd_server(self, ready_file: File) -> Result<Minijail> {
54         let mut inheritable_fds = Vec::new();
55         let mut args = vec![FD_SERVER_BIN.to_string()];
56         for fd in &self.ro_file_fds {
57             let raw_fd = fd.as_raw_fd();
58             args.push("--ro-fds".to_string());
59             args.push(raw_fd.to_string());
60             inheritable_fds.push(raw_fd);
61         }
62         for fd in &self.rw_file_fds {
63             let raw_fd = fd.as_raw_fd();
64             args.push("--rw-fds".to_string());
65             args.push(raw_fd.to_string());
66             inheritable_fds.push(raw_fd);
67         }
68         for fd in &self.ro_dir_fds {
69             let raw_fd = fd.as_raw_fd();
70             args.push("--ro-dirs".to_string());
71             args.push(raw_fd.to_string());
72             inheritable_fds.push(raw_fd);
73         }
74         for fd in &self.rw_dir_fds {
75             let raw_fd = fd.as_raw_fd();
76             args.push("--rw-dirs".to_string());
77             args.push(raw_fd.to_string());
78             inheritable_fds.push(raw_fd);
79         }
80         let ready_fd = ready_file.as_raw_fd();
81         args.push("--ready-fd".to_string());
82         args.push(ready_fd.to_string());
83         inheritable_fds.push(ready_fd);
84 
85         debug!("Spawn fd_server {:?} (inheriting FDs: {:?})", args, inheritable_fds);
86         let jail = Minijail::new()?;
87         let _pid = jail.run(Path::new(FD_SERVER_BIN), &inheritable_fds, &args)?;
88         Ok(jail)
89     }
90 }
91 
92 /// `FdServer` represents a running `fd_server` process. The process lifetime is associated with
93 /// the instance lifetime.
94 pub struct FdServer {
95     jailed_process: Minijail,
96 }
97 
98 impl Drop for FdServer {
drop(&mut self)99     fn drop(&mut self) {
100         if let Err(e) = self.jailed_process.kill() {
101             if !matches!(e, minijail::Error::Killed(_)) {
102                 warn!("Failed to kill fd_server: {}", e);
103             }
104         }
105     }
106 }
107 
create_pipe() -> Result<(File, File)>108 fn create_pipe() -> Result<(File, File)> {
109     let (read_fd, write_fd) = pipe2(OFlag::O_CLOEXEC)?;
110     Ok((read_fd.into(), write_fd.into()))
111 }
112 
wait_for_fd_server_ready(mut ready_fd: File) -> Result<()>113 fn wait_for_fd_server_ready(mut ready_fd: File) -> Result<()> {
114     let mut buffer = [0];
115     // When fd_server is ready it closes its end of the pipe. And if it exits, the pipe is also
116     // closed. Either way this read will return 0 bytes at that point, and there's no point waiting
117     // any longer.
118     let _ = ready_fd.read(&mut buffer).context("Waiting for fd_server to be ready")?;
119     debug!("fd_server is ready");
120     Ok(())
121 }
122