blob: 2370acb3f32a06985bcb55d64d2fc144c73d0092 [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};
19use 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 Hsieh88ac6ca2020-11-13 15:20:24 -080027use std::path::Path;
Victor Hsieh45636232021-10-15 17:52:51 -070028use std::sync::{Arc, 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
50/// `AuthFsEntry` defines the filesystem entry type supported by AuthFS.
51pub enum AuthFsEntry {
Victor Hsieh1bcf4112021-03-19 14:26:57 -070052 /// A file type that is verified against fs-verity signature (thus read-only). The file is
Victor Hsieh1bcf4112021-03-19 14:26:57 -070053 /// served from a remote server.
Victor Hsieh88e50172021-10-15 13:27:13 -070054 VerifiedReadonly {
Victor Hsieh1bcf4112021-03-19 14:26:57 -070055 reader: VerifiedFileReader<RemoteFileReader, RemoteMerkleTreeReader>,
56 file_size: u64,
57 },
58 /// A file type that is a read-only passthrough from a file on a remote serrver.
Victor Hsieh88e50172021-10-15 13:27:13 -070059 UnverifiedReadonly { reader: RemoteFileReader, file_size: u64 },
Victor Hsieh1bcf4112021-03-19 14:26:57 -070060 /// A file type that is initially empty, and the content is stored on a remote server. File
61 /// integrity is guaranteed with private Merkle tree.
Victor Hsieh88e50172021-10-15 13:27:13 -070062 VerifiedNew { editor: VerifiedFileEditor<RemoteFileEditor> },
Victor Hsieh45636232021-10-15 17:52:51 -070063 /// A directory type that is initially empty. One can create new file (`VerifiedNew`) and new
64 /// directory (`VerifiedNewDirectory` itself) with integrity guaranteed within the VM.
65 VerifiedNewDirectory { dir: RemoteDirEditor },
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080066}
67
68struct AuthFs {
Victor Hsieh26cea2f2021-11-03 10:28:33 -070069 /// A table for looking up an `AuthFsEntry` given an `Inode`. This needs to be `Sync` to be
70 /// used in `fuse::worker::start_message_loop`.
71 inode_table: Arc<Mutex<BTreeMap<Inode, AuthFsEntry>>>,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080072
Victor Hsieh26cea2f2021-11-03 10:28:33 -070073 /// Maximum bytes in the write transaction to the FUSE device. This limits the maximum buffer
74 /// size in a read request (including FUSE protocol overhead) that the filesystem writes to.
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080075 max_write: u32,
76}
77
78impl AuthFs {
Victor Hsieh26cea2f2021-11-03 10:28:33 -070079 pub fn new(root_entries: Arc<Mutex<BTreeMap<Inode, AuthFsEntry>>>, max_write: u32) -> AuthFs {
80 // TODO(203251769): Make root_entries a path -> entry map, then assign inodes internally.
81 AuthFs { inode_table: root_entries, max_write }
Victor Hsieh88ac6ca2020-11-13 15:20:24 -080082 }
83
Victor Hsieh45636232021-10-15 17:52:51 -070084 /// Handles the file associated with `inode` if found. This function returns whatever
85 /// `handle_fn` returns.
Victor Hsieh26cea2f2021-11-03 10:28:33 -070086 fn handle_inode<F, R>(&self, inode: &Inode, handle_fn: F) -> io::Result<R>
Victor Hsiehc85e4ef2021-10-18 15:28:53 -070087 where
Victor Hsieh26cea2f2021-11-03 10:28:33 -070088 F: FnOnce(&AuthFsEntry) -> io::Result<R>,
Victor Hsiehc85e4ef2021-10-18 15:28:53 -070089 {
Victor Hsieh26cea2f2021-11-03 10:28:33 -070090 let inode_table = self.inode_table.lock().unwrap();
Victor Hsiehc85e4ef2021-10-18 15:28:53 -070091 let config =
Victor Hsieh26cea2f2021-11-03 10:28:33 -070092 inode_table.get(inode).ok_or_else(|| io::Error::from_raw_os_error(libc::ENOENT))?;
Victor Hsieh45636232021-10-15 17:52:51 -070093 handle_fn(config)
94 }
95
Victor Hsieh26cea2f2021-11-03 10:28:33 -070096 /// Inserts a new inode and corresponding `AuthFsEntry` created by `create_fn` to the inode
97 /// table, then returns the new inode number.
Victor Hsieh45636232021-10-15 17:52:51 -070098 fn insert_new_inode<F>(&self, inode: &Inode, create_fn: F) -> io::Result<Inode>
99 where
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700100 F: FnOnce(&mut AuthFsEntry) -> io::Result<(Inode, AuthFsEntry)>,
Victor Hsieh45636232021-10-15 17:52:51 -0700101 {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700102 let mut inode_table = self.inode_table.lock().unwrap();
Victor Hsieh45636232021-10-15 17:52:51 -0700103 let mut config =
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700104 inode_table.get_mut(inode).ok_or_else(|| io::Error::from_raw_os_error(libc::ENOENT))?;
Victor Hsieh45636232021-10-15 17:52:51 -0700105 let (new_inode, new_file_config) = create_fn(&mut config)?;
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700106 if let btree_map::Entry::Vacant(entry) = inode_table.entry(new_inode) {
Victor Hsieh45636232021-10-15 17:52:51 -0700107 entry.insert(new_file_config);
108 Ok(new_inode)
109 } else {
110 // We can't assume fd_server is trusted, so the returned FD may collide with existing
111 // one, even when we are creating a new file. Do not override an existing FD. In terms
112 // of security, it is better to "leak" the file created earlier, than returning an
113 // existing inode as a new file.
114 error!("Inode {} already exists, do not override", new_inode);
115 Err(io::Error::from_raw_os_error(libc::EIO))
116 }
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800117 }
118}
119
120fn check_access_mode(flags: u32, mode: libc::c_int) -> io::Result<()> {
121 if (flags & libc::O_ACCMODE as u32) == mode as u32 {
122 Ok(())
123 } else {
124 Err(io::Error::from_raw_os_error(libc::EACCES))
125 }
126}
127
128cfg_if::cfg_if! {
129 if #[cfg(all(target_arch = "aarch64", target_pointer_width = "64"))] {
Victor Hsiehda3fbc42021-02-23 16:12:49 -0800130 fn blk_size() -> libc::c_int { CHUNK_SIZE as libc::c_int }
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800131 } else {
Victor Hsiehda3fbc42021-02-23 16:12:49 -0800132 fn blk_size() -> libc::c_long { CHUNK_SIZE as libc::c_long }
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800133 }
134}
135
Victor Hsieh45636232021-10-15 17:52:51 -0700136#[allow(clippy::enum_variant_names)]
137enum AccessMode {
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800138 ReadOnly,
139 ReadWrite,
140}
141
Victor Hsieh45636232021-10-15 17:52:51 -0700142fn create_stat(
143 ino: libc::ino_t,
144 file_size: u64,
145 access_mode: AccessMode,
146) -> io::Result<libc::stat64> {
147 // SAFETY: stat64 is a plan C struct without pointer.
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800148 let mut st = unsafe { MaybeUninit::<libc::stat64>::zeroed().assume_init() };
149
150 st.st_ino = ino;
Victor Hsieh45636232021-10-15 17:52:51 -0700151 st.st_mode = match access_mode {
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800152 // Until needed, let's just grant the owner access.
Victor Hsieh45636232021-10-15 17:52:51 -0700153 // TODO(205169366): Implement mode properly.
154 AccessMode::ReadOnly => libc::S_IFREG | libc::S_IRUSR,
155 AccessMode::ReadWrite => libc::S_IFREG | libc::S_IRUSR | libc::S_IWUSR,
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800156 };
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800157 st.st_nlink = 1;
158 st.st_uid = 0;
159 st.st_gid = 0;
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800160 st.st_size = libc::off64_t::try_from(file_size)
161 .map_err(|_| io::Error::from_raw_os_error(libc::EFBIG))?;
162 st.st_blksize = blk_size();
163 // Per man stat(2), st_blocks is "Number of 512B blocks allocated".
164 st.st_blocks = libc::c_longlong::try_from(divide_roundup(file_size, 512))
165 .map_err(|_| io::Error::from_raw_os_error(libc::EFBIG))?;
166 Ok(st)
167}
168
Victor Hsieh45636232021-10-15 17:52:51 -0700169fn create_dir_stat(ino: libc::ino_t, file_number: u16) -> io::Result<libc::stat64> {
170 // SAFETY: stat64 is a plan C struct without pointer.
171 let mut st = unsafe { MaybeUninit::<libc::stat64>::zeroed().assume_init() };
172
173 st.st_ino = ino;
174 // TODO(205169366): Implement mode properly.
175 st.st_mode = libc::S_IFDIR
176 | libc::S_IXUSR
177 | libc::S_IWUSR
178 | libc::S_IRUSR
179 | libc::S_IXGRP
180 | libc::S_IXOTH;
181
182 // 2 extra for . and ..
183 st.st_nlink = file_number
184 .checked_add(2)
185 .ok_or_else(|| io::Error::from_raw_os_error(libc::EOVERFLOW))?
186 .into();
187
188 st.st_uid = 0;
189 st.st_gid = 0;
190 Ok(st)
191}
192
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800193fn offset_to_chunk_index(offset: u64) -> u64 {
Victor Hsiehda3fbc42021-02-23 16:12:49 -0800194 offset / CHUNK_SIZE
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800195}
196
Victor Hsiehd0bb5d32021-03-19 12:48:03 -0700197fn read_chunks<W: io::Write, T: ReadByChunk>(
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800198 mut w: W,
199 file: &T,
200 file_size: u64,
201 offset: u64,
202 size: u32,
203) -> io::Result<usize> {
204 let remaining = file_size.saturating_sub(offset);
205 let size_to_read = std::cmp::min(size as usize, remaining as usize);
Victor Hsiehac4f3f42021-02-26 12:35:58 -0800206 let total = ChunkedSizeIter::new(size_to_read, offset, CHUNK_SIZE as usize).try_fold(
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800207 0,
208 |total, (current_offset, planned_data_size)| {
209 // TODO(victorhsieh): There might be a non-trivial way to avoid this copy. For example,
210 // instead of accepting a buffer, the writer could expose the final destination buffer
211 // for the reader to write to. It might not be generally applicable though, e.g. with
212 // virtio transport, the buffer may not be continuous.
Victor Hsiehda3fbc42021-02-23 16:12:49 -0800213 let mut buf = [0u8; CHUNK_SIZE as usize];
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800214 let read_size = file.read_chunk(offset_to_chunk_index(current_offset), &mut buf)?;
215 if read_size < planned_data_size {
216 return Err(io::Error::from_raw_os_error(libc::ENODATA));
217 }
218
Victor Hsiehda3fbc42021-02-23 16:12:49 -0800219 let begin = (current_offset % CHUNK_SIZE) as usize;
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800220 let end = begin + planned_data_size;
221 let s = w.write(&buf[begin..end])?;
222 if s != planned_data_size {
223 return Err(io::Error::from_raw_os_error(libc::EIO));
224 }
225 Ok(total + s)
226 },
227 )?;
228
229 Ok(total)
230}
231
232// No need to support enumerating directory entries.
233struct EmptyDirectoryIterator {}
234
235impl DirectoryIterator for EmptyDirectoryIterator {
236 fn next(&mut self) -> Option<DirEntry> {
237 None
238 }
239}
240
241impl FileSystem for AuthFs {
242 type Inode = Inode;
243 type Handle = Handle;
244 type DirIter = EmptyDirectoryIterator;
245
246 fn max_buffer_size(&self) -> u32 {
247 self.max_write
248 }
249
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800250 fn init(&self, _capable: FsOptions) -> io::Result<FsOptions> {
251 // Enable writeback cache for better performance especially since our bandwidth to the
252 // backend service is limited.
253 Ok(FsOptions::WRITEBACK_CACHE)
254 }
255
Victor Hsieh45636232021-10-15 17:52:51 -0700256 fn lookup(&self, _ctx: Context, parent: Inode, name: &CStr) -> io::Result<Entry> {
257 // TODO(victorhsieh): convert root directory (inode == 1) to a readonly directory. Right
258 // now, it's the (global) inode pool, so all inodes can be accessed from root.
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700259 if parent == ROOT_INODE {
Victor Hsieh45636232021-10-15 17:52:51 -0700260 // Only accept file name that looks like an integrer. Files in the pool are simply
261 // exposed by their inode number. Also, there is currently no directory structure.
262 let num = name.to_str().map_err(|_| io::Error::from_raw_os_error(libc::EINVAL))?;
263 // Normally, `lookup` is required to increase a reference count for the inode (while
264 // `forget` will decrease it). It is not necessary here since the files are configured
265 // to be static.
266 let inode =
267 num.parse::<Inode>().map_err(|_| io::Error::from_raw_os_error(libc::ENOENT))?;
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700268 let st = self.handle_inode(&inode, |config| match config {
269 AuthFsEntry::UnverifiedReadonly { file_size, .. }
270 | AuthFsEntry::VerifiedReadonly { file_size, .. } => {
Victor Hsieh45636232021-10-15 17:52:51 -0700271 create_stat(inode, *file_size, AccessMode::ReadOnly)
272 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700273 AuthFsEntry::VerifiedNew { editor } => {
Victor Hsieh45636232021-10-15 17:52:51 -0700274 create_stat(inode, editor.size(), AccessMode::ReadWrite)
275 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700276 AuthFsEntry::VerifiedNewDirectory { dir } => {
Victor Hsieh45636232021-10-15 17:52:51 -0700277 create_dir_stat(inode, dir.number_of_entries())
278 }
279 })?;
280 Ok(Entry {
281 inode,
282 generation: 0,
283 attr: st,
284 entry_timeout: DEFAULT_METADATA_TIMEOUT,
285 attr_timeout: DEFAULT_METADATA_TIMEOUT,
286 })
287 } else {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700288 let inode = self.handle_inode(&parent, |config| match config {
289 AuthFsEntry::VerifiedNewDirectory { dir } => {
Victor Hsieh45636232021-10-15 17:52:51 -0700290 let path: &Path = cstr_to_path(name);
291 dir.find_inode(path).ok_or_else(|| io::Error::from_raw_os_error(libc::ENOENT))
292 }
293 _ => Err(io::Error::from_raw_os_error(libc::ENOTDIR)),
294 })?;
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700295 let st = self.handle_inode(&inode, |config| match config {
296 AuthFsEntry::VerifiedNew { editor } => {
Victor Hsieh45636232021-10-15 17:52:51 -0700297 create_stat(inode, editor.size(), AccessMode::ReadWrite)
298 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700299 AuthFsEntry::VerifiedNewDirectory { dir } => {
Victor Hsieh45636232021-10-15 17:52:51 -0700300 create_dir_stat(inode, dir.number_of_entries())
301 }
302 _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
303 })?;
304 Ok(Entry {
305 inode,
306 generation: 0,
307 attr: st,
308 entry_timeout: DEFAULT_METADATA_TIMEOUT,
309 attr_timeout: DEFAULT_METADATA_TIMEOUT,
310 })
311 }
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800312 }
313
314 fn getattr(
315 &self,
316 _ctx: Context,
317 inode: Inode,
318 _handle: Option<Handle>,
319 ) -> io::Result<(libc::stat64, Duration)> {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700320 self.handle_inode(&inode, |config| {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700321 Ok((
322 match config {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700323 AuthFsEntry::UnverifiedReadonly { file_size, .. }
324 | AuthFsEntry::VerifiedReadonly { file_size, .. } => {
Victor Hsieh45636232021-10-15 17:52:51 -0700325 create_stat(inode, *file_size, AccessMode::ReadOnly)?
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700326 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700327 AuthFsEntry::VerifiedNew { editor } => {
Victor Hsieh45636232021-10-15 17:52:51 -0700328 create_stat(inode, editor.size(), AccessMode::ReadWrite)?
329 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700330 AuthFsEntry::VerifiedNewDirectory { dir } => {
Victor Hsieh45636232021-10-15 17:52:51 -0700331 create_dir_stat(inode, dir.number_of_entries())?
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700332 }
333 },
334 DEFAULT_METADATA_TIMEOUT,
335 ))
336 })
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800337 }
338
339 fn open(
340 &self,
341 _ctx: Context,
342 inode: Self::Inode,
343 flags: u32,
344 ) -> io::Result<(Option<Self::Handle>, fuse::sys::OpenOptions)> {
345 // Since file handle is not really used in later operations (which use Inode directly),
Victor Hsieh09e26262021-03-03 16:00:55 -0800346 // return None as the handle.
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700347 self.handle_inode(&inode, |config| {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700348 match config {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700349 AuthFsEntry::VerifiedReadonly { .. } | AuthFsEntry::UnverifiedReadonly { .. } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700350 check_access_mode(flags, libc::O_RDONLY)?;
351 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700352 AuthFsEntry::VerifiedNew { .. } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700353 // No need to check access modes since all the modes are allowed to the
354 // read-writable file.
355 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700356 AuthFsEntry::VerifiedNewDirectory { .. } => {
Victor Hsieh45636232021-10-15 17:52:51 -0700357 // TODO(victorhsieh): implement when needed.
358 return Err(io::Error::from_raw_os_error(libc::ENOSYS));
359 }
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800360 }
Victor Hsieh45636232021-10-15 17:52:51 -0700361 // Always cache the file content. There is currently no need to support direct I/O or
362 // avoid the cache buffer. Memory mapping is only possible with cache enabled.
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700363 Ok((None, fuse::sys::OpenOptions::KEEP_CACHE))
364 })
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800365 }
366
Victor Hsieh45636232021-10-15 17:52:51 -0700367 fn create(
368 &self,
369 _ctx: Context,
370 parent: Self::Inode,
371 name: &CStr,
372 _mode: u32,
373 _flags: u32,
374 _umask: u32,
375 ) -> io::Result<(Entry, Option<Self::Handle>, fuse::sys::OpenOptions)> {
376 // TODO(205169366): Implement mode properly.
377 // TODO(205172873): handle O_TRUNC and O_EXCL properly.
378 let new_inode = self.insert_new_inode(&parent, |config| match config {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700379 AuthFsEntry::VerifiedNewDirectory { dir } => {
Victor Hsieh45636232021-10-15 17:52:51 -0700380 let basename: &Path = cstr_to_path(name);
381 if dir.find_inode(basename).is_some() {
382 return Err(io::Error::from_raw_os_error(libc::EEXIST));
383 }
384 let (new_inode, new_file) = dir.create_file(basename)?;
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700385 Ok((new_inode, AuthFsEntry::VerifiedNew { editor: new_file }))
Victor Hsieh45636232021-10-15 17:52:51 -0700386 }
387 _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
388 })?;
389
390 Ok((
391 Entry {
392 inode: new_inode,
393 generation: 0,
394 attr: create_stat(new_inode, /* file_size */ 0, AccessMode::ReadWrite)?,
395 entry_timeout: DEFAULT_METADATA_TIMEOUT,
396 attr_timeout: DEFAULT_METADATA_TIMEOUT,
397 },
398 // See also `open`.
399 /* handle */ None,
400 fuse::sys::OpenOptions::KEEP_CACHE,
401 ))
402 }
403
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800404 fn read<W: io::Write + ZeroCopyWriter>(
405 &self,
406 _ctx: Context,
407 inode: Inode,
408 _handle: Handle,
409 w: W,
410 size: u32,
411 offset: u64,
412 _lock_owner: Option<u64>,
413 _flags: u32,
414 ) -> io::Result<usize> {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700415 self.handle_inode(&inode, |config| {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700416 match config {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700417 AuthFsEntry::VerifiedReadonly { reader, file_size } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700418 read_chunks(w, reader, *file_size, offset, size)
419 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700420 AuthFsEntry::UnverifiedReadonly { reader, file_size } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700421 read_chunks(w, reader, *file_size, offset, size)
422 }
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700423 AuthFsEntry::VerifiedNew { editor } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700424 // Note that with FsOptions::WRITEBACK_CACHE, it's possible for the kernel to
425 // request a read even if the file is open with O_WRONLY.
426 read_chunks(w, editor, editor.size(), offset, size)
427 }
Victor Hsieh45636232021-10-15 17:52:51 -0700428 _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800429 }
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700430 })
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800431 }
432
433 fn write<R: io::Read + ZeroCopyReader>(
434 &self,
435 _ctx: Context,
436 inode: Self::Inode,
437 _handle: Self::Handle,
438 mut r: R,
439 size: u32,
440 offset: u64,
441 _lock_owner: Option<u64>,
442 _delayed_write: bool,
443 _flags: u32,
444 ) -> io::Result<usize> {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700445 self.handle_inode(&inode, |config| match config {
446 AuthFsEntry::VerifiedNew { editor } => {
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800447 let mut buf = vec![0; size as usize];
448 r.read_exact(&mut buf)?;
Victor Hsieh1bcf4112021-03-19 14:26:57 -0700449 editor.write_at(&buf, offset)
Victor Hsieh6a47e7f2021-03-03 15:53:49 -0800450 }
451 _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700452 })
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800453 }
Victor Hsieh9d0ab622021-04-26 17:07:02 -0700454
455 fn setattr(
456 &self,
457 _ctx: Context,
458 inode: Inode,
459 attr: libc::stat64,
460 _handle: Option<Handle>,
461 valid: SetattrValid,
462 ) -> io::Result<(libc::stat64, Duration)> {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700463 self.handle_inode(&inode, |config| {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700464 match config {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700465 AuthFsEntry::VerifiedNew { editor } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700466 // Initialize the default stat.
Victor Hsieh45636232021-10-15 17:52:51 -0700467 let mut new_attr = create_stat(inode, editor.size(), AccessMode::ReadWrite)?;
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700468 // `valid` indicates what fields in `attr` are valid. Update to return correctly.
469 if valid.contains(SetattrValid::SIZE) {
470 // st_size is i64, but the cast should be safe since kernel should not give a
471 // negative size.
472 debug_assert!(attr.st_size >= 0);
473 new_attr.st_size = attr.st_size;
474 editor.resize(attr.st_size as u64)?;
475 }
Victor Hsieh9d0ab622021-04-26 17:07:02 -0700476
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700477 if valid.contains(SetattrValid::MODE) {
478 warn!("Changing st_mode is not currently supported");
479 return Err(io::Error::from_raw_os_error(libc::ENOSYS));
480 }
481 if valid.contains(SetattrValid::UID) {
482 warn!("Changing st_uid is not currently supported");
483 return Err(io::Error::from_raw_os_error(libc::ENOSYS));
484 }
485 if valid.contains(SetattrValid::GID) {
486 warn!("Changing st_gid is not currently supported");
487 return Err(io::Error::from_raw_os_error(libc::ENOSYS));
488 }
489 if valid.contains(SetattrValid::CTIME) {
490 debug!(
491 "Ignoring ctime change as authfs does not maintain timestamp currently"
492 );
493 }
494 if valid.intersects(SetattrValid::ATIME | SetattrValid::ATIME_NOW) {
495 debug!(
496 "Ignoring atime change as authfs does not maintain timestamp currently"
497 );
498 }
499 if valid.intersects(SetattrValid::MTIME | SetattrValid::MTIME_NOW) {
500 debug!(
501 "Ignoring mtime change as authfs does not maintain timestamp currently"
502 );
503 }
504 Ok((new_attr, DEFAULT_METADATA_TIMEOUT))
Victor Hsieh9d0ab622021-04-26 17:07:02 -0700505 }
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700506 _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
Victor Hsieh9d0ab622021-04-26 17:07:02 -0700507 }
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700508 })
Victor Hsieh9d0ab622021-04-26 17:07:02 -0700509 }
Victor Hsieh71f10032021-08-13 11:24:02 -0700510
511 fn getxattr(
512 &self,
513 _ctx: Context,
514 inode: Self::Inode,
515 name: &CStr,
516 size: u32,
517 ) -> io::Result<GetxattrReply> {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700518 self.handle_inode(&inode, |config| {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700519 match config {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700520 AuthFsEntry::VerifiedNew { editor } => {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700521 // FUSE ioctl is limited, thus we can't implement fs-verity ioctls without a kernel
522 // change (see b/196635431). Until it's possible, use xattr to expose what we need
523 // as an authfs specific API.
524 if name != CStr::from_bytes_with_nul(b"authfs.fsverity.digest\0").unwrap() {
525 return Err(io::Error::from_raw_os_error(libc::ENODATA));
526 }
Victor Hsieh71f10032021-08-13 11:24:02 -0700527
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700528 if size == 0 {
529 // Per protocol, when size is 0, return the value size.
530 Ok(GetxattrReply::Count(editor.get_fsverity_digest_size() as u32))
Victor Hsieh71f10032021-08-13 11:24:02 -0700531 } else {
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700532 let digest = editor.calculate_fsverity_digest()?;
533 if digest.len() > size as usize {
534 Err(io::Error::from_raw_os_error(libc::ERANGE))
535 } else {
536 Ok(GetxattrReply::Value(digest.to_vec()))
537 }
Victor Hsieh71f10032021-08-13 11:24:02 -0700538 }
539 }
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700540 _ => Err(io::Error::from_raw_os_error(libc::ENODATA)),
Victor Hsieh71f10032021-08-13 11:24:02 -0700541 }
Victor Hsiehc85e4ef2021-10-18 15:28:53 -0700542 })
Victor Hsieh71f10032021-08-13 11:24:02 -0700543 }
Victor Hsieh45636232021-10-15 17:52:51 -0700544
545 fn mkdir(
546 &self,
547 _ctx: Context,
548 parent: Self::Inode,
549 name: &CStr,
550 _mode: u32,
551 _umask: u32,
552 ) -> io::Result<Entry> {
553 // TODO(205169366): Implement mode properly.
554 let new_inode = self.insert_new_inode(&parent, |config| match config {
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700555 AuthFsEntry::VerifiedNewDirectory { dir } => {
Victor Hsieh45636232021-10-15 17:52:51 -0700556 let basename: &Path = cstr_to_path(name);
557 if dir.find_inode(basename).is_some() {
558 return Err(io::Error::from_raw_os_error(libc::EEXIST));
559 }
560 let (new_inode, new_dir) = dir.mkdir(basename)?;
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700561 Ok((new_inode, AuthFsEntry::VerifiedNewDirectory { dir: new_dir }))
Victor Hsieh45636232021-10-15 17:52:51 -0700562 }
563 _ => Err(io::Error::from_raw_os_error(libc::EBADF)),
564 })?;
565
566 Ok(Entry {
567 inode: new_inode,
568 generation: 0,
569 attr: create_dir_stat(new_inode, /* file_number */ 0)?,
570 entry_timeout: DEFAULT_METADATA_TIMEOUT,
571 attr_timeout: DEFAULT_METADATA_TIMEOUT,
572 })
573 }
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800574}
575
576/// Mount and start the FUSE instance. This requires CAP_SYS_ADMIN.
577pub fn loop_forever(
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700578 root_entries: BTreeMap<Inode, AuthFsEntry>,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800579 mountpoint: &Path,
Victor Hsieh4cc3b792021-08-04 12:00:04 -0700580 extra_options: &Option<String>,
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800581) -> Result<(), fuse::Error> {
582 let max_read: u32 = 65536;
583 let max_write: u32 = 65536;
584 let dev_fuse = OpenOptions::new()
585 .read(true)
586 .write(true)
587 .open("/dev/fuse")
588 .expect("Failed to open /dev/fuse");
589
Victor Hsieh4cc3b792021-08-04 12:00:04 -0700590 let mut mount_options = vec![
591 MountOption::FD(dev_fuse.as_raw_fd()),
592 MountOption::RootMode(libc::S_IFDIR | libc::S_IXUSR | libc::S_IXGRP | libc::S_IXOTH),
593 MountOption::AllowOther,
594 MountOption::UserId(0),
595 MountOption::GroupId(0),
596 MountOption::MaxRead(max_read),
597 ];
598 if let Some(value) = extra_options {
599 mount_options.push(MountOption::Extra(value));
600 }
601
602 fuse::mount(mountpoint, "authfs", libc::MS_NOSUID | libc::MS_NODEV, &mount_options)
603 .expect("Failed to mount fuse");
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800604
605 fuse::worker::start_message_loop(
606 dev_fuse,
607 max_write,
608 max_read,
Victor Hsieh26cea2f2021-11-03 10:28:33 -0700609 AuthFs::new(Arc::new(Mutex::new(root_entries)), max_write),
Victor Hsieh88ac6ca2020-11-13 15:20:24 -0800610 )
611}
Victor Hsieh45636232021-10-15 17:52:51 -0700612
613fn cstr_to_path(cstr: &CStr) -> &Path {
614 OsStr::from_bytes(cstr.to_bytes()).as_ref()
615}