diff --git a/bin/ofs/src/fuse.rs b/bin/ofs/src/fuse.rs index 40df1b041e77..c8bb50dbc6fd 100644 --- a/bin/ofs/src/fuse.rs +++ b/bin/ofs/src/fuse.rs @@ -20,10 +20,12 @@ use std::ffi::OsString; use std::num::NonZeroU32; use std::ops::Deref; use std::path::PathBuf; +use std::sync::RwLock; use std::time::Duration; use std::time::SystemTime; use bytes::Bytes; + use fuse3::path::prelude::*; use fuse3::Errno; use fuse3::Result; @@ -46,6 +48,7 @@ struct OpenedFile { is_read: bool, is_write: bool, is_append: bool, + offset: u64, } #[derive(Debug, Clone, Copy)] @@ -72,7 +75,7 @@ pub(super) struct Fuse { op: Operator, gid: u32, uid: u32, - opened_files: Slab, + opened_files: Slab>, } impl Fuse { @@ -107,13 +110,18 @@ impl Fuse { // Get opened file and check given path fn get_opened_file(&self, key: FileKey, path: Option<&OsStr>) -> Result { - let file = self + let file = match self .opened_files .get(key.0) .as_ref() .ok_or(Errno::from(libc::ENOENT))? .deref() - .clone(); + .read() + { + Ok(file) => file.clone(), + Err(_) => Err(Errno::from(libc::EBADF))?, + }; + if matches!(path, Some(path) if path != file.path) { log::trace!( "get_opened_file: path not match: path={:?}, file={:?}", @@ -125,6 +133,37 @@ impl Fuse { Ok(file) } + + // Set opened file offset + fn set_opened_file_offset( + &self, + key: FileKey, + path: Option<&OsStr>, + offset: u64, + ) -> Result<()> { + let binding = self.opened_files.get(key.0); + let mut file = match binding + .as_ref() + .ok_or(Errno::from(libc::ENOENT))? + .deref() + .write() + { + Ok(file) => file, + Err(_) => Err(Errno::from(libc::EBADF))?, + }; + if matches!(path, Some(path) if path != file.path) { + log::trace!( + "set_opened_file: path not match: path={:?}, file={:?}", + path, + file.path + ); + Err(Errno::from(libc::EBADF))?; + } + + file.offset = offset; + + Ok(()) + } } impl PathFilesystem for Fuse { @@ -170,6 +209,7 @@ impl PathFilesystem for Fuse { self.opened_files .get(FileKey::try_from(fh).ok()?.0) .as_ref() + .and_then(|f| f.read().ok()) .map(|f| f.path.clone()) }); @@ -208,7 +248,8 @@ impl PathFilesystem for Fuse { fh, set_attr ); - Err(libc::EOPNOTSUPP.into()) + + self.getattr(_req, path, fh, 0).await } async fn symlink( @@ -313,6 +354,10 @@ impl PathFilesystem for Fuse { name ); + if !self.op.info().full_capability().rename { + return Err(Errno::from(libc::ENOTSUP))?; + } + let origin_path = PathBuf::from(origin_parent).join(origin_name); let path = PathBuf::from(parent).join(name); @@ -369,12 +414,13 @@ impl PathFilesystem for Fuse { let key = self .opened_files - .insert(OpenedFile { + .insert(RwLock::new(OpenedFile { path: path.into(), is_read, is_write, is_append: flags & libc::O_APPEND as u32 != 0, - }) + offset: 0, + })) .ok_or(Errno::from(libc::EBUSY))?; Ok(ReplyCreated { @@ -407,6 +453,7 @@ impl PathFilesystem for Fuse { let file = self .opened_files .take(FileKey::try_from(fh)?.0) + .map(|f| f.read().unwrap().clone()) .ok_or(Errno::from(libc::EBADF))?; if matches!(path, Some(ref p) if p != &file.path) { Err(Errno::from(libc::EBADF))?; @@ -422,12 +469,13 @@ impl PathFilesystem for Fuse { let key = self .opened_files - .insert(OpenedFile { + .insert(RwLock::new(OpenedFile { path: path.into(), is_read, is_write, is_append: flags & libc::O_APPEND as u32 != 0, - }) + offset: 0, + })) .ok_or(Errno::from(libc::EBUSY))?; Ok(ReplyOpen { @@ -465,6 +513,8 @@ impl PathFilesystem for Fuse { .await .map_err(opendal_error2errno)?; + self.set_opened_file_offset(FileKey::try_from(fh)?, path, offset + data.len() as u64)?; + Ok(ReplyData { data: data.into() }) } @@ -505,6 +555,8 @@ impl PathFilesystem for Fuse { .await .map_err(opendal_error2errno)?; + self.set_opened_file_offset(FileKey::try_from(fh)?, path, offset + data.len() as u64)?; + Ok(ReplyWrite { written: data.len() as _, }) @@ -638,6 +690,109 @@ impl PathFilesystem for Fuse { entries: relative_paths.chain(children).skip(offset as usize).boxed(), }) } + async fn rename2( + &self, + req: Request, + origin_parent: &OsStr, + origin_name: &OsStr, + parent: &OsStr, + name: &OsStr, + _flags: u32, + ) -> Result<()> { + log::debug!( + "rename2(origin_parent={:?}, origin_name={:?}, parent={:?}, name={:?})", + origin_parent, + origin_name, + parent, + name + ); + self.rename(req, origin_parent, origin_name, parent, name) + .await + } + + async fn lseek( + &self, + _req: Request, + path: Option<&OsStr>, + fh: u64, + offset: u64, + whence: u32, + ) -> Result { + log::debug!( + "lseek(path={:?}, fh={}, offset={}, whence={})", + path, + fh, + offset, + whence + ); + + let whence = whence as i32; + + let file = self.get_opened_file(FileKey::try_from(fh)?, path)?; + + if !file.is_read && !file.is_write { + Err(Errno::from(libc::EACCES))?; + } + + let offset = if whence == libc::SEEK_SET { + offset + } else if whence == libc::SEEK_CUR { + file.offset + offset + } else if whence == libc::SEEK_END { + let metadata = self + .op + .stat(&path.unwrap().to_string_lossy()) + .await + .map_err(opendal_error2errno)?; + let content_size = metadata.content_length(); + + if content_size >= offset as _ { + content_size as u64 - offset + } else { + 0 + } + } else { + return Err(libc::ENOSYS.into()); + }; + + Ok(ReplyLSeek { offset }) + } + + async fn copy_file_range( + &self, + req: Request, + from_path: Option<&OsStr>, + fh_in: u64, + offset_in: u64, + to_path: Option<&OsStr>, + fh_out: u64, + offset_out: u64, + length: u64, + flags: u64, + ) -> Result { + log::debug!( + "copy_file_range(from_path={:?}, fh_in={}, offset_in={}, to_path={:?}, fh_out={}, offset_out={}, length={}, flags={})", + from_path, + fh_in, + offset_in, + to_path, + fh_out, + offset_out, + length, + flags + ); + let data = self + .read(req, from_path, fh_in, offset_in, length as _) + .await?; + + let ReplyWrite { written } = self + .write(req, to_path, fh_out, offset_out, &data.data, 0, flags as _) + .await?; + + Ok(ReplyCopyFileRange { + copied: u64::from(written), + }) + } } const fn entry_mode2file_type(mode: EntryMode) -> FileType {