Merge pull request #138 from arihant2math/xattr

Implement xattr syscalls
This commit is contained in:
Matthew Leach
2026-01-14 19:13:04 +00:00
committed by GitHub
12 changed files with 520 additions and 12 deletions

View File

@@ -7,18 +7,18 @@
| 0x2 (2) | io_submit | (aio_context_t ctx_id, long nr, struct iocb **iocbpp) | __arm64_sys_io_submit | false |
| 0x3 (3) | io_cancel | (aio_context_t ctx_id, struct iocb *iocb, struct io_event *result) | __arm64_sys_io_cancel | false |
| 0x4 (4) | io_getevents | (aio_context_t ctx_id, long min_nr, long nr, struct io_event *events, struct __kernel_timespec *timeout) | __arm64_sys_io_getevents | false |
| 0x5 (5) | setxattr | (const char *pathname, const char *name, const void *value, size_t size, int flags) | __arm64_sys_setxattr | false |
| 0x6 (6) | lsetxattr | (const char *pathname, const char *name, const void *value, size_t size, int flags) | __arm64_sys_lsetxattr | false |
| 0x7 (7) | fsetxattr | (int fd, const char *name, const void *value, size_t size, int flags) | __arm64_sys_fsetxattr | false |
| 0x8 (8) | getxattr | (const char *pathname, const char *name, void *value, size_t size) | __arm64_sys_getxattr | false |
| 0x9 (9) | lgetxattr | (const char *pathname, const char *name, void *value, size_t size) | __arm64_sys_lgetxattr | false |
| 0xa (10) | fgetxattr | (int fd, const char *name, void *value, size_t size) | __arm64_sys_fgetxattr | false |
| 0xb (11) | listxattr | (const char *pathname, char *list, size_t size) | __arm64_sys_listxattr | false |
| 0xc (12) | llistxattr | (const char *pathname, char *list, size_t size) | __arm64_sys_llistxattr | false |
| 0xd (13) | flistxattr | (int fd, char *list, size_t size) | __arm64_sys_flistxattr | false |
| 0xe (14) | removexattr | (const char *pathname, const char *name) | __arm64_sys_removexattr | false |
| 0xf (15) | lremovexattr | (const char *pathname, const char *name) | __arm64_sys_lremovexattr | false |
| 0x10 (16) | fremovexattr | (int fd, const char *name) | __arm64_sys_fremovexattr | false |
| 0x5 (5) | setxattr | (const char *pathname, const char *name, const void *value, size_t size, int flags) | __arm64_sys_setxattr | true |
| 0x6 (6) | lsetxattr | (const char *pathname, const char *name, const void *value, size_t size, int flags) | __arm64_sys_lsetxattr | true |
| 0x7 (7) | fsetxattr | (int fd, const char *name, const void *value, size_t size, int flags) | __arm64_sys_fsetxattr | true |
| 0x8 (8) | getxattr | (const char *pathname, const char *name, void *value, size_t size) | __arm64_sys_getxattr | true |
| 0x9 (9) | lgetxattr | (const char *pathname, const char *name, void *value, size_t size) | __arm64_sys_lgetxattr | true |
| 0xa (10) | fgetxattr | (int fd, const char *name, void *value, size_t size) | __arm64_sys_fgetxattr | true |
| 0xb (11) | listxattr | (const char *pathname, char *list, size_t size) | __arm64_sys_listxattr | true |
| 0xc (12) | llistxattr | (const char *pathname, char *list, size_t size) | __arm64_sys_llistxattr | true |
| 0xd (13) | flistxattr | (int fd, char *list, size_t size) | __arm64_sys_flistxattr | true |
| 0xe (14) | removexattr | (const char *pathname, const char *name) | __arm64_sys_removexattr | true |
| 0xf (15) | lremovexattr | (const char *pathname, const char *name) | __arm64_sys_lremovexattr | true |
| 0x10 (16) | fremovexattr | (int fd, const char *name) | __arm64_sys_fremovexattr | true |
| 0x11 (17) | getcwd | (char *buf, unsigned long size) | __arm64_sys_getcwd | true |
| 0x13 (19) | eventfd2 | (unsigned int count, int flags) | __arm64_sys_eventfd2 | false |
| 0x14 (20) | epoll_create1 | (int flags) | __arm64_sys_epoll_create1 | false |

View File

@@ -186,6 +186,9 @@ pub enum KernelError {
#[error("Operation timed out")]
TimedOut,
#[error("Value out of range")]
RangeError,
#[error("{0}")]
Other(&'static str),
}

View File

@@ -53,6 +53,7 @@ pub fn kern_err_to_syscall(err: KernelError) -> isize {
KernelError::NotSupported => ENOSYS,
KernelError::NoMemory => ENOMEM,
KernelError::TimedOut => ETIMEDOUT,
KernelError::RangeError => ERANGE,
KernelError::NoChildProcess => ECHILD,
e => todo!("{e}"),
}

View File

@@ -19,6 +19,7 @@ use crate::{
},
};
use alloc::string::ToString;
use alloc::vec::Vec;
use alloc::{
boxed::Box,
sync::{Arc, Weak},

View File

@@ -687,6 +687,7 @@ struct TmpFsSymlinkInode<C: CpuOps> {
id: InodeId,
target: PathBuf,
attr: SpinLockIrq<FileAttr, C>,
xattr: SpinLockIrq<Vec<(String, Vec<u8>)>, C>,
}
#[async_trait]
@@ -707,6 +708,48 @@ impl<C: CpuOps> Inode for TmpFsSymlinkInode<C> {
async fn readlink(&self) -> Result<PathBuf> {
Ok(self.target.clone())
}
async fn getxattr(&self, name: &str) -> Result<Vec<u8>> {
let guard = self.xattr.lock_save_irq();
if let Some((_, value)) = guard.iter().find(|(key, _)| key == name) {
Ok(value.clone())
} else {
Err(FsError::NotFound.into())
}
}
async fn removexattr(&self, _name: &str) -> Result<()> {
let mut guard = self.xattr.lock_save_irq();
if let Some(pos) = guard.iter().position(|(key, _)| key == _name) {
guard.remove(pos);
Ok(())
} else {
Err(FsError::NotFound.into())
}
}
async fn listxattr(&self) -> Result<Vec<String>> {
let guard = self.xattr.lock_save_irq();
Ok(guard.iter().map(|(key, _)| key.clone()).collect())
}
async fn setxattr(&self, name: &str, buf: &[u8], create: bool, replace: bool) -> Result<()> {
let mut guard = self.xattr.lock_save_irq();
if let Some((_, value)) = guard.iter_mut().find(|(key, _)| key == name) {
if create {
return Err(FsError::AlreadyExists.into());
}
*value = buf.to_vec();
Ok(())
} else {
if replace {
return Err(FsError::NotFound.into());
}
guard.push((name.to_owned(), buf.to_vec()));
Ok(())
}
}
}
impl<C: CpuOps> TmpFsSymlinkInode<C> {
@@ -720,6 +763,7 @@ impl<C: CpuOps> TmpFsSymlinkInode<C> {
nlinks: 1,
..Default::default()
}),
xattr: SpinLockIrq::new(Vec::new()),
})
}
}

View File

@@ -26,6 +26,7 @@ use crate::{
error::{FsError, KernelError, Result},
fs::{path::Path, pathbuf::PathBuf},
};
use alloc::vec::Vec;
use alloc::{boxed::Box, string::String, sync::Arc};
use async_trait::async_trait;
use attr::{FileAttr, FilePermissions};
@@ -211,6 +212,34 @@ pub trait Inode: Send + Sync + Any {
Err(KernelError::NotSupported)
}
/// Gets an extended attribute.
async fn getxattr(&self, _name: &str) -> Result<Vec<u8>> {
Err(KernelError::NotSupported)
}
/// Sets an extended attribute.
/// Can only create an attribute if `create` is true.
/// Can only replace an existing attribute if `replace` is true.
async fn setxattr(
&self,
_name: &str,
_buf: &[u8],
_create: bool,
_replace: bool,
) -> Result<()> {
Err(KernelError::NotSupported)
}
/// Removes an extended attribute.
async fn removexattr(&self, _name: &str) -> Result<()> {
Err(KernelError::NotSupported)
}
/// Lists all extended attribute names.
async fn listxattr(&self) -> Result<Vec<String>> {
Ok(Vec::new())
}
/// Looks up a name within a directory, returning the corresponding inode.
async fn lookup(&self, _name: &str) -> Result<Arc<dyn Inode>> {
Err(KernelError::NotSupported)

View File

@@ -24,10 +24,14 @@ use crate::{
chmod::sys_fchmod,
chown::sys_fchown,
close::sys_close,
getxattr::{sys_fgetxattr, sys_getxattr, sys_lgetxattr},
ioctl::sys_ioctl,
iov::{sys_preadv, sys_preadv2, sys_pwritev, sys_pwritev2, sys_readv, sys_writev},
listxattr::{sys_flistxattr, sys_listxattr, sys_llistxattr},
removexattr::{sys_fremovexattr, sys_lremovexattr, sys_removexattr},
rw::{sys_pread64, sys_pwrite64, sys_read, sys_write},
seek::sys_lseek,
setxattr::{sys_fsetxattr, sys_lsetxattr, sys_setxattr},
splice::sys_sendfile,
stat::sys_fstat,
sync::{sys_fdatasync, sys_fsync, sys_sync, sys_syncfs},
@@ -102,6 +106,83 @@ pub async fn handle_syscall() {
};
let res = match nr {
0x5 => {
sys_setxattr(
TUA::from_value(arg1 as _),
TUA::from_value(arg2 as _),
TUA::from_value(arg3 as _),
arg4 as _,
arg5 as _,
)
.await
}
0x6 => {
sys_lsetxattr(
TUA::from_value(arg1 as _),
TUA::from_value(arg2 as _),
TUA::from_value(arg3 as _),
arg4 as _,
arg5 as _,
)
.await
}
0x7 => {
sys_fsetxattr(
arg1.into(),
TUA::from_value(arg2 as _),
TUA::from_value(arg3 as _),
arg4 as _,
arg5 as _,
)
.await
}
0x8 => {
sys_getxattr(
TUA::from_value(arg1 as _),
TUA::from_value(arg2 as _),
TUA::from_value(arg3 as _),
arg4 as _,
)
.await
}
0x9 => {
sys_lgetxattr(
TUA::from_value(arg1 as _),
TUA::from_value(arg2 as _),
TUA::from_value(arg3 as _),
arg4 as _,
)
.await
}
0xa => {
sys_fgetxattr(
arg1.into(),
TUA::from_value(arg2 as _),
TUA::from_value(arg3 as _),
arg4 as _,
)
.await
}
0xb => {
sys_listxattr(
TUA::from_value(arg1 as _),
TUA::from_value(arg2 as _),
arg3 as _,
)
.await
}
0xc => {
sys_llistxattr(
TUA::from_value(arg1 as _),
TUA::from_value(arg2 as _),
arg3 as _,
)
.await
}
0xd => sys_flistxattr(arg1.into(), TUA::from_value(arg2 as _), arg3 as _).await,
0xe => sys_removexattr(TUA::from_value(arg1 as _), TUA::from_value(arg2 as _)).await,
0xf => sys_lremovexattr(TUA::from_value(arg1 as _), TUA::from_value(arg2 as _)).await,
0x10 => sys_fremovexattr(arg1.into(), TUA::from_value(arg2 as _)).await,
0x11 => sys_getcwd(TUA::from_value(arg1 as _), arg2 as _).await,
0x17 => sys_dup(arg1.into()),
0x18 => sys_dup3(arg1.into(), arg2.into(), arg3 as _),

View File

@@ -0,0 +1,88 @@
use crate::fs::VFS;
use crate::memory::uaccess::copy_to_user_slice;
use crate::memory::uaccess::cstr::UserCStr;
use crate::process::fd_table::Fd;
use crate::sched::current::current_task_shared;
use alloc::sync::Arc;
use core::ffi::c_char;
use libkernel::error::{KernelError, Result};
use libkernel::fs::Inode;
use libkernel::fs::path::Path;
use libkernel::memory::address::{TUA, UA};
async fn getxattr(node: Arc<dyn Inode>, name: &str, ua: UA, size: usize) -> Result<usize> {
let value = node.getxattr(name).await?;
if size < value.len() {
Err(KernelError::RangeError)
} else {
copy_to_user_slice(&value, ua).await?;
Ok(value.len())
}
}
pub async fn sys_getxattr(
path: TUA<c_char>,
name: TUA<c_char>,
value: UA,
size: usize,
) -> Result<usize> {
let mut buf = [0; 1024];
let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?);
let task = current_task_shared();
let node = VFS.resolve_path(path, VFS.root_inode(), &task).await?;
let mut buf = [0; 1024];
getxattr(
node,
UserCStr::from_ptr(name).copy_from_user(&mut buf).await?,
value,
size,
)
.await
}
pub async fn sys_lgetxattr(
path: TUA<c_char>,
name: TUA<c_char>,
value: UA,
size: usize,
) -> Result<usize> {
let mut buf = [0; 1024];
let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?);
let task = current_task_shared();
let node = VFS
.resolve_path_nofollow(path, VFS.root_inode(), &task)
.await?;
let mut buf = [0; 1024];
getxattr(
node,
UserCStr::from_ptr(name).copy_from_user(&mut buf).await?,
value,
size,
)
.await
}
pub async fn sys_fgetxattr(fd: Fd, name: TUA<c_char>, value: UA, size: usize) -> Result<usize> {
let node = {
let task = current_task_shared();
let file = task
.fd_table
.lock_save_irq()
.get(fd)
.ok_or(KernelError::BadFd)?;
file.inode().ok_or(KernelError::BadFd)?
};
let mut buf = [0; 1024];
getxattr(
node,
UserCStr::from_ptr(name).copy_from_user(&mut buf).await?,
value,
size,
)
.await
}

View File

@@ -0,0 +1,59 @@
use crate::fs::VFS;
use crate::memory::uaccess::copy_to_user_slice;
use crate::memory::uaccess::cstr::UserCStr;
use crate::process::fd_table::Fd;
use crate::sched::current::current_task_shared;
use alloc::sync::Arc;
use libkernel::error::{KernelError, Result};
use libkernel::fs::Inode;
use libkernel::fs::path::Path;
use libkernel::memory::address::{TUA, UA};
async fn listxattr(node: Arc<dyn Inode>, ua: UA, size: usize) -> Result<usize> {
let list = node.listxattr().await?;
// Join with \0
let list = list.join("\0");
let list_bytes = list.as_bytes();
if size < list_bytes.len() {
Err(KernelError::RangeError)
} else {
copy_to_user_slice(list_bytes, ua).await?;
Ok(list_bytes.len())
}
}
pub async fn sys_listxattr(path: TUA<core::ffi::c_char>, list: UA, size: usize) -> Result<usize> {
let mut buf = [0; 1024];
let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?);
let task = current_task_shared();
let node = VFS.resolve_path(path, VFS.root_inode(), &task).await?;
listxattr(node, list, size).await
}
pub async fn sys_llistxattr(path: TUA<core::ffi::c_char>, list: UA, size: usize) -> Result<usize> {
let mut buf = [0; 1024];
let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?);
let task = current_task_shared();
let node = VFS
.resolve_path_nofollow(path, VFS.root_inode(), &task)
.await?;
listxattr(node, list, size).await
}
pub async fn sys_flistxattr(fd: Fd, list: UA, size: usize) -> Result<usize> {
let node = {
let task = current_task_shared();
let file = task
.fd_table
.lock_save_irq()
.get(fd)
.ok_or(KernelError::BadFd)?;
file.inode().ok_or(KernelError::BadFd)?
};
listxattr(node, list, size).await
}

View File

@@ -3,11 +3,15 @@ pub mod chdir;
pub mod chmod;
pub mod chown;
pub mod close;
pub mod getxattr;
pub mod ioctl;
pub mod iov;
pub mod listxattr;
pub mod open;
pub mod removexattr;
pub mod rw;
pub mod seek;
pub mod setxattr;
pub mod splice;
pub mod stat;
pub mod sync;

View File

@@ -0,0 +1,69 @@
use crate::fs::VFS;
use crate::memory::uaccess::cstr::UserCStr;
use crate::process::fd_table::Fd;
use crate::sched::current::current_task_shared;
use alloc::sync::Arc;
use core::ffi::c_char;
use libkernel::error::{KernelError, Result};
use libkernel::fs::Inode;
use libkernel::fs::path::Path;
use libkernel::memory::address::TUA;
async fn removexattr(node: Arc<dyn Inode>, name: &str) -> Result<()> {
node.removexattr(name).await?;
Ok(())
}
pub async fn sys_removexattr(path: TUA<c_char>, name: TUA<c_char>) -> Result<usize> {
let mut buf = [0; 1024];
let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?);
let task = current_task_shared();
let node = VFS.resolve_path(path, VFS.root_inode(), &task).await?;
let mut buf = [0; 1024];
removexattr(
node,
UserCStr::from_ptr(name).copy_from_user(&mut buf).await?,
)
.await?;
Ok(0)
}
pub async fn sys_lremovexattr(path: TUA<c_char>, name: TUA<c_char>) -> Result<usize> {
let mut buf = [0; 1024];
let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?);
let task = current_task_shared();
let node = VFS
.resolve_path_nofollow(path, VFS.root_inode(), &task)
.await?;
let mut buf = [0; 1024];
removexattr(
node,
UserCStr::from_ptr(name).copy_from_user(&mut buf).await?,
)
.await?;
Ok(0)
}
pub async fn sys_fremovexattr(fd: Fd, name: TUA<c_char>) -> Result<usize> {
let node = {
let task = current_task_shared();
let file = task
.fd_table
.lock_save_irq()
.get(fd)
.ok_or(KernelError::BadFd)?;
file.inode().ok_or(KernelError::BadFd)?
};
let mut buf = [0; 1024];
removexattr(
node,
UserCStr::from_ptr(name).copy_from_user(&mut buf).await?,
)
.await?;
Ok(0)
}

129
src/fs/syscalls/setxattr.rs Normal file
View File

@@ -0,0 +1,129 @@
use crate::fs::VFS;
use crate::memory::uaccess::copy_from_user_slice;
use crate::memory::uaccess::cstr::UserCStr;
use crate::process::fd_table::Fd;
use crate::sched::current::current_task_shared;
use alloc::sync::Arc;
use alloc::vec;
use bitflags::bitflags;
use core::ffi::c_char;
use libkernel::error::{KernelError, Result};
use libkernel::fs::Inode;
use libkernel::fs::path::Path;
use libkernel::memory::address::{TUA, UA};
bitflags! {
pub struct SetXattrFlags: i32 {
const CREATE = 0x1;
const REPLACE = 0x2;
}
}
async fn setxattr(
node: Arc<dyn Inode>,
name: &str,
value: UA,
size: usize,
flags: i32,
) -> Result<usize> {
let flags = match flags {
0 => SetXattrFlags::all(),
1 => SetXattrFlags::CREATE,
2 => SetXattrFlags::REPLACE,
_ => return Err(KernelError::InvalidValue),
};
if name.is_empty() || name.len() > 255 {
return Err(KernelError::RangeError);
}
if size > 2 * 1024 * 1024 {
return Err(KernelError::RangeError);
}
let mut value_vec = vec![0u8; size];
copy_from_user_slice(value, &mut value_vec[..]).await?;
node.setxattr(
name,
&value_vec,
flags.contains(SetXattrFlags::CREATE),
flags.contains(SetXattrFlags::REPLACE),
)
.await?;
Ok(size)
}
pub async fn sys_setxattr(
path: TUA<c_char>,
name: TUA<c_char>,
value: UA,
size: usize,
flags: i32,
) -> Result<usize> {
let mut buf = [0; 1024];
let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?);
let task = current_task_shared();
let node = VFS.resolve_path(path, VFS.root_inode(), &task).await?;
let mut buf = [0; 1024];
setxattr(
node,
UserCStr::from_ptr(name).copy_from_user(&mut buf).await?,
value,
size,
flags,
)
.await
}
pub async fn sys_lsetxattr(
path: TUA<c_char>,
name: TUA<c_char>,
value: UA,
size: usize,
flags: i32,
) -> Result<usize> {
let mut buf = [0; 1024];
let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?);
let task = current_task_shared();
let node = VFS
.resolve_path_nofollow(path, VFS.root_inode(), &task)
.await?;
let mut buf = [0; 1024];
setxattr(
node,
UserCStr::from_ptr(name).copy_from_user(&mut buf).await?,
value,
size,
flags,
)
.await
}
pub async fn sys_fsetxattr(
fd: Fd,
name: TUA<c_char>,
value: UA,
size: usize,
flags: i32,
) -> Result<usize> {
let node = {
let task = current_task_shared();
let file = task
.fd_table
.lock_save_irq()
.get(fd)
.ok_or(KernelError::BadFd)?;
file.inode().ok_or(KernelError::BadFd)?
};
let mut buf = [0; 1024];
setxattr(
node,
UserCStr::from_ptr(name).copy_from_user(&mut buf).await?,
value,
size,
flags,
)
.await
}