Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * controldata_utils.c
4 : : * Common code for control data file output.
5 : : *
6 : : *
7 : : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
8 : : * Portions Copyright (c) 1994, Regents of the University of California
9 : : *
10 : : *
11 : : * IDENTIFICATION
12 : : * src/common/controldata_utils.c
13 : : *
14 : : *-------------------------------------------------------------------------
15 : : */
16 : :
17 : : #ifndef FRONTEND
18 : : #include "postgres.h"
19 : : #else
20 : : #include "postgres_fe.h"
21 : : #endif
22 : :
23 : : #include <unistd.h>
24 : : #include <sys/stat.h>
25 : : #include <fcntl.h>
26 : : #include <time.h>
27 : :
28 : : #include "access/xlog_internal.h"
29 : : #include "catalog/pg_control.h"
30 : : #include "common/controldata_utils.h"
31 : : #include "common/file_perm.h"
32 : : #ifdef FRONTEND
33 : : #include "common/logging.h"
34 : : #endif
35 : : #include "port/pg_crc32c.h"
36 : :
37 : : #ifndef FRONTEND
38 : : #include "pgstat.h"
39 : : #include "storage/fd.h"
40 : : #endif
41 : :
42 : : /*
43 : : * get_controlfile()
44 : : *
45 : : * Get controlfile values. The result is returned as a palloc'd copy of the
46 : : * control file data.
47 : : *
48 : : * crc_ok_p can be used by the caller to see whether the CRC of the control
49 : : * file data is correct.
50 : : */
51 : : ControlFileData *
1840 peter@eisentraut.org 52 :CBC 238 : get_controlfile(const char *DataDir, bool *crc_ok_p)
53 : : {
54 : : char ControlFilePath[MAXPGPATH];
55 : :
32 rhaas@postgresql.org 56 :GNC 238 : snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir);
57 : :
58 : 238 : return get_controlfile_by_exact_path(ControlFilePath, crc_ok_p);
59 : : }
60 : :
61 : : /*
62 : : * get_controlfile_by_exact_path()
63 : : *
64 : : * As above, but the caller specifies the path to the control file itself,
65 : : * rather than the path to the data directory.
66 : : */
67 : : ControlFileData *
68 : 340 : get_controlfile_by_exact_path(const char *ControlFilePath, bool *crc_ok_p)
69 : : {
70 : : ControlFileData *ControlFile;
71 : : int fd;
72 : : pg_crc32c crc;
73 : : int r;
74 : : #ifdef FRONTEND
75 : : pg_crc32c last_crc;
181 tmunro@postgresql.or 76 :CBC 314 : int retries = 0;
77 : : #endif
78 : :
534 peter@eisentraut.org 79 [ - + ]: 340 : Assert(crc_ok_p);
80 : :
580 81 : 340 : ControlFile = palloc_object(ControlFileData);
82 : :
83 : : #ifdef FRONTEND
181 tmunro@postgresql.or 84 : 314 : INIT_CRC32C(last_crc);
85 : :
86 : 315 : retry:
87 : : #endif
88 : :
89 : : #ifndef FRONTEND
1872 mail@joeconway.com 90 [ - + ]: 26 : if ((fd = OpenTransientFile(ControlFilePath, O_RDONLY | PG_BINARY)) == -1)
2960 mail@joeconway.com 91 [ # # ]:UBC 0 : ereport(ERROR,
92 : : (errcode_for_file_access(),
93 : : errmsg("could not open file \"%s\" for reading: %m",
94 : : ControlFilePath)));
95 : : #else
1872 mail@joeconway.com 96 [ + + ]:CBC 315 : if ((fd = open(ControlFilePath, O_RDONLY | PG_BINARY, 0)) == -1)
737 tgl@sss.pgh.pa.us 97 : 1 : pg_fatal("could not open file \"%s\" for reading: %m",
98 : : ControlFilePath);
99 : : #endif
100 : :
2158 magnus@hagander.net 101 : 340 : r = read(fd, ControlFile, sizeof(ControlFileData));
102 [ - + ]: 340 : if (r != sizeof(ControlFileData))
103 : : {
2158 magnus@hagander.net 104 [ # # ]:UBC 0 : if (r < 0)
105 : : #ifndef FRONTEND
106 [ # # ]: 0 : ereport(ERROR,
107 : : (errcode_for_file_access(),
108 : : errmsg("could not read file \"%s\": %m", ControlFilePath)));
109 : : #else
737 tgl@sss.pgh.pa.us 110 : 0 : pg_fatal("could not read file \"%s\": %m", ControlFilePath);
111 : : #endif
112 : : else
113 : : #ifndef FRONTEND
2158 magnus@hagander.net 114 [ # # ]: 0 : ereport(ERROR,
115 : : (errcode(ERRCODE_DATA_CORRUPTED),
116 : : errmsg("could not read file \"%s\": read %d of %zu",
117 : : ControlFilePath, r, sizeof(ControlFileData))));
118 : : #else
737 tgl@sss.pgh.pa.us 119 : 0 : pg_fatal("could not read file \"%s\": read %d of %zu",
120 : : ControlFilePath, r, sizeof(ControlFileData));
121 : : #endif
122 : : }
123 : :
124 : : #ifndef FRONTEND
1744 peter@eisentraut.org 125 [ - + ]:CBC 26 : if (CloseTransientFile(fd) != 0)
1863 michael@paquier.xyz 126 [ # # ]:UBC 0 : ereport(ERROR,
127 : : (errcode_for_file_access(),
128 : : errmsg("could not close file \"%s\": %m",
129 : : ControlFilePath)));
130 : : #else
1744 peter@eisentraut.org 131 [ - + ]:CBC 314 : if (close(fd) != 0)
737 tgl@sss.pgh.pa.us 132 :UBC 0 : pg_fatal("could not close file \"%s\": %m", ControlFilePath);
133 : : #endif
134 : :
135 : : /* Check the CRC. */
2962 mail@joeconway.com 136 :CBC 340 : INIT_CRC32C(crc);
137 : 340 : COMP_CRC32C(crc,
138 : : (char *) ControlFile,
139 : : offsetof(ControlFileData, crc));
140 : 340 : FIN_CRC32C(crc);
141 : :
2755 peter_e@gmx.net 142 : 340 : *crc_ok_p = EQ_CRC32C(crc, ControlFile->crc);
143 : :
144 : : #ifdef FRONTEND
145 : :
146 : : /*
147 : : * If the server was writing at the same time, it is possible that we read
148 : : * partially updated contents on some systems. If the CRC doesn't match,
149 : : * retry a limited number of times until we compute the same bad CRC twice
150 : : * in a row with a short sleep in between. Then the failure is unlikely
151 : : * to be due to a concurrent write.
152 : : */
181 tmunro@postgresql.or 153 [ + + + + ]: 314 : if (!*crc_ok_p &&
154 [ - + + - ]: 2 : (retries == 0 || !EQ_CRC32C(crc, last_crc)) &&
155 : : retries < 10)
156 : : {
157 : 1 : retries++;
158 : 1 : last_crc = crc;
159 : 1 : pg_usleep(10000);
160 : 1 : goto retry;
161 : : }
162 : : #endif
163 : :
164 : : /* Make sure the control file is valid byte order. */
2962 mail@joeconway.com 165 [ + + ]: 339 : if (ControlFile->pg_control_version % 65536 == 0 &&
166 [ - + ]: 1 : ControlFile->pg_control_version / 65536 != 0)
167 : : #ifndef FRONTEND
2962 mail@joeconway.com 168 [ # # ]:UBC 0 : elog(ERROR, _("byte ordering mismatch"));
169 : : #else
1840 peter@eisentraut.org 170 : 0 : pg_log_warning("possible byte ordering mismatch\n"
171 : : "The byte ordering used to store the pg_control file might not match the one\n"
172 : : "used by this program. In that case the results below would be incorrect, and\n"
173 : : "the PostgreSQL installation would be incompatible with this data directory.");
174 : : #endif
175 : :
2962 mail@joeconway.com 176 :CBC 339 : return ControlFile;
177 : : }
178 : :
179 : : /*
180 : : * update_controlfile()
181 : : *
182 : : * Update controlfile values with the contents given by caller. The
183 : : * contents to write are included in "ControlFile". "do_sync" can be
184 : : * optionally used to flush the updated control file. Note that it is up
185 : : * to the caller to properly lock ControlFileLock when calling this
186 : : * routine in the backend.
187 : : */
188 : : void
1840 peter@eisentraut.org 189 : 10408 : update_controlfile(const char *DataDir,
190 : : ControlFileData *ControlFile, bool do_sync)
191 : : {
192 : : int fd;
193 : : char buffer[PG_CONTROL_FILE_SIZE];
194 : : char ControlFilePath[MAXPGPATH];
195 : :
196 : : /* Update timestamp */
867 michael@paquier.xyz 197 : 10408 : ControlFile->time = (pg_time_t) time(NULL);
198 : :
199 : : /* Recalculate CRC of control file */
1860 200 : 10408 : INIT_CRC32C(ControlFile->crc);
201 : 10408 : COMP_CRC32C(ControlFile->crc,
202 : : (char *) ControlFile,
203 : : offsetof(ControlFileData, crc));
204 : 10408 : FIN_CRC32C(ControlFile->crc);
205 : :
206 : : /*
207 : : * Write out PG_CONTROL_FILE_SIZE bytes into pg_control by zero-padding
208 : : * the excess over sizeof(ControlFileData), to avoid premature EOF related
209 : : * errors when reading it.
210 : : */
211 : 10408 : memset(buffer, 0, PG_CONTROL_FILE_SIZE);
212 : 10408 : memcpy(buffer, ControlFile, sizeof(ControlFileData));
213 : :
214 : 10408 : snprintf(ControlFilePath, sizeof(ControlFilePath), "%s/%s", DataDir, XLOG_CONTROL_FILE);
215 : :
216 : : #ifndef FRONTEND
217 : :
218 : : /*
219 : : * All errors issue a PANIC, so no need to use OpenTransientFile() and to
220 : : * worry about file descriptor leaks.
221 : : */
1854 222 [ - + ]: 10366 : if ((fd = BasicOpenFile(ControlFilePath, O_RDWR | PG_BINARY)) < 0)
1860 michael@paquier.xyz 223 [ # # ]:UBC 0 : ereport(PANIC,
224 : : (errcode_for_file_access(),
225 : : errmsg("could not open file \"%s\": %m",
226 : : ControlFilePath)));
227 : : #else
1860 michael@paquier.xyz 228 [ - + ]:CBC 42 : if ((fd = open(ControlFilePath, O_WRONLY | PG_BINARY,
229 : : pg_file_create_mode)) == -1)
737 tgl@sss.pgh.pa.us 230 :UBC 0 : pg_fatal("could not open file \"%s\": %m", ControlFilePath);
231 : : #endif
232 : :
1860 michael@paquier.xyz 233 :CBC 10408 : errno = 0;
234 : : #ifndef FRONTEND
1854 235 : 10366 : pgstat_report_wait_start(WAIT_EVENT_CONTROL_FILE_WRITE_UPDATE);
236 : : #endif
1860 237 [ - + ]: 10408 : if (write(fd, buffer, PG_CONTROL_FILE_SIZE) != PG_CONTROL_FILE_SIZE)
238 : : {
239 : : /* if write didn't set errno, assume problem is no disk space */
1860 michael@paquier.xyz 240 [ # # ]:UBC 0 : if (errno == 0)
241 : 0 : errno = ENOSPC;
242 : :
243 : : #ifndef FRONTEND
244 [ # # ]: 0 : ereport(PANIC,
245 : : (errcode_for_file_access(),
246 : : errmsg("could not write file \"%s\": %m",
247 : : ControlFilePath)));
248 : : #else
737 tgl@sss.pgh.pa.us 249 : 0 : pg_fatal("could not write file \"%s\": %m", ControlFilePath);
250 : : #endif
251 : : }
252 : : #ifndef FRONTEND
1854 michael@paquier.xyz 253 :CBC 10366 : pgstat_report_wait_end();
254 : : #endif
255 : :
256 [ + + ]: 10408 : if (do_sync)
257 : : {
258 : : #ifndef FRONTEND
259 : 10366 : pgstat_report_wait_start(WAIT_EVENT_CONTROL_FILE_SYNC_UPDATE);
260 [ - + ]: 10366 : if (pg_fsync(fd) != 0)
1854 michael@paquier.xyz 261 [ # # ]:UBC 0 : ereport(PANIC,
262 : : (errcode_for_file_access(),
263 : : errmsg("could not fsync file \"%s\": %m",
264 : : ControlFilePath)));
1854 michael@paquier.xyz 265 :CBC 10366 : pgstat_report_wait_end();
266 : : #else
267 [ - + ]: 30 : if (fsync(fd) != 0)
737 tgl@sss.pgh.pa.us 268 :UBC 0 : pg_fatal("could not fsync file \"%s\": %m", ControlFilePath);
269 : : #endif
270 : : }
271 : :
1744 peter@eisentraut.org 272 [ - + ]:CBC 10408 : if (close(fd) != 0)
273 : : {
274 : : #ifndef FRONTEND
1860 michael@paquier.xyz 275 [ # # ]:UBC 0 : ereport(PANIC,
276 : : (errcode_for_file_access(),
277 : : errmsg("could not close file \"%s\": %m",
278 : : ControlFilePath)));
279 : : #else
737 tgl@sss.pgh.pa.us 280 : 0 : pg_fatal("could not close file \"%s\": %m", ControlFilePath);
281 : : #endif
282 : : }
1860 michael@paquier.xyz 283 :CBC 10408 : }
|