diff --git a/Cargo.lock b/Cargo.lock index d9cab6843..9f6a17929 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,15 +11,6 @@ dependencies = [ "tock-registers 0.9.0", ] -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - [[package]] name = "allocator" version = "0.1.1" @@ -80,31 +71,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "arceos_posix_api" -version = "0.1.0" -dependencies = [ - "axalloc", - "axconfig", - "axerrno", - "axfeat", - "axfs", - "axhal", - "axio", - "axlog", - "axnet", - "axns", - "axruntime", - "axsync", - "axtask", - "bindgen", - "ctor_bare", - "flatten_objects", - "lazy_static", - "spin", - "static_assertions", -] - [[package]] name = "arm_gicv2" version = "0.1.0" @@ -458,7 +424,7 @@ dependencies = [ "bitflags 2.9.0", "cfg-if", "derive_more", - "linux-raw-sys 0.9.4", + "linux-raw-sys", "lock_api", "log", "strum_macros", @@ -498,29 +464,6 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" -[[package]] -name = "bindgen" -version = "0.69.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" -dependencies = [ - "bitflags 2.9.0", - "cexpr", - "clang-sys", - "itertools", - "lazy_static", - "lazycell", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn 2.0.100", - "which", -] - [[package]] name = "bit" version = "0.1.1" @@ -575,15 +518,6 @@ dependencies = [ "bitflags 2.9.0", ] -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - [[package]] name = "cfg-if" version = "1.0.0" @@ -599,22 +533,11 @@ dependencies = [ "num-traits", ] -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", -] - [[package]] name = "clap" -version = "4.5.32" +version = "4.5.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" +checksum = "e958897981290da2a852763fe9cdb89cd36977a5d729023127095fa94d95e2ff" dependencies = [ "clap_builder", "clap_derive", @@ -622,9 +545,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.32" +version = "4.5.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" +checksum = "83b0f35019843db2160b5bb19ae09b4e6411ac33fc6a712003c33e03090e2489" dependencies = [ "anstream", "anstyle", @@ -780,12 +703,6 @@ dependencies = [ "tock-registers 0.8.1", ] -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - [[package]] name = "embedded-hal" version = "1.0.0" @@ -798,16 +715,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" -[[package]] -name = "errno" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" -dependencies = [ - "libc", - "windows-sys", -] - [[package]] name = "fatfs" version = "0.4.0" @@ -826,12 +733,6 @@ dependencies = [ "bitmaps", ] -[[package]] -name = "glob" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" - [[package]] name = "handler_table" version = "0.1.2" @@ -872,15 +773,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "home" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" -dependencies = [ - "windows-sys", -] - [[package]] name = "indexmap" version = "2.8.0" @@ -903,15 +795,6 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "kernel-elf-parser" version = "0.3.2" @@ -946,21 +829,6 @@ dependencies = [ "kernel_guard", ] -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin", -] - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "lazyinit" version = "0.2.1" @@ -973,16 +841,6 @@ version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" -[[package]] -name = "libloading" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" -dependencies = [ - "cfg-if", - "windows-targets", -] - [[package]] name = "linked_list" version = "0.1.0" @@ -1008,12 +866,6 @@ dependencies = [ "syn 2.0.100", ] -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - [[package]] name = "linux-raw-sys" version = "0.9.4" @@ -1054,22 +906,6 @@ dependencies = [ "log", ] -[[package]] -name = "macro_rules_attribute" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a82271f7bc033d84bbca59a3ce3e4159938cb08a9c3aebbe54d215131518a13" -dependencies = [ - "macro_rules_attribute-proc_macro", - "paste", -] - -[[package]] -name = "macro_rules_attribute-proc_macro" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8dd856d451cc0da70e2ef2ce95a18e39a93b7558bedf10201ad28503f918568" - [[package]] name = "managed" version = "0.8.0" @@ -1097,22 +933,6 @@ dependencies = [ "memory_addr", ] -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - [[package]] name = "ns16550a" version = "0.4.0" @@ -1156,9 +976,9 @@ checksum = "300e4bdb6b46b592948e700ea1ef24a4296491f6a0ee722b258040abd15a3714" [[package]] name = "once_cell" -version = "1.21.1" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "page_table_entry" @@ -1212,16 +1032,6 @@ dependencies = [ "syn 2.0.100", ] -[[package]] -name = "prettyplease" -version = "0.2.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5316f57387668042f561aae71480de936257848f9c43ce528e311d89a07cadeb" -dependencies = [ - "proc-macro2", - "syn 2.0.100", -] - [[package]] name = "proc-macro-error-attr2" version = "2.0.0" @@ -1280,35 +1090,6 @@ dependencies = [ "bitflags 2.9.0", ] -[[package]] -name = "regex" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" - [[package]] name = "riscv" version = "0.12.1" @@ -1351,12 +1132,6 @@ dependencies = [ "svgbobdoc", ] -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - [[package]] name = "rustc_version" version = "0.4.1" @@ -1366,19 +1141,6 @@ dependencies = [ "semver", ] -[[package]] -name = "rustix" -version = "0.38.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" -dependencies = [ - "bitflags 2.9.0", - "errno", - "libc", - "linux-raw-sys 0.4.15", - "windows-sys", -] - [[package]] name = "rustversion" version = "1.0.20" @@ -1459,18 +1221,18 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" name = "starry" version = "0.1.0" dependencies = [ - "arceos_posix_api", "axerrno", "axfeat", "axfs", "axhal", "axlog", "axprocess", + "axruntime", "axsignal", "axsync", "axtask", "linkme", - "linux-raw-sys 0.9.4", + "linux-raw-sys", "shlex", "starry-api", "starry-core", @@ -1481,24 +1243,30 @@ dependencies = [ name = "starry-api" version = "0.1.0" dependencies = [ - "arceos_posix_api", "axconfig", "axerrno", + "axfeat", "axfs", "axhal", + "axio", "axlog", + "axmm", + "axnet", + "axns", "axprocess", "axsignal", "axsync", "axtask", "bitflags 2.9.0", + "cfg-if", + "ctor_bare", + "flatten_objects", "linkme", - "linux-raw-sys 0.9.4", - "macro_rules_attribute", + "linux-raw-sys", "memory_addr", "num_enum", + "spin", "starry-core", - "static_assertions", "x86", ] @@ -1700,18 +1468,6 @@ name = "weak-map" version = "0.1.0" source = "git+https://github.com/Starry-OS/weak-map.git#6a528cb160b8e2bcdb6bbc1b4086aa1de08e73ec" -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - [[package]] name = "windows-sys" version = "0.59.0" diff --git a/Cargo.toml b/Cargo.toml index 5a32bb249..f5fd7f96f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,17 +14,12 @@ homepage = "https://github.com/arceos-org/arceos" repository = "https://github.com/arceos-org/starry-next" [workspace.dependencies] -axfeat = { git = "https://github.com/oscomp/arceos.git" } -arceos_posix_api = { git = "https://github.com/oscomp/arceos.git", features = [ - "uspace", - "smp", - "irq", +axfeat = { git = "https://github.com/oscomp/arceos.git", features = [ "fs", + "irq", "multitask", "net", - "pipe", - "select", - "epoll", + "smp", ] } axconfig = { git = "https://github.com/oscomp/arceos.git" } @@ -32,9 +27,11 @@ axfs = { git = "https://github.com/oscomp/arceos.git" } axhal = { git = "https://github.com/oscomp/arceos.git", features = ["uspace"] } axlog = { git = "https://github.com/oscomp/arceos.git" } axmm = { git = "https://github.com/oscomp/arceos.git" } +axnet = { git = "https://github.com/oscomp/arceos.git" } axns = { git = "https://github.com/oscomp/arceos.git", features = [ "thread-local", ] } +axruntime = { git = "https://github.com/oscomp/arceos.git" } axsync = { git = "https://github.com/oscomp/arceos.git" } axtask = { git = "https://github.com/oscomp/arceos.git" } @@ -43,6 +40,7 @@ axsignal = { git = "https://github.com/Starry-OS/axsignal.git", rev = "b5b6089" axerrno = "0.1" bitflags = "2.6" +cfg-if = "1.0" linkme = "0.3" linux-raw-sys = { version = "0.9.3", default-features = false, features = [ "no_std", @@ -52,6 +50,7 @@ linux-raw-sys = { version = "0.9.3", default-features = false, features = [ "system", ] } memory_addr = "0.3" +spin = "0.9" starry-core = { path = "./core" } starry-api = { path = "./api" } @@ -73,9 +72,9 @@ axfeat.workspace = true axfs.workspace = true axhal.workspace = true axlog.workspace = true +axruntime.workspace = true axsync.workspace = true axtask.workspace = true -arceos_posix_api.workspace = true axprocess.workspace = true axsignal.workspace = true diff --git a/api/Cargo.toml b/api/Cargo.toml index d94b36453..1065ed0ac 100644 --- a/api/Cargo.toml +++ b/api/Cargo.toml @@ -7,28 +7,35 @@ homepage.workspace = true repository.workspace = true [dependencies] +axfeat.workspace = true + axconfig.workspace = true axfs.workspace = true axhal.workspace = true axlog.workspace = true +axmm.workspace = true +axnet.workspace = true +axns.workspace = true axsync.workspace = true axtask.workspace = true -arceos_posix_api.workspace = true axprocess.workspace = true axsignal.workspace = true axerrno.workspace = true bitflags.workspace = true +cfg-if.workspace = true linkme.workspace = true linux-raw-sys.workspace = true memory_addr.workspace = true +spin.workspace = true starry-core.workspace = true -macro_rules_attribute = "0.2" +axio = "0.1.1" +ctor_bare = "0.2.1" +flatten_objects = "0.2.3" num_enum = { version = "0.7", default-features = false } -static_assertions = "1.1" [target.'cfg(target_arch = "x86_64")'.dependencies] x86 = "0.52" diff --git a/api/src/file/fs.rs b/api/src/file/fs.rs new file mode 100644 index 000000000..d4f4d9dc6 --- /dev/null +++ b/api/src/file/fs.rs @@ -0,0 +1,145 @@ +use core::{any::Any, ffi::c_int}; + +use alloc::{string::String, sync::Arc}; +use axerrno::{LinuxError, LinuxResult}; +use axfs::fops::DirEntry; +use axio::PollState; +use axsync::{Mutex, MutexGuard}; +use linux_raw_sys::general::S_IFDIR; + +use super::{FileLike, Kstat, get_file_like}; + +/// File wrapper for `axfs::fops::File`. +pub struct File { + inner: Mutex, + path: String, +} + +impl File { + pub fn new(inner: axfs::fops::File, path: String) -> Self { + Self { + inner: Mutex::new(inner), + path, + } + } + + /// Get the path of the file. + pub fn path(&self) -> &str { + &self.path + } + + /// Get the inner node of the file. + pub fn inner(&self) -> MutexGuard { + self.inner.lock() + } +} + +impl FileLike for File { + fn read(&self, buf: &mut [u8]) -> LinuxResult { + Ok(self.inner().read(buf)?) + } + + fn write(&self, buf: &[u8]) -> LinuxResult { + Ok(self.inner().write(buf)?) + } + + fn stat(&self) -> LinuxResult { + let metadata = self.inner().get_attr()?; + let ty = metadata.file_type() as u8; + let perm = metadata.perm().bits() as u32; + + Ok(Kstat { + mode: ((ty as u32) << 12) | perm, + size: metadata.size(), + blocks: metadata.blocks(), + blksize: 512, + ..Default::default() + }) + } + + fn into_any(self: Arc) -> Arc { + self + } + + fn poll(&self) -> LinuxResult { + Ok(PollState { + readable: true, + writable: true, + }) + } + + fn set_nonblocking(&self, _nonblocking: bool) -> LinuxResult { + Ok(()) + } +} + +/// Directory wrapper for `axfs::fops::Directory`. +pub struct Directory { + inner: Mutex, + path: String, + last_dirent: Mutex>, +} + +impl Directory { + pub fn new(inner: axfs::fops::Directory, path: String) -> Self { + Self { + inner: Mutex::new(inner), + path, + last_dirent: Mutex::new(None), + } + } + + /// Get the path of the directory. + pub fn path(&self) -> &str { + &self.path + } + + /// Get the inner node of the directory. + pub fn inner(&self) -> MutexGuard { + self.inner.lock() + } + + /// Get the last directory entry. + pub fn last_dirent(&self) -> MutexGuard> { + self.last_dirent.lock() + } +} + +impl FileLike for Directory { + fn read(&self, _buf: &mut [u8]) -> LinuxResult { + Err(LinuxError::EBADF) + } + + fn write(&self, _buf: &[u8]) -> LinuxResult { + Err(LinuxError::EBADF) + } + + fn stat(&self) -> LinuxResult { + Ok(Kstat { + mode: S_IFDIR | 0o755u32, // rwxr-xr-x + ..Default::default() + }) + } + + fn into_any(self: Arc) -> Arc { + self + } + + fn poll(&self) -> LinuxResult { + Ok(PollState { + readable: true, + writable: false, + }) + } + + fn set_nonblocking(&self, _nonblocking: bool) -> LinuxResult { + Ok(()) + } + + fn from_fd(fd: c_int) -> LinuxResult> { + get_file_like(fd)? + .into_any() + .downcast::() + .map_err(|_| LinuxError::ENOTDIR) + } +} diff --git a/api/src/file/mod.rs b/api/src/file/mod.rs new file mode 100644 index 000000000..8a19843dc --- /dev/null +++ b/api/src/file/mod.rs @@ -0,0 +1,174 @@ +mod fs; +mod net; +mod pipe; +mod stdio; + +use core::{any::Any, ffi::c_int}; + +use alloc::{sync::Arc, vec::Vec}; +use axerrno::{LinuxError, LinuxResult}; +use axio::PollState; +use axns::{ResArc, def_resource}; +use flatten_objects::FlattenObjects; +use linux_raw_sys::general::{stat, statx}; +use spin::RwLock; + +pub use self::{ + fs::{Directory, File}, + net::Socket, + pipe::Pipe, +}; + +pub const AX_FILE_LIMIT: usize = 1024; + +#[derive(Debug, Clone, Copy)] +pub struct Kstat { + ino: u64, + nlink: u32, + uid: u32, + gid: u32, + mode: u32, + size: u64, + blocks: u64, + blksize: u32, +} + +impl Default for Kstat { + fn default() -> Self { + Self { + ino: 1, + nlink: 1, + uid: 1, + gid: 1, + mode: 0, + size: 0, + blocks: 0, + blksize: 4096, + } + } +} + +impl From for stat { + fn from(value: Kstat) -> Self { + // SAFETY: valid for stat + let mut stat: stat = unsafe { core::mem::zeroed() }; + stat.st_ino = value.ino as _; + stat.st_nlink = value.nlink as _; + stat.st_mode = value.mode as _; + stat.st_uid = value.uid as _; + stat.st_gid = value.gid as _; + stat.st_size = value.size as _; + stat.st_blksize = value.blksize as _; + stat.st_blocks = value.blocks as _; + + stat + } +} + +impl From for statx { + fn from(value: Kstat) -> Self { + // SAFETY: valid for statx + let mut statx: statx = unsafe { core::mem::zeroed() }; + statx.stx_blksize = value.blksize as _; + statx.stx_attributes = value.mode as _; + statx.stx_nlink = value.nlink as _; + statx.stx_uid = value.uid as _; + statx.stx_gid = value.gid as _; + statx.stx_mode = value.mode as _; + statx.stx_ino = value.ino as _; + statx.stx_size = value.size as _; + statx.stx_blocks = value.blocks as _; + + statx + } +} + +#[allow(dead_code)] +pub trait FileLike: Send + Sync { + fn read(&self, buf: &mut [u8]) -> LinuxResult; + fn write(&self, buf: &[u8]) -> LinuxResult; + fn stat(&self) -> LinuxResult; + fn into_any(self: Arc) -> Arc; + fn poll(&self) -> LinuxResult; + fn set_nonblocking(&self, nonblocking: bool) -> LinuxResult; + + fn from_fd(fd: c_int) -> LinuxResult> + where + Self: Sized + 'static, + { + get_file_like(fd)? + .into_any() + .downcast::() + .map_err(|_| LinuxError::EINVAL) + } + + fn add_to_fd_table(self) -> LinuxResult + where + Self: Sized + 'static, + { + add_file_like(Arc::new(self)) + } +} + +def_resource! { + pub static FD_TABLE: ResArc, AX_FILE_LIMIT>>> = ResArc::new(); +} + +impl FD_TABLE { + /// Return a copy of the inner table. + pub fn copy_inner(&self) -> RwLock, AX_FILE_LIMIT>> { + let table = self.read(); + let mut new_table = FlattenObjects::new(); + for id in table.ids() { + let _ = new_table.add_at(id, table.get(id).unwrap().clone()); + } + RwLock::new(new_table) + } + + pub fn clear(&self) { + let mut table = self.write(); + let ids = table.ids().collect::>(); + for id in ids { + let _ = table.remove(id); + } + } +} + +/// Get a file-like object by `fd`. +pub fn get_file_like(fd: c_int) -> LinuxResult> { + FD_TABLE + .read() + .get(fd as usize) + .cloned() + .ok_or(LinuxError::EBADF) +} + +/// Add a file to the file descriptor table. +pub fn add_file_like(f: Arc) -> LinuxResult { + Ok(FD_TABLE.write().add(f).map_err(|_| LinuxError::EMFILE)? as c_int) +} + +/// Close a file by `fd`. +pub fn close_file_like(fd: c_int) -> LinuxResult { + let f = FD_TABLE + .write() + .remove(fd as usize) + .ok_or(LinuxError::EBADF)?; + debug!("close_file_like <= count: {}", Arc::strong_count(&f)); + Ok(()) +} + +#[ctor_bare::register_ctor] +fn init_stdio() { + let mut fd_table = flatten_objects::FlattenObjects::new(); + fd_table + .add_at(0, Arc::new(stdio::stdin()) as _) + .unwrap_or_else(|_| panic!()); // stdin + fd_table + .add_at(1, Arc::new(stdio::stdout()) as _) + .unwrap_or_else(|_| panic!()); // stdout + fd_table + .add_at(2, Arc::new(stdio::stdout()) as _) + .unwrap_or_else(|_| panic!()); // stderr + FD_TABLE.init_new(spin::RwLock::new(fd_table)); +} diff --git a/api/src/file/net.rs b/api/src/file/net.rs new file mode 100644 index 000000000..a4b87ae05 --- /dev/null +++ b/api/src/file/net.rs @@ -0,0 +1,111 @@ +use core::net::SocketAddr; + +use alloc::sync::Arc; +use axerrno::{LinuxError, LinuxResult}; +use axio::PollState; +use axnet::{TcpSocket, UdpSocket}; +use axsync::Mutex; +use linux_raw_sys::general::S_IFSOCK; + +use super::{FileLike, Kstat}; + +pub enum Socket { + Udp(Mutex), + Tcp(Mutex), +} + +macro_rules! impl_socket { + ($pub:vis fn $name:ident(&self $(,$arg:ident: $arg_ty:ty)*) -> $ret:ty) => { + $pub fn $name(&self, $($arg: $arg_ty),*) -> $ret { + match self { + Socket::Udp(udpsocket) => Ok(udpsocket.lock().$name($($arg),*)?), + Socket::Tcp(tcpsocket) => Ok(tcpsocket.lock().$name($($arg),*)?), + } + } + }; +} + +impl Socket { + pub fn recv(&self, buf: &mut [u8]) -> LinuxResult { + match self { + Socket::Udp(udpsocket) => Ok(udpsocket.lock().recv_from(buf).map(|e| e.0)?), + Socket::Tcp(tcpsocket) => Ok(tcpsocket.lock().recv(buf)?), + } + } + + pub fn sendto(&self, buf: &[u8], addr: SocketAddr) -> LinuxResult { + match self { + // diff: must bind before sendto + Socket::Udp(udpsocket) => Ok(udpsocket.lock().send_to(buf, addr)?), + Socket::Tcp(_) => Err(LinuxError::EISCONN), + } + } + + pub fn recvfrom(&self, buf: &mut [u8]) -> LinuxResult<(usize, Option)> { + match self { + // diff: must bind before recvfrom + Socket::Udp(udpsocket) => Ok(udpsocket + .lock() + .recv_from(buf) + .map(|res| (res.0, Some(res.1)))?), + Socket::Tcp(tcpsocket) => Ok(tcpsocket.lock().recv(buf).map(|res| (res, None))?), + } + } + + pub fn listen(&self) -> LinuxResult { + match self { + Socket::Udp(_) => Err(LinuxError::EOPNOTSUPP), + Socket::Tcp(tcpsocket) => Ok(tcpsocket.lock().listen()?), + } + } + + pub fn accept(&self) -> LinuxResult { + match self { + Socket::Udp(_) => Err(LinuxError::EOPNOTSUPP), + Socket::Tcp(tcpsocket) => Ok(tcpsocket.lock().accept()?), + } + } + + impl_socket!(pub fn send(&self, buf: &[u8]) -> LinuxResult); + impl_socket!(pub fn poll(&self) -> LinuxResult); + impl_socket!(pub fn local_addr(&self) -> LinuxResult); + impl_socket!(pub fn peer_addr(&self) -> LinuxResult); + impl_socket!(pub fn bind(&self, addr: SocketAddr) -> LinuxResult); + impl_socket!(pub fn connect(&self, addr: SocketAddr) -> LinuxResult); + impl_socket!(pub fn shutdown(&self) -> LinuxResult); +} + +impl FileLike for Socket { + fn read(&self, buf: &mut [u8]) -> LinuxResult { + self.recv(buf) + } + + fn write(&self, buf: &[u8]) -> LinuxResult { + self.send(buf) + } + + fn stat(&self) -> LinuxResult { + // not really implemented + Ok(Kstat { + mode: S_IFSOCK | 0o777u32, // rwxrwxrwx + blksize: 4096, + ..Default::default() + }) + } + + fn into_any(self: Arc) -> Arc { + self + } + + fn poll(&self) -> LinuxResult { + self.poll() + } + + fn set_nonblocking(&self, nonblock: bool) -> LinuxResult { + match self { + Socket::Udp(udpsocket) => udpsocket.lock().set_nonblocking(nonblock), + Socket::Tcp(tcpsocket) => tcpsocket.lock().set_nonblocking(nonblock), + } + Ok(()) + } +} diff --git a/api/src/file/pipe.rs b/api/src/file/pipe.rs new file mode 100644 index 000000000..aa4501d1e --- /dev/null +++ b/api/src/file/pipe.rs @@ -0,0 +1,194 @@ +use core::any::Any; + +use alloc::sync::Arc; +use axerrno::{LinuxError, LinuxResult}; +use axio::PollState; +use axsync::Mutex; +use linux_raw_sys::general::S_IFIFO; + +use super::{FileLike, Kstat}; + +#[derive(Copy, Clone, PartialEq)] +enum RingBufferStatus { + Full, + Empty, + Normal, +} + +const RING_BUFFER_SIZE: usize = 256; + +struct PipeRingBuffer { + arr: [u8; RING_BUFFER_SIZE], + head: usize, + tail: usize, + status: RingBufferStatus, +} + +impl PipeRingBuffer { + const fn new() -> Self { + Self { + arr: [0; RING_BUFFER_SIZE], + head: 0, + tail: 0, + status: RingBufferStatus::Empty, + } + } + + fn write_byte(&mut self, byte: u8) { + self.status = RingBufferStatus::Normal; + self.arr[self.tail] = byte; + self.tail = (self.tail + 1) % RING_BUFFER_SIZE; + if self.tail == self.head { + self.status = RingBufferStatus::Full; + } + } + + fn read_byte(&mut self) -> u8 { + self.status = RingBufferStatus::Normal; + let c = self.arr[self.head]; + self.head = (self.head + 1) % RING_BUFFER_SIZE; + if self.head == self.tail { + self.status = RingBufferStatus::Empty; + } + c + } + + /// Get the length of remaining data in the buffer + const fn available_read(&self) -> usize { + if matches!(self.status, RingBufferStatus::Empty) { + 0 + } else if self.tail > self.head { + self.tail - self.head + } else { + self.tail + RING_BUFFER_SIZE - self.head + } + } + + /// Get the length of remaining space in the buffer + const fn available_write(&self) -> usize { + if matches!(self.status, RingBufferStatus::Full) { + 0 + } else { + RING_BUFFER_SIZE - self.available_read() + } + } +} + +pub struct Pipe { + readable: bool, + buffer: Arc>, +} + +impl Pipe { + pub fn new() -> (Pipe, Pipe) { + let buffer = Arc::new(Mutex::new(PipeRingBuffer::new())); + let read_end = Pipe { + readable: true, + buffer: buffer.clone(), + }; + let write_end = Pipe { + readable: false, + buffer, + }; + (read_end, write_end) + } + + pub const fn readable(&self) -> bool { + self.readable + } + + pub const fn writable(&self) -> bool { + !self.readable + } + + pub fn closed(&self) -> bool { + Arc::strong_count(&self.buffer) == 1 + } +} + +impl FileLike for Pipe { + fn read(&self, buf: &mut [u8]) -> LinuxResult { + if !self.readable() { + return Err(LinuxError::EPERM); + } + if buf.is_empty() { + return Ok(0); + } + + loop { + let mut ring_buffer = self.buffer.lock(); + let read_size = ring_buffer.available_read().min(buf.len()); + if read_size == 0 { + if self.closed() { + return Ok(0); + } + drop(ring_buffer); + // Data not ready, wait for write end + axtask::yield_now(); // TODO: use synconize primitive + continue; + } + for c in buf.iter_mut().take(read_size) { + *c = ring_buffer.read_byte(); + } + return Ok(read_size); + } + } + + fn write(&self, buf: &[u8]) -> LinuxResult { + if !self.writable() { + return Err(LinuxError::EPERM); + } + if self.closed() { + return Err(LinuxError::EPIPE); + } + if buf.is_empty() { + return Ok(0); + } + + let mut write_size = 0usize; + let total_len = buf.len(); + loop { + let mut ring_buffer = self.buffer.lock(); + let loop_write = ring_buffer.available_write(); + if loop_write == 0 { + if self.closed() { + return Ok(write_size); + } + drop(ring_buffer); + // Buffer is full, wait for read end to consume + axtask::yield_now(); // TODO: use synconize primitive + continue; + } + for _ in 0..loop_write { + if write_size == total_len { + return Ok(write_size); + } + ring_buffer.write_byte(buf[write_size]); + write_size += 1; + } + } + } + + fn stat(&self) -> LinuxResult { + Ok(Kstat { + mode: S_IFIFO | 0o600u32, // rw------- + ..Default::default() + }) + } + + fn into_any(self: Arc) -> Arc { + self + } + + fn poll(&self) -> LinuxResult { + let buf = self.buffer.lock(); + Ok(PollState { + readable: self.readable() && buf.available_read() > 0, + writable: self.writable() && buf.available_write() > 0, + }) + } + + fn set_nonblocking(&self, _nonblocking: bool) -> LinuxResult { + Ok(()) + } +} diff --git a/api/src/file/stdio.rs b/api/src/file/stdio.rs new file mode 100644 index 000000000..beccf231f --- /dev/null +++ b/api/src/file/stdio.rs @@ -0,0 +1,170 @@ +use core::any::Any; + +use alloc::sync::Arc; +use axerrno::{AxResult, LinuxError, LinuxResult}; +use axio::{BufReader, PollState, prelude::*}; +use axsync::Mutex; +use linux_raw_sys::general::S_IFCHR; + +use super::Kstat; + +fn console_read_bytes(buf: &mut [u8]) -> AxResult { + let len = axhal::console::read_bytes(buf); + for c in &mut buf[..len] { + if *c == b'\r' { + *c = b'\n'; + } + } + Ok(len) +} + +fn console_write_bytes(buf: &[u8]) -> AxResult { + axhal::console::write_bytes(buf); + Ok(buf.len()) +} + +struct StdinRaw; +struct StdoutRaw; + +impl Read for StdinRaw { + // Non-blocking read, returns number of bytes read. + fn read(&mut self, buf: &mut [u8]) -> AxResult { + let mut read_len = 0; + while read_len < buf.len() { + let len = console_read_bytes(buf[read_len..].as_mut())?; + if len == 0 { + break; + } + read_len += len; + } + Ok(read_len) + } +} + +impl Write for StdoutRaw { + fn write(&mut self, buf: &[u8]) -> AxResult { + console_write_bytes(buf) + } + + fn flush(&mut self) -> AxResult { + Ok(()) + } +} + +pub struct Stdin { + inner: &'static Mutex>, +} + +impl Stdin { + // Block until at least one byte is read. + fn read_blocked(&self, buf: &mut [u8]) -> AxResult { + let read_len = self.inner.lock().read(buf)?; + if buf.is_empty() || read_len > 0 { + return Ok(read_len); + } + // try again until we get something + loop { + let read_len = self.inner.lock().read(buf)?; + if read_len > 0 { + return Ok(read_len); + } + axtask::yield_now(); + } + } +} + +impl Read for Stdin { + fn read(&mut self, buf: &mut [u8]) -> AxResult { + self.read_blocked(buf) + } +} + +pub struct Stdout { + inner: &'static Mutex, +} + +impl Write for Stdout { + fn write(&mut self, buf: &[u8]) -> AxResult { + self.inner.lock().write(buf) + } + + fn flush(&mut self) -> AxResult { + self.inner.lock().flush() + } +} + +/// Constructs a new handle to the standard input of the current process. +pub fn stdin() -> Stdin { + static INSTANCE: Mutex> = Mutex::new(BufReader::new(StdinRaw)); + Stdin { inner: &INSTANCE } +} + +/// Constructs a new handle to the standard output of the current process. +pub fn stdout() -> Stdout { + static INSTANCE: Mutex = Mutex::new(StdoutRaw); + Stdout { inner: &INSTANCE } +} + +impl super::FileLike for Stdin { + fn read(&self, buf: &mut [u8]) -> LinuxResult { + Ok(self.read_blocked(buf)?) + } + + fn write(&self, _buf: &[u8]) -> LinuxResult { + Err(LinuxError::EPERM) + } + + fn stat(&self) -> LinuxResult { + Ok(Kstat { + mode: S_IFCHR | 0o444u32, // r--r--r-- + ..Default::default() + }) + } + + fn into_any(self: Arc) -> Arc { + self + } + + fn poll(&self) -> LinuxResult { + Ok(PollState { + readable: true, + writable: true, + }) + } + + fn set_nonblocking(&self, _nonblocking: bool) -> LinuxResult { + Ok(()) + } +} + +impl super::FileLike for Stdout { + fn read(&self, _buf: &mut [u8]) -> LinuxResult { + Err(LinuxError::EPERM) + } + + fn write(&self, buf: &[u8]) -> LinuxResult { + Ok(self.inner.lock().write(buf)?) + } + + fn stat(&self) -> LinuxResult { + Ok(Kstat { + mode: S_IFCHR | 0o220u32, // -w--w---- + ..Default::default() + }) + } + + fn into_any(self: Arc) -> Arc { + self + } + + fn poll(&self) -> LinuxResult { + Ok(PollState { + readable: true, + writable: true, + }) + } + + fn set_nonblocking(&self, _nonblocking: bool) -> LinuxResult { + Ok(()) + } +} diff --git a/api/src/imp/fs/ctl.rs b/api/src/imp/fs/ctl.rs index 37dce1f3f..3d9415dc5 100644 --- a/api/src/imp/fs/ctl.rs +++ b/api/src/imp/fs/ctl.rs @@ -1,13 +1,20 @@ -use core::ffi::{c_char, c_void}; +use core::{ + ffi::{c_char, c_int, c_void}, + mem::offset_of, +}; -use alloc::string::ToString; -use arceos_posix_api::AT_FDCWD; -use axerrno::{AxError, LinuxError, LinuxResult}; -use macro_rules_attribute::apply; +use alloc::ffi::CString; +use axerrno::{LinuxError, LinuxResult}; +use axfs::fops::DirEntry; +use linux_raw_sys::general::{ + AT_FDCWD, AT_REMOVEDIR, DT_BLK, DT_CHR, DT_DIR, DT_FIFO, DT_LNK, DT_REG, DT_SOCK, DT_UNKNOWN, + linux_dirent64, +}; use crate::{ - ptr::{PtrWrapper, UserConstPtr, UserPtr}, - syscall_instrument, + file::{Directory, FileLike}, + path::{HARDLINK_MANAGER, handle_file_path}, + ptr::{UserConstPtr, UserPtr, nullable}, }; /// The ioctl() system call manipulates the underlying device parameters @@ -18,7 +25,6 @@ use crate::{ /// * `op` - The request code. It is of type unsigned long in glibc and BSD, /// and of type int in musl and other UNIX systems. /// * `argp` - The argument to the request. It is a pointer to a memory location -#[apply(syscall_instrument)] pub fn sys_ioctl(_fd: i32, _op: usize, _argp: UserPtr) -> LinuxResult { warn!("Unimplemented syscall: SYS_IOCTL"); Ok(0) @@ -26,53 +32,41 @@ pub fn sys_ioctl(_fd: i32, _op: usize, _argp: UserPtr) -> LinuxResult) -> LinuxResult { let path = path.get_as_str()?; - axfs::api::set_current_dir(path).map(|_| 0).map_err(|err| { - warn!("Failed to change directory: {err:?}"); - err.into() - }) + debug!("sys_chdir <= {:?}", path); + + axfs::api::set_current_dir(path)?; + Ok(0) } pub fn sys_mkdirat(dirfd: i32, path: UserConstPtr, mode: u32) -> LinuxResult { let path = path.get_as_str()?; - - if !path.starts_with("/") && dirfd != AT_FDCWD as i32 { - warn!("unsupported."); - return Err(LinuxError::EINVAL); - } + debug!( + "sys_mkdirat <= dirfd: {}, path: {}, mode: {}", + dirfd, path, mode + ); if mode != 0 { - info!("directory mode not supported."); + warn!("directory mode not supported."); } - axfs::api::create_dir(path).map(|_| 0).map_err(|err| { - warn!("Failed to create directory {path}: {err:?}"); - err.into() - }) -} + let path = handle_file_path(dirfd, path)?; + axfs::api::create_dir(path.as_str())?; -#[repr(C)] -#[derive(Debug, Clone, Copy)] -struct DirEnt { - d_ino: u64, - d_off: i64, - d_reclen: u16, - d_type: u8, - d_name: [u8; 0], + Ok(0) } #[allow(dead_code)] #[repr(u8)] #[derive(Debug, Clone, Copy)] pub enum FileType { - Unknown = 0, - Fifo = 1, - Chr = 2, - Dir = 4, - Blk = 6, - Reg = 8, - Lnk = 10, - Socket = 12, - Wht = 14, + Unknown = DT_UNKNOWN as u8, + Fifo = DT_FIFO as u8, + Chr = DT_CHR as u8, + Dir = DT_DIR as u8, + Blk = DT_BLK as u8, + Reg = DT_REG as u8, + Lnk = DT_LNK as u8, + Socket = DT_SOCK as u8, } impl From for FileType { @@ -85,29 +79,6 @@ impl From for FileType { } } -impl DirEnt { - const FIXED_SIZE: usize = core::mem::size_of::() - + core::mem::size_of::() - + core::mem::size_of::() - + core::mem::size_of::(); - - fn new(ino: u64, off: i64, reclen: usize, file_type: FileType) -> Self { - Self { - d_ino: ino, - d_off: off, - d_reclen: reclen as u16, - d_type: file_type as u8, - d_name: [], - } - } - - unsafe fn write_name(&mut self, name: &[u8]) { - unsafe { - core::ptr::copy_nonoverlapping(name.as_ptr(), self.d_name.as_mut_ptr(), name.len()); - } - } -} - // Directory buffer for getdents64 syscall struct DirBuffer<'a> { buf: &'a mut [u8], @@ -123,95 +94,77 @@ impl<'a> DirBuffer<'a> { self.buf.len().saturating_sub(self.offset) } - fn can_fit_entry(&self, entry_size: usize) -> bool { - self.remaining_space() >= entry_size - } + fn write_entry(&mut self, d_type: FileType, name: &[u8]) -> bool { + const NAME_OFFSET: usize = offset_of!(linux_dirent64, d_name); - fn write_entry(&mut self, dirent: DirEnt, name: &[u8]) -> Result<(), ()> { - if !self.can_fit_entry(dirent.d_reclen as usize) { - return Err(()); + let len = NAME_OFFSET + name.len() + 1; + // alignment + let len = len.next_multiple_of(align_of::()); + if self.remaining_space() < len { + return false; } + unsafe { - let entry_ptr = self.buf.as_mut_ptr().add(self.offset) as *mut DirEnt; - entry_ptr.write(dirent); - (*entry_ptr).write_name(name); + let entry_ptr = self.buf.as_mut_ptr().add(self.offset); + entry_ptr.cast::().write(linux_dirent64 { + // FIXME: real inode number + d_ino: 1, + d_off: 0, + d_reclen: len as _, + d_type: d_type as _, + d_name: Default::default(), + }); + + let name_ptr = entry_ptr.add(NAME_OFFSET); + name_ptr.copy_from_nonoverlapping(name.as_ptr(), name.len()); + name_ptr.add(name.len()).write(0); } - self.offset += dirent.d_reclen as usize; - Ok(()) + self.offset += len; + true } } -pub fn sys_getdents64(fd: i32, buf: UserPtr, len: usize) -> LinuxResult { - let buf = buf.get_as_bytes(len)?; +pub fn sys_getdents64(fd: i32, buf: UserPtr, len: usize) -> LinuxResult { + let buf = buf.get_as_mut_slice(len)?; + debug!( + "sys_getdents64 <= fd: {}, buf: {:p}, len: {}", + fd, + buf.as_ptr(), + buf.len() + ); - if len < DirEnt::FIXED_SIZE { - warn!("Buffer size too small: {len}"); - return Err(LinuxError::EINVAL); + let mut buffer = DirBuffer::new(buf); + + let dir = Directory::from_fd(fd)?; + + let mut last_dirent = dir.last_dirent(); + if let Some(ent) = last_dirent.take() { + if !buffer.write_entry(ent.entry_type().into(), ent.name_as_bytes()) { + *last_dirent = Some(ent); + return Err(LinuxError::EINVAL); + } } - let path = match arceos_posix_api::Directory::from_fd(fd).map(|dir| dir.path().to_string()) { - Ok(path) => path, - Err(err) => { - warn!("Invalid directory descriptor: {:?}", err); - return Err(LinuxError::EBADF); + let mut inner = dir.inner(); + loop { + let mut dirents = [DirEntry::default()]; + let cnt = inner.read_dir(&mut dirents)?; + if cnt == 0 { + break; } - }; - let mut buffer = - unsafe { DirBuffer::new(core::slice::from_raw_parts_mut(buf as *mut u8, len)) }; - - let (initial_offset, count) = unsafe { - let mut buf_offset = 0; - let mut count = 0; - while buf_offset + DirEnt::FIXED_SIZE <= len { - let dir_ent = *(buf.add(buf_offset) as *const DirEnt); - if dir_ent.d_reclen == 0 { - break; - } - - buf_offset += dir_ent.d_reclen as usize; - assert_eq!(dir_ent.d_off, buf_offset as i64); - count += 1; + let [ent] = dirents; + if !buffer.write_entry(ent.entry_type().into(), ent.name_as_bytes()) { + *last_dirent = Some(ent); + break; } - (buf_offset as i64, count) - }; + } - axfs::api::read_dir(&path) - .map(|entries| { - let mut total_size = initial_offset as usize; - let mut current_offset = initial_offset; - - for entry in entries.flatten().skip(count) { - let mut name = entry.file_name(); - name.push('\0'); - let name_bytes = name.as_bytes(); - - let entry_size = DirEnt::FIXED_SIZE + name_bytes.len(); - current_offset += entry_size as i64; - - let dirent = DirEnt::new( - 1, - current_offset, - entry_size, - FileType::from(entry.file_type()), - ); - - if buffer.write_entry(dirent, name_bytes).is_err() { - break; - } - - total_size += entry_size; - } - - if total_size > 0 && buffer.can_fit_entry(DirEnt::FIXED_SIZE) { - let terminal = DirEnt::new(1, current_offset, 0, FileType::Reg); - let _ = buffer.write_entry(terminal, &[]); - } - - total_size as isize - }) - .map_err(|err| err.into()) + if last_dirent.is_some() && buffer.offset == 0 { + return Err(LinuxError::EINVAL); + } + Ok(buffer.offset as _) } /// create a link from new_path to old_path @@ -220,40 +173,38 @@ pub fn sys_getdents64(fd: i32, buf: UserPtr, len: usize) -> LinuxResult< /// flags: link flags /// return value: return 0 when success, else return -1. pub fn sys_linkat( - old_dirfd: i32, + old_dirfd: c_int, old_path: UserConstPtr, - new_dirfd: i32, + new_dirfd: c_int, new_path: UserConstPtr, flags: i32, ) -> LinuxResult { - let old_path = old_path.get_as_null_terminated()?; - let new_path = new_path.get_as_null_terminated()?; + let old_path = old_path.get_as_str()?; + let new_path = new_path.get_as_str()?; + debug!( + "sys_linkat <= old_dirfd: {}, old_path: {}, new_dirfd: {}, new_path: {}, flags: {}", + old_dirfd, old_path, new_dirfd, new_path, flags + ); if flags != 0 { warn!("Unsupported flags: {flags}"); } // handle old path - arceos_posix_api::handle_file_path(old_dirfd as isize, Some(old_path.as_ptr() as _), false) - .inspect_err(|err| warn!("Failed to convert new path: {err:?}")) - .and_then(|old_path| { - //handle new path - arceos_posix_api::handle_file_path( - new_dirfd as isize, - Some(new_path.as_ptr() as _), - false, - ) - .inspect_err(|err| warn!("Failed to convert new path: {err:?}")) - .map(|new_path| (old_path, new_path)) - }) - .and_then(|(old_path, new_path)| { - arceos_posix_api::HARDLINK_MANAGER - .create_link(&new_path, &old_path) - .inspect_err(|err| warn!("Failed to create link: {err:?}")) - .map_err(Into::into) - }) - .map(|_| 0) - .map_err(|err| err.into()) + let old_path = handle_file_path(old_dirfd, old_path)?; + // handle new path + let new_path = handle_file_path(new_dirfd, new_path)?; + + HARDLINK_MANAGER.create_link(&new_path, &old_path)?; + + Ok(0) +} + +pub fn sys_link( + old_path: UserConstPtr, + new_path: UserConstPtr, +) -> LinuxResult { + sys_linkat(AT_FDCWD, old_path, AT_FDCWD, new_path, 0) } /// remove link of specific file (can be used to delete file) @@ -261,38 +212,49 @@ pub fn sys_linkat( /// path: the name of link to be removed /// flags: can be 0 or AT_REMOVEDIR /// return 0 when success, else return -1 -pub fn sys_unlinkat(dir_fd: isize, path: UserConstPtr, flags: usize) -> LinuxResult { - let path = path.get_as_null_terminated()?; - - const AT_REMOVEDIR: usize = 0x200; - - arceos_posix_api::handle_file_path(dir_fd, Some(path.as_ptr() as _), false) - .inspect_err(|e| warn!("unlinkat error: {:?}", e)) - .and_then(|path| { - if flags == AT_REMOVEDIR { - axfs::api::remove_dir(path.as_str()) - .inspect_err(|e| warn!("unlinkat error: {:?}", e)) - .map(|_| 0) - } else { - axfs::api::metadata(path.as_str()).and_then(|metadata| { - if metadata.is_dir() { - Err(AxError::IsADirectory) - } else { - debug!("unlink file: {:?}", path); - arceos_posix_api::HARDLINK_MANAGER - .remove_link(&path) - .ok_or_else(|| { - debug!("unlink file error"); - AxError::NotFound - }) - .map(|_| 0) - } - }) - } - }) - .map_err(|err| err.into()) +pub fn sys_unlinkat(dirfd: c_int, path: UserConstPtr, flags: u32) -> LinuxResult { + let path = path.get_as_str()?; + debug!( + "sys_unlinkat <= dirfd: {}, path: {}, flags: {}", + dirfd, path, flags + ); + + let path = handle_file_path(dirfd, path)?; + + if flags == AT_REMOVEDIR { + axfs::api::remove_dir(path.as_str())?; + } else { + let metadata = axfs::api::metadata(path.as_str())?; + if metadata.is_dir() { + return Err(LinuxError::EISDIR); + } else { + debug!("unlink file: {:?}", path); + HARDLINK_MANAGER + .remove_link(&path) + .ok_or(LinuxError::ENOENT)?; + } + } + Ok(0) +} + +pub fn sys_unlink(path: UserConstPtr) -> LinuxResult { + sys_unlinkat(AT_FDCWD, path, 0) } -pub fn sys_getcwd(buf: UserPtr, size: usize) -> LinuxResult { - Ok(arceos_posix_api::sys_getcwd(buf.get_as_null_terminated()?.as_ptr() as _, size) as _) +pub fn sys_getcwd(buf: UserPtr, size: usize) -> LinuxResult { + let buf = nullable!(buf.get_as_mut_slice(size))?; + + let Some(buf) = buf else { + return Ok(0); + }; + + let cwd = CString::new(axfs::api::current_dir()?).map_err(|_| LinuxError::EINVAL)?; + let cwd = cwd.as_bytes_with_nul(); + + if cwd.len() <= buf.len() { + buf[..cwd.len()].copy_from_slice(cwd); + Ok(buf.as_ptr() as _) + } else { + Err(LinuxError::ERANGE) + } } diff --git a/api/src/imp/fs/fd_ops.rs b/api/src/imp/fs/fd_ops.rs index d4f5605b4..0d5fa34b3 100644 --- a/api/src/imp/fs/fd_ops.rs +++ b/api/src/imp/fs/fd_ops.rs @@ -1,20 +1,168 @@ -use core::ffi::c_int; +use core::{ + ffi::{c_char, c_int}, + panic, +}; -use arceos_posix_api as api; -use axerrno::LinuxResult; +use alloc::string::ToString; +use axerrno::{AxError, LinuxError, LinuxResult}; +use axfs::fops::OpenOptions; +use linux_raw_sys::general::{ + __kernel_mode_t, AT_FDCWD, F_DUPFD, F_DUPFD_CLOEXEC, F_SETFL, O_APPEND, O_CREAT, O_DIRECTORY, + O_NONBLOCK, O_PATH, O_RDONLY, O_TRUNC, O_WRONLY, +}; -pub fn sys_dup(old_fd: c_int) -> LinuxResult { - Ok(api::sys_dup(old_fd) as _) +use crate::{ + file::{Directory, FD_TABLE, File, FileLike, add_file_like, close_file_like, get_file_like}, + path::handle_file_path, + ptr::UserConstPtr, +}; + +const O_EXEC: u32 = O_PATH; + +/// Convert open flags to [`OpenOptions`]. +fn flags_to_options(flags: c_int, _mode: __kernel_mode_t) -> OpenOptions { + let flags = flags as u32; + let mut options = OpenOptions::new(); + match flags & 0b11 { + O_RDONLY => options.read(true), + O_WRONLY => options.write(true), + _ => { + options.read(true); + options.write(true); + } + }; + if flags & O_APPEND != 0 { + options.append(true); + } + if flags & O_TRUNC != 0 { + options.truncate(true); + } + if flags & O_CREAT != 0 { + options.create(true); + } + if flags & O_EXEC != 0 { + //options.create_new(true); + options.execute(true); + } + if flags & O_DIRECTORY != 0 { + options.directory(true); + } + options } -pub fn sys_dup3(old_fd: c_int, new_fd: c_int) -> LinuxResult { - Ok(api::sys_dup2(old_fd, new_fd) as _) +/// Open or create a file. +/// fd: file descriptor +/// filename: file path to be opened or created +/// flags: open flags +/// mode: see man 7 inode +/// return new file descriptor if succeed, or return -1. +pub fn sys_openat( + dirfd: c_int, + path: UserConstPtr, + flags: i32, + mode: __kernel_mode_t, +) -> LinuxResult { + let path = path.get_as_str()?; + let opts = flags_to_options(flags, mode); + debug!("sys_openat <= {} {} {:?}", dirfd, path, opts); + + let dir = if path.starts_with('/') || dirfd == AT_FDCWD { + None + } else { + Some(Directory::from_fd(dirfd)?) + }; + let real_path = handle_file_path(dirfd, path)?; + + if !opts.has_directory() { + match dir.as_ref().map_or_else( + || axfs::fops::File::open(path, &opts), + |dir| dir.inner().open_file_at(path, &opts), + ) { + Err(AxError::IsADirectory) => {} + r => { + let fd = File::new(r?, real_path.to_string()).add_to_fd_table()?; + return Ok(fd as _); + } + } + } + + let fd = Directory::new( + dir.map_or_else( + || axfs::fops::Directory::open_dir(path, &opts), + |dir| dir.inner().open_dir_at(path, &opts), + )?, + real_path.to_string(), + ) + .add_to_fd_table()?; + Ok(fd as _) +} + +/// Open a file by `filename` and insert it into the file descriptor table. +/// +/// Return its index in the file table (`fd`). Return `EMFILE` if it already +/// has the maximum number of files open. +pub fn sys_open( + path: UserConstPtr, + flags: i32, + mode: __kernel_mode_t, +) -> LinuxResult { + sys_openat(AT_FDCWD as _, path, flags, mode) } pub fn sys_close(fd: c_int) -> LinuxResult { - Ok(api::sys_close(fd) as _) + debug!("sys_close <= {}", fd); + close_file_like(fd)?; + Ok(0) +} + +fn dup_fd(old_fd: c_int) -> LinuxResult { + let f = get_file_like(old_fd)?; + let new_fd = add_file_like(f)?; + Ok(new_fd as _) +} + +pub fn sys_dup(old_fd: c_int) -> LinuxResult { + debug!("sys_dup <= {}", old_fd); + dup_fd(old_fd) +} + +pub fn sys_dup2(old_fd: c_int, new_fd: c_int) -> LinuxResult { + debug!("sys_dup2 <= old_fd: {}, new_fd: {}", old_fd, new_fd); + let mut fd_table = FD_TABLE.write(); + let f = fd_table + .get(old_fd as _) + .cloned() + .ok_or(LinuxError::EBADF)?; + + if old_fd != new_fd { + fd_table.remove(new_fd as _); + fd_table + .add_at(new_fd as _, f) + .unwrap_or_else(|_| panic!("new_fd should be valid")); + } + + Ok(new_fd as _) } pub fn sys_fcntl(fd: c_int, cmd: c_int, arg: usize) -> LinuxResult { - Ok(api::sys_fcntl(fd, cmd, arg) as _) + debug!("sys_fcntl <= fd: {} cmd: {} arg: {}", fd, cmd, arg); + + match cmd as u32 { + F_DUPFD => dup_fd(fd), + F_DUPFD_CLOEXEC => { + warn!("sys_fcntl: treat F_DUPFD_CLOEXEC as F_DUPFD"); + dup_fd(fd) + } + F_SETFL => { + if fd == 0 || fd == 1 || fd == 2 { + return Ok(0); + } + get_file_like(fd)?.set_nonblocking(arg & (O_NONBLOCK as usize) > 0)?; + Ok(0) + } + _ => { + warn!("unsupported fcntl parameters: cmd: {}", cmd); + Ok(0) + } + } } diff --git a/api/src/imp/fs/io.rs b/api/src/imp/fs/io.rs index 8be57b4b9..4d025dfbe 100644 --- a/api/src/imp/fs/io.rs +++ b/api/src/imp/fs/io.rs @@ -1,40 +1,81 @@ -use core::ffi::{c_char, c_void}; +use core::ffi::c_int; -use arceos_posix_api::{self as api, ctypes::mode_t}; -use axerrno::LinuxResult; +use axerrno::{LinuxError, LinuxResult}; +use axio::SeekFrom; +use linux_raw_sys::general::{__kernel_off_t, iovec}; -use crate::ptr::{PtrWrapper, UserConstPtr, UserPtr}; +use crate::{ + file::{File, FileLike, get_file_like}, + ptr::{UserConstPtr, UserPtr}, +}; -pub fn sys_read(fd: i32, buf: UserPtr, count: usize) -> LinuxResult { - let buf = buf.get_as_bytes(count)?; - Ok(api::sys_read(fd, buf, count)) +/// Read data from the file indicated by `fd`. +/// +/// Return the read size if success. +pub fn sys_read(fd: i32, buf: UserPtr, len: usize) -> LinuxResult { + let buf = buf.get_as_mut_slice(len)?; + debug!( + "sys_read <= fd: {}, buf: {:p}, len: {}", + fd, + buf.as_ptr(), + buf.len() + ); + Ok(get_file_like(fd)?.read(buf)? as _) } -pub fn sys_write(fd: i32, buf: UserConstPtr, count: usize) -> LinuxResult { - let buf = buf.get_as_bytes(count)?; - Ok(api::sys_write(fd, buf, count)) +/// Write data to the file indicated by `fd`. +/// +/// Return the written size if success. +pub fn sys_write(fd: i32, buf: UserConstPtr, len: usize) -> LinuxResult { + let buf = buf.get_as_slice(len)?; + debug!( + "sys_write <= fd: {}, buf: {:p}, len: {}", + fd, + buf.as_ptr(), + buf.len() + ); + Ok(get_file_like(fd)?.write(buf)? as _) } -pub fn sys_writev( - fd: i32, - iov: UserConstPtr, - iocnt: i32, -) -> LinuxResult { - let iov = iov.get_as_bytes(iocnt as _)?; - unsafe { Ok(api::sys_writev(fd, iov, iocnt)) } -} +pub fn sys_writev(fd: i32, iov: UserConstPtr, iocnt: usize) -> LinuxResult { + if !(0..=1024).contains(&iocnt) { + return Err(LinuxError::EINVAL); + } + + let iovs = iov.get_as_slice(iocnt)?; + let mut ret = 0; + for iov in iovs { + if iov.iov_len == 0 { + continue; + } + let buf = UserConstPtr::::from(iov.iov_base as usize); + let buf = buf.get_as_slice(iov.iov_len as _)?; + debug!( + "sys_writev <= fd: {}, buf: {:p}, len: {}", + fd, + buf.as_ptr(), + buf.len() + ); + + let written = get_file_like(fd)?.write(buf)?; + ret += written as isize; + + if written < buf.len() { + break; + } + } -pub fn sys_openat( - dirfd: i32, - path: UserConstPtr, - flags: i32, - modes: mode_t, -) -> LinuxResult { - let path = path.get_as_null_terminated()?; - Ok(api::sys_openat(dirfd, path.as_ptr(), flags, modes) as _) + Ok(ret) } -pub fn sys_open(path: UserConstPtr, flags: i32, modes: mode_t) -> LinuxResult { - use arceos_posix_api::AT_FDCWD; - sys_openat(AT_FDCWD as _, path, flags, modes) +pub fn sys_lseek(fd: c_int, offset: __kernel_off_t, whence: c_int) -> LinuxResult { + debug!("sys_lseek <= {} {} {}", fd, offset, whence); + let pos = match whence { + 0 => SeekFrom::Start(offset as _), + 1 => SeekFrom::Current(offset as _), + 2 => SeekFrom::End(offset as _), + _ => return Err(LinuxError::EINVAL), + }; + let off = File::from_fd(fd)?.inner().seek(pos)?; + Ok(off as _) } diff --git a/api/src/imp/fs/mount.rs b/api/src/imp/fs/mount.rs index 0bbdbd899..cab3f70d4 100644 --- a/api/src/imp/fs/mount.rs +++ b/api/src/imp/fs/mount.rs @@ -1,24 +1,32 @@ +use core::ffi::{c_char, c_void}; + use alloc::vec::Vec; -use arceos_posix_api::{AT_FDCWD, FilePath, handle_file_path}; use axerrno::{LinuxError, LinuxResult}; use axsync::Mutex; -use core::ffi::{c_char, c_void}; +use linux_raw_sys::general::AT_FDCWD; -use crate::ptr::UserConstPtr; +use crate::{ + path::{FilePath, handle_file_path}, + ptr::UserConstPtr, +}; pub fn sys_mount( source: UserConstPtr, target: UserConstPtr, fs_type: UserConstPtr, - _flags: i32, + flags: i32, _data: UserConstPtr, ) -> LinuxResult { - info!("sys_mount"); - let source = source.get_as_null_terminated()?; - let target = target.get_as_null_terminated()?; + let source = source.get_as_str()?; + let target = target.get_as_str()?; let fs_type = fs_type.get_as_str()?; - let device_path = handle_file_path(AT_FDCWD, Some(source.as_ptr() as _), false)?; - let mount_path = handle_file_path(AT_FDCWD, Some(target.as_ptr() as _), true)?; + info!( + "sys_mount <= source: {}, target: {}, fs_type: {}, flags: {}", + source, target, fs_type, flags + ); + + let device_path = handle_file_path(AT_FDCWD, source)?; + let mount_path = handle_file_path(AT_FDCWD, target)?; info!( "mount {:?} to {:?} with fs_type={:?}", device_path, mount_path, fs_type @@ -47,9 +55,10 @@ pub fn sys_mount( } pub fn sys_umount2(target: UserConstPtr, flags: i32) -> LinuxResult { - info!("sys_umount2"); - let target = target.get_as_null_terminated()?; - let mount_path = handle_file_path(AT_FDCWD, Some(target.as_ptr() as _), true)?; + let target = target.get_as_str()?; + info!("sys_umount2 <= target: {}, flags: {}", target, flags); + + let mount_path = handle_file_path(AT_FDCWD, target)?; if flags != 0 { debug!("flags unimplemented"); return Err(LinuxError::EPERM); @@ -69,7 +78,7 @@ pub fn sys_umount2(target: UserConstPtr, flags: i32) -> LinuxResult>, pub device: FilePath, pub mnt_dir: FilePath, @@ -77,15 +86,12 @@ pub struct MountedFs { impl MountedFs { pub fn new(device: &FilePath, mnt_dir: &FilePath) -> Self { - assert!( - device.is_file() && mnt_dir.is_dir(), - "device must be a file and mnt_dir must be a dir" - ); Self { device: device.clone(), mnt_dir: mnt_dir.clone(), } } + #[allow(unused)] pub fn device(&self) -> FilePath { self.device.clone() diff --git a/api/src/imp/fs/pipe.rs b/api/src/imp/fs/pipe.rs index 323f2c1ec..c0840d443 100644 --- a/api/src/imp/fs/pipe.rs +++ b/api/src/imp/fs/pipe.rs @@ -1,12 +1,28 @@ use core::ffi::c_int; -use arceos_posix_api as api; use axerrno::LinuxResult; -use crate::ptr::{PtrWrapper, UserPtr}; +use crate::{ + file::{FileLike, Pipe, close_file_like}, + ptr::UserPtr, +}; -pub fn sys_pipe2(fds: UserPtr) -> LinuxResult { - let fds = fds.get_as_array(2)?; - let fds_slice: &mut [c_int] = unsafe { core::slice::from_raw_parts_mut(fds, 2) }; - Ok(api::sys_pipe(fds_slice) as _) +pub fn sys_pipe2(fds: UserPtr<[c_int; 2]>, flags: i32) -> LinuxResult { + if flags != 0 { + warn!("sys_pipe2: unsupported flags: {}", flags); + } + + let fds = fds.get_as_mut()?; + + let (read_end, write_end) = Pipe::new(); + let read_fd = read_end.add_to_fd_table()?; + let write_fd = write_end + .add_to_fd_table() + .inspect_err(|_| close_file_like(read_fd).unwrap())?; + + fds[0] = read_fd; + fds[1] = write_fd; + + info!("sys_pipe2 <= fds: {:?}", fds); + Ok(0) } diff --git a/api/src/imp/fs/stat.rs b/api/src/imp/fs/stat.rs index 3533fac3b..31d08190c 100644 --- a/api/src/imp/fs/stat.rs +++ b/api/src/imp/fs/stat.rs @@ -1,267 +1,88 @@ -use core::ffi::c_char; +use core::ffi::{c_char, c_int}; -use axerrno::{LinuxError, LinuxResult}; -use macro_rules_attribute::apply; +use axerrno::{AxError, LinuxError, LinuxResult}; +use axfs::fops::OpenOptions; +use linux_raw_sys::general::{AT_EMPTY_PATH, stat, statx}; use crate::{ - ptr::{PtrWrapper, UserConstPtr, UserPtr}, - syscall_instrument, + file::{Directory, File, FileLike, Kstat, get_file_like}, + path::handle_file_path, + ptr::{UserConstPtr, UserPtr, nullable}, }; -#[cfg(target_arch = "x86_64")] -#[derive(Debug, Clone, Copy, Default)] -#[repr(C)] -pub struct Kstat { - /// 设备 - pub st_dev: u64, - /// inode 编号 - pub st_ino: u64, - /// 硬链接数 - pub st_nlink: u64, - /// 文件类型 - pub st_mode: u32, - /// 用户id - pub st_uid: u32, - /// 用户组id - pub st_gid: u32, - /// padding - pub __pad0: u32, - /// 设备号 - pub st_rdev: u64, - /// 文件大小 - pub st_size: i64, - /// 块大小 - pub st_blksize: i64, - /// 块个数 - pub st_blocks: i64, - /// 最后一次访问时间(秒) - pub st_atime_sec: u64, - /// 最后一次访问时间(纳秒) - pub st_atime_nsec: u64, - /// 最后一次修改时间(秒) - pub st_mtime_sec: u64, - /// 最后一次修改时间(纳秒) - pub st_mtime_nsec: u64, - /// 最后一次改变状态时间(秒) - pub st_ctime_sec: u64, - /// 最后一次改变状态时间(纳秒) - pub st_ctime_nsec: u64, - pub __unused: [i64; 3], -} - -#[cfg(not(target_arch = "x86_64"))] -#[derive(Debug, Clone, Copy, Default)] -#[repr(C)] -pub struct Kstat { - /// 设备 - pub st_dev: u64, - /// inode 编号 - pub st_ino: u64, - /// 文件类型 - pub st_mode: u32, - /// 硬链接数 - pub st_nlink: u32, - /// 用户id - pub st_uid: u32, - /// 用户组id - pub st_gid: u32, - /// 设备号 - pub st_rdev: u64, - /// padding - pub __pad1: u64, - /// 文件大小 - pub st_size: i64, - /// 块大小 - pub st_blksize: i32, - /// padding - pub __pad2: i32, - /// 块个数 - pub st_blocks: i64, - /// 最后一次访问时间(秒) - pub st_atime_sec: i64, - /// 最后一次访问时间(纳秒) - pub st_atime_nsec: u64, - /// 最后一次修改时间(秒) - pub st_mtime_sec: i64, - /// 最后一次修改时间(纳秒) - pub st_mtime_nsec: u64, - /// 最后一次改变状态时间(秒) - pub st_ctime_sec: i64, - /// 最后一次改变状态时间(纳秒) - pub st_ctime_nsec: u64, - pub __unused4: u32, - pub __unused5: u32, -} - -#[cfg(target_arch = "x86_64")] -impl From for Kstat { - fn from(stat: arceos_posix_api::ctypes::stat) -> Self { - Self { - st_dev: stat.st_dev, - st_ino: stat.st_ino, - st_nlink: stat.st_nlink as u64, - st_mode: stat.st_mode, - st_uid: stat.st_uid, - st_gid: stat.st_gid, - __pad0: 0_u32, - st_rdev: stat.st_rdev, - st_size: stat.st_size, - st_blksize: stat.st_blksize, - st_blocks: stat.st_blocks, - st_atime_sec: stat.st_atime.tv_sec as u64, - st_atime_nsec: stat.st_atime.tv_nsec as u64, - st_mtime_sec: stat.st_mtime.tv_sec as u64, - st_mtime_nsec: stat.st_mtime.tv_nsec as u64, - st_ctime_sec: stat.st_ctime.tv_sec as u64, - st_ctime_nsec: stat.st_ctime.tv_nsec as u64, - __unused: [0_i64; 3], +fn stat_at_path(path: &str) -> LinuxResult { + let opts = OpenOptions::new().set_read(true); + match axfs::fops::File::open(path, &opts) { + Ok(file) => File::new(file, path.into()).stat(), + Err(AxError::IsADirectory) => { + let dir = axfs::fops::Directory::open_dir(path, &opts)?; + Directory::new(dir, path.into()).stat() } + Err(e) => Err(e.into()), } } -#[cfg(not(target_arch = "x86_64"))] -impl From for Kstat { - fn from(stat: arceos_posix_api::ctypes::stat) -> Self { - Self { - st_dev: stat.st_dev, - st_ino: stat.st_ino, - st_mode: stat.st_mode, - st_nlink: stat.st_nlink, - st_uid: stat.st_uid, - st_gid: stat.st_gid, - st_rdev: stat.st_rdev, - __pad1: 0_u64, - st_size: stat.st_size, - st_blksize: stat.st_blksize as i32, - __pad2: 0_i32, - st_blocks: stat.st_blocks, - st_atime_sec: stat.st_atime.tv_sec, - st_atime_nsec: stat.st_atime.tv_nsec as u64, - st_mtime_sec: stat.st_mtime.tv_sec, - st_mtime_nsec: stat.st_mtime.tv_nsec as u64, - st_ctime_sec: stat.st_ctime.tv_sec, - st_ctime_nsec: stat.st_ctime.tv_nsec as u64, - __unused4: 0_u32, - __unused5: 0_u32, - } - } -} +/// Get the file metadata by `path` and write into `statbuf`. +/// +/// Return 0 if success. +pub fn sys_stat(path: UserConstPtr, statbuf: UserPtr) -> LinuxResult { + let path = path.get_as_str()?; + debug!("sys_stat <= path: {}", path); -pub fn sys_fstat(fd: i32, kstatbuf: UserPtr) -> LinuxResult { - let kstatbuf = kstatbuf.get()?; - let mut statbuf = arceos_posix_api::ctypes::stat::default(); + *statbuf.get_as_mut()? = stat_at_path(path)?.into(); - let result = unsafe { - arceos_posix_api::sys_fstat(fd, &mut statbuf as *mut arceos_posix_api::ctypes::stat) - }; - if result < 0 { - return Ok(result as _); - } + Ok(0) +} - unsafe { - let kstat = Kstat::from(statbuf); - kstatbuf.write(kstat); - } +/// Get file metadata by `fd` and write into `statbuf`. +/// +/// Return 0 if success. +pub fn sys_fstat(fd: i32, statbuf: UserPtr) -> LinuxResult { + debug!("sys_fstat <= fd: {}", fd); + *statbuf.get_as_mut()? = get_file_like(fd)?.stat()?.into(); Ok(0) } -#[apply(syscall_instrument)] +/// Get the metadata of the symbolic link and write into `buf`. +/// +/// Return 0 if success. +pub fn sys_lstat(path: UserConstPtr, statbuf: UserPtr) -> LinuxResult { + // TODO: symlink + sys_stat(path, statbuf) +} + pub fn sys_fstatat( - dir_fd: isize, + dirfd: c_int, path: UserConstPtr, - kstatbuf: UserPtr, - _flags: i32, + statbuf: UserPtr, + flags: u32, ) -> LinuxResult { - let path = path.get_as_null_terminated()?; - let path = arceos_posix_api::handle_file_path(dir_fd, Some(path.as_ptr() as _), false)?; - - let kstatbuf = kstatbuf.get()?; - - let mut statbuf = arceos_posix_api::ctypes::stat::default(); - let result = unsafe { - arceos_posix_api::sys_stat( - path.as_ptr() as _, - &mut statbuf as *mut arceos_posix_api::ctypes::stat, - ) + let path = nullable!(path.get_as_str())?; + debug!( + "sys_fstatat <= dirfd: {}, path: {:?}, flags: {}", + dirfd, path, flags + ); + + *statbuf.get_as_mut()? = if path.is_none_or(|s| s.is_empty()) { + if (flags & AT_EMPTY_PATH) == 0 { + return Err(LinuxError::ENOENT); + } + let f = get_file_like(dirfd)?; + f.stat()?.into() + } else { + let path = handle_file_path(dirfd, path.unwrap_or_default())?; + stat_at_path(path.as_str())?.into() }; - if result < 0 { - return Ok(result as _); - } - - unsafe { - let kstat = Kstat::from(statbuf); - kstatbuf.write(kstat); - } Ok(0) } -#[repr(C)] -#[derive(Debug, Default)] -pub struct FsStatxTimestamp { - pub tv_sec: i64, - pub tv_nsec: u32, -} - -/// statx - get file status (extended) -/// Standard C library (libc, -lc) -/// -#[repr(C)] -#[derive(Debug, Default)] -pub struct StatX { - /// Bitmask of what information to get. - pub stx_mask: u32, - /// Block size for filesystem I/O. - pub stx_blksize: u32, - /// File attributes. - pub stx_attributes: u64, - /// Number of hard links. - pub stx_nlink: u32, - /// User ID of owner. - pub stx_uid: u32, - /// Group ID of owner. - pub stx_gid: u32, - /// File mode (permissions). - pub stx_mode: u16, - /// Inode number. - pub stx_ino: u64, - /// Total size, in bytes. - pub stx_size: u64, - /// Number of 512B blocks allocated. - pub stx_blocks: u64, - /// Mask to show what's supported in stx_attributes. - pub stx_attributes_mask: u64, - /// Last access timestamp. - pub stx_atime: FsStatxTimestamp, - /// Birth (creation) timestamp. - pub stx_btime: FsStatxTimestamp, - /// Last status change timestamp. - pub stx_ctime: FsStatxTimestamp, - /// Last modification timestamp. - pub stx_mtime: FsStatxTimestamp, - /// Major device ID (if special file). - pub stx_rdev_major: u32, - /// Minor device ID (if special file). - pub stx_rdev_minor: u32, - /// Major device ID of file system. - pub stx_dev_major: u32, - /// Minor device ID of file system. - pub stx_dev_minor: u32, - /// Mount ID. - pub stx_mnt_id: u64, - /// Memory alignment for direct I/O. - pub stx_dio_mem_align: u32, - /// Offset alignment for direct I/O. - pub stx_dio_offset_align: u32, -} - -#[apply(syscall_instrument)] pub fn sys_statx( - dirfd: i32, - pathname: UserConstPtr, + dirfd: c_int, + path: UserConstPtr, flags: u32, _mask: u32, - statxbuf: UserPtr, + statxbuf: UserPtr, ) -> LinuxResult { // `statx()` uses pathname, dirfd, and flags to identify the target // file in one of the following ways: @@ -290,38 +111,22 @@ pub fn sys_statx( // below), then the target file is the one referred to by the // file descriptor dirfd. - let path = pathname.get_as_str()?; + let path = nullable!(path.get_as_str())?; + debug!( + "sys_statx <= dirfd: {}, path: {:?}, flags: {}", + dirfd, path, flags + ); - const AT_EMPTY_PATH: u32 = 0x1000; - if path.is_empty() { - if flags & AT_EMPTY_PATH == 0 { - return Err(LinuxError::EINVAL); - } - // Alloc a new space for stat struct - let mut status = arceos_posix_api::ctypes::stat::default(); - let res = unsafe { arceos_posix_api::sys_fstat(dirfd, &mut status as *mut _) }; - if res < 0 { - return Err(LinuxError::try_from(-res).unwrap()); + *statxbuf.get_as_mut()? = if path.is_none_or(|s| s.is_empty()) { + if (flags & AT_EMPTY_PATH) == 0 { + return Err(LinuxError::ENOENT); } - let statx = unsafe { &mut *statxbuf.get()? }; - statx.stx_blksize = status.st_blksize as u32; - statx.stx_attributes = status.st_mode as u64; - statx.stx_nlink = status.st_nlink; - statx.stx_uid = status.st_uid; - statx.stx_gid = status.st_gid; - statx.stx_mode = status.st_mode as u16; - statx.stx_ino = status.st_ino; - statx.stx_size = status.st_size as u64; - statx.stx_blocks = status.st_blocks as u64; - statx.stx_attributes_mask = 0x7FF; - statx.stx_atime.tv_sec = status.st_atime.tv_sec; - statx.stx_atime.tv_nsec = status.st_atime.tv_nsec as u32; - statx.stx_ctime.tv_sec = status.st_ctime.tv_sec; - statx.stx_ctime.tv_nsec = status.st_ctime.tv_nsec as u32; - statx.stx_mtime.tv_sec = status.st_mtime.tv_sec; - statx.stx_mtime.tv_nsec = status.st_mtime.tv_nsec as u32; - Ok(0) + let f = get_file_like(dirfd)?; + f.stat()?.into() } else { - Err(LinuxError::ENOSYS) - } + let path = handle_file_path(dirfd, path.unwrap_or_default())?; + stat_at_path(path.as_str())?.into() + }; + + Ok(0) } diff --git a/api/src/imp/futex.rs b/api/src/imp/futex.rs index 29e401953..3ee5216c0 100644 --- a/api/src/imp/futex.rs +++ b/api/src/imp/futex.rs @@ -1,11 +1,13 @@ -use arceos_posix_api::ctypes::timespec; use axerrno::{LinuxError, LinuxResult}; use axtask::{TaskExtRef, current}; use linux_raw_sys::general::{ - FUTEX_CMD_MASK, FUTEX_CMP_REQUEUE, FUTEX_REQUEUE, FUTEX_WAIT, FUTEX_WAKE, + FUTEX_CMD_MASK, FUTEX_CMP_REQUEUE, FUTEX_REQUEUE, FUTEX_WAIT, FUTEX_WAKE, timespec, }; -use crate::ptr::{PtrWrapper, UserConstPtr, UserPtr}; +use crate::{ + ptr::{UserConstPtr, UserPtr, nullable}, + time::TimeValueLike, +}; pub fn sys_futex( uaddr: UserConstPtr, @@ -24,13 +26,13 @@ pub fn sys_futex( let command = futex_op & (FUTEX_CMD_MASK as u32); match command { FUTEX_WAIT => { - if unsafe { uaddr.get()?.read() } != value { + if *uaddr.get_as_ref()? != value { return Err(LinuxError::EAGAIN); } let wq = futex_table.lock().get_or_insert(addr); - if let Some(timeout) = timeout.nullable(UserConstPtr::get)? { - wq.wait_timeout(unsafe { *timeout }.into()); + if let Some(timeout) = nullable!(timeout.get_as_ref())? { + wq.wait_timeout(timeout.to_time_value()); } else { wq.wait(); } @@ -52,7 +54,7 @@ pub fn sys_futex( Ok(count) } FUTEX_REQUEUE | FUTEX_CMP_REQUEUE => { - if command == FUTEX_CMP_REQUEUE && unsafe { uaddr.get()?.read() } != value3 { + if command == FUTEX_CMP_REQUEUE && *uaddr.get_as_ref()? != value3 { return Err(LinuxError::EAGAIN); } let value2 = timeout.address().as_usize() as u32; diff --git a/api/src/imp/mm/brk.rs b/api/src/imp/mm/brk.rs index cbf25b82d..1276d9f6e 100644 --- a/api/src/imp/mm/brk.rs +++ b/api/src/imp/mm/brk.rs @@ -1,10 +1,6 @@ use axerrno::LinuxResult; use axtask::{TaskExtRef, current}; -use macro_rules_attribute::apply; -use crate::syscall_instrument; - -#[apply(syscall_instrument)] pub fn sys_brk(addr: usize) -> LinuxResult { let task = current(); let process_data = task.task_ext().process_data(); diff --git a/api/src/imp/mm/mmap.rs b/api/src/imp/mm/mmap.rs index 1e9ad7ddd..57958edb3 100644 --- a/api/src/imp/mm/mmap.rs +++ b/api/src/imp/mm/mmap.rs @@ -2,43 +2,43 @@ use alloc::vec; use axerrno::{LinuxError, LinuxResult}; use axhal::paging::MappingFlags; use axtask::{TaskExtRef, current}; -use macro_rules_attribute::apply; +use linux_raw_sys::general::{ + MAP_ANONYMOUS, MAP_FIXED, MAP_NORESERVE, MAP_PRIVATE, MAP_SHARED, MAP_STACK, PROT_EXEC, + PROT_GROWSDOWN, PROT_GROWSUP, PROT_READ, PROT_WRITE, +}; use memory_addr::{VirtAddr, VirtAddrRange}; -use crate::{ - ptr::{PtrWrapper, UserPtr}, - syscall_instrument, -}; +use crate::file::{File, FileLike}; bitflags::bitflags! { - /// permissions for sys_mmap + /// `PROT_*` flags for use with [`sys_mmap`]. /// - /// See + /// For `PROT_NONE`, use `ProtFlags::empty()`. #[derive(Debug)] - struct MmapProt: i32 { + struct MmapProt: u32 { /// Page can be read. - const PROT_READ = 1 << 0; + const READ = PROT_READ; /// Page can be written. - const PROT_WRITE = 1 << 1; + const WRITE = PROT_WRITE; /// Page can be executed. - const PROT_EXEC = 1 << 2; + const EXEC = PROT_EXEC; /// Extend change to start of growsdown vma (mprotect only). - const PROT_GROWDOWN = 0x01000000; + const GROWDOWN = PROT_GROWSDOWN; /// Extend change to start of growsup vma (mprotect only). - const PROT_GROWSUP = 0x02000000; + const GROWSUP = PROT_GROWSUP; } } impl From for MappingFlags { fn from(value: MmapProt) -> Self { let mut flags = MappingFlags::USER; - if value.contains(MmapProt::PROT_READ) { + if value.contains(MmapProt::READ) { flags |= MappingFlags::READ; } - if value.contains(MmapProt::PROT_WRITE) { + if value.contains(MmapProt::WRITE) { flags |= MappingFlags::WRITE; } - if value.contains(MmapProt::PROT_EXEC) { + if value.contains(MmapProt::EXEC) { flags |= MappingFlags::EXECUTE; } flags @@ -50,34 +50,30 @@ bitflags::bitflags! { /// /// See #[derive(Debug)] - struct MmapFlags: i32 { + struct MmapFlags: u32 { /// Share changes - const MAP_SHARED = 1 << 0; + const SHARED = MAP_SHARED; /// Changes private; copy pages on write. - const MAP_PRIVATE = 1 << 1; + const PRIVATE = MAP_PRIVATE; /// Map address must be exactly as requested, no matter whether it is available. - const MAP_FIXED = 1 << 4; + const FIXED = MAP_FIXED; /// Don't use a file. - const MAP_ANONYMOUS = 1 << 5; + const ANONYMOUS = MAP_ANONYMOUS; /// Don't check for reservations. - const MAP_NORESERVE = 1 << 14; + const NORESERVE = MAP_NORESERVE; /// Allocation is for a stack. - const MAP_STACK = 0x20000; + const STACK = MAP_STACK; } } -#[apply(syscall_instrument)] pub fn sys_mmap( - addr: UserPtr, + addr: usize, length: usize, - prot: i32, - flags: i32, + prot: u32, + flags: u32, fd: i32, offset: isize, ) -> LinuxResult { - // Safety: addr is used for mapping, and we won't directly access it. - let mut addr = unsafe { addr.into_inner() }; - let curr = current(); let process_data = curr.task_ext().process_data(); let mut aspace = process_data.aspace.lock(); @@ -85,34 +81,31 @@ pub fn sys_mmap( // TODO: check illegal flags for mmap // An example is the flags contained none of MAP_PRIVATE, MAP_SHARED, or MAP_SHARED_VALIDATE. let map_flags = MmapFlags::from_bits_truncate(flags); - let mut aligned_length = length; - - if addr.is_null() { - aligned_length = memory_addr::align_up_4k(aligned_length); - } else { - let start = addr as usize; - let mut end = start + aligned_length; - addr = memory_addr::align_down_4k(start) as *mut usize; - end = memory_addr::align_up_4k(end); - aligned_length = end - start; - } info!( - "mmap: addr: {:?}, length: {:x?}, prot: {:?}, flags: {:?}, fd: {:?}, offset: {:?}", + "sys_mmap: addr: {:x?}, length: {:x?}, prot: {:?}, flags: {:?}, fd: {:?}, offset: {:?}", addr, length, permission_flags, map_flags, fd, offset ); - let start_addr = if map_flags.contains(MmapFlags::MAP_FIXED) { - if addr.is_null() { + let start = memory_addr::align_down_4k(addr); + let end = memory_addr::align_up_4k(addr + length); + let aligned_length = end - start; + debug!( + "start: {:x?}, end: {:x?}, aligned_length: {:x?}", + start, end, aligned_length + ); + + let start_addr = if map_flags.contains(MmapFlags::FIXED) { + if start == 0 { return Err(LinuxError::EINVAL); } - let dst_addr = VirtAddr::from(addr as usize); + let dst_addr = VirtAddr::from(start); aspace.unmap(dst_addr, aligned_length)?; dst_addr } else { aspace .find_free_area( - VirtAddr::from(addr as usize), + VirtAddr::from(start), aligned_length, VirtAddrRange::new(aspace.base(), aspace.end()), ) @@ -127,7 +120,7 @@ pub fn sys_mmap( let populate = if fd == -1 { false } else { - !map_flags.contains(MmapFlags::MAP_ANONYMOUS) + !map_flags.contains(MmapFlags::ANONYMOUS) }; aspace.map_alloc( @@ -138,13 +131,9 @@ pub fn sys_mmap( )?; if populate { - let file = arceos_posix_api::get_file_like(fd)?; - let file_size = file.stat()?.st_size as usize; - let file = file - .into_any() - .downcast::() - .map_err(|_| LinuxError::EBADF)?; - let file = file.inner().lock(); + let file = File::from_fd(fd)?; + let file = file.inner(); + let file_size = file.get_attr()?.size() as usize; if offset < 0 || offset as usize >= file_size { return Err(LinuxError::EINVAL); } @@ -157,31 +146,23 @@ pub fn sys_mmap( Ok(start_addr.as_usize() as _) } -#[apply(syscall_instrument)] -pub fn sys_munmap(addr: UserPtr, length: usize) -> LinuxResult { - // Safety: addr is used for mapping, and we won't directly access it. - let addr = unsafe { addr.into_inner() }; - +pub fn sys_munmap(addr: usize, length: usize) -> LinuxResult { let curr = current(); let process_data = curr.task_ext().process_data(); let mut aspace = process_data.aspace.lock(); let length = memory_addr::align_up_4k(length); - let start_addr = VirtAddr::from(addr as usize); + let start_addr = VirtAddr::from(addr); aspace.unmap(start_addr, length)?; axhal::arch::flush_tlb(None); Ok(0) } -#[apply(syscall_instrument)] -pub fn sys_mprotect(addr: UserPtr, length: usize, prot: i32) -> LinuxResult { - // Safety: addr is used for mapping, and we won't directly access it. - let addr = unsafe { addr.into_inner() }; - +pub fn sys_mprotect(addr: usize, length: usize, prot: u32) -> LinuxResult { // TODO: implement PROT_GROWSUP & PROT_GROWSDOWN let Some(permission_flags) = MmapProt::from_bits(prot) else { return Err(LinuxError::EINVAL); }; - if permission_flags.contains(MmapProt::PROT_GROWDOWN | MmapProt::PROT_GROWSUP) { + if permission_flags.contains(MmapProt::GROWDOWN | MmapProt::GROWSUP) { return Err(LinuxError::EINVAL); } @@ -189,7 +170,7 @@ pub fn sys_mprotect(addr: UserPtr, length: usize, prot: i32) -> LinuxResu let process_data = curr.task_ext().process_data(); let mut aspace = process_data.aspace.lock(); let length = memory_addr::align_up_4k(length); - let start_addr = VirtAddr::from(addr as usize); + let start_addr = VirtAddr::from(addr); aspace.protect(start_addr, length, permission_flags.into())?; Ok(0) diff --git a/api/src/imp/mod.rs b/api/src/imp/mod.rs index 93ed237d6..b4de588fa 100644 --- a/api/src/imp/mod.rs +++ b/api/src/imp/mod.rs @@ -4,6 +4,6 @@ mod mm; mod signal; mod sys; mod task; -mod utils; +mod time; -pub use self::{fs::*, futex::*, mm::*, signal::*, sys::*, task::*, utils::*}; +pub use self::{fs::*, futex::*, mm::*, signal::*, sys::*, task::*, time::*}; diff --git a/api/src/imp/signal.rs b/api/src/imp/signal.rs index d87c4b699..642294bfb 100644 --- a/api/src/imp/signal.rs +++ b/api/src/imp/signal.rs @@ -1,68 +1,22 @@ use core::{mem, time::Duration}; use alloc::sync::Arc; -use arceos_posix_api::ctypes::timespec; use axerrno::{LinuxError, LinuxResult}; -use axprocess::{Pid, Process, ProcessGroup, Thread}; +use axhal::arch::TrapFrame; +use axprocess::{Pid, Thread}; +use axsignal::{SignalInfo, SignalSet, SignalStack, Signo}; +use axtask::{TaskExtRef, current}; use linux_raw_sys::general::{ MINSIGSTKSZ, SI_TKILL, SI_USER, SIG_BLOCK, SIG_SETMASK, SIG_UNBLOCK, kernel_sigaction, siginfo, + timespec, }; -use starry_core::task::{ - ProcessData, ThreadData, get_process, get_process_group, get_thread, processes, -}; - -use crate::ptr::{PtrWrapper, UserConstPtr, UserPtr}; +use starry_core::task::{get_process, get_process_group, get_thread, processes}; -use axhal::{ - arch::TrapFrame, - trap::{POST_TRAP, register_trap_handler}, +use crate::{ + ptr::{UserConstPtr, UserPtr, nullable}, + signal::{check_signals, send_signal_process, send_signal_process_group, send_signal_thread}, + time::TimeValueLike, }; -use axsignal::{SignalInfo, SignalOSAction, SignalSet, SignalStack, Signo}; -use axtask::{TaskExtRef, current}; - -use super::do_exit; - -fn check_signals(tf: &mut TrapFrame, restore_blocked: Option) -> bool { - let Some((sig, os_action)) = current() - .task_ext() - .thread_data() - .signal - .check_signals(tf, restore_blocked) - else { - return false; - }; - - let signo = sig.signo(); - match os_action { - SignalOSAction::Terminate => { - do_exit(128 + signo as i32, true); - } - SignalOSAction::CoreDump => { - // TODO: implement core dump - do_exit(128 + signo as i32, true); - } - SignalOSAction::Stop => { - // TODO: implement stop - do_exit(1, true); - } - SignalOSAction::Continue => { - // TODO: implement continue - } - SignalOSAction::Handler => { - // do nothing - } - } - true -} - -#[register_trap_handler(POST_TRAP)] -fn post_trap_callback(tf: &mut TrapFrame, from_user: bool) { - if !from_user { - return; - } - - check_signals(tf, None); -} fn check_sigset_size(size: usize) -> LinuxResult<()> { if size != size_of::() { @@ -88,16 +42,15 @@ pub fn sys_rt_sigprocmask( .thread_data() .signal .with_blocked_mut::>(|blocked| { - if let Some(oldset) = oldset.nullable(UserPtr::get)? { - unsafe { *oldset = *blocked }; + if let Some(oldset) = nullable!(oldset.get_as_mut())? { + *oldset = *blocked; } - if let Some(set) = set.nullable(UserConstPtr::get)? { - let set = unsafe { *set }; + if let Some(set) = nullable!(set.get_as_ref())? { match how as u32 { - SIG_BLOCK => *blocked |= set, - SIG_UNBLOCK => *blocked &= !set, - SIG_SETMASK => *blocked = set, + SIG_BLOCK => *blocked |= *set, + SIG_UNBLOCK => *blocked &= !*set, + SIG_SETMASK => *blocked = *set, _ => return Err(LinuxError::EINVAL), } } @@ -122,52 +75,21 @@ pub fn sys_rt_sigaction( let curr = current(); let mut actions = curr.task_ext().process_data().signal.actions.lock(); - if let Some(oldact) = oldact.nullable(UserPtr::get)? { - actions[signo].to_ctype(unsafe { &mut *oldact }); + if let Some(oldact) = nullable!(oldact.get_as_mut())? { + actions[signo].to_ctype(oldact); } - if let Some(act) = act.nullable(UserConstPtr::get)? { - actions[signo] = unsafe { (*act).try_into()? }; + if let Some(act) = nullable!(act.get_as_ref())? { + actions[signo] = (*act).try_into()?; } Ok(0) } pub fn sys_rt_sigpending(set: UserPtr, sigsetsize: usize) -> LinuxResult { check_sigset_size(sigsetsize)?; - unsafe { - *set.get()? = current().task_ext().thread_data().signal.pending(); - } + *set.get_as_mut()? = current().task_ext().thread_data().signal.pending(); Ok(0) } -pub fn send_signal_thread(thr: &Thread, sig: SignalInfo) -> LinuxResult<()> { - info!("Send signal {:?} to thread {}", sig.signo(), thr.tid()); - let Some(thr) = thr.data::() else { - return Err(LinuxError::EPERM); - }; - thr.signal.send_signal(sig); - Ok(()) -} -pub fn send_signal_process(proc: &Process, sig: SignalInfo) -> LinuxResult<()> { - info!("Send signal {:?} to process {}", sig.signo(), proc.pid()); - let Some(proc) = proc.data::() else { - return Err(LinuxError::EPERM); - }; - proc.signal.send_signal(sig); - Ok(()) -} -pub fn send_signal_process_group(pg: &ProcessGroup, sig: SignalInfo) -> usize { - info!( - "Send signal {:?} to process group {}", - sig.signo(), - pg.pgid() - ); - let mut count = 0; - for proc in pg.processes() { - count += send_signal_process(&proc, sig.clone()).is_ok() as usize; - } - count -} - fn make_siginfo(signo: u32, code: i32) -> LinuxResult> { if signo == 0 { return Ok(None); @@ -247,7 +169,7 @@ fn make_queue_signal_info( sig: UserConstPtr, ) -> LinuxResult { let signo = parse_signo(signo)?; - let mut sig = unsafe { sig.get()?.read() }; + let mut sig = sig.get_as_ref()?.clone(); sig.set_signo(signo); if current().task_ext().thread.process().pid() != tgid && (sig.code() >= 0 || sig.code() == SI_TKILL) @@ -298,10 +220,8 @@ pub fn sys_rt_sigtimedwait( ) -> LinuxResult { check_sigset_size(sigsetsize)?; - let set = unsafe { *set.get()? }; - let timeout: Option = timeout - .nullable(UserConstPtr::get)? - .map(|ts| unsafe { *ts }.into()); + let set = *set.get_as_ref()?; + let timeout: Option = nullable!(timeout.get_as_ref())?.map(|ts| ts.to_time_value()); let Some(sig) = current() .task_ext() @@ -312,8 +232,8 @@ pub fn sys_rt_sigtimedwait( return Err(LinuxError::EAGAIN); }; - if let Some(info) = info.nullable(UserPtr::get)? { - unsafe { *info = sig.0 }; + if let Some(info) = nullable!(info.get_as_mut())? { + *info = sig.0; } Ok(0) @@ -321,14 +241,14 @@ pub fn sys_rt_sigtimedwait( pub fn sys_rt_sigsuspend( tf: &mut TrapFrame, - set: UserPtr, + set: UserConstPtr, sigsetsize: usize, ) -> LinuxResult { check_sigset_size(sigsetsize)?; let curr = current(); let thr_data = curr.task_ext().thread_data(); - let mut set = unsafe { *set.get()? }; + let mut set = *set.get_as_ref()?; set.remove(Signo::SIGKILL); set.remove(Signo::SIGSTOP); @@ -358,16 +278,15 @@ pub fn sys_sigaltstack( .thread_data() .signal .with_stack_mut(|stack| { - if let Some(old_ss) = old_ss.nullable(UserPtr::get)? { - unsafe { *old_ss = stack.clone() }; + if let Some(old_ss) = nullable!(old_ss.get_as_mut())? { + *old_ss = stack.clone(); } - if let Some(ss) = ss.nullable(UserConstPtr::get)? { - let ss = unsafe { ss.read() }; + if let Some(ss) = nullable!(ss.get_as_ref())? { if ss.size <= MINSIGSTKSZ as usize { return Err(LinuxError::ENOMEM); } let stack_ptr: UserConstPtr = ss.sp.into(); - let _ = stack_ptr.get_as_array(ss.size)?; + let _ = stack_ptr.get_as_slice(ss.size)?; *stack = ss.clone(); } diff --git a/api/src/imp/sys.rs b/api/src/imp/sys.rs index ccfc15661..827a4bd94 100644 --- a/api/src/imp/sys.rs +++ b/api/src/imp/sys.rs @@ -1,49 +1,46 @@ +use core::ffi::c_char; + use axerrno::LinuxResult; +use linux_raw_sys::system::new_utsname; -use crate::ptr::{PtrWrapper, UserPtr}; +use crate::ptr::UserPtr; pub fn sys_getuid() -> LinuxResult { Ok(0) } -#[repr(C)] -pub struct UtsName { - /// sysname - pub sysname: [u8; 65], - /// nodename - pub nodename: [u8; 65], - /// release - pub release: [u8; 65], - /// version - pub version: [u8; 65], - /// machine - pub machine: [u8; 65], - /// domainname - pub domainname: [u8; 65], +pub fn sys_geteuid() -> LinuxResult { + Ok(1) } -impl Default for UtsName { - fn default() -> Self { - Self { - sysname: Self::from_str("Starry"), - nodename: Self::from_str("Starry - machine[0]"), - release: Self::from_str("10.0.0"), - version: Self::from_str("10.0.0"), - machine: Self::from_str("10.0.0"), - domainname: Self::from_str("https://github.com/BattiestStone4/Starry-On-ArceOS"), - } - } +pub fn sys_getgid() -> LinuxResult { + Ok(0) } -impl UtsName { - fn from_str(info: &str) -> [u8; 65] { - let mut data: [u8; 65] = [0; 65]; - data[..info.len()].copy_from_slice(info.as_bytes()); - data +pub fn sys_getegid() -> LinuxResult { + Ok(1) +} + +const fn pad_str(info: &str) -> [c_char; 65] { + let mut data: [c_char; 65] = [0; 65]; + // this needs #![feature(const_copy_from_slice)] + // data[..info.len()].copy_from_slice(info.as_bytes()); + unsafe { + core::ptr::copy_nonoverlapping(info.as_ptr().cast(), data.as_mut_ptr(), info.len()); } + data } -pub fn sys_uname(name: UserPtr) -> LinuxResult { - unsafe { *name.get()? = UtsName::default() }; +const UTSNAME: new_utsname = new_utsname { + sysname: pad_str("Starry"), + nodename: pad_str("Starry - machine[0]"), + release: pad_str("10.0.0"), + version: pad_str("10.0.0"), + machine: pad_str("10.0.0"), + domainname: pad_str("https://github.com/oscomp/starry-next"), +}; + +pub fn sys_uname(name: UserPtr) -> LinuxResult { + *name.get_as_mut()? = UTSNAME; Ok(0) } diff --git a/api/src/imp/task/clone.rs b/api/src/imp/task/clone.rs index 22c782c82..ea848d3a4 100644 --- a/api/src/imp/task/clone.rs +++ b/api/src/imp/task/clone.rs @@ -1,5 +1,4 @@ use alloc::sync::Arc; -use arceos_posix_api::FD_TABLE; use axerrno::{LinuxError, LinuxResult}; use axfs::{CURRENT_DIR, CURRENT_DIR_PATH}; use axhal::arch::{TrapFrame, UspaceContext}; @@ -14,7 +13,7 @@ use starry_core::{ task::{ProcessData, TaskExt, ThreadData, add_thread_to_table, new_user_task}, }; -use crate::ptr::{PtrWrapper, UserPtr}; +use crate::{file::FD_TABLE, ptr::UserPtr}; bitflags! { /// Options for use with [`sys_clone`]. @@ -115,7 +114,7 @@ pub fn sys_clone( new_uctx.set_retval(0); let set_child_tid = if flags.contains(CloneFlags::CHILD_SETTID) { - unsafe { UserPtr::::from(child_tid).get()?.as_mut() } + Some(UserPtr::::from(child_tid).get_as_mut()?) } else { None }; @@ -125,7 +124,7 @@ pub fn sys_clone( let tid = new_task.id().as_u64() as Pid; if flags.contains(CloneFlags::PARENT_SETTID) { - unsafe { UserPtr::::from(parent_tid).get()?.write(tid) }; + *UserPtr::::from(parent_tid).get_as_mut()? = tid; } let process = if flags.contains(CloneFlags::THREAD) { diff --git a/api/src/imp/task/execve.rs b/api/src/imp/task/execve.rs index ac9cb2436..37a201611 100644 --- a/api/src/imp/task/execve.rs +++ b/api/src/imp/task/execve.rs @@ -4,36 +4,26 @@ use alloc::{string::ToString, vec::Vec}; use axerrno::{LinuxError, LinuxResult}; use axhal::arch::UspaceContext; use axtask::{TaskExtRef, current}; -use macro_rules_attribute::apply; use starry_core::mm::{load_user_app, map_trampoline}; -use crate::{ptr::UserConstPtr, syscall_instrument}; +use crate::ptr::UserConstPtr; -#[apply(syscall_instrument)] pub fn sys_execve( path: UserConstPtr, - argv: UserConstPtr, - envp: UserConstPtr, + argv: UserConstPtr>, + envp: UserConstPtr>, ) -> LinuxResult { let path = path.get_as_str()?.to_string(); let args = argv .get_as_null_terminated()? .iter() - .map(|arg| { - UserConstPtr::::from(*arg) - .get_as_str() - .map(|s| s.to_string()) - }) + .map(|arg| arg.get_as_str().map(Into::into)) .collect::, _>>()?; let envs = envp .get_as_null_terminated()? .iter() - .map(|env| { - UserConstPtr::::from(*env) - .get_as_str() - .map(|s| s.to_string()) - }) + .map(|env| env.get_as_str().map(Into::into)) .collect::, _>>()?; info!( diff --git a/api/src/imp/task/exit.rs b/api/src/imp/task/exit.rs index 866323b6a..33c64fbf8 100644 --- a/api/src/imp/task/exit.rs +++ b/api/src/imp/task/exit.rs @@ -5,8 +5,9 @@ use linux_raw_sys::general::SI_KERNEL; use starry_core::task::ProcessData; use crate::{ - ptr::{PtrWrapper, UserPtr}, - send_signal_process, send_signal_thread, + file::FD_TABLE, + ptr::UserPtr, + signal::{send_signal_process, send_signal_thread}, }; pub fn do_exit(exit_code: i32, group_exit: bool) -> ! { @@ -17,8 +18,8 @@ pub fn do_exit(exit_code: i32, group_exit: bool) -> ! { info!("{:?} exit with code: {}", thread, exit_code); let clear_child_tid = UserPtr::::from(curr_ext.thread_data().clear_child_tid()); - if let Ok(clear_tid) = clear_child_tid.get() { - unsafe { clear_tid.write(0) }; + if let Ok(clear_tid) = clear_child_tid.get_as_mut() { + *clear_tid = 0; // Since `guard` (WaitQueueGuard) acquires lock to the futex table on // drop, we need to ensure that the temporary lock's lifetime is @@ -48,6 +49,8 @@ pub fn do_exit(exit_code: i32, group_exit: bool) -> ! { process.exit(); // TODO: clear namespace resources + // FIXME: axns should drop all the resources + FD_TABLE.clear(); } if group_exit && !process.is_group_exited() { process.group_exit(); diff --git a/api/src/imp/task/schedule.rs b/api/src/imp/task/schedule.rs index 692311ed5..0b93e5e8f 100644 --- a/api/src/imp/task/schedule.rs +++ b/api/src/imp/task/schedule.rs @@ -1,15 +1,42 @@ -use arceos_posix_api as api; -use axerrno::LinuxResult; +use axerrno::{LinuxError, LinuxResult}; +use linux_raw_sys::general::timespec; -use crate::ptr::{PtrWrapper, UserConstPtr, UserPtr}; +use crate::{ + ptr::{UserConstPtr, UserPtr, nullable}, + time::TimeValueLike, +}; pub fn sys_sched_yield() -> LinuxResult { - Ok(api::sys_sched_yield() as _) + axtask::yield_now(); + Ok(0) } -pub fn sys_nanosleep( - req: UserConstPtr, - rem: UserPtr, -) -> LinuxResult { - unsafe { Ok(api::sys_nanosleep(req.get()?, rem.get()?) as _) } +/// Sleep some nanoseconds +/// +/// TODO: should be woken by signals, and set errno +pub fn sys_nanosleep(req: UserConstPtr, rem: UserPtr) -> LinuxResult { + let req = req.get_as_ref()?; + + if req.tv_nsec < 0 || req.tv_nsec > 999_999_999 || req.tv_sec < 0 { + return Err(LinuxError::EINVAL); + } + + let dur = req.to_time_value(); + debug!("sys_nanosleep <= {:?}", dur); + + let now = axhal::time::monotonic_time(); + + axtask::sleep(dur); + + let after = axhal::time::monotonic_time(); + let actual = after - now; + + if let Some(diff) = dur.checked_sub(actual) { + if let Some(rem) = nullable!(rem.get_as_mut())? { + *rem = timespec::from_time_value(diff); + } + Err(LinuxError::EINTR) + } else { + Ok(0) + } } diff --git a/api/src/imp/task/thread.rs b/api/src/imp/task/thread.rs index 58b349310..39a9a47bd 100644 --- a/api/src/imp/task/thread.rs +++ b/api/src/imp/task/thread.rs @@ -1,16 +1,11 @@ use axerrno::LinuxResult; use axtask::{TaskExtRef, current}; -use macro_rules_attribute::apply; use num_enum::TryFromPrimitive; -use crate::syscall_instrument; - -#[apply(syscall_instrument)] pub fn sys_getpid() -> LinuxResult { Ok(axtask::current().task_ext().thread.process().pid() as _) } -#[apply(syscall_instrument)] pub fn sys_getppid() -> LinuxResult { Ok(axtask::current() .task_ext() @@ -21,7 +16,6 @@ pub fn sys_getppid() -> LinuxResult { .pid() as _) } -#[apply(syscall_instrument)] pub fn sys_gettid() -> LinuxResult { Ok(axtask::current().id().as_u64() as _) } @@ -50,7 +44,6 @@ enum ArchPrctlCode { /// To set the clear_child_tid field in the task extended data. /// /// The set_tid_address() always succeeds -#[apply(syscall_instrument)] pub fn sys_set_tid_address(clear_child_tid: usize) -> LinuxResult { let curr = current(); curr.task_ext() @@ -60,13 +53,12 @@ pub fn sys_set_tid_address(clear_child_tid: usize) -> LinuxResult { } #[cfg(target_arch = "x86_64")] -#[apply(syscall_instrument)] pub fn sys_arch_prctl( tf: &mut axhal::arch::TrapFrame, code: i32, addr: usize, ) -> LinuxResult { - use crate::ptr::{PtrWrapper, UserPtr}; + use crate::ptr::UserPtr; let code = ArchPrctlCode::try_from(code).map_err(|_| axerrno::LinuxError::EINVAL)?; debug!("sys_arch_prctl: code = {:?}, addr = {:#x}", code, addr); @@ -75,9 +67,7 @@ pub fn sys_arch_prctl( // According to Linux implementation, SetFs & SetGs does not return // error at all ArchPrctlCode::GetFs => { - unsafe { - *UserPtr::from(addr).get()? = tf.tls(); - } + *UserPtr::from(addr).get_as_mut()? = tf.tls(); Ok(0) } ArchPrctlCode::SetFs => { @@ -85,9 +75,8 @@ pub fn sys_arch_prctl( Ok(0) } ArchPrctlCode::GetGs => { - unsafe { - *UserPtr::from(addr).get()? = x86::msr::rdmsr(x86::msr::IA32_KERNEL_GSBASE); - } + *UserPtr::from(addr).get_as_mut()? = + unsafe { x86::msr::rdmsr(x86::msr::IA32_KERNEL_GSBASE) }; Ok(0) } ArchPrctlCode::SetGs => { diff --git a/api/src/imp/task/wait.rs b/api/src/imp/task/wait.rs index 757d7db43..1a368b7c0 100644 --- a/api/src/imp/task/wait.rs +++ b/api/src/imp/task/wait.rs @@ -6,13 +6,9 @@ use bitflags::bitflags; use linux_raw_sys::general::{ __WALL, __WCLONE, __WNOTHREAD, WCONTINUED, WEXITED, WNOHANG, WNOWAIT, WUNTRACED, }; -use macro_rules_attribute::apply; use starry_core::task::ProcessData; -use crate::{ - ptr::{PtrWrapper, UserPtr}, - syscall_instrument, -}; +use crate::ptr::{UserPtr, nullable}; bitflags! { #[derive(Debug)] @@ -59,7 +55,6 @@ impl WaitPid { } } -#[apply(syscall_instrument)] pub fn sys_waitpid(pid: i32, exit_code_ptr: UserPtr, options: u32) -> LinuxResult { let options = WaitOptions::from_bits_truncate(options); info!("sys_waitpid <= pid: {:?}, options: {:?}", pid, options); @@ -92,14 +87,14 @@ pub fn sys_waitpid(pid: i32, exit_code_ptr: UserPtr, options: u32) -> Linux return Err(LinuxError::ECHILD); } - let exit_code = exit_code_ptr.nullable(UserPtr::get)?; + let exit_code = nullable!(exit_code_ptr.get_as_mut())?; loop { if let Some(child) = children.iter().find(|child| child.is_zombie()) { if !options.contains(WaitOptions::WNOWAIT) { child.free(); } if let Some(exit_code) = exit_code { - unsafe { exit_code.write(child.exit_code()) }; + *exit_code = child.exit_code(); } return Ok(child.pid() as _); } else if options.contains(WaitOptions::WNOHANG) { diff --git a/api/src/imp/time.rs b/api/src/imp/time.rs new file mode 100644 index 000000000..6311f8b61 --- /dev/null +++ b/api/src/imp/time.rs @@ -0,0 +1,55 @@ +use axerrno::{LinuxError, LinuxResult}; +use axhal::time::{monotonic_time, monotonic_time_nanos, nanos_to_ticks, wall_time}; +use linux_raw_sys::general::{ + __kernel_clockid_t, CLOCK_MONOTONIC, CLOCK_REALTIME, timespec, timeval, +}; +use starry_core::task::time_stat_output; + +use crate::{ptr::UserPtr, time::TimeValueLike}; + +pub fn sys_clock_gettime( + clock_id: __kernel_clockid_t, + ts: UserPtr, +) -> LinuxResult { + let now = match clock_id as u32 { + CLOCK_REALTIME => wall_time(), + CLOCK_MONOTONIC => monotonic_time(), + _ => { + warn!( + "Called sys_clock_gettime for unsupported clock {}", + clock_id + ); + return Err(LinuxError::EINVAL); + } + }; + *ts.get_as_mut()? = timespec::from_time_value(now); + Ok(0) +} + +pub fn sys_gettimeofday(ts: UserPtr) -> LinuxResult { + *ts.get_as_mut()? = timeval::from_time_value(wall_time()); + Ok(0) +} + +#[repr(C)] +pub struct Tms { + /// user time + tms_utime: usize, + /// system time + tms_stime: usize, + /// user time of children + tms_cutime: usize, + /// system time of children + tms_cstime: usize, +} + +pub fn sys_times(tms: UserPtr) -> LinuxResult { + let (_, utime_us, _, stime_us) = time_stat_output(); + *tms.get_as_mut()? = Tms { + tms_utime: utime_us, + tms_stime: stime_us, + tms_cutime: utime_us, + tms_cstime: stime_us, + }; + Ok(nanos_to_ticks(monotonic_time_nanos()) as _) +} diff --git a/api/src/imp/utils/mod.rs b/api/src/imp/utils/mod.rs deleted file mode 100644 index e8d2509b4..000000000 --- a/api/src/imp/utils/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod time; - -pub use self::time::*; diff --git a/api/src/imp/utils/time.rs b/api/src/imp/utils/time.rs deleted file mode 100644 index db34d73de..000000000 --- a/api/src/imp/utils/time.rs +++ /dev/null @@ -1,39 +0,0 @@ -use arceos_posix_api::{self as api, ctypes::timeval}; -use axerrno::LinuxResult; -use axhal::time::{monotonic_time_nanos, nanos_to_ticks}; -use starry_core::task::time_stat_output; - -use crate::ptr::{PtrWrapper, UserPtr}; - -pub fn sys_clock_gettime(clock_id: i32, tp: UserPtr) -> LinuxResult { - unsafe { Ok(api::sys_clock_gettime(clock_id, tp.get()?) as _) } -} - -pub fn sys_get_time_of_day(ts: UserPtr) -> LinuxResult { - unsafe { Ok(api::sys_get_time_of_day(ts.get()?) as _) } -} - -#[repr(C)] -pub struct Tms { - /// 进程用户态执行时间,单位为us - tms_utime: usize, - /// 进程内核态执行时间,单位为us - tms_stime: usize, - /// 子进程用户态执行时间和,单位为us - tms_cutime: usize, - /// 子进程内核态执行时间和,单位为us - tms_cstime: usize, -} - -pub fn sys_times(tms: UserPtr) -> LinuxResult { - let (_, utime_us, _, stime_us) = time_stat_output(); - unsafe { - *tms.get()? = Tms { - tms_utime: utime_us, - tms_stime: stime_us, - tms_cutime: utime_us, - tms_cstime: stime_us, - } - } - Ok(nanos_to_ticks(monotonic_time_nanos()) as _) -} diff --git a/api/src/lib.rs b/api/src/lib.rs index 3f34a7566..92f8ef5e0 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -5,39 +5,12 @@ extern crate axlog; extern crate alloc; -mod imp; -mod ptr; +pub mod file; +pub mod path; +pub mod ptr; +pub mod signal; +pub mod sockaddr; +pub mod time; +mod imp; pub use imp::*; - -macro_rules! syscall_instrument {( - $( #[$attr:meta] )* - $pub:vis - fn $fname:ident ( - $( $arg_name:ident : $ArgTy:ty ),* $(,)? - ) -> $RetTy:ty - $body:block -) => ( - $( #[$attr] )* - #[allow(unused_parens)] - $pub - fn $fname ( - $( $arg_name : $ArgTy ),* - ) -> $RetTy - { - /// Re-emit the original function definition, but as a scoped helper - $( #[$attr] )* - fn __original_func__ ( - $($arg_name: $ArgTy),* - ) -> $RetTy - $body - - let res = __original_func__($($arg_name),*); - match res { - Ok(_) | Err(axerrno::LinuxError::EAGAIN) => debug!(concat!(stringify!($fname), " => {:?}"), res), - Err(_) => info!(concat!(stringify!($fname), " => {:?}"), res), - } - res - } -)} -pub(crate) use syscall_instrument; diff --git a/api/src/path.rs b/api/src/path.rs new file mode 100644 index 000000000..134cfa155 --- /dev/null +++ b/api/src/path.rs @@ -0,0 +1,306 @@ +use core::{ffi::c_int, fmt, ops::Deref}; + +use alloc::{ + collections::btree_map::BTreeMap, + string::{String, ToString}, +}; +use axerrno::{AxError, AxResult, LinuxError, LinuxResult}; +use axfs::api::canonicalize; +use linux_raw_sys::general::AT_FDCWD; +use spin::RwLock; + +use crate::file::{Directory, File, FileLike}; + +/// 一个规范化的文件路径表示 +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +pub struct FilePath(String); + +impl FilePath { + /// 从路径字符串创建一个新的 `FilePath`,路径将被规范化。 + /// 输入路径可以是绝对路径或相对路径。 + pub fn new>(path: P) -> AxResult { + let path = path.as_ref(); + let canonical = canonicalize(path).map_err(|_| AxError::NotFound)?; + let mut new_path = canonical.trim().to_string(); + + // 如果原始路径以 '/' 结尾,那么规范化后的路径也应以 '/' 结尾 + if path.ends_with('/') && !new_path.ends_with('/') { + new_path.push('/'); + } + + assert!( + new_path.starts_with('/'), + "canonical path should start with /" + ); + + Ok(Self(HARDLINK_MANAGER.real_path(&new_path))) + } + + /// 返回底层路径的字符串切片 + pub fn as_str(&self) -> &str { + &self.0 + } + + /// 返回父目录路径 + pub fn parent(&self) -> AxResult<&str> { + if self.is_root() { + return Ok("/"); + } + + // 查找最后一个斜杠,考虑可能的尾部斜杠 + let mut path = self.as_str(); + if path.ends_with('/') { + path = path.strip_suffix('/').unwrap(); + } + let pos = path.rfind('/').ok_or(AxError::NotFound)?; + + Ok(&path[..=pos]) + } + + /// 返回文件名或目录名组件 + pub fn name(&self) -> AxResult<&str> { + if self.is_root() { + return Ok("/"); + } + + let mut path = self.as_str(); + if path.ends_with('/') { + path = path.strip_suffix('/').unwrap(); + } + let start_pos = path.rfind('/').ok_or(AxError::NotFound)?; + + let end_pos = if path.ends_with('/') { + path.len() - 1 + } else { + path.len() + }; + Ok(&path[start_pos + 1..end_pos]) + } + + /// 判断是否为根目录 + pub fn is_root(&self) -> bool { + self.0 == "/" + } + + /// 判断是否为目录(以 '/' 结尾) + pub fn is_dir(&self) -> bool { + self.0.ends_with('/') + } + + /// 判断是否为常规文件(不以 '/' 结尾) + pub fn is_file(&self) -> bool { + !self.is_dir() + } + + /// Whether the path exists + pub fn exists(&self) -> bool { + axfs::api::absolute_path_exists(&self.0) + } + + /// 判断此路径是否以给定前缀路径开头 + pub fn starts_with(&self, prefix: &FilePath) -> bool { + self.0.starts_with(&prefix.0) + } + + /// 判断此路径是否以给定后缀路径结尾 + pub fn ends_with(&self, suffix: &FilePath) -> bool { + self.0.ends_with(&suffix.0) + } + + /// 将此路径与相对路径组件连接 + pub fn join>(&self, path: P) -> AxResult { + let mut new_path = self.0.clone(); + if !new_path.ends_with('/') { + new_path.push('/'); + } + new_path.push_str(path.as_ref()); + FilePath::new(new_path) + } + + /// 返回此路径组件的迭代器 + pub fn components(&self) -> impl Iterator { + self.0.trim_matches('/').split('/') + } +} + +impl fmt::Display for FilePath { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.0) + } +} + +impl AsRef for FilePath { + fn as_ref(&self) -> &str { + &self.0 + } +} + +impl From<&str> for FilePath { + fn from(s: &str) -> Self { + FilePath::new(s).unwrap() + } +} + +impl Deref for FilePath { + type Target = str; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +/// 错误类型 +#[derive(Debug)] +pub enum LinkError { + LinkExists, // 链接已存在 + InvalidPath, // 无效路径 + NotFound, // 文件不存在 + NotFile, // 不是文件 +} + +impl From for AxError { + fn from(err: LinkError) -> AxError { + match err { + LinkError::LinkExists => AxError::AlreadyExists, + LinkError::InvalidPath => AxError::InvalidInput, + LinkError::NotFound => AxError::NotFound, + LinkError::NotFile => AxError::InvalidInput, + } + } +} + +impl From for LinuxError { + fn from(err: LinkError) -> LinuxError { + AxError::from(err).into() + } +} + +/// A global hardlink manager +pub static HARDLINK_MANAGER: HardlinkManager = HardlinkManager::new(); + +/// A manager for hardlinks +pub struct HardlinkManager { + inner: RwLock, +} +struct LinkManagerInner { + links: BTreeMap, + ref_counts: BTreeMap, +} + +// 关于innner的操作都在atomic_开头的函数中 +impl HardlinkManager { + const fn new() -> Self { + Self { + inner: RwLock::new(LinkManagerInner { + links: BTreeMap::new(), + ref_counts: BTreeMap::new(), + }), + } + } + + /// 创建链接 + /// 如果目标路径不存在,则返回 `LinkError::NotFound` + /// 如果目标路径不是文件,则返回 `LinkError::NotFile` + pub fn create_link(&self, src: &FilePath, dst: &FilePath) -> Result<(), LinkError> { + if !dst.exists() { + return Err(LinkError::NotFound); + } + if !dst.is_dir() { + return Err(LinkError::NotFile); + } + + let mut inner = self.inner.write(); + self.atomic_link_update(&mut inner, src, dst); + Ok(()) + } + + /// 移除链接 + /// 链接数量为零 或 没有链接时, 删除文件 + /// 如果路径对应的链接不存在 或 路径对应的文件不存在,则返回 `None` + /// 否则返回链接的目标路径 + pub fn remove_link(&self, src: &FilePath) -> Option { + let mut inner = self.inner.write(); + self.atomic_link_remove(&mut inner, src).or_else(|| { + axfs::api::remove_file(src.as_str()) + .ok() + .map(|_| src.to_string()) + }) + } + + pub fn real_path(&self, path: &str) -> String { + self.inner + .read() + .links + .get(path) + .cloned() + .unwrap_or_else(|| path.to_string()) + } + + pub fn link_count(&self, path: &FilePath) -> usize { + let inner = self.inner.read(); + inner + .ref_counts + .get(path.as_str()) + .copied() + .unwrap_or_else(|| if path.exists() { 1 } else { 0 }) + } + + // 原子操作helpers + + /// 创建或更新链接 + /// 如果链接已存在,则更新目标路径 + /// 如果目标路径不存在,则返回 `LinkError::NotFound` + fn atomic_link_update(&self, inner: &mut LinkManagerInner, src: &FilePath, dst: &FilePath) { + if let Some(old_dst) = inner.links.get(src.as_str()) { + if old_dst == dst.as_str() { + return; + } + self.decrease_ref_count(inner, &old_dst.to_string()); + } + inner.links.insert(src.to_string(), dst.to_string()); + *inner.ref_counts.entry(dst.to_string()).or_insert(0) += 1; + } + + /// 移除链接 + /// 如果链接不存在,则返回 `None`,否则返回链接的目标路径 + fn atomic_link_remove(&self, inner: &mut LinkManagerInner, src: &FilePath) -> Option { + inner.links.remove(src.as_str()).inspect(|dst| { + self.decrease_ref_count(inner, dst); + }) + } + + /// 减少引用计数 + /// 如果引用计数为零,则删除链接,并删除文件,如果删除文件失败,则返回 `None` + /// 如果链接不存在,则返回 `None` + fn decrease_ref_count(&self, inner: &mut LinkManagerInner, path: &str) -> Option<()> { + match inner.ref_counts.get_mut(path) { + Some(count) => { + *count -= 1; + if *count == 0 { + inner.ref_counts.remove(path); + axfs::api::remove_file(path).ok()? + } + Some(()) + } + None => { + axlog::error!("link exists but ref count is zero"); + None + } + } + } +} + +pub fn handle_file_path(dirfd: c_int, path: &str) -> LinuxResult { + if path.starts_with('/') { + Ok(FilePath::new(path)?) + } else if path.is_empty() { + Ok(FilePath::new(File::from_fd(dirfd)?.path())?) + } else { + let base = if dirfd == AT_FDCWD { + FilePath::new("")? + } else { + FilePath::new(Directory::from_fd(dirfd)?.path())? + }; + Ok(base.join(path)?) + } +} diff --git a/api/src/ptr.rs b/api/src/ptr.rs index 6dc173ebf..2c4d3b201 100644 --- a/api/src/ptr.rs +++ b/api/src/ptr.rs @@ -1,4 +1,4 @@ -use core::{alloc::Layout, ffi::c_char, mem, slice, str}; +use core::{alloc::Layout, ffi::c_char, mem::transmute, ptr, slice, str}; use axerrno::{LinuxError, LinuxResult}; use axhal::paging::MappingFlags; @@ -29,10 +29,10 @@ fn check_region(start: VirtAddr, layout: Layout, access_flags: MappingFlags) -> Ok(()) } -fn check_null_terminated( +fn check_null_terminated( start: VirtAddr, access_flags: MappingFlags, -) -> LinuxResult<(*const T, usize)> { +) -> LinuxResult { let align = Layout::new::().align(); if start.as_usize() & (align - 1) != 0 { return Err(LinuxError::EFAULT); @@ -80,76 +80,12 @@ fn check_null_terminated( Ok(()) })?; - Ok((start, len)) -} - -/// A trait representing a pointer in user space, which can be converted to a -/// pointer in kernel space through a series of checks. -/// -/// Converting a `PtrWrapper` to `*T` is done by `PtrWrapper::get` (or -/// `get_as_*`). It checks whether the pointer along with its layout is valid in -/// the current task's address space, and raises EFAULT if not. -pub trait PtrWrapper: Sized { - type Ptr; - - const ACCESS_FLAGS: MappingFlags; - - /// Unwrap the pointer to the inner type. - /// - /// This function is unsafe because it assumes that the pointer is valid and - /// points to a valid memory region. - unsafe fn into_inner(self) -> Self::Ptr; - - /// Get the address of the pointer. - fn address(&self) -> VirtAddr; - - /// Get the pointer as a raw pointer to `T`. - fn get(self) -> LinuxResult { - self.get_as(Layout::new::()) - } - - /// Get the pointer as a raw pointer to `T`, validating the memory - /// region given by the layout. - fn get_as(self, layout: Layout) -> LinuxResult { - check_region(self.address(), layout, Self::ACCESS_FLAGS)?; - unsafe { Ok(self.into_inner()) } - } - - /// Get the pointer as a raw pointer to `T`, validating the memory - /// region specified by the size. - fn get_as_bytes(self, size: usize) -> LinuxResult { - check_region( - self.address(), - Layout::from_size_align(size, 1).unwrap(), - Self::ACCESS_FLAGS, - )?; - unsafe { Ok(self.into_inner()) } - } - - /// Get the pointer as a raw pointer to `T`, validating the memory - /// region given by the layout of `[T; len]`. - fn get_as_array(self, len: usize) -> LinuxResult { - check_region( - self.address(), - Layout::array::(len).unwrap(), - Self::ACCESS_FLAGS, - )?; - unsafe { Ok(self.into_inner()) } - } - - fn nullable(self, f: impl FnOnce(Self) -> LinuxResult) -> LinuxResult> { - if self.address().as_ptr().is_null() { - Ok(None) - } else { - f(self).map(Some) - } - } + Ok(len) } /// A pointer to user space memory. -/// -/// See [`PtrWrapper`] for more details. #[repr(transparent)] +#[derive(PartialEq, Clone, Copy)] pub struct UserPtr(*mut T); impl From for UserPtr { @@ -158,37 +94,49 @@ impl From for UserPtr { } } -impl PtrWrapper for UserPtr { - type Ptr = *mut T; +impl Default for UserPtr { + fn default() -> Self { + Self(ptr::null_mut()) + } +} +impl UserPtr { const ACCESS_FLAGS: MappingFlags = MappingFlags::READ.union(MappingFlags::WRITE); - unsafe fn into_inner(self) -> Self::Ptr { - self.0 + pub fn address(&self) -> VirtAddr { + VirtAddr::from_ptr_of(self.0) } - fn address(&self) -> VirtAddr { - VirtAddr::from_mut_ptr_of(self.0) + pub fn is_null(&self) -> bool { + self.0.is_null() } -} -impl UserPtr { - /// Get the pointer as `&mut [T]`, terminated by a null value, validating - /// the memory region. - pub fn get_as_null_terminated(self) -> LinuxResult<&'static mut [T]> + pub fn get_as_mut(self) -> LinuxResult<&'static mut T> { + check_region(self.address(), Layout::new::(), Self::ACCESS_FLAGS)?; + Ok(unsafe { &mut *self.0 }) + } + + pub fn get_as_mut_slice(self, len: usize) -> LinuxResult<&'static mut [T]> { + check_region( + self.address(), + Layout::array::(len).unwrap(), + Self::ACCESS_FLAGS, + )?; + Ok(unsafe { slice::from_raw_parts_mut(self.0, len) }) + } + + pub fn get_as_mut_null_terminated(self) -> LinuxResult<&'static mut [T]> where - T: Eq + Default, + T: PartialEq + Default, { - let (ptr, len) = check_null_terminated::(self.address(), Self::ACCESS_FLAGS)?; - // SAFETY: We've validated the memory region. - unsafe { Ok(slice::from_raw_parts_mut(ptr as *mut _, len)) } + let len = check_null_terminated::(self.address(), Self::ACCESS_FLAGS)?; + Ok(unsafe { slice::from_raw_parts_mut(self.0, len) }) } } /// An immutable pointer to user space memory. -/// -/// See [`PtrWrapper`] for more details. #[repr(transparent)] +#[derive(PartialEq, Clone, Copy)] pub struct UserConstPtr(*const T); impl From for UserConstPtr { @@ -197,42 +145,64 @@ impl From for UserConstPtr { } } -impl PtrWrapper for UserConstPtr { - type Ptr = *const T; +impl Default for UserConstPtr { + fn default() -> Self { + Self(ptr::null()) + } +} +impl UserConstPtr { const ACCESS_FLAGS: MappingFlags = MappingFlags::READ; - unsafe fn into_inner(self) -> Self::Ptr { - self.0 + pub fn address(&self) -> VirtAddr { + VirtAddr::from_ptr_of(self.0) } - fn address(&self) -> VirtAddr { - VirtAddr::from_ptr_of(self.0) + pub fn is_null(&self) -> bool { + self.0.is_null() + } + + pub fn get_as_ref(self) -> LinuxResult<&'static T> { + check_region(self.address(), Layout::new::(), Self::ACCESS_FLAGS)?; + Ok(unsafe { &*self.0 }) + } + + pub fn get_as_slice(self, len: usize) -> LinuxResult<&'static [T]> { + check_region( + self.address(), + Layout::array::(len).unwrap(), + Self::ACCESS_FLAGS, + )?; + Ok(unsafe { slice::from_raw_parts(self.0, len) }) } -} -impl UserConstPtr { - /// Get the pointer as `&[T]`, terminated by a null value, validating the - /// memory region. pub fn get_as_null_terminated(self) -> LinuxResult<&'static [T]> where - T: Eq + Default, + T: PartialEq + Default, { - let (ptr, len) = check_null_terminated::(self.address(), Self::ACCESS_FLAGS)?; - // SAFETY: We've validated the memory region. - unsafe { Ok(slice::from_raw_parts(ptr, len)) } + let len = check_null_terminated::(self.address(), Self::ACCESS_FLAGS)?; + Ok(unsafe { slice::from_raw_parts(self.0, len) }) } } -static_assertions::const_assert_eq!(size_of::(), size_of::()); - impl UserConstPtr { /// Get the pointer as `&str`, validating the memory region. pub fn get_as_str(self) -> LinuxResult<&'static str> { let slice = self.get_as_null_terminated()?; // SAFETY: c_char is u8 - let slice = unsafe { mem::transmute::<&[c_char], &[u8]>(slice) }; + let slice = unsafe { transmute::<&[c_char], &[u8]>(slice) }; str::from_utf8(slice).map_err(|_| LinuxError::EILSEQ) } } + +macro_rules! nullable { + ($ptr:ident.$func:ident($($arg:expr),*)) => { + if $ptr.is_null() { + Ok(None) + } else { + Some($ptr.$func($($arg),*)).transpose() + } + }; +} +pub(crate) use nullable; diff --git a/api/src/signal.rs b/api/src/signal.rs new file mode 100644 index 000000000..70d660a42 --- /dev/null +++ b/api/src/signal.rs @@ -0,0 +1,84 @@ +use axerrno::{LinuxError, LinuxResult}; +use axhal::{ + arch::TrapFrame, + trap::{POST_TRAP, register_trap_handler}, +}; +use axprocess::{Process, ProcessGroup, Thread}; +use axsignal::{SignalInfo, SignalOSAction, SignalSet}; +use axtask::{TaskExtRef, current}; +use starry_core::task::{ProcessData, ThreadData}; + +use crate::do_exit; + +pub fn check_signals(tf: &mut TrapFrame, restore_blocked: Option) -> bool { + let Some((sig, os_action)) = current() + .task_ext() + .thread_data() + .signal + .check_signals(tf, restore_blocked) + else { + return false; + }; + + let signo = sig.signo(); + match os_action { + SignalOSAction::Terminate => { + do_exit(128 + signo as i32, true); + } + SignalOSAction::CoreDump => { + // TODO: implement core dump + do_exit(128 + signo as i32, true); + } + SignalOSAction::Stop => { + // TODO: implement stop + do_exit(1, true); + } + SignalOSAction::Continue => { + // TODO: implement continue + } + SignalOSAction::Handler => { + // do nothing + } + } + true +} + +#[register_trap_handler(POST_TRAP)] +fn post_trap_callback(tf: &mut TrapFrame, from_user: bool) { + if !from_user { + return; + } + + check_signals(tf, None); +} + +pub fn send_signal_thread(thr: &Thread, sig: SignalInfo) -> LinuxResult<()> { + info!("Send signal {:?} to thread {}", sig.signo(), thr.tid()); + let Some(thr) = thr.data::() else { + return Err(LinuxError::EPERM); + }; + thr.signal.send_signal(sig); + Ok(()) +} + +pub fn send_signal_process(proc: &Process, sig: SignalInfo) -> LinuxResult<()> { + info!("Send signal {:?} to process {}", sig.signo(), proc.pid()); + let Some(proc) = proc.data::() else { + return Err(LinuxError::EPERM); + }; + proc.signal.send_signal(sig); + Ok(()) +} + +pub fn send_signal_process_group(pg: &ProcessGroup, sig: SignalInfo) -> usize { + info!( + "Send signal {:?} to process group {}", + sig.signo(), + pg.pgid() + ); + let mut count = 0; + for proc in pg.processes() { + count += send_signal_process(&proc, sig.clone()).is_ok() as usize; + } + count +} diff --git a/api/src/sockaddr.rs b/api/src/sockaddr.rs new file mode 100644 index 000000000..6809c9cf1 --- /dev/null +++ b/api/src/sockaddr.rs @@ -0,0 +1,196 @@ +//! Wrapper for `sockaddr`. Adapted from [`rustix::net::SocketAddrAny`]. +//! +//! [`rustix::net::SocketAddrAny`]: https://docs.rs/rustix/latest/rustix/net/struct.SocketAddrAny.html + +use core::{ + mem::MaybeUninit, + net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}, +}; + +use axerrno::{LinuxError, LinuxResult}; +use linux_raw_sys::net::{ + __kernel_sa_family_t, AF_INET, AF_INET6, in_addr, in6_addr, sockaddr, sockaddr_in, + sockaddr_in6, socklen_t, +}; + +/// A type that can hold any kind of socket address, as a safe abstraction for +/// `sockaddr`. +/// +/// Socket addresses can be converted to `SocketAddrAny` via the [`From`] and +/// [`Into`] traits. `SocketAddrAny` can be converted back to a specific socket +/// address type with [`TryFrom`] and [`TryInto`]. These implementations return +/// [`LinuxError::EAFNOSUPPORT`] if the address family does not match the requested +/// type. +#[derive(Clone)] +pub struct SockAddr { + // Invariants: + // - `len` is at least `size_of::<__kernel_sa_family_t>()` + // - `len` is at most `size_of::()` + // - The first `len` bytes of `storage` are initialized. + len: socklen_t, + storage: MaybeUninit, +} + +// SAFETY: Bindgen adds a union with a raw pointer for alignment but it's never +// used. `sockaddr` is just a bunch of bytes and it doesn't hold pointers. +unsafe impl Send for SockAddr {} + +// SAFETY: Same as with `Send`. +unsafe impl Sync for SockAddr {} + +impl SockAddr { + /// Creates a socket address from reading from `ptr`, which points at `len` + /// initialized bytes. + /// + /// Returns [`LinuxError::EINVAL`] if `len` is smaller than `__kernel_sa_family_t` or larger than + /// `sockaddr`. + /// + /// # Safety + /// + /// - `ptr` must be a pointer to memory containing a valid socket address. + /// - `len` bytes must be initialized. + pub unsafe fn read(ptr: *const sockaddr, len: socklen_t) -> LinuxResult { + if size_of::<__kernel_sa_family_t>() < len as usize || len as usize > size_of::() + { + return Err(LinuxError::EINVAL); + } + let mut storage = MaybeUninit::::uninit(); + unsafe { + core::ptr::copy_nonoverlapping( + ptr as *const u8, + storage.as_mut_ptr() as *mut u8, + len as usize, + ) + }; + Ok(Self { storage, len }) + } + + /// Gets the address family of this socket address. + #[inline] + pub fn family(&self) -> u32 { + // SAFETY: Our invariants maintain that the `sa_family` field is + // initialized. + unsafe { + self.storage + .assume_init_ref() + .__storage + .__bindgen_anon_1 + .__bindgen_anon_1 + .ss_family as u32 + } + } + + /// Returns the length of the encoded sockaddr. + #[inline] + pub fn addr_len(&self) -> socklen_t { + self.len + } + + /// Gets the initialized part of the storage as bytes. + #[inline] + pub fn bytes(&self) -> &[u8] { + unsafe { core::slice::from_raw_parts(self.storage.as_ptr().cast(), self.len as usize) } + } +} + +impl From for SockAddr { + fn from(v4: SocketAddrV4) -> Self { + let addr = sockaddr_in { + sin_family: AF_INET as _, + sin_port: v4.port().to_be(), + sin_addr: in_addr { + s_addr: u32::from_ne_bytes(v4.ip().octets()), + }, + __pad: [0_u8; 8], + }; + unsafe { + Self::read( + &addr as *const sockaddr_in as *const sockaddr, + core::mem::size_of::() as socklen_t, + ) + .unwrap() + } + } +} + +impl From for SockAddr { + fn from(v6: SocketAddrV6) -> Self { + let addr = sockaddr_in6 { + sin6_family: AF_INET6 as _, + sin6_port: v6.port().to_be(), + sin6_flowinfo: v6.flowinfo().to_be(), + sin6_addr: in6_addr { + in6_u: linux_raw_sys::net::in6_addr__bindgen_ty_1 { + u6_addr8: v6.ip().octets(), + }, + }, + sin6_scope_id: v6.scope_id(), + }; + unsafe { + Self::read( + &addr as *const sockaddr_in6 as *const sockaddr, + core::mem::size_of::() as socklen_t, + ) + .unwrap() + } + } +} + +impl From for SockAddr { + fn from(addr: SocketAddr) -> Self { + match addr { + SocketAddr::V4(v4) => v4.into(), + SocketAddr::V6(v6) => v6.into(), + } + } +} + +impl TryFrom for SocketAddrV4 { + type Error = LinuxError; + + fn try_from(addr: SockAddr) -> LinuxResult { + if addr.family() != AF_INET { + return Err(LinuxError::EAFNOSUPPORT); + } + if size_of::() < addr.addr_len() as usize { + return Err(LinuxError::EINVAL); + } + let addr = unsafe { &*(addr.storage.as_ptr() as *const sockaddr_in) }; + Ok(SocketAddrV4::new( + Ipv4Addr::from_bits(u32::from_be(addr.sin_addr.s_addr)), + u16::from_be(addr.sin_port), + )) + } +} + +impl TryFrom for SocketAddrV6 { + type Error = LinuxError; + + fn try_from(addr: SockAddr) -> LinuxResult { + if addr.family() != AF_INET6 { + return Err(LinuxError::EAFNOSUPPORT); + } + if size_of::() < addr.addr_len() as usize { + return Err(LinuxError::EINVAL); + } + let addr = unsafe { &*(addr.storage.as_ptr() as *const sockaddr_in6) }; + Ok(SocketAddrV6::new( + Ipv6Addr::from(unsafe { addr.sin6_addr.in6_u.u6_addr8 }), + u16::from_be(addr.sin6_port), + u32::from_be(addr.sin6_flowinfo), + addr.sin6_scope_id, + )) + } +} + +impl TryFrom for SocketAddr { + type Error = LinuxError; + + fn try_from(addr: SockAddr) -> LinuxResult { + match addr.family() { + AF_INET => Ok(SocketAddr::V4(addr.try_into()?)), + AF_INET6 => Ok(SocketAddr::V6(addr.try_into()?)), + _ => Err(LinuxError::EAFNOSUPPORT), + } + } +} diff --git a/api/src/time.rs b/api/src/time.rs new file mode 100644 index 000000000..2cd41499e --- /dev/null +++ b/api/src/time.rs @@ -0,0 +1,102 @@ +use axhal::time::TimeValue; +use linux_raw_sys::general::{ + __kernel_old_timespec, __kernel_old_timeval, __kernel_sock_timeval, __kernel_timespec, + timespec, timeval, +}; + +/// A helper trait for converting from and to `TimeValue`. +pub trait TimeValueLike { + /// Converts from `TimeValue`. + fn from_time_value(tv: TimeValue) -> Self; + + /// Converts to `TimeValue`. + fn to_time_value(self) -> TimeValue; +} + +impl TimeValueLike for TimeValue { + fn from_time_value(tv: TimeValue) -> Self { + tv + } + + fn to_time_value(self) -> TimeValue { + self + } +} + +impl TimeValueLike for timespec { + fn from_time_value(tv: TimeValue) -> Self { + Self { + tv_sec: tv.as_secs() as _, + tv_nsec: tv.subsec_nanos() as _, + } + } + + fn to_time_value(self) -> TimeValue { + TimeValue::new(self.tv_sec as u64, self.tv_nsec as u32) + } +} + +impl TimeValueLike for __kernel_timespec { + fn from_time_value(tv: TimeValue) -> Self { + Self { + tv_sec: tv.as_secs() as _, + tv_nsec: tv.subsec_nanos() as _, + } + } + + fn to_time_value(self) -> TimeValue { + TimeValue::new(self.tv_sec as u64, self.tv_nsec as u32) + } +} + +impl TimeValueLike for __kernel_old_timespec { + fn from_time_value(tv: TimeValue) -> Self { + Self { + tv_sec: tv.as_secs() as _, + tv_nsec: tv.subsec_nanos() as _, + } + } + + fn to_time_value(self) -> TimeValue { + TimeValue::new(self.tv_sec as u64, self.tv_nsec as u32) + } +} + +impl TimeValueLike for timeval { + fn from_time_value(tv: TimeValue) -> Self { + Self { + tv_sec: tv.as_secs() as _, + tv_usec: tv.subsec_micros() as _, + } + } + + fn to_time_value(self) -> TimeValue { + TimeValue::new(self.tv_sec as u64, self.tv_usec as u32 * 1000) + } +} + +impl TimeValueLike for __kernel_old_timeval { + fn from_time_value(tv: TimeValue) -> Self { + Self { + tv_sec: tv.as_secs() as _, + tv_usec: tv.subsec_micros() as _, + } + } + + fn to_time_value(self) -> TimeValue { + TimeValue::new(self.tv_sec as u64, self.tv_usec as u32 * 1000) + } +} + +impl TimeValueLike for __kernel_sock_timeval { + fn from_time_value(tv: TimeValue) -> Self { + Self { + tv_sec: tv.as_secs() as _, + tv_usec: tv.subsec_micros() as _, + } + } + + fn to_time_value(self) -> TimeValue { + TimeValue::new(self.tv_sec as u64, self.tv_usec as u32 * 1000) + } +} diff --git a/core/Cargo.toml b/core/Cargo.toml index d6090810a..9cf20b203 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -22,12 +22,12 @@ axsignal.workspace = true axerrno.workspace = true linkme.workspace = true memory_addr.workspace = true +spin.workspace = true crate_interface = "0.1" kernel-elf-parser = "0.3" numeric-enum-macro = "0.2" percpu = "0.2.0" -spin = "0.9" xmas-elf = "0.9" weak-map = { git = "https://github.com/Starry-OS/weak-map.git" } diff --git a/scripts/config.toml.temp b/scripts/config.toml.temp index d7cd7dcc2..5676d053f 100644 --- a/scripts/config.toml.temp +++ b/scripts/config.toml.temp @@ -1,12 +1,13 @@ [patch.'https://github.com/oscomp/arceos.git'] axfeat = { path = "%AX_ROOT%/api/axfeat" } -arceos_posix_api = { path = "%AX_ROOT%/api/arceos_posix_api" } axconfig = { path = "%AX_ROOT%/modules/axconfig" } axfs = { path = "%AX_ROOT%/modules/axfs" } axhal = { path = "%AX_ROOT%/modules/axhal" } axlog = { path = "%AX_ROOT%/modules/axlog" } axmm = { path = "%AX_ROOT%/modules/axmm" } +axnet = { path = "%AX_ROOT%/modules/axnet" } axns = { path = "%AX_ROOT%/modules/axns" } +axruntime = { path = "%AX_ROOT%/modules/axruntime" } axsync = { path = "%AX_ROOT%/modules/axsync" } axtask = { path = "%AX_ROOT%/modules/axtask" } diff --git a/src/entry.rs b/src/entry.rs index c4b0ea637..362ea15c9 100644 --- a/src/entry.rs +++ b/src/entry.rs @@ -1,10 +1,10 @@ use alloc::{string::String, sync::Arc}; -use arceos_posix_api::FD_TABLE; use axfs::{CURRENT_DIR, CURRENT_DIR_PATH, api::set_current_dir}; use axhal::arch::UspaceContext; use axprocess::{Pid, init_proc}; use axsignal::Signo; use axsync::Mutex; +use starry_api::file::FD_TABLE; use starry_core::{ mm::{copy_from_kernel, load_user_app, map_trampoline, new_user_aspace_empty}, task::{ProcessData, TaskExt, ThreadData, add_thread_to_table, new_user_task}, diff --git a/src/main.rs b/src/main.rs index 556106627..96bdbbc53 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ #[macro_use] extern crate axlog; extern crate alloc; +extern crate axruntime; mod entry; mod mm; diff --git a/src/syscall.rs b/src/syscall.rs index ccd4b3dca..178122bb6 100644 --- a/src/syscall.rs +++ b/src/syscall.rs @@ -13,54 +13,10 @@ fn handle_syscall(tf: &mut TrapFrame, syscall_num: usize) -> isize { info!("Syscall {}", sysno); time_stat_from_user_to_kernel(); let result = match sysno { - Sysno::read => sys_read(tf.arg0() as _, tf.arg1().into(), tf.arg2() as _), - Sysno::write => sys_write(tf.arg0() as _, tf.arg1().into(), tf.arg2() as _), - Sysno::mmap => sys_mmap( - tf.arg0().into(), - tf.arg1() as _, - tf.arg2() as _, - tf.arg3() as _, - tf.arg4() as _, - tf.arg5() as _, - ), + // fs ctl Sysno::ioctl => sys_ioctl(tf.arg0() as _, tf.arg1() as _, tf.arg2().into()), - Sysno::writev => sys_writev(tf.arg0() as _, tf.arg1().into(), tf.arg2() as _), - Sysno::sched_yield => sys_sched_yield(), - Sysno::nanosleep => sys_nanosleep(tf.arg0().into(), tf.arg1().into()), - Sysno::getpid => sys_getpid(), - Sysno::getppid => sys_getppid(), - Sysno::gettid => sys_gettid(), - Sysno::exit => sys_exit(tf.arg0() as _), - Sysno::exit_group => sys_exit_group(tf.arg0() as _), - Sysno::gettimeofday => sys_get_time_of_day(tf.arg0().into()), - Sysno::getcwd => sys_getcwd(tf.arg0().into(), tf.arg1() as _), - Sysno::dup => sys_dup(tf.arg0() as _), - Sysno::dup3 => sys_dup3(tf.arg0() as _, tf.arg1() as _), - Sysno::fcntl => sys_fcntl(tf.arg0() as _, tf.arg1() as _, tf.arg2() as _), - Sysno::clone => sys_clone( - tf, - tf.arg0() as _, - tf.arg1() as _, - tf.arg2(), - tf.arg3(), - tf.arg4(), - ), - #[cfg(target_arch = "x86_64")] - Sysno::fork => sys_fork(tf), - Sysno::wait4 => sys_waitpid(tf.arg0() as _, tf.arg1().into(), tf.arg2() as _), - Sysno::pipe2 => sys_pipe2(tf.arg0().into()), - Sysno::close => sys_close(tf.arg0() as _), Sysno::chdir => sys_chdir(tf.arg0().into()), Sysno::mkdirat => sys_mkdirat(tf.arg0() as _, tf.arg1().into(), tf.arg2() as _), - Sysno::execve => sys_execve(tf.arg0().into(), tf.arg1().into(), tf.arg2().into()), - Sysno::openat => sys_openat( - tf.arg0() as _, - tf.arg1().into(), - tf.arg2() as _, - tf.arg3() as _, - ), - #[cfg(target_arch = "x86_64")] - Sysno::open => sys_open(tf.arg0().into(), tf.arg1() as _, tf.arg2() as _), Sysno::getdents64 => sys_getdents64(tf.arg0() as _, tf.arg1().into(), tf.arg2() as _), Sysno::linkat => sys_linkat( tf.arg0() as _, @@ -69,9 +25,36 @@ fn handle_syscall(tf: &mut TrapFrame, syscall_num: usize) -> isize { tf.arg3().into(), tf.arg4() as _, ), + #[cfg(target_arch = "x86_64")] + Sysno::link => sys_link(tf.arg0().into(), tf.arg1().into()), Sysno::unlinkat => sys_unlinkat(tf.arg0() as _, tf.arg1().into(), tf.arg2() as _), - Sysno::uname => sys_uname(tf.arg0().into()), - Sysno::fstat => sys_fstat(tf.arg0() as _, tf.arg1().into()), + #[cfg(target_arch = "x86_64")] + Sysno::unlink => sys_unlink(tf.arg0().into()), + Sysno::getcwd => sys_getcwd(tf.arg0().into(), tf.arg1() as _), + + // fd ops + Sysno::openat => sys_openat( + tf.arg0() as _, + tf.arg1().into(), + tf.arg2() as _, + tf.arg3() as _, + ), + #[cfg(target_arch = "x86_64")] + Sysno::open => sys_open(tf.arg0().into(), tf.arg1() as _, tf.arg2() as _), + Sysno::close => sys_close(tf.arg0() as _), + Sysno::dup => sys_dup(tf.arg0() as _), + #[cfg(target_arch = "x86_64")] + Sysno::dup2 => sys_dup2(tf.arg0() as _, tf.arg1() as _), + Sysno::dup3 => sys_dup2(tf.arg0() as _, tf.arg1() as _), + Sysno::fcntl => sys_fcntl(tf.arg0() as _, tf.arg1() as _, tf.arg2() as _), + + // io + Sysno::read => sys_read(tf.arg0() as _, tf.arg1().into(), tf.arg2() as _), + Sysno::write => sys_write(tf.arg0() as _, tf.arg1().into(), tf.arg2() as _), + Sysno::writev => sys_writev(tf.arg0() as _, tf.arg1().into(), tf.arg2() as _), + Sysno::lseek => sys_lseek(tf.arg0() as _, tf.arg1() as _, tf.arg2() as _), + + // fs mount Sysno::mount => sys_mount( tf.arg0().into(), tf.arg1().into(), @@ -80,6 +63,18 @@ fn handle_syscall(tf: &mut TrapFrame, syscall_num: usize) -> isize { tf.arg4().into(), ) as _, Sysno::umount2 => sys_umount2(tf.arg0().into(), tf.arg1() as _) as _, + + // pipe + Sysno::pipe2 => sys_pipe2(tf.arg0().into(), tf.arg1() as _), + #[cfg(target_arch = "x86_64")] + Sysno::pipe => sys_pipe2(tf.arg0().into(), 0), + + // fs stat + #[cfg(target_arch = "x86_64")] + Sysno::stat => sys_stat(tf.arg0().into(), tf.arg1().into()), + Sysno::fstat => sys_fstat(tf.arg0() as _, tf.arg1().into()), + #[cfg(target_arch = "x86_64")] + Sysno::lstat => sys_lstat(tf.arg0().into(), tf.arg1().into()), #[cfg(target_arch = "x86_64")] Sysno::newfstatat => sys_fstatat( tf.arg0() as _, @@ -101,15 +96,51 @@ fn handle_syscall(tf: &mut TrapFrame, syscall_num: usize) -> isize { tf.arg3() as _, tf.arg4().into(), ), - Sysno::munmap => sys_munmap(tf.arg0().into(), tf.arg1() as _), - Sysno::mprotect => sys_mprotect(tf.arg0().into(), tf.arg1() as _, tf.arg2() as _), - Sysno::times => sys_times(tf.arg0().into()), + + // mm Sysno::brk => sys_brk(tf.arg0() as _), + Sysno::mmap => sys_mmap( + tf.arg0(), + tf.arg1() as _, + tf.arg2() as _, + tf.arg3() as _, + tf.arg4() as _, + tf.arg5() as _, + ), + Sysno::munmap => sys_munmap(tf.arg0(), tf.arg1() as _), + Sysno::mprotect => sys_mprotect(tf.arg0(), tf.arg1() as _, tf.arg2() as _), + + // task info + Sysno::getpid => sys_getpid(), + Sysno::getppid => sys_getppid(), + Sysno::gettid => sys_gettid(), + + // task sched + Sysno::sched_yield => sys_sched_yield(), + Sysno::nanosleep => sys_nanosleep(tf.arg0().into(), tf.arg1().into()), + + // task ops + Sysno::execve => sys_execve(tf.arg0().into(), tf.arg1().into(), tf.arg2().into()), + Sysno::set_tid_address => sys_set_tid_address(tf.arg0()), #[cfg(target_arch = "x86_64")] Sysno::arch_prctl => sys_arch_prctl(tf, tf.arg0() as _, tf.arg1() as _), - Sysno::set_tid_address => sys_set_tid_address(tf.arg0()), - Sysno::clock_gettime => sys_clock_gettime(tf.arg0() as _, tf.arg1().into()), - Sysno::getuid => sys_getuid(), + + // task management + Sysno::clone => sys_clone( + tf, + tf.arg0() as _, + tf.arg1() as _, + tf.arg2(), + tf.arg3(), + tf.arg4(), + ), + #[cfg(target_arch = "x86_64")] + Sysno::fork => sys_fork(tf), + Sysno::exit => sys_exit(tf.arg0() as _), + Sysno::exit_group => sys_exit_group(tf.arg0() as _), + Sysno::wait4 => sys_waitpid(tf.arg0() as _, tf.arg1().into(), tf.arg2() as _), + + // signal Sysno::rt_sigprocmask => sys_rt_sigprocmask( tf.arg0() as _, tf.arg1().into(), @@ -156,6 +187,19 @@ fn handle_syscall(tf: &mut TrapFrame, syscall_num: usize) -> isize { tf.arg4().into(), tf.arg5() as _, ), + + // sys + Sysno::getuid => sys_getuid(), + Sysno::geteuid => sys_geteuid(), + Sysno::getgid => sys_getgid(), + Sysno::getegid => sys_getegid(), + Sysno::uname => sys_uname(tf.arg0().into()), + + // time + Sysno::gettimeofday => sys_gettimeofday(tf.arg0().into()), + Sysno::times => sys_times(tf.arg0().into()), + Sysno::clock_gettime => sys_clock_gettime(tf.arg0() as _, tf.arg1().into()), + _ => { warn!("Unimplemented syscall: {}", sysno); Err(LinuxError::ENOSYS)