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 : // Someday this can be const when the right compiler features land.
38 12 : fn pg_control_crc_offset() -> usize {
39 12 : memoffset::offset_of!(ControlFileData, crc)
40 12 : }
41 :
42 : ///
43 : /// Interpret a slice of bytes as a Postgres control file.
44 : ///
45 12 : pub fn decode(buf: &[u8]) -> Result<ControlFileData> {
46 : use utils::bin_ser::LeSer;
47 :
48 : // Check that the slice has the expected size. The control file is
49 : // padded with zeros up to a 512 byte sector size, so accept a
50 : // larger size too, so that the caller can just the whole file
51 : // contents without knowing the exact size of the struct.
52 12 : if buf.len() < SIZEOF_CONTROLDATA {
53 0 : bail!("control file is too short");
54 12 : }
55 12 :
56 12 : // Compute the expected CRC of the content.
57 12 : let OFFSETOF_CRC = Self::pg_control_crc_offset();
58 12 : let expectedcrc = crc32c::crc32c(&buf[0..OFFSETOF_CRC]);
59 :
60 : // Use serde to deserialize the input as a ControlFileData struct.
61 12 : let controlfile = ControlFileData::des_prefix(buf)?;
62 :
63 : // Check the CRC
64 12 : if expectedcrc != controlfile.crc {
65 0 : bail!(
66 0 : "invalid CRC in control file: expected {:08X}, was {:08X}",
67 0 : expectedcrc,
68 0 : controlfile.crc
69 0 : );
70 12 : }
71 12 :
72 12 : Ok(controlfile)
73 12 : }
74 :
75 : ///
76 : /// Convert a struct representing a Postgres control file into raw bytes.
77 : ///
78 : /// The CRC is recomputed to match the contents of the fields.
79 0 : pub fn encode(&self) -> Bytes {
80 : use utils::bin_ser::LeSer;
81 :
82 : // Serialize into a new buffer.
83 0 : let b = self.ser().unwrap();
84 0 :
85 0 : // Recompute the CRC
86 0 : let OFFSETOF_CRC = Self::pg_control_crc_offset();
87 0 : let newcrc = crc32c::crc32c(&b[0..OFFSETOF_CRC]);
88 0 :
89 0 : let mut buf = BytesMut::with_capacity(PG_CONTROL_FILE_SIZE as usize);
90 0 : buf.extend_from_slice(&b[0..OFFSETOF_CRC]);
91 0 : buf.extend_from_slice(&newcrc.to_ne_bytes());
92 0 : // Fill the rest of the control file with zeros.
93 0 : buf.resize(PG_CONTROL_FILE_SIZE as usize, 0);
94 0 :
95 0 : buf.into()
96 0 : }
97 : }
|