mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-08 21:06:48 +00:00
Add xattr support for ramfs
This commit is contained in:
parent
a47eda413c
commit
97c27e8d2a
@ -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>(
|
||||
|
@ -5,6 +5,7 @@
|
||||
pub use fs::RamFS;
|
||||
|
||||
mod fs;
|
||||
mod xattr;
|
||||
|
||||
const RAMFS_MAGIC: u64 = 0x0102_1994;
|
||||
const BLOCK_SIZE: usize = 4096;
|
||||
|
205
kernel/src/fs/ramfs/xattr.rs
Normal file
205
kernel/src/fs/ramfs/xattr.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user