linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Ariel Miculas <amiculas@cisco.com>
To: rust-for-linux@vger.kernel.org
Cc: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org,
	tycho@tycho.pizza, brauner@kernel.org, viro@zeniv.linux.org.uk,
	ojeda@kernel.org, alex.gaynor@gmail.com, wedsonaf@gmail.com,
	shallyn@cisco.com, Ariel Miculas <amiculas@cisco.com>
Subject: [RFC PATCH v3 17/22] fs: puzzlefs: add extended attributes support
Date: Thu, 16 May 2024 22:03:40 +0300	[thread overview]
Message-ID: <20240516190345.957477-18-amiculas@cisco.com> (raw)
In-Reply-To: <20240516190345.957477-1-amiculas@cisco.com>

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

Signed-off-by: Ariel Miculas <amiculas@cisco.com>
---
 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


  parent reply	other threads:[~2024-05-16 19:04 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-05-16 19:03 [RFC PATCH v3 00/22] Rust PuzzleFS filesystem driver Ariel Miculas
2024-05-16 19:03 ` [RFC PATCH v3 01/22] kernel: configs: add qemu-busybox-min.config Ariel Miculas
2024-05-16 19:03 ` [RFC PATCH v3 02/22] rust: hex: import crate Ariel Miculas
2024-05-16 19:03 ` [RFC PATCH v3 03/22] rust: hex: add SPDX license identifiers Ariel Miculas
2024-05-16 19:03 ` [RFC PATCH v3 04/22] rust: Kbuild: enable `hex` Ariel Miculas
2024-05-16 19:03 ` [RFC PATCH v3 05/22] rust: hex: add encode_hex_iter and encode_hex_upper_iter methods Ariel Miculas
2024-05-16 19:03 ` [RFC PATCH v3 06/22] rust: capnp: import crate Ariel Miculas
2024-05-16 19:03 ` [RFC PATCH v3 07/22] rust: capnp: add SPDX License Identifiers Ariel Miculas
2024-05-16 19:03 ` [RFC PATCH v3 08/22] rust: capnp: return an error when trying to display floating point values Ariel Miculas
2024-05-16 19:03 ` [RFC PATCH v3 09/22] rust: Kbuild: enable `capnp` Ariel Miculas
2024-05-16 19:03 ` [RFC PATCH v3 10/22] rust: kernel: add an abstraction over vfsmount to allow cloning a new private mount Ariel Miculas
2024-05-16 19:03 ` [RFC PATCH v3 11/22] rust: file: add bindings for `struct file` Ariel Miculas
2024-05-16 19:03 ` [RFC PATCH v3 12/22] rust: file: Add support for reading files using their path Ariel Miculas
2024-05-16 19:03 ` [RFC PATCH v3 13/22] fs: puzzlefs: Implement the initial version of PuzzleFS Ariel Miculas
2024-05-16 19:03 ` [RFC PATCH v3 14/22] rust: kernel: add from_iter_fallible for Vec<T> Ariel Miculas
2024-05-16 19:03 ` [RFC PATCH v3 15/22] kernel: configs: add puzzlefs config fragment Ariel Miculas
2024-05-16 19:03 ` [RFC PATCH v3 16/22] scripts: add fs directory to rust-analyzer Ariel Miculas
2024-05-16 19:03 ` Ariel Miculas [this message]
2024-05-16 19:03 ` [RFC PATCH v3 18/22] rust: add improved version of `ForeignOwnable::borrow_mut` Ariel Miculas
2024-05-17  8:37   ` Alice Ryhl
2024-05-16 19:03 ` [RFC PATCH v3 19/22] Add borrow_mut implementation to a ForeignOwnable CString Ariel Miculas
2024-05-16 19:03 ` [RFC PATCH v3 20/22] rust: add support for file system parameters Ariel Miculas
2024-05-16 19:03 ` [RFC PATCH v3 21/22] fs: puzzlefs: add oci_root_dir and image_manifest mount parameters Ariel Miculas
2024-05-16 19:03 ` [RFC PATCH v3 22/22] fs: puzzlefs: implement statfs for puzzlefs Ariel Miculas

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240516190345.957477-18-amiculas@cisco.com \
    --to=amiculas@cisco.com \
    --cc=alex.gaynor@gmail.com \
    --cc=brauner@kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=ojeda@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=shallyn@cisco.com \
    --cc=tycho@tycho.pizza \
    --cc=viro@zeniv.linux.org.uk \
    --cc=wedsonaf@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).