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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
//! XFS internal journal — minimal "empty / clean" log writer.
//!
//! The XFS write path currently never replays log records: every
//! transaction is committed in place. The kernel, however, refuses to
//! mount read/write unless the on-disk log is recognised as "clean"
//! (last record is an unmount record). This module lays down such a
//! log: a single record header at log block 0 followed by an `unmount`
//! op-header + payload, the rest of the log left zeroed.
//!
//! ## On-disk layout (per the XFS PDF "Journaling Log" section and the
//! kernel's `xlog_rec_header` layout)
//!
//! Each log "basic block" is 512 bytes. The first 4 bytes of every BB
//! are reserved for cycle-stamping (the kernel overwrites them with
//! the current cycle when it writes the log); the original 4 bytes are
//! preserved in `h_cycle_data[]` of the log-record header.
//!
//! Record header at BB 0 (big-endian on disk):
//!
//! ```text
//! 0 4 h_magicno = 0xFEEDBABE
//! 4 4 h_cycle = 1
//! 8 4 h_version = 2 (LOGV2)
//! 12 4 h_len = bytes of payload after header (≥ BB size)
//! 16 8 h_lsn = (cycle << 32) | block (cycle=1, block=0)
//! 24 8 h_tail_lsn = same as h_lsn for a clean log
//! 32 4 h_crc = 0 (not validated for empty/clean log)
//! 36 4 h_prev_block = 0xFFFFFFFF (none)
//! 40 4 h_num_logops = 1
//! 44 256 h_cycle_data[64] — preserved-first-4-bytes per BB
//! 300 4 h_fmt = 1 (XLOG_FMT_LINUX_LE)
//! 304 16 h_fs_uuid
//! 320 4 h_size = 32768
//! ```
//!
//! Total header = 324 bytes; we round h_len up to one BB (512).
//!
//! At BB 1 we lay down an XLOG_UNMOUNT_TYPE op:
//!
//! ```text
//! xlog_op_header (12 bytes, big-endian on disk):
//! 0 4 oh_tid (any, we use 1)
//! 4 4 oh_len = 8 (unmount payload size)
//! 8 1 oh_clientid = XFS_LOG (0xAA) -- "log client"
//! 9 1 oh_flags = XLOG_COMMIT_TRANS | XLOG_UNMOUNT_TRANS (0x20)
//! 10 2 oh_res2 = 0
//! --- payload (8 bytes, little-endian) ---
//! 12 2 um_magic = XLOG_UNMOUNT_TYPE (0x556e = "Un")
//! 14 6 pad
//! ```
//!
//! The kernel only validates the unmount magic; the rest of the BB is
//! zero. The first 4 bytes of BB 1 are overwritten with the cycle
//! stamp (1), and the original first 4 bytes (the oh_tid) are stored
//! in `h_cycle_data[0]` of the record header.
//!
//! ## Limitations
//!
//! - We do NOT compute `h_crc`. The kernel's policy is to log a
//! `Corruption` warning if the CRC is wrong, but to still accept the
//! log when `h_num_logops == 1` and the only op is an unmount
//! record. (Real mounts succeed in practice.) A future revision can
//! add crc32c stamping.
//! - We always write `h_size = 32768`, `h_version = 2`, `h_fmt = 1`
//! (little-endian Linux) — the same values mkfs.xfs emits today.
//! - We never wrap-around. The log is single-pass.
use crateResult;
use crateBlockDevice;
/// XFS log basic-block size (always 512, independent of `sb_blocksize`).
pub const BBSIZE: u64 = 512;
/// `XLOG_HEADER_MAGIC_NUM` — first 4 bytes of every log record header.
pub const XLOG_HEADER_MAGIC_NUM: u32 = 0xFEED_BABE;
/// `XLOG_VERSION_2` — v2 log layout (LOGV2BIT must be set in sb_versionnum).
pub const XLOG_VERSION_2: u32 = 2;
/// `XLOG_FMT_LINUX_LE` — log format byte. mkfs.xfs writes this on any
/// little-endian host (which is everything we care about today).
pub const XLOG_FMT_LINUX_LE: u32 = 1;
/// Default in-memory iclog buffer size that mkfs.xfs records in
/// `h_size`. The kernel will not log a warning at any of {16k, 32k,
/// 64k, 128k, 256k}.
pub const XLOG_DEFAULT_H_SIZE: u32 = 32 * 1024;
/// `XLOG_UNMOUNT_TYPE` — 2-byte magic that immediately follows the op
/// header. Stored little-endian on disk regardless of the rest of the
/// log because `h_fmt = XLOG_FMT_LINUX_LE`.
pub const XLOG_UNMOUNT_TYPE: u16 = 0x556e; // "Un"
/// `XFS_TRANSACTION` clientid that mkfs.xfs writes for the unmount op.
pub const XFS_LOG_CLIENTID: u8 = 0xAA;
/// `XLOG_COMMIT_TRANS | XLOG_UNMOUNT_TRANS` flags.
pub const XLOG_OP_UNMOUNT_FLAGS: u8 = 0x20;
/// Default log size in FS blocks chosen by [`format`] when laying out a
/// fresh image. 512 blocks at 4 KiB = 2 MiB — well below mkfs.xfs's
/// recommended 10 MiB minimum but enough that the kernel will accept
/// the image as cleanly unmounted. Real production images should be
/// formatted with a larger log.
pub const DEFAULT_LOG_BLOCKS: u32 = 512;
/// Write the "empty unmount" log content into `dev` starting at
/// `log_byte_off` and covering `log_bytes` bytes. The region MUST be
/// pre-zeroed (we only stamp the bytes we know about). `uuid` is the
/// volume's `sb_uuid`.