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-2023, 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 *
52 390 : get_controlfile(const char *DataDir, bool *crc_ok_p)
53 : {
54 : ControlFileData *ControlFile;
55 : int fd;
56 : char ControlFilePath[MAXPGPATH];
57 : pg_crc32c crc;
58 : int r;
59 : #ifdef FRONTEND
60 : pg_crc32c last_crc;
61 360 : int retries = 0;
62 : #endif
63 :
64 : Assert(crc_ok_p);
65 :
66 390 : ControlFile = palloc_object(ControlFileData);
67 390 : snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", DataDir);
68 :
69 : #ifdef FRONTEND
70 360 : INIT_CRC32C(last_crc);
71 :
72 362 : retry:
73 : #endif
74 :
75 : #ifndef FRONTEND
76 30 : if ((fd = OpenTransientFile(ControlFilePath, O_RDONLY | PG_BINARY)) == -1)
77 0 : ereport(ERROR,
78 : (errcode_for_file_access(),
79 : errmsg("could not open file \"%s\" for reading: %m",
80 : ControlFilePath)));
81 : #else
82 362 : if ((fd = open(ControlFilePath, O_RDONLY | PG_BINARY, 0)) == -1)
83 2 : pg_fatal("could not open file \"%s\" for reading: %m",
84 : ControlFilePath);
85 : #endif
86 :
87 390 : r = read(fd, ControlFile, sizeof(ControlFileData));
88 390 : if (r != sizeof(ControlFileData))
89 : {
90 0 : if (r < 0)
91 : #ifndef FRONTEND
92 0 : ereport(ERROR,
93 : (errcode_for_file_access(),
94 : errmsg("could not read file \"%s\": %m", ControlFilePath)));
95 : #else
96 0 : pg_fatal("could not read file \"%s\": %m", ControlFilePath);
97 : #endif
98 : else
99 : #ifndef FRONTEND
100 0 : ereport(ERROR,
101 : (errcode(ERRCODE_DATA_CORRUPTED),
102 : errmsg("could not read file \"%s\": read %d of %zu",
103 : ControlFilePath, r, sizeof(ControlFileData))));
104 : #else
105 0 : pg_fatal("could not read file \"%s\": read %d of %zu",
106 : ControlFilePath, r, sizeof(ControlFileData));
107 : #endif
108 : }
109 :
110 : #ifndef FRONTEND
111 30 : if (CloseTransientFile(fd) != 0)
112 0 : ereport(ERROR,
113 : (errcode_for_file_access(),
114 : errmsg("could not close file \"%s\": %m",
115 : ControlFilePath)));
116 : #else
117 360 : if (close(fd) != 0)
118 0 : pg_fatal("could not close file \"%s\": %m", ControlFilePath);
119 : #endif
120 :
121 : /* Check the CRC. */
122 390 : INIT_CRC32C(crc);
123 390 : COMP_CRC32C(crc,
124 : (char *) ControlFile,
125 : offsetof(ControlFileData, crc));
126 390 : FIN_CRC32C(crc);
127 :
128 390 : *crc_ok_p = EQ_CRC32C(crc, ControlFile->crc);
129 :
130 : #ifdef FRONTEND
131 :
132 : /*
133 : * If the server was writing at the same time, it is possible that we read
134 : * partially updated contents on some systems. If the CRC doesn't match,
135 : * retry a limited number of times until we compute the same bad CRC twice
136 : * in a row with a short sleep in between. Then the failure is unlikely
137 : * to be due to a concurrent write.
138 : */
139 360 : if (!*crc_ok_p &&
140 4 : (retries == 0 || !EQ_CRC32C(crc, last_crc)) &&
141 : retries < 10)
142 : {
143 2 : retries++;
144 2 : last_crc = crc;
145 2 : pg_usleep(10000);
146 2 : goto retry;
147 : }
148 : #endif
149 :
150 : /* Make sure the control file is valid byte order. */
151 388 : if (ControlFile->pg_control_version % 65536 == 0 &&
152 2 : ControlFile->pg_control_version / 65536 != 0)
153 : #ifndef FRONTEND
154 0 : elog(ERROR, _("byte ordering mismatch"));
155 : #else
156 0 : pg_log_warning("possible byte ordering mismatch\n"
157 : "The byte ordering used to store the pg_control file might not match the one\n"
158 : "used by this program. In that case the results below would be incorrect, and\n"
159 : "the PostgreSQL installation would be incompatible with this data directory.");
160 : #endif
161 :
162 388 : return ControlFile;
163 : }
164 :
165 : /*
166 : * update_controlfile()
167 : *
168 : * Update controlfile values with the contents given by caller. The
169 : * contents to write are included in "ControlFile". "do_sync" can be
170 : * optionally used to flush the updated control file. Note that it is up
171 : * to the caller to properly lock ControlFileLock when calling this
172 : * routine in the backend.
173 : */
174 : void
175 16028 : update_controlfile(const char *DataDir,
176 : ControlFileData *ControlFile, bool do_sync)
177 : {
178 : int fd;
179 : char buffer[PG_CONTROL_FILE_SIZE];
180 : char ControlFilePath[MAXPGPATH];
181 :
182 : /* Update timestamp */
183 16028 : ControlFile->time = (pg_time_t) time(NULL);
184 :
185 : /* Recalculate CRC of control file */
186 16028 : INIT_CRC32C(ControlFile->crc);
187 16028 : COMP_CRC32C(ControlFile->crc,
188 : (char *) ControlFile,
189 : offsetof(ControlFileData, crc));
190 16028 : FIN_CRC32C(ControlFile->crc);
191 :
192 : /*
193 : * Write out PG_CONTROL_FILE_SIZE bytes into pg_control by zero-padding
194 : * the excess over sizeof(ControlFileData), to avoid premature EOF related
195 : * errors when reading it.
196 : */
197 16028 : memset(buffer, 0, PG_CONTROL_FILE_SIZE);
198 16028 : memcpy(buffer, ControlFile, sizeof(ControlFileData));
199 :
200 16028 : snprintf(ControlFilePath, sizeof(ControlFilePath), "%s/%s", DataDir, XLOG_CONTROL_FILE);
201 :
202 : #ifndef FRONTEND
203 :
204 : /*
205 : * All errors issue a PANIC, so no need to use OpenTransientFile() and to
206 : * worry about file descriptor leaks.
207 : */
208 15962 : if ((fd = BasicOpenFile(ControlFilePath, O_RDWR | PG_BINARY)) < 0)
209 0 : ereport(PANIC,
210 : (errcode_for_file_access(),
211 : errmsg("could not open file \"%s\": %m",
212 : ControlFilePath)));
213 : #else
214 66 : if ((fd = open(ControlFilePath, O_WRONLY | PG_BINARY,
215 : pg_file_create_mode)) == -1)
216 0 : pg_fatal("could not open file \"%s\": %m", ControlFilePath);
217 : #endif
218 :
219 16028 : errno = 0;
220 : #ifndef FRONTEND
221 15962 : pgstat_report_wait_start(WAIT_EVENT_CONTROL_FILE_WRITE_UPDATE);
222 : #endif
223 16028 : if (write(fd, buffer, PG_CONTROL_FILE_SIZE) != PG_CONTROL_FILE_SIZE)
224 : {
225 : /* if write didn't set errno, assume problem is no disk space */
226 0 : if (errno == 0)
227 0 : errno = ENOSPC;
228 :
229 : #ifndef FRONTEND
230 0 : ereport(PANIC,
231 : (errcode_for_file_access(),
232 : errmsg("could not write file \"%s\": %m",
233 : ControlFilePath)));
234 : #else
235 0 : pg_fatal("could not write file \"%s\": %m", ControlFilePath);
236 : #endif
237 : }
238 : #ifndef FRONTEND
239 15962 : pgstat_report_wait_end();
240 : #endif
241 :
242 16028 : if (do_sync)
243 : {
244 : #ifndef FRONTEND
245 15962 : pgstat_report_wait_start(WAIT_EVENT_CONTROL_FILE_SYNC_UPDATE);
246 15962 : if (pg_fsync(fd) != 0)
247 0 : ereport(PANIC,
248 : (errcode_for_file_access(),
249 : errmsg("could not fsync file \"%s\": %m",
250 : ControlFilePath)));
251 15962 : pgstat_report_wait_end();
252 : #else
253 42 : if (fsync(fd) != 0)
254 0 : pg_fatal("could not fsync file \"%s\": %m", ControlFilePath);
255 : #endif
256 : }
257 :
258 16028 : if (close(fd) != 0)
259 : {
260 : #ifndef FRONTEND
261 0 : ereport(PANIC,
262 : (errcode_for_file_access(),
263 : errmsg("could not close file \"%s\": %m",
264 : ControlFilePath)));
265 : #else
266 0 : pg_fatal("could not close file \"%s\": %m", ControlFilePath);
267 : #endif
268 : }
269 16028 : }
|