mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-08 12:56:48 +00:00
Add xattr support for ext2
This commit is contained in:
parent
97c27e8d2a
commit
2af71ff72a
@ -11,7 +11,7 @@ use crate::{
|
||||
ext2::{FilePerm, Inode as Ext2Inode},
|
||||
utils::{
|
||||
DirentVisitor, Extension, FallocMode, FileSystem, Inode, InodeMode, InodeType,
|
||||
IoctlCmd, Metadata, MknodType,
|
||||
IoctlCmd, Metadata, MknodType, XattrName, XattrNamespace, XattrSetFlags,
|
||||
},
|
||||
},
|
||||
prelude::*,
|
||||
@ -194,6 +194,27 @@ impl Inode for Ext2Inode {
|
||||
fn extension(&self) -> Option<&Extension> {
|
||||
Some(self.extension())
|
||||
}
|
||||
|
||||
fn set_xattr(
|
||||
&self,
|
||||
name: XattrName,
|
||||
value_reader: &mut VmReader,
|
||||
flags: XattrSetFlags,
|
||||
) -> Result<()> {
|
||||
self.set_xattr(name, value_reader, flags)
|
||||
}
|
||||
|
||||
fn get_xattr(&self, name: XattrName, value_writer: &mut VmWriter) -> Result<usize> {
|
||||
self.get_xattr(name, value_writer)
|
||||
}
|
||||
|
||||
fn list_xattr(&self, namespace: XattrNamespace, list_writer: &mut VmWriter) -> Result<usize> {
|
||||
self.list_xattr(namespace, list_writer)
|
||||
}
|
||||
|
||||
fn remove_xattr(&self, name: XattrName) -> Result<()> {
|
||||
self.remove_xattr(name)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FilePerm> for InodeMode {
|
||||
|
@ -16,11 +16,15 @@ use super::{
|
||||
indirect_block_cache::{IndirectBlock, IndirectBlockCache},
|
||||
prelude::*,
|
||||
utils::now,
|
||||
xattr::Xattr,
|
||||
};
|
||||
use crate::{
|
||||
fs::{
|
||||
path::{is_dot, is_dot_or_dotdot, is_dotdot},
|
||||
utils::{Extension, FallocMode, InodeMode, Metadata},
|
||||
utils::{
|
||||
Extension, FallocMode, Inode as _, InodeMode, Metadata, Permission, XattrName,
|
||||
XattrNamespace, XattrSetFlags,
|
||||
},
|
||||
},
|
||||
process::{posix_thread::AsPosixThread, Gid, Uid},
|
||||
};
|
||||
@ -39,6 +43,7 @@ pub struct Inode {
|
||||
inner: RwMutex<InodeInner>,
|
||||
fs: Weak<Ext2>,
|
||||
extension: Extension,
|
||||
xattr: Option<Xattr>,
|
||||
}
|
||||
|
||||
impl Inode {
|
||||
@ -52,6 +57,9 @@ impl Inode {
|
||||
ino,
|
||||
type_: desc.type_,
|
||||
block_group_idx,
|
||||
xattr: desc
|
||||
.acl
|
||||
.map(|acl| Xattr::new(acl, weak_self.clone(), fs.clone())),
|
||||
inner: RwMutex::new(InodeInner::new(desc, weak_self.clone(), fs.clone())),
|
||||
fs,
|
||||
extension: Extension::new(),
|
||||
@ -745,6 +753,9 @@ impl Inode {
|
||||
let mut inner = self.inner.write();
|
||||
inner.sync_data()?;
|
||||
inner.sync_metadata()?;
|
||||
if let Some(xattr) = self.xattr.as_ref() {
|
||||
xattr.flush()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -808,6 +819,48 @@ impl Inode {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_xattr(
|
||||
&self,
|
||||
name: XattrName,
|
||||
value_reader: &mut VmReader,
|
||||
flags: XattrSetFlags,
|
||||
) -> Result<()> {
|
||||
let xattr = self.xattr.as_ref().ok_or(Error::with_message(
|
||||
Errno::EPERM,
|
||||
"xattr is not supported on the file type",
|
||||
))?;
|
||||
self.check_permission(Permission::MAY_WRITE)?;
|
||||
xattr.set(name, value_reader, flags)
|
||||
}
|
||||
|
||||
pub fn get_xattr(&self, name: XattrName, value_writer: &mut VmWriter) -> Result<usize> {
|
||||
if self.xattr.is_none() {
|
||||
return_errno_with_message!(Errno::ENODATA, "no available xattrs");
|
||||
}
|
||||
self.check_permission(Permission::MAY_READ)?;
|
||||
self.xattr.as_ref().unwrap().get(name, value_writer)
|
||||
}
|
||||
|
||||
pub fn list_xattr(
|
||||
&self,
|
||||
namespace: XattrNamespace,
|
||||
list_writer: &mut VmWriter,
|
||||
) -> Result<usize> {
|
||||
if self.xattr.is_none() || self.check_permission(Permission::MAY_ACCESS).is_err() {
|
||||
return Ok(0);
|
||||
}
|
||||
self.xattr.as_ref().unwrap().list(namespace, list_writer)
|
||||
}
|
||||
|
||||
pub fn remove_xattr(&self, name: XattrName) -> Result<()> {
|
||||
let xattr = self.xattr.as_ref().ok_or(Error::with_message(
|
||||
Errno::EPERM,
|
||||
"xattr is not supported on the file type",
|
||||
))?;
|
||||
self.check_permission(Permission::MAY_WRITE)?;
|
||||
self.xattr.as_ref().unwrap().remove(name)
|
||||
}
|
||||
}
|
||||
|
||||
#[inherit_methods(from = "self.inner.read()")]
|
||||
@ -827,6 +880,7 @@ impl Inode {
|
||||
|
||||
#[inherit_methods(from = "self.inner.write()")]
|
||||
impl Inode {
|
||||
pub fn set_acl(&self, bid: Bid);
|
||||
pub fn set_atime(&self, time: Duration);
|
||||
pub fn set_mtime(&self, time: Duration);
|
||||
pub fn set_ctime(&self, time: Duration);
|
||||
@ -1123,6 +1177,7 @@ impl InodeInner {
|
||||
pub fn dec_hard_links(&mut self);
|
||||
pub fn blocks_count(&self) -> Ext2Bid;
|
||||
pub fn acl(&self) -> Option<Bid>;
|
||||
pub fn set_acl(&mut self, bid: Bid);
|
||||
pub fn atime(&self) -> Duration;
|
||||
pub fn set_atime(&mut self, time: Duration);
|
||||
pub fn mtime(&self) -> Duration;
|
||||
@ -1224,6 +1279,10 @@ impl InodeImpl {
|
||||
self.desc.acl
|
||||
}
|
||||
|
||||
pub fn set_acl(&mut self, bid: Bid) {
|
||||
self.desc.acl = Some(bid);
|
||||
}
|
||||
|
||||
pub fn atime(&self) -> Duration {
|
||||
self.desc.atime
|
||||
}
|
||||
@ -1292,6 +1351,9 @@ impl InodeImpl {
|
||||
inode
|
||||
.fs()
|
||||
.free_inode(inode.ino(), self.desc.type_ == InodeType::Dir)?;
|
||||
if let Some(xattr) = &inode.xattr {
|
||||
xattr.free()?;
|
||||
}
|
||||
self.is_freed = true;
|
||||
}
|
||||
}
|
||||
|
@ -50,3 +50,4 @@ mod inode;
|
||||
mod prelude;
|
||||
mod super_block;
|
||||
mod utils;
|
||||
mod xattr;
|
||||
|
490
kernel/src/fs/ext2/xattr.rs
Normal file
490
kernel/src/fs/ext2/xattr.rs
Normal file
@ -0,0 +1,490 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use ostd::mm::UntypedMem;
|
||||
|
||||
use super::{block_ptr::Ext2Bid, prelude::*, Ext2, Inode};
|
||||
use crate::fs::utils::{XattrName, XattrNamespace, XattrSetFlags, XATTR_NAME_MAX_LEN};
|
||||
|
||||
const EXT2_XATTR_MAGIC: u32 = 0xEA020000;
|
||||
|
||||
/// The xattr header of an ext2 inode, organized
|
||||
/// at the beginning of an xattr block.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Pod, Debug)]
|
||||
struct XattrHeader {
|
||||
magic: u32,
|
||||
ref_count: u32,
|
||||
nblocks: u32,
|
||||
hash: u32,
|
||||
reserved: [u32; 4],
|
||||
}
|
||||
|
||||
const XATTR_HEADER_SIZE: usize = size_of::<XattrHeader>();
|
||||
const XATTR_ALIGN: usize = 4;
|
||||
const XATTR_ENTRY_VALUE_GAP: usize = XATTR_ALIGN;
|
||||
|
||||
/// The xattr entry of an ext2 inode, organized on an xattr block.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Pod, Debug)]
|
||||
struct XattrEntry {
|
||||
name_len: u8,
|
||||
name_index: u8,
|
||||
value_offset: u16,
|
||||
value_block: u32,
|
||||
value_len: u32,
|
||||
hash: u32,
|
||||
}
|
||||
|
||||
const XATTR_ENTRY_SIZE: usize = size_of::<XattrEntry>();
|
||||
|
||||
/// An xattr object of an ext2 inode.
|
||||
/// An xattr is used to manage special 'name-value' pairs of an inode.
|
||||
///
|
||||
/// An xattr block layout (objects are aligned to 4 bytes):
|
||||
/// +--------------------+
|
||||
/// | XattrHeader |
|
||||
/// | XattrEntry 1 | |
|
||||
/// | XattrName 1 | |
|
||||
/// | XattrEntry 2 | | growing downwards
|
||||
/// | XattrName 2 | |
|
||||
/// | XattrEntry 3 | v
|
||||
/// | XattrName 3 |
|
||||
/// | ... |
|
||||
/// | Gap (4 null bytes) |
|
||||
/// | ... |
|
||||
/// | XattrValue 3 | ^
|
||||
/// | XattrValue 2 | | growing upwards
|
||||
/// | XattrValue 1 | |
|
||||
/// +--------------------+
|
||||
#[derive(Debug)]
|
||||
pub(super) struct Xattr {
|
||||
/// The buffer of the xattr block, which always keeps its content up-to-date.
|
||||
blocks_buf: USegment,
|
||||
/// A cache to assist the xattr operations.
|
||||
cache: RwMutex<Dirty<XattrCache>>,
|
||||
inode: Weak<Inode>,
|
||||
fs: Weak<Ext2>,
|
||||
}
|
||||
|
||||
/// Gathers helper metadata to describe an xattr block. This is
|
||||
/// primarily for convenience when manipulating xattr entries or values.
|
||||
/// Without this cache, the raw buffer would need to be parsed every time.
|
||||
#[derive(Debug)]
|
||||
struct XattrCache {
|
||||
bid: Bid,
|
||||
header: Option<XattrHeader>,
|
||||
entries: BTreeMap<usize, XattrEntry>, // K: offset, V: entry
|
||||
values: BTreeMap<usize, usize>, // K: offset, V: len
|
||||
capacity_bytes: usize,
|
||||
}
|
||||
|
||||
/// The number of blocks for an xattr. This value should be
|
||||
/// the consistent with `XattrHeader::nblocks`.
|
||||
pub(super) const XATTR_NBLOCKS: usize = 1;
|
||||
|
||||
impl XattrEntry {
|
||||
fn total_len(&self) -> usize {
|
||||
XATTR_ENTRY_SIZE + (self.name_len as usize).align_up(XATTR_ALIGN)
|
||||
}
|
||||
|
||||
fn target_len(name_len: usize) -> usize {
|
||||
XATTR_ENTRY_SIZE + name_len.align_up(XATTR_ALIGN)
|
||||
}
|
||||
}
|
||||
|
||||
impl Xattr {
|
||||
pub fn new(bid: Bid, inode: Weak<Inode>, fs: Weak<Ext2>) -> Self {
|
||||
let blocks_buf: USegment = FrameAllocOptions::new()
|
||||
.zeroed(true)
|
||||
.alloc_segment(XATTR_NBLOCKS)
|
||||
.unwrap()
|
||||
.into();
|
||||
Self {
|
||||
cache: RwMutex::new(Dirty::new(XattrCache::new(bid, blocks_buf.size()))),
|
||||
blocks_buf,
|
||||
inode,
|
||||
fs,
|
||||
}
|
||||
}
|
||||
|
||||
/// Lazily initialize the xattr structures only when actual xattr operations are performed.
|
||||
fn lazy_init(&self) -> Result<()> {
|
||||
let fs = self.fs();
|
||||
let cache = self.cache.upread();
|
||||
|
||||
// Need to allocate a new xattr block
|
||||
if cache.bid.to_raw() == 0 {
|
||||
assert!(cache.header.is_none());
|
||||
let new_bid = {
|
||||
let allocated = fs
|
||||
.alloc_blocks(self.inode().block_group_idx(), XATTR_NBLOCKS as _)
|
||||
.ok_or(Error::new(Errno::ENOSPC))?
|
||||
.start as u64;
|
||||
Bid::new(allocated)
|
||||
};
|
||||
|
||||
let new_header = XattrHeader::default();
|
||||
self.blocks_buf.write_val(0, &new_header)?;
|
||||
|
||||
let mut cache = cache.upgrade();
|
||||
cache.header = Some(new_header);
|
||||
cache.bid = new_bid;
|
||||
self.inode().set_acl(new_bid);
|
||||
// Need to load the xattr block from device
|
||||
} else if cache.header.is_none() {
|
||||
fs.block_device().read_blocks(
|
||||
cache.bid,
|
||||
BioSegment::new_from_segment(self.blocks_buf.clone(), BioDirection::FromDevice),
|
||||
)?;
|
||||
|
||||
let header = self.blocks_buf.read_val::<XattrHeader>(0)?;
|
||||
if header.magic != EXT2_XATTR_MAGIC {
|
||||
return_errno_with_message!(Errno::EINVAL, "invalid xattr magic");
|
||||
}
|
||||
|
||||
let mut cache = cache.upgrade();
|
||||
cache.header = Some(header);
|
||||
cache.build_entries_from_buf(&self.blocks_buf)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set(
|
||||
&self,
|
||||
name: XattrName,
|
||||
value_reader: &mut VmReader,
|
||||
flags: XattrSetFlags,
|
||||
) -> Result<()> {
|
||||
self.lazy_init()?;
|
||||
|
||||
let namespace = name.namespace();
|
||||
let name_len = name.full_name_len();
|
||||
let value_len = value_reader.remain();
|
||||
let cache = self.cache.upread();
|
||||
if let Some((offset, mut entry)) = cache.find_entry(&name, &self.blocks_buf) {
|
||||
if flags.contains(XattrSetFlags::CREATE_ONLY) {
|
||||
return_errno_with_message!(Errno::EEXIST, "the target xattr already exists");
|
||||
}
|
||||
|
||||
if value_len <= entry.value_len as usize {
|
||||
self.blocks_buf
|
||||
.write(entry.value_offset as _, value_reader)?;
|
||||
|
||||
if value_len != entry.value_len as usize {
|
||||
entry.value_len = value_len as _;
|
||||
self.blocks_buf.write_val(offset, &entry)?;
|
||||
|
||||
let mut cache = cache.upgrade();
|
||||
let _ = cache.entries.insert(offset, entry);
|
||||
let _ = cache.values.insert(entry.value_offset as _, value_len);
|
||||
}
|
||||
} else {
|
||||
let new_value_offset = cache
|
||||
.find_room_for_new_value(value_len)
|
||||
.ok_or(Error::new(Errno::ENOSPC))?;
|
||||
self.blocks_buf.write(new_value_offset, value_reader)?;
|
||||
|
||||
let mut cache = cache.upgrade();
|
||||
let _ = cache.values.remove(&(entry.value_offset as _));
|
||||
entry.value_offset = new_value_offset as _;
|
||||
entry.value_len = value_len as _;
|
||||
self.blocks_buf.write_val(offset, &entry)?;
|
||||
|
||||
let _ = cache.entries.insert(offset, entry);
|
||||
let _ = cache.values.insert(new_value_offset as _, value_len);
|
||||
}
|
||||
} else {
|
||||
if flags.contains(XattrSetFlags::REPLACE_ONLY) {
|
||||
return_errno_with_message!(Errno::ENODATA, "the target xattr does not exist");
|
||||
}
|
||||
|
||||
let new_entry_offset = cache
|
||||
.find_room_for_new_entry(name_len)
|
||||
.ok_or(Error::new(Errno::ENOSPC))?;
|
||||
let new_value_offset = cache
|
||||
.find_room_for_new_value(value_len)
|
||||
.ok_or(Error::new(Errno::ENOSPC))?;
|
||||
let new_entry = XattrEntry {
|
||||
name_len: name_len as _,
|
||||
name_index: namespace as _,
|
||||
value_offset: new_value_offset as _,
|
||||
value_block: 0, // TBD
|
||||
value_len: value_len as _,
|
||||
hash: 0, // TBD
|
||||
};
|
||||
|
||||
self.blocks_buf.write_val(new_entry_offset, &new_entry)?;
|
||||
self.blocks_buf.write_bytes(
|
||||
new_entry_offset + XATTR_ENTRY_SIZE,
|
||||
name.full_name().as_bytes(),
|
||||
)?;
|
||||
self.blocks_buf.write(new_value_offset, value_reader)?;
|
||||
|
||||
let mut cache = cache.upgrade();
|
||||
let _ = cache.entries.insert(new_entry_offset, new_entry);
|
||||
let _ = cache.values.insert(new_value_offset, value_len);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get(&self, name: XattrName, value_writer: &mut VmWriter) -> Result<usize> {
|
||||
self.lazy_init()?;
|
||||
|
||||
let value_avail_len = value_writer.avail();
|
||||
let (_, entry) = self
|
||||
.cache
|
||||
.read()
|
||||
.find_entry(&name, &self.blocks_buf)
|
||||
.ok_or(Error::new(Errno::ENODATA))?;
|
||||
|
||||
let value_len = entry.value_len as usize;
|
||||
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 self
|
||||
.blocks_buf
|
||||
.reader()
|
||||
.to_fallible()
|
||||
.skip(entry.value_offset as usize)
|
||||
.limit(value_len),
|
||||
)?;
|
||||
|
||||
Ok(value_len)
|
||||
}
|
||||
|
||||
pub fn list(&self, namespace: XattrNamespace, list_writer: &mut VmWriter) -> Result<usize> {
|
||||
self.lazy_init()?;
|
||||
|
||||
let list_avail_len = list_writer.avail();
|
||||
let cache = self.cache.read();
|
||||
|
||||
let target_list: Vec<_> = cache
|
||||
.entries
|
||||
.iter()
|
||||
.filter_map(|(offset, entry)| {
|
||||
if namespace.is_user() && entry.name_index != XattrNamespace::User as u8 {
|
||||
None
|
||||
} else {
|
||||
Some((offset, entry.name_len as usize))
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
// Include the null byte following each name
|
||||
let list_actual_len = target_list
|
||||
.iter()
|
||||
.map(|(_, name_len)| name_len)
|
||||
.sum::<usize>()
|
||||
+ target_list.len();
|
||||
|
||||
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 (offset, name_len) in target_list {
|
||||
let mut reader = self.blocks_buf.reader().to_fallible();
|
||||
let name_reader = reader.skip(offset + XATTR_ENTRY_SIZE).limit(name_len);
|
||||
|
||||
list_writer.write_fallible(name_reader)?;
|
||||
list_writer.write_val(&0u8)?;
|
||||
}
|
||||
|
||||
Ok(list_actual_len)
|
||||
}
|
||||
|
||||
pub fn remove(&self, name: XattrName) -> Result<()> {
|
||||
self.lazy_init()?;
|
||||
|
||||
let cache = self.cache.upread();
|
||||
let (offset, entry) =
|
||||
cache
|
||||
.find_entry(&name, &self.blocks_buf)
|
||||
.ok_or(Error::with_message(
|
||||
Errno::ENODATA,
|
||||
"the target xattr does not exist",
|
||||
))?;
|
||||
|
||||
let len = entry.total_len();
|
||||
self.blocks_buf
|
||||
.writer()
|
||||
.to_fallible()
|
||||
.skip(offset)
|
||||
.limit(len)
|
||||
.fill_zeros(len)?;
|
||||
|
||||
let mut cache = cache.upgrade();
|
||||
let _ = cache.entries.remove(&offset);
|
||||
let _ = cache.values.remove(&(entry.value_offset as _));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn flush(&self) -> Result<()> {
|
||||
let cache = self.cache.upread();
|
||||
if cache.is_dirty() {
|
||||
self.fs().block_device().write_blocks(
|
||||
cache.bid,
|
||||
BioSegment::new_from_segment(self.blocks_buf.clone(), BioDirection::ToDevice),
|
||||
)?;
|
||||
cache.upgrade().clear_dirty();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn free(&self) -> Result<()> {
|
||||
let cache = self.cache.upread();
|
||||
let bid = cache.bid.to_raw() as Ext2Bid;
|
||||
if bid == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
self.fs().free_blocks(bid..bid)?;
|
||||
cache.upgrade().bid = Bid::new(0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fs(&self) -> Arc<Ext2> {
|
||||
self.fs.upgrade().unwrap()
|
||||
}
|
||||
|
||||
fn inode(&self) -> Arc<Inode> {
|
||||
self.inode.upgrade().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl XattrCache {
|
||||
pub fn new(bid: Bid, capacity_bytes: usize) -> Self {
|
||||
Self {
|
||||
bid,
|
||||
header: None,
|
||||
entries: BTreeMap::new(),
|
||||
values: BTreeMap::new(),
|
||||
capacity_bytes,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_entries_from_buf(&mut self, blocks_buf: &USegment) -> Result<()> {
|
||||
let mut offset = XATTR_HEADER_SIZE;
|
||||
|
||||
while offset < self.capacity_bytes - XATTR_ENTRY_VALUE_GAP - XATTR_ENTRY_SIZE {
|
||||
let entry = blocks_buf.read_val::<XattrEntry>(offset)?;
|
||||
if entry.name_len == 0
|
||||
|| XattrNamespace::try_from(entry.name_index).is_err()
|
||||
|| entry.value_offset as usize + entry.value_len as usize > self.capacity_bytes
|
||||
{
|
||||
offset += XATTR_ALIGN;
|
||||
continue;
|
||||
}
|
||||
|
||||
let _ = self.entries.insert(offset, entry);
|
||||
let _ = self
|
||||
.values
|
||||
.insert(entry.value_offset as _, entry.value_len as _);
|
||||
offset += entry.total_len();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn find_entry(&self, name: &XattrName, buf: &USegment) -> Option<(usize, XattrEntry)> {
|
||||
let namespace = name.namespace();
|
||||
let name_bytes = name.full_name().as_bytes();
|
||||
let name_len = name_bytes.len();
|
||||
debug_assert!(name_len > 0);
|
||||
let mut name_buf = [0u8; XATTR_NAME_MAX_LEN];
|
||||
for (offset, entry) in &self.entries {
|
||||
if entry.name_index == namespace as u8 && entry.name_len == name_len as u8 {
|
||||
buf.read_bytes(offset + XATTR_ENTRY_SIZE, &mut name_buf[..name_len])
|
||||
.unwrap();
|
||||
if &name_buf[..name_len] == name_bytes {
|
||||
return Some((*offset, *entry));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn find_room_for_new_entry(&self, name_len: usize) -> Option<usize> {
|
||||
let target_len = XattrEntry::target_len(name_len);
|
||||
|
||||
// Fast path: find at the entries' end
|
||||
let bottom = self
|
||||
.values
|
||||
.first_key_value()
|
||||
.map(|(offset, _)| *offset)
|
||||
.unwrap_or(self.capacity_bytes)
|
||||
- XATTR_ENTRY_VALUE_GAP;
|
||||
let last_entry_end = self
|
||||
.entries
|
||||
.last_key_value()
|
||||
.map(|(offset, entry)| *offset + entry.total_len())
|
||||
.unwrap_or(XATTR_HEADER_SIZE);
|
||||
if bottom - last_entry_end >= target_len {
|
||||
return Some(last_entry_end);
|
||||
}
|
||||
|
||||
// Slow path: find around the gaps
|
||||
let mut pre_end = XATTR_HEADER_SIZE;
|
||||
for (offset, entry) in &self.entries {
|
||||
if offset - pre_end >= target_len {
|
||||
return Some(pre_end);
|
||||
} else {
|
||||
pre_end = offset + entry.total_len();
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn find_room_for_new_value(&self, value_len: usize) -> Option<usize> {
|
||||
let target_len = value_len.align_up(XATTR_ALIGN);
|
||||
|
||||
// Fast path: find at the values' end
|
||||
let top = self
|
||||
.entries
|
||||
.last_key_value()
|
||||
.map(|(offset, entry)| *offset + entry.total_len())
|
||||
.unwrap_or(XATTR_HEADER_SIZE)
|
||||
+ XATTR_ENTRY_VALUE_GAP;
|
||||
let last_value_start = self
|
||||
.values
|
||||
.first_key_value()
|
||||
.map(|(offset, _)| *offset)
|
||||
.unwrap_or(self.capacity_bytes);
|
||||
|
||||
if last_value_start - top >= target_len {
|
||||
return Some(last_value_start - target_len);
|
||||
}
|
||||
|
||||
// Slow path: find around the gaps
|
||||
let mut pre_end = self.capacity_bytes;
|
||||
for (offset, value_len) in self.values.iter().rev() {
|
||||
let cur_end = offset + value_len.align_up(XATTR_ALIGN);
|
||||
if pre_end - cur_end >= target_len {
|
||||
return Some(cur_end);
|
||||
} else {
|
||||
pre_end = *offset;
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for XattrHeader {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
magic: EXT2_XATTR_MAGIC,
|
||||
nblocks: XATTR_NBLOCKS as _,
|
||||
ref_count: Default::default(),
|
||||
hash: Default::default(),
|
||||
reserved: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user