diff --git a/.github/scripts/behavior_test_judge.py b/.github/scripts/behavior_test_judge.py index 20d4d4f..9772b11 100644 --- a/.github/scripts/behavior_test_judge.py +++ b/.github/scripts/behavior_test_judge.py @@ -26,7 +26,16 @@ print(errors) sys.exit(1) -test_command = "python3 /mnt/behavior_test.py" +test_command = "python3 /mnt/file_behavior_test.py" +stdin, stdout, stderr = ssh.exec_command(test_command) +output = stdout.read().decode('utf-8') +errors = stderr.read().decode('utf-8') +exit_status = stdout.channel.recv_exit_status() +if exit_status != 0: + print(errors) + sys.exit(1) + +test_command = "python3 /mnt/path_behavior_test.py" stdin, stdout, stderr = ssh.exec_command(test_command) output = stdout.read().decode('utf-8') errors = stderr.read().decode('utf-8') diff --git a/.github/scripts/behavior_test.py b/.github/scripts/file_behavior_test.py similarity index 100% rename from .github/scripts/behavior_test.py rename to .github/scripts/file_behavior_test.py diff --git a/.github/scripts/path_behavior_test.py b/.github/scripts/path_behavior_test.py new file mode 100644 index 0000000..65c7ab2 --- /dev/null +++ b/.github/scripts/path_behavior_test.py @@ -0,0 +1,34 @@ +import os + +TEST_POINT = "/mnt" +TEST_PRESET_PATHS = [ + ("./behavior_test_judge.py", True), + ("./build_and_run_ovfs.sh", True), + ("./file_behavior_test.py", True), + ("./image.img", True), + ("./install_and_run_vm.sh", True), + ("./meta-data", True), + ("./path_behavior_test.py", True), + ("./seed.iso", True), + ("./ubuntu-20.04.6-live-server-amd64.iso", True), + ("./user-data", True), +] + +def list_paths(): + walked_entries = [] + for dirpath, dirnames, filenames in os.walk(TEST_POINT): + rel_dir = os.path.relpath(dirpath, TEST_POINT) + for dirname in dirnames: + walked_entries.append((os.path.join(rel_dir, dirname), False)) + for filename in filenames: + walked_entries.append((os.path.join(rel_dir, filename), True)) + return walked_entries + +def test_path(): + paths = list_paths() + paths.sort() + TEST_PRESET_PATHS.sort() + assert paths == TEST_PRESET_PATHS + +if __name__ == "__main__": + test_path() diff --git a/.github/workflows/behavior_test.yml b/.github/workflows/behavior_test.yml index 990dfd4..aae6d83 100644 --- a/.github/workflows/behavior_test.yml +++ b/.github/workflows/behavior_test.yml @@ -23,5 +23,5 @@ jobs: nohup ./build_and_run_ovfs.sh & nohup ./install_and_run_vm.sh & pip install paramiko - python behavior_test.py + python behavior_test_judge.py working-directory: .github/scripts diff --git a/.github/workflows/format_check.yml b/.github/workflows/format_check.yml new file mode 100644 index 0000000..30ba079 --- /dev/null +++ b/.github/workflows/format_check.yml @@ -0,0 +1,20 @@ +name: OVFS Format Check + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + build: + name: Format Check + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Run Format Check + run: cargo fmt -- --check diff --git a/src/error.rs b/src/error.rs index e65f92f..8e28823 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,6 +1,9 @@ +use std::ffi::CStr; use std::io; use anyhow::Error as AnyError; +use log::debug; +use opendal::ErrorKind; use snafu::prelude::Snafu; #[derive(Debug, Snafu)] @@ -20,6 +23,39 @@ pub enum Error { }, } +impl From for Error { + fn from(error: opendal::Error) -> Error { + debug!("opendal error occurred: {:?}", error); + match error.kind() { + ErrorKind::Unsupported => Error::from(libc::EOPNOTSUPP), + ErrorKind::IsADirectory => Error::from(libc::EISDIR), + ErrorKind::NotFound => Error::from(libc::ENOENT), + ErrorKind::PermissionDenied => Error::from(libc::EACCES), + ErrorKind::AlreadyExists => Error::from(libc::EEXIST), + ErrorKind::NotADirectory => Error::from(libc::ENOTDIR), + ErrorKind::RangeNotSatisfied => Error::from(libc::EINVAL), + ErrorKind::RateLimited => Error::from(libc::EBUSY), + _ => Error::from(libc::ENOENT), + } + } +} + +impl From for Error { + fn from(errno: libc::c_int) -> Error { + let err_str = unsafe { libc::strerror(errno) }; + let message = if err_str.is_null() { + format!("errno: {}", errno) + } else { + let c_str = unsafe { CStr::from_ptr(err_str) }; + c_str.to_string_lossy().into_owned() + }; + Error::VhostUserFsError { + message, + source: None, + } + } +} + impl From for io::Error { fn from(error: Error) -> io::Error { match error { diff --git a/src/filesystem.rs b/src/filesystem.rs index 8489df4..b8a7a52 100644 --- a/src/filesystem.rs +++ b/src/filesystem.rs @@ -3,59 +3,72 @@ use std::ffi::CStr; use std::io::Read; use std::io::Write; use std::mem::size_of; -use std::path::PathBuf; -use std::sync::RwLock; +use std::sync::Mutex; use std::time::Duration; use log::debug; -use opendal::{Buffer, Operator}; +use opendal::Buffer; +use opendal::Operator; use sharded_slab::Slab; -use tokio::runtime::{Builder, Runtime}; +use tokio::runtime::Builder; +use tokio::runtime::Runtime; use vm_memory::ByteValued; use crate::buffer::BufferWrapper; use crate::error::*; use crate::filesystem_message::*; -use crate::util::{Reader, Writer}; +use crate::util::Reader; +use crate::util::Writer; const KERNEL_VERSION: u32 = 7; const KERNEL_MINOR_VERSION: u32 = 38; const MIN_KERNEL_MINOR_VERSION: u32 = 27; const BUFFER_HEADER_SIZE: u32 = 4096; const MAX_BUFFER_SIZE: u32 = 1 << 20; +const DEFAULT_TTL: Duration = Duration::from_secs(1); +const DEFAULT_GID: u32 = 1000; +const DEFAULT_UID: u32 = 1000; +const DEFAULT_DIR_NLINK: u32 = 2; +const DEFAULT_FILE_NLINK: u32 = 1; +const DEFAULT_MODE: u32 = 0o755; +const DEAFULT_DIR_TYPE_IN_DIR_ENTRY: u32 = 4; +const DEAFULT_FILE_TYPE_IN_DIR_ENTRY: u32 = 8; const DIRENT_PADDING: [u8; 8] = [0; 8]; enum FileType { Dir, File, - Unknown, +} + +struct InnerWriter { + writer: opendal::Writer, + written: u64, } #[derive(Clone)] struct OpenedFile { path: String, - stat: libc::stat64, + metadata: Attr, } impl OpenedFile { fn new(file_type: FileType, path: &str) -> OpenedFile { - let mut stat: libc::stat64 = unsafe { std::mem::zeroed() }; - stat.st_uid = 1000; - stat.st_gid = 1000; + let mut attr: Attr = unsafe { std::mem::zeroed() }; + attr.uid = DEFAULT_UID; + attr.gid = DEFAULT_GID; match file_type { FileType::Dir => { - stat.st_nlink = 2; - stat.st_mode = libc::S_IFDIR | 0o755; + attr.nlink = DEFAULT_DIR_NLINK; + attr.mode = libc::S_IFDIR | DEFAULT_MODE; } FileType::File => { - stat.st_nlink = 1; - stat.st_mode = libc::S_IFREG | 0o755; + attr.nlink = DEFAULT_FILE_NLINK; + attr.mode = libc::S_IFREG | DEFAULT_MODE; } - FileType::Unknown => (), } OpenedFile { - stat, path: path.to_string(), + metadata: attr, } } } @@ -67,32 +80,12 @@ struct DirEntry { name: String, } -fn opendal_error2error(error: opendal::Error) -> Error { - match error.kind() { - opendal::ErrorKind::Unsupported => { - new_vhost_user_fs_error("unsupported error occurred in backend storage system", None) - } - opendal::ErrorKind::NotFound => { - new_vhost_user_fs_error("notfound error occurred in backend storage system", None) - } - _ => new_vhost_user_fs_error("unexpected error occurred in backend storage system", None), - } -} - -fn opendal_metadata2opened_file(path: &str, metadata: &opendal::Metadata) -> OpenedFile { - let file_type = match metadata.mode() { - opendal::EntryMode::DIR => FileType::Dir, - opendal::EntryMode::FILE => FileType::File, - opendal::EntryMode::Unknown => FileType::Unknown, - }; - OpenedFile::new(file_type, path) -} - pub struct Filesystem { rt: Runtime, core: Operator, - opened_files: Slab>, - opened_files_map: RwLock>, + opened_files: Slab, + opened_files_map: Mutex>, + opened_files_writer: Mutex>, } impl Filesystem { @@ -106,67 +99,71 @@ impl Filesystem { Filesystem { rt, core, - opened_files: Slab::default(), - opened_files_map: RwLock::new(HashMap::default()), + opened_files: Slab::new(), + opened_files_map: Mutex::new(HashMap::new()), + opened_files_writer: Mutex::new(HashMap::new()), } } pub fn handle_message(&self, mut r: Reader, w: Writer) -> Result { - let in_header: InHeader = r.read_obj().map_err(|e| { - new_vhost_user_fs_error("failed to decode protocol messages", Some(e.into())) - })?; + let in_header: InHeader = r.read_obj().map_err(|_| Error::from(libc::EIO))?; if in_header.len > (MAX_BUFFER_SIZE + BUFFER_HEADER_SIZE) { - return Filesystem::reply_error(in_header.unique, w); + return Filesystem::reply_error(in_header.unique, w, libc::EIO); } if let Ok(opcode) = Opcode::try_from(in_header.opcode) { + debug!( + "received request: opcode={}, inode={}", + in_header.opcode, in_header.nodeid + ); match opcode { Opcode::Init => self.init(in_header, r, w), + Opcode::Destroy => self.destory(), + Opcode::Forget => self.forget(in_header), Opcode::Lookup => self.lookup(in_header, r, w), Opcode::Getattr => self.getattr(in_header, r, w), Opcode::Setattr => self.setattr(in_header, r, w), Opcode::Create => self.create(in_header, r, w), Opcode::Unlink => self.unlink(in_header, r, w), - Opcode::Mkdir => self.mkdir(in_header, r, w), - Opcode::Rmdir => self.rmdir(in_header, r, w), + Opcode::Release => self.release(in_header, r, w), + Opcode::Flush => self.flush(in_header, r, w), Opcode::Open => self.open(in_header, r, w), - Opcode::Opendir => self.opendir(in_header, r, w), Opcode::Read => self.read(in_header, r, w), Opcode::Write => self.write(in_header, r, w), - Opcode::Readdir => self.readdir(in_header, r, w), - Opcode::Destroy => self.destory(), - Opcode::Access => self.access(in_header, r, w), - Opcode::Forget => self.forget(in_header, r), - Opcode::Release => self.release(in_header, r, w), + Opcode::Mkdir => Filesystem::reply_error(in_header.unique, w, libc::ENOSYS), + Opcode::Rmdir => Filesystem::reply_error(in_header.unique, w, libc::ENOSYS), Opcode::Releasedir => self.releasedir(in_header, r, w), - Opcode::Flush => self.flush(in_header, r, w), Opcode::Fsyncdir => self.fsyncdir(in_header, r, w), - Opcode::Getxattr => Filesystem::reply_unimplemented(in_header.unique, w), + Opcode::Opendir => self.opendir(in_header, r, w), + Opcode::Readdir => self.readdir(in_header, r, w), } } else { debug!( - "[Filesystem] received unknown request: opcode={}, inode={}", + "received unknown request: opcode={}, inode={}", in_header.opcode, in_header.nodeid ); - Filesystem::reply_error(in_header.unique, w) + Filesystem::reply_error(in_header.unique, w, libc::ENOSYS) } } } impl Filesystem { fn init(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { - let InitIn { major, minor, .. } = r.read_obj().map_err(|e| { - new_vhost_user_fs_error("failed to decode protocol messages", Some(e.into())) - })?; + let InitIn { major, minor, .. } = r.read_obj().map_err(|_| Error::from(libc::EIO))?; if major != KERNEL_VERSION || minor < MIN_KERNEL_MINOR_VERSION { - return Filesystem::reply_error(in_header.unique, w); + return Filesystem::reply_error(in_header.unique, w, libc::EIO); } - let file = OpenedFile::new(FileType::Dir, "/"); - let file_key = self.insert_opened_inode(file.clone()); - self.insert_opened("/", file_key); - let file_key = self.insert_opened_inode(file.clone()); - self.insert_opened("/", file_key); + let mut attr = OpenedFile::new(FileType::Dir, "/"); + attr.metadata.ino = 1; + self.opened_files + .insert(attr.clone()) + .expect("failed to allocate inode"); + self.opened_files + .insert(attr.clone()) + .expect("failed to allocate inode"); + let mut opened_files_map = self.opened_files_map.lock().unwrap(); + opened_files_map.insert("/".to_string(), 1); let out = InitOut { major: KERNEL_VERSION, @@ -178,365 +175,231 @@ impl Filesystem { } fn destory(&self) -> Result { + // do nothing for destroy. Ok(0) } - fn access(&self, _in_header: InHeader, _r: Reader, _w: Writer) -> Result { - Ok(0) - } - - fn forget(&self, _in_header: InHeader, _r: Reader) -> Result { - Ok(0) - } - - fn release(&self, _in_header: InHeader, _r: Reader, _w: Writer) -> Result { - Ok(0) - } - - fn releasedir(&self, _in_header: InHeader, _r: Reader, _w: Writer) -> Result { - Ok(0) - } - - fn flush(&self, _in_header: InHeader, _r: Reader, _w: Writer) -> Result { - Ok(0) - } - - fn fsyncdir(&self, _in_header: InHeader, _r: Reader, _w: Writer) -> Result { + fn forget(&self, _in_header: InHeader) -> Result { + // do nothing for forget. Ok(0) } fn lookup(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let name_len = in_header.len as usize - size_of::(); - let mut buf = vec![0u8; name_len]; - r.read_exact(&mut buf).map_err(|e| { - new_unexpected_error("failed to decode protocol messages", Some(e.into())) - })?; + let mut buf = vec![0; name_len]; + r.read_exact(&mut buf).map_err(|_| Error::from(libc::EIO))?; let name = match Filesystem::bytes_to_str(buf.as_ref()) { Ok(name) => name, - Err(_) => return Filesystem::reply_error(in_header.unique, w), + Err(_) => return Filesystem::reply_error(in_header.unique, w, libc::EIO), }; - debug!( - "[Filesystem] lookup: parent_key={} name={}", - in_header.nodeid, name - ); + debug!("lookup: parent inode={} name={}", in_header.nodeid, name); - let parent_file = self.get_opened_inode(in_header.nodeid as usize); - let parent_path = match parent_file { - Ok(parent_file) => parent_file.path, - Err(_) => return Filesystem::reply_error(in_header.unique, w), - }; - - let path = PathBuf::from(parent_path) - .join(name) - .to_string_lossy() - .to_string(); - - if let Some(file_key) = self.get_opened(&path) { - let mut file = self.get_opened_inode(file_key).unwrap(); - file.stat.st_ino = file_key as u64; - file.stat.st_size = 2; - let out = EntryOut { - nodeid: file_key as u64, - generation: 0, - entry_valid: Duration::from_secs(5).as_secs(), - attr_valid: Duration::from_secs(5).as_secs(), - entry_valid_nsec: Duration::from_secs(5).subsec_nanos(), - attr_valid_nsec: Duration::from_secs(5).subsec_nanos(), - attr: file.stat.into(), - }; - return Filesystem::reply_ok(Some(out), None, in_header.unique, w); - } + let parent_path = match self + .opened_files + .get(in_header.nodeid as usize) + .map(|f| f.path.clone()) + { + Some(path) => path, + None => return Filesystem::reply_error(in_header.unique, w, libc::ENOENT), + }; - let mut file = match self.rt.block_on(self.do_get_stat(&path)) { - Ok(stat) => stat, - Err(_) => return Filesystem::reply_error(in_header.unique, w), + let path = format!("{}/{}", parent_path, name); + let metadata = match self.rt.block_on(self.do_get_metadata(&path)) { + Ok(metadata) => metadata, + Err(_) => return Filesystem::reply_error(in_header.unique, w, libc::ENOENT), }; - let file_key = self.insert_opened_inode(file.clone()); - self.insert_opened(&path, file_key); - file.stat.st_ino = file_key as u64; - file.stat.st_size = 2; let out = EntryOut { - nodeid: file_key as u64, - generation: 0, - entry_valid: Duration::from_secs(5).as_secs(), - attr_valid: Duration::from_secs(5).as_secs(), - entry_valid_nsec: Duration::from_secs(5).subsec_nanos(), - attr_valid_nsec: Duration::from_secs(5).subsec_nanos(), - attr: file.stat.into(), + nodeid: metadata.metadata.ino, + entry_valid: DEFAULT_TTL.as_secs(), + attr_valid: DEFAULT_TTL.as_secs(), + entry_valid_nsec: DEFAULT_TTL.subsec_nanos(), + attr_valid_nsec: DEFAULT_TTL.subsec_nanos(), + attr: metadata.metadata, + ..Default::default() }; - return Filesystem::reply_ok(Some(out), None, in_header.unique, w); + Filesystem::reply_ok(Some(out), None, in_header.unique, w) } fn getattr(&self, in_header: InHeader, _r: Reader, w: Writer) -> Result { - debug!("[Filesystem] getattr: key={}", in_header.nodeid); + debug!("getattr: inode={}", in_header.nodeid); + + let path = match self + .opened_files + .get(in_header.nodeid as usize) + .map(|f| f.path.clone()) + { + Some(path) => path, + None => return Filesystem::reply_error(in_header.unique, w, libc::ENOENT), + }; - let file = self.get_opened_inode(in_header.nodeid as usize); - let mut stat = match file { - Ok(file) => file.stat, - Err(_) => return Filesystem::reply_error(in_header.unique, w), + let metadata = match self.rt.block_on(self.do_get_metadata(&path)) { + Ok(metadata) => metadata, + Err(_) => return Filesystem::reply_error(in_header.unique, w, libc::ENOENT), }; - stat.st_ino = in_header.nodeid; - stat.st_size = 2; let out = AttrOut { - attr_valid: Duration::from_secs(5).as_secs(), - attr_valid_nsec: Duration::from_secs(5).subsec_nanos(), - dummy: 0, - attr: stat.into(), + attr_valid: DEFAULT_TTL.as_secs(), + attr_valid_nsec: DEFAULT_TTL.subsec_nanos(), + attr: metadata.metadata, + ..Default::default() }; Filesystem::reply_ok(Some(out), None, in_header.unique, w) } fn setattr(&self, in_header: InHeader, _r: Reader, w: Writer) -> Result { - debug!("[Filesystem] setattr: key={}", in_header.nodeid); - - let file = self.get_opened_inode(in_header.nodeid as usize); - let mut stat = match file { - Ok(file) => file.stat, - Err(_) => return Filesystem::reply_error(in_header.unique, w), - }; - stat.st_ino = in_header.nodeid; + debug!("setattr: inode={}", in_header.nodeid); - let out = AttrOut { - attr_valid: Duration::from_secs(5).as_secs(), - attr_valid_nsec: Duration::from_secs(5).subsec_nanos(), - dummy: 0, - attr: stat.into(), - }; - Filesystem::reply_ok(Some(out), None, in_header.unique, w) + // do nothing for setattr. + self.getattr(in_header, _r, w) } fn create(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { - let CreateIn { .. } = r.read_obj().map_err(|e| { + let CreateIn { flags, .. } = r.read_obj().map_err(|e| { new_vhost_user_fs_error("failed to decode protocol messages", Some(e.into())) })?; let name_len = in_header.len as usize - size_of::() - size_of::(); - let mut buf = vec![0u8; name_len]; + let mut buf = vec![0; name_len]; r.read_exact(&mut buf).map_err(|e| { new_unexpected_error("failed to decode protocol messages", Some(e.into())) })?; - let mut components = buf.split_inclusive(|c| *c == b'\0'); - let buf = components.next().ok_or(new_unexpected_error( - "one or more parameters are missing", - None, - ))?; let name = match Filesystem::bytes_to_str(buf.as_ref()) { Ok(name) => name, - Err(_) => return Filesystem::reply_error(in_header.unique, w), + Err(_) => return Filesystem::reply_error(in_header.unique, w, libc::ENOENT), }; debug!( - "[Filesystem] create: parent_key={} name={}", - in_header.nodeid, name + "create: parent inode={} name={} flags={}", + in_header.nodeid, name, flags ); - let parent_file = self.get_opened_inode(in_header.nodeid as usize); - let parent_path = match parent_file { - Ok(parent_file) => parent_file.path, - Err(_) => return Filesystem::reply_error(in_header.unique, w), + let parent_path = match self + .opened_files + .get(in_header.nodeid as usize) + .map(|f| f.path.clone()) + { + Some(path) => path, + None => return Filesystem::reply_error(in_header.unique, w, libc::ENOENT), }; - let path = PathBuf::from(parent_path) - .join(name) - .to_string_lossy() - .to_string(); + let path = format!("{}/{}", parent_path, name); + let mut attr = OpenedFile::new(FileType::File, &path); + let inode = self + .opened_files + .insert(attr.clone()) + .expect("failed to allocate inode"); + attr.metadata.ino = inode as u64; + let mut opened_files_map = self.opened_files_map.lock().unwrap(); + opened_files_map.insert(path.to_string(), inode as u64); - if self.rt.block_on(self.do_create_file(&path)).is_err() { - return Filesystem::reply_error(in_header.unique, w); - } - - let file = OpenedFile::new(FileType::File, &path); - let file_key = self.insert_opened_inode(file.clone()); - self.insert_opened(&path, file_key); - - let mut stat = file.stat; - stat.st_ino = file_key as u64; + match self.rt.block_on(self.do_set_writer(&path, flags)) { + Ok(writer) => writer, + Err(_) => return Filesystem::reply_error(in_header.unique, w, libc::ENOENT), + }; let entry_out = EntryOut { - nodeid: file_key as u64, - generation: 0, - entry_valid: Duration::from_secs(5).as_secs(), - attr_valid: Duration::from_secs(5).as_secs(), - entry_valid_nsec: Duration::from_secs(5).subsec_nanos(), - attr_valid_nsec: Duration::from_secs(5).subsec_nanos(), - attr: stat.into(), + nodeid: attr.metadata.ino, + entry_valid: DEFAULT_TTL.as_secs(), + attr_valid: DEFAULT_TTL.as_secs(), + entry_valid_nsec: DEFAULT_TTL.subsec_nanos(), + attr_valid_nsec: DEFAULT_TTL.subsec_nanos(), + attr: attr.metadata, + ..Default::default() }; let open_out = OpenOut { ..Default::default() }; - return Filesystem::reply_ok( + Filesystem::reply_ok( Some(entry_out), Some(open_out.as_slice()), in_header.unique, w, - ); + ) } fn unlink(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { let name_len = in_header.len as usize - size_of::(); - let mut buf = vec![0u8; name_len]; + let mut buf = vec![0; name_len]; r.read_exact(&mut buf).map_err(|e| { new_unexpected_error("failed to decode protocol messages", Some(e.into())) })?; - let mut components = buf.split_inclusive(|c| *c == b'\0'); - let buf = components.next().ok_or(new_unexpected_error( - "one or more parameters are missing", - None, - ))?; let name = match Filesystem::bytes_to_str(buf.as_ref()) { Ok(name) => name, - Err(_) => return Filesystem::reply_error(in_header.unique, w), + Err(_) => return Filesystem::reply_error(in_header.unique, w, libc::ENOENT), }; - debug!( - "[Filesystem] unlink: parent_key={} name={}", - in_header.nodeid, name - ); + debug!("unlink: parent inode={} name={}", in_header.nodeid, name); - let parent_file = self.get_opened_inode(in_header.nodeid as usize); - let parent_path = match parent_file { - Ok(parent_file) => parent_file.path, - Err(_) => return Filesystem::reply_error(in_header.unique, w), + let parent_path = match self + .opened_files + .get(in_header.nodeid as usize) + .map(|f| f.path.clone()) + { + Some(path) => path, + None => return Filesystem::reply_error(in_header.unique, w, libc::ENOENT), }; - let path = PathBuf::from(parent_path) - .join(name) - .to_string_lossy() - .to_string(); - + let path = format!("{}/{}", parent_path, name); if self.rt.block_on(self.do_delete(&path)).is_err() { - return Filesystem::reply_error(in_header.unique, w); + return Filesystem::reply_error(in_header.unique, w, libc::ENOENT); } - Ok(0) - } - - fn mkdir(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { - let MkdirIn { .. } = r.read_obj().map_err(|e| { - new_vhost_user_fs_error("failed to decode protocol messages", Some(e.into())) - })?; + let mut opened_files_map = self.opened_files_map.lock().unwrap(); + opened_files_map.remove(&path); - let name_len = in_header.len as usize - size_of::() - size_of::(); - let mut buf = vec![0u8; name_len]; - r.read_exact(&mut buf).map_err(|e| { - new_unexpected_error("failed to decode protocol messages", Some(e.into())) - })?; - let mut components = buf.split_inclusive(|c| *c == b'\0'); - let buf = components.next().ok_or(new_unexpected_error( - "one or more parameters are missing", - None, - ))?; - let name = match Filesystem::bytes_to_str(buf.as_ref()) { - Ok(name) => name, - Err(_) => return Filesystem::reply_error(in_header.unique, w), - }; + Filesystem::reply_ok(None::, None, in_header.unique, w) + } - debug!( - "[Filesystem] mkdir: parent_key={} name={}", - in_header.nodeid, name - ); + fn release(&self, in_header: InHeader, _r: Reader, w: Writer) -> Result { + debug!("release: inode={}", in_header.nodeid); - let parent_file = self.get_opened_inode(in_header.nodeid as usize); - let parent_path = match parent_file { - Ok(parent_file) => parent_file.path, - Err(_) => return Filesystem::reply_error(in_header.unique, w), + let path = match self + .opened_files + .get(in_header.nodeid as usize) + .map(|f| f.path.clone()) + { + Some(path) => path, + None => return Filesystem::reply_error(in_header.unique, w, libc::ENOENT), }; - let path = PathBuf::from(parent_path) - .join(name) - .to_string_lossy() - .to_string(); - - if self.rt.block_on(self.do_create_dir(&path)).is_err() { - return Filesystem::reply_error(in_header.unique, w); - } - - let file = OpenedFile::new(FileType::Dir, &path); - let file_key = self.insert_opened_inode(file.clone()); - self.insert_opened(&path, file_key); - - let mut stat = file.stat; - stat.st_ino = file_key as u64; + let mut opened_file_writer = self.opened_files_writer.lock().unwrap(); + opened_file_writer.remove(&path); - let entry_out = EntryOut { - nodeid: file_key as u64, - generation: 0, - entry_valid: Duration::from_secs(5).as_secs(), - attr_valid: Duration::from_secs(5).as_secs(), - entry_valid_nsec: Duration::from_secs(5).subsec_nanos(), - attr_valid_nsec: Duration::from_secs(5).subsec_nanos(), - attr: stat.into(), - }; - return Filesystem::reply_ok(Some(entry_out), None, in_header.unique, w); + Filesystem::reply_ok(None::, None, in_header.unique, w) } - fn rmdir(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { - let name_len = in_header.len as usize - size_of::(); - let mut buf = vec![0u8; name_len]; - r.read_exact(&mut buf).map_err(|e| { - new_unexpected_error("failed to decode protocol messages", Some(e.into())) - })?; - let mut components = buf.split_inclusive(|c| *c == b'\0'); - let buf = components.next().ok_or(new_unexpected_error( - "one or more parameters are missing", - None, - ))?; - let name = match Filesystem::bytes_to_str(buf.as_ref()) { - Ok(name) => name, - Err(_) => return Filesystem::reply_error(in_header.unique, w), - }; - - debug!( - "[Filesystem] rmdir: parent_key={} name={}", - in_header.nodeid, name - ); - - let parent_file = self.get_opened_inode(in_header.nodeid as usize); - let parent_path = match parent_file { - Ok(parent_file) => parent_file.path, - Err(_) => return Filesystem::reply_error(in_header.unique, w), - }; - - let path = PathBuf::from(parent_path) - .join(name) - .to_string_lossy() - .to_string(); + fn flush(&self, in_header: InHeader, _r: Reader, w: Writer) -> Result { + debug!("flush: inode={}", in_header.nodeid); - if self.rt.block_on(self.do_delete(&path)).is_err() { - return Filesystem::reply_error(in_header.unique, w); + if self.opened_files.get(in_header.nodeid as usize).is_none() { + return Filesystem::reply_error(in_header.unique, w, libc::ENOENT); } - Ok(0) + Filesystem::reply_ok(None::, None, in_header.unique, w) } - fn open(&self, in_header: InHeader, _r: Reader, w: Writer) -> Result { - debug!("[Filesystem] open: key={}", in_header.nodeid); + fn open(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { + debug!("open: inode={}", in_header.nodeid); - let file = self.get_opened_inode(in_header.nodeid as usize); - let mut stat = match file { - Ok(file) => file.stat, - Err(_) => return Filesystem::reply_error(in_header.unique, w), - }; - stat.st_ino = in_header.nodeid; + let OpenIn { flags, .. } = r.read_obj().map_err(|e| { + new_vhost_user_fs_error("failed to decode protocol messages", Some(e.into())) + })?; - let out = OpenOut { - ..Default::default() + let path = match self + .opened_files + .get(in_header.nodeid as usize) + .map(|f| f.path.clone()) + { + Some(path) => path, + None => return Filesystem::reply_error(in_header.unique, w, libc::ENOENT), }; - Filesystem::reply_ok(Some(out), None, in_header.unique, w) - } - - fn opendir(&self, in_header: InHeader, _r: Reader, w: Writer) -> Result { - debug!("[Filesystem] opendir: key={}", in_header.nodeid); - let file = self.get_opened_inode(in_header.nodeid as usize); - let mut stat = match file { - Ok(file) => file.stat, - Err(_) => return Filesystem::reply_error(in_header.unique, w), + match self.rt.block_on(self.do_set_writer(&path, flags)) { + Ok(writer) => writer, + Err(_) => return Filesystem::reply_error(in_header.unique, w, libc::ENOENT), }; - stat.st_ino = in_header.nodeid; let out = OpenOut { ..Default::default() @@ -545,44 +408,36 @@ impl Filesystem { } fn read(&self, in_header: InHeader, mut r: Reader, mut w: Writer) -> Result { - debug!("[Filesystem] read: key={}", in_header.nodeid); - - let file = self.get_opened_inode(in_header.nodeid as usize); - let path = match file { - Ok(file) => file.path, - Err(_) => return Filesystem::reply_error(in_header.unique, w), + let path = match self + .opened_files + .get(in_header.nodeid as usize) + .map(|f| f.path.clone()) + { + Some(path) => path, + None => return Filesystem::reply_error(in_header.unique, w, libc::ENOENT), }; let ReadIn { offset, size, .. } = r.read_obj().map_err(|e| { new_vhost_user_fs_error("failed to decode protocol messages", Some(e.into())) })?; - debug!( - "[Filesystem] read: key={} offset={} size={}", - in_header.nodeid, offset, size - ); - - let data = match self.rt.block_on(self.do_read(&path)) { + let data = match self.rt.block_on(self.do_read(&path, offset)) { Ok(data) => data, - Err(_) => return Filesystem::reply_error(in_header.unique, w), + Err(_) => return Filesystem::reply_error(in_header.unique, w, libc::ENOENT), }; let len = data.len(); let buffer = BufferWrapper::new(data); - debug!( - "[Filesystem] read: key={} offset={} size={} len={} buffer={:?}", - in_header.nodeid, - offset, - size, - len, - buffer.get_buffer(), - ); - let mut data_writer = w.split_at(size_of::()).unwrap(); data_writer.write_from_at(&buffer, len).map_err(|e| { new_vhost_user_fs_error("failed to encode protocol messages", Some(e.into())) })?; + debug!( + "read: inode={} offset={} size={} len={}", + in_header.nodeid, offset, size, len + ); + let out = OutHeader { len: (size_of::() + len) as u32, error: 0, @@ -595,15 +450,18 @@ impl Filesystem { } fn write(&self, in_header: InHeader, mut r: Reader, w: Writer) -> Result { - debug!("[Filesystem] write: key={}", in_header.nodeid); + debug!("write: inode={}", in_header.nodeid); - let file = self.get_opened_inode(in_header.nodeid as usize); - let path = match file { - Ok(file) => file.path, - Err(_) => return Filesystem::reply_error(in_header.unique, w), + let path = match self + .opened_files + .get(in_header.nodeid as usize) + .map(|f| f.path.clone()) + { + Some(path) => path, + None => return Filesystem::reply_error(in_header.unique, w, libc::EIO), }; - let WriteIn { size, .. } = r.read_obj().map_err(|e| { + let WriteIn { offset, size, .. } = r.read_obj().map_err(|e| { new_vhost_user_fs_error("failed to decode protocol messages", Some(e.into())) })?; @@ -613,9 +471,10 @@ impl Filesystem { })?; let buffer = buffer.get_buffer(); - if self.rt.block_on(self.do_write(&path, buffer)).is_err() { - return Filesystem::reply_error(in_header.unique, w); - } + match self.rt.block_on(self.do_write(&path, offset, buffer)) { + Ok(writer) => writer, + Err(_) => return Filesystem::reply_error(in_header.unique, w, libc::EIO), + }; let out = WriteOut { size, @@ -624,19 +483,53 @@ impl Filesystem { Filesystem::reply_ok(Some(out), None, in_header.unique, w) } + fn releasedir(&self, in_header: InHeader, _r: Reader, w: Writer) -> Result { + debug!("releasedir: inode={}", in_header.nodeid); + + if self.opened_files.get(in_header.nodeid as usize).is_none() { + return Filesystem::reply_error(in_header.unique, w, libc::ENOENT); + } + + Filesystem::reply_ok(None::, None, in_header.unique, w) + } + + fn fsyncdir(&self, in_header: InHeader, _r: Reader, w: Writer) -> Result { + debug!("fsyncdir: inode={}", in_header.nodeid); + + if self.opened_files.get(in_header.nodeid as usize).is_none() { + return Filesystem::reply_error(in_header.unique, w, libc::ENOENT); + } + + Filesystem::reply_ok(None::, None, in_header.unique, w) + } + + fn opendir(&self, in_header: InHeader, _r: Reader, w: Writer) -> Result { + debug!("opendir: inode={}", in_header.nodeid); + + if self.opened_files.get(in_header.nodeid as usize).is_none() { + return Filesystem::reply_error(in_header.unique, w, libc::ENOENT); + } + + let out = OpenOut { + ..Default::default() + }; + Filesystem::reply_ok(Some(out), None, in_header.unique, w) + } + fn readdir(&self, in_header: InHeader, mut r: Reader, mut w: Writer) -> Result { - let file = self.get_opened_inode(in_header.nodeid as usize); - let path = match file { - Ok(file) => file.path, - Err(_) => return Filesystem::reply_error(in_header.unique, w), + let path = match self + .opened_files + .get(in_header.nodeid as usize) + .map(|f| f.path.clone()) + { + Some(path) => path, + None => return Filesystem::reply_error(in_header.unique, w, libc::ENOENT), }; - let ReadIn { offset, size, .. } = r.read_obj().map_err(|e| { - new_vhost_user_fs_error("failed to decode protocol messages", Some(e.into())) - })?; + let ReadIn { offset, size, .. } = r.read_obj().map_err(|_| Error::from(libc::EIO))?; debug!( - "[Filesystem] readdir: key={} offset={} size={}", + "readdir: inode={} offset={} size={}", in_header.nodeid, offset, size ); @@ -644,7 +537,7 @@ impl Filesystem { let entries = match self.rt.block_on(self.do_readdir(&path)) { Ok(entries) => entries, - Err(_) => return Filesystem::reply_error(in_header.unique, w), + Err(_) => return Filesystem::reply_error(in_header.unique, w, libc::ENOENT), }; if offset as usize >= entries.len() { @@ -653,9 +546,8 @@ impl Filesystem { error: 0, unique: in_header.unique, }; - w.write_all(out.as_slice()).map_err(|e| { - new_vhost_user_fs_error("failed to encode protocol messages", Some(e.into())) - })?; + w.write_all(out.as_slice()) + .map_err(|_| Error::from(libc::EIO))?; return Ok(out.len as usize); } @@ -665,39 +557,22 @@ impl Filesystem { ino: in_header.nodeid, off: 1, name: ".".to_string(), - type_: 4, + type_: DEAFULT_DIR_TYPE_IN_DIR_ENTRY, }; match Filesystem::reply_add_dir_entry(&mut data_writer, entry) { Ok(len) => { total_written += len; } - Err(_) => return Filesystem::reply_error(in_header.unique, w), + Err(_) => return Filesystem::reply_error(in_header.unique, w, libc::EIO), }; - for mut entry in entries { - let path = PathBuf::from(&path) - .join(&entry.name) - .to_string_lossy() - .to_string(); - - if let Some(file_key) = self.get_opened(&path) { - entry.ino = file_key as u64; - } else { - let file = match self.rt.block_on(self.do_get_stat(&path)) { - Ok(stat) => stat, - Err(_) => return Filesystem::reply_error(in_header.unique, w), - }; - let file_key = self.insert_opened_inode(file); - self.insert_opened(&path, file_key); - entry.ino = file_key as u64; - }; - + for entry in entries { match Filesystem::reply_add_dir_entry(&mut data_writer, entry) { Ok(len) => { total_written += len; } - Err(_) => return Filesystem::reply_error(in_header.unique, w), + Err(_) => return Filesystem::reply_error(in_header.unique, w, libc::EIO), }; } @@ -707,9 +582,8 @@ impl Filesystem { unique: in_header.unique, }; - w.write_all(out.as_slice()).map_err(|e| { - new_vhost_user_fs_error("failed to encode protocol messages", Some(e.into())) - })?; + w.write_all(out.as_slice()) + .map_err(|_| Error::from(libc::EIO))?; Ok(out.len as usize) } } @@ -733,46 +607,18 @@ impl Filesystem { error: 0, len: len as u32, }; - w.write_all(header.as_slice()).map_err(|e| { - new_vhost_user_fs_error("failed to encode protocol messages", Some(e.into())) - })?; + w.write_all(header.as_slice()) + .map_err(|_| Error::from(libc::EIO))?; if let Some(out) = out { - w.write_all(out.as_slice()).map_err(|e| { - new_vhost_user_fs_error("failed to encode protocol messages", Some(e.into())) - })?; + w.write_all(out.as_slice()) + .map_err(|_| Error::from(libc::EIO))?; } if let Some(data) = data { - w.write_all(data).map_err(|e| { - new_vhost_user_fs_error("failed to encode protocol messages", Some(e.into())) - })?; + w.write_all(data).map_err(|_| Error::from(libc::EIO))?; } Ok(w.bytes_written()) } - fn reply_error(unique: u64, mut w: Writer) -> Result { - let header = OutHeader { - unique, - error: -libc::ENOENT, - len: size_of::() as u32, - }; - w.write_all(header.as_slice()).map_err(|e| { - new_vhost_user_fs_error("failed to encode protocol messages", Some(e.into())) - })?; - Ok(w.bytes_written()) - } - - fn reply_unimplemented(unique: u64, mut w: Writer) -> Result { - let header = OutHeader { - unique, - error: -libc::ENOSYS, - len: size_of::() as u32, - }; - w.write_all(header.as_slice()).map_err(|e| { - new_vhost_user_fs_error("failed to encode protocol messages", Some(e.into())) - })?; - Ok(w.bytes_written()) - } - fn reply_add_dir_entry(cursor: &mut Writer, entry: DirEntry) -> Result { let entry_len = size_of::() + entry.name.len(); let total_len = (entry_len + 7) & !7; @@ -784,110 +630,154 @@ impl Filesystem { type_: entry.type_, }; - cursor.write_all(out.as_slice()).map_err(|e| { - new_vhost_user_fs_error("failed to encode protocol messages", Some(e.into())) - })?; - cursor.write_all(entry.name.as_bytes()).map_err(|e| { - new_vhost_user_fs_error("failed to encode protocol messages", Some(e.into())) - })?; + cursor + .write_all(out.as_slice()) + .map_err(|_| Error::from(libc::EIO))?; + cursor + .write_all(entry.name.as_bytes()) + .map_err(|_| Error::from(libc::EIO))?; let padding = total_len - entry_len; if padding > 0 { - cursor.write_all(&DIRENT_PADDING[..padding]).map_err(|e| { - new_vhost_user_fs_error("failed to encode protocol messages", Some(e.into())) - })?; + cursor + .write_all(&DIRENT_PADDING[..padding]) + .map_err(|_| Error::from(libc::EIO))?; } Ok(total_len) } - fn bytes_to_str(buf: &[u8]) -> Result<&str> { - return Filesystem::bytes_to_cstr(buf).map(|cstr| cstr.to_str().unwrap()); + fn reply_error(unique: u64, mut w: Writer, error: libc::c_int) -> Result { + let header = OutHeader { + unique, + error: -error, + len: size_of::() as u32, + }; + w.write_all(header.as_slice()) + .map_err(|_| Error::from(libc::EIO))?; + Ok(w.bytes_written()) } - fn bytes_to_cstr(buf: &[u8]) -> Result<&CStr> { - CStr::from_bytes_with_nul(buf).map_err(|e| { - new_vhost_user_fs_error("failed to decode protocol messages", Some(e.into())) - }) + fn bytes_to_str(buf: &[u8]) -> Result<&str> { + Filesystem::bytes_to_cstr(buf)? + .to_str() + .map_err(|_| Error::from(libc::EINVAL)) } -} -impl Filesystem { - fn get_opened(&self, path: &str) -> Option { - let map = self.opened_files_map.read().unwrap(); - map.get(path).copied() + fn bytes_to_cstr(buf: &[u8]) -> Result<&CStr> { + CStr::from_bytes_with_nul(buf).map_err(|_| Error::from(libc::EINVAL)) } - fn insert_opened(&self, path: &str, key: usize) { - let mut map = self.opened_files_map.write().unwrap(); - map.insert(path.to_string(), key); - } + fn check_flags(&self, flags: u32) -> Result<(bool, bool)> { + let is_trunc = flags & libc::O_TRUNC as u32 != 0 || flags & libc::O_CREAT as u32 != 0; + let is_append = flags & libc::O_APPEND as u32 != 0; + let mode = flags & libc::O_ACCMODE as u32; + let is_write = mode == libc::O_WRONLY as u32 || mode == libc::O_RDWR as u32 || is_append; - fn get_opened_inode(&self, key: usize) -> Result { - if let Some(opened_inode) = self.opened_files.get(key) { - let inode_data = opened_inode.read().unwrap().clone(); - Ok(inode_data) - } else { - Err(new_unexpected_error("invalid file", None)) + let capability = self.core.info().full_capability(); + if is_trunc && !capability.write { + Err(Error::from(libc::EACCES))?; } - } - - fn insert_opened_inode(&self, value: OpenedFile) -> usize { - self.opened_files.insert(RwLock::new(value)).unwrap() + if is_append && !capability.write_can_append { + Err(Error::from(libc::EACCES))?; + } + Ok((is_write, is_append)) } } impl Filesystem { - async fn do_get_stat(&self, path: &str) -> Result { - let metadata = self.core.stat(path).await.map_err(opendal_error2error)?; - let attr = opendal_metadata2opened_file(path, &metadata); + async fn do_get_metadata(&self, path: &str) -> Result { + let metadata = self.core.stat(path).await.map_err(|err| Error::from(err))?; + let file_type = match metadata.mode() { + opendal::EntryMode::DIR => FileType::Dir, + _ => FileType::File, + }; + let mut attr = OpenedFile::new(file_type, path); + attr.metadata.size = metadata.content_length(); + let mut opened_files_map = self.opened_files_map.lock().unwrap(); + if let Some(inode) = opened_files_map.get(path) { + attr.metadata.ino = *inode; + } else { + let inode = self + .opened_files + .insert(attr.clone()) + .expect("failed to allocate inode"); + attr.metadata.ino = inode as u64; + opened_files_map.insert(path.to_string(), inode as u64); + } Ok(attr) } - async fn do_create_file(&self, path: &str) -> Result<()> { - self.core - .write(path, Buffer::new()) - .await - .map_err(opendal_error2error)?; - - Ok(()) - } + async fn do_set_writer(&self, path: &str, flags: u32) -> Result<()> { + let (is_write, is_append) = self.check_flags(flags)?; + if !is_write { + return Ok(()); + } - async fn do_create_dir(&self, path: &str) -> Result<()> { - let path = if !path.ends_with('/') { - format!("{}/", path) + let writer = self + .core + .writer_with(path) + .append(is_append) + .await + .map_err(|err| Error::from(err))?; + let written = if is_append { + self.core + .stat(path) + .await + .map_err(|err| Error::from(err))? + .content_length() } else { - path.to_string() + 0 }; - self.core - .create_dir(&path) - .await - .map_err(opendal_error2error)?; + let inner_writer = InnerWriter { writer, written }; + let mut opened_file_writer = self.opened_files_writer.lock().unwrap(); + opened_file_writer.insert(path.to_string(), inner_writer); Ok(()) } async fn do_delete(&self, path: &str) -> Result<()> { - self.core.delete(path).await.map_err(opendal_error2error)?; + self.core + .delete(path) + .await + .map_err(|err| Error::from(err))?; Ok(()) } - async fn do_read(&self, path: &str) -> Result { - let data = self.core.read(path).await.map_err(opendal_error2error)?; + async fn do_read(&self, path: &str, offset: u64) -> Result { + let data = self + .core + .read_with(path) + .range(offset..) + .await + .map_err(|err| Error::from(err))?; Ok(data) } - async fn do_write(&self, path: &str, data: Buffer) -> Result<()> { - self.core - .write(path, data) + async fn do_write(&self, path: &str, offset: u64, data: Buffer) -> Result { + let len = data.len(); + let mut opened_file_writer = self.opened_files_writer.lock().unwrap(); + let inner_writer = opened_file_writer + .get_mut(path) + .ok_or(new_unexpected_error( + "one or more parameters are missing", + None, + ))?; + if offset != inner_writer.written { + return Err(Error::from(libc::EINVAL)); + } + inner_writer + .writer + .write_from(data) .await - .map_err(opendal_error2error)?; + .map_err(|err| Error::from(err))?; + inner_writer.written += len as u64; - Ok(()) + Ok(len) } async fn do_readdir(&self, path: &str) -> Result> { @@ -895,14 +785,42 @@ impl Filesystem { .core .list(path) .await - .map_err(opendal_error2error)? + .map_err(|err| Error::from(err))? .into_iter() .enumerate() - .map(|(i, entry)| DirEntry { - ino: 0, - off: i as u64 + 1, - name: entry.name().to_string(), - type_: 8, + .map(|(i, entry)| { + let metadata = entry.metadata(); + let file_type = match metadata.mode() { + opendal::EntryMode::DIR => FileType::Dir, + _ => FileType::File, + }; + let mut attr = OpenedFile::new(file_type, path); + attr.metadata.size = metadata.content_length(); + + let path = format!("{}/{}", path, entry.name()); + let mut opened_files_map = self.opened_files_map.lock().unwrap(); + let inode = if let Some(inode) = opened_files_map.get(&path) { + *inode + } else { + let inode = self + .opened_files + .insert(attr) + .expect("failed to allocate inode"); + opened_files_map.insert(path.to_string(), inode as u64); + inode as u64 + }; + + let type_ = match metadata.mode() { + opendal::EntryMode::DIR => DEAFULT_DIR_TYPE_IN_DIR_ENTRY, + _ => DEAFULT_FILE_TYPE_IN_DIR_ENTRY, + }; + + DirEntry { + ino: inode, + off: i as u64 + 1, + name: entry.name().to_string(), + type_, + } }) .collect(); diff --git a/src/filesystem_message.rs b/src/filesystem_message.rs index 055b801..11d728f 100644 --- a/src/filesystem_message.rs +++ b/src/filesystem_message.rs @@ -16,14 +16,12 @@ pub enum Opcode { Read = 15, Write = 16, Release = 18, - Getxattr = 22, Flush = 25, Init = 26, Opendir = 27, Readdir = 28, Releasedir = 29, Fsyncdir = 30, - Access = 34, Create = 35, Destroy = 38, } @@ -44,14 +42,12 @@ impl TryFrom for Opcode { 15 => Ok(Opcode::Read), 16 => Ok(Opcode::Write), 18 => Ok(Opcode::Release), - 22 => Ok(Opcode::Getxattr), 25 => Ok(Opcode::Flush), 26 => Ok(Opcode::Init), 27 => Ok(Opcode::Opendir), 28 => Ok(Opcode::Readdir), 29 => Ok(Opcode::Releasedir), 30 => Ok(Opcode::Fsyncdir), - 34 => Ok(Opcode::Access), 35 => Ok(Opcode::Create), 38 => Ok(Opcode::Destroy), _ => Err(new_vhost_user_fs_error("failed to decode opcode", None)), @@ -80,29 +76,6 @@ pub struct Attr { pub flags: u32, } -impl From for Attr { - fn from(st: libc::stat64) -> Attr { - Attr { - ino: st.st_ino, - size: st.st_size as u64, - blocks: st.st_blocks as u64, - atime: st.st_atime as u64, - mtime: st.st_mtime as u64, - ctime: st.st_ctime as u64, - atimensec: st.st_atime_nsec as u32, - mtimensec: st.st_mtime_nsec as u32, - ctimensec: st.st_ctime_nsec as u32, - mode: st.st_mode, - nlink: st.st_nlink as u32, - uid: st.st_uid, - gid: st.st_gid, - rdev: st.st_rdev as u32, - blksize: st.st_blksize as u32, - flags: 0, - } - } -} - #[repr(C)] #[derive(Debug, Default, Clone, Copy)] pub struct InHeader { @@ -197,6 +170,13 @@ pub struct MkdirIn { pub umask: u32, } +#[repr(C)] +#[derive(Debug, Default, Clone, Copy)] +pub struct OpenIn { + pub flags: u32, + pub open_flags: u32, +} + #[repr(C)] #[derive(Debug, Default, Clone, Copy)] pub struct OpenOut { @@ -245,6 +225,7 @@ unsafe impl ByteValued for EntryOut {} unsafe impl ByteValued for DirEntryOut {} unsafe impl ByteValued for CreateIn {} unsafe impl ByteValued for MkdirIn {} +unsafe impl ByteValued for OpenIn {} unsafe impl ByteValued for OpenOut {} unsafe impl ByteValued for WriteIn {} unsafe impl ByteValued for WriteOut {} diff --git a/test.sh b/test.sh deleted file mode 100755 index 97d1fc0..0000000 --- a/test.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash - -cargo build --release - -rm -rf share -mkdir share - -# export KIND=fs -# export ROOT=/home/zjregee/Code/virtio/ovfs/share - -export KIND=s3 -export BUCKET=test -export ENDPOINT=http://127.0.0.1:9000 -export ACCESS_KEY_ID=minioadmin -export SECRET_ACCESS_KEY=minioadmin -export REGION=us-east-1 - -RUST_LOG=debug ./target/release/ovfs