use alloc::{sync::Arc, vec, vec::Vec};
use core::{ffi::c_char, mem::MaybeUninit};
use ax_config::ARCH;
use ax_errno::{AxError, AxResult};
use ax_fs::FS_CONTEXT;
use ax_task::current;
use linux_raw_sys::{
general::{GRND_INSECURE, GRND_NONBLOCK, GRND_RANDOM},
system::{new_utsname, sysinfo},
};
use starry_vm::{VmMutPtr, vm_read_slice, vm_write_slice};
use crate::task::{AsThread, processes};
const NOCHG: u32 = u32::MAX;
pub fn sys_getuid() -> AxResult<isize> {
let cred = current().as_thread().cred();
Ok(cred.uid as isize)
}
pub fn sys_geteuid() -> AxResult<isize> {
let cred = current().as_thread().cred();
Ok(cred.euid as isize)
}
pub fn sys_getgid() -> AxResult<isize> {
let cred = current().as_thread().cred();
Ok(cred.gid as isize)
}
pub fn sys_getegid() -> AxResult<isize> {
let cred = current().as_thread().cred();
Ok(cred.egid as isize)
}
pub fn sys_getresuid(ruid: *mut u32, euid: *mut u32, suid: *mut u32) -> AxResult<isize> {
let cred = current().as_thread().cred();
ruid.vm_write(cred.uid)?;
euid.vm_write(cred.euid)?;
suid.vm_write(cred.suid)?;
Ok(0)
}
pub fn sys_getresgid(rgid: *mut u32, egid: *mut u32, sgid: *mut u32) -> AxResult<isize> {
let cred = current().as_thread().cred();
rgid.vm_write(cred.gid)?;
egid.vm_write(cred.egid)?;
sgid.vm_write(cred.sgid)?;
Ok(0)
}
pub fn sys_setresuid(ruid: u32, euid: u32, suid: u32) -> AxResult<isize> {
debug!("sys_setresuid <= ruid: {ruid}, euid: {euid}, suid: {suid}");
let thread = current();
let thread = thread.as_thread();
let old = thread.cred();
let mut new = (*old).clone();
if old.has_cap_setuid() {
if ruid != NOCHG {
new.uid = ruid;
}
if euid != NOCHG {
new.euid = euid;
}
if suid != NOCHG {
new.suid = suid;
}
} else {
let allowed = [old.uid, old.euid, old.suid];
if ruid != NOCHG {
if !allowed.contains(&ruid) {
return Err(AxError::OperationNotPermitted);
}
new.uid = ruid;
}
if euid != NOCHG {
if !allowed.contains(&euid) {
return Err(AxError::OperationNotPermitted);
}
new.euid = euid;
}
if suid != NOCHG {
if !allowed.contains(&suid) {
return Err(AxError::OperationNotPermitted);
}
new.suid = suid;
}
}
new.fsuid = new.euid;
thread.set_cred(new);
Ok(0)
}
pub fn sys_setresgid(rgid: u32, egid: u32, sgid: u32) -> AxResult<isize> {
debug!("sys_setresgid <= rgid: {rgid}, egid: {egid}, sgid: {sgid}");
let thread = current();
let thread = thread.as_thread();
let old = thread.cred();
let mut new = (*old).clone();
if old.has_cap_setgid() {
if rgid != NOCHG {
new.gid = rgid;
}
if egid != NOCHG {
new.egid = egid;
}
if sgid != NOCHG {
new.sgid = sgid;
}
} else {
let allowed = [old.gid, old.egid, old.sgid];
if rgid != NOCHG {
if !allowed.contains(&rgid) {
return Err(AxError::OperationNotPermitted);
}
new.gid = rgid;
}
if egid != NOCHG {
if !allowed.contains(&egid) {
return Err(AxError::OperationNotPermitted);
}
new.egid = egid;
}
if sgid != NOCHG {
if !allowed.contains(&sgid) {
return Err(AxError::OperationNotPermitted);
}
new.sgid = sgid;
}
}
new.fsgid = new.egid;
thread.set_cred(new);
Ok(0)
}
pub fn sys_setuid(uid: u32) -> AxResult<isize> {
debug!("sys_setuid <= uid: {uid}");
let thread = current();
let thread = thread.as_thread();
let old = thread.cred();
let mut new = (*old).clone();
if old.has_cap_setuid() {
new.uid = uid;
new.euid = uid;
new.suid = uid;
} else {
if uid != old.uid && uid != old.suid {
return Err(AxError::OperationNotPermitted);
}
new.euid = uid;
}
new.fsuid = new.euid;
thread.set_cred(new);
Ok(0)
}
pub fn sys_setgid(gid: u32) -> AxResult<isize> {
debug!("sys_setgid <= gid: {gid}");
let thread = current();
let thread = thread.as_thread();
let old = thread.cred();
let mut new = (*old).clone();
if old.has_cap_setgid() {
new.gid = gid;
new.egid = gid;
new.sgid = gid;
} else {
if gid != old.gid && gid != old.sgid {
return Err(AxError::OperationNotPermitted);
}
new.egid = gid;
}
new.fsgid = new.egid;
thread.set_cred(new);
Ok(0)
}
pub fn sys_setreuid(ruid: u32, euid: u32) -> AxResult<isize> {
debug!("sys_setreuid <= ruid: {ruid}, euid: {euid}");
let thread = current();
let thread = thread.as_thread();
let old = thread.cred();
let mut new = (*old).clone();
if old.has_cap_setuid() {
if ruid != NOCHG {
new.uid = ruid;
}
if euid != NOCHG {
new.euid = euid;
}
} else {
if ruid != NOCHG {
if ruid != old.uid && ruid != old.euid {
return Err(AxError::OperationNotPermitted);
}
new.uid = ruid;
}
if euid != NOCHG {
if euid != old.uid && euid != old.euid && euid != old.suid {
return Err(AxError::OperationNotPermitted);
}
new.euid = euid;
}
}
if ruid != NOCHG || (euid != NOCHG && new.euid != old.uid) {
new.suid = new.euid;
}
new.fsuid = new.euid;
thread.set_cred(new);
Ok(0)
}
pub fn sys_setregid(rgid: u32, egid: u32) -> AxResult<isize> {
debug!("sys_setregid <= rgid: {rgid}, egid: {egid}");
let thread = current();
let thread = thread.as_thread();
let old = thread.cred();
let mut new = (*old).clone();
if old.has_cap_setgid() {
if rgid != NOCHG {
new.gid = rgid;
}
if egid != NOCHG {
new.egid = egid;
}
} else {
if rgid != NOCHG {
if rgid != old.gid && rgid != old.egid {
return Err(AxError::OperationNotPermitted);
}
new.gid = rgid;
}
if egid != NOCHG {
if egid != old.gid && egid != old.egid && egid != old.sgid {
return Err(AxError::OperationNotPermitted);
}
new.egid = egid;
}
}
if rgid != NOCHG || (egid != NOCHG && new.egid != old.gid) {
new.sgid = new.egid;
}
new.fsgid = new.egid;
thread.set_cred(new);
Ok(0)
}
pub fn sys_getgroups(size: usize, list: *mut u32) -> AxResult<isize> {
debug!("sys_getgroups <= size: {size}");
let cred = current().as_thread().cred();
let ngroups = cred.groups.len();
if size == 0 {
return Ok(ngroups as isize);
}
if size < ngroups {
return Err(AxError::InvalidInput);
}
if ngroups > 0 {
vm_write_slice(list, &cred.groups)?;
}
Ok(ngroups as isize)
}
const NGROUPS_MAX: usize = 65536;
pub fn sys_setgroups(size: usize, list: *const u32) -> AxResult<isize> {
debug!("sys_setgroups <= size: {size}");
let thread = current();
let thread = thread.as_thread();
let old = thread.cred();
if !old.has_cap_setgid() {
return Err(AxError::OperationNotPermitted);
}
if size > NGROUPS_MAX {
return Err(AxError::InvalidInput);
}
let groups = if size > 0 {
let mut buf: Vec<MaybeUninit<u32>> = vec![MaybeUninit::uninit(); size];
vm_read_slice(list, &mut buf)?;
buf.into_iter()
.map(|v| unsafe { v.assume_init() })
.collect()
} else {
Vec::new()
};
let mut new = (*old).clone();
new.groups = Arc::from(groups.into_boxed_slice());
thread.set_cred(new);
Ok(0)
}
const fn pad_str(info: &str) -> [c_char; 65] {
let mut data: [c_char; 65] = [0; 65];
unsafe {
core::ptr::copy_nonoverlapping(info.as_ptr().cast(), data.as_mut_ptr(), info.len());
}
data
}
const UTSNAME: new_utsname = new_utsname {
sysname: pad_str("Linux"),
nodename: pad_str("starry"),
release: pad_str("10.0.0"),
version: pad_str("10.0.0"),
machine: pad_str(ARCH),
domainname: pad_str("https://github.com/Starry-OS/StarryOS"),
};
pub fn sys_uname(name: *mut new_utsname) -> AxResult<isize> {
name.vm_write(UTSNAME)?;
Ok(0)
}
pub fn sys_sysinfo(info: *mut sysinfo) -> AxResult<isize> {
let mut kinfo: sysinfo = unsafe { core::mem::zeroed() };
let total = ax_hal::mem::total_ram_size();
let usages = ax_alloc::global_allocator().usages();
let used = usages.get(ax_alloc::UsageKind::RustHeap)
+ usages.get(ax_alloc::UsageKind::VirtMem)
+ usages.get(ax_alloc::UsageKind::PageCache)
+ usages.get(ax_alloc::UsageKind::PageTable)
+ usages.get(ax_alloc::UsageKind::Dma)
+ usages.get(ax_alloc::UsageKind::Global);
let free = total.saturating_sub(used);
let uptime = ax_hal::time::monotonic_time();
kinfo.uptime = uptime.as_secs() as _;
kinfo.totalram = total as _;
kinfo.freeram = free as _;
kinfo.procs = processes().len() as _;
kinfo.mem_unit = 1;
info.vm_write(kinfo)?;
Ok(0)
}
pub fn sys_syslog(_type: i32, _buf: *mut c_char, _len: usize) -> AxResult<isize> {
Ok(0)
}
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct GetRandomFlags: u32 {
const NONBLOCK = GRND_NONBLOCK;
const RANDOM = GRND_RANDOM;
const INSECURE = GRND_INSECURE;
}
}
pub fn sys_getrandom(buf: *mut u8, len: usize, flags: u32) -> AxResult<isize> {
if len == 0 {
return Ok(0);
}
let flags = GetRandomFlags::from_bits(flags).ok_or(AxError::InvalidInput)?;
if flags.contains(GetRandomFlags::INSECURE) && flags.contains(GetRandomFlags::RANDOM) {
return Err(AxError::InvalidInput);
}
debug!("sys_getrandom <= buf: {buf:p}, len: {len}, flags: {flags:?}");
let path = if flags.contains(GetRandomFlags::RANDOM) {
"/dev/random"
} else {
"/dev/urandom"
};
let f = FS_CONTEXT.lock().resolve(path)?;
let mut kbuf = vec![0; len];
let len = f.entry().as_file()?.read_at(&mut kbuf, 0)?;
vm_write_slice(buf, &kbuf)?;
Ok(len as _)
}
pub fn sys_seccomp(_op: u32, _flags: u32, _args: *const ()) -> AxResult<isize> {
warn!("dummy sys_seccomp");
Ok(0)
}
#[cfg(target_arch = "riscv64")]
pub fn sys_riscv_flush_icache() -> AxResult<isize> {
riscv::asm::fence_i();
Ok(0)
}