1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
//! **Writing an executable file is a CHILD's job, never this process's**
//! (bl-fd28, generalized to production by bl-e6c9) — the whole of the ETXTBSY
//! discipline, in the one place the crate spells it.
//!
//! `fs::write` on a file holds a write fd for the length of the write. A `fork`
//! on ANY thread copies that fd into a child that keeps it until its own `exec`
//! completes, and an `exec` of that file inside the window is **ETXTBSY**. The
//! window cannot be closed from the fork side: [`super`]'s `cfg(test)` lock
//! covered only yog's own forks, and yog links `balls`, `litany` and `brazen`,
//! each of which forks `git` on its own account (measured, and measured out —
//! [`super`]'s module doc carries both numbers).
//!
//! bl-fd28 closed it on the side that owns it, for test fixtures. **The engine
//! had the same hazard and kept it**: `world::tools::ensure_shim` wrote the
//! world's shims with `fs::write` + `set_permissions` and a caller exec'd one
//! immediately after — yog composes a world's shims and then runs them, and yog
//! forks from every thread. Reproduced at bl-fd28's own recipe with the
//! `world::tools`/`world::tests` beats folded into the filter, 16 workers x 70
//! iterations: **7 ETXTBSY failures**, every one of them a shim exec.
//!
//! So there is one helper and it is production's: [`write_exec`]. The fd lives
//! in `sh`, which holds it for as long as `cat` runs and never for a moment in
//! this process, so a peer fork — in yog or in any crate yog links, at any
//! moment — has nothing of ours to copy. `rules/no-hand-chmod.yml` refuses the
//! hand-rolled spelling everywhere in `src`, so the discipline is structural
//! rather than a convention the next executable forgets.
//!
//! Two shapes were rejected. A **retry on ETXTBSY** turns a hazard into a
//! production loop and leaves the window open for whoever does not spin. A
//! **write-then-rename** does not work at all: a rename does not change the
//! inode, so the copied write fd still refers to the file the caller execs.
//!
//! The body goes down a pipe rather than an argv word, so nothing here has an
//! `ARG_MAX`. The pipe is [`io::pipe`] rather than `Stdio::piped()` for a
//! smaller reason that is worth the two `drop`s: `Child::stdin` is an `Option`
//! that can only be `Some` here, and an owned pipe has no unreachable arm to
//! answer for. A body large enough to fill the pipe buffer before `cat` drains
//! it would deadlock; a shim is a few hundred bytes and a fixture is a script.
//! `sh`'s stderr is captured rather than inherited, so the reason a write
//! failed rides the error instead of the caller's terminal.
use ;
use Path;
use Stdio;
/// The one recipe: read the body from stdin, then set the executable bits. Both
/// acts are the child's, which is the point — this process opens nothing.
const RECIPE: &str = r#"cat > "$1" && chmod 755 "$1""#;
/// Write `body` to `path` and mark it `0755` — **executable by all, writable
/// only by the owner** — entirely inside a child process. The crate's one way
/// to create an executable file, in production and in tests alike
/// (`crate::test_support::write_exec` is this function with the error turned
/// into a panic, which is all a fixture wants).
pub