Line data Source code
1 : //!
2 : //! Utilities for reading and writing the PostgreSQL control file.
3 : //!
4 : //! The PostgreSQL control file is one the first things that the PostgreSQL
5 : //! server reads when it starts up. It indicates whether the server was shut
6 : //! down cleanly, or if it crashed or was restored from online backup so that
7 : //! WAL recovery needs to be performed. It also contains a copy of the latest
8 : //! checkpoint record and its location in the WAL.
9 : //!
10 : //! The control file also contains fields for detecting whether the
11 : //! data directory is compatible with a postgres binary. That includes
12 : //! a version number, configuration options that can be set at
13 : //! compilation time like the block size, and the platform's alignment
14 : //! and endianness information. (The PostgreSQL on-disk file format is
15 : //! not portable across platforms.)
16 : //!
17 : //! The control file is stored in the PostgreSQL data directory, as
18 : //! `global/pg_control`. The data stored in it is designed to be smaller than
19 : //! 512 bytes, on the assumption that it can be updated atomically. The actual
20 : //! file is larger, 8192 bytes, but the rest of it is just filled with zeros.
21 : //!
22 : //! See src/include/catalog/pg_control.h in the PostgreSQL sources for more
23 : //! information. You can use PostgreSQL's pg_controldata utility to view its
24 : //! contents.
25 : //!
26 : use super::bindings::{ControlFileData, PG_CONTROL_FILE_SIZE};
27 :
28 : use anyhow::{bail, Result};
29 : use bytes::{Bytes, BytesMut};
30 :
31 : /// Equivalent to sizeof(ControlFileData) in C
32 : const SIZEOF_CONTROLDATA: usize = size_of::<ControlFileData>();
33 :
34 : impl ControlFileData {
35 : /// Compute the offset of the `crc` field within the `ControlFileData` struct.
36 : /// Equivalent to offsetof(ControlFileData, crc) in C.
37 2 : const fn pg_control_crc_offset() -> usize {
38 2 : std::mem::offset_of!(ControlFileData, crc)
39 2 : }
40 :
41 : ///
42 : /// Interpret a slice of bytes as a Postgres control file.
43 : ///
44 2 : pub fn decode(buf: &[u8]) -> Result<ControlFileData> {
45 : use utils::bin_ser::LeSer;
46 :
47 : // Check that the slice has the expected size. The control file is
48 : // padded with zeros up to a 512 byte sector size, so accept a
49 : // larger size too, so that the caller can just the whole file
50 : // contents without knowing the exact size of the struct.
51 2 : if buf.len() < SIZEOF_CONTROLDATA {
52 0 : bail!("control file is too short");
53 2 : }
54 :
55 : // Compute the expected CRC of the content.
56 2 : let OFFSETOF_CRC = Self::pg_control_crc_offset();
57 2 : let expectedcrc = crc32c::crc32c(&buf[0..OFFSETOF_CRC]);
58 :
59 : // Use serde to deserialize the input as a ControlFileData struct.
60 2 : let controlfile = ControlFileData::des_prefix(buf)?;
61 :
62 : // Check the CRC
63 2 : if expectedcrc != controlfile.crc {
64 0 : bail!(
65 0 : "invalid CRC in control file: expected {:08X}, was {:08X}",
66 : expectedcrc,
67 : controlfile.crc
68 : );
69 2 : }
70 :
71 2 : Ok(controlfile)
72 2 : }
73 :
74 : ///
75 : /// Convert a struct representing a Postgres control file into raw bytes.
76 : ///
77 : /// The CRC is recomputed to match the contents of the fields.
78 0 : pub fn encode(&self) -> Bytes {
79 : use utils::bin_ser::LeSer;
80 :
81 : // Serialize into a new buffer.
82 0 : let b = self.ser().unwrap();
83 :
84 : // Recompute the CRC
85 0 : let OFFSETOF_CRC = Self::pg_control_crc_offset();
86 0 : let newcrc = crc32c::crc32c(&b[0..OFFSETOF_CRC]);
87 :
88 0 : let mut buf = BytesMut::with_capacity(PG_CONTROL_FILE_SIZE as usize);
89 0 : buf.extend_from_slice(&b[0..OFFSETOF_CRC]);
90 0 : buf.extend_from_slice(&newcrc.to_ne_bytes());
91 : // Fill the rest of the control file with zeros.
92 0 : buf.resize(PG_CONTROL_FILE_SIZE as usize, 0);
93 :
94 0 : buf.into()
95 0 : }
96 : }
|