From 2f34dd807df4b5de2abdf0534fbb2ce8fb015c7e Mon Sep 17 00:00:00 2001 From: BeGild Date: Wed, 17 Jul 2024 08:49:01 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=BF=E6=8D=A2=E4=B8=BAfuser=5Fmt=20?= =?UTF-8?q?=E4=BD=BF=E7=94=A8high=E2=80=94=E2=80=94level=E7=9A=84=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E8=BF=9B=E8=A1=8C=E5=AE=9E=E7=8E=B0=E3=80=82=20?= =?UTF-8?q?=E5=A4=84=E7=90=86=E5=A5=BD=E4=BA=86=E5=90=8C=E6=AD=A5=E5=92=8C?= =?UTF-8?q?=E5=BC=82=E6=AD=A5=E7=9A=84code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 42 +++-- Cargo.toml | 3 +- proto/lws.proto | 1 + src/client.rs | 2 +- src/lib.rs | 396 +++++++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 411 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 703ad77..c520eb8 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -214,10 +214,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] -name = "fuser" -version = "0.14.0" +name = "fuse_mt" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e697f6f62c20b6fad1ba0f84ae909f25971cf16e735273524e3977c94604cf8" +checksum = "e098b8dc4cd32e9ba31d9c8cdfef11271d8191233c64c2a671432ff19d354948" +dependencies = [ + "fuser", + "libc", + "log", + "threadpool", +] + +[[package]] +name = "fuser" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21370f84640642c8ea36dfb2a6bfc4c55941f476fcf431f6fef25a5ddcf0169b" dependencies = [ "libc", "log", @@ -454,7 +466,8 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" name = "lws_client" version = "0.1.0" dependencies = [ - "fuser", + "fuse_mt", + "libc", "prost", "serde", "serde_json", @@ -534,9 +547,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "page_size" -version = "0.6.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" +checksum = "1b7663cbd190cfd818d08efa8497f6cd383076688c49a391ef7c0d03cd12b561" dependencies = [ "libc", "winapi", @@ -858,6 +871,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + [[package]] name = "tokio" version = "1.38.0" @@ -1213,9 +1235,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "zerocopy" -version = "0.7.35" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +checksum = "854e949ac82d619ee9a14c66a1b674ac730422372ccb759ce0c39cabcf2bf8e6" dependencies = [ "byteorder", "zerocopy-derive", @@ -1223,9 +1245,9 @@ dependencies = [ [[package]] name = "zerocopy-derive" -version = "0.7.35" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 566991b..23454f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,8 @@ prost = "0.12" tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -fuser = "0.14.0" +fuse_mt = "0.6.1" +libc = "0.2" [build-dependencies] tonic-build = "0.11" diff --git a/proto/lws.proto b/proto/lws.proto index ef8942b..834b5b4 100644 --- a/proto/lws.proto +++ b/proto/lws.proto @@ -76,6 +76,7 @@ message fstat { message getattr { string path = 1; fstat stat = 2; + file_info fi = 3; int32 ret = 15; } diff --git a/src/client.rs b/src/client.rs index f9a57a4..3a8f21a 100755 --- a/src/client.rs +++ b/src/client.rs @@ -8,7 +8,7 @@ async fn main() -> Result<(), Box> { return Err(e); } }; - match lws_ins.hello().await{ + match lws_ins.hello(){ Err(e) => { println!("lws client instance hello err {:?}", e); return Err(e); diff --git a/src/lib.rs b/src/lib.rs index b787cd1..6390ba4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,16 +1,14 @@ -use lws_client::HelloRequest; +use lws_client::{Getattr, HelloRequest, FileInfo}; +use std::borrow::BorrowMut; +use std::cell::RefCell; use std::{io::Read as _}; use std::fs::File; use std::error::Error; use std::collections::HashMap; use serde_json::{self, Value}; use lws_client::lws_vfs_client::LwsVfsClient; -use std::os::raw::c_int; +use std::ffi::{OsStr, OsString}; -use fuser::{ - KernelConfig, FileAttr, FileType, Filesystem, MountOption, ReplyAttr, ReplyData, ReplyDirectory, ReplyEntry, - Request, -}; pub mod lws_client { tonic::include_proto!("lws_vfs"); //导入lws vfs proto buffer @@ -66,43 +64,399 @@ impl Config { } } +struct vir_fs { + name: String, +} + pub struct LwsVfsIns { pub config: Config, - rpc: LwsVfsClient + rpc: RefCell>, + async_rt: tokio::runtime::Runtime, } -impl Filesystem for LwsVfsIns { - fn init(&mut self, _req: &Request, #[allow(unused_variables)] config: &mut KernelConfig, - ) -> Result<(), c_int> { - - Ok(()) +use fuse_mt::*; +use std::path::{Path, PathBuf}; +use std::time::{Duration, SystemTime}; +impl FilesystemMT for LwsVfsIns { + /// Called on mount, before any other function. + fn init(&self, _req: RequestInfo) -> ResultEmpty { + match self.hello() { + Ok(()) => { + Ok(()) + }, + Err(e) =>{ + Err(libc::ENOSYS) + } + } } -} + /// Called on filesystem unmount. + fn destroy(&self) { + // Nothing. + } + + /// Get the attributes of a filesystem entry. + /// + /// * `fh`: a file handle if this is called on an open file. + fn getattr(&self, _req: RequestInfo, path: &Path, _fh: Option) -> ResultEntry { + let request = tonic::Request::new(Getattr { + path : path.to_str().unwrap().into(), + fi : FileInfo { + + }, + ..Getattr::default() + }); + let response = self.async_rt.block_on(self.rpc.borrow_mut().fgetattr(request)); + Err(libc::ENOSYS) + } + + // The following operations in the FUSE C API are all one kernel call: setattr + // We split them out to match the C API's behavior. + + /// Change the mode of a filesystem entry. + /// + /// * `fh`: a file handle if this is called on an open file. + /// * `mode`: the mode to change the file to. + fn chmod(&self, _req: RequestInfo, _path: &Path, _fh: Option, _mode: u32) -> ResultEmpty { + Err(libc::ENOSYS) + } + + /// Change the owner UID and/or group GID of a filesystem entry. + /// + /// * `fh`: a file handle if this is called on an open file. + /// * `uid`: user ID to change the file's owner to. If `None`, leave the UID unchanged. + /// * `gid`: group ID to change the file's group to. If `None`, leave the GID unchanged. + fn chown(&self, _req: RequestInfo, _path: &Path, _fh: Option, _uid: Option, _gid: Option) -> ResultEmpty { + Err(libc::ENOSYS) + } + + /// Set the length of a file. + /// + /// * `fh`: a file handle if this is called on an open file. + /// * `size`: size in bytes to set as the file's length. + fn truncate(&self, _req: RequestInfo, _path: &Path, _fh: Option, _size: u64) -> ResultEmpty { + Err(libc::ENOSYS) + } + + /// Set timestamps of a filesystem entry. + /// + /// * `fh`: a file handle if this is called on an open file. + /// * `atime`: the time of last access. + /// * `mtime`: the time of last modification. + fn utimens(&self, _req: RequestInfo, _path: &Path, _fh: Option, _atime: Option, _mtime: Option) -> ResultEmpty { + Err(libc::ENOSYS) + } + + /// Set timestamps of a filesystem entry (with extra options only used on MacOS). + #[allow(clippy::too_many_arguments)] + fn utimens_macos(&self, _req: RequestInfo, _path: &Path, _fh: Option, _crtime: Option, _chgtime: Option, _bkuptime: Option, _flags: Option) -> ResultEmpty { + Err(libc::ENOSYS) + } + + // END OF SETATTR FUNCTIONS + + /// Read a symbolic link. + fn readlink(&self, _req: RequestInfo, _path: &Path) -> ResultData { + Err(libc::ENOSYS) + } + + /// Create a special file. + /// + /// * `parent`: path to the directory to make the entry under. + /// * `name`: name of the entry. + /// * `mode`: mode for the new entry. + /// * `rdev`: if mode has the bits `S_IFCHR` or `S_IFBLK` set, this is the major and minor numbers for the device file. Otherwise it should be ignored. + fn mknod(&self, _req: RequestInfo, _parent: &Path, _name: &OsStr, _mode: u32, _rdev: u32) -> ResultEntry { + Err(libc::ENOSYS) + } + + /// Create a directory. + /// + /// * `parent`: path to the directory to make the directory under. + /// * `name`: name of the directory. + /// * `mode`: permissions for the new directory. + fn mkdir(&self, _req: RequestInfo, _parent: &Path, _name: &OsStr, _mode: u32) -> ResultEntry { + Err(libc::ENOSYS) + } + + /// Remove a file. + /// + /// * `parent`: path to the directory containing the file to delete. + /// * `name`: name of the file to delete. + fn unlink(&self, _req: RequestInfo, _parent: &Path, _name: &OsStr) -> ResultEmpty { + Err(libc::ENOSYS) + } + + /// Remove a directory. + /// + /// * `parent`: path to the directory containing the directory to delete. + /// * `name`: name of the directory to delete. + fn rmdir(&self, _req: RequestInfo, _parent: &Path, _name: &OsStr) -> ResultEmpty { + Err(libc::ENOSYS) + } + + /// Create a symbolic link. + /// + /// * `parent`: path to the directory to make the link in. + /// * `name`: name of the symbolic link. + /// * `target`: path (may be relative or absolute) to the target of the link. + fn symlink(&self, _req: RequestInfo, _parent: &Path, _name: &OsStr, _target: &Path) -> ResultEntry { + Err(libc::ENOSYS) + } + + /// Rename a filesystem entry. + /// + /// * `parent`: path to the directory containing the existing entry. + /// * `name`: name of the existing entry. + /// * `newparent`: path to the directory it should be renamed into (may be the same as `parent`). + /// * `newname`: name of the new entry. + fn rename(&self, _req: RequestInfo, _parent: &Path, _name: &OsStr, _newparent: &Path, _newname: &OsStr) -> ResultEmpty { + Err(libc::ENOSYS) + } + + /// Create a hard link. + /// + /// * `path`: path to an existing file. + /// * `newparent`: path to the directory for the new link. + /// * `newname`: name for the new link. + fn link(&self, _req: RequestInfo, _path: &Path, _newparent: &Path, _newname: &OsStr) -> ResultEntry { + Err(libc::ENOSYS) + } + + /// Open a file. + /// + /// * `path`: path to the file. + /// * `flags`: one of `O_RDONLY`, `O_WRONLY`, or `O_RDWR`, plus maybe additional flags. + /// + /// Return a tuple of (file handle, flags). The file handle will be passed to any subsequent + /// calls that operate on the file, and can be any value you choose, though it should allow + /// your filesystem to identify the file opened even without any path info. + fn open(&self, _req: RequestInfo, _path: &Path, _flags: u32) -> ResultOpen { + Err(libc::ENOSYS) + } + + /// Read from a file. + /// + /// Note that it is not an error for this call to request to read past the end of the file, and + /// you should only return data up to the end of the file (i.e. the number of bytes returned + /// will be fewer than requested; possibly even zero). Do not extend the file in this case. + /// + /// * `path`: path to the file. + /// * `fh`: file handle returned from the `open` call. + /// * `offset`: offset into the file to stasync_rt reading. + /// * `size`: number of bytes to read. + /// * `callback`: a callback that must be invoked to return the result of the operation: either + /// the result data as a slice, or an error code. + /// + /// Return the return value from the `callback` function. + fn read(&self, _req: RequestInfo, _path: &Path, _fh: u64, _offset: u64, _size: u32, callback: impl FnOnce(ResultSlice<'_>) -> CallbackResult) -> CallbackResult { + callback(Err(libc::ENOSYS)) + } + + /// Write to a file. + /// + /// * `path`: path to the file. + /// * `fh`: file handle returned from the `open` call. + /// * `offset`: offset into the file to stasync_rt writing. + /// * `data`: the data to write + /// * `flags`: + /// + /// Return the number of bytes written. + fn write(&self, _req: RequestInfo, _path: &Path, _fh: u64, _offset: u64, _data: Vec, _flags: u32) -> ResultWrite { + Err(libc::ENOSYS) + } + + /// Called each time a program calls `close` on an open file. + /// + /// Note that because file descriptors can be duplicated (by `dup`, `dup2`, `fork`) this may be + /// called multiple times for a given file handle. The main use of this function is if the + /// filesystem would like to return an error to the `close` call. Note that most programs + /// ignore the return value of `close`, though. + /// + /// * `path`: path to the file. + /// * `fh`: file handle returned from the `open` call. + /// * `lock_owner`: if the filesystem supports locking (`setlk`, `getlk`), remove all locks + /// belonging to this lock owner. + fn flush(&self, _req: RequestInfo, _path: &Path, _fh: u64, _lock_owner: u64) -> ResultEmpty { + Err(libc::ENOSYS) + } + + /// Called when an open file is closed. + /// + /// There will be one of these for each `open` call. After `release`, no more calls will be + /// made with the given file handle. + /// + /// * `path`: path to the file. + /// * `fh`: file handle returned from the `open` call. + /// * `flags`: the flags passed when the file was opened. + /// * `lock_owner`: if the filesystem supports locking (`setlk`, `getlk`), remove all locks + /// belonging to this lock owner. + /// * `flush`: whether pending data must be flushed or not. + fn release(&self, _req: RequestInfo, _path: &Path, _fh: u64, _flags: u32, _lock_owner: u64, _flush: bool) -> ResultEmpty { + Err(libc::ENOSYS) + } + + /// Write out any pending changes of a file. + /// + /// When this returns, data should be written to persistent storage. + /// + /// * `path`: path to the file. + /// * `fh`: file handle returned from the `open` call. + /// * `datasync`: if `false`, also write metadata, otherwise just write file data. + fn fsync(&self, _req: RequestInfo, _path: &Path, _fh: u64, _datasync: bool) -> ResultEmpty { + Err(libc::ENOSYS) + } + + /// Open a directory. + /// + /// Analogous to the `opend` call. + /// + /// * `path`: path to the directory. + /// * `flags`: file access flags. Will contain `O_DIRECTORY` at least. + /// + /// Return a tuple of (file handle, flags). The file handle will be passed to any subsequent + /// calls that operate on the directory, and can be any value you choose, though it should + /// allow your filesystem to identify the directory opened even without any path info. + fn opendir(&self, _req: RequestInfo, _path: &Path, _flags: u32) -> ResultOpen { + Err(libc::ENOSYS) + } + + /// Get the entries of a directory. + /// + /// * `path`: path to the directory. + /// * `fh`: file handle returned from the `opendir` call. + /// + /// Return all the entries of the directory. + fn readdir(&self, _req: RequestInfo, _path: &Path, _fh: u64) -> ResultReaddir { + Err(libc::ENOSYS) + } + + /// Close an open directory. + /// + /// This will be called exactly once for each `opendir` call. + /// + /// * `path`: path to the directory. + /// * `fh`: file handle returned from the `opendir` call. + /// * `flags`: the file access flags passed to the `opendir` call. + fn releasedir(&self, _req: RequestInfo, _path: &Path, _fh: u64, _flags: u32) -> ResultEmpty { + Err(libc::ENOSYS) + } + + /// Write out any pending changes to a directory. + /// + /// Analogous to the `fsync` call. + fn fsyncdir(&self, _req: RequestInfo, _path: &Path, _fh: u64, _datasync: bool) -> ResultEmpty { + Err(libc::ENOSYS) + } + + /// Get filesystem statistics. + /// + /// * `path`: path to some folder in the filesystem. + /// + /// See the `Statfs` struct for more details. + fn statfs(&self, _req: RequestInfo, _path: &Path) -> ResultStatfs { + Err(libc::ENOSYS) + } + + /// Set a file extended attribute. + /// + /// * `path`: path to the file. + /// * `name`: attribute name. + /// * `value`: the data to set the value to. + /// * `flags`: can be either `XATTR_CREATE` or `XATTR_REPLACE`. + /// * `position`: offset into the attribute value to write data. + fn setxattr(&self, _req: RequestInfo, _path: &Path, _name: &OsStr, _value: &[u8], _flags: u32, _position: u32) -> ResultEmpty { + Err(libc::ENOSYS) + } + + /// Get a file extended attribute. + /// + /// * `path`: path to the file + /// * `name`: attribute name. + /// * `size`: the maximum number of bytes to read. + /// + /// If `size` is 0, return `Xattr::Size(n)` where `n` is the size of the attribute data. + /// Otherwise, return `Xattr::Data(data)` with the requested data. + fn getxattr(&self, _req: RequestInfo, _path: &Path, _name: &OsStr, _size: u32) -> ResultXattr { + Err(libc::ENOSYS) + } + + /// List extended attributes for a file. + /// + /// * `path`: path to the file. + /// * `size`: maximum number of bytes to return. + /// + /// If `size` is 0, return `Xattr::Size(n)` where `n` is the size required for the list of + /// attribute names. + /// Otherwise, return `Xattr::Data(data)` where `data` is all the null-terminated attribute + /// names. + fn listxattr(&self, _req: RequestInfo, _path: &Path, _size: u32) -> ResultXattr { + Err(libc::ENOSYS) + } + + /// Remove an extended attribute for a file. + /// + /// * `path`: path to the file. + /// * `name`: name of the attribute to remove. + fn removexattr(&self, _req: RequestInfo, _path: &Path, _name: &OsStr) -> ResultEmpty { + Err(libc::ENOSYS) + } + + /// Check for access to a file. + /// + /// * `path`: path to the file. + /// * `mask`: mode bits to check for access to. + /// + /// Return `Ok(())` if all requested permissions are allowed, otherwise return `Err(EACCES)` + /// or other error code as appropriate (e.g. `ENOENT` if the file doesn't exist). + fn access(&self, _req: RequestInfo, _path: &Path, _mask: u32) -> ResultEmpty { + Err(libc::ENOSYS) + } + + /// Create and open a new file. + /// + /// * `parent`: path to the directory to create the file in. + /// * `name`: name of the file to be created. + /// * `mode`: the mode to set on the new file. + /// * `flags`: flags like would be passed to `open`. + /// + /// Return a `CreatedEntry` (which contains the new file's attributes as well as a file handle + /// -- see documentation on `open` for more info on that). + fn create(&self, _req: RequestInfo, _parent: &Path, _name: &OsStr, _mode: u32, _flags: u32) -> ResultCreate { + Err(libc::ENOSYS) + } + +} +use std::env; impl LwsVfsIns { pub async fn new(json: &str) -> Result> { let config = Config::new(json)?; let addr = format!("http://{}:{}", config.get_addr(), config.get_port()); //connect to server - let rpc = LwsVfsClient::connect(addr).await?; + let rpc = RefCell::new(LwsVfsClient::connect(addr).await?); + let async_rt = tokio::runtime::Runtime::new().unwrap(); Ok(LwsVfsIns { config, rpc, + async_rt, }) } - pub async fn hello(&mut self) -> Result<(), Box> { + pub fn hello(&self) -> Result<(), Box> { let request = tonic::Request::new(HelloRequest { name: "Ekko lws hello".into(), }); - let response = &self.rpc.say_hello(request).await?; - + let response = self.async_rt.block_on(self.rpc.borrow_mut().say_hello(request))?; println!("RESPONSE={:?}", response); Ok(()) } - pub fn mount(file_system:FS) -> Result<(), Box> { - let mountpoint = "/mnt"; - let options = vec![MountOption::RO, MountOption::FSName("lws_fs".to_string())]; - fuser::mount2(file_system, mountpoint, &options).unwrap(); + + pub fn mount(file_system:F) -> Result<(), Box> + where + F: FilesystemMT + Sync + Send + 'static + { + let args: Vec = env::args_os().collect(); + let fuse_args = [OsStr::new("-o"), OsStr::new("fsname=passthrufs")]; + fuse_mt::mount(fuse_mt::FuseMT::new(file_system, 1), &args[2], &fuse_args[..])?; Ok(()) } }