From 334928f1514ae78c4d85b6124aa63642e6c75729 Mon Sep 17 00:00:00 2001 From: Ibuki Omatsu Date: Wed, 21 Jan 2026 03:56:58 +0000 Subject: [PATCH] feat: Introduce userspace namespace manager and adapt all schemes. --- Cargo.lock | 130 ++--- Cargo.toml | 12 +- audiod/src/main.rs | 26 +- audiod/src/scheme.rs | 27 +- bootstrap/Cargo.lock | 23 +- bootstrap/Cargo.toml | 9 +- bootstrap/src/aarch64.rs | 2 +- bootstrap/src/exec.rs | 131 ++++- bootstrap/src/i686.rs | 2 +- bootstrap/src/initfs.rs | 112 +++- bootstrap/src/initnsmgr.rs | 536 ++++++++++++++++++++ bootstrap/src/lib.rs | 93 ++-- bootstrap/src/procmgr.rs | 78 ++- bootstrap/src/riscv64.rs | 2 +- bootstrap/src/start.rs | 62 +-- bootstrap/src/x86_64.rs | 2 +- drivers/acpid/Cargo.toml | 4 +- drivers/acpid/src/acpi.rs | 27 +- drivers/acpid/src/aml_physmem.rs | 27 +- drivers/acpid/src/main.rs | 31 +- drivers/acpid/src/scheme.rs | 155 ++++-- drivers/audio/ac97d/src/device.rs | 36 +- drivers/audio/ac97d/src/main.rs | 6 +- drivers/audio/ihdad/src/hda/device.rs | 22 +- drivers/audio/ihdad/src/main.rs | 6 +- drivers/audio/sb16d/src/device.rs | 22 +- drivers/audio/sb16d/src/main.rs | 6 +- drivers/common/src/dma.rs | 6 +- drivers/common/src/lib.rs | 51 +- drivers/common/src/logger.rs | 1 + drivers/graphics/bgad/src/main.rs | 10 +- drivers/graphics/bgad/src/scheme.rs | 17 +- drivers/graphics/driver-graphics/src/lib.rs | 48 +- drivers/graphics/fbbootlogd/src/main.rs | 37 +- drivers/graphics/fbbootlogd/src/scheme.rs | 25 +- drivers/graphics/fbcond/src/main.rs | 12 +- drivers/graphics/fbcond/src/scheme.rs | 54 +- drivers/graphics/vesad/src/main.rs | 1 + drivers/graphics/virtio-gpud/src/main.rs | 3 +- drivers/hwd/Cargo.toml | 1 + drivers/hwd/src/backend/acpi.rs | 16 +- drivers/input/ps2d/src/main.rs | 3 + drivers/input/usbhidd/src/main.rs | 2 +- drivers/inputd/src/main.rs | 37 +- drivers/net/alxd/src/device/mod.rs | 17 +- drivers/net/alxd/src/main.rs | 16 +- drivers/net/driver-network/src/lib.rs | 34 +- drivers/net/e1000d/src/main.rs | 1 + drivers/net/ixgbed/src/main.rs | 1 + drivers/net/rtl8139d/src/main.rs | 1 + drivers/net/rtl8168d/src/main.rs | 1 + drivers/net/virtio-netd/src/main.rs | 1 + drivers/pcid-spawner/src/main.rs | 2 +- drivers/pcid/src/driver_interface/mod.rs | 5 +- drivers/pcid/src/main.rs | 46 +- drivers/pcid/src/scheme.rs | 59 ++- drivers/redoxerd/src/main.rs | 43 +- drivers/storage/ahcid/src/main.rs | 1 + drivers/storage/driver-block/src/lib.rs | 59 ++- drivers/storage/ided/src/main.rs | 1 + drivers/storage/nvmed/src/main.rs | 1 + drivers/storage/usbscsid/src/main.rs | 9 +- drivers/usb/usbctl/Cargo.toml | 1 + drivers/usb/usbctl/src/main.rs | 4 +- drivers/usb/usbhubd/src/main.rs | 7 +- drivers/usb/xhcid/src/driver_interface.rs | 77 ++- drivers/usb/xhcid/src/main.rs | 13 +- drivers/usb/xhcid/src/xhci/scheme.rs | 42 +- drivers/vboxd/src/main.rs | 1 + ipcd/src/chan.rs | 67 ++- ipcd/src/main.rs | 34 +- ipcd/src/shm.rs | 30 +- ipcd/src/uds/dgram.rs | 169 ++++-- ipcd/src/uds/mod.rs | 97 ++-- ipcd/src/uds/stream.rs | 104 +++- logd/src/main.rs | 24 +- logd/src/scheme.rs | 103 ++-- netstack/src/main.rs | 28 +- netstack/src/scheme/mod.rs | 39 +- netstack/src/scheme/netcfg/mod.rs | 145 ++++-- netstack/src/scheme/socket.rs | 310 ++++++----- ptyd/src/controlterm.rs | 3 +- ptyd/src/main.rs | 37 +- ptyd/src/scheme.rs | 123 +++-- ptyd/src/subterm.rs | 3 +- ramfs/src/main.rs | 10 +- ramfs/src/scheme.rs | 121 ++++- randd/src/main.rs | 345 +++++++------ zerod/src/main.rs | 11 +- zerod/src/scheme.rs | 35 +- 90 files changed, 3069 insertions(+), 1125 deletions(-) create mode 100644 bootstrap/src/initnsmgr.rs diff --git a/Cargo.lock b/Cargo.lock index a1f6fd1476..d34bc70473 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,7 +14,7 @@ dependencies = [ "pcid", "redox-scheme", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", "spin 0.9.8", ] @@ -48,7 +48,7 @@ dependencies = [ "plain", "redox-scheme", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", "ron", "rustc-hash", "serde", @@ -68,7 +68,7 @@ dependencies = [ "log", "pcid", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -90,7 +90,7 @@ dependencies = [ "libredox", "redox-scheme", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -214,7 +214,7 @@ dependencies = [ "libc", "libredox", "redox-scheme", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -245,7 +245,7 @@ dependencies = [ "fdt 0.2.0-alpha1", "libredox", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -260,7 +260,7 @@ dependencies = [ "orbclient", "pcid", "redox-scheme", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -462,7 +462,7 @@ dependencies = [ "libredox", "log", "redox-log", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -600,7 +600,7 @@ dependencies = [ "log", "partitionlib", "redox-scheme", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -614,7 +614,7 @@ dependencies = [ "libredox", "log", "redox-scheme", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -624,7 +624,7 @@ dependencies = [ "daemon", "libredox", "redox-scheme", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -677,7 +677,7 @@ dependencies = [ "log", "pcid", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -757,7 +757,7 @@ dependencies = [ "ransid", "redox-scheme", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -776,7 +776,7 @@ dependencies = [ "ransid", "redox-scheme", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -903,7 +903,7 @@ dependencies = [ [[package]] name = "generic-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#3362ca356a9dafdfd7d74fc88d97609f400f7329" +source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#3a8f64aa143184dc11fe1927a4f9e55c33ae3052" [[package]] name = "getrandom" @@ -960,7 +960,7 @@ dependencies = [ "libredox", "log", "redox-ioctl", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -1019,6 +1019,7 @@ dependencies = [ "common", "daemon", "fdt 0.1.5", + "libredox", "log", "ron", ] @@ -1058,7 +1059,7 @@ dependencies = [ "log", "pcid", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -1073,7 +1074,7 @@ dependencies = [ "pcid", "redox-scheme", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", "spin 0.9.8", ] @@ -1097,7 +1098,7 @@ dependencies = [ "range-alloc", "redox-scheme", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", "void", ] @@ -1129,7 +1130,7 @@ dependencies = [ "log", "orbclient", "redox-scheme", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -1154,7 +1155,7 @@ dependencies = [ "redox-rt", "redox-scheme", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -1180,7 +1181,7 @@ dependencies = [ "libredox", "pcid", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -1207,14 +1208,14 @@ checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "libredox" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df15f6eac291ed1cf25865b1ee60399f57e7c227e7f51bdbd4c5270396a9ed50" +checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ "bitflags 2.10.0", "ioslice", "libc", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -1238,7 +1239,7 @@ dependencies = [ "driver-block", "libredox", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -1263,7 +1264,7 @@ dependencies = [ "daemon", "libredox", "redox-scheme", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -1360,7 +1361,7 @@ dependencies = [ "partitionlib", "pcid", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", "smallvec 1.15.1", ] @@ -1486,7 +1487,7 @@ dependencies = [ "pico-args", "plain", "redox-scheme", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", "serde", ] @@ -1499,7 +1500,7 @@ dependencies = [ "log", "pcid", "pico-args", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", "serde", "toml 0.5.11", ] @@ -1581,7 +1582,7 @@ dependencies = [ "orbclient", "redox-scheme", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -1592,7 +1593,7 @@ dependencies = [ "libredox", "redox-scheme", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", "redox_termios", ] @@ -1625,7 +1626,7 @@ dependencies = [ "indexmap", "libredox", "redox-scheme", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", "slab", ] @@ -1713,7 +1714,7 @@ dependencies = [ "rand_core 0.5.1", "raw-cpuid", "redox-scheme", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", "sha2", ] @@ -1778,17 +1779,16 @@ dependencies = [ [[package]] name = "redox-ioctl" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#3362ca356a9dafdfd7d74fc88d97609f400f7329" +source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#3a8f64aa143184dc11fe1927a4f9e55c33ae3052" dependencies = [ "drm-sys", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] name = "redox-log" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81460b1526438123d16f0c968dbe42ba7f61e99645109b70e57864a8b66710fb" +version = "0.1.5" +source = "git+https://gitlab.redox-os.org/redox-os/redox-log.git#ac8cbc40a281879f1fee77483342edcc4de89983" dependencies = [ "chrono", "log", @@ -1805,31 +1805,32 @@ checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717" [[package]] name = "redox-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#3362ca356a9dafdfd7d74fc88d97609f400f7329" +source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#3a8f64aa143184dc11fe1927a4f9e55c33ae3052" dependencies = [ "bitflags 2.10.0", "generic-rt", "goblin", + "ioslice", "plain", "redox-path", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] name = "redox-scheme" -version = "0.8.3" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cea2668a5932f878a4298a1d7f8950249bbbb77120fb263da252c589152f5ea" +checksum = "5eb0cee524a5c6e4db180250f699e101ee25f3f79ad2618527e539e028cb6722" dependencies = [ "libredox", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] name = "redox_event" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8eafe403549fab08a0c0862769493ad992625bb5a694aae2df6901e7e4fd05a" +checksum = "c30b73c93693667b5a8e6f54bddcc5a57449ef17c99f4b55c12d5bae4ab79e82" dependencies = [ "bitflags 2.10.0", "libredox", @@ -1847,7 +1848,7 @@ dependencies = [ "redox-rt", "redox-scheme", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", "smoltcp", ] @@ -1862,9 +1863,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96166dafa0886eb81fe1c0a388bece180fbef2135f97c1e2cf8302e74b43b5" +checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" dependencies = [ "bitflags 2.10.0", ] @@ -1884,7 +1885,7 @@ dependencies = [ "libredox", "qemu-exit", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", "redox_termios", ] @@ -1958,7 +1959,7 @@ dependencies = [ "log", "pcid", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -1973,7 +1974,7 @@ dependencies = [ "log", "pcid", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -2011,7 +2012,7 @@ dependencies = [ "log", "redox-scheme", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", "spin 0.9.8", ] @@ -2411,6 +2412,7 @@ name = "usbctl" version = "0.1.0" dependencies = [ "clap 2.34.0", + "common", "xhcid", ] @@ -2423,7 +2425,7 @@ dependencies = [ "inputd", "log", "orbclient", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", "rehid", "xhcid", ] @@ -2434,7 +2436,7 @@ version = "0.1.0" dependencies = [ "common", "log", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", "xhcid", ] @@ -2448,7 +2450,7 @@ dependencies = [ "libredox", "plain", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", "thiserror 1.0.69", "xhcid", ] @@ -2486,7 +2488,7 @@ dependencies = [ "orbclient", "pcid", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -2515,7 +2517,7 @@ dependencies = [ "orbclient", "ransid", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] @@ -2531,7 +2533,7 @@ dependencies = [ "log", "pcid", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", "spin 0.10.0", "static_assertions", "thiserror 1.0.69", @@ -2550,7 +2552,7 @@ dependencies = [ "log", "pcid", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", "static_assertions", "thiserror 1.0.69", ] @@ -2573,7 +2575,7 @@ dependencies = [ "orbclient", "pcid", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", "spin 0.9.8", "static_assertions", "virtio-core", @@ -2590,7 +2592,7 @@ dependencies = [ "libredox", "log", "pcid", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", "static_assertions", "virtio-core", ] @@ -2801,7 +2803,7 @@ dependencies = [ "plain", "redox-scheme", "redox_event", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", "regex", "serde", "serde_json", @@ -2837,7 +2839,7 @@ dependencies = [ "daemon", "libredox", "redox-scheme", - "redox_syscall 0.6.0", + "redox_syscall 0.7.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 651ad56140..64c99ae0a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,16 +77,14 @@ exclude = ["bootstrap"] [workspace.dependencies] libc = "0.2.180" log = "0.4" -# libredox 0.1.12 uses redox_syscall 0.7, wait until that is supported -libredox = "=0.1.11" +libredox = "0.1.12" orbclient = { git = "https://gitlab.redox-os.org/redox-os/orbclient" } -# redox_event 0.4.3 uses redox_syscall 0.7, wait until that is supported -redox_event = "=0.4.2" +redox_event = "0.4.3" redox-ioctl = { git = "https://gitlab.redox-os.org/redox-os/relibc.git" } -redox-log = "0.1.4" +redox-log = { git = "https://gitlab.redox-os.org/redox-os/redox-log.git" } redox-rt = { git = "https://gitlab.redox-os.org/redox-os/relibc.git", default-features = false } -redox-scheme = "0.8.3" -redox_syscall = "0.6" +redox-scheme = "0.9.0" +redox_syscall = "0.7" redox_termios = "0.1.3" [patch.crates-io] diff --git a/audiod/src/main.rs b/audiod/src/main.rs index 192967572f..c17c7a58a9 100644 --- a/audiod/src/main.rs +++ b/audiod/src/main.rs @@ -8,6 +8,7 @@ use ioslice::IoSlice; use libredox::flag; use libredox::{error::Result, Fd}; +use redox_scheme::scheme::register_sync_scheme; use redox_scheme::wrappers::ReadinessBased; use redox_scheme::Socket; @@ -47,29 +48,32 @@ fn daemon(daemon: Daemon) -> anyhow::Result<()> { let pid = libredox::call::getpid()?; - let socket = Socket::create("audio").context("failed to create scheme")?; + let hw_file = Fd::open("/scheme/audiohw", flag::O_WRONLY | flag::O_CLOEXEC, 0)?; + + let socket = Socket::create().context("failed to create scheme")?; + + let scheme = Arc::new(Mutex::new(AudioScheme::new())); + + { + let mut scheme = scheme.lock().unwrap(); + register_sync_scheme(&socket, "audio", &mut *scheme) + .expect("audiod: failed to register scheme to namespace"); + } // The scheme is now ready to accept requests, notify the original process daemon.ready(); - let hw_file = Fd::open("/scheme/audiohw", flag::O_WRONLY | flag::O_CLOEXEC, 0)?; - - let scheme = Arc::new(Mutex::new(AudioScheme::new())); - // Enter a constrained namespace let ns = libredox::call::mkns(&[ - //IoSlice::new(b"memory"), TODO: already included, uncommenting gives EEXIST + IoSlice::new(b"memory"), IoSlice::new(b"rand"), // for HashMap ]) .context("failed to make namespace")?; - libredox::call::setrens(ns, ns).context("failed to set namespace")?; + libredox::call::setns(ns).context("failed to set namespace")?; // Spawn a thread to mix and send audio data let scheme_thread = scheme.clone(); - let _thread = thread::spawn(move || { - libredox::call::setrens(ns, ns).unwrap(); - thread(scheme_thread, pid, hw_file) - }); + let _thread = thread::spawn(move || thread(scheme_thread, pid, hw_file)); let mut readiness = ReadinessBased::new(&socket, 16); diff --git a/audiod/src/scheme.rs b/audiod/src/scheme.rs index 2e078361c2..e23a7098a3 100644 --- a/audiod/src/scheme.rs +++ b/audiod/src/scheme.rs @@ -1,7 +1,7 @@ use redox_scheme::{CallerCtx, OpenResult}; use std::collections::{BTreeMap, VecDeque}; use std::str; -use syscall::error::{Error, Result, EBADF, EINVAL, ENOENT, EWOULDBLOCK}; +use syscall::error::{Error, Result, EACCES, EBADF, EINVAL, ENOENT, EWOULDBLOCK}; use redox_scheme::scheme::SchemeSync; use syscall::schemev2::NewFdFlags; @@ -16,6 +16,7 @@ enum Handle { // TODO: move volume to audiohw:? // TODO: Use SYS_CALL to handle this better? Volume, + SchemeRoot, } pub struct AudioScheme { @@ -64,7 +65,27 @@ impl AudioScheme { } impl SchemeSync for AudioScheme { - fn open(&mut self, path: &str, _flags: usize, _ctx: &CallerCtx) -> Result { + fn scheme_root(&mut self) -> Result { + let id = self.next_id; + self.next_id += 1; + self.handles.insert(id, Handle::SchemeRoot); + Ok(id) + } + fn openat( + &mut self, + dirfd: usize, + path: &str, + _flags: usize, + _fcntl_flags: u32, + _ctx: &CallerCtx, + ) -> Result { + if !matches!( + self.handles.get(&dirfd).ok_or(Error::new(EBADF))?, + Handle::SchemeRoot + ) { + return Err(Error::new(EACCES)); + } + let (handle, flags) = match path.trim_matches('/') { "" => ( Handle::Audio { @@ -109,6 +130,7 @@ impl SchemeSync for AudioScheme { Ok(len) } + Handle::SchemeRoot => Err(Error::new(EBADF)), } } @@ -158,6 +180,7 @@ impl SchemeSync for AudioScheme { Ok(0) } } + Handle::SchemeRoot => Err(Error::new(EBADF)), } } } diff --git a/bootstrap/Cargo.lock b/bootstrap/Cargo.lock index 3ec912b073..a551a4d34b 100644 --- a/bootstrap/Cargo.lock +++ b/bootstrap/Cargo.lock @@ -41,7 +41,7 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "generic-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#5d378c146078a58c025ad9c857402c983e5e8b37" +source = "git+https://gitlab.redox-os.org/Ibuki.O/relibc.git?branch=namespace-improvement#aa4bd6493b3682c8bee1f5c391043b54554d2ba2" [[package]] name = "goblin" @@ -63,6 +63,12 @@ dependencies = [ "foldhash", ] +[[package]] +name = "ioslice" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e571352c8a3b89074d12e3ee5173ffe162159105352aaaf1fc5764da747e31b" + [[package]] name = "libc" version = "0.2.180" @@ -71,9 +77,9 @@ checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "libredox" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df15f6eac291ed1cf25865b1ee60399f57e7c227e7f51bdbd4c5270396a9ed50" +checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ "bitflags", "libc", @@ -144,11 +150,12 @@ checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717" [[package]] name = "redox-rt" version = "0.1.0" -source = "git+https://gitlab.redox-os.org/redox-os/relibc.git#5d378c146078a58c025ad9c857402c983e5e8b37" +source = "git+https://gitlab.redox-os.org/Ibuki.O/relibc.git?branch=namespace-improvement#aa4bd6493b3682c8bee1f5c391043b54554d2ba2" dependencies = [ "bitflags", "generic-rt", "goblin", + "ioslice", "plain", "redox-path", "redox_syscall", @@ -156,9 +163,9 @@ dependencies = [ [[package]] name = "redox-scheme" -version = "0.8.3" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cea2668a5932f878a4298a1d7f8950249bbbb77120fb263da252c589152f5ea" +checksum = "5eb0cee524a5c6e4db180250f699e101ee25f3f79ad2618527e539e028cb6722" dependencies = [ "libredox", "redox_syscall", @@ -166,9 +173,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96166dafa0886eb81fe1c0a388bece180fbef2135f97c1e2cf8302e74b43b5" +checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" dependencies = [ "bitflags", ] diff --git a/bootstrap/Cargo.toml b/bootstrap/Cargo.toml index c6696c1257..7ad38af110 100644 --- a/bootstrap/Cargo.toml +++ b/bootstrap/Cargo.toml @@ -15,19 +15,18 @@ hashbrown = { version = "0.15", default-features = false, features = [ "default-hasher", ] } linked_list_allocator = "0.10" -# libredox 0.1.12 uses redox_syscall 0.7, wait until that is supported -libredox = { version = "=0.1.11", default-features = false } +libredox = { version = "0.1.12", default-features = false } log = { version = "0.4", default-features = false } plain = "0.2" redox-initfs = { path = "../initfs", default-features = false } -redox_syscall = { version = "0.6", default-features = false } -redox-scheme = { version = "0.8.3", default-features = false } +redox_syscall = { version = "0.7", default-features = false } +redox-scheme = { version = "0.9.0", default-features = false } redox-path = "0.3.1" slab = { version = "0.4.9", default-features = false } arrayvec = { version = "0.7.6", default-features = false } [target.'cfg(target_os = "redox")'.dependencies] -redox-rt = { git = "https://gitlab.redox-os.org/redox-os/relibc.git", default-features = false } +redox-rt = { git = "https://gitlab.redox-os.org/Ibuki.O/relibc.git", branch = "namespace-improvement", default-features = false } [profile.release] panic = "abort" diff --git a/bootstrap/src/aarch64.rs b/bootstrap/src/aarch64.rs index 6e1a732175..ee39725f86 100644 --- a/bootstrap/src/aarch64.rs +++ b/bootstrap/src/aarch64.rs @@ -2,7 +2,7 @@ use core::mem; use syscall::{data::Map, flag::MapFlags, number::SYS_FMAP}; pub const USERMODE_END: usize = 0x0000_8000_0000_0000; -pub const STACK_START: usize = USERMODE_END - STACK_SIZE; +pub const STACK_START: usize = USERMODE_END - syscall::KERNEL_METADATA_SIZE - STACK_SIZE; const STACK_SIZE: usize = 64 * 1024; // 64 KiB static MAP: Map = Map { diff --git a/bootstrap/src/exec.rs b/bootstrap/src/exec.rs index fe45a04c5e..7f39a1b0d8 100644 --- a/bootstrap/src/exec.rs +++ b/bootstrap/src/exec.rs @@ -1,13 +1,18 @@ +use alloc::sync::Arc; use core::str::FromStr; use alloc::borrow::ToOwned; use alloc::vec::Vec; +use syscall::CallFlags; +use syscall::data::{GlobalSchemes, KernelSchemeInfo}; use syscall::flag::{O_CLOEXEC, O_RDONLY}; -use syscall::{Error, EINTR}; +use syscall::{EINTR, Error}; use redox_rt::proc::*; +use crate::KernelSchemeMap; + struct Logger; impl log::Log for Logger { @@ -27,9 +32,38 @@ impl log::Log for Logger { fn flush(&self) {} } +const KERNEL_METADATA_BASE: usize = crate::arch::USERMODE_END - syscall::KERNEL_METADATA_SIZE; + pub fn main() -> ! { - let auth = FdGuard::open("/scheme/kernel.proc/authority", O_CLOEXEC) - .expect("failed to get proc authority"); + let mut cursor = KERNEL_METADATA_BASE; + let kernel_scheme_infos = unsafe { + let base_ptr = cursor as *const u8; + let infos_len = *(base_ptr as *const usize); + let infos_ptr = base_ptr.add(core::mem::size_of::()) as *const KernelSchemeInfo; + let slice = core::slice::from_raw_parts(infos_ptr, infos_len); + cursor += core::mem::size_of::() // kernel scheme number size + + infos_len // kernel scheme number + * core::mem::size_of::(); + slice + }; + let scheme_creation_cap = unsafe { + let base_ptr = cursor as *const u8; + let cap = *(base_ptr as *const usize); + cap + }; + + let kernel_schemes = KernelSchemeMap::new(kernel_scheme_infos); + + let auth = FdGuard::new( + *kernel_schemes + .get(GlobalSchemes::Proc) + .expect("failed to get proc fd"), + ); + let pipe_fd = *kernel_schemes + .get(GlobalSchemes::Pipe) + .expect("failed to get pipe fd"); + let infos_arc = Arc::new(kernel_schemes); + let this_thr_fd = auth .dup(b"cur-context") .expect("failed to open open_via_dup") @@ -39,8 +73,17 @@ pub fn main() -> ! { let mut env_bytes = [0_u8; 4096]; let envs = { - let fd = FdGuard::open("/scheme/sys/env", O_RDONLY | O_CLOEXEC) - .expect("bootstrap: failed to open env"); + let fd = FdGuard::new( + syscall::openat( + *infos_arc + .get(GlobalSchemes::Sys) + .expect("failed to get sys fd"), + "env", + O_RDONLY | O_CLOEXEC, + 0, + ) + .expect("bootstrap: failed to open env"), + ); let bytes_read = fd .read(&mut env_bytes) .expect("bootstrap: failed to read env"); @@ -80,10 +123,12 @@ pub fn main() -> ! { (*(core::ptr::addr_of!(__initfs_header) as *const redox_initfs::types::Header)).initfs_size }; - spawn( + let infos_arc_clone = infos_arc.clone(); + let initfs_fd = spawn( "initfs daemon", &auth, &this_thr_fd, + pipe_fd, move |write_fd| unsafe { // Creating a reference to NULL is UB. Mask the UB for now using black_box. // FIXME use a raw pointer and inline asm for reading instead for the initfs header. @@ -93,14 +138,39 @@ pub fn main() -> ! { crate::initfs::run( core::slice::from_raw_parts(initfs_start, initfs_length), write_fd, + &infos_arc_clone, + scheme_creation_cap, ); }, ); - spawn("process manager", &auth, &this_thr_fd, |write_fd| { - crate::procmgr::run(write_fd, &auth) - }); - let (init_proc_fd, init_thr_fd) = unsafe { redox_rt::proc::make_init() }; + let infos_arc_clone = infos_arc.clone(); + let proc_fd = spawn( + "process manager", + &auth, + &this_thr_fd, + pipe_fd, + |write_fd| crate::procmgr::run(write_fd, &auth, &infos_arc_clone, scheme_creation_cap), + ); + + let infos_arc_clone = infos_arc.clone(); + let initns_fd = spawn( + "init namespace manager", + &auth, + &this_thr_fd, + pipe_fd, + |write_fd| { + crate::initnsmgr::run( + write_fd, + &infos_arc_clone, + initfs_fd, + proc_fd, + scheme_creation_cap, + ) + }, + ); + + let (init_proc_fd, init_thr_fd) = unsafe { make_init(proc_fd) }; // from this point, this_thr_fd is no longer valid const CWD: &[u8] = b"/scheme/initfs"; @@ -111,21 +181,27 @@ pub fn main() -> ! { umask: redox_rt::sys::get_umask(), thr_fd: init_thr_fd.as_raw_fd(), proc_fd: init_proc_fd.as_raw_fd(), + ns_fd: Some(initns_fd), }; - let path = "/scheme/initfs/bin/init"; + let path = "/bin/init"; - let image_file = FdGuard::open(path, O_RDONLY | O_CLOEXEC) - .expect("failed to open init") - .to_upper() - .unwrap(); + let image_file = FdGuard::new( + syscall::openat(initfs_fd, path, O_RDONLY | O_CLOEXEC, 0).expect("failed to open init"), + ) + .to_upper() + .unwrap(); + + drop(infos_arc); + + let exe_path = alloc::format!("/scheme/initfs{}", path); fexec_impl( image_file, init_thr_fd, init_proc_fd, - path.as_bytes(), - &[path.as_bytes()], + exe_path.as_bytes(), + &[exe_path.as_bytes()], &envs, &extrainfo, None, @@ -139,9 +215,10 @@ pub(crate) fn spawn( name: &str, auth: &FdGuard, this_thr_fd: &FdGuardUpper, - inner: impl FnOnce(usize), -) { - let read = syscall::open("/scheme/pipe", O_CLOEXEC).expect("failed to open sync read pipe"); + pipe_fd: usize, + inner: impl FnOnce(usize) -> !, +) -> usize { + let read = syscall::openat(pipe_fd, "", O_CLOEXEC, 0).expect("failed to open sync read pipe"); // The write pipe will not inherit O_CLOEXEC, but is closed by the daemon later. let write = syscall::dup(read, b"write").expect("failed to open sync write pipe"); @@ -157,15 +234,23 @@ pub(crate) fn spawn( // Return in order to execute init, as the parent. Ok(_) => { let _ = syscall::close(write); + + let mut new_fd = usize::MAX; + let fd_bytes = unsafe { + core::slice::from_raw_parts_mut( + core::slice::from_mut(&mut new_fd).as_mut_ptr() as *mut u8, + core::mem::size_of::(), + ) + }; loop { - match syscall::read(read, &mut [0]) { + match syscall::call_ro(read, fd_bytes, CallFlags::FD | CallFlags::FD_UPPER, &[]) { Err(Error { errno: EINTR }) => continue, _ => break, } } - return; + return new_fd; } } - inner(write); + inner(write) } diff --git a/bootstrap/src/i686.rs b/bootstrap/src/i686.rs index a3805d10e2..5fbbf47908 100644 --- a/bootstrap/src/i686.rs +++ b/bootstrap/src/i686.rs @@ -3,7 +3,7 @@ use syscall::{data::Map, flag::MapFlags, number::SYS_FMAP}; const STACK_SIZE: usize = 64 * 1024; // 64 KiB pub const USERMODE_END: usize = 0x8000_0000; -pub const STACK_START: usize = USERMODE_END - STACK_SIZE; +pub const STACK_START: usize = USERMODE_END - syscall::KERNEL_METADATA_SIZE - STACK_SIZE; static MAP: Map = Map { offset: 0, diff --git a/bootstrap/src/initfs.rs b/bootstrap/src/initfs.rs index 5a1c57d18a..8196500c77 100644 --- a/bootstrap/src/initfs.rs +++ b/bootstrap/src/initfs.rs @@ -6,13 +6,12 @@ use core::str; use alloc::string::String; use hashbrown::HashMap; -use redox_initfs::{InitFs, Inode, InodeDir, InodeKind, InodeStruct, types::Timespec}; +use redox_initfs::{types::Timespec, InitFs, Inode, InodeDir, InodeKind, InodeStruct}; use redox_path::canonicalize_to_standard; -use redox_scheme::{CallerCtx, OpenResult, RequestKind, scheme::SchemeSync}; +use redox_scheme::{scheme::SchemeSync, CallerCtx, OpenResult, RequestKind}; use redox_scheme::{SignalBehavior, Socket}; -use syscall::PAGE_SIZE; use syscall::data::Stat; use syscall::dirent::DirEntry; use syscall::dirent::DirentBuf; @@ -20,8 +19,30 @@ use syscall::dirent::DirentKind; use syscall::error::*; use syscall::flag::*; use syscall::schemev2::NewFdFlags; +use syscall::PAGE_SIZE; -struct Handle { +use crate::KernelSchemeMap; + +enum Handle { + Node(Node), + SchemeRoot, +} +impl Handle { + fn as_node(&self) -> Result<&Node> { + match self { + Handle::Node(n) => Ok(n), + _ => Err(Error::new(EBADF)), + } + } + fn as_node_mut(&mut self) -> Result<&mut Node> { + match self { + Handle::Node(n) => Ok(n), + _ => Err(Error::new(EBADF)), + } + } +} + +struct Node { inode: Inode, // TODO: Any better way to implement fpath? Or maybe work around it, e.g. by giving paths such // as `initfs:__inodes__/`? @@ -91,7 +112,20 @@ fn inode_len(inode: InodeStruct<'static>) -> Result { } impl SchemeSync for InitFsScheme { - fn open(&mut self, path: &str, flags: usize, _ctx: &CallerCtx) -> Result { + fn openat( + &mut self, + dirfd: usize, + path: &str, + flags: usize, + _fcntl_flags: u32, + ctx: &CallerCtx, + ) -> Result { + if !matches!( + self.handles.get(&dirfd).ok_or(Error::new(EBADF))?, + Handle::SchemeRoot + ) { + return Err(Error::new(EACCES)); + } let mut components = path // trim leading and trailing slash .trim_matches('/') @@ -158,10 +192,10 @@ impl SchemeSync for InitFsScheme { let id = self.next_id(); let old = self.handles.insert( id, - Handle { + Handle::Node(Node { inode: current_inode, filename: path.into(), - }, + }), ); assert!(old.is_none()); @@ -183,7 +217,11 @@ impl SchemeSync for InitFsScheme { return Ok(0); }; - let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + let handle = self + .handles + .get_mut(&id) + .ok_or(Error::new(EBADF))? + .as_node_mut()?; match Self::get_inode(&self.fs, handle.inode)?.kind() { InodeKind::File(file) => { @@ -222,7 +260,7 @@ impl SchemeSync for InitFsScheme { let Ok(offset) = u32::try_from(opaque_offset) else { return Ok(buf); }; - let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?.as_node()?; let InodeKind::Dir(dir) = Self::get_inode(&self.fs, handle.inode)?.kind() else { return Err(Error::new(ENOTDIR)); }; @@ -247,19 +285,23 @@ impl SchemeSync for InitFsScheme { } fn fsize(&mut self, id: usize, _ctx: &CallerCtx) -> Result { - let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + let handle = self + .handles + .get_mut(&id) + .ok_or(Error::new(EBADF))? + .as_node_mut()?; Ok(inode_len(Self::get_inode(&self.fs, handle.inode)?)? as u64) } fn fcntl(&mut self, id: usize, _cmd: usize, _arg: usize, _ctx: &CallerCtx) -> Result { - let _handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + let _handle = self.handles.get(&id).ok_or(Error::new(EBADF))?.as_node()?; Ok(0) } fn fpath(&mut self, id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result { - let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?.as_node()?; // TODO: Copy scheme part in kernel let scheme_path = b"/scheme/initfs"; @@ -274,7 +316,7 @@ impl SchemeSync for InitFsScheme { } fn fstat(&mut self, id: usize, stat: &mut Stat, _ctx: &CallerCtx) -> Result<()> { - let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?.as_node()?; let Timespec { sec, nsec } = self.fs.image_creation_time(); @@ -315,8 +357,10 @@ impl SchemeSync for InitFsScheme { _ctx: &CallerCtx, ) -> syscall::Result { let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; - - let data = match Self::get_inode(&self.fs, handle.inode)?.kind() { + let Handle::Node(node) = handle else { + return Err(Error::new(EBADF)); + }; + let data = match Self::get_inode(&self.fs, node.inode)?.kind() { InodeKind::File(file) => file.data().map_err(|_| Error::new(EIO))?, InodeKind::Dir(_) => return Err(Error::new(EISDIR)), InodeKind::Link(link) => return Err(Error::new(ELOOP)), @@ -339,12 +383,30 @@ impl SchemeSync for InitFsScheme { } } -pub fn run(bytes: &'static [u8], sync_pipe: usize) -> ! { +pub fn run( + bytes: &'static [u8], + sync_pipe: usize, + kernel_schemes: &KernelSchemeMap, + scheme_creation_cap: usize, +) -> ! { + log::info!("bootstrap: starting initfs scheme"); let mut scheme = InitFsScheme::new(bytes); - let socket = Socket::create("initfs").expect("failed to open initfs scheme socket"); + let socket = Socket::create_inner(scheme_creation_cap, false) + .expect("failed to open initfs scheme socket"); + let _ = syscall::close(scheme_creation_cap); - let _ = syscall::write(sync_pipe, &[0]); + for fd in kernel_schemes.0.values() { + let _ = syscall::close(*fd); + } + + // send open-capability to bootstrap + let new_id = scheme.next_id(); + scheme.handles.insert(new_id, Handle::SchemeRoot); + let cap_fd = socket + .create_this_scheme_fd(0, new_id, 0, 0) + .expect("failed to issue initfs root fd"); + let _ = syscall::call_rw(sync_pipe, &mut cap_fd.to_ne_bytes(), CallFlags::FD, &[]); let _ = syscall::close(sync_pipe); loop { @@ -380,17 +442,23 @@ pub fn run(bytes: &'static [u8], sync_pipe: usize) -> ! { #[unsafe(no_mangle)] pub unsafe extern "C" fn redox_read_v1(fd: usize, ptr: *mut u8, len: usize) -> isize { - Error::mux(syscall::read(fd, core::slice::from_raw_parts_mut(ptr, len))) as isize + Error::mux(syscall::read(fd, unsafe { + core::slice::from_raw_parts_mut(ptr, len) + })) as isize } #[unsafe(no_mangle)] pub unsafe extern "C" fn redox_write_v1(fd: usize, ptr: *const u8, len: usize) -> isize { - Error::mux(syscall::write(fd, core::slice::from_raw_parts(ptr, len))) as isize + Error::mux(syscall::write(fd, unsafe { + core::slice::from_raw_parts(ptr, len) + })) as isize } #[unsafe(no_mangle)] -pub unsafe extern "C" fn redox_open_v1(ptr: *const u8, len: usize, flags: usize) -> isize { - Error::mux(syscall::open(core::str::from_raw_parts(ptr, len), flags)) as isize +pub unsafe fn redox_dup_v1(fd: usize, buf: *const u8, len: usize) -> isize { + Error::mux(syscall::dup(fd, unsafe { + core::slice::from_raw_parts(buf, len) + })) as isize } #[unsafe(no_mangle)] diff --git a/bootstrap/src/initnsmgr.rs b/bootstrap/src/initnsmgr.rs new file mode 100644 index 0000000000..7e79270388 --- /dev/null +++ b/bootstrap/src/initnsmgr.rs @@ -0,0 +1,536 @@ +use super::KernelSchemeMap; + +use alloc::rc::Rc; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use core::cell::RefCell; +use core::fmt::Debug; +use core::mem; +use hashbrown::HashMap; +use log::{error, warn}; +use redox_path::RedoxPath; +use redox_path::RedoxScheme; +use redox_rt::protocol::{NsDup, NsPermissions}; +use redox_scheme::{ + CallerCtx, OpenResult, RequestKind, Response, SendFdRequest, SignalBehavior, Socket, + scheme::SchemeSync, +}; +use syscall::dirent::{DirEntry, DirentBuf, DirentKind}; +use syscall::{CallFlags, FobtainFdFlags, error::*, schemev2::NewFdFlags}; + +#[derive(Debug, Clone)] +struct Namespace { + schemes: HashMap, +} + +impl Namespace { + fn fork(&self, buf: &[u8]) -> Result { + let mut schemes = HashMap::new(); + let mut cursor = 0; + while cursor < buf.len() { + let len = read_num::(&buf[cursor..])?; + cursor += mem::size_of::(); + let name = String::from_utf8(Vec::from(&buf[cursor..cursor + len])) + .map_err(|_| Error::new(EINVAL))?; + cursor += len; + if name.ends_with('*') { + let prefix = &name[..name.len() - 1]; + for (registered_name, fd) in &self.schemes { + if registered_name.starts_with(prefix) { + schemes.insert(registered_name.clone(), *fd); + } + } + } else { + let Some(fd) = self.schemes.get(&name) else { + warn!("Scheme {} not found in namespace", name); + continue; + }; + schemes.insert(name, *fd); + } + } + Ok(Self { schemes }) + } + fn get_scheme_fd(&self, scheme: &str) -> Option { + self.schemes.get(scheme).copied() + } +} + +#[derive(Debug, Clone)] +struct NamespaceAccess { + namespace: Rc>, + permission: NsPermissions, +} + +impl NamespaceAccess { + fn has_permission(&self, permission: NsPermissions) -> bool { + self.permission.contains(permission) + } +} + +#[derive(Debug, Clone)] +struct SchemeRegister { + target_namespace: Rc>, + scheme_name: String, +} + +impl SchemeRegister { + fn register(&self, fd: usize) -> Result<()> { + let mut ns = self.target_namespace.borrow_mut(); + if ns.schemes.contains_key(&self.scheme_name) { + return Err(Error::new(EEXIST)); + } + ns.schemes.insert(self.scheme_name.clone(), fd); + Ok(()) + } +} + +#[derive(Debug, Clone)] +enum Handle { + Access(NamespaceAccess), + Register(SchemeRegister), + List(NamespaceAccess), +} + +pub struct NamespaceScheme<'sock> { + socket: &'sock Socket, + handles: HashMap, + root_namespace: Namespace, + next_id: usize, + scheme_creation_cap: usize, +} + +const HIGH_PERMISSIONS: NsPermissions = NsPermissions::SCHEME_CREATE; + +impl<'sock> NamespaceScheme<'sock> { + pub fn new( + socket: &'sock Socket, + schemes: HashMap, + scheme_creation_cap: usize, + ) -> Self { + let mut scheme = Self { + socket, + handles: HashMap::new(), + root_namespace: Namespace { schemes }, + next_id: 0, + scheme_creation_cap, + }; + + scheme + } + + fn add_namespace(&mut self, id: usize, schemes: Namespace, permission: NsPermissions) { + let handle = Handle::Access(NamespaceAccess { + namespace: Rc::new(RefCell::new(schemes)), + permission, + }); + self.handles.insert(id, handle); + } + + fn get_ns_access(&self, id: usize) -> Option<&NamespaceAccess> { + let handle = self.handles.get(&id); + match handle { + Some(Handle::Access(access)) => Some(access), + _ => None, + } + } + + fn open_namespace_resource( + &self, + ns_access: &NamespaceAccess, + reference: &str, + _flags: usize, + _fcntl_flags: u32, + _ctx: &CallerCtx, + ) -> Result { + match reference { + "scheme-creation-cap" => { + if !ns_access.has_permission(NsPermissions::SCHEME_CREATE) { + error!("Permission denied to get scheme creation capability"); + return Err(Error::new(EACCES)); + } + Ok(syscall::dup(self.scheme_creation_cap, &[])?) + } + _ => { + error!("Unknown special reference: {}", reference); + return Err(Error::new(EINVAL)); + } + } + } + + fn open_scheme_resource( + &self, + ns: &Namespace, + scheme: &str, + reference: &str, + flags: usize, + fcntl_flags: u32, + ctx: &CallerCtx, + ) -> Result { + let Some(cap_fd) = ns.get_scheme_fd(scheme) else { + warn!("Scheme {} not found in namespace", scheme); + return Err(Error::new(ENODEV)); + }; + + let scheme_fd = syscall::openat_with_filter( + cap_fd, + reference, + flags, + fcntl_flags as usize, + ctx.uid, + ctx.gid, + )?; + + Ok(scheme_fd) + } + + fn fork_namespace(&mut self, namespace: Rc>, names: &[u8]) -> Result { + let new_id = self.next_id; + let new_namespace = namespace.borrow().fork(names).map_err(|e| { + error!("Failed to fork namespace {}: {}", new_id, e); + e + })?; + self.add_namespace( + new_id, + new_namespace, + NsPermissions::all().difference(HIGH_PERMISSIONS), + ); + self.next_id += 1; + Ok(new_id) + } + + fn shrink_permissions( + &mut self, + mut ns: NamespaceAccess, + permission: NsPermissions, + ) -> Result { + ns.permission = ns.permission.intersection(permission); + let next_id = self.next_id; + self.handles.insert(next_id, Handle::Access(ns)); + self.next_id += 1; + Ok(next_id) + } +} + +impl<'sock> SchemeSync for NamespaceScheme<'sock> { + fn openat( + &mut self, + fd: usize, + path: &str, + flags: usize, + fcntl_flags: u32, + ctx: &CallerCtx, + ) -> Result { + let ns_access = { + let handle = self.handles.get(&fd); + match handle { + Some(Handle::Access(access)) => Some(access), + _ => None, + } + } + .ok_or_else(|| { + error!("Namespace with ID {} not found", fd); + Error::new(ENOENT) + })?; + let redox_path = RedoxPath::from_absolute(path).ok_or(Error::new(EINVAL))?; + let (scheme, reference) = redox_path.as_parts().ok_or(Error::new(EINVAL))?; + + let res_fd = match scheme.as_ref() { + "namespace" => self.open_namespace_resource( + ns_access, + reference.as_ref(), + flags, + fcntl_flags, + ctx, + )?, + "" => { + if !ns_access.has_permission(NsPermissions::LIST) { + error!("Permission denied to list schemes in namespace {}", fd); + return Err(Error::new(EACCES)); + } + + let new_id = self.next_id; + self.next_id += 1; + + self.handles.insert(new_id, Handle::List(ns_access.clone())); + + return Ok(OpenResult::ThisScheme { + number: new_id, + flags: NewFdFlags::empty(), + }); + } + _ => self.open_scheme_resource( + &ns_access.namespace.borrow(), + scheme.as_ref(), + reference.as_ref(), + flags, + fcntl_flags, + ctx, + )?, + }; + + Ok(OpenResult::OtherScheme { fd: res_fd }) + } + + fn dup(&mut self, id: usize, buf: &[u8], _ctx: &CallerCtx) -> Result { + let ns_access = self.get_ns_access(id).ok_or_else(|| { + error!("Namespace with ID {} not found", id); + Error::new(ENOENT) + })?; + + let raw_kind = read_num::(buf)?; + let Some(kind) = NsDup::try_from_raw(raw_kind) else { + error!("Unknown dup kind: {}", raw_kind); + return Err(Error::new(EINVAL)); + }; + let payload = &buf[mem::size_of::()..]; + let new_id = match kind { + NsDup::ForkNs => { + let ns = ns_access.namespace.clone(); + let _ = ns_access; + self.fork_namespace(ns, payload)? + } + NsDup::ShrinkPermissions => self.shrink_permissions( + ns_access.clone(), + NsPermissions::from_bits_truncate(read_num::(payload)?), + )?, + NsDup::IssueRegister => { + let name = core::str::from_utf8(payload).map_err(|_| Error::new(EINVAL))?; + let scheme_name = RedoxScheme::new(name).ok_or_else(|| { + error!("Invalid scheme name: {}", name); + Error::new(EINVAL) + })?; + + if !ns_access.has_permission(NsPermissions::INSERT) { + error!( + "Permission denied to issue register capability for namespace {}", + id + ); + return Err(Error::new(EACCES)); + } + let new_id = self.next_id; + let register_cap = Handle::Register(SchemeRegister { + target_namespace: ns_access.namespace.clone(), + scheme_name: scheme_name.as_ref().to_string(), + }); + self.handles.insert(new_id, register_cap); + self.next_id += 1; + new_id + } + }; + + Ok(OpenResult::ThisScheme { + number: new_id, + flags: NewFdFlags::empty(), + }) + } + + fn unlinkat(&mut self, fd: usize, path: &str, flags: usize, ctx: &CallerCtx) -> Result<()> { + let ns_access = { + let handle = self.handles.get(&fd); + match handle { + Some(Handle::Access(access)) => Some(access), + _ => None, + } + } + .ok_or_else(|| { + error!("Namespace with ID {} not found", fd); + Error::new(ENOENT) + })?; + let ns = ns_access.namespace.borrow(); + + let redox_path = RedoxPath::from_absolute(path).ok_or(Error::new(EINVAL))?; + let (scheme, reference) = redox_path.as_parts().ok_or(Error::new(EINVAL))?; + let Some(cap_fd) = ns.get_scheme_fd(scheme.as_ref()) else { + error!("Scheme {} not found in namespace", scheme); + return Err(Error::new(ENODEV)); + }; + + syscall::unlinkat_with_filter(cap_fd, reference, flags, ctx.uid, ctx.gid)?; + + Ok(()) + } + + fn on_close(&mut self, id: usize) { + self.handles.remove(&id); + } + + fn on_sendfd(&mut self, sendfd_request: &SendFdRequest) -> Result { + let namespace_id = sendfd_request.id(); + let num_fds = sendfd_request.num_fds(); + + let handle = self.handles.get(&namespace_id).ok_or_else(|| { + error!("Namespace with ID {} not found", namespace_id); + Error::new(ENOENT) + })?; + let Handle::Register(register_cap) = handle else { + error!( + "Handle with ID {} is not a register capability", + namespace_id + ); + return Err(Error::new(EACCES)); + }; + + if num_fds == 0 { + return Ok(0); + } + if num_fds > 1 { + error!("Can only send one fd at a time"); + return Err(Error::new(EINVAL)); + } + let mut new_fd = usize::MAX; + if let Err(e) = sendfd_request.obtain_fd( + &self.socket, + FobtainFdFlags::UPPER_TBL, + core::slice::from_mut(&mut new_fd), + ) { + error!("on_sendfd: obtain_fd failed with error: {:?}", e); + return Err(e); + } + register_cap.register(new_fd)?; + + Ok(num_fds) + } + + fn getdents<'buf>( + &mut self, + id: usize, + mut buf: DirentBuf<&'buf mut [u8]>, + opaque_offset: u64, + ) -> Result> { + let Handle::List(ns_access) = self.handles.get(&id).ok_or(Error::new(EBADF))? else { + return Err(Error::new(ENOTDIR)); + }; + + if !ns_access.has_permission(NsPermissions::LIST) { + return Err(Error::new(EACCES)); + } + + let ns = ns_access.namespace.borrow(); + + let opaque_offset = opaque_offset as usize; + for (i, (name, _)) in ns.schemes.iter().enumerate().skip(opaque_offset) { + if name.is_empty() { + continue; + } + if let Err(err) = buf.entry(DirEntry { + kind: DirentKind::Unspecified, + name: &name.clone(), + inode: 0, + next_opaque_id: i as u64 + 1, + }) { + if err.errno == EINVAL && i > opaque_offset { + // POSIX allows partial result of getdents + break; + } else { + return Err(err); + } + } + } + + Ok(buf) + } +} + +trait NumFromBytes: Sized + Debug { + fn from_le_bytes_slice(buffer: &[u8]) -> Result; +} + +macro_rules! num_from_bytes_impl { + ($($t:ty),*) => { + $( + impl NumFromBytes for $t { + fn from_le_bytes_slice(buffer: &[u8]) -> Result { + let size = mem::size_of::(); + let buffer_slice = buffer.get(..size).and_then(|s| s.try_into().ok()); + + if let Some(slice) = buffer_slice { + Ok(Self::from_le_bytes(slice)) + } else { + error!( + "read_num: buffer is too short to read num of size {} (buffer len: {})", + size, buffer.len() + ); + Err(Error::new(EINVAL)) + } + } + } + )* + }; +} + +num_from_bytes_impl!(usize); + +fn read_num(buffer: &[u8]) -> Result +where + T: NumFromBytes, +{ + T::from_le_bytes_slice(buffer) +} + +pub fn run( + sync_pipe: usize, + kernel_schemes: &KernelSchemeMap, + initfs_cap: usize, + proc_cap: usize, + scheme_creation_cap: usize, +) -> ! { + let socket = Socket::create_inner(scheme_creation_cap, false) + .expect("failed to open init namespace scheme socket"); + + let mut schemes = HashMap::default(); + for (scheme, fd) in kernel_schemes.0.iter() { + schemes.insert(scheme.as_str().to_string(), *fd); + } + schemes.insert("proc".to_string(), proc_cap); + schemes.insert("initfs".to_string(), initfs_cap); + + let mut scheme = NamespaceScheme::new(&socket, schemes, scheme_creation_cap); + + // send namespace fd to bootstrap + let new_id = scheme.next_id; + scheme.add_namespace(new_id, scheme.root_namespace.clone(), NsPermissions::all()); + scheme.next_id += 1; + let cap_fd = scheme + .socket + .create_this_scheme_fd(0, new_id, 0, 0) + .expect("nsmgr: failed to create namespace fd"); + let _ = syscall::call_wo(sync_pipe, &cap_fd.to_ne_bytes(), CallFlags::FD, &[]); + let _ = syscall::close(sync_pipe); + + log::info!("bootstrap: namespace scheme start!"); + loop { + let Some(req) = socket + .next_request(SignalBehavior::Restart) + .expect("bootstrap: failed to read scheme request from kernel") + else { + break; + }; + match req.kind() { + RequestKind::Call(req) => { + let resp = req.handle_sync(&mut scheme); + + if !socket + .write_response(resp, SignalBehavior::Restart) + .expect("bootstrap: failed to write scheme response to kernel") + { + break; + } + } + RequestKind::OnClose { id } => scheme.on_close(id), + RequestKind::SendFd(sendfd_request) => { + let result = scheme.on_sendfd(&sendfd_request); + let resp = Response::new(result, sendfd_request); + if !socket + .write_response(resp, SignalBehavior::Restart) + .expect("bootstrap: failed to write scheme response to kernel") + { + break; + } + } + + _ => (), + } + } + + unreachable!() +} diff --git a/bootstrap/src/lib.rs b/bootstrap/src/lib.rs index 9b26ce8457..0c4ec3fe5d 100644 --- a/bootstrap/src/lib.rs +++ b/bootstrap/src/lib.rs @@ -1,6 +1,6 @@ #![no_std] #![allow(internal_features)] -#![feature(core_intrinsics, str_from_raw_parts)] +#![feature(core_intrinsics, str_from_raw_parts, never_type)] #[cfg(target_arch = "aarch64")] #[path = "aarch64.rs"] @@ -20,6 +20,7 @@ pub mod arch; pub mod exec; pub mod initfs; +pub mod initnsmgr; pub mod procmgr; pub mod start; @@ -27,7 +28,9 @@ extern crate alloc; use core::cell::UnsafeCell; +use alloc::collections::btree_map::BTreeMap; use syscall::data::Map; +use syscall::data::{GlobalSchemes, KernelSchemeInfo}; use syscall::flag::MapFlags; #[panic_handler] @@ -71,23 +74,25 @@ const HEAP_INCREASE_BY: usize = SIZE; unsafe impl alloc::alloc::GlobalAlloc for Allocator { unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 { - let state = &mut (*ALLOC_STATE.0.get()); + let state = unsafe { &mut (*ALLOC_STATE.0.get()) }; let heap = state.heap.get_or_insert_with(|| { state.heap_top = HEAP_OFF + SIZE; - let _ = syscall::fmap( - !0, - &Map { - offset: 0, - size: SIZE, - address: HEAP_OFF, - flags: MapFlags::PROT_WRITE - | MapFlags::PROT_READ - | MapFlags::MAP_PRIVATE - | MapFlags::MAP_FIXED_NOREPLACE, - }, - ) + let _ = unsafe { + syscall::fmap( + !0, + &Map { + offset: 0, + size: SIZE, + address: HEAP_OFF, + flags: MapFlags::PROT_WRITE + | MapFlags::PROT_READ + | MapFlags::MAP_PRIVATE + | MapFlags::MAP_FIXED_NOREPLACE, + }, + ) + } .expect("failed to map initial heap"); - linked_list_allocator::Heap::new(HEAP_OFF as *mut u8, SIZE) + unsafe { linked_list_allocator::Heap::new(HEAP_OFF as *mut u8, SIZE) } }); match heap.allocate_first_fit(layout) { @@ -97,31 +102,51 @@ unsafe impl alloc::alloc::GlobalAlloc for Allocator { return core::ptr::null_mut(); } - let _ = syscall::fmap( - !0, - &Map { - offset: 0, - size: HEAP_INCREASE_BY, - address: state.heap_top, - flags: MapFlags::PROT_WRITE - | MapFlags::PROT_READ - | MapFlags::MAP_PRIVATE - | MapFlags::MAP_FIXED_NOREPLACE, - }, - ) + let _ = unsafe { + syscall::fmap( + !0, + &Map { + offset: 0, + size: HEAP_INCREASE_BY, + address: state.heap_top, + flags: MapFlags::PROT_WRITE + | MapFlags::PROT_READ + | MapFlags::MAP_PRIVATE + | MapFlags::MAP_FIXED_NOREPLACE, + }, + ) + } .expect("failed to extend heap"); - heap.extend(HEAP_INCREASE_BY); + unsafe { heap.extend(HEAP_INCREASE_BY) }; state.heap_top += HEAP_INCREASE_BY; - return self.alloc(layout); + return unsafe { self.alloc(layout) }; } } } unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) { - (&mut *ALLOC_STATE.0.get()) - .heap - .as_mut() - .unwrap() - .deallocate(core::ptr::NonNull::new(ptr).unwrap(), layout) + unsafe { + (&mut *ALLOC_STATE.0.get()) + .heap + .as_mut() + .unwrap() + .deallocate(core::ptr::NonNull::new(ptr).unwrap(), layout) + } + } +} + +pub struct KernelSchemeMap(BTreeMap); +impl KernelSchemeMap { + fn new(kernel_scheme_infos: &[KernelSchemeInfo]) -> Self { + let mut map = BTreeMap::new(); + for info in kernel_scheme_infos { + if let Some(scheme_id) = GlobalSchemes::try_from_raw(info.scheme_id) { + map.insert(scheme_id, info.fd); + } + } + Self(map) + } + fn get(&self, scheme: GlobalSchemes) -> Option<&usize> { + self.0.get(&scheme) } } diff --git a/bootstrap/src/procmgr.rs b/bootstrap/src/procmgr.rs index 6635ab98ad..c240a15da0 100644 --- a/bootstrap/src/procmgr.rs +++ b/bootstrap/src/procmgr.rs @@ -29,15 +29,18 @@ use redox_scheme::{ Socket, Tag, }; use slab::Slab; +use syscall::data::GlobalSchemes; use syscall::schemev2::NewFdFlags; use syscall::{ - ContextStatus, ContextVerb, CtxtStsBuf, EACCES, EAGAIN, EBADF, EBADFD, ECANCELED, ECHILD, - EEXIST, EINTR, EINVAL, ENOENT, ENOSYS, EOPNOTSUPP, EOWNERDEAD, EPERM, ERESTART, ESRCH, + CallFlags, ContextStatus, ContextVerb, CtxtStsBuf, EACCES, EAGAIN, EBADF, EBADFD, ECANCELED, + ECHILD, EEXIST, EINTR, EINVAL, ENOENT, ENOSYS, EOPNOTSUPP, EOWNERDEAD, EPERM, ERESTART, ESRCH, EWOULDBLOCK, Error, Event, EventFlags, FobtainFdFlags, MapFlags, O_ACCMODE, O_CREAT, O_RDONLY, PAGE_SIZE, ProcSchemeAttrs, Result, SenderInfo, SetSighandlerData, SigProcControl, Sigcontrol, sig_bit, }; +use crate::KernelSchemeMap; + #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] enum VirtualId { KernelId(Id), @@ -45,13 +48,31 @@ enum VirtualId { InternalId(u64), } -pub fn run(write_fd: usize, auth: &FdGuard) { - let socket = Socket::nonblock("proc").expect("failed to open proc scheme socket"); +pub fn run( + write_fd: usize, + auth: &FdGuard, + kernel_schemes: &KernelSchemeMap, + scheme_creation_cap: usize, +) -> ! { + let socket = + Socket::create_inner(scheme_creation_cap, true).expect("failed to open proc scheme socket"); + let _ = syscall::close(scheme_creation_cap); // TODO? let socket_ident = socket.inner().raw(); - let queue = RawEventQueue::new().expect("failed to create event queue"); + let queue = RawEventQueue::new( + *kernel_schemes + .get(GlobalSchemes::Event) + .expect("failed to get event fd"), + ) + .expect("failed to create event queue"); + for (scheme, fd) in kernel_schemes.0.iter() { + if *scheme == GlobalSchemes::Proc { + continue; + } + let _ = syscall::close(*fd); + } queue .subscribe(socket.inner().raw(), socket_ident, EventFlags::EVENT_READ) @@ -59,8 +80,14 @@ pub fn run(write_fd: usize, auth: &FdGuard) { let mut scheme = ProcScheme::new(auth, &queue); + // send open-capability to bootstrap + let new_id = scheme.handles.insert(Handle::SchemeRoot); + let cap_fd = socket + .create_this_scheme_fd(0, new_id, 0, 0) + .expect("failed to issue procmgr root fd"); + log::debug!("process manager started"); - let _ = syscall::write(write_fd, &[0]); + let _ = syscall::call_wo(write_fd, &cap_fd.to_ne_bytes(), CallFlags::FD, &[]); let _ = syscall::close(write_fd); let mut states = HashMap::::new(); @@ -205,8 +232,8 @@ fn handle_scheme<'a>( Err(req) => return Response::ready_err(ENOSYS, req), }; match op { - Op::Open(op) => Ready(Response::open_dup_like( - scheme.on_open(op.path(), op.flags, &caller), + Op::OpenAt(op) => Ready(Response::open_dup_like( + scheme.on_openat(op.fd, op.path(), *op.flags(), op.fcntl_flags, &caller), op, )), Op::Dup(op) => Ready(Response::open_dup_like(scheme.on_dup(op.fd, op.buf()), op)), @@ -523,6 +550,9 @@ enum Handle { // A handle that grants the holder the capability to obtain process credentials. ProcCredsCapability, + + // A handle that grants the holder the capability to open process scheme resource. + SchemeRoot, } #[derive(Clone, Copy, Debug)] @@ -536,8 +566,10 @@ enum WaitpidTarget { // backend struct RawEventQueue(FdGuard); impl RawEventQueue { - pub fn new() -> Result { - FdGuard::open("/scheme/event", O_CREAT).map(Self) + pub fn new(cap_fd: usize) -> Result { + syscall::openat(cap_fd, "", O_CREAT, 0) + .map(FdGuard::new) + .map(Self) } pub fn subscribe(&self, fd: usize, ident: usize, flags: EventFlags) -> Result<()> { self.0.write(&Event { @@ -586,7 +618,11 @@ impl<'a> ProcScheme<'a> { match self.handles[req.id()] { ref mut st @ Handle::Init => { let mut fd_out = usize::MAX; - if let Err(e) = req.obtain_fd(socket, FobtainFdFlags::empty(), core::slice::from_mut(&mut fd_out)) { + if let Err(e) = req.obtain_fd( + socket, + FobtainFdFlags::empty(), + core::slice::from_mut(&mut fd_out), + ) { return Response::new(Err(e), req); }; let fd = FdGuard::new(fd_out); @@ -765,7 +801,18 @@ impl<'a> ProcScheme<'a> { self.thread_lookup.insert(ident, thread_weak); Ok(thread) } - fn on_open(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result { + fn on_openat( + &mut self, + fd: usize, + path: &str, + flags: usize, + _fcntl_flags: u32, + ctx: &CallerCtx, + ) -> Result { + match self.handles[fd] { + Handle::SchemeRoot => {} + _ => return Err(Error::new(EACCES)), + }; let path = path.trim_start_matches('/'); Ok(match path { "init" => { @@ -833,7 +880,7 @@ impl<'a> ProcScheme<'a> { buf[..len].copy_from_slice(&src_buf[..len]); Ok(len) } - Handle::Init | Handle::Thread(_) | Handle::ProcCredsCapability => { + Handle::Init | Handle::Thread(_) | Handle::ProcCredsCapability | Handle::SchemeRoot => { return Err(Error::new(EBADF)); } } @@ -882,7 +929,9 @@ impl<'a> ProcScheme<'a> { fd: thread.fd.dup(buf)?.take(), }) } - Handle::Init | Handle::Ps(_) | Handle::ProcCredsCapability => Err(Error::new(EBADF)), + Handle::Init | Handle::Ps(_) | Handle::ProcCredsCapability | Handle::SchemeRoot => { + Err(Error::new(EBADF)) + } } } fn on_call( @@ -1063,6 +1112,7 @@ impl<'a> ProcScheme<'a> { _ => Response::ready_err(EINVAL, op), } } + Handle::SchemeRoot => Response::ready_err(EBADF, op), } } fn on_getpgid(&mut self, caller_pid: ProcessId, target_pid: ProcessId) -> Result { diff --git a/bootstrap/src/riscv64.rs b/bootstrap/src/riscv64.rs index 6a36039a4f..18a582a626 100644 --- a/bootstrap/src/riscv64.rs +++ b/bootstrap/src/riscv64.rs @@ -3,7 +3,7 @@ use syscall::{data::Map, flag::MapFlags, number::SYS_FMAP}; const STACK_SIZE: usize = 64 * 1024; // 64 KiB pub const USERMODE_END: usize = 1 << 47; // Assuming Sv48; it should work with Sv57 also -pub const STACK_START: usize = USERMODE_END - STACK_SIZE; +pub const STACK_START: usize = USERMODE_END - syscall::KERNEL_METADATA_SIZE - STACK_SIZE; static MAP: Map = Map { offset: 0, diff --git a/bootstrap/src/start.rs b/bootstrap/src/start.rs index 1be39fc286..e681df452d 100644 --- a/bootstrap/src/start.rs +++ b/bootstrap/src/start.rs @@ -46,37 +46,41 @@ pub unsafe extern "C" fn start() -> ! { let (rodata_start, rodata_end) = offsets::rodata(); let (data_start, data_end) = offsets::data_and_bss(); - let _ = syscall::open("/scheme/debug", syscall::O_RDONLY); // stdin - let _ = syscall::open("/scheme/debug", syscall::O_WRONLY); // stdout - let _ = syscall::open("/scheme/debug", syscall::O_WRONLY); // stderr + // NOTE: Assuming the debug scheme root fd is always placed at this position + let debug_fd = syscall::UPPER_FDTBL_TAG + syscall::data::GlobalSchemes::Debug as usize; + let _ = syscall::openat(debug_fd, "", syscall::O_RDONLY, 0); // stdin + let _ = syscall::openat(debug_fd, "", syscall::O_WRONLY, 0); // stdout + let _ = syscall::openat(debug_fd, "", syscall::O_WRONLY, 0); // stderr - let _ = syscall::mprotect(4096, 4096, MapFlags::PROT_READ | MapFlags::MAP_PRIVATE) - .expect("mprotect failed for initfs header page"); + unsafe { + let _ = syscall::mprotect(4096, 4096, MapFlags::PROT_READ | MapFlags::MAP_PRIVATE) + .expect("mprotect failed for initfs header page"); - let _ = syscall::mprotect( - text_start, - text_end - text_start, - MapFlags::PROT_READ | MapFlags::PROT_EXEC | MapFlags::MAP_PRIVATE, - ) - .expect("mprotect failed for .text"); - let _ = syscall::mprotect( - rodata_start, - rodata_end - rodata_start, - MapFlags::PROT_READ | MapFlags::MAP_PRIVATE, - ) - .expect("mprotect failed for .rodata"); - let _ = syscall::mprotect( - data_start, - data_end - data_start, - MapFlags::PROT_READ | MapFlags::PROT_WRITE | MapFlags::MAP_PRIVATE, - ) - .expect("mprotect failed for .data/.bss"); - let _ = syscall::mprotect( - data_end, - crate::arch::STACK_START - data_end, - MapFlags::PROT_READ | MapFlags::MAP_PRIVATE, - ) - .expect("mprotect failed for rest of memory"); + let _ = syscall::mprotect( + text_start, + text_end - text_start, + MapFlags::PROT_READ | MapFlags::PROT_EXEC | MapFlags::MAP_PRIVATE, + ) + .expect("mprotect failed for .text"); + let _ = syscall::mprotect( + rodata_start, + rodata_end - rodata_start, + MapFlags::PROT_READ | MapFlags::MAP_PRIVATE, + ) + .expect("mprotect failed for .rodata"); + let _ = syscall::mprotect( + data_start, + data_end - data_start, + MapFlags::PROT_READ | MapFlags::PROT_WRITE | MapFlags::MAP_PRIVATE, + ) + .expect("mprotect failed for .data/.bss"); + let _ = syscall::mprotect( + data_end, + crate::arch::STACK_START - data_end, + MapFlags::PROT_READ | MapFlags::MAP_PRIVATE, + ) + .expect("mprotect failed for rest of memory"); + } // FIXME make the initfs read-only diff --git a/bootstrap/src/x86_64.rs b/bootstrap/src/x86_64.rs index f7110a64e9..23d52cf4dc 100644 --- a/bootstrap/src/x86_64.rs +++ b/bootstrap/src/x86_64.rs @@ -3,7 +3,7 @@ use syscall::{data::Map, flag::MapFlags, number::SYS_FMAP}; const STACK_SIZE: usize = 64 * 1024; // 64 KiB pub const USERMODE_END: usize = 0x0000_8000_0000_0000; -pub const STACK_START: usize = USERMODE_END - STACK_SIZE; +pub const STACK_START: usize = USERMODE_END - syscall::KERNEL_METADATA_SIZE - STACK_SIZE; static MAP: Map = Map { offset: 0, diff --git a/drivers/acpid/Cargo.toml b/drivers/acpid/Cargo.toml index d3bf9c5a42..cc317b0fad 100644 --- a/drivers/acpid/Cargo.toml +++ b/drivers/acpid/Cargo.toml @@ -18,11 +18,11 @@ redox_event.workspace = true rustc-hash = "1.1.0" thiserror = "1" ron = "0.8.1" +arrayvec = "0.7.6" +serde = { version = "1.0.228", features = ["derive"] } amlserde = { path = "../amlserde" } common = { path = "../common" } daemon = { path = "../../daemon" } libredox.workspace = true redox-scheme.workspace = true -arrayvec = "0.7.6" -serde = { version = "1.0.228", features = ["derive"] } diff --git a/drivers/acpid/src/acpi.rs b/drivers/acpid/src/acpi.rs index 1a058623c7..cc061d8661 100644 --- a/drivers/acpid/src/acpi.rs +++ b/drivers/acpid/src/acpi.rs @@ -257,12 +257,12 @@ impl AmlSymbols { } } - pub fn init(&mut self) -> Result<(), Box> { + pub fn init(&mut self, pci_fd: Option<&libredox::Fd>) -> Result<(), Box> { if self.aml_context.is_some() { return Err("AML interpreter already initialized".into()); } let format_err = |err| format!("{:?}", err); - let handler = AmlPhysMemHandler::new(Arc::clone(&self.page_cache)); + let handler = AmlPhysMemHandler::new(pci_fd, Arc::clone(&self.page_cache)); //TODO: use these parsed tables for the rest of acpid let rsdp_address = usize::from_str_radix(&std::env::var("RSDP_ADDR")?, 16)?; let tables = @@ -276,10 +276,12 @@ impl AmlSymbols { Ok(()) } - pub fn aml_context_mut(&mut self) -> &mut Interpreter { - // PCID must be running by this time! + pub fn aml_context_mut( + &mut self, + pci_fd: Option<&libredox::Fd>, + ) -> &mut Interpreter { if self.aml_context.is_none() { - match self.init() { + match self.init(pci_fd) { Ok(()) => (), Err(err) => { log::error!("failed to initialize AML context: {}", err); @@ -303,8 +305,8 @@ impl AmlSymbols { None } - pub fn build_cache(&mut self) { - let aml_context = self.aml_context_mut(); + pub fn build_cache(&mut self, pci_fd: Option<&libredox::Fd>) { + let aml_context = self.aml_context_mut(pci_fd); let mut symbol_list: Vec<(AmlName, String)> = Vec::with_capacity(5000); @@ -391,7 +393,7 @@ impl AcpiContext { args: Vec, ) -> Result { let mut symbols = self.aml_symbols.write(); - let interpreter = symbols.aml_context_mut(); + let interpreter = symbols.aml_context_mut(None); interpreter.acquire_global_lock(16)?; let args = args @@ -520,14 +522,17 @@ impl AcpiContext { } pub fn aml_lookup(&self, symbol: &str) -> Option { - if let Ok(aml_symbols) = self.aml_symbols() { + if let Ok(aml_symbols) = self.aml_symbols(None) { aml_symbols.lookup(symbol) } else { None } } - pub fn aml_symbols(&self) -> Result, AmlError> { + pub fn aml_symbols( + &self, + pci_fd: Option<&libredox::Fd>, + ) -> Result, AmlError> { // return the cached value if it exists let symbols = self.aml_symbols.read(); if !symbols.symbols_cache().is_empty() { @@ -541,7 +546,7 @@ impl AcpiContext { let mut aml_symbols = self.aml_symbols.write(); - aml_symbols.build_cache(); + aml_symbols.build_cache(pci_fd); // return the cached value Ok(RwLockWriteGuard::downgrade(aml_symbols)) diff --git a/drivers/acpid/src/aml_physmem.rs b/drivers/acpid/src/aml_physmem.rs index 15dfa25b16..2bdd667ba2 100644 --- a/drivers/acpid/src/aml_physmem.rs +++ b/drivers/acpid/src/aml_physmem.rs @@ -146,22 +146,17 @@ pub struct AmlPhysMemHandler { /// Read from a physical address. /// Generic parameter must be u8, u16, u32 or u64. impl AmlPhysMemHandler { - pub fn new(page_cache: Arc>) -> Self { - //TODO: have PCID send a socket? - let pci_fd = Arc::new( - match libredox::Fd::open( - "/scheme/pci/access", - libredox::flag::O_RDWR | libredox::flag::O_CLOEXEC, - 0, - ) { - Ok(fd) => Some(fd), - Err(err) => { - log::error!("failed to open /scheme/pci/access: {}", err); - None - } - }, - ); - Self { page_cache, pci_fd } + pub fn new(pci_fd_opt: Option<&libredox::Fd>, page_cache: Arc>) -> Self { + let pci_fd = if let Some(pci_fd) = pci_fd_opt { + Some(libredox::Fd::new(pci_fd.raw())) + } else { + log::error!("pci_fd is not registered"); + None + }; + Self { + page_cache, + pci_fd: Arc::new(pci_fd), + } } fn pci_call_metadata(kind: u8, addr: PciAddress, off: u16) -> [u64; 2] { diff --git a/drivers/acpid/src/main.rs b/drivers/acpid/src/main.rs index 80bd06815e..26ca395adc 100644 --- a/drivers/acpid/src/main.rs +++ b/drivers/acpid/src/main.rs @@ -6,7 +6,10 @@ use std::sync::Arc; use ::acpi::aml::op_region::{RegionHandler, RegionSpace}; use event::{EventFlags, RawEventQueue}; -use redox_scheme::{RequestKind, SignalBehavior, Socket}; +use redox_scheme::{ + scheme::{register_sync_scheme, SchemeSync}, + RequestKind, Response, SignalBehavior, Socket, +}; use syscall::{EAGAIN, EWOULDBLOCK}; mod acpi; @@ -25,6 +28,8 @@ fn daemon(daemon: daemon::Daemon) -> ! { common::file_level(), ); + log::info!("acpid start"); + let rxsdt_raw_data: Arc<[u8]> = std::fs::read("/scheme/kernel.acpi/rxsdt") .expect("acpid: failed to read `/scheme/kernel.acpi/rxsdt`") .into(); @@ -78,11 +83,9 @@ fn daemon(daemon: daemon::Daemon) -> ! { .expect("acpid: failed to open `/scheme/kernel.acpi/kstop`"); let mut event_queue = RawEventQueue::new().expect("acpid: failed to create event queue"); - let socket = Socket::nonblock("acpi").expect("acpid: failed to create disk scheme"); + let socket = Socket::nonblock().expect("acpid: failed to create disk scheme"); - daemon.ready(); - - //TODO: needs to open /scheme/pci/access later! libredox::call::setrens(0, 0).expect("acpid: failed to enter null namespace"); + let mut scheme = self::scheme::AcpiScheme::new(&acpi_context, &socket); event_queue .subscribe(shutdown_pipe.as_raw_fd() as usize, 0, EventFlags::READ) @@ -91,7 +94,12 @@ fn daemon(daemon: daemon::Daemon) -> ! { .subscribe(socket.inner().raw(), 1, EventFlags::READ) .expect("acpid: failed to register scheme socket for event queue"); - let mut scheme = self::scheme::AcpiScheme::new(&acpi_context); + register_sync_scheme(&socket, "acpi", &mut scheme) + .expect("acpid: failed to register acpi scheme to namespace"); + + daemon.ready(); + + libredox::call::setrens(0, 0).expect("acpid: failed to enter null namespace"); let mut mounted = true; while mounted { @@ -119,7 +127,6 @@ fn daemon(daemon: daemon::Daemon) -> ! { } } }; - match req.kind() { RequestKind::Call(call) => { let response = call.handle_sync(&mut scheme); @@ -130,6 +137,15 @@ fn daemon(daemon: daemon::Daemon) -> ! { RequestKind::OnClose { id } => { scheme.on_close(id); } + RequestKind::SendFd(sendfd_request) => { + let result = scheme.on_sendfd(&sendfd_request); + socket + .write_response( + Response::new(result, sendfd_request), + SignalBehavior::Restart, + ) + .expect("acpid: failed to write response"); + } _ => (), } } @@ -151,5 +167,6 @@ fn daemon(daemon: daemon::Daemon) -> ! { } fn main() { + common::init(); daemon::Daemon::new(daemon); } diff --git a/drivers/acpid/src/scheme.rs b/drivers/acpid/src/scheme.rs index 688dbc0018..17ff1afb42 100644 --- a/drivers/acpid/src/scheme.rs +++ b/drivers/acpid/src/scheme.rs @@ -2,29 +2,33 @@ use acpi::aml::namespace::AmlName; use amlserde::aml_serde_name::to_aml_format; use amlserde::AmlSerdeValue; use core::str; +use libredox::Fd; use parking_lot::RwLockReadGuard; use redox_scheme::scheme::SchemeSync; -use redox_scheme::{CallerCtx, OpenResult}; +use redox_scheme::{CallerCtx, OpenResult, SendFdRequest, Socket}; use ron::de::SpannedError; use std::collections::BTreeMap; use std::convert::{TryFrom, TryInto}; use std::str::FromStr; use syscall::dirent::{DirEntry, DirentBuf, DirentKind}; use syscall::schemev2::NewFdFlags; +use syscall::FobtainFdFlags; use syscall::data::Stat; use syscall::error::{Error, Result}; -use syscall::error::{EBADF, EBADFD, EINVAL, EIO, EISDIR, ENOENT, ENOTDIR}; +use syscall::error::{EACCES, EBADF, EBADFD, EINVAL, EIO, EISDIR, ENOENT, ENOTDIR}; use syscall::flag::{MODE_DIR, MODE_FILE}; use syscall::flag::{O_ACCMODE, O_DIRECTORY, O_RDONLY, O_STAT, O_SYMLINK}; use syscall::{EOPNOTSUPP, EOVERFLOW, EPERM}; use crate::acpi::{AcpiContext, AmlSymbols, SdtSignature}; -pub struct AcpiScheme<'acpi> { +pub struct AcpiScheme<'acpi, 'sock> { ctx: &'acpi AcpiContext, handles: BTreeMap>, next_fd: usize, + pci_fd: Option, + socket: &'sock Socket, } struct Handle<'a> { @@ -38,6 +42,8 @@ enum HandleKind<'a> { Table(SdtSignature), Symbols(RwLockReadGuard<'a, AmlSymbols>), Symbol { name: String, description: String }, + SchemeRoot, + RegisterPci, } impl HandleKind<'_> { @@ -48,6 +54,8 @@ impl HandleKind<'_> { Self::Table(_) => false, Self::Symbols(_) => true, Self::Symbol { .. } => false, + Self::SchemeRoot => false, + Self::RegisterPci => false, } } fn len(&self, acpi_ctx: &AcpiContext) -> Result { @@ -60,16 +68,19 @@ impl HandleKind<'_> { Self::Symbol { description, .. } => description.len(), // Directories Self::TopLevel | Self::Symbols(_) | Self::Tables => 0, + Self::SchemeRoot | Self::RegisterPci => return Err(Error::new(EBADF)), }) } } -impl<'acpi> AcpiScheme<'acpi> { - pub fn new(ctx: &'acpi AcpiContext) -> Self { +impl<'acpi, 'sock> AcpiScheme<'acpi, 'sock> { + pub fn new(ctx: &'acpi AcpiContext, socket: &'sock Socket) -> Self { Self { ctx, handles: BTreeMap::new(), next_fd: 0, + pci_fd: None, + socket, } } } @@ -148,53 +159,92 @@ fn parse_table(table: &[u8]) -> Option { }) } -impl SchemeSync for AcpiScheme<'_> { - fn open(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result { +impl SchemeSync for AcpiScheme<'_, '_> { + fn scheme_root(&mut self) -> Result { + let fd = self.next_fd; + self.next_fd += 1; + + self.handles.insert( + fd, + Handle { + stat: false, + kind: HandleKind::SchemeRoot, + allowed_to_eval: false, + }, + ); + Ok(fd) + } + fn openat( + &mut self, + dirfd: usize, + path: &str, + flags: usize, + _fcntl_flags: u32, + ctx: &CallerCtx, + ) -> Result { + let handle = self.handles.get(&dirfd).ok_or(Error::new(EBADF))?; + let path = path.trim_start_matches('/'); let flag_stat = flags & O_STAT == O_STAT; let flag_dir = flags & O_DIRECTORY == O_DIRECTORY; - // TODO: arrayvec - let components = { - let mut v = arrayvec::ArrayVec::<&str, 3>::new(); - let it = path.split('/'); - for component in it.take(3) { - v.push(component); - } + let kind = match handle.kind { + HandleKind::SchemeRoot => { + // TODO: arrayvec + let components = { + let mut v = arrayvec::ArrayVec::<&str, 3>::new(); + let it = path.split('/'); + for component in it.take(3) { + v.push(component); + } - v - }; + v + }; - let kind = match &*components { - [""] => HandleKind::TopLevel, - ["tables"] => HandleKind::Tables, + match &*components { + [""] => HandleKind::TopLevel, + ["register_pci"] => HandleKind::RegisterPci, + ["tables"] => HandleKind::Tables, - ["tables", table] => { - let signature = parse_table(table.as_bytes()).ok_or(Error::new(ENOENT))?; - HandleKind::Table(signature) - } + ["tables", table] => { + let signature = parse_table(table.as_bytes()).ok_or(Error::new(ENOENT))?; + HandleKind::Table(signature) + } - ["symbols"] => { - if let Ok(aml_symbols) = self.ctx.aml_symbols() { - HandleKind::Symbols(aml_symbols) - } else { - return Err(Error::new(EIO)); + ["symbols"] => { + if let Ok(aml_symbols) = self.ctx.aml_symbols(self.pci_fd.as_ref()) { + HandleKind::Symbols(aml_symbols) + } else { + return Err(Error::new(EIO)); + } + } + + ["symbols", symbol] => { + if let Some(description) = self.ctx.aml_lookup(symbol) { + HandleKind::Symbol { + name: (*symbol).to_owned(), + description, + } + } else { + return Err(Error::new(ENOENT)); + } + } + + _ => return Err(Error::new(ENOENT)), } } - - ["symbols", symbol] => { - if let Some(description) = self.ctx.aml_lookup(symbol) { + HandleKind::Symbols(ref aml_symbols) => { + if let Some(description) = aml_symbols.lookup(path) { HandleKind::Symbol { - name: (*symbol).to_owned(), + name: (*path).to_owned(), description, } } else { return Err(Error::new(ENOENT)); } } - - _ => return Err(Error::new(ENOENT)), + _ => return Err(Error::new(EACCES)), }; if kind.is_dir() && !flag_dir && !flag_stat { @@ -419,9 +469,44 @@ impl SchemeSync for AcpiScheme<'_> { Ok(result_len) } + + fn on_sendfd(&mut self, sendfd_request: &SendFdRequest) -> Result { + let id = sendfd_request.id(); + let num_fds = sendfd_request.num_fds(); + + let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + if !matches!(handle.kind, HandleKind::RegisterPci) { + return Err(Error::new(EACCES)); + } + + if num_fds == 0 { + return Ok(0); + } + + if num_fds > 1 { + return Err(Error::new(EINVAL)); + } + let mut new_fd = usize::MAX; + if let Err(e) = sendfd_request.obtain_fd( + &self.socket, + FobtainFdFlags::UPPER_TBL, + std::slice::from_mut(&mut new_fd), + ) { + return Err(e); + } + let new_fd = libredox::Fd::new(new_fd); + + if self.pci_fd.is_some() { + return Err(Error::new(EINVAL)); + } else { + self.pci_fd = Some(new_fd); + } + + Ok(num_fds) + } } -impl AcpiScheme<'_> { +impl AcpiScheme<'_, '_> { pub fn on_close(&mut self, id: usize) { self.handles.remove(&id); } diff --git a/drivers/audio/ac97d/src/device.rs b/drivers/audio/ac97d/src/device.rs index da73fb54be..e5933acac3 100644 --- a/drivers/audio/ac97d/src/device.rs +++ b/drivers/audio/ac97d/src/device.rs @@ -22,6 +22,7 @@ const SUB_BUFF_SIZE: usize = 2048; enum Handle { Todo, + SchemeRoot, } #[allow(dead_code)] @@ -262,7 +263,26 @@ impl Ac97 { } impl SchemeSync for Ac97 { - fn open(&mut self, _path: &str, _flags: usize, ctx: &CallerCtx) -> Result { + fn scheme_root(&mut self) -> Result { + let id = self.next_id.fetch_add(1, Ordering::SeqCst); + self.handles.lock().insert(id, Handle::SchemeRoot); + Ok(id) + } + fn openat( + &mut self, + dirfd: usize, + _path: &str, + _flags: usize, + _fcntl_flags: u32, + ctx: &CallerCtx, + ) -> Result { + { + let mut handles = self.handles.lock(); + let handle = handles.get(&dirfd).ok_or(Error::new(EBADF))?; + if !matches!(handle, Handle::SchemeRoot) { + return Err(Error::new(EACCES)); + } + } if ctx.uid == 0 { let id = self.next_id.fetch_add(1, Ordering::SeqCst); self.handles.lock().insert(id, Handle::Todo); @@ -285,7 +305,10 @@ impl SchemeSync for Ac97 { ) -> Result { { let mut handles = self.handles.lock(); - let _handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; + let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; + if !matches!(handle, Handle::Todo) { + return Err(Error::new(EBADF)); + } } if buf.len() != SUB_BUFF_SIZE { @@ -310,8 +333,13 @@ impl SchemeSync for Ac97 { } fn fpath(&mut self, id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result { - let mut handles = self.handles.lock(); - let _handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; + { + let mut handles = self.handles.lock(); + let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?; + if !matches!(handle, Handle::Todo) { + return Err(Error::new(EBADF)); + } + } let mut i = 0; let scheme_path = b"/scheme/audiohw"; diff --git a/drivers/audio/ac97d/src/main.rs b/drivers/audio/ac97d/src/main.rs index 02c33ffa6b..4c4f6d8ec4 100644 --- a/drivers/audio/ac97d/src/main.rs +++ b/drivers/audio/ac97d/src/main.rs @@ -11,6 +11,7 @@ use std::usize; use event::{user_data, EventQueue}; use pcid_interface::PciFunctionHandle; +use redox_scheme::scheme::register_sync_scheme; use redox_scheme::wrappers::ReadinessBased; use redox_scheme::Socket; use std::cell::RefCell; @@ -19,6 +20,7 @@ use std::cell::RefCell; pub mod device; fn main() { + common::init(); pcid_interface::pci_daemon(daemon); } @@ -51,10 +53,10 @@ fn daemon(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! { let mut irq_file = irq.irq_handle("ac97d"); + let socket = Socket::nonblock().expect("ac97d: failed to create socket"); let device = RefCell::new(unsafe { device::Ac97::new(bar0, bar1).expect("ac97d: failed to allocate device") }); - let socket = Socket::nonblock("audiohw").expect("ac97d: failed to create socket"); let mut readiness_based = ReadinessBased::new(&socket, 16); user_data! { @@ -80,6 +82,8 @@ fn daemon(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! { ) .unwrap(); + register_sync_scheme(&socket, "audiohw", &mut *device.borrow_mut()) + .expect("ac97d: failed to register audiohw scheme to namespace"); daemon.ready(); libredox::call::setrens(0, 0).expect("ac97d: failed to enter null namespace"); diff --git a/drivers/audio/ihdad/src/hda/device.rs b/drivers/audio/ihdad/src/hda/device.rs index e12112f3ef..134edfea76 100755 --- a/drivers/audio/ihdad/src/hda/device.rs +++ b/drivers/audio/ihdad/src/hda/device.rs @@ -68,6 +68,7 @@ enum Handle { Pcmout(usize, usize, usize), // Card, index, block_ptr Pcmin(usize, usize, usize), // Card, index, block_ptr StrBuf(Vec), + SchemeRoot, } #[repr(C, packed)] @@ -984,7 +985,26 @@ impl Drop for IntelHDA { } impl SchemeSync for IntelHDA { - fn open(&mut self, path: &str, _flags: usize, ctx: &CallerCtx) -> Result { + fn scheme_root(&mut self) -> Result { + let id = self.next_id.fetch_add(1, Ordering::SeqCst); + self.handles.lock().insert(id, Handle::SchemeRoot); + Ok(id) + } + fn openat( + &mut self, + dirfd: usize, + path: &str, + _flags: usize, + _fcntl_flags: u32, + ctx: &CallerCtx, + ) -> Result { + { + let mut handles = self.handles.lock(); + let handle = handles.get(&dirfd).ok_or(Error::new(EBADF))?; + if !matches!(handle, Handle::SchemeRoot) { + return Err(Error::new(EACCES)); + } + } //let path: Vec<&str>; /* match str::from_utf8(_path) { diff --git a/drivers/audio/ihdad/src/main.rs b/drivers/audio/ihdad/src/main.rs index dce556328f..025aa58875 100755 --- a/drivers/audio/ihdad/src/main.rs +++ b/drivers/audio/ihdad/src/main.rs @@ -3,6 +3,7 @@ extern crate event; extern crate spin; extern crate syscall; +use redox_scheme::scheme::register_sync_scheme; use redox_scheme::wrappers::ReadinessBased; use redox_scheme::Socket; use std::cell::RefCell; @@ -24,6 +25,7 @@ QEMU ICH9 8086:293E */ fn main() { + common::init(); pcid_interface::pci_daemon(daemon); } @@ -60,12 +62,14 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { let event_queue = EventQueue::::new().expect("ihdad: Could not create event queue."); + let socket = Socket::nonblock().expect("ihdad: failed to create socket"); let device = RefCell::new(unsafe { hda::IntelHDA::new(address, vend_prod).expect("ihdad: failed to allocate device") }); - let socket = Socket::nonblock("audiohw").expect("ihdad: failed to create socket"); let mut readiness_based = ReadinessBased::new(&socket, 16); + register_sync_scheme(&socket, "audiohw", &mut *device.borrow_mut()) + .expect("ihdad: failed to register audiohw scheme to namespace"); daemon.ready(); event_queue diff --git a/drivers/audio/sb16d/src/device.rs b/drivers/audio/sb16d/src/device.rs index ae9f4a0f04..4e271aa614 100644 --- a/drivers/audio/sb16d/src/device.rs +++ b/drivers/audio/sb16d/src/device.rs @@ -19,6 +19,7 @@ const SUB_BUFF_SIZE: usize = 2048; enum Handle { Todo, + SchemeRoot, } #[allow(dead_code)] @@ -185,7 +186,26 @@ impl Sb16 { } impl SchemeSync for Sb16 { - fn open(&mut self, _path: &str, _flags: usize, ctx: &CallerCtx) -> Result { + fn scheme_root(&mut self) -> Result { + let id = self.next_id.fetch_add(1, Ordering::SeqCst); + self.handles.lock().insert(id, Handle::SchemeRoot); + Ok(id) + } + fn openat( + &mut self, + dirfd: usize, + _path: &str, + _flags: usize, + _fcntl_flags: u32, + ctx: &CallerCtx, + ) -> Result { + { + let mut handles = self.handles.lock(); + let handle = handles.get(&dirfd).ok_or(Error::new(EBADF))?; + if !matches!(handle, Handle::SchemeRoot) { + return Err(Error::new(EACCES)); + } + } if ctx.uid == 0 { let id = self.next_id.fetch_add(1, Ordering::SeqCst); self.handles.lock().insert(id, Handle::Todo); diff --git a/drivers/audio/sb16d/src/main.rs b/drivers/audio/sb16d/src/main.rs index 570b9c3a02..6abae32772 100644 --- a/drivers/audio/sb16d/src/main.rs +++ b/drivers/audio/sb16d/src/main.rs @@ -1,6 +1,7 @@ //#![deny(warnings)] use libredox::{flag, Fd}; +use redox_scheme::scheme::register_sync_scheme; use redox_scheme::wrappers::ReadinessBased; use redox_scheme::Socket; use std::cell::RefCell; @@ -34,9 +35,9 @@ fn daemon(daemon: daemon::Daemon) -> ! { common::acquire_port_io_rights().expect("sb16d: failed to acquire port IO rights"); + let socket = Socket::nonblock().expect("sb16d: failed to create socket"); let device = RefCell::new(unsafe { device::Sb16::new(addr).expect("sb16d: failed to allocate device") }); - let socket = Socket::nonblock("audiohw").expect("sb16d: failed to create socket"); let mut readiness_based = ReadinessBased::new(&socket, 16); //TODO: error on multiple IRQs? @@ -64,6 +65,9 @@ fn daemon(daemon: daemon::Daemon) -> ! { ) .unwrap(); + register_sync_scheme(&socket, "sb16d", &mut *device.borrow_mut()) + .expect("sb16d: failed to register audiohw scheme to namespace"); + daemon.ready(); libredox::call::setrens(0, 0).expect("sb16d: failed to enter null namespace"); diff --git a/drivers/common/src/dma.rs b/drivers/common/src/dma.rs index 98f7abc23d..47ab51efb9 100644 --- a/drivers/common/src/dma.rs +++ b/drivers/common/src/dma.rs @@ -7,7 +7,7 @@ use libredox::call::MmapArgs; use libredox::{error::Result, flag, Fd}; use syscall::PAGE_SIZE; -use crate::{MemoryType, VirtaddrTranslationHandle}; +use crate::{memory_root_fd, MemoryType, VirtaddrTranslationHandle}; /// Defines the platform-specific memory type for DMA operations /// @@ -42,8 +42,8 @@ const DMA_MEMTY: MemoryType = { /// /// - The request for the physical memory fails. pub(crate) fn phys_contiguous_fd() -> Result { - Fd::open( - &format!("/scheme/memory/zeroed@{DMA_MEMTY}?phys_contiguous"), + memory_root_fd().openat( + &format!("zeroed@{DMA_MEMTY}?phys_contiguous"), flag::O_CLOEXEC, 0, ) diff --git a/drivers/common/src/lib.rs b/drivers/common/src/lib.rs index 9a10dcc798..8259bbdc53 100644 --- a/drivers/common/src/lib.rs +++ b/drivers/common/src/lib.rs @@ -21,6 +21,28 @@ pub mod timeout; pub use logger::{file_level, output_level, setup_logging}; +use std::sync::OnceLock; + +static MEMORY_ROOT_FD: OnceLock = OnceLock::new(); + +pub fn init() { + if MEMORY_ROOT_FD + .set( + libredox::Fd::open("/scheme/memory/scheme-root", 0, 0) + .expect("drivers common: failed to open memory root fd"), + ) + .is_err() + { + panic!("drivers common: failed to set memory root fd"); + } +} + +pub fn memory_root_fd() -> &'static libredox::Fd { + MEMORY_ROOT_FD + .get() + .expect("acpid: failed to get memory_root_fd") +} + /// Specifies the write behavior for a specific region of memory /// /// These types indicate to the driver how writes to a specific memory region are handled by the @@ -136,7 +158,7 @@ pub unsafe fn physmap( } let path = format!( - "/scheme/memory/physical@{}", + "physical@{}", match ty { MemoryType::Writeback => "wb", MemoryType::Uncacheable => "uc", @@ -158,7 +180,7 @@ pub unsafe fn physmap( prot |= flag::PROT_WRITE; } - let fd = Fd::open(&path, O_CLOEXEC | mode, 0)?; + let fd = memory_root_fd().openat(&path, O_CLOEXEC | mode, 0)?; Ok(libredox::call::mmap(MmapArgs { fd: fd.raw(), offset: base_phys as u64, @@ -245,18 +267,6 @@ impl Drop for PhysBorrowed { } } -// TODO: temporary wrapper in redox_syscall? -unsafe fn sys_call(fd: usize, buf: &mut [u8], metadata: &[u64]) -> Result { - Ok(syscall::syscall5( - syscall::SYS_CALL, - fd, - buf.as_mut_ptr() as usize, - buf.len(), - metadata.len(), - metadata.as_ptr() as usize, - )?) -} - /// Instructs the kernel to enable I/O ports for this (usermode) process (x86-specific). /// /// On Redox, x86 privilege ring 3 represents userspace. Most Redox drivers run in userspace to @@ -268,7 +278,14 @@ pub fn acquire_port_io_rights() -> Result<()> { fn redox_cur_thrfd_v0() -> usize; } let kernel_fd = syscall::dup(unsafe { redox_cur_thrfd_v0() }, b"open_via_dup")?; - let res = unsafe { sys_call(kernel_fd, &mut [], &[ProcSchemeVerb::Iopl as u64]) }; + let res = unsafe { + libredox::call::call_wo( + kernel_fd, + &[], + syscall::CallFlags::empty(), + &[ProcSchemeVerb::Iopl as u64], + ) + }; let _ = syscall::close(kernel_fd); res?; Ok(()) @@ -287,14 +304,14 @@ impl VirtaddrTranslationHandle { /// Create a new handle, requires uid=0 but this may change. pub fn new() -> Result { Ok(Self { - fd: Fd::open("/scheme/memory/translation", O_CLOEXEC, 0)?, + fd: memory_root_fd().openat("translation", O_CLOEXEC, 0)?, }) } /// Translate physical => virtual. pub fn translate(&self, physical: usize) -> Result { let mut buf = physical.to_ne_bytes(); unsafe { - sys_call(self.fd.raw(), &mut buf, &[])?; + libredox::call::call_ro(self.fd.raw(), &mut buf, syscall::CallFlags::empty(), &[])?; } Ok(usize::from_ne_bytes(buf)) } diff --git a/drivers/common/src/logger.rs b/drivers/common/src/logger.rs index 05cd76a070..51e7620119 100644 --- a/drivers/common/src/logger.rs +++ b/drivers/common/src/logger.rs @@ -20,6 +20,7 @@ pub fn setup_logging( mut output_level: log::LevelFilter, file_level: log::LevelFilter, ) { + RedoxLogger::init_timezone(); if let Some(log_level) = read_bootloader_log_level_env(category, subcategory) { output_level = log_level; } diff --git a/drivers/graphics/bgad/src/main.rs b/drivers/graphics/bgad/src/main.rs index 2170d21b2d..e939f3c66d 100644 --- a/drivers/graphics/bgad/src/main.rs +++ b/drivers/graphics/bgad/src/main.rs @@ -1,8 +1,9 @@ //! +use common::acquire_port_io_rights; use inputd::ProducerHandle; use pcid_interface::PciFunctionHandle; -use redox_scheme::{RequestKind, SignalBehavior, Socket}; +use redox_scheme::{scheme::register_sync_scheme, RequestKind, SignalBehavior, Socket}; use crate::bga::Bga; use crate::scheme::BgaScheme; @@ -13,6 +14,7 @@ mod scheme; // FIXME add a driver-graphics implementation fn main() { + common::init(); pcid_interface::pci_daemon(daemon); } @@ -32,7 +34,7 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { log::info!("BGA {}", pci_config.func.display()); - let socket = Socket::create("bga").expect("bgad: failed to create bga scheme"); + let socket = Socket::create().expect("bgad: failed to create bga scheme"); let bar = unsafe { pcid_handle.map_bar(2) }.ptr.as_ptr(); @@ -46,10 +48,12 @@ fn daemon(daemon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> ! { scheme.update_size(); - libredox::call::setrens(0, 0).expect("bgad: failed to enter null namespace"); + register_sync_scheme(&socket, "bga", &mut scheme).expect("bgad: failed to register bga scheme"); daemon.ready(); + libredox::call::setrens(0, 0).expect("bgad: failed to enter null namespace"); + loop { let Some(request) = socket .next_request(SignalBehavior::Restart) diff --git a/drivers/graphics/bgad/src/scheme.rs b/drivers/graphics/bgad/src/scheme.rs index 5cd214e91e..80cdc957cc 100644 --- a/drivers/graphics/bgad/src/scheme.rs +++ b/drivers/graphics/bgad/src/scheme.rs @@ -27,8 +27,23 @@ impl BgaScheme { } } +const SCHEME_ROOT_ID: usize = 1; + impl SchemeSync for BgaScheme { - fn open(&mut self, _path: &str, _flags: usize, ctx: &CallerCtx) -> Result { + fn scheme_root(&mut self) -> Result { + Ok(SCHEME_ROOT_ID) + } + fn openat( + &mut self, + dirfd: usize, + _path: &str, + _flags: usize, + _fcntl_flags: u32, + ctx: &CallerCtx, + ) -> Result { + if dirfd != SCHEME_ROOT_ID { + return Err(Error::new(EACCES)); + } if ctx.uid == 0 { Ok(OpenResult::ThisScheme { number: 0, diff --git a/drivers/graphics/driver-graphics/src/lib.rs b/drivers/graphics/driver-graphics/src/lib.rs index ada783988e..a2d2c56796 100644 --- a/drivers/graphics/driver-graphics/src/lib.rs +++ b/drivers/graphics/driver-graphics/src/lib.rs @@ -19,10 +19,12 @@ use graphics_ipc::v1::CursorDamage; use graphics_ipc::v2::Damage; use inputd::{VtEvent, VtEventKind}; use libredox::Fd; -use redox_scheme::scheme::SchemeSync; +use redox_scheme::scheme::{register_scheme_inner, SchemeSync}; use redox_scheme::{CallerCtx, OpenResult, RequestKind, SignalBehavior, Socket}; use syscall::schemev2::NewFdFlags; -use syscall::{Error, MapFlags, Result, EAGAIN, EBADF, EINVAL, ENOENT, EOPNOTSUPP}; +use syscall::{ + CallFlags, Error, MapFlags, Result, EACCES, EAGAIN, EBADF, EINVAL, ENOENT, EOPNOTSUPP, +}; use crate::objects::{DrmObjectId, DrmObjects}; use crate::properties::DrmPropertyKind; @@ -119,12 +121,13 @@ enum Handle { next_id: u32, fbs: HashMap>, }, + SchemeRoot, } impl GraphicsScheme { pub fn new(mut adapter: T, scheme_name: String) -> Self { assert!(scheme_name.starts_with("display")); - let socket = Socket::nonblock(&scheme_name).expect("failed to create graphics scheme"); + let socket = Socket::nonblock().expect("failed to create graphics scheme"); let disable_graphical_debug = Some( File::open("/scheme/debug/disable-graphical-debug") @@ -152,7 +155,7 @@ impl GraphicsScheme { adapter.probe_connector(&mut objects, &standard_properties, connector_id) } - GraphicsScheme { + let mut scheme = GraphicsScheme { adapter, scheme_name, disable_graphical_debug, @@ -163,7 +166,15 @@ impl GraphicsScheme { handles: BTreeMap::new(), active_vt: 0, vts: HashMap::new(), - } + }; + + let cap_id = scheme + .scheme_root() + .expect("failed to get this scheme root"); + register_scheme_inner(&scheme.socket, &scheme.scheme_name, cap_id) + .expect("failed to register graphics scheme root"); + + scheme } pub fn event_handle(&self) -> &Fd { @@ -308,7 +319,26 @@ impl GraphicsScheme { const MAP_FAKE_OFFSET_MULTIPLIER: usize = 0x10_000_000; impl SchemeSync for GraphicsScheme { - fn open(&mut self, path: &str, _flags: usize, _ctx: &CallerCtx) -> Result { + fn scheme_root(&mut self) -> Result { + let id = self.next_id; + self.next_id += 1; + self.handles.insert(id, Handle::SchemeRoot); + Ok(id) + } + fn openat( + &mut self, + dirfd: usize, + path: &str, + _flags: usize, + _fcntl_flags: u32, + _ctx: &CallerCtx, + ) -> Result { + if !matches!( + self.handles.get(&dirfd).ok_or(Error::new(EBADF))?, + Handle::SchemeRoot + ) { + return Err(Error::new(EACCES)); + } if path.is_empty() { return Err(Error::new(EINVAL)); } @@ -369,6 +399,7 @@ impl SchemeSync for GraphicsScheme { next_id: _, fbs: _, } => format!("/scheme/{}/v2/{vt}", self.scheme_name), + Handle::SchemeRoot => return Err(Error::new(EOPNOTSUPP)), }; buf[..path.len()].copy_from_slice(path.as_bytes()); Ok(path.len()) @@ -390,6 +421,7 @@ impl SchemeSync for GraphicsScheme { Ok(()) } Handle::V2 { .. } => Err(Error::new(EOPNOTSUPP)), + Handle::SchemeRoot => Err(Error::new(EOPNOTSUPP)), } } @@ -414,6 +446,7 @@ impl SchemeSync for GraphicsScheme { Ok(1) } Handle::V2 { .. } => Err(Error::new(EOPNOTSUPP)), + Handle::SchemeRoot => Err(Error::new(EOPNOTSUPP)), } } @@ -493,6 +526,7 @@ impl SchemeSync for GraphicsScheme { Ok(buf.len()) } Handle::V2 { .. } => Err(Error::new(EOPNOTSUPP)), + Handle::SchemeRoot => Err(Error::new(EOPNOTSUPP)), } } @@ -535,6 +569,7 @@ impl SchemeSync for GraphicsScheme { Handle::V1Screen { .. } => { return Err(Error::new(EOPNOTSUPP)); } + Handle::SchemeRoot => return Err(Error::new(EOPNOTSUPP)), Handle::V2 { vt, next_id, fbs } => match metadata[0] { ipc::VERSION => ipc::DrmVersion::with(payload, |mut data| { data.set_version_major(1); @@ -887,6 +922,7 @@ impl SchemeSync for GraphicsScheme { .unwrap(), offset & (MAP_FAKE_OFFSET_MULTIPLIER as u64 - 1), ), + Handle::SchemeRoot => return Err(Error::new(EOPNOTSUPP)), }; let ptr = T::map_dumb_framebuffer(&mut self.adapter, framebuffer); Ok(unsafe { ptr.add(offset as usize) } as usize) diff --git a/drivers/graphics/fbbootlogd/src/main.rs b/drivers/graphics/fbbootlogd/src/main.rs index 69e18659c8..cf16e22a8d 100644 --- a/drivers/graphics/fbbootlogd/src/main.rs +++ b/drivers/graphics/fbbootlogd/src/main.rs @@ -14,7 +14,7 @@ use event::EventQueue; use inputd::ConsumerHandleEvent; use libredox::errno::EAGAIN; use orbclient::Event; -use redox_scheme::{RequestKind, SignalBehavior, Socket}; +use redox_scheme::{scheme::register_sync_scheme, RequestKind, SignalBehavior, Socket}; use crate::scheme::FbbootlogScheme; @@ -33,17 +33,9 @@ fn daemon(daemon: daemon::Daemon) -> ! { } } - let socket = - Socket::nonblock("fbbootlog").expect("fbbootlogd: failed to create fbbootlog scheme"); + let socket = Socket::nonblock().expect("fbbootlogd: failed to create fbbootlog scheme"); - { - // Add ourself as log sink - let mut log_file = std::fs::OpenOptions::new() - .write(true) - .open("/scheme/log/add_sink") - .unwrap(); - log_file.write(b"/scheme/fbbootlog").unwrap(); - } + let mut scheme = FbbootlogScheme::new(); event_queue .subscribe( @@ -51,9 +43,7 @@ fn daemon(daemon: daemon::Daemon) -> ! { Source::Scheme, event::EventFlags::READ, ) - .expect("fbcond: failed to subscribe to scheme events"); - - let mut scheme = FbbootlogScheme::new(); + .expect("fbbootlogd: failed to subscribe to scheme events"); event_queue .subscribe( @@ -63,6 +53,25 @@ fn daemon(daemon: daemon::Daemon) -> ! { ) .expect("fbbootlogd: failed to subscribe to scheme events"); + { + let log_fd = socket + .create_this_scheme_fd(0, 0, 0, 0) + .expect("fbbootlogd: failed to create log fd"); + // Add ourself as log sink + let mut log_file = libredox::Fd::open( + "/scheme/log/add_sink", + libredox::flag::O_WRONLY | libredox::flag::O_CLOEXEC, + 0, + ) + .expect("fbbootlogd: failed to open log/add_sink"); + log_file + .call_wo(&log_fd.to_ne_bytes(), syscall::CallFlags::FD, &[]) + .expect("fbbootlogd: failed to send log fd to log scheme."); + } + + register_sync_scheme(&socket, "fbbootlog", &mut scheme) + .expect("fbbootlog: failed to register scheme to namespace"); + // This is not possible for now as fbbootlogd needs to open new displays at runtime for graphics // driver handoff. In the future inputd may directly pass a handle to the display instead. //libredox::call::setrens(0, 0).expect("fbbootlogd: failed to enter null namespace"); diff --git a/drivers/graphics/fbbootlogd/src/scheme.rs b/drivers/graphics/fbbootlogd/src/scheme.rs index ff0108e46c..e94d72d2dc 100644 --- a/drivers/graphics/fbbootlogd/src/scheme.rs +++ b/drivers/graphics/fbbootlogd/src/scheme.rs @@ -10,7 +10,7 @@ use orbclient::{Event, EventOption}; use redox_scheme::scheme::SchemeSync; use redox_scheme::{CallerCtx, OpenResult}; use syscall::schemev2::NewFdFlags; -use syscall::{Error, Result, EINVAL, ENOENT}; +use syscall::{Error, Result, EACCES, EBADF, EINVAL, ENOENT}; pub struct FbbootlogScheme { pub input_handle: ConsumerHandle, @@ -173,8 +173,24 @@ impl FbbootlogScheme { } } +const SCHEME_ROOT_ID: usize = 1; + impl SchemeSync for FbbootlogScheme { - fn open(&mut self, path_str: &str, _flags: usize, _ctx: &CallerCtx) -> Result { + fn scheme_root(&mut self) -> Result { + Ok(SCHEME_ROOT_ID) + } + + fn openat( + &mut self, + dirfd: usize, + path_str: &str, + flags: usize, + _fcntl_flags: u32, + _ctx: &CallerCtx, + ) -> Result { + if dirfd != SCHEME_ROOT_ID { + return Err(Error::new(EACCES)); + } if !path_str.is_empty() { return Err(Error::new(ENOENT)); } @@ -214,12 +230,15 @@ impl SchemeSync for FbbootlogScheme { fn write( &mut self, - _id: usize, + id: usize, buf: &[u8], _offset: u64, _fcntl_flags: u32, _ctx: &CallerCtx, ) -> Result { + if id == SCHEME_ROOT_ID { + return Err(Error::new(EBADF)); + } if let Some(map) = &mut self.display_map { Self::handle_resize(map, &mut self.text_screen); self.text_buffer.write(buf); diff --git a/drivers/graphics/fbcond/src/main.rs b/drivers/graphics/fbcond/src/main.rs index e7e6764eae..3d74e8b31b 100644 --- a/drivers/graphics/fbcond/src/main.rs +++ b/drivers/graphics/fbcond/src/main.rs @@ -3,13 +3,13 @@ use inputd::ConsumerHandleEvent; use libredox::errno::{EAGAIN, EINTR}; use orbclient::Event; use redox_scheme::{ - scheme::{Op, SchemeResponse, SchemeSync}, + scheme::{register_sync_scheme, Op, SchemeResponse, SchemeSync}, CallerCtx, RequestKind, Response, SignalBehavior, Socket, }; use std::env; use syscall::{EOPNOTSUPP, EVENT_READ}; -use crate::scheme::{FbconScheme, VtIndex}; +use crate::scheme::{FbconScheme, Handle, VtIndex}; mod display; mod scheme; @@ -35,7 +35,7 @@ fn daemon(daemon: daemon::Daemon) -> ! { // FIXME listen for resize events from inputd and handle them - let mut socket = Socket::nonblock("fbcon").expect("fbcond: failed to create fbcon scheme"); + let mut socket = Socket::nonblock().expect("fbcond: failed to create fbcon scheme"); event_queue .subscribe( socket.inner().raw(), @@ -46,6 +46,9 @@ fn daemon(daemon: daemon::Daemon) -> ! { let mut scheme = FbconScheme::new(&vt_ids, &mut event_queue); + register_sync_scheme(&socket, "fbcon", &mut scheme) + .expect("fbcond: failed to register scheme to namespace"); + // This is not possible for now as fbcond needs to open new displays at runtime for graphics // driver handoff. In the future inputd may directly pass a handle to the display instead. // libredox::call::setrens(0, 0).expect("fbcond: failed to enter null namespace"); @@ -204,7 +207,8 @@ fn handle_event( for (handle_id, handle) in scheme.handles.iter_mut() { let handle = match handle { - handle => handle, + Handle::SchemeRoot => continue, + Handle::Vt(handle) => handle, }; if !handle.events.contains(EVENT_READ) { diff --git a/drivers/graphics/fbcond/src/scheme.rs b/drivers/graphics/fbcond/src/scheme.rs index bdb4583783..cc31bcb390 100644 --- a/drivers/graphics/fbcond/src/scheme.rs +++ b/drivers/graphics/fbcond/src/scheme.rs @@ -34,10 +34,15 @@ pub struct FdHandle { pub notified_read: bool, } +pub enum Handle { + Vt(FdHandle), + SchemeRoot, +} + pub struct FbconScheme { pub vts: BTreeMap, next_id: usize, - pub handles: BTreeMap, + pub handles: BTreeMap, } impl FbconScheme { @@ -65,14 +70,36 @@ impl FbconScheme { fn get_vt_handle_mut(&mut self, id: usize) -> Result<&mut FdHandle> { match self.handles.get_mut(&id) { - Some(handle) => Ok(handle), + Some(Handle::Vt(handle)) => Ok(handle), + Some(Handle::SchemeRoot) => Err(Error::new(EBADF)), None => Err(Error::new(EBADF)), } } } impl SchemeSync for FbconScheme { - fn open(&mut self, path_str: &str, flags: usize, _ctx: &CallerCtx) -> Result { + fn scheme_root(&mut self) -> Result { + let id = self.next_id; + self.next_id += 1; + self.handles.insert(id, Handle::SchemeRoot); + Ok(id) + } + + fn openat( + &mut self, + dirfd: usize, + path_str: &str, + flags: usize, + fcntl_flags: u32, + _ctx: &CallerCtx, + ) -> Result { + if !matches!( + self.handles.get(&dirfd).ok_or(Error::new(EBADF))?, + Handle::SchemeRoot + ) { + return Err(Error::new(EACCES)); + } + let vt_i = VtIndex(path_str.parse::().map_err(|_| Error::new(ENOENT))?); if self.vts.contains_key(&vt_i) { let id = self.next_id; @@ -80,12 +107,12 @@ impl SchemeSync for FbconScheme { self.handles.insert( id, - FdHandle { + Handle::Vt(FdHandle { vt_i, - flags: flags, + flags: flags | fcntl_flags as usize, events: EventFlags::empty(), notified_read: false, - }, + }), ); Ok(OpenResult::ThisScheme { @@ -104,7 +131,8 @@ impl SchemeSync for FbconScheme { _ctx: &CallerCtx, ) -> Result { let handle = match self.handles.get_mut(&id) { - Some(handle) => Ok(handle), + Some(Handle::Vt(handle)) => Ok(handle), + Some(Handle::SchemeRoot) => Err(Error::new(EBADF)), None => Err(Error::new(EBADF)), }?; @@ -116,7 +144,8 @@ impl SchemeSync for FbconScheme { fn fpath(&mut self, id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result { let handle = match self.handles.get(&id) { - Some(handle) => Ok(handle), + Some(Handle::Vt(handle)) => Ok(handle), + Some(Handle::SchemeRoot) => Err(Error::new(EBADF)), None => Err(Error::new(EBADF)), }?; @@ -134,7 +163,8 @@ impl SchemeSync for FbconScheme { fn fsync(&mut self, id: usize, _ctx: &CallerCtx) -> Result<()> { match self.handles.get(&id) { - Some(_) => Ok(()), + Some(Handle::Vt(_)) => Ok(()), + Some(Handle::SchemeRoot) => Err(Error::new(EBADF)), None => Err(Error::new(EBADF)), } } @@ -155,7 +185,8 @@ impl SchemeSync for FbconScheme { _ctx: &CallerCtx, ) -> Result { let handle = match self.handles.get(&id) { - Some(handle) => Ok(handle), + Some(Handle::Vt(handle)) => Ok(handle), + Some(Handle::SchemeRoot) => Err(Error::new(EBADF)), None => Err(Error::new(EBADF)), }?; @@ -183,7 +214,8 @@ impl SchemeSync for FbconScheme { _ctx: &CallerCtx, ) -> Result { let handle = match self.handles.get(&id) { - Some(handle) => Ok(handle), + Some(Handle::Vt(handle)) => Ok(handle), + Some(Handle::SchemeRoot) => Err(Error::new(EBADF)), None => Err(Error::new(EBADF)), }?; diff --git a/drivers/graphics/vesad/src/main.rs b/drivers/graphics/vesad/src/main.rs index 5d71b9d5fb..bf605cfe9a 100644 --- a/drivers/graphics/vesad/src/main.rs +++ b/drivers/graphics/vesad/src/main.rs @@ -12,6 +12,7 @@ use crate::scheme::{FbAdapter, FrameBuffer}; mod scheme; fn main() { + common::init(); daemon::Daemon::new(daemon); } fn daemon(daemon: daemon::Daemon) -> ! { diff --git a/drivers/graphics/virtio-gpud/src/main.rs b/drivers/graphics/virtio-gpud/src/main.rs index b34af2c60c..f46a17a3ba 100644 --- a/drivers/graphics/virtio-gpud/src/main.rs +++ b/drivers/graphics/virtio-gpud/src/main.rs @@ -476,6 +476,7 @@ impl MoveCursor { static DEVICE: spin::Once = spin::Once::new(); fn main() { + common::init(); pcid_interface::pci_daemon(daemon_runner); } @@ -524,7 +525,6 @@ fn deamon(deamon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> anyhow: device.transport.setup_config_notify(MSIX_PRIMARY_VECTOR); device.transport.run_device(); - deamon.ready(); let (mut scheme, mut inputd_handle) = scheme::GpuScheme::new( config, @@ -533,6 +533,7 @@ fn deamon(deamon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> anyhow: device.transport.clone(), has_edid, )?; + deamon.ready(); user_data! { enum Source { diff --git a/drivers/hwd/Cargo.toml b/drivers/hwd/Cargo.toml index 8ca6d9c04a..855c160c40 100644 --- a/drivers/hwd/Cargo.toml +++ b/drivers/hwd/Cargo.toml @@ -7,6 +7,7 @@ edition = "2018" fdt = "0.1.5" log.workspace = true ron = "0.8.1" +libredox = { version = "0.1.12", default-features = false, features = ["std", "call"] } amlserde = { path = "../amlserde" } common = { path = "../common" } diff --git a/drivers/hwd/src/backend/acpi.rs b/drivers/hwd/src/backend/acpi.rs index 42170b2221..1d24bb6f83 100644 --- a/drivers/hwd/src/backend/acpi.rs +++ b/drivers/hwd/src/backend/acpi.rs @@ -20,11 +20,23 @@ impl Backend for AcpiBackend { fn probe(&mut self) -> Result<(), Box> { // Read symbols from acpi scheme - for entry_res in fs::read_dir("/scheme/acpi/symbols")? { + let entries = fs::read_dir("/scheme/acpi/symbols")?; + // TODO: Reimplement with getdents? + let symbols_fd = libredox::Fd::open( + "/scheme/acpi/symbols", + libredox::flag::O_DIRECTORY | libredox::flag::O_RDONLY, + 0, + )?; + for entry_res in entries { let entry = entry_res?; if let Some(file_name) = entry.file_name().to_str() { if file_name.ends_with("_CID") || file_name.ends_with("_HID") { - let ron = fs::read_to_string(entry.path())?; + let symbol_fd = symbols_fd.openat(file_name, libredox::flag::O_RDONLY, 0)?; + let stat = symbol_fd.stat()?; + let mut buf: Vec = vec![0u8; stat.st_size as usize]; + let count = symbol_fd.read(&mut buf)?; + buf.truncate(count); + let ron = String::from_utf8(buf)?; let AmlSerde { name, value } = ron::from_str(&ron)?; let id = match value { AmlSerdeValue::Integer(integer) => { diff --git a/drivers/input/ps2d/src/main.rs b/drivers/input/ps2d/src/main.rs index ce044ce7ae..db87bc55fa 100644 --- a/drivers/input/ps2d/src/main.rs +++ b/drivers/input/ps2d/src/main.rs @@ -12,6 +12,9 @@ use std::{env, process}; use common::acquire_port_io_rights; use event::{user_data, EventQueue}; use inputd::ProducerHandle; +use log::info; +use redox_scheme::{scheme::register_sync_scheme, RequestKind, SignalBehavior, Socket}; +use syscall::{EAGAIN, EWOULDBLOCK}; use crate::state::Ps2d; diff --git a/drivers/input/usbhidd/src/main.rs b/drivers/input/usbhidd/src/main.rs index b6ab32d130..34ab9a9749 100644 --- a/drivers/input/usbhidd/src/main.rs +++ b/drivers/input/usbhidd/src/main.rs @@ -186,7 +186,7 @@ fn main() { interface_num ); - let handle = XhciClientHandle::new(scheme, port); + let handle = XhciClientHandle::new(scheme, port).expect("Faild to open XhciClientHandle"); let desc: DevDesc = handle .get_standard_descs() .expect("Failed to get standard descriptors"); diff --git a/drivers/inputd/src/main.rs b/drivers/inputd/src/main.rs index 16c2b241a9..ea3e13bbc2 100644 --- a/drivers/inputd/src/main.rs +++ b/drivers/inputd/src/main.rs @@ -20,13 +20,12 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use inputd::{ControlEvent, VtEvent, VtEventKind}; use libredox::errno::ESTALE; -use log::warn; -use redox_scheme::scheme::SchemeSync; +use redox_scheme::scheme::{register_sync_scheme, SchemeSync}; use redox_scheme::{CallerCtx, OpenResult, RequestKind, Response, SignalBehavior, Socket}; use orbclient::{Event, EventOption}; use syscall::schemev2::NewFdFlags; -use syscall::{Error as SysError, EventFlags, EINVAL}; +use syscall::{Error as SysError, EventFlags, EACCES, EBADF, EINVAL}; pub mod keymap; @@ -54,6 +53,7 @@ enum Handle { is_earlyfb: bool, }, Control, + SchemeRoot, } struct InputScheme { @@ -152,7 +152,27 @@ impl InputScheme { } impl SchemeSync for InputScheme { - fn open(&mut self, path: &str, _flags: usize, _ctx: &CallerCtx) -> syscall::Result { + fn scheme_root(&mut self) -> syscall::Result { + let fd = self.next_id.fetch_add(1, Ordering::SeqCst); + self.handles.insert(fd, Handle::SchemeRoot); + Ok(fd) + } + + fn openat( + &mut self, + dirfd: usize, + path: &str, + _flags: usize, + _fcntl_flags: u32, + _ctx: &CallerCtx, + ) -> syscall::Result { + if !matches!( + self.handles.get(&dirfd).ok_or(SysError::new(EINVAL))?, + Handle::SchemeRoot + ) { + return Err(SysError::new(EACCES)); + } + let mut path_parts = path.split('/'); let command = path_parts.next().ok_or(SysError::new(EINVAL))?; @@ -167,7 +187,6 @@ impl SchemeSync for InputScheme { if self.active_vt.is_none() { self.switch_vt(vt); } - Handle::Consumer { events: EventFlags::empty(), pending: Vec::new(), @@ -319,6 +338,7 @@ impl SchemeSync for InputScheme { log::error!("control tried to read"); return Err(SysError::new(EINVAL)); } + Handle::SchemeRoot => return Err(SysError::new(EBADF)), } } @@ -364,6 +384,7 @@ impl SchemeSync for InputScheme { return Err(SysError::new(EINVAL)); } Handle::Producer => {} + Handle::SchemeRoot => return Err(SysError::new(EBADF)), } if buf.len() == 1 && buf[0] > 0xf4 { @@ -508,11 +529,10 @@ impl SchemeSync for InputScheme { log::error!("producer or control tried to use an event queue"); Err(SysError::new(EINVAL)) } + Handle::SchemeRoot => Err(SysError::new(EBADF)), } } -} -impl InputScheme { fn on_close(&mut self, id: usize) { let handle = self.handles.remove(&id).unwrap(); @@ -534,9 +554,10 @@ impl InputScheme { fn deamon(deamon: daemon::Daemon) -> anyhow::Result<()> { // Create the ":input" scheme. - let socket_file = Socket::create("input")?; + let socket_file = Socket::create()?; let mut scheme = InputScheme::new(); + register_sync_scheme(&socket_file, "input", &mut scheme)?; deamon.ready(); loop { diff --git a/drivers/net/alxd/src/device/mod.rs b/drivers/net/alxd/src/device/mod.rs index 94a09b8a86..1c3f8dc3ce 100644 --- a/drivers/net/alxd/src/device/mod.rs +++ b/drivers/net/alxd/src/device/mod.rs @@ -1787,8 +1787,23 @@ impl Alx { } } +const SCHEME_ROOT_ID: usize = usize::MAX; + impl SchemeSync for Alx { - fn open(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result { + fn scheme_root(&mut self) -> Result { + Ok(SCHEME_ROOT_ID) + } + fn openat( + &mut self, + dirfd: usize, + path_str: &str, + flags: usize, + _fcntl_flags: u32, + ctx: &CallerCtx, + ) -> Result { + if dirfd != SCHEME_ROOT_ID { + return Err(Error::new(EACCES)); + } if ctx.uid == 0 { Ok(OpenResult::ThisScheme { number: flags, diff --git a/drivers/net/alxd/src/main.rs b/drivers/net/alxd/src/main.rs index 3d9d266599..628c0f824d 100644 --- a/drivers/net/alxd/src/main.rs +++ b/drivers/net/alxd/src/main.rs @@ -12,6 +12,7 @@ use std::os::fd::AsRawFd; use std::{env, iter}; use event::{user_data, EventQueue}; +use redox_scheme::scheme::register_sync_scheme; use redox_scheme::wrappers::ReadinessBased; use redox_scheme::Socket; use std::cell::RefCell; @@ -19,6 +20,7 @@ use std::cell::RefCell; pub mod device; fn main() { + common::init(); daemon::Daemon::new(daemon); } @@ -36,11 +38,9 @@ fn daemon(daemon: daemon::Daemon) -> ! { println!(" + ALX {} on: {:X}, IRQ: {}\n", name, bar, irq); - let socket = Socket::nonblock("network").expect("alxd: failed to create socket"); + let socket = Socket::nonblock().expect("alxd: failed to create socket"); let mut readiness_based = ReadinessBased::new(&socket, 16); - daemon.ready(); - let mut irq_file = File::open(format!("/scheme/irq/{}", irq)).expect("alxd: failed to open IRQ file"); @@ -54,10 +54,16 @@ fn daemon(daemon: daemon::Daemon) -> ! { .expect("alxd: failed to map address") as usize }; { - let device = RefCell::new(unsafe { - device::Alx::new(address).expect("alxd: failed to allocate device") + let device = RefCell::new({ + let mut device = + unsafe { device::Alx::new(address).expect("alxd: failed to allocate device") }; + register_sync_scheme(&socket, "network", &mut device) + .expect("alxd: failed to register network scheme to namespace"); + device }); + daemon.ready(); + user_data! { enum Source { Irq, diff --git a/drivers/net/driver-network/src/lib.rs b/drivers/net/driver-network/src/lib.rs index d2e3fc0b04..a4f5418907 100644 --- a/drivers/net/driver-network/src/lib.rs +++ b/drivers/net/driver-network/src/lib.rs @@ -52,9 +52,12 @@ impl NetworkScheme { daemon: daemon::Daemon, scheme_name: String, ) -> Self { - let socket = Socket::nonblock(&scheme_name).expect("failed to create network scheme"); + assert!(scheme_name.starts_with("network")); + let socket = Socket::nonblock().expect("failed to create network scheme"); let adapter = adapter_fn(); - let scheme = NetworkSchemeInner::new(adapter, scheme_name.clone()); + let mut scheme = NetworkSchemeInner::new(adapter, scheme_name.clone()); + redox_scheme::scheme::register_sync_scheme(&socket, &scheme_name, &mut scheme) + .expect("failed to regitster network scheme"); daemon.ready(); Self { scheme, @@ -196,6 +199,7 @@ struct NetworkSchemeInner { enum Handle { Data, Mac, + SchemeRoot, } impl NetworkSchemeInner { @@ -210,7 +214,27 @@ impl NetworkSchemeInner { } impl SchemeSync for NetworkSchemeInner { - fn open(&mut self, path: &str, _flags: usize, ctx: &CallerCtx) -> Result { + fn scheme_root(&mut self) -> Result { + let id = self.next_id; + self.next_id += 1; + self.handles.insert(id, Handle::SchemeRoot); + Ok(id) + } + + fn openat( + &mut self, + fd: usize, + path: &str, + _flags: usize, + _fcntl_flags: u32, + ctx: &CallerCtx, + ) -> Result { + if !matches!( + self.handles.get(&fd).ok_or(Error::new(EINVAL))?, + Handle::SchemeRoot + ) { + return Err(Error::new(EACCES)); + } if ctx.uid != 0 { return Err(Error::new(EACCES)); } @@ -247,6 +271,7 @@ impl SchemeSync for NetworkSchemeInner { buf[..i].copy_from_slice(&data[..i]); return Ok(i); } + _ => return Err(Error::new(EBADF)), }; match self.adapter.read_packet(buf)? { @@ -274,6 +299,7 @@ impl SchemeSync for NetworkSchemeInner { match handle { Handle::Data => {} Handle::Mac { .. } => return Err(Error::new(EINVAL)), + _ => return Err(Error::new(EBADF)), } Ok(self.adapter.write_packet(buf)?) @@ -306,6 +332,7 @@ impl SchemeSync for NetworkSchemeInner { let path = match handle { Handle::Data { .. } => &b""[..], Handle::Mac { .. } => &b"mac"[..], + _ => &b""[..], }; j = 0; @@ -329,6 +356,7 @@ impl SchemeSync for NetworkSchemeInner { stat.st_mode = MODE_FILE | 0o400; stat.st_size = 6; } + _ => return Err(Error::new(EBADF)), } Ok(()) diff --git a/drivers/net/e1000d/src/main.rs b/drivers/net/e1000d/src/main.rs index 373ea9b38b..a70d3904ed 100644 --- a/drivers/net/e1000d/src/main.rs +++ b/drivers/net/e1000d/src/main.rs @@ -8,6 +8,7 @@ use pcid_interface::PciFunctionHandle; pub mod device; fn main() { + common::init(); pcid_interface::pci_daemon(daemon); } diff --git a/drivers/net/ixgbed/src/main.rs b/drivers/net/ixgbed/src/main.rs index 4a6ce74dcd..7dfcd114aa 100644 --- a/drivers/net/ixgbed/src/main.rs +++ b/drivers/net/ixgbed/src/main.rs @@ -10,6 +10,7 @@ pub mod device; mod ixgbe; fn main() { + common::init(); pcid_interface::pci_daemon(daemon); } diff --git a/drivers/net/rtl8139d/src/main.rs b/drivers/net/rtl8139d/src/main.rs index d470e814df..66d5329770 100644 --- a/drivers/net/rtl8139d/src/main.rs +++ b/drivers/net/rtl8139d/src/main.rs @@ -36,6 +36,7 @@ fn map_bar(pcid_handle: &mut PciFunctionHandle) -> *mut u8 { } fn main() { + common::init(); pcid_interface::pci_daemon(daemon); } diff --git a/drivers/net/rtl8168d/src/main.rs b/drivers/net/rtl8168d/src/main.rs index 1d9963a3ad..9fae9eed1b 100644 --- a/drivers/net/rtl8168d/src/main.rs +++ b/drivers/net/rtl8168d/src/main.rs @@ -36,6 +36,7 @@ fn map_bar(pcid_handle: &mut PciFunctionHandle) -> *mut u8 { } fn main() { + common::init(); pcid_interface::pci_daemon(daemon); } diff --git a/drivers/net/virtio-netd/src/main.rs b/drivers/net/virtio-netd/src/main.rs index 17d168efdf..833fd9e843 100644 --- a/drivers/net/virtio-netd/src/main.rs +++ b/drivers/net/virtio-netd/src/main.rs @@ -27,6 +27,7 @@ static_assertions::const_assert_eq!(core::mem::size_of::(), 12); const MAX_BUFFER_LEN: usize = 65535; fn main() { + common::init(); pcid_interface::pci_daemon(daemon_runner); } diff --git a/drivers/pcid-spawner/src/main.rs b/drivers/pcid-spawner/src/main.rs index 97cc3cece0..f57cb50f92 100644 --- a/drivers/pcid-spawner/src/main.rs +++ b/drivers/pcid-spawner/src/main.rs @@ -90,7 +90,7 @@ fn main() -> Result<()> { match command.status() { Ok(status) if !status.success() => { - log::error!("pcid-spawner: driver {command:?} failed with {status}"); + log::error!("pcid-spawner: driver {command:?} failed with {status}") } Ok(_) => {} Err(err) => log::error!("pcid-spawner: failed to execute {command:?}: {err}"), diff --git a/drivers/pcid/src/driver_interface/mod.rs b/drivers/pcid/src/driver_interface/mod.rs index 10d725e8e7..a41f9fc829 100644 --- a/drivers/pcid/src/driver_interface/mod.rs +++ b/drivers/pcid/src/driver_interface/mod.rs @@ -327,9 +327,10 @@ impl PciFunctionHandle { } pub fn connect_by_path(device_path: &Path) -> io::Result { - let channel_fd = syscall::open( + let channel_fd = libredox::call::open( device_path.join("channel").to_str().unwrap(), - syscall::O_RDWR, + libredox::flag::O_RDWR, + 0, ) .map_err(|err| io::Error::other(format!("failed to open pcid channel: {}", err)))?; Ok(Self::connect_common(channel_fd as RawFd)) diff --git a/drivers/pcid/src/main.rs b/drivers/pcid/src/main.rs index 88aa2807e4..5d8100f5da 100644 --- a/drivers/pcid/src/main.rs +++ b/drivers/pcid/src/main.rs @@ -10,7 +10,7 @@ use pci_types::{ Bar as TyBar, CommandRegister, EndpointHeader, HeaderType, PciAddress, PciHeader as TyPciHeader, PciPciBridgeHeader, }; -use redox_scheme::{RequestKind, SignalBehavior}; +use redox_scheme::{scheme::register_sync_scheme, RequestKind, SignalBehavior}; use crate::cfg_access::Pcie; use pcid_interface::{FullDeviceId, LegacyInterruptLine, PciBar, PciFunction, PciRom}; @@ -232,6 +232,7 @@ fn enable_function( } fn main() { + common::init(); daemon::Daemon::new(daemon); } @@ -245,10 +246,39 @@ fn daemon(daemon: daemon::Daemon) -> ! { ); let pcie = Pcie::new(); - let mut tree = BTreeMap::new(); info!("PCI SG-BS:DV.F VEND:DEVI CL.SC.IN.RV"); + let mut scheme = scheme::PciScheme::new(pcie); + let socket = redox_scheme::Socket::create().expect("failed to open pci scheme socket"); + + { + match libredox::Fd::open("/scheme/acpi/register_pci", libredox::flag::O_WRONLY, 0) { + Ok(register_pci) => { + let access_id = scheme.access(); + + let access_fd = socket + .create_this_scheme_fd(0, access_id, syscall::O_RDWR, 0) + .expect("failed to issue this resource"); + let access_bytes = access_fd.to_ne_bytes(); + let _ = register_pci + .call_wo( + &access_bytes, + syscall::CallFlags::WRITE | syscall::CallFlags::FD, + &[], + ) + .expect("failed to send pci_fd to acpid"); + } + Err(err) => { + if err.errno() == libredox::errno::ENODEV { + debug!("pcid: acpid not found. Running without ACPI integration."); + } else { + warn!("pcid: failed to open acpid register_pci (error: {}). Running without ACPI integration.", err); + } + } + } + } + // FIXME Use full ACPI for enumerating the host bridges. MCFG only describes the first // host bridge, while multi-processor systems likely have a host bridge for each CPU. // See also https://www.kernel.org/doc/html/latest/PCI/acpi-info.html @@ -261,13 +291,19 @@ fn daemon(daemon: daemon::Daemon) -> ! { bus_i += 1; for dev_num in 0..32 { - scan_device(&mut tree, &pcie, &mut bus_nums, bus_num, dev_num); + scan_device( + &mut scheme.tree, + &scheme.pcie, + &mut bus_nums, + bus_num, + dev_num, + ); } } debug!("Enumeration complete, now starting pci scheme"); - let mut scheme = scheme::PciScheme::new(pcie, tree); - let socket = redox_scheme::Socket::create("pci").expect("failed to open pci scheme socket"); + register_sync_scheme(&socket, "pci", &mut scheme) + .expect("failed to register pci scheme to namespace"); let _ = daemon.ready(); diff --git a/drivers/pcid/src/scheme.rs b/drivers/pcid/src/scheme.rs index 4da2ea15f7..b13d31b9a1 100644 --- a/drivers/pcid/src/scheme.rs +++ b/drivers/pcid/src/scheme.rs @@ -14,14 +14,15 @@ use crate::cfg_access::Pcie; pub struct PciScheme { handles: BTreeMap, next_id: usize, - pcie: Pcie, - tree: BTreeMap, + pub pcie: Pcie, + pub tree: BTreeMap, } enum Handle { TopLevel { entries: Vec }, Access, Device, Channel { addr: PciAddress, st: ChannelState }, + SchemeRoot, } struct HandleWrapper { inner: Handle, @@ -38,6 +39,9 @@ impl Handle { fn requires_root(&self) -> bool { matches!(self, Self::Access | Self::Channel { .. }) } + fn is_scheme_root(&self) -> bool { + matches!(self, Self::SchemeRoot) + } } enum ChannelState { @@ -47,8 +51,50 @@ enum ChannelState { const DEVICE_CONTENTS: &[&str] = &["channel"]; +impl PciScheme { + pub fn access(&mut self) -> usize { + let id = self.next_id; + self.next_id += 1; + + self.handles.insert( + id, + HandleWrapper { + inner: Handle::Access, + stat: false, + }, + ); + + id + } +} + impl SchemeSync for PciScheme { - fn open(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result { + fn scheme_root(&mut self) -> Result { + let id = self.next_id; + self.next_id += 1; + + self.handles.insert( + id, + HandleWrapper { + inner: Handle::SchemeRoot, + stat: false, + }, + ); + Ok(id) + } + fn openat( + &mut self, + dirfd: usize, + path: &str, + flags: usize, + _fcntl_flags: u32, + ctx: &CallerCtx, + ) -> Result { + let handle = self.handles.get(&dirfd).ok_or(Error::new(EBADF))?; + if !handle.inner.is_scheme_root() { + return Err(Error::new(EACCES)); + } + log::trace!("OPEN `{}` flags {}", path, flags); // TODO: Check flags are correct @@ -108,6 +154,7 @@ impl SchemeSync for PciScheme { Handle::TopLevel { ref entries } => (entries.len(), MODE_DIR | 0o755), Handle::Device => (DEVICE_CONTENTS.len(), MODE_DIR | 0o755), Handle::Access | Handle::Channel { .. } => (0, MODE_CHR | 0o600), + Handle::SchemeRoot => return Err(Error::new(EBADF)), }; stat.st_size = len as u64; stat.st_mode = mode; @@ -134,6 +181,7 @@ impl SchemeSync for PciScheme { addr: _, ref mut st, } => Self::read_channel(st, buf), + Handle::SchemeRoot => Err(Error::new(EBADF)), _ => Err(Error::new(EBADF)), } } @@ -167,6 +215,7 @@ impl SchemeSync for PciScheme { } Handle::Device => DEVICE_CONTENTS, Handle::Access | Handle::Channel { .. } => return Err(Error::new(ENOTDIR)), + Handle::SchemeRoot => return Err(Error::new(EBADF)), }; for (i, dent_name) in entries.iter().enumerate().skip(offset) { @@ -296,12 +345,12 @@ impl PciScheme { } impl PciScheme { - pub fn new(pcie: Pcie, tree: BTreeMap) -> Self { + pub fn new(pcie: Pcie) -> Self { Self { handles: BTreeMap::new(), next_id: 0, pcie, - tree, + tree: BTreeMap::new(), } } fn parse_after_pci_addr(&mut self, addr: PciAddress, after: &str) -> Result { diff --git a/drivers/redoxerd/src/main.rs b/drivers/redoxerd/src/main.rs index ee39cbe4e7..5ecd0b4672 100644 --- a/drivers/redoxerd/src/main.rs +++ b/drivers/redoxerd/src/main.rs @@ -9,10 +9,6 @@ mod sys; const DEFAULT_COLS: u32 = 80; const DEFAULT_LINES: u32 = 30; -pub fn syscall_error(error: syscall::Error) -> io::Error { - io::Error::from_raw_os_error(error.errno) -} - event::user_data! { enum EventData { Pty, @@ -32,15 +28,15 @@ fn handle( let mut packet = [0; 4096]; loop { // Read data from PTY master - let count = match syscall::read(master_fd as usize, &mut packet) { + let count = match libredox::call::read(master_fd as usize, &mut packet) { Ok(0) => return Ok(false), Ok(count) => count, - Err(ref err) if err.errno == syscall::EAGAIN => return Ok(true), - Err(err) => return Err(syscall_error(err)), + Err(ref err) if err.errno() == libredox::errno::EAGAIN => return Ok(true), + Err(err) => return Err(err.into()), }; // Write data to stdout - syscall::write(1, &packet[1..count]).map_err(syscall_error)?; + libredox::call::write(1, &packet[1..count])?; for i in 1..count { // Write byte to QEMU debugcon (Bochs compatible) @@ -50,10 +46,10 @@ fn handle( } EventData::Timer => { let mut timespec = syscall::TimeSpec::default(); - syscall::read(timeout_fd as usize, &mut timespec).map_err(syscall_error)?; + libredox::call::read(timeout_fd as usize, &mut timespec)?; timespec.tv_sec += 1; - syscall::write(timeout_fd as usize, &mut timespec).map_err(syscall_error)?; + libredox::call::write(timeout_fd as usize, &mut timespec)?; Ok(true) } @@ -85,25 +81,28 @@ fn handle( } fn getpty(columns: u32, lines: u32) -> io::Result<(RawFd, String)> { - let master = syscall::open( + let master = libredox::call::open( "/scheme/pty", - syscall::O_CLOEXEC | syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK, - ) - .map_err(syscall_error)?; + libredox::flag::O_CLOEXEC + | libredox::flag::O_RDWR + | libredox::flag::O_CREAT + | libredox::flag::O_NONBLOCK, + 0, + )?; - if let Ok(winsize_fd) = syscall::dup(master, b"winsize") { - let _ = syscall::write( + if let Ok(winsize_fd) = libredox::call::dup(master, b"winsize") { + let _ = libredox::call::write( winsize_fd, &redox_termios::Winsize { ws_row: lines as u16, ws_col: columns as u16, }, ); - let _ = syscall::close(winsize_fd); + let _ = libredox::call::close(winsize_fd); } let mut buf: [u8; 4096] = [0; 4096]; - let count = syscall::fpath(master, &mut buf).map_err(syscall_error)?; + let count = libredox::call::fpath(master, &mut buf)?; Ok((master as RawFd, unsafe { String::from_utf8_unchecked(Vec::from(&buf[..count])) })) @@ -118,11 +117,11 @@ fn inner() -> anyhow::Result<()> { let (columns, lines) = (DEFAULT_COLS, DEFAULT_LINES); let (master_fd, pty) = getpty(columns, lines)?; - let timeout_fd = syscall::open( + let timeout_fd = libredox::call::open( "/scheme/time/4", - syscall::O_CLOEXEC | syscall::O_RDWR | syscall::O_NONBLOCK, - ) - .map_err(syscall_error)? as RawFd; + libredox::flag::O_CLOEXEC | libredox::flag::O_RDWR | libredox::flag::O_NONBLOCK, + 0, + )? as RawFd; let event_queue = event::EventQueue::new()?; event_queue.subscribe(master_fd as usize, EventData::Pty, event::EventFlags::READ)?; diff --git a/drivers/storage/ahcid/src/main.rs b/drivers/storage/ahcid/src/main.rs index 1f130a2974..59aada4164 100644 --- a/drivers/storage/ahcid/src/main.rs +++ b/drivers/storage/ahcid/src/main.rs @@ -14,6 +14,7 @@ use log::{error, info}; pub mod ahci; fn main() { + common::init(); pcid_interface::pci_daemon(daemon); } diff --git a/drivers/storage/driver-block/src/lib.rs b/drivers/storage/driver-block/src/lib.rs index b8cfdb1963..35241f6eeb 100644 --- a/drivers/storage/driver-block/src/lib.rs +++ b/drivers/storage/driver-block/src/lib.rs @@ -11,13 +11,13 @@ use std::task::Poll; use executor::LocalExecutor; use libredox::Fd; use partitionlib::{LogicalBlockSize, PartitionTable}; -use redox_scheme::scheme::SchemeAsync; +use redox_scheme::scheme::{register_scheme_inner, SchemeAsync}; use redox_scheme::{CallerCtx, OpenResult, RequestKind, Response, SignalBehavior, Socket}; use syscall::dirent::DirentBuf; use syscall::schemev2::NewFdFlags; use syscall::{ - Error, Result, Stat, EACCES, EAGAIN, EBADF, EINTR, EINVAL, EISDIR, ENOENT, ENOLCK, EOPNOTSUPP, - EOVERFLOW, EWOULDBLOCK, MODE_DIR, MODE_FILE, O_DIRECTORY, O_STAT, + CallFlags, Error, Result, Stat, EACCES, EAGAIN, EBADF, EINTR, EINVAL, EISDIR, ENOENT, ENOLCK, + EOPNOTSUPP, EOVERFLOW, EWOULDBLOCK, MODE_DIR, MODE_FILE, O_DIRECTORY, O_STAT, }; /// Split the read operation into a series of block reads. @@ -258,6 +258,7 @@ enum Handle { List(Vec), // entries Disk(u32), // disk num Partition(u32, u32), // disk num, part num + SchemeRoot, } pub struct DiskScheme { @@ -310,14 +311,10 @@ impl DiskScheme { executor: &impl ExecutorTrait, ) -> Self { assert!(scheme_name.starts_with("disk")); - let socket = Socket::nonblock(&scheme_name).expect("failed to create disk scheme"); + let socket = Socket::nonblock().expect("failed to create disk scheme"); - if let Some(daemon) = daemon { - daemon.ready(); - } - - Self { - scheme_name, + let mut scheme = Self { + scheme_name: scheme_name, socket, disks: disks .into_iter() @@ -325,7 +322,19 @@ impl DiskScheme { .collect(), next_id: 0, handles: BTreeMap::new(), + }; + + let cap_id = scheme + .scheme_root() + .expect("failed to get this scheme root"); + register_scheme_inner(&scheme.socket, &scheme.scheme_name, cap_id) + .expect("failed to register disk scheme root"); + + if let Some(daemon) = daemon { + daemon.ready(); } + + scheme } pub fn event_handle(&self) -> &Fd { @@ -358,7 +367,8 @@ impl DiskScheme { // correctly ordered wrt IO on the same fd. call_request.handle_async(self).await } - RequestKind::SendFd(sendfd_request) => Response::err(EOPNOTSUPP, sendfd_request), + RequestKind::SendFd(request) => Response::err(EOPNOTSUPP, request), + RequestKind::RecvFd(request) => Response::err(EOPNOTSUPP, request), RequestKind::Cancellation(_cancellation_request) => { // FIXME implement cancellation continue; @@ -410,7 +420,27 @@ impl DiskScheme { } impl SchemeAsync for DiskScheme { - async fn open(&mut self, path_str: &str, flags: usize, ctx: &CallerCtx) -> Result { + fn scheme_root(&mut self) -> Result { + let id = self.next_id; + self.next_id += 1; + self.handles.insert(id, Handle::SchemeRoot); + Ok(id) + } + async fn openat( + &mut self, + dirfd: usize, + path_str: &str, + flags: usize, + _fcntl_flags: u32, + ctx: &CallerCtx, + ) -> Result { + if !matches!( + self.handles.get(&dirfd).ok_or(Error::new(EBADF))?, + Handle::SchemeRoot + ) { + return Err(Error::new(EACCES)); + } + if ctx.uid != 0 { return Err(Error::new(EACCES)); } @@ -522,6 +552,7 @@ impl SchemeAsync for DiskScheme { stat.st_blksize = disk.block_size(); Ok(()) } + Handle::SchemeRoot => Err(Error::new(EBADF)), } } @@ -566,6 +597,7 @@ impl SchemeAsync for DiskScheme { j += 1; } } + Handle::SchemeRoot => return Err(Error::new(EBADF)), } Ok(i) @@ -599,6 +631,7 @@ impl SchemeAsync for DiskScheme { let block = offset / u64::from(disk.block_size()); disk.read(Some(part_num as usize), block, buf).await } + Handle::SchemeRoot => Err(Error::new(EBADF)), } } @@ -622,6 +655,7 @@ impl SchemeAsync for DiskScheme { let block = offset / u64::from(disk.block_size()); disk.write(Some(part_num as usize), block, buf).await } + Handle::SchemeRoot => Err(Error::new(EBADF)), } } @@ -644,6 +678,7 @@ impl SchemeAsync for DiskScheme { part.size * u64::from(disk.block_size()) } + Handle::SchemeRoot => return Err(Error::new(EBADF)), }) } } diff --git a/drivers/storage/ided/src/main.rs b/drivers/storage/ided/src/main.rs index 4197217dcd..35860c62ac 100644 --- a/drivers/storage/ided/src/main.rs +++ b/drivers/storage/ided/src/main.rs @@ -20,6 +20,7 @@ use crate::ide::{AtaCommand, AtaDisk, Channel}; pub mod ide; fn main() { + common::init(); pcid_interface::pci_daemon(daemon); } diff --git a/drivers/storage/nvmed/src/main.rs b/drivers/storage/nvmed/src/main.rs index beb1b68940..a488f4bc46 100644 --- a/drivers/storage/nvmed/src/main.rs +++ b/drivers/storage/nvmed/src/main.rs @@ -57,6 +57,7 @@ fn time_arm(time_handle: &mut File, secs: i64) -> io::Result<()> { } fn main() { + common::init(); pcid_interface::pci_daemon(daemon); } diff --git a/drivers/storage/usbscsid/src/main.rs b/drivers/storage/usbscsid/src/main.rs index c438c8781f..b52acfc9d9 100644 --- a/drivers/storage/usbscsid/src/main.rs +++ b/drivers/storage/usbscsid/src/main.rs @@ -39,10 +39,8 @@ fn daemon(daemon: daemon::Daemon) -> ! { let disk_scheme_name = format!("disk.usb-{scheme}+{port}-scsi"); // TODO: Use eventfds. - let handle = XhciClientHandle::new(scheme.to_owned(), port); - - // FIXME should this wait notifying readiness until the disk scheme is created? - daemon.ready(); + let handle = + XhciClientHandle::new(scheme.to_owned(), port).expect("Faild to open XhciClientHandle"); let desc = handle .get_standard_descs() @@ -110,6 +108,9 @@ fn daemon(daemon: daemon::Daemon) -> ! { &driver_block::FuturesExecutor, ); + // FIXME should this wait notifying readiness until the disk scheme is created? + daemon.ready(); + //libredox::call::setrens(0, 0).expect("nvmed: failed to enter null namespace"); event_queue diff --git a/drivers/usb/usbctl/Cargo.toml b/drivers/usb/usbctl/Cargo.toml index ee18af204b..c2a11e2b81 100644 --- a/drivers/usb/usbctl/Cargo.toml +++ b/drivers/usb/usbctl/Cargo.toml @@ -9,3 +9,4 @@ edition = "2018" [dependencies] clap = "2.33" xhcid = { path = "../xhcid" } +common = { path = "../../common" } diff --git a/drivers/usb/usbctl/src/main.rs b/drivers/usb/usbctl/src/main.rs index 517cde041a..04c90eb99a 100644 --- a/drivers/usb/usbctl/src/main.rs +++ b/drivers/usb/usbctl/src/main.rs @@ -2,6 +2,7 @@ use clap::{App, Arg}; use xhcid_interface::{PortId, XhciClientHandle}; fn main() { + common::init(); let matches = App::new("usbctl") .arg( Arg::with_name("SCHEME") @@ -35,7 +36,8 @@ fn main() { .parse::() .expect("expected PORT ID"); - let handle = XhciClientHandle::new(scheme.to_owned(), port); + let handle = + XhciClientHandle::new(scheme.to_owned(), port).expect("Faild to open XhciClientHandle"); if let Some(_status_scmd_matches) = port_scmd_matches.subcommand_matches("status") { let state = handle.port_state().expect("Failed to get port state"); diff --git a/drivers/usb/usbhubd/src/main.rs b/drivers/usb/usbhubd/src/main.rs index 8df44c0493..1ef070f526 100644 --- a/drivers/usb/usbhubd/src/main.rs +++ b/drivers/usb/usbhubd/src/main.rs @@ -6,6 +6,7 @@ use xhcid_interface::{ }; fn main() { + common::init(); let mut args = env::args().skip(1); const USAGE: &'static str = "usbhubd "; @@ -38,7 +39,8 @@ fn main() { common::file_level(), ); - let handle = XhciClientHandle::new(scheme.clone(), port_id); + let handle = + XhciClientHandle::new(scheme.clone(), port_id).expect("Faild to open XhciClientHandle"); let desc: DevDesc = handle .get_standard_descs() .expect("Failed to get standard descriptors"); @@ -146,7 +148,8 @@ fn main() { } else { usb::HubPortStatus::V2(usb::HubPortStatusV2::default()) }, - handle: XhciClientHandle::new(scheme.clone(), child_port_id), + handle: XhciClientHandle::new(scheme.clone(), child_port_id) + .expect("Faild to open XhciClientHandle"), attached: false, }); } diff --git a/drivers/usb/xhcid/src/driver_interface.rs b/drivers/usb/xhcid/src/driver_interface.rs index 3268aa3de9..f1547dc876 100644 --- a/drivers/usb/xhcid/src/driver_interface.rs +++ b/drivers/usb/xhcid/src/driver_interface.rs @@ -2,9 +2,11 @@ pub extern crate serde; pub extern crate smallvec; use std::convert::TryFrom; -use std::fs::{File, OpenOptions}; +use std::fs::File; use std::io::prelude::*; use std::num::NonZeroU8; +use std::os::fd::{FromRawFd, RawFd}; +use std::string::FromUtf8Error; use std::{fmt, io, result, str}; use serde::{Deserialize, Serialize}; @@ -392,11 +394,20 @@ impl str::FromStr for PortId { } } -#[derive(Debug)] pub struct XhciClientHandle { + fd: libredox::Fd, scheme: String, port: PortId, } +impl fmt::Debug for XhciClientHandle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("XhciClientHandle") + .field("scheme", &self.scheme) + .field("port", &self.port) + .field("fd", &"libredox::Fd") + .finish() + } +} #[repr(u8)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum PortState { @@ -522,34 +533,43 @@ impl DeviceReqData<'_> { } impl XhciClientHandle { - pub fn new(scheme: String, port: PortId) -> Self { - Self { scheme, port } + pub fn new(scheme: String, port: PortId) -> result::Result { + let path = format!("/scheme/{}/port{}", scheme, port); + let fd = libredox::Fd::open(&path, libredox::flag::O_DIRECTORY, 0)?; + Ok(Self { fd, scheme, port }) + } + fn read(&self, path: &str) -> result::Result, XhciClientHandleError> { + let target_fd = self.fd.openat(path, libredox::flag::O_RDONLY, 0)?; + let stat = target_fd.stat()?; + let mut buf: Vec = vec![0u8; stat.st_size as usize]; + let count = target_fd.read(&mut buf)?; + buf.truncate(count); + Ok(buf) + } + fn read_to_string(&self, path: &str) -> result::Result { + let buf = self.read(path)?; + Ok(String::from_utf8(buf)?) } - pub fn attach(&self) -> result::Result<(), XhciClientHandleError> { - let path = format!("/scheme/{}/port{}/attach", self.scheme, self.port); - let mut file = OpenOptions::new().read(false).write(true).open(path)?; + let mut file = self.fd.openat("attach", libredox::flag::O_WRONLY, 0)?; let _bytes_written = file.write(&[])?; Ok(()) } pub fn detach(&self) -> result::Result<(), XhciClientHandleError> { - let path = format!("/scheme/{}/port{}/detach", self.scheme, self.port); - let mut file = OpenOptions::new().read(false).write(true).open(path)?; + let mut file = self.fd.openat("detach", libredox::flag::O_WRONLY, 0)?; let _bytes_written = file.write(&[])?; Ok(()) } pub fn get_standard_descs(&self) -> result::Result { - let path = format!("/scheme/{}/port{}/descriptors", self.scheme, self.port); - let json = std::fs::read(path)?; + let json = self.read("descriptors")?; Ok(serde_json::from_slice(&json)?) } pub fn configure_endpoints( &self, req: &ConfigureEndpointsReq, ) -> result::Result<(), XhciClientHandleError> { - let path = format!("/scheme/{}/port{}/configure", self.scheme, self.port); let json = serde_json::to_vec(req)?; - let mut file = OpenOptions::new().read(false).write(true).open(path)?; + let mut file = self.fd.openat("configure", libredox::flag::O_WRONLY, 0)?; let json_bytes_written = file.write(&json)?; if json_bytes_written != json.len() { return Err(XhciClientHandleError::InvalidResponse(Invalid( @@ -559,23 +579,18 @@ impl XhciClientHandle { Ok(()) } pub fn port_state(&self) -> result::Result { - let path = format!("/scheme/{}/port{}/state", self.scheme, self.port); - let string = std::fs::read_to_string(path)?; + let string = self.read_to_string("state")?; Ok(string.parse()?) } pub fn open_endpoint_ctl(&self, num: u8) -> result::Result { - let path = format!( - "/scheme/{}/port{}/endpoints/{}/ctl", - self.scheme, self.port, num - ); - Ok(File::open(path)?) + let path = format!("endpoints/{}/ctl", num); + let fd = self.fd.openat(&path, libredox::flag::O_RDWR, 0)?; + Ok(unsafe { File::from_raw_fd(fd.into_raw() as RawFd) }) } pub fn open_endpoint_data(&self, num: u8) -> result::Result { - let path = format!( - "/scheme/{}/port{}/endpoints/{}/data", - self.scheme, self.port, num - ); - Ok(File::open(path)?) + let path = format!("endpoints/{}/data", num); + let fd = self.fd.openat(&path, libredox::flag::O_RDWR, 0)?; + Ok(unsafe { File::from_raw_fd(fd.into_raw() as RawFd) }) } pub fn open_endpoint(&self, num: u8) -> result::Result { Ok(XhciEndpHandle { @@ -607,8 +622,7 @@ impl XhciClientHandle { }; let json = serde_json::to_vec(&req)?; - let path = format!("/scheme/{}/port{}/request", self.scheme, self.port); - let mut file = File::open(path)?; + let file = self.fd.openat("request", libredox::flag::O_RDWR, 0)?; let json_bytes_written = file.write(&json)?; if json_bytes_written != json.len() { @@ -863,4 +877,13 @@ pub enum XhciClientHandleError { #[error("unexpected short packet of size {0}")] UnexpectedShortPacket(usize), + + #[error("utf8 error: {0}")] + Utf8Error(#[from] FromUtf8Error), +} + +impl From for XhciClientHandleError { + fn from(error: libredox::error::Error) -> Self { + Self::IoError(error.into()) + } } diff --git a/drivers/usb/xhcid/src/main.rs b/drivers/usb/xhcid/src/main.rs index 0083f89906..d480c479ac 100644 --- a/drivers/usb/xhcid/src/main.rs +++ b/drivers/usb/xhcid/src/main.rs @@ -37,7 +37,7 @@ use pcid_interface::irq_helpers::{ }; use pcid_interface::{PciFeature, PciFeatureInfo, PciFunctionHandle}; -use redox_scheme::{RequestKind, SignalBehavior, Socket}; +use redox_scheme::{scheme::register_sync_scheme, RequestKind, SignalBehavior, Socket}; use crate::xhci::{InterruptMethod, Xhci}; @@ -143,14 +143,16 @@ fn daemon_with_context_size( log::info!("XHCI {}", pci_config.func.display()); let scheme_name = format!("usb.{}", name); - let socket = Socket::create(scheme_name.clone()).expect("xhcid: failed to create usb scheme"); - - daemon.ready(); + let socket = Socket::create().expect("xhcid: failed to create usb scheme"); let hci = Arc::new( - Xhci::::new(scheme_name, address, interrupt_method, pcid_handle) + Xhci::::new(scheme_name.clone(), address, interrupt_method, pcid_handle) .expect("xhcid: failed to allocate device"), ); + register_sync_scheme(&socket, &scheme_name, &mut &*hci) + .expect("xhcid: failed to regsiter scheme to namespace"); + + daemon.ready(); xhci::start_irq_reactor(&hci, irq_file); xhci::start_device_enumerator(&hci); @@ -182,6 +184,7 @@ fn daemon_with_context_size( } fn main() { + common::init(); pcid_interface::pci_daemon(daemon); } diff --git a/drivers/usb/xhcid/src/xhci/scheme.rs b/drivers/usb/xhcid/src/xhci/scheme.rs index 1b92fa51aa..ada4542aec 100644 --- a/drivers/usb/xhcid/src/xhci/scheme.rs +++ b/drivers/usb/xhcid/src/xhci/scheme.rs @@ -75,6 +75,8 @@ lazy_static! { r"port([\d\.]+)/endpoints/(\d{1,3})/(ctl|data)$" ) .expect("Failed to create the regex for the port/endpoints// scheme"); + static ref REGEX_PORT_ROOT: Regex = + Regex::new(r"^port([\d\.]+)$").expect("Failed to create the regex for the port scheme."); static ref REGEX_TOP_LEVEL: Regex = Regex::new(r"^$").expect("Failed to create the regex for the top-level scheme"); } @@ -141,6 +143,7 @@ pub enum Handle { ConfigureEndpoints(PortId), // port AttachDevice(PortId), // port DetachDevice(PortId), // port + SchemeRoot, } /// The type of handle. @@ -232,6 +235,7 @@ impl Handle { Handle::DetachDevice(port_num) => { format!("port{}/detach", port_num) } + Handle::SchemeRoot => String::from(""), } } @@ -263,6 +267,7 @@ impl Handle { EndpointHandleTy::Ctl => HandleType::Character, EndpointHandleTy::Root(_) => HandleType::Directory, }, + &Handle::SchemeRoot => HandleType::Directory, } } @@ -293,6 +298,7 @@ impl Handle { EndpointHandleTy::Ctl => None, EndpointHandleTy::Root(ref buf) => Some(buf.len()), }, + &Handle::SchemeRoot => None, } } } @@ -404,6 +410,9 @@ impl SchemeParameters { let handle_type = get_string_from_regex(®EX_PORT_SUB_ENDPOINT, scheme, 2)?; Ok(Self::Endpoint(port_num, endpoint_num, handle_type)) + } else if REGEX_PORT_ROOT.is_match(scheme) { + let port_num = get_port_id_from_regex(®EX_PORT_ROOT, scheme, 0)?; + Ok(Self::Port(port_num)) } else if REGEX_TOP_LEVEL.is_match(scheme) { Ok(Self::TopLevel) } else { @@ -2099,7 +2108,31 @@ impl Xhci { } impl SchemeSync for &Xhci { - fn open(&mut self, path_str: &str, flags: usize, ctx: &CallerCtx) -> Result { + fn scheme_root(&mut self) -> Result { + let fd = self.next_handle.fetch_add(1, atomic::Ordering::Relaxed); + self.handles.insert(fd, Handle::SchemeRoot); + Ok(fd) + } + fn openat( + &mut self, + dirfd: usize, + path_str: &str, + mut flags: usize, + fcntl_flags: u32, + ctx: &CallerCtx, + ) -> Result { + let full_path = match *self.handles.get(&dirfd).ok_or(Error::new(EBADF))? { + Handle::SchemeRoot => path_str.trim_start_matches('/').to_string(), + Handle::Port(port_num, _) => { + let clean_path = path_str.trim_start_matches('/'); + if clean_path.is_empty() { + format!("port{}", port_num) + } else { + format!("port{}/{}", port_num, clean_path) + } + } + _ => return Err(Error::new(EACCES)), + }; if ctx.uid != 0 { return Err(Error::new(EACCES)); } @@ -2107,7 +2140,9 @@ impl SchemeSync for &Xhci { //Parse the scheme, determine if it's in the valid format, return an error if not. //This doesn't guarantee that the parameters themselves are valid (i.e. bounded correctly) //only that the scheme itself was parseable. - let scheme_parameters = SchemeParameters::from_scheme(path_str)?; + let scheme_parameters = SchemeParameters::from_scheme(&full_path)?; + + flags |= fcntl_flags as usize; //Once we have our scheme parsed into parameters, we can match on those parameters to //find the correct routine to open a handle @@ -2142,7 +2177,7 @@ impl SchemeSync for &Xhci { let fd = self.next_handle.fetch_add(1, atomic::Ordering::Relaxed); - trace!("OPENED {} to FD={}, handle: {:?}", path_str, fd, handle); + trace!("OPENED {} to FD={}, handle: {:?}", full_path, fd, handle); self.handles.insert(fd, handle); @@ -2228,6 +2263,7 @@ impl SchemeSync for &Xhci { Handle::ConfigureEndpoints(_) => Err(Error::new(EBADF)), Handle::AttachDevice(_) => Err(Error::new(EBADF)), Handle::DetachDevice(_) => Err(Error::new(EBADF)), + Handle::SchemeRoot => Err(Error::new(EBADF)), &mut Handle::Endpoint(port_num, endp_num, ref mut st) => match st { EndpointHandleTy::Ctl => self.on_read_endp_ctl(port_num, endp_num, buf), diff --git a/drivers/vboxd/src/main.rs b/drivers/vboxd/src/main.rs index bcb9bb157d..05e5db0172 100644 --- a/drivers/vboxd/src/main.rs +++ b/drivers/vboxd/src/main.rs @@ -190,6 +190,7 @@ impl VboxGuestInfo { } fn main() { + common::init(); pcid_interface::pci_daemon(daemon); } diff --git a/ipcd/src/chan.rs b/ipcd/src/chan.rs index 59462b221a..f4a94e2444 100644 --- a/ipcd/src/chan.rs +++ b/ipcd/src/chan.rs @@ -19,6 +19,7 @@ pub struct Listener { pub enum Extra { Client(Client), Listener(Listener), + SchemeRoot, } impl Default for Extra { fn default() -> Self { @@ -115,16 +116,8 @@ impl<'sock> ChanScheme<'sock> { Err(err) => Err(err), // Error writing response } } -} -impl<'sock> SchemeSync for ChanScheme<'sock> { - // ___ ____ _____ _ _ - // / _ \| _ \| ____| \ | | - // | | | | |_) | _| | \| | - // | |_| | __/| |___| |\ | - // \___/|_| |_____|_| \_| - - fn open(&mut self, path: &str, flags: usize, _ctx: &CallerCtx) -> Result { + fn open(&mut self, path: &str, flags: usize) -> Result { let new_id = self.next_id; let mut new = Handle::default(); new.flags = flags; @@ -164,7 +157,45 @@ impl<'sock> SchemeSync for ChanScheme<'sock> { flags: NewFdFlags::empty(), }) } - fn dup(&mut self, id: usize, buf: &[u8], ctx: &CallerCtx) -> Result { +} + +impl<'sock> SchemeSync for ChanScheme<'sock> { + fn scheme_root(&mut self) -> Result { + let id = self.next_id; + self.next_id += 1; + + self.handles.insert( + id, + Handle { + flags: 0, + extra: Extra::SchemeRoot, + path: None, + }, + ); + Ok(id) + } + // ___ ____ _____ _ _ + // / _ \| _ \| ____| \ | | + // | | | | |_) | _| | \| | + // | |_| | __/| |___| |\ | + // \___/|_| |_____|_| \_| + fn openat( + &mut self, + dirfd: usize, + path: &str, + flags: usize, + _fcntl_flags: u32, + _ctx: &CallerCtx, + ) -> Result { + let handle = self.handles.get(&dirfd).ok_or(Error::new(EBADF))?; + + if !matches!(handle.extra, Extra::SchemeRoot) { + return Err(Error::new(EACCES)); + } + + self.open(path, flags) + } + fn dup(&mut self, id: usize, buf: &[u8], _ctx: &CallerCtx) -> Result { match buf { b"listen" => { loop { @@ -190,6 +221,7 @@ impl<'sock> SchemeSync for ChanScheme<'sock> { Extra::Listener(_) => { panic!("newly created handle can't possibly be a listener") } + Extra::SchemeRoot => return Err(Error::new(EBADF)), } self.post_fevent(remote_id, EVENT_WRITE.bits())?; @@ -245,8 +277,12 @@ impl<'sock> SchemeSync for ChanScheme<'sock> { return Err(Error::new(EBADF)); } + if matches!(handle.extra, Extra::SchemeRoot) { + return Err(Error::new(EBADF)); + } + let flags = handle.flags; - return self.open(path, flags, ctx); + return self.open(path, flags); } } } @@ -283,6 +319,7 @@ impl<'sock> SchemeSync for ChanScheme<'sock> { Extra::Listener(_) => { panic!("somehow, a client was connected to a listener directly") } + Extra::SchemeRoot => panic!("somehow, a client was connected to a SchemeRoot"), } } else if client.remote == Connection::Closed { Err(Error::new(EPIPE)) @@ -303,6 +340,9 @@ impl<'sock> SchemeSync for ChanScheme<'sock> { // Write path let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + if let Extra::SchemeRoot = handle.extra { + return Ok(len); + } let path = handle.path.as_ref().ok_or(Error::new(EBADF))?; let len = cmp::min(path.len(), buf.len() - PREFIX.len()); buf[PREFIX.len()..][..len].copy_from_slice(&path.as_bytes()[..len]); @@ -358,6 +398,9 @@ impl<'sock> SchemeSync for ChanScheme<'sock> { } } Extra::Listener(_) => panic!("a client can't be connected to a listener!"), + Extra::SchemeRoot => { + panic!("a client can't be connected to a scheme root!") + } } } } @@ -366,6 +409,7 @@ impl<'sock> SchemeSync for ChanScheme<'sock> { self.listeners.remove(&path); } } + Extra::SchemeRoot => {} } } @@ -403,6 +447,7 @@ impl<'sock> SchemeSync for ChanScheme<'sock> { events |= EVENT_READ | EVENT_WRITE; } } + Extra::SchemeRoot => {} } Ok(events) } diff --git a/ipcd/src/main.rs b/ipcd/src/main.rs index 177e82ef07..5e07fe5b57 100644 --- a/ipcd/src/main.rs +++ b/ipcd/src/main.rs @@ -1,5 +1,5 @@ use event::{EventFlags, EventQueue}; -use redox_scheme::{wrappers::ReadinessBased, Socket}; +use redox_scheme::{scheme::register_sync_scheme, wrappers::ReadinessBased, Socket}; use std::sync::Mutex; mod chan; @@ -37,19 +37,19 @@ fn inner(daemon: daemon::Daemon) -> anyhow::Result<()> { } // Prepare chan scheme - let chan_socket = Socket::nonblock("chan") - .map_err(|e| anyhow::anyhow!("failed to create chan scheme: {e}"))?; + let chan_socket = + Socket::nonblock().map_err(|e| anyhow::anyhow!("failed to create chan scheme: {e}"))?; let chan = Mutex::new(ChanScheme::new(&chan_socket)); let mut chan_handler = ReadinessBased::new(&chan_socket, 16); // Prepare shm scheme let shm_socket = - Socket::nonblock("shm").map_err(|e| anyhow::anyhow!("failed to create shm socket: {e}"))?; + Socket::nonblock().map_err(|e| anyhow::anyhow!("failed to create shm socket: {e}"))?; let shm = Mutex::new(ShmScheme::new()); let mut shm_handler = ReadinessBased::new(&shm_socket, 16); // Prepare uds stream scheme - let uds_stream_socket = Socket::nonblock("uds_stream") + let uds_stream_socket = Socket::nonblock() .map_err(|e| anyhow::anyhow!("failed to create uds stream scheme: {e}"))?; let uds_stream = Mutex::new( UdsStreamScheme::new(&uds_stream_socket) @@ -58,7 +58,7 @@ fn inner(daemon: daemon::Daemon) -> anyhow::Result<()> { let mut uds_stream_handler = ReadinessBased::new(&uds_stream_socket, 16); // Prepare uds dgram scheme - let uds_dgram_socket = Socket::nonblock("uds_dgram") + let uds_dgram_socket = Socket::nonblock() .map_err(|e| anyhow::anyhow!("failed to create uds dgram scheme: {e}"))?; let uds_dgram = Mutex::new( UdsDgramScheme::new(&uds_dgram_socket) @@ -66,6 +66,28 @@ fn inner(daemon: daemon::Daemon) -> anyhow::Result<()> { ); let mut uds_dgram_handler = ReadinessBased::new(&uds_dgram_socket, 16); + { + let mut chan_guard = chan.lock().unwrap(); + register_sync_scheme(&chan_socket, "chan", &mut *chan_guard) + .map_err(|e| anyhow::anyhow!("failed to register chan scheme: {e}"))?; + } + { + let mut shm_guard = shm.lock().unwrap(); + register_sync_scheme(&shm_socket, "shm", &mut *shm_guard) + .map_err(|e| anyhow::anyhow!("failed to register shm scheme: {e}"))?; + } + { + let mut uds_stream_guard = uds_stream.lock().unwrap(); + register_sync_scheme(&uds_stream_socket, "uds_stream", &mut *uds_stream_guard) + .map_err(|e| anyhow::anyhow!("failed to register uds stream scheme: {e}"))?; + } + { + let mut uds_dgram_guard = uds_dgram.lock().unwrap(); + // Register schemes to namespace + register_sync_scheme(&uds_dgram_socket, "uds_dgram", &mut *uds_dgram_guard) + .map_err(|e| anyhow::anyhow!("failed to register uds dgram scheme: {e}"))?; + } + daemon.ready(); // Create event listener for both files diff --git a/ipcd/src/shm.rs b/ipcd/src/shm.rs index e0983a2d4f..291e0f7def 100644 --- a/ipcd/src/shm.rs +++ b/ipcd/src/shm.rs @@ -47,19 +47,27 @@ impl ShmScheme { } impl SchemeSync for ShmScheme { + fn scheme_root(&mut self) -> Result { + let id = self.next_id; + self.next_id += 1; + + self.handles.insert(id, Handle::SchemeRoot); + Ok(id) + } //FIXME: Handle O_RDONLY/O_WRONLY/O_RDWR - fn open(&mut self, path: &str, flags: usize, _ctx: &CallerCtx) -> Result { - if path.is_empty() { - let id = self.next_id; - self.next_id += 1; - - self.handles.insert(id, Handle::SchemeRoot); - - return Ok(OpenResult::ThisScheme { - number: id, - flags: NewFdFlags::empty(), - }); + fn openat( + &mut self, + dirfd: usize, + path: &str, + flags: usize, + _fcntl_flags: u32, + _ctx: &CallerCtx, + ) -> Result { + let handle = self.handles.get(&dirfd).ok_or(Error::new(EBADF))?; + if !matches!(handle, Handle::SchemeRoot) { + return Err(Error::new(EACCES)); } + let path = Rc::from(path); let entry = match self.maps.entry(Rc::clone(&path)) { Entry::Occupied(e) => { diff --git a/ipcd/src/uds/dgram.rs b/ipcd/src/uds/dgram.rs index 816218c5c0..1121b618b8 100644 --- a/ipcd/src/uds/dgram.rs +++ b/ipcd/src/uds/dgram.rs @@ -22,6 +22,44 @@ use std::{ }; use syscall::{error::*, flag::*, schemev2::NewFdFlags, Error, FobtainFdFlags, Stat}; +#[derive(Debug, Default)] +pub struct Socket { + primary_id: usize, + path: Option, + state: State, + peer: Option, + messages: VecDeque, + options: HashSet, + fds: VecDeque, + flags: usize, + issued_token: Option, +} + +impl Socket { + fn drop_fds(&mut self, num_fd: usize) -> Result<()> { + for i in 0..num_fd { + if self.fds.pop_front().is_none() { + eprintln!("Socket::drop_fds: Attempted to drop FD #{} of {}, but fd queue is empty. State inconsistency.", i + 1, num_fd); + return Err(Error::new(EINVAL)); + } + } + Ok(()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum State { + Unbound, + Bound, + Closed, +} + +impl Default for State { + fn default() -> Self { + Self::Unbound + } +} + impl DataPacket { pub fn serialize_to_stream( self, @@ -70,46 +108,26 @@ impl DataPacket { } } -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum State { - Unbound, - Bound, - Closed, +enum Handle { + Socket(Rc>), + SchemeRoot, } -impl Default for State { - fn default() -> Self { - Self::Unbound - } -} - -#[derive(Debug, Default)] -pub struct Socket { - primary_id: usize, - path: Option, - state: State, - peer: Option, - messages: VecDeque, - options: HashSet, - fds: VecDeque, - flags: usize, - issued_token: Option, -} - -impl Socket { - fn drop_fds(&mut self, num_fd: usize) -> Result<()> { - for i in 0..num_fd { - if self.fds.pop_front().is_none() { - eprintln!("Socket::drop_fds: Attempted to drop FD #{} of {}, but fd queue is empty. State inconsistency.", i + 1, num_fd); - return Err(Error::new(EINVAL)); - } +impl Handle { + fn as_socket(&self) -> Option<&Rc>> { + if let Self::Socket(socket) = self { + Some(socket) + } else { + None } - Ok(()) + } + fn is_scheme_root(&self) -> bool { + matches!(self, Self::SchemeRoot) } } pub struct UdsDgramScheme<'sock> { - sockets: HashMap>>, + handles: HashMap, next_id: usize, socket_paths: HashMap>>, socket_tokens: HashMap>>, @@ -121,15 +139,18 @@ pub struct UdsDgramScheme<'sock> { impl<'sock> UdsDgramScheme<'sock> { pub fn new(socket: &'sock SchemeSocket) -> Result { Ok(Self { - sockets: HashMap::new(), + handles: HashMap::new(), next_id: 0, socket_paths: HashMap::new(), socket_tokens: HashMap::new(), socket, - proc_creds_capability: syscall::open( - "/scheme/proc/proc-creds-capability", - syscall::O_RDONLY, - )?, + proc_creds_capability: { + libredox::call::open( + "/scheme/proc/proc-creds-capability", + libredox::flag::O_RDONLY, + 0, + )? + }, rng: SmallRng::from_entropy(), }) } @@ -147,7 +168,14 @@ impl<'sock> UdsDgramScheme<'sock> { } fn get_socket(&self, id: usize) -> Result<&Rc>, Error> { - self.sockets.get(&id).ok_or(Error::new(EBADF)) + self.handles + .get(&id) + .and_then(Handle::as_socket) + .ok_or(Error::new(EBADF)) + } + + fn insert_socket(&mut self, id: usize, socket: Rc>) { + self.handles.insert(id, Handle::Socket(socket)); } fn get_connected_peer(&self, id: usize) -> Result<(usize, Rc>), Error> { @@ -177,7 +205,7 @@ impl<'sock> UdsDgramScheme<'sock> { new.flags = flags; new.primary_id = new_id; - self.sockets.insert(new_id, Rc::new(RefCell::new(new))); + self.insert_socket(new_id, Rc::new(RefCell::new(new))); self.next_id += 1; new_id } @@ -473,7 +501,7 @@ impl<'sock> UdsDgramScheme<'sock> { // why not) self.post_fevent(id, (EVENT_READ | EVENT_WRITE).bits())?; - self.sockets.insert(new_id, Rc::new(RefCell::new(new))); + self.insert_socket(new_id, Rc::new(RefCell::new(new))); self.next_id += 1; @@ -496,7 +524,7 @@ impl<'sock> UdsDgramScheme<'sock> { let new_id = self.next_id; - self.sockets.insert(new_id, socket_rc.clone()); + self.insert_socket(new_id, socket_rc.clone()); self.next_id += 1; Ok(OpenResult::ThisScheme { @@ -625,9 +653,45 @@ impl<'sock> UdsDgramScheme<'sock> { } impl<'sock> SchemeSync for UdsDgramScheme<'sock> { - fn open(&mut self, path: &str, flags: usize, _ctx: &CallerCtx) -> Result { + fn scheme_root(&mut self) -> Result { + let new_id = self.next_id; + self.handles.insert(new_id, Handle::SchemeRoot); + self.next_id += 1; + Ok(new_id) + } + + fn openat( + &mut self, + fd: usize, + path: &str, + mut flags: usize, + fcntl_flags: u32, + _ctx: &CallerCtx, + ) -> Result { + { + let Some(handle) = self.handles.get(&fd) else { + return Err(Error::new(EBADF)); + }; + if !handle.is_scheme_root() { + eprintln!( + "openat(fd: {}, path: '{}'): fd is not an open capability.", + fd, path + ); + return Err(Error::new(EACCES)); + } + } + flags |= fcntl_flags as usize; + let new_id = if path.is_empty() { - self.handle_unnamed_socket(flags) + if flags & O_CREAT == O_CREAT { + self.handle_unnamed_socket(flags) + } else { + eprintln!( + "open(path: '{}'): Attempting to open an unnamed socket without O_CREAT.", + path + ); + return Err(Error::new(EINVAL)); + } } else { eprintln!( "open(path: '{}'): Attempting to open a named socket, which is not supported.", @@ -674,12 +738,15 @@ impl<'sock> SchemeSync for UdsDgramScheme<'sock> { } fn fpath(&mut self, id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result { - let socket_rc = self.get_socket(id)?; - let socket = socket_rc.borrow(); - - let empty = String::new(); - let path = socket.path.as_ref().unwrap_or(&empty); - Ok(Self::fpath_inner(path, buf)?) + match self.handles.get(&id).ok_or(Error::new(EBADF))? { + Handle::SchemeRoot => Ok(Self::fpath_inner(&String::new(), buf)?), + Handle::Socket(socket_rc) => { + let socket = socket_rc.borrow(); + let empty = String::new(); + let path = socket.path.as_ref().unwrap_or(&empty); + Ok(Self::fpath_inner(path, buf)?) + } + } } fn fsync(&mut self, id: usize, _ctx: &CallerCtx) -> Result<()> { @@ -698,7 +765,7 @@ impl<'sock> SchemeSync for UdsDgramScheme<'sock> { } fn on_close(&mut self, id: usize) { - let Some(socket_rc) = self.sockets.remove(&id) else { + let Some(Handle::Socket(socket_rc)) = self.handles.remove(&id) else { return; }; let mut socket = socket_rc.borrow_mut(); diff --git a/ipcd/src/uds/mod.rs b/ipcd/src/uds/mod.rs index d8ac8276c8..af8b46be1b 100644 --- a/ipcd/src/uds/mod.rs +++ b/ipcd/src/uds/mod.rs @@ -1,4 +1,5 @@ use redox_rt::protocol::ProcMeta; +use std::fmt::Debug; use std::{cmp, convert::TryInto, mem}; use syscall::{error::*, Error}; @@ -143,79 +144,41 @@ impl DataPacket { } } +trait NumFromBytes: Sized + Debug { + fn from_le_bytes_slice(buffer: &[u8]) -> Result; +} + +macro_rules! num_from_bytes_impl { + ($($t:ty),*) => { + $( + impl NumFromBytes for $t { + fn from_le_bytes_slice(buffer: &[u8]) -> Result { + let size = mem::size_of::(); + let buffer_slice = buffer.get(..size).and_then(|s| s.try_into().ok()); + + if let Some(slice) = buffer_slice { + Ok(Self::from_le_bytes(slice)) + } else { + eprintln!( + "read_num: buffer is too short to read num of size {} (buffer len: {})", + size, buffer.len() + ); + Err(Error::new(EINVAL)) + } + } + } + )* + }; +} + +num_from_bytes_impl!(i32, u32, u64, usize); + fn read_num(buffer: &[u8]) -> Result where T: NumFromBytes, { T::from_le_bytes_slice(buffer) } -trait NumFromBytes: Sized { - fn from_le_bytes_slice(buffer: &[u8]) -> Result; -} -impl NumFromBytes for i32 { - fn from_le_bytes_slice(buffer: &[u8]) -> Result { - Ok(i32::from_le_bytes( - buffer - .get(..mem::size_of::()) - .and_then(|slice| slice.try_into().ok()) - .ok_or_else(|| { - eprintln!( - "read_num: buffer is too short to read num len: {}", - buffer.len() - ); - Error::new(EINVAL) - })?, - )) - } -} -impl NumFromBytes for u32 { - fn from_le_bytes_slice(buffer: &[u8]) -> Result { - Ok(u32::from_le_bytes( - buffer - .get(..mem::size_of::()) - .and_then(|slice| slice.try_into().ok()) - .ok_or_else(|| { - eprintln!( - "read_num: buffer is too short to read num len: {}", - buffer.len() - ); - Error::new(EINVAL) - })?, - )) - } -} -impl NumFromBytes for u64 { - fn from_le_bytes_slice(buffer: &[u8]) -> Result { - Ok(u64::from_le_bytes( - buffer - .get(..mem::size_of::()) - .and_then(|slice| slice.try_into().ok()) - .ok_or_else(|| { - eprintln!( - "read_num: buffer is too short to read num len: {}", - buffer.len() - ); - Error::new(EINVAL) - })?, - )) - } -} -impl NumFromBytes for usize { - fn from_le_bytes_slice(buffer: &[u8]) -> Result { - Ok(usize::from_le_bytes( - buffer - .get(..mem::size_of::()) - .and_then(|slice| slice.try_into().ok()) - .ok_or_else(|| { - eprintln!( - "read_num: buffer is too short to read num len: {}", - buffer.len() - ); - Error::new(EINVAL) - })?, - )) - } -} fn get_uid_gid_from_pid(cap_fd: usize, target_pid: usize) -> Result<(u32, u32, u32)> { let mut buffer = [0u8; mem::size_of::()]; diff --git a/ipcd/src/uds/stream.rs b/ipcd/src/uds/stream.rs index 9d75642aac..19707e6e6a 100644 --- a/ipcd/src/uds/stream.rs +++ b/ipcd/src/uds/stream.rs @@ -364,8 +364,26 @@ impl Socket { } } +enum Handle { + Socket(Rc>), + SchemeRoot, +} + +impl Handle { + fn as_socket(&self) -> Option<&Rc>> { + if let Self::Socket(socket) = self { + Some(socket) + } else { + None + } + } + fn is_scheme_root(&self) -> bool { + matches!(self, Self::SchemeRoot) + } +} + pub struct UdsStreamScheme<'sock> { - sockets: HashMap>>, + handles: HashMap, next_id: usize, socket_paths: HashMap>>, socket_tokens: HashMap>>, @@ -377,15 +395,18 @@ pub struct UdsStreamScheme<'sock> { impl<'sock> UdsStreamScheme<'sock> { pub fn new(socket: &'sock SchemeSocket) -> Result { Ok(Self { - sockets: HashMap::new(), + handles: HashMap::new(), next_id: 0, socket_paths: HashMap::new(), socket_tokens: HashMap::new(), socket, - proc_creds_capability: syscall::open( - "/scheme/proc/proc-creds-capability", - syscall::O_RDONLY, - )?, + proc_creds_capability: { + libredox::call::open( + "/scheme/proc/proc-creds-capability", + libredox::flag::O_RDONLY, + 0, + )? + }, rng: SmallRng::from_entropy(), }) } @@ -409,7 +430,14 @@ impl<'sock> UdsStreamScheme<'sock> { } fn get_socket(&self, id: usize) -> Result<&Rc>, Error> { - self.sockets.get(&id).ok_or(Error::new(EBADF)) + self.handles + .get(&id) + .and_then(Handle::as_socket) + .ok_or(Error::new(EBADF)) + } + + fn insert_socket(&mut self, id: usize, socket: Rc>) { + self.handles.insert(id, Handle::Socket(socket)); } fn get_connected_peer(&self, id: usize) -> Result<(usize, Rc>), Error> { @@ -443,7 +471,7 @@ impl<'sock> UdsStreamScheme<'sock> { None, ctx, ); - self.sockets.insert(new_id, Rc::new(RefCell::new(new))); + self.insert_socket(new_id, Rc::new(RefCell::new(new))); self.next_id += 1; new_id } @@ -850,7 +878,7 @@ impl<'sock> UdsStreamScheme<'sock> { }; self.next_id += 1; - self.sockets.insert(new_id, Rc::new(RefCell::new(new))); + self.insert_socket(new_id, Rc::new(RefCell::new(new))); self.post_fevent(client_id, EVENT_READ | EVENT_WRITE)?; Ok(Some(OpenResult::ThisScheme { number: new_id, @@ -961,7 +989,7 @@ impl<'sock> UdsStreamScheme<'sock> { // why not) self.post_fevent(id, EVENT_READ | EVENT_WRITE)?; - self.sockets.insert(new_id, Rc::new(RefCell::new(new))); + self.insert_socket(new_id, Rc::new(RefCell::new(new))); self.next_id += 1; @@ -1195,9 +1223,45 @@ impl<'sock> UdsStreamScheme<'sock> { } impl<'sock> SchemeSync for UdsStreamScheme<'sock> { - fn open(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result { + fn scheme_root(&mut self) -> Result { + let new_id = self.next_id; + self.handles.insert(new_id, Handle::SchemeRoot); + self.next_id += 1; + Ok(new_id) + } + fn openat( + &mut self, + fd: usize, + path: &str, + mut flags: usize, + fcntl_flags: u32, + ctx: &CallerCtx, + ) -> Result { + { + let Some(handle) = self.handles.get(&fd) else { + return Err(Error::new(EBADF)); + }; + if !handle.is_scheme_root() { + eprintln!( + "openat(fd: {}, path: '{}'): fd is not an open capability.", + fd, path + ); + return Err(Error::new(EACCES)); + } + } + + flags |= fcntl_flags as usize; + let new_id = if path.is_empty() { - self.handle_unnamed_socket(flags, ctx) + if flags & O_CREAT == O_CREAT { + self.handle_unnamed_socket(flags, ctx) + } else { + eprintln!( + "open(path: '{}'): Attempting to open an unnamed socket without O_CREAT.", + path + ); + return Err(Error::new(EINVAL)); + } } else { eprintln!( "open(path: '{}'): Attempting to open a named socket, which is not supported.", @@ -1243,11 +1307,15 @@ impl<'sock> SchemeSync for UdsStreamScheme<'sock> { } fn fpath(&mut self, id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result { - let socket_rc = self.get_socket(id)?; - let socket = socket_rc.borrow(); - let empty = String::new(); - let path = socket.path.as_ref().unwrap_or(&empty); - Ok(Self::fpath_inner(path, buf)?) + match self.handles.get(&id).ok_or(Error::new(EBADF))? { + Handle::SchemeRoot => Ok(Self::fpath_inner(&String::new(), buf)?), + Handle::Socket(socket_rc) => { + let socket = socket_rc.borrow(); + let empty = String::new(); + let path = socket.path.as_ref().unwrap_or(&empty); + Ok(Self::fpath_inner(path, buf)?) + } + } } fn fsync(&mut self, id: usize, _ctx: &CallerCtx) -> Result<()> { @@ -1288,7 +1356,7 @@ impl<'sock> SchemeSync for UdsStreamScheme<'sock> { } fn on_close(&mut self, id: usize) { - let Some(socket_rc) = self.sockets.remove(&id) else { + let Some(Handle::Socket(socket_rc)) = self.handles.remove(&id) else { return; }; diff --git a/logd/src/main.rs b/logd/src/main.rs index 6bdd6f7ea3..3f35617e3c 100644 --- a/logd/src/main.rs +++ b/logd/src/main.rs @@ -1,4 +1,7 @@ -use redox_scheme::{RequestKind, SignalBehavior, Socket}; +use redox_scheme::{ + scheme::{register_sync_scheme, SchemeSync}, + RequestKind, Response, SignalBehavior, Socket, +}; use std::process; use crate::scheme::LogScheme; @@ -6,12 +9,17 @@ use crate::scheme::LogScheme; mod scheme; fn daemon(daemon: daemon::Daemon) -> ! { - let socket = Socket::create("log").expect("logd: failed to create log scheme"); + let socket = Socket::create().expect("logd: failed to create log scheme"); + + let mut scheme = LogScheme::new(&socket); + + register_sync_scheme(&socket, "log", &mut scheme) + .expect("logd: failed to register scheme to namespace"); + + libredox::call::setrens(0, 0).expect("logd: failed to enter null namespace"); daemon.ready(); - let mut scheme = LogScheme::new(); - while let Some(request) = socket .next_request(SignalBehavior::Restart) .expect("logd: failed to read events from log scheme") @@ -22,6 +30,14 @@ fn daemon(daemon: daemon::Daemon) -> ! { scheme.on_close(id); continue; } + RequestKind::SendFd(sendfd_request) => { + let result = scheme.on_sendfd(&sendfd_request); + let resp = Response::new(result, sendfd_request); + socket + .write_response(resp, SignalBehavior::Restart) + .expect("logd: failed to write responses to log scheme"); + continue; + } _ => continue, }; diff --git a/logd/src/scheme.rs b/logd/src/scheme.rs index 0d3ada8c7a..28ef562ddc 100644 --- a/logd/src/scheme.rs +++ b/logd/src/scheme.rs @@ -2,11 +2,12 @@ use std::collections::{BTreeMap, VecDeque}; use std::fs::{File, OpenOptions}; use std::io::{Read, Write}; use std::mem; +use std::os::fd::{FromRawFd, RawFd}; use std::path::PathBuf; use std::sync::mpsc::{self, Sender}; use redox_scheme::scheme::SchemeSync; -use redox_scheme::{CallerCtx, OpenResult}; +use redox_scheme::{CallerCtx, OpenResult, SendFdRequest, Socket}; use syscall::error::*; use syscall::schemev2::NewFdFlags; @@ -16,10 +17,12 @@ pub enum LogHandle { bufs: BTreeMap>, }, AddSink, + SchemeRoot, } -pub struct LogScheme { +pub struct LogScheme<'sock> { next_id: usize, + socket: &'sock Socket, output_tx: Sender, handles: BTreeMap, } @@ -28,16 +31,18 @@ enum OutputCmd { Log(Vec), /// Log a message from the kernel. This skips writing it back to the kernel debug output. LogKernel(Vec), - AddSink(PathBuf), + AddSink(usize), } -impl LogScheme { - pub fn new() -> Self { +impl<'sock> LogScheme<'sock> { + pub fn new(socket: &'sock Socket) -> Self { let mut kernel_debug = OpenOptions::new() .write(true) .open("/scheme/debug") .unwrap(); + let mut kernel_sys_log = std::fs::File::open("/scheme/sys/log").unwrap(); + let (output_tx, output_rx) = mpsc::channel::(); std::thread::spawn(move || { @@ -69,20 +74,14 @@ impl LogScheme { logs.pop_front(); } } - OutputCmd::AddSink(sink_path) => { - match OpenOptions::new().write(true).open(&sink_path) { - Ok(mut file) => { - for line in &logs { - let _ = file.write(line); - let _ = file.flush(); - } - - files.push(file) - } - Err(err) => { - eprintln!("logd: failed to open {:?}: {:?}", sink_path, err) - } + OutputCmd::AddSink(log_fd) => { + let mut file = unsafe { File::from_raw_fd(log_fd as RawFd) }; + for line in &logs { + let _ = file.write(line); + let _ = file.flush(); } + + files.push(file) } } } @@ -90,12 +89,11 @@ impl LogScheme { let output_tx2 = output_tx.clone(); std::thread::spawn(move || { - let mut debug_file = std::fs::File::open("/scheme/sys/log").unwrap(); let mut handle_buf = vec![]; let mut buf = [0; 4096]; buf[.."kernel: ".len()].copy_from_slice(b"kernel: "); loop { - let n = debug_file.read(&mut buf["kernel: ".len()..]).unwrap(); + let n = kernel_sys_log.read(&mut buf["kernel: ".len()..]).unwrap(); if n == 0 { // FIXME currently possible as /scheme/log/kernel presents a snapshot of the log queue break; @@ -106,6 +104,7 @@ impl LogScheme { LogScheme { next_id: 0, + socket, output_tx, handles: BTreeMap::new(), } @@ -144,8 +143,28 @@ impl LogScheme { } } -impl SchemeSync for LogScheme { - fn open(&mut self, path: &str, _flags: usize, _ctx: &CallerCtx) -> Result { +impl<'sock> SchemeSync for LogScheme<'sock> { + fn scheme_root(&mut self) -> Result { + let id = self.next_id; + self.next_id += 1; + self.handles.insert(id, LogHandle::SchemeRoot); + Ok(id) + } + fn openat( + &mut self, + dirfd: usize, + path: &str, + _flags: usize, + _fcntl_flags: u32, + _ctx: &CallerCtx, + ) -> Result { + if !matches!( + self.handles.get(&dirfd).ok_or(Error::new(EBADF))?, + LogHandle::SchemeRoot + ) { + return Err(Error::new(EACCES)); + } + let id = self.next_id; self.next_id += 1; @@ -192,17 +211,7 @@ impl SchemeSync for LogScheme { ) -> Result { let (context, bufs) = match self.handles.get_mut(&id).ok_or(Error::new(EBADF))? { LogHandle::Log { context, bufs } => (context, bufs), - LogHandle::AddSink => { - // FIXME maybe check if root - - let sink_path = PathBuf::from( - String::from_utf8(buf.to_owned()).map_err(|_| Error::new(EINVAL))?, - ); - - self.output_tx.send(OutputCmd::AddSink(sink_path)).unwrap(); - - return Ok(buf.len()); - } + LogHandle::SchemeRoot | LogHandle::AddSink => return Err(Error::new(EBADF)), }; let handle_buf = bufs.entry(ctx.pid).or_insert_with(|| Vec::new()); @@ -212,6 +221,29 @@ impl SchemeSync for LogScheme { Ok(buf.len()) } + fn on_sendfd(&mut self, sendfd_request: &SendFdRequest) -> Result { + let id = sendfd_request.id(); + + if !matches!( + self.handles.get(&id).ok_or(Error::new(EBADF))?, + LogHandle::AddSink + ) { + return Err(Error::new(EBADF)); + } + + let mut new_fd = usize::MAX; + if let Err(e) = sendfd_request.obtain_fd( + &self.socket, + syscall::FobtainFdFlags::CLOEXEC, + std::slice::from_mut(&mut new_fd), + ) { + return Err(e); + } + self.output_tx.send(OutputCmd::AddSink(new_fd)).unwrap(); + + Ok(1) + } + fn fcntl(&mut self, id: usize, _cmd: usize, _arg: usize, _ctx: &CallerCtx) -> Result { let _handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; @@ -232,6 +264,7 @@ impl SchemeSync for LogScheme { let path_bytes = match handle { LogHandle::Log { context, .. } => context.as_bytes(), LogHandle::AddSink => b"add_sink", + LogHandle::SchemeRoot => return Err(Error::new(EBADF)), }; let mut j = 0; while i < buf.len() && j < path_bytes.len() { @@ -250,10 +283,8 @@ impl SchemeSync for LogScheme { Ok(()) } -} -impl LogScheme { - pub fn on_close(&mut self, id: usize) { + fn on_close(&mut self, id: usize) { self.handles.remove(&id); } } diff --git a/netstack/src/main.rs b/netstack/src/main.rs index bf461853f2..b18f32266c 100644 --- a/netstack/src/main.rs +++ b/netstack/src/main.rs @@ -63,24 +63,24 @@ fn run(daemon: daemon::Daemon) -> Result<()> { .context("failed to get mac address from network adapter")?; trace!("opening ip scheme socket"); - let ip_fd = Socket::nonblock("ip") - .map_err(|e| anyhow!("failed to open create ip scheme socket: {}", e))?; + let ip_fd = Socket::nonblock() + .map_err(|e| anyhow!("failed to open create ip scheme socket: {:?}", e))?; trace!("opening udp scheme socket"); let udp_fd = - Socket::nonblock("udp").map_err(|e| anyhow!("failed to open udp scheme socket: {}", e))?; + Socket::nonblock().map_err(|e| anyhow!("failed to open udp scheme socket: {:?}", e))?; trace!("opening tcp scheme socket"); let tcp_fd = - Socket::nonblock("tcp").map_err(|e| anyhow!("failed to open tcp scheme socket: {}", e))?; + Socket::nonblock().map_err(|e| anyhow!("failed to open tcp scheme socket: {:?}", e))?; trace!("opening icmp scheme socket"); - let icmp_fd = Socket::nonblock("icmp") - .map_err(|e| anyhow!("failed to open icmp scheme socket: {}", e))?; + let icmp_fd = + Socket::nonblock().map_err(|e| anyhow!("failed to open icmp scheme socket: {:?}", e))?; trace!("opening netcfg scheme socket"); - let netcfg_fd = Socket::nonblock("netcfg") - .map_err(|e| anyhow!("failed to open netcfg scheme socket {}", e))?; + let netcfg_fd = + Socket::nonblock().map_err(|e| anyhow!("failed to open netcfg scheme socket {:?}", e))?; let time_path = format!("/scheme/time/{}", syscall::CLOCK_MONOTONIC); let time_fd = Fd::open(&time_path, O_RDWR, 0).context("failed to open /scheme/time")?; @@ -97,17 +97,18 @@ fn run(daemon: daemon::Daemon) -> Result<()> { } } - let event_queue = EventQueue::::new().context("failed to create event queue")?; + let event_queue = EventQueue::::new() + .map_err(|e| anyhow!("failed to create event queue: {:?}", e))?; daemon.ready(); event_queue .subscribe(network_fd.raw(), EventSource::Network, EventFlags::READ) - .context("failed to listen to network events")?; + .map_err(|e| anyhow!("failed to listen to network events: {:?}", e))?; event_queue .subscribe(time_fd.raw(), EventSource::Time, EventFlags::READ) - .context("failed to listen to timer events")?; + .map_err(|e| anyhow!("failed to listen to timer events: {:?}", e))?; event_queue .subscribe(ip_fd.inner().raw(), EventSource::IpScheme, EventFlags::READ) @@ -154,7 +155,8 @@ fn run(daemon: daemon::Daemon) -> Result<()> { icmp_fd, time_fd, netcfg_fd, - ); + ) + .context("smolnetd: failed to initialize smolnetd")?; libredox::call::setrens(0, 0).context("smolnetd: failed to enter null namespace")?; @@ -167,7 +169,7 @@ fn run(daemon: daemon::Daemon) -> Result<()> { .into_iter() .chain(event_queue.map(|r| r.map(|e| e.user_data))) { - match event_res? { + match event_res.map_err(|e| anyhow!("event result is error: {:?}", e))? { EventSource::Network => smolnetd.on_network_scheme_event(), EventSource::Time => smolnetd.on_time_event(), EventSource::IpScheme => smolnetd.on_ip_scheme_event(), diff --git a/netstack/src/scheme/mod.rs b/netstack/src/scheme/mod.rs index d3c00faf62..b59ac44f24 100644 --- a/netstack/src/scheme/mod.rs +++ b/netstack/src/scheme/mod.rs @@ -4,7 +4,7 @@ use crate::link::{loopback::LoopbackDevice, DeviceList}; use crate::router::route_table::{RouteTable, Rule}; use crate::router::Router; use crate::scheme::smoltcp::iface::SocketSet as SmoltcpSocketSet; -use crate::scheme::socket::{SchemeSocket, SocketScheme}; +use crate::scheme::socket::{Handle, SchemeSocket, SocketScheme}; use libredox::flag; use libredox::Fd; use redox_scheme::{ @@ -87,7 +87,7 @@ impl Smolnetd { icmp_file: Socket, time_file: Fd, netcfg_file: Socket, - ) -> Smolnetd { + ) -> Result { let protocol_addrs = vec![ //This is a placeholder IP for DHCP IpCidr::new(IpAddress::v4(0, 0, 0, 0), 8), @@ -131,43 +131,47 @@ impl Smolnetd { devices.borrow_mut().push(loopback); devices.borrow_mut().push(eth0); - Smolnetd { + Ok(Smolnetd { iface: Rc::clone(&iface), router_device: network_device, socket_set: Rc::clone(&socket_set), timer: ::std::time::Instant::now(), time_file: unsafe { File::from_raw_fd(time_file.into_raw() as RawFd) }, ip_scheme: IpScheme::new( + "ip", Rc::clone(&iface), Rc::clone(&route_table), Rc::clone(&socket_set), ip_file, - ), + )?, udp_scheme: UdpScheme::new( + "udp", Rc::clone(&iface), Rc::clone(&route_table), Rc::clone(&socket_set), udp_file, - ), + )?, tcp_scheme: TcpScheme::new( + "tcp", Rc::clone(&iface), Rc::clone(&route_table), Rc::clone(&socket_set), tcp_file, - ), + )?, icmp_scheme: IcmpScheme::new( + "icmp", Rc::clone(&iface), Rc::clone(&route_table), Rc::clone(&socket_set), icmp_file, - ), + )?, netcfg_scheme: NetCfgScheme::new( Rc::clone(&iface), netcfg_file, Rc::clone(&route_table), Rc::clone(&devices), - ), - } + )?, + }) } pub fn on_network_scheme_event(&mut self) -> Result<()> { @@ -319,15 +323,19 @@ where SocketT: SchemeSocket + AnySocket<'static>, { pub fn new( + name: &str, iface: Interface, route_table: Rc>, socket_set: Rc>, scheme_file: Socket, - ) -> Self { - Self { - scheme: SocketScheme::::new(iface, route_table, socket_set, scheme_file), + ) -> Result { + Ok(Self { + scheme: SocketScheme::::new(name, iface, route_table, socket_set, scheme_file) + .map_err(|e| { + Error::from_syscall_error(e, &format!("failed to initialize {} scheme", name)) + })?, wait_queue: Vec::new(), - } + }) } pub fn on_scheme_event(&mut self) -> Result> { let result = loop { @@ -442,7 +450,10 @@ where // Notify non-blocking sockets let scheme = &mut self.scheme; - for (&fd, ref mut file) in &mut scheme.files { + for (&fd, handle) in &mut scheme.handles { + let Handle::File(file) = handle else { + continue; + }; let events = { let mut socket_set = scheme.socket_set.borrow_mut(); file.events(&mut socket_set) diff --git a/netstack/src/scheme/netcfg/mod.rs b/netstack/src/scheme/netcfg/mod.rs index 9fe4a71161..bef9318c21 100644 --- a/netstack/src/scheme/netcfg/mod.rs +++ b/netstack/src/scheme/netcfg/mod.rs @@ -3,7 +3,8 @@ mod nodes; mod notifier; use redox_scheme::{ - scheme::SchemeSync, CallerCtx, OpenResult, RequestKind, Response, SignalBehavior, Socket, + scheme::register_scheme_inner, scheme::SchemeSync, CallerCtx, OpenResult, RequestKind, + Response, SignalBehavior, Socket, }; use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address}; use std::cell::RefCell; @@ -324,10 +325,15 @@ impl NetCfgFile { } } +enum Handle { + SchemeRoot, + File(NetCfgFile), +} + pub struct NetCfgScheme { scheme_file: Socket, next_fd: usize, - files: BTreeMap, + handles: BTreeMap, root_node: CfgNodeRef, notifier: NotifierRef, } @@ -338,15 +344,15 @@ impl NetCfgScheme { scheme_file: Socket, route_table: Rc>, devices: Rc>, - ) -> NetCfgScheme { + ) -> Result { let notifier = Notifier::new_ref(); let dns_config = Rc::new(RefCell::new(DNSConfig { name_server: Ipv4Address::new(8, 8, 8, 8), })); - NetCfgScheme { + let mut scheme = NetCfgScheme { scheme_file, next_fd: 1, - files: BTreeMap::new(), + handles: BTreeMap::new(), root_node: mk_root_node( iface, Rc::clone(¬ifier), @@ -355,7 +361,14 @@ impl NetCfgScheme { devices, ), notifier, - } + }; + let cap_id = scheme + .scheme_root() + .map_err(|e| Error::from_syscall_error(e, "failed to get scheme root id"))?; + register_scheme_inner(&scheme.scheme_file, "netcfg", cap_id).map_err(|e| { + Error::from_syscall_error(e, "failed to register netcfg scheme to namespace") + })?; + Ok(scheme) } pub fn on_scheme_event(&mut self) -> Result> { @@ -418,7 +431,32 @@ impl NetCfgScheme { } impl SchemeSync for NetCfgScheme { - fn open(&mut self, path: &str, _flags: usize, ctx: &CallerCtx) -> SyscallResult { + fn scheme_root(&mut self) -> SyscallResult { + let id = self.next_fd; + self.next_fd += 1; + self.handles.insert(id, Handle::SchemeRoot); + Ok(id) + } + + fn openat( + &mut self, + dirfd: usize, + path: &str, + _flags: usize, + _fcntl_flags: u32, + ctx: &CallerCtx, + ) -> SyscallResult { + { + let handle = self + .handles + .get(&dirfd) + .ok_or_else(|| SyscallError::new(syscall::EBADF))?; + + if !matches!(handle, Handle::SchemeRoot) { + return Err(SyscallError::new(syscall::EACCES)); + } + } + let mut current_node = Rc::clone(&self.root_node); for part in path.split('/') { if part.is_empty() { @@ -435,9 +473,9 @@ impl SchemeSync for NetCfgScheme { let fd = self.next_fd; trace!("open {} {}", fd, path); self.next_fd += 1; - self.files.insert( + self.handles.insert( fd, - NetCfgFile { + Handle::File(NetCfgFile { path: path.to_owned(), is_dir: current_node.is_dir(), is_writable: current_node.is_writable(), @@ -452,7 +490,7 @@ impl SchemeSync for NetCfgScheme { read_buf, write_buf: vec![], done: false, - }, + }), ); Ok(OpenResult::ThisScheme { number: fd, @@ -462,10 +500,17 @@ impl SchemeSync for NetCfgScheme { fn on_close(&mut self, fd: usize) { trace!("close {}", fd); - if let Some(mut file) = self.files.remove(&fd) { - self.notifier.borrow_mut().unsubscribe(&file.path, fd); - if !file.done { - let _ = file.commit().map(|_| 0); + if let Some(handle) = self.handles.remove(&fd) { + match handle { + Handle::SchemeRoot => { + // SchemeRoot closed, nothing specific to clean up + } + Handle::File(mut file) => { + self.notifier.borrow_mut().unsubscribe(&file.path, fd); + if !file.done { + let _ = file.commit().map(|_| 0); + } + } } } } @@ -478,11 +523,16 @@ impl SchemeSync for NetCfgScheme { _fcntl_flags: u32, _ctx: &CallerCtx, ) -> SyscallResult { - let file = self - .files + let handle = self + .handles .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; + let file = match handle { + Handle::File(file) => file, + Handle::SchemeRoot => return Err(SyscallError::new(syscall::EBADF)), + }; + if file.done { return Err(SyscallError::new(syscall::EBADF)); } @@ -514,11 +564,16 @@ impl SchemeSync for NetCfgScheme { _fcntl_flags: u32, _ctx: &CallerCtx, ) -> SyscallResult { - let file = self - .files + let handle = self + .handles .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; + let file = match handle { + Handle::File(file) => file, + Handle::SchemeRoot => return Err(SyscallError::new(syscall::EBADF)), + }; + let mut i = 0; while i < buf.len() && file.pos < file.read_buf.len() { buf[i] = file.read_buf[file.pos]; @@ -529,21 +584,26 @@ impl SchemeSync for NetCfgScheme { } fn fstat(&mut self, fd: usize, stat: &mut Stat, _ctx: &CallerCtx) -> SyscallResult<()> { - let file = self - .files + let handle = self + .handles .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; - stat.st_mode = if file.is_dir { MODE_DIR } else { MODE_FILE }; - if file.is_writable { - stat.st_mode |= 0o222; + match handle { + Handle::SchemeRoot => return Err(SyscallError::new(syscall::EBADF)), + Handle::File(file) => { + stat.st_mode = if file.is_dir { MODE_DIR } else { MODE_FILE }; + if file.is_writable { + stat.st_mode |= 0o222; + } + if file.is_readable { + stat.st_mode |= 0o444; + } + stat.st_uid = 0; + stat.st_gid = 0; + stat.st_size = file.read_buf.len() as u64; + } } - if file.is_readable { - stat.st_mode |= 0o444; - } - stat.st_uid = 0; - stat.st_gid = 0; - stat.st_size = file.read_buf.len() as u64; Ok(()) } @@ -554,24 +614,35 @@ impl SchemeSync for NetCfgScheme { events: SyscallEventFlags, _ctx: &CallerCtx, ) -> SyscallResult { - let file = self - .files + let handle = self + .handles .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; - if events.contains(syscall::EVENT_READ) { - self.notifier.borrow_mut().subscribe(&file.path, fd); - } else { - self.notifier.borrow_mut().unsubscribe(&file.path, fd); + + match handle { + Handle::SchemeRoot => return Err(SyscallError::new(syscall::EBADF)), + Handle::File(file) => { + if events.contains(syscall::EVENT_READ) { + self.notifier.borrow_mut().subscribe(&file.path, fd); + } else { + self.notifier.borrow_mut().unsubscribe(&file.path, fd); + } + } } Ok(SyscallEventFlags::empty()) } fn fsync(&mut self, fd: usize, _ctx: &CallerCtx) -> SyscallResult<()> { - let file = self - .files + let handle = self + .handles .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; + let file = match handle { + Handle::File(file) => file, + Handle::SchemeRoot => return Err(SyscallError::new(syscall::EBADF)), + }; + if !file.done { let res = file.commit(); file.done = true; diff --git a/netstack/src/scheme/socket.rs b/netstack/src/scheme/socket.rs index ab6690c8cd..3703932869 100644 --- a/netstack/src/scheme/socket.rs +++ b/netstack/src/scheme/socket.rs @@ -12,7 +12,7 @@ use std::str; use libredox::flag::CLOCK_MONOTONIC; use redox_rt::protocol::SocketCall; use redox_scheme::{ - scheme::{Op, SchemeSync}, + scheme::{register_scheme_inner, Op, SchemeSync}, CallerCtx, OpenResult, Socket, }; use syscall::data::TimeSpec; @@ -239,13 +239,21 @@ where fn handle_shutdown(&mut self, file: &mut SchemeFile, how: usize) -> SyscallResult; } +pub enum Handle +where + SocketT: SchemeSocket, +{ + SchemeRoot, + Null(NullFile), + File(SchemeFile), +} + pub struct SocketScheme where SocketT: SchemeSocket + AnySocket<'static>, { next_fd: usize, - nulls: BTreeMap, - pub files: BTreeMap>, + pub handles: BTreeMap>, ref_counts: BTreeMap, context: Context, pub socket_set: Rc>, @@ -259,33 +267,36 @@ where SocketT: SchemeSocket + AnySocket<'static>, { pub fn new( + name: &str, iface: Interface, route_table: Rc>, socket_set: Rc>, scheme_file: Socket, - ) -> SocketScheme { - SocketScheme { + ) -> SyscallResult> { + let mut scheme = SocketScheme { next_fd: 1, - nulls: BTreeMap::new(), - files: BTreeMap::new(), + handles: BTreeMap::new(), ref_counts: BTreeMap::new(), socket_set, scheme_data: SocketT::new_scheme_data(), scheme_file, _phantom_socket: PhantomData, context: Context { iface, route_table }, - } + }; + let cap_id = scheme.scheme_root()?; + register_scheme_inner(&scheme.scheme_file, name, cap_id)?; + Ok(scheme) } pub fn handle_block(&mut self, op: &Op) -> SyscallResult> { let fd = op.file_id().expect("op is not fd based request"); let (read_timeout, write_timeout) = { - let file = self - .files + let handle = self + .handles .get(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; - if let SchemeFile::Socket(ref scheme_file) = *file { + if let Handle::File(SchemeFile::Socket(ref scheme_file)) = *handle { Ok((scheme_file.read_timeout, scheme_file.write_timeout)) } else { Err(SyscallError::new(syscall::EBADF)) @@ -319,14 +330,13 @@ where buf: &mut [u8], ) -> SyscallResult { let file = self - .files + .handles .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; + let file = match *file { - SchemeFile::Socket(ref mut file) => file, - _ => { - return Err(SyscallError::new(syscall::EBADF)); - } + Handle::File(SchemeFile::Socket(ref mut file)) => file, + _ => return Err(SyscallError::new(syscall::EBADF)), }; match setting { @@ -369,14 +379,12 @@ where buf: &[u8], ) -> SyscallResult { let file = self - .files + .handles .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; let file = match *file { - SchemeFile::Socket(ref mut file) => file, - _ => { - return Err(SyscallError::new(syscall::EBADF)); - } + Handle::File(SchemeFile::Socket(ref mut file)) => file, + _ => return Err(SyscallError::new(syscall::EBADF)), }; match setting { Setting::ReadTimeout | Setting::WriteTimeout => { @@ -445,9 +453,14 @@ where // SocketCall::GetToken => self.handle_get_token(id, payload), SocketCall::GetPeerName => { let file = self - .files + .handles .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; + + let file = match *file { + Handle::File(ref mut f) => f, + _ => return Err(SyscallError::new(syscall::EBADF)), + }; let mut socket_set = self.socket_set.borrow_mut(); let socket = socket_set.get_mut::(file.socket_handle()); @@ -456,28 +469,31 @@ where SocketCall::Shutdown => { let how = metadata[1] as usize; - if let Some(null_file) = self.nulls.get_mut(&fd) { - match how { - 0 => null_file.read_enabled = false, - 1 => null_file.write_enabled = false, - 2 => { - null_file.read_enabled = false; - null_file.write_enabled = false; - } - _ => return Err(SyscallError::new(EINVAL)), - } - return Ok(0); - } - - let file = self - .files + match self + .handles .get_mut(&fd) - .ok_or_else(|| SyscallError::new(syscall::EBADF))?; + .ok_or_else(|| SyscallError::new(syscall::EBADF))? + { + Handle::File(file) => { + let mut socket_set = self.socket_set.borrow_mut(); + let socket = socket_set.get_mut::(file.socket_handle()); - let mut socket_set = self.socket_set.borrow_mut(); - let socket = socket_set.get_mut::(file.socket_handle()); - - SocketT::handle_shutdown(socket, file, how) + SocketT::handle_shutdown(socket, file, how) + } + Handle::Null(null_file) => { + match how { + 0 => null_file.read_enabled = false, + 1 => null_file.write_enabled = false, + 2 => { + null_file.read_enabled = false; + null_file.write_enabled = false; + } + _ => return Err(SyscallError::new(EINVAL)), + } + Ok(0) + } + Handle::SchemeRoot => Err(SyscallError::new(syscall::EBADF)), + } } _ => Err(SyscallError::new(EOPNOTSUPP)), } @@ -504,7 +520,7 @@ where let id = self.next_fd; self.next_fd += 1; - self.nulls.insert(id, null); + self.handles.insert(id, Handle::Null(null)); Ok(OpenResult::ThisScheme { number: id, @@ -536,7 +552,7 @@ where self.next_fd += 1; self.ref_counts.insert(socket_handle, 1); - self.files.insert(id, file); + self.handles.insert(id, Handle::File(file)); Ok(OpenResult::ThisScheme { number: id, @@ -550,8 +566,30 @@ impl SchemeSync for SocketScheme where SocketT: SchemeSocket + AnySocket<'static>, { - fn open(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> SyscallResult { - self.open_inner(path, flags, ctx.uid, ctx.gid, true, true) + fn scheme_root(&mut self) -> SyscallResult { + let id = self.next_fd; + self.next_fd += 1; + self.handles.insert(id, Handle::SchemeRoot); + Ok(id) + } + + fn openat( + &mut self, + fd: usize, + path: &str, + flags: usize, + _fcntl_flags: u32, + ctx: &CallerCtx, + ) -> SyscallResult { + let handle = self + .handles + .get(&fd) + .ok_or_else(|| SyscallError::new(syscall::EBADF))?; + + match handle { + Handle::SchemeRoot => self.open_inner(path, flags, ctx.uid, ctx.gid, true, true), + _ => Err(SyscallError::new(syscall::EACCES)), + } } fn call( @@ -565,22 +603,11 @@ where } fn on_close(&mut self, fd: usize) { - if let Some(_null) = self.nulls.remove(&fd) { + let handle = if let Some(handle) = self.handles.remove(&fd) { + handle + } else { return; - } - - let socket_handle = { - let Some(file) = self.files.get(&fd) else { - return; - }; - file.socket_handle() }; - let scheme_file = self.files.remove(&fd); - let mut socket_set = self.socket_set.borrow_mut(); - if let Some(scheme_file) = scheme_file { - let socket = socket_set.get::(socket_handle); - let _ = socket.close_file(&scheme_file, &mut self.scheme_data); - } // incorrect, and kernel can't send close until all references are gone /*self.wait_queue.retain( @@ -590,30 +617,42 @@ where }| a != fd, );*/ - let remove = match self.ref_counts.entry(socket_handle) { - Entry::Vacant(_) => { - warn!("Closing a socket_handle with no ref"); - true - } - Entry::Occupied(mut e) => { - if *e.get() == 0 { - warn!("Closing a socket_handle with no ref"); - e.remove(); - true - } else { - *e.get_mut() -= 1; - if *e.get() == 0 { - e.remove(); + match handle { + Handle::SchemeRoot => return, + Handle::Null(_) => return, + Handle::File(scheme_file) => { + let socket_handle = scheme_file.socket_handle(); + let mut socket_set = self.socket_set.borrow_mut(); + + let socket = socket_set.get::(socket_handle); + let _ = socket.close_file(&scheme_file, &mut self.scheme_data); + + let remove = match self.ref_counts.entry(socket_handle) { + Entry::Vacant(_) => { + warn!("Closing a socket_handle with no ref"); true - } else { - false } + Entry::Occupied(mut e) => { + if *e.get() == 0 { + warn!("Closing a socket_handle with no ref"); + e.remove(); + true + } else { + *e.get_mut() -= 1; + if *e.get() == 0 { + e.remove(); + true + } else { + false + } + } + } + }; + + if remove { + socket_set.remove(socket_handle); } } - }; - - if remove { - socket_set.remove(socket_handle); } } @@ -627,15 +666,15 @@ where ) -> SyscallResult { let (fd, setting) = { let file = self - .files + .handles .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; match *file { - SchemeFile::Setting(ref setting_handle) => { + Handle::File(SchemeFile::Setting(ref setting_handle)) => { (setting_handle.fd, setting_handle.setting) } - SchemeFile::Socket(ref mut file) => { + Handle::File(SchemeFile::Socket(ref mut file)) => { let mut socket_set = self.socket_set.borrow_mut(); let socket = socket_set.get_mut::(file.socket_handle); let ret = SocketT::write_buf(socket, file, buf); @@ -646,6 +685,7 @@ where } return ret; } + _ => return Err(SyscallError::new(syscall::EBADF)), } }; self.update_setting(fd, setting, buf) @@ -661,14 +701,14 @@ where ) -> SyscallResult { let (fd, setting) = { let file = self - .files + .handles .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; match *file { - SchemeFile::Setting(ref setting_handle) => { + Handle::File(SchemeFile::Setting(ref setting_handle)) => { (setting_handle.fd, setting_handle.setting) } - SchemeFile::Socket(ref mut file) => { + Handle::File(SchemeFile::Socket(ref mut file)) => { let mut socket_set = self.socket_set.borrow_mut(); let socket = socket_set.get_mut::(file.socket_handle); @@ -681,6 +721,7 @@ where return ret; } + _ => return Err(SyscallError::new(syscall::EBADF)), } }; self.get_setting(fd, setting, buf) @@ -689,27 +730,28 @@ where fn dup(&mut self, fd: usize, buf: &[u8], _ctx: &CallerCtx) -> SyscallResult { let path = str::from_utf8(buf).or_else(|_| Err(SyscallError::new(syscall::EINVAL)))?; - if let Some((flags, uid, gid, read_enabled, write_enabled)) = - self.nulls.get(&fd).map(|null| { - ( - null.flags, - null.uid, - null.gid, - null.read_enabled, - null.write_enabled, - ) - }) - { - // dup from empty path to a new path - return self.open_inner(path, flags, uid, gid, read_enabled, write_enabled); - } - let new_file = { - let file = self - .files + let handle = self + .handles .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; + let file = match *handle { + Handle::SchemeRoot => return Err(SyscallError::new(syscall::EBADF)), + Handle::Null(ref null) => { + let (flags, uid, gid, read_enabled, write_enabled) = ( + null.flags, + null.uid, + null.gid, + null.read_enabled, + null.write_enabled, + ); + // dup from empty path to a new path + return self.open_inner(path, flags, uid, gid, read_enabled, write_enabled); + } + Handle::File(ref mut file) => file, + }; + let socket_handle = file.socket_handle(); let (new_handle, update_with) = match path { @@ -757,7 +799,10 @@ where .and_modify(|e| *e = e.saturating_sub(1)) .or_insert(0); - *self.ref_counts.entry(socket_handle).or_insert(0) += 1; + *self + .ref_counts + .entry(new_handle.socket_handle()) + .or_insert(0) += 1; file.socket_handle = socket_handle; file.data = data; @@ -771,7 +816,7 @@ where }; let id = self.next_fd; - self.files.insert(id, new_file); + self.handles.insert(id, Handle::File(new_file)); self.next_fd += 1; Ok(OpenResult::ThisScheme { @@ -787,9 +832,15 @@ where _ctx: &CallerCtx, ) -> SyscallResult { let file = self - .files + .handles .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; + + let file = match *file { + Handle::File(ref mut f) => f, + _ => return Err(SyscallError::new(syscall::EBADF)), + }; + match *file { SchemeFile::Setting(_) => return Err(SyscallError::new(syscall::EBADF)), SchemeFile::Socket(ref mut file) => { @@ -806,7 +857,7 @@ where fn fsync(&mut self, fd: usize, _ctx: &CallerCtx) -> SyscallResult<()> { { let _file = self - .files + .handles .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; } @@ -817,10 +868,15 @@ where fn fpath(&mut self, fd: usize, buf: &mut [u8], _ctx: &CallerCtx) -> SyscallResult { let file = self - .files + .handles .get_mut(&fd) .ok_or_else(|| SyscallError::new(syscall::EBADF))?; + let file = match *file { + Handle::File(ref mut f) => f, + _ => return Err(SyscallError::new(syscall::EBADF)), + }; + let socket_set = self.socket_set.borrow(); let socket = socket_set.get::(file.socket_handle()); @@ -834,33 +890,29 @@ where arg: usize, _ctx: &CallerCtx, ) -> SyscallResult { - if let Some(ref mut null) = self.nulls.get_mut(&fd) { - match cmd { + let handle = self + .handles + .get_mut(&fd) + .ok_or_else(|| SyscallError::new(syscall::EBADF))?; + + match *handle { + Handle::File(SchemeFile::Socket(ref mut socket_file)) => match cmd { + syscall::F_GETFL => Ok(socket_file.flags), + syscall::F_SETFL => { + socket_file.flags = arg & !syscall::O_ACCMODE; + Ok(0) + } + _ => Err(SyscallError::new(syscall::EINVAL)), + }, + Handle::Null(ref mut null) => match cmd { syscall::F_GETFL => Ok(null.flags), syscall::F_SETFL => { null.flags = arg & !syscall::O_ACCMODE; Ok(0) } _ => Err(SyscallError::new(syscall::EINVAL)), - } - } else { - let file = self - .files - .get_mut(&fd) - .ok_or_else(|| SyscallError::new(syscall::EBADF))?; - - if let SchemeFile::Socket(ref mut socket_file) = *file { - match cmd { - syscall::F_GETFL => Ok(socket_file.flags), - syscall::F_SETFL => { - socket_file.flags = arg & !syscall::O_ACCMODE; - Ok(0) - } - _ => Err(SyscallError::new(syscall::EINVAL)), - } - } else { - Err(SyscallError::new(syscall::EBADF)) - } + }, + _ => Err(SyscallError::new(syscall::EBADF)), } } } diff --git a/ptyd/src/controlterm.rs b/ptyd/src/controlterm.rs index 727c63ded8..10c66edda4 100644 --- a/ptyd/src/controlterm.rs +++ b/ptyd/src/controlterm.rs @@ -1,9 +1,8 @@ use std::cell::RefCell; use std::rc::{Rc, Weak}; -use syscall::error::{Error, Result, EAGAIN, EINVAL}; +use syscall::error::{Error, Result, EAGAIN, EINVAL, EWOULDBLOCK}; use syscall::flag::{EventFlags, F_GETFL, F_SETFL, O_ACCMODE, O_NONBLOCK}; -use syscall::EWOULDBLOCK; use crate::pty::Pty; use crate::resource::Resource; diff --git a/ptyd/src/main.rs b/ptyd/src/main.rs index 46bdfff79e..d82199b5cf 100644 --- a/ptyd/src/main.rs +++ b/ptyd/src/main.rs @@ -4,7 +4,7 @@ use event::{user_data, EventFlags, EventQueue}; use libredox::{flag, Fd}; use redox_scheme::wrappers::ReadinessBased; -use redox_scheme::{Response, SignalBehavior, Socket}; +use redox_scheme::{scheme::register_sync_scheme, Response, SignalBehavior, Socket}; use syscall::data::TimeSpec; mod controlterm; @@ -16,7 +16,7 @@ mod subterm; mod termios; mod winsize; -use scheme::PtyScheme; +use scheme::{Handle, PtyScheme}; fn main() { daemon::Daemon::new(daemon); @@ -36,9 +36,14 @@ fn daemon(daemon: daemon::Daemon) -> ! { let mut time_file = Fd::open(&time_path, flag::O_NONBLOCK, 0).expect("pty: failed to open time:"); - let socket = redox_scheme::Socket::nonblock("pty").expect("pty: failed to create pty scheme"); + let socket = redox_scheme::Socket::nonblock().expect("pty: failed to create pty scheme"); let mut handler = ReadinessBased::new(&socket, 16); + let scheme = RefCell::new(PtyScheme::new()); + register_sync_scheme(&socket, "pty", &mut *scheme.borrow_mut()) + .expect("ptyd: failed to register scheme to namespace"); + daemon.ready(); + libredox::call::setrens(0, 0).expect("ptyd: failed to enter null namespace"); event_queue @@ -48,12 +53,9 @@ fn daemon(daemon: daemon::Daemon) -> ! { .subscribe(time_file.raw(), EventSource::Time, EventFlags::READ) .expect("pty: failed to watch events on time:"); - daemon.ready(); - //TODO: do not set timeout if not necessary timeout(&mut time_file).expect("pty: failed to set timeout"); - let scheme = RefCell::new(PtyScheme::new()); let mut timeout_count = 0u64; scan_requests(&mut handler, &scheme).expect("pty: could not scan requests"); @@ -74,7 +76,9 @@ fn daemon(daemon: daemon::Daemon) -> ! { timeout_count = timeout_count.wrapping_add(1); for (_id, handle) in scheme.borrow_mut().handles.iter_mut() { - handle.timeout(timeout_count); + if let Handle::Resource(res) = handle { + res.timeout(timeout_count); + } } handler @@ -127,14 +131,16 @@ fn scan_requests( fn issue_events(socket: &Socket, scheme: &mut PtyScheme) { for (id, handle) in scheme.handles.iter_mut() { - let events = handle.events(); - if events != syscall::EventFlags::empty() { - socket - .write_response( - Response::post_fevent(*id, events.bits()), - SignalBehavior::Restart, - ) - .expect("pty: failed to send scheme event"); + if let Handle::Resource(ref mut res) = handle { + let events = res.events(); + if events != syscall::EventFlags::empty() { + socket + .write_response( + Response::post_fevent(*id, events.bits()), + SignalBehavior::Restart, + ) + .expect("pty: failed to send scheme event"); + } } } } @@ -143,6 +149,7 @@ fn timeout(time_file: &mut Fd) -> libredox::error::Result<()> { let mut time = TimeSpec::default(); time_file.read(&mut time)?; + // Set timeout for 100ms time.tv_nsec += 100_000_000; while time.tv_nsec >= 1_000_000_000 { time.tv_sec += 1; diff --git a/ptyd/src/scheme.rs b/ptyd/src/scheme.rs index f0402ca654..c7136a4e81 100644 --- a/ptyd/src/scheme.rs +++ b/ptyd/src/scheme.rs @@ -6,7 +6,7 @@ use std::str; use redox_scheme::scheme::SchemeSync; use redox_scheme::{CallerCtx, OpenResult}; use syscall::data::Stat; -use syscall::error::{Error, Result, EBADF, EINVAL, ENOENT}; +use syscall::error::{Error, Result, EACCES, EBADF, EINVAL, ENOENT}; use syscall::flag::{EventFlags, MODE_CHR}; use syscall::schemev2::NewFdFlags; @@ -18,9 +18,14 @@ use crate::subterm::PtySubTerm; use crate::termios::PtyTermios; use crate::winsize::PtyWinsize; +pub enum Handle { + Resource(Box), + SchemeRoot, +} + pub struct PtyScheme { next_id: usize, - pub handles: BTreeMap>, + pub handles: BTreeMap, } impl PtyScheme { @@ -30,24 +35,49 @@ impl PtyScheme { handles: BTreeMap::new(), } } + + fn get_resource_mut(&mut self, id: usize) -> Result<&mut Box> { + match self.handles.get_mut(&id).ok_or(Error::new(EBADF))? { + Handle::Resource(res) => Ok(res), + Handle::SchemeRoot => Err(Error::new(EBADF)), + } + } } impl SchemeSync for PtyScheme { - fn open(&mut self, path: &str, flags: usize, _ctx: &CallerCtx) -> Result { + fn scheme_root(&mut self) -> Result { + let id = self.next_id; + self.next_id += 1; + self.handles.insert(id, Handle::SchemeRoot); + Ok(id) + } + + fn openat( + &mut self, + dirfd: usize, + path: &str, + flags: usize, + fcntl_flags: u32, + _ctx: &CallerCtx, + ) -> Result { + if !matches!( + self.handles.get(&dirfd).ok_or(Error::new(EBADF))?, + Handle::SchemeRoot + ) { + return Err(Error::new(EACCES)); + } + let path = path.trim_matches('/'); + let id = self.next_id; + self.next_id += 1; + if path.is_empty() { - let id = self.next_id; - self.next_id += 1; - let pty = Rc::new(RefCell::new(Pty::new(id))); - self.handles - .insert(id, Box::new(PtyControlTerm::new(pty, flags))); - - Ok(OpenResult::ThisScheme { - number: id, - flags: NewFdFlags::empty(), - }) + self.handles.insert( + id, + Handle::Resource(Box::new(PtyControlTerm::new(pty, flags))), + ); } else { let control_term_id = path.parse::().or(Err(Error::new(EINVAL)))?; let pty = { @@ -55,32 +85,40 @@ impl SchemeSync for PtyScheme { .handles .get(&control_term_id) .ok_or(Error::new(ENOENT))?; - handle.pty() + + match handle { + Handle::Resource(res) => res.pty(), + Handle::SchemeRoot => return Err(Error::new(ENOENT)), + } }; - let id = self.next_id; - self.next_id += 1; - - self.handles - .insert(id, Box::new(PtySubTerm::new(pty, flags))); - - Ok(OpenResult::ThisScheme { - number: id, - flags: NewFdFlags::empty(), - }) + self.handles.insert( + id, + Handle::Resource(Box::new(PtySubTerm::new(pty, flags | fcntl_flags as usize))), + ); } + + Ok(OpenResult::ThisScheme { + number: id, + flags: NewFdFlags::empty(), + }) } fn dup(&mut self, old_id: usize, buf: &[u8], _ctx: &CallerCtx) -> Result { let handle: Box = { let old_handle = self.handles.get(&old_id).ok_or(Error::new(EBADF))?; + let old_resource = match old_handle { + Handle::Resource(res) => res, + Handle::SchemeRoot => return Err(Error::new(EBADF)), + }; + if buf == b"pgrp" { - Box::new(PtyPgrp::new(old_handle.pty(), old_handle.flags())) + Box::new(PtyPgrp::new(old_resource.pty(), old_resource.flags())) } else if buf == b"termios" { - Box::new(PtyTermios::new(old_handle.pty(), old_handle.flags())) + Box::new(PtyTermios::new(old_resource.pty(), old_resource.flags())) } else if buf == b"winsize" { - Box::new(PtyWinsize::new(old_handle.pty(), old_handle.flags())) + Box::new(PtyWinsize::new(old_resource.pty(), old_resource.flags())) } else { return Err(Error::new(EINVAL)); } @@ -88,7 +126,7 @@ impl SchemeSync for PtyScheme { let id = self.next_id; self.next_id += 1; - self.handles.insert(id, handle); + self.handles.insert(id, Handle::Resource(handle)); Ok(OpenResult::ThisScheme { number: id, @@ -104,7 +142,7 @@ impl SchemeSync for PtyScheme { _fcntl_flags: u32, _ctx: &CallerCtx, ) -> Result { - let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + let handle = self.get_resource_mut(id)?; handle.read(buf) } @@ -116,42 +154,47 @@ impl SchemeSync for PtyScheme { _fcntl_flags: u32, _ctx: &CallerCtx, ) -> Result { - let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + let handle = self.get_resource_mut(id)?; handle.write(buf) } fn fcntl(&mut self, id: usize, cmd: usize, arg: usize, _ctx: &CallerCtx) -> Result { - let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + let handle = self.get_resource_mut(id)?; handle.fcntl(cmd, arg) } fn fevent(&mut self, id: usize, _flags: EventFlags, _ctx: &CallerCtx) -> Result { - let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + let handle = self.get_resource_mut(id)?; handle.fevent() } fn fpath(&mut self, id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result { - let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + let handle = self.get_resource_mut(id)?; handle.path(buf) } fn fstat(&mut self, id: usize, stat: &mut Stat, _ctx: &CallerCtx) -> Result<()> { - let _handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; - *stat = Stat { - st_mode: MODE_CHR | 0o666, - ..Default::default() - }; + match handle { + Handle::SchemeRoot => return Err(Error::new(EBADF)), + Handle::Resource(_res) => { + *stat = Stat { + st_mode: MODE_CHR | 0o666, + ..Default::default() + }; + } + } Ok(()) } fn fsync(&mut self, id: usize, _ctx: &CallerCtx) -> Result<()> { - let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + let handle = self.get_resource_mut(id)?; handle.sync() } fn on_close(&mut self, id: usize) { - self.handles.remove(&id); + let _ = self.handles.remove(&id); } } diff --git a/ptyd/src/subterm.rs b/ptyd/src/subterm.rs index 098182da32..4bf521d8ed 100644 --- a/ptyd/src/subterm.rs +++ b/ptyd/src/subterm.rs @@ -1,9 +1,8 @@ use std::cell::RefCell; use std::rc::Weak; -use syscall::error::{Error, Result, EAGAIN, EINVAL, EPIPE}; +use syscall::error::{Error, Result, EAGAIN, EINVAL, EPIPE, EWOULDBLOCK}; use syscall::flag::{EventFlags, F_GETFL, F_SETFL, O_ACCMODE, O_NONBLOCK}; -use syscall::EWOULDBLOCK; use crate::pty::Pty; use crate::resource::Resource; diff --git a/ramfs/src/main.rs b/ramfs/src/main.rs index a22554f478..b675c6f4b6 100644 --- a/ramfs/src/main.rs +++ b/ramfs/src/main.rs @@ -14,14 +14,14 @@ fn main() { fn daemon(daemon: daemon::Daemon) -> ! { let scheme_name = env::args().nth(1).expect("Usage:\n\tramfs SCHEME_NAME"); - let socket = - redox_scheme::Socket::create(&scheme_name).expect("ramfs: failed to create socket"); + let socket = redox_scheme::Socket::create().expect("ramfs: failed to create socket"); - let mut scheme = Scheme::new(scheme_name).expect("ramfs: failed to initialize scheme"); - - libredox::call::setrens(0, 0).expect("ramfs: failed to enter null namespace"); + let mut scheme = Scheme::new(scheme_name.clone()).expect("ramfs: failed to initialize scheme"); + redox_scheme::scheme::register_sync_scheme(&socket, &scheme_name, &mut scheme) + .expect("ramfs: failed to register to namespace"); daemon.ready(); + libredox::call::setrens(0, 0).expect("ramfs: failed to enter null namespace"); loop { let Some(request) = socket diff --git a/ramfs/src/scheme.rs b/ramfs/src/scheme.rs index f569380dda..0b164062da 100644 --- a/ramfs/src/scheme.rs +++ b/ramfs/src/scheme.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::convert::{TryFrom, TryInto}; use std::io::{Cursor, Seek}; use std::iter; @@ -22,9 +23,19 @@ use redox_scheme::{CallerCtx, OpenResult}; use crate::filesystem::{self, File, FileData, Filesystem, Inode}; +enum Handle { + Inode(usize), + SchemeRoot, +} + +// TODO: Move to relibc +const AT_REMOVEDIR: usize = 0x200; + pub struct Scheme { scheme_name: String, filesystem: Filesystem, + handles: HashMap, + next_id: usize, } impl Scheme { /// Create the scheme, with the name being used for `fpath`. @@ -32,10 +43,11 @@ impl Scheme { Ok(Self { scheme_name, filesystem: Filesystem::new()?, + handles: HashMap::new(), + next_id: 0, }) } - /// Remove a directory entry, where the entry can be both a file or a directory. Used by both - /// `unlink` and `rmdir`. + /// Remove a directory entry, where the entry can be both a file or a directory. Used by `unlinkat`. pub fn remove_dentry(&mut self, path: &str, uid: u32, gid: u32, directory: bool) -> Result<()> { let removed_inode = { let (parent_dir_inode, name_to_delete) = @@ -148,7 +160,27 @@ impl Scheme { } impl SchemeSync for Scheme { - fn open(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result { + fn scheme_root(&mut self) -> Result { + let id = self.next_id; + self.handles.insert(id, Handle::SchemeRoot); + self.next_id += 1; + Ok(id) + } + fn openat( + &mut self, + dirfd: usize, + path: &str, + flags: usize, + fcntl_flags: u32, + ctx: &CallerCtx, + ) -> Result { + if !matches!( + self.handles.get(&dirfd).ok_or(Error::new(EBADF))?, + Handle::SchemeRoot + ) { + return Err(Error::new(EACCES)); + } + let exists = self.filesystem.resolve(path, 0, 0).is_ok(); if flags & O_CREAT != 0 && flags & O_EXCL != 0 && exists { return Err(Error::new(EEXIST)); @@ -232,19 +264,26 @@ impl SchemeSync for Scheme { new_inode_number } else { - self.open_existing(path, flags, ctx.uid, ctx.gid)?.0 + self.open_existing(path, flags | fcntl_flags as usize, ctx.uid, ctx.gid)? + .0 }; + let new_id = self.next_id; + self.handles.insert(new_id, Handle::Inode(inode)); + self.next_id += 1; Ok(OpenResult::ThisScheme { - number: inode, + number: new_id, flags: NewFdFlags::POSITIONED, }) } fn unlinkat(&mut self, dirfd: usize, path: &str, flags: usize, ctx: &CallerCtx) -> Result<()> { - let _ = self - .filesystem - .files - .get_mut(&dirfd) - .ok_or(Error::new(EBADFD))?; + { + if !matches!( + self.handles.get(&dirfd).ok_or(Error::new(EBADF))?, + Handle::SchemeRoot + ) { + return Err(Error::new(EACCES)); + } + } self.remove_dentry( path, ctx.uid, @@ -254,7 +293,7 @@ impl SchemeSync for Scheme { } fn read( &mut self, - inode: usize, + fd: usize, buf: &mut [u8], offset: u64, fcntl_flags: u32, @@ -263,7 +302,10 @@ impl SchemeSync for Scheme { let Ok(offset) = usize::try_from(offset) else { return Ok(0); }; - + let inode = match self.handles.get(&fd).ok_or(Error::new(EBADFD))? { + Handle::Inode(inode) => inode, + Handle::SchemeRoot => return Err(Error::new(EISDIR)), + }; let file = self .filesystem .files @@ -289,13 +331,17 @@ impl SchemeSync for Scheme { } fn getdents<'buf>( &mut self, - inode: usize, + fd: usize, mut buf: DirentBuf<&'buf mut [u8]>, opaque_offset: u64, ) -> Result> { let Ok(offset) = usize::try_from(opaque_offset) else { return Ok(buf); }; + let inode = match self.handles.get(&fd).ok_or(Error::new(EBADFD))? { + Handle::Inode(inode) => inode, + Handle::SchemeRoot => return Err(Error::new(EISDIR)), + }; let file = self .filesystem .files @@ -318,7 +364,7 @@ impl SchemeSync for Scheme { } fn write( &mut self, - inode: usize, + fd: usize, buf: &[u8], offset: u64, _fcntl_flags: u32, @@ -327,7 +373,10 @@ impl SchemeSync for Scheme { let Ok(offset) = usize::try_from(offset) else { return Ok(0); }; - + let inode = match self.handles.get(&fd).ok_or(Error::new(EBADFD))? { + Handle::Inode(inode) => inode, + Handle::SchemeRoot => return Err(Error::new(EISDIR)), + }; let file = self .filesystem .files @@ -353,7 +402,11 @@ impl SchemeSync for Scheme { Err(Error::new(EISDIR)) } } - fn fchmod(&mut self, inode: usize, mode: u16, _ctx: &CallerCtx) -> Result<()> { + fn fchmod(&mut self, fd: usize, mode: u16, _ctx: &CallerCtx) -> Result<()> { + let inode = match self.handles.get(&fd).ok_or(Error::new(EBADFD))? { + Handle::Inode(inode) => inode, + Handle::SchemeRoot => return Err(Error::new(EISDIR)), + }; let file = self .filesystem .files @@ -413,12 +466,12 @@ impl SchemeSync for Scheme { // TODO Err(Error::new(ENOSYS)) } - fn fpath( - &mut self, - mut current_inode: usize, - buf: &mut [u8], - _ctx: &CallerCtx, - ) -> Result { + fn fpath(&mut self, fd: usize, buf: &mut [u8], _ctx: &CallerCtx) -> Result { + let mut current_inode = match *self.handles.get(&fd).ok_or(Error::new(EBADFD))? { + Handle::Inode(inode) => inode, + Handle::SchemeRoot => return Err(Error::new(EISDIR)), + }; + let mut chain = Vec::new(); let mut current_info = self @@ -462,7 +515,12 @@ impl SchemeSync for Scheme { // TODO Err(Error::new(ENOSYS)) } - fn fstat(&mut self, inode: usize, stat: &mut Stat, _ctx: &CallerCtx) -> Result<()> { + fn fstat(&mut self, fd: usize, stat: &mut Stat, _ctx: &CallerCtx) -> Result<()> { + let inode = match *self.handles.get(&fd).ok_or(Error::new(EBADFD))? { + Handle::Inode(inode) => inode, + Handle::SchemeRoot => return Err(Error::new(EISDIR)), + }; + let block_size = self.filesystem.block_size(); let file = self .filesystem @@ -535,7 +593,11 @@ impl SchemeSync for Scheme { fn fsync(&mut self, _inode: usize, _ctx: &CallerCtx) -> Result<()> { Ok(()) } - fn ftruncate(&mut self, inode: usize, size: u64, _ctx: &CallerCtx) -> Result<()> { + fn ftruncate(&mut self, fd: usize, size: u64, _ctx: &CallerCtx) -> Result<()> { + let inode = match self.handles.get(&fd).ok_or(Error::new(EBADFD))? { + Handle::Inode(inode) => inode, + Handle::SchemeRoot => return Err(Error::new(EISDIR)), + }; let file = self .filesystem .files @@ -560,7 +622,11 @@ impl SchemeSync for Scheme { } Ok(()) } - fn futimens(&mut self, inode: usize, times: &[TimeSpec], _ctx: &CallerCtx) -> Result<()> { + fn futimens(&mut self, fd: usize, times: &[TimeSpec], _ctx: &CallerCtx) -> Result<()> { + let inode = match self.handles.get(&fd).ok_or(Error::new(EBADFD))? { + Handle::Inode(inode) => inode, + Handle::SchemeRoot => return Err(Error::new(EISDIR)), + }; let file = self .filesystem .files @@ -577,7 +643,10 @@ impl SchemeSync for Scheme { } } impl Scheme { - pub fn on_close(&mut self, inode: usize) { + pub fn on_close(&mut self, fd: usize) { + let Some(Handle::Inode(inode)) = self.handles.remove(&fd) else { + return; + }; let Some(inode_info) = self.filesystem.files.get_mut(&inode) else { return; }; diff --git a/randd/src/main.rs b/randd/src/main.rs index 45bf7129c8..0bc50a5173 100644 --- a/randd/src/main.rs +++ b/randd/src/main.rs @@ -13,15 +13,14 @@ pub const MODE_READ: u16 = 0o4; #[cfg(target_arch = "x86_64")] use raw_cpuid::CpuId; -use redox_scheme::scheme::SchemeSync; -use redox_scheme::{CallerCtx, OpenResult, RequestKind, SignalBehavior, Socket}; -use syscall::data::Stat; -use syscall::flag::EventFlags; -use syscall::schemev2::NewFdFlags; -use syscall::{ - Error, Result, EBADF, EEXIST, ENOENT, EPERM, MODE_CHR, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, - O_WRONLY, +use redox_scheme::{ + scheme::{register_sync_scheme, SchemeSync}, + CallerCtx, OpenResult, RequestKind, Response, SignalBehavior, Socket, }; +use syscall::data::Stat; +use syscall::flag::{EventFlags, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_WRONLY}; +use syscall::schemev2::NewFdFlags; +use syscall::{Error, Result, EACCES, EBADF, EEXIST, ENOENT, EOPNOTSUPP, EPERM, MODE_CHR}; // Create an RNG Seed to create initial seed from the rdrand intel instruction use rand_core::SeedableRng; @@ -100,6 +99,20 @@ impl OpenFileInfo { } } +enum Handle { + File(OpenFileInfo), + SchemeRoot, +} + +impl Handle { + fn as_file(&self) -> Option<&OpenFileInfo> { + match self { + Self::File(info) => Some(info), + _ => None, + } + } +} + /// Struct to represent the rand scheme. struct RandScheme { prng: ChaCha20Rng, @@ -107,11 +120,11 @@ struct RandScheme { // https://docs.rs/rand/0.5.0/rand/prng/chacha/struct.ChaChaRng.html // Allows 2^64 streams of random numbers, which we will equate with file numbers prng_stat: Stat, - open_descriptors: BTreeMap, // Cannot use HashMap as the implementation + handles: BTreeMap, // calls the system RNG (us) for entropy to protect against HashDOS attacks. // Trying to create a HashMap causes a system crash. // - next_fd: Wrapping, + next_id: Wrapping, } impl RandScheme { @@ -125,19 +138,16 @@ impl RandScheme { st_uid: 0, ..Default::default() }, - open_descriptors: BTreeMap::new(), - next_fd: Wrapping(0), + handles: BTreeMap::new(), + next_id: Wrapping(0), } } /// Gets the open file info for a file descriptor if it is open - error otherwise. fn get_fd(&self, fd: usize) -> Result<&OpenFileInfo> { // Check we've got a valid file descriptor - let file_info = match self.open_descriptors.get(&fd) { - Some(m) => m, - None => return Err(Error::new(EBADF)), - }; - Ok(file_info) + let handle = self.handles.get(&fd).ok_or(Error::new(EBADF))?; + handle.as_file().ok_or(Error::new(EBADF)) } /// Checks to see if the op (MODE_READ, MODE_WRITE) can be performed on the open file /// descriptor - Will return the open file info if successful, and error if the file @@ -162,120 +172,19 @@ impl RandScheme { entropy_array.copy_from_slice(hash.as_slice()); self.prng = ChaCha20Rng::from_seed(entropy_array); } -} -#[test] -fn test_scheme_perms() { - use syscall::{O_CLOEXEC, O_STAT}; - - let mut ctx = CallerCtx { - pid: 0, - uid: 1, - gid: 1, - id: unsafe { std::mem::zeroed() }, // Id doesn't have a public constructor - }; - - let mut scheme = RandScheme::new(); - scheme.prng_stat.st_mode = MODE_CHR | 0o200; - scheme.prng_stat.st_uid = 1; - scheme.prng_stat.st_gid = 1; - assert!(scheme.open("/", O_RDWR, &ctx).is_err()); - assert!(scheme.open("/", O_RDONLY, &ctx).is_err()); - - scheme.prng_stat.st_mode = MODE_CHR | 0o400; - let mut fd = match scheme.open("", O_RDONLY, &ctx).unwrap() { - OpenResult::ThisScheme { number, .. } => number, - _ => panic!(), - }; - assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_ok()); - assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_err()); - scheme.on_close(fd); - - assert!(scheme.open("", O_WRONLY, &ctx).is_err()); - assert!(scheme.open("", O_RDWR, &ctx).is_err()); - - scheme.prng_stat.st_mode = MODE_CHR | 0o600; - fd = match scheme.open("", O_RDWR, &ctx).unwrap() { - OpenResult::ThisScheme { number, .. } => number, - _ => panic!(), - }; - assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_ok()); - assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_ok()); - scheme.on_close(fd); - - ctx.uid = 2; - ctx.gid = 2; - fd = match scheme.open("", O_STAT, &ctx).unwrap() { - OpenResult::ThisScheme { number, .. } => number, - _ => panic!(), - }; - assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_err()); - assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_err()); - scheme.on_close(fd); - fd = match scheme.open("", O_STAT | O_CLOEXEC, &ctx).unwrap() { - OpenResult::ThisScheme { number, .. } => number, - _ => panic!(), - }; - assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_err()); - assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_err()); - scheme.on_close(fd); - - // Try another user in group (no group perms) - ctx.uid = 2; - ctx.gid = 1; - fd = match scheme.open("", O_STAT | O_CLOEXEC, &ctx).unwrap() { - OpenResult::ThisScheme { number, .. } => number, - _ => panic!(), - }; - assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_err()); - assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_err()); - scheme.on_close(fd); - scheme.prng_stat.st_mode = MODE_CHR | 0o660; - fd = match scheme.open("", O_STAT | O_CLOEXEC, &ctx).unwrap() { - OpenResult::ThisScheme { number, .. } => number, - _ => panic!(), - }; - assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_ok()); - assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_ok()); - scheme.on_close(fd); - - // Check root can do anything - scheme.prng_stat.st_mode = MODE_CHR | 0o000; - ctx.uid = 0; - ctx.gid = 0; - fd = match scheme.open("", O_STAT | O_CLOEXEC, &ctx).unwrap() { - OpenResult::ThisScheme { number, .. } => number, - _ => panic!(), - }; - assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_ok()); - assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_ok()); - scheme.on_close(fd); - - // Check the rand:/urandom URL (Equivalent to rand:/) - scheme.prng_stat.st_mode = MODE_CHR | 0o660; - ctx.uid = 2; - ctx.gid = 1; - fd = match scheme.open("/urandom", O_STAT | O_CLOEXEC, &ctx).unwrap() { - OpenResult::ThisScheme { number, .. } => number, - _ => panic!(), - }; - assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_ok()); - assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_ok()); - scheme.on_close(fd); -} - -impl SchemeSync for RandScheme { - fn open(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result { + fn open_inner(&mut self, path: &str, flags: usize, ctx: &CallerCtx) -> Result { // We are only allowing - // reads/writes from rand:/ and rand:/urandom - the root directory on its own is passed as an empty slice + // reads/writes from /scheme/rand/ and /scheme/rand/urandom - the root directory on its own is passed as an empty slice if path != "" && path != "/urandom" { return Err(Error::new(ENOENT)); } + if flags & (O_CREAT | O_EXCL) == O_CREAT | O_EXCL { return Err(Error::new(EEXIST)); } - let fd = self.next_fd; + let id = self.next_id; let open_file_info = OpenFileInfo { o_flags: flags, file_stat: self.prng_stat, @@ -293,38 +202,179 @@ impl SchemeSync for RandScheme { { return Err(Error::new(EPERM)); } - self.open_descriptors.insert(fd.0, open_file_info); - // Get the next file descriptor - self.next_fd += Wrapping(1); + + self.handles.insert(id.0, Handle::File(open_file_info)); + // If we've looped round there's a small chance that the file descriptor still exists, so loop till we get one that doesn't + self.next_id += Wrapping(1); loop { - if !self.open_descriptors.contains_key(&self.next_fd.0) { + if !self.handles.contains_key(&self.next_id.0) { break; } else { - self.next_fd += Wrapping(1); + self.next_id += Wrapping(1); } } + Ok(OpenResult::ThisScheme { - number: fd.0, + number: id.0, flags: NewFdFlags::empty(), }) } +} +#[test] +fn test_scheme_perms() { + use syscall::{O_CLOEXEC, O_STAT}; + + let mut ctx = CallerCtx { + pid: 0, + uid: 1, + gid: 1, + id: unsafe { std::mem::zeroed() }, // Id doesn't have a public constructor + }; + + let mut scheme = RandScheme::new(); + scheme.prng_stat.st_mode = MODE_CHR | 0o200; + scheme.prng_stat.st_uid = 1; + scheme.prng_stat.st_gid = 1; + assert!(scheme.open_inner("/", O_RDWR, &ctx).is_err()); + assert!(scheme.open_inner("/", O_RDONLY, &ctx).is_err()); + + scheme.prng_stat.st_mode = MODE_CHR | 0o400; + let mut fd = match scheme.open("", O_RDONLY, &ctx).unwrap() { + OpenResult::ThisScheme { number, .. } => number, + _ => panic!(), + }; + assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_ok()); + assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_err()); + scheme.on_close(fd); + + assert!(scheme.open_inner("", O_WRONLY, &ctx).is_err()); + assert!(scheme.open_inner("", O_RDWR, &ctx).is_err()); + + scheme.prng_stat.st_mode = MODE_CHR | 0o600; + fd = match scheme.open_inner("", O_RDWR, &ctx).unwrap() { + OpenResult::ThisScheme { number, .. } => number, + _ => panic!(), + }; + assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_ok()); + assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_ok()); + scheme.on_close(fd); + + ctx.uid = 2; + ctx.gid = 2; + fd = match scheme.open_inner("", O_STAT, &ctx).unwrap() { + OpenResult::ThisScheme { number, .. } => number, + _ => panic!(), + }; + assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_err()); + assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_err()); + scheme.on_close(fd); + fd = match scheme.open_inner("", O_STAT | O_CLOEXEC, &ctx).unwrap() { + OpenResult::ThisScheme { number, .. } => number, + _ => panic!(), + }; + assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_err()); + assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_err()); + scheme.on_close(fd); + + // Try another user in group (no group perms) + ctx.uid = 2; + ctx.gid = 1; + fd = match scheme.open_inner("", O_STAT | O_CLOEXEC, &ctx).unwrap() { + OpenResult::ThisScheme { number, .. } => number, + _ => panic!(), + }; + assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_err()); + assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_err()); + scheme.on_close(fd); + scheme.prng_stat.st_mode = MODE_CHR | 0o660; + fd = match scheme.open_inner("", O_STAT | O_CLOEXEC, &ctx).unwrap() { + OpenResult::ThisScheme { number, .. } => number, + _ => panic!(), + }; + assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_ok()); + assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_ok()); + scheme.on_close(fd); + + // Check root can do anything + scheme.prng_stat.st_mode = MODE_CHR | 0o000; + ctx.uid = 0; + ctx.gid = 0; + fd = match scheme.open_inner("", O_STAT | O_CLOEXEC, &ctx).unwrap() { + OpenResult::ThisScheme { number, .. } => number, + _ => panic!(), + }; + assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_ok()); + assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_ok()); + scheme.on_close(fd); + + // Check the rand:/urandom URL (Equivalent to rand:/) + scheme.prng_stat.st_mode = MODE_CHR | 0o660; + ctx.uid = 2; + ctx.gid = 1; + fd = match scheme + .open_inner("/urandom", O_STAT | O_CLOEXEC, &ctx) + .unwrap() + { + OpenResult::ThisScheme { number, .. } => number, + _ => panic!(), + }; + assert!(scheme.can_perform_op_on_fd(fd, MODE_READ).is_ok()); + assert!(scheme.can_perform_op_on_fd(fd, MODE_WRITE).is_ok()); + scheme.on_close(fd); +} + +impl SchemeSync for RandScheme { + fn scheme_root(&mut self) -> Result { + let id = self.next_id; + self.handles.insert(id.0, Handle::SchemeRoot); + + // Get the next file descriptor + self.next_id += Wrapping(1); + // If we've looped round there's a small chance that the file descriptor still exists, so loop till we get one that doesn't + loop { + if !self.handles.contains_key(&self.next_id.0) { + break; + } else { + self.next_id += Wrapping(1); + } + } + Ok(id.0) + } + + fn openat( + &mut self, + dirfd: usize, + path: &str, + flags: usize, + _fcntl_flags: u32, + ctx: &CallerCtx, + ) -> Result { + if !matches!( + self.handles.get(&dirfd).ok_or(Error::new(EBADF))?, + Handle::SchemeRoot + ) { + return Err(Error::new(EACCES)); + } + + self.open_inner(path, flags, ctx) + } /* Resource operations */ fn read( &mut self, - file: usize, + id: usize, buf: &mut [u8], _offset: u64, - _flags: u32, + _fcntl_flags: u32, _ctx: &CallerCtx, ) -> Result { // Check fd and permissions - self.can_perform_op_on_fd(file, MODE_READ)?; + self.can_perform_op_on_fd(id, MODE_READ)?; // Setting the stream will ensure that if two clients are reading concurrently, they won't get the same numbers - self.prng.set_stream(file as u64); // Should probably find a way to re-instate the counter for this stream, but - // not doing so won't make the output any less 'random' + self.prng.set_stream(id as u64); // Should probably find a way to re-instate the counter for this stream, but + // not doing so won't make the output any less 'random' self.prng.fill_bytes(buf); Ok(buf.len()) @@ -332,14 +382,14 @@ impl SchemeSync for RandScheme { fn write( &mut self, - file: usize, + id: usize, buf: &[u8], _offset: u64, - _flags: u32, + _fcntl_flags: u32, _ctx: &CallerCtx, ) -> Result { // Check fd and permissions - self.can_perform_op_on_fd(file, MODE_WRITE)?; + self.can_perform_op_on_fd(id, MODE_WRITE)?; // TODO - when we support other entropy sources, just add this to an entropy pool // TODO - consider having trusted and untrusted entropy writing paths @@ -357,23 +407,23 @@ impl SchemeSync for RandScheme { Ok(buf.len()) } - fn fchmod(&mut self, file: usize, mode: u16, _ctx: &CallerCtx) -> Result<()> { + fn fchmod(&mut self, id: usize, mode: u16, ctx: &CallerCtx) -> Result<()> { // Check fd and permissions - let file_info = self.get_fd(file)?; + let file_info = self.get_fd(id)?; // only root and owner can chmod - if file_info.uid != file_info.file_stat.st_uid && file_info.uid != 0 { + if ctx.uid != file_info.file_stat.st_uid && ctx.uid != 0 { return Err(Error::new(EPERM)); } - self.prng_stat.st_mode = MODE_CHR | mode; + self.prng_stat.st_mode = MODE_CHR | (mode & MODE_PERM); // Apply mask Ok(()) } - fn fchown(&mut self, file: usize, uid: u32, gid: u32, _ctx: &CallerCtx) -> Result<()> { + fn fchown(&mut self, id: usize, uid: u32, gid: u32, ctx: &CallerCtx) -> Result<()> { // Check fd and permissions - let file_info = self.get_fd(file)?; - // only root and owner can chmod - if file_info.uid != file_info.file_stat.st_uid && file_info.uid != 0 { + let file_info = self.get_fd(id)?; + // only root and owner can fchown + if ctx.uid != file_info.file_stat.st_uid && ctx.uid != 0 { return Err(Error::new(EPERM)); } @@ -411,15 +461,18 @@ impl SchemeSync for RandScheme { fn on_close(&mut self, file: usize) { // just remove the file descriptor from the open descriptors - self.open_descriptors.remove(&file); + let _ = self.handles.remove(&file); } } fn daemon(daemon: daemon::Daemon) -> ! { - let socket = Socket::create("rand").expect("randd: failed to create rand scheme"); + let socket = Socket::create().expect("randd: failed to create rand scheme"); let mut scheme = RandScheme::new(); + register_sync_scheme(&socket, "rand", &mut scheme) + .expect("randd: failed to register scheme to namespace"); + daemon.ready(); libredox::call::setrens(0, 0).expect("randd: failed to enter null namespace"); diff --git a/zerod/src/main.rs b/zerod/src/main.rs index 5e884d2cd3..6dc8b75931 100644 --- a/zerod/src/main.rs +++ b/zerod/src/main.rs @@ -1,5 +1,4 @@ -use redox_scheme::scheme::SchemeSync; -use redox_scheme::{RequestKind, SignalBehavior, Socket}; +use redox_scheme::{scheme::register_sync_scheme, RequestKind, SignalBehavior, Socket}; use scheme::ZeroScheme; @@ -25,13 +24,16 @@ fn daemon(daemon: daemon::Daemon) -> ! { Ty::Null => "null", Ty::Zero => "zero", }; - let socket = Socket::create(name).expect("zerod: failed to create zero scheme"); + let socket = Socket::create().expect("zerod: failed to create zero scheme"); let mut zero_scheme = ZeroScheme(ty); - libredox::call::setrens(0, 0).expect("zerod: failed to enter null namespace"); + register_sync_scheme(&socket, name, &mut zero_scheme) + .expect("zerod: failed to register scheme to namespace"); daemon.ready(); + libredox::call::setrens(0, 0).expect("zerod: failed to enter null namespace"); + loop { let Some(request) = socket .next_request(SignalBehavior::Restart) @@ -47,7 +49,6 @@ fn daemon(daemon: daemon::Daemon) -> ! { .write_response(response, SignalBehavior::Restart) .expect("zerod: failed to write responses to zero scheme"); } - RequestKind::OnClose { id } => zero_scheme.on_close(id), _ => (), } } diff --git a/zerod/src/scheme.rs b/zerod/src/scheme.rs index dc13043aa3..d39772f026 100644 --- a/zerod/src/scheme.rs +++ b/zerod/src/scheme.rs @@ -1,23 +1,36 @@ use redox_scheme::scheme::SchemeSync; use redox_scheme::{CallerCtx, OpenResult}; -use syscall::schemev2::NewFdFlags; -use syscall::{error::*, MODE_CHR}; +use syscall::{error::*, schemev2::NewFdFlags, MODE_CHR}; use crate::Ty; pub struct ZeroScheme(pub Ty); +const SCHEME_ROOT_ID: usize = 1; + impl SchemeSync for ZeroScheme { - fn open(&mut self, _path: &str, _flags: usize, _ctx: &CallerCtx) -> Result { + fn scheme_root(&mut self) -> Result { + Ok(SCHEME_ROOT_ID) + } + fn openat( + &mut self, + dirfd: usize, + path: &str, + _flags: usize, + _fcntl_flags: u32, + _ctx: &CallerCtx, + ) -> Result { + if dirfd != SCHEME_ROOT_ID { + return Err(Error::new(EACCES)); + } Ok(OpenResult::ThisScheme { number: 0, flags: NewFdFlags::empty(), }) } - fn read( &mut self, - _file: usize, + _id: usize, buf: &mut [u8], _offset: u64, _flags: u32, @@ -34,13 +47,13 @@ impl SchemeSync for ZeroScheme { fn write( &mut self, - _file: usize, - buffer: &[u8], + _id: usize, + buf: &[u8], _offset: u64, _flags: u32, _ctx: &CallerCtx, ) -> Result { - Ok(buffer.len()) + Ok(buf.len()) } fn fcntl(&mut self, _id: usize, _cmd: usize, _arg: usize, _ctx: &CallerCtx) -> Result { @@ -62,13 +75,11 @@ impl SchemeSync for ZeroScheme { Ok(size) } - fn fsync(&mut self, _file: usize, _ctx: &CallerCtx) -> Result<()> { + fn fsync(&mut self, _id: usize, _ctx: &CallerCtx) -> Result<()> { Ok(()) } - /// Close the file `number` - fn on_close(&mut self, _file: usize) {} - fn fstat(&mut self, _: usize, stat: &mut syscall::Stat, _ctx: &CallerCtx) -> Result<()> { + fn fstat(&mut self, _id: usize, stat: &mut syscall::Stat, _ctx: &CallerCtx) -> Result<()> { stat.st_mode = 0o666 | MODE_CHR; stat.st_size = 0; stat.st_blocks = 0;