mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-09 05:16:47 +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
|
## System Calls
|
||||||
|
|
||||||
At the time of writing,
|
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.
|
provided by Linux on x86-64 architecture.
|
||||||
|
|
||||||
| Numbers | Names | Is Implemented |
|
| Numbers | Names | Is Implemented |
|
||||||
@ -208,18 +208,18 @@ provided by Linux on x86-64 architecture.
|
|||||||
| 185 | security | ❌ |
|
| 185 | security | ❌ |
|
||||||
| 186 | gettid | ✅ |
|
| 186 | gettid | ✅ |
|
||||||
| 187 | readahead | ❌ |
|
| 187 | readahead | ❌ |
|
||||||
| 188 | setxattr | ❌ |
|
| 188 | setxattr | ✅ |
|
||||||
| 189 | lsetxattr | ❌ |
|
| 189 | lsetxattr | ✅ |
|
||||||
| 190 | fsetxattr | ❌ |
|
| 190 | fsetxattr | ✅ |
|
||||||
| 191 | getxattr | ❌ |
|
| 191 | getxattr | ✅ |
|
||||||
| 192 | lgetxattr | ❌ |
|
| 192 | lgetxattr | ✅ |
|
||||||
| 193 | fgetxattr | ❌ |
|
| 193 | fgetxattr | ✅ |
|
||||||
| 194 | listxattr | ❌ |
|
| 194 | listxattr | ✅ |
|
||||||
| 195 | llistxattr | ❌ |
|
| 195 | llistxattr | ✅ |
|
||||||
| 196 | flistxattr | ❌ |
|
| 196 | flistxattr | ✅ |
|
||||||
| 197 | removexattr | ❌ |
|
| 197 | removexattr | ✅ |
|
||||||
| 198 | lremovexattr | ❌ |
|
| 198 | lremovexattr | ✅ |
|
||||||
| 199 | fremovexattr | ❌ |
|
| 199 | fremovexattr | ✅ |
|
||||||
| 200 | tkill | ❌ |
|
| 200 | tkill | ❌ |
|
||||||
| 201 | time | ✅ |
|
| 201 | time | ✅ |
|
||||||
| 202 | futex | ✅ |
|
| 202 | futex | ✅ |
|
||||||
|
@ -17,7 +17,8 @@ use crate::{
|
|||||||
fs::{
|
fs::{
|
||||||
path::mount::MountNode,
|
path::mount::MountNode,
|
||||||
utils::{
|
utils::{
|
||||||
FileSystem, Inode, InodeMode, InodeType, Metadata, MknodType, Permission, NAME_MAX,
|
FileSystem, Inode, InodeMode, InodeType, Metadata, MknodType, Permission, XattrName,
|
||||||
|
XattrNamespace, XattrSetFlags, NAME_MAX,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
@ -356,6 +357,19 @@ impl Dentry_ {
|
|||||||
pub fn ctime(&self) -> Duration;
|
pub fn ctime(&self) -> Duration;
|
||||||
pub fn set_ctime(&self, time: Duration);
|
pub fn set_ctime(&self, time: Duration);
|
||||||
pub fn is_dentry_cacheable(&self) -> bool;
|
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_ {
|
impl Debug for Dentry_ {
|
||||||
@ -769,4 +783,17 @@ impl Dentry {
|
|||||||
pub fn inode(&self) -> &Arc<dyn Inode>;
|
pub fn inode(&self) -> &Arc<dyn Inode>;
|
||||||
pub fn is_root_of_mount(&self) -> bool;
|
pub fn is_root_of_mount(&self) -> bool;
|
||||||
pub fn is_mountpoint(&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 core2::io::{Error as IoError, ErrorKind as IoErrorKind, Result as IoResult, Write};
|
||||||
use ostd::task::Task;
|
use ostd::task::Task;
|
||||||
|
|
||||||
use super::{AccessMode, DirentVisitor, FallocMode, FileSystem, IoctlCmd};
|
use super::{
|
||||||
|
AccessMode, DirentVisitor, FallocMode, FileSystem, IoctlCmd, XattrName, XattrNamespace,
|
||||||
|
XattrSetFlags,
|
||||||
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
events::IoEvents,
|
events::IoEvents,
|
||||||
fs::device::{Device, DeviceType},
|
fs::device::{Device, DeviceType},
|
||||||
@ -497,6 +500,27 @@ pub trait Inode: Any + Sync + Send {
|
|||||||
None
|
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.
|
/// Used to check for read/write/execute permissions on a file.
|
||||||
///
|
///
|
||||||
/// Similar to Linux, using "fsuid" here allows setting filesystem permissions
|
/// 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,
|
FileRange, RangeLockItem, RangeLockItemBuilder, RangeLockList, RangeLockType, OFFSET_MAX,
|
||||||
};
|
};
|
||||||
pub use status_flags::StatusFlags;
|
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 access_mode;
|
||||||
mod channel;
|
mod channel;
|
||||||
@ -35,6 +39,7 @@ mod page_cache;
|
|||||||
mod random_test;
|
mod random_test;
|
||||||
mod range_lock;
|
mod range_lock;
|
||||||
mod status_flags;
|
mod status_flags;
|
||||||
|
mod xattr;
|
||||||
|
|
||||||
use core::{
|
use core::{
|
||||||
borrow::Borrow,
|
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,
|
gettid::sys_gettid,
|
||||||
gettimeofday::sys_gettimeofday,
|
gettimeofday::sys_gettimeofday,
|
||||||
getuid::sys_getuid,
|
getuid::sys_getuid,
|
||||||
|
getxattr::{sys_fgetxattr, sys_getxattr, sys_lgetxattr},
|
||||||
impl_syscall_nums_and_dispatch_fn,
|
impl_syscall_nums_and_dispatch_fn,
|
||||||
ioctl::sys_ioctl,
|
ioctl::sys_ioctl,
|
||||||
kill::sys_kill,
|
kill::sys_kill,
|
||||||
link::{sys_link, sys_linkat},
|
link::{sys_link, sys_linkat},
|
||||||
listen::sys_listen,
|
listen::sys_listen,
|
||||||
|
listxattr::{sys_flistxattr, sys_listxattr, sys_llistxattr},
|
||||||
lseek::sys_lseek,
|
lseek::sys_lseek,
|
||||||
madvise::sys_madvise,
|
madvise::sys_madvise,
|
||||||
mkdir::{sys_mkdir, sys_mkdirat},
|
mkdir::{sys_mkdir, sys_mkdirat},
|
||||||
@ -82,6 +84,7 @@ use crate::syscall::{
|
|||||||
readlink::{sys_readlink, sys_readlinkat},
|
readlink::{sys_readlink, sys_readlinkat},
|
||||||
recvfrom::sys_recvfrom,
|
recvfrom::sys_recvfrom,
|
||||||
recvmsg::sys_recvmsg,
|
recvmsg::sys_recvmsg,
|
||||||
|
removexattr::{sys_fremovexattr, sys_lremovexattr, sys_removexattr},
|
||||||
rename::{sys_rename, sys_renameat},
|
rename::{sys_rename, sys_renameat},
|
||||||
rmdir::sys_rmdir,
|
rmdir::sys_rmdir,
|
||||||
rt_sigaction::sys_rt_sigaction,
|
rt_sigaction::sys_rt_sigaction,
|
||||||
@ -122,6 +125,7 @@ use crate::syscall::{
|
|||||||
setsid::sys_setsid,
|
setsid::sys_setsid,
|
||||||
setsockopt::sys_setsockopt,
|
setsockopt::sys_setsockopt,
|
||||||
setuid::sys_setuid,
|
setuid::sys_setuid,
|
||||||
|
setxattr::{sys_fsetxattr, sys_lsetxattr, sys_setxattr},
|
||||||
shutdown::sys_shutdown,
|
shutdown::sys_shutdown,
|
||||||
sigaltstack::sys_sigaltstack,
|
sigaltstack::sys_sigaltstack,
|
||||||
signalfd::{sys_signalfd, sys_signalfd4},
|
signalfd::{sys_signalfd, sys_signalfd4},
|
||||||
@ -284,6 +288,18 @@ impl_syscall_nums_and_dispatch_fn! {
|
|||||||
SYS_MOUNT = 165 => sys_mount(args[..5]);
|
SYS_MOUNT = 165 => sys_mount(args[..5]);
|
||||||
SYS_UMOUNT2 = 166 => sys_umount(args[..2]);
|
SYS_UMOUNT2 = 166 => sys_umount(args[..2]);
|
||||||
SYS_GETTID = 186 => sys_gettid(args[..0]);
|
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_TIME = 201 => sys_time(args[..1]);
|
||||||
SYS_FUTEX = 202 => sys_futex(args[..6]);
|
SYS_FUTEX = 202 => sys_futex(args[..6]);
|
||||||
SYS_SCHED_SETAFFINITY = 203 => sys_sched_setaffinity(args[..3]);
|
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 gettid;
|
||||||
mod gettimeofday;
|
mod gettimeofday;
|
||||||
mod getuid;
|
mod getuid;
|
||||||
|
mod getxattr;
|
||||||
mod ioctl;
|
mod ioctl;
|
||||||
mod kill;
|
mod kill;
|
||||||
mod link;
|
mod link;
|
||||||
mod listen;
|
mod listen;
|
||||||
|
mod listxattr;
|
||||||
mod lseek;
|
mod lseek;
|
||||||
mod madvise;
|
mod madvise;
|
||||||
mod mkdir;
|
mod mkdir;
|
||||||
@ -89,6 +91,7 @@ mod read;
|
|||||||
mod readlink;
|
mod readlink;
|
||||||
mod recvfrom;
|
mod recvfrom;
|
||||||
mod recvmsg;
|
mod recvmsg;
|
||||||
|
mod removexattr;
|
||||||
mod rename;
|
mod rename;
|
||||||
mod rmdir;
|
mod rmdir;
|
||||||
mod rt_sigaction;
|
mod rt_sigaction;
|
||||||
@ -129,6 +132,7 @@ mod setreuid;
|
|||||||
mod setsid;
|
mod setsid;
|
||||||
mod setsockopt;
|
mod setsockopt;
|
||||||
mod setuid;
|
mod setuid;
|
||||||
|
mod setxattr;
|
||||||
mod shutdown;
|
mod shutdown;
|
||||||
mod sigaltstack;
|
mod sigaltstack;
|
||||||
mod signalfd;
|
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