diff --git a/Cargo.lock b/Cargo.lock index e41d6c8010..b7ce0329b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3725,6 +3725,7 @@ dependencies = [ "prost", "protobuf-src", "spfs", + "thiserror", "tokio", "tonic", "tonic-build", diff --git a/crates/spfs-vfs/Cargo.toml b/crates/spfs-vfs/Cargo.toml index 2872713439..0ecf2014df 100644 --- a/crates/spfs-vfs/Cargo.toml +++ b/crates/spfs-vfs/Cargo.toml @@ -35,6 +35,7 @@ libc = "0.2" miette = { workspace = true, features = ["fancy"] } prost = { workspace = true, optional = true } spfs = { workspace = true } +thiserror = { workspace = true } tokio = { version = "1.20", features = ["rt", "rt-multi-thread"] } tracing = { workspace = true } tonic = { workspace = true, optional = true } diff --git a/crates/spfs-vfs/src/error.rs b/crates/spfs-vfs/src/error.rs new file mode 100644 index 0000000000..9ed6f33f90 --- /dev/null +++ b/crates/spfs-vfs/src/error.rs @@ -0,0 +1,13 @@ +// Copyright (c) Sony Pictures Imageworks, et al. +// SPDX-License-Identifier: Apache-2.0 +// https://github.com/imageworks/spk + +use thiserror::Error; + +/// Errors specific to fuse operations. +#[derive(Debug, Error)] +pub enum Error { + /// An operation was attempted on a mask entry. + #[error("Entry is a mask")] + EntryIsMask, +} diff --git a/crates/spfs-vfs/src/fuse.rs b/crates/spfs-vfs/src/fuse.rs index 82a0471499..49f54d5b83 100644 --- a/crates/spfs-vfs/src/fuse.rs +++ b/crates/spfs-vfs/src/fuse.rs @@ -34,6 +34,10 @@ use spfs::tracking::{Entry, EntryKind, EnvSpec, Manifest}; use spfs::OsError; use tokio::io::AsyncReadExt; +use crate::Error; + +type Result = std::result::Result; + /// Options to configure the FUSE filesystem and /// its behavior at runtime #[derive(Debug, Clone)] @@ -157,12 +161,12 @@ impl Filesystem { } } - fn attr_from_entry(&self, entry: &Entry) -> FileAttr { + fn attr_from_entry(&self, entry: &Entry) -> Result { let kind = match entry.kind { EntryKind::Blob(_) if entry.is_symlink() => FileType::Symlink, EntryKind::Blob(_) => FileType::RegularFile, EntryKind::Tree => FileType::Directory, - EntryKind::Mask => unreachable!(), + EntryKind::Mask => return Err(Error::EntryIsMask), }; let size = if entry.is_dir() { entry.entries.len() as u64 @@ -179,7 +183,7 @@ impl Filesystem { 1 }; - FileAttr { + Ok(FileAttr { ino: entry.user_data, size, perm: entry.mode as u16, // truncate the non-perm bits @@ -197,7 +201,7 @@ impl Filesystem { rdev: 0, blksize: Self::BLOCK_SIZE, flags: 0, - } + }) } } @@ -281,7 +285,10 @@ impl Filesystem { return; }; - let attr = self.attr_from_entry(entry); + let Ok(attr) = self.attr_from_entry(entry) else { + reply.error(libc::ENOENT); + return; + }; reply.entry(&self.ttl, &attr, 0); } @@ -296,7 +303,10 @@ impl Filesystem { return; }; - let attr = self.attr_from_entry(inode.value()); + let Ok(attr) = self.attr_from_entry(inode.value()) else { + reply.error(libc::ENOENT); + return; + }; reply.attr(&self.ttl, &attr); } @@ -595,7 +605,9 @@ impl Filesystem { for (name, entry) in remaining { let ino = entry.user_data; let next_offset = ino as i64; - let attr = self.attr_from_entry(entry); + let Ok(attr) = self.attr_from_entry(entry) else { + continue; + }; tracing::trace!("readdirplus add {name}"); let buffer_full = reply.add(ino, next_offset, name, &self.ttl, &attr, 0); if buffer_full { diff --git a/crates/spfs-vfs/src/lib.rs b/crates/spfs-vfs/src/lib.rs index c11cbd3c17..ce85da322b 100644 --- a/crates/spfs-vfs/src/lib.rs +++ b/crates/spfs-vfs/src/lib.rs @@ -9,6 +9,9 @@ #![deny(missing_docs)] +mod error; +pub use error::Error; + #[cfg(all(unix, feature = "fuse-backend"))] mod fuse; #[cfg(all(windows, feature = "winfsp-backend"))]