mirror of
https://github.com/postgrespro/pg_probackup.git
synced 2024-11-28 09:33:54 +02:00
Fix a critical bug that pg_rman cannot restore database from incremental backup. Backup itself worked correctly, but restore command broke database files.
git-svn-id: http://pg-rman.googlecode.com/svn/trunk@25 182aca00-e38e-11de-a668-6fd11605f5ce
This commit is contained in:
parent
bc5717b1e0
commit
59d0911fa7
85
backup.c
85
backup.c
@ -30,7 +30,7 @@ static void backup_cleanup(bool fatal, void *userdata);
|
||||
static void delete_old_files(const char *root, parray *files, int keep_files,
|
||||
int keep_days, int server_version, bool is_arclog);
|
||||
static void backup_files(const char *from_root, const char *to_root,
|
||||
parray *files, parray *prev_files, XLogRecPtr *lsn, bool compress_data);
|
||||
parray *files, parray *prev_files, const XLogRecPtr *lsn, bool compress);
|
||||
static parray *do_backup_database(parray *backup_list, bool smooth_checkpoint);
|
||||
static parray *do_backup_arclog(parray *backup_list);
|
||||
static parray *do_backup_srvlog(parray *backup_list);
|
||||
@ -137,7 +137,7 @@ do_backup_database(parray *backup_list, bool smooth_checkpoint)
|
||||
files = parray_new();
|
||||
dir_list_file(files, pgdata, pgdata_exclude, true, false);
|
||||
|
||||
/* mark files as 'datafile' which are under base/global/pg_tblspc */
|
||||
/* mark files that are possible datafile as 'datafile' */
|
||||
for (i = 0; i < parray_num(files); i++)
|
||||
{
|
||||
pgFile *file = (pgFile *) parray_get(files, i);
|
||||
@ -148,7 +148,7 @@ do_backup_database(parray *backup_list, bool smooth_checkpoint)
|
||||
if (!S_ISREG(file->mode))
|
||||
continue;
|
||||
|
||||
/* data files are under base/global/pg_tblspc */
|
||||
/* data files are under "base", "global", or "pg_tblspc" */
|
||||
relative = file->path + strlen(pgdata) + 1;
|
||||
if (!path_is_prefix_of_path("base", relative) &&
|
||||
!path_is_prefix_of_path("global", relative) &&
|
||||
@ -214,16 +214,16 @@ do_backup_database(parray *backup_list, bool smooth_checkpoint)
|
||||
static parray *
|
||||
do_backup_arclog(parray *backup_list)
|
||||
{
|
||||
int i;
|
||||
parray *files;
|
||||
parray *prev_files = NULL; /* file list of previous database backup */
|
||||
FILE *fp;
|
||||
char path[MAXPGPATH];
|
||||
char timeline_dir[MAXPGPATH];
|
||||
char prev_file_txt[MAXPGPATH];
|
||||
pgBackup *prev_backup;
|
||||
int64 arclog_write_bytes = 0;
|
||||
char last_wal[MAXPGPATH];
|
||||
int i;
|
||||
parray *files;
|
||||
parray *prev_files = NULL; /* file list of previous database backup */
|
||||
FILE *fp;
|
||||
char path[MAXPGPATH];
|
||||
char timeline_dir[MAXPGPATH];
|
||||
char prev_file_txt[MAXPGPATH];
|
||||
pgBackup *prev_backup;
|
||||
int64 arclog_write_bytes = 0;
|
||||
char last_wal[MAXPGPATH];
|
||||
|
||||
if (!HAVE_ARCLOG(¤t))
|
||||
return NULL;
|
||||
@ -345,14 +345,14 @@ do_backup_arclog(parray *backup_list)
|
||||
static parray *
|
||||
do_backup_srvlog(parray *backup_list)
|
||||
{
|
||||
int i;
|
||||
parray *files;
|
||||
parray *prev_files = NULL; /* file list of previous database backup */
|
||||
FILE *fp;
|
||||
char path[MAXPGPATH];
|
||||
char prev_file_txt[MAXPGPATH];
|
||||
pgBackup *prev_backup;
|
||||
int64 srvlog_write_bytes = 0;
|
||||
int i;
|
||||
parray *files;
|
||||
parray *prev_files = NULL; /* file list of previous database backup */
|
||||
FILE *fp;
|
||||
char path[MAXPGPATH];
|
||||
char prev_file_txt[MAXPGPATH];
|
||||
pgBackup *prev_backup;
|
||||
int64 srvlog_write_bytes = 0;
|
||||
|
||||
if (!current.with_serverlog)
|
||||
return NULL;
|
||||
@ -818,10 +818,14 @@ backup_cleanup(bool fatal, void *userdata)
|
||||
|
||||
/* take incremental backup. */
|
||||
static void
|
||||
backup_files(const char *from_root, const char *to_root, parray *files,
|
||||
parray *prev_files, XLogRecPtr *lsn, bool compress_data)
|
||||
backup_files(const char *from_root,
|
||||
const char *to_root,
|
||||
parray *files,
|
||||
parray *prev_files,
|
||||
const XLogRecPtr *lsn,
|
||||
bool compress)
|
||||
{
|
||||
int i;
|
||||
int i;
|
||||
struct timeval tv;
|
||||
|
||||
/* sort pathname ascending */
|
||||
@ -832,8 +836,9 @@ backup_files(const char *from_root, const char *to_root, parray *files,
|
||||
/* backup a file or create a directory */
|
||||
for (i = 0; i < parray_num(files); i++)
|
||||
{
|
||||
int ret;
|
||||
struct stat buf;
|
||||
int ret;
|
||||
struct stat buf;
|
||||
|
||||
pgFile *file = (pgFile *) parray_get(files, i);
|
||||
|
||||
/* check for interrupt */
|
||||
@ -869,6 +874,7 @@ backup_files(const char *from_root, const char *to_root, parray *files,
|
||||
if (S_ISDIR(buf.st_mode))
|
||||
{
|
||||
char dirpath[MAXPGPATH];
|
||||
|
||||
snprintf(dirpath, lengthof(dirpath), "%s/%s", to_root,
|
||||
file->path + strlen(from_root) + 1);
|
||||
if (!check)
|
||||
@ -918,7 +924,7 @@ backup_files(const char *from_root, const char *to_root, parray *files,
|
||||
/* copy the file into backup */
|
||||
if (file->is_datafile)
|
||||
{
|
||||
backup_data_file(from_root, to_root, file, lsn, compress_data);
|
||||
backup_data_file(from_root, to_root, file, lsn, compress);
|
||||
if (file->write_size == 0 && file->read_size > 0)
|
||||
{
|
||||
/* record as skipped file in file_xxx.txt */
|
||||
@ -930,7 +936,7 @@ backup_files(const char *from_root, const char *to_root, parray *files,
|
||||
}
|
||||
else
|
||||
copy_file(from_root, to_root, file,
|
||||
compress_data ? COMPRESSION : NO_COMPRESSION);
|
||||
compress ? COMPRESSION : NO_COMPRESSION);
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
@ -965,11 +971,10 @@ delete_old_files(const char *root,
|
||||
int server_version,
|
||||
bool is_arclog)
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
int file_num = 0;
|
||||
time_t days_threashold =
|
||||
current.start_time - (keep_days * 60 * 60 * 24);
|
||||
int i;
|
||||
int j;
|
||||
int file_num = 0;
|
||||
time_t days_threshold = current.start_time - (keep_days * 60 * 60 * 24);
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
@ -990,7 +995,7 @@ delete_old_files(const char *root,
|
||||
root, files_str, days_str);
|
||||
}
|
||||
|
||||
/* delete files which satisfy both condition */
|
||||
/* delete files which satisfy both conditions */
|
||||
if (keep_files == KEEP_INFINITE || keep_days == KEEP_INFINITE)
|
||||
{
|
||||
elog(LOG, "%s() infinite", __FUNCTION__);
|
||||
@ -1003,7 +1008,7 @@ delete_old_files(const char *root,
|
||||
pgFile *file = (pgFile *) parray_get(files, i);
|
||||
|
||||
elog(LOG, "%s() %s", __FUNCTION__, file->path);
|
||||
/* Delete complete WAL only. */
|
||||
/* Delete completed WALs only. */
|
||||
if (is_arclog && !xlog_is_complete_wal(file, server_version))
|
||||
{
|
||||
elog(LOG, "%s() not complete WAL", __FUNCTION__);
|
||||
@ -1013,17 +1018,17 @@ delete_old_files(const char *root,
|
||||
file_num++;
|
||||
|
||||
/*
|
||||
* If the mtime of the file is older than the threashold and there are
|
||||
* If the mtime of the file is older than the threshold and there are
|
||||
* enough number of files newer than the files, delete the file.
|
||||
*/
|
||||
if (file->mtime >= days_threashold)
|
||||
if (file->mtime >= days_threshold)
|
||||
{
|
||||
elog(LOG, "%s() %lu is not older than %lu", __FUNCTION__,
|
||||
file->mtime, days_threashold);
|
||||
file->mtime, days_threshold);
|
||||
continue;
|
||||
}
|
||||
elog(LOG, "%s() %lu is older than %lu", __FUNCTION__,
|
||||
file->mtime, days_threashold);
|
||||
file->mtime, days_threshold);
|
||||
|
||||
if (file_num <= keep_files)
|
||||
{
|
||||
@ -1035,7 +1040,7 @@ delete_old_files(const char *root,
|
||||
if (verbose)
|
||||
printf(_("delete \"%s\"\n"), file->path + strlen(root) + 1);
|
||||
|
||||
/* delete corresponding backup history file if any */
|
||||
/* delete corresponding backup history file if exists */
|
||||
file = (pgFile *) parray_remove(files, i);
|
||||
for (j = parray_num(files) - 1; j >= 0; j--)
|
||||
{
|
||||
|
@ -247,8 +247,8 @@ err_proc:
|
||||
pgBackup *
|
||||
catalog_get_last_data_backup(parray *backup_list)
|
||||
{
|
||||
int i;
|
||||
pgBackup *backup = NULL;
|
||||
int i;
|
||||
pgBackup *backup = NULL;
|
||||
|
||||
/* backup_list is sorted in order of descending ID */
|
||||
for (i = 0; i < parray_num(backup_list); i++)
|
||||
@ -269,8 +269,8 @@ catalog_get_last_data_backup(parray *backup_list)
|
||||
pgBackup *
|
||||
catalog_get_last_arclog_backup(parray *backup_list)
|
||||
{
|
||||
int i;
|
||||
pgBackup *backup = NULL;
|
||||
int i;
|
||||
pgBackup *backup = NULL;
|
||||
|
||||
/* backup_list is sorted in order of descending ID */
|
||||
for (i = 0; i < parray_num(backup_list); i++)
|
||||
|
122
data.c
122
data.c
@ -261,7 +261,8 @@ parse_page(const DataPage *page, int server_version,
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
*offset = *length = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -273,7 +274,7 @@ parse_page(const DataPage *page, int server_version,
|
||||
*/
|
||||
void
|
||||
backup_data_file(const char *from_root, const char *to_root,
|
||||
pgFile *file, const XLogRecPtr *lsn, bool compress_data)
|
||||
pgFile *file, const XLogRecPtr *lsn, bool compress)
|
||||
{
|
||||
char to_path[MAXPGPATH];
|
||||
FILE *in;
|
||||
@ -324,7 +325,7 @@ backup_data_file(const char *from_root, const char *to_root,
|
||||
file->write_size = 0;
|
||||
|
||||
#ifdef HAVE_LIBZ
|
||||
if (compress_data)
|
||||
if (compress)
|
||||
{
|
||||
z.zalloc = Z_NULL;
|
||||
z.zfree = Z_NULL;
|
||||
@ -366,7 +367,7 @@ backup_data_file(const char *from_root, const char *to_root,
|
||||
fclose(out);
|
||||
file->is_datafile = false;
|
||||
copy_file(from_root, to_root, file,
|
||||
compress_data ? COMPRESSION : NO_COMPRESSION);
|
||||
compress ? COMPRESSION : NO_COMPRESSION);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -380,7 +381,7 @@ backup_data_file(const char *from_root, const char *to_root,
|
||||
upper_length = BLCKSZ - upper_offset;
|
||||
|
||||
#ifdef HAVE_LIBZ
|
||||
if (compress_data)
|
||||
if (compress)
|
||||
{
|
||||
doDeflate(&z, sizeof(header), sizeof(outbuf), &header, outbuf, in,
|
||||
out, &crc, &file->write_size, Z_NO_FLUSH);
|
||||
@ -444,7 +445,7 @@ backup_data_file(const char *from_root, const char *to_root,
|
||||
header.hole_length = 0;
|
||||
|
||||
#ifdef HAVE_LIBZ
|
||||
if (compress_data)
|
||||
if (compress)
|
||||
{
|
||||
doDeflate(&z, sizeof(header), sizeof(outbuf), &header, outbuf,
|
||||
in, out, &crc, &file->write_size, Z_NO_FLUSH);
|
||||
@ -469,7 +470,7 @@ backup_data_file(const char *from_root, const char *to_root,
|
||||
|
||||
/* write odd size page image */
|
||||
#ifdef HAVE_LIBZ
|
||||
if (compress_data)
|
||||
if (compress)
|
||||
{
|
||||
doDeflate(&z, read_len, sizeof(outbuf), page.data, outbuf, in, out,
|
||||
&crc, &file->write_size, Z_NO_FLUSH);
|
||||
@ -495,7 +496,7 @@ backup_data_file(const char *from_root, const char *to_root,
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBZ
|
||||
if (compress_data)
|
||||
if (compress)
|
||||
{
|
||||
if (file->read_size > 0)
|
||||
{
|
||||
@ -512,9 +513,12 @@ backup_data_file(const char *from_root, const char *to_root,
|
||||
elog(ERROR_SYSTEM, _("can't close compression stream: %s"), z.msg);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
/* update file permission */
|
||||
|
||||
/*
|
||||
* update file permission
|
||||
* FIXME: Should set permission on open?
|
||||
*/
|
||||
if (!check && chmod(to_path, FILE_PERMISSION) == -1)
|
||||
{
|
||||
int errno_tmp = errno;
|
||||
@ -551,26 +555,29 @@ backup_data_file(const char *from_root, const char *to_root,
|
||||
* same relative path.
|
||||
*/
|
||||
void
|
||||
restore_data_file(const char *from_root, const char *to_root, pgFile *file, bool compress_data)
|
||||
restore_data_file(const char *from_root,
|
||||
const char *to_root,
|
||||
pgFile *file,
|
||||
bool compress)
|
||||
{
|
||||
char to_path[MAXPGPATH];
|
||||
FILE *in;
|
||||
FILE *out;
|
||||
BackupPageHeader header;
|
||||
BlockNumber blknum;
|
||||
char to_path[MAXPGPATH];
|
||||
FILE *in;
|
||||
FILE *out;
|
||||
BackupPageHeader header;
|
||||
BlockNumber blknum;
|
||||
#ifdef HAVE_LIBZ
|
||||
z_stream z;
|
||||
int status;
|
||||
char inbuf[zlibInSize];
|
||||
pg_crc32 crc;
|
||||
size_t read_size;
|
||||
z_stream z;
|
||||
int status;
|
||||
char inbuf[zlibInSize];
|
||||
pg_crc32 crc;
|
||||
size_t read_size;
|
||||
#endif
|
||||
|
||||
/* If the file is not a datafile, copy it. */
|
||||
/* If the file is not a datafile, just copy it. */
|
||||
if (!file->is_datafile)
|
||||
{
|
||||
copy_file(from_root, to_root, file,
|
||||
compress_data ? DECOMPRESSION : NO_COMPRESSION);
|
||||
compress ? DECOMPRESSION : NO_COMPRESSION);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -582,10 +589,16 @@ restore_data_file(const char *from_root, const char *to_root, pgFile *file, bool
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
/* open backup file for write */
|
||||
/*
|
||||
* Open backup file for write. We use "r+" at first to overwrite only
|
||||
* modified pages for incremental restore. If the file is not exists,
|
||||
* re-open it with "w" to create an empty file.
|
||||
*/
|
||||
snprintf(to_path, lengthof(to_path), "%s/%s", to_root,
|
||||
file->path + strlen(from_root) + 1);
|
||||
out = fopen(to_path, "w");
|
||||
out = fopen(to_path, "r+");
|
||||
if (out == NULL && errno == ENOENT)
|
||||
out = fopen(to_path, "w");
|
||||
if (out == NULL)
|
||||
{
|
||||
int errno_tmp = errno;
|
||||
@ -595,7 +608,7 @@ restore_data_file(const char *from_root, const char *to_root, pgFile *file, bool
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBZ
|
||||
if (compress_data)
|
||||
if (compress)
|
||||
{
|
||||
z.zalloc = Z_NULL;
|
||||
z.zfree = Z_NULL;
|
||||
@ -611,16 +624,16 @@ restore_data_file(const char *from_root, const char *to_root, pgFile *file, bool
|
||||
}
|
||||
#endif
|
||||
|
||||
for (blknum = 0; ;blknum++)
|
||||
for (blknum = 0; ; blknum++)
|
||||
{
|
||||
size_t read_len;
|
||||
DataPage page; /* used as read buffer */
|
||||
int upper_offset;
|
||||
int upper_length;
|
||||
size_t read_len;
|
||||
DataPage page; /* used as read buffer */
|
||||
int upper_offset;
|
||||
int upper_length;
|
||||
|
||||
/* read BackupPageHeader */
|
||||
#ifdef HAVE_LIBZ
|
||||
if (compress_data)
|
||||
if (compress)
|
||||
{
|
||||
status = doInflate(&z, sizeof(inbuf), sizeof(header), inbuf,
|
||||
&header, in, out, &crc, &read_size);
|
||||
@ -669,14 +682,15 @@ restore_data_file(const char *from_root, const char *to_root, pgFile *file, bool
|
||||
|
||||
/* read lower/upper into page.data and restore hole */
|
||||
memset(page.data + header.hole_offset, 0, header.hole_length);
|
||||
|
||||
#ifdef HAVE_LIBZ
|
||||
if (compress_data)
|
||||
if (compress)
|
||||
{
|
||||
elog(LOG, "\n%s() %s %d %d", __FUNCTION__, file->path, header.hole_offset, upper_length);
|
||||
if (header.hole_offset > 0)
|
||||
{
|
||||
doInflate(&z, sizeof(inbuf), header.hole_offset, inbuf, page.data,
|
||||
in, out, &crc, &read_size);
|
||||
doInflate(&z, sizeof(inbuf), header.hole_offset, inbuf,
|
||||
page.data, in, out, &crc, &read_size);
|
||||
if (z.avail_out != 0)
|
||||
elog(ERROR_SYSTEM, _("can't read block %u of \"%s\""),
|
||||
blknum, file->path);
|
||||
@ -702,25 +716,21 @@ restore_data_file(const char *from_root, const char *to_root, pgFile *file, bool
|
||||
}
|
||||
}
|
||||
|
||||
/* by the incremental backup, we skip in a page without the update. */
|
||||
if (blknum != header.block)
|
||||
{
|
||||
if (fseek(out, (header.block - blknum) * BLCKSZ, SEEK_CUR) < 0)
|
||||
elog(ERROR_SYSTEM, _("can't seek restore target file \"%s\": %s"),
|
||||
to_path, strerror(errno));
|
||||
blknum = header.block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Seek and write the restored page. Backup might have holes in
|
||||
* incremental backups.
|
||||
*/
|
||||
blknum = header.block;
|
||||
if (fseek(out, blknum * BLCKSZ, SEEK_SET) < 0)
|
||||
elog(ERROR_SYSTEM, _("can't seek block %u of \"%s\": %s"),
|
||||
blknum, to_path, strerror(errno));
|
||||
if (fwrite(page.data, 1, sizeof(page), out) != sizeof(page))
|
||||
{
|
||||
elog(ERROR_SYSTEM, _("can't write block %u of \"%s\": %s"),
|
||||
blknum, file->path, strerror(errno));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBZ
|
||||
if (compress_data && inflateEnd(&z) != Z_OK)
|
||||
if (compress && inflateEnd(&z) != Z_OK)
|
||||
elog(ERROR_SYSTEM, _("can't close compression stream: %s"), z.msg);
|
||||
#endif
|
||||
|
||||
@ -742,14 +752,14 @@ void
|
||||
copy_file(const char *from_root, const char *to_root, pgFile *file,
|
||||
CompressionMode mode)
|
||||
{
|
||||
char to_path[MAXPGPATH];
|
||||
FILE *in;
|
||||
FILE *out;
|
||||
size_t read_len = 0;
|
||||
int errno_tmp;
|
||||
char buf[8192];
|
||||
struct stat st;
|
||||
pg_crc32 crc;
|
||||
char to_path[MAXPGPATH];
|
||||
FILE *in;
|
||||
FILE *out;
|
||||
size_t read_len = 0;
|
||||
int errno_tmp;
|
||||
char buf[8192];
|
||||
struct stat st;
|
||||
pg_crc32 crc;
|
||||
#ifdef HAVE_LIBZ
|
||||
z_stream z;
|
||||
int status;
|
||||
|
10
delete.c
10
delete.c
@ -74,7 +74,7 @@ pgBackupDelete(int keep_generations, int keep_days)
|
||||
int i;
|
||||
parray *backup_list;
|
||||
int backup_num;
|
||||
time_t days_threashold = current.start_time - (keep_days * 60 * 60 * 24);
|
||||
time_t days_threshold = current.start_time - (keep_days * 60 * 60 * 24);
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
@ -130,18 +130,18 @@ pgBackupDelete(int keep_generations, int keep_days)
|
||||
}
|
||||
|
||||
/*
|
||||
* If the start time of the backup is older than the threashold and
|
||||
* If the start time of the backup is older than the threshold and
|
||||
* there are enough generations of full backups, delete the backup.
|
||||
*/
|
||||
if (backup->start_time >= days_threashold)
|
||||
if (backup->start_time >= days_threshold)
|
||||
{
|
||||
elog(LOG, "%s() %lu is not older than %lu", __FUNCTION__,
|
||||
backup->start_time, days_threashold);
|
||||
backup->start_time, days_threshold);
|
||||
continue;
|
||||
}
|
||||
|
||||
elog(LOG, "%s() %lu is older than %lu", __FUNCTION__,
|
||||
backup->start_time, days_threashold);
|
||||
backup->start_time, days_threshold);
|
||||
|
||||
/* delete backup and update status to DELETED */
|
||||
pgBackupDeleteFiles(backup);
|
||||
|
73
dir.c
73
dir.c
@ -56,8 +56,8 @@ dir_create_dir(const char *dir, mode_t mode)
|
||||
static pgFile *
|
||||
pgFileNew(const char *path, bool omit_symlink)
|
||||
{
|
||||
struct stat st;
|
||||
pgFile *file;
|
||||
struct stat st;
|
||||
pgFile *file;
|
||||
|
||||
/* stat the file */
|
||||
if ((omit_symlink ? stat(path, &st) : lstat(path, &st)) == -1)
|
||||
@ -84,28 +84,6 @@ pgFileNew(const char *path, bool omit_symlink)
|
||||
return file;
|
||||
}
|
||||
|
||||
void
|
||||
pgFileDump(pgFile *file, FILE *out)
|
||||
{
|
||||
char mtime_str[100];
|
||||
|
||||
fprintf(out, "=================\n");
|
||||
if (file)
|
||||
{
|
||||
time2iso(mtime_str, 100, file->mtime);
|
||||
fprintf(out, "mtime=%lu(%s)\n", file->mtime, mtime_str);
|
||||
fprintf(out, "size=" UINT64_FORMAT "\n", (uint64)file->size);
|
||||
fprintf(out, "read_size=" UINT64_FORMAT "\n", (uint64)file->read_size);
|
||||
fprintf(out, "write_size=" UINT64_FORMAT "\n", (uint64)file->write_size);
|
||||
fprintf(out, "mode=0%o\n", file->mode);
|
||||
fprintf(out, "crc=%u\n", file->crc);
|
||||
fprintf(out, "is_datafile=%s\n", file->is_datafile ? "true" : "false");
|
||||
fprintf(out, "linked=\"%s\"\n", file->linked ? file->linked : "nil");
|
||||
fprintf(out, "path=\"%s\"\n", file->path);
|
||||
}
|
||||
fprintf(out, "=================\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete file pointed by the pgFile.
|
||||
* If the pgFile points directory, the directory must be empty.
|
||||
@ -141,11 +119,11 @@ delete_file:
|
||||
pg_crc32
|
||||
pgFileGetCRC(pgFile *file)
|
||||
{
|
||||
FILE *fp;
|
||||
pg_crc32 crc = 0;
|
||||
char buf[1024];
|
||||
size_t len;
|
||||
int errno_tmp;
|
||||
FILE *fp;
|
||||
pg_crc32 crc = 0;
|
||||
char buf[1024];
|
||||
size_t len;
|
||||
int errno_tmp;
|
||||
|
||||
/* open file in binary read mode */
|
||||
fp = fopen(file->path, "r");
|
||||
@ -211,7 +189,8 @@ pgFileCompareMtime(const void *f1, const void *f2)
|
||||
return 1;
|
||||
else if (f1p->mtime < f2p->mtime)
|
||||
return -1;
|
||||
else return 0;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Compare two pgFile with their modify timestamp in descending order. */
|
||||
@ -287,11 +266,11 @@ dir_list_file(parray *files, const char *root, const char *exclude[], bool omit_
|
||||
*/
|
||||
while (S_ISDIR(file->mode))
|
||||
{
|
||||
int i;
|
||||
bool skip = false;
|
||||
DIR *dir;
|
||||
struct dirent *dent;
|
||||
char *dirname;
|
||||
int i;
|
||||
bool skip = false;
|
||||
DIR *dir;
|
||||
struct dirent *dent;
|
||||
char *dirname;
|
||||
|
||||
/* skip entry which matches exclude list */
|
||||
dirname = strrchr(file->path, '/');
|
||||
@ -566,3 +545,27 @@ dir_copy_files(const char *from_root, const char *to_root)
|
||||
parray_walk(files, pgFileFree);
|
||||
parray_free(files);
|
||||
}
|
||||
|
||||
#ifdef NOT_USED
|
||||
void
|
||||
pgFileDump(pgFile *file, FILE *out)
|
||||
{
|
||||
char mtime_str[100];
|
||||
|
||||
fprintf(out, "=================\n");
|
||||
if (file)
|
||||
{
|
||||
time2iso(mtime_str, 100, file->mtime);
|
||||
fprintf(out, "mtime=%lu(%s)\n", file->mtime, mtime_str);
|
||||
fprintf(out, "size=" UINT64_FORMAT "\n", (uint64)file->size);
|
||||
fprintf(out, "read_size=" UINT64_FORMAT "\n", (uint64)file->read_size);
|
||||
fprintf(out, "write_size=" UINT64_FORMAT "\n", (uint64)file->write_size);
|
||||
fprintf(out, "mode=0%o\n", file->mode);
|
||||
fprintf(out, "crc=%u\n", file->crc);
|
||||
fprintf(out, "is_datafile=%s\n", file->is_datafile ? "true" : "false");
|
||||
fprintf(out, "linked=\"%s\"\n", file->linked ? file->linked : "nil");
|
||||
fprintf(out, "path=\"%s\"\n", file->path);
|
||||
}
|
||||
fprintf(out, "=================\n");
|
||||
}
|
||||
#endif
|
||||
|
@ -235,7 +235,6 @@ extern parray *dir_read_file_list(const char *root, const char *file_txt);
|
||||
extern int dir_create_dir(const char *path, mode_t mode);
|
||||
extern void dir_copy_files(const char *from_root, const char *to_root);
|
||||
|
||||
extern void pgFileDump(pgFile *file, FILE *out);
|
||||
extern void pgFileDelete(pgFile *file);
|
||||
extern void pgFileFree(void *file);
|
||||
extern pg_crc32 pgFileGetCRC(pgFile *file);
|
||||
|
12
restore.c
12
restore.c
@ -156,9 +156,7 @@ do_restore(const char *target_time,
|
||||
}
|
||||
#endif
|
||||
if (satisfy_timeline(timelines, base_backup))
|
||||
{
|
||||
goto base_backup_found;
|
||||
}
|
||||
}
|
||||
/* no full backup found, can't restore */
|
||||
elog(ERROR_NO_BACKUP, _("no full backup found, can't restore."));
|
||||
@ -291,12 +289,12 @@ base_backup_found:
|
||||
void
|
||||
restore_database(pgBackup *backup)
|
||||
{
|
||||
char timestamp[100];
|
||||
char path[MAXPGPATH];
|
||||
char list_path[MAXPGPATH];
|
||||
int ret;
|
||||
char timestamp[100];
|
||||
char path[MAXPGPATH];
|
||||
char list_path[MAXPGPATH];
|
||||
int ret;
|
||||
parray *files;
|
||||
int i;
|
||||
int i;
|
||||
|
||||
/* confirm block size compatibility */
|
||||
if (backup->block_size != BLCKSZ)
|
||||
|
@ -20,7 +20,7 @@ static bool pgBackupValidateFiles(parray *files, const char *root, bool size_onl
|
||||
int
|
||||
do_validate(pgBackupRange *range)
|
||||
{
|
||||
int i;
|
||||
int i;
|
||||
parray *backup_list;
|
||||
|
||||
catalog_lock();
|
||||
@ -133,6 +133,7 @@ pgBackupValidateFiles(parray *files, const char *root, bool size_only)
|
||||
for (i = 0; i < parray_num(files); i++)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
pgFile *file = (pgFile *) parray_get(files, i);
|
||||
|
||||
if (interrupted)
|
||||
@ -168,7 +169,8 @@ pgBackupValidateFiles(parray *files, const char *root, bool size_only)
|
||||
/* validate CRC too */
|
||||
if (!size_only)
|
||||
{
|
||||
pg_crc32 crc;
|
||||
pg_crc32 crc;
|
||||
|
||||
crc = pgFileGetCRC(file);
|
||||
if (crc != file->crc)
|
||||
{
|
||||
|
70
xlog.c
70
xlog.c
@ -26,29 +26,17 @@ typedef struct MemoryContextData *MemoryContext;
|
||||
#define XLOG_PAGE_MAGIC_v82 0xD05E /* 8.2 */
|
||||
#define XLOG_PAGE_MAGIC_v83 0xD062 /* 8.3 */
|
||||
#define XLOG_PAGE_MAGIC_v84 0xD063 /* 8.4 */
|
||||
#define XLOG_PAGE_MAGIC_v85 0xD063 /* 8.5 */
|
||||
|
||||
typedef struct XLogLongPageHeaderData_v81
|
||||
{
|
||||
XLogPageHeaderData std;
|
||||
uint64 xlp_sysid;
|
||||
uint32 xlp_seg_size;
|
||||
} XLogLongPageHeaderData_v81, *XLogLongPageHeader_v81;
|
||||
|
||||
typedef struct XLogLongPageHeaderData_v82
|
||||
{
|
||||
XLogPageHeaderData std; /* standard header fields */
|
||||
uint64 xlp_sysid; /* system identifier from pg_control */
|
||||
uint32 xlp_seg_size; /* just as a cross-check */
|
||||
uint32 xlp_xlog_blcksz; /* just as a cross-check */
|
||||
} XLogLongPageHeaderData_v82, *XLogLongPageHeader_v82;
|
||||
#define XLOG_PAGE_MAGIC_v85 0xD166 /* 8.5 */
|
||||
|
||||
/*
|
||||
* XLogLongPageHeaderData is modified in 8.3, but the layout is compatible
|
||||
* except xlp_xlog_blcksz.
|
||||
*/
|
||||
typedef union XLogPage
|
||||
{
|
||||
XLogPageHeaderData header;
|
||||
XLogLongPageHeaderData_v81 long_v81; /* 8.1 - 8.2 */
|
||||
XLogLongPageHeaderData_v82 long_v82; /* 8.3 - */
|
||||
char data[XLOG_BLCKSZ];
|
||||
XLogPageHeaderData header;
|
||||
XLogLongPageHeaderData lheader;
|
||||
char data[XLOG_BLCKSZ];
|
||||
} XLogPage;
|
||||
|
||||
/*
|
||||
@ -73,7 +61,9 @@ xlog_is_complete_wal(const pgFile *file, int server_version)
|
||||
fclose(fp);
|
||||
|
||||
/* xlog_page_magic from server version */
|
||||
if (server_version < 80100)
|
||||
if (server_version < 80000)
|
||||
return false; /* never happen */
|
||||
else if (server_version < 80100)
|
||||
xlog_page_magic = XLOG_PAGE_MAGIC_v80;
|
||||
else if (server_version < 80200)
|
||||
xlog_page_magic = XLOG_PAGE_MAGIC_v81;
|
||||
@ -83,37 +73,29 @@ xlog_is_complete_wal(const pgFile *file, int server_version)
|
||||
xlog_page_magic = XLOG_PAGE_MAGIC_v83;
|
||||
else if (server_version < 80500)
|
||||
xlog_page_magic = XLOG_PAGE_MAGIC_v84;
|
||||
else
|
||||
else if (server_version < 80600)
|
||||
xlog_page_magic = XLOG_PAGE_MAGIC_v85;
|
||||
else
|
||||
return false; /* not supported */
|
||||
|
||||
/* check header */
|
||||
if (page.header.xlp_magic != xlog_page_magic)
|
||||
return false;
|
||||
if ((page.header.xlp_info & ~XLP_ALL_FLAGS) != 0)
|
||||
return false;
|
||||
if (page.header.xlp_info & XLP_LONG_HEADER)
|
||||
{
|
||||
if (page.long_v81.xlp_seg_size != XLogSegSize)
|
||||
return false;
|
||||
if ((page.header.xlp_info & XLP_LONG_HEADER) == 0)
|
||||
return false;
|
||||
if (page.lheader.xlp_seg_size != XLogSegSize)
|
||||
return false;
|
||||
if (server_version >= 80300 && page.lheader.xlp_xlog_blcksz != XLOG_BLCKSZ)
|
||||
return false;
|
||||
|
||||
/* compressed WAL (with lesslog) has 0 in lheader->xlp_xlog_blcksz. */
|
||||
if (server_version >= 80300)
|
||||
{
|
||||
if (page.long_v82.xlp_xlog_blcksz == XLOG_BLCKSZ)
|
||||
{
|
||||
/* check size (actual file size, not backup file size) */
|
||||
if (file->size != XLogSegSize)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (page.long_v82.xlp_xlog_blcksz != 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (file->size != XLogSegSize)
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
* check size (actual file size, not backup file size)
|
||||
* TODO: Support pre-compressed xlog. They might have different file sizes.
|
||||
*/
|
||||
if (file->size != XLogSegSize)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user