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
127
128
129
130
//! One way to put a binary somewhere: stage a sibling, then `rename(2)`.
//!
//! forjar had three, and two of them were wrong.
//!
//! # The refusals
//!
//! `cp` opens the destination in place (`O_WRONLY|O_TRUNC`), which the kernel
//! and coreutils both refuse for the two states a provisioning tool most needs
//! to repair:
//!
//! - **`ETXTBSY` — "Text file busy".** Raised for a file that is currently
//! being executed. It is a property of the INODE, not of the directory
//! entry, so replacing the entry is legal while opening the inode for write
//! is not. This is why a self-applying host could not manage its own tools:
//! paiml/infra's lambda-labs box left forjar undeclared for exactly this
//! reason and drifted to 1.20.1 while the fleet ran 1.21.x. It is not
//! confined to forjar-updating-forjar — the same provider installs `rclone`,
//! `age` and `sops` into /usr/local/bin, and a running `rclone` is enough.
//!
//! - **"cp: not writing through dangling symlink".** coreutils refuses to
//! create the target of a symlink that points at nothing. That is precisely
//! the wreckage forjar exists to repair: a CI cache-prune deletes the real
//! files in a shared `~/.cargo/bin` and leaves the symlinks behind, so
//! `apply --refresh` DETECTED the divergence and then died on `cp`.
//!
//! Both are the same root cause — an in-place open of the destination path —
//! which is why one fix retires both.
//!
//! # Why not `install(1)`
//!
//! The cargo provider already moved to `install`, which does clear both
//! refusals: GNU unlinks the destination first, BSD writes a sibling temp and
//! renames. But GNU's unlink-then-create leaves the path ABSENT in between,
//! and on a host where sixteen CI runners share one `$CARGO_HOME/bin` an
//! `exec` landing in that window fails ENOENT.
//!
//! Measured on paiml's lambda-labs, statting the destination in a tight loop
//! while it was replaced 4000 times:
//!
//! ```text
//! install(1): 10611 absent of 396132 stats (2.7%)
//! temp + mv: 0 absent of 741725 stats
//! ```
//!
//! `rename(2)` is atomic by definition: a concurrent `exec` gets the old
//! binary or the new one, never nothing. It also does not follow a symlink at
//! the destination, so it replaces a dangling link rather than chasing it.
//! Staging as a SIBLING of the destination is what keeps the rename within one
//! filesystem, where atomicity is guaranteed and `mv` cannot silently
//! degrade into copy-then-unlink.
/// Shell function name emitted by [`atomic_install_fn`].
pub const ATOMIC_INSTALL_FN: &str = "_fj_install_bin";
/// POSIX shell definition of `_fj_install_bin <src> <dest> [runner]`.
///
/// `runner` is the command runner for destinations the current user cannot
/// write: `command` (the POSIX builtin, and the default) or `sudo`. It is
/// expanded QUOTED — `"$_fji_run" cp ...` — which is why the emitted shell
/// carries no SC2086/SC2183 findings. An earlier draft used an unquoted
/// prefix defaulting to the empty string; it worked, and cost the shipped
/// `install.sh` sixteen new bashrs warnings, in a repo whose whole premise is
/// that every script it ships passes that gate.
///
/// The destination DIRECTORY is the caller's responsibility. A `mkdir -p
/// "$dir"` here tripped bashrs SEC010 (path traversal) in `install.sh`, and
/// all three call sites create the directory already — so a missing one is a
/// loud failure rather than something this helper papers over.
///
/// Emitted rather than shelled out to because forjar's job is to produce a
/// script that runs on the target host, and the target may be reached over
/// ssh with no forjar on it.
/// `_fj_install_bin` plus a loop that lands every file of a staging `bin/`
/// directory into `$2`, used by the cargo provider where one crate may ship
/// several binaries.