From: Wedson Almeida Filho <walmeida@xxxxxxxxxxxxx> Allow Rust file systems to create inodes that are children of a directory inode when they're looked up by name. Signed-off-by: Wedson Almeida Filho <walmeida@xxxxxxxxxxxxx> --- rust/kernel/error.rs | 1 - rust/kernel/fs.rs | 65 +++++++++++++++++++++++++++++++++++++-- samples/rust/rust_rofs.rs | 25 +++++++++++++++ 3 files changed, 88 insertions(+), 3 deletions(-) diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs index e6d7ce46be55..484fa7c11de1 100644 --- a/rust/kernel/error.rs +++ b/rust/kernel/error.rs @@ -131,7 +131,6 @@ pub fn to_errno(self) -> core::ffi::c_int { } /// Returns the error encoded as a pointer. - #[allow(dead_code)] pub(crate) fn to_ptr<T>(self) -> *mut T { // SAFETY: self.0 is a valid error due to its invariant. unsafe { bindings::ERR_PTR(self.0.into()) as *mut _ } diff --git a/rust/kernel/fs.rs b/rust/kernel/fs.rs index 89611c44e4c5..681fef8e3af1 100644 --- a/rust/kernel/fs.rs +++ b/rust/kernel/fs.rs @@ -33,6 +33,9 @@ pub trait FileSystem { /// /// [`DirEmitter::pos`] holds the current position of the directory reader. fn read_dir(inode: &INode<Self>, emitter: &mut DirEmitter) -> Result; + + /// Returns the inode corresponding to the directory entry with the given name. + fn lookup(parent: &INode<Self>, name: &[u8]) -> Result<ARef<INode<Self>>>; } /// The types of directory entries reported by [`FileSystem::read_dir`]. @@ -226,8 +229,7 @@ pub fn init(self, params: INodeParams) -> Result<ARef<INode<T>>> { let mode = match params.typ { INodeType::Dir => { inode.__bindgen_anon_3.i_fop = &Tables::<T>::DIR_FILE_OPERATIONS; - // SAFETY: `simple_dir_inode_operations` never changes, it's safe to reference it. - inode.i_op = unsafe { &bindings::simple_dir_inode_operations }; + inode.i_op = &Tables::<T>::DIR_INODE_OPERATIONS; bindings::S_IFDIR } }; @@ -530,6 +532,62 @@ impl<T: FileSystem + ?Sized> Tables<T> { } }) } + + const DIR_INODE_OPERATIONS: bindings::inode_operations = bindings::inode_operations { + lookup: Some(Self::lookup_callback), + get_link: None, + permission: None, + get_inode_acl: None, + readlink: None, + create: None, + link: None, + unlink: None, + symlink: None, + mkdir: None, + rmdir: None, + mknod: None, + rename: None, + setattr: None, + getattr: None, + listxattr: None, + fiemap: None, + update_time: None, + atomic_open: None, + tmpfile: None, + get_acl: None, + set_acl: None, + fileattr_set: None, + fileattr_get: None, + get_offset_ctx: None, + }; + + extern "C" fn lookup_callback( + parent_ptr: *mut bindings::inode, + dentry: *mut bindings::dentry, + _flags: u32, + ) -> *mut bindings::dentry { + // SAFETY: The C API guarantees that `parent_ptr` is a valid inode. + let parent = unsafe { &*parent_ptr.cast::<INode<T>>() }; + + // SAFETY: The C API guarantees that `dentry` is valid for read. Since the name is + // immutable, it's ok to read its length directly. + let len = unsafe { (*dentry).d_name.__bindgen_anon_1.__bindgen_anon_1.len }; + let Ok(name_len) = usize::try_from(len) else { + return ENOENT.to_ptr(); + }; + + // SAFETY: The C API guarantees that `dentry` is valid for read. Since the name is + // immutable, it's ok to read it directly. + let name = unsafe { core::slice::from_raw_parts((*dentry).d_name.name, name_len) }; + match T::lookup(parent, name) { + Err(e) => e.to_ptr(), + // SAFETY: The returned inode is valid and referenced (by the type invariants), so + // it is ok to transfer this increment to `d_splice_alias`. + Ok(inode) => unsafe { + bindings::d_splice_alias(ManuallyDrop::new(inode).0.get(), dentry) + }, + } + } } /// Directory entry emitter. @@ -637,6 +695,9 @@ fn init(module: &'static ThisModule) -> impl PinInit<Self, Error> { /// fn read_dir(_: &INode<Self>, _: &mut DirEmitter) -> Result { /// todo!() /// } +/// fn lookup(_: &INode<Self>, _: &[u8]) -> Result<ARef<INode<Self>>> { +/// todo!() +/// } /// } /// # } /// ``` diff --git a/samples/rust/rust_rofs.rs b/samples/rust/rust_rofs.rs index 4e61a94afa70..4cc8525884a9 100644 --- a/samples/rust/rust_rofs.rs +++ b/samples/rust/rust_rofs.rs @@ -89,4 +89,29 @@ fn read_dir(inode: &INode<Self>, emitter: &mut DirEmitter) -> Result { Ok(()) } + + fn lookup(parent: &INode<Self>, name: &[u8]) -> Result<ARef<INode<Self>>> { + if parent.ino() != 1 { + return Err(ENOENT); + } + + match name { + b"subdir" => match parent.super_block().get_or_create_inode(2)? { + Either::Left(existing) => Ok(existing), + Either::Right(new) => new.init(INodeParams { + typ: INodeType::Dir, + mode: 0o555, + size: 0, + blocks: 1, + nlink: 2, + uid: 0, + gid: 0, + atime: UNIX_EPOCH, + ctime: UNIX_EPOCH, + mtime: UNIX_EPOCH, + }), + }, + _ => Err(ENOENT), + } + } } -- 2.34.1