From 97e2d5d2668b2533c09e787ce994e4acdaf13286 Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Fri, 20 Mar 2020 14:08:26 +0100 Subject: [PATCH] vhost_user_fs: Add support for CopyFileRange Add support for the CopyFileRange request, introduced in FUSE 7.28. Signed-off-by: Sergio Lopez --- vhost_user_fs/src/filesystem.rs | 16 +++++++++ vhost_user_fs/src/fuse.rs | 13 ++++++++ vhost_user_fs/src/passthrough.rs | 56 ++++++++++++++++++++++++++++++++ vhost_user_fs/src/server.rs | 36 ++++++++++++++++++++ 4 files changed, 121 insertions(+) diff --git a/vhost_user_fs/src/filesystem.rs b/vhost_user_fs/src/filesystem.rs index c755113b9..0f3104680 100644 --- a/vhost_user_fs/src/filesystem.rs +++ b/vhost_user_fs/src/filesystem.rs @@ -1096,6 +1096,22 @@ pub trait FileSystem { Err(io::Error::from_raw_os_error(libc::ENOSYS)) } + #[allow(clippy::too_many_arguments)] + fn copyfilerange( + &self, + ctx: Context, + inode_in: Self::Inode, + handle_in: Self::Handle, + offset_in: u64, + inode_out: Self::Inode, + handle_out: Self::Handle, + offset_out: u64, + len: u64, + flags: u64, + ) -> io::Result { + Err(io::Error::from_raw_os_error(libc::ENOSYS)) + } + /// TODO: support this fn getlk(&self) -> io::Result<()> { Err(io::Error::from_raw_os_error(libc::ENOSYS)) diff --git a/vhost_user_fs/src/fuse.rs b/vhost_user_fs/src/fuse.rs index d7c47ef27..897e3cfbc 100644 --- a/vhost_user_fs/src/fuse.rs +++ b/vhost_user_fs/src/fuse.rs @@ -1049,6 +1049,19 @@ pub struct LseekOut { } unsafe impl ByteValued for LseekOut {} +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct CopyfilerangeIn { + pub fh_in: u64, + pub off_in: u64, + pub nodeid_out: u64, + pub fh_out: u64, + pub off_out: u64, + pub len: u64, + pub flags: u64, +} +unsafe impl ByteValued for CopyfilerangeIn {} + bitflags! { pub struct SetupmappingFlags: u64 { const WRITE = 0x1; diff --git a/vhost_user_fs/src/passthrough.rs b/vhost_user_fs/src/passthrough.rs index 83b7e300b..2a1456cd2 100644 --- a/vhost_user_fs/src/passthrough.rs +++ b/vhost_user_fs/src/passthrough.rs @@ -4,6 +4,7 @@ use std::collections::btree_map; use std::collections::BTreeMap; +use std::convert::TryInto; use std::ffi::{CStr, CString}; use std::fs::File; use std::io; @@ -1644,4 +1645,59 @@ impl FileSystem for PassthroughFs { Ok(res as u64) } } + + fn copyfilerange( + &self, + _ctx: Context, + inode_in: Inode, + handle_in: Handle, + offset_in: u64, + inode_out: Inode, + handle_out: Handle, + offset_out: u64, + len: u64, + flags: u64, + ) -> io::Result { + let data_in = self + .handles + .read() + .unwrap() + .get(&handle_in) + .filter(|hd| hd.inode == inode_in) + .map(Arc::clone) + .ok_or_else(ebadf)?; + + // Take just a read lock as we're not going to alter the file descriptor offset. + let fd_in = data_in.file.read().unwrap().as_raw_fd(); + + let data_out = self + .handles + .read() + .unwrap() + .get(&handle_out) + .filter(|hd| hd.inode == inode_out) + .map(Arc::clone) + .ok_or_else(ebadf)?; + + // Take just a read lock as we're not going to alter the file descriptor offset. + let fd_out = data_out.file.read().unwrap().as_raw_fd(); + + // Safe because this will only modify `offset_in` and `offset_out` and we check + // the return value. + let res = unsafe { + libc::copy_file_range( + fd_in, + &mut (offset_in as i64) as &mut _ as *mut _, + fd_out, + &mut (offset_out as i64) as &mut _ as *mut _, + len.try_into().unwrap(), + flags.try_into().unwrap(), + ) + }; + if res < 0 { + Err(io::Error::last_os_error()) + } else { + Ok(res as usize) + } + } } diff --git a/vhost_user_fs/src/server.rs b/vhost_user_fs/src/server.rs index d07baf82a..4c2242140 100644 --- a/vhost_user_fs/src/server.rs +++ b/vhost_user_fs/src/server.rs @@ -124,6 +124,7 @@ impl Server { x if x == Opcode::Readdirplus as u32 => self.readdirplus(in_header, r, w), x if x == Opcode::Rename2 as u32 => self.rename2(in_header, r, w), x if x == Opcode::Lseek as u32 => self.lseek(in_header, r, w), + x if x == Opcode::CopyFileRange as u32 => self.copyfilerange(in_header, r, w), x if x == Opcode::SetupMapping as u32 => self.setupmapping(in_header, r, w, vu_req), x if x == Opcode::RemoveMapping as u32 => self.removemapping(in_header, r, w, vu_req), _ => reply_error( @@ -1261,6 +1262,41 @@ impl Server { Err(e) => reply_error(e, in_header.unique, w), } } + + fn copyfilerange(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { + let CopyfilerangeIn { + fh_in, + off_in, + nodeid_out, + fh_out, + off_out, + len, + flags, + .. + } = r.read_obj().map_err(Error::DecodeMessage)?; + + match self.fs.copyfilerange( + Context::from(in_header), + in_header.nodeid.into(), + fh_in.into(), + off_in, + nodeid_out.into(), + fh_out.into(), + off_out, + len, + flags, + ) { + Ok(count) => { + let out = WriteOut { + size: count as u32, + ..Default::default() + }; + + reply_ok(Some(out), None, in_header.unique, w) + } + Err(e) => reply_error(e, in_header.unique, w), + } + } } fn reply_ok(