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
//! Atomic file replacement: write to a temporary file in the destination directory, then
//! `rename` it into place.
//!
//! Rename within a directory is atomic on POSIX, so a reader never observes a half-written
//! TZif file, and an interrupted run leaves either the old file or nothing — never a
//! truncated one. The temp file is created in the *same directory* as the target so the
//! rename stays within one filesystem (cross-device renames fail).
//!
//! **The durability contract (T17.4), three layers — claimed exactly, not more:**
//! 1. **content fsync** — the temp file's bytes are `sync_all`'d *before* the publish, so the
//! published name never points at unflushed content.
//! 2. **atomic publish** — `hard_link` (exclusive create, default) or `rename` (`--force`) makes the
//! name appear (or replace) in one step; a reader/crash never sees a partial file.
//! 3. **directory-entry fsync** — when `durable` is set (the *install* path, not ephemeral scratch),
//! the **parent directory** is fsync'd after the publish, so the new directory entry itself
//! survives a crash. Without this layer a crash after the rename can still lose the entry on some
//! filesystems (the file content was durable, but the link to it was not).
//!
//! **What this does NOT claim (`RISK.INSTALL.1`):** a **whole-tree** crash-atomic install. Each file
//! is durably published, but a crash *mid-run* can leave some files published and others not — there is
//! no tree-level transaction. Directory-entry fsync is a **Unix** guarantee (opening a directory and
//! `sync_all`-ing it); on non-Unix it is a documented no-op, so the durability claim is Unix-scoped.
use ;
use ;
use Path;
use ;
use crate;
/// fsync a directory so a just-published entry within it is crash-durable (T17.4 layer 3). On Unix,
/// opening the directory read-only and `sync_all`-ing it flushes the directory inode. On non-Unix this
/// is a no-op (directory fsync is not portably available) — durability is therefore a Unix claim, stated
/// honestly rather than faked.
pub
pub
/// Per-process counter so concurrent writes within one process get distinct temp names.
static TEMP_SEQ: AtomicU64 = new;
/// Atomically write `bytes` to `target`.
///
/// We always write the content to a temp file in the *same directory* (so the final step
/// stays within one filesystem) and then publish it atomically:
///
/// * `overwrite = false` (the default): publish with `hard_link(temp → target)`, which the
/// kernel performs as an **atomic exclusive create** — it fails with `EEXIST` if `target`
/// already exists. This closes the check-then-act (TOCTOU) race that a separate
/// `exists()` test would leave open: there is no window between testing and creating.
/// * `overwrite = true` (`--force`): publish with `rename`, which atomically replaces any
/// existing file.
///
/// Either way a reader never observes a partially-written file. When `durable` is set, the parent
/// directory is fsync'd after the publish (T17.4 layer 3) so the new directory *entry* is crash-durable
/// — the install path sets it; ephemeral scratch writes (e.g. the release-diff zdump tree, the `compare`
/// oracle tree) pass `false` to skip the (pointless, for soon-deleted files) directory fsync.