1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * controldata_utils.c |
4 | * Common code for control data file output. |
5 | * |
6 | * |
7 | * Portions Copyright (c) 1996-2019, 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 | |
27 | #include "access/xlog_internal.h" |
28 | #include "catalog/pg_control.h" |
29 | #include "common/controldata_utils.h" |
30 | #include "common/file_perm.h" |
31 | #ifdef FRONTEND |
32 | #include "common/logging.h" |
33 | #endif |
34 | #include "port/pg_crc32c.h" |
35 | |
36 | #ifndef FRONTEND |
37 | #include "pgstat.h" |
38 | #include "storage/fd.h" |
39 | #endif |
40 | |
41 | /* |
42 | * get_controlfile() |
43 | * |
44 | * Get controlfile values. The result is returned as a palloc'd copy of the |
45 | * control file data. |
46 | * |
47 | * crc_ok_p can be used by the caller to see whether the CRC of the control |
48 | * file data is correct. |
49 | */ |
50 | ControlFileData * |
51 | get_controlfile(const char *DataDir, bool *crc_ok_p) |
52 | { |
53 | ControlFileData *ControlFile; |
54 | int fd; |
55 | char ControlFilePath[MAXPGPATH]; |
56 | pg_crc32c crc; |
57 | int r; |
58 | |
59 | AssertArg(crc_ok_p); |
60 | |
61 | ControlFile = palloc(sizeof(ControlFileData)); |
62 | snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control" , DataDir); |
63 | |
64 | #ifndef FRONTEND |
65 | if ((fd = OpenTransientFile(ControlFilePath, O_RDONLY | PG_BINARY)) == -1) |
66 | ereport(ERROR, |
67 | (errcode_for_file_access(), |
68 | errmsg("could not open file \"%s\" for reading: %m" , |
69 | ControlFilePath))); |
70 | #else |
71 | if ((fd = open(ControlFilePath, O_RDONLY | PG_BINARY, 0)) == -1) |
72 | { |
73 | pg_log_fatal("could not open file \"%s\" for reading: %m" , |
74 | ControlFilePath); |
75 | exit(EXIT_FAILURE); |
76 | } |
77 | #endif |
78 | |
79 | r = read(fd, ControlFile, sizeof(ControlFileData)); |
80 | if (r != sizeof(ControlFileData)) |
81 | { |
82 | if (r < 0) |
83 | #ifndef FRONTEND |
84 | ereport(ERROR, |
85 | (errcode_for_file_access(), |
86 | errmsg("could not read file \"%s\": %m" , ControlFilePath))); |
87 | #else |
88 | { |
89 | pg_log_fatal("could not read file \"%s\": %m" , ControlFilePath); |
90 | exit(EXIT_FAILURE); |
91 | } |
92 | #endif |
93 | else |
94 | #ifndef FRONTEND |
95 | ereport(ERROR, |
96 | (errcode(ERRCODE_DATA_CORRUPTED), |
97 | errmsg("could not read file \"%s\": read %d of %zu" , |
98 | ControlFilePath, r, sizeof(ControlFileData)))); |
99 | #else |
100 | { |
101 | pg_log_fatal("could not read file \"%s\": read %d of %zu" , |
102 | ControlFilePath, r, sizeof(ControlFileData)); |
103 | exit(EXIT_FAILURE); |
104 | } |
105 | #endif |
106 | } |
107 | |
108 | #ifndef FRONTEND |
109 | if (CloseTransientFile(fd)) |
110 | ereport(ERROR, |
111 | (errcode_for_file_access(), |
112 | errmsg("could not close file \"%s\": %m" , |
113 | ControlFilePath))); |
114 | #else |
115 | if (close(fd)) |
116 | { |
117 | pg_log_fatal("could not close file \"%s\": %m" , ControlFilePath); |
118 | exit(EXIT_FAILURE); |
119 | } |
120 | #endif |
121 | |
122 | /* Check the CRC. */ |
123 | INIT_CRC32C(crc); |
124 | COMP_CRC32C(crc, |
125 | (char *) ControlFile, |
126 | offsetof(ControlFileData, crc)); |
127 | FIN_CRC32C(crc); |
128 | |
129 | *crc_ok_p = EQ_CRC32C(crc, ControlFile->crc); |
130 | |
131 | /* Make sure the control file is valid byte order. */ |
132 | if (ControlFile->pg_control_version % 65536 == 0 && |
133 | ControlFile->pg_control_version / 65536 != 0) |
134 | #ifndef FRONTEND |
135 | elog(ERROR, _("byte ordering mismatch" )); |
136 | #else |
137 | pg_log_warning("possible byte ordering mismatch\n" |
138 | "The byte ordering used to store the pg_control file might not match the one\n" |
139 | "used by this program. In that case the results below would be incorrect, and\n" |
140 | "the PostgreSQL installation would be incompatible with this data directory." ); |
141 | #endif |
142 | |
143 | return ControlFile; |
144 | } |
145 | |
146 | /* |
147 | * update_controlfile() |
148 | * |
149 | * Update controlfile values with the contents given by caller. The |
150 | * contents to write are included in "ControlFile". "do_sync" can be |
151 | * optionally used to flush the updated control file. Note that it is up |
152 | * to the caller to properly lock ControlFileLock when calling this |
153 | * routine in the backend. |
154 | */ |
155 | void |
156 | update_controlfile(const char *DataDir, |
157 | ControlFileData *ControlFile, bool do_sync) |
158 | { |
159 | int fd; |
160 | char buffer[PG_CONTROL_FILE_SIZE]; |
161 | char ControlFilePath[MAXPGPATH]; |
162 | |
163 | /* |
164 | * Apply the same static assertions as in backend's WriteControlFile(). |
165 | */ |
166 | StaticAssertStmt(sizeof(ControlFileData) <= PG_CONTROL_MAX_SAFE_SIZE, |
167 | "pg_control is too large for atomic disk writes" ); |
168 | StaticAssertStmt(sizeof(ControlFileData) <= PG_CONTROL_FILE_SIZE, |
169 | "sizeof(ControlFileData) exceeds PG_CONTROL_FILE_SIZE" ); |
170 | |
171 | /* Recalculate CRC of control file */ |
172 | INIT_CRC32C(ControlFile->crc); |
173 | COMP_CRC32C(ControlFile->crc, |
174 | (char *) ControlFile, |
175 | offsetof(ControlFileData, crc)); |
176 | FIN_CRC32C(ControlFile->crc); |
177 | |
178 | /* |
179 | * Write out PG_CONTROL_FILE_SIZE bytes into pg_control by zero-padding |
180 | * the excess over sizeof(ControlFileData), to avoid premature EOF related |
181 | * errors when reading it. |
182 | */ |
183 | memset(buffer, 0, PG_CONTROL_FILE_SIZE); |
184 | memcpy(buffer, ControlFile, sizeof(ControlFileData)); |
185 | |
186 | snprintf(ControlFilePath, sizeof(ControlFilePath), "%s/%s" , DataDir, XLOG_CONTROL_FILE); |
187 | |
188 | #ifndef FRONTEND |
189 | |
190 | /* |
191 | * All errors issue a PANIC, so no need to use OpenTransientFile() and to |
192 | * worry about file descriptor leaks. |
193 | */ |
194 | if ((fd = BasicOpenFile(ControlFilePath, O_RDWR | PG_BINARY)) < 0) |
195 | ereport(PANIC, |
196 | (errcode_for_file_access(), |
197 | errmsg("could not open file \"%s\": %m" , |
198 | ControlFilePath))); |
199 | #else |
200 | if ((fd = open(ControlFilePath, O_WRONLY | PG_BINARY, |
201 | pg_file_create_mode)) == -1) |
202 | { |
203 | pg_log_fatal("could not open file \"%s\": %m" , ControlFilePath); |
204 | exit(EXIT_FAILURE); |
205 | } |
206 | #endif |
207 | |
208 | errno = 0; |
209 | #ifndef FRONTEND |
210 | pgstat_report_wait_start(WAIT_EVENT_CONTROL_FILE_WRITE_UPDATE); |
211 | #endif |
212 | if (write(fd, buffer, PG_CONTROL_FILE_SIZE) != PG_CONTROL_FILE_SIZE) |
213 | { |
214 | /* if write didn't set errno, assume problem is no disk space */ |
215 | if (errno == 0) |
216 | errno = ENOSPC; |
217 | |
218 | #ifndef FRONTEND |
219 | ereport(PANIC, |
220 | (errcode_for_file_access(), |
221 | errmsg("could not write file \"%s\": %m" , |
222 | ControlFilePath))); |
223 | #else |
224 | pg_log_fatal("could not write file \"%s\": %m" , ControlFilePath); |
225 | exit(EXIT_FAILURE); |
226 | #endif |
227 | } |
228 | #ifndef FRONTEND |
229 | pgstat_report_wait_end(); |
230 | #endif |
231 | |
232 | if (do_sync) |
233 | { |
234 | #ifndef FRONTEND |
235 | pgstat_report_wait_start(WAIT_EVENT_CONTROL_FILE_SYNC_UPDATE); |
236 | if (pg_fsync(fd) != 0) |
237 | ereport(PANIC, |
238 | (errcode_for_file_access(), |
239 | errmsg("could not fsync file \"%s\": %m" , |
240 | ControlFilePath))); |
241 | pgstat_report_wait_end(); |
242 | #else |
243 | if (fsync(fd) != 0) |
244 | { |
245 | pg_log_fatal("could not fsync file \"%s\": %m" , ControlFilePath); |
246 | exit(EXIT_FAILURE); |
247 | } |
248 | #endif |
249 | } |
250 | |
251 | if (close(fd) < 0) |
252 | { |
253 | #ifndef FRONTEND |
254 | ereport(PANIC, |
255 | (errcode_for_file_access(), |
256 | errmsg("could not close file \"%s\": %m" , |
257 | ControlFilePath))); |
258 | #else |
259 | pg_log_fatal("could not close file \"%s\": %m" , ControlFilePath); |
260 | exit(EXIT_FAILURE); |
261 | #endif |
262 | } |
263 | } |
264 | |