mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-08 12:56:48 +00:00
Add extended attribute (xattr) syscalls: setxattr, getxattr, listxattr and removexattr
This commit is contained in:
parent
8b5a6f0d58
commit
a47eda413c
@ -15,7 +15,7 @@ support the loading of Linux kernel modules.
|
||||
## System Calls
|
||||
|
||||
At the time of writing,
|
||||
Asterinas implements 190 out of the 336 system calls
|
||||
Asterinas implements 204 out of the 336 system calls
|
||||
provided by Linux on x86-64 architecture.
|
||||
|
||||
| Numbers | Names | Is Implemented |
|
||||
@ -208,18 +208,18 @@ provided by Linux on x86-64 architecture.
|
||||
| 185 | security | ❌ |
|
||||
| 186 | gettid | ✅ |
|
||||
| 187 | readahead | ❌ |
|
||||
| 188 | setxattr | ❌ |
|
||||
| 189 | lsetxattr | ❌ |
|
||||
| 190 | fsetxattr | ❌ |
|
||||
| 191 | getxattr | ❌ |
|
||||
| 192 | lgetxattr | ❌ |
|
||||
| 193 | fgetxattr | ❌ |
|
||||
| 194 | listxattr | ❌ |
|
||||
| 195 | llistxattr | ❌ |
|
||||
| 196 | flistxattr | ❌ |
|
||||
| 197 | removexattr | ❌ |
|
||||
| 198 | lremovexattr | ❌ |
|
||||
| 199 | fremovexattr | ❌ |
|
||||
| 188 | setxattr | ✅ |
|
||||
| 189 | lsetxattr | ✅ |
|
||||
| 190 | fsetxattr | ✅ |
|
||||
| 191 | getxattr | ✅ |
|
||||
| 192 | lgetxattr | ✅ |
|
||||
| 193 | fgetxattr | ✅ |
|
||||
| 194 | listxattr | ✅ |
|
||||
| 195 | llistxattr | ✅ |
|
||||
| 196 | flistxattr | ✅ |
|
||||
| 197 | removexattr | ✅ |
|
||||
| 198 | lremovexattr | ✅ |
|
||||
| 199 | fremovexattr | ✅ |
|
||||
| 200 | tkill | ❌ |
|
||||
| 201 | time | ✅ |
|
||||
| 202 | futex | ✅ |
|
||||
|
@ -17,7 +17,8 @@ use crate::{
|
||||
fs::{
|
||||
path::mount::MountNode,
|
||||
utils::{
|
||||
FileSystem, Inode, InodeMode, InodeType, Metadata, MknodType, Permission, NAME_MAX,
|
||||
FileSystem, Inode, InodeMode, InodeType, Metadata, MknodType, Permission, XattrName,
|
||||
XattrNamespace, XattrSetFlags, NAME_MAX,
|
||||
},
|
||||
},
|
||||
prelude::*,
|
||||
@ -356,6 +357,19 @@ impl Dentry_ {
|
||||
pub fn ctime(&self) -> Duration;
|
||||
pub fn set_ctime(&self, time: Duration);
|
||||
pub fn is_dentry_cacheable(&self) -> bool;
|
||||
pub fn set_xattr(
|
||||
&self,
|
||||
name: XattrName,
|
||||
value_reader: &mut VmReader,
|
||||
flags: XattrSetFlags,
|
||||
) -> Result<()>;
|
||||
pub fn get_xattr(&self, name: XattrName, value_writer: &mut VmWriter) -> Result<usize>;
|
||||
pub fn list_xattr(
|
||||
&self,
|
||||
namespace: XattrNamespace,
|
||||
list_writer: &mut VmWriter,
|
||||
) -> Result<usize>;
|
||||
pub fn remove_xattr(&self, name: XattrName) -> Result<()>;
|
||||
}
|
||||
|
||||
impl Debug for Dentry_ {
|
||||
@ -769,4 +783,17 @@ impl Dentry {
|
||||
pub fn inode(&self) -> &Arc<dyn Inode>;
|
||||
pub fn is_root_of_mount(&self) -> bool;
|
||||
pub fn is_mountpoint(&self) -> bool;
|
||||
pub fn set_xattr(
|
||||
&self,
|
||||
name: XattrName,
|
||||
value_reader: &mut VmReader,
|
||||
flags: XattrSetFlags,
|
||||
) -> Result<()>;
|
||||
pub fn get_xattr(&self, name: XattrName, value_writer: &mut VmWriter) -> Result<usize>;
|
||||
pub fn list_xattr(
|
||||
&self,
|
||||
namespace: XattrNamespace,
|
||||
list_writer: &mut VmWriter,
|
||||
) -> Result<usize>;
|
||||
pub fn remove_xattr(&self, name: XattrName) -> Result<()>;
|
||||
}
|
||||
|
@ -8,7 +8,10 @@ use aster_rights::Full;
|
||||
use core2::io::{Error as IoError, ErrorKind as IoErrorKind, Result as IoResult, Write};
|
||||
use ostd::task::Task;
|
||||
|
||||
use super::{AccessMode, DirentVisitor, FallocMode, FileSystem, IoctlCmd};
|
||||
use super::{
|
||||
AccessMode, DirentVisitor, FallocMode, FileSystem, IoctlCmd, XattrName, XattrNamespace,
|
||||
XattrSetFlags,
|
||||
};
|
||||
use crate::{
|
||||
events::IoEvents,
|
||||
fs::device::{Device, DeviceType},
|
||||
@ -497,6 +500,27 @@ pub trait Inode: Any + Sync + Send {
|
||||
None
|
||||
}
|
||||
|
||||
fn set_xattr(
|
||||
&self,
|
||||
name: XattrName,
|
||||
value_reader: &mut VmReader,
|
||||
flags: XattrSetFlags,
|
||||
) -> Result<()> {
|
||||
Err(Error::new(Errno::EOPNOTSUPP))
|
||||
}
|
||||
|
||||
fn get_xattr(&self, name: XattrName, value_writer: &mut VmWriter) -> Result<usize> {
|
||||
Err(Error::new(Errno::EOPNOTSUPP))
|
||||
}
|
||||
|
||||
fn list_xattr(&self, namespace: XattrNamespace, list_writer: &mut VmWriter) -> Result<usize> {
|
||||
Err(Error::new(Errno::EOPNOTSUPP))
|
||||
}
|
||||
|
||||
fn remove_xattr(&self, name: XattrName) -> Result<()> {
|
||||
Err(Error::new(Errno::EOPNOTSUPP))
|
||||
}
|
||||
|
||||
/// Used to check for read/write/execute permissions on a file.
|
||||
///
|
||||
/// Similar to Linux, using "fsuid" here allows setting filesystem permissions
|
||||
|
@ -19,6 +19,10 @@ pub use range_lock::{
|
||||
FileRange, RangeLockItem, RangeLockItemBuilder, RangeLockList, RangeLockType, OFFSET_MAX,
|
||||
};
|
||||
pub use status_flags::StatusFlags;
|
||||
pub use xattr::{
|
||||
XattrName, XattrNamespace, XattrSetFlags, XATTR_LIST_MAX_LEN, XATTR_NAME_MAX_LEN,
|
||||
XATTR_VALUE_MAX_LEN,
|
||||
};
|
||||
|
||||
mod access_mode;
|
||||
mod channel;
|
||||
@ -35,6 +39,7 @@ mod page_cache;
|
||||
mod random_test;
|
||||
mod range_lock;
|
||||
mod status_flags;
|
||||
mod xattr;
|
||||
|
||||
use core::{
|
||||
borrow::Borrow,
|
||||
|
93
kernel/src/fs/utils/xattr.rs
Normal file
93
kernel/src/fs/utils/xattr.rs
Normal file
@ -0,0 +1,93 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
pub const XATTR_NAME_MAX_LEN: usize = 255;
|
||||
pub const XATTR_VALUE_MAX_LEN: usize = 65536;
|
||||
pub const XATTR_LIST_MAX_LEN: usize = 65536;
|
||||
|
||||
/// Represents different namespaces with different capabilities
|
||||
/// for extended attributes (xattrs).
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, TryFromInt, Hash)]
|
||||
#[repr(u8)]
|
||||
pub enum XattrNamespace {
|
||||
User = 1,
|
||||
Trusted = 2,
|
||||
System = 3,
|
||||
Security = 4,
|
||||
// More namespaces can be added here.
|
||||
}
|
||||
|
||||
/// Represents the name of an xattr. It includes both a valid namespace
|
||||
/// and a full name string slice, which contains the namespace prefix.
|
||||
///
|
||||
/// For example, "user.foo" is a valid xattr name, and its namespace
|
||||
/// is `XattrNamespace::User`.
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct XattrName<'a> {
|
||||
namespace: XattrNamespace,
|
||||
full_name: &'a str,
|
||||
}
|
||||
|
||||
impl XattrNamespace {
|
||||
pub fn try_from_full_name(full_name: &str) -> Option<XattrNamespace> {
|
||||
const USER_PREFIX: &str = "user.";
|
||||
const TRUSTED_PREFIX: &str = "trusted.";
|
||||
const SYSTEM_PREFIX: &str = "system.";
|
||||
const SECURITY_PREFIX: &str = "security.";
|
||||
|
||||
if full_name.starts_with(USER_PREFIX) {
|
||||
Some(XattrNamespace::User)
|
||||
} else if full_name.starts_with(TRUSTED_PREFIX) {
|
||||
Some(XattrNamespace::Trusted)
|
||||
} else if full_name.starts_with(SYSTEM_PREFIX) {
|
||||
Some(XattrNamespace::System)
|
||||
} else if full_name.starts_with(SECURITY_PREFIX) {
|
||||
Some(XattrNamespace::Security)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_user(&self) -> bool {
|
||||
matches!(self, XattrNamespace::User)
|
||||
}
|
||||
|
||||
pub fn is_admin(&self) -> bool {
|
||||
matches!(self, XattrNamespace::Trusted)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> XattrName<'a> {
|
||||
pub fn try_from_full_name(full_name: &'a str) -> Option<Self> {
|
||||
let namespace = XattrNamespace::try_from_full_name(full_name)?;
|
||||
Some(Self {
|
||||
namespace,
|
||||
full_name,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn namespace(&self) -> XattrNamespace {
|
||||
self.namespace
|
||||
}
|
||||
|
||||
pub const fn full_name(&self) -> &'a str {
|
||||
self.full_name
|
||||
}
|
||||
|
||||
pub const fn full_name_len(&self) -> usize {
|
||||
self.full_name.len()
|
||||
}
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
/// Flags for setting an xattr value.
|
||||
pub struct XattrSetFlags: u8 {
|
||||
/// Creates a new xattr if it doesn't exist, or replaces the value if it does.
|
||||
const CREATE_OR_REPLACE = 0;
|
||||
/// Creates a new xattr, fails if it already exists.
|
||||
const CREATE_ONLY = 1;
|
||||
/// Replaces the value of an existing xattr, fails if it doesn't exist.
|
||||
const REPLACE_ONLY = 2;
|
||||
}
|
||||
}
|
@ -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]);
|
||||
|
103
kernel/src/syscall/getxattr.rs
Normal file
103
kernel/src/syscall/getxattr.rs
Normal 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)
|
||||
}
|
107
kernel/src/syscall/listxattr.rs
Normal file
107
kernel/src/syscall/listxattr.rs
Normal 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
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
57
kernel/src/syscall/removexattr.rs
Normal file
57
kernel/src/syscall/removexattr.rs
Normal 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)
|
||||
}
|
198
kernel/src/syscall/setxattr.rs
Normal file
198
kernel/src/syscall/setxattr.rs
Normal 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(())
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user