diff --git a/local/recovered-stashes/README.md b/local/recovered-stashes/README.md new file mode 100644 index 0000000000..10433eb79e --- /dev/null +++ b/local/recovered-stashes/README.md @@ -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 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. diff --git a/local/recovered-stashes/base--netstack-proptest-prop_filter_rule_priority.patch b/local/recovered-stashes/base--netstack-proptest-prop_filter_rule_priority.patch new file mode 100644 index 0000000000..03c7d9de47 --- /dev/null +++ b/local/recovered-stashes/base--netstack-proptest-prop_filter_rule_priority.patch @@ -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 diff --git a/local/recovered-stashes/base--netstack-round5-cargo+table.patch b/local/recovered-stashes/base--netstack-round5-cargo+table.patch new file mode 100644 index 0000000000..dff2c177ac --- /dev/null +++ b/local/recovered-stashes/base--netstack-round5-cargo+table.patch @@ -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 diff --git a/local/recovered-stashes/relibc--get_dns_server-daemon-path.patch b/local/recovered-stashes/relibc--get_dns_server-daemon-path.patch new file mode 100644 index 0000000000..8f1bd76c56 --- /dev/null +++ b/local/recovered-stashes/relibc--get_dns_server-daemon-path.patch @@ -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 { ++ // Try the cached daemon path first ++ if let Ok(first_ns) = (|| -> Result { ++ 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) diff --git a/local/recovered-stashes/relibc--getnetbyaddr+semaphore.patch b/local/recovered-stashes/relibc--getnetbyaddr+semaphore.patch new file mode 100644 index 0000000000..d7ec25fa50 --- /dev/null +++ b/local/recovered-stashes/relibc--getnetbyaddr+semaphore.patch @@ -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::() + } + +-/// See . ++/// See . ++#[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::() + } + + /// See . +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 // for clockid_t from sys/types.h + #include // for timespec from time.h ++#include // 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 . +-// #[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 . +@@ -54,13 +72,31 @@ pub unsafe extern "C" fn sem_init(sem: *mut sem_t, _pshared: c_int, value: c_uin + } + + /// See . +-// 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::() }; ++ let _ = unsafe { __valist.next_arg::() }; ++ } ++ platform::ERRNO.set(errno::ENOSYS); ++ core::ptr::null_mut() + } + + /// See . +@@ -83,9 +119,17 @@ pub unsafe extern "C" fn sem_trywait(sem: *mut sem_t) -> c_int { + } + + /// See . +-// #[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 . +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", ++ )), + } + } + diff --git a/local/scripts/build-redbear.sh b/local/scripts/build-redbear.sh index 50b12200dd..d02f327078 100755 --- a/local/scripts/build-redbear.sh +++ b/local/scripts/build-redbear.sh @@ -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