diff --git a/kernel/src/fs/ramfs/fs.rs b/kernel/src/fs/ramfs/fs.rs index 8aaf3e08..7921e665 100644 --- a/kernel/src/fs/ramfs/fs.rs +++ b/kernel/src/fs/ramfs/fs.rs @@ -15,7 +15,7 @@ use ostd::{ sync::{PreemptDisabled, RwLockWriteGuard}, }; -use super::*; +use super::{xattr::RamXattr, *}; use crate::{ events::IoEvents, fs::{ @@ -26,7 +26,7 @@ use crate::{ utils::{ CStr256, CachePage, DirentVisitor, Extension, FallocMode, FileSystem, FsFlags, Inode, InodeMode, InodeType, IoctlCmd, Metadata, MknodType, PageCache, PageCacheBackend, - SuperBlock, + Permission, SuperBlock, XattrName, XattrNamespace, XattrSetFlags, }, }, prelude::*, @@ -61,6 +61,7 @@ impl RamFS { this: weak_root.clone(), fs: weak_fs.clone(), extension: Extension::new(), + xattr: RamXattr::new(), }), inode_allocator: AtomicU64::new(ROOT_INO + 1), }) @@ -106,6 +107,8 @@ struct RamInode { fs: Weak, /// Extensions extension: Extension, + /// Extended attributes + xattr: RamXattr, } /// Inode inner specifics. @@ -399,6 +402,7 @@ impl RamInode { this: weak_self.clone(), fs: Arc::downgrade(fs), extension: Extension::new(), + xattr: RamXattr::new(), }) } @@ -411,6 +415,7 @@ impl RamInode { this: weak_self.clone(), fs: Arc::downgrade(fs), extension: Extension::new(), + xattr: RamXattr::new(), }) } @@ -423,6 +428,7 @@ impl RamInode { this: weak_self.clone(), fs: Arc::downgrade(fs), extension: Extension::new(), + xattr: RamXattr::new(), }) } @@ -441,6 +447,7 @@ impl RamInode { this: weak_self.clone(), fs: Arc::downgrade(fs), extension: Extension::new(), + xattr: RamXattr::new(), }) } @@ -453,6 +460,7 @@ impl RamInode { this: weak_self.clone(), fs: Arc::downgrade(fs), extension: Extension::new(), + xattr: RamXattr::new(), }) } @@ -465,6 +473,7 @@ impl RamInode { this: weak_self.clone(), fs: Arc::downgrade(fs), extension: Extension::new(), + xattr: RamXattr::new(), }) } @@ -1190,6 +1199,40 @@ impl Inode for RamInode { fn extension(&self) -> Option<&Extension> { Some(&self.extension) } + + fn set_xattr( + &self, + name: XattrName, + value_reader: &mut VmReader, + flags: XattrSetFlags, + ) -> Result<()> { + RamXattr::check_file_type_for_xattr(self.typ)?; + self.check_permission(Permission::MAY_WRITE)?; + self.xattr.set(name, value_reader, flags) + } + + fn get_xattr(&self, name: XattrName, value_writer: &mut VmWriter) -> Result { + RamXattr::check_file_type_for_xattr(self.typ) + .map_err(|_| Error::with_message(Errno::ENODATA, "no available xattrs"))?; + self.check_permission(Permission::MAY_READ)?; + self.xattr.get(name, value_writer) + } + + fn list_xattr(&self, namespace: XattrNamespace, list_writer: &mut VmWriter) -> Result { + if RamXattr::check_file_type_for_xattr(self.typ).is_err() { + return Ok(0); + } + if self.check_permission(Permission::MAY_ACCESS).is_err() { + return Ok(0); + } + self.xattr.list(namespace, list_writer) + } + + fn remove_xattr(&self, name: XattrName) -> Result<()> { + RamXattr::check_file_type_for_xattr(self.typ)?; + self.check_permission(Permission::MAY_WRITE)?; + self.xattr.remove(name) + } } fn write_lock_two_direntries_by_ino<'a>( diff --git a/kernel/src/fs/ramfs/mod.rs b/kernel/src/fs/ramfs/mod.rs index 3f47d3dd..74bf09e4 100644 --- a/kernel/src/fs/ramfs/mod.rs +++ b/kernel/src/fs/ramfs/mod.rs @@ -5,6 +5,7 @@ pub use fs::RamFS; mod fs; +mod xattr; const RAMFS_MAGIC: u64 = 0x0102_1994; const BLOCK_SIZE: usize = 4096; diff --git a/kernel/src/fs/ramfs/xattr.rs b/kernel/src/fs/ramfs/xattr.rs new file mode 100644 index 00000000..8ee4a35d --- /dev/null +++ b/kernel/src/fs/ramfs/xattr.rs @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: MPL-2.0 + +use hashbrown::{hash_map::Entry, Equivalent, HashMap}; +use spin::Once; + +use crate::{ + fs::utils::{InodeType, XattrName, XattrNamespace, XattrSetFlags}, + prelude::*, +}; + +/// An in-memory xattr object of a `RamInode`. +/// An xattr is used to manage special 'name-value' pairs of an inode. +pub struct RamXattr(Once>>); + +/// An owned in-memory xattr name that possesses a valid namespace. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +struct RamXattrName { + namespace: XattrNamespace, + full_name: String, +} + +/// The value type of an in-memory xattr. +type RamXattrValue = Vec; + +#[derive(Debug, Clone)] +struct RamXattrInner { + map: HashMap, + total_name_count: usize, + total_name_len: usize, + user_name_count: usize, + user_name_len: usize, +} + +impl RamXattr { + pub fn new() -> Self { + Self(Once::new()) + } + + pub fn set( + &self, + name: XattrName, + value_reader: &mut VmReader, + flags: XattrSetFlags, + ) -> Result<()> { + let inner = self + .0 + .call_once(|| Box::new(RwMutex::new(RamXattrInner::new()))); + let mut xattr = inner.write(); + + let namespace = name.namespace(); + let name_len = name.full_name_len(); + match xattr.map.entry(RamXattrName::from(name)) { + Entry::Occupied(mut entry) => { + if flags.contains(XattrSetFlags::CREATE_ONLY) { + return_errno_with_message!(Errno::EEXIST, "the target xattr already exists"); + } + + let value = { + let mut value = vec![0u8; value_reader.remain()]; + value_reader.read_fallible(&mut VmWriter::from(value.as_mut_slice()))?; + value + }; + let _ = entry.insert(value); + } + Entry::Vacant(entry) => { + if flags.contains(XattrSetFlags::REPLACE_ONLY) { + return_errno_with_message!(Errno::ENODATA, "the target xattr does not exist"); + } + + let value = { + let mut value = vec![0u8; value_reader.remain()]; + value_reader.read_fallible(&mut VmWriter::from(value.as_mut_slice()))?; + value + }; + let _ = entry.insert(value); + + xattr.total_name_count += 1; + xattr.total_name_len += name_len; + if namespace.is_user() { + xattr.user_name_count += 1; + xattr.user_name_len += name_len; + } + } + }; + + Ok(()) + } + + pub fn get(&self, name: XattrName, value_writer: &mut VmWriter) -> Result { + let existence_error = + Error::with_message(Errno::ENODATA, "the target xattr does not exist"); + let inner = self.0.get().ok_or(existence_error)?; + + let xattr = inner.read(); + if xattr.total_name_count == 0 { + return Err(existence_error); + } + + let value = xattr.map.get(&name).ok_or(existence_error)?; + let value_len = value.len(); + + let value_avail_len = value_writer.avail(); + if value_avail_len == 0 { + return Ok(value_len); + } + if value_len > value_avail_len { + return_errno_with_message!(Errno::ERANGE, "the xattr value buffer is too small"); + } + + value_writer.write_fallible(&mut VmReader::from(value.as_slice()))?; + Ok(value_len) + } + + pub fn list(&self, namespace: XattrNamespace, list_writer: &mut VmWriter) -> Result { + let Some(inner) = self.0.get() else { + return Ok(0); + }; + let xattr = inner.read(); + + // Include the null byte following each name + let list_actual_len = if namespace.is_user() { + xattr.user_name_len + xattr.user_name_count + } else { + xattr.total_name_len + xattr.total_name_count + }; + let list_avail_len = list_writer.avail(); + if list_avail_len == 0 { + return Ok(list_actual_len); + } + if list_actual_len > list_avail_len { + return_errno_with_message!(Errno::ERANGE, "the xattr list buffer is too small"); + } + + for (name, _) in &xattr.map { + if namespace.is_user() && !name.namespace.is_user() { + continue; + } + + list_writer.write_fallible(&mut VmReader::from(name.full_name.as_bytes()))?; + list_writer.write_val(&0u8)?; + } + Ok(list_actual_len) + } + + pub fn remove(&self, name: XattrName) -> Result<()> { + let existence_error = + Error::with_message(Errno::ENODATA, "the target xattr does not exist"); + let inner = self.0.get().ok_or(existence_error)?; + + let mut xattr = inner.write(); + if xattr.total_name_count == 0 { + return Err(existence_error); + } + + xattr.map.remove(&name).ok_or(existence_error)?; + + let namespace = name.namespace(); + let name_len = name.full_name_len(); + xattr.total_name_count -= 1; + xattr.total_name_len -= name_len; + if namespace.is_user() { + xattr.user_name_count -= 1; + xattr.user_name_len -= name_len; + } + Ok(()) + } + + /// Checks if the file type is valid for xattr support. + pub fn check_file_type_for_xattr(file_type: InodeType) -> Result<()> { + match file_type { + InodeType::File | InodeType::Dir => Ok(()), + _ => Err(Error::with_message( + Errno::EPERM, + "xattr is not supported on the file type", + )), + } + } +} + +impl From> for RamXattrName { + fn from(value: XattrName) -> Self { + Self { + namespace: value.namespace(), + full_name: value.full_name().to_string(), + } + } +} + +impl Equivalent for XattrName<'_> { + fn equivalent(&self, key: &RamXattrName) -> bool { + self.namespace() == key.namespace && self.full_name() == key.full_name + } +} + +impl RamXattrInner { + pub fn new() -> Self { + Self { + map: HashMap::new(), + total_name_count: 0, + total_name_len: 0, + user_name_count: 0, + user_name_len: 0, + } + } +}