blob: 7f26fd79acac51582b8acddfa7180abe968af504 [file] [log] [blame]
Victor Hsieh45636232021-10-15 17:52:51 -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
Victor Hsiehdd99b462021-12-02 17:36:15 -080017use log::warn;
Victor Hsiehf393a722021-12-08 13:04:27 -080018use nix::sys::stat::Mode;
Victor Hsieh4d6b9d42021-11-08 15:53:49 -080019use std::collections::{hash_map, HashMap};
Victor Hsieh45636232021-10-15 17:52:51 -070020use std::io;
21use std::path::{Path, PathBuf};
22
Victor Hsiehf393a722021-12-08 13:04:27 -080023use super::attr::Attr;
Victor Hsieh45636232021-10-15 17:52:51 -070024use super::remote_file::RemoteFileEditor;
Victor Hsieh4d6b9d42021-11-08 15:53:49 -080025use super::{validate_basename, VirtFdService, VirtFdServiceStatus};
Victor Hsieh45636232021-10-15 17:52:51 -070026use crate::fsverity::VerifiedFileEditor;
27use crate::fusefs::Inode;
28
29const MAX_ENTRIES: u16 = 100; // Arbitrary limit
30
Victor Hsiehdd99b462021-12-02 17:36:15 -080031struct DirEntry {
32 inode: Inode,
33
34 // This information is duplicated since it is also available in `AuthFs::inode_table` via the
35 // type system. But it makes it simple to deal with deletion, where otherwise we need to get a
36 // mutable parent directory in the table, and query the table for directory/file type checking
37 // at the same time.
38 is_dir: bool,
39}
40
Victor Hsieh45636232021-10-15 17:52:51 -070041/// A remote directory backed by a remote directory FD, where the provider/fd_server is not
42/// trusted.
43///
44/// The directory is assumed empty initially without the trust to the storage. Functionally, when
45/// the backing storage is not clean, the fd_server can fail to create a file or directory when
46/// there is name collision. From RemoteDirEditor's perspective of security, the creation failure
47/// is just one of possible errors that can happen, and what matters is RemoteDirEditor maintains
48/// the integrity itself.
49///
50/// When new files are created through RemoteDirEditor, the file integrity are maintained within the
51/// VM. Similarly, integrity (namely the list of entries) of the directory, or new directories
52/// created within such a directory, are also maintained within the VM. A compromised fd_server or
53/// malicious client can't affect the view to the files and directories within such a directory in
54/// the VM.
55pub struct RemoteDirEditor {
56 service: VirtFdService,
57 remote_dir_fd: i32,
58
Victor Hsiehdd99b462021-12-02 17:36:15 -080059 /// Mapping of entry names to the corresponding inode. The actual file/directory is stored in
60 /// the global pool in fusefs.
61 entries: HashMap<PathBuf, DirEntry>,
Victor Hsieh45636232021-10-15 17:52:51 -070062}
63
64impl RemoteDirEditor {
65 pub fn new(service: VirtFdService, remote_dir_fd: i32) -> Self {
66 RemoteDirEditor { service, remote_dir_fd, entries: HashMap::new() }
67 }
68
69 /// Returns the number of entries created.
70 pub fn number_of_entries(&self) -> u16 {
71 self.entries.len() as u16 // limited to MAX_ENTRIES
72 }
73
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -080074 /// Creates a remote file named `basename` with corresponding `inode` at the current directory.
Victor Hsieh45636232021-10-15 17:52:51 -070075 pub fn create_file(
76 &mut self,
77 basename: &Path,
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -080078 inode: Inode,
Victor Hsiehf393a722021-12-08 13:04:27 -080079 mode: libc::mode_t,
80 ) -> io::Result<(VerifiedFileEditor<RemoteFileEditor>, Attr)> {
81 let mode = self.validate_arguments(basename, mode)?;
Victor Hsieh45636232021-10-15 17:52:51 -070082 let basename_str =
83 basename.to_str().ok_or_else(|| io::Error::from_raw_os_error(libc::EINVAL))?;
84 let new_fd = self
85 .service
Victor Hsiehf393a722021-12-08 13:04:27 -080086 .createFileInDirectory(self.remote_dir_fd, basename_str, mode as i32)
Victor Hsieh45636232021-10-15 17:52:51 -070087 .map_err(into_io_error)?;
Victor Hsieh45636232021-10-15 17:52:51 -070088
89 let new_remote_file =
90 VerifiedFileEditor::new(RemoteFileEditor::new(self.service.clone(), new_fd));
Victor Hsiehdd99b462021-12-02 17:36:15 -080091 self.entries.insert(basename.to_path_buf(), DirEntry { inode, is_dir: false });
Victor Hsiehf393a722021-12-08 13:04:27 -080092 let new_attr = Attr::new_file_with_mode(self.service.clone(), new_fd, mode);
93 Ok((new_remote_file, new_attr))
Victor Hsieh45636232021-10-15 17:52:51 -070094 }
95
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -080096 /// Creates a remote directory named `basename` with corresponding `inode` at the current
97 /// directory.
Victor Hsiehf393a722021-12-08 13:04:27 -080098 pub fn mkdir(
99 &mut self,
100 basename: &Path,
101 inode: Inode,
102 mode: libc::mode_t,
103 ) -> io::Result<(RemoteDirEditor, Attr)> {
104 let mode = self.validate_arguments(basename, mode)?;
Victor Hsieh45636232021-10-15 17:52:51 -0700105 let basename_str =
106 basename.to_str().ok_or_else(|| io::Error::from_raw_os_error(libc::EINVAL))?;
107 let new_fd = self
108 .service
Victor Hsiehf393a722021-12-08 13:04:27 -0800109 .createDirectoryInDirectory(self.remote_dir_fd, basename_str, mode as i32)
Victor Hsieh45636232021-10-15 17:52:51 -0700110 .map_err(into_io_error)?;
Victor Hsieh45636232021-10-15 17:52:51 -0700111
112 let new_remote_dir = RemoteDirEditor::new(self.service.clone(), new_fd);
Victor Hsiehdd99b462021-12-02 17:36:15 -0800113 self.entries.insert(basename.to_path_buf(), DirEntry { inode, is_dir: true });
Victor Hsiehf393a722021-12-08 13:04:27 -0800114 let new_attr = Attr::new_dir_with_mode(self.service.clone(), new_fd, mode);
115 Ok((new_remote_dir, new_attr))
Victor Hsieh45636232021-10-15 17:52:51 -0700116 }
117
Victor Hsiehdd99b462021-12-02 17:36:15 -0800118 /// Deletes a file
119 pub fn delete_file(&mut self, basename: &Path) -> io::Result<Inode> {
120 let inode = self.force_delete_entry(basename, /* expect_dir */ false)?;
121
122 let basename_str =
123 basename.to_str().ok_or_else(|| io::Error::from_raw_os_error(libc::EINVAL))?;
124 if let Err(e) = self.service.deleteFile(self.remote_dir_fd, basename_str) {
125 // Ignore the error to honor the local state.
126 warn!("Deletion on the host is reportedly failed: {:?}", e);
127 }
128 Ok(inode)
129 }
130
131 /// Forces to delete a directory. The caller must only call if `basename` is a directory and
132 /// empty.
133 pub fn force_delete_directory(&mut self, basename: &Path) -> io::Result<Inode> {
134 let inode = self.force_delete_entry(basename, /* expect_dir */ true)?;
135
136 let basename_str =
137 basename.to_str().ok_or_else(|| io::Error::from_raw_os_error(libc::EINVAL))?;
138 if let Err(e) = self.service.deleteDirectory(self.remote_dir_fd, basename_str) {
139 // Ignore the error to honor the local state.
140 warn!("Deletion on the host is reportedly failed: {:?}", e);
141 }
142 Ok(inode)
143 }
144
Victor Hsieh45636232021-10-15 17:52:51 -0700145 /// Returns the inode number of a file or directory named `name` previously created through
146 /// `RemoteDirEditor`.
Victor Hsiehdd99b462021-12-02 17:36:15 -0800147 pub fn find_inode(&self, name: &Path) -> io::Result<Inode> {
148 self.entries
149 .get(name)
150 .map(|entry| entry.inode)
151 .ok_or_else(|| io::Error::from_raw_os_error(libc::ENOENT))
152 }
153
154 /// Returns whether the directory has an entry of the given name.
155 pub fn has_entry(&self, name: &Path) -> bool {
156 self.entries.contains_key(name)
157 }
158
159 fn force_delete_entry(&mut self, basename: &Path, expect_dir: bool) -> io::Result<Inode> {
160 // Kernel should only give us a basename.
161 debug_assert!(validate_basename(basename).is_ok());
162
163 if let Some(entry) = self.entries.get(basename) {
164 match (expect_dir, entry.is_dir) {
165 (true, false) => Err(io::Error::from_raw_os_error(libc::ENOTDIR)),
166 (false, true) => Err(io::Error::from_raw_os_error(libc::EISDIR)),
167 _ => {
168 let inode = entry.inode;
169 let _ = self.entries.remove(basename);
170 Ok(inode)
171 }
172 }
173 } else {
174 Err(io::Error::from_raw_os_error(libc::ENOENT))
175 }
Victor Hsieh45636232021-10-15 17:52:51 -0700176 }
177
Victor Hsiehf393a722021-12-08 13:04:27 -0800178 fn validate_arguments(&self, basename: &Path, mode: u32) -> io::Result<u32> {
Victor Hsieh45636232021-10-15 17:52:51 -0700179 // Kernel should only give us a basename.
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800180 debug_assert!(validate_basename(basename).is_ok());
181
Victor Hsieh45636232021-10-15 17:52:51 -0700182 if self.entries.contains_key(basename) {
Victor Hsiehf393a722021-12-08 13:04:27 -0800183 return Err(io::Error::from_raw_os_error(libc::EEXIST));
Victor Hsieh45636232021-10-15 17:52:51 -0700184 }
Victor Hsiehf393a722021-12-08 13:04:27 -0800185
186 if self.entries.len() >= MAX_ENTRIES.into() {
187 return Err(io::Error::from_raw_os_error(libc::EMLINK));
188 }
189
190 Ok(Mode::from_bits_truncate(mode).bits())
Victor Hsieh45636232021-10-15 17:52:51 -0700191 }
192}
193
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800194/// An in-memory directory representation of a directory structure.
195pub struct InMemoryDir(HashMap<PathBuf, Inode>);
196
197impl InMemoryDir {
198 /// Creates an empty instance of `InMemoryDir`.
199 pub fn new() -> Self {
200 // Hash map is empty since "." and ".." are excluded in entries.
201 InMemoryDir(HashMap::new())
202 }
203
204 /// Returns the number of entries in the directory (not including "." and "..").
205 pub fn number_of_entries(&self) -> u16 {
206 self.0.len() as u16 // limited to MAX_ENTRIES
207 }
208
209 /// Adds an entry (name and the inode number) to the directory. Fails if already exists. The
210 /// caller is responsible for ensure the inode uniqueness.
211 pub fn add_entry(&mut self, basename: &Path, inode: Inode) -> io::Result<()> {
212 validate_basename(basename)?;
213 if self.0.len() >= MAX_ENTRIES.into() {
214 return Err(io::Error::from_raw_os_error(libc::EMLINK));
215 }
216
217 if let hash_map::Entry::Vacant(entry) = self.0.entry(basename.to_path_buf()) {
218 entry.insert(inode);
219 Ok(())
220 } else {
221 Err(io::Error::from_raw_os_error(libc::EEXIST))
222 }
223 }
224
225 /// Looks up an entry inode by name. `None` if not found.
226 pub fn lookup_inode(&self, basename: &Path) -> Option<Inode> {
227 self.0.get(basename).copied()
228 }
229}
230
Victor Hsieh45636232021-10-15 17:52:51 -0700231fn into_io_error(e: VirtFdServiceStatus) -> io::Error {
232 let maybe_errno = e.service_specific_error();
233 if maybe_errno > 0 {
234 io::Error::from_raw_os_error(maybe_errno)
235 } else {
236 io::Error::new(io::ErrorKind::Other, e.get_description())
237 }
238}