blob: 69a5cb86ea0e131d5909344b7a38701dd3ab9ce0 [file] [log] [blame]
Victor Hsieh88ac6ca2020-11-13 15:20:24 -08001/*
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::Result;
Victor Hsieh45636232021-10-15 17:52:51 -070018use log::{debug, error, warn};
Victor Hsieh60c2f412021-11-03 13:02:19 -070019use std::collections::{btree_map, BTreeMap, HashMap};
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080020use std::convert::TryFrom;
Victor Hsieh45636232021-10-15 17:52:51 -070021use std::ffi::{CStr, OsStr};
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080022use std::fs::OpenOptions;
23use std::io;
24use std::mem::MaybeUninit;
25use std::option::Option;
Victor Hsieh45636232021-10-15 17:52:51 -070026use std::os::unix::{ffi::OsStrExt, io::AsRawFd};
Victor Hsieh60c2f412021-11-03 13:02:19 -070027use std::path::{Path, PathBuf};
28use std::sync::Mutex;
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080029use std::time::Duration;
30
Victor Hsieh6a47e7f2021-03-03 15:53:49 -080031use fuse::filesystem::{
Victor Hsieh71f10032021-08-13 11:24:02 -070032 Context, DirEntry, DirectoryIterator, Entry, FileSystem, FsOptions, GetxattrReply,
33 SetattrValid, ZeroCopyReader, ZeroCopyWriter,
Victor Hsieh6a47e7f2021-03-03 15:53:49 -080034};
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080035use fuse::mount::MountOption;
36
Victor Hsiehac4f3f42021-02-26 12:35:58 -080037use crate::common::{divide_roundup, ChunkedSizeIter, CHUNK_SIZE};
Victor Hsieh6a47e7f2021-03-03 15:53:49 -080038use crate::file::{
Victor Hsieh45636232021-10-15 17:52:51 -070039 RandomWrite, ReadByChunk, RemoteDirEditor, RemoteFileEditor, RemoteFileReader,
40 RemoteMerkleTreeReader,
Victor Hsieh6a47e7f2021-03-03 15:53:49 -080041};
42use crate::fsverity::{VerifiedFileEditor, VerifiedFileReader};
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080043
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080044pub type Inode = u64;
45type Handle = u64;
46
Victor Hsieh26cea2f2021-11-03 10:28:33 -070047const DEFAULT_METADATA_TIMEOUT: Duration = Duration::from_secs(5);
48const ROOT_INODE: Inode = 1;
49
Victor Hsieh766e5332021-11-09 09:41:25 -080050/// Maximum bytes in the write transaction to the FUSE device. This limits the maximum buffer
51/// size in a read request (including FUSE protocol overhead) that the filesystem writes to.
52const MAX_WRITE_BYTES: u32 = 65536;
53
54/// Maximum bytes in a read operation.
55/// TODO(victorhsieh): This option is deprecated by FUSE. Figure out if we can remove this.
56const MAX_READ_BYTES: u32 = 65536;
57
Victor Hsieh26cea2f2021-11-03 10:28:33 -070058/// `AuthFsEntry` defines the filesystem entry type supported by AuthFS.
59pub enum AuthFsEntry {
Victor Hsieh1bcf4112021-03-19 14:26:57 -070060 /// A file type that is verified against fs-verity signature (thus read-only). The file is
Victor Hsieh1bcf4112021-03-19 14:26:57 -070061 /// served from a remote server.
Victor Hsieh88e50172021-10-15 13:27:13 -070062 VerifiedReadonly {
Victor Hsieh1bcf4112021-03-19 14:26:57 -070063 reader: VerifiedFileReader<RemoteFileReader, RemoteMerkleTreeReader>,
64 file_size: u64,
65 },
66 /// A file type that is a read-only passthrough from a file on a remote serrver.
Victor Hsieh88e50172021-10-15 13:27:13 -070067 UnverifiedReadonly { reader: RemoteFileReader, file_size: u64 },
Victor Hsieh1bcf4112021-03-19 14:26:57 -070068 /// A file type that is initially empty, and the content is stored on a remote server. File
69 /// integrity is guaranteed with private Merkle tree.
Victor Hsieh88e50172021-10-15 13:27:13 -070070 VerifiedNew { editor: VerifiedFileEditor<RemoteFileEditor> },
Victor Hsieh45636232021-10-15 17:52:51 -070071 /// A directory type that is initially empty. One can create new file (`VerifiedNew`) and new
72 /// directory (`VerifiedNewDirectory` itself) with integrity guaranteed within the VM.
73 VerifiedNewDirectory { dir: RemoteDirEditor },
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080074}
75
Victor Hsieh60c2f412021-11-03 13:02:19 -070076// AuthFS needs to be `Sync` to be accepted by fuse::worker::start_message_loop as a `FileSystem`.
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080077struct AuthFs {
Victor Hsieh60c2f412021-11-03 13:02:19 -070078 /// Table for `Inode` to `AuthFsEntry` lookup. This needs to be `Sync` to be used in
79 /// `fuse::worker::start_message_loop`.
80 inode_table: Mutex<BTreeMap<Inode, AuthFsEntry>>,
81
82 /// Root directory entry table for path to `Inode` lookup. The root directory content should
83 /// remain constant throughout the filesystem's lifetime.
84 root_entries: HashMap<PathBuf, Inode>,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080085}
86
87impl AuthFs {
Victor Hsieh766e5332021-11-09 09:41:25 -080088 pub fn new(root_entries_by_path: HashMap<PathBuf, AuthFsEntry>) -> AuthFs {
Victor Hsieh60c2f412021-11-03 13:02:19 -070089 let mut next_inode = ROOT_INODE + 1;
90 let mut inode_table = BTreeMap::new();
91 let mut root_entries = HashMap::new();
92
93 root_entries_by_path.into_iter().for_each(|(path_buf, entry)| {
94 next_inode += 1;
95 root_entries.insert(path_buf, next_inode);
96 inode_table.insert(next_inode, entry);
97 });
98
Victor Hsieh766e5332021-11-09 09:41:25 -080099 AuthFs { inode_table: Mutex::new(inode_table), root_entries }
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800100 }
101
Victor Hsieh45636232021-10-15 17:52:51 -0700102 /// Handles the file associated with `inode` if found. This function returns whatever
103 /// `handle_fn` returns.
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700104 fn handle_inode<F, R>(&self, inode: &Inode, handle_fn: F) -> io::Result<R>
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700105 where
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700106 F: FnOnce(&AuthFsEntry) -> io::Result<R>,
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700107 {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700108 let inode_table = self.inode_table.lock().unwrap();
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700109 let config =
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700110 inode_table.get(inode).ok_or_else(|| io::Error::from_raw_os_error(libc::ENOENT))?;
Victor Hsieh45636232021-10-15 17:52:51 -0700111 handle_fn(config)
112 }
113
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700114 /// Inserts a new inode and corresponding `AuthFsEntry` created by `create_fn` to the inode
115 /// table, then returns the new inode number.
Victor Hsieh45636232021-10-15 17:52:51 -0700116 fn insert_new_inode<F>(&self, inode: &Inode, create_fn: F) -> io::Result<Inode>
117 where
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700118 F: FnOnce(&mut AuthFsEntry) -> io::Result<(Inode, AuthFsEntry)>,
Victor Hsieh45636232021-10-15 17:52:51 -0700119 {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700120 let mut inode_table = self.inode_table.lock().unwrap();
Victor Hsieh45636232021-10-15 17:52:51 -0700121 let mut config =
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700122 inode_table.get_mut(inode).ok_or_else(|| io::Error::from_raw_os_error(libc::ENOENT))?;
Victor Hsieh45636232021-10-15 17:52:51 -0700123 let (new_inode, new_file_config) = create_fn(&mut config)?;
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700124 if let btree_map::Entry::Vacant(entry) = inode_table.entry(new_inode) {
Victor Hsieh45636232021-10-15 17:52:51 -0700125 entry.insert(new_file_config);
126 Ok(new_inode)
127 } else {
128 // We can't assume fd_server is trusted, so the returned FD may collide with existing
129 // one, even when we are creating a new file. Do not override an existing FD. In terms
130 // of security, it is better to "leak" the file created earlier, than returning an
131 // existing inode as a new file.
132 error!("Inode {} already exists, do not override", new_inode);
133 Err(io::Error::from_raw_os_error(libc::EIO))
134 }
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800135 }
136}
137
138fn check_access_mode(flags: u32, mode: libc::c_int) -> io::Result<()> {
139 if (flags & libc::O_ACCMODE as u32) == mode as u32 {
140 Ok(())
141 } else {
142 Err(io::Error::from_raw_os_error(libc::EACCES))
143 }
144}
145
146cfg_if::cfg_if! {
147 if #[cfg(all(target_arch = "aarch64", target_pointer_width = "64"))] {
Victor Hsiehda3fbc42021-02-23 16:12:49 -0800148 fn blk_size() -> libc::c_int { CHUNK_SIZE as libc::c_int }
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800149 } else {
Victor Hsiehda3fbc42021-02-23 16:12:49 -0800150 fn blk_size() -> libc::c_long { CHUNK_SIZE as libc::c_long }
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800151 }
152}
153
Victor Hsieh45636232021-10-15 17:52:51 -0700154#[allow(clippy::enum_variant_names)]
155enum AccessMode {
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800156 ReadOnly,
157 ReadWrite,
158}
159
Victor Hsieh45636232021-10-15 17:52:51 -0700160fn create_stat(
161 ino: libc::ino_t,
162 file_size: u64,
163 access_mode: AccessMode,
164) -> io::Result<libc::stat64> {
165 // SAFETY: stat64 is a plan C struct without pointer.
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800166 let mut st = unsafe { MaybeUninit::<libc::stat64>::zeroed().assume_init() };
167
168 st.st_ino = ino;
Victor Hsieh45636232021-10-15 17:52:51 -0700169 st.st_mode = match access_mode {
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800170 // Until needed, let's just grant the owner access.
Victor Hsieh45636232021-10-15 17:52:51 -0700171 // TODO(205169366): Implement mode properly.
172 AccessMode::ReadOnly => libc::S_IFREG | libc::S_IRUSR,
173 AccessMode::ReadWrite => libc::S_IFREG | libc::S_IRUSR | libc::S_IWUSR,
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800174 };
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800175 st.st_nlink = 1;
176 st.st_uid = 0;
177 st.st_gid = 0;
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800178 st.st_size = libc::off64_t::try_from(file_size)
179 .map_err(|_| io::Error::from_raw_os_error(libc::EFBIG))?;
180 st.st_blksize = blk_size();
181 // Per man stat(2), st_blocks is "Number of 512B blocks allocated".
182 st.st_blocks = libc::c_longlong::try_from(divide_roundup(file_size, 512))
183 .map_err(|_| io::Error::from_raw_os_error(libc::EFBIG))?;
184 Ok(st)
185}
186
Victor Hsieh45636232021-10-15 17:52:51 -0700187fn create_dir_stat(ino: libc::ino_t, file_number: u16) -> io::Result<libc::stat64> {
188 // SAFETY: stat64 is a plan C struct without pointer.
189 let mut st = unsafe { MaybeUninit::<libc::stat64>::zeroed().assume_init() };
190
191 st.st_ino = ino;
192 // TODO(205169366): Implement mode properly.
193 st.st_mode = libc::S_IFDIR
194 | libc::S_IXUSR
195 | libc::S_IWUSR
196 | libc::S_IRUSR
197 | libc::S_IXGRP
198 | libc::S_IXOTH;
199
200 // 2 extra for . and ..
201 st.st_nlink = file_number
202 .checked_add(2)
203 .ok_or_else(|| io::Error::from_raw_os_error(libc::EOVERFLOW))?
204 .into();
205
206 st.st_uid = 0;
207 st.st_gid = 0;
208 Ok(st)
209}
210
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800211fn offset_to_chunk_index(offset: u64) -> u64 {
Victor Hsiehda3fbc42021-02-23 16:12:49 -0800212 offset / CHUNK_SIZE
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800213}
214
Victor Hsiehd0bb5d32021-03-19 12:48:03 -0700215fn read_chunks<W: io::Write, T: ReadByChunk>(
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800216 mut w: W,
217 file: &T,
218 file_size: u64,
219 offset: u64,
220 size: u32,
221) -> io::Result<usize> {
222 let remaining = file_size.saturating_sub(offset);
223 let size_to_read = std::cmp::min(size as usize, remaining as usize);
Victor Hsiehac4f3f42021-02-26 12:35:58 -0800224 let total = ChunkedSizeIter::new(size_to_read, offset, CHUNK_SIZE as usize).try_fold(
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800225 0,
226 |total, (current_offset, planned_data_size)| {
227 // TODO(victorhsieh): There might be a non-trivial way to avoid this copy. For example,
228 // instead of accepting a buffer, the writer could expose the final destination buffer
229 // for the reader to write to. It might not be generally applicable though, e.g. with
230 // virtio transport, the buffer may not be continuous.
Victor Hsiehda3fbc42021-02-23 16:12:49 -0800231 let mut buf = [0u8; CHUNK_SIZE as usize];
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800232 let read_size = file.read_chunk(offset_to_chunk_index(current_offset), &mut buf)?;
233 if read_size < planned_data_size {
234 return Err(io::Error::from_raw_os_error(libc::ENODATA));
235 }
236
Victor Hsiehda3fbc42021-02-23 16:12:49 -0800237 let begin = (current_offset % CHUNK_SIZE) as usize;
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800238 let end = begin + planned_data_size;
239 let s = w.write(&buf[begin..end])?;
240 if s != planned_data_size {
241 return Err(io::Error::from_raw_os_error(libc::EIO));
242 }
243 Ok(total + s)
244 },
245 )?;
246
247 Ok(total)
248}
249
250// No need to support enumerating directory entries.
251struct EmptyDirectoryIterator {}
252
253impl DirectoryIterator for EmptyDirectoryIterator {
254 fn next(&mut self) -> Option<DirEntry> {
255 None
256 }
257}
258
259impl FileSystem for AuthFs {
260 type Inode = Inode;
261 type Handle = Handle;
262 type DirIter = EmptyDirectoryIterator;
263
264 fn max_buffer_size(&self) -> u32 {
Victor Hsieh766e5332021-11-09 09:41:25 -0800265 MAX_WRITE_BYTES
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800266 }
267
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800268 fn init(&self, _capable: FsOptions) -> io::Result<FsOptions> {
269 // Enable writeback cache for better performance especially since our bandwidth to the
270 // backend service is limited.
271 Ok(FsOptions::WRITEBACK_CACHE)
272 }
273
Victor Hsieh45636232021-10-15 17:52:51 -0700274 fn lookup(&self, _ctx: Context, parent: Inode, name: &CStr) -> io::Result<Entry> {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700275 if parent == ROOT_INODE {
Victor Hsieh60c2f412021-11-03 13:02:19 -0700276 let inode = *self
277 .root_entries
278 .get(cstr_to_path(name))
279 .ok_or_else(|| io::Error::from_raw_os_error(libc::ENOENT))?;
Victor Hsieh45636232021-10-15 17:52:51 -0700280 // Normally, `lookup` is required to increase a reference count for the inode (while
Victor Hsieh60c2f412021-11-03 13:02:19 -0700281 // `forget` will decrease it). It is not yet necessary until we start to support
282 // deletion (only for `VerifiedNewDirectory`).
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700283 let st = self.handle_inode(&inode, |config| match config {
284 AuthFsEntry::UnverifiedReadonly { file_size, .. }
285 | AuthFsEntry::VerifiedReadonly { file_size, .. } => {
Victor Hsieh45636232021-10-15 17:52:51 -0700286 create_stat(inode, *file_size, AccessMode::ReadOnly)
287 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700288 AuthFsEntry::VerifiedNew { editor } => {
Victor Hsieh45636232021-10-15 17:52:51 -0700289 create_stat(inode, editor.size(), AccessMode::ReadWrite)
290 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700291 AuthFsEntry::VerifiedNewDirectory { dir } => {
Victor Hsieh45636232021-10-15 17:52:51 -0700292 create_dir_stat(inode, dir.number_of_entries())
293 }
294 })?;
295 Ok(Entry {
296 inode,
297 generation: 0,
298 attr: st,
299 entry_timeout: DEFAULT_METADATA_TIMEOUT,
300 attr_timeout: DEFAULT_METADATA_TIMEOUT,
301 })
302 } else {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700303 let inode = self.handle_inode(&parent, |config| match config {
304 AuthFsEntry::VerifiedNewDirectory { dir } => {
Victor Hsieh45636232021-10-15 17:52:51 -0700305 let path: &Path = cstr_to_path(name);
306 dir.find_inode(path).ok_or_else(|| io::Error::from_raw_os_error(libc::ENOENT))
307 }
308 _ => Err(io::Error::from_raw_os_error(libc::ENOTDIR)),
309 })?;
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700310 let st = self.handle_inode(&inode, |config| match config {
311 AuthFsEntry::VerifiedNew { editor } => {
Victor Hsieh45636232021-10-15 17:52:51 -0700312 create_stat(inode, editor.size(), AccessMode::ReadWrite)
313 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700314 AuthFsEntry::VerifiedNewDirectory { dir } => {
Victor Hsieh45636232021-10-15 17:52:51 -0700315 create_dir_stat(inode, dir.number_of_entries())
316 }
317 _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
318 })?;
319 Ok(Entry {
320 inode,
321 generation: 0,
322 attr: st,
323 entry_timeout: DEFAULT_METADATA_TIMEOUT,
324 attr_timeout: DEFAULT_METADATA_TIMEOUT,
325 })
326 }
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800327 }
328
329 fn getattr(
330 &self,
331 _ctx: Context,
332 inode: Inode,
333 _handle: Option<Handle>,
334 ) -> io::Result<(libc::stat64, Duration)> {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700335 self.handle_inode(&inode, |config| {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700336 Ok((
337 match config {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700338 AuthFsEntry::UnverifiedReadonly { file_size, .. }
339 | AuthFsEntry::VerifiedReadonly { file_size, .. } => {
Victor Hsieh45636232021-10-15 17:52:51 -0700340 create_stat(inode, *file_size, AccessMode::ReadOnly)?
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700341 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700342 AuthFsEntry::VerifiedNew { editor } => {
Victor Hsieh45636232021-10-15 17:52:51 -0700343 create_stat(inode, editor.size(), AccessMode::ReadWrite)?
344 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700345 AuthFsEntry::VerifiedNewDirectory { dir } => {
Victor Hsieh45636232021-10-15 17:52:51 -0700346 create_dir_stat(inode, dir.number_of_entries())?
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700347 }
348 },
349 DEFAULT_METADATA_TIMEOUT,
350 ))
351 })
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800352 }
353
354 fn open(
355 &self,
356 _ctx: Context,
357 inode: Self::Inode,
358 flags: u32,
359 ) -> io::Result<(Option<Self::Handle>, fuse::sys::OpenOptions)> {
360 // Since file handle is not really used in later operations (which use Inode directly),
Victor Hsieh09e26262021-03-03 16:00:55 -0800361 // return None as the handle.
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700362 self.handle_inode(&inode, |config| {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700363 match config {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700364 AuthFsEntry::VerifiedReadonly { .. } | AuthFsEntry::UnverifiedReadonly { .. } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700365 check_access_mode(flags, libc::O_RDONLY)?;
366 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700367 AuthFsEntry::VerifiedNew { .. } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700368 // No need to check access modes since all the modes are allowed to the
369 // read-writable file.
370 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700371 AuthFsEntry::VerifiedNewDirectory { .. } => {
Victor Hsieh45636232021-10-15 17:52:51 -0700372 // TODO(victorhsieh): implement when needed.
373 return Err(io::Error::from_raw_os_error(libc::ENOSYS));
374 }
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800375 }
Victor Hsieh45636232021-10-15 17:52:51 -0700376 // Always cache the file content. There is currently no need to support direct I/O or
377 // avoid the cache buffer. Memory mapping is only possible with cache enabled.
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700378 Ok((None, fuse::sys::OpenOptions::KEEP_CACHE))
379 })
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800380 }
381
Victor Hsieh45636232021-10-15 17:52:51 -0700382 fn create(
383 &self,
384 _ctx: Context,
385 parent: Self::Inode,
386 name: &CStr,
387 _mode: u32,
388 _flags: u32,
389 _umask: u32,
390 ) -> io::Result<(Entry, Option<Self::Handle>, fuse::sys::OpenOptions)> {
391 // TODO(205169366): Implement mode properly.
392 // TODO(205172873): handle O_TRUNC and O_EXCL properly.
393 let new_inode = self.insert_new_inode(&parent, |config| match config {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700394 AuthFsEntry::VerifiedNewDirectory { dir } => {
Victor Hsieh45636232021-10-15 17:52:51 -0700395 let basename: &Path = cstr_to_path(name);
396 if dir.find_inode(basename).is_some() {
397 return Err(io::Error::from_raw_os_error(libc::EEXIST));
398 }
399 let (new_inode, new_file) = dir.create_file(basename)?;
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700400 Ok((new_inode, AuthFsEntry::VerifiedNew { editor: new_file }))
Victor Hsieh45636232021-10-15 17:52:51 -0700401 }
402 _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
403 })?;
404
405 Ok((
406 Entry {
407 inode: new_inode,
408 generation: 0,
409 attr: create_stat(new_inode, /* file_size */ 0, AccessMode::ReadWrite)?,
410 entry_timeout: DEFAULT_METADATA_TIMEOUT,
411 attr_timeout: DEFAULT_METADATA_TIMEOUT,
412 },
413 // See also `open`.
414 /* handle */ None,
415 fuse::sys::OpenOptions::KEEP_CACHE,
416 ))
417 }
418
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800419 fn read<W: io::Write + ZeroCopyWriter>(
420 &self,
421 _ctx: Context,
422 inode: Inode,
423 _handle: Handle,
424 w: W,
425 size: u32,
426 offset: u64,
427 _lock_owner: Option<u64>,
428 _flags: u32,
429 ) -> io::Result<usize> {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700430 self.handle_inode(&inode, |config| {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700431 match config {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700432 AuthFsEntry::VerifiedReadonly { reader, file_size } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700433 read_chunks(w, reader, *file_size, offset, size)
434 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700435 AuthFsEntry::UnverifiedReadonly { reader, file_size } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700436 read_chunks(w, reader, *file_size, offset, size)
437 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700438 AuthFsEntry::VerifiedNew { editor } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700439 // Note that with FsOptions::WRITEBACK_CACHE, it's possible for the kernel to
440 // request a read even if the file is open with O_WRONLY.
441 read_chunks(w, editor, editor.size(), offset, size)
442 }
Victor Hsieh45636232021-10-15 17:52:51 -0700443 _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800444 }
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700445 })
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800446 }
447
448 fn write<R: io::Read + ZeroCopyReader>(
449 &self,
450 _ctx: Context,
451 inode: Self::Inode,
452 _handle: Self::Handle,
453 mut r: R,
454 size: u32,
455 offset: u64,
456 _lock_owner: Option<u64>,
457 _delayed_write: bool,
458 _flags: u32,
459 ) -> io::Result<usize> {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700460 self.handle_inode(&inode, |config| match config {
461 AuthFsEntry::VerifiedNew { editor } => {
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800462 let mut buf = vec![0; size as usize];
463 r.read_exact(&mut buf)?;
Victor Hsieh1bcf4112021-03-19 14:26:57 -0700464 editor.write_at(&buf, offset)
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800465 }
466 _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700467 })
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800468 }
Victor Hsieh9d0ab622021-04-26 17:07:02 -0700469
470 fn setattr(
471 &self,
472 _ctx: Context,
473 inode: Inode,
474 attr: libc::stat64,
475 _handle: Option<Handle>,
476 valid: SetattrValid,
477 ) -> io::Result<(libc::stat64, Duration)> {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700478 self.handle_inode(&inode, |config| {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700479 match config {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700480 AuthFsEntry::VerifiedNew { editor } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700481 // Initialize the default stat.
Victor Hsieh45636232021-10-15 17:52:51 -0700482 let mut new_attr = create_stat(inode, editor.size(), AccessMode::ReadWrite)?;
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700483 // `valid` indicates what fields in `attr` are valid. Update to return correctly.
484 if valid.contains(SetattrValid::SIZE) {
485 // st_size is i64, but the cast should be safe since kernel should not give a
486 // negative size.
487 debug_assert!(attr.st_size >= 0);
488 new_attr.st_size = attr.st_size;
489 editor.resize(attr.st_size as u64)?;
490 }
Victor Hsieh9d0ab622021-04-26 17:07:02 -0700491
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700492 if valid.contains(SetattrValid::MODE) {
493 warn!("Changing st_mode is not currently supported");
494 return Err(io::Error::from_raw_os_error(libc::ENOSYS));
495 }
496 if valid.contains(SetattrValid::UID) {
497 warn!("Changing st_uid is not currently supported");
498 return Err(io::Error::from_raw_os_error(libc::ENOSYS));
499 }
500 if valid.contains(SetattrValid::GID) {
501 warn!("Changing st_gid is not currently supported");
502 return Err(io::Error::from_raw_os_error(libc::ENOSYS));
503 }
504 if valid.contains(SetattrValid::CTIME) {
505 debug!(
506 "Ignoring ctime change as authfs does not maintain timestamp currently"
507 );
508 }
509 if valid.intersects(SetattrValid::ATIME | SetattrValid::ATIME_NOW) {
510 debug!(
511 "Ignoring atime change as authfs does not maintain timestamp currently"
512 );
513 }
514 if valid.intersects(SetattrValid::MTIME | SetattrValid::MTIME_NOW) {
515 debug!(
516 "Ignoring mtime change as authfs does not maintain timestamp currently"
517 );
518 }
519 Ok((new_attr, DEFAULT_METADATA_TIMEOUT))
Victor Hsieh9d0ab622021-04-26 17:07:02 -0700520 }
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700521 _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
Victor Hsieh9d0ab622021-04-26 17:07:02 -0700522 }
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700523 })
Victor Hsieh9d0ab622021-04-26 17:07:02 -0700524 }
Victor Hsieh71f10032021-08-13 11:24:02 -0700525
526 fn getxattr(
527 &self,
528 _ctx: Context,
529 inode: Self::Inode,
530 name: &CStr,
531 size: u32,
532 ) -> io::Result<GetxattrReply> {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700533 self.handle_inode(&inode, |config| {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700534 match config {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700535 AuthFsEntry::VerifiedNew { editor } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700536 // FUSE ioctl is limited, thus we can't implement fs-verity ioctls without a kernel
537 // change (see b/196635431). Until it's possible, use xattr to expose what we need
538 // as an authfs specific API.
539 if name != CStr::from_bytes_with_nul(b"authfs.fsverity.digest\0").unwrap() {
540 return Err(io::Error::from_raw_os_error(libc::ENODATA));
541 }
Victor Hsieh71f10032021-08-13 11:24:02 -0700542
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700543 if size == 0 {
544 // Per protocol, when size is 0, return the value size.
545 Ok(GetxattrReply::Count(editor.get_fsverity_digest_size() as u32))
Victor Hsieh71f10032021-08-13 11:24:02 -0700546 } else {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700547 let digest = editor.calculate_fsverity_digest()?;
548 if digest.len() > size as usize {
549 Err(io::Error::from_raw_os_error(libc::ERANGE))
550 } else {
551 Ok(GetxattrReply::Value(digest.to_vec()))
552 }
Victor Hsieh71f10032021-08-13 11:24:02 -0700553 }
554 }
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700555 _ => Err(io::Error::from_raw_os_error(libc::ENODATA)),
Victor Hsieh71f10032021-08-13 11:24:02 -0700556 }
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700557 })
Victor Hsieh71f10032021-08-13 11:24:02 -0700558 }
Victor Hsieh45636232021-10-15 17:52:51 -0700559
560 fn mkdir(
561 &self,
562 _ctx: Context,
563 parent: Self::Inode,
564 name: &CStr,
565 _mode: u32,
566 _umask: u32,
567 ) -> io::Result<Entry> {
568 // TODO(205169366): Implement mode properly.
569 let new_inode = self.insert_new_inode(&parent, |config| match config {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700570 AuthFsEntry::VerifiedNewDirectory { dir } => {
Victor Hsieh45636232021-10-15 17:52:51 -0700571 let basename: &Path = cstr_to_path(name);
572 if dir.find_inode(basename).is_some() {
573 return Err(io::Error::from_raw_os_error(libc::EEXIST));
574 }
575 let (new_inode, new_dir) = dir.mkdir(basename)?;
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700576 Ok((new_inode, AuthFsEntry::VerifiedNewDirectory { dir: new_dir }))
Victor Hsieh45636232021-10-15 17:52:51 -0700577 }
578 _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
579 })?;
580
581 Ok(Entry {
582 inode: new_inode,
583 generation: 0,
584 attr: create_dir_stat(new_inode, /* file_number */ 0)?,
585 entry_timeout: DEFAULT_METADATA_TIMEOUT,
586 attr_timeout: DEFAULT_METADATA_TIMEOUT,
587 })
588 }
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800589}
590
591/// Mount and start the FUSE instance. This requires CAP_SYS_ADMIN.
592pub fn loop_forever(
Victor Hsieh60c2f412021-11-03 13:02:19 -0700593 root_entries: HashMap<PathBuf, AuthFsEntry>,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800594 mountpoint: &Path,
Victor Hsieh4cc3b792021-08-04 12:00:04 -0700595 extra_options: &Option<String>,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800596) -> Result<(), fuse::Error> {
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800597 let dev_fuse = OpenOptions::new()
598 .read(true)
599 .write(true)
600 .open("/dev/fuse")
601 .expect("Failed to open /dev/fuse");
602
Victor Hsieh4cc3b792021-08-04 12:00:04 -0700603 let mut mount_options = vec![
604 MountOption::FD(dev_fuse.as_raw_fd()),
605 MountOption::RootMode(libc::S_IFDIR | libc::S_IXUSR | libc::S_IXGRP | libc::S_IXOTH),
606 MountOption::AllowOther,
607 MountOption::UserId(0),
608 MountOption::GroupId(0),
Victor Hsieh766e5332021-11-09 09:41:25 -0800609 MountOption::MaxRead(MAX_READ_BYTES),
Victor Hsieh4cc3b792021-08-04 12:00:04 -0700610 ];
611 if let Some(value) = extra_options {
612 mount_options.push(MountOption::Extra(value));
613 }
614
615 fuse::mount(mountpoint, "authfs", libc::MS_NOSUID | libc::MS_NODEV, &mount_options)
616 .expect("Failed to mount fuse");
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800617
618 fuse::worker::start_message_loop(
619 dev_fuse,
Victor Hsieh766e5332021-11-09 09:41:25 -0800620 MAX_WRITE_BYTES,
621 MAX_READ_BYTES,
622 AuthFs::new(root_entries),
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800623 )
624}
Victor Hsieh45636232021-10-15 17:52:51 -0700625
626fn cstr_to_path(cstr: &CStr) -> &Path {
627 OsStr::from_bytes(cstr.to_bytes()).as_ref()
628}