From 6cc253d9c95beb39f7f3ac4fd68aab90f091c5e4 Mon Sep 17 00:00:00 2001 From: Wei Zhang Date: Mon, 30 Oct 2023 14:59:51 +0800 Subject: [PATCH] CI: add xfstests for passthrough fs Run xfstests on pathrough fs. Signed-off-by: Wei Zhang --- .github/workflows/xfstests.yml | 34 +++++ .gitignore | 2 +- tests/passthrough/Cargo.toml | 13 ++ tests/passthrough/src/main.rs | 201 +++++++++++++++++++++++++++ tests/scripts/xfstests_pathr.exclude | 11 ++ tests/scripts/xfstests_pathr.sh | 48 +++++++ 6 files changed, 308 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/xfstests.yml create mode 100644 tests/passthrough/Cargo.toml create mode 100644 tests/passthrough/src/main.rs create mode 100644 tests/scripts/xfstests_pathr.exclude create mode 100755 tests/scripts/xfstests_pathr.sh diff --git a/.github/workflows/xfstests.yml b/.github/workflows/xfstests.yml new file mode 100644 index 000000000..e8b104d84 --- /dev/null +++ b/.github/workflows/xfstests.yml @@ -0,0 +1,34 @@ +name: Rust + +on: + push: + branches: '*' + pull_request: + branches: [ master ] + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: 1 + +jobs: + xfstests_on_passthrough: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - name: Build passthrough binary + run: | + cd tests/passthrough + cargo build --release + sudo install -t /usr/sbin/ -m 700 ./target/release/passthrough + - name: Setup and run xfstest + run: | + cd $GITHUB_WORKSPACE + sudo ./tests/scripts/xfstests_pathr.sh + diff --git a/.gitignore b/.gitignore index 38e7a96cf..679e8900c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -/target +**/target **/*.rs.bk **/Cargo.lock **/.vscode diff --git a/tests/passthrough/Cargo.toml b/tests/passthrough/Cargo.toml new file mode 100644 index 000000000..d02f6aaa9 --- /dev/null +++ b/tests/passthrough/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "passthrough" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +fuse-backend-rs = { path = "../../", features = ["fusedev"] } +log = ">=0.4.6" +libc = ">=0.2.68" +simple_logger = ">=1.13.0" +signal-hook = ">=0.3.10" diff --git a/tests/passthrough/src/main.rs b/tests/passthrough/src/main.rs new file mode 100644 index 000000000..c4dce7bdf --- /dev/null +++ b/tests/passthrough/src/main.rs @@ -0,0 +1,201 @@ +use log::{error, info, warn, LevelFilter}; +use std::env; +use std::fs; +use std::io::{Error, Result}; +use std::path::Path; +use std::sync::Arc; +use std::thread; + +use signal_hook::{consts::TERM_SIGNALS, iterator::Signals}; + +use fuse_backend_rs::api::{server::Server, Vfs, VfsOptions}; +use fuse_backend_rs::passthrough::{Config, PassthroughFs}; +use fuse_backend_rs::transport::{FuseChannel, FuseSession}; + +use simple_logger::SimpleLogger; + +/// A fusedev daemon example +#[allow(dead_code)] +pub struct Daemon { + mountpoint: String, + server: Arc>>, + thread_cnt: u32, + session: Option, +} + +#[allow(dead_code)] +impl Daemon { + /// Creates a fusedev daemon instance + pub fn new(src: &str, mountpoint: &str, thread_cnt: u32) -> Result { + // create vfs + let vfs = Vfs::new(VfsOptions { + no_open: false, + no_opendir: false, + ..Default::default() + }); + + // create passthrough fs + let mut cfg = Config::default(); + cfg.root_dir = src.to_string(); + cfg.do_import = false; + let fs = PassthroughFs::<()>::new(cfg).unwrap(); + fs.import().unwrap(); + + // attach passthrough fs to vfs root + vfs.mount(Box::new(fs), "/").unwrap(); + + Ok(Daemon { + mountpoint: mountpoint.to_string(), + server: Arc::new(Server::new(Arc::new(vfs))), + thread_cnt, + session: None, + }) + } + + /// Mounts a fusedev daemon to the mountpoint, then start service threads to handle + /// FUSE requests. + pub fn mount(&mut self) -> Result<()> { + let mut se = + FuseSession::new(Path::new(&self.mountpoint), "testpassthrough", "", false).unwrap(); + se.mount().unwrap(); + for _ in 0..self.thread_cnt { + let mut server = FuseServer { + server: self.server.clone(), + ch: se.new_channel().unwrap(), + }; + let _thread = thread::Builder::new() + .name("fuse_server".to_string()) + .spawn(move || { + info!("new fuse thread"); + let _ = server.svc_loop(); + warn!("fuse service thread exits"); + }) + .unwrap(); + } + self.session = Some(se); + Ok(()) + } + + /// Umounts and destroies a fusedev daemon + pub fn umount(&mut self) -> Result<()> { + if let Some(mut se) = self.session.take() { + se.umount().unwrap(); + se.wake().unwrap(); + } + Ok(()) + } +} + +impl Drop for Daemon { + fn drop(&mut self) { + let _ = self.umount(); + } +} + +struct FuseServer { + server: Arc>>, + ch: FuseChannel, +} + +impl FuseServer { + fn svc_loop(&mut self) -> Result<()> { + // Given error EBADF, it means kernel has shut down this session. + let _ebadf = std::io::Error::from_raw_os_error(libc::EBADF); + loop { + if let Some((reader, writer)) = self + .ch + .get_request() + .map_err(|_| std::io::Error::from_raw_os_error(libc::EINVAL))? + { + if let Err(e) = self + .server + .handle_message(reader, writer.into(), None, None) + { + match e { + fuse_backend_rs::Error::EncodeMessage(_ebadf) => { + break; + } + _ => { + error!("Handling fuse message failed"); + continue; + } + } + } + } else { + info!("fuse server exits"); + break; + } + } + Ok(()) + } +} + +struct Args { + src: String, + dest: String, +} + +fn help() { + println!("Usage:\n passthrough \n"); +} + +fn parse_args() -> Result { + let args = env::args().collect::>(); + let cmd_args = Args { + src: args[1].clone(), + dest: args[2].clone(), + }; + if cmd_args.src.len() == 0 || cmd_args.dest.len() == 0 { + help(); + return Err(Error::from_raw_os_error(libc::EINVAL)); + } + Ok(cmd_args) +} + +fn main() -> Result<()> { + SimpleLogger::new() + .with_level(LevelFilter::Info) + .init() + .unwrap(); + let args = parse_args().unwrap(); + + // Check if src exists, create dir if not. + let src = Path::new(args.src.as_str()); + let src_dir = src.to_str().unwrap(); + if src.exists() { + if !src.is_dir() { + error!("src {} is not a directory", src_dir); + return Err(Error::from_raw_os_error(libc::EINVAL)); + } + } else { + fs::create_dir_all(src_dir).unwrap(); + } + + let dest = Path::new(args.dest.as_str()); + let dest_dir = dest.to_str().unwrap(); + if dest.exists() { + if !dest.is_dir() { + error!("dest {} is not a directory", dest_dir); + return Err(Error::from_raw_os_error(libc::EINVAL)); + } + } else { + fs::create_dir_all(dest_dir).unwrap(); + } + info!( + "test passthroughfs src {:?} mountpoint {}", + src_dir, dest_dir + ); + + let mut daemon = Daemon::new(src_dir, dest_dir, 2).unwrap(); + daemon.mount().unwrap(); + + // main thread + let mut signals = Signals::new(TERM_SIGNALS).unwrap(); + for _sig in signals.forever() { + break; + } + + daemon.umount().unwrap(); + + Ok(()) +} diff --git a/tests/scripts/xfstests_pathr.exclude b/tests/scripts/xfstests_pathr.exclude new file mode 100644 index 000000000..aa0cbab5a --- /dev/null +++ b/tests/scripts/xfstests_pathr.exclude @@ -0,0 +1,11 @@ +# Exclude list for tests that we know are broken in passthrough fs +# +generic/002 +generic/184 +generic/426 +generic/434 +generic/467 +generic/471 +generic/477 +generic/591 +generic/633 \ No newline at end of file diff --git a/tests/scripts/xfstests_pathr.sh b/tests/scripts/xfstests_pathr.sh new file mode 100755 index 000000000..05155eced --- /dev/null +++ b/tests/scripts/xfstests_pathr.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +current_dir=$(dirname $(realpath $0)) + +sudo apt-get update +sudo apt-get install acl attr automake bc dbench dump e2fsprogs fio gawk \ + gcc git indent libacl1-dev libaio-dev libcap-dev libgdbm-dev libtool \ + libtool-bin liburing-dev libuuid1 lvm2 make psmisc python3 quota sed \ + uuid-dev uuid-runtime xfsprogs linux-headers-$(uname -r) sqlite3 \ + exfatprogs f2fs-tools ocfs2-tools udftools xfsdump \ + xfslibs-dev + +# clone xfstests and install. +cd /tmp/ +git clone git://git.kernel.org/pub/scm/fs/xfs/xfstests-dev.git +cd xfstests-dev +make +sudo make install +# overwrite local config. +cat >local.config </usr/sbin/mount.fuse.testpassthrough <>/tmp/testpassthrough.log 2>&1 & +sleep 1 +EOF +sudo chmod +x /usr/sbin/mount.fuse.testpassthrough + +# create related dirs. +mkdir -p /tmp/pathr_src /tmp/pathr_dst + +echo "====> Start to run xfstests." +# run tests. +cd /tmp/xfstests-dev +# Some tests are not supported by fuse or cannot pass currently. +sudo ./check -fuse -E $current_dir/xfstests_pathr.exclude + +