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 *
60 10 : bbsink_server_new(bbsink *next, char *pathname)
61 : {
62 10 : bbsink_server *sink = palloc0(sizeof(bbsink_server));
63 :
64 10 : *((const bbsink_ops **) &sink->base.bbs_ops) = &bbsink_server_ops;
65 10 : sink->pathname = pathname;
66 10 : sink->base.bbs_next = next;
67 :
68 : /* Replication permission is not sufficient in this case. */
69 10 : StartTransactionCommand();
70 10 : if (!has_privs_of_role(GetUserId(), ROLE_PG_WRITE_SERVER_FILES))
71 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")));
76 10 : 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 10 : if (!is_absolute_path(pathname))
87 0 : ereport(ERROR,
88 : (errcode(ERRCODE_INVALID_NAME),
89 : errmsg("relative path not allowed for backup stored on server")));
90 :
91 10 : switch (pg_check_dir(pathname))
92 : {
93 10 : 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 10 : if (MakePGDirectory(pathname) < 0)
100 0 : ereport(ERROR,
101 : (errcode_for_file_access(),
102 : errmsg("could not create directory \"%s\": %m", pathname)));
103 10 : break;
104 :
105 0 : case 1:
106 : /* Exists, empty. */
107 0 : break;
108 :
109 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 :
127 10 : return &sink->base;
128 : }
129 :
130 : /*
131 : * Open the correct output file for this archive.
132 : */
133 : static void
134 16 : bbsink_server_begin_archive(bbsink *sink, const char *archive_name)
135 : {
136 16 : bbsink_server *mysink = (bbsink_server *) sink;
137 : char *filename;
138 :
139 : Assert(mysink->file == 0);
140 : Assert(mysink->filepos == 0);
141 :
142 16 : filename = psprintf("%s/%s", mysink->pathname, archive_name);
143 :
144 16 : mysink->file = PathNameOpenFile(filename,
145 : O_CREAT | O_EXCL | O_WRONLY | PG_BINARY);
146 16 : if (mysink->file <= 0)
147 0 : ereport(ERROR,
148 : (errcode_for_file_access(),
149 : errmsg("could not create file \"%s\": %m", filename)));
150 :
151 16 : pfree(filename);
152 :
153 16 : bbsink_forward_begin_archive(sink, archive_name);
154 16 : }
155 :
156 : /*
157 : * Write the data to the output file.
158 : */
159 : static void
160 14590 : bbsink_server_archive_contents(bbsink *sink, size_t len)
161 : {
162 14590 : bbsink_server *mysink = (bbsink_server *) sink;
163 : int nbytes;
164 :
165 14590 : nbytes = FileWrite(mysink->file, mysink->base.bbs_buffer, len,
166 : mysink->filepos, WAIT_EVENT_BASEBACKUP_WRITE);
167 :
168 14590 : if (nbytes != len)
169 : {
170 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 :
185 14590 : mysink->filepos += nbytes;
186 :
187 14590 : bbsink_forward_archive_contents(sink, len);
188 14590 : }
189 :
190 : /*
191 : * fsync and close the current output file.
192 : */
193 : static void
194 16 : bbsink_server_end_archive(bbsink *sink)
195 : {
196 16 : 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 16 : if (FileSync(mysink->file, WAIT_EVENT_BASEBACKUP_SYNC) < 0)
205 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. */
212 16 : FileClose(mysink->file);
213 16 : mysink->file = 0;
214 16 : mysink->filepos = 0;
215 :
216 16 : bbsink_forward_end_archive(sink);
217 16 : }
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 10 : bbsink_server_begin_manifest(bbsink *sink)
229 : {
230 10 : bbsink_server *mysink = (bbsink_server *) sink;
231 : char *tmp_filename;
232 :
233 : Assert(mysink->file == 0);
234 :
235 10 : tmp_filename = psprintf("%s/backup_manifest.tmp", mysink->pathname);
236 :
237 10 : mysink->file = PathNameOpenFile(tmp_filename,
238 : O_CREAT | O_EXCL | O_WRONLY | PG_BINARY);
239 10 : if (mysink->file <= 0)
240 0 : ereport(ERROR,
241 : (errcode_for_file_access(),
242 : errmsg("could not create file \"%s\": %m", tmp_filename)));
243 :
244 10 : pfree(tmp_filename);
245 :
246 10 : bbsink_forward_begin_manifest(sink);
247 10 : }
248 :
249 : /*
250 : * Each chunk of manifest data is sent using a CopyData message.
251 : */
252 : static void
253 50 : bbsink_server_manifest_contents(bbsink *sink, size_t len)
254 : {
255 50 : bbsink_server *mysink = (bbsink_server *) sink;
256 : int nbytes;
257 :
258 50 : nbytes = FileWrite(mysink->file, mysink->base.bbs_buffer, len,
259 : mysink->filepos, WAIT_EVENT_BASEBACKUP_WRITE);
260 :
261 50 : if (nbytes != len)
262 : {
263 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 :
278 50 : mysink->filepos += nbytes;
279 :
280 50 : bbsink_forward_manifest_contents(sink, len);
281 50 : }
282 :
283 : /*
284 : * fsync the backup manifest, close the file, and then rename it into place.
285 : */
286 : static void
287 10 : bbsink_server_end_manifest(bbsink *sink)
288 : {
289 10 : bbsink_server *mysink = (bbsink_server *) sink;
290 : char *tmp_filename;
291 : char *filename;
292 :
293 : /* We're done with this file now. */
294 10 : FileClose(mysink->file);
295 10 : 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 10 : tmp_filename = psprintf("%s/backup_manifest.tmp", mysink->pathname);
303 10 : filename = psprintf("%s/backup_manifest", mysink->pathname);
304 10 : durable_rename(tmp_filename, filename, ERROR);
305 10 : pfree(filename);
306 10 : pfree(tmp_filename);
307 :
308 10 : bbsink_forward_end_manifest(sink);
309 10 : }
|