blob: 8ca82f8316a78f1b8474a78073b3ed892ad3e686 [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 Hsieh79f296b2021-12-02 15:38:08 -080017mod mount;
18
Victor Hsiehd18b9752021-11-09 16:03:34 -080019use anyhow::{anyhow, bail, Result};
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -080020use log::{debug, warn};
Victor Hsieh4d6b9d42021-11-08 15:53:49 -080021use std::collections::{btree_map, BTreeMap};
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080022use std::convert::TryFrom;
Victor Hsieh45636232021-10-15 17:52:51 -070023use std::ffi::{CStr, OsStr};
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080024use std::io;
Victor Hsiehf7fc3d32021-11-22 10:20:33 -080025use std::mem::{zeroed, MaybeUninit};
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080026use std::option::Option;
Victor Hsieh79f296b2021-12-02 15:38:08 -080027use std::os::unix::ffi::OsStrExt;
Victor Hsiehd18b9752021-11-09 16:03:34 -080028use std::path::{Component, Path, PathBuf};
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -080029use std::sync::atomic::{AtomicU64, Ordering};
Victor Hsieh60c2f412021-11-03 13:02:19 -070030use std::sync::Mutex;
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080031use std::time::Duration;
32
Victor Hsieh6a47e7f2021-03-03 15:53:49 -080033use fuse::filesystem::{
Victor Hsieh71f10032021-08-13 11:24:02 -070034 Context, DirEntry, DirectoryIterator, Entry, FileSystem, FsOptions, GetxattrReply,
35 SetattrValid, ZeroCopyReader, ZeroCopyWriter,
Victor Hsieh6a47e7f2021-03-03 15:53:49 -080036};
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080037
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 Hsiehd18b9752021-11-09 16:03:34 -080040 validate_basename, InMemoryDir, RandomWrite, ReadByChunk, RemoteDirEditor, RemoteFileEditor,
41 RemoteFileReader, RemoteMerkleTreeReader,
Victor Hsieh6a47e7f2021-03-03 15:53:49 -080042};
Victor Hsiehf7fc3d32021-11-22 10:20:33 -080043use crate::fsstat::RemoteFsStatsReader;
Victor Hsieh6a47e7f2021-03-03 15:53:49 -080044use crate::fsverity::{VerifiedFileEditor, VerifiedFileReader};
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080045
Victor Hsieh79f296b2021-12-02 15:38:08 -080046pub use self::mount::mount_and_enter_message_loop;
47use self::mount::MAX_WRITE_BYTES;
48
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080049pub type Inode = u64;
50type Handle = u64;
51
Victor Hsieh26cea2f2021-11-03 10:28:33 -070052const DEFAULT_METADATA_TIMEOUT: Duration = Duration::from_secs(5);
53const ROOT_INODE: Inode = 1;
54
55/// `AuthFsEntry` defines the filesystem entry type supported by AuthFS.
56pub enum AuthFsEntry {
Victor Hsieh4d6b9d42021-11-08 15:53:49 -080057 /// A read-only directory (writable during initialization). Root directory is an example.
58 ReadonlyDirectory { dir: InMemoryDir },
Victor Hsieh1bcf4112021-03-19 14:26:57 -070059 /// A file type that is verified against fs-verity signature (thus read-only). The file is
Victor Hsieh1bcf4112021-03-19 14:26:57 -070060 /// served from a remote server.
Victor Hsieh88e50172021-10-15 13:27:13 -070061 VerifiedReadonly {
Victor Hsieh1bcf4112021-03-19 14:26:57 -070062 reader: VerifiedFileReader<RemoteFileReader, RemoteMerkleTreeReader>,
63 file_size: u64,
64 },
Victor Hsiehf7fc3d32021-11-22 10:20:33 -080065 /// A file type that is a read-only passthrough from a file on a remote server.
Victor Hsieh88e50172021-10-15 13:27:13 -070066 UnverifiedReadonly { reader: RemoteFileReader, file_size: u64 },
Victor Hsieh1bcf4112021-03-19 14:26:57 -070067 /// A file type that is initially empty, and the content is stored on a remote server. File
68 /// integrity is guaranteed with private Merkle tree.
Victor Hsieh88e50172021-10-15 13:27:13 -070069 VerifiedNew { editor: VerifiedFileEditor<RemoteFileEditor> },
Victor Hsieh45636232021-10-15 17:52:51 -070070 /// A directory type that is initially empty. One can create new file (`VerifiedNew`) and new
71 /// directory (`VerifiedNewDirectory` itself) with integrity guaranteed within the VM.
72 VerifiedNewDirectory { dir: RemoteDirEditor },
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080073}
74
Victor Hsieh60c2f412021-11-03 13:02:19 -070075// AuthFS needs to be `Sync` to be accepted by fuse::worker::start_message_loop as a `FileSystem`.
Victor Hsieh4d6b9d42021-11-08 15:53:49 -080076pub struct AuthFs {
Victor Hsieh60c2f412021-11-03 13:02:19 -070077 /// Table for `Inode` to `AuthFsEntry` lookup. This needs to be `Sync` to be used in
78 /// `fuse::worker::start_message_loop`.
79 inode_table: Mutex<BTreeMap<Inode, AuthFsEntry>>,
80
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -080081 /// The next available inode number.
82 next_inode: AtomicU64,
Victor Hsiehf7fc3d32021-11-22 10:20:33 -080083
84 /// A reader to access the remote filesystem stats, which is supposed to be of "the" output
85 /// directory. We assume all output are stored in the same partition.
86 remote_fs_stats_reader: RemoteFsStatsReader,
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 Hsiehf7fc3d32021-11-22 10:20:33 -080093 pub fn new(remote_fs_stats_reader: RemoteFsStatsReader) -> 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 Hsiehf7fc3d32021-11-22 10:20:33 -080097 AuthFs {
98 inode_table: Mutex::new(inode_table),
99 next_inode: AtomicU64::new(ROOT_INODE + 1),
100 remote_fs_stats_reader,
101 }
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800102 }
103
Victor Hsiehd18b9752021-11-09 16:03:34 -0800104 /// Add an `AuthFsEntry` as `basename` to the filesystem root.
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800105 pub fn add_entry_at_root_dir(
106 &mut self,
107 basename: PathBuf,
108 entry: AuthFsEntry,
109 ) -> Result<Inode> {
Victor Hsiehd18b9752021-11-09 16:03:34 -0800110 validate_basename(&basename)?;
111 self.add_entry_at_ro_dir_by_path(ROOT_INODE, &basename, entry)
112 }
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800113
Victor Hsiehd18b9752021-11-09 16:03:34 -0800114 /// Add an `AuthFsEntry` by path from the `ReadonlyDirectory` represented by `dir_inode`. The
115 /// path must be a related path. If some ancestor directories do not exist, they will be
116 /// created (also as `ReadonlyDirectory`) automatically.
117 pub fn add_entry_at_ro_dir_by_path(
118 &mut self,
119 dir_inode: Inode,
120 path: &Path,
121 entry: AuthFsEntry,
122 ) -> Result<Inode> {
123 // 1. Make sure the parent directories all exist. Derive the entry's parent inode.
124 let parent_path =
125 path.parent().ok_or_else(|| anyhow!("No parent directory: {:?}", path))?;
126 let parent_inode =
127 parent_path.components().try_fold(dir_inode, |current_dir_inode, path_component| {
128 match path_component {
129 Component::RootDir => bail!("Absolute path is not supported"),
130 Component::Normal(name) => {
131 let inode_table = self.inode_table.get_mut().unwrap();
132 // Locate the internal directory structure.
133 let current_dir_entry =
134 inode_table.get_mut(&current_dir_inode).ok_or_else(|| {
135 anyhow!("Unknown directory inode {}", current_dir_inode)
136 })?;
137 let dir = match current_dir_entry {
138 AuthFsEntry::ReadonlyDirectory { dir } => dir,
139 _ => unreachable!("Not a ReadonlyDirectory"),
140 };
141 // Return directory inode. Create first if not exists.
142 if let Some(existing_inode) = dir.lookup_inode(name.as_ref()) {
143 Ok(existing_inode)
144 } else {
145 let new_inode = self.next_inode.fetch_add(1, Ordering::Relaxed);
146 let new_dir_entry =
147 AuthFsEntry::ReadonlyDirectory { dir: InMemoryDir::new() };
148
149 // Actually update the tables.
150 dir.add_entry(name.as_ref(), new_inode)?;
151 if inode_table.insert(new_inode, new_dir_entry).is_some() {
152 bail!("Unexpected to find a duplicated inode");
153 }
154 Ok(new_inode)
155 }
156 }
157 _ => Err(anyhow!("Path is not canonical: {:?}", path)),
158 }
159 })?;
160
161 // 2. Insert the entry to the parent directory, as well as the inode table.
162 let inode_table = self.inode_table.get_mut().unwrap();
163 match inode_table.get_mut(&parent_inode).expect("previously returned inode") {
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800164 AuthFsEntry::ReadonlyDirectory { dir } => {
Victor Hsiehd18b9752021-11-09 16:03:34 -0800165 let basename =
166 path.file_name().ok_or_else(|| anyhow!("Bad file name: {:?}", path))?;
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800167 let new_inode = self.next_inode.fetch_add(1, Ordering::Relaxed);
168
Victor Hsiehd18b9752021-11-09 16:03:34 -0800169 // Actually update the tables.
170 dir.add_entry(basename.as_ref(), new_inode)?;
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800171 if inode_table.insert(new_inode, entry).is_some() {
Victor Hsiehd18b9752021-11-09 16:03:34 -0800172 bail!("Unexpected to find a duplicated inode");
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800173 }
174 Ok(new_inode)
175 }
Victor Hsiehd18b9752021-11-09 16:03:34 -0800176 _ => unreachable!("Not a ReadonlyDirectory"),
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800177 }
178 }
179}
180
181// Implementation for serving requests.
182impl AuthFs {
Victor Hsieh45636232021-10-15 17:52:51 -0700183 /// Handles the file associated with `inode` if found. This function returns whatever
184 /// `handle_fn` returns.
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700185 fn handle_inode<F, R>(&self, inode: &Inode, handle_fn: F) -> io::Result<R>
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700186 where
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700187 F: FnOnce(&AuthFsEntry) -> io::Result<R>,
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700188 {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700189 let inode_table = self.inode_table.lock().unwrap();
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -0800190 let entry =
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700191 inode_table.get(inode).ok_or_else(|| io::Error::from_raw_os_error(libc::ENOENT))?;
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -0800192 handle_fn(entry)
Victor Hsieh45636232021-10-15 17:52:51 -0700193 }
194
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -0800195 /// Adds a new entry `name` created by `create_fn` at `parent_inode`.
196 ///
197 /// The operation involves two updates: adding the name with a new allocated inode to the
198 /// parent directory, and insert the new inode and the actual `AuthFsEntry` to the global inode
199 /// table.
200 ///
201 /// `create_fn` receives the parent directory, through which it can create the new entry at and
202 /// register the new inode to. Its returned entry is then added to the inode table.
203 fn create_new_entry<F>(
204 &self,
205 parent_inode: Inode,
206 name: &CStr,
207 create_fn: F,
208 ) -> io::Result<Inode>
Victor Hsieh45636232021-10-15 17:52:51 -0700209 where
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -0800210 F: FnOnce(&mut AuthFsEntry, &Path, Inode) -> io::Result<AuthFsEntry>,
Victor Hsieh45636232021-10-15 17:52:51 -0700211 {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700212 let mut inode_table = self.inode_table.lock().unwrap();
Chris Wailes641fc4a2021-12-01 15:03:21 -0800213 let parent_entry = inode_table
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -0800214 .get_mut(&parent_inode)
215 .ok_or_else(|| io::Error::from_raw_os_error(libc::ENOENT))?;
216
217 let new_inode = self.next_inode.fetch_add(1, Ordering::Relaxed);
218 let basename: &Path = cstr_to_path(name);
Chris Wailes641fc4a2021-12-01 15:03:21 -0800219 let new_file_entry = create_fn(parent_entry, basename, new_inode)?;
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700220 if let btree_map::Entry::Vacant(entry) = inode_table.entry(new_inode) {
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -0800221 entry.insert(new_file_entry);
Victor Hsieh45636232021-10-15 17:52:51 -0700222 Ok(new_inode)
223 } else {
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -0800224 unreachable!("Unexpected duplication of inode {}", new_inode);
Victor Hsieh45636232021-10-15 17:52:51 -0700225 }
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800226 }
227}
228
229fn check_access_mode(flags: u32, mode: libc::c_int) -> io::Result<()> {
230 if (flags & libc::O_ACCMODE as u32) == mode as u32 {
231 Ok(())
232 } else {
233 Err(io::Error::from_raw_os_error(libc::EACCES))
234 }
235}
236
237cfg_if::cfg_if! {
238 if #[cfg(all(target_arch = "aarch64", target_pointer_width = "64"))] {
Victor Hsiehda3fbc42021-02-23 16:12:49 -0800239 fn blk_size() -> libc::c_int { CHUNK_SIZE as libc::c_int }
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800240 } else {
Victor Hsiehda3fbc42021-02-23 16:12:49 -0800241 fn blk_size() -> libc::c_long { CHUNK_SIZE as libc::c_long }
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800242 }
243}
244
Victor Hsieh45636232021-10-15 17:52:51 -0700245#[allow(clippy::enum_variant_names)]
246enum AccessMode {
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800247 ReadOnly,
248 ReadWrite,
249}
250
Victor Hsieh45636232021-10-15 17:52:51 -0700251fn create_stat(
252 ino: libc::ino_t,
253 file_size: u64,
254 access_mode: AccessMode,
255) -> io::Result<libc::stat64> {
256 // SAFETY: stat64 is a plan C struct without pointer.
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800257 let mut st = unsafe { MaybeUninit::<libc::stat64>::zeroed().assume_init() };
258
259 st.st_ino = ino;
Victor Hsieh45636232021-10-15 17:52:51 -0700260 st.st_mode = match access_mode {
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800261 // Until needed, let's just grant the owner access.
Victor Hsieh45636232021-10-15 17:52:51 -0700262 // TODO(205169366): Implement mode properly.
263 AccessMode::ReadOnly => libc::S_IFREG | libc::S_IRUSR,
264 AccessMode::ReadWrite => libc::S_IFREG | libc::S_IRUSR | libc::S_IWUSR,
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800265 };
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800266 st.st_nlink = 1;
267 st.st_uid = 0;
268 st.st_gid = 0;
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800269 st.st_size = libc::off64_t::try_from(file_size)
270 .map_err(|_| io::Error::from_raw_os_error(libc::EFBIG))?;
271 st.st_blksize = blk_size();
272 // Per man stat(2), st_blocks is "Number of 512B blocks allocated".
273 st.st_blocks = libc::c_longlong::try_from(divide_roundup(file_size, 512))
274 .map_err(|_| io::Error::from_raw_os_error(libc::EFBIG))?;
275 Ok(st)
276}
277
Victor Hsieh45636232021-10-15 17:52:51 -0700278fn create_dir_stat(ino: libc::ino_t, file_number: u16) -> io::Result<libc::stat64> {
279 // SAFETY: stat64 is a plan C struct without pointer.
280 let mut st = unsafe { MaybeUninit::<libc::stat64>::zeroed().assume_init() };
281
282 st.st_ino = ino;
283 // TODO(205169366): Implement mode properly.
284 st.st_mode = libc::S_IFDIR
285 | libc::S_IXUSR
286 | libc::S_IWUSR
287 | libc::S_IRUSR
288 | libc::S_IXGRP
289 | libc::S_IXOTH;
290
291 // 2 extra for . and ..
292 st.st_nlink = file_number
293 .checked_add(2)
294 .ok_or_else(|| io::Error::from_raw_os_error(libc::EOVERFLOW))?
295 .into();
296
297 st.st_uid = 0;
298 st.st_gid = 0;
299 Ok(st)
300}
301
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800302fn offset_to_chunk_index(offset: u64) -> u64 {
Victor Hsiehda3fbc42021-02-23 16:12:49 -0800303 offset / CHUNK_SIZE
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800304}
305
Victor Hsiehd0bb5d32021-03-19 12:48:03 -0700306fn read_chunks<W: io::Write, T: ReadByChunk>(
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800307 mut w: W,
308 file: &T,
309 file_size: u64,
310 offset: u64,
311 size: u32,
312) -> io::Result<usize> {
313 let remaining = file_size.saturating_sub(offset);
314 let size_to_read = std::cmp::min(size as usize, remaining as usize);
Victor Hsiehac4f3f42021-02-26 12:35:58 -0800315 let total = ChunkedSizeIter::new(size_to_read, offset, CHUNK_SIZE as usize).try_fold(
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800316 0,
317 |total, (current_offset, planned_data_size)| {
318 // TODO(victorhsieh): There might be a non-trivial way to avoid this copy. For example,
319 // instead of accepting a buffer, the writer could expose the final destination buffer
320 // for the reader to write to. It might not be generally applicable though, e.g. with
321 // virtio transport, the buffer may not be continuous.
Victor Hsiehda3fbc42021-02-23 16:12:49 -0800322 let mut buf = [0u8; CHUNK_SIZE as usize];
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800323 let read_size = file.read_chunk(offset_to_chunk_index(current_offset), &mut buf)?;
324 if read_size < planned_data_size {
325 return Err(io::Error::from_raw_os_error(libc::ENODATA));
326 }
327
Victor Hsiehda3fbc42021-02-23 16:12:49 -0800328 let begin = (current_offset % CHUNK_SIZE) as usize;
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800329 let end = begin + planned_data_size;
330 let s = w.write(&buf[begin..end])?;
331 if s != planned_data_size {
332 return Err(io::Error::from_raw_os_error(libc::EIO));
333 }
334 Ok(total + s)
335 },
336 )?;
337
338 Ok(total)
339}
340
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800341// TODO(205715172): Support enumerating directory entries.
342pub struct EmptyDirectoryIterator {}
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800343
344impl DirectoryIterator for EmptyDirectoryIterator {
345 fn next(&mut self) -> Option<DirEntry> {
346 None
347 }
348}
349
350impl FileSystem for AuthFs {
351 type Inode = Inode;
352 type Handle = Handle;
353 type DirIter = EmptyDirectoryIterator;
354
355 fn max_buffer_size(&self) -> u32 {
Victor Hsieh766e5332021-11-09 09:41:25 -0800356 MAX_WRITE_BYTES
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800357 }
358
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800359 fn init(&self, _capable: FsOptions) -> io::Result<FsOptions> {
360 // Enable writeback cache for better performance especially since our bandwidth to the
361 // backend service is limited.
362 Ok(FsOptions::WRITEBACK_CACHE)
363 }
364
Victor Hsieh45636232021-10-15 17:52:51 -0700365 fn lookup(&self, _ctx: Context, parent: Inode, name: &CStr) -> io::Result<Entry> {
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800366 // Look up the entry's inode number in parent directory.
367 let inode = self.handle_inode(&parent, |parent_entry| match parent_entry {
368 AuthFsEntry::ReadonlyDirectory { dir } => {
369 let path = cstr_to_path(name);
370 dir.lookup_inode(path).ok_or_else(|| io::Error::from_raw_os_error(libc::ENOENT))
371 }
372 AuthFsEntry::VerifiedNewDirectory { dir } => {
373 let path = cstr_to_path(name);
374 dir.find_inode(path).ok_or_else(|| io::Error::from_raw_os_error(libc::ENOENT))
375 }
376 _ => Err(io::Error::from_raw_os_error(libc::ENOTDIR)),
377 })?;
378
379 // Normally, `lookup` is required to increase a reference count for the inode (while
380 // `forget` will decrease it). It is not yet necessary until we start to support
381 // deletion (only for `VerifiedNewDirectory`).
382
383 // Create the entry's stat if found.
384 let st = self.handle_inode(&inode, |entry| match entry {
Victor Hsiehd18b9752021-11-09 16:03:34 -0800385 AuthFsEntry::ReadonlyDirectory { dir } => {
386 create_dir_stat(inode, dir.number_of_entries())
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800387 }
388 AuthFsEntry::UnverifiedReadonly { file_size, .. }
389 | AuthFsEntry::VerifiedReadonly { file_size, .. } => {
390 create_stat(inode, *file_size, AccessMode::ReadOnly)
391 }
392 AuthFsEntry::VerifiedNew { editor } => {
393 create_stat(inode, editor.size(), AccessMode::ReadWrite)
394 }
395 AuthFsEntry::VerifiedNewDirectory { dir } => {
396 create_dir_stat(inode, dir.number_of_entries())
397 }
398 })?;
399 Ok(Entry {
400 inode,
401 generation: 0,
402 attr: st,
403 entry_timeout: DEFAULT_METADATA_TIMEOUT,
404 attr_timeout: DEFAULT_METADATA_TIMEOUT,
405 })
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800406 }
407
408 fn getattr(
409 &self,
410 _ctx: Context,
411 inode: Inode,
412 _handle: Option<Handle>,
413 ) -> io::Result<(libc::stat64, Duration)> {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700414 self.handle_inode(&inode, |config| {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700415 Ok((
416 match config {
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800417 AuthFsEntry::ReadonlyDirectory { dir } => {
418 create_dir_stat(inode, dir.number_of_entries())
419 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700420 AuthFsEntry::UnverifiedReadonly { file_size, .. }
421 | AuthFsEntry::VerifiedReadonly { file_size, .. } => {
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800422 create_stat(inode, *file_size, AccessMode::ReadOnly)
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700423 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700424 AuthFsEntry::VerifiedNew { editor } => {
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800425 create_stat(inode, editor.size(), AccessMode::ReadWrite)
Victor Hsieh45636232021-10-15 17:52:51 -0700426 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700427 AuthFsEntry::VerifiedNewDirectory { dir } => {
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800428 create_dir_stat(inode, dir.number_of_entries())
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700429 }
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800430 }?,
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700431 DEFAULT_METADATA_TIMEOUT,
432 ))
433 })
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800434 }
435
436 fn open(
437 &self,
438 _ctx: Context,
439 inode: Self::Inode,
440 flags: u32,
441 ) -> io::Result<(Option<Self::Handle>, fuse::sys::OpenOptions)> {
442 // Since file handle is not really used in later operations (which use Inode directly),
Victor Hsieh09e26262021-03-03 16:00:55 -0800443 // return None as the handle.
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700444 self.handle_inode(&inode, |config| {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700445 match config {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700446 AuthFsEntry::VerifiedReadonly { .. } | AuthFsEntry::UnverifiedReadonly { .. } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700447 check_access_mode(flags, libc::O_RDONLY)?;
448 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700449 AuthFsEntry::VerifiedNew { .. } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700450 // No need to check access modes since all the modes are allowed to the
451 // read-writable file.
452 }
Victor Hsieh4d6b9d42021-11-08 15:53:49 -0800453 AuthFsEntry::ReadonlyDirectory { .. }
454 | AuthFsEntry::VerifiedNewDirectory { .. } => {
Victor Hsieh45636232021-10-15 17:52:51 -0700455 // TODO(victorhsieh): implement when needed.
456 return Err(io::Error::from_raw_os_error(libc::ENOSYS));
457 }
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800458 }
Victor Hsieh45636232021-10-15 17:52:51 -0700459 // Always cache the file content. There is currently no need to support direct I/O or
460 // avoid the cache buffer. Memory mapping is only possible with cache enabled.
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700461 Ok((None, fuse::sys::OpenOptions::KEEP_CACHE))
462 })
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800463 }
464
Victor Hsieh45636232021-10-15 17:52:51 -0700465 fn create(
466 &self,
467 _ctx: Context,
468 parent: Self::Inode,
469 name: &CStr,
470 _mode: u32,
471 _flags: u32,
472 _umask: u32,
473 ) -> io::Result<(Entry, Option<Self::Handle>, fuse::sys::OpenOptions)> {
474 // TODO(205169366): Implement mode properly.
475 // TODO(205172873): handle O_TRUNC and O_EXCL properly.
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -0800476 let new_inode =
477 self.create_new_entry(parent, name, |parent_entry, basename, new_inode| {
478 match parent_entry {
479 AuthFsEntry::VerifiedNewDirectory { dir } => {
480 if dir.find_inode(basename).is_some() {
481 return Err(io::Error::from_raw_os_error(libc::EEXIST));
482 }
483 let new_file = dir.create_file(basename, new_inode)?;
484 Ok(AuthFsEntry::VerifiedNew { editor: new_file })
485 }
486 _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
Victor Hsieh45636232021-10-15 17:52:51 -0700487 }
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -0800488 })?;
Victor Hsieh45636232021-10-15 17:52:51 -0700489
490 Ok((
491 Entry {
492 inode: new_inode,
493 generation: 0,
494 attr: create_stat(new_inode, /* file_size */ 0, AccessMode::ReadWrite)?,
495 entry_timeout: DEFAULT_METADATA_TIMEOUT,
496 attr_timeout: DEFAULT_METADATA_TIMEOUT,
497 },
498 // See also `open`.
499 /* handle */ None,
500 fuse::sys::OpenOptions::KEEP_CACHE,
501 ))
502 }
503
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800504 fn read<W: io::Write + ZeroCopyWriter>(
505 &self,
506 _ctx: Context,
507 inode: Inode,
508 _handle: Handle,
509 w: W,
510 size: u32,
511 offset: u64,
512 _lock_owner: Option<u64>,
513 _flags: u32,
514 ) -> io::Result<usize> {
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::VerifiedReadonly { reader, file_size } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700518 read_chunks(w, reader, *file_size, offset, size)
519 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700520 AuthFsEntry::UnverifiedReadonly { reader, file_size } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700521 read_chunks(w, reader, *file_size, offset, size)
522 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700523 AuthFsEntry::VerifiedNew { editor } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700524 // Note that with FsOptions::WRITEBACK_CACHE, it's possible for the kernel to
525 // request a read even if the file is open with O_WRONLY.
526 read_chunks(w, editor, editor.size(), offset, size)
527 }
Victor Hsieh45636232021-10-15 17:52:51 -0700528 _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800529 }
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700530 })
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800531 }
532
533 fn write<R: io::Read + ZeroCopyReader>(
534 &self,
535 _ctx: Context,
536 inode: Self::Inode,
537 _handle: Self::Handle,
538 mut r: R,
539 size: u32,
540 offset: u64,
541 _lock_owner: Option<u64>,
542 _delayed_write: bool,
543 _flags: u32,
544 ) -> io::Result<usize> {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700545 self.handle_inode(&inode, |config| match config {
546 AuthFsEntry::VerifiedNew { editor } => {
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800547 let mut buf = vec![0; size as usize];
548 r.read_exact(&mut buf)?;
Victor Hsieh1bcf4112021-03-19 14:26:57 -0700549 editor.write_at(&buf, offset)
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800550 }
551 _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700552 })
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800553 }
Victor Hsieh9d0ab622021-04-26 17:07:02 -0700554
555 fn setattr(
556 &self,
557 _ctx: Context,
558 inode: Inode,
559 attr: libc::stat64,
560 _handle: Option<Handle>,
561 valid: SetattrValid,
562 ) -> io::Result<(libc::stat64, Duration)> {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700563 self.handle_inode(&inode, |config| {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700564 match config {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700565 AuthFsEntry::VerifiedNew { editor } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700566 // Initialize the default stat.
Victor Hsieh45636232021-10-15 17:52:51 -0700567 let mut new_attr = create_stat(inode, editor.size(), AccessMode::ReadWrite)?;
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700568 // `valid` indicates what fields in `attr` are valid. Update to return correctly.
569 if valid.contains(SetattrValid::SIZE) {
570 // st_size is i64, but the cast should be safe since kernel should not give a
571 // negative size.
572 debug_assert!(attr.st_size >= 0);
573 new_attr.st_size = attr.st_size;
574 editor.resize(attr.st_size as u64)?;
575 }
Victor Hsieh9d0ab622021-04-26 17:07:02 -0700576
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700577 if valid.contains(SetattrValid::MODE) {
578 warn!("Changing st_mode is not currently supported");
579 return Err(io::Error::from_raw_os_error(libc::ENOSYS));
580 }
581 if valid.contains(SetattrValid::UID) {
582 warn!("Changing st_uid is not currently supported");
583 return Err(io::Error::from_raw_os_error(libc::ENOSYS));
584 }
585 if valid.contains(SetattrValid::GID) {
586 warn!("Changing st_gid is not currently supported");
587 return Err(io::Error::from_raw_os_error(libc::ENOSYS));
588 }
589 if valid.contains(SetattrValid::CTIME) {
590 debug!(
591 "Ignoring ctime change as authfs does not maintain timestamp currently"
592 );
593 }
594 if valid.intersects(SetattrValid::ATIME | SetattrValid::ATIME_NOW) {
595 debug!(
596 "Ignoring atime change as authfs does not maintain timestamp currently"
597 );
598 }
599 if valid.intersects(SetattrValid::MTIME | SetattrValid::MTIME_NOW) {
600 debug!(
601 "Ignoring mtime change as authfs does not maintain timestamp currently"
602 );
603 }
604 Ok((new_attr, DEFAULT_METADATA_TIMEOUT))
Victor Hsieh9d0ab622021-04-26 17:07:02 -0700605 }
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700606 _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
Victor Hsieh9d0ab622021-04-26 17:07:02 -0700607 }
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700608 })
Victor Hsieh9d0ab622021-04-26 17:07:02 -0700609 }
Victor Hsieh71f10032021-08-13 11:24:02 -0700610
611 fn getxattr(
612 &self,
613 _ctx: Context,
614 inode: Self::Inode,
615 name: &CStr,
616 size: u32,
617 ) -> io::Result<GetxattrReply> {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700618 self.handle_inode(&inode, |config| {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700619 match config {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700620 AuthFsEntry::VerifiedNew { editor } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700621 // FUSE ioctl is limited, thus we can't implement fs-verity ioctls without a kernel
622 // change (see b/196635431). Until it's possible, use xattr to expose what we need
623 // as an authfs specific API.
624 if name != CStr::from_bytes_with_nul(b"authfs.fsverity.digest\0").unwrap() {
625 return Err(io::Error::from_raw_os_error(libc::ENODATA));
626 }
Victor Hsieh71f10032021-08-13 11:24:02 -0700627
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700628 if size == 0 {
629 // Per protocol, when size is 0, return the value size.
630 Ok(GetxattrReply::Count(editor.get_fsverity_digest_size() as u32))
Victor Hsieh71f10032021-08-13 11:24:02 -0700631 } else {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700632 let digest = editor.calculate_fsverity_digest()?;
633 if digest.len() > size as usize {
634 Err(io::Error::from_raw_os_error(libc::ERANGE))
635 } else {
636 Ok(GetxattrReply::Value(digest.to_vec()))
637 }
Victor Hsieh71f10032021-08-13 11:24:02 -0700638 }
639 }
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700640 _ => Err(io::Error::from_raw_os_error(libc::ENODATA)),
Victor Hsieh71f10032021-08-13 11:24:02 -0700641 }
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700642 })
Victor Hsieh71f10032021-08-13 11:24:02 -0700643 }
Victor Hsieh45636232021-10-15 17:52:51 -0700644
645 fn mkdir(
646 &self,
647 _ctx: Context,
648 parent: Self::Inode,
649 name: &CStr,
650 _mode: u32,
651 _umask: u32,
652 ) -> io::Result<Entry> {
653 // TODO(205169366): Implement mode properly.
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -0800654 let new_inode =
655 self.create_new_entry(parent, name, |parent_entry, basename, new_inode| {
656 match parent_entry {
657 AuthFsEntry::VerifiedNewDirectory { dir } => {
658 if dir.find_inode(basename).is_some() {
659 return Err(io::Error::from_raw_os_error(libc::EEXIST));
660 }
661 let new_dir = dir.mkdir(basename, new_inode)?;
662 Ok(AuthFsEntry::VerifiedNewDirectory { dir: new_dir })
663 }
Victor Hsiehd18b9752021-11-09 16:03:34 -0800664 AuthFsEntry::ReadonlyDirectory { .. } => {
665 Err(io::Error::from_raw_os_error(libc::EACCES))
666 }
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -0800667 _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
Victor Hsieh45636232021-10-15 17:52:51 -0700668 }
Victor Hsiehd5a5b1e2021-11-09 11:42:34 -0800669 })?;
Victor Hsieh45636232021-10-15 17:52:51 -0700670
671 Ok(Entry {
672 inode: new_inode,
673 generation: 0,
674 attr: create_dir_stat(new_inode, /* file_number */ 0)?,
675 entry_timeout: DEFAULT_METADATA_TIMEOUT,
676 attr_timeout: DEFAULT_METADATA_TIMEOUT,
677 })
678 }
Victor Hsiehf7fc3d32021-11-22 10:20:33 -0800679
680 fn statfs(&self, _ctx: Context, _inode: Self::Inode) -> io::Result<libc::statvfs64> {
681 let remote_stat = self.remote_fs_stats_reader.statfs()?;
682
683 // Safe because we are zero-initializing a struct with only POD fields. Not all fields
684 // matter to FUSE. See also:
685 // https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/fuse/inode.c?h=v5.15#n460
686 let mut st: libc::statvfs64 = unsafe { zeroed() };
687
688 // Use the remote stat as a template, since it'd matter the most to consider the writable
689 // files/directories that are written to the remote.
690 st.f_bsize = remote_stat.block_size;
691 st.f_frsize = remote_stat.fragment_size;
692 st.f_blocks = remote_stat.block_numbers;
693 st.f_bavail = remote_stat.block_available;
694 st.f_favail = remote_stat.inodes_available;
695 st.f_namemax = remote_stat.max_filename;
696 // Assuming we are not privileged to use all free spaces on the remote server, set the free
697 // blocks/fragment to the same available amount.
698 st.f_bfree = st.f_bavail;
699 st.f_ffree = st.f_favail;
700 // Number of inodes on the filesystem
701 st.f_files = self.inode_table.lock().unwrap().len() as u64;
702
703 Ok(st)
704 }
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800705}
706
Victor Hsieh45636232021-10-15 17:52:51 -0700707fn cstr_to_path(cstr: &CStr) -> &Path {
708 OsStr::from_bytes(cstr.to_bytes()).as_ref()
709}