/src/postgres/src/backend/backup/basebackup_server.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * basebackup_server.c |
4 | | * store basebackup archives on the server |
5 | | * |
6 | | * IDENTIFICATION |
7 | | * src/backend/backup/basebackup_server.c |
8 | | * |
9 | | *------------------------------------------------------------------------- |
10 | | */ |
11 | | #include "postgres.h" |
12 | | |
13 | | #include "access/xact.h" |
14 | | #include "backup/basebackup_sink.h" |
15 | | #include "catalog/pg_authid.h" |
16 | | #include "miscadmin.h" |
17 | | #include "storage/fd.h" |
18 | | #include "utils/acl.h" |
19 | | #include "utils/wait_event.h" |
20 | | |
21 | | typedef struct bbsink_server |
22 | | { |
23 | | /* Common information for all types of sink. */ |
24 | | bbsink base; |
25 | | |
26 | | /* Directory in which backup is to be stored. */ |
27 | | char *pathname; |
28 | | |
29 | | /* Currently open file (or 0 if nothing open). */ |
30 | | File file; |
31 | | |
32 | | /* Current file position. */ |
33 | | off_t filepos; |
34 | | } bbsink_server; |
35 | | |
36 | | static void bbsink_server_begin_archive(bbsink *sink, |
37 | | const char *archive_name); |
38 | | static void bbsink_server_archive_contents(bbsink *sink, size_t len); |
39 | | static void bbsink_server_end_archive(bbsink *sink); |
40 | | static void bbsink_server_begin_manifest(bbsink *sink); |
41 | | static void bbsink_server_manifest_contents(bbsink *sink, size_t len); |
42 | | static void bbsink_server_end_manifest(bbsink *sink); |
43 | | |
44 | | static const bbsink_ops bbsink_server_ops = { |
45 | | .begin_backup = bbsink_forward_begin_backup, |
46 | | .begin_archive = bbsink_server_begin_archive, |
47 | | .archive_contents = bbsink_server_archive_contents, |
48 | | .end_archive = bbsink_server_end_archive, |
49 | | .begin_manifest = bbsink_server_begin_manifest, |
50 | | .manifest_contents = bbsink_server_manifest_contents, |
51 | | .end_manifest = bbsink_server_end_manifest, |
52 | | .end_backup = bbsink_forward_end_backup, |
53 | | .cleanup = bbsink_forward_cleanup |
54 | | }; |
55 | | |
56 | | /* |
57 | | * Create a new 'server' bbsink. |
58 | | */ |
59 | | bbsink * |
60 | | bbsink_server_new(bbsink *next, char *pathname) |
61 | 0 | { |
62 | 0 | bbsink_server *sink = palloc0(sizeof(bbsink_server)); |
63 | |
|
64 | 0 | *((const bbsink_ops **) &sink->base.bbs_ops) = &bbsink_server_ops; |
65 | 0 | sink->pathname = pathname; |
66 | 0 | sink->base.bbs_next = next; |
67 | | |
68 | | /* Replication permission is not sufficient in this case. */ |
69 | 0 | StartTransactionCommand(); |
70 | 0 | if (!has_privs_of_role(GetUserId(), ROLE_PG_WRITE_SERVER_FILES)) |
71 | 0 | ereport(ERROR, |
72 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
73 | 0 | errmsg("permission denied to create backup stored on server"), |
74 | 0 | errdetail("Only roles with privileges of the \"%s\" role may create a backup stored on the server.", |
75 | 0 | "pg_write_server_files"))); |
76 | 0 | CommitTransactionCommand(); |
77 | | |
78 | | /* |
79 | | * It's not a good idea to store your backups in the same directory that |
80 | | * you're backing up. If we allowed a relative path here, that could |
81 | | * easily happen accidentally, so we don't. The user could still |
82 | | * accomplish the same thing by including the absolute path to $PGDATA in |
83 | | * the pathname, but that's likely an intentional bad decision rather than |
84 | | * an accident. |
85 | | */ |
86 | 0 | if (!is_absolute_path(pathname)) |
87 | 0 | ereport(ERROR, |
88 | 0 | (errcode(ERRCODE_INVALID_NAME), |
89 | 0 | errmsg("relative path not allowed for backup stored on server"))); |
90 | | |
91 | 0 | switch (pg_check_dir(pathname)) |
92 | 0 | { |
93 | 0 | case 0: |
94 | | |
95 | | /* |
96 | | * Does not exist, so create it using the same permissions we'd |
97 | | * use for a new subdirectory of the data directory itself. |
98 | | */ |
99 | 0 | if (MakePGDirectory(pathname) < 0) |
100 | 0 | ereport(ERROR, |
101 | 0 | (errcode_for_file_access(), |
102 | 0 | errmsg("could not create directory \"%s\": %m", pathname))); |
103 | 0 | break; |
104 | | |
105 | 0 | case 1: |
106 | | /* Exists, empty. */ |
107 | 0 | break; |
108 | | |
109 | 0 | case 2: |
110 | 0 | case 3: |
111 | 0 | case 4: |
112 | | /* Exists, not empty. */ |
113 | 0 | ereport(ERROR, |
114 | 0 | (errcode(ERRCODE_DUPLICATE_FILE), |
115 | 0 | errmsg("directory \"%s\" exists but is not empty", |
116 | 0 | pathname))); |
117 | 0 | break; |
118 | | |
119 | 0 | default: |
120 | | /* Access problem. */ |
121 | 0 | ereport(ERROR, |
122 | 0 | (errcode_for_file_access(), |
123 | 0 | errmsg("could not access directory \"%s\": %m", |
124 | 0 | pathname))); |
125 | 0 | } |
126 | | |
127 | 0 | return &sink->base; |
128 | 0 | } |
129 | | |
130 | | /* |
131 | | * Open the correct output file for this archive. |
132 | | */ |
133 | | static void |
134 | | bbsink_server_begin_archive(bbsink *sink, const char *archive_name) |
135 | 0 | { |
136 | 0 | bbsink_server *mysink = (bbsink_server *) sink; |
137 | 0 | char *filename; |
138 | |
|
139 | 0 | Assert(mysink->file == 0); |
140 | 0 | Assert(mysink->filepos == 0); |
141 | |
|
142 | 0 | filename = psprintf("%s/%s", mysink->pathname, archive_name); |
143 | |
|
144 | 0 | mysink->file = PathNameOpenFile(filename, |
145 | 0 | O_CREAT | O_EXCL | O_WRONLY | PG_BINARY); |
146 | 0 | if (mysink->file <= 0) |
147 | 0 | ereport(ERROR, |
148 | 0 | (errcode_for_file_access(), |
149 | 0 | errmsg("could not create file \"%s\": %m", filename))); |
150 | | |
151 | 0 | pfree(filename); |
152 | |
|
153 | 0 | bbsink_forward_begin_archive(sink, archive_name); |
154 | 0 | } |
155 | | |
156 | | /* |
157 | | * Write the data to the output file. |
158 | | */ |
159 | | static void |
160 | | bbsink_server_archive_contents(bbsink *sink, size_t len) |
161 | 0 | { |
162 | 0 | bbsink_server *mysink = (bbsink_server *) sink; |
163 | 0 | int nbytes; |
164 | |
|
165 | 0 | nbytes = FileWrite(mysink->file, mysink->base.bbs_buffer, len, |
166 | 0 | mysink->filepos, WAIT_EVENT_BASEBACKUP_WRITE); |
167 | |
|
168 | 0 | if (nbytes != len) |
169 | 0 | { |
170 | 0 | if (nbytes < 0) |
171 | 0 | ereport(ERROR, |
172 | 0 | (errcode_for_file_access(), |
173 | 0 | errmsg("could not write file \"%s\": %m", |
174 | 0 | FilePathName(mysink->file)), |
175 | 0 | errhint("Check free disk space."))); |
176 | | /* short write: complain appropriately */ |
177 | 0 | ereport(ERROR, |
178 | 0 | (errcode(ERRCODE_DISK_FULL), |
179 | 0 | errmsg("could not write file \"%s\": wrote only %d of %d bytes at offset %u", |
180 | 0 | FilePathName(mysink->file), |
181 | 0 | nbytes, (int) len, (unsigned) mysink->filepos), |
182 | 0 | errhint("Check free disk space."))); |
183 | 0 | } |
184 | | |
185 | 0 | mysink->filepos += nbytes; |
186 | |
|
187 | 0 | bbsink_forward_archive_contents(sink, len); |
188 | 0 | } |
189 | | |
190 | | /* |
191 | | * fsync and close the current output file. |
192 | | */ |
193 | | static void |
194 | | bbsink_server_end_archive(bbsink *sink) |
195 | 0 | { |
196 | 0 | bbsink_server *mysink = (bbsink_server *) sink; |
197 | | |
198 | | /* |
199 | | * We intentionally don't use data_sync_elevel here, because the server |
200 | | * shouldn't PANIC just because we can't guarantee that the backup has |
201 | | * been written down to disk. Running recovery won't fix anything in this |
202 | | * case anyway. |
203 | | */ |
204 | 0 | if (FileSync(mysink->file, WAIT_EVENT_BASEBACKUP_SYNC) < 0) |
205 | 0 | ereport(ERROR, |
206 | 0 | (errcode_for_file_access(), |
207 | 0 | errmsg("could not fsync file \"%s\": %m", |
208 | 0 | FilePathName(mysink->file)))); |
209 | | |
210 | | |
211 | | /* We're done with this file now. */ |
212 | 0 | FileClose(mysink->file); |
213 | 0 | mysink->file = 0; |
214 | 0 | mysink->filepos = 0; |
215 | |
|
216 | 0 | bbsink_forward_end_archive(sink); |
217 | 0 | } |
218 | | |
219 | | /* |
220 | | * Open the output file to which we will write the manifest. |
221 | | * |
222 | | * Just like pg_basebackup, we write the manifest first under a temporary |
223 | | * name and then rename it into place after fsync. That way, if the manifest |
224 | | * is there and under the correct name, the user can be sure that the backup |
225 | | * completed. |
226 | | */ |
227 | | static void |
228 | | bbsink_server_begin_manifest(bbsink *sink) |
229 | 0 | { |
230 | 0 | bbsink_server *mysink = (bbsink_server *) sink; |
231 | 0 | char *tmp_filename; |
232 | |
|
233 | 0 | Assert(mysink->file == 0); |
234 | |
|
235 | 0 | tmp_filename = psprintf("%s/backup_manifest.tmp", mysink->pathname); |
236 | |
|
237 | 0 | mysink->file = PathNameOpenFile(tmp_filename, |
238 | 0 | O_CREAT | O_EXCL | O_WRONLY | PG_BINARY); |
239 | 0 | if (mysink->file <= 0) |
240 | 0 | ereport(ERROR, |
241 | 0 | (errcode_for_file_access(), |
242 | 0 | errmsg("could not create file \"%s\": %m", tmp_filename))); |
243 | | |
244 | 0 | pfree(tmp_filename); |
245 | |
|
246 | 0 | bbsink_forward_begin_manifest(sink); |
247 | 0 | } |
248 | | |
249 | | /* |
250 | | * Each chunk of manifest data is sent using a CopyData message. |
251 | | */ |
252 | | static void |
253 | | bbsink_server_manifest_contents(bbsink *sink, size_t len) |
254 | 0 | { |
255 | 0 | bbsink_server *mysink = (bbsink_server *) sink; |
256 | 0 | int nbytes; |
257 | |
|
258 | 0 | nbytes = FileWrite(mysink->file, mysink->base.bbs_buffer, len, |
259 | 0 | mysink->filepos, WAIT_EVENT_BASEBACKUP_WRITE); |
260 | |
|
261 | 0 | if (nbytes != len) |
262 | 0 | { |
263 | 0 | if (nbytes < 0) |
264 | 0 | ereport(ERROR, |
265 | 0 | (errcode_for_file_access(), |
266 | 0 | errmsg("could not write file \"%s\": %m", |
267 | 0 | FilePathName(mysink->file)), |
268 | 0 | errhint("Check free disk space."))); |
269 | | /* short write: complain appropriately */ |
270 | 0 | ereport(ERROR, |
271 | 0 | (errcode(ERRCODE_DISK_FULL), |
272 | 0 | errmsg("could not write file \"%s\": wrote only %d of %d bytes at offset %u", |
273 | 0 | FilePathName(mysink->file), |
274 | 0 | nbytes, (int) len, (unsigned) mysink->filepos), |
275 | 0 | errhint("Check free disk space."))); |
276 | 0 | } |
277 | | |
278 | 0 | mysink->filepos += nbytes; |
279 | |
|
280 | 0 | bbsink_forward_manifest_contents(sink, len); |
281 | 0 | } |
282 | | |
283 | | /* |
284 | | * fsync the backup manifest, close the file, and then rename it into place. |
285 | | */ |
286 | | static void |
287 | | bbsink_server_end_manifest(bbsink *sink) |
288 | 0 | { |
289 | 0 | bbsink_server *mysink = (bbsink_server *) sink; |
290 | 0 | char *tmp_filename; |
291 | 0 | char *filename; |
292 | | |
293 | | /* We're done with this file now. */ |
294 | 0 | FileClose(mysink->file); |
295 | 0 | mysink->file = 0; |
296 | | |
297 | | /* |
298 | | * Rename it into place. This also fsyncs the temporary file, so we don't |
299 | | * need to do that here. We don't use data_sync_elevel here for the same |
300 | | * reasons as in bbsink_server_end_archive. |
301 | | */ |
302 | 0 | tmp_filename = psprintf("%s/backup_manifest.tmp", mysink->pathname); |
303 | 0 | filename = psprintf("%s/backup_manifest", mysink->pathname); |
304 | 0 | durable_rename(tmp_filename, filename, ERROR); |
305 | 0 | pfree(filename); |
306 | 0 | pfree(tmp_filename); |
307 | |
|
308 | 0 | bbsink_forward_end_manifest(sink); |
309 | 0 | } |