postgresql/src/backend/backup/basebackup_server.c

310 lines
8.4 KiB
C

/*-------------------------------------------------------------------------
*
* basebackup_server.c
* store basebackup archives on the server
*
* IDENTIFICATION
* src/backend/backup/basebackup_server.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/xact.h"
#include "backup/basebackup_sink.h"
#include "catalog/pg_authid.h"
#include "miscadmin.h"
#include "storage/fd.h"
#include "utils/acl.h"
#include "utils/wait_event.h"
typedef struct bbsink_server
{
/* Common information for all types of sink. */
bbsink base;
/* Directory in which backup is to be stored. */
char *pathname;
/* Currently open file (or 0 if nothing open). */
File file;
/* Current file position. */
off_t filepos;
} bbsink_server;
static void bbsink_server_begin_archive(bbsink *sink,
const char *archive_name);
static void bbsink_server_archive_contents(bbsink *sink, size_t len);
static void bbsink_server_end_archive(bbsink *sink);
static void bbsink_server_begin_manifest(bbsink *sink);
static void bbsink_server_manifest_contents(bbsink *sink, size_t len);
static void bbsink_server_end_manifest(bbsink *sink);
static const bbsink_ops bbsink_server_ops = {
.begin_backup = bbsink_forward_begin_backup,
.begin_archive = bbsink_server_begin_archive,
.archive_contents = bbsink_server_archive_contents,
.end_archive = bbsink_server_end_archive,
.begin_manifest = bbsink_server_begin_manifest,
.manifest_contents = bbsink_server_manifest_contents,
.end_manifest = bbsink_server_end_manifest,
.end_backup = bbsink_forward_end_backup,
.cleanup = bbsink_forward_cleanup
};
/*
* Create a new 'server' bbsink.
*/
bbsink *
bbsink_server_new(bbsink *next, char *pathname)
{
bbsink_server *sink = palloc0(sizeof(bbsink_server));
*((const bbsink_ops **) &sink->base.bbs_ops) = &bbsink_server_ops;
sink->pathname = pathname;
sink->base.bbs_next = next;
/* Replication permission is not sufficient in this case. */
StartTransactionCommand();
if (!has_privs_of_role(GetUserId(), ROLE_PG_WRITE_SERVER_FILES))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to create backup stored on server"),
errdetail("Only roles with privileges of the \"%s\" role may create a backup stored on the server.",
"pg_write_server_files")));
CommitTransactionCommand();
/*
* It's not a good idea to store your backups in the same directory that
* you're backing up. If we allowed a relative path here, that could
* easily happen accidentally, so we don't. The user could still
* accomplish the same thing by including the absolute path to $PGDATA in
* the pathname, but that's likely an intentional bad decision rather than
* an accident.
*/
if (!is_absolute_path(pathname))
ereport(ERROR,
(errcode(ERRCODE_INVALID_NAME),
errmsg("relative path not allowed for backup stored on server")));
switch (pg_check_dir(pathname))
{
case 0:
/*
* Does not exist, so create it using the same permissions we'd
* use for a new subdirectory of the data directory itself.
*/
if (MakePGDirectory(pathname) < 0)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not create directory \"%s\": %m", pathname)));
break;
case 1:
/* Exists, empty. */
break;
case 2:
case 3:
case 4:
/* Exists, not empty. */
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_FILE),
errmsg("directory \"%s\" exists but is not empty",
pathname)));
break;
default:
/* Access problem. */
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not access directory \"%s\": %m",
pathname)));
}
return &sink->base;
}
/*
* Open the correct output file for this archive.
*/
static void
bbsink_server_begin_archive(bbsink *sink, const char *archive_name)
{
bbsink_server *mysink = (bbsink_server *) sink;
char *filename;
Assert(mysink->file == 0);
Assert(mysink->filepos == 0);
filename = psprintf("%s/%s", mysink->pathname, archive_name);
mysink->file = PathNameOpenFile(filename,
O_CREAT | O_EXCL | O_WRONLY | PG_BINARY);
if (mysink->file <= 0)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not create file \"%s\": %m", filename)));
pfree(filename);
bbsink_forward_begin_archive(sink, archive_name);
}
/*
* Write the data to the output file.
*/
static void
bbsink_server_archive_contents(bbsink *sink, size_t len)
{
bbsink_server *mysink = (bbsink_server *) sink;
int nbytes;
nbytes = FileWrite(mysink->file, mysink->base.bbs_buffer, len,
mysink->filepos, WAIT_EVENT_BASEBACKUP_WRITE);
if (nbytes != len)
{
if (nbytes < 0)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not write file \"%s\": %m",
FilePathName(mysink->file)),
errhint("Check free disk space.")));
/* short write: complain appropriately */
ereport(ERROR,
(errcode(ERRCODE_DISK_FULL),
errmsg("could not write file \"%s\": wrote only %d of %d bytes at offset %u",
FilePathName(mysink->file),
nbytes, (int) len, (unsigned) mysink->filepos),
errhint("Check free disk space.")));
}
mysink->filepos += nbytes;
bbsink_forward_archive_contents(sink, len);
}
/*
* fsync and close the current output file.
*/
static void
bbsink_server_end_archive(bbsink *sink)
{
bbsink_server *mysink = (bbsink_server *) sink;
/*
* We intentionally don't use data_sync_elevel here, because the server
* shouldn't PANIC just because we can't guarantee that the backup has
* been written down to disk. Running recovery won't fix anything in this
* case anyway.
*/
if (FileSync(mysink->file, WAIT_EVENT_BASEBACKUP_SYNC) < 0)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not fsync file \"%s\": %m",
FilePathName(mysink->file))));
/* We're done with this file now. */
FileClose(mysink->file);
mysink->file = 0;
mysink->filepos = 0;
bbsink_forward_end_archive(sink);
}
/*
* Open the output file to which we will write the manifest.
*
* Just like pg_basebackup, we write the manifest first under a temporary
* name and then rename it into place after fsync. That way, if the manifest
* is there and under the correct name, the user can be sure that the backup
* completed.
*/
static void
bbsink_server_begin_manifest(bbsink *sink)
{
bbsink_server *mysink = (bbsink_server *) sink;
char *tmp_filename;
Assert(mysink->file == 0);
tmp_filename = psprintf("%s/backup_manifest.tmp", mysink->pathname);
mysink->file = PathNameOpenFile(tmp_filename,
O_CREAT | O_EXCL | O_WRONLY | PG_BINARY);
if (mysink->file <= 0)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not create file \"%s\": %m", tmp_filename)));
pfree(tmp_filename);
bbsink_forward_begin_manifest(sink);
}
/*
* Each chunk of manifest data is sent using a CopyData message.
*/
static void
bbsink_server_manifest_contents(bbsink *sink, size_t len)
{
bbsink_server *mysink = (bbsink_server *) sink;
int nbytes;
nbytes = FileWrite(mysink->file, mysink->base.bbs_buffer, len,
mysink->filepos, WAIT_EVENT_BASEBACKUP_WRITE);
if (nbytes != len)
{
if (nbytes < 0)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not write file \"%s\": %m",
FilePathName(mysink->file)),
errhint("Check free disk space.")));
/* short write: complain appropriately */
ereport(ERROR,
(errcode(ERRCODE_DISK_FULL),
errmsg("could not write file \"%s\": wrote only %d of %d bytes at offset %u",
FilePathName(mysink->file),
nbytes, (int) len, (unsigned) mysink->filepos),
errhint("Check free disk space.")));
}
mysink->filepos += nbytes;
bbsink_forward_manifest_contents(sink, len);
}
/*
* fsync the backup manifest, close the file, and then rename it into place.
*/
static void
bbsink_server_end_manifest(bbsink *sink)
{
bbsink_server *mysink = (bbsink_server *) sink;
char *tmp_filename;
char *filename;
/* We're done with this file now. */
FileClose(mysink->file);
mysink->file = 0;
/*
* Rename it into place. This also fsyncs the temporary file, so we don't
* need to do that here. We don't use data_sync_elevel here for the same
* reasons as in bbsink_server_end_archive.
*/
tmp_filename = psprintf("%s/backup_manifest.tmp", mysink->pathname);
filename = psprintf("%s/backup_manifest", mysink->pathname);
durable_rename(tmp_filename, filename, ERROR);
pfree(filename);
pfree(tmp_filename);
bbsink_forward_end_manifest(sink);
}