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