Age Owner Branch data 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_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 *
880 rhaas@postgresql.org 60 :CBC 7 : bbsink_server_new(bbsink *next, char *pathname)
61 : : {
62 : 7 : bbsink_server *sink = palloc0(sizeof(bbsink_server));
63 : :
64 : 7 : *((const bbsink_ops **) &sink->base.bbs_ops) = &bbsink_server_ops;
65 : 7 : sink->pathname = pathname;
66 : 7 : sink->base.bbs_next = next;
67 : :
68 : : /* Replication permission is not sufficient in this case. */
802 69 : 7 : StartTransactionCommand();
743 mail@joeconway.com 70 [ - + ]: 7 : if (!has_privs_of_role(GetUserId(), ROLE_PG_WRITE_SERVER_FILES))
880 rhaas@postgresql.org 71 [ # # ]:UBC 0 : ereport(ERROR,
72 : : (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
73 : : errmsg("permission denied to create backup stored on server"),
74 : : errdetail("Only roles with privileges of the \"%s\" role may create a backup stored on the server.",
75 : : "pg_write_server_files")));
802 rhaas@postgresql.org 76 :CBC 7 : 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 : : */
880 86 [ - + ]: 7 : if (!is_absolute_path(pathname))
880 rhaas@postgresql.org 87 [ # # ]:UBC 0 : ereport(ERROR,
88 : : (errcode(ERRCODE_INVALID_NAME),
89 : : errmsg("relative path not allowed for backup stored on server")));
90 : :
880 rhaas@postgresql.org 91 [ + + - - ]:CBC 7 : switch (pg_check_dir(pathname))
92 : : {
93 : 3 : 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 [ - + ]: 3 : if (MakePGDirectory(pathname) < 0)
880 rhaas@postgresql.org 100 [ # # ]:UBC 0 : ereport(ERROR,
101 : : (errcode_for_file_access(),
102 : : errmsg("could not create directory \"%s\": %m", pathname)));
880 rhaas@postgresql.org 103 :CBC 3 : break;
104 : :
105 : 4 : case 1:
106 : : /* Exists, empty. */
107 : 4 : break;
108 : :
880 rhaas@postgresql.org 109 :UBC 0 : case 2:
110 : : case 3:
111 : : case 4:
112 : : /* Exists, not empty. */
113 [ # # ]: 0 : ereport(ERROR,
114 : : (errcode(ERRCODE_DUPLICATE_FILE),
115 : : errmsg("directory \"%s\" exists but is not empty",
116 : : pathname)));
117 : : break;
118 : :
119 : 0 : default:
120 : : /* Access problem. */
121 [ # # ]: 0 : ereport(ERROR,
122 : : (errcode_for_file_access(),
123 : : errmsg("could not access directory \"%s\": %m",
124 : : pathname)));
125 : : }
126 : :
880 rhaas@postgresql.org 127 :CBC 7 : return &sink->base;
128 : : }
129 : :
130 : : /*
131 : : * Open the correct output file for this archive.
132 : : */
133 : : static void
134 : 7 : bbsink_server_begin_archive(bbsink *sink, const char *archive_name)
135 : : {
136 : 7 : bbsink_server *mysink = (bbsink_server *) sink;
137 : : char *filename;
138 : :
139 [ - + ]: 7 : Assert(mysink->file == 0);
140 [ - + ]: 7 : Assert(mysink->filepos == 0);
141 : :
142 : 7 : filename = psprintf("%s/%s", mysink->pathname, archive_name);
143 : :
144 : 7 : mysink->file = PathNameOpenFile(filename,
145 : : O_CREAT | O_EXCL | O_WRONLY | PG_BINARY);
146 [ - + ]: 7 : if (mysink->file <= 0)
880 rhaas@postgresql.org 147 [ # # ]:UBC 0 : ereport(ERROR,
148 : : (errcode_for_file_access(),
149 : : errmsg("could not create file \"%s\": %m", filename)));
150 : :
880 rhaas@postgresql.org 151 :CBC 7 : pfree(filename);
152 : :
153 : 7 : bbsink_forward_begin_archive(sink, archive_name);
154 : 7 : }
155 : :
156 : : /*
157 : : * Write the data to the output file.
158 : : */
159 : : static void
160 : 7451 : bbsink_server_archive_contents(bbsink *sink, size_t len)
161 : : {
162 : 7451 : bbsink_server *mysink = (bbsink_server *) sink;
163 : : int nbytes;
164 : :
165 : 7451 : nbytes = FileWrite(mysink->file, mysink->base.bbs_buffer, len,
166 : : mysink->filepos, WAIT_EVENT_BASEBACKUP_WRITE);
167 : :
168 [ - + ]: 7451 : if (nbytes != len)
169 : : {
880 rhaas@postgresql.org 170 [ # # ]:UBC 0 : if (nbytes < 0)
171 [ # # ]: 0 : ereport(ERROR,
172 : : (errcode_for_file_access(),
173 : : errmsg("could not write file \"%s\": %m",
174 : : FilePathName(mysink->file)),
175 : : errhint("Check free disk space.")));
176 : : /* short write: complain appropriately */
177 [ # # ]: 0 : ereport(ERROR,
178 : : (errcode(ERRCODE_DISK_FULL),
179 : : errmsg("could not write file \"%s\": wrote only %d of %d bytes at offset %u",
180 : : FilePathName(mysink->file),
181 : : nbytes, (int) len, (unsigned) mysink->filepos),
182 : : errhint("Check free disk space.")));
183 : : }
184 : :
880 rhaas@postgresql.org 185 :CBC 7451 : mysink->filepos += nbytes;
186 : :
187 : 7451 : bbsink_forward_archive_contents(sink, len);
188 : 7451 : }
189 : :
190 : : /*
191 : : * fsync and close the current output file.
192 : : */
193 : : static void
194 : 7 : bbsink_server_end_archive(bbsink *sink)
195 : : {
196 : 7 : 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 [ - + ]: 7 : if (FileSync(mysink->file, WAIT_EVENT_BASEBACKUP_SYNC) < 0)
880 rhaas@postgresql.org 205 [ # # ]:UBC 0 : ereport(ERROR,
206 : : (errcode_for_file_access(),
207 : : errmsg("could not fsync file \"%s\": %m",
208 : : FilePathName(mysink->file))));
209 : :
210 : :
211 : : /* We're done with this file now. */
880 rhaas@postgresql.org 212 :CBC 7 : FileClose(mysink->file);
213 : 7 : mysink->file = 0;
214 : 7 : mysink->filepos = 0;
215 : :
216 : 7 : bbsink_forward_end_archive(sink);
217 : 7 : }
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 : 7 : bbsink_server_begin_manifest(bbsink *sink)
229 : : {
230 : 7 : bbsink_server *mysink = (bbsink_server *) sink;
231 : : char *tmp_filename;
232 : :
233 [ - + ]: 7 : Assert(mysink->file == 0);
234 : :
235 : 7 : tmp_filename = psprintf("%s/backup_manifest.tmp", mysink->pathname);
236 : :
237 : 7 : mysink->file = PathNameOpenFile(tmp_filename,
238 : : O_CREAT | O_EXCL | O_WRONLY | PG_BINARY);
239 [ - + ]: 7 : if (mysink->file <= 0)
880 rhaas@postgresql.org 240 [ # # ]:UBC 0 : ereport(ERROR,
241 : : (errcode_for_file_access(),
242 : : errmsg("could not create file \"%s\": %m", tmp_filename)));
243 : :
880 rhaas@postgresql.org 244 :CBC 7 : pfree(tmp_filename);
245 : :
246 : 7 : bbsink_forward_begin_manifest(sink);
247 : 7 : }
248 : :
249 : : /*
250 : : * Each chunk of manifest data is sent using a CopyData message.
251 : : */
252 : : static void
253 : 35 : bbsink_server_manifest_contents(bbsink *sink, size_t len)
254 : : {
255 : 35 : bbsink_server *mysink = (bbsink_server *) sink;
256 : : int nbytes;
257 : :
258 : 35 : nbytes = FileWrite(mysink->file, mysink->base.bbs_buffer, len,
259 : : mysink->filepos, WAIT_EVENT_BASEBACKUP_WRITE);
260 : :
261 [ - + ]: 35 : if (nbytes != len)
262 : : {
880 rhaas@postgresql.org 263 [ # # ]:UBC 0 : if (nbytes < 0)
264 [ # # ]: 0 : ereport(ERROR,
265 : : (errcode_for_file_access(),
266 : : errmsg("could not write file \"%s\": %m",
267 : : FilePathName(mysink->file)),
268 : : errhint("Check free disk space.")));
269 : : /* short write: complain appropriately */
270 [ # # ]: 0 : ereport(ERROR,
271 : : (errcode(ERRCODE_DISK_FULL),
272 : : errmsg("could not write file \"%s\": wrote only %d of %d bytes at offset %u",
273 : : FilePathName(mysink->file),
274 : : nbytes, (int) len, (unsigned) mysink->filepos),
275 : : errhint("Check free disk space.")));
276 : : }
277 : :
880 rhaas@postgresql.org 278 :CBC 35 : mysink->filepos += nbytes;
279 : :
280 : 35 : bbsink_forward_manifest_contents(sink, len);
281 : 35 : }
282 : :
283 : : /*
284 : : * fsync the backup manifest, close the file, and then rename it into place.
285 : : */
286 : : static void
287 : 7 : bbsink_server_end_manifest(bbsink *sink)
288 : : {
289 : 7 : bbsink_server *mysink = (bbsink_server *) sink;
290 : : char *tmp_filename;
291 : : char *filename;
292 : :
293 : : /* We're done with this file now. */
294 : 7 : FileClose(mysink->file);
295 : 7 : 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 : 7 : tmp_filename = psprintf("%s/backup_manifest.tmp", mysink->pathname);
303 : 7 : filename = psprintf("%s/backup_manifest", mysink->pathname);
304 : 7 : durable_rename(tmp_filename, filename, ERROR);
305 : 7 : pfree(filename);
306 : 7 : pfree(tmp_filename);
307 : :
308 : 7 : bbsink_forward_end_manifest(sink);
309 : 7 : }
|