Add extended attribute (xattr) syscalls: setxattr, getxattr, listxattr and removexattr

This commit is contained in:
Shaowei Song
2025-03-28 05:58:37 +00:00
committed by Tate, Hongliang Tian
parent 8b5a6f0d58
commit a47eda413c
11 changed files with 649 additions and 15 deletions

View File

@ -52,11 +52,13 @@ use crate::syscall::{
gettid::sys_gettid,
gettimeofday::sys_gettimeofday,
getuid::sys_getuid,
getxattr::{sys_fgetxattr, sys_getxattr, sys_lgetxattr},
impl_syscall_nums_and_dispatch_fn,
ioctl::sys_ioctl,
kill::sys_kill,
link::{sys_link, sys_linkat},
listen::sys_listen,
listxattr::{sys_flistxattr, sys_listxattr, sys_llistxattr},
lseek::sys_lseek,
madvise::sys_madvise,
mkdir::{sys_mkdir, sys_mkdirat},
@ -82,6 +84,7 @@ use crate::syscall::{
readlink::{sys_readlink, sys_readlinkat},
recvfrom::sys_recvfrom,
recvmsg::sys_recvmsg,
removexattr::{sys_fremovexattr, sys_lremovexattr, sys_removexattr},
rename::{sys_rename, sys_renameat},
rmdir::sys_rmdir,
rt_sigaction::sys_rt_sigaction,
@ -122,6 +125,7 @@ use crate::syscall::{
setsid::sys_setsid,
setsockopt::sys_setsockopt,
setuid::sys_setuid,
setxattr::{sys_fsetxattr, sys_lsetxattr, sys_setxattr},
shutdown::sys_shutdown,
sigaltstack::sys_sigaltstack,
signalfd::{sys_signalfd, sys_signalfd4},
@ -284,6 +288,18 @@ impl_syscall_nums_and_dispatch_fn! {
SYS_MOUNT = 165 => sys_mount(args[..5]);
SYS_UMOUNT2 = 166 => sys_umount(args[..2]);
SYS_GETTID = 186 => sys_gettid(args[..0]);
SYS_SETXATTR = 188 => sys_setxattr(args[..5]);
SYS_LSETXATTR = 189 => sys_lsetxattr(args[..5]);
SYS_FSETXATTR = 190 => sys_fsetxattr(args[..5]);
SYS_GETXATTR = 191 => sys_getxattr(args[..4]);
SYS_LGETXATTR = 192 => sys_lgetxattr(args[..4]);
SYS_FGETXATTR = 193 => sys_fgetxattr(args[..4]);
SYS_LISTXATTR = 194 => sys_listxattr(args[..3]);
SYS_LLISTXATTR = 195 => sys_llistxattr(args[..3]);
SYS_FLISTXATTR = 196 => sys_flistxattr(args[..3]);
SYS_REMOVEXATTR = 197 => sys_removexattr(args[..2]);
SYS_LREMOVEXATTR = 198 => sys_lremovexattr(args[..2]);
SYS_FREMOVEXATTR = 199 => sys_fremovexattr(args[..2]);
SYS_TIME = 201 => sys_time(args[..1]);
SYS_FUTEX = 202 => sys_futex(args[..6]);
SYS_SCHED_SETAFFINITY = 203 => sys_sched_setaffinity(args[..3]);

View File

@ -0,0 +1,103 @@
// SPDX-License-Identifier: MPL-2.0
use super::{
setxattr::{
check_xattr_namespace, lookup_dentry_for_xattr, parse_xattr_name,
read_xattr_name_cstr_from_user, XattrFileCtx,
},
SyscallReturn,
};
use crate::{
fs::{
file_table::{get_file_fast, FileDesc},
utils::XATTR_VALUE_MAX_LEN,
},
prelude::*,
syscall::constants::MAX_FILENAME_LEN,
};
pub fn sys_getxattr(
path_ptr: Vaddr,
name_ptr: Vaddr,
value_ptr: Vaddr,
value_len: usize,
ctx: &Context,
) -> Result<SyscallReturn> {
let user_space = ctx.user_space();
let path = user_space.read_cstring(path_ptr, MAX_FILENAME_LEN)?;
let len = getxattr(
XattrFileCtx::Path(path),
name_ptr,
value_ptr,
value_len,
&user_space,
ctx,
)?;
Ok(SyscallReturn::Return(len as _))
}
pub fn sys_lgetxattr(
path_ptr: Vaddr,
name_ptr: Vaddr,
value_ptr: Vaddr,
value_len: usize,
ctx: &Context,
) -> Result<SyscallReturn> {
let user_space = ctx.user_space();
let path = user_space.read_cstring(path_ptr, MAX_FILENAME_LEN)?;
let len = getxattr(
XattrFileCtx::PathNoFollow(path),
name_ptr,
value_ptr,
value_len,
&user_space,
ctx,
)?;
Ok(SyscallReturn::Return(len as _))
}
pub fn sys_fgetxattr(
fd: FileDesc,
name_ptr: Vaddr,
value_ptr: Vaddr,
value_len: usize,
ctx: &Context,
) -> Result<SyscallReturn> {
let mut file_table = ctx.thread_local.file_table().borrow_mut();
let file = get_file_fast!(&mut file_table, fd);
let user_space = ctx.user_space();
let len = getxattr(
XattrFileCtx::FileHandle(file),
name_ptr,
value_ptr,
value_len,
&user_space,
ctx,
)?;
Ok(SyscallReturn::Return(len as _))
}
fn getxattr(
file_ctx: XattrFileCtx,
name_ptr: Vaddr,
value_ptr: Vaddr,
value_len: usize,
user_space: &CurrentUserSpace,
ctx: &Context,
) -> Result<usize> {
let name_cstr = read_xattr_name_cstr_from_user(name_ptr, user_space)?;
let name_str = name_cstr.to_string_lossy();
let xattr_name = parse_xattr_name(name_str.as_ref())?;
check_xattr_namespace(xattr_name.namespace(), ctx).map_err(|_| Error::new(Errno::ENODATA))?;
let mut value_writer = user_space.writer(value_ptr, value_len.min(XATTR_VALUE_MAX_LEN))?;
let dentry = lookup_dentry_for_xattr(&file_ctx, ctx)?;
dentry.get_xattr(xattr_name, &mut value_writer)
}

View File

@ -0,0 +1,107 @@
// SPDX-License-Identifier: MPL-2.0
use super::{
setxattr::{lookup_dentry_for_xattr, XattrFileCtx},
SyscallReturn,
};
use crate::{
fs::{
file_table::{get_file_fast, FileDesc},
utils::{XattrNamespace, XATTR_LIST_MAX_LEN},
},
prelude::*,
process::credentials::capabilities::CapSet,
syscall::constants::MAX_FILENAME_LEN,
};
pub fn sys_listxattr(
path_ptr: Vaddr,
list_ptr: Vaddr, // The given list is used to place xattr (null-terminated) names.
list_len: usize,
ctx: &Context,
) -> Result<SyscallReturn> {
let user_space = ctx.user_space();
let path = user_space.read_cstring(path_ptr, MAX_FILENAME_LEN)?;
let len = listxattr(
XattrFileCtx::Path(path),
list_ptr,
list_len,
&user_space,
ctx,
)?;
Ok(SyscallReturn::Return(len as _))
}
pub fn sys_llistxattr(
path_ptr: Vaddr,
list_ptr: Vaddr, // The given list is used to place xattr (null-terminated) names.
list_len: usize,
ctx: &Context,
) -> Result<SyscallReturn> {
let user_space = ctx.user_space();
let path = user_space.read_cstring(path_ptr, MAX_FILENAME_LEN)?;
let len = listxattr(
XattrFileCtx::PathNoFollow(path),
list_ptr,
list_len,
&user_space,
ctx,
)?;
Ok(SyscallReturn::Return(len as _))
}
pub fn sys_flistxattr(
fd: FileDesc,
list_ptr: Vaddr, // The given list is used to place xattr (null-terminated) names.
list_len: usize,
ctx: &Context,
) -> Result<SyscallReturn> {
let mut file_table = ctx.thread_local.file_table().borrow_mut();
let file = get_file_fast!(&mut file_table, fd);
let user_space = ctx.user_space();
let len = listxattr(
XattrFileCtx::FileHandle(file),
list_ptr,
list_len,
&user_space,
ctx,
)?;
Ok(SyscallReturn::Return(len as _))
}
fn listxattr(
file_ctx: XattrFileCtx,
list_ptr: Vaddr,
list_len: usize,
user_space: &CurrentUserSpace,
ctx: &Context,
) -> Result<usize> {
if list_len > XATTR_LIST_MAX_LEN {
return_errno_with_message!(Errno::E2BIG, "xattr list too long");
}
let namespace = get_current_xattr_namespace(ctx);
let mut list_writer = user_space.writer(list_ptr, list_len)?;
let dentry = lookup_dentry_for_xattr(&file_ctx, ctx)?;
dentry.list_xattr(namespace, &mut list_writer)
}
fn get_current_xattr_namespace(ctx: &Context) -> XattrNamespace {
let credentials = ctx.posix_thread.credentials();
let permitted_capset = credentials.permitted_capset();
let effective_capset = credentials.effective_capset();
if permitted_capset.contains(CapSet::SYS_ADMIN) && effective_capset.contains(CapSet::SYS_ADMIN)
{
XattrNamespace::Trusted
} else {
XattrNamespace::User
}
}

View File

@ -60,10 +60,12 @@ mod getsockopt;
mod gettid;
mod gettimeofday;
mod getuid;
mod getxattr;
mod ioctl;
mod kill;
mod link;
mod listen;
mod listxattr;
mod lseek;
mod madvise;
mod mkdir;
@ -89,6 +91,7 @@ mod read;
mod readlink;
mod recvfrom;
mod recvmsg;
mod removexattr;
mod rename;
mod rmdir;
mod rt_sigaction;
@ -129,6 +132,7 @@ mod setreuid;
mod setsid;
mod setsockopt;
mod setuid;
mod setxattr;
mod shutdown;
mod sigaltstack;
mod signalfd;

View File

@ -0,0 +1,57 @@
// SPDX-License-Identifier: MPL-2.0
use super::{
setxattr::{
check_xattr_namespace, lookup_dentry_for_xattr, parse_xattr_name,
read_xattr_name_cstr_from_user, XattrFileCtx,
},
SyscallReturn,
};
use crate::{
fs::file_table::{get_file_fast, FileDesc},
prelude::*,
syscall::constants::MAX_FILENAME_LEN,
};
pub fn sys_removexattr(path_ptr: Vaddr, name_ptr: Vaddr, ctx: &Context) -> Result<SyscallReturn> {
let user_space = ctx.user_space();
let path = user_space.read_cstring(path_ptr, MAX_FILENAME_LEN)?;
removexattr(XattrFileCtx::Path(path), name_ptr, &user_space, ctx)?;
Ok(SyscallReturn::Return(0))
}
pub fn sys_lremovexattr(path_ptr: Vaddr, name_ptr: Vaddr, ctx: &Context) -> Result<SyscallReturn> {
let user_space = ctx.user_space();
let path = user_space.read_cstring(path_ptr, MAX_FILENAME_LEN)?;
removexattr(XattrFileCtx::PathNoFollow(path), name_ptr, &user_space, ctx)?;
Ok(SyscallReturn::Return(0))
}
pub fn sys_fremovexattr(fd: FileDesc, name_ptr: Vaddr, ctx: &Context) -> Result<SyscallReturn> {
let mut file_table = ctx.thread_local.file_table().borrow_mut();
let file = get_file_fast!(&mut file_table, fd);
let user_space = ctx.user_space();
removexattr(XattrFileCtx::FileHandle(file), name_ptr, &user_space, ctx)?;
Ok(SyscallReturn::Return(0))
}
fn removexattr(
file_ctx: XattrFileCtx,
name_ptr: Vaddr,
user_space: &CurrentUserSpace,
ctx: &Context,
) -> Result<()> {
let name_cstr = read_xattr_name_cstr_from_user(name_ptr, user_space)?;
let name_str = name_cstr.to_string_lossy();
let xattr_name = parse_xattr_name(name_str.as_ref())?;
check_xattr_namespace(xattr_name.namespace(), ctx)?;
let dentry = lookup_dentry_for_xattr(&file_ctx, ctx)?;
dentry.remove_xattr(xattr_name)
}

View File

@ -0,0 +1,198 @@
// SPDX-License-Identifier: MPL-2.0
use alloc::borrow::Cow;
use super::SyscallReturn;
use crate::{
fs::{
file_handle::FileLike,
file_table::{get_file_fast, FileDesc},
fs_resolver::{FsPath, AT_FDCWD},
path::Dentry,
utils::{
XattrName, XattrNamespace, XattrSetFlags, XATTR_NAME_MAX_LEN, XATTR_VALUE_MAX_LEN,
},
},
prelude::*,
process::credentials::capabilities::CapSet,
syscall::constants::MAX_FILENAME_LEN,
};
pub fn sys_setxattr(
path_ptr: Vaddr,
name_ptr: Vaddr,
value_ptr: Vaddr,
value_len: usize,
flags: i32,
ctx: &Context,
) -> Result<SyscallReturn> {
let user_space = ctx.user_space();
let path = user_space.read_cstring(path_ptr, MAX_FILENAME_LEN)?;
setxattr(
XattrFileCtx::Path(path),
name_ptr,
value_ptr,
value_len,
flags,
&user_space,
ctx,
)?;
Ok(SyscallReturn::Return(0))
}
pub fn sys_lsetxattr(
path_ptr: Vaddr,
name_ptr: Vaddr,
value_ptr: Vaddr,
value_len: usize,
flags: i32,
ctx: &Context,
) -> Result<SyscallReturn> {
let user_space = ctx.user_space();
let path = user_space.read_cstring(path_ptr, MAX_FILENAME_LEN)?;
setxattr(
XattrFileCtx::PathNoFollow(path),
name_ptr,
value_ptr,
value_len,
flags,
&user_space,
ctx,
)?;
Ok(SyscallReturn::Return(0))
}
pub fn sys_fsetxattr(
fd: FileDesc,
name_ptr: Vaddr,
value_ptr: Vaddr,
value_len: usize,
flags: i32,
ctx: &Context,
) -> Result<SyscallReturn> {
let mut file_table = ctx.thread_local.file_table().borrow_mut();
let file = get_file_fast!(&mut file_table, fd);
let user_space = ctx.user_space();
setxattr(
XattrFileCtx::FileHandle(file),
name_ptr,
value_ptr,
value_len,
flags,
&user_space,
ctx,
)?;
Ok(SyscallReturn::Return(0))
}
fn setxattr(
file_ctx: XattrFileCtx,
name_ptr: Vaddr,
value_ptr: Vaddr,
value_len: usize,
flags: i32,
user_space: &CurrentUserSpace,
ctx: &Context,
) -> Result<()> {
let flags = XattrSetFlags::from_bits(flags as _)
.ok_or(Error::with_message(Errno::EINVAL, "invalid xattr flags"))?;
let name_cstr = read_xattr_name_cstr_from_user(name_ptr, user_space)?;
let name_str = name_cstr.to_string_lossy();
let xattr_name = parse_xattr_name(name_str.as_ref())?;
check_xattr_namespace(xattr_name.namespace(), ctx)?;
if value_len > XATTR_VALUE_MAX_LEN {
return_errno_with_message!(Errno::E2BIG, "xattr value too long");
}
let mut value_reader = user_space.reader(value_ptr, value_len)?;
let dentry = lookup_dentry_for_xattr(&file_ctx, ctx)?;
dentry.set_xattr(xattr_name, &mut value_reader, flags)
}
/// The context to describe the target file for xattr operations.
pub(super) enum XattrFileCtx<'a> {
Path(CString),
PathNoFollow(CString),
FileHandle(Cow<'a, Arc<dyn FileLike>>),
}
pub(super) fn lookup_dentry_for_xattr<'a>(
file_ctx: &'a XattrFileCtx<'a>,
ctx: &'a Context,
) -> Result<Cow<'a, Dentry>> {
let lookup_dentry_from_fs =
|path: &CString, ctx: &Context, symlink_no_follow: bool| -> Result<Cow<'_, Dentry>> {
let path = path.to_string_lossy();
let fs_path = FsPath::new(AT_FDCWD, path.as_ref())?;
let fs = ctx.posix_thread.fs().resolver().read();
let dentry = if symlink_no_follow {
fs.lookup_no_follow(&fs_path)?
} else {
fs.lookup(&fs_path)?
};
Ok(Cow::Owned(dentry))
};
match file_ctx {
XattrFileCtx::Path(path) => lookup_dentry_from_fs(path, ctx, false),
XattrFileCtx::PathNoFollow(path) => lookup_dentry_from_fs(path, ctx, true),
XattrFileCtx::FileHandle(file) => {
let dentry = file.as_inode_or_err()?.dentry();
Ok(Cow::Borrowed(dentry))
}
}
}
pub(super) fn read_xattr_name_cstr_from_user<'a>(
name_ptr: Vaddr,
user_space: &CurrentUserSpace,
) -> Result<CString> {
let mut reader = user_space.reader(name_ptr, XATTR_NAME_MAX_LEN + 1)?;
reader.read_cstring().map_err(|e| {
if reader.remain() == 0 {
Error::with_message(Errno::ERANGE, "xattr name too long")
} else {
e
}
})
}
pub(super) fn parse_xattr_name(name_str: &str) -> Result<XattrName> {
if name_str.is_empty() || name_str.len() > XATTR_NAME_MAX_LEN {
return_errno_with_message!(Errno::ERANGE, "xattr name empty or too long");
}
let xattr_name = XattrName::try_from_full_name(name_str.as_ref()).ok_or(
Error::with_message(Errno::EOPNOTSUPP, "invalid xattr namespace"),
)?;
Ok(xattr_name)
}
pub(super) fn check_xattr_namespace(namespace: XattrNamespace, ctx: &Context) -> Result<()> {
let credentials = ctx.posix_thread.credentials();
let permitted_capset = credentials.permitted_capset();
let effective_capset = credentials.effective_capset();
match namespace {
XattrNamespace::Trusted => {
if !permitted_capset.contains(CapSet::SYS_ADMIN)
|| !effective_capset.contains(CapSet::SYS_ADMIN)
{
return_errno_with_message!(
Errno::EPERM,
"try to access trusted xattr without CAP_SYS_ADMIN"
);
}
}
_ => {}
}
Ok(())
}