mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-11 06:16:49 +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},
|
sync::{PreemptDisabled, RwLockWriteGuard},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::{xattr::RamXattr, *};
|
||||||
use crate::{
|
use crate::{
|
||||||
events::IoEvents,
|
events::IoEvents,
|
||||||
fs::{
|
fs::{
|
||||||
@ -26,7 +26,7 @@ use crate::{
|
|||||||
utils::{
|
utils::{
|
||||||
CStr256, CachePage, DirentVisitor, Extension, FallocMode, FileSystem, FsFlags, Inode,
|
CStr256, CachePage, DirentVisitor, Extension, FallocMode, FileSystem, FsFlags, Inode,
|
||||||
InodeMode, InodeType, IoctlCmd, Metadata, MknodType, PageCache, PageCacheBackend,
|
InodeMode, InodeType, IoctlCmd, Metadata, MknodType, PageCache, PageCacheBackend,
|
||||||
SuperBlock,
|
Permission, SuperBlock, XattrName, XattrNamespace, XattrSetFlags,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
@ -61,6 +61,7 @@ impl RamFS {
|
|||||||
this: weak_root.clone(),
|
this: weak_root.clone(),
|
||||||
fs: weak_fs.clone(),
|
fs: weak_fs.clone(),
|
||||||
extension: Extension::new(),
|
extension: Extension::new(),
|
||||||
|
xattr: RamXattr::new(),
|
||||||
}),
|
}),
|
||||||
inode_allocator: AtomicU64::new(ROOT_INO + 1),
|
inode_allocator: AtomicU64::new(ROOT_INO + 1),
|
||||||
})
|
})
|
||||||
@ -106,6 +107,8 @@ struct RamInode {
|
|||||||
fs: Weak<RamFS>,
|
fs: Weak<RamFS>,
|
||||||
/// Extensions
|
/// Extensions
|
||||||
extension: Extension,
|
extension: Extension,
|
||||||
|
/// Extended attributes
|
||||||
|
xattr: RamXattr,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inode inner specifics.
|
/// Inode inner specifics.
|
||||||
@ -399,6 +402,7 @@ impl RamInode {
|
|||||||
this: weak_self.clone(),
|
this: weak_self.clone(),
|
||||||
fs: Arc::downgrade(fs),
|
fs: Arc::downgrade(fs),
|
||||||
extension: Extension::new(),
|
extension: Extension::new(),
|
||||||
|
xattr: RamXattr::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -411,6 +415,7 @@ impl RamInode {
|
|||||||
this: weak_self.clone(),
|
this: weak_self.clone(),
|
||||||
fs: Arc::downgrade(fs),
|
fs: Arc::downgrade(fs),
|
||||||
extension: Extension::new(),
|
extension: Extension::new(),
|
||||||
|
xattr: RamXattr::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -423,6 +428,7 @@ impl RamInode {
|
|||||||
this: weak_self.clone(),
|
this: weak_self.clone(),
|
||||||
fs: Arc::downgrade(fs),
|
fs: Arc::downgrade(fs),
|
||||||
extension: Extension::new(),
|
extension: Extension::new(),
|
||||||
|
xattr: RamXattr::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -441,6 +447,7 @@ impl RamInode {
|
|||||||
this: weak_self.clone(),
|
this: weak_self.clone(),
|
||||||
fs: Arc::downgrade(fs),
|
fs: Arc::downgrade(fs),
|
||||||
extension: Extension::new(),
|
extension: Extension::new(),
|
||||||
|
xattr: RamXattr::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -453,6 +460,7 @@ impl RamInode {
|
|||||||
this: weak_self.clone(),
|
this: weak_self.clone(),
|
||||||
fs: Arc::downgrade(fs),
|
fs: Arc::downgrade(fs),
|
||||||
extension: Extension::new(),
|
extension: Extension::new(),
|
||||||
|
xattr: RamXattr::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -465,6 +473,7 @@ impl RamInode {
|
|||||||
this: weak_self.clone(),
|
this: weak_self.clone(),
|
||||||
fs: Arc::downgrade(fs),
|
fs: Arc::downgrade(fs),
|
||||||
extension: Extension::new(),
|
extension: Extension::new(),
|
||||||
|
xattr: RamXattr::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1190,6 +1199,40 @@ impl Inode for RamInode {
|
|||||||
fn extension(&self) -> Option<&Extension> {
|
fn extension(&self) -> Option<&Extension> {
|
||||||
Some(&self.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>(
|
fn write_lock_two_direntries_by_ino<'a>(
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
pub use fs::RamFS;
|
pub use fs::RamFS;
|
||||||
|
|
||||||
mod fs;
|
mod fs;
|
||||||
|
mod xattr;
|
||||||
|
|
||||||
const RAMFS_MAGIC: u64 = 0x0102_1994;
|
const RAMFS_MAGIC: u64 = 0x0102_1994;
|
||||||
const BLOCK_SIZE: usize = 4096;
|
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