blob: ca7317414b4d86a48cd0cc7282d18c74aaf68e37 [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
Victor Hsieh4d6b9d42021-11-08 15:53:49 -080017use anyhow::{bail, Result};
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -080018use log::{debug, warn};
Victor Hsieh4d6b9d42021-11-08 15:53:49 -080019use std::collections::{btree_map, BTreeMap};
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};
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -080028use std::sync::atomic::{AtomicU64, Ordering};
Victor Hsieh60c2f412021-11-03 13:02:19 -070029use std::sync::Mutex;
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080030use std::time::Duration;
31
Victor Hsieh6a47e7f2021-03-03 15:53:49 -080032use fuse::filesystem::{
Victor Hsieh71f10032021-08-13 11:24:02 -070033 Context, DirEntry, DirectoryIterator, Entry, FileSystem, FsOptions, GetxattrReply,
34 SetattrValid, ZeroCopyReader, ZeroCopyWriter,
Victor Hsieh6a47e7f2021-03-03 15:53:49 -080035};
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080036use fuse::mount::MountOption;
37
Victor Hsiehac4f3f42021-02-26 12:35:58 -080038use crate::common::{divide_roundup, ChunkedSizeIter, CHUNK_SIZE};
Victor Hsieh6a47e7f2021-03-03 15:53:49 -080039use crate::file::{
Victor Hsieh4d6b9d42021-11-08 15:53:49 -080040 InMemoryDir, RandomWrite, ReadByChunk, RemoteDirEditor, RemoteFileEditor, RemoteFileReader,
Victor Hsieh45636232021-10-15 17:52:51 -070041 RemoteMerkleTreeReader,
Victor Hsieh6a47e7f2021-03-03 15:53:49 -080042};
43use crate::fsverity::{VerifiedFileEditor, VerifiedFileReader};
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080044
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080045pub type Inode = u64;
46type Handle = u64;
47
Victor Hsieh26cea2f2021-11-03 10:28:33 -070048const DEFAULT_METADATA_TIMEOUT: Duration = Duration::from_secs(5);
49const ROOT_INODE: Inode = 1;
50
Victor Hsieh766e5332021-11-09 09:41:25 -080051/// Maximum bytes in the write transaction to the FUSE device. This limits the maximum buffer
52/// size in a read request (including FUSE protocol overhead) that the filesystem writes to.
53const MAX_WRITE_BYTES: u32 = 65536;
54
55/// Maximum bytes in a read operation.
56/// TODO(victorhsieh): This option is deprecated by FUSE. Figure out if we can remove this.
57const MAX_READ_BYTES: u32 = 65536;
58
Victor Hsieh26cea2f2021-11-03 10:28:33 -070059/// `AuthFsEntry` defines the filesystem entry type supported by AuthFS.
60pub enum AuthFsEntry {
Victor Hsieh4d6b9d42021-11-08 15:53:49 -080061 /// A read-only directory (writable during initialization). Root directory is an example.
62 ReadonlyDirectory { dir: InMemoryDir },
Victor Hsieh1bcf4112021-03-19 14:26:57 -070063 /// A file type that is verified against fs-verity signature (thus read-only). The file is
Victor Hsieh1bcf4112021-03-19 14:26:57 -070064 /// served from a remote server.
Victor Hsieh88e50172021-10-15 13:27:13 -070065 VerifiedReadonly {
Victor Hsieh1bcf4112021-03-19 14:26:57 -070066 reader: VerifiedFileReader<RemoteFileReader, RemoteMerkleTreeReader>,
67 file_size: u64,
68 },
69 /// A file type that is a read-only passthrough from a file on a remote serrver.
Victor Hsieh88e50172021-10-15 13:27:13 -070070 UnverifiedReadonly { reader: RemoteFileReader, file_size: u64 },
Victor Hsieh1bcf4112021-03-19 14:26:57 -070071 /// A file type that is initially empty, and the content is stored on a remote server. File
72 /// integrity is guaranteed with private Merkle tree.
Victor Hsieh88e50172021-10-15 13:27:13 -070073 VerifiedNew { editor: VerifiedFileEditor<RemoteFileEditor> },
Victor Hsieh45636232021-10-15 17:52:51 -070074 /// A directory type that is initially empty. One can create new file (`VerifiedNew`) and new
75 /// directory (`VerifiedNewDirectory` itself) with integrity guaranteed within the VM.
76 VerifiedNewDirectory { dir: RemoteDirEditor },
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080077}
78
Victor Hsieh60c2f412021-11-03 13:02:19 -070079// AuthFS needs to be `Sync` to be accepted by fuse::worker::start_message_loop as a `FileSystem`.
Victor Hsieh4d6b9d42021-11-08 15:53:49 -080080pub struct AuthFs {
Victor Hsieh60c2f412021-11-03 13:02:19 -070081 /// Table for `Inode` to `AuthFsEntry` lookup. This needs to be `Sync` to be used in
82 /// `fuse::worker::start_message_loop`.
83 inode_table: Mutex<BTreeMap<Inode, AuthFsEntry>>,
84
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -080085 /// The next available inode number.
86 next_inode: AtomicU64,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080087}
88
Victor Hsieh4d6b9d42021-11-08 15:53:49 -080089// Implementation for preparing an `AuthFs` instance, before starting to serve.
90// TODO(victorhsieh): Consider implement a builder to separate the mutable initialization from the
91// immutable / interiorly mutable serving phase.
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080092impl AuthFs {
Victor Hsieh4d6b9d42021-11-08 15:53:49 -080093 pub fn new() -> AuthFs {
Victor Hsieh60c2f412021-11-03 13:02:19 -070094 let mut inode_table = BTreeMap::new();
Victor Hsieh4d6b9d42021-11-08 15:53:49 -080095 inode_table.insert(ROOT_INODE, AuthFsEntry::ReadonlyDirectory { dir: InMemoryDir::new() });
Victor Hsieh60c2f412021-11-03 13:02:19 -070096
Victor Hsieh4d6b9d42021-11-08 15:53:49 -080097 AuthFs { inode_table: Mutex::new(inode_table), next_inode: AtomicU64::new(ROOT_INODE + 1) }
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080098 }
99
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800100 pub fn add_entry_at_root_dir(
101 &mut self,
102 basename: PathBuf,
103 entry: AuthFsEntry,
104 ) -> Result<Inode> {
105 if basename.is_absolute() {
106 bail!("Invalid entry name: {:?}", basename);
107 }
108
109 let inode_table = &mut *self.inode_table.get_mut().unwrap();
110 match inode_table
111 .get_mut(&ROOT_INODE)
112 .ok_or_else(|| io::Error::from_raw_os_error(libc::ENOENT))?
113 {
114 AuthFsEntry::ReadonlyDirectory { dir } => {
115 let new_inode = self.next_inode.fetch_add(1, Ordering::Relaxed);
116
117 dir.add_entry(&basename, new_inode)?;
118 if inode_table.insert(new_inode, entry).is_some() {
119 bail!(
120 "Found duplicated inode {} when adding {}",
121 new_inode,
122 basename.display()
123 );
124 }
125 Ok(new_inode)
126 }
127 _ => bail!("Not a ReadonlyDirectory"),
128 }
129 }
130}
131
132// Implementation for serving requests.
133impl AuthFs {
Victor Hsieh45636232021-10-15 17:52:51 -0700134 /// Handles the file associated with `inode` if found. This function returns whatever
135 /// `handle_fn` returns.
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700136 fn handle_inode<F, R>(&self, inode: &Inode, handle_fn: F) -> io::Result<R>
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700137 where
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700138 F: FnOnce(&AuthFsEntry) -> io::Result<R>,
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700139 {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700140 let inode_table = self.inode_table.lock().unwrap();
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -0800141 let entry =
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700142 inode_table.get(inode).ok_or_else(|| io::Error::from_raw_os_error(libc::ENOENT))?;
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -0800143 handle_fn(entry)
Victor Hsieh45636232021-10-15 17:52:51 -0700144 }
145
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -0800146 /// Adds a new entry `name` created by `create_fn` at `parent_inode`.
147 ///
148 /// The operation involves two updates: adding the name with a new allocated inode to the
149 /// parent directory, and insert the new inode and the actual `AuthFsEntry` to the global inode
150 /// table.
151 ///
152 /// `create_fn` receives the parent directory, through which it can create the new entry at and
153 /// register the new inode to. Its returned entry is then added to the inode table.
154 fn create_new_entry<F>(
155 &self,
156 parent_inode: Inode,
157 name: &CStr,
158 create_fn: F,
159 ) -> io::Result<Inode>
Victor Hsieh45636232021-10-15 17:52:51 -0700160 where
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -0800161 F: FnOnce(&mut AuthFsEntry, &Path, Inode) -> io::Result<AuthFsEntry>,
Victor Hsieh45636232021-10-15 17:52:51 -0700162 {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700163 let mut inode_table = self.inode_table.lock().unwrap();
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -0800164 let mut parent_entry = inode_table
165 .get_mut(&parent_inode)
166 .ok_or_else(|| io::Error::from_raw_os_error(libc::ENOENT))?;
167
168 let new_inode = self.next_inode.fetch_add(1, Ordering::Relaxed);
169 let basename: &Path = cstr_to_path(name);
170 let new_file_entry = create_fn(&mut parent_entry, basename, new_inode)?;
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700171 if let btree_map::Entry::Vacant(entry) = inode_table.entry(new_inode) {
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -0800172 entry.insert(new_file_entry);
Victor Hsieh45636232021-10-15 17:52:51 -0700173 Ok(new_inode)
174 } else {
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -0800175 unreachable!("Unexpected duplication of inode {}", new_inode);
Victor Hsieh45636232021-10-15 17:52:51 -0700176 }
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800177 }
178}
179
180fn check_access_mode(flags: u32, mode: libc::c_int) -> io::Result<()> {
181 if (flags & libc::O_ACCMODE as u32) == mode as u32 {
182 Ok(())
183 } else {
184 Err(io::Error::from_raw_os_error(libc::EACCES))
185 }
186}
187
188cfg_if::cfg_if! {
189 if #[cfg(all(target_arch = "aarch64", target_pointer_width = "64"))] {
Victor Hsiehda3fbc42021-02-23 16:12:49 -0800190 fn blk_size() -> libc::c_int { CHUNK_SIZE as libc::c_int }
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800191 } else {
Victor Hsiehda3fbc42021-02-23 16:12:49 -0800192 fn blk_size() -> libc::c_long { CHUNK_SIZE as libc::c_long }
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800193 }
194}
195
Victor Hsieh45636232021-10-15 17:52:51 -0700196#[allow(clippy::enum_variant_names)]
197enum AccessMode {
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800198 ReadOnly,
199 ReadWrite,
200}
201
Victor Hsieh45636232021-10-15 17:52:51 -0700202fn create_stat(
203 ino: libc::ino_t,
204 file_size: u64,
205 access_mode: AccessMode,
206) -> io::Result<libc::stat64> {
207 // SAFETY: stat64 is a plan C struct without pointer.
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800208 let mut st = unsafe { MaybeUninit::<libc::stat64>::zeroed().assume_init() };
209
210 st.st_ino = ino;
Victor Hsieh45636232021-10-15 17:52:51 -0700211 st.st_mode = match access_mode {
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800212 // Until needed, let's just grant the owner access.
Victor Hsieh45636232021-10-15 17:52:51 -0700213 // TODO(205169366): Implement mode properly.
214 AccessMode::ReadOnly => libc::S_IFREG | libc::S_IRUSR,
215 AccessMode::ReadWrite => libc::S_IFREG | libc::S_IRUSR | libc::S_IWUSR,
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800216 };
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800217 st.st_nlink = 1;
218 st.st_uid = 0;
219 st.st_gid = 0;
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800220 st.st_size = libc::off64_t::try_from(file_size)
221 .map_err(|_| io::Error::from_raw_os_error(libc::EFBIG))?;
222 st.st_blksize = blk_size();
223 // Per man stat(2), st_blocks is "Number of 512B blocks allocated".
224 st.st_blocks = libc::c_longlong::try_from(divide_roundup(file_size, 512))
225 .map_err(|_| io::Error::from_raw_os_error(libc::EFBIG))?;
226 Ok(st)
227}
228
Victor Hsieh45636232021-10-15 17:52:51 -0700229fn create_dir_stat(ino: libc::ino_t, file_number: u16) -> io::Result<libc::stat64> {
230 // SAFETY: stat64 is a plan C struct without pointer.
231 let mut st = unsafe { MaybeUninit::<libc::stat64>::zeroed().assume_init() };
232
233 st.st_ino = ino;
234 // TODO(205169366): Implement mode properly.
235 st.st_mode = libc::S_IFDIR
236 | libc::S_IXUSR
237 | libc::S_IWUSR
238 | libc::S_IRUSR
239 | libc::S_IXGRP
240 | libc::S_IXOTH;
241
242 // 2 extra for . and ..
243 st.st_nlink = file_number
244 .checked_add(2)
245 .ok_or_else(|| io::Error::from_raw_os_error(libc::EOVERFLOW))?
246 .into();
247
248 st.st_uid = 0;
249 st.st_gid = 0;
250 Ok(st)
251}
252
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800253fn offset_to_chunk_index(offset: u64) -> u64 {
Victor Hsiehda3fbc42021-02-23 16:12:49 -0800254 offset / CHUNK_SIZE
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800255}
256
Victor Hsiehd0bb5d32021-03-19 12:48:03 -0700257fn read_chunks<W: io::Write, T: ReadByChunk>(
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800258 mut w: W,
259 file: &T,
260 file_size: u64,
261 offset: u64,
262 size: u32,
263) -> io::Result<usize> {
264 let remaining = file_size.saturating_sub(offset);
265 let size_to_read = std::cmp::min(size as usize, remaining as usize);
Victor Hsiehac4f3f42021-02-26 12:35:58 -0800266 let total = ChunkedSizeIter::new(size_to_read, offset, CHUNK_SIZE as usize).try_fold(
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800267 0,
268 |total, (current_offset, planned_data_size)| {
269 // TODO(victorhsieh): There might be a non-trivial way to avoid this copy. For example,
270 // instead of accepting a buffer, the writer could expose the final destination buffer
271 // for the reader to write to. It might not be generally applicable though, e.g. with
272 // virtio transport, the buffer may not be continuous.
Victor Hsiehda3fbc42021-02-23 16:12:49 -0800273 let mut buf = [0u8; CHUNK_SIZE as usize];
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800274 let read_size = file.read_chunk(offset_to_chunk_index(current_offset), &mut buf)?;
275 if read_size < planned_data_size {
276 return Err(io::Error::from_raw_os_error(libc::ENODATA));
277 }
278
Victor Hsiehda3fbc42021-02-23 16:12:49 -0800279 let begin = (current_offset % CHUNK_SIZE) as usize;
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800280 let end = begin + planned_data_size;
281 let s = w.write(&buf[begin..end])?;
282 if s != planned_data_size {
283 return Err(io::Error::from_raw_os_error(libc::EIO));
284 }
285 Ok(total + s)
286 },
287 )?;
288
289 Ok(total)
290}
291
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800292// TODO(205715172): Support enumerating directory entries.
293pub struct EmptyDirectoryIterator {}
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800294
295impl DirectoryIterator for EmptyDirectoryIterator {
296 fn next(&mut self) -> Option<DirEntry> {
297 None
298 }
299}
300
301impl FileSystem for AuthFs {
302 type Inode = Inode;
303 type Handle = Handle;
304 type DirIter = EmptyDirectoryIterator;
305
306 fn max_buffer_size(&self) -> u32 {
Victor Hsieh766e5332021-11-09 09:41:25 -0800307 MAX_WRITE_BYTES
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800308 }
309
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800310 fn init(&self, _capable: FsOptions) -> io::Result<FsOptions> {
311 // Enable writeback cache for better performance especially since our bandwidth to the
312 // backend service is limited.
313 Ok(FsOptions::WRITEBACK_CACHE)
314 }
315
Victor Hsieh45636232021-10-15 17:52:51 -0700316 fn lookup(&self, _ctx: Context, parent: Inode, name: &CStr) -> io::Result<Entry> {
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800317 // Look up the entry's inode number in parent directory.
318 let inode = self.handle_inode(&parent, |parent_entry| match parent_entry {
319 AuthFsEntry::ReadonlyDirectory { dir } => {
320 let path = cstr_to_path(name);
321 dir.lookup_inode(path).ok_or_else(|| io::Error::from_raw_os_error(libc::ENOENT))
322 }
323 AuthFsEntry::VerifiedNewDirectory { dir } => {
324 let path = cstr_to_path(name);
325 dir.find_inode(path).ok_or_else(|| io::Error::from_raw_os_error(libc::ENOENT))
326 }
327 _ => Err(io::Error::from_raw_os_error(libc::ENOTDIR)),
328 })?;
329
330 // Normally, `lookup` is required to increase a reference count for the inode (while
331 // `forget` will decrease it). It is not yet necessary until we start to support
332 // deletion (only for `VerifiedNewDirectory`).
333
334 // Create the entry's stat if found.
335 let st = self.handle_inode(&inode, |entry| match entry {
336 AuthFsEntry::ReadonlyDirectory { .. } => {
337 unreachable!("FUSE shouldn't need to look up the root inode");
338 //create_dir_stat(inode, dir.number_of_entries())
339 }
340 AuthFsEntry::UnverifiedReadonly { file_size, .. }
341 | AuthFsEntry::VerifiedReadonly { file_size, .. } => {
342 create_stat(inode, *file_size, AccessMode::ReadOnly)
343 }
344 AuthFsEntry::VerifiedNew { editor } => {
345 create_stat(inode, editor.size(), AccessMode::ReadWrite)
346 }
347 AuthFsEntry::VerifiedNewDirectory { dir } => {
348 create_dir_stat(inode, dir.number_of_entries())
349 }
350 })?;
351 Ok(Entry {
352 inode,
353 generation: 0,
354 attr: st,
355 entry_timeout: DEFAULT_METADATA_TIMEOUT,
356 attr_timeout: DEFAULT_METADATA_TIMEOUT,
357 })
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800358 }
359
360 fn getattr(
361 &self,
362 _ctx: Context,
363 inode: Inode,
364 _handle: Option<Handle>,
365 ) -> io::Result<(libc::stat64, Duration)> {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700366 self.handle_inode(&inode, |config| {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700367 Ok((
368 match config {
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800369 AuthFsEntry::ReadonlyDirectory { dir } => {
370 create_dir_stat(inode, dir.number_of_entries())
371 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700372 AuthFsEntry::UnverifiedReadonly { file_size, .. }
373 | AuthFsEntry::VerifiedReadonly { file_size, .. } => {
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800374 create_stat(inode, *file_size, AccessMode::ReadOnly)
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700375 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700376 AuthFsEntry::VerifiedNew { editor } => {
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800377 create_stat(inode, editor.size(), AccessMode::ReadWrite)
Victor Hsieh45636232021-10-15 17:52:51 -0700378 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700379 AuthFsEntry::VerifiedNewDirectory { dir } => {
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800380 create_dir_stat(inode, dir.number_of_entries())
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700381 }
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800382 }?,
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700383 DEFAULT_METADATA_TIMEOUT,
384 ))
385 })
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800386 }
387
388 fn open(
389 &self,
390 _ctx: Context,
391 inode: Self::Inode,
392 flags: u32,
393 ) -> io::Result<(Option<Self::Handle>, fuse::sys::OpenOptions)> {
394 // Since file handle is not really used in later operations (which use Inode directly),
Victor Hsieh09e26262021-03-03 16:00:55 -0800395 // return None as the handle.
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700396 self.handle_inode(&inode, |config| {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700397 match config {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700398 AuthFsEntry::VerifiedReadonly { .. } | AuthFsEntry::UnverifiedReadonly { .. } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700399 check_access_mode(flags, libc::O_RDONLY)?;
400 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700401 AuthFsEntry::VerifiedNew { .. } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700402 // No need to check access modes since all the modes are allowed to the
403 // read-writable file.
404 }
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800405 AuthFsEntry::ReadonlyDirectory { .. }
406 | AuthFsEntry::VerifiedNewDirectory { .. } => {
Victor Hsieh45636232021-10-15 17:52:51 -0700407 // TODO(victorhsieh): implement when needed.
408 return Err(io::Error::from_raw_os_error(libc::ENOSYS));
409 }
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800410 }
Victor Hsieh45636232021-10-15 17:52:51 -0700411 // Always cache the file content. There is currently no need to support direct I/O or
412 // avoid the cache buffer. Memory mapping is only possible with cache enabled.
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700413 Ok((None, fuse::sys::OpenOptions::KEEP_CACHE))
414 })
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800415 }
416
Victor Hsieh45636232021-10-15 17:52:51 -0700417 fn create(
418 &self,
419 _ctx: Context,
420 parent: Self::Inode,
421 name: &CStr,
422 _mode: u32,
423 _flags: u32,
424 _umask: u32,
425 ) -> io::Result<(Entry, Option<Self::Handle>, fuse::sys::OpenOptions)> {
426 // TODO(205169366): Implement mode properly.
427 // TODO(205172873): handle O_TRUNC and O_EXCL properly.
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -0800428 let new_inode =
429 self.create_new_entry(parent, name, |parent_entry, basename, new_inode| {
430 match parent_entry {
431 AuthFsEntry::VerifiedNewDirectory { dir } => {
432 if dir.find_inode(basename).is_some() {
433 return Err(io::Error::from_raw_os_error(libc::EEXIST));
434 }
435 let new_file = dir.create_file(basename, new_inode)?;
436 Ok(AuthFsEntry::VerifiedNew { editor: new_file })
437 }
438 _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
Victor Hsieh45636232021-10-15 17:52:51 -0700439 }
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -0800440 })?;
Victor Hsieh45636232021-10-15 17:52:51 -0700441
442 Ok((
443 Entry {
444 inode: new_inode,
445 generation: 0,
446 attr: create_stat(new_inode, /* file_size */ 0, AccessMode::ReadWrite)?,
447 entry_timeout: DEFAULT_METADATA_TIMEOUT,
448 attr_timeout: DEFAULT_METADATA_TIMEOUT,
449 },
450 // See also `open`.
451 /* handle */ None,
452 fuse::sys::OpenOptions::KEEP_CACHE,
453 ))
454 }
455
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800456 fn read<W: io::Write + ZeroCopyWriter>(
457 &self,
458 _ctx: Context,
459 inode: Inode,
460 _handle: Handle,
461 w: W,
462 size: u32,
463 offset: u64,
464 _lock_owner: Option<u64>,
465 _flags: u32,
466 ) -> io::Result<usize> {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700467 self.handle_inode(&inode, |config| {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700468 match config {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700469 AuthFsEntry::VerifiedReadonly { reader, file_size } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700470 read_chunks(w, reader, *file_size, offset, size)
471 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700472 AuthFsEntry::UnverifiedReadonly { reader, file_size } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700473 read_chunks(w, reader, *file_size, offset, size)
474 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700475 AuthFsEntry::VerifiedNew { editor } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700476 // Note that with FsOptions::WRITEBACK_CACHE, it's possible for the kernel to
477 // request a read even if the file is open with O_WRONLY.
478 read_chunks(w, editor, editor.size(), offset, size)
479 }
Victor Hsieh45636232021-10-15 17:52:51 -0700480 _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800481 }
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700482 })
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800483 }
484
485 fn write<R: io::Read + ZeroCopyReader>(
486 &self,
487 _ctx: Context,
488 inode: Self::Inode,
489 _handle: Self::Handle,
490 mut r: R,
491 size: u32,
492 offset: u64,
493 _lock_owner: Option<u64>,
494 _delayed_write: bool,
495 _flags: u32,
496 ) -> io::Result<usize> {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700497 self.handle_inode(&inode, |config| match config {
498 AuthFsEntry::VerifiedNew { editor } => {
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800499 let mut buf = vec![0; size as usize];
500 r.read_exact(&mut buf)?;
Victor Hsieh1bcf4112021-03-19 14:26:57 -0700501 editor.write_at(&buf, offset)
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800502 }
503 _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700504 })
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800505 }
Victor Hsieh9d0ab622021-04-26 17:07:02 -0700506
507 fn setattr(
508 &self,
509 _ctx: Context,
510 inode: Inode,
511 attr: libc::stat64,
512 _handle: Option<Handle>,
513 valid: SetattrValid,
514 ) -> io::Result<(libc::stat64, Duration)> {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700515 self.handle_inode(&inode, |config| {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700516 match config {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700517 AuthFsEntry::VerifiedNew { editor } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700518 // Initialize the default stat.
Victor Hsieh45636232021-10-15 17:52:51 -0700519 let mut new_attr = create_stat(inode, editor.size(), AccessMode::ReadWrite)?;
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700520 // `valid` indicates what fields in `attr` are valid. Update to return correctly.
521 if valid.contains(SetattrValid::SIZE) {
522 // st_size is i64, but the cast should be safe since kernel should not give a
523 // negative size.
524 debug_assert!(attr.st_size >= 0);
525 new_attr.st_size = attr.st_size;
526 editor.resize(attr.st_size as u64)?;
527 }
Victor Hsieh9d0ab622021-04-26 17:07:02 -0700528
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700529 if valid.contains(SetattrValid::MODE) {
530 warn!("Changing st_mode is not currently supported");
531 return Err(io::Error::from_raw_os_error(libc::ENOSYS));
532 }
533 if valid.contains(SetattrValid::UID) {
534 warn!("Changing st_uid is not currently supported");
535 return Err(io::Error::from_raw_os_error(libc::ENOSYS));
536 }
537 if valid.contains(SetattrValid::GID) {
538 warn!("Changing st_gid is not currently supported");
539 return Err(io::Error::from_raw_os_error(libc::ENOSYS));
540 }
541 if valid.contains(SetattrValid::CTIME) {
542 debug!(
543 "Ignoring ctime change as authfs does not maintain timestamp currently"
544 );
545 }
546 if valid.intersects(SetattrValid::ATIME | SetattrValid::ATIME_NOW) {
547 debug!(
548 "Ignoring atime change as authfs does not maintain timestamp currently"
549 );
550 }
551 if valid.intersects(SetattrValid::MTIME | SetattrValid::MTIME_NOW) {
552 debug!(
553 "Ignoring mtime change as authfs does not maintain timestamp currently"
554 );
555 }
556 Ok((new_attr, DEFAULT_METADATA_TIMEOUT))
Victor Hsieh9d0ab622021-04-26 17:07:02 -0700557 }
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700558 _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
Victor Hsieh9d0ab622021-04-26 17:07:02 -0700559 }
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700560 })
Victor Hsieh9d0ab622021-04-26 17:07:02 -0700561 }
Victor Hsieh71f10032021-08-13 11:24:02 -0700562
563 fn getxattr(
564 &self,
565 _ctx: Context,
566 inode: Self::Inode,
567 name: &CStr,
568 size: u32,
569 ) -> io::Result<GetxattrReply> {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700570 self.handle_inode(&inode, |config| {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700571 match config {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700572 AuthFsEntry::VerifiedNew { editor } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700573 // FUSE ioctl is limited, thus we can't implement fs-verity ioctls without a kernel
574 // change (see b/196635431). Until it's possible, use xattr to expose what we need
575 // as an authfs specific API.
576 if name != CStr::from_bytes_with_nul(b"authfs.fsverity.digest\0").unwrap() {
577 return Err(io::Error::from_raw_os_error(libc::ENODATA));
578 }
Victor Hsieh71f10032021-08-13 11:24:02 -0700579
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700580 if size == 0 {
581 // Per protocol, when size is 0, return the value size.
582 Ok(GetxattrReply::Count(editor.get_fsverity_digest_size() as u32))
Victor Hsieh71f10032021-08-13 11:24:02 -0700583 } else {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700584 let digest = editor.calculate_fsverity_digest()?;
585 if digest.len() > size as usize {
586 Err(io::Error::from_raw_os_error(libc::ERANGE))
587 } else {
588 Ok(GetxattrReply::Value(digest.to_vec()))
589 }
Victor Hsieh71f10032021-08-13 11:24:02 -0700590 }
591 }
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700592 _ => Err(io::Error::from_raw_os_error(libc::ENODATA)),
Victor Hsieh71f10032021-08-13 11:24:02 -0700593 }
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700594 })
Victor Hsieh71f10032021-08-13 11:24:02 -0700595 }
Victor Hsieh45636232021-10-15 17:52:51 -0700596
597 fn mkdir(
598 &self,
599 _ctx: Context,
600 parent: Self::Inode,
601 name: &CStr,
602 _mode: u32,
603 _umask: u32,
604 ) -> io::Result<Entry> {
605 // TODO(205169366): Implement mode properly.
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -0800606 let new_inode =
607 self.create_new_entry(parent, name, |parent_entry, basename, new_inode| {
608 match parent_entry {
609 AuthFsEntry::VerifiedNewDirectory { dir } => {
610 if dir.find_inode(basename).is_some() {
611 return Err(io::Error::from_raw_os_error(libc::EEXIST));
612 }
613 let new_dir = dir.mkdir(basename, new_inode)?;
614 Ok(AuthFsEntry::VerifiedNewDirectory { dir: new_dir })
615 }
616 _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
Victor Hsieh45636232021-10-15 17:52:51 -0700617 }
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -0800618 })?;
Victor Hsieh45636232021-10-15 17:52:51 -0700619
620 Ok(Entry {
621 inode: new_inode,
622 generation: 0,
623 attr: create_dir_stat(new_inode, /* file_number */ 0)?,
624 entry_timeout: DEFAULT_METADATA_TIMEOUT,
625 attr_timeout: DEFAULT_METADATA_TIMEOUT,
626 })
627 }
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800628}
629
630/// Mount and start the FUSE instance. This requires CAP_SYS_ADMIN.
631pub fn loop_forever(
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800632 authfs: AuthFs,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800633 mountpoint: &Path,
Victor Hsieh4cc3b792021-08-04 12:00:04 -0700634 extra_options: &Option<String>,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800635) -> Result<(), fuse::Error> {
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800636 let dev_fuse = OpenOptions::new()
637 .read(true)
638 .write(true)
639 .open("/dev/fuse")
640 .expect("Failed to open /dev/fuse");
641
Victor Hsieh4cc3b792021-08-04 12:00:04 -0700642 let mut mount_options = vec![
643 MountOption::FD(dev_fuse.as_raw_fd()),
644 MountOption::RootMode(libc::S_IFDIR | libc::S_IXUSR | libc::S_IXGRP | libc::S_IXOTH),
645 MountOption::AllowOther,
646 MountOption::UserId(0),
647 MountOption::GroupId(0),
Victor Hsieh766e5332021-11-09 09:41:25 -0800648 MountOption::MaxRead(MAX_READ_BYTES),
Victor Hsieh4cc3b792021-08-04 12:00:04 -0700649 ];
650 if let Some(value) = extra_options {
651 mount_options.push(MountOption::Extra(value));
652 }
653
654 fuse::mount(mountpoint, "authfs", libc::MS_NOSUID | libc::MS_NODEV, &mount_options)
655 .expect("Failed to mount fuse");
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800656
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800657 fuse::worker::start_message_loop(dev_fuse, MAX_WRITE_BYTES, MAX_READ_BYTES, authfs)
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800658}
Victor Hsieh45636232021-10-15 17:52:51 -0700659
660fn cstr_to_path(cstr: &CStr) -> &Path {
661 OsStr::from_bytes(cstr.to_bytes()).as_ref()
662}