[PATCH] erofs-utils: rust: add rust version fuse program
Yiyang Wu
toolmanp at tlmp.cc
Sun Sep 29 12:58:30 AEST 2024
This patch adds new configure script for checking
cross-compiling rustc/cargo/rustup and build erofsfuse
Rust version fuse based on autotools. The Xattr feature
hasn't been completed yet.
The program is the same copy from erofs-rs repo[1]
[1]: https://github.com/ToolmanP/erofs-rs
Signed-off-by: Yiyang Wu <toolmanp at tlmp.cc>
---
Makefile.am | 5 +
configure.ac | 48 ++++-
rust/fuse/Cargo.lock | 359 ++++++++++++++++++++++++++++++++++++++
rust/fuse/Cargo.toml | 9 +
rust/fuse/Makefile.am | 14 ++
rust/fuse/src/main.rs | 394 ++++++++++++++++++++++++++++++++++++++++++
6 files changed, 828 insertions(+), 1 deletion(-)
create mode 100644 rust/fuse/Cargo.lock
create mode 100644 rust/fuse/Cargo.toml
create mode 100644 rust/fuse/Makefile.am
create mode 100644 rust/fuse/src/main.rs
diff --git a/Makefile.am b/Makefile.am
index fc464e8..959b123 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3,6 +3,11 @@
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = man lib mkfs dump fsck
+
if ENABLE_FUSE
+if ENABLE_RUST
+SUBDIRS += rust/fuse
+else
SUBDIRS += fuse
endif
+endif
diff --git a/configure.ac b/configure.ac
index 9c1657b..38e1ae6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -171,6 +171,15 @@ AC_ARG_WITH(selinux,
;;
esac], [with_selinux=no])
+AC_ARG_WITH(rust,
+ [AS_HELP_STRING([--with-rust],
+ [enable and build with rust implementation @<:@default=no@:>@])],
+ [case "$with_rust" in
+ yes|no) ;;
+ *) AC_MSG_ERROR([invalid argument to --with-rust])
+ ;;
+ esac], [with_rust=no])
+
# Checks for libraries.
# Use customized LZ4 library path when specified.
AC_ARG_WITH(lz4-incdir,
@@ -364,6 +373,37 @@ AS_IF([test "x$with_selinux" != "xno"], [
LIBS="${saved_LIBS}"
CPPFLAGS="${saved_CPPFLAGS}"], [have_selinux="no"])
+# Configure Rust
+AS_IF([test "x$with_rust" != "xno"], [
+ RUSTC_TARGET=""
+ RUSTC_MODE=""
+ case "$build" in
+ *linux*)
+ arch="${build%%-*}"
+ RUSTC_TARGET="${arch}-unknown-linux-gnu"
+ ;;
+ *apple*)
+ AC_MSG_ERROR(["Apple Rust Build isn't supported currently."])
+ ;;
+ esac
+ AC_PATH_PROG([RUSTUP], [rustup], [notfound])
+ AS_IF([test "x$RUSTUP" = "xnotfound"], [AC_MSG_ERROR([rustup is required for rust build.])])
+ AC_PATH_PROG([CARGO], [cargo], [notfound])
+ AS_IF([test "x$CARGO" = "xnotfound"], [AC_MSG_ERROR([cargo is required for rust build.])])
+ AC_PATH_PROG([RUSTC], [rustc], [notfound])
+ AS_IF([test "x$RUSTC" = "xnotfound"], [AC_MSG_ERROR([rustc is required for rust build.])])
+ AC_MSG_CHECKING([whether rustc has target $RUSTC_TARGET installed])
+ AS_IF([test "xCFLAGS" = *-g*], [RUSTC_MODE="debug"], [RUSTC_MODE="release"])
+ if $RUSTUP target list | grep -q $RUSTC_TARGET; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ AC_MSG_ERROR([Rust tatget $RUSTC_TARGET is not installed. Please run 'rustup target add $RUSTC_TARGET' to install it.])
+ fi
+ AC_SUBST(RUSTC_TARGET)
+ AC_SUBST(RUSTC_MODE)
+], [have_rust="no"])
+
# Configure fuse
AS_IF([test "x$enable_fuse" != "xno"], [
# Paranoia: don't trust the result reported by pkgconfig before trying out
@@ -566,6 +606,7 @@ AM_CONDITIONAL([ENABLE_LIBDEFLATE], [test "x${have_libdeflate}" = "xyes"])
AM_CONDITIONAL([ENABLE_LIBZSTD], [test "x${have_libzstd}" = "xyes"])
AM_CONDITIONAL([ENABLE_QPL], [test "x${have_qpl}" = "xyes"])
AM_CONDITIONAL([ENABLE_STATIC_FUSE], [test "x${enable_static_fuse}" = "xyes"])
+AM_CONDITIONAL([ENABLE_RUST], [test "x${with_rust}" = "xyes"])
if test "x$have_uuid" = "xyes"; then
AC_DEFINE([HAVE_LIBUUID], 1, [Define to 1 if libuuid is found])
@@ -575,6 +616,10 @@ if test "x$have_selinux" = "xyes"; then
AC_DEFINE([HAVE_LIBSELINUX], 1, [Define to 1 if libselinux is found])
fi
+if test "x${with_rust}" = "xyes"; then
+ AC_DEFINE([HAVE_RUST], 1, [Define to 1 if rust is enabled.])
+fi
+
if test "x${have_lz4}" = "xyes"; then
AC_DEFINE([LZ4_ENABLED], [1], [Define to 1 if lz4 is enabled.])
@@ -634,5 +679,6 @@ AC_CONFIG_FILES([Makefile
mkfs/Makefile
dump/Makefile
fuse/Makefile
- fsck/Makefile])
+ fsck/Makefile
+ rust/fuse/Makefile])
AC_OUTPUT
diff --git a/rust/fuse/Cargo.lock b/rust/fuse/Cargo.lock
new file mode 100644
index 0000000..d4f72d2
--- /dev/null
+++ b/rust/fuse/Cargo.lock
@@ -0,0 +1,359 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "anstream"
+version = "0.6.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
+dependencies = [
+ "anstyle",
+ "windows-sys",
+]
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "clap"
+version = "4.5.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.5.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
+
+[[package]]
+name = "erofs-sys"
+version = "0.1.0"
+source = "git+https://github.com/ToolmanP/erofs-rs.git#b2b45d5e7fa5eebe48f5bc2a21c48fff820df8a1"
+
+[[package]]
+name = "erofsfuse"
+version = "0.1.0"
+dependencies = [
+ "clap",
+ "erofs-sys",
+ "fuser",
+]
+
+[[package]]
+name = "fuser"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "104ed58f182bc2975062cd3fab229e82b5762de420e26cf5645f661402694599"
+dependencies = [
+ "libc",
+ "log",
+ "memchr",
+ "page_size",
+ "pkg-config",
+ "smallvec",
+ "users",
+ "zerocopy",
+]
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "is_terminal_polyfill"
+version = "1.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
+
+[[package]]
+name = "libc"
+version = "0.2.159"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
+
+[[package]]
+name = "log"
+version = "0.4.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "page_size"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eebde548fbbf1ea81a99b128872779c437752fb99f217c45245e1a61dcd9edcd"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "syn"
+version = "2.0.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
+
+[[package]]
+name = "users"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032"
+dependencies = [
+ "libc",
+ "log",
+]
+
+[[package]]
+name = "utf8parse"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+
+[[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"
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "zerocopy"
+version = "0.6.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "854e949ac82d619ee9a14c66a1b674ac730422372ccb759ce0c39cabcf2bf8e6"
+dependencies = [
+ "byteorder",
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.6.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
diff --git a/rust/fuse/Cargo.toml b/rust/fuse/Cargo.toml
new file mode 100644
index 0000000..26a63d3
--- /dev/null
+++ b/rust/fuse/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "erofsfuse"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+fuser = "0.11"
+erofs-sys = { git = "https://github.com/ToolmanP/erofs-rs.git" }
+clap = { version = "4", features = ["derive", "cargo"] }
diff --git a/rust/fuse/Makefile.am b/rust/fuse/Makefile.am
new file mode 100644
index 0000000..6b0e9fe
--- /dev/null
+++ b/rust/fuse/Makefile.am
@@ -0,0 +1,14 @@
+AUTOMAKE_OPTIONS = foreign
+
+EXTRA_PROGRAMS = target/$(RUSTC_TARGET)/$(RUSTC_MODE)/erofsfuse
+EXTRA_DIST = src/main.rs Cargo.toml Cargo.lock
+
+all-local:
+ $(CARGO) build -vvv --$(RUSTC_MODE) --target $(RUSTC_TARGET)
+
+install-exec-local:
+ $(MKDIR_P) $(DESTDIR)$(bindir)
+ $(INSTALL_PROGRAM) target/$(RUSTC_TARGET)/$(RUSTC_MODE)/erofsfuse-rs $(DESTDIR)$(bindir)/erofsfuse
+
+clean-local:
+ $(CARGO) clean
diff --git a/rust/fuse/src/main.rs b/rust/fuse/src/main.rs
new file mode 100644
index 0000000..c929e2f
--- /dev/null
+++ b/rust/fuse/src/main.rs
@@ -0,0 +1,394 @@
+use clap::{arg, Parser};
+use erofs_sys::data::backends::uncompressed::UncompressedBackend;
+use erofs_sys::data::*;
+use erofs_sys::dir::DirentDesc;
+use erofs_sys::errnos::Errno::*;
+use erofs_sys::file::ImageFileSystem;
+use erofs_sys::inode::*;
+use erofs_sys::operations::*;
+use erofs_sys::superblock::FileSystem as ErofsFileSystem;
+use erofs_sys::superblock::SuperBlock;
+use erofs_sys::xattrs::*;
+use erofs_sys::{Nid, Off, PosixResult};
+use fuser::Filesystem as FuseFileSystem;
+use fuser::MountOption;
+use fuser::{
+ FileAttr, FileType, ReplyAttr, ReplyData, ReplyDirectory, ReplyEntry, Request, FUSE_ROOT_ID,
+};
+use std::collections::{hash_map::Entry, HashMap};
+use std::ffi::OsStr;
+use std::ffi::*;
+use std::fs::File;
+use std::mem::size_of;
+use std::os::unix::ffi::OsStrExt;
+use std::os::unix::fs::FileExt;
+use std::path::Path;
+use std::sync::{Arc, Mutex};
+use std::time::Duration;
+use std::time::SystemTime;
+use std::time::UNIX_EPOCH;
+
+struct FuseCollection(HashMap<Nid, SimpleInode>);
+struct FuseFile(File);
+
+impl Source for FuseFile {
+ fn fill(&self, data: &mut [u8], _device_id: i32, offset: Off) -> PosixResult<u64> {
+ self.0
+ .read_at(data, offset)
+ .map_or(Err(ERANGE), |size| Ok(size as u64))
+ }
+}
+
+impl FileSource for FuseFile {}
+
+struct SimpleInode {
+ info: InodeInfo,
+ xattr_shared_entries: XAttrSharedEntries,
+ nid: Nid,
+}
+
+impl Inode for SimpleInode {
+ fn new(_sb: &SuperBlock, info: InodeInfo, nid: Nid, xattr_header: XAttrSharedEntries) -> Self {
+ Self {
+ info,
+ xattr_shared_entries: xattr_header,
+ nid,
+ }
+ }
+ fn xattrs_shared_entries(&self) -> &XAttrSharedEntries {
+ &self.xattr_shared_entries
+ }
+ fn nid(&self) -> Nid {
+ self.nid
+ }
+ fn info(&self) -> &InodeInfo {
+ &self.info
+ }
+}
+
+impl InodeCollection for FuseCollection {
+ type I = SimpleInode;
+ fn iget(&mut self, nid: Nid, f: &dyn ErofsFileSystem<Self::I>) -> PosixResult<&mut Self::I> {
+ match self.0.entry(nid) {
+ Entry::Vacant(v) => {
+ let info = f.read_inode_info(nid)?;
+ let xattrs_header = f.read_inode_xattrs_shared_entries(nid, &info)?;
+ Ok(v.insert(Self::I::new(f.superblock(), info, nid, xattrs_header)))
+ }
+ Entry::Occupied(o) => Ok(o.into_mut()),
+ }
+ }
+ fn release(&mut self, nid: Nid) {
+ self.0.remove(&nid);
+ }
+}
+
+struct ErofsFuse {
+ filesystem: Box<dyn ErofsFileSystem<SimpleInode>>,
+ collection: FuseCollection,
+}
+
+fn system_time_from_time(secs: i64, nsecs: u32) -> SystemTime {
+ if secs >= 0 {
+ UNIX_EPOCH + Duration::new(secs as u64, nsecs)
+ } else {
+ UNIX_EPOCH - Duration::new((-secs) as u64, nsecs)
+ }
+}
+
+fn file_type_from_type(ty: Type) -> FileType {
+ match ty {
+ Type::Regular => FileType::RegularFile,
+ Type::Directory => FileType::Directory,
+ Type::Link => FileType::Symlink,
+ Type::Fifo => FileType::NamedPipe,
+ Type::Character => FileType::CharDevice,
+ Type::Block => FileType::BlockDevice,
+ Type::Socket => FileType::Socket,
+ Type::Unknown => panic!("Unknown Type"),
+ }
+}
+
+fn get_file_attr_from_filesystem_inode(inode: &SimpleInode, sb: &SuperBlock) -> FileAttr {
+ match *inode.info() {
+ InodeInfo::Extended(e) => FileAttr {
+ atime: system_time_from_time(e.i_mtime as i64, e.i_mtime_nsec),
+ ino: inode.nid() + FUSE_ROOT_ID,
+ size: e.i_size,
+ blocks: sb.blk_round_up_generic(e.i_size) as u64,
+ mtime: system_time_from_time(e.i_mtime as i64, e.i_mtime_nsec),
+ ctime: system_time_from_time(e.i_mtime as i64, e.i_mtime_nsec),
+ crtime: system_time_from_time(e.i_mtime as i64, e.i_mtime_nsec),
+ perm: inode.info().inode_perm(),
+ kind: file_type_from_type(inode.info().inode_type()),
+ nlink: e.i_nlink,
+ blksize: 512,
+ uid: e.i_uid,
+ gid: e.i_gid,
+ rdev: 0,
+ flags: 0,
+ },
+ InodeInfo::Compact(c) => FileAttr {
+ atime: system_time_from_time(sb.build_time, sb.build_time_nsec as u32),
+ ino: inode.nid() + FUSE_ROOT_ID,
+ size: c.i_size as u64,
+ blocks: sb.blk_round_up_generic(c.i_size as u64) as u64,
+ mtime: system_time_from_time(sb.build_time, sb.build_time_nsec as u32),
+ ctime: system_time_from_time(sb.build_time, sb.build_time_nsec as u32),
+ crtime: system_time_from_time(sb.build_time, sb.build_time_nsec as u32),
+ perm: inode.info().inode_perm(),
+ kind: file_type_from_type(inode.info().inode_type()),
+ nlink: c.i_nlink as u32,
+ blksize: 512,
+ uid: c.i_uid as u32,
+ gid: c.i_gid as u32,
+ rdev: 0,
+ flags: 0,
+ },
+ }
+}
+
+fn filetype_from_dtype(ty: u8) -> FileType {
+ match ty {
+ 1 => FileType::RegularFile,
+ 2 => FileType::Directory,
+ 3 => FileType::CharDevice,
+ 4 => FileType::BlockDevice,
+ 5 => FileType::NamedPipe,
+ 6 => FileType::Socket,
+ 7 => FileType::Symlink,
+ _ => panic!("unknown"),
+ }
+}
+
+fn nid_to_ino(sb: &SuperBlock, nid: Nid) -> u64 {
+ if nid == sb.root_nid as u64 {
+ FUSE_ROOT_ID
+ } else {
+ nid + FUSE_ROOT_ID
+ }
+}
+impl ErofsFuse {
+ fn ino_to_nid(&self, ino: u64) -> Nid {
+ if ino == FUSE_ROOT_ID {
+ self.filesystem.superblock().root_nid as u64
+ } else {
+ ino - FUSE_ROOT_ID
+ }
+ }
+ fn nid_to_ino(&self, nid: Nid) -> u64 {
+ if nid == self.filesystem.superblock().root_nid as u64 {
+ FUSE_ROOT_ID
+ } else {
+ nid + FUSE_ROOT_ID
+ }
+ }
+ fn try_read_link(&mut self, ino: u64) -> PosixResult<Vec<u8>> {
+ let inode = self
+ .collection
+ .iget(self.ino_to_nid(ino), self.filesystem.as_filesystem())?;
+ let mut symlink: Vec<u8> = Vec::new();
+ for res in self.filesystem.mapped_iter(inode, 0)? {
+ let block = res?;
+ let data = block.content();
+ symlink.extend_from_slice(data);
+ }
+ symlink.push(b'\0');
+ Ok(symlink)
+ }
+ fn try_read(&mut self, ino: u64, offset: i64, mut size: u32) -> PosixResult<Vec<u8>> {
+ let inode = self
+ .collection
+ .iget(self.ino_to_nid(ino), self.filesystem.as_filesystem())?;
+ let mut result: Vec<u8> = Vec::new();
+ for res in self.filesystem.mapped_iter(inode, offset as u64)? {
+ let block = res?;
+ let data = block.content();
+ let nsize = data.len().min(size as usize);
+ result.extend_from_slice(&data[..nsize]);
+ size -= nsize as u32;
+ if size == 0 {
+ break;
+ }
+ }
+ Ok(result)
+ }
+}
+const TTL: Duration = Duration::from_secs(1); // 1 second
+impl FuseFileSystem for ErofsFuse {
+ fn init(&mut self, _req: &Request<'_>, _config: &mut fuser::KernelConfig) -> Result<(), c_int> {
+ Ok(())
+ }
+ fn readdir(
+ &mut self,
+ _req: &Request<'_>,
+ ino: u64,
+ _fh: u64,
+ offset: i64,
+ mut reply: ReplyDirectory,
+ ) {
+ let sb = self.filesystem.superblock();
+ match self
+ .collection
+ .iget(self.ino_to_nid(ino), self.filesystem.as_filesystem())
+ {
+ Ok(inode) => {
+ let mut count = 1;
+ match self
+ .filesystem
+ .fill_dentries(inode, 0, offset as u64, &mut |dirent, _| {
+ if reply.add(
+ nid_to_ino(sb, dirent.desc().nid),
+ count + 1,
+ filetype_from_dtype(dirent.desc().file_type),
+ OsStr::from_bytes(dirent.dirname()),
+ ) {
+ true
+ } else {
+ count += 1;
+ false
+ }
+ }) {
+ Ok(()) => reply.ok(),
+ Err(e) => reply.error(e as i32),
+ }
+ }
+ Err(e) => reply.error(e as i32),
+ }
+ }
+ fn lookup(
+ &mut self,
+ _req: &Request<'_>,
+ parent: u64,
+ _name: &std::ffi::OsStr,
+ reply: ReplyEntry,
+ ) {
+ let nid = self.ino_to_nid(parent);
+ match lookup(
+ self.filesystem.as_filesystem(),
+ &mut self.collection,
+ nid,
+ _name.to_str().unwrap(),
+ ) {
+ Ok(inode) => {
+ reply.entry(
+ &TTL,
+ &get_file_attr_from_filesystem_inode(inode, self.filesystem.superblock()),
+ 0,
+ );
+ }
+ Err(e) => reply.error(e as i32),
+ }
+ }
+ fn getattr(&mut self, _req: &Request<'_>, ino: u64, reply: ReplyAttr) {
+ match self
+ .collection
+ .iget(self.ino_to_nid(ino), self.filesystem.as_filesystem())
+ {
+ Ok(inode) => reply.attr(
+ &TTL,
+ &get_file_attr_from_filesystem_inode(inode, self.filesystem.superblock()),
+ ),
+ Err(e) => reply.error(e as i32),
+ }
+ }
+
+ fn readlink(&mut self, _req: &Request<'_>, ino: u64, reply: ReplyData) {
+ match self.try_read_link(ino) {
+ Ok(symlink) => reply.data(&symlink),
+ Err(e) => reply.error(e as i32),
+ }
+ }
+
+ fn read(
+ &mut self,
+ _req: &Request,
+ ino: u64,
+ _fh: u64,
+ offset: i64,
+ size: u32,
+ _flags: i32,
+ _lock: Option<u64>,
+ reply: ReplyData,
+ ) {
+ match self.try_read(ino, offset, size) {
+ Ok(data) => reply.data(&data),
+ Err(e) => reply.error(e as i32),
+ }
+ }
+ fn open(&mut self, _req: &Request<'_>, ino: u64, _flags: i32, reply: fuser::ReplyOpen) {
+ match self
+ .collection
+ .iget(self.ino_to_nid(ino), self.filesystem.as_filesystem())
+ {
+ Ok(_) => reply.opened(0, 0),
+ Err(e) => reply.error(e as i32),
+ }
+ }
+ fn opendir(&mut self, _req: &Request<'_>, ino: u64, _flags: i32, reply: fuser::ReplyOpen) {
+ match self
+ .collection
+ .iget(self.ino_to_nid(ino), self.filesystem.as_filesystem())
+ {
+ Ok(_) => reply.opened(0, 0),
+ Err(e) => reply.error(e as i32),
+ }
+ }
+ fn releasedir(
+ &mut self,
+ _req: &Request<'_>,
+ ino: u64,
+ _fh: u64,
+ _flags: i32,
+ reply: fuser::ReplyEmpty,
+ ) {
+ self.collection.release(self.ino_to_nid(ino));
+ reply.ok()
+ }
+ fn release(
+ &mut self,
+ _req: &Request<'_>,
+ ino: u64,
+ _fh: u64,
+ _flags: i32,
+ _lock_owner: Option<u64>,
+ _flush: bool,
+ reply: fuser::ReplyEmpty,
+ ) {
+ self.collection.release(self.ino_to_nid(ino));
+ reply.ok()
+ }
+}
+
+#[derive(Parser, Debug)]
+#[command(version, about, long_about = None)]
+struct ErofsArgs {
+ #[arg(short, long)]
+ image: String,
+ #[arg(short, long)]
+ mountpoint: String,
+}
+fn main() {
+ let args = ErofsArgs::parse();
+ let file = File::options()
+ .read(true)
+ .write(true)
+ .open(Path::new(&args.image))
+ .unwrap();
+ let filesystem =
+ Box::new(ImageFileSystem::try_new(UncompressedBackend::new(FuseFile(file))).unwrap());
+ let collection = FuseCollection(HashMap::new());
+ let erofs_fuse = ErofsFuse {
+ filesystem,
+ collection,
+ };
+ fuser::mount2(
+ erofs_fuse,
+ args.mountpoint,
+ &[
+ MountOption::FSName("erofs_fuse_rs".to_string()),
+ MountOption::AutoUnmount,
+ ],
+ )
+ .unwrap()
+}
--
2.46.1
More information about the Linux-erofs
mailing list