[RFC PATCH v3 17/22] fs: puzzlefs: add extended attributes support

From: Ariel Miculas
Date: Thu May 16 2024 - 15:05:45 EST


Implement the listxattr callback in the filesystem abstractions.
Implement both read_xattr and listxattr for PuzzleFS.

Signed-off-by: Ariel Miculas <amiculas@xxxxxxxxx>
---
fs/puzzlefs/puzzlefs.rs | 50 +++++++++++++++++++++++++++++--
rust/kernel/fs/inode.rs | 66 +++++++++++++++++++++++++++++++++++++++--
2 files changed, 112 insertions(+), 4 deletions(-)

diff --git a/fs/puzzlefs/puzzlefs.rs b/fs/puzzlefs/puzzlefs.rs
index a062bf0249f6..9622ea71eda0 100644
--- a/fs/puzzlefs/puzzlefs.rs
+++ b/fs/puzzlefs/puzzlefs.rs
@@ -107,8 +107,8 @@ fn fill_super(
_: Option<inode::Mapper>,
) -> Result<Box<PuzzleFS>> {
let puzzlefs = PuzzleFS::open(
- c_str!("/home/puzzlefs_oci"),
- c_str!("83aa96c40a20671edc4490cfefadbb487b2ab23dfc0570049b56f0cc49b56eaf"),
+ c_str!("/home/puzzlefs_xattr"),
+ c_str!("ed63ace21eccceabab08d89afb75e94dae47973f82a17a172396a19ea953c8ab"),
);

if let Err(ref e) = puzzlefs {
@@ -124,6 +124,36 @@ fn init_root(sb: &sb::SuperBlock<Self>) -> Result<dentry::Root<Self>> {
let inode = Self::iget(sb, 1)?;
dentry::Root::try_new(inode)
}
+
+ fn read_xattr(
+ _dentry: &DEntry<Self>,
+ inode: &INode<Self>,
+ name: &CStr,
+ outbuf: &mut [u8],
+ ) -> Result<usize> {
+ let inode = inode.data();
+ let readonly = outbuf.len() == 0;
+ // pr_info!("outbuf len {}\n", outbuf.len());
+
+ if let Some(add) = &inode.additional {
+ let xattr = add
+ .xattrs
+ .iter()
+ .find(|elem| elem.key == name.as_bytes())
+ .ok_or(ENODATA)?;
+ if readonly {
+ return Ok(xattr.val.len());
+ }
+
+ if xattr.val.len() > outbuf.len() {
+ return Err(ERANGE);
+ }
+
+ outbuf[0..xattr.val.len()].copy_from_slice(xattr.val.as_slice());
+ return Ok(xattr.val.len());
+ }
+ Err(ENODATA)
+ }
}

#[vtable]
@@ -143,6 +173,22 @@ fn lookup(
}
}

+ fn listxattr(
+ inode: &INode<Self>,
+ mut add_entry: impl FnMut(&[i8]) -> Result<()>,
+ ) -> Result<()> {
+ let inode = inode.data();
+
+ if let Some(add) = &inode.additional {
+ for xattr in &add.xattrs {
+ // convert a u8 slice into an i8 slice
+ let i8slice = unsafe { &*(xattr.key.as_slice() as *const _ as *const [i8]) };
+ add_entry(i8slice)?;
+ }
+ }
+ Ok(())
+ }
+
fn get_link<'a>(
dentry: Option<&DEntry<PuzzleFsModule>>,
inode: &'a INode<PuzzleFsModule>,
diff --git a/rust/kernel/fs/inode.rs b/rust/kernel/fs/inode.rs
index b2b7d000080e..a092ee150d43 100644
--- a/rust/kernel/fs/inode.rs
+++ b/rust/kernel/fs/inode.rs
@@ -10,7 +10,7 @@
address_space, dentry, dentry::DEntry, file, mode, sb::SuperBlock, FileSystem, Offset,
PageOffset, UnspecifiedFS,
};
-use crate::error::{code::*, from_err_ptr, Result};
+use crate::error::{code::*, from_err_ptr, from_result, Result};
use crate::types::{ARef, AlwaysRefCounted, Either, ForeignOwnable, Lockable, Locked, Opaque};
use crate::{
bindings, block, build_error, container_of, folio, folio::Folio, mem_cache::MemCache,
@@ -48,6 +48,14 @@ fn lookup(
) -> Result<Option<ARef<DEntry<Self::FileSystem>>>> {
Err(ENOTSUPP)
}
+
+ /// Get extended attributes list
+ fn listxattr<'a>(
+ _inode: &'a INode<Self::FileSystem>,
+ mut _add_entry: impl FnMut(&[i8]) -> Result<()>,
+ ) -> Result<()> {
+ Err(ENOSYS)
+ }
}

/// A node (inode) in the file index.
@@ -615,7 +623,7 @@ impl<T: Operations + ?Sized> Table<T> {
rename: None,
setattr: None,
getattr: None,
- listxattr: None,
+ listxattr: Some(Self::listxattr_callback),
fiemap: None,
update_time: None,
atomic_open: None,
@@ -688,6 +696,60 @@ extern "C" fn drop_cstring(ptr: *mut core::ffi::c_void) {
}
}
}
+
+ extern "C" fn listxattr_callback(
+ dentry: *mut bindings::dentry,
+ buffer: *mut core::ffi::c_char,
+ buffer_size: usize,
+ ) -> isize {
+ from_result(|| {
+ // SAFETY: The C API guarantees that `dentry` is valid for read.
+ let inode = unsafe { bindings::d_inode(dentry) };
+ // SAFETY: The C API guarantees that `d_inode` inside `dentry` is valid for read.
+ let inode = unsafe { INode::from_raw(inode) };
+
+ // `buffer_size` should be 0 when `buffer` is NULL, but we enforce it
+ let (mut buffer_ptr, buffer_size) = match ptr::NonNull::new(buffer) {
+ Some(buf) => (buf, buffer_size),
+ None => (ptr::NonNull::dangling(), 0),
+ };
+
+ // SAFETY: The C API guarantees that `buffer` is at least `buffer_size` bytes in
+ // length. Also, when `buffer_size` is 0, `buffer_ptr` is NonNull::dangling, as
+ // suggested by `from_raw_parts_mut` documentation
+ let outbuf = unsafe {
+ core::slice::from_raw_parts_mut(buffer_ptr.as_mut(), buffer_size)
+ };
+
+ let mut offset = 0;
+ let mut total_len = 0;
+
+ // The extended attributes keys must be placed into the output buffer sequentially,
+ // separated by the NUL character. We do this in the callback because it simplifies
+ // the implementation of the `listxattr` abstraction: the user just calls the
+ // add_entry function for each extended attribute key, passing a slice.
+ T::listxattr(inode, |xattr_key| {
+ let len = xattr_key.len();
+ total_len += isize::try_from(len)? + 1;
+
+ if buffer_size == 0 {
+ return Ok(());
+ }
+
+ let max = offset + len + 1;
+ if max > buffer_size {
+ return Err(ERANGE);
+ }
+
+ outbuf[offset..max - 1].copy_from_slice(xattr_key);
+ outbuf[max - 1] = 0;
+ offset = max;
+ Ok(())
+ })?;
+
+ Ok(total_len)
+ })
+ }
}
Self(&Table::<U>::TABLE, PhantomData)
}
--
2.34.1