From 76af01b78eee732a3bdf668923d14b8ae02d88c9 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Wed, 7 Apr 2021 09:39:44 +0800 Subject: [PATCH 01/55] machine_manager: add configuration information for virtio fs Signed-off-by: Fei Xu --- machine_manager/src/cmdline.rs | 9 +++ machine_manager/src/config/fs.rs | 124 ++++++++++++++++++++++++++++++ machine_manager/src/config/mod.rs | 10 +++ 3 files changed, 143 insertions(+) diff --git a/machine_manager/src/cmdline.rs b/machine_manager/src/cmdline.rs index a80bfc7c9..fe6d272c2 100644 --- a/machine_manager/src/cmdline.rs +++ b/machine_manager/src/cmdline.rs @@ -157,6 +157,14 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .help("use 'file' as a drive image") .takes_values(true), ) + .arg( + Arg::with_name("fs") + .multiple(true) + .long("fs") + .value_name("fs[,tag=str][,sock=socket_path]") + .help("config a virtio fs with tag 'str'") + .takes_values(true), + ) .arg( Arg::with_name("netdev") .multiple(true) @@ -304,6 +312,7 @@ pub fn create_vmconfig(args: &ArgMatches) -> Result { update_args_to_config_multi!((args.values_of("netdev")), vm_cfg, update_net); update_args_to_config_multi!((args.values_of("chardev")), vm_cfg, update_console); update_args_to_config_multi!((args.values_of("iothread")), vm_cfg, update_iothread); + update_args_to_config_multi!((args.values_of("fs")), vm_cfg, update_fs); // Check the mini-set for Vm to start is ok vm_cfg diff --git a/machine_manager/src/config/fs.rs b/machine_manager/src/config/fs.rs index 4466fbfb1..18f58ae5c 100644 --- a/machine_manager/src/config/fs.rs +++ b/machine_manager/src/config/fs.rs @@ -22,6 +22,7 @@ const MAX_STRING_LENGTH: usize = 255; const MAX_PATH_LENGTH: usize = 4096; const MAX_SERIAL_NUM: usize = 20; const MAX_IOPS: u64 = 1_000_000; +pub const MAX_TAG_LENGTH: usize = 36; /// Config struct for `drive`. /// Contains block device's attr. @@ -170,6 +171,101 @@ impl VmConfig { } } +/// Config struct for `fs`. +/// Contains fs device's attr. +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct FsConfig { + pub tag: String, + pub sock: String, +} + +impl FsConfig { + /// Create `FsConfig` from `Value` structure. + /// + /// # Arguments + /// + /// * `Value` - structure can be gotten by `json_file`. + pub fn from_value(value: &serde_json::Value) -> Result> { + let ret = serde_json::from_value(value.clone())?; + Ok(ret) + } +} + +impl Default for FsConfig { + fn default() -> Self { + FsConfig { + tag: "".to_string(), + sock: "".to_string(), + } + } +} + +impl ConfigCheck for FsConfig { + fn check(&self) -> Result<()> { + if self.tag.len() >= MAX_TAG_LENGTH { + return Err(ErrorKind::StringLengthTooLong( + "fs device tag".to_string(), + MAX_TAG_LENGTH - 1, + ) + .into()); + } + + if self.sock.len() > MAX_PATH_LENGTH { + return Err(ErrorKind::StringLengthTooLong( + "fs sock path".to_string(), + MAX_PATH_LENGTH, + ) + .into()); + } + + Ok(()) + } +} + +impl VmConfig { + /// Add new fs device to `VmConfig`. + fn add_fs(&mut self, fs_cfg: FsConfig) -> Result<()> { + if let Some(mut fs) = self.fs.clone() { + for exist_cfg in &fs { + if exist_cfg.tag == fs_cfg.tag { + bail!("The tag {} for virtio fs is existed", exist_cfg.tag); + } + + if exist_cfg.sock == fs_cfg.sock { + bail!("The sock {} for virtio fs is existed", exist_cfg.tag); + } + } + fs.push(fs_cfg); + self.fs = Some(fs); + } else { + let mut fs: Vec = Vec::new(); + fs.push(fs_cfg); + self.fs = Some(fs); + } + + Ok(()) + } + + /// Update '-fs ...' fs config to `VmConfig`. + pub fn update_fs(&mut self, fs_config: &str) -> Result<()> { + let mut cmd_parser = CmdParser::new("fs"); + cmd_parser.push("tag").push("sock"); + + cmd_parser.parse(fs_config)?; + + let mut fs_cfg = FsConfig::default(); + if let Some(tag) = cmd_parser.get_value::("tag")? { + fs_cfg.tag = tag; + } + if let Some(sock) = cmd_parser.get_value::("sock")? { + fs_cfg.sock = sock; + } + + self.add_fs(fs_cfg) + } +} + #[cfg(test)] mod tests { use super::*; @@ -228,4 +324,32 @@ mod tests { drive_configs[0].serial_num = Some(String::from("222222222222222222222")); assert!(drive_configs[0].check().is_err()); } + + #[test] + fn test_fs_config_json_parser() { + let json = r#" + [{ + "tag": "test", + "sock": "fs.sock" + }] + "#; + let value = serde_json::from_str(json).unwrap(); + let configs = FsConfig::from_value(&value); + assert!(configs.is_some()); + let fs_configs = configs.unwrap(); + assert_eq!(fs_configs[0].tag, "test"); + assert_eq!(fs_configs[0].sock, "fs.sock"); + } + + #[test] + fn test_fs_config_cmdline_parser() { + let mut vm_config = VmConfig::default(); + assert!(vm_config.update_fs("tag=test,sock=fs.sock").is_ok()); + let configs = vm_config.fs.clone(); + assert!(configs.is_some()); + let fs_configs = configs.unwrap(); + assert_eq!(fs_configs[0].tag, "test"); + assert_eq!(fs_configs[0].sock, "fs.sock"); + assert!(fs_configs[0].check().is_ok()); + } } diff --git a/machine_manager/src/config/mod.rs b/machine_manager/src/config/mod.rs index 7d940c66b..0ed873769 100644 --- a/machine_manager/src/config/mod.rs +++ b/machine_manager/src/config/mod.rs @@ -109,6 +109,7 @@ pub struct VmConfig { pub drives: Option>, pub nets: Option>, pub consoles: Option>, + pub fs: Option>, pub vsock: Option, pub serial: Option, pub iothreads: Option>, @@ -127,6 +128,7 @@ impl VmConfig { let mut drives = None; let mut nets = None; let mut consoles = None; + let mut fs = None; let mut vsock = None; let mut serial = None; let mut iothreads = None; @@ -140,6 +142,7 @@ impl VmConfig { "drive" => drives = Some(DriveConfig::from_value(&item)?), "net" => nets = Some(NetworkInterfaceConfig::from_value(&item)?), "console" => consoles = Some(ConsoleConfig::from_value(&item)?), + "fs" => fs = Some(FsConfig::from_value(&item)?), "vsock" => vsock = Some(VsockConfig::from_value(&item)?), "serial" => serial = Some(SerialConfig::from_value(&item)?), "iothread" => iothreads = Some(IothreadConfig::from_value(&item)?), @@ -156,6 +159,7 @@ impl VmConfig { drives, nets, consoles, + fs, vsock, serial, iothreads, @@ -194,6 +198,12 @@ impl VmConfig { } } + if self.fs.is_some() { + for fs_cfg in self.fs.as_ref().unwrap() { + fs_cfg.check()?; + } + } + if self.vsock.is_some() { self.vsock.as_ref().unwrap().check()?; } -- Gitee From 2015bcdf2d0b4bc2fd339219cb655bde1795e7b7 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Sat, 30 Jan 2021 17:37:51 +0800 Subject: [PATCH 02/55] docs: add documents for virtio fs Signed-off-by: Fei Xu --- docs/config_guidebook.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index a127e4e60..6b60571da 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -411,6 +411,43 @@ This feature can prevent OOM occur in guest. } ``` +### 2.8 Virtio-fs +Virtio-fs is a shared file system for VM, it can access a directory tree on the host. + +Two properties are supported for virtio fs device in StratoVirt. +* tag: the alias of virtio fs device +* sock: the path of socket file in the host which can communicate with the process that implements userspace filesystem + +```shell +# cmdline +-fs tag=fs_tag,sock=path_on_host +# json +{ + "fs": [ + { + "tag": "fs_tag", + "sock": "/path/to/socket/path" + } + ], + ... +} +``` + +Two properties are supported for the process of vhost_user_fs that implements userspace filesystem +* sock: the path of socket file in the host that can communicate with StratoVirt +* dir: the directory in host + +$ ./vhost_user_fs -sock /path/to/socket/path -dir /path/to/host/directory + +# In host +Firstly, start the process of vhost_user_fs like the example above + +Secondly, start the process of StratoVirt with the cmdline to configure virtio fs device + +# In guest +Finally, mount the filesystem to the directory in guest + +$ mount -t virtiofs fs_tag /path/to/guest/directory ## 3. StratoVirt Management -- Gitee From 97ca0821cc0fec8756c3ca5095f5e46f45312e65 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Sat, 30 Jan 2021 18:26:19 +0800 Subject: [PATCH 03/55] device_model: create virtio fs device and add virtio fs to micro_vm 1. Create virtio fs device in vhost/user/fs.rs 2. Add virtio fs to mmio bus for micro_vm Signed-off-by: Fei Xu --- device_model/src/micro_vm/mod.rs | 21 +++- device_model/src/virtio/mod.rs | 2 +- device_model/src/virtio/vhost/mod.rs | 1 + device_model/src/virtio/vhost/user/fs.rs | 145 ++++++++++++++++++++++ device_model/src/virtio/vhost/user/mod.rs | 15 +++ 5 files changed, 181 insertions(+), 3 deletions(-) create mode 100644 device_model/src/virtio/vhost/user/fs.rs create mode 100644 device_model/src/virtio/vhost/user/mod.rs diff --git a/device_model/src/micro_vm/mod.rs b/device_model/src/micro_vm/mod.rs index 62b481534..3d2a36651 100644 --- a/device_model/src/micro_vm/mod.rs +++ b/device_model/src/micro_vm/mod.rs @@ -60,8 +60,8 @@ use machine_manager::machine::{ use machine_manager::{ config::BalloonConfig, config::{ - BootSource, ConsoleConfig, DriveConfig, NetworkInterfaceConfig, SerialConfig, VmConfig, - VsockConfig, + BootSource, ConsoleConfig, DriveConfig, FsConfig, NetworkInterfaceConfig, SerialConfig, + VmConfig, VsockConfig, }, event_loop::EventLoop, qmp::{qmp_schema, QmpChannel, Response}, @@ -178,6 +178,16 @@ impl ConfigDevBuilder for SerialConfig { } } +impl ConfigDevBuilder for FsConfig { + fn build_dev(&self, sys_mem: Arc, bus: &mut Bus) -> Result<()> { + let fs = Arc::new(Mutex::new(vhost::user::Fs::new(self.clone()))); + let device = Arc::new(Mutex::new(VirtioMmioDevice::new(sys_mem, fs))); + bus.attach_device(device) + .chain_err(|| "build dev from fs config failed")?; + Ok(()) + } +} + /// A wrapper around creating and using a kvm-based micro VM. pub struct LightMachine { /// KVM VM file descriptor, represent VM entry in kvm module. @@ -597,6 +607,13 @@ impl LightMachine { .chain_err(|| "Failed to register balloon device")?; } + if let Some(fs_cfgs) = vm_config.fs { + for fs in fs_cfgs { + self.register_device(&fs) + .chain_err(|| format!("Failed to register fs device {}", fs.tag))? + } + } + Ok(()) } diff --git a/device_model/src/virtio/mod.rs b/device_model/src/virtio/mod.rs index 10b7e9cb2..4ec155864 100644 --- a/device_model/src/virtio/mod.rs +++ b/device_model/src/virtio/mod.rs @@ -55,7 +55,7 @@ pub const VIRTIO_TYPE_CONSOLE: u32 = 3; pub const _VIRTIO_TYPE_RNG: u32 = 4; pub const VIRTIO_TYPE_BALLOON: u32 = 5; pub const VIRTIO_TYPE_VSOCK: u32 = 19; -pub const _VIRTIO_TYPE_FS: u32 = 26; +pub const VIRTIO_TYPE_FS: u32 = 26; /// Feature Bits, refer to Virtio Spec. /// Negotiating this feature indicates that the driver can use descriptors diff --git a/device_model/src/virtio/vhost/mod.rs b/device_model/src/virtio/vhost/mod.rs index b6669f9e8..cf02d67a1 100644 --- a/device_model/src/virtio/vhost/mod.rs +++ b/device_model/src/virtio/vhost/mod.rs @@ -11,6 +11,7 @@ // See the Mulan PSL v2 for more details. pub mod kernel; +pub mod user; use std::sync::{Arc, Mutex}; diff --git a/device_model/src/virtio/vhost/user/fs.rs b/device_model/src/virtio/vhost/user/fs.rs new file mode 100644 index 000000000..90e3e9d78 --- /dev/null +++ b/device_model/src/virtio/vhost/user/fs.rs @@ -0,0 +1,145 @@ +// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::cmp; +use std::io::Write; +use std::sync::atomic::AtomicU32; +use std::sync::{Arc, Mutex}; + +use vmm_sys_util::eventfd::EventFd; + +use address_space::AddressSpace; +use machine_manager::config::{FsConfig, MAX_TAG_LENGTH}; +use util::byte_code::ByteCode; +use util::num_ops::{read_u32, write_u32}; + +use super::super::super::errors::{ErrorKind, Result}; +use super::super::super::{Queue, VirtioDevice, VIRTIO_TYPE_FS}; + +// The num of high priority queue +const VIRIOT_FS_HIGH_PRIO_QUEUE_NUM: usize = 1; +// The num of request queue +const VIRTIO_FS_REQ_QUEUES_NUM: usize = 1; +// The size of queue for virtio fs +const VIRTIO_FS_QUEUE_SIZE: u16 = 1024; + +#[derive(Copy, Clone)] +#[repr(C, packed)] +struct VirtioFsConfig { + tag: [u8; MAX_TAG_LENGTH], + num_request_queues: u32, +} + +impl Default for VirtioFsConfig { + fn default() -> Self { + VirtioFsConfig { + tag: [0; MAX_TAG_LENGTH], + num_request_queues: 0, + } + } +} + +impl ByteCode for VirtioFsConfig {} + +pub struct Fs { + fs_cfg: FsConfig, + config: VirtioFsConfig, + avail_features: u64, + acked_features: u64, +} + +impl Fs { + pub fn new(fs_cfg: FsConfig) -> Self { + Fs { + fs_cfg, + config: VirtioFsConfig::default(), + avail_features: 0_u64, + acked_features: 0_u64, + } + } +} + +impl VirtioDevice for Fs { + fn realize(&mut self) -> Result<()> { + let tag_bytes_vec = self.fs_cfg.tag.clone().into_bytes(); + self.config.tag[..tag_bytes_vec.len()].copy_from_slice(tag_bytes_vec.as_slice()); + self.config.num_request_queues = VIRTIO_FS_REQ_QUEUES_NUM as u32; + + Ok(()) + } + + fn device_type(&self) -> u32 { + VIRTIO_TYPE_FS as u32 + } + + fn queue_num(&self) -> usize { + VIRIOT_FS_HIGH_PRIO_QUEUE_NUM + VIRTIO_FS_REQ_QUEUES_NUM + } + + fn queue_size(&self) -> u16 { + VIRTIO_FS_QUEUE_SIZE + } + + fn get_device_features(&self, features_select: u32) -> u32 { + read_u32(self.avail_features, features_select) + } + + fn set_driver_features(&mut self, page: u32, value: u32) { + let mut features = write_u32(value, page); + let unsupported_features = features & !self.avail_features; + if unsupported_features != 0 { + warn!( + "Received acknowledge request with unsupported feature for virtio fs: 0x{:x}", + features + ); + features &= !unsupported_features; + } + self.acked_features |= features; + } + + fn read_config(&self, offset: u64, mut data: &mut [u8]) -> Result<()> { + let config_slice = self.config.as_bytes(); + let config_size = config_slice.len() as u64; + if offset >= config_size { + return Err(ErrorKind::DevConfigOverflow(offset, config_size).into()); + } + if let Some(end) = offset.checked_add(data.len() as u64) { + data.write_all(&config_slice[offset as usize..cmp::min(end, config_size) as usize])?; + } + + Ok(()) + } + + fn write_config(&mut self, offset: u64, data: &[u8]) -> Result<()> { + let data_len = data.len(); + let config_slice = self.config.as_mut_bytes(); + let config_len = config_slice.len(); + if offset as usize + data_len > config_len { + return Err(ErrorKind::DevConfigOverflow(offset, config_len as u64).into()); + } + + config_slice[(offset as usize)..(offset as usize + data_len)].copy_from_slice(&data[..]); + + Ok(()) + } + + fn activate( + &mut self, + _mem_space: Arc, + _interrupt_evt: EventFd, + _interrupt_status: Arc, + _queues: Vec>>, + _queue_evts: Vec, + ) -> Result<()> { + Ok(()) + } +} diff --git a/device_model/src/virtio/vhost/user/mod.rs b/device_model/src/virtio/vhost/user/mod.rs new file mode 100644 index 000000000..d2e93b9f8 --- /dev/null +++ b/device_model/src/virtio/vhost/user/mod.rs @@ -0,0 +1,15 @@ +// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +mod fs; + +pub use self::fs::*; -- Gitee From 6c4789c3239be5d1759100b96a57452570598d2f Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Mon, 1 Feb 2021 16:37:16 +0800 Subject: [PATCH 04/55] device_model: implement unix socket function and create a client to communicate with the userspace filesystem process 1. Implement unix socket function for client and server to create, receive and send messages 2. Add new client code for virtio fs device Signed-off-by: Fei Xu --- device_model/src/virtio/vhost/user/fs.rs | 12 +- device_model/src/virtio/vhost/user/mod.rs | 6 +- .../virtio/vhost/user/vhost_user_client.rs | 58 ++++ .../src/virtio/vhost/user/vhost_user_sock.rs | 274 ++++++++++++++++++ 4 files changed, 348 insertions(+), 2 deletions(-) create mode 100644 device_model/src/virtio/vhost/user/vhost_user_client.rs create mode 100644 device_model/src/virtio/vhost/user/vhost_user_sock.rs diff --git a/device_model/src/virtio/vhost/user/fs.rs b/device_model/src/virtio/vhost/user/fs.rs index 90e3e9d78..7389c6d18 100644 --- a/device_model/src/virtio/vhost/user/fs.rs +++ b/device_model/src/virtio/vhost/user/fs.rs @@ -22,8 +22,9 @@ use machine_manager::config::{FsConfig, MAX_TAG_LENGTH}; use util::byte_code::ByteCode; use util::num_ops::{read_u32, write_u32}; -use super::super::super::errors::{ErrorKind, Result}; +use super::super::super::errors::{ErrorKind, Result, ResultExt}; use super::super::super::{Queue, VirtioDevice, VIRTIO_TYPE_FS}; +use super::VhostUserClient; // The num of high priority queue const VIRIOT_FS_HIGH_PRIO_QUEUE_NUM: usize = 1; @@ -53,6 +54,7 @@ impl ByteCode for VirtioFsConfig {} pub struct Fs { fs_cfg: FsConfig, config: VirtioFsConfig, + client: Option, avail_features: u64, acked_features: u64, } @@ -62,6 +64,7 @@ impl Fs { Fs { fs_cfg, config: VirtioFsConfig::default(), + client: None, avail_features: 0_u64, acked_features: 0_u64, } @@ -74,6 +77,13 @@ impl VirtioDevice for Fs { self.config.tag[..tag_bytes_vec.len()].copy_from_slice(tag_bytes_vec.as_slice()); self.config.num_request_queues = VIRTIO_FS_REQ_QUEUES_NUM as u32; + let queues_num = VIRIOT_FS_HIGH_PRIO_QUEUE_NUM + VIRTIO_FS_REQ_QUEUES_NUM; + let client = + VhostUserClient::new(&self.fs_cfg.sock, queues_num as u64).chain_err(|| { + "Failed to create the client which communicates with the server for virtio fs" + })?; + self.client = Some(client); + Ok(()) } diff --git a/device_model/src/virtio/vhost/user/mod.rs b/device_model/src/virtio/vhost/user/mod.rs index d2e93b9f8..61afca58a 100644 --- a/device_model/src/virtio/vhost/user/mod.rs +++ b/device_model/src/virtio/vhost/user/mod.rs @@ -10,6 +10,10 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -mod fs; +pub mod fs; +pub mod vhost_user_client; +pub mod vhost_user_sock; pub use self::fs::*; +pub use self::vhost_user_client::*; +pub use self::vhost_user_sock::*; diff --git a/device_model/src/virtio/vhost/user/vhost_user_client.rs b/device_model/src/virtio/vhost/user/vhost_user_client.rs new file mode 100644 index 000000000..d51391574 --- /dev/null +++ b/device_model/src/virtio/vhost/user/vhost_user_client.rs @@ -0,0 +1,58 @@ +// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::sync::{Arc, Mutex}; + +use super::super::super::errors::{Result, ResultExt}; +use super::vhost_user_sock::UnixDomainSock; + +#[allow(dead_code)] +struct ClientInternal { + // Used to send requests to the process of filesystem in userspace. + sock: UnixDomainSock, + // Maxinum number of queues which is supported. + max_queue_num: u64, +} + +impl ClientInternal { + fn new(sock: UnixDomainSock, max_queue_num: u64) -> Self { + ClientInternal { + sock, + max_queue_num, + } + } +} + +/// Struct for virtio fs device which can communicate with the process +/// of filesystem in userspace +#[derive(Clone)] +pub struct VhostUserClient { + client: Arc>, +} + +impl VhostUserClient { + pub fn new(path: &str, max_queue_num: u64) -> Result { + let mut sock = UnixDomainSock::new(path); + sock.client_connect().chain_err(|| { + format!( + "Failed to connect the socket {} for vhost user client", + path + ) + })?; + + let client = VhostUserClient { + client: Arc::new(Mutex::new(ClientInternal::new(sock, max_queue_num))), + }; + + Ok(client) + } +} diff --git a/device_model/src/virtio/vhost/user/vhost_user_sock.rs b/device_model/src/virtio/vhost/user/vhost_user_sock.rs new file mode 100644 index 000000000..c7d45241c --- /dev/null +++ b/device_model/src/virtio/vhost/user/vhost_user_sock.rs @@ -0,0 +1,274 @@ +// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use libc::{ + c_long, c_void, cmsghdr, iovec, msghdr, recvmsg, sendmsg, MSG_NOSIGNAL, MSG_WAITALL, + SCM_RIGHTS, SOL_SOCKET, +}; +use std::mem::size_of; +use std::os::unix::io::{AsRawFd, RawFd}; +use std::os::unix::net::{UnixListener, UnixStream}; +use std::ptr::{copy_nonoverlapping, null_mut, write_unaligned}; + +use super::super::super::errors::{Result, ResultExt}; +use vmm_sys_util::errno::Error; + +#[cfg(not(target_env = "musl"))] +macro_rules! CMSG_ALIGN { + ($len:expr) => { + (($len) + size_of::() - 1) & !(size_of::() - 1) + }; +} + +#[cfg(target_env = "musl")] +macro_rules! CMSG_ALIGN { + ($len:expr) => { + (($len) as usize + size_of::() - 1) & !(size_of::() - 1) + }; +} + +macro_rules! CMSG_SPACE { + ($len:expr) => { + CMSG_ALIGN!(size_of::()) + CMSG_ALIGN!($len) + }; +} + +macro_rules! CMSG_LEN { + ($len:expr) => { + CMSG_ALIGN!(size_of::()) + ($len) + }; +} + +#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))] +fn cmsg_data(cmsg_buffer: *mut cmsghdr) -> *mut RawFd { + (cmsg_buffer as *mut u8).wrapping_add(CMSG_LEN!(0)) as *mut RawFd +} + +#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))] +fn get_next_cmsg(msghdr: &msghdr, cmsg: &cmsghdr, cmsg_ptr: *mut cmsghdr) -> *mut cmsghdr { + let next_cmsg = (cmsg_ptr as *mut u8).wrapping_add(CMSG_ALIGN!(cmsg.cmsg_len)) as *mut cmsghdr; + let nex_cmsg_pos = (next_cmsg as *mut u8).wrapping_sub(msghdr.msg_control as usize) as usize; + + if nex_cmsg_pos.wrapping_add(CMSG_LEN!(0)) > msghdr.msg_controllen as usize { + null_mut() + } else { + next_cmsg + } +} + +pub struct UnixDomainSock { + path: String, + listener: Option, + sock: Option, +} + +impl Clone for UnixDomainSock { + fn clone(&self) -> Self { + let listener = match &self.listener { + Some(listener_) => Some(listener_.try_clone().unwrap()), + None => None, + }; + + let sock = match &self.sock { + Some(sock_) => Some(sock_.try_clone().unwrap()), + None => None, + }; + + UnixDomainSock { + path: self.path.clone(), + listener, + sock, + } + } +} + +impl UnixDomainSock { + pub fn new(path: &str) -> Self { + UnixDomainSock { + path: path.to_string(), + listener: None, + sock: None, + } + } + + #[allow(dead_code)] + pub fn server_bind(&mut self, unlink: bool) -> Result<()> { + if unlink { + let _ = std::fs::remove_file(self.path.as_str()); + } + + let listener = UnixListener::bind(self.path.as_str()) + .chain_err(|| format!("Failed to bind the socket {}", self.path))?; + + self.listener = Some(listener); + Ok(()) + } + + #[allow(dead_code)] + pub fn server_accept(&mut self) -> Result<()> { + let (sock, _addr) = self + .listener + .as_ref() + .unwrap() + .accept() + .chain_err(|| format!("Failed to accept the socket {}", self.path))?; + + self.sock = Some(sock); + Ok(()) + } + + pub fn client_connect(&mut self) -> Result<()> { + let sock = UnixStream::connect(self.path.as_str()) + .chain_err(|| format!("Failed to connect the socket {}", self.path))?; + + self.sock = Some(sock); + Ok(()) + } + + #[allow(dead_code)] + pub fn get_stream_raw_fd(&self) -> RawFd { + self.sock.as_ref().unwrap().as_raw_fd() + } + + #[allow(dead_code)] + pub fn get_listener_raw_fd(&self) -> RawFd { + self.listener.as_ref().unwrap().as_raw_fd() + } + + #[allow(dead_code)] + fn sendmsg(&self, iovecs: &mut [iovec], out_fds: &[RawFd]) -> Result { + #[cfg(not(target_env = "musl"))] + let cmsg_capacity = CMSG_SPACE!(size_of::() * out_fds.len()); + #[cfg(target_env = "musl")] + let cmsg_capacity = CMSG_SPACE!(size_of::() * out_fds.len()) as u32; + #[cfg(not(target_env = "musl"))] + let iovecs_len = iovecs.len(); + #[cfg(target_env = "musl")] + let iovecs_len = iovecs.len() as i32; + #[cfg(not(target_env = "musl"))] + let cmsg_len = CMSG_LEN!(size_of::() * out_fds.len()); + #[cfg(target_env = "musl")] + let cmsg_len = CMSG_LEN!(size_of::() * out_fds.len()) as u32; + + let mut cmsg_buffer = vec![0u64; cmsg_capacity as usize]; + + let mut msg: msghdr = unsafe { std::mem::zeroed() }; + msg.msg_name = null_mut(); + msg.msg_namelen = 0; + msg.msg_iov = iovecs.as_mut_ptr(); + msg.msg_iovlen = iovecs_len; + msg.msg_control = null_mut(); + msg.msg_controllen = 0; + msg.msg_flags = 0; + + if !out_fds.is_empty() { + let cmsg = cmsghdr { + cmsg_len, + #[cfg(target_env = "musl")] + __pad1: 0, + cmsg_level: SOL_SOCKET, + cmsg_type: SCM_RIGHTS, + }; + unsafe { + write_unaligned(cmsg_buffer.as_mut_ptr() as *mut cmsghdr, cmsg); + + copy_nonoverlapping( + out_fds.as_ptr(), + cmsg_data(cmsg_buffer.as_mut_ptr() as *mut cmsghdr), + out_fds.len(), + ); + } + + msg.msg_control = cmsg_buffer.as_mut_ptr() as *mut c_void; + msg.msg_controllen = cmsg_capacity; + } + + let write_count = + unsafe { sendmsg(self.sock.as_ref().unwrap().as_raw_fd(), &msg, MSG_NOSIGNAL) }; + + if write_count == -1 { + bail!("Failed to send msg, err: {}", Error::last()); + } else { + Ok(write_count as usize) + } + } + + #[allow(dead_code)] + fn recvmsg(&self, iovecs: &mut [iovec], in_fds: &mut [RawFd]) -> Result<(usize, usize)> { + #[cfg(not(target_env = "musl"))] + let cmsg_capacity = CMSG_SPACE!(size_of::() * in_fds.len()); + #[cfg(target_env = "musl")] + let cmsg_capacity = CMSG_SPACE!(size_of::() * in_fds.len()) as u32; + #[cfg(not(target_env = "musl"))] + let iovecs_len = iovecs.len(); + #[cfg(target_env = "musl")] + let iovecs_len = iovecs.len() as i32; + + let mut cmsg_buffer = vec![0u64; cmsg_capacity as usize]; + + let mut msg: msghdr = unsafe { std::mem::zeroed() }; + msg.msg_name = null_mut(); + msg.msg_namelen = 0; + msg.msg_iov = iovecs.as_mut_ptr(); + msg.msg_iovlen = iovecs_len; + msg.msg_control = null_mut(); + msg.msg_controllen = 0; + msg.msg_flags = 0; + + if !in_fds.is_empty() { + msg.msg_control = cmsg_buffer.as_mut_ptr() as *mut c_void; + msg.msg_controllen = cmsg_capacity; + } + + let total_read = unsafe { + recvmsg( + self.sock.as_ref().unwrap().as_raw_fd(), + &mut msg, + MSG_WAITALL, + ) + }; + + if total_read == -1 { + bail!("Failed to recv msg, err: {}", Error::last()); + } + + if total_read == 0 && (msg.msg_controllen as usize) < size_of::() { + bail!( + "The length of control message is invalid, {} {}", + msg.msg_controllen, + size_of::() + ); + } + + let mut cmsg_ptr = msg.msg_control as *mut cmsghdr; + let mut in_fds_count = 0usize; + while !cmsg_ptr.is_null() { + let cmsg = unsafe { (cmsg_ptr as *mut cmsghdr).read_unaligned() }; + + if cmsg.cmsg_level == SOL_SOCKET && cmsg.cmsg_type == SCM_RIGHTS { + let fd_count = (cmsg.cmsg_len as usize - CMSG_LEN!(0)) / size_of::(); + unsafe { + copy_nonoverlapping( + cmsg_data(cmsg_ptr), + in_fds[in_fds_count..(in_fds_count + fd_count)].as_mut_ptr(), + fd_count, + ); + } + in_fds_count += fd_count; + } + + cmsg_ptr = get_next_cmsg(&msg, &cmsg, cmsg_ptr); + } + + Ok((total_read as usize, in_fds_count as usize)) + } +} -- Gitee From 4a7e98d7aa6e343f92aececd2c60188d4f7b2fe7 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Mon, 1 Feb 2021 17:52:26 +0800 Subject: [PATCH 05/55] device_model: implement the sending and receiving interface for vhost user client 1. Implement the sending and receiving interface in vhost_user_sock.rs 2. Add vhost_user_msg.rs for defining information for vhost user messages 3. Implement the messages of SetOwner and GetFeatures for vhost user client Signed-off-by: Fei Xu --- device_model/src/virtio/vhost/mod.rs | 9 + device_model/src/virtio/vhost/user/mod.rs | 2 + .../virtio/vhost/user/vhost_user_client.rs | 101 ++++++++- .../src/virtio/vhost/user/vhost_user_msg.rs | 202 ++++++++++++++++++ .../src/virtio/vhost/user/vhost_user_sock.rs | 95 +++++++- 5 files changed, 406 insertions(+), 3 deletions(-) create mode 100644 device_model/src/virtio/vhost/user/vhost_user_msg.rs diff --git a/device_model/src/virtio/vhost/mod.rs b/device_model/src/virtio/vhost/mod.rs index cf02d67a1..8c5c6c872 100644 --- a/device_model/src/virtio/vhost/mod.rs +++ b/device_model/src/virtio/vhost/mod.rs @@ -80,4 +80,13 @@ pub trait VhostOps { /// * `queue_idx` - Index of the queue to modify. /// * `fd` - EventFd that will be signaled from guest. fn set_vring_kick(&self, queue_idx: usize, fd: &EventFd) -> Result<()>; + + /// Set the status of ring. + /// + /// # Arguments + /// * `queue_idx` - Index of the queue to set. + /// * `status` - Status of the virtqueue. + fn set_vring_enable(&self, _queue_idx: usize, _status: bool) -> Result<()> { + Ok(()) + } } diff --git a/device_model/src/virtio/vhost/user/mod.rs b/device_model/src/virtio/vhost/user/mod.rs index 61afca58a..99bf78e9b 100644 --- a/device_model/src/virtio/vhost/user/mod.rs +++ b/device_model/src/virtio/vhost/user/mod.rs @@ -12,8 +12,10 @@ pub mod fs; pub mod vhost_user_client; +pub mod vhost_user_msg; pub mod vhost_user_sock; pub use self::fs::*; pub use self::vhost_user_client::*; +pub use self::vhost_user_msg::*; pub use self::vhost_user_sock::*; diff --git a/device_model/src/virtio/vhost/user/vhost_user_client.rs b/device_model/src/virtio/vhost/user/vhost_user_client.rs index d51391574..d19a42d54 100644 --- a/device_model/src/virtio/vhost/user/vhost_user_client.rs +++ b/device_model/src/virtio/vhost/user/vhost_user_client.rs @@ -10,9 +10,18 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::mem::size_of; use std::sync::{Arc, Mutex}; -use super::super::super::errors::{Result, ResultExt}; +use vmm_sys_util::eventfd::EventFd; + +use super::super::super::{ + errors::{Result, ResultExt}, + QueueConfig, +}; + +use super::super::VhostOps; +use super::vhost_user_msg::*; use super::vhost_user_sock::UnixDomainSock; #[allow(dead_code)] @@ -30,6 +39,28 @@ impl ClientInternal { max_queue_num, } } + + fn wait_ack_msg(&self, request: u32) -> Result { + let mut hdr = VhostUserMsgHeader::default(); + let mut body: T = Default::default(); + let payload_opt: Option<&mut [u8]> = None; + + let (recv_len, _fds_num) = self + .sock + .vhostuser_msgrecv(Some(&mut hdr), Some(&mut body), payload_opt, &mut []) + .chain_err(|| "Failed to recv ack msg")?; + + if request != hdr.request + || recv_len != (size_of::() + size_of::()) + || !hdr.is_reply() + { + bail!("The ack msg is invalid, request: {}, header request: {}, reply type: {}, recv len: {}, len: {}", + request, hdr.request, hdr.is_reply(), recv_len, size_of::() + size_of::(), + ); + } + + Ok(body) + } } /// Struct for virtio fs device which can communicate with the process @@ -56,3 +87,71 @@ impl VhostUserClient { Ok(client) } } + +impl VhostOps for VhostUserClient { + fn set_owner(&self) -> Result<()> { + let hdr = VhostUserMsgHeader::new(VhostUserMsgReq::SetOwner as u32, 0, 0); + + let body_opt: Option<&u32> = None; + let payload_opt: Option<&[u8]> = None; + + let client = self.client.lock().unwrap(); + client + .sock + .vhostuser_msgsend(Some(&hdr), body_opt, payload_opt, &[]) + .chain_err(|| "Failed to send msg for setting owner")?; + + Ok(()) + } + + fn get_features(&self) -> Result { + let request = VhostUserMsgReq::GetFeatures as u32; + let hdr = VhostUserMsgHeader::new(request, VhostUserHeaderFlag::NeedReply as u32, 0); + let body_opt: Option<&u32> = None; + let payload_opt: Option<&[u8]> = None; + + let client = self.client.lock().unwrap(); + client + .sock + .vhostuser_msgsend(Some(&hdr), body_opt, payload_opt, &[]) + .chain_err(|| "Failed to send msg for getting features")?; + + let features = client + .wait_ack_msg::(request) + .chain_err(|| "Failed to wait ack msg for getting features")?; + + Ok(features) + } + + fn set_features(&self, _features: u64) -> Result<()> { + Ok(()) + } + + fn set_mem_table(&self) -> Result<()> { + Ok(()) + } + + fn set_vring_num(&self, _queue_idx: usize, _num: u16) -> Result<()> { + Ok(()) + } + + fn set_vring_addr(&self, _queue: &QueueConfig, _index: usize, _flags: u32) -> Result<()> { + Ok(()) + } + + fn set_vring_base(&self, _queue_idx: usize, _last_avail_idx: u16) -> Result<()> { + Ok(()) + } + + fn set_vring_call(&self, _queue_idx: usize, _fd: &EventFd) -> Result<()> { + Ok(()) + } + + fn set_vring_kick(&self, _queue_idx: usize, _fd: &EventFd) -> Result<()> { + Ok(()) + } + + fn set_vring_enable(&self, _queue_idx: usize, _status: bool) -> Result<()> { + Ok(()) + } +} diff --git a/device_model/src/virtio/vhost/user/vhost_user_msg.rs b/device_model/src/virtio/vhost/user/vhost_user_msg.rs new file mode 100644 index 000000000..1b0d41842 --- /dev/null +++ b/device_model/src/virtio/vhost/user/vhost_user_msg.rs @@ -0,0 +1,202 @@ +// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use super::vhost_user_sock::VHOST_USER_MSG_MAX_SIZE; + +/// Type of requests sending from virtio fs device to the userspace filesystem process. +#[repr(u32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum VhostUserMsgReq { + /// Null operation. + Noop = 0, + /// Get from the underlying vhost implementation the features bit mask. + GetFeatures = 1, + /// Enable features in the underlying vhost implementation using a bit mask. + SetFeatures = 2, + /// Set the current Master as an owner of the session. + SetOwner = 3, + /// No longer used. + ResetOwner = 4, + /// Set the memory map regions on the slave so it can translate the vring addresses. + SetMemTable = 5, + /// Set logging shared memory space. + SetLogBase = 6, + /// Set the logging file descriptor, which is passed as ancillary data. + SetLogFd = 7, + /// Set the size of the queue. + SetVringNum = 8, + /// Set the addresses of the different aspects of the vring. + SetVringAddr = 9, + /// Set the base offset in the available vring. + SetVringBase = 10, + /// Get the available vring base offset. + GetVringBase = 11, + /// Set the event file descriptor for adding buffers to the vring. + SetVringKick = 12, + /// Set the event file descriptor to signal when buffers are used. + SetVringCall = 13, + /// Set the event file descriptor to signal when error occurs. + SetVringErr = 14, + /// Get the protocol feature bit mask from the underlying vhost implementation. + GetProtocolFeatures = 15, + /// Enable protocol features in the underlying vhost implementation. + SetProtocolFeatures = 16, + /// Query how many queues the backend supports. + GetQueueNum = 17, + /// Signal slave to enable or disable corresponding vring. + SetVringEnable = 18, + /// Ask vhost user backend to broadcast a fake RARP to notify the migration is terminated + /// for guest that does not support GUEST_ANNOUNCE. + SendRarp = 19, + /// Set host MTU value exposed to the guest. + NetSetMtu = 20, + /// Set the socket file descriptor for slave initiated requests. + SetSlaveReqFd = 21, + /// Send IOTLB messages with struct vhost_iotlb_msg as payload. + IotlbMsg = 22, + /// Set the endianness of a VQ for legacy devices. + SetVringEndian = 23, + /// Fetch the contents of the virtio device configuration space. + GetConfig = 24, + /// Change the contents of the virtio device configuration space. + SetConfig = 25, + /// Create a session for crypto operation. + CreateCryptoSession = 26, + /// Close a session for crypto operation. + CloseCryptoSession = 27, + /// Advise slave that a migration with postcopy enabled is underway. + PostcopyAdvise = 28, + /// Advise slave that a transition to postcopy mode has happened. + PostcopyListen = 29, + /// Advise that postcopy migration has now completed. + PostcopyEnd = 30, + /// Get a shared buffer from slave. + GetInflightFd = 31, + /// Send the shared inflight buffer back to slave + SetInflightFd = 32, + /// Upper bound of valid commands. + MaxCmd = 33, +} + +impl From for VhostUserMsgReq { + fn from(t: u32) -> Self { + match t { + 0 => VhostUserMsgReq::Noop, + 1 => VhostUserMsgReq::GetFeatures, + 2 => VhostUserMsgReq::SetFeatures, + 3 => VhostUserMsgReq::SetOwner, + 4 => VhostUserMsgReq::ResetOwner, + 5 => VhostUserMsgReq::SetMemTable, + 6 => VhostUserMsgReq::SetLogBase, + 7 => VhostUserMsgReq::SetLogFd, + 8 => VhostUserMsgReq::SetVringNum, + 9 => VhostUserMsgReq::SetVringAddr, + 10 => VhostUserMsgReq::SetVringBase, + 11 => VhostUserMsgReq::GetVringBase, + 12 => VhostUserMsgReq::SetVringKick, + 13 => VhostUserMsgReq::SetVringCall, + 14 => VhostUserMsgReq::SetVringErr, + 15 => VhostUserMsgReq::GetProtocolFeatures, + 16 => VhostUserMsgReq::SetProtocolFeatures, + 17 => VhostUserMsgReq::GetQueueNum, + 18 => VhostUserMsgReq::SetVringEnable, + 19 => VhostUserMsgReq::SendRarp, + 20 => VhostUserMsgReq::NetSetMtu, + 21 => VhostUserMsgReq::SetSlaveReqFd, + 22 => VhostUserMsgReq::IotlbMsg, + 23 => VhostUserMsgReq::SetVringEndian, + 24 => VhostUserMsgReq::GetConfig, + 25 => VhostUserMsgReq::SetConfig, + 26 => VhostUserMsgReq::CreateCryptoSession, + 27 => VhostUserMsgReq::CloseCryptoSession, + 28 => VhostUserMsgReq::PostcopyAdvise, + 29 => VhostUserMsgReq::PostcopyListen, + 30 => VhostUserMsgReq::PostcopyEnd, + 31 => VhostUserMsgReq::GetInflightFd, + 32 => VhostUserMsgReq::SetInflightFd, + _ => VhostUserMsgReq::MaxCmd, + } + } +} + +/// The meaning of flag bits for header of vhost user message +pub enum VhostUserHeaderFlag { + /// Bits[0..1] is message version number. + _Version = 0x3, + /// Bits[2] Mark message as reply. + Reply = 0x4, + /// Bits[3] Sender anticipates a reply message from the peer. + NeedReply = 0x8, + /// All valid bits. + AllFlags = 0xc, + /// All reserved bits. + ReservedBits = !0xf, +} + +///the struct for the header of vhost user message +pub struct VhostUserMsgHeader { + /// The request id for vhost-user message + pub request: u32, + /// The flags for property setting + pub flags: u32, + /// The total length of vhost user message + pub size: u32, +} + +impl VhostUserMsgHeader { + /// Create a new instance of `VhostUserMsgHeader`. + pub fn new(request: u32, flags: u32, size: u32) -> Self { + // Default to protocol version 1 + let flag = (flags & VhostUserHeaderFlag::AllFlags as u32) | 0x1; + VhostUserMsgHeader { + request, + flags: flag, + size, + } + } + + /// Get message version number. + #[allow(dead_code)] + fn get_version(&self) -> u32 { + self.flags & 0x3 + } + + /// Check whether reply for this message is requested. + #[allow(dead_code)] + pub fn is_need_reply(&self) -> bool { + (self.flags & VhostUserHeaderFlag::NeedReply as u32) != 0 + } + + ///Check whether reply for message. + pub fn is_reply(&self) -> bool { + (self.flags & VhostUserHeaderFlag::Reply as u32) != 0 + } + + ///Check the header of vhost user message is invaild + #[allow(dead_code)] + pub fn is_invalid(&self) -> bool { + self.request >= VhostUserMsgReq::MaxCmd as u32 + || self.size > VHOST_USER_MSG_MAX_SIZE as u32 + || self.flags & (VhostUserHeaderFlag::ReservedBits as u32) != 0 + || self.get_version() != 0x1 + } +} + +impl Default for VhostUserMsgHeader { + fn default() -> Self { + VhostUserMsgHeader { + request: 0, + flags: 0x1, + size: 0, + } + } +} diff --git a/device_model/src/virtio/vhost/user/vhost_user_sock.rs b/device_model/src/virtio/vhost/user/vhost_user_sock.rs index c7d45241c..f9b2001bb 100644 --- a/device_model/src/virtio/vhost/user/vhost_user_sock.rs +++ b/device_model/src/virtio/vhost/user/vhost_user_sock.rs @@ -22,6 +22,9 @@ use std::ptr::{copy_nonoverlapping, null_mut, write_unaligned}; use super::super::super::errors::{Result, ResultExt}; use vmm_sys_util::errno::Error; +pub const VHOST_USER_MSG_MAX_SIZE: usize = 0x1000; +pub const MAX_ATTACHED_FD_ENTRIES: usize = 32; + #[cfg(not(target_env = "musl"))] macro_rules! CMSG_ALIGN { ($len:expr) => { @@ -144,7 +147,6 @@ impl UnixDomainSock { self.listener.as_ref().unwrap().as_raw_fd() } - #[allow(dead_code)] fn sendmsg(&self, iovecs: &mut [iovec], out_fds: &[RawFd]) -> Result { #[cfg(not(target_env = "musl"))] let cmsg_capacity = CMSG_SPACE!(size_of::() * out_fds.len()); @@ -202,7 +204,6 @@ impl UnixDomainSock { } } - #[allow(dead_code)] fn recvmsg(&self, iovecs: &mut [iovec], in_fds: &mut [RawFd]) -> Result<(usize, usize)> { #[cfg(not(target_env = "musl"))] let cmsg_capacity = CMSG_SPACE!(size_of::() * in_fds.len()); @@ -271,4 +272,94 @@ impl UnixDomainSock { Ok((total_read as usize, in_fds_count as usize)) } + + pub fn vhostuser_msgsend( + &self, + hdr_opt: Option<&D>, + body_opt: Option<&T>, + payload_opt: Option<&[P]>, + fds: &[RawFd], + ) -> Result<()> { + let mut iovs = Vec::with_capacity(3); + let mut total_len = size_of::(); + + if let Some(hdr) = hdr_opt { + iovs.push(iovec { + iov_base: hdr as *const D as *const u8 as *mut c_void, + iov_len: size_of::(), + }); + } + + if let Some(body) = body_opt { + iovs.push(iovec { + iov_base: body as *const T as *const u8 as *mut c_void, + iov_len: size_of::(), + }); + total_len += size_of::(); + } + + if let Some(payload) = payload_opt { + iovs.push(iovec { + iov_base: payload.as_ptr() as *const u8 as *mut c_void, + iov_len: payload.len() * size_of::

(), + }); + total_len += payload.len() * size_of::

(); + } + + if (total_len - size_of::()) > VHOST_USER_MSG_MAX_SIZE { + bail!( + "The total length is invaild {}", + (total_len - size_of::()) + ); + } + + if fds.len() > MAX_ATTACHED_FD_ENTRIES { + bail!("The number of fds is invaild {}", fds.len()); + } + + let snd_len = self.sendmsg(&mut iovs, fds)?; + if snd_len != total_len { + bail!( + "The actual sending length {} is less than the sending length {}", + snd_len, + total_len + ); + } + Ok(()) + } + + pub fn vhostuser_msgrecv( + &self, + hdr_opt: Option<&mut D>, + body_opt: Option<&mut T>, + payload_opt: Option<&mut [P]>, + fds: &mut [RawFd], + ) -> Result<(usize, usize)> { + let mut iovs = Vec::with_capacity(3); + + if let Some(hdr) = hdr_opt { + iovs.push(iovec { + iov_base: hdr as *const D as *const u8 as *mut c_void, + iov_len: size_of::(), + }); + } + + if let Some(body) = body_opt { + iovs.push(iovec { + iov_base: body as *const T as *const u8 as *mut c_void, + iov_len: size_of::(), + }); + } + + if let Some(payload) = payload_opt { + iovs.push(iovec { + iov_base: payload.as_ptr() as *const u8 as *mut c_void, + iov_len: payload.len() * size_of::

(), + }); + } + + let (rcv_len, fds_num) = self.recvmsg(&mut iovs, fds)?; + + Ok((rcv_len, fds_num)) + } } -- Gitee From 73b4bb4b181ed6977a92e37101b6d5e0a34efc87 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Mon, 1 Feb 2021 19:33:43 +0800 Subject: [PATCH 06/55] device_model: implement the messages of SetFeatures and SetMemTable for vhost user client Signed-off-by: Fei Xu --- device_model/src/micro_vm/mod.rs | 5 +- device_model/src/virtio/vhost/user/fs.rs | 8 +- .../virtio/vhost/user/vhost_user_client.rs | 168 +++++++++++++++++- .../src/virtio/vhost/user/vhost_user_msg.rs | 45 +++++ 4 files changed, 219 insertions(+), 7 deletions(-) diff --git a/device_model/src/micro_vm/mod.rs b/device_model/src/micro_vm/mod.rs index 3d2a36651..9563dbeac 100644 --- a/device_model/src/micro_vm/mod.rs +++ b/device_model/src/micro_vm/mod.rs @@ -180,7 +180,10 @@ impl ConfigDevBuilder for SerialConfig { impl ConfigDevBuilder for FsConfig { fn build_dev(&self, sys_mem: Arc, bus: &mut Bus) -> Result<()> { - let fs = Arc::new(Mutex::new(vhost::user::Fs::new(self.clone()))); + let fs = Arc::new(Mutex::new(vhost::user::Fs::new( + self.clone(), + sys_mem.clone(), + ))); let device = Arc::new(Mutex::new(VirtioMmioDevice::new(sys_mem, fs))); bus.attach_device(device) .chain_err(|| "build dev from fs config failed")?; diff --git a/device_model/src/virtio/vhost/user/fs.rs b/device_model/src/virtio/vhost/user/fs.rs index 7389c6d18..e23427b5c 100644 --- a/device_model/src/virtio/vhost/user/fs.rs +++ b/device_model/src/virtio/vhost/user/fs.rs @@ -57,16 +57,18 @@ pub struct Fs { client: Option, avail_features: u64, acked_features: u64, + mem_space: Arc, } impl Fs { - pub fn new(fs_cfg: FsConfig) -> Self { + pub fn new(fs_cfg: FsConfig, mem_space: Arc) -> Self { Fs { fs_cfg, config: VirtioFsConfig::default(), client: None, avail_features: 0_u64, acked_features: 0_u64, + mem_space, } } } @@ -78,8 +80,8 @@ impl VirtioDevice for Fs { self.config.num_request_queues = VIRTIO_FS_REQ_QUEUES_NUM as u32; let queues_num = VIRIOT_FS_HIGH_PRIO_QUEUE_NUM + VIRTIO_FS_REQ_QUEUES_NUM; - let client = - VhostUserClient::new(&self.fs_cfg.sock, queues_num as u64).chain_err(|| { + let client = VhostUserClient::new(&self.mem_space, &self.fs_cfg.sock, queues_num as u64) + .chain_err(|| { "Failed to create the client which communicates with the server for virtio fs" })?; self.client = Some(client); diff --git a/device_model/src/virtio/vhost/user/vhost_user_client.rs b/device_model/src/virtio/vhost/user/vhost_user_client.rs index d19a42d54..05563eabd 100644 --- a/device_model/src/virtio/vhost/user/vhost_user_client.rs +++ b/device_model/src/virtio/vhost/user/vhost_user_client.rs @@ -11,15 +11,19 @@ // See the Mulan PSL v2 for more details. use std::mem::size_of; +use std::os::unix::io::AsRawFd; use std::sync::{Arc, Mutex}; use vmm_sys_util::eventfd::EventFd; +use address_space::{ + AddressSpace, FileBackend, FlatRange, Listener, ListenerReqType, RegionIoEventFd, RegionType, +}; + use super::super::super::{ errors::{Result, ResultExt}, QueueConfig, }; - use super::super::VhostOps; use super::vhost_user_msg::*; use super::vhost_user_sock::UnixDomainSock; @@ -63,15 +67,127 @@ impl ClientInternal { } } +#[derive(Clone)] +struct VhostUserMemoryRegionInfo { + region: VhostUserMemoryRegion, + file_back: FileBackend, +} + +#[derive(Clone)] +struct VhostUserMemInfo { + regions: Arc>>, +} + +impl VhostUserMemInfo { + fn new() -> Self { + VhostUserMemInfo { + regions: Arc::new(Mutex::new(Vec::new())), + } + } + + fn check_vhost_mem_range(fr: &FlatRange) -> bool { + fr.owner.region_type() == RegionType::Ram + } + + fn add_mem_range(&self, fr: &FlatRange) { + let guest_phys_addr = fr.addr_range.base.raw_value(); + let memory_size = fr.addr_range.size; + let host_address = if let Some(addr) = fr.owner.get_host_address() { + addr + } else { + panic!("Failed to get host address to add mem range for virtio fs"); + }; + let userspace_addr = host_address + fr.offset_in_region; + let file_back = match fr.owner.get_file_backend() { + Some(file_back_) => file_back_, + _ => { + panic!("It is not share memory for virtio fs"); + } + }; + + let region = VhostUserMemoryRegion { + guest_phys_addr, + memory_size, + userspace_addr, + mmap_offset: file_back.offset, + }; + + let region_info = VhostUserMemoryRegionInfo { region, file_back }; + self.regions.lock().unwrap().push(region_info); + } + + fn delete_mem_range(&self, fr: &FlatRange) { + let file_back = fr.owner.get_file_backend().unwrap(); + let mut mem_regions = self.regions.lock().unwrap(); + let host_address = if let Some(addr) = fr.owner.get_host_address() { + addr + } else { + panic!("Failed to get host address to delete mem range for virtio fs"); + }; + + let target = VhostUserMemoryRegion { + guest_phys_addr: fr.addr_range.base.raw_value(), + memory_size: fr.addr_range.size, + userspace_addr: host_address + fr.offset_in_region, + mmap_offset: file_back.offset, + }; + for (index, region_info) in mem_regions.iter().enumerate() { + let mr = ®ion_info.region; + if mr.guest_phys_addr == target.guest_phys_addr + && mr.memory_size == target.memory_size + && mr.userspace_addr == target.userspace_addr + && mr.mmap_offset == target.mmap_offset + && region_info.file_back.file.as_raw_fd() == file_back.file.as_raw_fd() + { + mem_regions.remove(index); + return; + } + } + error!( + "Vhost user: deleting mem region {:?} failed: not matched", + target + ); + } +} + +impl Listener for VhostUserMemInfo { + fn priority(&self) -> i32 { + 0 + } + + fn handle_request( + &self, + range: Option<&FlatRange>, + _evtfd: Option<&RegionIoEventFd>, + req_type: ListenerReqType, + ) -> std::result::Result<(), address_space::errors::Error> { + match req_type { + ListenerReqType::AddRegion => { + if Self::check_vhost_mem_range(&range.unwrap()) { + self.add_mem_range(range.unwrap()); + } + } + ListenerReqType::DeleteRegion => { + if Self::check_vhost_mem_range(&range.unwrap()) { + self.delete_mem_range(range.unwrap()); + } + } + _ => {} + } + Ok(()) + } +} + /// Struct for virtio fs device which can communicate with the process /// of filesystem in userspace #[derive(Clone)] pub struct VhostUserClient { client: Arc>, + mem_info: VhostUserMemInfo, } impl VhostUserClient { - pub fn new(path: &str, max_queue_num: u64) -> Result { + pub fn new(mem_space: &Arc, path: &str, max_queue_num: u64) -> Result { let mut sock = UnixDomainSock::new(path); sock.client_connect().chain_err(|| { format!( @@ -80,8 +196,14 @@ impl VhostUserClient { ) })?; + let mem_info = VhostUserMemInfo::new(); + mem_space + .register_listener(Box::new(mem_info.clone())) + .chain_err(|| "Failed to register listener for memory for vhost user client")?; + let client = VhostUserClient { client: Arc::new(Mutex::new(ClientInternal::new(sock, max_queue_num))), + mem_info, }; Ok(client) @@ -123,11 +245,51 @@ impl VhostOps for VhostUserClient { Ok(features) } - fn set_features(&self, _features: u64) -> Result<()> { + fn set_features(&self, features: u64) -> Result<()> { + let hdr = VhostUserMsgHeader::new( + VhostUserMsgReq::SetFeatures as u32, + 0, + size_of::() as u32, + ); + let payload_opt: Option<&[u8]> = None; + + let client = self.client.lock().unwrap(); + client + .sock + .vhostuser_msgsend(Some(&hdr), Some(&features), payload_opt, &[]) + .chain_err(|| "Failed to send msg for setting features")?; + Ok(()) } fn set_mem_table(&self) -> Result<()> { + let num_region = self.mem_info.regions.lock().unwrap().len(); + let mut fds = Vec::with_capacity(num_region); + + let memhdr = VhostUserMemoryHdr::new(num_region as u32, 0); + let mut memcontext = VhostUserMemoryContext::new(); + + for region_info in self.mem_info.regions.lock().unwrap().iter() { + memcontext.region_add(region_info.region.clone()); + fds.push(region_info.file_back.file.as_raw_fd()); + } + + let len = size_of::() + num_region * size_of::(); + let request = VhostUserMsgReq::SetMemTable as u32; + let client = self.client.lock().unwrap(); + let flags = 0; + let hdr = VhostUserMsgHeader::new(request, flags, len as u32); + + client + .sock + .vhostuser_msgsend( + Some(&hdr), + Some(&memhdr), + Some(memcontext.regions.as_slice()), + &fds, + ) + .chain_err(|| "Failed to send msg for setting mem table")?; + Ok(()) } diff --git a/device_model/src/virtio/vhost/user/vhost_user_msg.rs b/device_model/src/virtio/vhost/user/vhost_user_msg.rs index 1b0d41842..7e8aac829 100644 --- a/device_model/src/virtio/vhost/user/vhost_user_msg.rs +++ b/device_model/src/virtio/vhost/user/vhost_user_msg.rs @@ -200,3 +200,48 @@ impl Default for VhostUserMsgHeader { } } } + +/// Memory region information for the message of memory table +#[derive(Clone, Debug)] +pub struct VhostUserMemoryRegion { + /// Guest physical address of the memory region. + pub guest_phys_addr: u64, + /// Size of the memory region. + pub memory_size: u64, + /// Virtual address in the current process. + pub userspace_addr: u64, + /// Offset where region starts in the mapped memory. + pub mmap_offset: u64, +} + +/// The header for the message of memory table +pub struct VhostUserMemoryHdr { + /// Number of memory regions in the payload. + pub nregion: u32, + /// Padding for alignment. + pub padding: u32, +} + +impl VhostUserMemoryHdr { + pub fn new(nregion: u32, padding: u32) -> Self { + VhostUserMemoryHdr { nregion, padding } + } +} + +/// The context for the message of memory table +pub struct VhostUserMemoryContext { + /// The vector of memory region information + pub regions: Vec, +} + +impl VhostUserMemoryContext { + pub fn new() -> Self { + VhostUserMemoryContext { + regions: Vec::new(), + } + } + + pub fn region_add(&mut self, region: VhostUserMemoryRegion) { + self.regions.push(region); + } +} -- Gitee From 6d76586fc3e3784f2013b9aa79019a27705cc7ec Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Mon, 1 Feb 2021 19:54:35 +0800 Subject: [PATCH 07/55] device_model: implement the messages of SetVringNum, SetVringAddr and SetVringBase for vhost user client Signed-off-by: Fei Xu --- .../virtio/vhost/user/vhost_user_client.rs | 67 +++++++++++++++++-- .../src/virtio/vhost/user/vhost_user_msg.rs | 34 +++++++++- 2 files changed, 95 insertions(+), 6 deletions(-) diff --git a/device_model/src/virtio/vhost/user/vhost_user_client.rs b/device_model/src/virtio/vhost/user/vhost_user_client.rs index 05563eabd..fade26441 100644 --- a/device_model/src/virtio/vhost/user/vhost_user_client.rs +++ b/device_model/src/virtio/vhost/user/vhost_user_client.rs @@ -28,7 +28,6 @@ use super::super::VhostOps; use super::vhost_user_msg::*; use super::vhost_user_sock::UnixDomainSock; -#[allow(dead_code)] struct ClientInternal { // Used to send requests to the process of filesystem in userspace. sock: UnixDomainSock, @@ -293,15 +292,75 @@ impl VhostOps for VhostUserClient { Ok(()) } - fn set_vring_num(&self, _queue_idx: usize, _num: u16) -> Result<()> { + fn set_vring_num(&self, queue_idx: usize, num: u16) -> Result<()> { + let request = VhostUserMsgReq::SetVringNum as u32; + let payload_opt: Option<&[u8]> = None; + + let client = self.client.lock().unwrap(); + if queue_idx as u64 > client.max_queue_num { + bail!( + "The queue index {} is invaild {} for setting vring num", + queue_idx, + client.max_queue_num + ); + } + + let vringstate = VhostUserVringState::new(queue_idx as u32, num as u32); + let hdr = VhostUserMsgHeader::new(request, 0, size_of::() as u32); + client + .sock + .vhostuser_msgsend(Some(&hdr), Some(&vringstate), payload_opt, &[]) + .chain_err(|| "Failed to send msg for setting vring num")?; + Ok(()) } - fn set_vring_addr(&self, _queue: &QueueConfig, _index: usize, _flags: u32) -> Result<()> { + fn set_vring_addr(&self, queue: &QueueConfig, index: usize, flags: u32) -> Result<()> { + let vringaddr = VhostUserVringAddr { + index: index as u32, + flags, + desc_user_addr: queue.desc_table.0, + used_user_addr: queue.used_ring.0, + avail_user_addr: queue.avail_ring.0, + log_guest_addr: 0, + }; + let payload_opt: Option<&[u8]> = None; + + let hdr = VhostUserMsgHeader::new( + VhostUserMsgReq::SetVringAddr as u32, + 0, + size_of::() as u32, + ); + let client = self.client.lock().unwrap(); + client + .sock + .vhostuser_msgsend(Some(&hdr), Some(&vringaddr), payload_opt, &[]) + .chain_err(|| "Failed to send msg for setting vring addr")?; + Ok(()) } - fn set_vring_base(&self, _queue_idx: usize, _last_avail_idx: u16) -> Result<()> { + fn set_vring_base(&self, queue_idx: usize, last_avail_idx: u16) -> Result<()> { + let request = VhostUserMsgReq::SetVringBase as u32; + let payload_opt: Option<&[u8]> = None; + + let client = self.client.lock().unwrap(); + if queue_idx as u64 > client.max_queue_num { + bail!( + "The queue index {} is invaild {} for setting vring base", + queue_idx, + client.max_queue_num + ); + } + + let vringstate = VhostUserVringState::new(queue_idx as u32, last_avail_idx as u32); + let hdr = VhostUserMsgHeader::new(request, 0, size_of::() as u32); + + client + .sock + .vhostuser_msgsend(Some(&hdr), Some(&vringstate), payload_opt, &[]) + .chain_err(|| "Failed to send msg for setting vring base")?; + Ok(()) } diff --git a/device_model/src/virtio/vhost/user/vhost_user_msg.rs b/device_model/src/virtio/vhost/user/vhost_user_msg.rs index 7e8aac829..811890aa2 100644 --- a/device_model/src/virtio/vhost/user/vhost_user_msg.rs +++ b/device_model/src/virtio/vhost/user/vhost_user_msg.rs @@ -176,12 +176,12 @@ impl VhostUserMsgHeader { (self.flags & VhostUserHeaderFlag::NeedReply as u32) != 0 } - ///Check whether reply for message. + /// Check whether reply for message. pub fn is_reply(&self) -> bool { (self.flags & VhostUserHeaderFlag::Reply as u32) != 0 } - ///Check the header of vhost user message is invaild + /// Check the header of vhost user message is invaild #[allow(dead_code)] pub fn is_invalid(&self) -> bool { self.request >= VhostUserMsgReq::MaxCmd as u32 @@ -245,3 +245,33 @@ impl VhostUserMemoryContext { self.regions.push(region); } } + +/// The configuraton for the state of virtual ring +pub struct VhostUserVringState { + /// Index for virtual ring + pub index: u32, + /// A common 32bit value to encapsulate vring state etc. + pub value: u32, +} + +impl VhostUserVringState { + pub fn new(index: u32, value: u32) -> Self { + VhostUserVringState { index, value } + } +} + +///The configuration for the address of virtual ring +pub struct VhostUserVringAddr { + /// Index for virtual ring + pub index: u32, + /// The option for virtual ring + pub flags: u32, + /// Address of the descriptor table. + pub desc_user_addr: u64, + /// Address of the used ring. + pub used_user_addr: u64, + /// Address of the available ring. + pub avail_user_addr: u64, + /// Guest address for logging. + pub log_guest_addr: u64, +} -- Gitee From 1cd762cddb95c379a10612211598ddcffd2ebe50 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Mon, 1 Feb 2021 20:00:17 +0800 Subject: [PATCH 08/55] device_model: implement the messages of SetVringCall, SetVringKick and SetVringEnable for vhost user client Signed-off-by: Fei Xu --- .../virtio/vhost/user/vhost_user_client.rs | 62 ++++++++++++++++++- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/device_model/src/virtio/vhost/user/vhost_user_client.rs b/device_model/src/virtio/vhost/user/vhost_user_client.rs index fade26441..a352a450b 100644 --- a/device_model/src/virtio/vhost/user/vhost_user_client.rs +++ b/device_model/src/virtio/vhost/user/vhost_user_client.rs @@ -364,15 +364,71 @@ impl VhostOps for VhostUserClient { Ok(()) } - fn set_vring_call(&self, _queue_idx: usize, _fd: &EventFd) -> Result<()> { + fn set_vring_call(&self, queue_idx: usize, fd: &EventFd) -> Result<()> { + let request = VhostUserMsgReq::SetVringCall as u32; + let payload_opt: Option<&[u8]> = None; + + let client = self.client.lock().unwrap(); + if queue_idx as u64 > client.max_queue_num { + bail!( + "The queue index {} is invaild {} for setting vring call", + queue_idx, + client.max_queue_num + ); + } + + let hdr = VhostUserMsgHeader::new(request, 0, size_of::() as u32); + client + .sock + .vhostuser_msgsend(Some(&hdr), Some(&queue_idx), payload_opt, &[fd.as_raw_fd()]) + .chain_err(|| "Failed to send msg for setting vring call")?; + Ok(()) } - fn set_vring_kick(&self, _queue_idx: usize, _fd: &EventFd) -> Result<()> { + fn set_vring_kick(&self, queue_idx: usize, fd: &EventFd) -> Result<()> { + let request = VhostUserMsgReq::SetVringKick as u32; + let payload_opt: Option<&[u8]> = None; + + let client = self.client.lock().unwrap(); + if queue_idx as u64 > client.max_queue_num { + bail!( + "The queue index {} is invaild {} for setting vring kick", + queue_idx, + client.max_queue_num + ); + } + + let hdr = VhostUserMsgHeader::new(request, 0, size_of::() as u32); + client + .sock + .vhostuser_msgsend(Some(&hdr), Some(&queue_idx), payload_opt, &[fd.as_raw_fd()]) + .chain_err(|| "Failed to send msg for setting vring kick")?; + Ok(()) } - fn set_vring_enable(&self, _queue_idx: usize, _status: bool) -> Result<()> { + fn set_vring_enable(&self, queue_idx: usize, status: bool) -> Result<()> { + let request = VhostUserMsgReq::SetVringEnable as u32; + let payload_opt: Option<&[u8]> = None; + + let client = self.client.lock().unwrap(); + if queue_idx as u64 > client.max_queue_num { + bail!( + "The queue index {} is invaild {} for setting vring enable", + queue_idx, + client.max_queue_num + ); + } + + let vringstate = VhostUserVringState::new(queue_idx as u32, status as u32); + let hdr = VhostUserMsgHeader::new(request, 0, size_of::() as u32); + + client + .sock + .vhostuser_msgsend(Some(&hdr), Some(&vringstate), payload_opt, &[]) + .chain_err(|| "Failed to send msg for setting vring enable")?; + Ok(()) } } -- Gitee From 83caaa920c12513812da24d19974313d78b29d70 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Mon, 1 Feb 2021 20:20:31 +0800 Subject: [PATCH 09/55] device_model: realize that virtio fs device is activated 1.Send management messages to the userspace filesystem process 2.Add interrupt events to EventLoop for interrupt processing Signed-off-by: Fei Xu --- device_model/src/virtio/vhost/user/fs.rs | 157 ++++++++++++++++++++++- 1 file changed, 150 insertions(+), 7 deletions(-) diff --git a/device_model/src/virtio/vhost/user/fs.rs b/device_model/src/virtio/vhost/user/fs.rs index e23427b5c..5f1d73004 100644 --- a/device_model/src/virtio/vhost/user/fs.rs +++ b/device_model/src/virtio/vhost/user/fs.rs @@ -12,18 +12,25 @@ use std::cmp; use std::io::Write; -use std::sync::atomic::AtomicU32; +use std::os::unix::io::{AsRawFd, RawFd}; +use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::{Arc, Mutex}; +use vmm_sys_util::epoll::EventSet; use vmm_sys_util::eventfd::EventFd; use address_space::AddressSpace; -use machine_manager::config::{FsConfig, MAX_TAG_LENGTH}; +use machine_manager::{ + config::{FsConfig, MAX_TAG_LENGTH}, + event_loop::EventLoop, +}; use util::byte_code::ByteCode; +use util::loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}; use util::num_ops::{read_u32, write_u32}; use super::super::super::errors::{ErrorKind, Result, ResultExt}; -use super::super::super::{Queue, VirtioDevice, VIRTIO_TYPE_FS}; +use super::super::super::{Queue, VirtioDevice, VIRTIO_MMIO_INT_VRING, VIRTIO_TYPE_FS}; +use super::super::{VhostNotify, VhostOps}; use super::VhostUserClient; // The num of high priority queue @@ -51,6 +58,46 @@ impl Default for VirtioFsConfig { impl ByteCode for VirtioFsConfig {} +struct VhostUserFsHandler { + interrupt_evt: EventFd, + interrupt_status: Arc, + host_notifies: Vec, +} + +impl EventNotifierHelper for VhostUserFsHandler { + fn internal_notifiers(vhost_user_handler: Arc>) -> Vec { + let mut notifiers = Vec::new(); + let vhost_user = vhost_user_handler.clone(); + + let handler: Box Option>> = + Box::new(move |_, fd: RawFd| { + read_fd(fd); + + let v = vhost_user.lock().unwrap(); + v.interrupt_status + .fetch_or(VIRTIO_MMIO_INT_VRING, Ordering::SeqCst); + if v.interrupt_evt.write(1).is_err() { + error!("Failed to write interrupt eventfd for virtio fs"); + } + + None as Option> + }); + let h = Arc::new(Mutex::new(handler)); + + for host_notify in vhost_user_handler.lock().unwrap().host_notifies.iter() { + notifiers.push(EventNotifier::new( + NotifierOperation::AddShared, + host_notify.notify_evt.as_raw_fd(), + None, + EventSet::IN, + vec![h.clone()], + )); + } + + notifiers + } +} + pub struct Fs { fs_cfg: FsConfig, config: VirtioFsConfig, @@ -84,6 +131,9 @@ impl VirtioDevice for Fs { .chain_err(|| { "Failed to create the client which communicates with the server for virtio fs" })?; + self.avail_features = client + .get_features() + .chain_err(|| "Failed to get features for virtio fs")?; self.client = Some(client); Ok(()) @@ -147,11 +197,104 @@ impl VirtioDevice for Fs { fn activate( &mut self, _mem_space: Arc, - _interrupt_evt: EventFd, - _interrupt_status: Arc, - _queues: Vec>>, - _queue_evts: Vec, + interrupt_evt: EventFd, + interrupt_status: Arc, + queues: Vec>>, + queue_evts: Vec, ) -> Result<()> { + let mut host_notifies = Vec::new(); + let client = match &self.client { + None => return Err("Failed to get client for virtio fs".into()), + Some(client_) => client_, + }; + + client + .set_owner() + .chain_err(|| "Failed to set owner for virtio fs")?; + client + .set_features(self.acked_features) + .chain_err(|| "Failed to set features for virtio fs")?; + client + .set_mem_table() + .chain_err(|| "Failed to set mem table for virtio fs")?; + + for (queue_index, queue_mutex) in queues.to_vec().iter().enumerate() { + let queue = queue_mutex.lock().unwrap(); + let actual_size = queue.vring.actual_size(); + let queue_config = queue.vring.get_queue_config(); + + client.set_vring_enable(queue_index, false).chain_err(|| { + format!( + "Failed to set vring enable for virtio fs, index: {}, false", + queue_index, + ) + })?; + client + .set_vring_num(queue_index, actual_size) + .chain_err(|| { + format!( + "Failed to set vring num for virtio fs, index: {}, size: {}", + queue_index, actual_size, + ) + })?; + client + .set_vring_addr(&queue_config, queue_index, 0) + .chain_err(|| { + format!( + "Failed to set vring addr for virtio fs, index: {}", + queue_index, + ) + })?; + client.set_vring_base(queue_index, 0).chain_err(|| { + format!( + "Failed to set vring base for virtio fs, index: {}", + queue_index, + ) + })?; + client + .set_vring_kick(queue_index, &queue_evts[queue_index]) + .chain_err(|| { + format!( + "Failed to set vring kick for virtio fs, index: {}", + queue_index, + ) + })?; + + drop(queue); + + let host_notify = VhostNotify { + notify_evt: EventFd::new(libc::EFD_NONBLOCK) + .chain_err(|| ErrorKind::EventFdCreate)?, + queue: queue_mutex.clone(), + }; + client + .set_vring_call(queue_index, &host_notify.notify_evt) + .chain_err(|| { + format!( + "Failed to set vring call for virtio fs, index: {}", + queue_index, + ) + })?; + host_notifies.push(host_notify); + + client.set_vring_enable(queue_index, true).chain_err(|| { + format!( + "Failed to set vring enable for virtio fs, index: {}, true", + queue_index, + ) + })?; + } + + let handler = VhostUserFsHandler { + interrupt_evt: interrupt_evt.try_clone()?, + interrupt_status, + host_notifies, + }; + EventLoop::update_event( + EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new(handler))), + None, + )?; + Ok(()) } } -- Gitee From fdb1393964fcd11ebdcdda4cc6346e38e6f0b7ba Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Wed, 7 Apr 2021 10:04:48 +0800 Subject: [PATCH 10/55] vhost_user_fs: add the binary of vhost_user_fs for the userspace filesystem process 1.Implement parameter analysis which contains source directory in host and socket file for communicating with StratoVirt 2.Initialize log file record and process panic hook Signed-off-by: Fei Xu --- Cargo.lock | 220 ++++++++++++++++++----------------- Cargo.toml | 6 + src/vhost_user_fs.rs | 91 +++++++++++++++ vhost_user_fs/Cargo.toml | 12 ++ vhost_user_fs/src/cmdline.rs | 106 +++++++++++++++++ vhost_user_fs/src/lib.rs | 28 +++++ 6 files changed, 357 insertions(+), 106 deletions(-) create mode 100644 src/vhost_user_fs.rs create mode 100644 vhost_user_fs/Cargo.toml create mode 100644 vhost_user_fs/src/cmdline.rs create mode 100644 vhost_user_fs/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index fe913a535..59c909594 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,298 +4,306 @@ name = "StratoVirt" version = "0.2.0" dependencies = [ - "device_model 0.2.0", - "error-chain 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "machine_manager 0.2.0", - "util 0.2.0", - "vmm-sys-util 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "device_model", + "error-chain", + "libc", + "log", + "machine_manager", + "util", + "vhost_user_fs", + "vmm-sys-util", ] [[package]] name = "addr2line" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a49806b9dadc843c61e7c97e72490ad7f7220ae249012fbda9ad0609457c0543" dependencies = [ - "gimli 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gimli", ] [[package]] name = "address_space" version = "0.2.0" dependencies = [ - "error-chain 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", - "kvm-bindings 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kvm-ioctls 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "machine_manager 0.2.0", - "util 0.2.0", - "vmm-sys-util 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain", + "kvm-bindings", + "kvm-ioctls", + "libc", + "log", + "machine_manager", + "util", + "vmm-sys-util", ] [[package]] name = "adler32" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b077b825e468cc974f0020d4082ee6e03132512f207ef1a02fd5d00d1f32d" [[package]] name = "backtrace" version = "0.3.49" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05100821de9e028f12ae3d189176b41ee198341eb8f369956407fea2f5cc666c" dependencies = [ - "addr2line 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz_oxide 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "object 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", ] [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "boot_loader" version = "0.2.0" dependencies = [ - "address_space 0.2.0", - "error-chain 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", - "kvm-bindings 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kvm-ioctls 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "util 0.2.0", - "vmm-sys-util 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "address_space", + "error-chain", + "kvm-bindings", + "kvm-ioctls", + "libc", + "log", + "util", + "vmm-sys-util", ] [[package]] name = "byteorder" version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "device_model" version = "0.2.0" dependencies = [ - "address_space 0.2.0", - "boot_loader 0.2.0", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", - "kvm-bindings 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kvm-ioctls 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "machine_manager 0.2.0", - "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", - "util 0.2.0", - "vmm-sys-util 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "address_space", + "boot_loader", + "byteorder", + "error-chain", + "kvm-bindings", + "kvm-ioctls", + "libc", + "log", + "machine_manager", + "serde", + "serde_json", + "util", + "vmm-sys-util", ] [[package]] name = "error-chain" version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" dependencies = [ - "backtrace 0.3.49 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace", + "version_check", ] [[package]] name = "gimli" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc8e0c9bce37868955864dbecd2b1ab2bdf967e6f28066d65aaac620444b65c" [[package]] name = "itoa" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" [[package]] name = "kvm-bindings" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1c07667561c3d12d77342baf28ca5d0f8c3ea2160778485640ec84a4571da2" dependencies = [ - "vmm-sys-util 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "vmm-sys-util", ] [[package]] name = "kvm-ioctls" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "158d15da895bddca8223fa31dc9e8b9317bdc2fbc4635dea8dd575fc40dae37f" dependencies = [ - "kvm-bindings 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", - "vmm-sys-util 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "kvm-bindings", + "libc", + "vmm-sys-util", ] [[package]] name = "libc" version = "0.2.71" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" [[package]] name = "log" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if", ] [[package]] name = "machine_manager" version = "0.2.0" dependencies = [ - "error-chain 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)", - "util 0.2.0", - "vmm-sys-util 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain", + "libc", + "log", + "serde", + "serde_json", + "util", + "vmm-sys-util", ] [[package]] name = "miniz_oxide" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" dependencies = [ - "adler32 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "adler32", ] [[package]] name = "object" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5" [[package]] name = "proc-macro2" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" dependencies = [ - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid", ] [[package]] name = "quote" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" dependencies = [ - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", ] [[package]] name = "rustc-demangle" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" [[package]] name = "ryu" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" [[package]] name = "serde" version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3" dependencies = [ - "serde_derive 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e" dependencies = [ - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "serde_json" version = "1.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec2c5d7e739bc07a3e73381a39d61fdb5f671c60c1df26a130690665803d8226" dependencies = [ - "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa", + "ryu", + "serde", ] [[package]] name = "syn" version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239f255b9e3429350f188c27b807fc9920a15eb9145230ff1a7d054c08fec319" dependencies = [ - "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "unicode-xid", ] [[package]] name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" [[package]] name = "util" version = "0.2.0" dependencies = [ - "error-chain 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", - "kvm-bindings 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "kvm-ioctls 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "vmm-sys-util 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain", + "kvm-bindings", + "kvm-ioctls", + "libc", + "log", + "vmm-sys-util", ] [[package]] name = "version_check" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + +[[package]] +name = "vhost_user_fs" +version = "0.1.0" +dependencies = [ + "error-chain", + "log", + "util", +] [[package]] name = "vmm-sys-util" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "183d25b56a61a6f518ef464ac578e790f04added34dfaab59a453d8a03cb7bd0" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", + "libc", ] - -[metadata] -"checksum addr2line 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a49806b9dadc843c61e7c97e72490ad7f7220ae249012fbda9ad0609457c0543" -"checksum adler32 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "567b077b825e468cc974f0020d4082ee6e03132512f207ef1a02fd5d00d1f32d" -"checksum backtrace 0.3.49 (registry+https://github.com/rust-lang/crates.io-index)" = "05100821de9e028f12ae3d189176b41ee198341eb8f369956407fea2f5cc666c" -"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" -"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" -"checksum error-chain 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" -"checksum gimli 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc8e0c9bce37868955864dbecd2b1ab2bdf967e6f28066d65aaac620444b65c" -"checksum itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" -"checksum kvm-bindings 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1c07667561c3d12d77342baf28ca5d0f8c3ea2160778485640ec84a4571da2" -"checksum kvm-ioctls 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "158d15da895bddca8223fa31dc9e8b9317bdc2fbc4635dea8dd575fc40dae37f" -"checksum libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)" = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" -"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" -"checksum miniz_oxide 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" -"checksum object 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5" -"checksum proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" -"checksum quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" -"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" -"checksum ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" -"checksum serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)" = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3" -"checksum serde_derive 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)" = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e" -"checksum serde_json 1.0.55 (registry+https://github.com/rust-lang/crates.io-index)" = "ec2c5d7e739bc07a3e73381a39d61fdb5f671c60c1df26a130690665803d8226" -"checksum syn 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "239f255b9e3429350f188c27b807fc9920a15eb9145230ff1a7d054c08fec319" -"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" -"checksum version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" -"checksum vmm-sys-util 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "183d25b56a61a6f518ef464ac578e790f04added34dfaab59a453d8a03cb7bd0" diff --git a/Cargo.toml b/Cargo.toml index ad1c31e47..445fcad65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ license = "Mulan PSL v2" util = { path = "util" } machine_manager = { path = "machine_manager" } device_model = { path = "device_model" } +vhost_user_fs = { path = "vhost_user_fs" } libc = "0.2.71" log = "0.4.8" @@ -23,12 +24,17 @@ members = [ "boot_loader", "util", "device_model", + "vhost_user_fs", ] [[bin]] name = "stratovirt" path = "src/main.rs" +[[bin]] +name = "vhost_user_fs" +path = "src/vhost_user_fs.rs" + [features] default = [] diff --git a/src/vhost_user_fs.rs b/src/vhost_user_fs.rs new file mode 100644 index 000000000..250f2e74e --- /dev/null +++ b/src/vhost_user_fs.rs @@ -0,0 +1,91 @@ +// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +#[macro_use] +extern crate error_chain; +#[macro_use] +extern crate log; + +use std::os::unix::fs::OpenOptionsExt; + +use util::{arg_parser, logger}; +use vhost_user_fs::cmdline::{create_args_parser, create_config, FsConfig}; + +error_chain! { + links { + VhostUserFs(vhost_user_fs::errors::Error, vhost_user_fs::errors::ErrorKind); + Util(util::errors::Error, util::errors::ErrorKind); + } + foreign_links { + Io(std::io::Error); + } +} + +quick_main!(run); + +fn run() -> Result<()> { + let cmd_args = create_args_parser().get_matches()?; + + if let Some(logfile_path) = cmd_args.value_of("display log") { + init_log(logfile_path)?; + } + + set_panic_hook(); + + match real_main(&cmd_args) { + Ok(()) => info!("MainLoop over, Vm exit"), + Err(ref e) => { + error!("{}", error_chain::ChainedError::display_chain(e)); + } + } + + Ok(()) +} + +fn real_main(cmd_args: &arg_parser::ArgMatches) -> Result<()> { + let fsconfig: FsConfig = create_config(cmd_args)?; + info!("FsConfig is {:?}", fsconfig); + + Ok(()) +} + +fn init_log(logfile_path: String) -> Result<()> { + if logfile_path.is_empty() { + logger::init_logger_with_env(Some(Box::new(std::io::stdout()))) + .chain_err(|| "Failed to init logger")?; + } else { + let logfile = std::fs::OpenOptions::new() + .read(false) + .write(true) + .append(true) + .create(true) + .mode(0o640) + .open(logfile_path.clone()) + .chain_err(|| format!("Failed to open log file {}", logfile_path))?; + logger::init_logger_with_env(Some(Box::new(logfile))) + .chain_err(|| format!("Failed to init logger {}", logfile_path))?; + } + + Ok(()) +} + +fn set_panic_hook() { + std::panic::set_hook(Box::new(|panic_msg| { + let panic_file = panic_msg.location().map_or("", |loc| loc.file()); + let panic_line = panic_msg.location().map_or(0, |loc| loc.line()); + if let Some(msg) = panic_msg.payload().downcast_ref::<&str>() { + error!("Panic at [{}: {}]: {}.", panic_file, panic_line, msg); + } else { + error!("Panic at [{}: {}].", panic_file, panic_line); + } + })); +} diff --git a/vhost_user_fs/Cargo.toml b/vhost_user_fs/Cargo.toml new file mode 100644 index 000000000..3512d8d2d --- /dev/null +++ b/vhost_user_fs/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "vhost_user_fs" +version = "0.1.0" +authors = ["Huawei StratoVirt Team"] +license = "Mulan PSL v2" +description = "provide virtio fs for VM" + +[dependencies] +log = "0.4.8" +error-chain = "0.12.4" + +util = { path = "../util" } diff --git a/vhost_user_fs/src/cmdline.rs b/vhost_user_fs/src/cmdline.rs new file mode 100644 index 000000000..091dca962 --- /dev/null +++ b/vhost_user_fs/src/cmdline.rs @@ -0,0 +1,106 @@ +// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::path::PathBuf; + +use util::arg_parser::{Arg, ArgMatches, ArgParser}; + +use crate::errors::{Result, ResultExt}; + +// Read the programe version in `Cargo.toml`. +const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); + +const MAX_STRING_LENGTH: usize = 255; + +/// This function is to define all commandline arguments. +pub fn create_args_parser<'a>() -> ArgParser<'a> { + ArgParser::new("VhostUserFs") + .version(VERSION.unwrap_or("unknown")) + .author("Huawei Technologies Co., Ltd") + .about("The process of Virtio fs for StratoVirt.") + .arg( + Arg::with_name("source dir") + .long("dir") + .value_name("source directory in host") + .help("set source directory in host") + .takes_value(true) + .required(true), + ) + .arg( + Arg::with_name("sock") + .long("sock") + .value_name("sock file path which communicates with StratoVirt") + .help("sock file path which communicates with StratoVirt") + .takes_value(true) + .required(true), + ) + .arg( + Arg::with_name("display log") + .long("D") + .value_name("log_path") + .help("output log to logfile") + .takes_value(true) + .can_no_value(true), + ) +} + +#[derive(Clone, Default, Debug)] +pub struct FsConfig { + pub source_dir: String, + pub sock_path: String, +} + +impl FsConfig { + pub fn check_config(&self) -> Result<()> { + if self.source_dir.len() > MAX_STRING_LENGTH { + bail!( + "The length of source directory is too long {}", + self.source_dir.len() + ); + } + + if self.sock_path.len() > MAX_STRING_LENGTH { + bail!( + "The length of socket file path is too long {}", + self.sock_path.len() + ); + } + + let source_dir = PathBuf::from(&self.source_dir); + if !source_dir.is_dir() { + bail!( + "The source directory is not a directory {}", + self.source_dir + ); + } + + Ok(()) + } +} + +pub fn create_config(args: &ArgMatches) -> Result { + let mut fs_config = FsConfig::default(); + + if let Some(source_dir) = args.value_of("source dir") { + fs_config.source_dir = source_dir; + } + + if let Some(sock_path) = args.value_of("sock") { + fs_config.sock_path = sock_path; + } + + fs_config + .check_config() + .chain_err(|| "Precheck failed, Config is unhealthy, stop running")?; + + Ok(fs_config) +} diff --git a/vhost_user_fs/src/lib.rs b/vhost_user_fs/src/lib.rs new file mode 100644 index 000000000..d3c608f5e --- /dev/null +++ b/vhost_user_fs/src/lib.rs @@ -0,0 +1,28 @@ +// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +#[macro_use] +extern crate error_chain; +extern crate util; + +pub mod cmdline; + +pub mod errors { + error_chain! { + links { + Util(util::errors::Error, util::errors::ErrorKind); + } + foreign_links { + Io(std::io::Error); + } + } +} -- Gitee From 4752d068149719b8724c6286206f849428e2c7fb Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Wed, 7 Apr 2021 10:18:19 +0800 Subject: [PATCH 11/55] vhost_user_fs: add the server code for vhost user fs 1.Parse unix socket messages from the client of StratoVirt for vhost user fs 2.clear new_without_default warnings for cargo clippy Signed-off-by: Fei Xu --- Cargo.lock | 4 + device_model/src/lib.rs | 2 +- device_model/src/mmio/bus.rs | 4 +- device_model/src/virtio/block.rs | 6 + device_model/src/virtio/mod.rs | 2 +- device_model/src/virtio/net.rs | 6 + device_model/src/virtio/vhost/kernel/mod.rs | 10 +- .../virtio/vhost/user/vhost_user_client.rs | 2 +- .../src/virtio/vhost/user/vhost_user_msg.rs | 8 +- src/vhost_user_fs.rs | 1 + vhost_user_fs/Cargo.toml | 4 + vhost_user_fs/src/lib.rs | 8 + vhost_user_fs/src/vhost_user_server.rs | 336 ++++++++++++++++++ 13 files changed, 385 insertions(+), 8 deletions(-) create mode 100644 vhost_user_fs/src/vhost_user_server.rs diff --git a/Cargo.lock b/Cargo.lock index 59c909594..4596e1e61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -293,9 +293,13 @@ checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" name = "vhost_user_fs" version = "0.1.0" dependencies = [ + "device_model", "error-chain", + "libc", "log", + "machine_manager", "util", + "vmm-sys-util", ] [[package]] diff --git a/device_model/src/lib.rs b/device_model/src/lib.rs index e9ba4bf0f..003bf6c47 100644 --- a/device_model/src/lib.rs +++ b/device_model/src/lib.rs @@ -43,7 +43,7 @@ mod interrupt_controller; mod legacy; mod micro_vm; mod mmio; -mod virtio; +pub mod virtio; pub use error_chain::*; pub use micro_vm::{ diff --git a/device_model/src/mmio/bus.rs b/device_model/src/mmio/bus.rs index 10983151f..e8fda3d3c 100644 --- a/device_model/src/mmio/bus.rs +++ b/device_model/src/mmio/bus.rs @@ -107,7 +107,7 @@ impl Bus { }; for _ in 0..MMIO_REPLACEABLE_BLK_NR { - let block = Arc::new(Mutex::new(Block::new())); + let block = Arc::new(Mutex::new(Block::default())); let device = Arc::new(Mutex::new(VirtioMmioDevice::new(sys_mem.clone(), block))); if let Ok(dev) = bus.attach_device(device.clone()) { bus.replaceable_info @@ -123,7 +123,7 @@ impl Bus { } for _ in 0..MMIO_REPLACEABLE_NET_NR { - let net = Arc::new(Mutex::new(Net::new())); + let net = Arc::new(Mutex::new(Net::default())); let device = Arc::new(Mutex::new(VirtioMmioDevice::new(sys_mem.clone(), net))); if let Ok(dev) = bus.attach_device(device.clone()) { bus.replaceable_info diff --git a/device_model/src/virtio/block.rs b/device_model/src/virtio/block.rs index ecfe8fd09..c7be340f4 100644 --- a/device_model/src/virtio/block.rs +++ b/device_model/src/virtio/block.rs @@ -767,6 +767,12 @@ impl Block { } } +impl Default for Block { + fn default() -> Self { + Self::new() + } +} + impl VirtioDevice for Block { /// Realize virtio block device. fn realize(&mut self) -> Result<()> { diff --git a/device_model/src/virtio/mod.rs b/device_model/src/virtio/mod.rs index 4ec155864..c1e994577 100644 --- a/device_model/src/virtio/mod.rs +++ b/device_model/src/virtio/mod.rs @@ -28,7 +28,7 @@ pub mod balloon; pub mod block; pub mod console; pub mod net; -mod queue; +pub mod queue; pub mod vhost; pub use self::block::Block; diff --git a/device_model/src/virtio/net.rs b/device_model/src/virtio/net.rs index 075b16503..755c6a99a 100644 --- a/device_model/src/virtio/net.rs +++ b/device_model/src/virtio/net.rs @@ -566,6 +566,12 @@ impl Net { } } +impl Default for Net { + fn default() -> Self { + Self::new() + } +} + impl VirtioDevice for Net { /// Realize virtio network device. fn realize(&mut self) -> Result<()> { diff --git a/device_model/src/virtio/vhost/kernel/mod.rs b/device_model/src/virtio/vhost/kernel/mod.rs index 47c2a13f4..bf80e479b 100644 --- a/device_model/src/virtio/vhost/kernel/mod.rs +++ b/device_model/src/virtio/vhost/kernel/mod.rs @@ -126,7 +126,7 @@ pub struct VhostMemInfo { } impl VhostMemInfo { - pub fn new() -> VhostMemInfo { + fn new() -> VhostMemInfo { VhostMemInfo { regions: Arc::new(Mutex::new(Vec::new())), } @@ -183,6 +183,12 @@ impl VhostMemInfo { } } +impl Default for VhostMemInfo { + fn default() -> Self { + Self::new() + } +} + impl Listener for VhostMemInfo { fn priority(&self) -> i32 { 0 @@ -232,7 +238,7 @@ impl VhostBackend { .open(path) .chain_err(|| format!("Failed to open {} for vhost backend.", path))?, }; - let mem_info = VhostMemInfo::new(); + let mem_info = VhostMemInfo::default(); mem_space.register_listener(Box::new(mem_info.clone()))?; Ok(VhostBackend { fd, mem_info }) diff --git a/device_model/src/virtio/vhost/user/vhost_user_client.rs b/device_model/src/virtio/vhost/user/vhost_user_client.rs index a352a450b..8ef9df670 100644 --- a/device_model/src/virtio/vhost/user/vhost_user_client.rs +++ b/device_model/src/virtio/vhost/user/vhost_user_client.rs @@ -266,7 +266,7 @@ impl VhostOps for VhostUserClient { let mut fds = Vec::with_capacity(num_region); let memhdr = VhostUserMemoryHdr::new(num_region as u32, 0); - let mut memcontext = VhostUserMemoryContext::new(); + let mut memcontext = VhostUserMemoryContext::default(); for region_info in self.mem_info.regions.lock().unwrap().iter() { memcontext.region_add(region_info.region.clone()); diff --git a/device_model/src/virtio/vhost/user/vhost_user_msg.rs b/device_model/src/virtio/vhost/user/vhost_user_msg.rs index 811890aa2..738bb295a 100644 --- a/device_model/src/virtio/vhost/user/vhost_user_msg.rs +++ b/device_model/src/virtio/vhost/user/vhost_user_msg.rs @@ -235,7 +235,7 @@ pub struct VhostUserMemoryContext { } impl VhostUserMemoryContext { - pub fn new() -> Self { + fn new() -> Self { VhostUserMemoryContext { regions: Vec::new(), } @@ -246,6 +246,12 @@ impl VhostUserMemoryContext { } } +impl Default for VhostUserMemoryContext { + fn default() -> Self { + Self::new() + } +} + /// The configuraton for the state of virtual ring pub struct VhostUserVringState { /// Index for virtual ring diff --git a/src/vhost_user_fs.rs b/src/vhost_user_fs.rs index 250f2e74e..ab98b74a6 100644 --- a/src/vhost_user_fs.rs +++ b/src/vhost_user_fs.rs @@ -14,6 +14,7 @@ extern crate error_chain; #[macro_use] extern crate log; +extern crate vhost_user_fs; use std::os::unix::fs::OpenOptionsExt; diff --git a/vhost_user_fs/Cargo.toml b/vhost_user_fs/Cargo.toml index 3512d8d2d..1fc8944dc 100644 --- a/vhost_user_fs/Cargo.toml +++ b/vhost_user_fs/Cargo.toml @@ -7,6 +7,10 @@ description = "provide virtio fs for VM" [dependencies] log = "0.4.8" +libc = "0.2.71" error-chain = "0.12.4" +vmm-sys-util = "0.6.1" util = { path = "../util" } +device_model = { path = "../device_model" } +machine_manager = { path = "../machine_manager" } diff --git a/vhost_user_fs/src/lib.rs b/vhost_user_fs/src/lib.rs index d3c608f5e..e82f4eb97 100644 --- a/vhost_user_fs/src/lib.rs +++ b/vhost_user_fs/src/lib.rs @@ -12,14 +12,22 @@ #[macro_use] extern crate error_chain; +#[macro_use] +extern crate log; +extern crate vmm_sys_util; + +extern crate device_model; +extern crate machine_manager; extern crate util; pub mod cmdline; +pub mod vhost_user_server; pub mod errors { error_chain! { links { Util(util::errors::Error, util::errors::ErrorKind); + DeviceModel(device_model::errors::Error, device_model::errors::ErrorKind); } foreign_links { Io(std::io::Error); diff --git a/vhost_user_fs/src/vhost_user_server.rs b/vhost_user_fs/src/vhost_user_server.rs new file mode 100644 index 000000000..012982bdb --- /dev/null +++ b/vhost_user_fs/src/vhost_user_server.rs @@ -0,0 +1,336 @@ +// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::mem::size_of; +use std::os::unix::io::RawFd; +use std::slice; + +use device_model::virtio::vhost::user::vhost_user_msg::*; +use device_model::virtio::vhost::user::vhost_user_sock::{UnixDomainSock, MAX_ATTACHED_FD_ENTRIES}; +use util::unix::limit_permission; + +use crate::errors::{Result, ResultExt}; + +#[derive(Clone)] +pub struct VhostUserServerHandler { + pub sock: UnixDomainSock, +} + +fn close_fds(fds: Vec) { + for fd in fds { + let _ = unsafe { libc::close(fd) }; + } +} + +fn is_invalid_fds(hdr: &mut VhostUserMsgHeader, rfds: Option>) -> Result<()> { + match VhostUserMsgReq::from(hdr.request) { + VhostUserMsgReq::SetMemTable => Ok(()), + VhostUserMsgReq::SetVringCall => Ok(()), + VhostUserMsgReq::SetVringKick => Ok(()), + VhostUserMsgReq::SetSlaveReqFd => Ok(()), + _ => { + if rfds.is_some() { + if let Some(fds) = rfds { + close_fds(fds); + } + bail!("The fds is invalid, request: {}", hdr.request); + } else { + Ok(()) + } + } + } +} + +impl VhostUserServerHandler { + pub fn new(path: &str) -> Result { + let mut server = UnixDomainSock::new(path); + server + .server_bind(true) + .chain_err(|| format!("Failed to bind for vhost user server {}", path))?; + limit_permission(path).chain_err(|| format!("Failed to limit permission {}", path))?; + + Ok(VhostUserServerHandler { sock: server }) + } + + fn recv_hdr_and_fds(&mut self) -> Result<(VhostUserMsgHeader, Option>)> { + let mut hdr = VhostUserMsgHeader::default(); + let body_opt: Option<&mut u32> = None; + let payload_opt: Option<&mut [u8]> = None; + let mut fds = vec![0; MAX_ATTACHED_FD_ENTRIES]; + + let (rcv_len, fds_num) = self + .sock + .vhostuser_msgrecv(Some(&mut hdr), body_opt, payload_opt, &mut fds) + .chain_err(|| "Failed to recv hdr and fds")?; + + if rcv_len != size_of::() { + bail!( + "The length {} {} of header is invalid", + rcv_len, + size_of::() + ); + } else if hdr.is_invalid() { + bail!( + "The header of vhost user msg is invalid, request: {}, size: {}, flags: {}", + hdr.request, + hdr.size, + hdr.flags + ); + } + + let rfds = match fds_num { + 0 => None, + n => { + let mut fds_temp = Vec::with_capacity(n); + fds_temp.extend_from_slice(&fds[0..n]); + Some(fds_temp) + } + }; + + is_invalid_fds(&mut hdr, rfds.clone())?; + + Ok((hdr, rfds)) + } + + fn recv_body(&mut self, len: usize) -> Result<(usize, Vec)> { + let mut rbuf = vec![0u8; len]; + let body_opt: Option<&mut u32> = None; + let hdr_opt: Option<&mut VhostUserMsgHeader> = None; + + let (rcv_len, _) = self + .sock + .vhostuser_msgrecv(hdr_opt, body_opt, Some(&mut rbuf), &mut []) + .chain_err(|| "Failed to recv msg body")?; + + if rcv_len != len { + bail!("The length of msg body is invalid {} {}", rcv_len, len); + } + + Ok((rcv_len, rbuf)) + } + + fn get_msg_body<'a, D: Sized>( + &self, + hdr: &VhostUserMsgHeader, + buf: &'a [u8], + len: usize, + ) -> Result<&'a D> { + if (hdr.size as usize) != len || size_of::() != len { + bail!( + "Failed to get msg body {} {} {}", + len, + hdr.size, + size_of::() + ); + } + + let body = unsafe { &*(buf.as_ptr() as *const D) }; + Ok(body) + } + + fn send_ack_msg(&mut self, request: u32, res: D, fds: &[RawFd]) -> Result<()> { + let hdr = VhostUserMsgHeader::new( + request, + VhostUserHeaderFlag::Reply as u32, + size_of::() as u32, + ); + let payload_opt: Option<&[u8]> = None; + + self.sock + .vhostuser_msgsend(Some(&hdr), Some(&res), payload_opt, fds) + .chain_err(|| "Failed to send ack msg")?; + + Ok(()) + } + + #[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))] + fn set_msg_mem_table( + &mut self, + hdr: &VhostUserMsgHeader, + buf: &[u8], + len: usize, + fds_opt: Option>, + ) -> Result<()> { + if (hdr.size as usize) != len || (hdr.size as usize) < size_of::() { + if let Some(fds) = fds_opt { + close_fds(fds); + } + bail!( + "The header length of mem table is invalid {} {} {}", + len, + hdr.size, + size_of::() + ); + } + + let memhdrsize = size_of::(); + // The length of buf has been judged above to ensure no buffer overflow + let memhdr = unsafe { &*(buf.as_ptr() as *const VhostUserMemoryHdr) }; + let total_size = + (memhdr.nregion as usize * size_of::()) + memhdrsize; + if (hdr.size as usize) != total_size { + if let Some(fds) = fds_opt { + close_fds(fds); + } + bail!( + "The body length of mem table is invalid {} {}", + hdr.size, + total_size + ); + } + + // The length of buf has been judged above to ensure no buffer overflow + let _regions = unsafe { + slice::from_raw_parts( + buf.as_ptr().add(memhdrsize) as *const VhostUserMemoryRegion, + memhdr.nregion as usize, + ) + }; + + if let Some(fds) = fds_opt { + let fds_len = fds.len(); + if fds_len != (memhdr.nregion as usize) { + close_fds(fds); + bail!( + "The length {} {} of fds for mem table is invalid", + fds_len, + memhdr.nregion + ); + } + } else { + bail!("The fds of mem table is null"); + } + + Ok(()) + } + + fn process_request( + &mut self, + hdr: &VhostUserMsgHeader, + buf: &[u8], + len: usize, + rfds: Option>, + ) -> Result<()> { + match VhostUserMsgReq::from(hdr.request) { + VhostUserMsgReq::GetFeatures => { + if len != 0 { + bail!("The length {} of getting features is invalid", len); + } + + let features = 0_u64; + if hdr.is_need_reply() { + self.send_ack_msg(VhostUserMsgReq::GetFeatures as u32, features, &[]) + .chain_err(|| "Failed to send ack msg for getting features")?; + } + } + VhostUserMsgReq::SetFeatures => { + let _features = self + .get_msg_body::(&hdr, buf, len) + .chain_err(|| "Failed to get msg body for setting features")?; + } + VhostUserMsgReq::SetOwner => { + if len != 0 { + bail!("The length {} of setting owner is invalid", len); + } + } + VhostUserMsgReq::SetMemTable => { + let ret = match self.set_msg_mem_table(&hdr, buf, len, rfds) { + Err(ref e) => { + error!( + "Failed to set mem table {}", + error_chain::ChainedError::display_chain(e) + ); + 1u64 + } + Ok(_) => 0u64, + }; + if hdr.is_need_reply() { + self.send_ack_msg(VhostUserMsgReq::SetMemTable as u32, ret, &[]) + .chain_err(|| "Failed to send ack msg for setting mem table")?; + } + } + VhostUserMsgReq::SetVringNum => { + let _vringstate = self + .get_msg_body::(&hdr, buf, len) + .chain_err(|| "Failed to get msg body for setting vring num")?; + } + VhostUserMsgReq::SetVringAddr => { + let _vringaddr = self + .get_msg_body::(&hdr, buf, len) + .chain_err(|| "Failed to get msg body for setting vring addr")?; + } + VhostUserMsgReq::SetVringBase => { + let _vringstate = self + .get_msg_body::(&hdr, buf, len) + .chain_err(|| "Failed to get msg body for setting vring base")?; + } + VhostUserMsgReq::SetVringEnable => { + let _vringstate = self + .get_msg_body::(&hdr, buf, len) + .chain_err(|| "Failed to get msg body for setting vring enable")?; + } + VhostUserMsgReq::SetVringKick => { + let _index = self + .get_msg_body::(&hdr, buf, len) + .chain_err(|| "Failed to get msg body for setting vring kick")?; + if let Some(fds) = rfds { + let fds_len = fds.len(); + if fds_len != 1 { + close_fds(fds); + bail!("The length {} of fds for kicking is invalid", fds_len); + } + } else { + bail!("The length {} of fds for kicking is null"); + } + } + VhostUserMsgReq::SetVringCall => { + let _index = self + .get_msg_body::(&hdr, buf, len) + .chain_err(|| "Failed to get msg body for setting vring call")?; + if let Some(fds) = rfds { + let fds_len = fds.len(); + if fds_len != 1 { + close_fds(fds); + bail!("The length {} of fds for calling is invalid", fds_len); + } + } else { + bail!("The length {} of fds for calling is null"); + } + } + _ => { + bail!("The request {} is unknown", hdr.request); + } + }; + + Ok(()) + } + + pub fn handle_request(&mut self) -> Result<()> { + let (hdr, rfds) = self + .recv_hdr_and_fds() + .chain_err(|| "Failed to recv header and fds")?; + + let (len, buf) = match hdr.size { + 0 => (0, vec![0u8; 0]), + _ => { + let (rcv_len, rbuf) = self + .recv_body(hdr.size as usize) + .chain_err(|| "Failed to recv msg body")?; + (rcv_len, rbuf) + } + }; + + self.process_request(&hdr, &buf, len, rfds) + .chain_err(|| format!("Failed to process the request {}", hdr.request))?; + Ok(()) + } +} -- Gitee From f4a4708a818bf99c0774d25cbeb16f4e05cffdb5 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Tue, 2 Feb 2021 20:17:11 +0800 Subject: [PATCH 12/55] vhost_user_fs: add the event of unix socket to EventLoop Signed-off-by: Fei Xu --- src/vhost_user_fs.rs | 17 +++- vhost_user_fs/src/lib.rs | 1 + vhost_user_fs/src/vhost_user_fs.rs | 139 +++++++++++++++++++++++++++++ 3 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 vhost_user_fs/src/vhost_user_fs.rs diff --git a/src/vhost_user_fs.rs b/src/vhost_user_fs.rs index ab98b74a6..fe2d205ba 100644 --- a/src/vhost_user_fs.rs +++ b/src/vhost_user_fs.rs @@ -17,9 +17,12 @@ extern crate log; extern crate vhost_user_fs; use std::os::unix::fs::OpenOptionsExt; +use std::sync::Arc; +use machine_manager::event_loop::EventLoop; use util::{arg_parser, logger}; use vhost_user_fs::cmdline::{create_args_parser, create_config, FsConfig}; +use vhost_user_fs::vhost_user_fs::VhostUserFs; error_chain! { links { @@ -43,7 +46,7 @@ fn run() -> Result<()> { set_panic_hook(); match real_main(&cmd_args) { - Ok(()) => info!("MainLoop over, Vm exit"), + Ok(()) => info!("EventLoop over, Vm exit"), Err(ref e) => { error!("{}", error_chain::ChainedError::display_chain(e)); } @@ -56,6 +59,18 @@ fn real_main(cmd_args: &arg_parser::ArgMatches) -> Result<()> { let fsconfig: FsConfig = create_config(cmd_args)?; info!("FsConfig is {:?}", fsconfig); + EventLoop::object_init(&None)?; + + let vhost_user_fs = + Arc::new(VhostUserFs::new(fsconfig).chain_err(|| "Failed to create vhost use fs")?); + EventLoop::set_manager(vhost_user_fs.clone(), None); + + vhost_user_fs + .add_event_notifier() + .chain_err(|| "Failed to add event")?; + + EventLoop::loop_run().chain_err(|| "EventLoop exits unexpectedly: error occurs")?; + Ok(()) } diff --git a/vhost_user_fs/src/lib.rs b/vhost_user_fs/src/lib.rs index e82f4eb97..b9bacb48d 100644 --- a/vhost_user_fs/src/lib.rs +++ b/vhost_user_fs/src/lib.rs @@ -21,6 +21,7 @@ extern crate machine_manager; extern crate util; pub mod cmdline; +pub mod vhost_user_fs; pub mod vhost_user_server; pub mod errors { diff --git a/vhost_user_fs/src/vhost_user_fs.rs b/vhost_user_fs/src/vhost_user_fs.rs new file mode 100644 index 000000000..fdf94dc8c --- /dev/null +++ b/vhost_user_fs/src/vhost_user_fs.rs @@ -0,0 +1,139 @@ +// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::os::unix::io::RawFd; +use std::sync::{Arc, Mutex}; + +use error_chain::ChainedError; +use vmm_sys_util::epoll::EventSet; + +use machine_manager::event_loop::EventLoop; +use util::loop_context::{ + EventLoopManager, EventNotifier, EventNotifierHelper, NotifierCallback, NotifierOperation, +}; + +use super::cmdline::FsConfig; +use super::vhost_user_server::VhostUserServerHandler; +use crate::errors::{Result, ResultExt}; + +#[derive(Clone)] +pub struct VhostUserFs { + pub server_handler: VhostUserServerHandler, +} + +pub trait CreateEventNotifier { + fn create_event_notifier( + &mut self, + server_handler: Arc>, + ) -> Option>; +} + +impl CreateEventNotifier for VhostUserServerHandler { + fn create_event_notifier( + &mut self, + server_handler: Arc>, + ) -> Option> { + let mut notifiers = Vec::new(); + self.sock.server_accept().unwrap(); + + let mut handlers = Vec::new(); + let handler: Box = Box::new(move |event, _| { + if event == EventSet::IN { + let mut lock_server_handler = server_handler.lock().unwrap(); + if let Err(e) = lock_server_handler.handle_request() { + error!( + "Failed to handle request for vhost user server, {}", + e.display_chain() + ); + } + } + + if event & EventSet::HANG_UP == EventSet::HANG_UP { + panic!("Receive the event of HANG_UP from stratovirt"); + } else { + None + } + }); + + handlers.push(Arc::new(Mutex::new(handler))); + + let notifier = EventNotifier::new( + NotifierOperation::AddShared, + self.sock.get_stream_raw_fd(), + Some(self.sock.get_listener_raw_fd()), + EventSet::IN | EventSet::HANG_UP, + handlers, + ); + + notifiers.push(notifier); + Some(notifiers) + } +} + +impl EventNotifierHelper for VhostUserServerHandler { + fn internal_notifiers(server_handler: Arc>) -> Vec { + let mut notifiers = Vec::new(); + + let server_handler_clone = server_handler.clone(); + let mut handlers = Vec::new(); + let handler: Box Option>> = + Box::new(move |_, _| { + server_handler_clone + .lock() + .unwrap() + .create_event_notifier(server_handler_clone.clone()) + }); + + handlers.push(Arc::new(Mutex::new(handler))); + + let notifier = EventNotifier::new( + NotifierOperation::AddShared, + server_handler.lock().unwrap().sock.get_listener_raw_fd(), + None, + EventSet::IN, + handlers, + ); + + notifiers.push(notifier); + + notifiers + } +} + +impl VhostUserFs { + pub fn new(fs_config: FsConfig) -> Result { + let server_handler = VhostUserServerHandler::new(&fs_config.sock_path) + .chain_err(|| format!("Failed to create vhost user server {}", fs_config.sock_path))?; + Ok(VhostUserFs { server_handler }) + } + + pub fn add_event_notifier(&self) -> Result<()> { + EventLoop::update_event( + EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new( + self.server_handler.clone(), + ))), + None, + )?; + + Ok(()) + } +} + +impl EventLoopManager for VhostUserFs { + fn loop_should_exit(&self) -> bool { + false + } + + fn loop_cleanup(&self) -> util::errors::Result<()> { + Ok(()) + } +} -- Gitee From f40737eea856650a6eb0db6c54cca05e4c33a299 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Tue, 2 Feb 2021 20:44:41 +0800 Subject: [PATCH 13/55] vhost_user_fs: add the backend of virtio fs for vhost user fs Signed-off-by: Fei Xu --- vhost_user_fs/src/lib.rs | 1 + vhost_user_fs/src/vhost_user_fs.rs | 9 ++- vhost_user_fs/src/vhost_user_server.rs | 101 ++++++++++++++++++++++--- vhost_user_fs/src/virtio_fs.rs | 77 +++++++++++++++++++ 4 files changed, 176 insertions(+), 12 deletions(-) create mode 100644 vhost_user_fs/src/virtio_fs.rs diff --git a/vhost_user_fs/src/lib.rs b/vhost_user_fs/src/lib.rs index b9bacb48d..058244e98 100644 --- a/vhost_user_fs/src/lib.rs +++ b/vhost_user_fs/src/lib.rs @@ -23,6 +23,7 @@ extern crate util; pub mod cmdline; pub mod vhost_user_fs; pub mod vhost_user_server; +pub mod virtio_fs; pub mod errors { error_chain! { diff --git a/vhost_user_fs/src/vhost_user_fs.rs b/vhost_user_fs/src/vhost_user_fs.rs index fdf94dc8c..838e1b49c 100644 --- a/vhost_user_fs/src/vhost_user_fs.rs +++ b/vhost_user_fs/src/vhost_user_fs.rs @@ -23,6 +23,8 @@ use util::loop_context::{ use super::cmdline::FsConfig; use super::vhost_user_server::VhostUserServerHandler; +use super::virtio_fs::VirtioFs; + use crate::errors::{Result, ResultExt}; #[derive(Clone)] @@ -111,7 +113,12 @@ impl EventNotifierHelper for VhostUserServerHandler { impl VhostUserFs { pub fn new(fs_config: FsConfig) -> Result { - let server_handler = VhostUserServerHandler::new(&fs_config.sock_path) + let virtio_fs = Arc::new(Mutex::new( + VirtioFs::new(&fs_config.source_dir) + .chain_err(|| format!("Failed to create virtio fs {}", fs_config.source_dir))?, + )); + + let server_handler = VhostUserServerHandler::new(&fs_config.sock_path, virtio_fs) .chain_err(|| format!("Failed to create vhost user server {}", fs_config.sock_path))?; Ok(VhostUserFs { server_handler }) } diff --git a/vhost_user_fs/src/vhost_user_server.rs b/vhost_user_fs/src/vhost_user_server.rs index 012982bdb..7280bdaaa 100644 --- a/vhost_user_fs/src/vhost_user_server.rs +++ b/vhost_user_fs/src/vhost_user_server.rs @@ -13,6 +13,7 @@ use std::mem::size_of; use std::os::unix::io::RawFd; use std::slice; +use std::sync::{Arc, Mutex}; use device_model::virtio::vhost::user::vhost_user_msg::*; use device_model::virtio::vhost::user::vhost_user_sock::{UnixDomainSock, MAX_ATTACHED_FD_ENTRIES}; @@ -20,9 +21,53 @@ use util::unix::limit_permission; use crate::errors::{Result, ResultExt}; +///the trait for dealing with vhost-user request in the server +pub trait VhostUserReqHandler: Send + Sync { + /// Set the current process as the owner of this file descriptor. + fn set_owner(&mut self) -> Result<()>; + + /// Get a bitmask of supported virtio/vhost features. + fn get_features(&self) -> Result; + + /// Inform the vhost subsystem which features to enable. This should be a subset of + /// supported features from VHOST_GET_FEATURES.. + fn set_features(&mut self, features: u64) -> Result<()>; + + /// Set the guest memory mappings for vhost to use. + fn set_mem_table(&mut self, regions: &[VhostUserMemoryRegion], fds: &[RawFd]) -> Result<()>; + + /// Set the number of descriptors in the vring. + fn set_vring_num(&mut self, queue_index: usize, num: u16) -> Result<()>; + + /// Set the addresses for a given vring. + fn set_vring_addr( + &mut self, + queue_index: usize, + flags: u32, + desc_table: u64, + used_ring: u64, + avail_ring: u64, + log: u64, + ) -> Result<()>; + + /// Set the first index to look for available descriptors. + fn set_vring_base(&mut self, queue_index: usize, num: u16) -> Result<()>; + + /// Set the eventfd to trigger when buffers have been used by the host. + fn set_vring_call(&mut self, queue_index: usize, fd: RawFd) -> Result<()>; + + /// Set the eventfd that will be signaled by the guest when buffers are + /// available for the host to process. + fn set_vring_kick(&mut self, queue_index: usize, fd: RawFd) -> Result<()>; + + ///set the status of vring + fn set_vring_enable(&mut self, queue_index: usize, status: u32) -> Result<()>; +} + #[derive(Clone)] pub struct VhostUserServerHandler { pub sock: UnixDomainSock, + pub backend: Arc>, } fn close_fds(fds: Vec) { @@ -51,14 +96,17 @@ fn is_invalid_fds(hdr: &mut VhostUserMsgHeader, rfds: Option>) -> Res } impl VhostUserServerHandler { - pub fn new(path: &str) -> Result { + pub fn new(path: &str, backend: Arc>) -> Result { let mut server = UnixDomainSock::new(path); server .server_bind(true) .chain_err(|| format!("Failed to bind for vhost user server {}", path))?; limit_permission(path).chain_err(|| format!("Failed to limit permission {}", path))?; - Ok(VhostUserServerHandler { sock: server }) + Ok(VhostUserServerHandler { + sock: server, + backend, + }) } fn recv_hdr_and_fds(&mut self) -> Result<(VhostUserMsgHeader, Option>)> { @@ -189,7 +237,7 @@ impl VhostUserServerHandler { } // The length of buf has been judged above to ensure no buffer overflow - let _regions = unsafe { + let regions = unsafe { slice::from_raw_parts( buf.as_ptr().add(memhdrsize) as *const VhostUserMemoryRegion, memhdr.nregion as usize, @@ -206,6 +254,7 @@ impl VhostUserServerHandler { memhdr.nregion ); } + self.backend.lock().unwrap().set_mem_table(regions, &fds)?; } else { bail!("The fds of mem table is null"); } @@ -226,21 +275,23 @@ impl VhostUserServerHandler { bail!("The length {} of getting features is invalid", len); } - let features = 0_u64; + let features = self.backend.lock().unwrap().get_features()?; if hdr.is_need_reply() { self.send_ack_msg(VhostUserMsgReq::GetFeatures as u32, features, &[]) .chain_err(|| "Failed to send ack msg for getting features")?; } } VhostUserMsgReq::SetFeatures => { - let _features = self + let features = self .get_msg_body::(&hdr, buf, len) .chain_err(|| "Failed to get msg body for setting features")?; + self.backend.lock().unwrap().set_features(*features)?; } VhostUserMsgReq::SetOwner => { if len != 0 { bail!("The length {} of setting owner is invalid", len); } + self.backend.lock().unwrap().set_owner()?; } VhostUserMsgReq::SetMemTable => { let ret = match self.set_msg_mem_table(&hdr, buf, len, rfds) { @@ -259,27 +310,47 @@ impl VhostUserServerHandler { } } VhostUserMsgReq::SetVringNum => { - let _vringstate = self + let vringstate = self .get_msg_body::(&hdr, buf, len) .chain_err(|| "Failed to get msg body for setting vring num")?; + self.backend + .lock() + .unwrap() + .set_vring_num(vringstate.index as usize, vringstate.value as u16)?; } VhostUserMsgReq::SetVringAddr => { - let _vringaddr = self + let vringaddr = self .get_msg_body::(&hdr, buf, len) .chain_err(|| "Failed to get msg body for setting vring addr")?; + self.backend.lock().unwrap().set_vring_addr( + vringaddr.index as usize, + vringaddr.flags, + vringaddr.desc_user_addr, + vringaddr.used_user_addr, + vringaddr.avail_user_addr, + vringaddr.log_guest_addr, + )?; } VhostUserMsgReq::SetVringBase => { - let _vringstate = self + let vringstate = self .get_msg_body::(&hdr, buf, len) .chain_err(|| "Failed to get msg body for setting vring base")?; + self.backend + .lock() + .unwrap() + .set_vring_base(vringstate.index as usize, vringstate.value as u16)?; } VhostUserMsgReq::SetVringEnable => { - let _vringstate = self + let vringstate = self .get_msg_body::(&hdr, buf, len) .chain_err(|| "Failed to get msg body for setting vring enable")?; + self.backend + .lock() + .unwrap() + .set_vring_enable(vringstate.index as usize, vringstate.value)?; } VhostUserMsgReq::SetVringKick => { - let _index = self + let index = self .get_msg_body::(&hdr, buf, len) .chain_err(|| "Failed to get msg body for setting vring kick")?; if let Some(fds) = rfds { @@ -288,12 +359,16 @@ impl VhostUserServerHandler { close_fds(fds); bail!("The length {} of fds for kicking is invalid", fds_len); } + self.backend + .lock() + .unwrap() + .set_vring_kick(*index as usize, fds[0])?; } else { bail!("The length {} of fds for kicking is null"); } } VhostUserMsgReq::SetVringCall => { - let _index = self + let index = self .get_msg_body::(&hdr, buf, len) .chain_err(|| "Failed to get msg body for setting vring call")?; if let Some(fds) = rfds { @@ -302,6 +377,10 @@ impl VhostUserServerHandler { close_fds(fds); bail!("The length {} of fds for calling is invalid", fds_len); } + self.backend + .lock() + .unwrap() + .set_vring_call(*index as usize, fds[0])?; } else { bail!("The length {} of fds for calling is null"); } diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs new file mode 100644 index 000000000..bf6082031 --- /dev/null +++ b/vhost_user_fs/src/virtio_fs.rs @@ -0,0 +1,77 @@ +// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::os::unix::io::RawFd; + +use device_model::virtio::vhost::user::vhost_user_msg::*; + +use super::vhost_user_server::VhostUserReqHandler; + +use crate::errors::Result; + +pub struct VirtioFs {} + +impl VirtioFs { + pub fn new(_source_dir: &str) -> Result { + Ok(VirtioFs {}) + } +} + +impl VhostUserReqHandler for VirtioFs { + fn set_owner(&mut self) -> Result<()> { + Ok(()) + } + + fn get_features(&self) -> Result { + Ok(0_u64) + } + + fn set_features(&mut self, _features: u64) -> Result<()> { + Ok(()) + } + + fn set_mem_table(&mut self, _regions: &[VhostUserMemoryRegion], _fds: &[RawFd]) -> Result<()> { + Ok(()) + } + + fn set_vring_num(&mut self, _queue_index: usize, _num: u16) -> Result<()> { + Ok(()) + } + + fn set_vring_addr( + &mut self, + _queue_index: usize, + _flags: u32, + _desc_table: u64, + _used_ring: u64, + _avail_ring: u64, + _log: u64, + ) -> Result<()> { + Ok(()) + } + + fn set_vring_base(&mut self, _queue_index: usize, _num: u16) -> Result<()> { + Ok(()) + } + + fn set_vring_call(&mut self, _queue_index: usize, _fd: RawFd) -> Result<()> { + Ok(()) + } + + fn set_vring_kick(&mut self, _queue_index: usize, _fd: RawFd) -> Result<()> { + Ok(()) + } + + fn set_vring_enable(&mut self, _queue_index: usize, _status: u32) -> Result<()> { + Ok(()) + } +} -- Gitee From 67e6d5be5b6e8570f32e0029a5f8fb879897aed4 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Wed, 7 Apr 2021 11:12:48 +0800 Subject: [PATCH 14/55] vhost_user_fs: add the kick event to EventLoop and deal with virtio queue Signed-off-by: Fei Xu --- Cargo.lock | 1 + vhost_user_fs/Cargo.toml | 1 + vhost_user_fs/src/lib.rs | 3 + vhost_user_fs/src/virtio_fs.rs | 323 +++++++++++++++++++++++++++++++-- 4 files changed, 312 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4596e1e61..1b251089d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -293,6 +293,7 @@ checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" name = "vhost_user_fs" version = "0.1.0" dependencies = [ + "address_space", "device_model", "error-chain", "libc", diff --git a/vhost_user_fs/Cargo.toml b/vhost_user_fs/Cargo.toml index 1fc8944dc..38d85e1cd 100644 --- a/vhost_user_fs/Cargo.toml +++ b/vhost_user_fs/Cargo.toml @@ -14,3 +14,4 @@ vmm-sys-util = "0.6.1" util = { path = "../util" } device_model = { path = "../device_model" } machine_manager = { path = "../machine_manager" } +address_space = { path = "../address_space" } diff --git a/vhost_user_fs/src/lib.rs b/vhost_user_fs/src/lib.rs index 058244e98..df5c32c38 100644 --- a/vhost_user_fs/src/lib.rs +++ b/vhost_user_fs/src/lib.rs @@ -16,6 +16,7 @@ extern crate error_chain; extern crate log; extern crate vmm_sys_util; +extern crate address_space; extern crate device_model; extern crate machine_manager; extern crate util; @@ -30,6 +31,8 @@ pub mod errors { links { Util(util::errors::Error, util::errors::ErrorKind); DeviceModel(device_model::errors::Error, device_model::errors::ErrorKind); + Virtio(device_model::virtio::errors::Error, device_model::virtio::errors::ErrorKind); + AddressSpace(address_space::errors::Error, address_space::errors::ErrorKind); } foreign_links { Io(std::io::Error); diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index bf6082031..aa0edf1cb 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -10,19 +10,196 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::os::unix::io::RawFd; +use std::fs::File; +use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use std::sync::{Arc, Mutex}; -use device_model::virtio::vhost::user::vhost_user_msg::*; +use error_chain::ChainedError; +use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; + +use address_space::{AddressSpace, FileBackend, GuestAddress, HostMemMapping, Region}; +use device_model::virtio::{ + queue::{Queue, QueueConfig, QUEUE_TYPE_SPLIT_VRING}, + vhost::user::vhost_user_msg::*, + VIRTIO_F_RING_EVENT_IDX, VIRTIO_F_RING_INDIRECT_DESC, VIRTIO_F_VERSION_1, +}; +use machine_manager::event_loop::EventLoop; +use util::loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}; use super::vhost_user_server::VhostUserReqHandler; -use crate::errors::Result; +use crate::errors::{Result, ResultExt}; + +// The num of high priority queue +const VIRIOT_FS_HIGH_PRIO_QUEUE_NUM: u64 = 1; +// The num of request queue +const VIRTIO_FS_REQ_QUEUES_NUM: u64 = 1; +const VIRTIO_FS_MAX_QUEUE_SIZE: u16 = 1024; + +pub struct FsIoHandler { + queue: Queue, + kick_evt: EventFd, + call_evt: EventFd, + mem_space: Arc, + driver_features: u64, +} + +impl FsIoHandler { + pub fn new( + queue_config: QueueConfig, + kick_evt: &EventFd, + call_evt: &EventFd, + mem_space: &Arc, + driver_features: u64, + ) -> Result { + let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING) + .chain_err(|| "Failed to create virtual queue")?; + if !queue.is_valid(mem_space) { + bail!("Invalid queue for fs handler"); + } + + Ok(FsIoHandler { + queue, + kick_evt: kick_evt.try_clone().unwrap(), + call_evt: call_evt.try_clone().unwrap(), + mem_space: mem_space.clone(), + driver_features, + }) + } + + fn process_queue(&mut self) -> Result<()> { + while let Ok(elem) = self + .queue + .vring + .pop_avail(&self.mem_space, self.driver_features) + { + self.queue.vring.add_used(&self.mem_space, elem.index, 0)?; + } + + if self + .queue + .vring + .should_notify(&self.mem_space, self.driver_features) + { + self.call_evt + .write(1) + .chain_err(|| "Failed to write call fd")?; + } + + Ok(()) + } + + pub fn delete_notifiers(&self) -> Vec { + let mut notifiers = Vec::new(); + notifiers.push(EventNotifier::new( + NotifierOperation::Delete, + self.kick_evt.as_raw_fd(), + None, + EventSet::IN, + Vec::new(), + )); -pub struct VirtioFs {} + notifiers + } +} + +impl EventNotifierHelper for FsIoHandler { + fn internal_notifiers(fs_handler: Arc>) -> Vec { + let mut notifiers = Vec::new(); + + let fs_handler_clone = fs_handler.clone(); + let handler = Box::new(move |_, fd: RawFd| { + read_fd(fd); + + if let Err(e) = fs_handler_clone.lock().unwrap().process_queue() { + error!("Failed to process fuse msg, {}", e.display_chain()); + } + + None + }); + + notifiers.push(EventNotifier::new( + NotifierOperation::AddShared, + fs_handler.lock().unwrap().kick_evt.as_raw_fd(), + None, + EventSet::IN, + vec![Arc::new(Mutex::new(handler))], + )); + + notifiers + } +} + +struct QueueInfo { + config: QueueConfig, + kick_evt: Option, + call_evt: Option, +} + +impl QueueInfo { + pub fn new(queue_size: u16) -> Self { + QueueInfo { + config: QueueConfig::new(queue_size), + kick_evt: None, + call_evt: None, + } + } +} + +struct VirtioFsConfig { + device_features: u64, + driver_features: u64, + queues_info: Vec, + mem_regions: Vec, +} + +impl VirtioFsConfig { + fn new() -> Self { + let device_features = 1_u64 << VIRTIO_F_VERSION_1 + | 1_u64 << VIRTIO_F_RING_INDIRECT_DESC + | 1_u64 << VIRTIO_F_RING_EVENT_IDX; + + let mut queues_info = Vec::new(); + for _i in 0..(VIRIOT_FS_HIGH_PRIO_QUEUE_NUM + VIRTIO_FS_REQ_QUEUES_NUM) { + queues_info.push(QueueInfo::new(VIRTIO_FS_MAX_QUEUE_SIZE)); + } + + VirtioFsConfig { + device_features, + driver_features: 0_u64, + queues_info, + mem_regions: Vec::new(), + } + } + + fn get_mut_queue_config(&mut self, queue_index: usize) -> Result<&mut QueueInfo> { + self.queues_info + .get_mut(queue_index) + .ok_or_else(|| format!("The select index of queue {} overflows", queue_index).into()) + } +} + +pub struct VirtioFs { + config: VirtioFsConfig, + fs_handlers: Vec>>>, + sys_mem: Arc, +} impl VirtioFs { pub fn new(_source_dir: &str) -> Result { - Ok(VirtioFs {}) + let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())) + .chain_err(|| "Failed to create address space")?; + + let mut fs_handlers = Vec::new(); + for _i in 0..(VIRIOT_FS_HIGH_PRIO_QUEUE_NUM + VIRTIO_FS_REQ_QUEUES_NUM) { + fs_handlers.push(None); + } + + Ok(VirtioFs { + config: VirtioFsConfig::new(), + fs_handlers, + sys_mem, + }) } } @@ -32,30 +209,98 @@ impl VhostUserReqHandler for VirtioFs { } fn get_features(&self) -> Result { - Ok(0_u64) + Ok(self.config.device_features) } - fn set_features(&mut self, _features: u64) -> Result<()> { + fn set_features(&mut self, features: u64) -> Result<()> { + self.config.driver_features = features; Ok(()) } - fn set_mem_table(&mut self, _regions: &[VhostUserMemoryRegion], _fds: &[RawFd]) -> Result<()> { + fn set_mem_table(&mut self, regions: &[VhostUserMemoryRegion], fds: &[RawFd]) -> Result<()> { + if !self.config.mem_regions.is_empty() { + for region in &self.config.mem_regions { + if let Err(e) = self.sys_mem.root().delete_subregion(region) { + error!( + "Failed to delete subregion for setting mem table, {}", + e.display_chain() + ); + } + } + self.config.mem_regions = Vec::new(); + } + + for (index, region_config) in regions.iter().enumerate() { + let file = unsafe { File::from_raw_fd(fds[index]) }; + let fileback = FileBackend { + file: Arc::new(file), + offset: region_config.mmap_offset, + page_size: 0_u64, + }; + + let mmap = Arc::new( + HostMemMapping::new( + GuestAddress(region_config.guest_phys_addr), + region_config.memory_size, + Some(fileback), + false, + true, + ) + .chain_err(|| + format!("Failed to create the mapping of host memory for setting mem table, addr: 0x{:X}, size: {}, offset: {}", + region_config.guest_phys_addr, region_config.memory_size, region_config.mmap_offset, + ) + )? + ); + + let region = Region::init_ram_region(mmap.clone()); + self.sys_mem + .root() + .add_subregion(region.clone(), mmap.start_address().raw_value()) + .chain_err(|| "Failed to add subregion for setting mem table")?; + + self.config.mem_regions.push(region); + } + Ok(()) } - fn set_vring_num(&mut self, _queue_index: usize, _num: u16) -> Result<()> { + fn set_vring_num(&mut self, queue_index: usize, num: u16) -> Result<()> { + self.config + .get_mut_queue_config(queue_index) + .map(|queue_info| { + queue_info.config.size = num; + }) + .chain_err(|| { + format!( + "Failed to set vring num, index: {}, num: {}", + queue_index, num, + ) + })?; Ok(()) } fn set_vring_addr( &mut self, - _queue_index: usize, + queue_index: usize, _flags: u32, - _desc_table: u64, - _used_ring: u64, - _avail_ring: u64, + desc_table: u64, + used_ring: u64, + avail_ring: u64, _log: u64, ) -> Result<()> { + self.config + .get_mut_queue_config(queue_index as usize) + .map(|queue_info| { + queue_info.config.desc_table = GuestAddress(desc_table); + queue_info.config.avail_ring = GuestAddress(avail_ring); + queue_info.config.used_ring = GuestAddress(used_ring); + }).chain_err(|| + format!("Failed to set vring addr, index: {}, desc: 0x{:X}, avail: 0x{:X}, used: 0x{:X}", + queue_index, desc_table, avail_ring, used_ring, + ) + )?; + Ok(()) } @@ -63,15 +308,61 @@ impl VhostUserReqHandler for VirtioFs { Ok(()) } - fn set_vring_call(&mut self, _queue_index: usize, _fd: RawFd) -> Result<()> { + fn set_vring_call(&mut self, queue_index: usize, fd: RawFd) -> Result<()> { + self.config + .get_mut_queue_config(queue_index) + .map(|queue_info| { + let call_evt = unsafe { EventFd::from_raw_fd(fd) }; + queue_info.call_evt = Some(call_evt); + }) + .chain_err(|| format!("Failed to set vring call, index: {}", queue_index))?; Ok(()) } - fn set_vring_kick(&mut self, _queue_index: usize, _fd: RawFd) -> Result<()> { + fn set_vring_kick(&mut self, queue_index: usize, fd: RawFd) -> Result<()> { + self.config + .get_mut_queue_config(queue_index) + .map(|queue_info| { + let kick_evt = unsafe { EventFd::from_raw_fd(fd) }; + queue_info.kick_evt = Some(kick_evt); + }) + .chain_err(|| format!("Failed to set vring kick, index: {}", queue_index))?; Ok(()) } - fn set_vring_enable(&mut self, _queue_index: usize, _status: u32) -> Result<()> { + fn set_vring_enable(&mut self, queue_index: usize, status: u32) -> Result<()> { + let driver_features = self.config.driver_features; + + let mut queue_info = self.config.get_mut_queue_config(queue_index)?; + queue_info.config.ready = status == 1; + + if status == 1 { + if queue_info.kick_evt.is_none() || queue_info.call_evt.is_none() { + bail!( + "The event for kicking {} or calling {} is none", + queue_info.kick_evt.is_none(), + queue_info.call_evt.is_none(), + ); + } + + let fs_handler = Arc::new(Mutex::new( + FsIoHandler::new( + queue_info.config, + queue_info.kick_evt.as_ref().unwrap(), + queue_info.call_evt.as_ref().unwrap(), + &self.sys_mem, + driver_features, + ) + .chain_err(|| "Failed to create fs handler")?, + )); + + self.fs_handlers[queue_index] = Some(fs_handler.clone()); + EventLoop::update_event(EventNotifierHelper::internal_notifiers(fs_handler), None) + .chain_err(|| "Failed to update event for queue status which is ready")?; + } else if let Some(fs_handler) = self.fs_handlers.get_mut(queue_index).unwrap().take() { + EventLoop::update_event(fs_handler.lock().unwrap().delete_notifiers(), None) + .chain_err(|| "Failed to update event for queue status which is not ready")?; + } Ok(()) } } -- Gitee From dbeed3b9ff6f7fa8fea6aaba5f6d1c29258aebbd Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Wed, 3 Feb 2021 10:45:43 +0800 Subject: [PATCH 15/55] vhost_user_fs: parse the fuse msg from output of virtio queue 1.Use FuseBuffer to parse the fuse msg from output of virtio queue 2.define FuseReq to save the FuseBuffer of reading and writing Signed-off-by: Fei Xu --- vhost_user_fs/src/fuse_msg.rs | 218 +++++++++++++++++++++++++++++++++ vhost_user_fs/src/fuse_req.rs | 65 ++++++++++ vhost_user_fs/src/lib.rs | 2 + vhost_user_fs/src/virtio_fs.rs | 5 +- 4 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 vhost_user_fs/src/fuse_msg.rs create mode 100644 vhost_user_fs/src/fuse_req.rs diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs new file mode 100644 index 000000000..a7a7a5f62 --- /dev/null +++ b/vhost_user_fs/src/fuse_msg.rs @@ -0,0 +1,218 @@ +// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::cmp; +use std::collections::VecDeque; +use std::ffi::CString; +use std::mem::size_of; +use std::sync::Arc; + +use address_space::AddressSpace; +use device_model::virtio::queue::ElemIovec; +use util::byte_code::ByteCode; + +use crate::errors::{Result, ResultExt}; + +pub const FUSE_OK: i32 = 0; + +pub struct FuseBuffer { + pub bufs: VecDeque, + pub btyes_total: usize, + pub bytes_processed: usize, +} + +impl FuseBuffer { + fn new() -> Self { + FuseBuffer { + bufs: VecDeque::new(), + btyes_total: 0_usize, + bytes_processed: 0_usize, + } + } + + pub fn read_cstring(&mut self, sys_mem: &Arc) -> Result { + let bytes_remain = self.btyes_total - self.bytes_processed; + let mut buffer = vec![0; bytes_remain]; + + let mut offset = 0_usize; + for buf in &self.bufs { + let mut slice = &mut buffer[offset..]; + let read_count = cmp::min(slice.len(), buf.len as usize); + sys_mem + .read(&mut slice, buf.addr, read_count as u64) + .chain_err(|| "Failed to read buffer for fuse req")?; + offset += read_count; + } + + let pos = match buffer.iter().position(|c| *c == b'\0') { + Some(p) => p + 1, + None => bail!("It is not a string"), + }; + + let str_slice = buffer.as_slice(); + let cstring = unsafe { CString::from_vec_unchecked(str_slice[0..pos].to_vec()) }; + + let mut need_read_count = pos; + let bufs = self.bufs.clone(); + for buf in bufs { + let read_count = cmp::min(need_read_count, buf.len as usize); + self.bytes_processed += read_count; + + if let Some(buftmp) = self.bufs.pop_front() { + if read_count < buftmp.len as usize { + //Add the remain length to the head of self.bufs + let len = buftmp.len - read_count as u32; + let addr = buftmp.addr.unchecked_add(read_count as u64); + let remain = ElemIovec { addr, len }; + self.bufs.push_front(remain); + break; + } else { + need_read_count -= buftmp.len as usize; + } + } + } + + Ok(cstring) + } + + pub fn read_slice( + &mut self, + sys_mem: &Arc, + dst: &mut [u8], + count: usize, + ) -> Result<()> { + let read_end = match self.bytes_processed.checked_add(count) { + Some(end_) => end_, + None => bail!("The read count {} {} overflow", count, self.bytes_processed), + }; + + if read_end > self.btyes_total { + bail!( + "The read count {} exceeds maximum {}", + read_end, + self.btyes_total + ); + } + + let bufs = self.bufs.clone(); + let mut offset = 0_usize; + for buf in bufs { + let mut slice = &mut dst[offset..]; + let read_count = cmp::min(slice.len(), buf.len as usize); + sys_mem + .read(&mut slice, buf.addr, read_count as u64) + .chain_err(|| "Failed to read buffer for fuse req")?; + self.bytes_processed += read_count; + offset += read_count; + + if let Some(buftmp) = self.bufs.pop_front() { + if read_count < buftmp.len as usize { + //Add the remain length to the head of self.bufs + let len = buftmp.len - read_count as u32; + let addr = buftmp.addr.unchecked_add(read_count as u64); + let remain = ElemIovec { addr, len }; + self.bufs.push_front(remain); + break; + } + } + } + + Ok(()) + } + + pub fn read_obj(&mut self, sys_mem: &Arc) -> Result { + let mut obj = T::default(); + self.read_slice(sys_mem, &mut obj.as_mut_bytes(), size_of::())?; + Ok(obj) + } + + pub fn write_slice( + &mut self, + sys_mem: &Arc, + src: &[u8], + count: usize, + ) -> Result<()> { + let write_end = match self.bytes_processed.checked_add(count) { + Some(end_) => end_, + None => bail!("The read count {} {} overflow", count, self.bytes_processed), + }; + + if write_end > self.btyes_total { + bail!( + "The read count {} exceeds maximum {}", + write_end, + self.btyes_total + ); + } + + let bufs = self.bufs.clone(); + let mut offset = 0_usize; + for buf in bufs { + let mut slice = &src[offset..]; + let write_count = cmp::min(slice.len(), buf.len as usize); + + sys_mem + .write(&mut slice, buf.addr, write_count as u64) + .chain_err(|| "Failed to read buffer for fuse req")?; + self.bytes_processed += write_count; + offset += write_count; + + if let Some(buftmp) = self.bufs.pop_front() { + if write_count < buftmp.len as usize { + //Add the remain length to the head of self.bufs + let len = buftmp.len - write_count as u32; + let addr = buftmp.addr.unchecked_add(write_count as u64); + let remain = ElemIovec { addr, len }; + self.bufs.push_front(remain); + break; + } + } + } + + Ok(()) + } + + pub fn write_obj(&mut self, sys_mem: &Arc, data: &T) -> Result<()> { + self.write_slice(sys_mem, data.as_bytes(), size_of::()) + } +} + +impl Default for FuseBuffer { + fn default() -> Self { + Self::new() + } +} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseInHeader { + pub len: u32, + pub opcode: u32, + pub unique: u64, + pub nodeid: u64, + pub uid: u32, + pub gid: u32, + pub pid: u32, + pub padding: u32, +} + +impl ByteCode for FuseInHeader {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseOutHeader { + pub len: u32, + pub error: i32, + pub unique: u64, +} + +impl ByteCode for FuseOutHeader {} diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs new file mode 100644 index 000000000..d99c5140b --- /dev/null +++ b/vhost_user_fs/src/fuse_req.rs @@ -0,0 +1,65 @@ +// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use error_chain::ChainedError; + +use address_space::AddressSpace; +use device_model::virtio::queue::Element; +use std::sync::Arc; + +use super::fuse_msg::*; + +#[allow(dead_code)] +pub struct FuseReq { + desc_index: u16, + reader: FuseBuffer, + writer: FuseBuffer, +} + +impl FuseReq { + pub fn new(elem: &Element) -> Self { + let desc_index = elem.index; + + let mut reader = FuseBuffer::default(); + for out_iov in &elem.out_iovec { + reader.btyes_total += out_iov.len as usize; + reader.bufs.push_back(*out_iov); + } + + let mut writer = FuseBuffer::default(); + for in_iov in &elem.in_iovec { + writer.btyes_total += in_iov.len as usize; + writer.bufs.push_back(*in_iov); + } + + FuseReq { + desc_index, + reader, + writer, + } + } + + pub fn execute(&mut self, sys_mem: &Arc) -> (u16, u32) { + let _in_header = match self.reader.read_obj::(sys_mem) { + Ok(data) => data, + Err(err) => { + error!( + "Failed to read the header of fuse msg, {}", + err.display_chain(), + ); + return (self.desc_index, 0); + } + }; + + (self.desc_index, 0) + } +} diff --git a/vhost_user_fs/src/lib.rs b/vhost_user_fs/src/lib.rs index df5c32c38..adfe2d336 100644 --- a/vhost_user_fs/src/lib.rs +++ b/vhost_user_fs/src/lib.rs @@ -22,6 +22,8 @@ extern crate machine_manager; extern crate util; pub mod cmdline; +pub mod fuse_msg; +pub mod fuse_req; pub mod vhost_user_fs; pub mod vhost_user_server; pub mod virtio_fs; diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index aa0edf1cb..f65620e6c 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -26,6 +26,7 @@ use device_model::virtio::{ use machine_manager::event_loop::EventLoop; use util::loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}; +use super::fuse_req::FuseReq; use super::vhost_user_server::VhostUserReqHandler; use crate::errors::{Result, ResultExt}; @@ -73,7 +74,9 @@ impl FsIoHandler { .vring .pop_avail(&self.mem_space, self.driver_features) { - self.queue.vring.add_used(&self.mem_space, elem.index, 0)?; + let mut req = FuseReq::new(&elem); + let (index, len) = req.execute(&self.mem_space); + self.queue.vring.add_used(&self.mem_space, index, len)?; } if self -- Gitee From cbce6d6e9822214905893d79925939bb3037bdef Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Wed, 7 Apr 2021 11:19:15 +0800 Subject: [PATCH 16/55] vhost_user_fs: add the management of userspace filesystem 1.Create the struct of FileSystem for managing userspace filesystem 2.Add FileSystem to the struct of VirtioFs and FsIoHandler Signed-off-by: Fei Xu --- Cargo.lock | 50 +++++++++++ vhost_user_fs/Cargo.toml | 1 + vhost_user_fs/src/fs.rs | 156 +++++++++++++++++++++++++++++++++ vhost_user_fs/src/fs_ops.rs | 39 +++++++++ vhost_user_fs/src/fuse_req.rs | 9 +- vhost_user_fs/src/lib.rs | 2 + vhost_user_fs/src/virtio_fs.rs | 15 +++- 7 files changed, 268 insertions(+), 4 deletions(-) create mode 100644 vhost_user_fs/src/fs.rs create mode 100644 vhost_user_fs/src/fs_ops.rs diff --git a/Cargo.lock b/Cargo.lock index 1b251089d..fb701b3af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,6 +108,27 @@ dependencies = [ "vmm-sys-util", ] +[[package]] +name = "errno" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa68f2fb9cae9d37c9b2b3584aba698a2e97f72d7aef7b9f7aa71d8b54ce46fe" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14ca354e36190500e1e1fb267c647932382b54053c50b14970856c0b00a35067" +dependencies = [ + "gcc", + "libc", +] + [[package]] name = "error-chain" version = "0.12.4" @@ -118,6 +139,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" + [[package]] name = "gimli" version = "0.21.0" @@ -295,6 +322,7 @@ version = "0.1.0" dependencies = [ "address_space", "device_model", + "errno", "error-chain", "libc", "log", @@ -312,3 +340,25 @@ dependencies = [ "bitflags", "libc", ] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/vhost_user_fs/Cargo.toml b/vhost_user_fs/Cargo.toml index 38d85e1cd..db2a88daf 100644 --- a/vhost_user_fs/Cargo.toml +++ b/vhost_user_fs/Cargo.toml @@ -6,6 +6,7 @@ license = "Mulan PSL v2" description = "provide virtio fs for VM" [dependencies] +errno = "0.2.7" log = "0.4.8" libc = "0.2.71" error-chain = "0.12.4" diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs new file mode 100644 index 000000000..a6448f9bd --- /dev/null +++ b/vhost_user_fs/src/fs.rs @@ -0,0 +1,156 @@ +// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::collections::BTreeMap; +use std::ffi::CString; +use std::fs::File; + +use super::fs_ops::*; +use super::fuse_msg::*; + +use crate::errors::Result; + +#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq)] +struct StatKey { + ino: libc::ino64_t, + dev: libc::dev_t, +} + +struct Entry { + value: Option, + used: bool, + free_next: usize, +} + +struct Map { + list: Vec>, + free_head: usize, +} + +impl Map { + fn new() -> Self { + Map { + list: Vec::new(), + free_head: 1, + } + } + + fn extend_map(&mut self) { + let mut next = self.list.len(); + + for _ in 0..256 { + next += 1; + self.list.push(Entry { + value: None, + used: false, + free_next: next, + }); + } + } + + fn get_map(&mut self, value: T) -> usize { + let id = self.free_head; + if id == 1 || id == self.list.len() { + self.extend_map(); + } + + match self.list.get_mut(id) { + Some(e) => { + e.value = Some(value); + e.used = true; + self.free_head = e.free_next; + + id + } + None => 0, + } + } +} + +struct Inode { + file: File, + nlookup: u64, + node_id: usize, + file_type: u32, + key: StatKey, +} + +impl Inode { + fn new(file: File, nlookup: u64, node_id: usize, file_type: u32, key: StatKey) -> Self { + Inode { + file, + nlookup, + node_id, + file_type, + key, + } + } +} + +impl Clone for Inode { + fn clone(&self) -> Self { + Inode { + file: self.file.try_clone().unwrap(), + nlookup: self.nlookup, + node_id: self.node_id, + file_type: self.file_type, + key: self.key, + } + } +} + +#[allow(dead_code)] +pub struct FileSystem { + root_inode: Inode, + inodes: BTreeMap, + inode_key_map: Map, + file_map: Map, + proc_dir: File, +} + +impl FileSystem { + pub fn new(source_dir: &str) -> Result { + let (root_file_opt, ret) = open(CString::new(source_dir).unwrap(), libc::O_PATH); + if ret != FUSE_OK { + bail!("Failed to open root file {}", source_dir); + } + let root_file = root_file_opt.unwrap(); + let (stat, ret) = fstat_at( + &root_file, + CString::new("").unwrap(), + libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW, + ); + if ret != FUSE_OK { + bail!("Failed to get stat of root file {}", source_dir); + } + let key = StatKey { + ino: stat.st_ino, + dev: stat.st_dev, + }; + let mut inode_key_map = Map::new(); + let root_id = inode_key_map.get_map(key); + let root_inode = Inode::new(root_file, 2, root_id, libc::S_IFDIR, key); + let mut inodes = BTreeMap::new(); + inodes.insert(key, root_inode.clone()); + let (proc_dir_opt, ret) = open(CString::new("/proc/self/fd").unwrap(), libc::O_PATH); + if ret != FUSE_OK { + bail!("Failed to open proc dir"); + } + Ok(FileSystem { + root_inode, + inodes, + inode_key_map, + file_map: Map::new(), + proc_dir: proc_dir_opt.unwrap(), + }) + } +} diff --git a/vhost_user_fs/src/fs_ops.rs b/vhost_user_fs/src/fs_ops.rs new file mode 100644 index 000000000..c71018079 --- /dev/null +++ b/vhost_user_fs/src/fs_ops.rs @@ -0,0 +1,39 @@ +// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. +use std::ffi::CString; +use std::fs::File; +use std::os::unix::io::{AsRawFd, FromRawFd}; + +use super::fuse_msg::*; + +pub fn fstat_at(file: &File, name: CString, flags: i32) -> (libc::stat, i32) { + let mut stat: libc::stat = unsafe { std::mem::zeroed() }; + + errno::set_errno(errno::Errno(0)); + if unsafe { libc::fstatat(file.as_raw_fd(), name.as_ptr(), &mut stat, flags) } < 0 { + return (stat, errno::errno().0); + } + + (stat, FUSE_OK) +} + +pub fn open(name: CString, flags: i32) -> (Option, i32) { + errno::set_errno(errno::Errno(0)); + let fd = unsafe { libc::open(name.as_ptr(), flags) }; + if fd < 0 { + return (None, errno::errno().0); + } + + let file = unsafe { File::from_raw_fd(fd) }; + + (Some(file), FUSE_OK) +} diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs index d99c5140b..db97ff992 100644 --- a/vhost_user_fs/src/fuse_req.rs +++ b/vhost_user_fs/src/fuse_req.rs @@ -14,8 +14,9 @@ use error_chain::ChainedError; use address_space::AddressSpace; use device_model::virtio::queue::Element; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; +use super::fs::FileSystem; use super::fuse_msg::*; #[allow(dead_code)] @@ -48,7 +49,11 @@ impl FuseReq { } } - pub fn execute(&mut self, sys_mem: &Arc) -> (u16, u32) { + pub fn execute( + &mut self, + sys_mem: &Arc, + _fs: Arc>, + ) -> (u16, u32) { let _in_header = match self.reader.read_obj::(sys_mem) { Ok(data) => data, Err(err) => { diff --git a/vhost_user_fs/src/lib.rs b/vhost_user_fs/src/lib.rs index adfe2d336..f435440c2 100644 --- a/vhost_user_fs/src/lib.rs +++ b/vhost_user_fs/src/lib.rs @@ -22,6 +22,8 @@ extern crate machine_manager; extern crate util; pub mod cmdline; +pub mod fs; +pub mod fs_ops; pub mod fuse_msg; pub mod fuse_req; pub mod vhost_user_fs; diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index f65620e6c..3afdeee45 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -26,6 +26,7 @@ use device_model::virtio::{ use machine_manager::event_loop::EventLoop; use util::loop_context::{read_fd, EventNotifier, EventNotifierHelper, NotifierOperation}; +use super::fs::FileSystem; use super::fuse_req::FuseReq; use super::vhost_user_server::VhostUserReqHandler; @@ -43,6 +44,7 @@ pub struct FsIoHandler { call_evt: EventFd, mem_space: Arc, driver_features: u64, + fs: Arc>, } impl FsIoHandler { @@ -52,6 +54,7 @@ impl FsIoHandler { call_evt: &EventFd, mem_space: &Arc, driver_features: u64, + fs: Arc>, ) -> Result { let queue = Queue::new(queue_config, QUEUE_TYPE_SPLIT_VRING) .chain_err(|| "Failed to create virtual queue")?; @@ -65,6 +68,7 @@ impl FsIoHandler { call_evt: call_evt.try_clone().unwrap(), mem_space: mem_space.clone(), driver_features, + fs, }) } @@ -75,7 +79,7 @@ impl FsIoHandler { .pop_avail(&self.mem_space, self.driver_features) { let mut req = FuseReq::new(&elem); - let (index, len) = req.execute(&self.mem_space); + let (index, len) = req.execute(&self.mem_space, self.fs.clone()); self.queue.vring.add_used(&self.mem_space, index, len)?; } @@ -186,10 +190,11 @@ pub struct VirtioFs { config: VirtioFsConfig, fs_handlers: Vec>>>, sys_mem: Arc, + fs: Arc>, } impl VirtioFs { - pub fn new(_source_dir: &str) -> Result { + pub fn new(source_dir: &str) -> Result { let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())) .chain_err(|| "Failed to create address space")?; @@ -198,10 +203,15 @@ impl VirtioFs { fs_handlers.push(None); } + let fs = Arc::new(Mutex::new(FileSystem::new(source_dir).chain_err(|| { + format!("Failed to create file system, source dir: {}", source_dir) + })?)); + Ok(VirtioFs { config: VirtioFsConfig::new(), fs_handlers, sys_mem, + fs, }) } } @@ -355,6 +365,7 @@ impl VhostUserReqHandler for VirtioFs { queue_info.call_evt.as_ref().unwrap(), &self.sys_mem, driver_features, + self.fs.clone(), ) .chain_err(|| "Failed to create fs handler")?, )); -- Gitee From 4e2106cb6fd60260ab3ecb871494e6554527dce3 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Wed, 3 Feb 2021 18:02:41 +0800 Subject: [PATCH 17/55] vhost_user_fs: implement the fuse message of FUSE_LOOKUP for userspace filesystem 1.Add fuse_proc.rs for parsing and replying fuse message 2.implement the fuse message of FUSE_LOOKUP in fs.rs Signed-off-by: Fei Xu --- vhost_user_fs/src/fs.rs | 94 +++++++++++++++++++++++++ vhost_user_fs/src/fs_ops.rs | 13 ++++ vhost_user_fs/src/fuse_msg.rs | 122 +++++++++++++++++++++++++++++++++ vhost_user_fs/src/fuse_proc.rs | 68 ++++++++++++++++++ vhost_user_fs/src/fuse_req.rs | 25 ++++++- vhost_user_fs/src/lib.rs | 1 + 6 files changed, 320 insertions(+), 3 deletions(-) create mode 100644 vhost_user_fs/src/fuse_proc.rs diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index a6448f9bd..a26222c50 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -74,6 +74,14 @@ impl Map { None => 0, } } + + fn get_value(&self, id: usize) -> Option<&T> { + if let Some(e) = self.list.get(id) { + e.value.as_ref().map(|v| v) + } else { + None + } + } } struct Inode { @@ -108,6 +116,15 @@ impl Clone for Inode { } } +fn path_is_dotdot(path: &CString) -> bool { + let bytes = path.as_bytes(); + if bytes.len() == 2 && bytes[0] == b'.' && bytes[1] == b'.' { + return true; + } + + false +} + #[allow(dead_code)] pub struct FileSystem { root_inode: Inode, @@ -153,4 +170,81 @@ impl FileSystem { proc_dir: proc_dir_opt.unwrap(), }) } + + fn find_inode(&self, node_id: usize) -> Option<&Inode> { + match self.inode_key_map.get_value(node_id) { + Some(k) => self.inodes.get(k), + _ => None, + } + } + + fn internal_lookup( + &mut self, + parent_inode: &Inode, + name: CString, + node_id: &mut u64, + fuse_attr: &mut FuseAttr, + ) -> i32 { + let mut son_name = name.clone(); + if parent_inode.node_id == self.root_inode.node_id && path_is_dotdot(&name) { + son_name = CString::new(".").unwrap(); + } + + let (stat, ret) = fstat_at( + &parent_inode.file, + son_name.clone(), + libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW, + ); + if ret != FUSE_OK { + return ret; + } + + let key = StatKey { + ino: stat.st_ino, + dev: stat.st_dev, + }; + if let Some(inode) = self.inodes.get_mut(&key) { + inode.nlookup += 1; + *node_id = inode.node_id as u64; + } else { + let (file_opt, ret) = open_at( + &parent_inode.file, + son_name, + libc::O_PATH | libc::O_NOFOLLOW, + 0, + ); + if ret != FUSE_OK { + return ret; + } + + let map_id = self.inode_key_map.get_map(key); + if let Some(file) = file_opt { + self.inodes.insert( + key, + Inode::new(file, 1, map_id, stat.st_mode & libc::S_IFMT, key), + ); + } + *node_id = map_id as u64; + }; + + *fuse_attr = FuseAttr::from_stat(stat); + + FUSE_OK + } + + pub fn lookup( + &mut self, + parent_nodeid: usize, + name: CString, + node_id: &mut u64, + fuse_attr: &mut FuseAttr, + ) -> i32 { + let inode = match self.find_inode(parent_nodeid) { + Some(i) => i.clone(), + _ => { + return libc::EBADF as i32; + } + }; + self.internal_lookup(&inode, name, node_id, fuse_attr) + } } diff --git a/vhost_user_fs/src/fs_ops.rs b/vhost_user_fs/src/fs_ops.rs index c71018079..8c6600f3e 100644 --- a/vhost_user_fs/src/fs_ops.rs +++ b/vhost_user_fs/src/fs_ops.rs @@ -37,3 +37,16 @@ pub fn open(name: CString, flags: i32) -> (Option, i32) { (Some(file), FUSE_OK) } + +pub fn open_at(file: &File, name: CString, flags: i32, mode: u32) -> (Option, i32) { + errno::set_errno(errno::Errno(0)); + + let fd = unsafe { libc::openat(file.as_raw_fd(), name.as_ptr(), flags, mode) }; + if fd < 0 { + return (None, errno::errno().0); + } + + let file = unsafe { File::from_raw_fd(fd) }; + + (Some(file), FUSE_OK) +} diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index a7a7a5f62..dbb5a4a49 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -16,6 +16,8 @@ use std::ffi::CString; use std::mem::size_of; use std::sync::Arc; +use error_chain::ChainedError; + use address_space::AddressSpace; use device_model::virtio::queue::ElemIovec; use util::byte_code::ByteCode; @@ -192,6 +194,66 @@ impl Default for FuseBuffer { } } +pub struct FuseIovec<'a> { + pub body: &'a [u8], + pub len: usize, +} + +impl<'a> FuseIovec<'a> { + pub fn from_obj(obj: &'a T) -> Self { + let body = obj.as_bytes(); + FuseIovec { + body, + len: body.len(), + } + } +} + +pub fn reply_fuse_msg( + writer: &mut FuseBuffer, + sys_mem: &Arc, + in_header: &FuseInHeader, + err: i32, + body_opt: Option>, + body_len: usize, +) -> u32 { + let len = size_of::() + body_len; + let mut written_len = len as u32; + + let fuse_out_header = FuseOutHeader { + len: len as u32, + error: -err, + unique: in_header.unique, + }; + + if let Err(e) = writer.write_obj(sys_mem, &fuse_out_header) { + error!( + "Failed to write out_header of fuse msg {}, {}", + in_header.opcode, + e.display_chain(), + ); + written_len = 0_u32; + }; + + //write the body of fuse message in address space + if let Some(body) = body_opt { + for fuse_iov in body.iter() { + if let Err(e) = writer.write_slice(sys_mem, fuse_iov.body, fuse_iov.len) { + error!( + "Failed to write the body of fuse msg {}, {}", + in_header.opcode, + e.display_chain(), + ); + written_len = 0_u32; + } + } + } + + written_len +} + +pub const FUSE_LOOKUP: u32 = 1; + #[repr(C)] #[derive(Debug, Default, Copy, Clone)] pub struct FuseInHeader { @@ -216,3 +278,63 @@ pub struct FuseOutHeader { } impl ByteCode for FuseOutHeader {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseAttr { + pub ino: u64, + pub size: u64, + pub blocks: u64, + pub atime: u64, + pub mtime: u64, + pub ctime: u64, + pub atimensec: u32, + pub mtimensec: u32, + pub ctimensec: u32, + pub mode: u32, + pub nlink: u32, + pub uid: u32, + pub gid: u32, + pub rdev: u32, + pub blksize: u32, + pub flags: u32, +} + +impl ByteCode for FuseAttr {} + +impl FuseAttr { + pub fn from_stat(stat: libc::stat) -> Self { + FuseAttr { + ino: stat.st_ino, + size: stat.st_size as u64, + blocks: stat.st_blocks as u64, + atime: stat.st_atime as u64, + mtime: stat.st_mtime as u64, + ctime: stat.st_ctime as u64, + atimensec: stat.st_atime_nsec as u32, + mtimensec: stat.st_mtime_nsec as u32, + ctimensec: stat.st_ctime_nsec as u32, + mode: stat.st_mode, + nlink: stat.st_nlink as u32, + uid: stat.st_uid, + gid: stat.st_gid, + rdev: stat.st_rdev as u32, + blksize: stat.st_blksize as u32, + flags: 0, + } + } +} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseEntryOut { + pub nodeid: u64, + pub generation: u64, + pub entry_valid: u64, + pub attr_valid: u64, + pub entry_valid_nsec: u32, + pub attr_valid_nsec: u32, + pub attr: FuseAttr, +} + +impl ByteCode for FuseEntryOut {} diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs new file mode 100644 index 000000000..26a513954 --- /dev/null +++ b/vhost_user_fs/src/fuse_proc.rs @@ -0,0 +1,68 @@ +// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. +// +// StratoVirt is licensed under Mulan PSL v2. +// You can use this software according to the terms and conditions of the Mulan +// PSL v2. +// You may obtain a copy of Mulan PSL v2 at: +// http://license.coscl.org.cn/MulanPSL2 +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +// See the Mulan PSL v2 for more details. + +use std::mem; +use std::sync::{Arc, Mutex}; + +use error_chain::ChainedError; + +use address_space::AddressSpace; + +use super::fs::FileSystem; +use super::fuse_msg::*; + +pub fn do_fuse_lookup( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let name = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!("Failed to read name for lookup, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut fuse_attr = FuseAttr::default(); + let mut node_id = 0_u64; + let ret = fs.lock().unwrap().lookup( + in_header.nodeid as usize, + name, + &mut node_id, + &mut fuse_attr, + ); + + if ret == FUSE_OK { + let entry_out = FuseEntryOut { + nodeid: node_id, + generation: 0, + entry_valid: 0, + entry_valid_nsec: 0, + attr_valid: 0, + attr_valid_nsec: 0, + attr: fuse_attr, + }; + reply_fuse_msg( + writer, + sys_mem, + in_header, + ret, + Some(vec![FuseIovec::from_obj(&entry_out)]), + mem::size_of::(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs index db97ff992..94400d0fe 100644 --- a/vhost_user_fs/src/fuse_req.rs +++ b/vhost_user_fs/src/fuse_req.rs @@ -18,6 +18,7 @@ use std::sync::{Arc, Mutex}; use super::fs::FileSystem; use super::fuse_msg::*; +use super::fuse_proc::*; #[allow(dead_code)] pub struct FuseReq { @@ -52,9 +53,9 @@ impl FuseReq { pub fn execute( &mut self, sys_mem: &Arc, - _fs: Arc>, + fs: Arc>, ) -> (u16, u32) { - let _in_header = match self.reader.read_obj::(sys_mem) { + let in_header = match self.reader.read_obj::(sys_mem) { Ok(data) => data, Err(err) => { error!( @@ -65,6 +66,24 @@ impl FuseReq { } }; - (self.desc_index, 0) + let written_len = match in_header.opcode { + FUSE_LOOKUP => { + do_fuse_lookup(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + _ => { + error!("The fuse msg {} is unsupported", in_header.opcode); + reply_fuse_msg( + &mut self.writer, + sys_mem, + &in_header, + libc::ENOSYS, + None, + 0_usize, + ) + } + }; + + // return the index of element and the length which is written + (self.desc_index, written_len) } } diff --git a/vhost_user_fs/src/lib.rs b/vhost_user_fs/src/lib.rs index f435440c2..273ea8071 100644 --- a/vhost_user_fs/src/lib.rs +++ b/vhost_user_fs/src/lib.rs @@ -25,6 +25,7 @@ pub mod cmdline; pub mod fs; pub mod fs_ops; pub mod fuse_msg; +pub mod fuse_proc; pub mod fuse_req; pub mod vhost_user_fs; pub mod vhost_user_server; -- Gitee From 3a1540eb7e48b06a855f412e21cc06b57c9ba2e2 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Wed, 3 Feb 2021 18:20:20 +0800 Subject: [PATCH 18/55] vhost_user_fs: implement the fuse message of FUSE_FORGET for userspace filesystem Signed-off-by: Fei Xu --- vhost_user_fs/src/fs.rs | 54 +++++++++++++++++++++++++++++++++- vhost_user_fs/src/fuse_msg.rs | 9 ++++++ vhost_user_fs/src/fuse_proc.rs | 20 +++++++++++++ vhost_user_fs/src/fuse_req.rs | 1 + 4 files changed, 83 insertions(+), 1 deletion(-) diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index a26222c50..6d49edaa2 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -19,6 +19,8 @@ use super::fuse_msg::*; use crate::errors::Result; +const MAP_EXTEND_LENGTH: usize = 256; + #[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq)] struct StatKey { ino: libc::ino64_t, @@ -47,7 +49,7 @@ impl Map { fn extend_map(&mut self) { let mut next = self.list.len(); - for _ in 0..256 { + for _ in 0..MAP_EXTEND_LENGTH { next += 1; self.list.push(Entry { value: None, @@ -75,6 +77,23 @@ impl Map { } } + fn put_map(&mut self, id: usize) { + if id >= self.list.len() { + return; + } + + if let Some(e) = self.list.get_mut(id) { + if !e.used { + return; + } + + e.value = None; + e.used = false; + e.free_next = self.free_head; + self.free_head = id + } + } + fn get_value(&self, id: usize) -> Option<&T> { if let Some(e) = self.list.get(id) { e.value.as_ref().map(|v| v) @@ -178,6 +197,28 @@ impl FileSystem { } } + fn find_mut_inode(&mut self, node_id: usize) -> Option<&mut Inode> { + match self.inode_key_map.get_value(node_id) { + Some(k) => self.inodes.get_mut(k), + _ => None, + } + } + + fn unref_inode(&mut self, inode: &mut Inode, count: u64) { + if count > inode.nlookup { + inode.nlookup = 0; + } else { + inode.nlookup -= count; + } + + if inode.nlookup == 0 { + self.inodes.remove(&inode.key); + self.inode_key_map.put_map(inode.node_id); + } else if let Some(inode_) = self.find_mut_inode(inode.node_id) { + inode_.nlookup = inode.nlookup; + } + } + fn internal_lookup( &mut self, parent_inode: &Inode, @@ -247,4 +288,15 @@ impl FileSystem { }; self.internal_lookup(&inode, name, node_id, fuse_attr) } + + pub fn forget(&mut self, node_id: usize, nlookup: u64) -> i32 { + let mut inode = match self.find_inode(node_id) { + Some(i) => i.clone(), + _ => { + return libc::EBADF as i32; + } + }; + self.unref_inode(&mut inode, nlookup); + FUSE_OK + } } diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index dbb5a4a49..93876a446 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -253,6 +253,7 @@ pub fn reply_fuse_msg( } pub const FUSE_LOOKUP: u32 = 1; +pub const FUSE_FORGET: u32 = 2; #[repr(C)] #[derive(Debug, Default, Copy, Clone)] @@ -338,3 +339,11 @@ pub struct FuseEntryOut { } impl ByteCode for FuseEntryOut {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseForgetIn { + pub nlookup: u64, +} + +impl ByteCode for FuseForgetIn {} diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs index 26a513954..584c1f12b 100644 --- a/vhost_user_fs/src/fuse_proc.rs +++ b/vhost_user_fs/src/fuse_proc.rs @@ -66,3 +66,23 @@ pub fn do_fuse_lookup( reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) } } + +pub fn do_fuse_forget( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let forget_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for forget_in, {}", e.display_chain()); + return 0_u32; + } + }; + + fs.lock() + .unwrap() + .forget(in_header.nodeid as usize, forget_in.nlookup); + 0_u32 +} diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs index 94400d0fe..4dcb90361 100644 --- a/vhost_user_fs/src/fuse_req.rs +++ b/vhost_user_fs/src/fuse_req.rs @@ -70,6 +70,7 @@ impl FuseReq { FUSE_LOOKUP => { do_fuse_lookup(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) } + FUSE_FORGET => do_fuse_forget(sys_mem, fs, &mut self.reader, &in_header), _ => { error!("The fuse msg {} is unsupported", in_header.opcode); reply_fuse_msg( -- Gitee From ccda1d3e794d17730387dfa0e78cf7171181fe71 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Thu, 4 Feb 2021 10:48:31 +0800 Subject: [PATCH 19/55] vhost_user_fs: implement the fuse message of FUSE_SETATTR and FUSE_GETATTR for userspace filesystem Signed-off-by: Fei Xu --- vhost_user_fs/src/fs.rs | 185 +++++++++++++++++++++++++++++++++ vhost_user_fs/src/fs_ops.rs | 84 +++++++++++++++ vhost_user_fs/src/fuse_msg.rs | 67 ++++++++++++ vhost_user_fs/src/fuse_proc.rs | 76 ++++++++++++++ vhost_user_fs/src/fuse_req.rs | 4 + 5 files changed, 416 insertions(+) diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index 6d49edaa2..425cc4b19 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -13,6 +13,7 @@ use std::collections::BTreeMap; use std::ffi::CString; use std::fs::File; +use std::os::unix::io::AsRawFd; use super::fs_ops::*; use super::fuse_msg::*; @@ -299,4 +300,188 @@ impl FileSystem { self.unref_inode(&mut inode, nlookup); FUSE_OK } + + pub fn getattr(&mut self, node_id: usize, fuse_attr: &mut FuseAttr) -> i32 { + let inode = match self.find_inode(node_id) { + Some(i) => i.clone(), + _ => { + return libc::EBADF as i32; + } + }; + let (stat, ret) = fstat_at( + &inode.file, + CString::new("").unwrap(), + libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW, + ); + if ret != FUSE_OK { + return ret; + } + *fuse_attr = FuseAttr::from_stat(stat); + FUSE_OK + } + + pub fn setattr( + &mut self, + node_id: usize, + attr: &FuseSetattrIn, + fuse_attr: &mut FuseAttr, + ) -> i32 { + if attr.valid & FUSE_SET_ATTR_MODE != 0 { + if attr.valid & FATTR_FH != 0 { + match self.file_map.get_value(attr.fh as usize) { + Some(file) => { + let ret = fchmod(&file, attr.mode); + if ret != FUSE_OK { + return ret; + } + } + _ => { + return libc::EBADF as i32; + } + }; + } else { + match self.find_inode(node_id) { + Some(i) => { + let ret = fchmod_at( + &self.proc_dir, + CString::new(format!("{}", &i.file.as_raw_fd())).unwrap(), + attr.mode, + ); + if ret != FUSE_OK { + return ret; + } + } + _ => { + return libc::EBADF as i32; + } + }; + } + } + + if attr.valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID) != 0 { + let uid = if attr.valid & FUSE_SET_ATTR_UID != 0 { + attr.uid + } else { + u32::MAX + }; + + let gid = if attr.valid & FUSE_SET_ATTR_GID != 0 { + attr.gid + } else { + u32::MAX + }; + + match self.find_inode(node_id) { + Some(i) => { + let ret = fchown_at( + &i.file, + CString::new("").unwrap(), + uid, + gid, + libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW, + ); + if ret != FUSE_OK { + return ret; + } + } + _ => { + return libc::EBADF as i32; + } + }; + } + + if attr.valid & FUSE_SET_ATTR_SIZE != 0 { + if attr.valid & FATTR_FH != 0 { + match self.file_map.get_value(attr.fh as usize) { + Some(file) => { + let ret = ftruncate(&file, attr.size); + if ret != FUSE_OK { + return ret; + } + } + _ => { + return libc::EBADF as i32; + } + }; + } else { + match self.find_inode(node_id) { + Some(i) => { + let (file_opt, ret) = open_at( + &self.proc_dir, + CString::new(format!("{}", &i.file.as_raw_fd())).unwrap(), + libc::O_RDWR, + 0, + ); + if ret != FUSE_OK { + return ret; + } + + if let Some(file) = file_opt { + let ret = ftruncate(&file, attr.size); + if ret != FUSE_OK { + return ret; + } + } + } + _ => { + return libc::EBADF as i32; + } + }; + } + } + + if attr.valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME) != 0 { + let (a_sec, a_nsec) = if attr.valid & FUSE_SET_ATTR_ATIME_NOW != 0 { + (0, libc::UTIME_NOW) + } else if attr.valid & FUSE_SET_ATTR_ATIME != 0 { + (attr.atime, attr.atimensec as i64) + } else { + (0, libc::UTIME_OMIT) + }; + + let (m_sec, m_nsec) = if attr.valid & FUSE_SET_ATTR_MTIME_NOW != 0 { + (0, libc::UTIME_NOW) + } else if attr.valid & FUSE_SET_ATTR_MTIME != 0 { + (attr.mtime, attr.mtimensec as i64) + } else { + (0, libc::UTIME_OMIT) + }; + + if attr.valid & FATTR_FH != 0 { + match self.file_map.get_value(attr.fh as usize) { + Some(file) => { + let ret = futimens(&file, a_sec, a_nsec, m_sec, m_nsec); + if ret != FUSE_OK { + return ret; + } + } + _ => { + return libc::EBADF as i32; + } + }; + } else { + match self.find_inode(node_id) { + Some(i) => { + let ret = utimensat( + &self.proc_dir, + CString::new(format!("{}", &i.file.as_raw_fd())).unwrap(), + a_sec, + a_nsec, + m_sec, + m_nsec, + 0, + ); + if ret != FUSE_OK { + return ret; + } + } + _ => { + return libc::EBADF as i32; + } + }; + } + } + + self.getattr(node_id, fuse_attr) + } } diff --git a/vhost_user_fs/src/fs_ops.rs b/vhost_user_fs/src/fs_ops.rs index 8c6600f3e..6e456431b 100644 --- a/vhost_user_fs/src/fs_ops.rs +++ b/vhost_user_fs/src/fs_ops.rs @@ -50,3 +50,87 @@ pub fn open_at(file: &File, name: CString, flags: i32, mode: u32) -> (Option i32 { + errno::set_errno(errno::Errno(0)); + if unsafe { libc::fchmod(file.as_raw_fd(), mode) } < 0 { + return errno::errno().0; + } + + FUSE_OK +} + +pub fn fchmod_at(file: &File, name: CString, mode: u32) -> i32 { + errno::set_errno(errno::Errno(0)); + if unsafe { libc::fchmodat(file.as_raw_fd(), name.as_ptr(), mode, 0) } < 0 { + return errno::errno().0; + } + + FUSE_OK +} + +pub fn fchown_at(file: &File, name: CString, uid: u32, gid: u32, flags: i32) -> i32 { + errno::set_errno(errno::Errno(0)); + if unsafe { libc::fchownat(file.as_raw_fd(), name.as_ptr(), uid, gid, flags) } < 0 { + return errno::errno().0; + } + + FUSE_OK +} + +pub fn ftruncate(file: &File, size: u64) -> i32 { + errno::set_errno(errno::Errno(0)); + if unsafe { libc::ftruncate(file.as_raw_fd(), size as i64) } < 0 { + return errno::errno().0; + } + + FUSE_OK +} + +pub fn futimens(file: &File, a_sec: u64, a_nsec: i64, m_sec: u64, m_nsec: i64) -> i32 { + let tv = vec![ + libc::timespec { + tv_sec: a_sec as i64, + tv_nsec: a_nsec, + }, + libc::timespec { + tv_sec: m_sec as i64, + tv_nsec: m_nsec, + }, + ]; + + errno::set_errno(errno::Errno(0)); + if unsafe { libc::futimens(file.as_raw_fd(), tv.as_ptr()) } < 0 { + return errno::errno().0; + } + + FUSE_OK +} + +pub fn utimensat( + file: &File, + name: CString, + a_sec: u64, + a_nsec: i64, + m_sec: u64, + m_nsec: i64, + flags: i32, +) -> i32 { + let tv = vec![ + libc::timespec { + tv_sec: a_sec as i64, + tv_nsec: a_nsec, + }, + libc::timespec { + tv_sec: m_sec as i64, + tv_nsec: m_nsec, + }, + ]; + + errno::set_errno(errno::Errno(0)); + if unsafe { libc::utimensat(file.as_raw_fd(), name.as_ptr(), tv.as_ptr(), flags) } < 0 { + return errno::errno().0; + } + + FUSE_OK +} diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index 93876a446..b59ee5b6d 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -254,6 +254,30 @@ pub fn reply_fuse_msg( pub const FUSE_LOOKUP: u32 = 1; pub const FUSE_FORGET: u32 = 2; +pub const FUSE_GETATTR: u32 = 3; +pub const FUSE_SETATTR: u32 = 4; + +pub const FATTR_MODE: u32 = 1 << 0; +pub const FATTR_UID: u32 = 1 << 1; +pub const FATTR_GID: u32 = 1 << 2; +pub const FATTR_SIZE: u32 = 1 << 3; +pub const FATTR_ATIME: u32 = 1 << 4; +pub const FATTR_MTIME: u32 = 1 << 5; +pub const FATTR_FH: u32 = 1 << 6; +pub const FATTR_ATIME_NOW: u32 = 1 << 7; +pub const FATTR_MTIME_NOW: u32 = 1 << 8; +pub const FATTR_LOCKOWNER: u32 = 1 << 9; +pub const FATTR_CTIME: u32 = 1 << 10; + +pub const FUSE_SET_ATTR_MODE: u32 = 1 << 0; +pub const FUSE_SET_ATTR_UID: u32 = 1 << 1; +pub const FUSE_SET_ATTR_GID: u32 = 1 << 2; +pub const FUSE_SET_ATTR_SIZE: u32 = 1 << 3; +pub const FUSE_SET_ATTR_ATIME: u32 = 1 << 4; +pub const FUSE_SET_ATTR_MTIME: u32 = 1 << 5; +pub const FUSE_SET_ATTR_ATIME_NOW: u32 = 1 << 7; +pub const FUSE_SET_ATTR_MTIME_NOW: u32 = 1 << 8; +pub const FUSE_SET_ATTR_CTIME: u32 = 1 << 10; #[repr(C)] #[derive(Debug, Default, Copy, Clone)] @@ -347,3 +371,46 @@ pub struct FuseForgetIn { } impl ByteCode for FuseForgetIn {} +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseAttrOut { + pub attr_valid: u64, + pub attr_valid_nsec: u32, + pub dummy: u32, + pub attr: FuseAttr, +} + +impl ByteCode for FuseAttrOut {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseGetAttrIn { + pub getattr_flags: u32, + pub dummy: u32, + pub fh: u64, +} + +impl ByteCode for FuseGetAttrIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseSetattrIn { + pub valid: u32, + pub padding: u32, + pub fh: u64, + pub size: u64, + pub lock_owner: u64, + pub atime: u64, + pub mtime: u64, + pub ctime: u64, + pub atimensec: u32, + pub mtimensec: u32, + pub ctimensec: u32, + pub mode: u32, + pub unused4: u32, + pub uid: u32, + pub gid: u32, + pub unused5: u32, +} + +impl ByteCode for FuseSetattrIn {} diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs index 584c1f12b..b7758098c 100644 --- a/vhost_user_fs/src/fuse_proc.rs +++ b/vhost_user_fs/src/fuse_proc.rs @@ -86,3 +86,79 @@ pub fn do_fuse_forget( .forget(in_header.nodeid as usize, forget_in.nlookup); 0_u32 } + +pub fn do_fuse_getattr( + sys_mem: &Arc, + fs: Arc>, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let mut fuse_attr = FuseAttr::default(); + let ret = fs + .lock() + .unwrap() + .getattr(in_header.nodeid as usize, &mut fuse_attr); + if ret == 0 { + let attr_out = FuseAttrOut { + attr_valid: 1, + attr_valid_nsec: 0, + dummy: 0, + attr: fuse_attr, + }; + + reply_fuse_msg( + writer, + sys_mem, + in_header, + ret, + Some(vec![FuseIovec::from_obj(&attr_out)]), + mem::size_of::(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +pub fn do_fuse_setattr( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let setattr_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!( + "Failed to read object for setattr_in, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut fuse_attr = FuseAttr::default(); + let ret = fs + .lock() + .unwrap() + .setattr(in_header.nodeid as usize, &setattr_in, &mut fuse_attr); + if ret == FUSE_OK { + let attr_out = FuseAttrOut { + attr_valid: 1, + attr_valid_nsec: 0, + dummy: 0, + attr: fuse_attr, + }; + + reply_fuse_msg( + writer, + sys_mem, + in_header, + ret, + Some(vec![FuseIovec::from_obj(&attr_out)]), + mem::size_of::(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs index 4dcb90361..0f0396c0b 100644 --- a/vhost_user_fs/src/fuse_req.rs +++ b/vhost_user_fs/src/fuse_req.rs @@ -71,6 +71,10 @@ impl FuseReq { do_fuse_lookup(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) } FUSE_FORGET => do_fuse_forget(sys_mem, fs, &mut self.reader, &in_header), + FUSE_GETATTR => do_fuse_getattr(sys_mem, fs, &mut self.writer, &in_header), + FUSE_SETATTR => { + do_fuse_setattr(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } _ => { error!("The fuse msg {} is unsupported", in_header.opcode); reply_fuse_msg( -- Gitee From b18f050ac4cb796cabf0b2aac74a235c4b1b5534 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Thu, 4 Feb 2021 11:37:06 +0800 Subject: [PATCH 20/55] vhost_user_fs: implement the fuse message of FUSE_READLINK and FUSE_SYMLINK for userspace filesystem Signed-off-by: Fei Xu --- vhost_user_fs/src/fs.rs | 55 +++++++++++++++++ vhost_user_fs/src/fs_ops.rs | 72 ++++++++++++++++++++++ vhost_user_fs/src/fuse_msg.rs | 9 +++ vhost_user_fs/src/fuse_proc.rs | 106 +++++++++++++++++++++++++++++++++ vhost_user_fs/src/fuse_req.rs | 4 ++ 5 files changed, 246 insertions(+) diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index 425cc4b19..e2b3207fd 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -484,4 +484,59 @@ impl FileSystem { self.getattr(node_id, fuse_attr) } + + pub fn readlink(&self, node_id: usize, buff: &mut Vec) -> i32 { + let inode = match self.find_inode(node_id) { + Some(i) => i.clone(), + None => { + return libc::EBADF as i32; + } + }; + + let (buf_opt, ret) = readlinkat(&inode.file, CString::new("").unwrap()); + if ret != FUSE_OK { + return ret; + } + + if let Some(mut buf) = buf_opt { + buff.append(&mut buf); + } else { + return libc::EBADF as i32; + } + + FUSE_OK + } + + pub fn symlink( + &mut self, + in_header: &FuseInHeader, + name: CString, + link_name: CString, + node_id: &mut u64, + fuse_attr: &mut FuseAttr, + ) -> i32 { + let parent_inode = match self.find_inode(in_header.nodeid as usize) { + Some(i) => i.clone(), + _ => { + return libc::EBADF as i32; + } + }; + + let mut old_uid = 0_u32; + let mut old_gid = 0_u32; + let ret = change_uid_gid(in_header.uid, in_header.gid, &mut old_uid, &mut old_gid); + if ret != FUSE_OK { + return ret; + } + + let ret = symlinkat(&parent_inode.file, name.clone(), link_name); + + recover_uid_gid(old_uid, old_gid); + + if ret != FUSE_OK { + return ret; + } + + self.internal_lookup(&parent_inode, name, node_id, fuse_attr) + } } diff --git a/vhost_user_fs/src/fs_ops.rs b/vhost_user_fs/src/fs_ops.rs index 6e456431b..23fa405a3 100644 --- a/vhost_user_fs/src/fs_ops.rs +++ b/vhost_user_fs/src/fs_ops.rs @@ -134,3 +134,75 @@ pub fn utimensat( FUSE_OK } + +const MAX_PATH_LEN: usize = 256; +pub fn readlinkat(file: &File, path: CString) -> (Option>, i32) { + let mut buf = vec![0; MAX_PATH_LEN + 1]; + + errno::set_errno(errno::Errno(0)); + let ret = unsafe { + libc::readlinkat( + file.as_raw_fd(), + path.as_ptr(), + buf.as_mut_ptr() as *mut libc::c_char, + buf.len(), + ) + }; + + if ret == -1 { + return (None, errno::errno().0 as i32); + } + + buf.resize(ret as usize, 0); + + (Some(buf), FUSE_OK) +} + +pub fn symlinkat(file: &File, name: CString, link_name: CString) -> i32 { + errno::set_errno(errno::Errno(0)); + + let ret = unsafe { libc::symlinkat(link_name.as_ptr(), file.as_raw_fd(), name.as_ptr()) }; + + if ret == -1 { + return errno::errno().0 as i32; + } + + FUSE_OK +} + +pub fn change_uid_gid(new_uid: u32, new_gid: u32, old_uid: &mut u32, old_gid: &mut u32) -> i32 { + let current_uid = unsafe { libc::geteuid() }; + let current_gid = unsafe { libc::getegid() }; + + errno::set_errno(errno::Errno(0)); + let ret = unsafe { libc::syscall(libc::SYS_setresgid, -1, new_gid, -1) }; + if ret == -1 { + return errno::errno().0; + } + + errno::set_errno(errno::Errno(0)); + let ret = unsafe { libc::syscall(libc::SYS_setresuid, -1, new_uid, -1) }; + if ret == -1 { + unsafe { libc::syscall(libc::SYS_setresgid, -1, current_gid, -1) }; + + return errno::errno().0; + } + + *old_uid = current_uid; + *old_gid = current_gid; + FUSE_OK +} + +pub fn recover_uid_gid(old_uid: u32, old_gid: u32) -> i32 { + let ret = unsafe { libc::syscall(libc::SYS_setresuid, -1, old_uid, -1) }; + if ret == -1 { + panic!("Failed to recover uid {} {}", old_uid, old_gid); + } + + let ret = unsafe { libc::syscall(libc::SYS_setresgid, -1, old_gid, -1) }; + if ret == -1 { + panic!("Failed to recover gid {} {}", old_uid, old_gid); + } + + FUSE_OK +} diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index b59ee5b6d..a32fe0172 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -207,6 +207,13 @@ impl<'a> FuseIovec<'a> { len: body.len(), } } + + pub fn from_slice(body: &'a [u8]) -> Self { + FuseIovec { + body, + len: body.len(), + } + } } pub fn reply_fuse_msg( @@ -256,6 +263,8 @@ pub const FUSE_LOOKUP: u32 = 1; pub const FUSE_FORGET: u32 = 2; pub const FUSE_GETATTR: u32 = 3; pub const FUSE_SETATTR: u32 = 4; +pub const FUSE_READLINK: u32 = 5; +pub const FUSE_SYMLINK: u32 = 6; pub const FATTR_MODE: u32 = 1 << 0; pub const FATTR_UID: u32 = 1 << 1; diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs index b7758098c..705fbeaac 100644 --- a/vhost_user_fs/src/fuse_proc.rs +++ b/vhost_user_fs/src/fuse_proc.rs @@ -10,6 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. +use std::ffi::CString; use std::mem; use std::sync::{Arc, Mutex}; @@ -20,6 +21,25 @@ use address_space::AddressSpace; use super::fs::FileSystem; use super::fuse_msg::*; +fn is_safe_path(path: CString) -> bool { + let path_str = match path.into_string() { + Ok(str_) => str_, + Err(_e) => return false, + }; + + if path_str.find('/').is_some() { + return false; + } + + //Check if the path is "." or ".." + let bytes = path_str.as_bytes(); + if bytes[0] == 0x2e && (bytes[1] == 0x0 || (bytes[1] == 0x2e && bytes[2] == 0x0)) { + return false; + } + + true +} + pub fn do_fuse_lookup( sys_mem: &Arc, fs: Arc>, @@ -162,3 +182,89 @@ pub fn do_fuse_setattr( reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) } } + +pub fn do_fuse_readlink( + sys_mem: &Arc, + fs: Arc>, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let mut buff = Vec::new(); + let ret = fs + .lock() + .unwrap() + .readlink(in_header.nodeid as usize, &mut buff); + + if ret == FUSE_OK { + reply_fuse_msg( + writer, + sys_mem, + in_header, + FUSE_OK, + Some(vec![FuseIovec::from_slice(buff.as_slice())]), + buff.len(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +pub fn do_fuse_symlink( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let name = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!("Failed to read name for symlink, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let link_name = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!( + "Failed to read link name for symlink, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + if !is_safe_path(link_name.clone()) { + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + + let mut node_id = 0_u64; + let mut fuse_attr = FuseAttr::default(); + let ret = fs + .lock() + .unwrap() + .symlink(in_header, name, link_name, &mut node_id, &mut fuse_attr); + if ret == FUSE_OK { + let entry_out = FuseEntryOut { + nodeid: node_id, + generation: 0, + entry_valid: 0, + entry_valid_nsec: 0, + attr_valid: 0, + attr_valid_nsec: 0, + attr: fuse_attr, + }; + + reply_fuse_msg( + writer, + sys_mem, + in_header, + FUSE_OK, + Some(vec![FuseIovec::from_obj(&entry_out)]), + mem::size_of::(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs index 0f0396c0b..ddc0ddd46 100644 --- a/vhost_user_fs/src/fuse_req.rs +++ b/vhost_user_fs/src/fuse_req.rs @@ -75,6 +75,10 @@ impl FuseReq { FUSE_SETATTR => { do_fuse_setattr(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) } + FUSE_READLINK => do_fuse_readlink(sys_mem, fs, &mut self.writer, &in_header), + FUSE_SYMLINK => { + do_fuse_symlink(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } _ => { error!("The fuse msg {} is unsupported", in_header.opcode); reply_fuse_msg( -- Gitee From e3679456a129d2f743d41ba4d9b4d2c1a720c409 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Thu, 4 Feb 2021 11:49:13 +0800 Subject: [PATCH 21/55] vhost_user_fs: implement the fuse message of FUSE_MKNOD and FUSE_MKDIR for userspace filesystem Signed-off-by: Fei Xu --- vhost_user_fs/src/fs.rs | 75 ++++++++++++++++++++++ vhost_user_fs/src/fs_ops.rs | 21 ++++++ vhost_user_fs/src/fuse_msg.rs | 22 +++++++ vhost_user_fs/src/fuse_proc.rs | 114 +++++++++++++++++++++++++++++++++ vhost_user_fs/src/fuse_req.rs | 6 ++ 5 files changed, 238 insertions(+) diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index e2b3207fd..91e47ec46 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -539,4 +539,79 @@ impl FileSystem { self.internal_lookup(&parent_inode, name, node_id, fuse_attr) } + + pub fn mknod( + &mut self, + in_header: &FuseInHeader, + mknod_in: &FuseMknodIn, + name: CString, + node_id: &mut u64, + fuse_attr: &mut FuseAttr, + ) -> i32 { + let parent_inode = match self.find_inode(in_header.nodeid as usize) { + Some(i) => i.clone(), + _ => { + return libc::EBADF as i32; + } + }; + + let mut old_uid = 0_u32; + let mut old_gid = 0_u32; + let ret = change_uid_gid(in_header.uid, in_header.gid, &mut old_uid, &mut old_gid); + if ret != FUSE_OK { + return ret; + } + + let ret = mknodat( + &parent_inode.file, + name.clone(), + mknod_in.mode & !mknod_in.umask, + mknod_in.rdev, + ); + + recover_uid_gid(old_uid, old_gid); + + if ret != FUSE_OK { + return ret; + } + + self.internal_lookup(&parent_inode, name, node_id, fuse_attr) + } + + pub fn mkdir( + &mut self, + in_header: &FuseInHeader, + mkdir_in: &FuseMkdirIn, + name: CString, + node_id: &mut u64, + fuse_attr: &mut FuseAttr, + ) -> i32 { + let parent_dir = match self.find_inode(in_header.nodeid as usize) { + Some(i) => i.clone(), + _ => { + return libc::EBADF as i32; + } + }; + + let mut old_uid = 0_u32; + let mut old_gid = 0_u32; + let ret = change_uid_gid(in_header.uid, in_header.gid, &mut old_uid, &mut old_gid); + if ret != FUSE_OK { + return ret; + } + + let ret = mkdir_at( + &parent_dir.file, + name.clone(), + mkdir_in.mode & !mkdir_in.umask, + ); + + recover_uid_gid(old_uid, old_gid); + + if ret != FUSE_OK { + return ret; + } + + self.internal_lookup(&parent_dir, name, node_id, fuse_attr) + } } diff --git a/vhost_user_fs/src/fs_ops.rs b/vhost_user_fs/src/fs_ops.rs index 23fa405a3..f5841dbb9 100644 --- a/vhost_user_fs/src/fs_ops.rs +++ b/vhost_user_fs/src/fs_ops.rs @@ -206,3 +206,24 @@ pub fn recover_uid_gid(old_uid: u32, old_gid: u32) -> i32 { FUSE_OK } + +pub fn mknodat(file: &File, name: CString, mode: u32, rdev: u32) -> i32 { + errno::set_errno(errno::Errno(0)); + + let ret = unsafe { libc::mknodat(file.as_raw_fd(), name.as_ptr(), mode, rdev as u64) }; + + if ret == -1 { + return errno::errno().0 as i32; + } + + FUSE_OK +} + +pub fn mkdir_at(file: &File, name: CString, mode: u32) -> i32 { + errno::set_errno(errno::Errno(0)); + if unsafe { libc::mkdirat(file.as_raw_fd(), name.as_ptr(), mode) } < 0 { + return errno::errno().0; + } + + FUSE_OK +} diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index a32fe0172..bacc029e3 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -265,6 +265,8 @@ pub const FUSE_GETATTR: u32 = 3; pub const FUSE_SETATTR: u32 = 4; pub const FUSE_READLINK: u32 = 5; pub const FUSE_SYMLINK: u32 = 6; +pub const FUSE_MKNOD: u32 = 8; +pub const FUSE_MKDIR: u32 = 9; pub const FATTR_MODE: u32 = 1 << 0; pub const FATTR_UID: u32 = 1 << 1; @@ -423,3 +425,23 @@ pub struct FuseSetattrIn { } impl ByteCode for FuseSetattrIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseMknodIn { + pub mode: u32, + pub rdev: u32, + pub umask: u32, + pub padding: u32, +} + +impl ByteCode for FuseMknodIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseMkdirIn { + pub mode: u32, + pub umask: u32, +} + +impl ByteCode for FuseMkdirIn {} diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs index 705fbeaac..9b25bb121 100644 --- a/vhost_user_fs/src/fuse_proc.rs +++ b/vhost_user_fs/src/fuse_proc.rs @@ -268,3 +268,117 @@ pub fn do_fuse_symlink( reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) } } + +pub fn do_fuse_mknod( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let mknod_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for mknod_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let name = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!("Failed to read name for mknod, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + if !is_safe_path(name.clone()) { + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + + let mut node_id = 0_u64; + let mut fuse_attr = FuseAttr::default(); + let ret = fs + .lock() + .unwrap() + .mknod(in_header, &mknod_in, name, &mut node_id, &mut fuse_attr); + if ret == FUSE_OK { + let entry_out = FuseEntryOut { + nodeid: node_id, + generation: 0, + entry_valid: 0, + entry_valid_nsec: 0, + attr_valid: 0, + attr_valid_nsec: 0, + attr: fuse_attr, + }; + + reply_fuse_msg( + writer, + sys_mem, + in_header, + FUSE_OK, + Some(vec![FuseIovec::from_obj(&entry_out)]), + mem::size_of::(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +pub fn do_fuse_mkdir( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let mkdir_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for mkdir_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let name = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!("Failed to read name for mkdir, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + if !is_safe_path(name.clone()) { + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + + let mut node_id = 0_u64; + let mut fuse_attr = FuseAttr::default(); + let ret = fs + .lock() + .unwrap() + .mkdir(in_header, &mkdir_in, name, &mut node_id, &mut fuse_attr); + if ret == FUSE_OK { + let entry_out = FuseEntryOut { + nodeid: node_id, + generation: 0, + entry_valid: 0, + entry_valid_nsec: 0, + attr_valid: 0, + attr_valid_nsec: 0, + attr: fuse_attr, + }; + + reply_fuse_msg( + writer, + sys_mem, + in_header, + FUSE_OK, + Some(vec![FuseIovec::from_obj(&entry_out)]), + mem::size_of::(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs index ddc0ddd46..da5b68216 100644 --- a/vhost_user_fs/src/fuse_req.rs +++ b/vhost_user_fs/src/fuse_req.rs @@ -79,6 +79,12 @@ impl FuseReq { FUSE_SYMLINK => { do_fuse_symlink(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) } + FUSE_MKNOD => { + do_fuse_mknod(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_MKDIR => { + do_fuse_mkdir(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } _ => { error!("The fuse msg {} is unsupported", in_header.opcode); reply_fuse_msg( -- Gitee From 22f9659619e1e8f124bad6e2bbec935367747d35 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Thu, 4 Feb 2021 11:56:39 +0800 Subject: [PATCH 22/55] vhost_user_fs: implement the fuse message of FUSE_UNLINK and FUSE_RMDIR for userspace filesystem Signed-off-by: Fei Xu --- vhost_user_fs/src/fs.rs | 66 ++++++++++++++++++++++++++++++++++ vhost_user_fs/src/fs_ops.rs | 12 +++++++ vhost_user_fs/src/fuse_msg.rs | 2 ++ vhost_user_fs/src/fuse_proc.rs | 48 +++++++++++++++++++++++++ vhost_user_fs/src/fuse_req.rs | 6 ++++ 5 files changed, 134 insertions(+) diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index 91e47ec46..136dbe0bf 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -614,4 +614,70 @@ impl FileSystem { self.internal_lookup(&parent_dir, name, node_id, fuse_attr) } + + pub fn unlink(&mut self, parent_nodeid: usize, name: CString) -> i32 { + let parent_inode = match self.find_inode(parent_nodeid) { + Some(i) => i.clone(), + None => return libc::EBADF as i32, + }; + + let (stat, ret) = fstat_at( + &parent_inode.file, + name.clone(), + libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW, + ); + if ret != FUSE_OK { + return ret; + } + + let key = StatKey { + ino: stat.st_ino, + dev: stat.st_dev, + }; + + match self.inodes.get(&key) { + Some(i) => i.clone(), + None => return libc::EIO as i32, + }; + + let ret = unlinkat(&parent_inode.file, name, 0); + if ret != FUSE_OK { + return ret; + } + + FUSE_OK + } + + pub fn rmdir(&mut self, parent_nodeid: usize, name: CString) -> i32 { + let parent_inode = match self.find_inode(parent_nodeid) { + Some(i) => i.clone(), + None => return libc::EBADF as i32, + }; + + let (stat, ret) = fstat_at( + &parent_inode.file, + name.clone(), + libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW, + ); + if ret != FUSE_OK { + return ret; + } + + let key = StatKey { + ino: stat.st_ino, + dev: stat.st_dev, + }; + + match self.inodes.get(&key) { + Some(i) => i.clone(), + None => return libc::EIO as i32, + }; + + let ret = unlinkat(&parent_inode.file, name, libc::AT_REMOVEDIR); + if ret != FUSE_OK { + return ret; + } + + FUSE_OK + } } diff --git a/vhost_user_fs/src/fs_ops.rs b/vhost_user_fs/src/fs_ops.rs index f5841dbb9..d2291eee5 100644 --- a/vhost_user_fs/src/fs_ops.rs +++ b/vhost_user_fs/src/fs_ops.rs @@ -227,3 +227,15 @@ pub fn mkdir_at(file: &File, name: CString, mode: u32) -> i32 { FUSE_OK } + +pub fn unlinkat(file: &File, name: CString, flags: i32) -> i32 { + errno::set_errno(errno::Errno(0)); + + let ret = unsafe { libc::unlinkat(file.as_raw_fd(), name.as_ptr(), flags) }; + + if ret == -1 { + return errno::errno().0 as i32; + } + + FUSE_OK +} diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index bacc029e3..3201215f5 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -267,6 +267,8 @@ pub const FUSE_READLINK: u32 = 5; pub const FUSE_SYMLINK: u32 = 6; pub const FUSE_MKNOD: u32 = 8; pub const FUSE_MKDIR: u32 = 9; +pub const FUSE_UNLINK: u32 = 10; +pub const FUSE_RMDIR: u32 = 11; pub const FATTR_MODE: u32 = 1 << 0; pub const FATTR_UID: u32 = 1 << 1; diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs index 9b25bb121..63f976714 100644 --- a/vhost_user_fs/src/fuse_proc.rs +++ b/vhost_user_fs/src/fuse_proc.rs @@ -382,3 +382,51 @@ pub fn do_fuse_mkdir( reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) } } + +pub fn do_fuse_unlink( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let name = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!("Failed to read name for unlink, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + if !is_safe_path(name.clone()) { + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + + let ret = fs.lock().unwrap().unlink(in_header.nodeid as usize, name); + + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} + +pub fn do_fuse_rmdir( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let name = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!("Failed to read name for rmdir, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + if !is_safe_path(name.clone()) { + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + + let ret = fs.lock().unwrap().rmdir(in_header.nodeid as usize, name); + + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs index da5b68216..aae583f2e 100644 --- a/vhost_user_fs/src/fuse_req.rs +++ b/vhost_user_fs/src/fuse_req.rs @@ -85,6 +85,12 @@ impl FuseReq { FUSE_MKDIR => { do_fuse_mkdir(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) } + FUSE_UNLINK => { + do_fuse_unlink(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_RMDIR => { + do_fuse_rmdir(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } _ => { error!("The fuse msg {} is unsupported", in_header.opcode); reply_fuse_msg( -- Gitee From a5a0ac0b36562b34655891f71942d474aa3fd565 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Thu, 4 Feb 2021 14:14:47 +0800 Subject: [PATCH 23/55] vhost_user_fs: implement the fuse message of FUSE_RENAME and FUSE_LINK for userspace filesystem Signed-off-by: Fei Xu --- vhost_user_fs/src/fs.rs | 96 +++++++++++++++++++++++++++++- vhost_user_fs/src/fs_ops.rs | 45 ++++++++++++++ vhost_user_fs/src/fuse_msg.rs | 18 ++++++ vhost_user_fs/src/fuse_proc.rs | 104 +++++++++++++++++++++++++++++++++ vhost_user_fs/src/fuse_req.rs | 4 ++ 5 files changed, 266 insertions(+), 1 deletion(-) diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index 136dbe0bf..fffa71231 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -13,7 +13,7 @@ use std::collections::BTreeMap; use std::ffi::CString; use std::fs::File; -use std::os::unix::io::AsRawFd; +use std::os::unix::io::{AsRawFd, RawFd}; use super::fs_ops::*; use super::fuse_msg::*; @@ -122,6 +122,10 @@ impl Inode { key, } } + + pub fn as_raw_fd(&self) -> RawFd { + self.file.as_raw_fd() + } } impl Clone for Inode { @@ -680,4 +684,94 @@ impl FileSystem { FUSE_OK } + + pub fn rename( + &self, + parent_nodeid: usize, + oldname: CString, + newparent_nodeid: usize, + newname: CString, + ) -> i32 { + let parent_inode = match self.find_inode(parent_nodeid) { + Some(i) => i.clone(), + None => { + return libc::EBADF as i32; + } + }; + + let newparent_inode = match self.find_inode(newparent_nodeid) { + Some(i) => i.clone(), + None => { + return libc::EBADF as i32; + } + }; + + let (stat, ret) = fstat_at( + &parent_inode.file, + oldname.clone(), + libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW, + ); + if ret != FUSE_OK { + return ret; + } + + let key = StatKey { + ino: stat.st_ino, + dev: stat.st_dev, + }; + + match self.inodes.get(&key) { + Some(_) => {} + None => return libc::EIO as i32, + }; + + rename(&parent_inode.file, oldname, &newparent_inode.file, newname) + } + + pub fn link( + &mut self, + parent_nodeid: usize, + old_nodeid: usize, + name: CString, + node_id: &mut u64, + fuse_attr: &mut FuseAttr, + ) -> i32 { + let proc_file = self.proc_dir.try_clone().unwrap(); + let parent_inode = match self.find_inode(parent_nodeid) { + Some(i) => i.clone(), + None => return libc::EBADF as i32, + }; + + let inode = match self.find_mut_inode(old_nodeid) { + Some(inode_) => inode_, + None => return libc::EBADF as i32, + }; + + let ret = linkat( + &proc_file, + CString::new(format!("{}", inode.as_raw_fd())).unwrap(), + &parent_inode.file, + name, + libc::AT_SYMLINK_FOLLOW, + ); + if ret != FUSE_OK { + return ret; + } + + let (stat, ret) = fstat_at( + &inode.file, + CString::new("").unwrap(), + libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW, + ); + + if ret != FUSE_OK { + return ret; + } + + *fuse_attr = FuseAttr::from_stat(stat); + *node_id = inode.node_id as u64; + inode.nlookup += 1; + + FUSE_OK + } } diff --git a/vhost_user_fs/src/fs_ops.rs b/vhost_user_fs/src/fs_ops.rs index d2291eee5..9b54da71b 100644 --- a/vhost_user_fs/src/fs_ops.rs +++ b/vhost_user_fs/src/fs_ops.rs @@ -239,3 +239,48 @@ pub fn unlinkat(file: &File, name: CString, flags: i32) -> i32 { FUSE_OK } + +pub fn rename(olddir: &File, name: CString, newdir: &File, newname: CString) -> i32 { + errno::set_errno(errno::Errno(0)); + + let ret = unsafe { + libc::renameat( + olddir.as_raw_fd(), + name.as_ptr(), + newdir.as_raw_fd(), + newname.as_ptr(), + ) + }; + + if ret != FUSE_OK { + return errno::errno().0 as i32; + } + + FUSE_OK +} + +pub fn linkat( + old_file: &File, + old_name: CString, + new_file: &File, + new_name: CString, + flags: i32, +) -> i32 { + errno::set_errno(errno::Errno(0)); + + let ret = unsafe { + libc::linkat( + old_file.as_raw_fd(), + old_name.as_ptr(), + new_file.as_raw_fd(), + new_name.as_ptr(), + flags, + ) + }; + + if ret == -1 { + return errno::errno().0 as i32; + } + + FUSE_OK +} diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index 3201215f5..d61dbe4f2 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -269,6 +269,8 @@ pub const FUSE_MKNOD: u32 = 8; pub const FUSE_MKDIR: u32 = 9; pub const FUSE_UNLINK: u32 = 10; pub const FUSE_RMDIR: u32 = 11; +pub const FUSE_RENAME: u32 = 12; +pub const FUSE_LINK: u32 = 13; pub const FATTR_MODE: u32 = 1 << 0; pub const FATTR_UID: u32 = 1 << 1; @@ -447,3 +449,19 @@ pub struct FuseMkdirIn { } impl ByteCode for FuseMkdirIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseRenameIn { + pub newdir: u64, +} + +impl ByteCode for FuseRenameIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseLinkIn { + pub oldnodeid: u64, +} + +impl ByteCode for FuseLinkIn {} diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs index 63f976714..8423de35e 100644 --- a/vhost_user_fs/src/fuse_proc.rs +++ b/vhost_user_fs/src/fuse_proc.rs @@ -430,3 +430,107 @@ pub fn do_fuse_rmdir( reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) } + +pub fn do_fuse_rename( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let rename_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for rename_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let oldname = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!("Failed to read old name for rename, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let newname = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!("Failed to read new name for rename, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + if !is_safe_path(oldname.clone()) || !is_safe_path(newname.clone()) { + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + + let ret = fs.lock().unwrap().rename( + in_header.nodeid as usize, + oldname, + rename_in.newdir as usize, + newname, + ); + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} + +pub fn do_fuse_link( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let link_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for link_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let name = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!("Failed to read name for link, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + if !is_safe_path(name.clone()) { + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + + let mut fuse_attr = FuseAttr::default(); + let mut node_id = 0_u64; + let ret = fs.lock().unwrap().link( + in_header.nodeid as usize, + link_in.oldnodeid as usize, + name, + &mut node_id, + &mut fuse_attr, + ); + + if ret == FUSE_OK { + let entry_out = FuseEntryOut { + nodeid: node_id, + generation: 0, + entry_valid: 0, + entry_valid_nsec: 0, + attr_valid: 0, + attr_valid_nsec: 0, + attr: fuse_attr, + }; + reply_fuse_msg( + writer, + sys_mem, + in_header, + ret, + Some(vec![FuseIovec::from_obj(&entry_out)]), + mem::size_of::(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs index aae583f2e..db34669d0 100644 --- a/vhost_user_fs/src/fuse_req.rs +++ b/vhost_user_fs/src/fuse_req.rs @@ -91,6 +91,10 @@ impl FuseReq { FUSE_RMDIR => { do_fuse_rmdir(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) } + FUSE_RENAME => { + do_fuse_rename(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_LINK => do_fuse_link(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header), _ => { error!("The fuse msg {} is unsupported", in_header.opcode); reply_fuse_msg( -- Gitee From a67ba16d5781172e6fad06b661e021516c4f1b60 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Thu, 4 Feb 2021 14:53:14 +0800 Subject: [PATCH 24/55] vhost_user_fs: implement the fuse message of FUSE_OPEN, FUSE_READ and FUSE_WRITE for userspace filesystem Signed-off-by: Fei Xu --- vhost_user_fs/src/fs.rs | 51 ++++++++++++ vhost_user_fs/src/fuse_msg.rs | 139 ++++++++++++++++++++++++++++++++- vhost_user_fs/src/fuse_proc.rs | 126 ++++++++++++++++++++++++++++++ vhost_user_fs/src/fuse_req.rs | 5 ++ 4 files changed, 319 insertions(+), 2 deletions(-) diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index fffa71231..cdb921161 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -774,4 +774,55 @@ impl FileSystem { FUSE_OK } + + pub fn open(&mut self, node_id: usize, flags: u32, fh: &mut u64) -> i32 { + let inode_fd = match self.find_inode(node_id) { + Some(i) => i.as_raw_fd(), + None => { + return libc::EBADF as i32; + } + }; + + let (file_opt, ret) = open_at( + &self.proc_dir, + CString::new(format!("{}", inode_fd)).unwrap(), + (flags as i32) & !libc::O_NOFOLLOW, + 0, + ); + if ret != FUSE_OK { + return ret; + } + + if let Some(file) = file_opt { + *fh = self.file_map.get_map(file.try_clone().unwrap()) as u64; + } + + FUSE_OK + } + + pub fn read(&mut self, fh: usize, fd: &mut RawFd) -> i32 { + match self.file_map.get_value(fh) { + Some(file) => { + *fd = file.as_raw_fd(); + } + _ => { + return libc::EBADF as i32; + } + } + + FUSE_OK + } + + pub fn write(&mut self, fh: usize, fd: &mut RawFd) -> i32 { + match self.file_map.get_value(fh) { + Some(file) => { + *fd = file.as_raw_fd(); + } + _ => { + return libc::EBADF as i32; + } + } + + FUSE_OK + } } diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index d61dbe4f2..99509b97a 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -186,6 +186,82 @@ impl FuseBuffer { pub fn write_obj(&mut self, sys_mem: &Arc, data: &T) -> Result<()> { self.write_slice(sys_mem, data.as_bytes(), size_of::()) } + + // if read is true, read data from host file, then write them to virtio queue. + // if read is false, get data from virtio queue, then write to host file. + pub fn access_file( + &mut self, + sys_mem: &Arc, + fd: i32, + offset: u64, + size: u32, + read: bool, + ) -> Result { + let mut remain_len = size; + let mut file_off = offset; + + let mut index = 0; + let mut bufs = self.bufs.clone(); + + loop { + if index >= bufs.len() { + bail!("{} out of bufs's index", index); + } + + let buf = if let Some(b) = bufs.get_mut(index) { + b + } else { + bail!("{} out of bufs's bound", index); + }; + + let len = if remain_len < buf.len { + remain_len + } else { + buf.len + }; + + let hva = if let Some(hva) = sys_mem.get_host_address(buf.addr) { + hva + } else { + bail!("read file error: get hva failed."); + }; + + let iov = vec![libc::iovec { + iov_base: hva as *mut libc::c_void, + iov_len: len as usize, + }]; + + let ret = unsafe { + if read { + libc::preadv(fd, iov.as_ptr(), iov.len() as i32, file_off as i64) + } else { + libc::pwritev(fd, iov.as_ptr(), iov.len() as i32, file_off as i64) + } + } as u32; + if ret == u32::MAX { + bail!("read file error"); + } + + remain_len -= ret; + file_off += ret as u64; + + self.bufs.pop_front(); + if ret < len { + buf.addr.0 += ret as u64; + buf.len -= ret; + + self.bufs.push_front(*buf); + } else { + index += 1; + } + + if ret == 0 || remain_len == 0 { + break; // finish + } + } + + Ok(size - remain_len) + } } impl Default for FuseBuffer { @@ -195,8 +271,8 @@ impl Default for FuseBuffer { } pub struct FuseIovec<'a> { - pub body: &'a [u8], - pub len: usize, + body: &'a [u8], + len: usize, } impl<'a> FuseIovec<'a> { @@ -271,6 +347,9 @@ pub const FUSE_UNLINK: u32 = 10; pub const FUSE_RMDIR: u32 = 11; pub const FUSE_RENAME: u32 = 12; pub const FUSE_LINK: u32 = 13; +pub const FUSE_OPEN: u32 = 14; +pub const FUSE_READ: u32 = 15; +pub const FUSE_WRITE: u32 = 16; pub const FATTR_MODE: u32 = 1 << 0; pub const FATTR_UID: u32 = 1 << 1; @@ -465,3 +544,59 @@ pub struct FuseLinkIn { } impl ByteCode for FuseLinkIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseOpenIn { + pub flags: u32, + pub unused: u32, +} + +impl ByteCode for FuseOpenIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseOpenOut { + pub fh: u64, + pub open_flags: u32, + pub padding: u32, +} + +impl ByteCode for FuseOpenOut {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseReadIn { + pub fh: u64, + pub offset: u64, + pub size: u32, + pub read_flags: u32, + pub lock_owner: u64, + pub flags: u32, + pub padding: u32, +} + +impl ByteCode for FuseReadIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseWriteIn { + pub fh: u64, + pub offset: u64, + pub size: u32, + pub write_flags: u32, + pub lock_owner: u64, + pub flags: u32, + pub padding: u32, +} + +impl ByteCode for FuseWriteIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseWriteOut { + pub size: u32, + pub padding: u32, +} + +impl ByteCode for FuseWriteOut {} diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs index 8423de35e..25d2e4ee7 100644 --- a/vhost_user_fs/src/fuse_proc.rs +++ b/vhost_user_fs/src/fuse_proc.rs @@ -534,3 +534,129 @@ pub fn do_fuse_link( reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) } } + +pub fn do_fuse_open( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let open_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for open_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut fh = 0_u64; + let ret = fs + .lock() + .unwrap() + .open(in_header.nodeid as usize, open_in.flags, &mut fh); + if ret == FUSE_OK { + let open_out = FuseOpenOut { + fh, + open_flags: 0, + padding: 0, + }; + + reply_fuse_msg( + writer, + sys_mem, + in_header, + ret, + Some(vec![FuseIovec::from_obj(&open_out)]), + mem::size_of::(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +pub fn do_fuse_read( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let read_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for read_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut fd = 0; + let ret = fs.lock().unwrap().read(read_in.fh as usize, &mut fd); + if ret == FUSE_OK { + let buf_header = match writer.bufs.get(0) { + Some(b) => *b, + None => { + error!("Failed to get the address of out_header"); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut f_ret = reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize); + + match writer.access_file(sys_mem, fd, read_in.offset, read_in.size, true) { + Ok(size) => { + f_ret += size; + // write size to FuseOutHeader.len + sys_mem.write_object(&f_ret, buf_header.addr).unwrap(); + } + Err(e) => { + error!("Failed to access file for reading, {}", e.display_chain()); + } + }; + + f_ret + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +pub fn do_fuse_write( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let write_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for write_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut fd = 0; + let ret = fs.lock().unwrap().write(write_in.fh as usize, &mut fd); + if ret == FUSE_OK { + match reader.access_file(sys_mem, fd, write_in.offset, write_in.size, false) { + Ok(size) => { + let write_out = FuseWriteOut { size, padding: 0 }; + + reply_fuse_msg( + writer, + sys_mem, + in_header, + ret, + Some(vec![FuseIovec::from_obj(&write_out)]), + mem::size_of::(), + ) + } + Err(e) => { + error!("Failed to access file for writing, {}", e.display_chain()); + reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize) + } + } + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs index db34669d0..8cc05ee1b 100644 --- a/vhost_user_fs/src/fuse_req.rs +++ b/vhost_user_fs/src/fuse_req.rs @@ -95,6 +95,11 @@ impl FuseReq { do_fuse_rename(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) } FUSE_LINK => do_fuse_link(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header), + FUSE_OPEN => do_fuse_open(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header), + FUSE_READ => do_fuse_read(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header), + FUSE_WRITE => { + do_fuse_write(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } _ => { error!("The fuse msg {} is unsupported", in_header.opcode); reply_fuse_msg( -- Gitee From b548e634817ea2d9fe335a862ae40543a1db69ce Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Thu, 4 Feb 2021 15:05:46 +0800 Subject: [PATCH 25/55] vhost_user_fs: implement the fuse message of FUSE_STATFS and FUSE_RELEASE for userspace filesystem Signed-off-by: Fei Xu --- vhost_user_fs/src/fs.rs | 24 +++++++++++++++ vhost_user_fs/src/fs_ops.rs | 11 +++++++ vhost_user_fs/src/fuse_msg.rs | 56 ++++++++++++++++++++++++++++++++++ vhost_user_fs/src/fuse_proc.rs | 47 ++++++++++++++++++++++++++++ vhost_user_fs/src/fuse_req.rs | 4 +++ 5 files changed, 142 insertions(+) diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index cdb921161..80b65c56f 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -825,4 +825,28 @@ impl FileSystem { FUSE_OK } + + pub fn statfs(&mut self, node_id: usize, fuse_statfs: &mut FuseStatfsOut) -> i32 { + let inode = match self.find_inode(node_id) { + Some(i) => i.clone(), + None => { + return libc::EBADF as i32; + } + }; + + let (stat, ret) = fstat_vfs(&inode.file); + if ret != FUSE_OK { + return ret; + } + + *fuse_statfs = FuseStatfsOut::from_stat(stat); + + FUSE_OK + } + + pub fn release(&mut self, fh: usize) -> i32 { + self.file_map.put_map(fh); + + FUSE_OK + } } diff --git a/vhost_user_fs/src/fs_ops.rs b/vhost_user_fs/src/fs_ops.rs index 9b54da71b..05291c603 100644 --- a/vhost_user_fs/src/fs_ops.rs +++ b/vhost_user_fs/src/fs_ops.rs @@ -284,3 +284,14 @@ pub fn linkat( FUSE_OK } + +pub fn fstat_vfs(file: &File) -> (libc::statvfs, i32) { + let mut stat: libc::statvfs = unsafe { std::mem::zeroed() }; + + errno::set_errno(errno::Errno(0)); + if unsafe { libc::fstatvfs(file.as_raw_fd(), &mut stat) } < 0 { + return (stat, errno::errno().0); + } + + (stat, FUSE_OK) +} diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index 99509b97a..08336518d 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -350,6 +350,8 @@ pub const FUSE_LINK: u32 = 13; pub const FUSE_OPEN: u32 = 14; pub const FUSE_READ: u32 = 15; pub const FUSE_WRITE: u32 = 16; +pub const FUSE_STATFS: u32 = 17; +pub const FUSE_RELEASE: u32 = 18; pub const FATTR_MODE: u32 = 1 << 0; pub const FATTR_UID: u32 = 1 << 1; @@ -600,3 +602,57 @@ pub struct FuseWriteOut { } impl ByteCode for FuseWriteOut {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseKstatfs { + pub blocks: u64, + pub bfree: u64, + pub bavail: u64, + pub files: u64, + pub ffree: u64, + pub bsize: u32, + pub namelen: u32, + pub frsize: u32, + pub padding: u32, + pub spare: [u32; 6], +} + +impl ByteCode for FuseKstatfs {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseStatfsOut { + pub st: FuseKstatfs, +} + +impl ByteCode for FuseStatfsOut {} + +impl FuseStatfsOut { + pub fn from_stat(stat: libc::statvfs) -> Self { + let st = FuseKstatfs { + blocks: stat.f_blocks, + bfree: stat.f_bfree, + bavail: stat.f_bavail, + files: stat.f_files, + ffree: stat.f_ffree, + bsize: stat.f_bsize as u32, + namelen: stat.f_namemax as u32, + frsize: stat.f_frsize as u32, + ..Default::default() + }; + + FuseStatfsOut { st } + } +} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseReleaseIn { + pub fh: u64, + pub flags: u32, + pub release_flags: u32, + pub lock_owner: u64, +} + +impl ByteCode for FuseReleaseIn {} diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs index 25d2e4ee7..463435315 100644 --- a/vhost_user_fs/src/fuse_proc.rs +++ b/vhost_user_fs/src/fuse_proc.rs @@ -660,3 +660,50 @@ pub fn do_fuse_write( reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) } } + +pub fn do_fuse_statfs( + sys_mem: &Arc, + fs: Arc>, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let mut statfs = FuseStatfsOut::default(); + let ret = fs + .lock() + .unwrap() + .statfs(in_header.nodeid as usize, &mut statfs); + if ret == FUSE_OK { + reply_fuse_msg( + writer, + sys_mem, + in_header, + ret, + Some(vec![FuseIovec::from_obj(&statfs)]), + mem::size_of::(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +pub fn do_fuse_release( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let release_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!( + "Failed to read object for release_in, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let ret = fs.lock().unwrap().release(release_in.fh as usize); + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs index 8cc05ee1b..c9ab69b41 100644 --- a/vhost_user_fs/src/fuse_req.rs +++ b/vhost_user_fs/src/fuse_req.rs @@ -100,6 +100,10 @@ impl FuseReq { FUSE_WRITE => { do_fuse_write(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) } + FUSE_STATFS => do_fuse_statfs(sys_mem, fs, &mut self.writer, &in_header), + FUSE_RELEASE => { + do_fuse_release(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } _ => { error!("The fuse msg {} is unsupported", in_header.opcode); reply_fuse_msg( -- Gitee From b1a67962ee8cc476b5828b993f3a50a40a9638dd Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Thu, 4 Feb 2021 15:20:38 +0800 Subject: [PATCH 26/55] vhost_user_fs: implement the fuse message of FUSE_FSYNC and FUSE_SETXATTR for userspace filesystem Signed-off-by: Fei Xu --- vhost_user_fs/src/fs.rs | 92 ++++++++++++++++++++++++++++++++++ vhost_user_fs/src/fs_ops.rs | 64 +++++++++++++++++++++++ vhost_user_fs/src/fuse_msg.rs | 21 ++++++++ vhost_user_fs/src/fuse_proc.rs | 69 +++++++++++++++++++++++++ vhost_user_fs/src/fuse_req.rs | 6 +++ 5 files changed, 252 insertions(+) diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index 80b65c56f..d2fe4204b 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -849,4 +849,96 @@ impl FileSystem { FUSE_OK } + + pub fn fsyncfile(&self, fh: usize, datasync: bool) -> i32 { + let mut ret = FUSE_OK; + + if fh == u64::max_value() as usize { + let inode_fd = match self.find_inode(fh) { + Some(i) => i.as_raw_fd(), + None => { + return libc::EBADF as i32; + } + }; + + let (file_opt, ret_) = open_at( + &self.proc_dir, + CString::new(format!("{}", inode_fd)).unwrap(), + libc::O_RDWR, + 0, + ); + if ret_ != FUSE_OK { + return ret; + } + + if let Some(file) = file_opt { + ret = fsync(&file, datasync); + } else { + return libc::EBADF as i32; + } + } else { + match self.file_map.get_value(fh) { + Some(file) => { + ret = fsync(file, datasync); + } + _ => { + return libc::EBADF as i32; + } + } + } + + ret + } + + pub fn setxattr( + &self, + node_id: usize, + name: CString, + value: CString, + size: u32, + flags: u32, + ) -> i32 { + let inode = match self.find_inode(node_id) { + Some(i) => i.clone(), + None => { + return libc::EBADF as i32; + } + }; + + if inode.file_type & libc::S_IFREG != 0 || inode.file_type & libc::S_IFDIR != 0 { + let (file_opt, ret_) = open_at( + &self.proc_dir, + CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(), + libc::O_RDONLY, + 0, + ); + if ret_ != FUSE_OK { + return ret_; + } + + if let Some(file) = file_opt { + fset_xattr(&file, name, value, size, flags) + } else { + libc::EBADF as i32 + } + } else { + if fchdir(&self.proc_dir) != FUSE_OK { + panic!("setxattr: failed to change process directoy"); + } + + let ret_ = set_xattr( + CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(), + name, + value, + size, + flags, + ); + + if fchdir(&self.root_inode.file) != FUSE_OK { + panic!("setxattr: failed to change directoy of root inode"); + } + + ret_ + } + } } diff --git a/vhost_user_fs/src/fs_ops.rs b/vhost_user_fs/src/fs_ops.rs index 05291c603..f7116f671 100644 --- a/vhost_user_fs/src/fs_ops.rs +++ b/vhost_user_fs/src/fs_ops.rs @@ -295,3 +295,67 @@ pub fn fstat_vfs(file: &File) -> (libc::statvfs, i32) { (stat, FUSE_OK) } + +pub fn fsync(file: &File, datasync: bool) -> i32 { + errno::set_errno(errno::Errno(0)); + + let ret = if datasync { + unsafe { libc::fdatasync(file.as_raw_fd()) } + } else { + unsafe { libc::fsync(file.as_raw_fd()) } + }; + + if ret != FUSE_OK { + return errno::errno().0; + } + + FUSE_OK +} + +pub fn fchdir(file: &File) -> i32 { + errno::set_errno(errno::Errno(0)); + let ret = unsafe { libc::fchdir(file.as_raw_fd()) }; + if ret == -1 { + return errno::errno().0; + } + + FUSE_OK +} + +pub fn set_xattr(path: CString, name: CString, value: CString, size: u32, flags: u32) -> i32 { + errno::set_errno(errno::Errno(0)); + let ret = unsafe { + libc::setxattr( + path.as_ptr(), + name.as_ptr(), + value.as_ptr() as *const libc::c_void, + size as usize, + flags as i32, + ) + }; + + if ret == -1 { + return errno::errno().0; + } + + FUSE_OK +} + +pub fn fset_xattr(file: &File, name: CString, value: CString, size: u32, flags: u32) -> i32 { + errno::set_errno(errno::Errno(0)); + let ret = unsafe { + libc::fsetxattr( + file.as_raw_fd(), + name.as_ptr(), + value.as_ptr() as *const libc::c_void, + size as usize, + flags as i32, + ) + }; + + if ret == -1 { + return errno::errno().0; + } + + FUSE_OK +} diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index 08336518d..f7eeee392 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -352,6 +352,8 @@ pub const FUSE_READ: u32 = 15; pub const FUSE_WRITE: u32 = 16; pub const FUSE_STATFS: u32 = 17; pub const FUSE_RELEASE: u32 = 18; +pub const FUSE_FSYNC: u32 = 20; +pub const FUSE_SETXATTR: u32 = 21; pub const FATTR_MODE: u32 = 1 << 0; pub const FATTR_UID: u32 = 1 << 1; @@ -656,3 +658,22 @@ pub struct FuseReleaseIn { } impl ByteCode for FuseReleaseIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseFsyncIn { + pub fh: u64, + pub fsync_flags: u32, + pub padding: u32, +} + +impl ByteCode for FuseFsyncIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseSetxattrIn { + pub size: u32, + pub flags: u32, +} + +impl ByteCode for FuseSetxattrIn {} diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs index 463435315..1beeb6bf6 100644 --- a/vhost_user_fs/src/fuse_proc.rs +++ b/vhost_user_fs/src/fuse_proc.rs @@ -707,3 +707,72 @@ pub fn do_fuse_release( let ret = fs.lock().unwrap().release(release_in.fh as usize); reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) } + +pub fn do_fuse_fsync( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let fsync_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read name for fsync_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let datasync = fsync_in.fsync_flags & 0x1 == 0x1; + + let ret = fs.lock().unwrap().fsyncfile(fsync_in.fh as usize, datasync); + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} + +pub fn do_fuse_setxattr( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let setxattr_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!( + "Failed to read object for setxattr_in, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let name = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!("Failed to read name for setxattr_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let value = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!( + "Failed to read value for setxattr_in, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let ret = fs.lock().unwrap().setxattr( + in_header.nodeid as usize, + name, + value, + setxattr_in.size, + setxattr_in.flags, + ); + + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs index c9ab69b41..44a5b0c61 100644 --- a/vhost_user_fs/src/fuse_req.rs +++ b/vhost_user_fs/src/fuse_req.rs @@ -104,6 +104,12 @@ impl FuseReq { FUSE_RELEASE => { do_fuse_release(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) } + FUSE_FSYNC => { + do_fuse_fsync(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_SETXATTR => { + do_fuse_setxattr(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } _ => { error!("The fuse msg {} is unsupported", in_header.opcode); reply_fuse_msg( -- Gitee From bcb1ec1fd16fb4fa90b9f8c88ae46c4ddc4c2259 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Thu, 4 Feb 2021 15:32:59 +0800 Subject: [PATCH 27/55] vhost_user_fs: implement the fuse message of FUSE_GETXATTR, FUSE_LISTXATTR and FUSE_REMOVEXATTR for userspace filesystem Signed-off-by: Fei Xu --- vhost_user_fs/src/fs.rs | 157 +++++++++++++++++++++++++++++++++ vhost_user_fs/src/fs_ops.rs | 97 ++++++++++++++++++++ vhost_user_fs/src/fuse_msg.rs | 12 +++ vhost_user_fs/src/fuse_proc.rs | 108 +++++++++++++++++++++++ vhost_user_fs/src/fuse_req.rs | 9 ++ 5 files changed, 383 insertions(+) diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index d2fe4204b..26cd000f2 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -941,4 +941,161 @@ impl FileSystem { ret_ } } + + pub fn getxattr(&self, node_id: usize, name: CString, size: u32, buff: &mut Vec) -> i32 { + let inode = match self.find_inode(node_id) { + Some(i) => i.clone(), + None => { + return libc::EBADF as i32; + } + }; + + if inode.file_type & libc::S_IFREG != 0 || inode.file_type & libc::S_IFDIR != 0 { + let (file_opt, ret) = open_at( + &self.proc_dir, + CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(), + libc::O_RDONLY, + 0, + ); + if ret != FUSE_OK { + return ret; + } + + if let Some(file) = file_opt { + let (buf_opt, ret) = fget_xattr(&file, name, size as usize); + if ret != FUSE_OK { + return ret; + } + if let Some(mut buf) = buf_opt { + buff.append(&mut buf); + } + } else { + return libc::EBADF as i32; + } + } else { + if fchdir(&self.proc_dir) != FUSE_OK { + panic!("getxattr: failed to change process directoy"); + } + + let (buf_opt, ret) = get_xattr( + CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(), + name, + size as usize, + ); + + if fchdir(&self.root_inode.file) != FUSE_OK { + panic!("getxattr: failed to change directoy of root inode"); + } + if ret != FUSE_OK { + return ret; + } + if let Some(mut buf) = buf_opt { + buff.append(&mut buf); + } + } + + FUSE_OK + } + + pub fn listxattr(&self, node_id: usize, size: u32, buff: &mut Vec) -> i32 { + let inode = match self.find_inode(node_id) { + Some(i) => i.clone(), + None => { + return libc::EBADF as i32; + } + }; + + if inode.file_type & libc::S_IFREG != 0 || inode.file_type & libc::S_IFDIR != 0 { + let (file_opt, ret) = open_at( + &self.proc_dir, + CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(), + libc::O_RDONLY, + 0, + ); + if ret != FUSE_OK { + return ret; + } + + if let Some(file) = file_opt { + let (buf_opt, ret) = flist_xattr(&file, size as usize); + if ret != FUSE_OK { + return ret; + } + if let Some(mut buf) = buf_opt { + buff.append(&mut buf); + } + } else { + return libc::EBADF as i32; + } + } else { + if fchdir(&self.proc_dir) != FUSE_OK { + panic!("listxattr: failed to change process directoy"); + } + + let (buf_opt, ret) = list_xattr( + CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(), + size as usize, + ); + + if fchdir(&self.root_inode.file) != FUSE_OK { + panic!("listxattr: failed to change directoy of root inode"); + } + if ret != FUSE_OK { + return ret; + } + if let Some(mut buf) = buf_opt { + buff.append(&mut buf); + } + } + + FUSE_OK + } + + pub fn removexattr(&self, node_id: usize, name: CString) -> i32 { + let inode = match self.find_inode(node_id) { + Some(i) => i.clone(), + None => { + return libc::EBADF as i32; + } + }; + + if inode.file_type & libc::S_IFREG != 0 || inode.file_type & libc::S_IFDIR != 0 { + let (file_opt, ret) = open_at( + &self.proc_dir, + CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(), + libc::O_RDONLY, + 0, + ); + if ret != FUSE_OK { + return ret; + } + + if let Some(file) = file_opt { + let ret = fremove_xattr(&file, name); + if ret != FUSE_OK { + return ret; + } + } else { + return libc::EBADF as i32; + } + } else { + if fchdir(&self.proc_dir) != FUSE_OK { + panic!("removexattr: failed to change process directoy"); + } + + let ret = remove_xattr( + CString::new(format!("{}", &inode.file.as_raw_fd())).unwrap(), + name, + ); + + if fchdir(&self.root_inode.file) != FUSE_OK { + panic!("removexattr: failed to change directoy of root inode"); + } + if ret != FUSE_OK { + return ret; + } + } + + FUSE_OK + } } diff --git a/vhost_user_fs/src/fs_ops.rs b/vhost_user_fs/src/fs_ops.rs index f7116f671..b7677bae8 100644 --- a/vhost_user_fs/src/fs_ops.rs +++ b/vhost_user_fs/src/fs_ops.rs @@ -359,3 +359,100 @@ pub fn fset_xattr(file: &File, name: CString, value: CString, size: u32, flags: FUSE_OK } + +pub fn get_xattr(path: CString, name: CString, size: usize) -> (Option>, i32) { + let mut buf = vec![0; size]; + + errno::set_errno(errno::Errno(0)); + let ret = unsafe { + libc::getxattr( + path.as_ptr(), + name.as_ptr(), + buf.as_mut_ptr() as *mut libc::c_void, + size, + ) + }; + if ret == -1 { + return (None, errno::errno().0); + } + + buf.resize(ret as usize, 0); + + (Some(buf), FUSE_OK) +} + +pub fn fget_xattr(file: &File, name: CString, size: usize) -> (Option>, i32) { + let mut buf = vec![0; size]; + + errno::set_errno(errno::Errno(0)); + let ret = unsafe { + libc::fgetxattr( + file.as_raw_fd(), + name.as_ptr(), + buf.as_mut_ptr() as *mut libc::c_void, + size, + ) + }; + if ret == -1 { + return (None, errno::errno().0); + } + + buf.resize(ret as usize, 0); + + (Some(buf), FUSE_OK) +} + +pub fn list_xattr(path: CString, size: usize) -> (Option>, i32) { + let mut buf = vec![0; size]; + + errno::set_errno(errno::Errno(0)); + let ret = + unsafe { libc::listxattr(path.as_ptr(), buf.as_mut_ptr() as *mut libc::c_char, size) }; + if ret == -1 { + return (None, errno::errno().0); + } + + buf.resize(ret as usize, 0); + + (Some(buf), FUSE_OK) +} + +pub fn flist_xattr(file: &File, size: usize) -> (Option>, i32) { + let mut buf = vec![0; size]; + + errno::set_errno(errno::Errno(0)); + let ret = unsafe { + libc::flistxattr( + file.as_raw_fd(), + buf.as_mut_ptr() as *mut libc::c_char, + size, + ) + }; + if ret == -1 { + return (None, errno::errno().0); + } + + buf.resize(ret as usize, 0); + + (Some(buf), FUSE_OK) +} + +pub fn remove_xattr(path: CString, name: CString) -> i32 { + errno::set_errno(errno::Errno(0)); + let ret = unsafe { libc::removexattr(path.as_ptr(), name.as_ptr()) }; + if ret == -1 { + return errno::errno().0; + } + + FUSE_OK +} + +pub fn fremove_xattr(file: &File, name: CString) -> i32 { + errno::set_errno(errno::Errno(0)); + let ret = unsafe { libc::fremovexattr(file.as_raw_fd(), name.as_ptr()) }; + if ret == -1 { + return errno::errno().0; + } + + FUSE_OK +} diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index f7eeee392..5479bd144 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -354,6 +354,9 @@ pub const FUSE_STATFS: u32 = 17; pub const FUSE_RELEASE: u32 = 18; pub const FUSE_FSYNC: u32 = 20; pub const FUSE_SETXATTR: u32 = 21; +pub const FUSE_GETXATTR: u32 = 22; +pub const FUSE_LISTXATTR: u32 = 23; +pub const FUSE_REMOVEXATTR: u32 = 24; pub const FATTR_MODE: u32 = 1 << 0; pub const FATTR_UID: u32 = 1 << 1; @@ -677,3 +680,12 @@ pub struct FuseSetxattrIn { } impl ByteCode for FuseSetxattrIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseGetxattrIn { + pub size: u32, + pub padding: u32, +} + +impl ByteCode for FuseGetxattrIn {} diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs index 1beeb6bf6..2e0aca9cc 100644 --- a/vhost_user_fs/src/fuse_proc.rs +++ b/vhost_user_fs/src/fuse_proc.rs @@ -776,3 +776,111 @@ pub fn do_fuse_setxattr( reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) } + +pub fn do_fuse_getxattr( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let getxattr_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!( + "Failed to read object for getxattr_in, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let name = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!("Failed to read name for getxattr_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut buff = Vec::new(); + let ret = + fs.lock() + .unwrap() + .getxattr(in_header.nodeid as usize, name, getxattr_in.size, &mut buff); + if ret == FUSE_OK { + reply_fuse_msg( + writer, + sys_mem, + in_header, + FUSE_OK, + Some(vec![FuseIovec::from_slice(buff.as_slice())]), + buff.len(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +pub fn do_fuse_listxattr( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let getxattr_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!( + "Failed to read object for listxattr_in, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut buff = Vec::new(); + let ret = fs + .lock() + .unwrap() + .listxattr(in_header.nodeid as usize, getxattr_in.size, &mut buff); + if ret == FUSE_OK { + reply_fuse_msg( + writer, + sys_mem, + in_header, + FUSE_OK, + Some(vec![FuseIovec::from_slice(buff.as_slice())]), + buff.len(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +pub fn do_fuse_removexattr( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let name = match reader.read_cstring(sys_mem) { + Ok(s) => s, + Err(e) => { + error!( + "Failed to read name for removexattr_in, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let ret = fs + .lock() + .unwrap() + .removexattr(in_header.nodeid as usize, name); + + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs index 44a5b0c61..ab5862ac5 100644 --- a/vhost_user_fs/src/fuse_req.rs +++ b/vhost_user_fs/src/fuse_req.rs @@ -110,6 +110,15 @@ impl FuseReq { FUSE_SETXATTR => { do_fuse_setxattr(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) } + FUSE_GETXATTR => { + do_fuse_getxattr(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_LISTXATTR => { + do_fuse_listxattr(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_REMOVEXATTR => { + do_fuse_removexattr(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } _ => { error!("The fuse msg {} is unsupported", in_header.opcode); reply_fuse_msg( -- Gitee From 20e754ae7430f14ef56829816abe5e5203870773 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Thu, 4 Feb 2021 16:19:36 +0800 Subject: [PATCH 28/55] vhost_user_fs: implement the fuse message of FUSE_INIT for userspace filesystem Signed-off-by: Fei Xu --- vhost_user_fs/src/fs.rs | 45 +++++++++++++++++++++++ vhost_user_fs/src/fs_ops.rs | 4 ++ vhost_user_fs/src/fuse_msg.rs | 67 ++++++++++++++++++++++++++++++++++ vhost_user_fs/src/fuse_proc.rs | 41 +++++++++++++++++++++ vhost_user_fs/src/fuse_req.rs | 1 + 5 files changed, 158 insertions(+) diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index 26cd000f2..7c7d751e5 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -1098,4 +1098,49 @@ impl FileSystem { FUSE_OK } + + pub fn init(&self, flags: u32, support_flags: &mut u32) { + if flags & FUSE_MAX_PAGES != 0 { + *support_flags |= FUSE_MAX_PAGES; + } + if flags & FUSE_CAP_ASYNC_READ != 0 { + *support_flags |= FUSE_ASYNC_READ; + } + if flags & FUSE_CAP_PARALLEL_DIROPS != 0 { + *support_flags |= FUSE_PARALLEL_DIROPS; + } + if flags & FUSE_CAP_POSIX_LOCKS != 0 { + *support_flags |= FUSE_POSIX_LOCKS; + } + if flags & FUSE_CAP_ATOMIC_O_TRUNC != 0 { + *support_flags |= FUSE_ATOMIC_O_TRUNC; + } + if flags & FUSE_CAP_EXPORT_SUPPORT != 0 { + *support_flags |= FUSE_EXPORT_SUPPORT; + } + if flags & FUSE_CAP_DONT_MASK != 0 { + *support_flags |= FUSE_DONT_MASK; + } + if flags & FUSE_CAP_FLOCK_LOCKS != 0 { + *support_flags |= FUSE_FLOCK_LOCKS; + } + if flags & FUSE_CAP_AUTO_INVAL_DATA != 0 { + *support_flags |= FUSE_AUTO_INVAL_DATA; + } + if flags & FUSE_CAP_READDIRPLUS != 0 { + *support_flags |= FUSE_DO_READDIRPLUS; + } + if flags & FUSE_CAP_READDIRPLUS_AUTO != 0 { + *support_flags |= FUSE_READDIRPLUS_AUTO; + } + if flags & FUSE_CAP_ASYNC_DIO != 0 { + *support_flags |= FUSE_ASYNC_DIO; + } + + if flags & FUSE_CAP_POSIX_ACL != 0 { + *support_flags |= FUSE_POSIX_ACL; + } + + umask(0o000); + } } diff --git a/vhost_user_fs/src/fs_ops.rs b/vhost_user_fs/src/fs_ops.rs index b7677bae8..5958ddb4f 100644 --- a/vhost_user_fs/src/fs_ops.rs +++ b/vhost_user_fs/src/fs_ops.rs @@ -456,3 +456,7 @@ pub fn fremove_xattr(file: &File, name: CString) -> i32 { FUSE_OK } + +pub fn umask(umask: u32) { + unsafe { libc::umask(umask) }; +} diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index 5479bd144..19e45ef5a 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -357,6 +357,44 @@ pub const FUSE_SETXATTR: u32 = 21; pub const FUSE_GETXATTR: u32 = 22; pub const FUSE_LISTXATTR: u32 = 23; pub const FUSE_REMOVEXATTR: u32 = 24; +pub const FUSE_INIT: u32 = 26; + +pub const FUSE_KERNEL_VERSION: u32 = 7; +pub const FUSE_KERNEL_MINOR_VERSION: u32 = 32; +pub const FUSE_DEFAULT_MAX_PAGES_PER_REQ: u32 = 32; +pub const FUSE_BUFFER_HEADER_SIZE: u32 = 0x1000; + +pub const FUSE_MIN_READ_BUFFER: u32 = 8192; +pub const FUSE_MAX_PAGES: u32 = 1 << 22; + +pub const FUSE_CAP_ASYNC_READ: u32 = 1 << 0; +pub const FUSE_CAP_POSIX_LOCKS: u32 = 1 << 1; +pub const FUSE_CAP_ATOMIC_O_TRUNC: u32 = 1 << 3; +pub const FUSE_CAP_EXPORT_SUPPORT: u32 = 1 << 4; +pub const FUSE_CAP_DONT_MASK: u32 = 1 << 6; +pub const FUSE_CAP_FLOCK_LOCKS: u32 = 1 << 10; +pub const FUSE_CAP_AUTO_INVAL_DATA: u32 = 1 << 12; +pub const FUSE_CAP_READDIRPLUS: u32 = 1 << 13; +pub const FUSE_CAP_READDIRPLUS_AUTO: u32 = 1 << 14; +pub const FUSE_CAP_ASYNC_DIO: u32 = 1 << 15; +pub const FUSE_CAP_WRITEBACK_CACHE: u32 = 1 << 16; +pub const FUSE_CAP_PARALLEL_DIROPS: u32 = 1 << 18; +pub const FUSE_CAP_POSIX_ACL: u32 = 1 << 19; + +pub const FUSE_ASYNC_READ: u32 = 1 << 0; +pub const FUSE_POSIX_LOCKS: u32 = 1 << 1; +pub const FUSE_ATOMIC_O_TRUNC: u32 = 1 << 3; +pub const FUSE_EXPORT_SUPPORT: u32 = 1 << 4; +pub const FUSE_BIG_WRITES: u32 = 1 << 5; +pub const FUSE_DONT_MASK: u32 = 1 << 6; +pub const FUSE_FLOCK_LOCKS: u32 = 1 << 10; +pub const FUSE_AUTO_INVAL_DATA: u32 = 1 << 12; +pub const FUSE_DO_READDIRPLUS: u32 = 1 << 13; +pub const FUSE_READDIRPLUS_AUTO: u32 = 1 << 14; +pub const FUSE_ASYNC_DIO: u32 = 1 << 15; +pub const FUSE_WRITEBACK_CACHE: u32 = 1 << 16; +pub const FUSE_PARALLEL_DIROPS: u32 = 1 << 18; +pub const FUSE_POSIX_ACL: u32 = 1 << 20; pub const FATTR_MODE: u32 = 1 << 0; pub const FATTR_UID: u32 = 1 << 1; @@ -689,3 +727,32 @@ pub struct FuseGetxattrIn { } impl ByteCode for FuseGetxattrIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseInitIn { + pub major: u32, + pub minor: u32, + pub max_readahead: u32, + pub flags: u32, +} + +impl ByteCode for FuseInitIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseInitOut { + pub major: u32, + pub minor: u32, + pub max_readahead: u32, + pub flags: u32, + pub max_background: u16, + pub congestion_threshold: u16, + pub max_write: u32, + pub time_gran: u32, + pub max_pages: u16, + pub map_alignment: u16, + pub unused: [u32; 8], +} + +impl ByteCode for FuseInitOut {} diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs index 2e0aca9cc..39152468e 100644 --- a/vhost_user_fs/src/fuse_proc.rs +++ b/vhost_user_fs/src/fuse_proc.rs @@ -884,3 +884,44 @@ pub fn do_fuse_removexattr( reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) } + +pub fn do_fuse_init( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let init_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for init_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut support_flags = 0_u32; + fs.lock().unwrap().init(init_in.flags, &mut support_flags); + let init_out = FuseInitOut { + major: FUSE_KERNEL_VERSION, + minor: FUSE_KERNEL_MINOR_VERSION, + max_readahead: 131072, + flags: support_flags, + max_background: 0, + congestion_threshold: 0, + max_write: 1048576, + time_gran: 1, + max_pages: 256, + map_alignment: 0, + ..Default::default() + }; + + reply_fuse_msg( + writer, + sys_mem, + in_header, + FUSE_OK, + Some(vec![FuseIovec::from_obj(&init_out)]), + mem::size_of::(), + ) +} diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs index ab5862ac5..781a15172 100644 --- a/vhost_user_fs/src/fuse_req.rs +++ b/vhost_user_fs/src/fuse_req.rs @@ -119,6 +119,7 @@ impl FuseReq { FUSE_REMOVEXATTR => { do_fuse_removexattr(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) } + FUSE_INIT => do_fuse_init(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header), _ => { error!("The fuse msg {} is unsupported", in_header.opcode); reply_fuse_msg( -- Gitee From cccb5b4c4c54863212c7315e9720f1a9f2a5713e Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Thu, 4 Feb 2021 16:45:37 +0800 Subject: [PATCH 29/55] vhost_user_fs: implement the fuse message of FUSE_OPENDIR and FUSE_READDIR for userspace filesystem Signed-off-by: Fei Xu --- vhost_user_fs/src/fs.rs | 196 +++++++++++++++++++++++++++++++++ vhost_user_fs/src/fs_ops.rs | 33 +++++- vhost_user_fs/src/fuse_msg.rs | 23 ++++ vhost_user_fs/src/fuse_proc.rs | 84 ++++++++++++++ vhost_user_fs/src/fuse_req.rs | 6 + 5 files changed, 341 insertions(+), 1 deletion(-) diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index 7c7d751e5..5d375de07 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -13,8 +13,12 @@ use std::collections::BTreeMap; use std::ffi::CString; use std::fs::File; +use std::mem; use std::os::unix::io::{AsRawFd, RawFd}; +use util::byte_code::ByteCode; +use util::num_ops::round_up; + use super::fs_ops::*; use super::fuse_msg::*; @@ -102,6 +106,14 @@ impl Map { None } } + + fn get_value_mut(&mut self, id: usize) -> Option<&mut T> { + if let Some(e) = self.list.get_mut(id) { + e.value.as_mut().map(|v| v) + } else { + None + } + } } struct Inode { @@ -140,6 +152,40 @@ impl Clone for Inode { } } +fn array_to_cstring( + #[cfg(target_arch = "x86_64")] array: &[i8], + #[cfg(target_arch = "aarch64")] array: &[u8], +) -> Result<(usize, CString)> { + let mut vec = Vec::new(); + for item in array { + if *item == 0 { + break; + } + vec.push(*item as u8); + } + + let len = vec.len(); + if len == 0 { + bail!("convert array to CString failed") + } + + let cstring = match CString::new(vec) { + Ok(c) => c, + Err(_) => bail!("convert array to CString failed"), + }; + + Ok((len, cstring)) +} + +fn path_is_dot(path: &CString) -> bool { + let bytes = path.as_bytes(); + if bytes.len() == 1 && bytes[0] == b'.' { + return true; + } + + false +} + fn path_is_dotdot(path: &CString) -> bool { let bytes = path.as_bytes(); if bytes.len() == 2 && bytes[0] == b'.' && bytes[1] == b'.' { @@ -1143,4 +1189,154 @@ impl FileSystem { umask(0o000); } + + pub fn opendir(&mut self, node_id: usize, dir_fh: &mut u64) -> i32 { + let inode = match self.find_inode(node_id) { + Some(i) => i.clone(), + None => { + return libc::EBADF as i32; + } + }; + + let (file_opt, ret) = open_at(&inode.file, CString::new(".").unwrap(), libc::O_RDONLY, 0); + if ret != FUSE_OK { + return ret; + } + + if let Some(file) = file_opt { + *dir_fh = self.file_map.get_map(file) as u64; + return FUSE_OK; + } + + libc::EBADF as i32 + } + + pub fn readdir( + &mut self, + node_id: usize, + dh: usize, + size: u32, + offset: u64, + plus: bool, + buff: &mut Vec, + ) -> i32 { + let dir_inode = match self.find_inode(node_id) { + Some(i) => i.clone(), + None => { + return libc::EBADF as i32; + } + }; + + let mut dirp = match self.file_map.get_value_mut(dh) { + Some(file) => { + let (dirp_opt, ret) = fdopen_dir(file.as_raw_fd()); + if ret != FUSE_OK { + return libc::EBADF as i32; + } + dirp_opt.unwrap() + } + _ => { + return libc::EBADF as i32; + } + }; + + seek_dir(&mut dirp, offset); + + let mut remain = size; + let mut son_nodeid = 0_u64; + loop { + let (dirent_opt, ret) = read_dir(&mut dirp); + if ret != FUSE_OK { + return ret; + } + let direntp = dirent_opt.unwrap(); + if direntp.is_null() { + break; + } + + let dirent = unsafe { *direntp }; + + let (name_len, son_name) = match array_to_cstring(&dirent.d_name[..]) { + Ok(v) => v, + Err(_) => { + continue; + } + }; + + let only_entry_size = if plus { + mem::size_of::() + } else { + mem::size_of::() + }; + + let (entry_size, gap) = match round_up((only_entry_size + name_len) as u64, 8) { + Some(v) => (v as u32, v as usize - (only_entry_size + name_len)), + _ => { + return libc::EINVAL as i32; + } + }; + if entry_size > remain { + if son_nodeid != 0 { + self.forget(son_nodeid as usize, 1); + } + break; + } + + let mut fuse_dirent = FuseDirent { + ino: dirent.d_ino, + off: dirent.d_off as u64, + namelen: name_len as u32, + type_: dirent.d_type as u32 & (libc::S_IFMT >> 12), + name: [0u8; 0], + }; + + if dir_inode.node_id == self.root_inode.node_id && path_is_dotdot(&son_name) { + fuse_dirent.ino = self.root_inode.key.ino; + fuse_dirent.type_ = libc::DT_DIR as u32 & (libc::S_IFMT >> 12); + } + + if plus { + son_nodeid = 0; + let mut son_attr = FuseAttr::default(); + if !path_is_dot(&son_name) && !path_is_dotdot(&son_name) { + let ret = self.internal_lookup( + &dir_inode, + son_name.clone(), + &mut son_nodeid, + &mut son_attr, + ); + if ret != FUSE_OK { + return ret; + } + } + + buff.extend_from_slice( + FuseDirentplus { + entry_out: FuseEntryOut { + nodeid: son_nodeid as u64, + generation: 0, + entry_valid: 0, + entry_valid_nsec: 0, + attr_valid: 0, + attr_valid_nsec: 0, + attr: son_attr, + }, + dirent: fuse_dirent, + } + .as_bytes(), + ); + } else { + buff.extend_from_slice(fuse_dirent.as_bytes()); + }; + + buff.extend_from_slice(son_name.as_bytes()); + if gap > 0 { + buff.append(&mut vec![0u8; gap]); + } + + remain -= entry_size; + } + + FUSE_OK + } } diff --git a/vhost_user_fs/src/fs_ops.rs b/vhost_user_fs/src/fs_ops.rs index 5958ddb4f..c2e953b5a 100644 --- a/vhost_user_fs/src/fs_ops.rs +++ b/vhost_user_fs/src/fs_ops.rs @@ -11,10 +11,13 @@ // See the Mulan PSL v2 for more details. use std::ffi::CString; use std::fs::File; -use std::os::unix::io::{AsRawFd, FromRawFd}; +use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use super::fuse_msg::*; +pub type DirPtr = *mut libc::DIR; +pub type DirentPtr = *mut libc::dirent; + pub fn fstat_at(file: &File, name: CString, flags: i32) -> (libc::stat, i32) { let mut stat: libc::stat = unsafe { std::mem::zeroed() }; @@ -460,3 +463,31 @@ pub fn fremove_xattr(file: &File, name: CString) -> i32 { pub fn umask(umask: u32) { unsafe { libc::umask(umask) }; } + +pub fn fdopen_dir(fd: RawFd) -> (Option, i32) { + errno::set_errno(errno::Errno(0)); + + let dirp = unsafe { libc::fdopendir(fd) }; + if errno::errno().0 != 0 { + return (None, errno::errno().0); + } + + (Some(dirp), FUSE_OK) +} + +pub fn seek_dir(dirp: &mut DirPtr, offset: u64) { + unsafe { + libc::seekdir(*dirp, offset as i64); + }; +} + +pub fn read_dir(dirp: &mut DirPtr) -> (Option, i32) { + errno::set_errno(errno::Errno(0)); + + let direntp = unsafe { libc::readdir(*dirp) }; + if errno::errno().0 != 0 { + return (None, errno::errno().0); + } + + (Some(direntp), FUSE_OK) +} diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index 19e45ef5a..ce2712e7e 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -358,6 +358,8 @@ pub const FUSE_GETXATTR: u32 = 22; pub const FUSE_LISTXATTR: u32 = 23; pub const FUSE_REMOVEXATTR: u32 = 24; pub const FUSE_INIT: u32 = 26; +pub const FUSE_OPENDIR: u32 = 27; +pub const FUSE_READDIR: u32 = 28; pub const FUSE_KERNEL_VERSION: u32 = 7; pub const FUSE_KERNEL_MINOR_VERSION: u32 = 32; @@ -756,3 +758,24 @@ pub struct FuseInitOut { } impl ByteCode for FuseInitOut {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseDirent { + pub ino: u64, + pub off: u64, + pub namelen: u32, + pub type_: u32, + pub name: [u8; 0], +} + +impl ByteCode for FuseDirent {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseDirentplus { + pub entry_out: FuseEntryOut, + pub dirent: FuseDirent, +} + +impl ByteCode for FuseDirentplus {} diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs index 39152468e..130094f91 100644 --- a/vhost_user_fs/src/fuse_proc.rs +++ b/vhost_user_fs/src/fuse_proc.rs @@ -925,3 +925,87 @@ pub fn do_fuse_init( mem::size_of::(), ) } + +pub fn do_fuse_opendir( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let _open_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!( + "Failed to read object for opendir_in, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut dir_fh = 0_u64; + let ret = fs + .lock() + .unwrap() + .opendir(in_header.nodeid as usize, &mut dir_fh); + if ret == FUSE_OK { + let open_out = FuseOpenOut { + fh: dir_fh, + open_flags: 0, + padding: 0, + }; + + reply_fuse_msg( + writer, + sys_mem, + in_header, + FUSE_OK, + Some(vec![FuseIovec::from_obj(&open_out)]), + mem::size_of::(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +pub fn do_fuse_readdir( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let read_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!( + "Failed to read object for readdir_in, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut buff = Vec::new(); + let ret = fs.lock().unwrap().readdir( + in_header.nodeid as usize, + read_in.fh as usize, + read_in.size, + read_in.offset, + false, + &mut buff, + ); + if ret == FUSE_OK { + reply_fuse_msg( + writer, + sys_mem, + in_header, + FUSE_OK, + Some(vec![FuseIovec::from_slice(buff.as_slice())]), + buff.len(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs index 781a15172..a6ca58207 100644 --- a/vhost_user_fs/src/fuse_req.rs +++ b/vhost_user_fs/src/fuse_req.rs @@ -120,6 +120,12 @@ impl FuseReq { do_fuse_removexattr(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) } FUSE_INIT => do_fuse_init(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header), + FUSE_OPENDIR => { + do_fuse_opendir(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_READDIR => { + do_fuse_readdir(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } _ => { error!("The fuse msg {} is unsupported", in_header.opcode); reply_fuse_msg( -- Gitee From 801e01daed5b5fb727dbab7791d5732d6f2e99ca Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Thu, 4 Feb 2021 16:52:03 +0800 Subject: [PATCH 30/55] vhost_user_fs: implement the fuse message of FUSE_RELEASEDIR and FUSE_FSYNCDIR for userspace filesystem Signed-off-by: Fei Xu --- vhost_user_fs/src/fs.rs | 19 +++++++++++++++ vhost_user_fs/src/fuse_msg.rs | 2 ++ vhost_user_fs/src/fuse_proc.rs | 42 ++++++++++++++++++++++++++++++++++ vhost_user_fs/src/fuse_req.rs | 6 +++++ 4 files changed, 69 insertions(+) diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index 5d375de07..742bed76f 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -1339,4 +1339,23 @@ impl FileSystem { FUSE_OK } + + pub fn releasedir(&mut self, dir_fh: usize) -> i32 { + self.file_map.put_map(dir_fh); + + FUSE_OK + } + + pub fn fsyncdir(&self, dir_fh: usize, datasync: bool) -> i32 { + if let Some(file) = self.file_map.get_value(dir_fh) { + let ret = fsync(file, datasync); + if ret != FUSE_OK { + return ret; + } + } else { + return libc::EBADF as i32; + } + + FUSE_OK + } } diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index ce2712e7e..3c2156dd1 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -360,6 +360,8 @@ pub const FUSE_REMOVEXATTR: u32 = 24; pub const FUSE_INIT: u32 = 26; pub const FUSE_OPENDIR: u32 = 27; pub const FUSE_READDIR: u32 = 28; +pub const FUSE_RELEASEDIR: u32 = 29; +pub const FUSE_FSYNCDIR: u32 = 30; pub const FUSE_KERNEL_VERSION: u32 = 7; pub const FUSE_KERNEL_MINOR_VERSION: u32 = 32; diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs index 130094f91..f282e9f7c 100644 --- a/vhost_user_fs/src/fuse_proc.rs +++ b/vhost_user_fs/src/fuse_proc.rs @@ -1009,3 +1009,45 @@ pub fn do_fuse_readdir( reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) } } + +pub fn do_fuse_releasedir( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let release_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!( + "Failed to read object for releasedir_in, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let ret = fs.lock().unwrap().releasedir(release_in.fh as usize); + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} + +pub fn do_fuse_fsyncdir( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let fsync_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for fsync_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let datasync = fsync_in.fsync_flags & 0x1 == 0x1; + let ret = fs.lock().unwrap().fsyncdir(fsync_in.fh as usize, datasync); + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs index a6ca58207..2f9b88b05 100644 --- a/vhost_user_fs/src/fuse_req.rs +++ b/vhost_user_fs/src/fuse_req.rs @@ -126,6 +126,12 @@ impl FuseReq { FUSE_READDIR => { do_fuse_readdir(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) } + FUSE_RELEASEDIR => { + do_fuse_releasedir(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_FSYNCDIR => { + do_fuse_fsyncdir(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } _ => { error!("The fuse msg {} is unsupported", in_header.opcode); reply_fuse_msg( -- Gitee From cd33597f3044d55dbe73ef1511033bb866ab3312 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Thu, 4 Feb 2021 17:12:43 +0800 Subject: [PATCH 31/55] vhost_user_fs: implement the fuse message of FUSE_FLUSH, FUSE_GETLK FUSE_SETLK and FUSE_SETLKW for userspace filesystem Signed-off-by: Fei Xu --- vhost_user_fs/src/fs.rs | 155 ++++++++++++++++++++++++++++++++- vhost_user_fs/src/fs_ops.rs | 52 +++++++++++ vhost_user_fs/src/fuse_msg.rs | 46 ++++++++++ vhost_user_fs/src/fuse_proc.rs | 140 +++++++++++++++++++++++++++++ vhost_user_fs/src/fuse_req.rs | 12 +++ 5 files changed, 404 insertions(+), 1 deletion(-) diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index 742bed76f..01e80c99c 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -10,7 +10,7 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashMap}; use std::ffi::CString; use std::fs::File; use std::mem; @@ -26,6 +26,10 @@ use crate::errors::Result; const MAP_EXTEND_LENGTH: usize = 256; +const F_RDLCK: u32 = 0; +const F_WDLCK: u32 = 1; +const F_UNLCK: u32 = 2; + #[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq)] struct StatKey { ino: libc::ino64_t, @@ -116,12 +120,33 @@ impl Map { } } +struct FileLock { + lock_owner: u64, + file: File, +} + +impl FileLock { + fn new(file: File, lock_owner: u64) -> Self { + FileLock { lock_owner, file } + } +} + +impl Clone for FileLock { + fn clone(&self) -> Self { + FileLock { + lock_owner: self.lock_owner, + file: self.file.try_clone().unwrap(), + } + } +} + struct Inode { file: File, nlookup: u64, node_id: usize, file_type: u32, key: StatKey, + locks: HashMap, } impl Inode { @@ -132,6 +157,7 @@ impl Inode { node_id, file_type, key, + locks: HashMap::new(), } } @@ -148,6 +174,7 @@ impl Clone for Inode { node_id: self.node_id, file_type: self.file_type, key: self.key, + locks: self.locks.clone(), } } } @@ -270,6 +297,46 @@ impl FileSystem { } } + fn create_file_lock(&mut self, node_id: usize, owner: u64) -> (Option, i32) { + let proc_file = self.proc_dir.try_clone().unwrap(); + let inode = match self.find_mut_inode(node_id) { + Some(inode_) => inode_, + None => return (None, libc::EBADF as i32), + }; + + if let Some(lock) = inode.locks.get_mut(&owner) { + return (Some(lock.file.try_clone().unwrap()), FUSE_OK); + } + + let (file_opt, ret) = open_at( + &proc_file, + CString::new(format!("{}", inode.as_raw_fd())).unwrap(), + libc::O_RDWR, + 0, + ); + + if ret != FUSE_OK { + return (None, ret); + } + + let file = file_opt.unwrap().try_clone().unwrap(); + let file_lock = FileLock::new(file.try_clone().unwrap(), owner); + inode.locks.insert(owner, file_lock); + + (Some(file), FUSE_OK) + } + + fn delete_file_lock(&mut self, node_id: usize, owner: u64) -> i32 { + let inode = match self.find_mut_inode(node_id) { + Some(inode_) => inode_, + None => return libc::EBADF as i32, + }; + + inode.locks.remove(&owner); + + FUSE_OK + } + fn internal_lookup( &mut self, parent_inode: &Inode, @@ -1145,6 +1212,10 @@ impl FileSystem { FUSE_OK } + pub fn flush(&mut self, node_id: usize, owner: u64) -> i32 { + self.delete_file_lock(node_id, owner) + } + pub fn init(&self, flags: u32, support_flags: &mut u32) { if flags & FUSE_MAX_PAGES != 0 { *support_flags |= FUSE_MAX_PAGES; @@ -1358,4 +1429,86 @@ impl FileSystem { FUSE_OK } + + pub fn getlk( + &mut self, + node_id: usize, + owner: u64, + file_lock_in: &FuseFileLock, + file_lock_out: &mut FuseFileLock, + ) -> i32 { + let (file_opt, ret) = self.create_file_lock(node_id, owner); + if ret != FUSE_OK { + return ret; + } + + let ret = fcntl_flock( + &file_opt.unwrap(), + libc::F_GETLK, + file_lock_in, + file_lock_out, + ); + if ret != FUSE_OK { + return ret; + } + + FUSE_OK + } + + pub fn setlk( + &mut self, + node_id: usize, + owner: u64, + is_blocking: bool, + file_lock_in: &FuseFileLock, + ) -> i32 { + if is_blocking { + return libc::EOPNOTSUPP as i32; + } + + let (file_opt, ret) = self.create_file_lock(node_id, owner); + if ret != FUSE_OK { + return ret; + } + + let mut file_lock_out = FuseFileLock::default(); + let ret = fcntl_flock( + &file_opt.unwrap(), + libc::F_SETLK, + file_lock_in, + &mut file_lock_out, + ); + if ret != FUSE_OK { + return ret; + } + + FUSE_OK + } + + pub fn flock(&self, fh: usize, lock_type: u32, is_blocking: bool) -> i32 { + let mut operation: i32 = 0; + + if lock_type == F_RDLCK { + operation = libc::LOCK_SH; + } else if lock_type == F_WDLCK { + operation = libc::LOCK_EX; + } else if lock_type == F_UNLCK { + operation = libc::LOCK_UN; + } + + if !is_blocking { + operation |= libc::LOCK_NB; + } + + if let Some(file) = self.file_map.get_value(fh) { + let ret = flock(file, operation); + if ret != FUSE_OK { + return ret; + } + } else { + return libc::EBADF as i32; + } + + FUSE_OK + } } diff --git a/vhost_user_fs/src/fs_ops.rs b/vhost_user_fs/src/fs_ops.rs index c2e953b5a..137f4c015 100644 --- a/vhost_user_fs/src/fs_ops.rs +++ b/vhost_user_fs/src/fs_ops.rs @@ -491,3 +491,55 @@ pub fn read_dir(dirp: &mut DirPtr) -> (Option, i32) { (Some(direntp), FUSE_OK) } + +pub fn flock(file: &File, operation: i32) -> i32 { + errno::set_errno(errno::Errno(0)); + let ret = unsafe { libc::flock(file.as_raw_fd(), operation) }; + if ret == -1 { + return errno::errno().0; + } + + FUSE_OK +} + +const OFFSET_MAX: u64 = 0x7fffffffffffffff; +pub fn fcntl_flock( + file: &File, + cmd: i32, + file_lock: &FuseFileLock, + file_lock_out: &mut FuseFileLock, +) -> i32 { + let mut flock: libc::flock = unsafe { std::mem::zeroed() }; + + flock.l_type = file_lock.lock_type as i16; + flock.l_whence = libc::SEEK_SET as i16; + flock.l_start = file_lock.start as i64; + flock.l_pid = file_lock.pid as i32; + + if file_lock.end == OFFSET_MAX { + flock.l_len = 0; + } else { + flock.l_len = file_lock.end as i64 - file_lock.start as i64 + 1; + } + + errno::set_errno(errno::Errno(0)); + let ret = unsafe { libc::fcntl(file.as_raw_fd(), cmd, &mut flock) }; + + if ret == -1 { + return errno::errno().0; + } + + file_lock_out.lock_type = flock.l_type as u32; + if flock.l_type != libc::F_ULOCK as i16 { + file_lock_out.start = flock.l_start as u64; + if flock.l_len == 0 { + file_lock_out.end = OFFSET_MAX; + } else { + file_lock_out.end = (flock.l_start + flock.l_len - 1) as u64; + } + } + + file_lock_out.pid = flock.l_pid as u32; + + FUSE_OK +} diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index 3c2156dd1..c893df560 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -357,11 +357,15 @@ pub const FUSE_SETXATTR: u32 = 21; pub const FUSE_GETXATTR: u32 = 22; pub const FUSE_LISTXATTR: u32 = 23; pub const FUSE_REMOVEXATTR: u32 = 24; +pub const FUSE_FLUSH: u32 = 25; pub const FUSE_INIT: u32 = 26; pub const FUSE_OPENDIR: u32 = 27; pub const FUSE_READDIR: u32 = 28; pub const FUSE_RELEASEDIR: u32 = 29; pub const FUSE_FSYNCDIR: u32 = 30; +pub const FUSE_GETLK: u32 = 31; +pub const FUSE_SETLK: u32 = 32; +pub const FUSE_SETLKW: u32 = 33; pub const FUSE_KERNEL_VERSION: u32 = 7; pub const FUSE_KERNEL_MINOR_VERSION: u32 = 32; @@ -781,3 +785,45 @@ pub struct FuseDirentplus { } impl ByteCode for FuseDirentplus {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseFlushIn { + pub fh: u64, + pub unused: u32, + pub padding: u32, + pub lock_owner: u64, +} + +impl ByteCode for FuseFlushIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseFileLock { + pub start: u64, + pub end: u64, + pub lock_type: u32, + pub pid: u32, +} + +impl ByteCode for FuseFileLock {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseLkIn { + pub fh: u64, + pub owner: u64, + pub lk: FuseFileLock, + pub lk_flags: u32, + pub padding: u32, +} + +impl ByteCode for FuseLkIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseLkOut { + pub lk: FuseFileLock, +} + +impl ByteCode for FuseLkOut {} diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs index f282e9f7c..426b939b2 100644 --- a/vhost_user_fs/src/fuse_proc.rs +++ b/vhost_user_fs/src/fuse_proc.rs @@ -885,6 +885,29 @@ pub fn do_fuse_removexattr( reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) } +pub fn do_fuse_flush( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let flush_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for flush_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let ret = fs + .lock() + .unwrap() + .flush(in_header.nodeid as usize, flush_in.lock_owner); + + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} + pub fn do_fuse_init( sys_mem: &Arc, fs: Arc>, @@ -1051,3 +1074,120 @@ pub fn do_fuse_fsyncdir( let ret = fs.lock().unwrap().fsyncdir(fsync_in.fh as usize, datasync); reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) } + +pub fn do_fuse_getlk( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let lk_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for get_lk_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut file_lock = FuseFileLock::default(); + let ret = fs.lock().unwrap().getlk( + in_header.nodeid as usize, + lk_in.owner, + &lk_in.lk, + &mut file_lock, + ); + + if ret == FUSE_OK { + let lk_out = FuseLkOut { lk: file_lock }; + reply_fuse_msg( + writer, + sys_mem, + in_header, + FUSE_OK, + Some(vec![FuseIovec::from_obj(&lk_out)]), + mem::size_of::(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +fn do_fuse_setlk_common( + sys_mem: &Arc, + fs: Arc>, + lk_in: &FuseLkIn, + is_blocking: bool, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let ret = fs.lock().unwrap().setlk( + in_header.nodeid as usize, + lk_in.owner, + is_blocking, + &lk_in.lk, + ); + + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} + +fn do_fuse_flock( + sys_mem: &Arc, + fs: Arc>, + lk_in: &FuseLkIn, + is_blocking: bool, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let ret = fs + .lock() + .unwrap() + .flock(lk_in.fh as usize, lk_in.lk.lock_type, is_blocking); + + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} + +const FUSE_LOCK_FLOCK: u32 = (1 << 0) as u32; +pub fn do_fuse_setlk( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let lk_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for set_lk_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + if lk_in.lk_flags & FUSE_LOCK_FLOCK == FUSE_LOCK_FLOCK { + do_fuse_flock(sys_mem, fs, &lk_in, false, writer, in_header) + } else { + do_fuse_setlk_common(sys_mem, fs, &lk_in, false, writer, in_header) + } +} + +pub fn do_fuse_setlkw( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let lk_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for setlkw_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + if lk_in.lk_flags & FUSE_LOCK_FLOCK == FUSE_LOCK_FLOCK { + do_fuse_flock(sys_mem, fs, &lk_in, true, writer, in_header) + } else { + do_fuse_setlk_common(sys_mem, fs, &lk_in, true, writer, in_header) + } +} diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs index 2f9b88b05..31a5ab0ca 100644 --- a/vhost_user_fs/src/fuse_req.rs +++ b/vhost_user_fs/src/fuse_req.rs @@ -119,6 +119,9 @@ impl FuseReq { FUSE_REMOVEXATTR => { do_fuse_removexattr(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) } + FUSE_FLUSH => { + do_fuse_flush(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } FUSE_INIT => do_fuse_init(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header), FUSE_OPENDIR => { do_fuse_opendir(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) @@ -132,6 +135,15 @@ impl FuseReq { FUSE_FSYNCDIR => { do_fuse_fsyncdir(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) } + FUSE_GETLK => { + do_fuse_getlk(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_SETLK => { + do_fuse_setlk(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_SETLKW => { + do_fuse_setlkw(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } _ => { error!("The fuse msg {} is unsupported", in_header.opcode); reply_fuse_msg( -- Gitee From 66c1dbaa7933e46539c5ed54ccc888668ed319a2 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Thu, 4 Feb 2021 17:24:16 +0800 Subject: [PATCH 32/55] vhost_user_fs: implement the fuse message of FUSE_CREATE, FUSE_DESTROY and FUSE_BATCH_FORGET for userspace filesystem Signed-off-by: Fei Xu --- vhost_user_fs/src/fs.rs | 56 +++++++++++++++ vhost_user_fs/src/fuse_msg.rs | 32 +++++++++ vhost_user_fs/src/fuse_proc.rs | 121 +++++++++++++++++++++++++++++++++ vhost_user_fs/src/fuse_req.rs | 5 ++ 4 files changed, 214 insertions(+) diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index 01e80c99c..ab3893630 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -55,6 +55,11 @@ impl Map { } } + fn destroy_map(&mut self) { + self.list = Vec::new(); + self.free_head = 1; + } + fn extend_map(&mut self) { let mut next = self.list.len(); @@ -1511,4 +1516,55 @@ impl FileSystem { FUSE_OK } + + pub fn create( + &mut self, + in_header: &FuseInHeader, + create_in: &FuseCreateIn, + name: CString, + fh: &mut u64, + node_id: &mut u64, + fuse_attr: &mut FuseAttr, + ) -> i32 { + let parent_dir = match self.find_inode(in_header.nodeid as usize) { + Some(i) => i.clone(), + _ => { + return libc::EBADF as i32; + } + }; + + let mut old_uid = 0_u32; + let mut old_gid = 0_u32; + let ret = change_uid_gid(in_header.uid, in_header.gid, &mut old_uid, &mut old_gid); + if ret != FUSE_OK { + return ret; + } + + let (file_opt, ret) = open_at( + &parent_dir.file, + name.clone(), + (create_in.flags as i32 | libc::O_CREAT) & !libc::O_NOFOLLOW, + create_in.mode & !(create_in.umask & 0o777), + ); + + recover_uid_gid(old_uid, old_gid); + + if ret != FUSE_OK { + return ret; + } + + if let Some(file) = file_opt { + *fh = self.file_map.get_map(file.try_clone().unwrap()) as u64; + } + + self.internal_lookup(&parent_dir, name, node_id, fuse_attr) + } + + pub fn destroy(&mut self) -> i32 { + self.inode_key_map.destroy_map(); + self.file_map.destroy_map(); + self.inodes = BTreeMap::new(); + + FUSE_OK + } } diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index c893df560..ac7faf6ff 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -366,6 +366,9 @@ pub const FUSE_FSYNCDIR: u32 = 30; pub const FUSE_GETLK: u32 = 31; pub const FUSE_SETLK: u32 = 32; pub const FUSE_SETLKW: u32 = 33; +pub const FUSE_CREATE: u32 = 35; +pub const FUSE_DESTROY: u32 = 38; +pub const FUSE_BATCH_FORGET: u32 = 42; pub const FUSE_KERNEL_VERSION: u32 = 7; pub const FUSE_KERNEL_MINOR_VERSION: u32 = 32; @@ -827,3 +830,32 @@ pub struct FuseLkOut { } impl ByteCode for FuseLkOut {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseCreateIn { + pub flags: u32, + pub mode: u32, + pub umask: u32, + pub padding: u32, +} + +impl ByteCode for FuseCreateIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseBatchForgetIn { + pub count: u32, + pub dummy: u32, +} + +impl ByteCode for FuseBatchForgetIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseForgetDataIn { + pub ino: u64, + pub nlookup: u64, +} + +impl ByteCode for FuseForgetDataIn {} diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs index 426b939b2..307bf7536 100644 --- a/vhost_user_fs/src/fuse_proc.rs +++ b/vhost_user_fs/src/fuse_proc.rs @@ -1191,3 +1191,124 @@ pub fn do_fuse_setlkw( do_fuse_setlk_common(sys_mem, fs, &lk_in, true, writer, in_header) } } + +pub fn do_fuse_create( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let create_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for create_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let name = match reader.read_cstring(sys_mem) { + Ok(string) => string, + Err(e) => { + error!( + "Failed to read name for creating file, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + if !is_safe_path(name.clone()) { + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + + let mut fh = 0_u64; + let mut node_id = 0_u64; + let mut fuse_attr = FuseAttr::default(); + let ret = fs.lock().unwrap().create( + in_header, + &create_in, + name, + &mut fh, + &mut node_id, + &mut fuse_attr, + ); + if ret == FUSE_OK { + let entry_out = FuseEntryOut { + nodeid: node_id, + generation: 0, + entry_valid: 0, + entry_valid_nsec: 0, + attr_valid: 0, + attr_valid_nsec: 0, + attr: fuse_attr, + }; + + let open_out = FuseOpenOut { + fh: fh as u64, + open_flags: 0, + padding: 0, + }; + + reply_fuse_msg( + writer, + sys_mem, + in_header, + ret, + Some(vec![ + FuseIovec::from_obj(&entry_out), + FuseIovec::from_obj(&open_out), + ]), + mem::size_of::() + mem::size_of::(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +pub fn do_fuse_destroy( + sys_mem: &Arc, + fs: Arc>, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let ret = fs.lock().unwrap().destroy(); + + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} + +pub fn do_fuse_batch_forget( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, +) -> u32 { + let batch_forget_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!( + "Failed to read object for batch_forget_in, {}", + e.display_chain() + ); + return 0_u32; + } + }; + + for _i in 0..batch_forget_in.count as usize { + let forget_data_in = match reader.read_obj::(sys_mem) { + Ok(data) => data, + Err(e) => { + error!( + "Failed to read object for forget_date_in, {}", + e.display_chain() + ); + return 0; + } + }; + + fs.lock() + .unwrap() + .forget(forget_data_in.ino as usize, forget_data_in.nlookup); + } + + 0_u32 +} diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs index 31a5ab0ca..515d65da8 100644 --- a/vhost_user_fs/src/fuse_req.rs +++ b/vhost_user_fs/src/fuse_req.rs @@ -144,6 +144,11 @@ impl FuseReq { FUSE_SETLKW => { do_fuse_setlkw(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) } + FUSE_CREATE => { + do_fuse_create(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_DESTROY => do_fuse_destroy(sys_mem, fs, &mut self.writer, &in_header), + FUSE_BATCH_FORGET => do_fuse_batch_forget(sys_mem, fs, &mut self.reader), _ => { error!("The fuse msg {} is unsupported", in_header.opcode); reply_fuse_msg( -- Gitee From 6479184c9276db502eac4f94f055d539428bf3d1 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Thu, 4 Feb 2021 17:33:12 +0800 Subject: [PATCH 33/55] vhost_user_fs: implement the fuse message of FUSE_FALLOCATE, FUSE_READDIRPLUS and FUSE_LSEEK for userspace filesystem Signed-off-by: Fei Xu --- vhost_user_fs/src/fs.rs | 27 +++++++++ vhost_user_fs/src/fs_ops.rs | 24 ++++++++ vhost_user_fs/src/fuse_msg.rs | 34 +++++++++++ vhost_user_fs/src/fuse_proc.rs | 108 +++++++++++++++++++++++++++++++++ vhost_user_fs/src/fuse_req.rs | 9 +++ 5 files changed, 202 insertions(+) diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index ab3893630..56cb29e65 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -1567,4 +1567,31 @@ impl FileSystem { FUSE_OK } + + pub fn fallocate(&self, fh: usize, mode: u32, offset: u64, length: u64) -> i32 { + if let Some(file) = self.file_map.get_value(fh) { + let ret = fallocate(file, mode, offset, length); + if ret != FUSE_OK { + return ret; + } + } else { + return libc::EBADF as i32; + } + + FUSE_OK + } + + pub fn lseek(&self, fh: usize, offset: u64, whence: u32, outoffset: &mut u64) -> i32 { + if let Some(file) = self.file_map.get_value(fh) { + let (offset_tmp, ret) = lseek(file, offset, whence); + if ret != FUSE_OK { + return ret; + } + *outoffset = offset_tmp; + } else { + return libc::EBADF as i32; + } + + FUSE_OK + } } diff --git a/vhost_user_fs/src/fs_ops.rs b/vhost_user_fs/src/fs_ops.rs index 137f4c015..5122657b1 100644 --- a/vhost_user_fs/src/fs_ops.rs +++ b/vhost_user_fs/src/fs_ops.rs @@ -543,3 +543,27 @@ pub fn fcntl_flock( FUSE_OK } + +pub fn fallocate(file: &File, mode: u32, offset: u64, length: u64) -> i32 { + errno::set_errno(errno::Errno(0)); + + let ret = + unsafe { libc::fallocate(file.as_raw_fd(), mode as i32, offset as i64, length as i64) }; + + if ret == -1 { + return errno::errno().0 as i32; + } + + FUSE_OK +} + +pub fn lseek(file: &File, offset: u64, whence: u32) -> (u64, i32) { + errno::set_errno(errno::Errno(0)); + let ret = unsafe { libc::lseek(file.as_raw_fd(), offset as i64, whence as i32) }; + + if ret == -1 { + return (0, errno::errno().0 as i32); + } + + (ret as u64, FUSE_OK) +} diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index ac7faf6ff..4f892f20b 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -369,6 +369,9 @@ pub const FUSE_SETLKW: u32 = 33; pub const FUSE_CREATE: u32 = 35; pub const FUSE_DESTROY: u32 = 38; pub const FUSE_BATCH_FORGET: u32 = 42; +pub const FUSE_FALLOCATE: u32 = 43; +pub const FUSE_READDIRPLUS: u32 = 44; +pub const FUSE_LSEEK: u32 = 46; pub const FUSE_KERNEL_VERSION: u32 = 7; pub const FUSE_KERNEL_MINOR_VERSION: u32 = 32; @@ -859,3 +862,34 @@ pub struct FuseForgetDataIn { } impl ByteCode for FuseForgetDataIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseFallocateIn { + pub fh: u64, + pub offset: u64, + pub length: u64, + pub mode: u32, + pub padding: u32, +} + +impl ByteCode for FuseFallocateIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseLseekIn { + pub fh: u64, + pub offset: u64, + pub whence: u32, + pub padding: u32, +} + +impl ByteCode for FuseLseekIn {} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct FuseLseekOut { + pub offset: u64, +} + +impl ByteCode for FuseLseekOut {} diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs index 307bf7536..6e36e0307 100644 --- a/vhost_user_fs/src/fuse_proc.rs +++ b/vhost_user_fs/src/fuse_proc.rs @@ -1312,3 +1312,111 @@ pub fn do_fuse_batch_forget( 0_u32 } + +pub fn do_fuse_fallocate( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let fallocate_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!( + "Failed to read object for fallocate_in, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let ret = fs.lock().unwrap().fallocate( + fallocate_in.fh as usize, + fallocate_in.mode, + fallocate_in.offset, + fallocate_in.length, + ); + + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) +} + +pub fn do_fuse_readdirplus( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let read_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!( + "Failed to read object for readdirplus_in, {}", + e.display_chain() + ); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut buff = Vec::new(); + let ret = fs.lock().unwrap().readdir( + in_header.nodeid as usize, + read_in.fh as usize, + read_in.size, + read_in.offset, + true, + &mut buff, + ); + if ret == FUSE_OK { + reply_fuse_msg( + writer, + sys_mem, + in_header, + FUSE_OK, + Some(vec![FuseIovec::from_slice(buff.as_slice())]), + buff.len(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} + +pub fn do_fuse_lseek( + sys_mem: &Arc, + fs: Arc>, + reader: &mut FuseBuffer, + writer: &mut FuseBuffer, + in_header: &FuseInHeader, +) -> u32 { + let lseek_in = match reader.read_obj::(sys_mem) { + Ok(d) => d, + Err(e) => { + error!("Failed to read object for lseek_in, {}", e.display_chain()); + return reply_fuse_msg(writer, sys_mem, in_header, libc::EINVAL, None, 0_usize); + } + }; + + let mut outoffset = 0_u64; + let ret = fs.lock().unwrap().lseek( + lseek_in.fh as usize, + lseek_in.offset, + lseek_in.whence, + &mut outoffset, + ); + + if ret == FUSE_OK { + let lseekout = FuseLseekOut { offset: outoffset }; + + reply_fuse_msg( + writer, + sys_mem, + in_header, + ret, + Some(vec![FuseIovec::from_obj(&lseekout)]), + mem::size_of::(), + ) + } else { + reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) + } +} diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs index 515d65da8..095807b91 100644 --- a/vhost_user_fs/src/fuse_req.rs +++ b/vhost_user_fs/src/fuse_req.rs @@ -149,6 +149,15 @@ impl FuseReq { } FUSE_DESTROY => do_fuse_destroy(sys_mem, fs, &mut self.writer, &in_header), FUSE_BATCH_FORGET => do_fuse_batch_forget(sys_mem, fs, &mut self.reader), + FUSE_FALLOCATE => { + do_fuse_fallocate(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_READDIRPLUS => { + do_fuse_readdirplus(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } + FUSE_LSEEK => { + do_fuse_lseek(sys_mem, fs, &mut self.reader, &mut self.writer, &in_header) + } _ => { error!("The fuse msg {} is unsupported", in_header.opcode); reply_fuse_msg( -- Gitee From b4024726517555b444f2f75e27c687c3f95dce9b Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Thu, 4 Feb 2021 17:38:50 +0800 Subject: [PATCH 34/55] vhost_user_fs: add the unsupported fuse message in fuse_msg.rs Signed-off-by: Fei Xu --- vhost_user_fs/src/fuse_msg.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index 4f892f20b..53e5b08cb 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -366,12 +366,22 @@ pub const FUSE_FSYNCDIR: u32 = 30; pub const FUSE_GETLK: u32 = 31; pub const FUSE_SETLK: u32 = 32; pub const FUSE_SETLKW: u32 = 33; +pub const FUSE_ACCESS: u32 = 34; pub const FUSE_CREATE: u32 = 35; +pub const FUSE_INTERRUPT: u32 = 36; +pub const FUSE_BMAP: u32 = 37; pub const FUSE_DESTROY: u32 = 38; +pub const FUSE_IOCTL: u32 = 39; +pub const FUSE_POLL: u32 = 40; +pub const FUSE_NOTIFY_REPLY: u32 = 41; pub const FUSE_BATCH_FORGET: u32 = 42; pub const FUSE_FALLOCATE: u32 = 43; pub const FUSE_READDIRPLUS: u32 = 44; +pub const FUSE_RENAME2: u32 = 45; pub const FUSE_LSEEK: u32 = 46; +pub const FUSE_COPY_FILE_RANGE: u32 = 47; +pub const FUSE_SETUPMAPPING: u32 = 48; +pub const FUSE_REMOVEMAPPING: u32 = 49; pub const FUSE_KERNEL_VERSION: u32 = 7; pub const FUSE_KERNEL_MINOR_VERSION: u32 = 32; -- Gitee From 2f1b1fd9268928a89275d0c449b7cacbb5264340 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Tue, 9 Feb 2021 18:14:36 +0800 Subject: [PATCH 35/55] vhost_user_fs: only one vhost_user_fs can connect to StratoVirt, additional vhost_user_fs will fail to connect Signed-off-by: Fei Xu --- .../src/virtio/vhost/user/vhost_user_sock.rs | 18 ++++++++++++++++++ vhost_user_fs/src/vhost_user_fs.rs | 18 ++++++++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/device_model/src/virtio/vhost/user/vhost_user_sock.rs b/device_model/src/virtio/vhost/user/vhost_user_sock.rs index f9b2001bb..22cb617b9 100644 --- a/device_model/src/virtio/vhost/user/vhost_user_sock.rs +++ b/device_model/src/virtio/vhost/user/vhost_user_sock.rs @@ -129,6 +129,24 @@ impl UnixDomainSock { Ok(()) } + #[allow(dead_code)] + pub fn is_server_accept(&self) -> bool { + self.sock.is_some() + } + + #[allow(dead_code)] + pub fn server_connection_refuse(&mut self) -> Result<()> { + //refuse connection by finishing life cycle of stream fd from listener fd + self.listener.as_ref().unwrap().accept().chain_err(|| { + format!( + "Failed to accept the socket for refused connection {}", + self.path + ) + })?; + + Ok(()) + } + pub fn client_connect(&mut self) -> Result<()> { let sock = UnixStream::connect(self.path.as_str()) .chain_err(|| format!("Failed to connect the socket {}", self.path))?; diff --git a/vhost_user_fs/src/vhost_user_fs.rs b/vhost_user_fs/src/vhost_user_fs.rs index 838e1b49c..ebca8e092 100644 --- a/vhost_user_fs/src/vhost_user_fs.rs +++ b/vhost_user_fs/src/vhost_user_fs.rs @@ -45,7 +45,21 @@ impl CreateEventNotifier for VhostUserServerHandler { server_handler: Arc>, ) -> Option> { let mut notifiers = Vec::new(); - self.sock.server_accept().unwrap(); + if self.sock.is_server_accept() { + if let Err(e) = self.sock.server_connection_refuse() { + error!( + "Failed to refuse socket for vhost user server, {}", + e.display_chain() + ); + } + return None; + } else if let Err(e) = self.sock.server_accept() { + error!( + "Failed to accept the socket for vhost user server, {}", + e.display_chain() + ); + return None; + } let mut handlers = Vec::new(); let handler: Box = Box::new(move |event, _| { @@ -71,7 +85,7 @@ impl CreateEventNotifier for VhostUserServerHandler { let notifier = EventNotifier::new( NotifierOperation::AddShared, self.sock.get_stream_raw_fd(), - Some(self.sock.get_listener_raw_fd()), + None, EventSet::IN | EventSet::HANG_UP, handlers, ); -- Gitee From e6bb37196797531bfba3f6767cac1909d19fc0a0 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Thu, 25 Feb 2021 11:36:31 +0800 Subject: [PATCH 36/55] device_model: when the process of vhost_user_fs exits abnormally, the process of stratovirt needs to exit. Add Hang_Up event monitoring of client sock for stratovirt process exit Signed-off-by: Fei Xu --- .../virtio/vhost/user/vhost_user_client.rs | 41 +++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/device_model/src/virtio/vhost/user/vhost_user_client.rs b/device_model/src/virtio/vhost/user/vhost_user_client.rs index 8ef9df670..fbd598fa9 100644 --- a/device_model/src/virtio/vhost/user/vhost_user_client.rs +++ b/device_model/src/virtio/vhost/user/vhost_user_client.rs @@ -11,14 +11,16 @@ // See the Mulan PSL v2 for more details. use std::mem::size_of; -use std::os::unix::io::AsRawFd; +use std::os::unix::io::{AsRawFd, RawFd}; use std::sync::{Arc, Mutex}; -use vmm_sys_util::eventfd::EventFd; +use vmm_sys_util::{epoll::EventSet, eventfd::EventFd}; use address_space::{ AddressSpace, FileBackend, FlatRange, Listener, ListenerReqType, RegionIoEventFd, RegionType, }; +use machine_manager::event_loop::EventLoop; +use util::loop_context::{EventNotifier, EventNotifierHelper, NotifierOperation}; use super::super::super::{ errors::{Result, ResultExt}, @@ -66,6 +68,32 @@ impl ClientInternal { } } +impl EventNotifierHelper for ClientInternal { + fn internal_notifiers(client_handler: Arc>) -> Vec { + let mut notifiers = Vec::new(); + let mut handlers = Vec::new(); + + let handler: Box Option>> = + Box::new(move |event, _| { + if event & EventSet::HANG_UP == EventSet::HANG_UP { + panic!("Receive the event of HANG_UP from vhost_user_fs"); + } else { + None + } + }); + handlers.push(Arc::new(Mutex::new(handler))); + + notifiers.push(EventNotifier::new( + NotifierOperation::AddShared, + client_handler.lock().unwrap().sock.get_stream_raw_fd(), + None, + EventSet::HANG_UP, + handlers, + )); + notifiers + } +} + #[derive(Clone)] struct VhostUserMemoryRegionInfo { region: VhostUserMemoryRegion, @@ -200,8 +228,15 @@ impl VhostUserClient { .register_listener(Box::new(mem_info.clone())) .chain_err(|| "Failed to register listener for memory for vhost user client")?; + let client_internal = Arc::new(Mutex::new(ClientInternal::new(sock, max_queue_num))); + EventLoop::update_event( + EventNotifierHelper::internal_notifiers(client_internal.clone()), + None, + ) + .chain_err(|| "Failed to update event for client sock")?; + let client = VhostUserClient { - client: Arc::new(Mutex::new(ClientInternal::new(sock, max_queue_num))), + client: client_internal, mem_info, }; -- Gitee From 5fc3ae7388f02fe8747f5bff9fcac0f71a39b703 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Fri, 26 Feb 2021 15:19:59 +0800 Subject: [PATCH 37/55] vhost_user_fs: Solve the problem that virtio fs can not be mounted after umount Signed-off-by: Fei Xu --- vhost_user_fs/src/fs.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index 56cb29e65..54f44bfa2 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -1561,10 +1561,18 @@ impl FileSystem { } pub fn destroy(&mut self) -> i32 { + let root_key = self.root_inode.key; + self.inode_key_map.destroy_map(); self.file_map.destroy_map(); self.inodes = BTreeMap::new(); + // Need to add root_inode back to the inode table and inode_key_map table for + // the filesystem function + let root_id = self.inode_key_map.get_map(root_key); + self.root_inode.node_id = root_id; + self.inodes.insert(root_key, self.root_inode.clone()); + FUSE_OK } -- Gitee From a028ece86dc8c1ae85e8991e0faab7b5605f81b6 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Wed, 10 Mar 2021 18:04:28 +0800 Subject: [PATCH 38/55] vhost_user_fs: don't open the file that is not a directory and a regular file Signed-off-by: Fei Xu --- vhost_user_fs/src/fs.rs | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index 54f44bfa2..799d6f019 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -313,6 +313,10 @@ impl FileSystem { return (Some(lock.file.try_clone().unwrap()), FUSE_OK); } + if inode.file_type & libc::S_IFDIR == 0 && inode.file_type & libc::S_IFREG == 0 { + return (None, libc::EBADF as i32); + } + let (file_opt, ret) = open_at( &proc_file, CString::new(format!("{}", inode.as_raw_fd())).unwrap(), @@ -528,6 +532,10 @@ impl FileSystem { } else { match self.find_inode(node_id) { Some(i) => { + if i.file_type & libc::S_IFREG == 0 && i.file_type & libc::S_IFDIR == 0 { + return libc::EBADF as i32; + } + let (file_opt, ret) = open_at( &self.proc_dir, CString::new(format!("{}", &i.file.as_raw_fd())).unwrap(), @@ -894,13 +902,17 @@ impl FileSystem { } pub fn open(&mut self, node_id: usize, flags: u32, fh: &mut u64) -> i32 { - let inode_fd = match self.find_inode(node_id) { - Some(i) => i.as_raw_fd(), + let (inode_fd, file_type) = match self.find_inode(node_id) { + Some(i) => (i.as_raw_fd(), i.file_type), None => { return libc::EBADF as i32; } }; + if file_type & libc::S_IFREG == 0 && file_type & libc::S_IFDIR == 0 { + return libc::EBADF as i32; + } + let (file_opt, ret) = open_at( &self.proc_dir, CString::new(format!("{}", inode_fd)).unwrap(), @@ -972,13 +984,17 @@ impl FileSystem { let mut ret = FUSE_OK; if fh == u64::max_value() as usize { - let inode_fd = match self.find_inode(fh) { - Some(i) => i.as_raw_fd(), + let (inode_fd, file_type) = match self.find_inode(fh) { + Some(i) => (i.as_raw_fd(), i.file_type), None => { return libc::EBADF as i32; } }; + if file_type & libc::S_IFREG == 0 && file_type & libc::S_IFDIR == 0 { + return libc::EBADF as i32; + } + let (file_opt, ret_) = open_at( &self.proc_dir, CString::new(format!("{}", inode_fd)).unwrap(), -- Gitee From ff4ce9affe095645ad0b494459473e3102bf03ce Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Thu, 11 Mar 2021 20:15:03 +0800 Subject: [PATCH 39/55] vhost_user_fs: add the limit of file resources which can be opened in the process by command line Signed-off-by: Fei Xu --- vhost_user_fs/src/cmdline.rs | 15 +++++++++++ vhost_user_fs/src/fs.rs | 42 ++++++++++++++++++++++++++++-- vhost_user_fs/src/fs_ops.rs | 13 +++++++++ vhost_user_fs/src/vhost_user_fs.rs | 6 +++++ 4 files changed, 74 insertions(+), 2 deletions(-) diff --git a/vhost_user_fs/src/cmdline.rs b/vhost_user_fs/src/cmdline.rs index 091dca962..84d1cb169 100644 --- a/vhost_user_fs/src/cmdline.rs +++ b/vhost_user_fs/src/cmdline.rs @@ -43,6 +43,13 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { .takes_value(true) .required(true), ) + .arg( + Arg::with_name("rlimit nofile") + .long("rlimit-nofile") + .value_name("file resource limits for the process") + .help("set file resource limits for the process") + .takes_value(true), + ) .arg( Arg::with_name("display log") .long("D") @@ -57,6 +64,7 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { pub struct FsConfig { pub source_dir: String, pub sock_path: String, + pub rlimit_nofile: Option, } impl FsConfig { @@ -98,6 +106,13 @@ pub fn create_config(args: &ArgMatches) -> Result { fs_config.sock_path = sock_path; } + if let Some(rlimit_nofile) = args.value_of("rlimit nofile") { + let limit = rlimit_nofile + .parse::() + .chain_err(|| "Failed to parse rlimit nofile")?; + fs_config.rlimit_nofile = Some(limit); + } + fs_config .check_config() .chain_err(|| "Precheck failed, Config is unhealthy, stop running")?; diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index 799d6f019..5cf260f52 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -12,7 +12,7 @@ use std::collections::{BTreeMap, HashMap}; use std::ffi::CString; -use std::fs::File; +use std::fs::{read_to_string, File}; use std::mem; use std::os::unix::io::{AsRawFd, RawFd}; @@ -22,7 +22,7 @@ use util::num_ops::round_up; use super::fs_ops::*; use super::fuse_msg::*; -use crate::errors::Result; +use crate::errors::{Result, ResultExt}; const MAP_EXTEND_LENGTH: usize = 256; @@ -227,6 +227,44 @@ fn path_is_dotdot(path: &CString) -> bool { false } +const RLIMIT_NOFILE_MIN: u64 = 20; +pub fn set_rlimit_nofile(limit: u64) -> Result<()> { + if limit < RLIMIT_NOFILE_MIN { + bail!("The limit {} exceeds minimum of files {}", limit, RLIMIT_NOFILE_MIN); + } + + let max_file_str = + read_to_string("/proc/sys/fs/file-max").chain_err(|| "Failed to read file-max")?; + let max_file = max_file_str + .trim() + .parse::() + .chain_err(|| "Failed to convert the string of max files")?; + if limit > max_file { + bail!("The limit {} exceeds maximum of files {}", limit, max_file); + } + + let nr_open_str = + read_to_string("/proc/sys/fs/nr_open").chain_err(|| "Failed to read nr_open")?; + let max_file = nr_open_str + .trim() + .parse::() + .chain_err(|| "Failed to convert the string of nr_open")?; + if limit > max_file { + bail!( + "The limit {} exceeds maximum of nr_open {}", + limit, + max_file + ); + } + + let ret = set_rlimit(limit, limit); + if ret != FUSE_OK { + bail!("Failed to set rlimit, err: {}", ret); + } + + Ok(()) +} + #[allow(dead_code)] pub struct FileSystem { root_inode: Inode, diff --git a/vhost_user_fs/src/fs_ops.rs b/vhost_user_fs/src/fs_ops.rs index 5122657b1..09bd2c3d5 100644 --- a/vhost_user_fs/src/fs_ops.rs +++ b/vhost_user_fs/src/fs_ops.rs @@ -567,3 +567,16 @@ pub fn lseek(file: &File, offset: u64, whence: u32) -> (u64, i32) { (ret as u64, FUSE_OK) } + +pub fn set_rlimit(rlim_cur: u64, rlim_max: u64) -> i32 { + let limit = libc::rlimit { rlim_cur, rlim_max }; + + errno::set_errno(errno::Errno(0)); + let ret = unsafe { libc::setrlimit(libc::RLIMIT_NOFILE, &limit) }; + + if ret == -1 { + return errno::errno().0 as i32; + } + + FUSE_OK +} diff --git a/vhost_user_fs/src/vhost_user_fs.rs b/vhost_user_fs/src/vhost_user_fs.rs index ebca8e092..4bdfb193f 100644 --- a/vhost_user_fs/src/vhost_user_fs.rs +++ b/vhost_user_fs/src/vhost_user_fs.rs @@ -22,6 +22,7 @@ use util::loop_context::{ }; use super::cmdline::FsConfig; +use super::fs::set_rlimit_nofile; use super::vhost_user_server::VhostUserServerHandler; use super::virtio_fs::VirtioFs; @@ -127,6 +128,11 @@ impl EventNotifierHelper for VhostUserServerHandler { impl VhostUserFs { pub fn new(fs_config: FsConfig) -> Result { + if let Some(limit) = fs_config.rlimit_nofile { + set_rlimit_nofile(limit) + .chain_err(|| format!("Failed to set rlimit nofile {}", limit))?; + } + let virtio_fs = Arc::new(Mutex::new( VirtioFs::new(&fs_config.source_dir) .chain_err(|| format!("Failed to create virtio fs {}", fs_config.source_dir))?, -- Gitee From 6f9eb175b17acdab7e9ffb90dc20bb96158b018e Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Fri, 12 Mar 2021 16:34:01 +0800 Subject: [PATCH 40/55] vhost_user_fs: add code comments and reconstruct part of code 1. Add code comments for cmdline.rs, fs.rs, fuse_req.rs, vhost_user_fs.rs, vhost_user_server.rs and virtio_fs.rs 2. Rename create_config to create_fs_config 3. Delete the useless public fields and dead_code attributes Signed-off-by: Fei Xu --- src/vhost_user_fs.rs | 4 +- vhost_user_fs/src/cmdline.rs | 14 +++-- vhost_user_fs/src/fs.rs | 6 ++- vhost_user_fs/src/fuse_req.rs | 13 ++++- vhost_user_fs/src/vhost_user_fs.rs | 12 ++++- vhost_user_fs/src/vhost_user_server.rs | 74 ++++++++++++++++++++++---- vhost_user_fs/src/virtio_fs.rs | 16 ++++-- 7 files changed, 117 insertions(+), 22 deletions(-) diff --git a/src/vhost_user_fs.rs b/src/vhost_user_fs.rs index fe2d205ba..0a85e88f9 100644 --- a/src/vhost_user_fs.rs +++ b/src/vhost_user_fs.rs @@ -21,7 +21,7 @@ use std::sync::Arc; use machine_manager::event_loop::EventLoop; use util::{arg_parser, logger}; -use vhost_user_fs::cmdline::{create_args_parser, create_config, FsConfig}; +use vhost_user_fs::cmdline::{create_args_parser, create_fs_config, FsConfig}; use vhost_user_fs::vhost_user_fs::VhostUserFs; error_chain! { @@ -56,7 +56,7 @@ fn run() -> Result<()> { } fn real_main(cmd_args: &arg_parser::ArgMatches) -> Result<()> { - let fsconfig: FsConfig = create_config(cmd_args)?; + let fsconfig: FsConfig = create_fs_config(cmd_args)?; info!("FsConfig is {:?}", fsconfig); EventLoop::object_init(&None)?; diff --git a/vhost_user_fs/src/cmdline.rs b/vhost_user_fs/src/cmdline.rs index 84d1cb169..cf4998b23 100644 --- a/vhost_user_fs/src/cmdline.rs +++ b/vhost_user_fs/src/cmdline.rs @@ -21,7 +21,7 @@ const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); const MAX_STRING_LENGTH: usize = 255; -/// This function is to define all commandline arguments. +/// This function is to define all command line arguments. pub fn create_args_parser<'a>() -> ArgParser<'a> { ArgParser::new("VhostUserFs") .version(VERSION.unwrap_or("unknown")) @@ -60,15 +60,19 @@ pub fn create_args_parser<'a>() -> ArgParser<'a> { ) } +/// Filesystem configuration parsed from command line for the process #[derive(Clone, Default, Debug)] pub struct FsConfig { + /// Source directory in host which can be accessed by guest pub source_dir: String, + /// The path of socket file which communicates with StratoVirt pub sock_path: String, + /// The limit of file resources which can be opened for the process pub rlimit_nofile: Option, } impl FsConfig { - pub fn check_config(&self) -> Result<()> { + fn check_config(&self) -> Result<()> { if self.source_dir.len() > MAX_STRING_LENGTH { bail!( "The length of source directory is too long {}", @@ -95,7 +99,11 @@ impl FsConfig { } } -pub fn create_config(args: &ArgMatches) -> Result { +/// Construct a filesystem configuration parsed from command line. +/// +/// # Arguments +/// * `args` - The collection of information about the arguments from command line. +pub fn create_fs_config(args: &ArgMatches) -> Result { let mut fs_config = FsConfig::default(); if let Some(source_dir) = args.value_of("source dir") { diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index 5cf260f52..d38b20723 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -230,7 +230,11 @@ fn path_is_dotdot(path: &CString) -> bool { const RLIMIT_NOFILE_MIN: u64 = 20; pub fn set_rlimit_nofile(limit: u64) -> Result<()> { if limit < RLIMIT_NOFILE_MIN { - bail!("The limit {} exceeds minimum of files {}", limit, RLIMIT_NOFILE_MIN); + bail!( + "The limit {} exceeds minimum of files {}", + limit, + RLIMIT_NOFILE_MIN + ); } let max_file_str = diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs index 095807b91..021a80adb 100644 --- a/vhost_user_fs/src/fuse_req.rs +++ b/vhost_user_fs/src/fuse_req.rs @@ -20,7 +20,7 @@ use super::fs::FileSystem; use super::fuse_msg::*; use super::fuse_proc::*; -#[allow(dead_code)] +/// The request of fuse message parsed from virtio queue. pub struct FuseReq { desc_index: u16, reader: FuseBuffer, @@ -28,6 +28,11 @@ pub struct FuseReq { } impl FuseReq { + /// Construct a request of fuse message by the element from virtio queue. + /// + /// # Arguments + /// + /// * `elem` - The element parsed from virtio queue. pub fn new(elem: &Element) -> Self { let desc_index = elem.index; @@ -50,6 +55,12 @@ impl FuseReq { } } + /// The function to deal with fuse message from the request. + /// + /// # Arguments + /// + /// * `sys_mem` - Address space mapped with StratoVirt. + /// * `fs` - The management of userspace filesystem. pub fn execute( &mut self, sys_mem: &Arc, diff --git a/vhost_user_fs/src/vhost_user_fs.rs b/vhost_user_fs/src/vhost_user_fs.rs index 4bdfb193f..9e6d7b8c2 100644 --- a/vhost_user_fs/src/vhost_user_fs.rs +++ b/vhost_user_fs/src/vhost_user_fs.rs @@ -28,12 +28,14 @@ use super::virtio_fs::VirtioFs; use crate::errors::{Result, ResultExt}; +/// The vhost-user filesystem device contains virtio fs device and the vhost-user +/// server which can be connected with the vhost-user client in StratoVirt. #[derive(Clone)] pub struct VhostUserFs { - pub server_handler: VhostUserServerHandler, + server_handler: VhostUserServerHandler, } -pub trait CreateEventNotifier { +trait CreateEventNotifier { fn create_event_notifier( &mut self, server_handler: Arc>, @@ -127,6 +129,11 @@ impl EventNotifierHelper for VhostUserServerHandler { } impl VhostUserFs { + /// Create a new vhost-user filesystem device. + /// + /// # Arguments + /// + /// * `fs_config` - Configuration of the vhost-user filesystem device. pub fn new(fs_config: FsConfig) -> Result { if let Some(limit) = fs_config.rlimit_nofile { set_rlimit_nofile(limit) @@ -143,6 +150,7 @@ impl VhostUserFs { Ok(VhostUserFs { server_handler }) } + /// Add events to epoll handler for the vhost-user filesystem device. pub fn add_event_notifier(&self) -> Result<()> { EventLoop::update_event( EventNotifierHelper::internal_notifiers(Arc::new(Mutex::new( diff --git a/vhost_user_fs/src/vhost_user_server.rs b/vhost_user_fs/src/vhost_user_server.rs index 7280bdaaa..22936205a 100644 --- a/vhost_user_fs/src/vhost_user_server.rs +++ b/vhost_user_fs/src/vhost_user_server.rs @@ -21,7 +21,7 @@ use util::unix::limit_permission; use crate::errors::{Result, ResultExt}; -///the trait for dealing with vhost-user request in the server +///the trait for dealing with vhost-user request in the server. pub trait VhostUserReqHandler: Send + Sync { /// Set the current process as the owner of this file descriptor. fn set_owner(&mut self) -> Result<()>; @@ -30,16 +30,40 @@ pub trait VhostUserReqHandler: Send + Sync { fn get_features(&self) -> Result; /// Inform the vhost subsystem which features to enable. This should be a subset of - /// supported features from VHOST_GET_FEATURES.. + /// supported features from VHOST_GET_FEATURES. + /// + /// # Arguments + /// + /// * `features` - The features from the vhost-user client in StratoVirt. fn set_features(&mut self, features: u64) -> Result<()>; /// Set the guest memory mappings for vhost to use. + /// + /// # Arguments + /// + /// * `regions` - The slice of memory region information for the message of memory table. + /// * `fds` - The files descriptors are used to map shared memory for the process and + /// StratoVirt. fn set_mem_table(&mut self, regions: &[VhostUserMemoryRegion], fds: &[RawFd]) -> Result<()>; - /// Set the number of descriptors in the vring. + /// Set the size of descriptors in the virtio queue. + /// + /// # Arguments + /// + /// * `queue_index` - The index of virtio queue. + /// * `num` - The total size of virtio queue. fn set_vring_num(&mut self, queue_index: usize, num: u16) -> Result<()>; - /// Set the addresses for a given vring. + /// Set the addresses for a given virtio queue. + /// + /// # Arguments + /// + /// * `queue_index` - The index of virtio queue. + /// * `flags` - Option flags. + /// * `desc_table` - The start address of descriptor table. + /// * `used_ring` - The start address of used ring. + /// * `avail_ring` - The start address of avail ring. + /// * `log` - The start address of log. fn set_vring_addr( &mut self, queue_index: usize, @@ -51,23 +75,48 @@ pub trait VhostUserReqHandler: Send + Sync { ) -> Result<()>; /// Set the first index to look for available descriptors. + /// + /// # Arguments + /// + /// * `queue_index` - The index of virtio queue. + /// * `num` - the first index to look for available descriptors. fn set_vring_base(&mut self, queue_index: usize, num: u16) -> Result<()>; - /// Set the eventfd to trigger when buffers have been used by the host. + /// Set the eventfd to trigger when buffers need to be processed + /// by the guest. + /// + /// # Arguments + /// + /// * `queue_index` - The index of virtio queue. + /// * `fd` - The files descriptor used to notify the guest. fn set_vring_call(&mut self, queue_index: usize, fd: RawFd) -> Result<()>; - /// Set the eventfd that will be signaled by the guest when buffers are - /// available for the host to process. + /// Set the eventfd that will be signaled by the guest when buffers + /// need to be processed by the host. + /// + /// # Arguments + /// + /// * `queue_index` - The index of virtio queue. + /// * `fd` - The files descriptor used to notify the host. fn set_vring_kick(&mut self, queue_index: usize, fd: RawFd) -> Result<()>; - ///set the status of vring + /// set the status of virtio queue + /// + /// # Arguments + /// + /// * `queue_index` - The index of virtio queue. + /// * `status` - The status of virtio queue. fn set_vring_enable(&mut self, queue_index: usize, status: u32) -> Result<()>; } +/// The vhost-user server handler can communicate with StratoVirt and set the data of requests +/// to the backend #[derive(Clone)] pub struct VhostUserServerHandler { + /// The information of socket used to communicate with StratoVirt pub sock: UnixDomainSock, - pub backend: Arc>, + // The backend used to save the data of requests from StratoVirt + backend: Arc>, } fn close_fds(fds: Vec) { @@ -96,6 +145,12 @@ fn is_invalid_fds(hdr: &mut VhostUserMsgHeader, rfds: Option>) -> Res } impl VhostUserServerHandler { + /// Construct a vhost-user server handler + /// + /// # Arguments + /// + /// * `path` - The path of unix socket file which communicates with StratoVirt. + /// * `backend` - The trait of backend used to save the data of requests from StratoVirt. pub fn new(path: &str, backend: Arc>) -> Result { let mut server = UnixDomainSock::new(path); server @@ -393,6 +448,7 @@ impl VhostUserServerHandler { Ok(()) } + /// The function used to process requests from StratoVirt pub fn handle_request(&mut self) -> Result<()> { let (hdr, rfds) = self .recv_hdr_and_fds() diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index 3afdeee45..6b85b825f 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -38,7 +38,7 @@ const VIRIOT_FS_HIGH_PRIO_QUEUE_NUM: u64 = 1; const VIRTIO_FS_REQ_QUEUES_NUM: u64 = 1; const VIRTIO_FS_MAX_QUEUE_SIZE: u16 = 1024; -pub struct FsIoHandler { +struct FsIoHandler { queue: Queue, kick_evt: EventFd, call_evt: EventFd, @@ -48,7 +48,7 @@ pub struct FsIoHandler { } impl FsIoHandler { - pub fn new( + fn new( queue_config: QueueConfig, kick_evt: &EventFd, call_evt: &EventFd, @@ -96,7 +96,7 @@ impl FsIoHandler { Ok(()) } - pub fn delete_notifiers(&self) -> Vec { + fn delete_notifiers(&self) -> Vec { let mut notifiers = Vec::new(); notifiers.push(EventNotifier::new( NotifierOperation::Delete, @@ -144,7 +144,7 @@ struct QueueInfo { } impl QueueInfo { - pub fn new(queue_size: u16) -> Self { + fn new(queue_size: u16) -> Self { QueueInfo { config: QueueConfig::new(queue_size), kick_evt: None, @@ -186,6 +186,9 @@ impl VirtioFsConfig { } } +/// The virtio fs device contains the configuration of virtio fs, the management of +/// userspace filesystem and the handler used to process requests in virtio queue +/// from the guest. pub struct VirtioFs { config: VirtioFsConfig, fs_handlers: Vec>>>, @@ -194,6 +197,11 @@ pub struct VirtioFs { } impl VirtioFs { + /// Construct a virtio fs device by the path of source directory shared in host. + /// + /// # Arguments + /// + /// * `source_dir` - The path of source directory shared in host. pub fn new(source_dir: &str) -> Result { let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value())) .chain_err(|| "Failed to create address space")?; -- Gitee From 35ede3dd0db5837039eaa100f98ee654f62cbfec Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Wed, 17 Mar 2021 15:56:13 +0800 Subject: [PATCH 41/55] vhost_user_fs: add code comments and reconstruct part of code for fuse_msg.rs and fuse_proc.rs Signed-off-by: Fei Xu --- vhost_user_fs/src/fuse_msg.rs | 130 +++++++++++-- vhost_user_fs/src/fuse_proc.rs | 327 +++++++++++++++++++++++++++++++++ 2 files changed, 438 insertions(+), 19 deletions(-) diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index 53e5b08cb..2bb523f3e 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -24,11 +24,17 @@ use util::byte_code::ByteCode; use crate::errors::{Result, ResultExt}; +/// Successfully process the fuse message pub const FUSE_OK: i32 = 0; +/// Get the buffers from the element of virtio queue to parse fuse message or +/// reply fuse message. pub struct FuseBuffer { + /// Get the buffers to read or write from the element of virtio queue. pub bufs: VecDeque, + /// The total size of the buffers from the element of virtio queue. pub btyes_total: usize, + /// The processed bytes to read or write fuse message. pub bytes_processed: usize, } @@ -41,6 +47,11 @@ impl FuseBuffer { } } + /// Read the CString ending with '\0' from the fuse buffers. + /// + /// # Arguments + /// + /// * `sys_mem` - Address space mapped with StratoVirt. pub fn read_cstring(&mut self, sys_mem: &Arc) -> Result { let bytes_remain = self.btyes_total - self.bytes_processed; let mut buffer = vec![0; bytes_remain]; @@ -63,6 +74,7 @@ impl FuseBuffer { let str_slice = buffer.as_slice(); let cstring = unsafe { CString::from_vec_unchecked(str_slice[0..pos].to_vec()) }; + // remove the processed bytes in self.bufs let mut need_read_count = pos; let bufs = self.bufs.clone(); for buf in bufs { @@ -86,12 +98,20 @@ impl FuseBuffer { Ok(cstring) } - pub fn read_slice( + fn read_slice( &mut self, sys_mem: &Arc, dst: &mut [u8], count: usize, ) -> Result<()> { + if dst.len() != count { + bail!( + "The length {} of dst slice is not equal to the count {}", + dst.len(), + count + ); + } + let read_end = match self.bytes_processed.checked_add(count) { Some(end_) => end_, None => bail!("The read count {} {} overflow", count, self.bytes_processed), @@ -110,12 +130,14 @@ impl FuseBuffer { for buf in bufs { let mut slice = &mut dst[offset..]; let read_count = cmp::min(slice.len(), buf.len as usize); + sys_mem .read(&mut slice, buf.addr, read_count as u64) .chain_err(|| "Failed to read buffer for fuse req")?; self.bytes_processed += read_count; offset += read_count; + // remove the processed bytes in self.bufs if let Some(buftmp) = self.bufs.pop_front() { if read_count < buftmp.len as usize { //Add the remain length to the head of self.bufs @@ -131,18 +153,21 @@ impl FuseBuffer { Ok(()) } + /// read an object from the fuse buffers. + /// + /// # Arguments + /// + /// * `sys_mem` - Address space mapped with StratoVirt. + /// + /// # Note + /// To use this method, it is necessary to implement `ByteCode` trait for your object. pub fn read_obj(&mut self, sys_mem: &Arc) -> Result { let mut obj = T::default(); self.read_slice(sys_mem, &mut obj.as_mut_bytes(), size_of::())?; Ok(obj) } - pub fn write_slice( - &mut self, - sys_mem: &Arc, - src: &[u8], - count: usize, - ) -> Result<()> { + fn write_slice(&mut self, sys_mem: &Arc, src: &[u8], count: usize) -> Result<()> { let write_end = match self.bytes_processed.checked_add(count) { Some(end_) => end_, None => bail!("The read count {} {} overflow", count, self.bytes_processed), @@ -168,6 +193,7 @@ impl FuseBuffer { self.bytes_processed += write_count; offset += write_count; + // remove the processed bytes in self.bufs if let Some(buftmp) = self.bufs.pop_front() { if write_count < buftmp.len as usize { //Add the remain length to the head of self.bufs @@ -183,19 +209,38 @@ impl FuseBuffer { Ok(()) } + /// write an object to the fuse buffers. + /// + /// # Arguments + /// + /// * `sys_mem` - Address space mapped with StratoVirt. + /// * `data` - The object the will be written to the fuse buffers. + /// + /// # Note + /// To use this method, it is necessary to implement `ByteCode` trait for your object. pub fn write_obj(&mut self, sys_mem: &Arc, data: &T) -> Result<()> { self.write_slice(sys_mem, data.as_bytes(), size_of::()) } - // if read is true, read data from host file, then write them to virtio queue. - // if read is false, get data from virtio queue, then write to host file. + /// Process the data for host file. if is_read is true, writing the data which is read from host + /// file to the fuse buffers. if is_read is false, writing the data which is read from the fuse + /// buffers to host file. + /// + /// # Arguments + /// + /// * `sys_mem` - Address space mapped with StratoVirt. + /// * `fd` - The file descriptor in host. + /// * `offset` - The offset which needs to be read and written in host file. + /// * `size` - The size which needs to be read and written in host file. + /// * `is_read` - If it is true, writing the data which is read from host file to the fuse buffers. + /// If it is false, writing the data which is read from the fuse buffers to host file. pub fn access_file( &mut self, sys_mem: &Arc, fd: i32, offset: u64, size: u32, - read: bool, + is_read: bool, ) -> Result { let mut remain_len = size; let mut file_off = offset; @@ -232,7 +277,7 @@ impl FuseBuffer { }]; let ret = unsafe { - if read { + if is_read { libc::preadv(fd, iov.as_ptr(), iov.len() as i32, file_off as i64) } else { libc::pwritev(fd, iov.as_ptr(), iov.len() as i32, file_off as i64) @@ -270,12 +315,21 @@ impl Default for FuseBuffer { } } +/// Save the address and the length for replying fuse message. pub struct FuseIovec<'a> { body: &'a [u8], len: usize, } impl<'a> FuseIovec<'a> { + /// Convert an object to the struct of FuseIovec for replying fuse message. + /// + /// # Arguments + /// + /// * `obj` - The object the will be converted to the struct of FuseIovec. + /// + /// # Note + /// To use this method, it is necessary to implement `ByteCode` trait for your object. pub fn from_obj(obj: &'a T) -> Self { let body = obj.as_bytes(); FuseIovec { @@ -284,6 +338,11 @@ impl<'a> FuseIovec<'a> { } } + /// Convert a slice to the struct of FuseIovec for replying fuse message. + /// + /// # Arguments + /// + /// * `obj` - The slice the will be converted to the struct of FuseIovec. pub fn from_slice(body: &'a [u8]) -> Self { FuseIovec { body, @@ -292,6 +351,19 @@ impl<'a> FuseIovec<'a> { } } +/// Reply the fuse messages by writing the data to the writable fuse buffers. +/// +/// # Arguments +/// +/// * `writer` - The writable fuse buffers. +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `in_header` - The in_header reading from the read-only fuse buffers. +/// * `err` - The error number for processing the fuse message. If it is ok, set +/// error number to 0. If it is false, set error number from linux. +/// * `body_opt` - The body for replying the fuse message needs to be written +/// to fuse buffers. +/// * `body_len` - The length fo body for replying the fuse message. if the body +/// is none, set the length to 0. pub fn reply_fuse_msg( writer: &mut FuseBuffer, sys_mem: &Arc, @@ -383,42 +455,62 @@ pub const FUSE_COPY_FILE_RANGE: u32 = 47; pub const FUSE_SETUPMAPPING: u32 = 48; pub const FUSE_REMOVEMAPPING: u32 = 49; +/// The kernel version which is supported by fuse messages. pub const FUSE_KERNEL_VERSION: u32 = 7; +/// The minor version which is supported by fuse messages. pub const FUSE_KERNEL_MINOR_VERSION: u32 = 32; -pub const FUSE_DEFAULT_MAX_PAGES_PER_REQ: u32 = 32; -pub const FUSE_BUFFER_HEADER_SIZE: u32 = 0x1000; - -pub const FUSE_MIN_READ_BUFFER: u32 = 8192; -pub const FUSE_MAX_PAGES: u32 = 1 << 22; +/// The capability bit supports asynchronous read requests. pub const FUSE_CAP_ASYNC_READ: u32 = 1 << 0; +/// The capability bit supports posix file locks. pub const FUSE_CAP_POSIX_LOCKS: u32 = 1 << 1; +/// The capability bit supports the O_TRUNC open flag. pub const FUSE_CAP_ATOMIC_O_TRUNC: u32 = 1 << 3; +/// The capability bit supports lookups of "." and "..". pub const FUSE_CAP_EXPORT_SUPPORT: u32 = 1 << 4; +/// The capability bit don't apply umask to file mode on create operation. pub const FUSE_CAP_DONT_MASK: u32 = 1 << 6; +/// The capability bit supports BSD file locks. pub const FUSE_CAP_FLOCK_LOCKS: u32 = 1 << 10; +/// The capability bit automatically checks invalid cached file. pub const FUSE_CAP_AUTO_INVAL_DATA: u32 = 1 << 12; +/// The capability bit supports readdirplus. pub const FUSE_CAP_READDIRPLUS: u32 = 1 << 13; +/// The capability bit supports adaptive readdirplus. pub const FUSE_CAP_READDIRPLUS_AUTO: u32 = 1 << 14; +/// The capability bit supports asynchronous direct I/O submission. pub const FUSE_CAP_ASYNC_DIO: u32 = 1 << 15; -pub const FUSE_CAP_WRITEBACK_CACHE: u32 = 1 << 16; +/// The capability bit supports for parallel directory operations. pub const FUSE_CAP_PARALLEL_DIROPS: u32 = 1 << 18; +/// The capability bit supports POSIX ACLs. pub const FUSE_CAP_POSIX_ACL: u32 = 1 << 19; +/// The supported bit that supports asynchronous read requests. pub const FUSE_ASYNC_READ: u32 = 1 << 0; +/// The supported bit that supports posix file locks. pub const FUSE_POSIX_LOCKS: u32 = 1 << 1; +/// The supported bit that supports the O_TRUNC open flag. pub const FUSE_ATOMIC_O_TRUNC: u32 = 1 << 3; +/// The supported bit that supports lookups of "." and "..". pub const FUSE_EXPORT_SUPPORT: u32 = 1 << 4; -pub const FUSE_BIG_WRITES: u32 = 1 << 5; +/// The supported bit that don't apply umask to file mode on create operation. pub const FUSE_DONT_MASK: u32 = 1 << 6; +/// The supported bit that supports BSD file locks. pub const FUSE_FLOCK_LOCKS: u32 = 1 << 10; +/// The supported bit that automatically checks invalid cached file. pub const FUSE_AUTO_INVAL_DATA: u32 = 1 << 12; +/// The supported bit that supports readdirplus. pub const FUSE_DO_READDIRPLUS: u32 = 1 << 13; +/// The supported bit that supports adaptive readdirplus. pub const FUSE_READDIRPLUS_AUTO: u32 = 1 << 14; +/// The supported bit that supports asynchronous direct I/O submission. pub const FUSE_ASYNC_DIO: u32 = 1 << 15; -pub const FUSE_WRITEBACK_CACHE: u32 = 1 << 16; +/// The capability bit that supports for parallel directory operations. pub const FUSE_PARALLEL_DIROPS: u32 = 1 << 18; +/// The capability bit that supports POSIX ACLs. pub const FUSE_POSIX_ACL: u32 = 1 << 20; +/// The capability bit that needs to reply the max number of pages in init fuse message. +pub const FUSE_MAX_PAGES: u32 = 1 << 22; pub const FATTR_MODE: u32 = 1 << 0; pub const FATTR_UID: u32 = 1 << 1; diff --git a/vhost_user_fs/src/fuse_proc.rs b/vhost_user_fs/src/fuse_proc.rs index 6e36e0307..df3bbc902 100644 --- a/vhost_user_fs/src/fuse_proc.rs +++ b/vhost_user_fs/src/fuse_proc.rs @@ -40,6 +40,15 @@ fn is_safe_path(path: CString) -> bool { true } +/// Process the fuse message of FUSE_LOOKUP. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_lookup( sys_mem: &Arc, fs: Arc>, @@ -87,6 +96,14 @@ pub fn do_fuse_lookup( } } +/// Process the fuse message of FUSE_FORGET. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_forget( sys_mem: &Arc, fs: Arc>, @@ -107,6 +124,14 @@ pub fn do_fuse_forget( 0_u32 } +/// Process the fuse message of FUSE_GETATTR. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_getattr( sys_mem: &Arc, fs: Arc>, @@ -139,6 +164,15 @@ pub fn do_fuse_getattr( } } +/// Process the fuse message of FUSE_SETATTR. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_setattr( sys_mem: &Arc, fs: Arc>, @@ -183,6 +217,14 @@ pub fn do_fuse_setattr( } } +/// Process the fuse message of FUSE_READLINK. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_readlink( sys_mem: &Arc, fs: Arc>, @@ -209,6 +251,15 @@ pub fn do_fuse_readlink( } } +/// Process the fuse message of FUSE_SYMLINK. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_symlink( sys_mem: &Arc, fs: Arc>, @@ -269,6 +320,15 @@ pub fn do_fuse_symlink( } } +/// Process the fuse message of FUSE_MKNOD. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_mknod( sys_mem: &Arc, fs: Arc>, @@ -326,6 +386,15 @@ pub fn do_fuse_mknod( } } +/// Process the fuse message of FUSE_MKDIR. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_mkdir( sys_mem: &Arc, fs: Arc>, @@ -383,6 +452,15 @@ pub fn do_fuse_mkdir( } } +/// Process the fuse message of FUSE_UNLINK. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_unlink( sys_mem: &Arc, fs: Arc>, @@ -407,6 +485,15 @@ pub fn do_fuse_unlink( reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) } +/// Process the fuse message of FUSE_RMDIR. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_rmdir( sys_mem: &Arc, fs: Arc>, @@ -431,6 +518,15 @@ pub fn do_fuse_rmdir( reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) } +/// Process the fuse message of FUSE_RENAME. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_rename( sys_mem: &Arc, fs: Arc>, @@ -475,6 +571,15 @@ pub fn do_fuse_rename( reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) } +/// Process the fuse message of FUSE_LINK. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_link( sys_mem: &Arc, fs: Arc>, @@ -535,6 +640,15 @@ pub fn do_fuse_link( } } +/// Process the fuse message of FUSE_OPEN. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_open( sys_mem: &Arc, fs: Arc>, @@ -575,6 +689,15 @@ pub fn do_fuse_open( } } +/// Process the fuse message of FUSE_READ. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_read( sys_mem: &Arc, fs: Arc>, @@ -620,6 +743,15 @@ pub fn do_fuse_read( } } +/// Process the fuse message of FUSE_WRITE. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_write( sys_mem: &Arc, fs: Arc>, @@ -661,6 +793,14 @@ pub fn do_fuse_write( } } +/// Process the fuse message of FUSE_STATFS. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_statfs( sys_mem: &Arc, fs: Arc>, @@ -686,6 +826,15 @@ pub fn do_fuse_statfs( } } +/// Process the fuse message of FUSE_RELEASE. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_release( sys_mem: &Arc, fs: Arc>, @@ -708,6 +857,15 @@ pub fn do_fuse_release( reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) } +/// Process the fuse message of FUSE_FSYNC. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_fsync( sys_mem: &Arc, fs: Arc>, @@ -729,6 +887,15 @@ pub fn do_fuse_fsync( reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) } +/// Process the fuse message of FUSE_SETXATTR. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_setxattr( sys_mem: &Arc, fs: Arc>, @@ -777,6 +944,15 @@ pub fn do_fuse_setxattr( reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) } +/// Process the fuse message of FUSE_GETXATTR. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_getxattr( sys_mem: &Arc, fs: Arc>, @@ -822,6 +998,15 @@ pub fn do_fuse_getxattr( } } +/// Process the fuse message of FUSE_LISTXATTR. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_listxattr( sys_mem: &Arc, fs: Arc>, @@ -859,6 +1044,15 @@ pub fn do_fuse_listxattr( } } +/// Process the fuse message of FUSE_REMOVEXATTR. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_removexattr( sys_mem: &Arc, fs: Arc>, @@ -885,6 +1079,15 @@ pub fn do_fuse_removexattr( reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) } +/// Process the fuse message of FUSE_FLUSH. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_flush( sys_mem: &Arc, fs: Arc>, @@ -908,6 +1111,15 @@ pub fn do_fuse_flush( reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) } +/// Process the fuse message of FUSE_INIT. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_init( sys_mem: &Arc, fs: Arc>, @@ -949,6 +1161,15 @@ pub fn do_fuse_init( ) } +/// Process the fuse message of FUSE_OPENDIR. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_opendir( sys_mem: &Arc, fs: Arc>, @@ -992,6 +1213,15 @@ pub fn do_fuse_opendir( } } +/// Process the fuse message of FUSE_READDIR. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_readdir( sys_mem: &Arc, fs: Arc>, @@ -1033,6 +1263,15 @@ pub fn do_fuse_readdir( } } +/// Process the fuse message of FUSE_RELEASEDIR. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_releasedir( sys_mem: &Arc, fs: Arc>, @@ -1055,6 +1294,15 @@ pub fn do_fuse_releasedir( reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) } +/// Process the fuse message of FUSE_FSYNCDIR. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_fsyncdir( sys_mem: &Arc, fs: Arc>, @@ -1075,6 +1323,15 @@ pub fn do_fuse_fsyncdir( reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) } +/// Process the fuse message of FUSE_GETLK. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_getlk( sys_mem: &Arc, fs: Arc>, @@ -1148,6 +1405,16 @@ fn do_fuse_flock( } const FUSE_LOCK_FLOCK: u32 = (1 << 0) as u32; + +/// Process the fuse message of FUSE_SETLK. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_setlk( sys_mem: &Arc, fs: Arc>, @@ -1170,6 +1437,15 @@ pub fn do_fuse_setlk( } } +/// Process the fuse message of FUSE_SETLKW. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_setlkw( sys_mem: &Arc, fs: Arc>, @@ -1192,6 +1468,15 @@ pub fn do_fuse_setlkw( } } +/// Process the fuse message of FUSE_CREATE. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_create( sys_mem: &Arc, fs: Arc>, @@ -1266,6 +1551,14 @@ pub fn do_fuse_create( } } +/// Process the fuse message of FUSE_DESTROY. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_destroy( sys_mem: &Arc, fs: Arc>, @@ -1277,6 +1570,13 @@ pub fn do_fuse_destroy( reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) } +/// Process the fuse message of FUSE_BATCH_FORGET. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. pub fn do_fuse_batch_forget( sys_mem: &Arc, fs: Arc>, @@ -1313,6 +1613,15 @@ pub fn do_fuse_batch_forget( 0_u32 } +/// Process the fuse message of FUSE_FALLOCATE. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_fallocate( sys_mem: &Arc, fs: Arc>, @@ -1341,6 +1650,15 @@ pub fn do_fuse_fallocate( reply_fuse_msg(writer, sys_mem, in_header, ret, None, 0_usize) } +/// Process the fuse message of FUSE_READDIRPLUS. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_readdirplus( sys_mem: &Arc, fs: Arc>, @@ -1382,6 +1700,15 @@ pub fn do_fuse_readdirplus( } } +/// Process the fuse message of FUSE_LSEEK. +/// +/// # Arguments +/// +/// * `sys_mem` - Address space mapped with StratoVirt. +/// * `fs` - The management of userspace filesystem. +/// * `reader` - The read-only buffers parsed from the element of virtio queue. +/// * `writer` - The write-only buffers parsed from the element of virtio queue. +/// * `in_header` - The in_header reading from the read-only buffers. pub fn do_fuse_lseek( sys_mem: &Arc, fs: Arc>, -- Gitee From 4687c80351a87e1cd1d7736385a970b75956c8cf Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Wed, 17 Mar 2021 17:14:33 +0800 Subject: [PATCH 42/55] vhost_user_fs: refactor FuseBuffer::new() for element initialization which is placed in the new() function Signed-off-by: Fei Xu --- vhost_user_fs/src/fuse_msg.rs | 41 ++++++++++++++++++++++++----------- vhost_user_fs/src/fuse_req.rs | 20 +++-------------- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index 2bb523f3e..cb3fd1cb0 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -32,17 +32,30 @@ pub const FUSE_OK: i32 = 0; pub struct FuseBuffer { /// Get the buffers to read or write from the element of virtio queue. pub bufs: VecDeque, - /// The total size of the buffers from the element of virtio queue. - pub btyes_total: usize, - /// The processed bytes to read or write fuse message. - pub bytes_processed: usize, + // The total size of the buffers from the element of virtio queue. + btyes_total: usize, + // The processed bytes to read or write fuse message. + bytes_processed: usize, } impl FuseBuffer { - fn new() -> Self { + /// Construct a fuse buffer to process fuse message. + /// + /// # Arguments + /// + /// * `elem_iovec` - The vectors of IO element from virtio queue. + pub fn new(elem_iovec: &[ElemIovec]) -> Self { + let mut btyes_total = 0; + let mut bufs = VecDeque::new(); + + for iov in elem_iovec { + btyes_total += iov.len as usize; + bufs.push_back(*iov); + } + FuseBuffer { - bufs: VecDeque::new(), - btyes_total: 0_usize, + bufs, + btyes_total, bytes_processed: 0_usize, } } @@ -168,6 +181,14 @@ impl FuseBuffer { } fn write_slice(&mut self, sys_mem: &Arc, src: &[u8], count: usize) -> Result<()> { + if src.len() != count { + bail!( + "The length {} of src slice is not equal to the count {}", + src.len(), + count + ); + } + let write_end = match self.bytes_processed.checked_add(count) { Some(end_) => end_, None => bail!("The read count {} {} overflow", count, self.bytes_processed), @@ -309,12 +330,6 @@ impl FuseBuffer { } } -impl Default for FuseBuffer { - fn default() -> Self { - Self::new() - } -} - /// Save the address and the length for replying fuse message. pub struct FuseIovec<'a> { body: &'a [u8], diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs index 021a80adb..fae8b18a0 100644 --- a/vhost_user_fs/src/fuse_req.rs +++ b/vhost_user_fs/src/fuse_req.rs @@ -34,24 +34,10 @@ impl FuseReq { /// /// * `elem` - The element parsed from virtio queue. pub fn new(elem: &Element) -> Self { - let desc_index = elem.index; - - let mut reader = FuseBuffer::default(); - for out_iov in &elem.out_iovec { - reader.btyes_total += out_iov.len as usize; - reader.bufs.push_back(*out_iov); - } - - let mut writer = FuseBuffer::default(); - for in_iov in &elem.in_iovec { - writer.btyes_total += in_iov.len as usize; - writer.bufs.push_back(*in_iov); - } - FuseReq { - desc_index, - reader, - writer, + desc_index: elem.index, + reader: FuseBuffer::new(&elem.out_iovec), + writer: FuseBuffer::new(&elem.in_iovec), } } -- Gitee From 2ba6b525e60047b815d2dae12a5be44967da88ff Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Tue, 23 Mar 2021 15:51:31 +0800 Subject: [PATCH 43/55] vhost_user_fs: add code comments for fs.rs and modify code comments for fuse_msg.rs Signed-off-by: Fei Xu --- vhost_user_fs/src/fs.rs | 280 +++++++++++++++++++++++++++++++++- vhost_user_fs/src/fuse_msg.rs | 2 +- 2 files changed, 279 insertions(+), 3 deletions(-) diff --git a/vhost_user_fs/src/fs.rs b/vhost_user_fs/src/fs.rs index d38b20723..418b7d71e 100644 --- a/vhost_user_fs/src/fs.rs +++ b/vhost_user_fs/src/fs.rs @@ -166,7 +166,7 @@ impl Inode { } } - pub fn as_raw_fd(&self) -> RawFd { + fn as_raw_fd(&self) -> RawFd { self.file.as_raw_fd() } } @@ -228,6 +228,14 @@ fn path_is_dotdot(path: &CString) -> bool { } const RLIMIT_NOFILE_MIN: u64 = 20; +/// Set file resources limits for the process. The limit value +/// must be more than or equal to 20 to ensure that the process +/// can start normally. The limit value must be less than or equal +/// to the value of "/proc/sys/fs/file-max" and "/proc/sys/fs/nr_open". +/// +/// # Arguments +/// +/// * `limit` - The limit value which needs to be set. pub fn set_rlimit_nofile(limit: u64) -> Result<()> { if limit < RLIMIT_NOFILE_MIN { bail!( @@ -269,7 +277,8 @@ pub fn set_rlimit_nofile(limit: u64) -> Result<()> { Ok(()) } -#[allow(dead_code)] +/// The management structure of filesystem that contains the management of inodes +/// and the information of files in host directory which needs to be shared. pub struct FileSystem { root_inode: Inode, inodes: BTreeMap, @@ -279,6 +288,11 @@ pub struct FileSystem { } impl FileSystem { + /// Create a filesystem management structure. + /// + /// # Arguments + /// + /// * `source_dir` - The path of the host directory which needs to be shared. pub fn new(source_dir: &str) -> Result { let (root_file_opt, ret) = open(CString::new(source_dir).unwrap(), libc::O_PATH); if ret != FUSE_OK { @@ -442,6 +456,14 @@ impl FileSystem { FUSE_OK } + /// Look up the directory or file information by name and reply attributes. + /// + /// # Arguments + /// + /// * `parent_nodeid` - The parent node id that is the starting directory to look up. + /// * `name` - The name that needs to be looked up. + /// * `node_id` - The node id that needs to be looked up by name. + /// * `fuse_attr` - The attributes that needs to be looked up by name. pub fn lookup( &mut self, parent_nodeid: usize, @@ -458,6 +480,12 @@ impl FileSystem { self.internal_lookup(&inode, name, node_id, fuse_attr) } + /// When the nlookup of inode is reduced to 0, delete the inode from the management structure. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to find the inode. + /// * `nlookup` - The number of nlookup for inode needs to be reduced. pub fn forget(&mut self, node_id: usize, nlookup: u64) -> i32 { let mut inode = match self.find_inode(node_id) { Some(i) => i.clone(), @@ -469,6 +497,12 @@ impl FileSystem { FUSE_OK } + /// Get the attributes of a file or directory. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to find the inode. + /// * `fuse_attr` - The attributes will be returned by the found inode. pub fn getattr(&mut self, node_id: usize, fuse_attr: &mut FuseAttr) -> i32 { let inode = match self.find_inode(node_id) { Some(i) => i.clone(), @@ -488,6 +522,13 @@ impl FileSystem { FUSE_OK } + /// Set the attributes of a file or directory. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to find the inode. + /// * `attr` - The attributes will be set to the found inode. + /// * `fuse_attr` - The attributes will be returned by the found inode. pub fn setattr( &mut self, node_id: usize, @@ -657,6 +698,12 @@ impl FileSystem { self.getattr(node_id, fuse_attr) } + /// Get the contexts of the symbolic link into the buffer. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to find the inode. + /// * `buff` - The buffer is saved by the contexts of the symbolic link. pub fn readlink(&self, node_id: usize, buff: &mut Vec) -> i32 { let inode = match self.find_inode(node_id) { Some(i) => i.clone(), @@ -679,6 +726,15 @@ impl FileSystem { FUSE_OK } + /// Get the contexts of the symbolic link into the buffer. + /// + /// # Arguments + /// + /// * `in_header` - The in_header of fuse message used to get uid and gid. + /// * `name` - The target link name used to create a symbolic link. + /// * `link_name` - The link name that will be created a symbolic link. + /// * `node_id` - The node id that is found by name. + /// * `fuse_attr` - The attributes will be returned by the found inode. pub fn symlink( &mut self, in_header: &FuseInHeader, @@ -712,6 +768,16 @@ impl FileSystem { self.internal_lookup(&parent_inode, name, node_id, fuse_attr) } + /// Create a file system node(file, device special file or named pipe) by the path name + /// with the mode and dev in the mknod information. + /// + /// # Arguments + /// + /// * `in_header` - The in_header of fuse message used to get uid and gid. + /// * `mknod_in` - The information of mknod to get the permissions and dev. + /// * `name` - The path name used to create a file system node. + /// * `node_id` - The node id that is found by name. + /// * `fuse_attr` - The attributes will be returned by the found inode. pub fn mknod( &mut self, in_header: &FuseInHeader, @@ -750,6 +816,15 @@ impl FileSystem { self.internal_lookup(&parent_inode, name, node_id, fuse_attr) } + /// Create a directory by the name with the permissions in the mkdir information. + /// + /// # Arguments + /// + /// * `in_header` - The in_header of fuse message used to get uid and gid. + /// * `mkdir_in` - The information of mkdir used to get permissions. + /// * `name` - The path name that will be created a directory. + /// * `node_id` - The node id that is found by the path name. + /// * `fuse_attr` - The attributes will be returned by the found inode. pub fn mkdir( &mut self, in_header: &FuseInHeader, @@ -787,6 +862,13 @@ impl FileSystem { self.internal_lookup(&parent_dir, name, node_id, fuse_attr) } + /// Delete a name from the host filesystem. + /// + /// # Arguments + /// + /// * `parent_nodeid` - The parent node id that is the starting directory to look up + /// in the management of filesystem. + /// * `name` - The name will be deleted. pub fn unlink(&mut self, parent_nodeid: usize, name: CString) -> i32 { let parent_inode = match self.find_inode(parent_nodeid) { Some(i) => i.clone(), @@ -820,6 +902,13 @@ impl FileSystem { FUSE_OK } + /// Delete a directory from the host filesystem by the path name. + /// + /// # Arguments + /// + /// * `parent_nodeid` - The parent node id that is the starting directory to look up + /// in the management of filesystem. + /// * `name` - The path name of the directory will be deleted. pub fn rmdir(&mut self, parent_nodeid: usize, name: CString) -> i32 { let parent_inode = match self.find_inode(parent_nodeid) { Some(i) => i.clone(), @@ -853,6 +942,16 @@ impl FileSystem { FUSE_OK } + /// Rename the old path name to the new path name in the host filesystem.. + /// + /// # Arguments + /// + /// * `parent_nodeid` - The parent node id that is the starting directory to look up + /// for old path name in the management of filesystem. + /// * `oldname` - The old path name that is relative to the directory of parent node. + /// * `newparent_nodeid` - The new parent node id that is the starting directory to + /// look up for new path name in the management of filesystem. + /// * `newname` - The new path name that is relative to the directory of new parent node. pub fn rename( &self, parent_nodeid: usize, @@ -896,6 +995,15 @@ impl FileSystem { rename(&parent_inode.file, oldname, &newparent_inode.file, newname) } + /// Create a new link to an existing file for the host filesystem. + /// + /// # Arguments + /// + /// * `parent_nodeid` - The parent node id that is the starting directory to look up. + /// * `old_nodeid` - The old node id in the management of filesystem. + /// * `name` - The path name that is relative to the directory of parent node. + /// * `node_id` - The node id that is found by the path name in the management of filesystem. + /// * `fuse_attr` - The attributes will be returned by the found inode. pub fn link( &mut self, parent_nodeid: usize, @@ -943,6 +1051,13 @@ impl FileSystem { FUSE_OK } + /// Open the file with the node id in the management of filesystem. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to look up the inode. + /// * `flags` - The flags used to open the file. + /// * `fh` - The file handler is returned in the management of filesystem. pub fn open(&mut self, node_id: usize, flags: u32, fh: &mut u64) -> i32 { let (inode_fd, file_type) = match self.find_inode(node_id) { Some(i) => (i.as_raw_fd(), i.file_type), @@ -972,6 +1087,12 @@ impl FileSystem { FUSE_OK } + /// Read the file descriptor by file hander in the management of filesystem. + /// + /// # Arguments + /// + /// * `fh` - The file handler in the management of filesystem. + /// * `fd` - The file descriptor in the host filesystem. pub fn read(&mut self, fh: usize, fd: &mut RawFd) -> i32 { match self.file_map.get_value(fh) { Some(file) => { @@ -985,6 +1106,12 @@ impl FileSystem { FUSE_OK } + /// write the file descriptor by file hander in the management of filesystem. + /// + /// # Arguments + /// + /// * `fh` - The file handler in the management of filesystem. + /// * `fd` - The file descriptor in the host filesystem. pub fn write(&mut self, fh: usize, fd: &mut RawFd) -> i32 { match self.file_map.get_value(fh) { Some(file) => { @@ -998,6 +1125,13 @@ impl FileSystem { FUSE_OK } + /// Get the information about a mounted filesystem. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to look up the inode. + /// * `fuse_statfs` - The information about the mounted filesystem is + /// returned by the found inode. pub fn statfs(&mut self, node_id: usize, fuse_statfs: &mut FuseStatfsOut) -> i32 { let inode = match self.find_inode(node_id) { Some(i) => i.clone(), @@ -1016,12 +1150,25 @@ impl FileSystem { FUSE_OK } + /// Release the file with file handler in the management of filesystem. + /// + /// # Arguments + /// + /// * `fh` - The file handler in the management of filesystem. pub fn release(&mut self, fh: usize) -> i32 { self.file_map.put_map(fh); FUSE_OK } + /// Transfer the file data to the storage device with file handler in + /// the management of filesystem. + /// + /// # Arguments + /// + /// * `fh` - The file handler in the management of filesystem. + /// * `datasync` - The datasync indicates whether to use the fdatasync + /// or fsync interface. pub fn fsyncfile(&self, fh: usize, datasync: bool) -> i32 { let mut ret = FUSE_OK; @@ -1066,6 +1213,16 @@ impl FileSystem { ret } + /// Set an extended attribute identified by name and associated with the node id + /// in the management of filesystem. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to look up the inode. + /// * `name` - The name associated with inode. + /// * `value` - The value of the extended attribute. + /// * `size` - The size of the value string. + /// * `flags` - The flags used to set an extended attribute. pub fn setxattr( &self, node_id: usize, @@ -1118,6 +1275,15 @@ impl FileSystem { } } + /// Get an extended attribute identified by name and associated with the node id + /// in the management of filesystem. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to look up the inode. + /// * `name` - The name associated with inode. + /// * `size` - The size of the buffer. + /// * `buff` - The buffer of the extended attribute. pub fn getxattr(&self, node_id: usize, name: CString, size: u32, buff: &mut Vec) -> i32 { let inode = match self.find_inode(node_id) { Some(i) => i.clone(), @@ -1173,6 +1339,14 @@ impl FileSystem { FUSE_OK } + /// List extended attribute names associated with the node id + /// in the management of filesystem. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to look up the inode. + /// * `size` - The size of the buffer. + /// * `buff` - The buffer of the extended attribute. pub fn listxattr(&self, node_id: usize, size: u32, buff: &mut Vec) -> i32 { let inode = match self.find_inode(node_id) { Some(i) => i.clone(), @@ -1227,6 +1401,13 @@ impl FileSystem { FUSE_OK } + /// Remove an extended attribute identified by name and associated with the node id + /// in the management of filesystem. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to look up the inode in the management of filesystem. + /// * `name` - The name associated with inode. pub fn removexattr(&self, node_id: usize, name: CString) -> i32 { let inode = match self.find_inode(node_id) { Some(i) => i.clone(), @@ -1275,10 +1456,22 @@ impl FileSystem { FUSE_OK } + /// Delete the file lock by the node id in the management of filesystem. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to look up the inode in the management of filesystem. + /// * `owner` - The name associated with inode. pub fn flush(&mut self, node_id: usize, owner: u64) -> i32 { self.delete_file_lock(node_id, owner) } + /// Initialize fuse message for getting supported features in the process. + /// + /// # Arguments + /// + /// * `flags` - The supported features in StratoVirt. + /// * `support_flags` - The supported features in the process. pub fn init(&self, flags: u32, support_flags: &mut u32) { if flags & FUSE_MAX_PAGES != 0 { *support_flags |= FUSE_MAX_PAGES; @@ -1324,6 +1517,12 @@ impl FileSystem { umask(0o000); } + /// Open a directory with the node id in the management of filesystem. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to look up the inode in the management of filesystem. + /// * `dir_fh` - The directory handler is returned in the management of filesystem. pub fn opendir(&mut self, node_id: usize, dir_fh: &mut u64) -> i32 { let inode = match self.find_inode(node_id) { Some(i) => i.clone(), @@ -1345,6 +1544,16 @@ impl FileSystem { libc::EBADF as i32 } + /// read a directory stream with the directory handler in the host filesystem. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to look up the inode in the management of filesystem. + /// * `dh` - The directory handler in the management of filesystem. + /// * `size` - The size of the buffer. + /// * `offset` - The offset indicates it opens a directory stream with the offset. + /// * `plus` - The plus indicates it uses FuseDirentplus struct to the buffer. + /// * `buff` - The buffer of all FuseDirent structs or FuseDirentplus structs. pub fn readdir( &mut self, node_id: usize, @@ -1388,6 +1597,7 @@ impl FileSystem { break; } + // The above code has checked the validity of direntp, so it is safe for *direntp. let dirent = unsafe { *direntp }; let (name_len, son_name) = match array_to_cstring(&dirent.d_name[..]) { @@ -1474,12 +1684,25 @@ impl FileSystem { FUSE_OK } + /// Release a directory in the management of filesystem. + /// + /// # Arguments + /// + /// * `dir_fh` - The directory handler in the management of filesystem. pub fn releasedir(&mut self, dir_fh: usize) -> i32 { self.file_map.put_map(dir_fh); FUSE_OK } + /// Transfer the directory data to the storage device with directory handler in + /// the management of filesystem. + /// + /// # Arguments + /// + /// * `dir_fh` - The directory handler in the management of filesystem. + /// * `datasync` - The datasync indicates whether to use the fdatasync + /// or fsync interface. pub fn fsyncdir(&self, dir_fh: usize, datasync: bool) -> i32 { if let Some(file) = self.file_map.get_value(dir_fh) { let ret = fsync(file, datasync); @@ -1493,6 +1716,14 @@ impl FileSystem { FUSE_OK } + /// Create the POSIX file lock with the node id in the management of filesystem. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to look up the inode in the management of filesystem. + /// * `owner` - The unique index for file lock in the inode. + /// * `file_lock_in` - The information of file lock will be set. + /// * `file_lock_out` - The information of file lock will be returned. pub fn getlk( &mut self, node_id: usize, @@ -1518,6 +1749,15 @@ impl FileSystem { FUSE_OK } + /// Lock the file or unlock the file by POSIX lock with the node id in + /// the management of filesystem. + /// + /// # Arguments + /// + /// * `node_id` - The node id used to look up the inode in the management of filesystem. + /// * `owner` - The unique index for file lock in the inode. + /// * `is_blocking` - The is_blocking indicates whether to use a blocking lock. + /// * `file_lock_in` - The information of file lock will be set. pub fn setlk( &mut self, node_id: usize, @@ -1548,6 +1788,14 @@ impl FileSystem { FUSE_OK } + /// Lock the file or unlock the file by BSD lock with file handler in + /// the management of filesystem. + /// + /// # Arguments + /// + /// * `fh` - The file handler in the management of filesystem. + /// * `lock_type` - The lock type contains the type of read lock, write lock and unlocking. + /// * `is_blocking` - The is_blocking indicates whether to use a blocking lock. pub fn flock(&self, fh: usize, lock_type: u32, is_blocking: bool) -> i32 { let mut operation: i32 = 0; @@ -1575,6 +1823,16 @@ impl FileSystem { FUSE_OK } + /// Create a file with name in the management of filesystem. + /// + /// # Arguments + /// + /// * `in_header` - The in_header of fuse message used to get uid and gid. + /// * `create_in` - The information of creating a file contains the flags, mode and umask. + /// * `name` - The string of name used to create a file. + /// * `fh` - The file handler is returned in the management of filesystem. + /// * `node_id` - The node id that is found by the name in the management of filesystem. + /// * `fuse_attr` - The attributes will be returned by the found inode. pub fn create( &mut self, in_header: &FuseInHeader, @@ -1618,6 +1876,7 @@ impl FileSystem { self.internal_lookup(&parent_dir, name, node_id, fuse_attr) } + /// Destroy the management of filesystem, except for the root inode. pub fn destroy(&mut self) -> i32 { let root_key = self.root_inode.key; @@ -1634,6 +1893,14 @@ impl FileSystem { FUSE_OK } + /// Allocate the disk space with file handler in the management of filesystem. + /// + /// # Arguments + /// + /// * `fh` - The file handler in the management of filesystem. + /// * `mode` - The mode determines the operation to be performed on the given range. + /// * `offset` - The offset in the file. + /// * `length` - The length that needs to be allocated. pub fn fallocate(&self, fh: usize, mode: u32, offset: u64, length: u64) -> i32 { if let Some(file) = self.file_map.get_value(fh) { let ret = fallocate(file, mode, offset, length); @@ -1647,6 +1914,15 @@ impl FileSystem { FUSE_OK } + /// Reposition the file offset of the open file with file handler + /// in the management of filesystem. + /// + /// # Arguments + /// + /// * `fh` - The file handler in the management of filesystem. + /// * `offset` - The offset in the file used together with the whence. + /// * `whence` - The whence determines the operation to be performed in the file. + /// * `outoffset` - The offset from the beginning of the file is returned. pub fn lseek(&self, fh: usize, offset: u64, whence: u32, outoffset: &mut u64) -> i32 { if let Some(file) = self.file_map.get_value(fh) { let (offset_tmp, ret) = lseek(file, offset, whence); diff --git a/vhost_user_fs/src/fuse_msg.rs b/vhost_user_fs/src/fuse_msg.rs index cb3fd1cb0..ee307c762 100644 --- a/vhost_user_fs/src/fuse_msg.rs +++ b/vhost_user_fs/src/fuse_msg.rs @@ -43,7 +43,7 @@ impl FuseBuffer { /// /// # Arguments /// - /// * `elem_iovec` - The vectors of IO element from virtio queue. + /// * `elem_iovec` - The vectors of IO vector element from virtio queue. pub fn new(elem_iovec: &[ElemIovec]) -> Self { let mut btyes_total = 0; let mut bufs = VecDeque::new(); -- Gitee From 79400e342e40a9a8d2265a8e7edef1c8a1e0bacd Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Wed, 7 Apr 2021 14:00:56 +0800 Subject: [PATCH 44/55] vhost_user_fs: add code comments for fs_ops.rs Signed-off-by: Fei Xu --- vhost_user_fs/src/fs_ops.rs | 277 ++++++++++++++++++++++++++++++++++++ 1 file changed, 277 insertions(+) diff --git a/vhost_user_fs/src/fs_ops.rs b/vhost_user_fs/src/fs_ops.rs index 09bd2c3d5..be3dc32d1 100644 --- a/vhost_user_fs/src/fs_ops.rs +++ b/vhost_user_fs/src/fs_ops.rs @@ -15,9 +15,18 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use super::fuse_msg::*; +/// The pointer to open a directory. pub type DirPtr = *mut libc::DIR; +/// The pointer to a directory entry in the directory stream. pub type DirentPtr = *mut libc::dirent; +/// Get the information of a file with path name that is relative to the starting directory. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the file descriptor of starting directory to look up for the file. +/// * `name` - The name indicates the file path is relative to the starting directory. +/// * `flags` - The flags used to get the information of the file. pub fn fstat_at(file: &File, name: CString, flags: i32) -> (libc::stat, i32) { let mut stat: libc::stat = unsafe { std::mem::zeroed() }; @@ -29,6 +38,12 @@ pub fn fstat_at(file: &File, name: CString, flags: i32) -> (libc::stat, i32) { (stat, FUSE_OK) } +/// Open a file with the path name. +/// +/// # Arguments +/// +/// * `name` - The path name in the host filesystem. +/// * `flags` - The flags used to open a file. pub fn open(name: CString, flags: i32) -> (Option, i32) { errno::set_errno(errno::Errno(0)); let fd = unsafe { libc::open(name.as_ptr(), flags) }; @@ -41,6 +56,13 @@ pub fn open(name: CString, flags: i32) -> (Option, i32) { (Some(file), FUSE_OK) } +/// Open a file with path name that is relative to the starting directory. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the file descriptor of starting directory to look up for the file. +/// * `name` - The name indicates the file path is relative to the starting directory. +/// * `mode` - The mode used to open a file. pub fn open_at(file: &File, name: CString, flags: i32, mode: u32) -> (Option, i32) { errno::set_errno(errno::Errno(0)); @@ -54,6 +76,12 @@ pub fn open_at(file: &File, name: CString, flags: i32, mode: u32) -> (Option i32 { errno::set_errno(errno::Errno(0)); if unsafe { libc::fchmod(file.as_raw_fd(), mode) } < 0 { @@ -63,6 +91,14 @@ pub fn fchmod(file: &File, mode: u32) -> i32 { FUSE_OK } +/// Change permissions of a file with path name that is relative to the starting directory. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the file descriptor of starting directory to look up for the +/// file. +/// * `name` - The name indicates the file path is relative to the starting directory. +/// * `mode` - The mode indicates the permissions of the file will be set. pub fn fchmod_at(file: &File, name: CString, mode: u32) -> i32 { errno::set_errno(errno::Errno(0)); if unsafe { libc::fchmodat(file.as_raw_fd(), name.as_ptr(), mode, 0) } < 0 { @@ -72,6 +108,16 @@ pub fn fchmod_at(file: &File, name: CString, mode: u32) -> i32 { FUSE_OK } +/// Change the owner and group of a file with path name that is relative to the starting directory. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the file descriptor of starting directory to look up for the +/// file. +/// * `name` - The name indicates the file path is relative to the starting directory. +/// * `uid` - The user id will be set. +/// * `gid` - The group id will be set. +/// * `flags` - The flags indicates the action of file will be set. pub fn fchown_at(file: &File, name: CString, uid: u32, gid: u32, flags: i32) -> i32 { errno::set_errno(errno::Errno(0)); if unsafe { libc::fchownat(file.as_raw_fd(), name.as_ptr(), uid, gid, flags) } < 0 { @@ -81,6 +127,12 @@ pub fn fchown_at(file: &File, name: CString, uid: u32, gid: u32, flags: i32) -> FUSE_OK } +/// Truncate file to specified length. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the open file descriptor. +/// * `size` - The size of truncating file. pub fn ftruncate(file: &File, size: u64) -> i32 { errno::set_errno(errno::Errno(0)); if unsafe { libc::ftruncate(file.as_raw_fd(), size as i64) } < 0 { @@ -90,6 +142,15 @@ pub fn ftruncate(file: &File, size: u64) -> i32 { FUSE_OK } +/// Update the timestamps of a file with nanosecond precision. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the open file descriptor. +/// * `a_sec` - The second of last access time. +/// * `a_nsec` - The nanosecond of last access time. +/// * `m_sec` - The second of last modification time. +/// * `m_nsec` - The nanosecond of last modification time. pub fn futimens(file: &File, a_sec: u64, a_nsec: i64, m_sec: u64, m_nsec: i64) -> i32 { let tv = vec![ libc::timespec { @@ -110,6 +171,17 @@ pub fn futimens(file: &File, a_sec: u64, a_nsec: i64, m_sec: u64, m_nsec: i64) - FUSE_OK } +/// Update the timestamps with nanosecond precision by path name that is relative to the starting directory. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the file descriptor of starting directory to look up for the +/// file. +/// * `name` - The name indicates the file path is relative to the starting directory. +/// * `a_sec` - The second of last access time. +/// * `a_nsec` - The nanosecond of last access time. +/// * `m_sec` - The second of last modification time. +/// * `m_nsec` - The nanosecond of last modification time. pub fn utimensat( file: &File, name: CString, @@ -139,6 +211,14 @@ pub fn utimensat( } const MAX_PATH_LEN: usize = 256; + +/// Read value of a symbolic link by path name that is relative to the starting directory. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the file descriptor of starting directory to look up for the +/// file. +/// * `name` - The name indicates the file path is relative to the starting directory. pub fn readlinkat(file: &File, path: CString) -> (Option>, i32) { let mut buf = vec![0; MAX_PATH_LEN + 1]; @@ -161,6 +241,14 @@ pub fn readlinkat(file: &File, path: CString) -> (Option>, i32) { (Some(buf), FUSE_OK) } +/// Creates a symbolic link to the target path name. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the file descriptor of starting directory to look up for the +/// file. +/// * `name` - The name indicates the file path is relative to the starting directory. +/// * `link_name` - The link name is new path name for the target path name. pub fn symlinkat(file: &File, name: CString, link_name: CString) -> i32 { errno::set_errno(errno::Errno(0)); @@ -173,6 +261,14 @@ pub fn symlinkat(file: &File, name: CString, link_name: CString) -> i32 { FUSE_OK } +/// Change user id and group id in the process. +/// +/// # Arguments +/// +/// * `new_uid` - The user id will be changed to the current user id. +/// * `new_gid` - The group id will be changed to the current group id. +/// * `old_uid` - The old user id will be returned. +/// * `old_gid` - The old group id will be returned. pub fn change_uid_gid(new_uid: u32, new_gid: u32, old_uid: &mut u32, old_gid: &mut u32) -> i32 { let current_uid = unsafe { libc::geteuid() }; let current_gid = unsafe { libc::getegid() }; @@ -196,6 +292,12 @@ pub fn change_uid_gid(new_uid: u32, new_gid: u32, old_uid: &mut u32, old_gid: &m FUSE_OK } +/// Recover user id and group id in the process. +/// +/// # Arguments +/// +/// * `old_uid` - The old user id will be recovered in the process. +/// * `old_gid` - The old group id will be recovered in the process. pub fn recover_uid_gid(old_uid: u32, old_gid: u32) -> i32 { let ret = unsafe { libc::syscall(libc::SYS_setresuid, -1, old_uid, -1) }; if ret == -1 { @@ -210,6 +312,15 @@ pub fn recover_uid_gid(old_uid: u32, old_gid: u32) -> i32 { FUSE_OK } +/// Create a special or ordinary file by path name that is relative to the starting directory. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the file descriptor of starting directory to look up for the +/// file. +/// * `name` - The name indicates the file path is relative to the starting directory. +/// * `mode` - The mode indicates both the file mode to use and the type of node to be created. +/// * `rdev` - The rdev indicates the major and minor numbers of the special file. pub fn mknodat(file: &File, name: CString, mode: u32, rdev: u32) -> i32 { errno::set_errno(errno::Errno(0)); @@ -222,6 +333,14 @@ pub fn mknodat(file: &File, name: CString, mode: u32, rdev: u32) -> i32 { FUSE_OK } +/// Create a directory by path name that is relative to the starting directory. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the file descriptor of starting directory to look up for the +/// file. +/// * `name` - The name indicates the file path is relative to the starting directory. +/// * `mode` - The mode indicates the permissions of the new directory. pub fn mkdir_at(file: &File, name: CString, mode: u32) -> i32 { errno::set_errno(errno::Errno(0)); if unsafe { libc::mkdirat(file.as_raw_fd(), name.as_ptr(), mode) } < 0 { @@ -231,6 +350,14 @@ pub fn mkdir_at(file: &File, name: CString, mode: u32) -> i32 { FUSE_OK } +/// Delete a name in host filesystem by path name that is relative to the starting directory. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the file descriptor of starting directory to look up for the +/// file. +/// * `name` - The name indicates the file path is relative to the starting directory. +/// * `flags` - The flags indicates the operation of deleting a name. pub fn unlinkat(file: &File, name: CString, flags: i32) -> i32 { errno::set_errno(errno::Errno(0)); @@ -243,6 +370,16 @@ pub fn unlinkat(file: &File, name: CString, flags: i32) -> i32 { FUSE_OK } +/// Modify a name in host filesystem by path name that is relative to the starting directory. +/// +/// # Arguments +/// +/// * `olddir` - The directory file handler saves the file descriptor of starting directory to look up for the +/// old file. +/// * `name` - The name indicates the file path is relative to the starting of old directory. +/// * `newdir` - The directory file handler saves the file descriptor of starting directory to look up for the +/// new file. +/// * `newname` - The name indicates the file path is relative to the starting of new directory. pub fn rename(olddir: &File, name: CString, newdir: &File, newname: CString) -> i32 { errno::set_errno(errno::Errno(0)); @@ -262,6 +399,17 @@ pub fn rename(olddir: &File, name: CString, newdir: &File, newname: CString) -> FUSE_OK } +/// Change a name for file in host filesystem by path name that is relative to the starting directory. +/// +/// # Arguments +/// +/// * `old_file` - The file handler saves the file descriptor of starting old directory to look up for the +/// file. +/// * `old_name` - The name indicates the file path is relative to the starting of old directory. +/// * `new_file` - The file handler saves the file descriptor of starting new directory to look up for the +/// file. +/// * `new_name` - The name indicates the file path is relative to the starting of new directory. +/// * `flags` - The flags indicates the operation of change a name. pub fn linkat( old_file: &File, old_name: CString, @@ -288,6 +436,11 @@ pub fn linkat( FUSE_OK } +/// Get the information about a mounted filesystem. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the open file descriptor. pub fn fstat_vfs(file: &File) -> (libc::statvfs, i32) { let mut stat: libc::statvfs = unsafe { std::mem::zeroed() }; @@ -299,6 +452,13 @@ pub fn fstat_vfs(file: &File) -> (libc::statvfs, i32) { (stat, FUSE_OK) } +/// Synchronize the data of file to storage device. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the open file descriptor. +/// * `datasync` - The datasync indicates whether to use the fdatasync +/// or fsync interface. pub fn fsync(file: &File, datasync: bool) -> i32 { errno::set_errno(errno::Errno(0)); @@ -315,6 +475,11 @@ pub fn fsync(file: &File, datasync: bool) -> i32 { FUSE_OK } +/// Change current working directory. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the open directory descriptor. pub fn fchdir(file: &File) -> i32 { errno::set_errno(errno::Errno(0)); let ret = unsafe { libc::fchdir(file.as_raw_fd()) }; @@ -325,6 +490,15 @@ pub fn fchdir(file: &File) -> i32 { FUSE_OK } +/// Set an extended attribute value. +/// +/// # Arguments +/// +/// * `path` - The path in the host filesystem. +/// * `name` - The name of extended attribute. +/// * `value` - The value of extended attribute. +/// * `size` - The size of the string of value. +/// * `flags` - The flags indicates the attribute will be set. pub fn set_xattr(path: CString, name: CString, value: CString, size: u32, flags: u32) -> i32 { errno::set_errno(errno::Errno(0)); let ret = unsafe { @@ -344,6 +518,15 @@ pub fn set_xattr(path: CString, name: CString, value: CString, size: u32, flags: FUSE_OK } +/// Set an extended attribute value by the open file file descriptor. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the open file descriptor. +/// * `name` - The name of extended attribute. +/// * `value` - The value of extended attribute. +/// * `size` - The size of the string of value. +/// * `flags` - The flags indicates the attribute will be set. pub fn fset_xattr(file: &File, name: CString, value: CString, size: u32, flags: u32) -> i32 { errno::set_errno(errno::Errno(0)); let ret = unsafe { @@ -363,6 +546,13 @@ pub fn fset_xattr(file: &File, name: CString, value: CString, size: u32, flags: FUSE_OK } +/// Get an extended attribute value. +/// +/// # Arguments +/// +/// * `path` - The path in the host filesystem. +/// * `name` - The name of extended attribute. +/// * `size` - The size of the extended attribute value that needs to be get. pub fn get_xattr(path: CString, name: CString, size: usize) -> (Option>, i32) { let mut buf = vec![0; size]; @@ -384,6 +574,13 @@ pub fn get_xattr(path: CString, name: CString, size: usize) -> (Option>, (Some(buf), FUSE_OK) } +/// Get an extended attribute value by the open file descriptor. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the open file descriptor. +/// * `name` - The name of extended attribute. +/// * `size` - The size of the extended attribute value that needs to be get. pub fn fget_xattr(file: &File, name: CString, size: usize) -> (Option>, i32) { let mut buf = vec![0; size]; @@ -405,6 +602,12 @@ pub fn fget_xattr(file: &File, name: CString, size: usize) -> (Option>, (Some(buf), FUSE_OK) } +/// List extended attribute names. +/// +/// # Arguments +/// +/// * `path` - The path in the host filesystem. +/// * `size` - The size of the extended attribute names that needs to be get. pub fn list_xattr(path: CString, size: usize) -> (Option>, i32) { let mut buf = vec![0; size]; @@ -420,6 +623,12 @@ pub fn list_xattr(path: CString, size: usize) -> (Option>, i32) { (Some(buf), FUSE_OK) } +/// List extended attribute names by the open file descriptor. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the open file descriptor. +/// * `size` - The size of the extended attribute names that needs to be get. pub fn flist_xattr(file: &File, size: usize) -> (Option>, i32) { let mut buf = vec![0; size]; @@ -440,6 +649,12 @@ pub fn flist_xattr(file: &File, size: usize) -> (Option>, i32) { (Some(buf), FUSE_OK) } +/// Remove an extended attribute value. +/// +/// # Arguments +/// +/// * `path` - The path in the host filesystem. +/// * `name` - The name of the extended attribute value. pub fn remove_xattr(path: CString, name: CString) -> i32 { errno::set_errno(errno::Errno(0)); let ret = unsafe { libc::removexattr(path.as_ptr(), name.as_ptr()) }; @@ -450,6 +665,12 @@ pub fn remove_xattr(path: CString, name: CString) -> i32 { FUSE_OK } +/// Remove an extended attribute value by the open file descriptor. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the open file descriptor. +/// * `name` - The name of the extended attribute value. pub fn fremove_xattr(file: &File, name: CString) -> i32 { errno::set_errno(errno::Errno(0)); let ret = unsafe { libc::fremovexattr(file.as_raw_fd(), name.as_ptr()) }; @@ -460,10 +681,20 @@ pub fn fremove_xattr(file: &File, name: CString) -> i32 { FUSE_OK } +/// Set mask for file mode creation. +/// +/// # Arguments +/// +/// * `umask` - The umask indicates the permissions in the umask are turned off. pub fn umask(umask: u32) { unsafe { libc::umask(umask) }; } +/// Open a directory stream by the file descriptor. +/// +/// # Arguments +/// +/// * `fd` - The open file descriptor used to open a directory stream. pub fn fdopen_dir(fd: RawFd) -> (Option, i32) { errno::set_errno(errno::Errno(0)); @@ -475,12 +706,23 @@ pub fn fdopen_dir(fd: RawFd) -> (Option, i32) { (Some(dirp), FUSE_OK) } +/// Set the position of the next readdir() in the directory stream. +/// +/// # Arguments +/// +/// * `dirp` - The pointer to open a directory. +/// * `offset` - The position of the next readdir(). pub fn seek_dir(dirp: &mut DirPtr, offset: u64) { unsafe { libc::seekdir(*dirp, offset as i64); }; } +/// Read a directory entry in the directory stream. +/// +/// # Arguments +/// +/// * `dirp` - The pointer to open a directory. pub fn read_dir(dirp: &mut DirPtr) -> (Option, i32) { errno::set_errno(errno::Errno(0)); @@ -492,6 +734,12 @@ pub fn read_dir(dirp: &mut DirPtr) -> (Option, i32) { (Some(direntp), FUSE_OK) } +/// Lock the file or unlock the file by BSD lock by the open file descriptor. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the open file descriptor. +/// * `operation` - The operation of lock type. pub fn flock(file: &File, operation: i32) -> i32 { errno::set_errno(errno::Errno(0)); let ret = unsafe { libc::flock(file.as_raw_fd(), operation) }; @@ -503,6 +751,14 @@ pub fn flock(file: &File, operation: i32) -> i32 { } const OFFSET_MAX: u64 = 0x7fffffffffffffff; +/// Lock the file or unlock the file by POSIX lock by the open file descriptor. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the open file descriptor. +/// * `cmd` - The command for creating, lock and unlock in POSIX lock. +/// * `file_lock` - The information of file lock will be set. +/// * `file_lock_out` - The information of file lock will be returned. pub fn fcntl_flock( file: &File, cmd: i32, @@ -544,6 +800,14 @@ pub fn fcntl_flock( FUSE_OK } +/// Allocate the disk space with the open file descriptor. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the open file descriptor. +/// * `mode` - The mode determines the operation to be performed on the given range. +/// * `offset` - The offset in the file. +/// * `length` - The length that needs to be allocated. pub fn fallocate(file: &File, mode: u32, offset: u64, length: u64) -> i32 { errno::set_errno(errno::Errno(0)); @@ -557,6 +821,13 @@ pub fn fallocate(file: &File, mode: u32, offset: u64, length: u64) -> i32 { FUSE_OK } +/// Reposition the file offset of the open file descriptor. +/// +/// # Arguments +/// +/// * `file` - The file handler saves the open file descriptor. +/// * `offset` - The offset in the file used together with the whence. +/// * `whence` - The length that needs to be allocated. pub fn lseek(file: &File, offset: u64, whence: u32) -> (u64, i32) { errno::set_errno(errno::Errno(0)); let ret = unsafe { libc::lseek(file.as_raw_fd(), offset as i64, whence as i32) }; @@ -568,6 +839,12 @@ pub fn lseek(file: &File, offset: u64, whence: u32) -> (u64, i32) { (ret as u64, FUSE_OK) } +/// Set file resource limits. +/// +/// # Arguments +/// +/// * `rlim_cur` - The soft limit of the file resource. +/// * `rlim_max` - The hard limit of the file resource. pub fn set_rlimit(rlim_cur: u64, rlim_max: u64) -> i32 { let limit = libc::rlimit { rlim_cur, rlim_max }; -- Gitee From 9ff47a616c51d8b1afb968122dc48a2337cdb396 Mon Sep 17 00:00:00 2001 From: Guo Xinle Date: Thu, 1 Apr 2021 16:59:21 +0800 Subject: [PATCH 45/55] vhost_user_fs/testcase: add virtio_fs testcase Imitate guest OS fuse protocol to create FuseCreateIn request. the vhost_user_fs will create a file named virtiofs in temp directory. Signed-off-by: Guo Xinle --- vhost_user_fs/src/virtio_fs.rs | 160 +++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index 6b85b825f..0bc1ba95a 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -388,3 +388,163 @@ impl VhostUserReqHandler for VirtioFs { Ok(()) } } + +#[cfg(test)] +mod tests { + use super::*; + use address_space::{AddressSpace, GuestAddress, Region}; + use device_model::virtio::queue::QueueConfig; + use device_model::virtio::SplitVringDesc; + use fuse_msg::{FuseCreateIn, FuseInHeader, FuseOutHeader, FUSE_CREATE}; + use std::env::temp_dir; + use std::mem::size_of; + use std::path::Path; + use std::sync::Arc; + use util::byte_code::ByteCode; + use vmm_sys_util::eventfd::EventFd; + + const SYSTEM_SPACE_SIZE: u64 = (1024 * 1024) as u64; + const VIRTQ_DESC_F_NEXT: u16 = 0x1; + const VIRTQ_DESC_F_WRITE: u16 = 0x2; + + #[repr(C)] + #[derive(Debug, Default, Copy, Clone)] + struct CreateName { + name: [u8; 9], + } + + impl ByteCode for CreateName {} + + fn address_space_init() -> Arc { + let root = Region::init_container_region(1 << 36); + let sys_space = AddressSpace::new(root).unwrap(); + let host_mmap = Arc::new( + HostMemMapping::new(GuestAddress(0), SYSTEM_SPACE_SIZE, None, false, false).unwrap(), + ); + sys_space + .root() + .add_subregion( + Region::init_ram_region(host_mmap.clone()), + host_mmap.start_address().raw_value(), + ) + .unwrap(); + sys_space + } + + // Imitate fuse protocol to create FuseCreateIn request. the vhost_user_fs + // will create a file named virtiofs in temp directory. + #[test] + fn test_valid_request() { + let mem_space = address_space_init(); + let kick_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); + let call_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); + + let mut queue_config = QueueConfig::new(256); + queue_config.desc_table = GuestAddress(0); + queue_config.avail_ring = GuestAddress(4096); + queue_config.used_ring = GuestAddress(8192); + queue_config.ready = true; + queue_config.size = 256; + + let source_dir = temp_dir(); + let filesystem = Arc::new(Mutex::new( + FileSystem::new(source_dir.to_str().unwrap()).unwrap(), + )); + let driver_features = 1000; + + let fs_handler = FsIoHandler::new( + queue_config, + &kick_evt, + &call_evt, + &mem_space, + driver_features, + filesystem, + ); + + // Setting the first desc. + let head_len = size_of::() as u32; + let desc = SplitVringDesc { + addr: GuestAddress(0x100), + len: head_len, + flags: VIRTQ_DESC_F_NEXT, + next: 1, + }; + mem_space + .write_object::(&desc, GuestAddress(queue_config.desc_table.0)) + .unwrap(); + + // Expected to call function do_fuse_create + let in_header_len = (size_of::() + + size_of::() + + size_of::()) as u32; + let in_header = FuseInHeader { + len: in_header_len, + opcode: FUSE_CREATE, + unique: 42, + nodeid: 1, + uid: 0, + gid: 0, + pid: 13579, + padding: 0, + }; + mem_space + .write_object::(&in_header, GuestAddress(0x100)) + .unwrap(); + + // Setting the second desc + let create_len = (size_of::() + size_of::()) as u32; + let desc = SplitVringDesc { + addr: GuestAddress(0x200), + len: create_len, + flags: 0, + next: 2, + }; + mem_space + .write_object::( + &desc, + GuestAddress(queue_config.desc_table.0 + 16 as u64), + ) + .unwrap(); + + // Expect file mode: 0644 + let create_node = FuseCreateIn { + flags: 34881, + mode: 0o100666, + umask: 0o00022, + padding: 0, + }; + // file name: virtiofs + let file_name = CreateName { + name: [118, 105, 114, 116, 105, 111, 102, 115, 0], + }; + mem_space + .write_object::(&create_node, GuestAddress(0x200)) + .unwrap(); + let name_offset = size_of::() as u64; + mem_space + .write_object( + &file_name, + GuestAddress(0x200).checked_add(name_offset).unwrap(), + ) + .unwrap(); + + // Setting avail_ring ring id + mem_space + .write_object::(&0, GuestAddress(queue_config.avail_ring.0 + 4 as u64)) + .unwrap(); + // Setting avail_ring idx + mem_space + .write_object::(&1, GuestAddress(queue_config.avail_ring.0 + 2 as u64)) + .unwrap(); + + assert!(fs_handler.unwrap().process_queue().is_ok()); + // Get used_ring data + let idx = mem_space + .read_object::(GuestAddress(queue_config.used_ring.0 + 2 as u64)) + .unwrap(); + assert_eq!(idx, 1); + // Open virtiofs file success. + let file_path = source_dir.join("virtiofs"); + assert_eq!(Path::new(file_path.to_str().unwrap()).exists(), true); + } +} -- Gitee From 9118d42ac8f31f96e6a921d04975b103986663f1 Mon Sep 17 00:00:00 2001 From: Guo Xinle Date: Thu, 1 Apr 2021 17:00:16 +0800 Subject: [PATCH 46/55] vhost_user_fs/testcase: add testcases that fuse protocal is incomplete 1. Not provide a name field after FuseCreateIn structure. 2. The request only has FuseInHeader structure. Signed-off-by: Guo Xinle --- vhost_user_fs/src/virtio_fs.rs | 209 +++++++++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index 0bc1ba95a..3bfd2c6ca 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -547,4 +547,213 @@ mod tests { let file_path = source_dir.join("virtiofs"); assert_eq!(Path::new(file_path.to_str().unwrap()).exists(), true); } + + // While creating a file for fuse protocol, must provide a name field after FuseCreateIn + // structure. + #[test] + fn test_missing_name_field_request() { + let mem_space = address_space_init(); + let kick_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); + let call_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); + + let mut queue_config = QueueConfig::new(256); + queue_config.desc_table = GuestAddress(0); + queue_config.avail_ring = GuestAddress(4096); + queue_config.used_ring = GuestAddress(8192); + queue_config.ready = true; + queue_config.size = 256; + + let source_dir = temp_dir(); + let filesystem = Arc::new(Mutex::new( + FileSystem::new(source_dir.to_str().unwrap()).unwrap(), + )); + let driver_features = 1000; + + let fs_handler = FsIoHandler::new( + queue_config, + &kick_evt, + &call_evt, + &mem_space, + driver_features, + filesystem, + ); + + // Missing name field data. + let head_len = size_of::() as u32; + let desc = SplitVringDesc { + addr: GuestAddress(0x100), + len: head_len, + flags: VIRTQ_DESC_F_NEXT, + next: 1, + }; + mem_space + .write_object::(&desc, GuestAddress(queue_config.desc_table.0)) + .unwrap(); + + let in_header_len = (size_of::() + + size_of::() + + size_of::()) as u32; + let in_header = FuseInHeader { + len: in_header_len, + opcode: FUSE_CREATE, + unique: 42, + nodeid: 1, + uid: 0, + gid: 0, + pid: 13579, + padding: 0, + }; + mem_space + .write_object::(&in_header, GuestAddress(0x100)) + .unwrap(); + + // Setting the second desc + let create_len = (size_of::() + size_of::()) as u32; + let desc = SplitVringDesc { + addr: GuestAddress(0x200), + len: create_len, + flags: VIRTQ_DESC_F_NEXT, + next: 2, + }; + mem_space + .write_object::( + &desc, + GuestAddress(queue_config.desc_table.0 + 16 as u64), + ) + .unwrap(); + + let create_node = FuseCreateIn { + flags: 34881, + mode: 0o100666, + umask: 0o00022, + padding: 0, + }; + mem_space + .write_object::(&create_node, GuestAddress(0x200)) + .unwrap(); + + // Setting the third desc + let out_header_len = (size_of::()) as u32; + let desc = SplitVringDesc { + addr: GuestAddress(0x300), + len: out_header_len, + flags: VIRTQ_DESC_F_WRITE, + next: 3, + }; + mem_space + .write_object::( + &desc, + GuestAddress(queue_config.desc_table.0 + 32 as u64), + ) + .unwrap(); + + // Setting avail_ring ring id + mem_space + .write_object::(&0, GuestAddress(queue_config.avail_ring.0 + 4 as u64)) + .unwrap(); + // Setting avail_ring idx + mem_space + .write_object::(&3, GuestAddress(queue_config.avail_ring.0 + 2 as u64)) + .unwrap(); + + assert!(fs_handler.unwrap().process_queue().is_ok()); + // Confirm the FuseOutHeader error code + let fuse_out_header = mem_space + .read_object::(GuestAddress(0x300)) + .unwrap(); + assert_eq!(-fuse_out_header.error, libc::ENOENT); + } + + // Creating a file, the request only has FuseInHeader structure. + #[test] + fn test_only_inheader() { + let mem_space = address_space_init(); + let kick_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); + let call_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); + + let mut queue_config = QueueConfig::new(256); + queue_config.desc_table = GuestAddress(0); + queue_config.avail_ring = GuestAddress(4096); + queue_config.used_ring = GuestAddress(8192); + queue_config.ready = true; + queue_config.size = 256; + + let source_dir = temp_dir(); + let filesystem = Arc::new(Mutex::new( + FileSystem::new(source_dir.to_str().unwrap()).unwrap(), + )); + let driver_features = 1000; + + let fs_handler = FsIoHandler::new( + queue_config, + &kick_evt, + &call_evt, + &mem_space, + driver_features, + filesystem, + ); + + // Send only one reader desc. + let head_len = size_of::() as u32; + let desc = SplitVringDesc { + addr: GuestAddress(0x100), + len: head_len, + flags: VIRTQ_DESC_F_NEXT, + next: 1, + }; + mem_space + .write_object::(&desc, GuestAddress(queue_config.desc_table.0)) + .unwrap(); + + let in_header_len = size_of::() as u32; + let in_header = FuseInHeader { + len: in_header_len, + opcode: FUSE_CREATE, + unique: 42, + nodeid: 1, + uid: 0, + gid: 0, + pid: 13579, + padding: 0, + }; + mem_space + .write_object::(&in_header, GuestAddress(0x100)) + .unwrap(); + + // Setting the second desc + let out_header_len = (size_of::()) as u32; + let desc = SplitVringDesc { + addr: GuestAddress(0x300), + len: out_header_len, + flags: VIRTQ_DESC_F_WRITE, + next: 2, + }; + mem_space + .write_object::( + &desc, + GuestAddress(queue_config.desc_table.0 + 16 as u64), + ) + .unwrap(); + + // Setting avail_ring ring id + mem_space + .write_object::(&0, GuestAddress(queue_config.avail_ring.0 + 4 as u64)) + .unwrap(); + // Setting avail_ring idx + mem_space + .write_object::(&1, GuestAddress(queue_config.avail_ring.0 + 2 as u64)) + .unwrap(); + + assert!(fs_handler.unwrap().process_queue().is_ok()); + // Confirm the FuseOutHeader error code + let fuse_out_header = mem_space + .read_object::(GuestAddress(0x300)) + .unwrap(); + assert_eq!(-fuse_out_header.error, libc::EINVAL); + // Get used_ring data. + let idx = mem_space + .read_object::(GuestAddress(queue_config.used_ring.0 + 2 as u64)) + .unwrap(); + assert_eq!(idx, 1); + } } -- Gitee From 6cfd0ead85451a464215431f4beb98a7023df35d Mon Sep 17 00:00:00 2001 From: Guo Xinle Date: Thu, 1 Apr 2021 17:01:56 +0800 Subject: [PATCH 47/55] vhost_user_fs/testcases: add testcases that desc tables are not support. 1. Set the second desc table invalid flags. 2. Set unsupport opcode in desc table. Signed-off-by: Guo Xinle --- vhost_user_fs/src/virtio_fs.rs | 223 +++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index 3bfd2c6ca..8ca5e4745 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -756,4 +756,227 @@ mod tests { .unwrap(); assert_eq!(idx, 1); } + + // There are tow desc tables, but the second flag is VIRTQ_DESC_F_WRITE. For creating a + // file, two desc tables are required. + #[test] + fn test_invalid_desc_flag() { + let mem_space = address_space_init(); + let kick_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); + let call_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); + + let mut queue_config = QueueConfig::new(256); + queue_config.desc_table = GuestAddress(0); + queue_config.avail_ring = GuestAddress(4096); + queue_config.used_ring = GuestAddress(8192); + queue_config.ready = true; + queue_config.size = 256; + + let source_dir = temp_dir(); + let filesystem = Arc::new(Mutex::new( + FileSystem::new(source_dir.to_str().unwrap()).unwrap(), + )); + let driver_features = 1000; + + let fs_handler = FsIoHandler::new( + queue_config, + &kick_evt, + &call_evt, + &mem_space, + driver_features, + filesystem, + ); + + // The second SplitVringDesc flag is invalid. + let head_len = size_of::() as u32; + let desc = SplitVringDesc { + addr: GuestAddress(0x100), + len: head_len, + flags: VIRTQ_DESC_F_NEXT, + next: 1, + }; + mem_space + .write_object::(&desc, GuestAddress(queue_config.desc_table.0)) + .unwrap(); + + let in_header_len = (size_of::() + + size_of::() + + size_of::()) as u32; + let in_header = FuseInHeader { + len: in_header_len, + opcode: FUSE_CREATE, + unique: 42, + nodeid: 1, + uid: 0, + gid: 0, + pid: 13579, + padding: 0, + }; + mem_space + .write_object::(&in_header, GuestAddress(0x100)) + .unwrap(); + + // Setting the second desc. + let create_len = (size_of::() + size_of::()) as u32; + // Invalid flags in desc table. + let desc = SplitVringDesc { + addr: GuestAddress(0x200), + len: create_len, + flags: VIRTQ_DESC_F_WRITE, + next: 2, + }; + mem_space + .write_object::( + &desc, + GuestAddress(queue_config.desc_table.0 + 16 as u64), + ) + .unwrap(); + + let create_node = FuseCreateIn { + flags: 34881, + mode: 0o100666, + umask: 0o00022, + padding: 0, + }; + + // file name: virtiofa + let file_name = CreateName { + name: [118, 105, 114, 116, 105, 111, 102, 97, 0], + }; + mem_space + .write_object::(&create_node, GuestAddress(0x200)) + .unwrap(); + let name_offset = size_of::() as u64; + mem_space + .write_object( + &file_name, + GuestAddress(0x200).checked_add(name_offset).unwrap(), + ) + .unwrap(); + + // Setting avail_ring ring id + mem_space + .write_object::(&0, GuestAddress(queue_config.avail_ring.0 + 4 as u64)) + .unwrap(); + // Setting avail_ring idx + mem_space + .write_object::(&1, GuestAddress(queue_config.avail_ring.0 + 2 as u64)) + .unwrap(); + + assert!(fs_handler.unwrap().process_queue().is_ok()); + // Open the file failed. + let file_path = source_dir.join("virtiofa"); + assert_eq!(Path::new(file_path.to_str().unwrap()).exists(), false); + } + + // Construct an unsupported opcode. + #[test] + fn test_unsupported_opcode() { + let mem_space = address_space_init(); + let kick_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); + let call_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); + + let mut queue_config = QueueConfig::new(256); + queue_config.desc_table = GuestAddress(0); + queue_config.avail_ring = GuestAddress(4096); + queue_config.used_ring = GuestAddress(8192); + queue_config.ready = true; + queue_config.size = 256; + + let source_dir = temp_dir(); + let filesystem = Arc::new(Mutex::new( + FileSystem::new(source_dir.to_str().unwrap()).unwrap(), + )); + let driver_features = 1000; + + let fs_handler = FsIoHandler::new( + queue_config, + &kick_evt, + &call_evt, + &mem_space, + driver_features, + filesystem, + ); + + // Unsupported opcode + let head_len = size_of::() as u32; + let desc = SplitVringDesc { + addr: GuestAddress(0x100), + len: head_len, + flags: VIRTQ_DESC_F_NEXT, + next: 1, + }; + mem_space + .write_object::(&desc, GuestAddress(queue_config.desc_table.0)) + .unwrap(); + + let in_header_len = (size_of::() + + size_of::() + + size_of::()) as u32; + // invalid operation code: 64 + let in_header = FuseInHeader { + len: in_header_len, + opcode: 64, + unique: 42, + nodeid: 1, + uid: 0, + gid: 0, + pid: 13579, + padding: 0, + }; + mem_space + .write_object::(&in_header, GuestAddress(0x100)) + .unwrap(); + + // Setting the second desc + let create_len = (size_of::() + size_of::()) as u32; + let desc = SplitVringDesc { + addr: GuestAddress(0x200), + len: create_len, + flags: 0, + next: 2, + }; + mem_space + .write_object::( + &desc, + GuestAddress(queue_config.desc_table.0 + 16 as u64), + ) + .unwrap(); + + let create_node = FuseCreateIn { + flags: 34881, + mode: 0o100666, + umask: 0o00022, + padding: 0, + }; + + // file name: virtiofe + let file_name = CreateName { + name: [118, 105, 114, 116, 105, 111, 102, 101, 0], + }; + mem_space + .write_object::(&create_node, GuestAddress(0x200)) + .unwrap(); + let name_offset = size_of::() as u64; + mem_space + .write_object( + &file_name, + GuestAddress(0x200).checked_add(name_offset).unwrap(), + ) + .unwrap(); + + // Setting avail_ring ring id + mem_space + .write_object::(&0, GuestAddress(queue_config.avail_ring.0 + 4 as u64)) + .unwrap(); + // Setting avail_ring idx + mem_space + .write_object::(&1, GuestAddress(queue_config.avail_ring.0 + 2 as u64)) + .unwrap(); + + assert!(fs_handler.unwrap().process_queue().is_ok()); + // Open the file failed. + let file_path = source_dir.join("virtiofe"); + assert_eq!(Path::new(file_path.to_str().unwrap()).exists(), false); + } } -- Gitee From 7dadc78d4fc9b8724a51eafe124f8aab0704ca72 Mon Sep 17 00:00:00 2001 From: Guo Xinle Date: Thu, 1 Apr 2021 17:02:56 +0800 Subject: [PATCH 48/55] vhost_user_fs/testcase: add testcases that desc table length or address is error The desc table length parameter does not match FuseInHeader structure. It will be out of mem_space range. Signed-off-by: Guo Xinle --- vhost_user_fs/src/virtio_fs.rs | 235 +++++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index 8ca5e4745..35182d802 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -979,4 +979,239 @@ mod tests { let file_path = source_dir.join("virtiofe"); assert_eq!(Path::new(file_path.to_str().unwrap()).exists(), false); } + + // The desc table length parameter must match FuseInHeader structure. And it will not work if + // the address of FuseInHeader is out of bounds. + #[test] + fn test_wrong_param_for_desc_01() { + let mem_space = address_space_init(); + let kick_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); + let call_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); + + let mut queue_config = QueueConfig::new(256); + queue_config.desc_table = GuestAddress(0); + queue_config.avail_ring = GuestAddress(4096); + queue_config.used_ring = GuestAddress(8192); + queue_config.ready = true; + queue_config.size = 256; + + let source_dir = temp_dir(); + let filesystem = Arc::new(Mutex::new( + FileSystem::new(source_dir.to_str().unwrap()).unwrap(), + )); + let driver_features = 1000; + + let fs_handler = FsIoHandler::new( + queue_config, + &kick_evt, + &call_evt, + &mem_space, + driver_features, + filesystem.clone(), + ); + + // Setting the first desc. + let head_len = size_of::() as u32; + let desc = SplitVringDesc { + addr: GuestAddress(0x100), + len: head_len, + flags: VIRTQ_DESC_F_NEXT, + next: 1, + }; + mem_space + .write_object::(&desc, GuestAddress(queue_config.desc_table.0)) + .unwrap(); + + let in_header_len = (size_of::() + + size_of::() + + size_of::()) as u32; + let in_header = FuseInHeader { + len: in_header_len, + opcode: FUSE_CREATE, + unique: 42, + nodeid: 1, + uid: 0, + gid: 0, + pid: 13579, + padding: 0, + }; + mem_space + .write_object::(&in_header, GuestAddress(0x100)) + .unwrap(); + + // The value of addr and len are out of mem_space range, set invalid address in desc table. + let create_len = (size_of::() + size_of::()) as u32; + let desc = SplitVringDesc { + addr: GuestAddress(0x000f_ffe6), + len: create_len, + flags: VIRTQ_DESC_F_NEXT, + next: 2, + }; + mem_space + .write_object::( + &desc, + GuestAddress(queue_config.desc_table.0 + 16 as u64), + ) + .unwrap(); + + let create_node = FuseCreateIn { + flags: 34881, + mode: 0o100666, + umask: 0o00022, + padding: 0, + }; + + let file_name = CreateName { + name: [118, 105, 114, 116, 105, 111, 102, 115, 0], + }; + mem_space + .write_object::(&create_node, GuestAddress(0x000f_ffe6)) + .unwrap(); + let name_offset = size_of::() as u64; + mem_space + .write_object( + &file_name, + GuestAddress(0x200).checked_add(name_offset).unwrap(), + ) + .unwrap(); + + // Setting the third desc + let out_header_len = (size_of::()) as u32; + let desc = SplitVringDesc { + addr: GuestAddress(0x300), + len: out_header_len, + flags: VIRTQ_DESC_F_WRITE, + next: 3, + }; + mem_space + .write_object::( + &desc, + GuestAddress(queue_config.desc_table.0 + 32 as u64), + ) + .unwrap(); + + // Setting avail_ring ring id + mem_space + .write_object::(&0, GuestAddress(queue_config.avail_ring.0 + 4 as u64)) + .unwrap(); + // Setting avail_ring idx + mem_space + .write_object::(&1, GuestAddress(queue_config.avail_ring.0 + 2 as u64)) + .unwrap(); + + assert!(fs_handler.unwrap().process_queue().is_ok()); + // Confirm the FuseOutHeader error code + let fuse_out_header = mem_space + .read_object::(GuestAddress(0x300)) + .unwrap(); + assert_eq!(-fuse_out_header.error, libc::ENOENT); + } + + #[test] + fn test_wrong_param_for_desc_02() { + let mem_space = address_space_init(); + let kick_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); + let call_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); + + let mut queue_config = QueueConfig::new(256); + queue_config.desc_table = GuestAddress(0); + queue_config.avail_ring = GuestAddress(4096); + queue_config.used_ring = GuestAddress(8192); + queue_config.ready = true; + queue_config.size = 256; + + let source_dir = temp_dir(); + let filesystem = Arc::new(Mutex::new( + FileSystem::new(source_dir.to_str().unwrap()).unwrap(), + )); + let driver_features = 1000; + + let fs_handler = FsIoHandler::new( + queue_config, + &kick_evt, + &call_evt, + &mem_space, + driver_features, + filesystem.clone(), + ); + + // SplitVringDesc len not match FuseInHeader, len is less than sizeof:: + let desc = SplitVringDesc { + addr: GuestAddress(0x100), + len: 20, + flags: VIRTQ_DESC_F_NEXT, + next: 1, + }; + mem_space + .write_object::(&desc, GuestAddress(queue_config.desc_table.0)) + .unwrap(); + + let in_header_len = (size_of::() + + size_of::() + + size_of::()) as u32; + let in_header = FuseInHeader { + len: in_header_len, + opcode: FUSE_CREATE, + unique: 42, + nodeid: 1, + uid: 0, + gid: 0, + pid: 13579, + padding: 0, + }; + mem_space + .write_object::(&in_header, GuestAddress(0x100)) + .unwrap(); + + // Setting the second desc + let create_len = (size_of::() + size_of::()) as u32; + let desc = SplitVringDesc { + addr: GuestAddress(0x200), + len: create_len, + flags: 0, + next: 2, + }; + mem_space + .write_object::( + &desc, + GuestAddress(queue_config.desc_table.0 + 16 as u64), + ) + .unwrap(); + + let create_node = FuseCreateIn { + flags: 34881, + mode: 0o100666, + umask: 0o00022, + padding: 0, + }; + + // file name: virtiofk + let file_name = CreateName { + name: [118, 105, 114, 116, 105, 111, 102, 107, 0], + }; + mem_space + .write_object::(&create_node, GuestAddress(0x200)) + .unwrap(); + let name_offset = size_of::() as u64; + mem_space + .write_object( + &file_name, + GuestAddress(0x200).checked_add(name_offset).unwrap(), + ) + .unwrap(); + + // Setting avail_ring ring id + mem_space + .write_object::(&0, GuestAddress(queue_config.avail_ring.0 + 4 as u64)) + .unwrap(); + // Setting avail_ring idx + mem_space + .write_object::(&1, GuestAddress(queue_config.avail_ring.0 + 2 as u64)) + .unwrap(); + + assert!(fs_handler.unwrap().process_queue().is_ok()); + // Open the file failed. + let file_path = source_dir.join("virtiofk"); + assert_eq!(Path::new(file_path.to_str().unwrap()).exists(), false); + } } -- Gitee From 1509afddb26718ca4495f692c550881dc384480b Mon Sep 17 00:00:00 2001 From: Guo Xinle Date: Thu, 1 Apr 2021 17:03:44 +0800 Subject: [PATCH 49/55] vhost_user_fs/testcase: add testcase that test invalid path param If input invalid source path or name path, it will return Err or error code. Signed-off-by: Guo Xinle --- vhost_user_fs/src/virtio_fs.rs | 143 +++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index 35182d802..61631e114 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -1214,4 +1214,147 @@ mod tests { let file_path = source_dir.join("virtiofk"); assert_eq!(Path::new(file_path.to_str().unwrap()).exists(), false); } + + // Input invalid source path and name path, it will return Err or error code. + #[test] + fn test_invalid_path() { + let mem_space = address_space_init(); + let kick_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); + let call_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); + + let mut queue_config = QueueConfig::new(256); + queue_config.desc_table = GuestAddress(0); + queue_config.avail_ring = GuestAddress(4096); + queue_config.used_ring = GuestAddress(8192); + queue_config.ready = true; + queue_config.size = 256; + + { + // Invalid path: `/aaa/` path is not exist. + let source_dir = "/aaa/"; + assert!(FileSystem::new(source_dir).is_err()); + // Access parent path is allowed. + let source_dir = temp_dir(); + assert!(FileSystem::new(source_dir.to_str().unwrap()).is_ok()); + let source_dir = "HDMriseqSfQLIGEUHnoiexNLyWALwnatXspd \ + YWYYzeQZrhVqvlHpgkMekdolTTUTXqIsWfPg \ + acdJdsUhfocpLQatpAAtUKoogwcaCByegcWE \ + BpWzNpbWLFCUcvdNBsyiBJ/"; + assert!(FileSystem::new(source_dir).is_err()); + } + + let source_dir = temp_dir(); + let filesystem = Arc::new(Mutex::new( + FileSystem::new(source_dir.to_str().unwrap()).unwrap(), + )); + let driver_features = 1000; + + let fs_handler = FsIoHandler::new( + queue_config, + &kick_evt, + &call_evt, + &mem_space, + driver_features, + filesystem, + ); + + // Setting the first desc. + let head_len = size_of::() as u32; + let desc = SplitVringDesc { + addr: GuestAddress(0x100), + len: head_len, + flags: VIRTQ_DESC_F_NEXT, + next: 1, + }; + mem_space + .write_object::(&desc, GuestAddress(queue_config.desc_table.0)) + .unwrap(); + + let in_header_len = (size_of::() + + size_of::() + + size_of::()) as u32; + let in_header = FuseInHeader { + len: in_header_len, + opcode: FUSE_CREATE, + unique: 42, + nodeid: 1, + uid: 0, + gid: 0, + pid: 13579, + padding: 0, + }; + mem_space + .write_object::(&in_header, GuestAddress(0x100)) + .unwrap(); + + // Setting the second desc + let create_len = (size_of::() + size_of::()) as u32; + let desc = SplitVringDesc { + addr: GuestAddress(0x200), + len: create_len, + flags: VIRTQ_DESC_F_NEXT, + next: 2, + }; + mem_space + .write_object::( + &desc, + GuestAddress(queue_config.desc_table.0 + 16 as u64), + ) + .unwrap(); + + let create_node = FuseCreateIn { + flags: 34881, + mode: 0o100666, + umask: 0o00022, + padding: 0, + }; + // invalid file name: .`\0`rtiofm + let file_name = CreateName { + name: [46, 0, 114, 116, 105, 111, 102, 109, 0], + }; + mem_space + .write_object::(&create_node, GuestAddress(0x200)) + .unwrap(); + let name_offset = size_of::() as u64; + mem_space + .write_object( + &file_name, + GuestAddress(0x200).checked_add(name_offset).unwrap(), + ) + .unwrap(); + + // Setting the third desc + let out_header_len = (size_of::()) as u32; + let desc = SplitVringDesc { + addr: GuestAddress(0x300), + len: out_header_len, + flags: VIRTQ_DESC_F_WRITE, + next: 3, + }; + mem_space + .write_object::( + &desc, + GuestAddress(queue_config.desc_table.0 + 32 as u64), + ) + .unwrap(); + + // Setting avail_ring ring id + mem_space + .write_object::(&0, GuestAddress(queue_config.avail_ring.0 + 4 as u64)) + .unwrap(); + // Setting avail_ring idx + mem_space + .write_object::(&1, GuestAddress(queue_config.avail_ring.0 + 2 as u64)) + .unwrap(); + + assert!(fs_handler.unwrap().process_queue().is_ok()); + // Confirm the FuseOutHeader error code + let fuse_out_header = mem_space + .read_object::(GuestAddress(0x300)) + .unwrap(); + assert_eq!(-fuse_out_header.error, libc::EINVAL); + // Open the file failed. + let file_path = source_dir.join("virtiofm"); + assert_eq!(Path::new(file_path.to_str().unwrap()).exists(), false); + } } -- Gitee From 31f4f37ac9a9c7fe0c9d824f0c76835f5ac94748 Mon Sep 17 00:00:00 2001 From: Guo Xinle Date: Thu, 1 Apr 2021 17:09:26 +0800 Subject: [PATCH 50/55] fuse_req/testcase: add testcase that lookup file success Testcase call FUSE_LOOKUP operation, if the file metadata is written to mem_space, it will return the written len. And fuse out header error code equals to zero. Signed-off-by: Guo Xinle --- vhost_user_fs/src/fuse_req.rs | 115 ++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs index fae8b18a0..73bcb502d 100644 --- a/vhost_user_fs/src/fuse_req.rs +++ b/vhost_user_fs/src/fuse_req.rs @@ -172,3 +172,118 @@ impl FuseReq { (self.desc_index, written_len) } } + +#[cfg(test)] +mod tests { + pub use super::super::*; + pub use super::*; + use address_space::{AddressSpace, GuestAddress, HostMemMapping, Region}; + use device_model::virtio::queue::*; + use std::env::temp_dir; + use std::fs::File; + use std::mem::size_of; + use util::byte_code::ByteCode; + + const SYSTEM_SPACE_SIZE: u64 = (1024 * 1024) as u64; + + #[repr(C)] + #[derive(Debug, Default, Copy, Clone)] + pub struct LookUp { + pub name: [u8; 9], + } + + impl ByteCode for LookUp {} + + fn address_space_init() -> Arc { + let root = Region::init_container_region(1 << 36); + let sys_space = AddressSpace::new(root).unwrap(); + let host_mmap = Arc::new( + HostMemMapping::new(GuestAddress(0), SYSTEM_SPACE_SIZE, None, false, false).unwrap(), + ); + sys_space + .root() + .add_subregion( + Region::init_ram_region(host_mmap.clone()), + host_mmap.start_address().raw_value(), + ) + .unwrap(); + sys_space + } + + // Testcase call FUSE_LOOKUP operation, if the file metadata is written to mem_space, it will + // return the written len. And fuse out header error code equals to zero. + #[test] + fn test_lookup_file_success() { + // create a file named vhostreq + let source_dir = temp_dir(); + let file_path = source_dir.join("vhostreq"); + let _file = File::create(&file_path).unwrap(); + + let mem_space = address_space_init(); + let filesystem = Arc::new(Mutex::new( + FileSystem::new(source_dir.to_str().unwrap()).unwrap(), + )); + + // reader buffer: FuseInHeader + name + let mut elem = Element::new(0); + elem.desc_num = 3; + let in_header_len = (size_of::() + size_of::()) as u32; + let iovec = ElemIovec { + addr: GuestAddress(0x00), + len: in_header_len, + }; + elem.out_iovec.push(iovec); + + // write buffers: FuseOutHeader buffer, FuseIovec.body buffer + let out_header_len = size_of::() as u32; + let iovec = ElemIovec { + addr: GuestAddress(0x100), + len: out_header_len, + }; + elem.in_iovec.push(iovec); + let iovec = ElemIovec { + addr: GuestAddress(0x200), + len: 128, + }; + elem.in_iovec.push(iovec); + let mut req = FuseReq::new(&elem); + + let head_len = size_of::() as u32; + let in_header = FuseInHeader { + len: head_len, + opcode: FUSE_LOOKUP, + unique: 42, + nodeid: 1, + uid: 0, + gid: 0, + pid: 13579, + padding: 0, + }; + mem_space + .write_object::(&in_header, GuestAddress(0x00)) + .unwrap(); + + // file name: vhostreq + let file_name = LookUp { + name: [118, 104, 111, 115, 116, 114, 101, 113, 0], + }; + let name_offset = std::mem::size_of::() as u64; + mem_space + .write_object( + &file_name, + GuestAddress(0x00).checked_add(name_offset).unwrap(), + ) + .unwrap(); + + let (_, written_len) = req.execute(&mem_space, filesystem.clone()); + // Confirm the FuseEntryOut data is written to mem_space + let fuse_out_header = mem_space + .read_object::(GuestAddress(0x100)) + .unwrap(); + let reply_msg_len = (size_of::() + size_of::()) as u32; + assert_eq!(fuse_out_header.error, 0); + assert_eq!(fuse_out_header.len, reply_msg_len); + assert_eq!(fuse_out_header.unique, in_header.unique); + assert_eq!(written_len, reply_msg_len); + } +} -- Gitee From bab98954bb1ff171d7f5ba2ad4314eacd9150f3d Mon Sep 17 00:00:00 2001 From: Guo Xinle Date: Thu, 1 Apr 2021 17:10:48 +0800 Subject: [PATCH 51/55] fuse_req/testcase: add testcases that lookup reader buffer contains invalid param If lookup ops reader buffer address is out of bounds or len is invalid, the fuse_out_header error code will be set to EINVAL. Signed-off-by: Guo Xinle --- vhost_user_fs/src/fuse_req.rs | 149 ++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) diff --git a/vhost_user_fs/src/fuse_req.rs b/vhost_user_fs/src/fuse_req.rs index 73bcb502d..10bdd2f49 100644 --- a/vhost_user_fs/src/fuse_req.rs +++ b/vhost_user_fs/src/fuse_req.rs @@ -286,4 +286,153 @@ mod tests { assert_eq!(fuse_out_header.unique, in_header.unique); assert_eq!(written_len, reply_msg_len); } + + // If reader buffer address is out of bounds, the fuse_out_header error code will be set to EINVAL + #[test] + fn test_lookup_addr_out_of_bounds() { + // create a file named vhostrea + let source_dir = temp_dir(); + let file_path = source_dir.join("vhostrea"); + let _file = File::create(&file_path).unwrap(); + + let mem_space = address_space_init(); + let filesystem = Arc::new(Mutex::new( + FileSystem::new(source_dir.to_str().unwrap()).unwrap(), + )); + + // Address plus len is out of bounds + let mut elem = Element::new(0); + elem.desc_num = 3; + let in_header_len = (size_of::() + size_of::()) as u32; + let iovec = ElemIovec { + addr: GuestAddress(0x000f_ffd0), // invalid + len: in_header_len, + }; + elem.out_iovec.push(iovec); + + let out_header_len = size_of::() as u32; + let iovec = ElemIovec { + addr: GuestAddress(0x100), + len: out_header_len, + }; + elem.in_iovec.push(iovec); + let iovec = ElemIovec { + addr: GuestAddress(0x200), + len: 128, + }; + elem.in_iovec.push(iovec); + let mut req = FuseReq::new(&elem); + + let head_len = size_of::() as u32; + let in_header = FuseInHeader { + len: head_len, + opcode: FUSE_LOOKUP, + unique: 42, + nodeid: 1, + uid: 0, + gid: 0, + pid: 13579, + padding: 0, + }; + mem_space + .write_object::(&in_header, GuestAddress(0x000f_ffd0)) + .unwrap(); + + // file name: vhostrea + let file_name = LookUp { + name: [118, 104, 111, 115, 116, 114, 101, 97, 0], + }; + let name_offset = std::mem::size_of::() as u64; + mem_space + .write_object( + &file_name, + GuestAddress(0x00).checked_add(name_offset).unwrap(), + ) + .unwrap(); + + let (_, written_len) = req.execute(&mem_space, filesystem.clone()); + // Confirm the FuseEntryOut data is written to mem_space + let fuse_out_header = mem_space + .read_object::(GuestAddress(0x100)) + .unwrap(); + let reply_msg_len = size_of::() as u32; + assert_eq!(-fuse_out_header.error, libc::EINVAL); + assert_eq!(fuse_out_header.unique, in_header.unique); + assert_eq!(fuse_out_header.len, reply_msg_len); + assert_eq!(written_len, reply_msg_len); + } + + // If reader buffer len is invalid, the fuse_out_header error code will be set to EINVAL + #[test] + fn test_lookup_invalid_elem_length() { + // create a file named vhostreb + let source_dir = temp_dir(); + let file_path = source_dir.join("vhostreb"); + let _file = File::create(&file_path).unwrap(); + + let mem_space = address_space_init(); + let filesystem = Arc::new(Mutex::new( + FileSystem::new(source_dir.to_str().unwrap()).unwrap(), + )); + + // ElemIovec len is less than required. + let mut elem = Element::new(0); + elem.desc_num = 3; + let iovec = ElemIovec { + addr: GuestAddress(0x00), + len: 45, // Invalid length + }; + elem.out_iovec.push(iovec); + + let out_header_len = size_of::() as u32; + let iovec = ElemIovec { + addr: GuestAddress(0x100), + len: out_header_len, + }; + elem.in_iovec.push(iovec); + let iovec = ElemIovec { + addr: GuestAddress(0x200), + len: 128, + }; + elem.in_iovec.push(iovec); + let mut req = FuseReq::new(&elem); + + let head_len = size_of::() as u32; + let in_header = FuseInHeader { + len: head_len, + opcode: FUSE_LOOKUP, + unique: 42, + nodeid: 1, + uid: 0, + gid: 0, + pid: 13579, + padding: 0, + }; + mem_space + .write_object::(&in_header, GuestAddress(0x00)) + .unwrap(); + + // file name: vhostreb + let file_name = LookUp { + name: [118, 104, 111, 115, 116, 114, 101, 98, 0], + }; + let name_offset = std::mem::size_of::() as u64; + mem_space + .write_object( + &file_name, + GuestAddress(0x00).checked_add(name_offset).unwrap(), + ) + .unwrap(); + + let (_, written_len) = req.execute(&mem_space, filesystem.clone()); + // Confirm the FuseEntryOut data is written to mem_space + let fuse_out_header = mem_space + .read_object::(GuestAddress(0x100)) + .unwrap(); + let reply_msg_len = size_of::() as u32; + assert_eq!(-fuse_out_header.error, libc::EINVAL); + assert_eq!(fuse_out_header.unique, in_header.unique); + assert_eq!(fuse_out_header.len, reply_msg_len); + assert_eq!(written_len, reply_msg_len); + } } -- Gitee From c21f534b77345a3baff4570b93171ce7343053ad Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Tue, 6 Apr 2021 10:43:35 +0800 Subject: [PATCH 52/55] device_model: create the function of updating event which is stripped from the new function of VhostUserClient Signed-off-by: Fei Xu --- device_model/src/virtio/vhost/user/fs.rs | 4 ++++ .../src/virtio/vhost/user/vhost_user_client.rs | 15 +++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/device_model/src/virtio/vhost/user/fs.rs b/device_model/src/virtio/vhost/user/fs.rs index 5f1d73004..6d697fabe 100644 --- a/device_model/src/virtio/vhost/user/fs.rs +++ b/device_model/src/virtio/vhost/user/fs.rs @@ -131,6 +131,10 @@ impl VirtioDevice for Fs { .chain_err(|| { "Failed to create the client which communicates with the server for virtio fs" })?; + client + .add_event_notifier() + .chain_err(|| "Failed to add event nitifier for virtio fs")?; + self.avail_features = client .get_features() .chain_err(|| "Failed to get features for virtio fs")?; diff --git a/device_model/src/virtio/vhost/user/vhost_user_client.rs b/device_model/src/virtio/vhost/user/vhost_user_client.rs index fbd598fa9..423072be7 100644 --- a/device_model/src/virtio/vhost/user/vhost_user_client.rs +++ b/device_model/src/virtio/vhost/user/vhost_user_client.rs @@ -228,19 +228,18 @@ impl VhostUserClient { .register_listener(Box::new(mem_info.clone())) .chain_err(|| "Failed to register listener for memory for vhost user client")?; - let client_internal = Arc::new(Mutex::new(ClientInternal::new(sock, max_queue_num))); + let client = Arc::new(Mutex::new(ClientInternal::new(sock, max_queue_num))); + Ok(VhostUserClient { client, mem_info }) + } + + pub fn add_event_notifier(&self) -> Result<()> { EventLoop::update_event( - EventNotifierHelper::internal_notifiers(client_internal.clone()), + EventNotifierHelper::internal_notifiers(self.client.clone()), None, ) .chain_err(|| "Failed to update event for client sock")?; - let client = VhostUserClient { - client: client_internal, - mem_info, - }; - - Ok(client) + Ok(()) } } -- Gitee From 029ceff7a19c4c8f43b9d99e834b4c2cde7b62b9 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Tue, 6 Apr 2021 14:35:52 +0800 Subject: [PATCH 53/55] vhost_user_fs: add testcases of SetOwner, SetFeatures and GetFeatures for vhost_user_server.rs Signed-off-by: Fei Xu --- machine_manager/src/config/fs.rs | 2 +- vhost_user_fs/src/vhost_user_server.rs | 207 +++++++++++++++++++++++++ 2 files changed, 208 insertions(+), 1 deletion(-) diff --git a/machine_manager/src/config/fs.rs b/machine_manager/src/config/fs.rs index 18f58ae5c..a769ae3eb 100644 --- a/machine_manager/src/config/fs.rs +++ b/machine_manager/src/config/fs.rs @@ -335,7 +335,7 @@ mod tests { "#; let value = serde_json::from_str(json).unwrap(); let configs = FsConfig::from_value(&value); - assert!(configs.is_some()); + assert!(configs.is_ok()); let fs_configs = configs.unwrap(); assert_eq!(fs_configs[0].tag, "test"); assert_eq!(fs_configs[0].sock, "fs.sock"); diff --git a/vhost_user_fs/src/vhost_user_server.rs b/vhost_user_fs/src/vhost_user_server.rs index 22936205a..3fd3c06ed 100644 --- a/vhost_user_fs/src/vhost_user_server.rs +++ b/vhost_user_fs/src/vhost_user_server.rs @@ -469,3 +469,210 @@ impl VhostUserServerHandler { Ok(()) } } + +#[cfg(test)] +mod tests { + extern crate address_space; + extern crate device_model; + + use std::env::temp_dir; + use std::os::unix::io::{FromRawFd, RawFd}; + use std::sync::{Arc, Mutex}; + use std::{thread, time}; + + use vmm_sys_util::eventfd::EventFd; + + use address_space::{create_host_mmaps, AddressSpace, GuestAddress, Region}; + use device_model::virtio::queue::QueueConfig; + use device_model::virtio::vhost::user::vhost_user_client::VhostUserClient; + use device_model::virtio::vhost::VhostOps; + use machine_manager::config::MachineMemConfig; + use vhost_user_server::VhostUserReqHandler; + + use super::super::errors::Result; + use super::*; + + const SYSTEM_SPACE_SIZE: u64 = (1024 * 1024) as u64; + const QUEUE_SIZE: u16 = 256; + const LAST_AVAIL_IDX: u16 = 100; + fn address_space_init() -> Arc { + let root = Region::init_container_region(1 << 36); + let sys_space = AddressSpace::new(root).unwrap(); + let addr_ranges = [(0x0, SYSTEM_SPACE_SIZE)]; + let mem_config = MachineMemConfig { + mem_size: SYSTEM_SPACE_SIZE, + mem_path: None, + dump_guest_core: false, + mem_share: true, + }; + let host_mmap_vec = create_host_mmaps(&addr_ranges, &mem_config).unwrap(); + for host_mmap in host_mmap_vec { + sys_space + .root() + .add_subregion( + Region::init_ram_region(host_mmap.clone()), + host_mmap.start_address().raw_value(), + ) + .unwrap(); + } + sys_space + } + + struct VhostUserBackend { + owner: bool, + features: u64, + } + + impl VhostUserBackend { + fn new() -> Self { + VhostUserBackend { + owner: false, + features: 0, + } + } + } + + impl VhostUserReqHandler for VhostUserBackend { + fn set_owner(&mut self) -> Result<()> { + println!("set owner"); + self.owner = true; + Ok(()) + } + + fn get_features(&self) -> Result { + println!("get features: {}", self.features); + Ok(self.features) + } + + fn set_features(&mut self, features: u64) -> Result<()> { + println!("set features: {}", self.features); + self.features = features; + Ok(()) + } + + fn set_mem_table( + &mut self, + regions: &[VhostUserMemoryRegion], + _fds: &[RawFd], + ) -> Result<()> { + for region in regions { + println!("guest_phys_addr 0x{:X}, memory_size: {}, userspace_addr: 0x{:X}, mmap_offset: {}", + region.guest_phys_addr, region.memory_size, + region.userspace_addr, region.mmap_offset, + ); + assert_eq!(region.guest_phys_addr, 0x0); + assert_eq!(region.memory_size, SYSTEM_SPACE_SIZE); + assert_eq!(region.mmap_offset, 0); + } + + Ok(()) + } + + fn set_vring_num(&mut self, queue_index: usize, num: u16) -> Result<()> { + println!("set vring num, index: {}, num: {}", queue_index, num); + assert_eq!(queue_index, 2); + assert_eq!(num, QUEUE_SIZE); + Ok(()) + } + + fn set_vring_addr( + &mut self, + queue_index: usize, + flags: u32, + desc_table: u64, + used_ring: u64, + avail_ring: u64, + log: u64, + ) -> Result<()> { + println!( + "queue_index: {}, table: 0x{:x}, avail: 0x{:x}, used: 0x{:x}, flags: {}, log: {}", + queue_index, desc_table, avail_ring, used_ring, flags, log + ); + assert_eq!(queue_index, 4); + assert_eq!(desc_table, 0x1); + assert_eq!(avail_ring, 0x2); + assert_eq!(used_ring, 0x3); + assert_eq!(log, 0); + assert_eq!(flags, 0x1); + Ok(()) + } + + fn set_vring_base(&mut self, queue_index: usize, num: u16) -> Result<()> { + println!("queue_index: {}, num: {}", queue_index, num); + assert_eq!(queue_index, 1); + assert_eq!(num, LAST_AVAIL_IDX); + Ok(()) + } + + fn set_vring_call(&mut self, queue_index: usize, fd: RawFd) -> Result<()> { + println!("queue_index: {}, call_fd: {}", queue_index, fd); + assert_eq!(queue_index, 6); + let file = unsafe { EventFd::from_raw_fd(fd) }; + file.write(200).expect("Failed to write call eventfd"); + Ok(()) + } + + fn set_vring_kick(&mut self, queue_index: usize, fd: RawFd) -> Result<()> { + println!("queue_index: {}, kick_fd: {}", queue_index, fd); + assert_eq!(queue_index, 7); + let file = unsafe { EventFd::from_raw_fd(fd) }; + file.write(300).expect("Failed to write kick eventfd"); + Ok(()) + } + + fn set_vring_enable(&mut self, queue_index: usize, status: u32) -> Result<()> { + println!("queue_index: {}, status: {}", queue_index, status); + assert_eq!(queue_index, 3); + assert_eq!(status, 1); + Ok(()) + } + } + + // It is ok to send the message of SetOwner, SetFeatures and GetFeatures from client to server. + #[test] + fn vhost_user_test_01() { + let exit_server_thread = Arc::new(Mutex::new(false)); + let test_path = temp_dir().join("test1"); + + let exit_thread_clone = exit_server_thread.clone(); + let test_path_clone = test_path.clone(); + let _handle = thread::Builder::new() + .spawn(move || { + let mut server = VhostUserServerHandler::new( + test_path_clone.to_str().unwrap(), + Arc::new(Mutex::new(VhostUserBackend::new())), + ) + .unwrap(); + + if server.sock.server_accept().is_err() { + return; + } + + loop { + let _ = server.handle_request(); + if *exit_thread_clone.lock().unwrap() == true { + break; + } + } + }) + .expect("thread spawn failed"); + + thread::sleep(time::Duration::from_micros(1000)); + + let mem_space = address_space_init(); + let queue_num = 255; + let client = + VhostUserClient::new(&mem_space, test_path.to_str().unwrap(), queue_num).unwrap(); + assert!(client.set_owner().is_ok()); + + assert!(client.set_features(1000).is_ok()); + let features = match client.get_features() { + Err(_e) => 0_u64, + Ok(ret) => ret, + }; + assert_eq!(features, 1000); + + *exit_server_thread.lock().unwrap() = true; + thread::sleep(time::Duration::from_micros(1000)); + } +} -- Gitee From f809115241732af1db94e312ce0945491c328073 Mon Sep 17 00:00:00 2001 From: Fei Xu Date: Tue, 6 Apr 2021 20:31:50 +0800 Subject: [PATCH 54/55] vhost_user_fs: add testcases of SetMemtable, SetVringEnable, SetVringAddr, SetVringNum, SetVringCall and SetVringKick for vhost_user_server.rs Signed-off-by: Fei Xu --- vhost_user_fs/src/vhost_user_server.rs | 112 +++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/vhost_user_fs/src/vhost_user_server.rs b/vhost_user_fs/src/vhost_user_server.rs index 3fd3c06ed..f6cf08723 100644 --- a/vhost_user_fs/src/vhost_user_server.rs +++ b/vhost_user_fs/src/vhost_user_server.rs @@ -675,4 +675,116 @@ mod tests { *exit_server_thread.lock().unwrap() = true; thread::sleep(time::Duration::from_micros(1000)); } + + // It is ok to send the message of SetMemTable, SetVringEnable, SetVringAddr and + // SetVringNum from client to server. + #[test] + fn vhost_user_test_02() { + let exit_server_thread = Arc::new(Mutex::new(false)); + let test_path = temp_dir().join("test2"); + + let exit_thread_clone = exit_server_thread.clone(); + let test_path_clone = test_path.clone(); + let _handle = thread::Builder::new() + .spawn(move || { + let mut server = VhostUserServerHandler::new( + test_path_clone.to_str().unwrap(), + Arc::new(Mutex::new(VhostUserBackend::new())), + ) + .unwrap(); + + if server.sock.server_accept().is_err() { + return; + } + + loop { + let _ = server.handle_request(); + if *exit_thread_clone.lock().unwrap() == true { + break; + } + } + }) + .expect("thread spawn failed"); + + thread::sleep(time::Duration::from_micros(1000)); + + let mem_space = address_space_init(); + let queue_num = 255; + let client = + VhostUserClient::new(&mem_space, test_path.to_str().unwrap(), queue_num).unwrap(); + assert!(client.set_mem_table().is_ok()); + + let queue_index: usize = 2; + assert!(client.set_vring_num(queue_index, QUEUE_SIZE).is_ok()); + + let queue_index: usize = 1; + assert!(client.set_vring_base(queue_index, LAST_AVAIL_IDX).is_ok()); + + let queue_index: usize = 3; + assert!(client.set_vring_enable(queue_index, true).is_ok()); + + let queue_index: usize = 4; + let config = QueueConfig { + desc_table: GuestAddress(0x1), + avail_ring: GuestAddress(0x2), + used_ring: GuestAddress(0x3), + max_size: 300, + size: 200, + ready: true, + }; + assert!(client.set_vring_addr(&config, queue_index, 0x1).is_ok()); + + *exit_server_thread.lock().unwrap() = true; + thread::sleep(time::Duration::from_micros(1000)); + } + + // It is ok to send the message of SetVringCall and SetVringKick from client to server. + #[test] + fn vhost_user_test_03() { + let exit_server_thread = Arc::new(Mutex::new(false)); + let test_path = temp_dir().join("test3"); + + let exit_thread_clone = exit_server_thread.clone(); + let test_path_clone = test_path.clone(); + let _handle = thread::Builder::new() + .spawn(move || { + let mut server = VhostUserServerHandler::new( + test_path_clone.to_str().unwrap(), + Arc::new(Mutex::new(VhostUserBackend::new())), + ) + .unwrap(); + + if server.sock.server_accept().is_err() { + return; + } + + loop { + let _ = server.handle_request(); + if *exit_thread_clone.lock().unwrap() == true { + break; + } + } + }) + .expect("thread spawn failed"); + + thread::sleep(time::Duration::from_micros(1000)); + + let mem_space = address_space_init(); + let queue_num = 255; + let client = + VhostUserClient::new(&mem_space, test_path.to_str().unwrap(), queue_num).unwrap(); + + let call_evt = EventFd::new(0).expect("Failed to create eventfd for calling"); + let queue_index = 6; + assert!(client.set_vring_call(queue_index, &call_evt).is_ok()); + assert_eq!(call_evt.read().expect("Failed to read call eventfd"), 200); + + let kick_evt = EventFd::new(0).expect("Failed to create eventfd for calling"); + let queue_index = 7; + assert!(client.set_vring_kick(queue_index, &kick_evt).is_ok()); + assert_eq!(kick_evt.read().expect("Failed to read kick eventfd"), 300); + + *exit_server_thread.lock().unwrap() = true; + thread::sleep(time::Duration::from_micros(1000)); + } } -- Gitee From adc195c684a09a7e145092d30b8f2e2a23575f5f Mon Sep 17 00:00:00 2001 From: Xinle Guo Date: Thu, 8 Apr 2021 10:49:05 +0800 Subject: [PATCH 55/55] test --- vhost_user_fs/src/virtio_fs.rs | 48 ++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/vhost_user_fs/src/virtio_fs.rs b/vhost_user_fs/src/virtio_fs.rs index 61631e114..402a75a80 100644 --- a/vhost_user_fs/src/virtio_fs.rs +++ b/vhost_user_fs/src/virtio_fs.rs @@ -477,13 +477,15 @@ mod tests { let in_header_len = (size_of::() + size_of::() + size_of::()) as u32; + let uid = unsafe { libc::geteuid() }; + let gid = unsafe { libc::getegid() }; let in_header = FuseInHeader { len: in_header_len, opcode: FUSE_CREATE, unique: 42, nodeid: 1, - uid: 0, - gid: 0, + uid, + gid, pid: 13579, padding: 0, }; @@ -593,13 +595,15 @@ mod tests { let in_header_len = (size_of::() + size_of::() + size_of::()) as u32; + let uid = unsafe { libc::geteuid() }; + let gid = unsafe { libc::getegid() }; let in_header = FuseInHeader { len: in_header_len, opcode: FUSE_CREATE, unique: 42, nodeid: 1, - uid: 0, - gid: 0, + uid, + gid, pid: 13579, padding: 0, }; @@ -706,13 +710,15 @@ mod tests { .unwrap(); let in_header_len = size_of::() as u32; + let uid = unsafe { libc::geteuid() }; + let gid = unsafe { libc::getegid() }; let in_header = FuseInHeader { len: in_header_len, opcode: FUSE_CREATE, unique: 42, nodeid: 1, - uid: 0, - gid: 0, + uid, + gid, pid: 13579, padding: 0, }; @@ -802,13 +808,15 @@ mod tests { let in_header_len = (size_of::() + size_of::() + size_of::()) as u32; + let uid = unsafe { libc::geteuid() }; + let gid = unsafe { libc::getegid() }; let in_header = FuseInHeader { len: in_header_len, opcode: FUSE_CREATE, unique: 42, nodeid: 1, - uid: 0, - gid: 0, + uid, + gid, pid: 13579, padding: 0, }; @@ -913,14 +921,16 @@ mod tests { let in_header_len = (size_of::() + size_of::() + size_of::()) as u32; + let uid = unsafe { libc::geteuid() }; + let gid = unsafe { libc::getegid() }; // invalid operation code: 64 let in_header = FuseInHeader { len: in_header_len, opcode: 64, unique: 42, nodeid: 1, - uid: 0, - gid: 0, + uid, + gid, pid: 13579, padding: 0, }; @@ -1025,13 +1035,15 @@ mod tests { let in_header_len = (size_of::() + size_of::() + size_of::()) as u32; + let uid = unsafe { libc::geteuid() }; + let gid = unsafe { libc::getegid() }; let in_header = FuseInHeader { len: in_header_len, opcode: FUSE_CREATE, unique: 42, nodeid: 1, - uid: 0, - gid: 0, + uid, + gid, pid: 13579, padding: 0, }; @@ -1149,13 +1161,15 @@ mod tests { let in_header_len = (size_of::() + size_of::() + size_of::()) as u32; + let uid = unsafe { libc::geteuid() }; + let gid = unsafe { libc::getegid() }; let in_header = FuseInHeader { len: in_header_len, opcode: FUSE_CREATE, unique: 42, nodeid: 1, - uid: 0, - gid: 0, + uid, + gid, pid: 13579, padding: 0, }; @@ -1273,13 +1287,15 @@ mod tests { let in_header_len = (size_of::() + size_of::() + size_of::()) as u32; + let uid = unsafe { libc::geteuid() }; + let gid = unsafe { libc::getegid() }; let in_header = FuseInHeader { len: in_header_len, opcode: FUSE_CREATE, unique: 42, nodeid: 1, - uid: 0, - gid: 0, + uid, + gid, pid: 13579, padding: 0, }; -- Gitee