[RFC PATCH 2/3] erofs: add implementation for erofs_sys
Yiyang Wu
toolmanp at tlmp.cc
Thu Aug 8 04:47:02 AEST 2024
Implements Inode, InodeCollection, MemorySource in Kernel Space.
Exposes VFS Compatible Function Pointers to the Kernel Space.
Note that currently vfs abstraction support is not completed,
a lot of c bindings are used here.
This commit also provides some helper functions to be exported
to rust so that rust can call inline functions not generated by bindgen.
Signed-off-by: Yiyang Wu <toolmanp at tlmp.cc>
---
fs/erofs/erofs_rust.rs | 294 ++++++++++++++++++++++++++
fs/erofs/erofs_rust_bindings.h | 47 ++++
fs/erofs/erofs_rust_helper.c | 107 ++++++++++
fs/erofs/erofs_rust_helper.h | 40 ++++
fs/erofs/rust/erofs_sys.rs | 67 ++++++
fs/erofs/rust/kinode.rs | 103 +++++++++
fs/erofs/rust/kinode/kinode_helper.rs | 26 +++
fs/erofs/rust/mod.rs | 6 +
fs/erofs/rust/sources.rs | 5 +
fs/erofs/rust/sources/mm.rs | 62 ++++++
fs/erofs/rust/sources/page_helper.rs | 12 ++
11 files changed, 769 insertions(+)
create mode 100644 fs/erofs/erofs_rust.rs
create mode 100644 fs/erofs/erofs_rust_bindings.h
create mode 100644 fs/erofs/erofs_rust_helper.c
create mode 100644 fs/erofs/erofs_rust_helper.h
create mode 100644 fs/erofs/rust/erofs_sys.rs
create mode 100644 fs/erofs/rust/kinode.rs
create mode 100644 fs/erofs/rust/kinode/kinode_helper.rs
create mode 100644 fs/erofs/rust/mod.rs
create mode 100644 fs/erofs/rust/sources.rs
create mode 100644 fs/erofs/rust/sources/mm.rs
create mode 100644 fs/erofs/rust/sources/page_helper.rs
diff --git a/fs/erofs/erofs_rust.rs b/fs/erofs/erofs_rust.rs
new file mode 100644
index 000000000000..c46876ded0ef
--- /dev/null
+++ b/fs/erofs/erofs_rust.rs
@@ -0,0 +1,294 @@
+// Copyright 2024 Yiyang Wu
+// SPDX-License-Identifier: MIT or GPL-2.0-only
+
+//! EROFS Rust Kernel Module Helpers Implementation
+//! This is only for experimental purpose. This is not guaranteed to work.
+//! Note that this module must be rewritten after the rust VFS abstraction is merged.
+//! Currently a lot of c bindings are used which violate the principles provided by Rust-For-Linux
+
+#[allow(dead_code)]
+#[allow(missing_docs)]
+pub(crate) mod rust;
+use core::ffi::c_void;
+use core::ptr::NonNull;
+use kernel::bindings::{
+ address_space, d_make_root, d_obtain_alias, d_splice_alias, dentry, dir_context, file, inode,
+ inode_init_once, super_block,
+};
+use kernel::types::ForeignOwnable;
+use kernel::{
+ container_of,
+ prelude::{Box, Vec},
+};
+use rust::{
+ erofs_sys::{
+ alloc_helper::*,
+ data::uncompressed::*,
+ data::*,
+ inode::*,
+ map::*,
+ operations::*,
+ superblock::{mem::*, *},
+ *,
+ },
+ kinode::{KernelInode, KernelInodeCollection},
+ sources::mm::FolioSource,
+};
+
+/// This is just a log prefix for kernel module so that pr_info can be used
+pub const __LOG_PREFIX: &[u8] = b"erofs_rust: \0";
+
+/// KernelSuperblockInfo defined by embedded Kernel Inode
+pub(crate) type KernelSuperblockInfo = SuperblockInfo<KernelInode, KernelInodeCollection>;
+
+/// Allocating a rust implementation of super_block_info c_void when calling from fill_super
+/// Note that this function needs a fixed address_space before we can use the vfs vtable to bind the
+/// operations. This is left as it is for now.
+#[no_mangle]
+pub unsafe extern "C" fn erofs_alloc_sbi_rust(
+ sb: NonNull<super_block>,
+ address_space: NonNull<address_space>,
+) -> *const c_void {
+ // We have to use heap_alloc here to erase the signature of MemFileSystem
+ let sb = heap_alloc(SuperblockInfo::new(
+ heap_alloc(MemFileSystem::new(UncompressedBackend::new(
+ FolioSource::new(address_space),
+ ))),
+ KernelInodeCollection::new(sb),
+ ));
+ return sb.into_foreign();
+}
+
+/// SAFETY:
+/// Cast the c_void back to KernelSuperblockInfo.
+/// This seems to prune to some concurrency issues
+/// but the fact is that only KernelInodeCollection field can have mutability.
+/// However, it's backed by the original iget_locked5 and it's already preventing
+/// any concurrency issues. So it's safe to be casted mutable here even if it's not backed by
+/// Arc/Mutex instead of using generic method from Foriegn Ownable which only provides
+/// immutable reference casting.
+fn as_sbi(f: *mut c_void) -> &'static mut KernelSuperblockInfo {
+ unsafe { &mut *(f as *mut KernelSuperblockInfo) }
+}
+
+/// Free the KernelSuperblockInfo that was embedded into the superblock in the c implementation
+#[no_mangle]
+pub unsafe extern "C" fn erofs_free_sbi_rust(sb: *mut super_block) {
+ // this will simply get dropped immediately after receiving the ownership.
+ unsafe { drop(Box::<KernelSuperblockInfo>::from_foreign((*sb).s_fs_info)) };
+}
+
+/// A helper sturct to map blocks for iomap_begin because iomap is not generated by bindgen
+#[repr(C)]
+pub struct ErofsRustMap {
+ m_pa: u64,
+ m_la: u64,
+ m_plen: u64,
+ m_llen: u64,
+ m_flags: u32,
+ inline_data: *const core::ffi::c_void,
+ private: *const core::ffi::c_void,
+}
+
+/// Lookup function for dentry-inode lookup replacement
+#[no_mangle]
+pub unsafe extern "C" fn erofs_lookup_rust(
+ k_inode: *mut inode,
+ dentry: *mut dentry,
+ _flags: u32,
+) -> *mut dentry {
+ // SAFETY: We are sure that the inode is a Kernel Inode since alloc_inode is called
+ let inode = unsafe { &*container_of!(k_inode, KernelInode, k_inode) };
+ let erofs_sbi = as_sbi(unsafe { (*(*k_inode).i_sb).s_fs_info });
+
+ // SAFETY: this is backed by qstr which is c representation of a valid slice.
+ let name = unsafe {
+ core::str::from_utf8_unchecked(core::slice::from_raw_parts(
+ (*dentry).d_name.name,
+ (*dentry).d_name.__bindgen_anon_1.__bindgen_anon_1.len as usize,
+ ))
+ };
+
+ let k_inode: *mut inode = dir_lookup(
+ erofs_sbi.filesystem.as_ref(),
+ &mut erofs_sbi.inodes,
+ inode,
+ name,
+ )
+ .map_or(core::ptr::null_mut(), |result| result.k_inode.as_mut_ptr());
+
+ // SAFETY: We are sure that the inner k_inode has already been initialized
+ unsafe { d_splice_alias(k_inode, dentry) }
+}
+
+/// Get parent inode
+#[no_mangle]
+pub unsafe extern "C" fn erofs_get_parent_rust(child: *mut dentry) -> *mut dentry {
+ // SAFETY: We are sure that the inode is a Kernel Inode since alloc_inode is called
+ let k_inode = unsafe { (*child).d_inode };
+ let erofs_sbi = as_sbi(unsafe { (*(*k_inode).i_sb).s_fs_info });
+ let inode = unsafe { &*container_of!(k_inode, KernelInode, k_inode) };
+
+ let k_inode: *mut inode = dir_lookup(
+ erofs_sbi.filesystem.as_ref(),
+ &mut erofs_sbi.inodes,
+ inode,
+ "..",
+ )
+ .map_or(core::ptr::null_mut(), |result| result.k_inode.as_mut_ptr());
+
+ // SAFETY: We are sure that the inner k_inode has already been initialized
+ unsafe { d_obtain_alias(k_inode) }
+}
+
+/// Readdir
+#[no_mangle]
+pub unsafe extern "C" fn erofs_readdir_rust(f: *mut file, ctx: *mut dir_context) -> i32 {
+ let inode = unsafe { &*container_of!((*f).f_inode, KernelInode, k_inode) };
+ let vnode = unsafe { (*f).f_inode };
+ let sb = unsafe { (*vnode).i_sb };
+ let erofs_sbi = as_sbi(unsafe { (*sb).s_fs_info });
+ let offset = unsafe { (*ctx).pos };
+ erofs_sbi
+ .filesystem
+ .fill_dentries(inode, offset as Off, &mut |dir, pos| unsafe {
+ // inline expansion from dir_emit
+ (*ctx).actor.unwrap()(
+ ctx,
+ dir.name.as_ptr().cast(),
+ dir.name.len() as i32,
+ pos as i64,
+ dir.desc.nid as u64,
+ dir.desc.file_type as u32,
+ );
+ (*ctx).pos = pos as i64;
+ });
+ unsafe { (*ctx).pos = inode.info().file_size() as i64 }
+ 0
+}
+
+/// MapBlocks used for iomap_begin
+#[no_mangle]
+pub unsafe extern "C" fn erofs_iomap_begin_rust(k_inode: *mut inode, map: *mut ErofsRustMap) {
+ let erofs_sbi = as_sbi(unsafe { (*(*k_inode).i_sb).s_fs_info });
+ let k_inode = unsafe { &*container_of!(k_inode, KernelInode, k_inode) };
+ let m = erofs_sbi.filesystem.map(k_inode, unsafe { (*map).m_la });
+
+ unsafe {
+ (*map).m_pa = m.physical.start;
+ (*map).m_la = m.logical.start;
+ (*map).m_plen = m.physical.len;
+ (*map).m_llen = m.logical.len;
+ (*map).m_flags = m.flags;
+ }
+ if m.flags & MAP_META != 0 && m.logical.len != 0 {
+ let buffer = erofs_sbi
+ .filesystem
+ .continous_iter(m.physical.start, m.physical.len)
+ .next()
+ .unwrap();
+ unsafe {
+ (*map).inline_data = buffer.content().as_ptr().cast();
+ (*map).private = heap_alloc(buffer).into_foreign();
+ }
+ } else {
+ unsafe {
+ (*map).inline_data = core::ptr::null_mut();
+ (*map).private = core::ptr::null_mut();
+ }
+ }
+}
+
+/// iomap_end
+#[no_mangle]
+pub unsafe extern "C" fn erofs_iomap_end_rust(map: *mut ErofsRustMap) {
+ unsafe {
+ if !(*map).private.is_null() {
+ drop(Box::<Box<dyn Buffer + 'static>>::from_foreign(
+ (*map).private,
+ ))
+ }
+ }
+}
+
+/// Map blocks in inline mode exported for iget5_locked to be actually functional
+#[no_mangle]
+pub unsafe extern "C" fn erofs_iget5_eq_rust(kinode: *const c_void, opaque: *const c_void) -> i32 {
+ let nid = unsafe { *(opaque as *const Nid) };
+ let inode = unsafe { container_of!(kinode, KernelInode, k_inode) };
+ (unsafe { (*inode).nid() == nid }) as i32
+}
+
+/// init_once replacement
+#[no_mangle]
+pub unsafe extern "C" fn erofs_inode_init_once_rust(ptr: *const c_void) {
+ let inode = unsafe { &mut *(ptr as *mut KernelInode) };
+ unsafe { inode_init_once(inode.k_inode.as_mut_ptr()) }
+}
+
+/// iget replacement
+#[no_mangle]
+pub unsafe extern "C" fn erofs_iget_rust(sb: *mut super_block, nid: Nid) -> *mut inode {
+ let erofs_sbi = as_sbi(unsafe { (*sb).s_fs_info });
+ read_inode(erofs_sbi.filesystem.as_ref(), &mut erofs_sbi.inodes, nid)
+ .k_inode
+ .as_mut_ptr()
+}
+
+///make_root_call for setting up the root dentry for superblock
+#[no_mangle]
+pub unsafe extern "C" fn erofs_make_root_rust(sb: *mut super_block) -> *mut dentry {
+ let erofs_sbi = as_sbi(unsafe { (*sb).s_fs_info });
+ unsafe {
+ d_make_root(
+ read_inode(
+ erofs_sbi.filesystem.as_ref(),
+ &mut erofs_sbi.inodes,
+ erofs_sbi.filesystem.superblock().root_nid as u64,
+ )
+ .k_inode
+ .as_mut_ptr(),
+ )
+ }
+}
+
+/// get_symlink_data_inline
+#[no_mangle]
+pub unsafe extern "C" fn erofs_get_inline_symlink_data_rust(k_inode: *mut inode) -> *mut c_void {
+ let inode = unsafe { &*container_of!(k_inode, KernelInode, k_inode) };
+ let erofs_sbi = as_sbi(unsafe { (*(*k_inode).i_sb).s_fs_info });
+
+ if inode.info().format().layout() != Layout::FlatInline
+ || inode.info().inode_size() >= erofs_sbi.filesystem.blksz()
+ {
+ core::ptr::null_mut()
+ } else {
+ let mut cursor: usize = 0;
+ let mut symlink: Vec<u8> = vec_with_capacity(inode.info().inode_size() as usize + 1);
+ for block in erofs_sbi.filesystem.mapped_iter(inode, 0) {
+ let data = block.content();
+ let len = data.len();
+ unsafe {
+ symlink
+ .as_mut_ptr()
+ .add(cursor)
+ .copy_from(data.as_ptr(), len);
+ }
+ cursor += len;
+ }
+
+ unsafe {
+ *(symlink.as_mut_ptr().add(cursor)) = '\0' as u8;
+ }
+
+ symlink.leak().as_mut_ptr().cast()
+ }
+}
+
+/// Exposed as the inode_size which is used as a param to guide the kmem_cache_create.
+#[no_mangle]
+pub static EROFS_INODE_SIZE: Off = core::mem::size_of::<KernelInode>() as Off;
+
+/// Exposed as the inode_offset so that when freeing the inode, the correct offset can be used.
+#[no_mangle]
+pub static EROFS_INODE_OFFSET: Off = core::mem::offset_of!(KernelInode, k_inode) as Off;
diff --git a/fs/erofs/erofs_rust_bindings.h b/fs/erofs/erofs_rust_bindings.h
new file mode 100644
index 000000000000..8193e18bd7e2
--- /dev/null
+++ b/fs/erofs/erofs_rust_bindings.h
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// EROFS Rust Bindings Before VFS Patch Sets for Rust
+
+#ifndef EROFS_RUST_BINDINGS_H
+#define EROFS_RUST_BINDINGS_H
+
+#include <linux/fs.h>
+#include <linux/dax.h>
+#include <linux/dcache.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/pagemap.h>
+#include <linux/bio.h>
+#include <linux/magic.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/iomap.h>
+
+#include "erofs_fs.h"
+
+struct erofs_rust_map {
+ __le64 m_pa;
+ __le64 m_la;
+ __le64 m_plen;
+ __le64 m_llen;
+ __le32 m_flags;
+ void *inline_data;
+ void *private;
+};
+
+extern const __le64 EROFS_INODE_SIZE;
+extern const __le64 EROFS_INODE_OFFSET;
+extern struct dentry *erofs_lookup_rust(struct inode *inode,
+ struct dentry *dentry,
+ unsigned int flags);
+extern int erofs_readdir_rust(struct file *f, struct dir_context *ctx);
+extern void *erofs_alloc_sbi_rust(struct super_block *sb,
+ struct address_space *address_space);
+extern void erofs_free_sbi_rust(struct super_block *sb);
+extern void erofs_inode_init_once_rust(void *ptr);
+extern void erofs_iomap_begin_rust(struct inode *inode,
+ struct erofs_rust_map *m);
+extern void erofs_iomap_end_rust(struct erofs_rust_map *m);
+extern struct inode *erofs_iget_rust(struct super_block *sb, ino_t ino);
+extern struct dentry *erofs_get_parent_rust(struct dentry *child);
+extern struct dentry *erofs_make_root_rust(struct super_block *sb);
+#endif
diff --git a/fs/erofs/erofs_rust_helper.c b/fs/erofs/erofs_rust_helper.c
new file mode 100644
index 000000000000..faf1377ec4ca
--- /dev/null
+++ b/fs/erofs/erofs_rust_helper.c
@@ -0,0 +1,107 @@
+#include "erofs_rust_helper.h"
+
+void *erofs_get_page(void *mapping, pgoff_t index)
+{
+ unsigned int nofs_flag;
+ struct folio *folio;
+ struct page *page;
+
+ nofs_flag = memalloc_nofs_save();
+ folio = read_cache_folio((struct address_space *)mapping, index, NULL,
+ NULL);
+ memalloc_nofs_restore(nofs_flag);
+ page = folio_file_page(folio, index);
+ return kmap_local_page(page);
+}
+
+void erofs_put_page(void *address)
+{
+ kunmap_local(address);
+ put_page(virt_to_page(address));
+}
+
+extern int erofs_iget5_eq_rust(void *inode, void *opaque);
+
+static ino_t erofs_squash_ino(erofs_nid_t nid)
+{
+ ino_t ino = (ino_t)nid;
+
+ if (sizeof(ino_t) < sizeof(erofs_nid_t))
+ ino ^= nid >> (sizeof(erofs_nid_t) - sizeof(ino_t)) * 8;
+ return ino;
+}
+
+static int erofs_iget5_eq(struct inode *inode, void *opaque)
+{
+ return erofs_iget5_eq_rust(inode, opaque);
+}
+
+static int erofs_iget5_set(struct inode *inode, void *opaque)
+{
+ const erofs_nid_t nid = *(erofs_nid_t *)opaque;
+ inode->i_ino = nid;
+ return 0;
+}
+
+void erofs_init_inode_unlock(struct inode *inode,
+ const struct erofs_inode_init_param_rust *param)
+{
+ inode->i_mode = param->i_mode;
+ inode->i_size = param->i_size;
+ i_uid_write(inode, param->i_uid);
+ i_gid_write(inode, param->i_gid);
+ set_nlink(inode, param->i_nlink);
+ inode_set_ctime(inode, param->i_mtime, param->i_mtime_nsec);
+ inode_set_mtime_to_ts(
+ inode, inode_set_atime_to_ts(inode, inode_get_ctime(inode)));
+
+ switch (inode->i_mode & S_IFMT) {
+ case S_IFREG:
+ inode->i_op = &erofs_generic_iops;
+ inode->i_fop = &erofs_file_fops;
+ break;
+ case S_IFDIR:
+ inode->i_op = &erofs_dir_iops;
+ inode->i_fop = &erofs_dir_fops;
+ inode_nohighmem(inode);
+ break;
+ case S_IFLNK:
+ inode->i_link = erofs_get_inline_symlink_data_rust(inode);
+ if (inode->i_link != NULL)
+ inode->i_op = &erofs_fast_symlink_iops;
+ else
+ inode->i_op = &erofs_symlink_iops;
+ inode_nohighmem(inode);
+ break;
+ case S_IFCHR:
+ case S_IFBLK:
+ case S_IFIFO:
+ case S_IFSOCK:
+ inode->i_op = &erofs_generic_iops;
+ init_special_inode(inode, inode->i_mode, inode->i_rdev);
+ break;
+ default:
+ panic("Not supported");
+ }
+
+ inode->i_mapping->a_ops = &erofs_raw_access_aops;
+ inode->i_blocks = param->i_blocks;
+ mapping_set_large_folios(inode->i_mapping);
+#ifdef CONFIG_EROFS_FS_ONDEMAND
+ if (erofs_is_fscache_mode(inode->i_sb))
+ inode->i_mapping->a_ops = &erofs_fscache_access_aops;
+#endif
+ unlock_new_inode(inode);
+}
+
+struct inode *erofs_iget_locked_noinit(struct super_block *sb, erofs_nid_t nid,
+ int *is_new)
+{
+ struct inode *inode;
+
+ inode = iget5_locked(sb, erofs_squash_ino(nid), erofs_iget5_eq,
+ erofs_iget5_set, &nid);
+
+ *is_new = inode->i_state & I_NEW;
+ return inode;
+}
diff --git a/fs/erofs/erofs_rust_helper.h b/fs/erofs/erofs_rust_helper.h
new file mode 100644
index 000000000000..b9f0175bc319
--- /dev/null
+++ b/fs/erofs/erofs_rust_helper.h
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// This is a helper header to dodge the missing static inline in bindgen
+
+#ifndef __EROFS_RUST_HELPER_H
+#define __EROFS_RUST_HELPER_H
+
+#include <linux/fs.h>
+#include <linux/dax.h>
+#include <linux/dcache.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/pagemap.h>
+#include <linux/bio.h>
+#include <linux/magic.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/iomap.h>
+
+#include "internal.h"
+
+
+struct erofs_inode_init_param_rust {
+ __le16 i_mode;
+ __le32 i_uid;
+ __le32 i_gid;
+ __le32 i_nlink;
+ __le32 i_mtime_nsec;
+ __le64 i_mtime;
+ __le64 i_size;
+ __le64 i_blocks;
+};
+void *erofs_get_page(void *mapping, pgoff_t index);
+void erofs_put_page(void *address);
+void erofs_init_inode_unlock(struct inode *inode, const struct erofs_inode_init_param_rust *param);
+struct inode *erofs_iget_locked_noinit(struct super_block *sb, erofs_nid_t nid,
+ int *init);
+
+void *erofs_get_inline_symlink_data_rust(struct inode *inode);
+
+#endif
diff --git a/fs/erofs/rust/erofs_sys.rs b/fs/erofs/rust/erofs_sys.rs
new file mode 100644
index 000000000000..8ac02c95b7f0
--- /dev/null
+++ b/fs/erofs/rust/erofs_sys.rs
@@ -0,0 +1,67 @@
+#![allow(dead_code)]
+// Copyright 2024 Yiyang Wu
+// SPDX-License-Identifier: MIT or GPL-2.0-only
+
+//! A pure Rust implementation of the EROFS filesystem.
+//! Technical Details are documented in the [EROFS Documentation](https://erofs.docs.kernel.org/en/latest/)
+
+pub(crate) const EROFS_PAGE_SZ: u64 = 4096;
+pub(crate) const EROFS_PAGE: Page = [0; EROFS_PAGE_SZ as usize];
+pub(crate) const EROFS_SUPER_OFFSET: Off = 1024;
+pub(crate) const EROFS_PAGE_BITS: u64 = 12;
+pub(crate) const EROFS_PAGE_MASK: u64 = EROFS_PAGE_SZ - 1;
+
+pub(crate) struct PageAddress {
+ pub(crate) page: u64,
+ pub(crate) pg_index: u64,
+ pub(crate) pg_off: u64,
+ pub(crate) pg_len: u64,
+}
+
+impl From<u64> for PageAddress {
+ fn from(address: u64) -> Self {
+ PageAddress {
+ page: (address >> EROFS_PAGE_BITS) << EROFS_PAGE_BITS,
+ pg_index: address >> EROFS_PAGE_BITS,
+ pg_off: address & EROFS_PAGE_MASK,
+ pg_len: EROFS_PAGE_SZ - (address & EROFS_PAGE_MASK),
+ }
+ }
+}
+
+// It's unavoidable to import alloc here. Since there are so many backends there and if we want to
+// to use trait object to export Filesystem pointer. The alloc crate here is necessary.
+
+#[cfg(not(CONFIG_EROFS_FS = "y"))]
+extern crate alloc;
+
+/// Erofs Operates on the block/page size of 4096 we respect that.
+pub(crate) type Page = [u8; EROFS_PAGE_SZ as usize];
+/// Erofs requires block index to a 32 bit unsigned integer.
+pub(crate) type Blk = u32;
+/// Erofs requires normal offset to be a 64bit unsigned integer.
+pub(crate) type Off = u64;
+/// Erofs requires inode nid to be a 64bit unsigned integer.
+pub(crate) type Nid = u64;
+
+pub(crate) mod alloc_helper;
+pub(crate) mod compression;
+pub(crate) mod data;
+pub(crate) mod devices;
+pub(crate) mod dir;
+pub(crate) mod inode;
+pub(crate) mod map;
+pub(crate) mod operations;
+pub(crate) mod superblock;
+pub(crate) mod xattrs;
+
+/// Helper macro to round up or down a number.
+#[macro_export]
+macro_rules! round {
+ (UP, $x: expr, $y: expr) => {
+ ($x + $y - 1) / $y * $y
+ };
+ (DOWN, $x: expr, $y: expr) => {
+ ($x / $y) * $y
+ };
+}
diff --git a/fs/erofs/rust/kinode.rs b/fs/erofs/rust/kinode.rs
new file mode 100644
index 000000000000..16f58c3814a5
--- /dev/null
+++ b/fs/erofs/rust/kinode.rs
@@ -0,0 +1,103 @@
+// Copyright 2024 Yiyang Wu
+// SPDX-License-Identifier: MIT or GPL-2.0-only
+
+pub(crate) mod kinode_helper;
+
+use core::mem::MaybeUninit;
+use core::ptr::NonNull;
+
+use super::erofs_sys::inode::*;
+use super::erofs_sys::superblock::*;
+use super::erofs_sys::xattrs::*;
+use super::erofs_sys::*;
+
+use kernel::bindings::{inode, super_block};
+use kernel::container_of;
+use kinode_helper::*;
+
+#[repr(C)]
+pub(crate) struct KernelInode {
+ pub(crate) info: MaybeUninit<InodeInfo>,
+ pub(crate) xattrs_header: MaybeUninit<MemEntryIndexHeader>,
+ pub(crate) nid: MaybeUninit<Nid>,
+ pub(crate) k_inode: MaybeUninit<inode>,
+}
+
+impl Inode for KernelInode {
+ fn new(
+ _sb: &SuperBlock,
+ _info: InodeInfo,
+ _nid: Nid,
+ _xattrs_header: xattrs::MemEntryIndexHeader,
+ ) -> Self {
+ Self {
+ info: MaybeUninit::uninit(),
+ xattrs_header: MaybeUninit::uninit(),
+ nid: MaybeUninit::uninit(),
+ k_inode: MaybeUninit::uninit(),
+ }
+ }
+ fn nid(&self) -> Nid {
+ unsafe { self.nid.assume_init() }
+ }
+ fn info(&self) -> &InodeInfo {
+ unsafe { self.info.assume_init_ref() }
+ }
+ fn xattrs_header(&self) -> &xattrs::MemEntryIndexHeader {
+ unsafe { self.xattrs_header.assume_init_ref() }
+ }
+}
+
+pub(crate) struct KernelInodeCollection {
+ sb: NonNull<super_block>,
+}
+
+impl InodeCollection for KernelInodeCollection {
+ type I = KernelInode;
+ fn iget(&mut self, nid: Nid, f: &dyn FileSystem<Self::I>) -> &mut Self::I {
+ let mut is_new: i32 = 0;
+ let k_inode = unsafe { iget_locked(self.sb.as_ptr().cast(), nid, &mut is_new as *mut i32) };
+ let inode: &mut KernelInode =
+ unsafe { &mut *(container_of!(k_inode, KernelInode, k_inode) as *mut KernelInode) };
+
+ if is_new != 0 {
+ let info = f.read_inode_info(nid);
+ inode.info.write(info.clone());
+ inode.xattrs_header.write(f.read_inode_xattrs_index(nid));
+ inode.nid.write(nid);
+ let sb = f.superblock();
+ let param = match info {
+ InodeInfo::Compact(compact) => ErofsInodeInitParam {
+ i_mode: compact.i_mode,
+ i_uid: compact.i_uid as u32,
+ i_gid: compact.i_gid as u32,
+ i_nlink: compact.i_nlink as u32,
+ i_mtime_nsec: sb.build_time_nsec as u32,
+ i_mtime: sb.build_time as u64,
+ i_size: compact.i_size as u64,
+ i_blocks: compact.i_size as u64 >> ((sb.blkszbits - 9) as u64),
+ },
+ InodeInfo::Extended(extended) => ErofsInodeInitParam {
+ i_mode: extended.i_mode,
+ i_uid: extended.i_uid,
+ i_gid: extended.i_gid,
+ i_nlink: extended.i_nlink,
+ i_mtime_nsec: extended.i_mtime_nsec as u32,
+ i_mtime: extended.i_mtime as u64,
+ i_size: extended.i_size as u64,
+ i_blocks: extended.i_size >> ((sb.blkszbits - 9) as u64),
+ },
+ };
+ unsafe {
+ iget_unlock(k_inode, ¶m as *const ErofsInodeInitParam);
+ }
+ }
+ return inode;
+ }
+}
+
+impl KernelInodeCollection {
+ pub(crate) fn new(sb: NonNull<super_block>) -> Self {
+ Self { sb }
+ }
+}
diff --git a/fs/erofs/rust/kinode/kinode_helper.rs b/fs/erofs/rust/kinode/kinode_helper.rs
new file mode 100644
index 000000000000..56c22a393aa0
--- /dev/null
+++ b/fs/erofs/rust/kinode/kinode_helper.rs
@@ -0,0 +1,26 @@
+// Copyright 2024 Yiyang Wu
+// SPDX-License-Identifier: MIT or GPL-2.0-only
+
+use super::super::erofs_sys::Off;
+use core::ffi::{c_int, c_void};
+
+#[repr(C)]
+pub(crate) struct ErofsInodeInitParam {
+ pub(crate) i_mode: u16,
+ pub(crate) i_uid: u32,
+ pub(crate) i_gid: u32,
+ pub(crate) i_nlink: u32,
+ pub(crate) i_mtime_nsec: u32,
+ pub(crate) i_mtime: u64,
+ pub(crate) i_size: u64,
+ pub(crate) i_blocks: u64
+}
+
+extern "C" {
+
+ #[link_name = "erofs_init_inode_unlock"]
+ pub(crate) fn iget_unlock(inode: *mut c_void, param: *const ErofsInodeInitParam);
+
+ #[link_name = "erofs_iget_locked_noinit"]
+ pub(crate) fn iget_locked(sb: *mut c_void, nid: Off, is_new: *mut c_int) -> *mut c_void;
+}
diff --git a/fs/erofs/rust/mod.rs b/fs/erofs/rust/mod.rs
new file mode 100644
index 000000000000..5ca116294f51
--- /dev/null
+++ b/fs/erofs/rust/mod.rs
@@ -0,0 +1,6 @@
+// Copyright 2024 Yiyang Wu
+// SPDX-License-Identifier: MIT or GPL-2.0-only
+
+pub(crate) mod erofs_sys;
+pub(crate) mod kinode;
+pub(crate) mod sources;
diff --git a/fs/erofs/rust/sources.rs b/fs/erofs/rust/sources.rs
new file mode 100644
index 000000000000..16bd37b80166
--- /dev/null
+++ b/fs/erofs/rust/sources.rs
@@ -0,0 +1,5 @@
+// Copyright 2024 Yiyang Wu
+// SPDX-License-Identifier: MIT or GPL-2.0-only
+
+pub(crate) mod mm;
+mod page_helper;
diff --git a/fs/erofs/rust/sources/mm.rs b/fs/erofs/rust/sources/mm.rs
new file mode 100644
index 000000000000..6176adfb2a76
--- /dev/null
+++ b/fs/erofs/rust/sources/mm.rs
@@ -0,0 +1,62 @@
+// Copyright 2024 Yiyang Wu
+// SPDX-License-Identifier: MIT or GPL-2.0-only
+use super::super::erofs_sys::data::*;
+use super::super::erofs_sys::*;
+use super::page_helper::*;
+
+use core::{
+ ffi::{c_ulong, c_void},
+ ptr::NonNull,
+ unimplemented,
+};
+
+use kernel::bindings::address_space;
+
+pub(crate) struct FolioSource {
+ address_space: NonNull<address_space>,
+}
+
+impl FolioSource {
+ pub(crate) fn new(address_space: NonNull<address_space>) -> Self {
+ Self { address_space }
+ }
+}
+
+impl Source for FolioSource {
+ fn fill(&self, data: &mut [u8], offset: Off) -> SourceResult<u64> {
+ self.as_buf(offset, data.len() as u64).map(|buf| {
+ data[..buf.content().len()].clone_from_slice(buf.content());
+ buf.content().len() as Off
+ })
+ }
+}
+
+fn put_page_safe(buf: *mut c_void) {
+ unsafe { put_page(buf) }
+}
+
+fn try_get_page(address_space: *mut c_void, index: c_ulong) -> SourceResult<*mut c_void> {
+ let ptr = unsafe { get_page(address_space, index as c_ulong) };
+ if !ptr.is_null() {
+ Ok(ptr)
+ } else {
+ Err(SourceError::Dummy)
+ }
+}
+
+impl<'a> PageSource<'a> for FolioSource {
+ fn as_buf(&'a self, offset: Off, len: Off) -> SourceResult<RefBuffer<'a>> {
+ let pa = PageAddress::from(offset);
+ if pa.pg_off + len > EROFS_PAGE_SZ {
+ return Err(SourceError::OutBound);
+ }
+ try_get_page(self.address_space.as_ptr().cast(), pa.pg_index as c_ulong).map(|ptr| {
+ let buf: &'a [u8] =
+ unsafe { core::slice::from_raw_parts(ptr as *const u8, EROFS_PAGE_SZ as usize) };
+ RefBuffer::new(buf, pa.pg_off as usize, len as usize, put_page_safe)
+ })
+ }
+ fn as_buf_mut(&'a mut self, _offset: Off, _len: Off) -> SourceResult<RefBufferMut<'a>> {
+ unimplemented!()
+ }
+}
diff --git a/fs/erofs/rust/sources/page_helper.rs b/fs/erofs/rust/sources/page_helper.rs
new file mode 100644
index 000000000000..c451021aa70f
--- /dev/null
+++ b/fs/erofs/rust/sources/page_helper.rs
@@ -0,0 +1,12 @@
+// Copyright 2024 Yiyang Wu
+// SPDX-License-Identifier: MIT or GPL-2.0-only
+
+use core::ffi::{c_ulong, c_void};
+
+extern "C" {
+ #[link_name = "erofs_put_page"]
+ pub(crate) fn put_page(addr: *mut c_void);
+
+ #[link_name = "erofs_get_page"]
+ pub(crate) fn get_page(mapping: *mut c_void, index: c_ulong) -> *mut c_void;
+}
--
2.45.2
More information about the Linux-erofs
mailing list