summary refs log tree commit diff
path: root/devices
diff options
context:
space:
mode:
authorChirantan Ekbote <chirantan@chromium.org>2020-03-17 17:51:19 +0900
committerCommit Bot <commit-bot@chromium.org>2020-03-19 08:13:29 +0000
commita07d84ad6876197368ed23b641c2400a44809e69 (patch)
treec3de9b9cb0017315ad7777f9b198b4b05d2317d0 /devices
parent6d2a83482737cac0e5777a00e27d34e704eedb71 (diff)
downloadcrosvm-a07d84ad6876197368ed23b641c2400a44809e69.tar
crosvm-a07d84ad6876197368ed23b641c2400a44809e69.tar.gz
crosvm-a07d84ad6876197368ed23b641c2400a44809e69.tar.bz2
crosvm-a07d84ad6876197368ed23b641c2400a44809e69.tar.lz
crosvm-a07d84ad6876197368ed23b641c2400a44809e69.tar.xz
crosvm-a07d84ad6876197368ed23b641c2400a44809e69.tar.zst
crosvm-a07d84ad6876197368ed23b641c2400a44809e69.zip
devices: fs: Add support for fuse minor version 28
BUG=b:150264964
TEST=vm.Virtiofs

Change-Id: I544329b63352956647d07aefdfce3118947d0821
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2105820
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Chirantan Ekbote <chirantan@chromium.org>
Diffstat (limited to 'devices')
-rw-r--r--devices/src/virtio/fs/filesystem.rs30
-rw-r--r--devices/src/virtio/fs/fuse.rs43
-rw-r--r--devices/src/virtio/fs/server.rs37
3 files changed, 107 insertions, 3 deletions
diff --git a/devices/src/virtio/fs/filesystem.rs b/devices/src/virtio/fs/filesystem.rs
index 232ff99..eb9726c 100644
--- a/devices/src/virtio/fs/filesystem.rs
+++ b/devices/src/virtio/fs/filesystem.rs
@@ -1139,4 +1139,34 @@ pub trait FileSystem {
     fn lseek(&self) -> io::Result<()> {
         Err(io::Error::from_raw_os_error(libc::ENOSYS))
     }
+
+    /// Copy a range of data from one file to another
+    ///
+    /// Performs an optimized copy between two file descriptors without the additional cost of
+    /// transferring data through the kernel module to user space (glibc) and then back into
+    /// the file system again.
+    ///
+    /// In case this method is not implemented, glibc falls back to reading data from the source and
+    /// writing to the destination.
+    ///
+    /// If this method fails with an `ENOSYS` error, then the kernel will treat that as a permanent
+    /// failure. The kernel will return `EOPNOTSUPP` for all future calls to `copy_file_range`
+    /// without forwarding them to the file system.
+    ///
+    /// All values accepted by the `copy_file_range(2)` system call are valid values for `flags` and
+    /// must be handled by the file system.
+    fn copy_file_range(
+        &self,
+        ctx: Context,
+        inode_src: Self::Inode,
+        handle_src: Self::Handle,
+        offset_src: u64,
+        inode_dst: Self::Inode,
+        handle_dst: Self::Handle,
+        offset_dst: u64,
+        length: u64,
+        flags: u64,
+    ) -> io::Result<usize> {
+        Err(io::Error::from_raw_os_error(libc::ENOSYS))
+    }
 }
diff --git a/devices/src/virtio/fs/fuse.rs b/devices/src/virtio/fs/fuse.rs
index 612202b..0533a5c 100644
--- a/devices/src/virtio/fs/fuse.rs
+++ b/devices/src/virtio/fs/fuse.rs
@@ -12,8 +12,11 @@ use libc;
 /// Version number of this interface.
 pub const KERNEL_VERSION: u32 = 7;
 
+/// Oldest supported minor version of the fuse interface.
+pub const OLDEST_SUPPORTED_KERNEL_MINOR_VERSION: u32 = 27;
+
 /// Minor version number of this interface.
-pub const KERNEL_MINOR_VERSION: u32 = 27;
+pub const KERNEL_MINOR_VERSION: u32 = 28;
 
 /// The ID of the inode corresponding to the root directory of the file system.
 pub const ROOT_ID: u64 = 1;
@@ -56,6 +59,12 @@ const FOPEN_KEEP_CACHE: u32 = 2;
 /// The file is not seekable.
 const FOPEN_NONSEEKABLE: u32 = 4;
 
+/// Allow caching the directory entries.
+const FOPEN_CACHE_DIR: u32 = 8;
+
+/// This file is stream-like (i.e., no file position).
+const FOPEN_STREAM: u32 = 16;
+
 bitflags! {
     /// Options controlling the behavior of files opened by the server in response
     /// to an open or create request.
@@ -63,6 +72,8 @@ bitflags! {
         const DIRECT_IO = FOPEN_DIRECT_IO;
         const KEEP_CACHE = FOPEN_KEEP_CACHE;
         const NONSEEKABLE = FOPEN_NONSEEKABLE;
+        const CACHE_DIR = FOPEN_CACHE_DIR;
+        const STREAM = FOPEN_STREAM;
     }
 }
 
@@ -131,6 +142,15 @@ const HANDLE_KILLPRIV: u32 = 524288;
 /// FileSystem supports posix acls.
 const POSIX_ACL: u32 = 1048576;
 
+/// Reading the device after an abort returns `ECONNABORTED`.
+const ABORT_ERROR: u32 = 2097152;
+
+/// The reply to the `init` message contains the max number of request pages.
+const MAX_PAGES: u32 = 4194304;
+
+/// Cache `readlink` responses.
+const CACHE_SYMLINKS: u32 = 8388608;
+
 bitflags! {
     /// A bitfield passed in as a parameter to and returned from the `init` method of the
     /// `FileSystem` trait.
@@ -297,6 +317,9 @@ bitflags! {
         ///
         /// This feature is disabled by default.
         const POSIX_ACL = POSIX_ACL;
+
+        /// Indicates that the kernel may cache responses to `readlink` calls.
+        const CACHE_SYMLINKS = CACHE_SYMLINKS;
     }
 }
 
@@ -511,6 +534,7 @@ pub enum Opcode {
     Readdirplus = 44,
     Rename2 = 45,
     Lseek = 46,
+    CopyFileRange = 47,
 }
 
 #[repr(u32)]
@@ -830,7 +854,9 @@ pub struct InitOut {
     pub congestion_threshold: u16,
     pub max_write: u32,
     pub time_gran: u32,
-    pub unused: [u32; 9],
+    pub max_pages: u16,
+    pub padding: u16,
+    pub unused: [u32; 8],
 }
 unsafe impl DataInit for InitOut {}
 
@@ -1049,3 +1075,16 @@ pub struct LseekOut {
     pub offset: u64,
 }
 unsafe impl DataInit for LseekOut {}
+
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct CopyFileRangeIn {
+    pub fh_src: u64,
+    pub off_src: u64,
+    pub nodeid_dst: u64,
+    pub fh_dst: u64,
+    pub off_dst: u64,
+    pub len: u64,
+    pub flags: u64,
+}
+unsafe impl DataInit for CopyFileRangeIn {}
diff --git a/devices/src/virtio/fs/server.rs b/devices/src/virtio/fs/server.rs
index 32b5008..76f2830 100644
--- a/devices/src/virtio/fs/server.rs
+++ b/devices/src/virtio/fs/server.rs
@@ -118,6 +118,7 @@ impl<F: FileSystem + Sync> Server<F> {
             Some(Opcode::Readdirplus) => self.readdirplus(in_header, r, w),
             Some(Opcode::Rename2) => self.rename2(in_header, r, w),
             Some(Opcode::Lseek) => self.lseek(in_header, r, w),
+            Some(Opcode::CopyFileRange) => self.copy_file_range(in_header, r, w),
             None => reply_error(
                 io::Error::from_raw_os_error(libc::ENOSYS),
                 in_header.unique,
@@ -809,7 +810,7 @@ impl<F: FileSystem + Sync> Server<F> {
             return reply_ok(Some(out), None, in_header.unique, w);
         }
 
-        if minor < KERNEL_MINOR_VERSION {
+        if minor < OLDEST_SUPPORTED_KERNEL_MINOR_VERSION {
             error!(
                 "Unsupported fuse protocol minor version: {}.{}",
                 major, minor
@@ -1208,6 +1209,40 @@ impl<F: FileSystem + Sync> Server<F> {
             Ok(0)
         }
     }
+
+    fn copy_file_range(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result<usize> {
+        let CopyFileRangeIn {
+            fh_src,
+            off_src,
+            nodeid_dst,
+            fh_dst,
+            off_dst,
+            len,
+            flags,
+        } = r.read_obj().map_err(Error::DecodeMessage)?;
+
+        match self.fs.copy_file_range(
+            Context::from(in_header),
+            in_header.nodeid.into(),
+            fh_src.into(),
+            off_src,
+            nodeid_dst.into(),
+            fh_dst.into(),
+            off_dst,
+            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 retry_ioctl(