#include "postgres.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include "access/xlog_internal.h"
#include "catalog/pg_type.h"
#include "common/relpath.h"
#include "lib/stringinfo.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "nodes/pg_list.h"
#include "replication/basebackup.h"
#include "replication/walsender.h"
#include "replication/walsender_private.h"
#include "storage/fd.h"
#include "storage/ipc.h"
#include "utils/builtins.h"
#include "utils/elog.h"
#include "utils/ps_status.h"
#include "pgtar.h"
Go to the source code of this file.
Data Structures | |
struct | basebackup_options |
struct | tablespaceinfo |
Defines | |
#define | TAR_SEND_SIZE 32768 |
#define | MAX_TAR_MEMBER_FILELEN (((int64) 1 << Min(33, sizeof(pgoff_t)*8 - 1)) - 1) |
Functions | |
static int64 | sendDir (char *path, int basepathlen, bool sizeonly) |
static int64 | sendTablespace (char *path, bool sizeonly) |
static bool | sendFile (char *readfilename, char *tarfilename, struct stat *statbuf, bool missing_ok) |
static void | sendFileWithContent (const char *filename, const char *content) |
static void | _tarWriteHeader (const char *filename, const char *linktarget, struct stat *statbuf) |
static void | send_int8_string (StringInfoData *buf, int64 intval) |
static void | SendBackupHeader (List *tablespaces) |
static void | base_backup_cleanup (int code, Datum arg) |
static void | perform_base_backup (basebackup_options *opt, DIR *tblspcdir) |
static void | parse_basebackup_options (List *options, basebackup_options *opt) |
static void | SendXlogRecPtrResult (XLogRecPtr ptr, TimeLineID tli) |
static int | compareWalFileNames (const void *a, const void *b) |
void | SendBaseBackup (BaseBackupCmd *cmd) |
Variables | |
static bool | backup_started_in_recovery = false |
#define MAX_TAR_MEMBER_FILELEN (((int64) 1 << Min(33, sizeof(pgoff_t)*8 - 1)) - 1) |
Definition at line 959 of file basebackup.c.
Referenced by sendFile().
#define TAR_SEND_SIZE 32768 |
Definition at line 69 of file basebackup.c.
static void _tarWriteHeader | ( | const char * | filename, | |
const char * | linktarget, | |||
struct stat * | statbuf | |||
) | [static] |
Definition at line 1047 of file basebackup.c.
References pq_putmessage(), and tarCreateHeader().
Referenced by perform_base_backup(), sendDir(), sendFile(), sendFileWithContent(), and sendTablespace().
{ char h[512]; tarCreateHeader(h, filename, linktarget, statbuf->st_size, statbuf->st_mode, statbuf->st_uid, statbuf->st_gid, statbuf->st_mtime); pq_putmessage('d', h, 512); }
static void base_backup_cleanup | ( | int | code, | |
Datum | arg | |||
) | [static] |
Definition at line 84 of file basebackup.c.
References do_pg_abort_backup().
Referenced by perform_base_backup().
{ do_pg_abort_backup(); }
static int compareWalFileNames | ( | const void * | a, | |
const void * | b | |||
) | [static] |
Definition at line 439 of file basebackup.c.
Referenced by perform_base_backup().
{ char *fna = *((char **) a); char *fnb = *((char **) b); return strcmp(fna + 8, fnb + 8); }
static void parse_basebackup_options | ( | List * | options, | |
basebackup_options * | opt | |||
) | [static] |
Definition at line 451 of file basebackup.c.
References DefElem::arg, DefElem::defname, elog, ereport, errcode(), errmsg(), ERROR, basebackup_options::fastcheckpoint, basebackup_options::includewal, basebackup_options::label, lfirst, MemSet, basebackup_options::nowait, NULL, basebackup_options::progress, and strVal.
Referenced by SendBaseBackup().
{ ListCell *lopt; bool o_label = false; bool o_progress = false; bool o_fast = false; bool o_nowait = false; bool o_wal = false; MemSet(opt, 0, sizeof(*opt)); foreach(lopt, options) { DefElem *defel = (DefElem *) lfirst(lopt); if (strcmp(defel->defname, "label") == 0) { if (o_label) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("duplicate option \"%s\"", defel->defname))); opt->label = strVal(defel->arg); o_label = true; } else if (strcmp(defel->defname, "progress") == 0) { if (o_progress) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("duplicate option \"%s\"", defel->defname))); opt->progress = true; o_progress = true; } else if (strcmp(defel->defname, "fast") == 0) { if (o_fast) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("duplicate option \"%s\"", defel->defname))); opt->fastcheckpoint = true; o_fast = true; } else if (strcmp(defel->defname, "nowait") == 0) { if (o_nowait) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("duplicate option \"%s\"", defel->defname))); opt->nowait = true; o_nowait = true; } else if (strcmp(defel->defname, "wal") == 0) { if (o_wal) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("duplicate option \"%s\"", defel->defname))); opt->includewal = true; o_wal = true; } else elog(ERROR, "option \"%s\" not recognized", defel->defname); } if (opt->label == NULL) opt->label = "base backup"; }
static void perform_base_backup | ( | basebackup_options * | opt, | |
DIR * | tblspcdir | |||
) | [static] |
Definition at line 96 of file basebackup.c.
References _tarWriteHeader(), AllocateDir(), AllocateFile(), Assert, BACKUP_LABEL_FILE, backup_started_in_recovery, base_backup_cleanup(), buf, CheckXLogRemoved(), compareWalFileNames(), dirent::d_name, do_pg_start_backup(), do_pg_stop_backup(), ereport, errcode(), errcode_for_file_access(), errmsg(), ERROR, basebackup_options::fastcheckpoint, FreeDir(), FreeFile(), i, basebackup_options::includewal, basebackup_options::label, lappend(), lfirst, list_length(), lnext, lstat, MAXPGPATH, Min, basebackup_options::nowait, NULL, tablespaceinfo::oid, palloc(), palloc0(), tablespaceinfo::path, PG_END_ENSURE_ERROR_CLEANUP, PG_ENSURE_ERROR_CLEANUP, pgoff_t, pq_beginmessage(), pq_endmessage(), pq_putemptymessage(), pq_putmessage(), pq_sendbyte(), pq_sendint(), basebackup_options::progress, pstrdup(), qsort, ReadDir(), RecoveryInProgress(), SendBackupHeader(), sendDir(), sendFile(), sendFileWithContent(), sendTablespace(), SendXlogRecPtrResult(), tablespaceinfo::size, snprintf(), ThisTimeLineID, WARNING, XLByteToPrevSeg, XLByteToSeg, XLOG_CONTROL_FILE, XLOGDIR, XLogFileName, XLogFromFileName, and XLogSegSize.
Referenced by SendBaseBackup().
{ XLogRecPtr startptr; TimeLineID starttli; XLogRecPtr endptr; TimeLineID endtli; char *labelfile; backup_started_in_recovery = RecoveryInProgress(); startptr = do_pg_start_backup(opt->label, opt->fastcheckpoint, &starttli, &labelfile); SendXlogRecPtrResult(startptr, starttli); PG_ENSURE_ERROR_CLEANUP(base_backup_cleanup, (Datum) 0); { List *tablespaces = NIL; ListCell *lc; struct dirent *de; tablespaceinfo *ti; /* Collect information about all tablespaces */ while ((de = ReadDir(tblspcdir, "pg_tblspc")) != NULL) { char fullpath[MAXPGPATH]; char linkpath[MAXPGPATH]; int rllen; /* Skip special stuff */ if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) continue; snprintf(fullpath, sizeof(fullpath), "pg_tblspc/%s", de->d_name); #if defined(HAVE_READLINK) || defined(WIN32) rllen = readlink(fullpath, linkpath, sizeof(linkpath)); if (rllen < 0) { ereport(WARNING, (errmsg("could not read symbolic link \"%s\": %m", fullpath))); continue; } else if (rllen >= sizeof(linkpath)) { ereport(WARNING, (errmsg("symbolic link \"%s\" target is too long", fullpath))); continue; } linkpath[rllen] = '\0'; ti = palloc(sizeof(tablespaceinfo)); ti->oid = pstrdup(de->d_name); ti->path = pstrdup(linkpath); ti->size = opt->progress ? sendTablespace(fullpath, true) : -1; tablespaces = lappend(tablespaces, ti); #else /* * If the platform does not have symbolic links, it should not be * possible to have tablespaces - clearly somebody else created * them. Warn about it and ignore. */ ereport(WARNING, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("tablespaces are not supported on this platform"))); #endif } /* Add a node for the base directory at the end */ ti = palloc0(sizeof(tablespaceinfo)); ti->size = opt->progress ? sendDir(".", 1, true) : -1; tablespaces = lappend(tablespaces, ti); /* Send tablespace header */ SendBackupHeader(tablespaces); /* Send off our tablespaces one by one */ foreach(lc, tablespaces) { tablespaceinfo *ti = (tablespaceinfo *) lfirst(lc); StringInfoData buf; /* Send CopyOutResponse message */ pq_beginmessage(&buf, 'H'); pq_sendbyte(&buf, 0); /* overall format */ pq_sendint(&buf, 0, 2); /* natts */ pq_endmessage(&buf); if (ti->path == NULL) { struct stat statbuf; /* In the main tar, include the backup_label first... */ sendFileWithContent(BACKUP_LABEL_FILE, labelfile); /* ... then the bulk of the files ... */ sendDir(".", 1, false); /* ... and pg_control after everything else. */ if (lstat(XLOG_CONTROL_FILE, &statbuf) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not stat control file \"%s\": %m", XLOG_CONTROL_FILE))); sendFile(XLOG_CONTROL_FILE, XLOG_CONTROL_FILE, &statbuf, false); } else sendTablespace(ti->path, false); /* * If we're including WAL, and this is the main data directory we * don't terminate the tar stream here. Instead, we will append * the xlog files below and terminate it then. This is safe since * the main data directory is always sent *last*. */ if (opt->includewal && ti->path == NULL) { Assert(lnext(lc) == NULL); } else pq_putemptymessage('c'); /* CopyDone */ } } PG_END_ENSURE_ERROR_CLEANUP(base_backup_cleanup, (Datum) 0); endptr = do_pg_stop_backup(labelfile, !opt->nowait, &endtli); if (opt->includewal) { /* * We've left the last tar file "open", so we can now append the * required WAL files to it. */ char pathbuf[MAXPGPATH]; XLogSegNo segno; XLogSegNo startsegno; XLogSegNo endsegno; struct stat statbuf; List *historyFileList = NIL; List *walFileList = NIL; char **walFiles; int nWalFiles; char firstoff[MAXFNAMELEN]; char lastoff[MAXFNAMELEN]; DIR *dir; struct dirent *de; int i; ListCell *lc; TimeLineID tli; /* * I'd rather not worry about timelines here, so scan pg_xlog and * include all WAL files in the range between 'startptr' and 'endptr', * regardless of the timeline the file is stamped with. If there are * some spurious WAL files belonging to timelines that don't belong * in this server's history, they will be included too. Normally there * shouldn't be such files, but if there are, there's little harm in * including them. */ XLByteToSeg(startptr, startsegno); XLogFileName(firstoff, ThisTimeLineID, startsegno); XLByteToPrevSeg(endptr, endsegno); XLogFileName(lastoff, ThisTimeLineID, endsegno); dir = AllocateDir("pg_xlog"); if (!dir) ereport(ERROR, (errmsg("could not open directory \"%s\": %m", "pg_xlog"))); while ((de = ReadDir(dir, "pg_xlog")) != NULL) { /* Does it look like a WAL segment, and is it in the range? */ if (strlen(de->d_name) == 24 && strspn(de->d_name, "0123456789ABCDEF") == 24 && strcmp(de->d_name + 8, firstoff + 8) >= 0 && strcmp(de->d_name + 8, lastoff + 8) <= 0) { walFileList = lappend(walFileList, pstrdup(de->d_name)); } /* Does it look like a timeline history file? */ else if (strlen(de->d_name) == 8 + strlen(".history") && strspn(de->d_name, "0123456789ABCDEF") == 8 && strcmp(de->d_name + 8, ".history") == 0) { historyFileList = lappend(historyFileList, pstrdup(de->d_name)); } } FreeDir(dir); /* * Before we go any further, check that none of the WAL segments we * need were removed. */ CheckXLogRemoved(startsegno, ThisTimeLineID); /* * Put the WAL filenames into an array, and sort. We send the files * in order from oldest to newest, to reduce the chance that a file * is recycled before we get a chance to send it over. */ nWalFiles = list_length(walFileList); walFiles = palloc(nWalFiles * sizeof(char *)); i = 0; foreach(lc, walFileList) { walFiles[i++] = lfirst(lc); } qsort(walFiles, nWalFiles, sizeof(char *), compareWalFileNames); /* * Sanity check: the first and last segment should cover startptr and * endptr, with no gaps in between. */ XLogFromFileName(walFiles[0], &tli, &segno); if (segno != startsegno) { char startfname[MAXFNAMELEN]; XLogFileName(startfname, ThisTimeLineID, startsegno); ereport(ERROR, (errmsg("could not find WAL file \"%s\"", startfname))); } for (i = 0; i < nWalFiles; i++) { XLogSegNo currsegno = segno; XLogSegNo nextsegno = segno + 1; XLogFromFileName(walFiles[i], &tli, &segno); if (!(nextsegno == segno || currsegno == segno)) { char nextfname[MAXFNAMELEN]; XLogFileName(nextfname, ThisTimeLineID, nextsegno); ereport(ERROR, (errmsg("could not find WAL file \"%s\"", nextfname))); } } if (segno != endsegno) { char endfname[MAXFNAMELEN]; XLogFileName(endfname, ThisTimeLineID, endsegno); ereport(ERROR, (errmsg("could not find WAL file \"%s\"", endfname))); } /* Ok, we have everything we need. Send the WAL files. */ for (i = 0; i < nWalFiles; i++) { FILE *fp; char buf[TAR_SEND_SIZE]; size_t cnt; pgoff_t len = 0; snprintf(pathbuf, MAXPGPATH, XLOGDIR "/%s", walFiles[i]); XLogFromFileName(walFiles[i], &tli, &segno); fp = AllocateFile(pathbuf, "rb"); if (fp == NULL) { /* * Most likely reason for this is that the file was already * removed by a checkpoint, so check for that to get a better * error message. */ CheckXLogRemoved(segno, tli); ereport(ERROR, (errcode_for_file_access(), errmsg("could not open file \"%s\": %m", pathbuf))); } if (fstat(fileno(fp), &statbuf) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not stat file \"%s\": %m", pathbuf))); if (statbuf.st_size != XLogSegSize) { CheckXLogRemoved(segno, tli); ereport(ERROR, (errcode_for_file_access(), errmsg("unexpected WAL file size \"%s\"", walFiles[i]))); } _tarWriteHeader(pathbuf, NULL, &statbuf); while ((cnt = fread(buf, 1, Min(sizeof(buf), XLogSegSize - len), fp)) > 0) { CheckXLogRemoved(segno, tli); /* Send the chunk as a CopyData message */ if (pq_putmessage('d', buf, cnt)) ereport(ERROR, (errmsg("base backup could not send data, aborting backup"))); len += cnt; if (len == XLogSegSize) break; } if (len != XLogSegSize) { CheckXLogRemoved(segno, tli); ereport(ERROR, (errcode_for_file_access(), errmsg("unexpected WAL file size \"%s\"", walFiles[i]))); } /* XLogSegSize is a multiple of 512, so no need for padding */ FreeFile(fp); } /* * Send timeline history files too. Only the latest timeline history * file is required for recovery, and even that only if there happens * to be a timeline switch in the first WAL segment that contains the * checkpoint record, or if we're taking a base backup from a standby * server and the target timeline changes while the backup is taken. * But they are small and highly useful for debugging purposes, so * better include them all, always. */ foreach(lc, historyFileList) { char *fname = lfirst(lc); snprintf(pathbuf, MAXPGPATH, XLOGDIR "/%s", fname); if (lstat(pathbuf, &statbuf) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not stat file \"%s\": %m", pathbuf))); sendFile(pathbuf, pathbuf, &statbuf, false); } /* Send CopyDone message for the last tar file */ pq_putemptymessage('c'); } SendXlogRecPtrResult(endptr, endtli); }
static void send_int8_string | ( | StringInfoData * | buf, | |
int64 | intval | |||
) | [static] |
Definition at line 557 of file basebackup.c.
References pq_sendbytes(), and pq_sendint().
Referenced by SendBackupHeader().
{ char is[32]; sprintf(is, INT64_FORMAT, intval); pq_sendint(buf, strlen(is), 4); pq_sendbytes(buf, is, strlen(is)); }
static void SendBackupHeader | ( | List * | tablespaces | ) | [static] |
Definition at line 567 of file basebackup.c.
References buf, INT8OID, lfirst, NULL, tablespaceinfo::oid, OIDOID, tablespaceinfo::path, pq_beginmessage(), pq_endmessage(), pq_puttextmessage(), pq_sendbytes(), pq_sendint(), pq_sendstring(), send_int8_string(), tablespaceinfo::size, and TEXTOID.
Referenced by perform_base_backup().
{ StringInfoData buf; ListCell *lc; /* Construct and send the directory information */ pq_beginmessage(&buf, 'T'); /* RowDescription */ pq_sendint(&buf, 3, 2); /* 3 fields */ /* First field - spcoid */ pq_sendstring(&buf, "spcoid"); pq_sendint(&buf, 0, 4); /* table oid */ pq_sendint(&buf, 0, 2); /* attnum */ pq_sendint(&buf, OIDOID, 4); /* type oid */ pq_sendint(&buf, 4, 2); /* typlen */ pq_sendint(&buf, 0, 4); /* typmod */ pq_sendint(&buf, 0, 2); /* format code */ /* Second field - spcpath */ pq_sendstring(&buf, "spclocation"); pq_sendint(&buf, 0, 4); pq_sendint(&buf, 0, 2); pq_sendint(&buf, TEXTOID, 4); pq_sendint(&buf, -1, 2); pq_sendint(&buf, 0, 4); pq_sendint(&buf, 0, 2); /* Third field - size */ pq_sendstring(&buf, "size"); pq_sendint(&buf, 0, 4); pq_sendint(&buf, 0, 2); pq_sendint(&buf, INT8OID, 4); pq_sendint(&buf, 8, 2); pq_sendint(&buf, 0, 4); pq_sendint(&buf, 0, 2); pq_endmessage(&buf); foreach(lc, tablespaces) { tablespaceinfo *ti = lfirst(lc); /* Send one datarow message */ pq_beginmessage(&buf, 'D'); pq_sendint(&buf, 3, 2); /* number of columns */ if (ti->path == NULL) { pq_sendint(&buf, -1, 4); /* Length = -1 ==> NULL */ pq_sendint(&buf, -1, 4); } else { pq_sendint(&buf, strlen(ti->oid), 4); /* length */ pq_sendbytes(&buf, ti->oid, strlen(ti->oid)); pq_sendint(&buf, strlen(ti->path), 4); /* length */ pq_sendbytes(&buf, ti->path, strlen(ti->path)); } if (ti->size >= 0) send_int8_string(&buf, ti->size / 1024); else pq_sendint(&buf, -1, 4); /* NULL */ pq_endmessage(&buf); } /* Send a CommandComplete message */ pq_puttextmessage('C', "SELECT"); }
void SendBaseBackup | ( | BaseBackupCmd * | cmd | ) |
Definition at line 527 of file basebackup.c.
References AllocateDir(), ereport, errmsg(), ERROR, FreeDir(), basebackup_options::label, BaseBackupCmd::options, parse_basebackup_options(), perform_base_backup(), set_ps_display(), snprintf(), update_process_title, WalSndSetState(), and WALSNDSTATE_BACKUP.
Referenced by exec_replication_command().
{ DIR *dir; basebackup_options opt; parse_basebackup_options(cmd->options, &opt); WalSndSetState(WALSNDSTATE_BACKUP); if (update_process_title) { char activitymsg[50]; snprintf(activitymsg, sizeof(activitymsg), "sending backup \"%s\"", opt.label); set_ps_display(activitymsg, false); } /* Make sure we can open the directory with tablespaces in it */ dir = AllocateDir("pg_tblspc"); if (!dir) ereport(ERROR, (errmsg("could not open directory \"%s\": %m", "pg_tblspc"))); perform_base_backup(&opt, dir); FreeDir(dir); }
static int64 sendDir | ( | char * | path, | |
int | basepathlen, | |||
bool | sizeonly | |||
) | [static] |
Definition at line 779 of file basebackup.c.
References _tarWriteHeader(), AllocateDir(), BACKUP_LABEL_FILE, backup_started_in_recovery, CHECK_FOR_INTERRUPTS, dirent::d_name, ereport, errcode(), errcode_for_file_access(), errhint(), errmsg(), ERROR, FreeDir(), lstat, MAXPGPATH, NULL, PG_TEMP_FILE_PREFIX, ReadDir(), RecoveryInProgress(), sendFile(), snprintf(), and WARNING.
Referenced by perform_base_backup(), and sendTablespace().
{ DIR *dir; struct dirent *de; char pathbuf[MAXPGPATH]; struct stat statbuf; int64 size = 0; dir = AllocateDir(path); while ((de = ReadDir(dir, path)) != NULL) { /* Skip special stuff */ if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0) continue; /* Skip temporary files */ if (strncmp(de->d_name, PG_TEMP_FILE_PREFIX, strlen(PG_TEMP_FILE_PREFIX)) == 0) continue; /* * If there's a backup_label file, it belongs to a backup started by * the user with pg_start_backup(). It is *not* correct for this * backup, our backup_label is injected into the tar separately. */ if (strcmp(de->d_name, BACKUP_LABEL_FILE) == 0) continue; /* * Check if the postmaster has signaled us to exit, and abort with an * error in that case. The error handler further up will call * do_pg_abort_backup() for us. Also check that if the backup was * started while still in recovery, the server wasn't promoted. * dp_pg_stop_backup() will check that too, but it's better to stop * the backup early than continue to the end and fail there. */ CHECK_FOR_INTERRUPTS(); if (RecoveryInProgress() != backup_started_in_recovery) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("the standby was promoted during online backup"), errhint("This means that the backup being taken is corrupt " "and should not be used. " "Try taking another online backup."))); snprintf(pathbuf, MAXPGPATH, "%s/%s", path, de->d_name); /* Skip postmaster.pid and postmaster.opts in the data directory */ if (strcmp(pathbuf, "./postmaster.pid") == 0 || strcmp(pathbuf, "./postmaster.opts") == 0) continue; /* Skip pg_control here to back up it last */ if (strcmp(pathbuf, "./global/pg_control") == 0) continue; if (lstat(pathbuf, &statbuf) != 0) { if (errno != ENOENT) ereport(ERROR, (errcode_for_file_access(), errmsg("could not stat file or directory \"%s\": %m", pathbuf))); /* If the file went away while scanning, it's no error. */ continue; } /* * We can skip pg_xlog, the WAL segments need to be fetched from the * WAL archive anyway. But include it as an empty directory anyway, so * we get permissions right. */ if (strcmp(pathbuf, "./pg_xlog") == 0) { if (!sizeonly) { /* If pg_xlog is a symlink, write it as a directory anyway */ #ifndef WIN32 if (S_ISLNK(statbuf.st_mode)) #else if (pgwin32_is_junction(pathbuf)) #endif statbuf.st_mode = S_IFDIR | S_IRWXU; _tarWriteHeader(pathbuf + basepathlen + 1, NULL, &statbuf); } size += 512; /* Size of the header just added */ continue; /* don't recurse into pg_xlog */ } /* Allow symbolic links in pg_tblspc only */ if (strcmp(path, "./pg_tblspc") == 0 && #ifndef WIN32 S_ISLNK(statbuf.st_mode) #else pgwin32_is_junction(pathbuf) #endif ) { #if defined(HAVE_READLINK) || defined(WIN32) char linkpath[MAXPGPATH]; int rllen; rllen = readlink(pathbuf, linkpath, sizeof(linkpath)); if (rllen < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not read symbolic link \"%s\": %m", pathbuf))); if (rllen >= sizeof(linkpath)) ereport(ERROR, (errmsg("symbolic link \"%s\" target is too long", pathbuf))); linkpath[rllen] = '\0'; if (!sizeonly) _tarWriteHeader(pathbuf + basepathlen + 1, linkpath, &statbuf); size += 512; /* Size of the header just added */ #else /* * If the platform does not have symbolic links, it should not be * possible to have tablespaces - clearly somebody else created * them. Warn about it and ignore. */ ereport(WARNING, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("tablespaces are not supported on this platform"))); continue; #endif /* HAVE_READLINK */ } else if (S_ISDIR(statbuf.st_mode)) { /* * Store a directory entry in the tar file so we can get the * permissions right. */ if (!sizeonly) _tarWriteHeader(pathbuf + basepathlen + 1, NULL, &statbuf); size += 512; /* Size of the header just added */ /* call ourselves recursively for a directory */ size += sendDir(pathbuf, basepathlen, sizeonly); } else if (S_ISREG(statbuf.st_mode)) { bool sent = false; if (!sizeonly) sent = sendFile(pathbuf, pathbuf + basepathlen + 1, &statbuf, true); if (sent || sizeonly) { /* Add size, rounded up to 512byte block */ size += ((statbuf.st_size + 511) & ~511); size += 512; /* Size of the header of the file */ } } else ereport(WARNING, (errmsg("skipping special file \"%s\"", pathbuf))); } FreeDir(dir); return size; }
static bool sendFile | ( | char * | readfilename, | |
char * | tarfilename, | |||
struct stat * | statbuf, | |||
bool | missing_ok | |||
) | [static] |
Definition at line 970 of file basebackup.c.
References _tarWriteHeader(), AllocateFile(), buf, ereport, errcode_for_file_access(), errmsg(), ERROR, FreeFile(), MAX_TAR_MEMBER_FILELEN, MemSet, Min, NULL, pgoff_t, and pq_putmessage().
Referenced by perform_base_backup(), and sendDir().
{ FILE *fp; char buf[TAR_SEND_SIZE]; size_t cnt; pgoff_t len = 0; size_t pad; fp = AllocateFile(readfilename, "rb"); if (fp == NULL) { if (errno == ENOENT && missing_ok) return false; ereport(ERROR, (errcode_for_file_access(), errmsg("could not open file \"%s\": %m", readfilename))); } /* * Some compilers will throw a warning knowing this test can never be true * because pgoff_t can't exceed the compared maximum on their platform. */ if (statbuf->st_size > MAX_TAR_MEMBER_FILELEN) ereport(ERROR, (errmsg("archive member \"%s\" too large for tar format", tarfilename))); _tarWriteHeader(tarfilename, NULL, statbuf); while ((cnt = fread(buf, 1, Min(sizeof(buf), statbuf->st_size - len), fp)) > 0) { /* Send the chunk as a CopyData message */ if (pq_putmessage('d', buf, cnt)) ereport(ERROR, (errmsg("base backup could not send data, aborting backup"))); len += cnt; if (len >= statbuf->st_size) { /* * Reached end of file. The file could be longer, if it was * extended while we were sending it, but for a base backup we can * ignore such extended data. It will be restored from WAL. */ break; } } /* If the file was truncated while we were sending it, pad it with zeros */ if (len < statbuf->st_size) { MemSet(buf, 0, sizeof(buf)); while (len < statbuf->st_size) { cnt = Min(sizeof(buf), statbuf->st_size - len); pq_putmessage('d', buf, cnt); len += cnt; } } /* Pad to 512 byte boundary, per tar format requirements */ pad = ((len + 511) & ~511) - len; if (pad > 0) { MemSet(buf, 0, pad); pq_putmessage('d', buf, pad); } FreeFile(fp); return true; }
static void sendFileWithContent | ( | const char * | filename, | |
const char * | content | |||
) | [static] |
Definition at line 691 of file basebackup.c.
References _tarWriteHeader(), buf, MemSet, NULL, and pq_putmessage().
Referenced by perform_base_backup().
{ struct stat statbuf; int pad, len; len = strlen(content); /* * Construct a stat struct for the backup_label file we're injecting in * the tar. */ /* Windows doesn't have the concept of uid and gid */ #ifdef WIN32 statbuf.st_uid = 0; statbuf.st_gid = 0; #else statbuf.st_uid = geteuid(); statbuf.st_gid = getegid(); #endif statbuf.st_mtime = time(NULL); statbuf.st_mode = S_IRUSR | S_IWUSR; statbuf.st_size = len; _tarWriteHeader(filename, NULL, &statbuf); /* Send the contents as a CopyData message */ pq_putmessage('d', content, len); /* Pad to 512 byte boundary, per tar format requirements */ pad = ((len + 511) & ~511) - len; if (pad > 0) { char buf[512]; MemSet(buf, 0, pad); pq_putmessage('d', buf, pad); } }
static int64 sendTablespace | ( | char * | path, | |
bool | sizeonly | |||
) | [static] |
Definition at line 736 of file basebackup.c.
References _tarWriteHeader(), ereport, errcode_for_file_access(), errmsg(), ERROR, lstat, NULL, sendDir(), snprintf(), and TABLESPACE_VERSION_DIRECTORY.
Referenced by perform_base_backup().
{ int64 size; char pathbuf[MAXPGPATH]; struct stat statbuf; /* * 'path' points to the tablespace location, but we only want to include * the version directory in it that belongs to us. */ snprintf(pathbuf, sizeof(pathbuf), "%s/%s", path, TABLESPACE_VERSION_DIRECTORY); /* * Store a directory entry in the tar file so we get the permissions right. */ if (lstat(pathbuf, &statbuf) != 0) { if (errno != ENOENT) ereport(ERROR, (errcode_for_file_access(), errmsg("could not stat file or directory \"%s\": %m", pathbuf))); /* If the tablespace went away while scanning, it's no error. */ return 0; } if (!sizeonly) _tarWriteHeader(TABLESPACE_VERSION_DIRECTORY, NULL, &statbuf); size = 512; /* Size of the header just added */ /* Send all the files in the tablespace version directory */ size += sendDir(pathbuf, strlen(path), sizeonly); return size; }
static void SendXlogRecPtrResult | ( | XLogRecPtr | ptr, | |
TimeLineID | tli | |||
) | [static] |
Definition at line 640 of file basebackup.c.
References buf, INT8OID, pq_beginmessage(), pq_endmessage(), pq_puttextmessage(), pq_sendbytes(), pq_sendint(), pq_sendstring(), snprintf(), and TEXTOID.
Referenced by perform_base_backup().
{ StringInfoData buf; char str[MAXFNAMELEN]; pq_beginmessage(&buf, 'T'); /* RowDescription */ pq_sendint(&buf, 2, 2); /* 2 fields */ /* Field headers */ pq_sendstring(&buf, "recptr"); pq_sendint(&buf, 0, 4); /* table oid */ pq_sendint(&buf, 0, 2); /* attnum */ pq_sendint(&buf, TEXTOID, 4); /* type oid */ pq_sendint(&buf, -1, 2); pq_sendint(&buf, 0, 4); pq_sendint(&buf, 0, 2); pq_sendstring(&buf, "tli"); pq_sendint(&buf, 0, 4); /* table oid */ pq_sendint(&buf, 0, 2); /* attnum */ /* * int8 may seem like a surprising data type for this, but in thory int4 * would not be wide enough for this, as TimeLineID is unsigned. */ pq_sendint(&buf, INT8OID, 4); /* type oid */ pq_sendint(&buf, -1, 2); pq_sendint(&buf, 0, 4); pq_sendint(&buf, 0, 2); pq_endmessage(&buf); /* Data row */ pq_beginmessage(&buf, 'D'); pq_sendint(&buf, 2, 2); /* number of columns */ snprintf(str, sizeof(str), "%X/%X", (uint32) (ptr >> 32), (uint32) ptr); pq_sendint(&buf, strlen(str), 4); /* length */ pq_sendbytes(&buf, str, strlen(str)); snprintf(str, sizeof(str), "%u", tli); pq_sendint(&buf, strlen(str), 4); /* length */ pq_sendbytes(&buf, str, strlen(str)); pq_endmessage(&buf); /* Send a CommandComplete message */ pq_puttextmessage('C', "SELECT"); }
bool backup_started_in_recovery = false [static] |
Definition at line 64 of file basebackup.c.
Referenced by do_pg_start_backup(), do_pg_stop_backup(), perform_base_backup(), and sendDir().