build system: remove working-tree stashing (data-loss risk); pre-cook CI=1; target-scoped diagnostics

Remove the stash-and-restore machinery entirely. It manipulated the operator's
working tree and had a fatal bug: modern git's 'stash push' does not print the
stash SHA on stdout, so the SHA was never recorded, the stash was never restored,
and with REDBEAR_ALLOW_DIRTY=1 the operator's dirty-fork WIP was silently
stranded in 'git stash list' on every build (base had accumulated 25 strands).
The build now cooks committed HEAD, or the working tree AS-IS under
REDBEAR_ALLOW_DIRTY=1 — it never touches the tree. A read-only startup advisory
surfaces any leftover redbear-build-* strands from the old code.

Recovered the valuable stranded work to local/recovered-stashes/ (netstack
proptest, relibc get_dns_server daemon-path + getnetbyaddr impl); originals
remain in each fork's git stash list. See local/recovered-stashes/README.md.

Also: pre-cook now runs 'repo cook' with CI=1 (matches make live), fixing the
'Entering raw terminal mode ... Inappropriate ioctl' noise; and the failure
diagnostics per-recipe dump is scoped to THIS build's artifacts (mtime >= build
start) so a bare/mini build no longer lists graphical packages left over from a
prior redbear-full build.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-27 12:59:08 +09:00
parent d41c0fd163
commit 6fc0366abb
6 changed files with 798 additions and 97 deletions
+43
View File
@@ -0,0 +1,43 @@
# Recovered / stranded stashes — 2026-07-27
The build system's stash-and-restore had a bug (`git stash push` never prints the
stash SHA on modern git, so the stash was created but never recorded, hence never
restored). With `REDBEAR_ALLOW_DIRTY=1`, every dirty build stranded the working
tree in `git stash list`. This directory preserves the VALUABLE strands as patch
files. **Nothing is lost** — the originals remain in each repo's `git stash list`.
Do NOT `git stash clear` any fork until you have triaged the list below.
## Extracted here (valuable, currently MISSING from the tree)
| Patch | Repo | What it is |
|-------|------|-----------|
| `base--netstack-proptest-prop_filter_rule_priority.patch` | local/sources/base `stash@{0}` | netstack filter-table property test (confirmed absent) |
| `base--netstack-round5-cargo+table.patch` | local/sources/base `stash@{5}` | netstack Cargo.toml (proptest dep) + filter/table.rs scaffolding |
| `relibc--get_dns_server-daemon-path.patch` | local/sources/relibc `stash@{2}` | `get_dns_server()` reads `/scheme/dns/nameservers` (daemon), falls back to `/etc/net/dns` |
| `relibc--getnetbyaddr+semaphore.patch` | local/sources/relibc `stash@{0}` | real `getnetbyaddr()` impl (was `unimplemented!()`) + semaphore cbindgen |
Review a patch, then apply with e.g.:
`git -C local/sources/relibc apply local/recovered-stashes/relibc--get_dns_server-daemon-path.patch`
(or `git -C <repo> stash apply stash@{N}` to pull the original from git.)
## NOT extracted — no value (still in git if ever needed)
- kernel `stash@{0}``ADDFILE_MIN` debug logging only.
- userutils `stash@{0}``LOGIN_DBG:` debug prints only.
- base `stash@{6,7,8}` + relibc misc — `Cargo.lock`-only churn (regenerable).
- base `stash@{1..4,9..24}``redbear-build-*` strands: mostly the same evolving
netstack/Cargo.lock WIP re-stranded each build; newest (`stash@{0}`) supersedes.
- base `stash@{12}` — "netstack+rtl8168d parallel-agent": Cargo.lock only, already in tree.
## Needs HUMAN review — do not bulk-restore
- parent repo `stash@{0}` ("wip-uncommitted-changes") — 202 files incl. binary
prefix tarballs and ~28k deletions; a large divergent snapshot. Inspect with
`git stash show -p 'stash@{0}'` before touching.
- parent `stash@{1}` (tlc menubar) — already in tree.
## Root cause fixed
Stashing was removed from `build-redbear.sh` (the build no longer touches your
working tree). The dirty-source gate still refuses dirty builds by default;
`REDBEAR_ALLOW_DIRTY=1` now cooks your working tree AS-IS instead of stashing it.
@@ -0,0 +1,63 @@
diff --git a/netstack/src/filter/table.rs b/netstack/src/filter/table.rs
index e6512c70..ed86c3dc 100644
--- a/netstack/src/filter/table.rs
+++ b/netstack/src/filter/table.rs
@@ -566,5 +566,55 @@ mod tests {
}
assert_eq!(table.rules.len(), 0, "remove clears the slot");
}
+
+ #[test]
+ fn prop_filter_rule_priority(
+ hook in 0u8..5u8,
+ n_rules in 1u8..10u8,
+ ) {
+ use crate::filter::rule::Protocol;
+ use crate::filter::{Hook, Verdict};
+ let mut table = FilterTable::new();
+ let hook_enum = match hook {
+ 0 => Hook::PreRouting,
+ 1 => Hook::InputLocal,
+ 2 => Hook::Forward,
+ 3 => Hook::OutputLocal,
+ _ => Hook::PostRouting,
+ };
+ // Insert n_rules Drop rules. evaluate() must hit the first
+ // one every time (table.add appends, evaluate walks the
+ // vector in order). The first rule's id is well-defined.
+ for _i in 0..n_rules {
+ let rule = FilterRule {
+ id: 0,
+ hook: hook_enum,
+ src_addr: None,
+ src_prefix_len: 0,
+ dst_addr: None,
+ dst_prefix_len: 0,
+ protocol: None,
+ src_port: None,
+ dst_port: None,
+ in_dev: None,
+ out_dev: None,
+ state_match: None,
+ verdict: Verdict::Drop,
+ match_count: 0,
+ };
+ let _ = table.add(rule);
+ }
+ let mut ctx = make_ctx(6, &[0; 64]);
+ ctx.hook = hook_enum;
+ let _ = table.evaluate(&ctx, smoltcp::time::Instant::from_millis(0));
+ // The first rule should always have match_count >= 1.
+ assert!(table.rules[0].match_count >= 1,
+ "first rule must match before later rules");
+ // And only the first rule should have matched if n_rules > 1.
+ if n_rules > 1 {
+ assert_eq!(table.rules[1].match_count, 0,
+ "second rule must not match (first rule is Drop)");
+ }
+ }
}
- }
\ No newline at end of file
+ }
\ No newline at end of file
@@ -0,0 +1,451 @@
diff --git a/Cargo.lock b/Cargo.lock
index f0eee42f..46252a3a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -53,6 +53,7 @@ dependencies = [
"num-traits",
"parking_lot 0.12.3",
"plain",
+ "redox-driver-sys",
"redox-scheme",
"redox_event",
"redox_syscall 0.9.0+rb0.3.1",
@@ -249,6 +250,21 @@ dependencies = [
"serde",
]
+[[package]]
+name = "bit-set"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
+
[[package]]
name = "bit_field"
version = "0.10.3"
@@ -778,6 +794,12 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
+[[package]]
+name = "fastrand"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
+
[[package]]
name = "fbbootlogd"
version = "0.1.0"
@@ -834,6 +856,12 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
@@ -948,6 +976,18 @@ dependencies = [
"wasi",
]
+[[package]]
+name = "getrandom"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
+dependencies = [
+ "cfg-if 1.0.4",
+ "libc",
+ "r-efi 5.3.0",
+ "wasip2",
+]
+
[[package]]
name = "getrandom"
version = "0.4.3"
@@ -956,7 +996,7 @@ checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
dependencies = [
"cfg-if 1.0.4",
"libc",
- "r-efi",
+ "r-efi 6.0.0",
"rand_core 0.10.1",
]
@@ -1012,9 +1052,9 @@ checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
[[package]]
name = "heapless"
-version = "0.8.0"
+version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
+checksum = "25ba4bd83f9415b58b4ed8dc5714c76e626a105be4646c02630ad730ad3b5aa4"
dependencies = [
"hash32",
"stable_deref_trait",
@@ -1492,6 +1532,7 @@ dependencies = [
"daemon",
"libredox",
"log",
+ "proptest",
"redox-log",
"redox-scheme",
"redox_event",
@@ -1717,6 +1758,25 @@ dependencies = [
"unicode-ident",
]
+[[package]]
+name = "proptest"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744"
+dependencies = [
+ "bit-set",
+ "bit-vec",
+ "bitflags 2.13.0",
+ "num-traits",
+ "rand 0.9.5",
+ "rand_chacha 0.9.0",
+ "rand_xorshift",
+ "regex-syntax",
+ "rusty-fork",
+ "tempfile",
+ "unarray",
+]
+
[[package]]
name = "ps2d"
version = "0.1.0"
@@ -1728,6 +1788,7 @@ dependencies = [
"libredox",
"log",
"orbclient",
+ "redox-driver-sys",
"redox-scheme",
"redox_event",
"redox_syscall 0.9.0+rb0.3.1",
@@ -1752,6 +1813,12 @@ version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bb0fd6580eeed0103c054e3fba2c2618ff476943762f28a645b63b8692b21c9"
+[[package]]
+name = "quick-error"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
+
[[package]]
name = "quote"
version = "1.0.46"
@@ -1761,6 +1828,12 @@ dependencies = [
"proc-macro2",
]
+[[package]]
+name = "r-efi"
+version = "5.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
+
[[package]]
name = "r-efi"
version = "6.0.0"
@@ -1804,6 +1877,16 @@ dependencies = [
"rand_core 0.6.4",
]
+[[package]]
+name = "rand"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41"
+dependencies = [
+ "rand_chacha 0.9.0",
+ "rand_core 0.9.5",
+]
+
[[package]]
name = "rand"
version = "0.10.2"
@@ -1835,6 +1918,16 @@ dependencies = [
"rand_core 0.6.4",
]
+[[package]]
+name = "rand_chacha"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
+dependencies = [
+ "ppv-lite86",
+ "rand_core 0.9.5",
+]
+
[[package]]
name = "rand_core"
version = "0.3.1"
@@ -1865,12 +1958,30 @@ dependencies = [
"getrandom 0.2.17",
]
+[[package]]
+name = "rand_core"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
+dependencies = [
+ "getrandom 0.3.4",
+]
+
[[package]]
name = "rand_core"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
+[[package]]
+name = "rand_xorshift"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a"
+dependencies = [
+ "rand_core 0.9.5",
+]
+
[[package]]
name = "randd"
version = "0.1.0"
@@ -2149,6 +2260,18 @@ version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
+[[package]]
+name = "rusty-fork"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2"
+dependencies = [
+ "fnv",
+ "quick-error",
+ "tempfile",
+ "wait-timeout",
+]
+
[[package]]
name = "sb16d"
version = "0.1.0"
@@ -2328,9 +2451,9 @@ dependencies = [
[[package]]
name = "smoltcp"
-version = "0.12.0"
+version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dad095989c1533c1c266d9b1e8d70a1329dd3723c3edac6d03bbd67e7bf6f4bb"
+checksum = "5f73d40463bba65efc9adc6370b56df76d563cc46e2482bba58351b4afb7535e"
dependencies = [
"bitflags 1.3.2",
"byteorder",
@@ -2417,6 +2540,19 @@ dependencies = [
"unicode-ident",
]
+[[package]]
+name = "tempfile"
+version = "3.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
+dependencies = [
+ "fastrand",
+ "getrandom 0.4.3",
+ "once_cell",
+ "rustix",
+ "windows-sys",
+]
+
[[package]]
name = "termcolor"
version = "1.4.1"
@@ -2600,6 +2736,12 @@ dependencies = [
"serde",
]
+[[package]]
+name = "unarray"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
+
[[package]]
name = "unicode-ident"
version = "1.0.24"
@@ -2809,12 +2951,30 @@ dependencies = [
"utf8parse 0.1.1",
]
+[[package]]
+name = "wait-timeout"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
+[[package]]
+name = "wasip2"
+version = "1.0.4+wasi-0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
+dependencies = [
+ "wit-bindgen",
+]
+
[[package]]
name = "wasm-bindgen"
version = "0.2.126"
@@ -3038,6 +3198,12 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1"
+[[package]]
+name = "wit-bindgen"
+version = "0.57.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
+
[[package]]
name = "xhcid"
version = "0.1.0"
diff --git a/Cargo.toml b/Cargo.toml
index f145d6a8..0652a7cf 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -127,6 +127,7 @@ spin = "0.10"
static_assertions = "1.1.0"
thiserror = "2"
toml = "1"
+proptest = "1.11"
[workspace.lints.rust]
missing_docs = "allow" #TODO: set to deny when all public functions are documented
diff --git a/netstack/Cargo.toml b/netstack/Cargo.toml
index e2a5c954..ead48cb8 100644
--- a/netstack/Cargo.toml
+++ b/netstack/Cargo.toml
@@ -37,5 +37,36 @@ features = [
]
#For debugging: "log", "verbose"
+[dev-dependencies]
+proptest = "1.11"
+
+[[bin]]
+name = "fuzz_ethernet"
+path = "fuzz/fuzz_targets/ethernet.rs"
+
+[[bin]]
+name = "fuzz_arp"
+path = "fuzz/fuzz_targets/arp.rs"
+
+[[bin]]
+name = "fuzz_icmp"
+path = "fuzz/fuzz_targets/icmp.rs"
+
+[[bin]]
+name = "fuzz_tcp"
+path = "fuzz/fuzz_targets/tcp.rs"
+
+[[bin]]
+name = "fuzz_udp"
+path = "fuzz/fuzz_targets/udp.rs"
+
+[[bin]]
+name = "fuzz_dhcp"
+path = "fuzz/fuzz_targets/dhcp.rs"
+
+[[bin]]
+name = "fuzz_dns"
+path = "fuzz/fuzz_targets/dns.rs"
+
[lints]
workspace = true
diff --git a/netstack/src/filter/table.rs b/netstack/src/filter/table.rs
index ebdab4b9..84c4924c 100644
--- a/netstack/src/filter/table.rs
+++ b/netstack/src/filter/table.rs
@@ -525,6 +525,47 @@ mod tests {
assert_eq!(t.chain_counters.get(&Hook::InputLocal).copied().unwrap_or((0, 0)), (0, 0),
"reset_counters must clear chain_counters");
assert_eq!(t.rules.len(), 1,
- "reset_counters must preserve rules");
- }
-}
\ No newline at end of file
+ "reset_counters must preserve rules");
+ }
+
+ use proptest::prelude::*;
+
+ proptest! {
+ #[test]
+ fn prop_filter_add_remove_roundtrip(
+ _id_seed in 0u32..1000u32,
+ hook_idx in 0usize..5,
+ verdict_idx in 0usize..4,
+ ) {
+ use crate::filter::rule::{Protocol, StateMatch};
+ use crate::filter::{Hook, Verdict};
+ let hook = [Hook::PreRouting, Hook::InputLocal, Hook::Forward, Hook::OutputLocal, Hook::PostRouting][hook_idx];
+ let verdict = [Verdict::Accept, Verdict::Drop, Verdict::Log, Verdict::Reject][verdict_idx];
+ let mut table = FilterTable::new();
+ let rule = FilterRule {
+ id: 0,
+ hook,
+ src_addr: None,
+ src_prefix_len: 0,
+ dst_addr: None,
+ dst_prefix_len: 0,
+ protocol: None,
+ src_port: None,
+ dst_port: None,
+ in_dev: None,
+ out_dev: None,
+ state_match: None,
+ verdict,
+ match_count: 0,
+ };
+ let _added_id = table.add(rule);
+ assert_eq!(table.rules.len(), 1, "rule was added");
+ // remove the rule by iterating; the id is table-assigned
+ let id_to_remove = table.rules.iter().next().map(|r| r.id);
+ if let Some(id) = id_to_remove {
+ assert!(table.remove(id), "remove returns true for the added id");
+ }
+ assert_eq!(table.rules.len(), 0, "remove clears the slot");
+ }
+ }
+ }
\ No newline at end of file
@@ -0,0 +1,30 @@
diff --git a/src/header/netdb/redox.rs b/src/header/netdb/redox.rs
index 314dffcd..6e24ebed 100644
--- a/src/header/netdb/redox.rs
+++ b/src/header/netdb/redox.rs
@@ -7,6 +7,25 @@ use crate::{
use alloc::string::String;
pub fn get_dns_server() -> Result<String, Errno> {
+ // Try the cached daemon path first
+ if let Ok(first_ns) = (|| -> Result<String, Errno> {
+ let mut s = String::new();
+ let mut file = File::open(c"/scheme/dns/nameservers".into(), fcntl::O_RDONLY)?;
+ file.read_to_string(&mut s)
+ .map_err(|_| Errno(errno::EIO).sync())?;
+ // Take the first nameserver line
+ if let Some(first) = s.lines().next() {
+ let trimmed = first.trim();
+ if !trimmed.is_empty() {
+ return Ok(trimmed.to_string());
+ }
+ }
+ Err(Errno(errno::ENOENT).sync())
+ })() {
+ return Ok(first_ns);
+ }
+
+ // Fallback: read the static /etc/net/dns file (legacy path)
let mut string = String::new();
let mut file = File::open(c"/etc/net/dns".into(), fcntl::O_RDONLY)?;
file.read_to_string(&mut string)
@@ -0,0 +1,176 @@
diff --git a/src/header/netdb/mod.rs b/src/header/netdb/mod.rs
index 36681613..9c0804a8 100644
--- a/src/header/netdb/mod.rs
+++ b/src/header/netdb/mod.rs
@@ -421,9 +421,36 @@ pub unsafe extern "C" fn gethostbyname(name: *const c_char) -> *mut hostent {
(&raw mut HOST_ENTRY).cast::<hostent>()
}
-/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endnetent.html>.
+/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getnetbyaddr.html>.
+#[unsafe(no_mangle)]
pub unsafe extern "C" fn getnetbyaddr(net: u32, net_type: c_int) -> *mut netent {
- unimplemented!();
+ let mut n: *mut netent;
+ if net_type != AF_INET && net_type != AF_UNSPEC && net_type != AF_INET6 {
+ platform::ERRNO.set(ENOENT);
+ return ptr::null_mut();
+ }
+ unsafe { setnetent(NET_STAYOPEN) };
+ while {
+ n = unsafe { getnetent() };
+ !n.is_null()
+ } {
+ let n_ref = unsafe { &*n };
+ let mut addr_ptr = n_ref.n_net;
+ let mut matched = false;
+ if !addr_ptr.is_null() {
+ let stored = unsafe { *addr_ptr };
+ let stored_be = u32::from_be(stored);
+ matched = net == stored_be;
+ }
+ if matched {
+ unsafe { setnetent(NET_STAYOPEN) };
+ return n;
+ }
+ }
+ unsafe { setnetent(NET_STAYOPEN) };
+
+ platform::ERRNO.set(ENOENT);
+ ptr::null_mut::<netent>()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endnetent.html>.
diff --git a/src/header/semaphore/cbindgen.toml b/src/header/semaphore/cbindgen.toml
index 30f09127..bce68ad1 100644
--- a/src/header/semaphore/cbindgen.toml
+++ b/src/header/semaphore/cbindgen.toml
@@ -11,6 +11,9 @@ include_guard = "_RELIBC_SEMAPHORE_H"
after_includes = """
#include <bits/clockid-t.h> // for clockid_t from sys/types.h
#include <bits/timespec.h> // for timespec from time.h
+#include <bits/valist.h> // for va_list from stdarg.h for sem_open variadic args
+
+#define SEM_FAILED ((sem_t *)0)
"""
language = "C"
style = "Type"
diff --git a/src/header/semaphore/mod.rs b/src/header/semaphore/mod.rs
index c8ed7f53..19adea2a 100644
--- a/src/header/semaphore/mod.rs
+++ b/src/header/semaphore/mod.rs
@@ -6,11 +6,12 @@ use crate::{
error::ResultExt,
header::{
errno,
+ fcntl::O_CREAT,
time::{self, timespec},
},
platform::{
self,
- types::{c_char, c_int, c_long, c_uint, clockid_t},
+ types::{c_char, c_int, c_long, c_uint, clockid_t, mode_t},
},
};
@@ -25,9 +26,26 @@ pub union sem_t {
pub type RlctSempahore = crate::sync::Semaphore;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_close.html>.
-// #[unsafe(no_mangle)]
+///
+/// Releases the calling process's reference to the named semaphore indicated
+/// by `sem`. Redox does not yet provide a kernel-backed named-semaphore
+/// facility (no `nsem:` scheme or equivalent), so named semaphores cannot be
+/// created via [`sem_open`]. For an unnamed semaphore this function is a no-op
+/// per POSIX ("If the sem argument refers to an unnamed semaphore, the
+/// sem_close() function shall have no effect").
+///
+/// Upon success, returns `0`. Upon failure, returns `-1` and sets `errno` to
+/// `EINVAL` if `sem` is a null pointer.
+#[unsafe(no_mangle)]
pub unsafe extern "C" fn sem_close(sem: *mut sem_t) -> c_int {
- todo!("named semaphores")
+ if sem.is_null() {
+ platform::ERRNO.set(errno::EINVAL);
+ -1
+ } else {
+ // Named semaphores are unsupported on Redox; for unnamed semaphores
+ // sem_close is defined to be a no-op that returns 0.
+ 0
+ }
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_destroy.html>.
@@ -54,13 +72,31 @@ pub unsafe extern "C" fn sem_init(sem: *mut sem_t, _pshared: c_int, value: c_uin
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_open.html>.
-// TODO: va_list
-// #[unsafe(no_mangle)]
+///
+/// Creates or opens a named semaphore identified by `name`. A real
+/// implementation requires a kernel-backed facility that Redox does not yet
+/// provide (for example a `nsem:` scheme serving semaphore objects under a
+/// well-known path). Per POSIX, when the implementation does not support
+/// named semaphores the correct response is to fail and set `errno` to
+/// `ENOSYS` rather than panic.
+///
+/// Upon failure, returns [`SEM_FAILED`] (a null pointer) and sets `errno` to
+/// `ENOSYS`. [`SEM_FAILED`] is defined in the generated `semaphore.h`.
+#[unsafe(no_mangle)]
pub unsafe extern "C" fn sem_open(
name: *const c_char,
- oflag: c_int, /* (va_list) value: c_uint */
+ oflag: c_int,
+ mut __valist: ...
) -> *mut sem_t {
- todo!("named semaphores")
+ let _ = name;
+ if oflag & O_CREAT == O_CREAT {
+ // Drain the mandatory `mode` (mode_t) and `value` (unsigned) varargs
+ // so the caller's va_list stays consistent, even though we fail.
+ let _ = unsafe { __valist.next_arg::<mode_t>() };
+ let _ = unsafe { __valist.next_arg::<c_uint>() };
+ }
+ platform::ERRNO.set(errno::ENOSYS);
+ core::ptr::null_mut()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_post.html>.
@@ -83,9 +119,17 @@ pub unsafe extern "C" fn sem_trywait(sem: *mut sem_t) -> c_int {
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_unlink.html>.
-// #[unsafe(no_mangle)]
+///
+/// Removes a named semaphore identified by `name`. Like [`sem_open`], this
+/// requires the kernel-backed named-semaphore facility that Redox does not
+/// yet provide. Per POSIX, the correct response is to fail with `ENOSYS`.
+///
+/// Upon failure, returns `-1` and sets `errno` to `ENOSYS`.
+#[unsafe(no_mangle)]
pub unsafe extern "C" fn sem_unlink(name: *const c_char) -> c_int {
- todo!("named semaphores")
+ let _ = name;
+ platform::ERRNO.set(errno::ENOSYS);
+ -1
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sem_trywait.html>.
diff --git a/src/platform/redox/ptrace.rs b/src/platform/redox/ptrace.rs
index 51cb7646..4ac81a00 100644
--- a/src/platform/redox/ptrace.rs
+++ b/src/platform/redox/ptrace.rs
@@ -261,7 +261,10 @@ unsafe fn inner_ptrace(
(&mut &session.regs).write(&redox_regs)?;
Ok(0)
}
- _ => unimplemented!(),
+ _ => Err(io::Error::new(
+ io::ErrorKind::InvalidInput,
+ "unsupported ptrace request",
+ )),
}
}
+35 -97
View File
@@ -9,7 +9,7 @@ PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# REDBEAR_VERSION (which tracks the OS release derived from the git branch).
# Starts at 1.0 and is bumped AUTOMATICALLY on every change by the pre-commit
# git hook (local/scripts/bump-build-version.sh); do not edit the minor by hand.
BUILD_REDBEAR_VERSION="1.1"
BUILD_REDBEAR_VERSION="1.2"
# ── Colorized output ──────────────────────────────────
# Enabled only on a TTY with NO_COLOR unset, so redirected build logs and CI
@@ -78,13 +78,6 @@ export REDBEAR_BUILD_STATE_DIR="$(mktemp -d -t redbear-build-state.XXXXXX)"
REDBEAR_BUILD_LOGS_DIR="$REDBEAR_BUILD_STATE_DIR/logs"
mkdir -p "$REDBEAR_BUILD_LOGS_DIR"
# Stash-and-restore: every Red Bear fork source whose working tree is dirty
# is stashed before the build runs, then popped on EXIT (success or failure).
# This ensures pkgar fingerprints always reflect the committed HEAD, not
# transient WIP that could be rolled back between builds.
declare -ga REDBEAR_STASHED_REPOS=()
declare -gA REDBEAR_STASH_MAP=()
declare -ga REDBEAR_FORK_SOURCES=(
"relibc:$PROJECT_ROOT/local/sources/relibc"
"kernel:$PROJECT_ROOT/local/sources/kernel"
@@ -152,87 +145,6 @@ redbear_check_fork_branches() {
fi
}
redbear_stash_one() {
local label="$1"
local dir="$2"
if [ -n "${REDBEAR_STASH_MAP[$label]:-}" ]; then
return 0
fi
if ! redbear_is_fork_dirty "$dir"; then
echo " [clean] $label"
return 0
fi
rm -f "$dir/.git/index.lock"
local msg="redbear-build-$REDBEAR_BUILD_START_EPOCH-$label"
local sha
local stash_output
stash_output=$(git -C "$dir" stash push --include-untracked -m "$msg" 2>/dev/null)
sha=$(echo "$stash_output" | grep -E '^[0-9a-f]{40}$' | head -1)
if [ -n "$sha" ]; then
REDBEAR_STASH_MAP[$label]="$sha"
REDBEAR_STASHED_REPOS+=("$label:$dir")
echo " [stashed] $label: $sha"
else
# Stash produced no SHA — either nothing to stash or locale issue.
# If the fork is now clean, treat as success.
if ! redbear_is_fork_dirty "$dir"; then
echo " [clean] $label (stash noop)"
return 0
fi
echo " [FAIL] $label: git stash push failed" >&2
return 1
fi
}
redbear_unstash_one() {
local label="$1"
local dir="$2"
local sha="${REDBEAR_STASH_MAP[$label]:-}"
if [ -z "$sha" ]; then
return 0
fi
rm -f "$dir/.git/index.lock"
if git -C "$dir" stash pop --quiet 2>/dev/null; then
echo " [restored] $label"
else
echo " [WARN] $label: stash pop failed — stash preserved at $sha" >&2
echo " Run: git -C '$dir' stash pop to restore manually" >&2
fi
unset REDBEAR_STASH_MAP[$label]
}
redbear_stash_all_dirty_forks() {
echo "${C_INFO}>>>${C_RESET} Stashing dirty fork sources (restored at exit)..."
local any_dirty=0
local entry label dir
for entry in "${REDBEAR_FORK_SOURCES[@]}"; do
label="${entry%%:*}"
dir="${entry#*:}"
[ -d "$dir" ] || continue
redbear_is_fork_dirty "$dir" && any_dirty=1
redbear_stash_one "$label" "$dir" || {
echo "ERROR: failed to stash $label — aborting to avoid cooking from dirty tree" >&2
redbear_unstash_all_forks "stash-failure"
exit 1
}
done
if [ "$any_dirty" = "0" ]; then
echo " (all fork sources clean)"
fi
return 0
}
redbear_unstash_all_forks() {
local reason="${1:-exit}"
[ "${#REDBEAR_STASHED_REPOS[@]}" -eq 0 ] && return 0
echo "${C_INFO}>>>${C_RESET} Restoring stashed fork sources (reason: $reason)..."
local i
for ((i=${#REDBEAR_STASHED_REPOS[@]}-1; i>=0; i--)); do
local entry="${REDBEAR_STASHED_REPOS[$i]}"
redbear_unstash_one "${entry%%:*}" "${entry#*:}"
done
}
# Cookbook binary fingerprint: BLAKE3-hash (via sha256sum on this system)
# of all .rs files in src/. Used to detect when the cookbook needs to be
# rebuilt. The hash is written to target/release/.cookbook-src-fingerprint
@@ -477,18 +389,30 @@ if [ -z "${REDBEAR_RELEASE:-}" ]; then
echo ""
fi
redbear_stash_all_dirty_forks
# The build system no longer stashes your working tree (it cooks committed HEAD,
# or your tree AS-IS under REDBEAR_ALLOW_DIRTY=1). A prior version stashed dirty
# forks and, due to a git-output-parsing bug, sometimes failed to restore them,
# leaving "redbear-build-*" stashes behind. Surface any leftovers read-only so
# stranded WIP is never silently forgotten. This NEVER touches your tree.
_rb_leftover_total=0
for _entry in "${REDBEAR_FORK_SOURCES[@]}"; do
_d="${_entry#*:}"
[ -d "$_d/.git" ] || continue
_n=$(git -C "$_d" stash list 2>/dev/null | grep -c "redbear-build-" || true)
if [ "${_n:-0}" -gt 0 ]; then
echo "${C_WARN}>>> NOTE:${C_RESET} ${_entry%%:*}: $_n leftover 'redbear-build-*' stash(es) from an older build — recover: git -C '$_d' stash list" >&2
_rb_leftover_total=$((_rb_leftover_total + _n))
fi
done
[ "$_rb_leftover_total" -gt 0 ] && echo "${C_WARN}>>> NOTE:${C_RESET} $_rb_leftover_total stranded build-stash(es) total; see local/recovered-stashes/README.md" >&2
# EXIT trap: always restore stashes (success or failure), always dump
# diagnostics, always preserve the original exit code. The trap fires
# EXIT trap: always dump diagnostics and preserve the original exit code. The trap fires
# even on signals (SIGINT/SIGTERM/SIGHUP) because we don't override
# ERR/DEBUG and we use EXIT (the POSIX-standard pseudo-signal that fires
# for any exit path). The trap runs LAST in the shell's exit order, so
# any explicit `exit N` we do inside the script triggers it with $? = N.
redbear_exit_trap() {
local rc=$?
# Phase 1: unstash so the operator's working tree is restored.
redbear_unstash_all_forks "exit"
# Phase 2: dump failure diagnostics if the build failed.
if [ "$rc" -ne 0 ]; then
redbear_dump_failure_diagnostics "$rc"
@@ -552,7 +476,14 @@ redbear_dump_failure_diagnostics() {
# vs which have only stage.tmp (in-progress).
echo ""
echo "--- Per-recipe build state ---"
# Scope to THIS build only. The dump walks the whole recipe tree, so without
# a time filter a bare/mini build would list graphical packages (mesa, qt,
# kf6-*, sddm, ...) whose stage/stage.tmp are leftovers from an earlier
# redbear-full build — packages not even part of the target being built.
# Report an artifact only if it was modified during this build.
local recipe_dir target_arch
local _rb_start="${REDBEAR_BUILD_START_EPOCH:-0}"
local _rb_shown=0
for recipe_dir in "$PROJECT_ROOT"/recipes/*/ "$PROJECT_ROOT"/local/recipes/*/*/; do
[ -d "$recipe_dir" ] || continue
[ -f "$recipe_dir/recipe.toml" ] || continue
@@ -561,17 +492,24 @@ redbear_dump_failure_diagnostics() {
for target_arch in "$PROJECT_ROOT"/recipes/"$pkg"/target/*/ \
"$PROJECT_ROOT"/local/recipes/*/"$pkg"/target/*/; do
[ -d "$target_arch" ] || continue
local stage stage_tmp size
local stage stage_tmp size ts
stage="$target_arch/stage"
stage_tmp="$target_arch/stage.tmp"
if [ -d "$stage_tmp" ]; then
ts=$(stat -c %Y "$stage_tmp" 2>/dev/null || echo 0)
[ "$ts" -ge "$_rb_start" ] || continue
size=$(du -sh "$stage_tmp" 2>/dev/null | cut -f1)
echo " $pkg [$target_arch]: INCOMPLETE (stage.tmp: $size)"
_rb_shown=1
elif [ -d "$stage" ]; then
echo " $pkg [$target_arch]: complete"
ts=$(stat -c %Y "$stage" 2>/dev/null || echo 0)
[ "$ts" -ge "$_rb_start" ] || continue
echo " $pkg [$target_arch]: complete (this build)"
_rb_shown=1
fi
done
done
[ "$_rb_shown" = "0" ] && echo " (no recipe artifacts were modified during this build)"
echo ""
echo "========================================"
@@ -1023,7 +961,7 @@ for pkg in $PRECOOK_PKGS; do
if [ -n "${REDBEAR_RELEASE:-}" ]; then
PRECOOK_OFFLINE=true
fi
if ! REDBEAR_BUILD_PHASE=pre-cook COOKBOOK_OFFLINE="$PRECOOK_OFFLINE" \
if ! REDBEAR_BUILD_PHASE=pre-cook CI=1 COOKBOOK_OFFLINE="$PRECOOK_OFFLINE" \
"$PROJECT_ROOT/target/release/repo" cook "$pkg" >"$log" 2>&1; then
echo "WARNING: pre-cook of $pkg failed (non-fatal; will build during main phase). Tail of repo log:" >&2
tail -50 "$log" >&2