Add xattr support for ramfs

This commit is contained in:
Shaowei Song 2025-03-28 06:00:34 +00:00 committed by Tate, Hongliang Tian
parent a47eda413c
commit 97c27e8d2a
3 changed files with 251 additions and 2 deletions

View File

@ -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<RamFS>,
/// 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<usize> {
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<usize> {
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>(

View File

@ -5,6 +5,7 @@
pub use fs::RamFS;
mod fs;
mod xattr;
const RAMFS_MAGIC: u64 = 0x0102_1994;
const BLOCK_SIZE: usize = 4096;

View File

@ -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<Box<RwMutex<RamXattrInner>>>);
/// 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<u8>;
#[derive(Debug, Clone)]
struct RamXattrInner {
map: HashMap<RamXattrName, RamXattrValue>,
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<usize> {
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<usize> {
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<XattrName<'_>> for RamXattrName {
fn from(value: XattrName) -> Self {
Self {
namespace: value.namespace(),
full_name: value.full_name().to_string(),
}
}
}
impl Equivalent<RamXattrName> 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,
}
}
}