mirror of
https://github.com/postgrespro/pg_probackup.git
synced 2026-06-21 01:34:15 +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:
@@ -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,
|
static void delete_old_files(const char *root, parray *files, int keep_files,
|
||||||
int keep_days, int server_version, bool is_arclog);
|
int keep_days, int server_version, bool is_arclog);
|
||||||
static void backup_files(const char *from_root, const char *to_root,
|
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_database(parray *backup_list, bool smooth_checkpoint);
|
||||||
static parray *do_backup_arclog(parray *backup_list);
|
static parray *do_backup_arclog(parray *backup_list);
|
||||||
static parray *do_backup_srvlog(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();
|
files = parray_new();
|
||||||
dir_list_file(files, pgdata, pgdata_exclude, true, false);
|
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++)
|
for (i = 0; i < parray_num(files); i++)
|
||||||
{
|
{
|
||||||
pgFile *file = (pgFile *) parray_get(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))
|
if (!S_ISREG(file->mode))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* data files are under base/global/pg_tblspc */
|
/* data files are under "base", "global", or "pg_tblspc" */
|
||||||
relative = file->path + strlen(pgdata) + 1;
|
relative = file->path + strlen(pgdata) + 1;
|
||||||
if (!path_is_prefix_of_path("base", relative) &&
|
if (!path_is_prefix_of_path("base", relative) &&
|
||||||
!path_is_prefix_of_path("global", relative) &&
|
!path_is_prefix_of_path("global", relative) &&
|
||||||
@@ -214,16 +214,16 @@ do_backup_database(parray *backup_list, bool smooth_checkpoint)
|
|||||||
static parray *
|
static parray *
|
||||||
do_backup_arclog(parray *backup_list)
|
do_backup_arclog(parray *backup_list)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
parray *files;
|
parray *files;
|
||||||
parray *prev_files = NULL; /* file list of previous database backup */
|
parray *prev_files = NULL; /* file list of previous database backup */
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
char path[MAXPGPATH];
|
char path[MAXPGPATH];
|
||||||
char timeline_dir[MAXPGPATH];
|
char timeline_dir[MAXPGPATH];
|
||||||
char prev_file_txt[MAXPGPATH];
|
char prev_file_txt[MAXPGPATH];
|
||||||
pgBackup *prev_backup;
|
pgBackup *prev_backup;
|
||||||
int64 arclog_write_bytes = 0;
|
int64 arclog_write_bytes = 0;
|
||||||
char last_wal[MAXPGPATH];
|
char last_wal[MAXPGPATH];
|
||||||
|
|
||||||
if (!HAVE_ARCLOG(¤t))
|
if (!HAVE_ARCLOG(¤t))
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -345,14 +345,14 @@ do_backup_arclog(parray *backup_list)
|
|||||||
static parray *
|
static parray *
|
||||||
do_backup_srvlog(parray *backup_list)
|
do_backup_srvlog(parray *backup_list)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
parray *files;
|
parray *files;
|
||||||
parray *prev_files = NULL; /* file list of previous database backup */
|
parray *prev_files = NULL; /* file list of previous database backup */
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
char path[MAXPGPATH];
|
char path[MAXPGPATH];
|
||||||
char prev_file_txt[MAXPGPATH];
|
char prev_file_txt[MAXPGPATH];
|
||||||
pgBackup *prev_backup;
|
pgBackup *prev_backup;
|
||||||
int64 srvlog_write_bytes = 0;
|
int64 srvlog_write_bytes = 0;
|
||||||
|
|
||||||
if (!current.with_serverlog)
|
if (!current.with_serverlog)
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -818,10 +818,14 @@ backup_cleanup(bool fatal, void *userdata)
|
|||||||
|
|
||||||
/* take incremental backup. */
|
/* take incremental backup. */
|
||||||
static void
|
static void
|
||||||
backup_files(const char *from_root, const char *to_root, parray *files,
|
backup_files(const char *from_root,
|
||||||
parray *prev_files, XLogRecPtr *lsn, bool compress_data)
|
const char *to_root,
|
||||||
|
parray *files,
|
||||||
|
parray *prev_files,
|
||||||
|
const XLogRecPtr *lsn,
|
||||||
|
bool compress)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
|
|
||||||
/* sort pathname ascending */
|
/* 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 */
|
/* backup a file or create a directory */
|
||||||
for (i = 0; i < parray_num(files); i++)
|
for (i = 0; i < parray_num(files); i++)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct stat buf;
|
struct stat buf;
|
||||||
|
|
||||||
pgFile *file = (pgFile *) parray_get(files, i);
|
pgFile *file = (pgFile *) parray_get(files, i);
|
||||||
|
|
||||||
/* check for interrupt */
|
/* 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))
|
if (S_ISDIR(buf.st_mode))
|
||||||
{
|
{
|
||||||
char dirpath[MAXPGPATH];
|
char dirpath[MAXPGPATH];
|
||||||
|
|
||||||
snprintf(dirpath, lengthof(dirpath), "%s/%s", to_root,
|
snprintf(dirpath, lengthof(dirpath), "%s/%s", to_root,
|
||||||
file->path + strlen(from_root) + 1);
|
file->path + strlen(from_root) + 1);
|
||||||
if (!check)
|
if (!check)
|
||||||
@@ -918,7 +924,7 @@ backup_files(const char *from_root, const char *to_root, parray *files,
|
|||||||
/* copy the file into backup */
|
/* copy the file into backup */
|
||||||
if (file->is_datafile)
|
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)
|
if (file->write_size == 0 && file->read_size > 0)
|
||||||
{
|
{
|
||||||
/* record as skipped file in file_xxx.txt */
|
/* 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
|
else
|
||||||
copy_file(from_root, to_root, file,
|
copy_file(from_root, to_root, file,
|
||||||
compress_data ? COMPRESSION : NO_COMPRESSION);
|
compress ? COMPRESSION : NO_COMPRESSION);
|
||||||
|
|
||||||
if (verbose)
|
if (verbose)
|
||||||
{
|
{
|
||||||
@@ -965,11 +971,10 @@ delete_old_files(const char *root,
|
|||||||
int server_version,
|
int server_version,
|
||||||
bool is_arclog)
|
bool is_arclog)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int j;
|
int j;
|
||||||
int file_num = 0;
|
int file_num = 0;
|
||||||
time_t days_threashold =
|
time_t days_threshold = current.start_time - (keep_days * 60 * 60 * 24);
|
||||||
current.start_time - (keep_days * 60 * 60 * 24);
|
|
||||||
|
|
||||||
if (verbose)
|
if (verbose)
|
||||||
{
|
{
|
||||||
@@ -990,7 +995,7 @@ delete_old_files(const char *root,
|
|||||||
root, files_str, days_str);
|
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)
|
if (keep_files == KEEP_INFINITE || keep_days == KEEP_INFINITE)
|
||||||
{
|
{
|
||||||
elog(LOG, "%s() infinite", __FUNCTION__);
|
elog(LOG, "%s() infinite", __FUNCTION__);
|
||||||
@@ -1003,7 +1008,7 @@ delete_old_files(const char *root,
|
|||||||
pgFile *file = (pgFile *) parray_get(files, i);
|
pgFile *file = (pgFile *) parray_get(files, i);
|
||||||
|
|
||||||
elog(LOG, "%s() %s", __FUNCTION__, file->path);
|
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))
|
if (is_arclog && !xlog_is_complete_wal(file, server_version))
|
||||||
{
|
{
|
||||||
elog(LOG, "%s() not complete WAL", __FUNCTION__);
|
elog(LOG, "%s() not complete WAL", __FUNCTION__);
|
||||||
@@ -1013,17 +1018,17 @@ delete_old_files(const char *root,
|
|||||||
file_num++;
|
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.
|
* 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__,
|
elog(LOG, "%s() %lu is not older than %lu", __FUNCTION__,
|
||||||
file->mtime, days_threashold);
|
file->mtime, days_threshold);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
elog(LOG, "%s() %lu is older than %lu", __FUNCTION__,
|
elog(LOG, "%s() %lu is older than %lu", __FUNCTION__,
|
||||||
file->mtime, days_threashold);
|
file->mtime, days_threshold);
|
||||||
|
|
||||||
if (file_num <= keep_files)
|
if (file_num <= keep_files)
|
||||||
{
|
{
|
||||||
@@ -1035,7 +1040,7 @@ delete_old_files(const char *root,
|
|||||||
if (verbose)
|
if (verbose)
|
||||||
printf(_("delete \"%s\"\n"), file->path + strlen(root) + 1);
|
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);
|
file = (pgFile *) parray_remove(files, i);
|
||||||
for (j = parray_num(files) - 1; j >= 0; j--)
|
for (j = parray_num(files) - 1; j >= 0; j--)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -247,8 +247,8 @@ err_proc:
|
|||||||
pgBackup *
|
pgBackup *
|
||||||
catalog_get_last_data_backup(parray *backup_list)
|
catalog_get_last_data_backup(parray *backup_list)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
pgBackup *backup = NULL;
|
pgBackup *backup = NULL;
|
||||||
|
|
||||||
/* backup_list is sorted in order of descending ID */
|
/* backup_list is sorted in order of descending ID */
|
||||||
for (i = 0; i < parray_num(backup_list); i++)
|
for (i = 0; i < parray_num(backup_list); i++)
|
||||||
@@ -269,8 +269,8 @@ catalog_get_last_data_backup(parray *backup_list)
|
|||||||
pgBackup *
|
pgBackup *
|
||||||
catalog_get_last_arclog_backup(parray *backup_list)
|
catalog_get_last_arclog_backup(parray *backup_list)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
pgBackup *backup = NULL;
|
pgBackup *backup = NULL;
|
||||||
|
|
||||||
/* backup_list is sorted in order of descending ID */
|
/* backup_list is sorted in order of descending ID */
|
||||||
for (i = 0; i < parray_num(backup_list); i++)
|
for (i = 0; i < parray_num(backup_list); i++)
|
||||||
|
|||||||
@@ -261,7 +261,8 @@ parse_page(const DataPage *page, int server_version,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*offset = *length = 0;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,7 +274,7 @@ parse_page(const DataPage *page, int server_version,
|
|||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
backup_data_file(const char *from_root, const char *to_root,
|
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];
|
char to_path[MAXPGPATH];
|
||||||
FILE *in;
|
FILE *in;
|
||||||
@@ -324,7 +325,7 @@ backup_data_file(const char *from_root, const char *to_root,
|
|||||||
file->write_size = 0;
|
file->write_size = 0;
|
||||||
|
|
||||||
#ifdef HAVE_LIBZ
|
#ifdef HAVE_LIBZ
|
||||||
if (compress_data)
|
if (compress)
|
||||||
{
|
{
|
||||||
z.zalloc = Z_NULL;
|
z.zalloc = Z_NULL;
|
||||||
z.zfree = Z_NULL;
|
z.zfree = Z_NULL;
|
||||||
@@ -366,7 +367,7 @@ backup_data_file(const char *from_root, const char *to_root,
|
|||||||
fclose(out);
|
fclose(out);
|
||||||
file->is_datafile = false;
|
file->is_datafile = false;
|
||||||
copy_file(from_root, to_root, file,
|
copy_file(from_root, to_root, file,
|
||||||
compress_data ? COMPRESSION : NO_COMPRESSION);
|
compress ? COMPRESSION : NO_COMPRESSION);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,7 +381,7 @@ backup_data_file(const char *from_root, const char *to_root,
|
|||||||
upper_length = BLCKSZ - upper_offset;
|
upper_length = BLCKSZ - upper_offset;
|
||||||
|
|
||||||
#ifdef HAVE_LIBZ
|
#ifdef HAVE_LIBZ
|
||||||
if (compress_data)
|
if (compress)
|
||||||
{
|
{
|
||||||
doDeflate(&z, sizeof(header), sizeof(outbuf), &header, outbuf, in,
|
doDeflate(&z, sizeof(header), sizeof(outbuf), &header, outbuf, in,
|
||||||
out, &crc, &file->write_size, Z_NO_FLUSH);
|
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;
|
header.hole_length = 0;
|
||||||
|
|
||||||
#ifdef HAVE_LIBZ
|
#ifdef HAVE_LIBZ
|
||||||
if (compress_data)
|
if (compress)
|
||||||
{
|
{
|
||||||
doDeflate(&z, sizeof(header), sizeof(outbuf), &header, outbuf,
|
doDeflate(&z, sizeof(header), sizeof(outbuf), &header, outbuf,
|
||||||
in, out, &crc, &file->write_size, Z_NO_FLUSH);
|
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 */
|
/* write odd size page image */
|
||||||
#ifdef HAVE_LIBZ
|
#ifdef HAVE_LIBZ
|
||||||
if (compress_data)
|
if (compress)
|
||||||
{
|
{
|
||||||
doDeflate(&z, read_len, sizeof(outbuf), page.data, outbuf, in, out,
|
doDeflate(&z, read_len, sizeof(outbuf), page.data, outbuf, in, out,
|
||||||
&crc, &file->write_size, Z_NO_FLUSH);
|
&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
|
#ifdef HAVE_LIBZ
|
||||||
if (compress_data)
|
if (compress)
|
||||||
{
|
{
|
||||||
if (file->read_size > 0)
|
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);
|
elog(ERROR_SYSTEM, _("can't close compression stream: %s"), z.msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
/* update file permission */
|
|
||||||
|
/*
|
||||||
|
* update file permission
|
||||||
|
* FIXME: Should set permission on open?
|
||||||
|
*/
|
||||||
if (!check && chmod(to_path, FILE_PERMISSION) == -1)
|
if (!check && chmod(to_path, FILE_PERMISSION) == -1)
|
||||||
{
|
{
|
||||||
int errno_tmp = errno;
|
int errno_tmp = errno;
|
||||||
@@ -551,26 +555,29 @@ backup_data_file(const char *from_root, const char *to_root,
|
|||||||
* same relative path.
|
* same relative path.
|
||||||
*/
|
*/
|
||||||
void
|
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];
|
char to_path[MAXPGPATH];
|
||||||
FILE *in;
|
FILE *in;
|
||||||
FILE *out;
|
FILE *out;
|
||||||
BackupPageHeader header;
|
BackupPageHeader header;
|
||||||
BlockNumber blknum;
|
BlockNumber blknum;
|
||||||
#ifdef HAVE_LIBZ
|
#ifdef HAVE_LIBZ
|
||||||
z_stream z;
|
z_stream z;
|
||||||
int status;
|
int status;
|
||||||
char inbuf[zlibInSize];
|
char inbuf[zlibInSize];
|
||||||
pg_crc32 crc;
|
pg_crc32 crc;
|
||||||
size_t read_size;
|
size_t read_size;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* If the file is not a datafile, copy it. */
|
/* If the file is not a datafile, just copy it. */
|
||||||
if (!file->is_datafile)
|
if (!file->is_datafile)
|
||||||
{
|
{
|
||||||
copy_file(from_root, to_root, file,
|
copy_file(from_root, to_root, file,
|
||||||
compress_data ? DECOMPRESSION : NO_COMPRESSION);
|
compress ? DECOMPRESSION : NO_COMPRESSION);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -582,10 +589,16 @@ restore_data_file(const char *from_root, const char *to_root, pgFile *file, bool
|
|||||||
strerror(errno));
|
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,
|
snprintf(to_path, lengthof(to_path), "%s/%s", to_root,
|
||||||
file->path + strlen(from_root) + 1);
|
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)
|
if (out == NULL)
|
||||||
{
|
{
|
||||||
int errno_tmp = errno;
|
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
|
#ifdef HAVE_LIBZ
|
||||||
if (compress_data)
|
if (compress)
|
||||||
{
|
{
|
||||||
z.zalloc = Z_NULL;
|
z.zalloc = Z_NULL;
|
||||||
z.zfree = 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
|
#endif
|
||||||
|
|
||||||
for (blknum = 0; ;blknum++)
|
for (blknum = 0; ; blknum++)
|
||||||
{
|
{
|
||||||
size_t read_len;
|
size_t read_len;
|
||||||
DataPage page; /* used as read buffer */
|
DataPage page; /* used as read buffer */
|
||||||
int upper_offset;
|
int upper_offset;
|
||||||
int upper_length;
|
int upper_length;
|
||||||
|
|
||||||
/* read BackupPageHeader */
|
/* read BackupPageHeader */
|
||||||
#ifdef HAVE_LIBZ
|
#ifdef HAVE_LIBZ
|
||||||
if (compress_data)
|
if (compress)
|
||||||
{
|
{
|
||||||
status = doInflate(&z, sizeof(inbuf), sizeof(header), inbuf,
|
status = doInflate(&z, sizeof(inbuf), sizeof(header), inbuf,
|
||||||
&header, in, out, &crc, &read_size);
|
&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 */
|
/* read lower/upper into page.data and restore hole */
|
||||||
memset(page.data + header.hole_offset, 0, header.hole_length);
|
memset(page.data + header.hole_offset, 0, header.hole_length);
|
||||||
|
|
||||||
#ifdef HAVE_LIBZ
|
#ifdef HAVE_LIBZ
|
||||||
if (compress_data)
|
if (compress)
|
||||||
{
|
{
|
||||||
elog(LOG, "\n%s() %s %d %d", __FUNCTION__, file->path, header.hole_offset, upper_length);
|
elog(LOG, "\n%s() %s %d %d", __FUNCTION__, file->path, header.hole_offset, upper_length);
|
||||||
if (header.hole_offset > 0)
|
if (header.hole_offset > 0)
|
||||||
{
|
{
|
||||||
doInflate(&z, sizeof(inbuf), header.hole_offset, inbuf, page.data,
|
doInflate(&z, sizeof(inbuf), header.hole_offset, inbuf,
|
||||||
in, out, &crc, &read_size);
|
page.data, in, out, &crc, &read_size);
|
||||||
if (z.avail_out != 0)
|
if (z.avail_out != 0)
|
||||||
elog(ERROR_SYSTEM, _("can't read block %u of \"%s\""),
|
elog(ERROR_SYSTEM, _("can't read block %u of \"%s\""),
|
||||||
blknum, file->path);
|
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)
|
* Seek and write the restored page. Backup might have holes in
|
||||||
{
|
* incremental backups.
|
||||||
if (fseek(out, (header.block - blknum) * BLCKSZ, SEEK_CUR) < 0)
|
*/
|
||||||
elog(ERROR_SYSTEM, _("can't seek restore target file \"%s\": %s"),
|
blknum = header.block;
|
||||||
to_path, strerror(errno));
|
if (fseek(out, blknum * BLCKSZ, SEEK_SET) < 0)
|
||||||
blknum = header.block;
|
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))
|
if (fwrite(page.data, 1, sizeof(page), out) != sizeof(page))
|
||||||
{
|
|
||||||
elog(ERROR_SYSTEM, _("can't write block %u of \"%s\": %s"),
|
elog(ERROR_SYSTEM, _("can't write block %u of \"%s\": %s"),
|
||||||
blknum, file->path, strerror(errno));
|
blknum, file->path, strerror(errno));
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_LIBZ
|
#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);
|
elog(ERROR_SYSTEM, _("can't close compression stream: %s"), z.msg);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -742,14 +752,14 @@ void
|
|||||||
copy_file(const char *from_root, const char *to_root, pgFile *file,
|
copy_file(const char *from_root, const char *to_root, pgFile *file,
|
||||||
CompressionMode mode)
|
CompressionMode mode)
|
||||||
{
|
{
|
||||||
char to_path[MAXPGPATH];
|
char to_path[MAXPGPATH];
|
||||||
FILE *in;
|
FILE *in;
|
||||||
FILE *out;
|
FILE *out;
|
||||||
size_t read_len = 0;
|
size_t read_len = 0;
|
||||||
int errno_tmp;
|
int errno_tmp;
|
||||||
char buf[8192];
|
char buf[8192];
|
||||||
struct stat st;
|
struct stat st;
|
||||||
pg_crc32 crc;
|
pg_crc32 crc;
|
||||||
#ifdef HAVE_LIBZ
|
#ifdef HAVE_LIBZ
|
||||||
z_stream z;
|
z_stream z;
|
||||||
int status;
|
int status;
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ pgBackupDelete(int keep_generations, int keep_days)
|
|||||||
int i;
|
int i;
|
||||||
parray *backup_list;
|
parray *backup_list;
|
||||||
int backup_num;
|
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)
|
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.
|
* 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__,
|
elog(LOG, "%s() %lu is not older than %lu", __FUNCTION__,
|
||||||
backup->start_time, days_threashold);
|
backup->start_time, days_threshold);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
elog(LOG, "%s() %lu is older than %lu", __FUNCTION__,
|
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 */
|
/* delete backup and update status to DELETED */
|
||||||
pgBackupDeleteFiles(backup);
|
pgBackupDeleteFiles(backup);
|
||||||
|
|||||||
@@ -56,8 +56,8 @@ dir_create_dir(const char *dir, mode_t mode)
|
|||||||
static pgFile *
|
static pgFile *
|
||||||
pgFileNew(const char *path, bool omit_symlink)
|
pgFileNew(const char *path, bool omit_symlink)
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
pgFile *file;
|
pgFile *file;
|
||||||
|
|
||||||
/* stat the file */
|
/* stat the file */
|
||||||
if ((omit_symlink ? stat(path, &st) : lstat(path, &st)) == -1)
|
if ((omit_symlink ? stat(path, &st) : lstat(path, &st)) == -1)
|
||||||
@@ -84,28 +84,6 @@ pgFileNew(const char *path, bool omit_symlink)
|
|||||||
return file;
|
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.
|
* Delete file pointed by the pgFile.
|
||||||
* If the pgFile points directory, the directory must be empty.
|
* If the pgFile points directory, the directory must be empty.
|
||||||
@@ -141,11 +119,11 @@ delete_file:
|
|||||||
pg_crc32
|
pg_crc32
|
||||||
pgFileGetCRC(pgFile *file)
|
pgFileGetCRC(pgFile *file)
|
||||||
{
|
{
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
pg_crc32 crc = 0;
|
pg_crc32 crc = 0;
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
size_t len;
|
size_t len;
|
||||||
int errno_tmp;
|
int errno_tmp;
|
||||||
|
|
||||||
/* open file in binary read mode */
|
/* open file in binary read mode */
|
||||||
fp = fopen(file->path, "r");
|
fp = fopen(file->path, "r");
|
||||||
@@ -211,7 +189,8 @@ pgFileCompareMtime(const void *f1, const void *f2)
|
|||||||
return 1;
|
return 1;
|
||||||
else if (f1p->mtime < f2p->mtime)
|
else if (f1p->mtime < f2p->mtime)
|
||||||
return -1;
|
return -1;
|
||||||
else return 0;
|
else
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Compare two pgFile with their modify timestamp in descending order. */
|
/* 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))
|
while (S_ISDIR(file->mode))
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
bool skip = false;
|
bool skip = false;
|
||||||
DIR *dir;
|
DIR *dir;
|
||||||
struct dirent *dent;
|
struct dirent *dent;
|
||||||
char *dirname;
|
char *dirname;
|
||||||
|
|
||||||
/* skip entry which matches exclude list */
|
/* skip entry which matches exclude list */
|
||||||
dirname = strrchr(file->path, '/');
|
dirname = strrchr(file->path, '/');
|
||||||
@@ -566,3 +545,27 @@ dir_copy_files(const char *from_root, const char *to_root)
|
|||||||
parray_walk(files, pgFileFree);
|
parray_walk(files, pgFileFree);
|
||||||
parray_free(files);
|
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 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 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 pgFileDelete(pgFile *file);
|
||||||
extern void pgFileFree(void *file);
|
extern void pgFileFree(void *file);
|
||||||
extern pg_crc32 pgFileGetCRC(pgFile *file);
|
extern pg_crc32 pgFileGetCRC(pgFile *file);
|
||||||
|
|||||||
@@ -156,9 +156,7 @@ do_restore(const char *target_time,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (satisfy_timeline(timelines, base_backup))
|
if (satisfy_timeline(timelines, base_backup))
|
||||||
{
|
|
||||||
goto base_backup_found;
|
goto base_backup_found;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/* no full backup found, can't restore */
|
/* no full backup found, can't restore */
|
||||||
elog(ERROR_NO_BACKUP, _("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
|
void
|
||||||
restore_database(pgBackup *backup)
|
restore_database(pgBackup *backup)
|
||||||
{
|
{
|
||||||
char timestamp[100];
|
char timestamp[100];
|
||||||
char path[MAXPGPATH];
|
char path[MAXPGPATH];
|
||||||
char list_path[MAXPGPATH];
|
char list_path[MAXPGPATH];
|
||||||
int ret;
|
int ret;
|
||||||
parray *files;
|
parray *files;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* confirm block size compatibility */
|
/* confirm block size compatibility */
|
||||||
if (backup->block_size != BLCKSZ)
|
if (backup->block_size != BLCKSZ)
|
||||||
|
|||||||
+4
-2
@@ -20,7 +20,7 @@ static bool pgBackupValidateFiles(parray *files, const char *root, bool size_onl
|
|||||||
int
|
int
|
||||||
do_validate(pgBackupRange *range)
|
do_validate(pgBackupRange *range)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
parray *backup_list;
|
parray *backup_list;
|
||||||
|
|
||||||
catalog_lock();
|
catalog_lock();
|
||||||
@@ -133,6 +133,7 @@ pgBackupValidateFiles(parray *files, const char *root, bool size_only)
|
|||||||
for (i = 0; i < parray_num(files); i++)
|
for (i = 0; i < parray_num(files); i++)
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
pgFile *file = (pgFile *) parray_get(files, i);
|
pgFile *file = (pgFile *) parray_get(files, i);
|
||||||
|
|
||||||
if (interrupted)
|
if (interrupted)
|
||||||
@@ -168,7 +169,8 @@ pgBackupValidateFiles(parray *files, const char *root, bool size_only)
|
|||||||
/* validate CRC too */
|
/* validate CRC too */
|
||||||
if (!size_only)
|
if (!size_only)
|
||||||
{
|
{
|
||||||
pg_crc32 crc;
|
pg_crc32 crc;
|
||||||
|
|
||||||
crc = pgFileGetCRC(file);
|
crc = pgFileGetCRC(file);
|
||||||
if (crc != file->crc)
|
if (crc != file->crc)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -26,29 +26,17 @@ typedef struct MemoryContextData *MemoryContext;
|
|||||||
#define XLOG_PAGE_MAGIC_v82 0xD05E /* 8.2 */
|
#define XLOG_PAGE_MAGIC_v82 0xD05E /* 8.2 */
|
||||||
#define XLOG_PAGE_MAGIC_v83 0xD062 /* 8.3 */
|
#define XLOG_PAGE_MAGIC_v83 0xD062 /* 8.3 */
|
||||||
#define XLOG_PAGE_MAGIC_v84 0xD063 /* 8.4 */
|
#define XLOG_PAGE_MAGIC_v84 0xD063 /* 8.4 */
|
||||||
#define XLOG_PAGE_MAGIC_v85 0xD063 /* 8.5 */
|
#define XLOG_PAGE_MAGIC_v85 0xD166 /* 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;
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XLogLongPageHeaderData is modified in 8.3, but the layout is compatible
|
||||||
|
* except xlp_xlog_blcksz.
|
||||||
|
*/
|
||||||
typedef union XLogPage
|
typedef union XLogPage
|
||||||
{
|
{
|
||||||
XLogPageHeaderData header;
|
XLogPageHeaderData header;
|
||||||
XLogLongPageHeaderData_v81 long_v81; /* 8.1 - 8.2 */
|
XLogLongPageHeaderData lheader;
|
||||||
XLogLongPageHeaderData_v82 long_v82; /* 8.3 - */
|
char data[XLOG_BLCKSZ];
|
||||||
char data[XLOG_BLCKSZ];
|
|
||||||
} XLogPage;
|
} XLogPage;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -73,7 +61,9 @@ xlog_is_complete_wal(const pgFile *file, int server_version)
|
|||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
||||||
/* xlog_page_magic from server version */
|
/* 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;
|
xlog_page_magic = XLOG_PAGE_MAGIC_v80;
|
||||||
else if (server_version < 80200)
|
else if (server_version < 80200)
|
||||||
xlog_page_magic = XLOG_PAGE_MAGIC_v81;
|
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;
|
xlog_page_magic = XLOG_PAGE_MAGIC_v83;
|
||||||
else if (server_version < 80500)
|
else if (server_version < 80500)
|
||||||
xlog_page_magic = XLOG_PAGE_MAGIC_v84;
|
xlog_page_magic = XLOG_PAGE_MAGIC_v84;
|
||||||
else
|
else if (server_version < 80600)
|
||||||
xlog_page_magic = XLOG_PAGE_MAGIC_v85;
|
xlog_page_magic = XLOG_PAGE_MAGIC_v85;
|
||||||
|
else
|
||||||
|
return false; /* not supported */
|
||||||
|
|
||||||
/* check header */
|
/* check header */
|
||||||
if (page.header.xlp_magic != xlog_page_magic)
|
if (page.header.xlp_magic != xlog_page_magic)
|
||||||
return false;
|
return false;
|
||||||
if ((page.header.xlp_info & ~XLP_ALL_FLAGS) != 0)
|
if ((page.header.xlp_info & ~XLP_ALL_FLAGS) != 0)
|
||||||
return false;
|
return false;
|
||||||
if (page.header.xlp_info & XLP_LONG_HEADER)
|
if ((page.header.xlp_info & XLP_LONG_HEADER) == 0)
|
||||||
{
|
return false;
|
||||||
if (page.long_v81.xlp_seg_size != XLogSegSize)
|
if (page.lheader.xlp_seg_size != XLogSegSize)
|
||||||
return false;
|
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)
|
* check size (actual file size, not backup file size)
|
||||||
{
|
* TODO: Support pre-compressed xlog. They might have different file sizes.
|
||||||
if (page.long_v82.xlp_xlog_blcksz == XLOG_BLCKSZ)
|
*/
|
||||||
{
|
if (file->size != XLogSegSize)
|
||||||
/* check size (actual file size, not backup file size) */
|
return false;
|
||||||
if (file->size != XLogSegSize)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (page.long_v82.xlp_xlog_blcksz != 0)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (file->size != XLogSegSize)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user