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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
//! The filesystem durability contract (DC-76): what prikk-store requires of a filesystem to
//! mutate a repository safely, stated as guarantees rather than primitives.
//!
//! **Why a trait, not prose.** A markdown description of "what we need" can drift from what the
//! code actually does, silently, the moment either changes without the other. A trait is checked
//! by the compiler: every mutation path in `anchored.rs` calls through [`DurabilityContract`], so
//! there is exactly one place that can state — and one place a future platform's implementation
//! must satisfy — what durability prikk-store depends on. `Linux` and, as of DC-81, `Macos` are the
//! real implementors (`super::anchored::LinuxDurability`, `super::anchored::MacosDurability`);
//! DC-82 added `NoDurability` (`super::anchored::none::NoDurability`) as the implementor for every
//! platform with neither — "unsupported" is a third implementor, not a `target_os` arm at each of
//! `anchored.rs`'s call sites.
//!
//! **Guarantee, not syscall — the whole point.** [`atomic_replace`](DurabilityContract::atomic_replace)
//! says "replace this file's content atomically, durably" — never "write a temp file and call
//! `renameat`". A method named after a primitive would already be platform-specific before a
//! second platform exists.
//!
//! [`durable_directory_entry`](DurabilityContract::durable_directory_entry) was originally the
//! worked example this contract was built around, and it was itself the one method that missed the
//! bar: it read "once this returns, every mutation made under `relative` since the last durability
//! point survives a crash" — a directory-scoped **batching** guarantee, shaped after `fsync` on a
//! directory fd rather than after any real caller's need. **DC-88 traced every caller in the
//! codebase and found none that wanted batching** — every other method above already bundles its
//! own transition-scoped directory sync as an integral part of *its own* guarantee (see each
//! method's doc comment), and this method's two real callers (`worktree.rs`'s checkout
//! materialization, confirming one file's presence is durable even when a call wrote nothing) only
//! ever wanted that narrower, single-entry confirmation. The method is restated accordingly: it
//! still resolves to `fsync` on the containing directory's fd on Linux, and (per DC-76 addendum-1,
//! confirmed against `rustix` 1.1.4's own source) `fcntl(fd, F_FULLFSYNC)`
//! (`rustix::fs::fcntl_fullfsync`) on macOS instead — `fsync` alone does not give the same guarantee
//! there, and stating the method as "fsync" would already have been wrong, before macOS was ever
//! implemented — but what it *promises* is now the requirement its callers actually rely on, not the
//! primitive that happens to satisfy it on POSIX.
//!
//! ## Cross-cutting invariants — properties every method must hold, not separate methods
//!
//! These are not enumerated as trait methods because every operation below needs all of them
//! simultaneously; stating them once here, and testing them once per operation in the conformance
//! suite (`super::tests::conformance`), is more precise than restating "...and refuses symlinks"
//! on every doc comment.
//!
//! - **G1 — root-anchored resolution.** Every path this contract accepts is resolved relative to
//! the [`MutationRoot`] that authorized it, one path component at a time, with no-follow on
//! every component including the last. A symlink swapped in anywhere along the path — not only
//! at the final component — must not let a mutation escape the root.
//! - **G6 — regular-file validation.** Any operation that opens an *existing* final entry
//! confirms it is a regular file before mutating it. A device, FIFO, or symlink that raced into
//! the resolved path is refused, not silently operated on.
//! - **G7 — non-blocking opens.** No operation may block indefinitely because a FIFO or device was
//! substituted at the resolved path.
//! - **G8 — concurrent-process-safe directory creation.** Two processes racing to create the same
//! directory component must both succeed; the loser observes and validates what the winner
//! created rather than erroring.
//!
//! ## Guarantee-to-method map
//!
//! | Guarantee | Method |
//! |---|---|
//! | G2 (atomic content replacement) | [`atomic_replace`](DurabilityContract::atomic_replace) |
//! | G3 (durable-after-return) | every method below returns only once its effect is durable; [`durable_directory_entry`](DurabilityContract::durable_directory_entry) confirms one entry's presence directly, without relying on any other method's write having happened in the same call |
//! | G4 (exclusive creation) | [`create_exclusive`](DurabilityContract::create_exclusive) |
//! | G5 (race-safe no-clobber publication) | [`publish_immutable`](DurabilityContract::publish_immutable) |
//! | G9 (mode-bit isolation) | [`set_permission_bits`](DurabilityContract::set_permission_bits) |
use Path;
use Result;
use MutationRoot;
/// What prikk-store requires of a filesystem to mutate a repository durably and safely. See the
/// module documentation for the cross-cutting invariants (G1, G6, G7, G8) every method upholds and
/// the guarantee-to-method map. `root` names the authority every path is resolved against; `relative`
/// is always relative to it, never absolute.
///
/// Deliberately **not** gated to any specific `target_os`: the whole point of this contract is a
/// platform-neutral statement of what the store requires, and a trait that vanishes on the
/// platforms it exists to enable would defeat that (DC-76 addendum-2 B1). The B1 repair's
/// `#[allow(dead_code)]` — needed because off Linux and macOS nothing implemented this trait — no
/// longer applies as of DC-82: `NoDurability` (`super::anchored::none::NoDurability`) is the
/// implementor for every platform without a real one, so the trait is used unconditionally now,
/// exactly the outcome the B1 repair's own doc comment predicted ("expected to stop applying the
/// moment a third platform implements this trait").
pub