1
0
mirror of https://github.com/postgrespro/pg_probackup.git synced 2025-04-12 11:58:15 +02:00

First import.

git-svn-id: http://pg-rman.googlecode.com/svn/trunk@2 182aca00-e38e-11de-a668-6fd11605f5ce
This commit is contained in:
itagaki.takahiro 2009-12-08 00:21:28 +00:00
parent 007464ae8f
commit 7fbb857d03
49 changed files with 9525 additions and 0 deletions

26
COPYRIGHT Normal file
View File

@ -0,0 +1,26 @@
Copyright (c) 2008-2009, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the NIPPON TELEGRAPH AND TELEPHONE CORPORATION
(NTT) nor the names of its contributors may be used to endorse or
promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

40
Makefile Normal file
View File

@ -0,0 +1,40 @@
PROGRAM = pg_rman
SRCS = \
backup.c \
catalog.c \
data.c \
delete.c \
dir.c \
init.c \
parray.c \
pg_rman.c \
restore.c \
show.c \
util.c \
validate.c \
xlog.c \
pgsql_src/pg_ctl.c \
pgsql_src/pg_crc.c \
pgut/pgut.c \
pgut/pgut-port.c
OBJS = $(SRCS:.c=.o)
# pg_crc.c and are copied from PostgreSQL source tree.
# XXX for debug, add -g and disable optimization
PG_CPPFLAGS = -I$(libpq_srcdir)
PG_LIBS = $(libpq_pgport)
REGRESS = option init show_validate backup_restore
ifdef USE_PGXS
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
else
subdir = contrib/pg_rman
top_builddir = ../..
include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif
$(OBJS): pg_rman.h

1110
backup.c Normal file

File diff suppressed because it is too large Load Diff

593
catalog.c Normal file
View File

@ -0,0 +1,593 @@
/*-------------------------------------------------------------------------
*
* catalog.c: backup catalog opration
*
* Copyright (c) 2009, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include <stdlib.h>
#include <libgen.h>
#include <unistd.h>
#include <dirent.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/file.h>
#include "pgut/pgut-port.h"
static pgBackup *catalog_read_ini(const char *path);
#define BOOL_TO_STR(val) ((val) ? "true" : "false")
static int lock_fd = -1;
/*
* Lock of the catalog with pg_rman.ini file and return 0.
* If the lock is held by another one, return 1 immediately.
*/
int
catalog_lock(void)
{
int ret;
char id_path[MAXPGPATH];
snprintf(id_path, lengthof(id_path), "%s/%s", backup_path,
PG_RMAN_INI_FILE);
lock_fd = open(id_path, O_RDWR);
if (lock_fd == -1)
elog(errno == ENOENT ? ERROR_CORRUPTED : ERROR_SYSTEM,
_("can't open file \"%s\": %s"), id_path, strerror(errno));
ret = flock(lock_fd, LOCK_EX | LOCK_NB); /* non-blocking */
if (ret == -1)
{
if (errno == EWOULDBLOCK)
{
close(lock_fd);
return 1;
}
else
{
int errno_tmp = errno;
close(lock_fd);
elog(ERROR_SYSTEM, _("can't lock file \"%s\": %s"), id_path,
strerror(errno_tmp));
}
}
return 0;
}
/*
* Release catalog lock.
*/
void
catalog_unlock(void)
{
close(lock_fd);
lock_fd = -1;
}
/*
* Create a pgBackup which taken at timestamp.
* If no backup matches, return NULL.
*/
pgBackup *
catalog_get_backup(time_t timestamp)
{
pgBackup tmp;
char ini_path[MAXPGPATH];
tmp.start_time = timestamp;
pgBackupGetPath(&tmp, ini_path, lengthof(ini_path), BACKUP_INI_FILE);
return catalog_read_ini(ini_path);
}
static bool
IsDir(const char *dirpath, const DIR *dir, const struct dirent *ent)
{
#if defined(DT_DIR)
return ent->d_type == DT_DIR;
#elif defined(_finddata_t)
return (dir->dd_dta.attrib & FILE_ATTRIBUTE_DIRECTORY) != 0;
#else
char path[MAXPGPATH];
DWORD attr;
/* dirent.d_type does not exists */
strlcpy(path, dirpath, MAXPGPATH);
strlcat(path, "/", MAXPGPATH);
strlcat(path, ent->d_name, MAXPGPATH);
attr = GetFileAttributes(path);
return attr != (DWORD) -1 && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0;
#endif
}
/*
* Create list fo backups started between begin and end from backup catalog.
* If range was NULL, all of backup are listed.
* The list is sorted in order of descending start time.
*/
parray *
catalog_get_backup_list(const pgBackupRange *range)
{
const pgBackupRange range_all = { 0, 0 };
DIR *date_dir = NULL;
struct dirent *date_ent = NULL;
DIR *time_dir = NULL;
struct dirent *time_ent = NULL;
char date_path[MAXPGPATH];
parray *backups = NULL;
pgBackup *backup = NULL;
struct tm *tm;
char begin_date[100];
char begin_time[100];
char end_date[100];
char end_time[100];
if (range == NULL)
range = &range_all;
/* make date/time string */
tm = localtime(&range->begin);
strftime(begin_date, lengthof(begin_date), "%Y%m%d", tm);
strftime(begin_time, lengthof(begin_time), "%H%M%S", tm);
tm = localtime(&range->end);
strftime(end_date, lengthof(end_date), "%Y%m%d", tm);
strftime(end_time, lengthof(end_time), "%H%M%S", tm);
/* open backup root directory */
date_dir = opendir(backup_path);
if (date_dir == NULL)
{
elog(WARNING, _("can't open directory \"%s\": %s"), backup_path,
strerror(errno));
goto err_proc;
}
/* scan date/time directories and list backups in the range */
backups = parray_new();
for (; (date_ent = readdir(date_dir)) != NULL; errno = 0)
{
/* skip not-directory entries and hidden entries */
if (!IsDir(backup_path, date_dir, date_ent) || date_ent->d_name[0] == '.')
continue;
/* skip online WAL & serverlog backup directory */
if (strcmp(date_ent->d_name, RESTORE_WORK_DIR) == 0)
continue;
/* If the date is out of range, skip it. */
if (pgBackupRangeIsValid(range) &&
(strcmp(begin_date, date_ent->d_name) > 0 ||
strcmp(end_date, date_ent->d_name) < 0))
continue;
/* open subdirectory (date directory) and search time directory */
snprintf(date_path, MAXPGPATH, "%s/%s", backup_path, date_ent->d_name);
time_dir = opendir(date_path);
if (time_dir == NULL)
{
elog(WARNING, _("can't open directory \"%s\": %s"),
date_ent->d_name, strerror(errno));
goto err_proc;
}
for (; (time_ent = readdir(time_dir)) != NULL; errno = 0)
{
char ini_path[MAXPGPATH];
/* skip not-directry and hidden directories */
if (!IsDir(date_path, date_dir, time_ent) || time_ent->d_name[0] == '.')
continue;
/* If the time is out of range, skip it. */
if (pgBackupRangeIsValid(range) &&
(strcmp(begin_time, time_ent->d_name) > 0 ||
strcmp(end_time, time_ent->d_name) < 0))
continue;
/* read backup information from backup.ini */
snprintf(ini_path, MAXPGPATH, "%s/%s/%s", date_path,
time_ent->d_name, BACKUP_INI_FILE);
backup = catalog_read_ini(ini_path);
/* ignore corrupted backup */
if (backup)
{
parray_append(backups, backup);
backup = NULL;
}
}
if (errno && errno != ENOENT)
{
elog(WARNING, _("can't read date directory \"%s\": %s"),
date_ent->d_name, strerror(errno));
goto err_proc;
}
closedir(time_dir);
time_dir = NULL;
}
if (errno)
{
elog(WARNING, _("can't read backup root directory \"%s\": %s"),
backup_path, strerror(errno));
goto err_proc;
}
closedir(date_dir);
date_dir = NULL;
parray_qsort(backups, pgBackupCompareIdDesc);
return backups;
err_proc:
if (time_dir)
closedir(time_dir);
if (date_dir)
closedir(date_dir);
if (backup)
pgBackupFree(backup);
if (backups)
parray_walk(backups, pgBackupFree);
parray_free(backups);
return NULL;
}
/*
* Find the last completed database backup from the backup list.
*/
pgBackup *
catalog_get_last_data_backup(parray *backup_list)
{
int i;
pgBackup *backup = NULL;
/* backup_list is sorted in order of descending ID */
for (i = 0; i < parray_num(backup_list); i++)
{
backup = (pgBackup *) parray_get(backup_list, i);
/* we need completed database backup */
if (backup->status == BACKUP_STATUS_OK && HAVE_DATABASE(backup))
return backup;
}
return NULL;
}
/*
* Find the last completed archived WAL backup from the backup list.
*/
pgBackup *
catalog_get_last_arclog_backup(parray *backup_list)
{
int i;
pgBackup *backup = NULL;
/* backup_list is sorted in order of descending ID */
for (i = 0; i < parray_num(backup_list); i++)
{
backup = (pgBackup *) parray_get(backup_list, i);
/* we need completed archived WAL backup */
if (backup->status == BACKUP_STATUS_OK && HAVE_ARCLOG(backup))
return backup;
}
return NULL;
}
/*
* Find the last completed serverlog backup from the backup list.
*/
pgBackup *
catalog_get_last_srvlog_backup(parray *backup_list)
{
int i;
pgBackup *backup = NULL;
/* backup_list is sorted in order of descending ID */
for (i = 0; i < parray_num(backup_list); i++)
{
backup = (pgBackup *) parray_get(backup_list, i);
/* we need completed serverlog backup */
if (backup->status == BACKUP_STATUS_OK && backup->with_serverlog)
return backup;
}
return NULL;
}
/* create backup directory in $BACKUP_PATH */
int
pgBackupCreateDir(pgBackup *backup)
{
int i;
char path[MAXPGPATH];
char *subdirs[] = { DATABASE_DIR, ARCLOG_DIR, SRVLOG_DIR, NULL };
pgBackupGetPath(backup, path, lengthof(path), NULL);
dir_create_dir(path, DIR_PERMISSION);
/* create directories for actual backup files */
for (i = 0; subdirs[i]; i++)
{
pgBackupGetPath(backup, path, lengthof(path), subdirs[i]);
dir_create_dir(path, DIR_PERMISSION);
}
return 0;
}
/*
* Write configuration section of backup.in to stream "out".
*/
void
pgBackupWriteConfigSection(FILE *out, pgBackup *backup)
{
static const char *modes[] = { "", "ARCHIVE", "INCREMENTAL", "FULL"};
fprintf(out, "# configuration\n");
fprintf(out, "BACKUP_MODE=%s\n", modes[backup->backup_mode]);
fprintf(out, "WITH_SERVERLOG=%s\n", BOOL_TO_STR(backup->with_serverlog));
fprintf(out, "COMPRESS_DATA=%s\n", BOOL_TO_STR(backup->compress_data));
}
/*
* Write result section of backup.in to stream "out".
*/
void
pgBackupWriteResultSection(FILE *out, pgBackup *backup)
{
char timestamp[20];
fprintf(out, "# result\n");
fprintf(out, "TIMELINEID=%d\n", backup->tli);
fprintf(out, "START_LSN=%x/%08x\n", backup->start_lsn.xlogid,
backup->start_lsn.xrecoff);
fprintf(out, "STOP_LSN=%x/%08x\n", backup->stop_lsn.xlogid,
backup->stop_lsn.xrecoff);
time2iso(timestamp, lengthof(timestamp), backup->start_time);
fprintf(out, "START_TIME='%s'\n", timestamp);
time2iso(timestamp, lengthof(timestamp), backup->end_time);
fprintf(out, "END_TIME='%s'\n", timestamp);
fprintf(out, "TOTAL_DATA_BYTES=" INT64_FORMAT "\n", backup->total_data_bytes);
fprintf(out, "READ_DATA_BYTES=" INT64_FORMAT "\n", backup->read_data_bytes);
fprintf(out, "READ_ARCLOG_BYTES=" INT64_FORMAT "\n", backup->read_arclog_bytes);
fprintf(out, "READ_SRVLOG_BYTES=" INT64_FORMAT "\n", backup->read_srvlog_bytes);
fprintf(out, "WRITE_BYTES=" INT64_FORMAT "\n", backup->write_bytes);
fprintf(out, "BLOCK_SIZE=%u\n", backup->block_size);
fprintf(out, "XLOG_BLOCK_SIZE=%u\n", backup->wal_block_size);
fprintf(out, "STATUS=%s\n", status2str(backup->status));
}
/* create backup.ini */
void
pgBackupWriteIni(pgBackup *backup)
{
FILE *fp = NULL;
char ini_path[MAXPGPATH];
pgBackupGetPath(backup, ini_path, lengthof(ini_path), BACKUP_INI_FILE);
fp = fopen(ini_path, "wt");
if (fp == NULL)
elog(ERROR_SYSTEM, _("can't open INI file \"%s\": %s"), ini_path,
strerror(errno));
/* configuration section */
pgBackupWriteConfigSection(fp, backup);
/* result section */
pgBackupWriteResultSection(fp, backup);
fclose(fp);
}
/*
* Read backup.ini and create pgBackup.
* - Comment starts with ';'.
* - Do not care section.
*/
static pgBackup *
catalog_read_ini(const char *path)
{
pgBackup *backup;
char *backup_mode = NULL;
char *start_lsn = NULL;
char *stop_lsn = NULL;
char *status = NULL;
int i;
pgut_option options[] =
{
{ 's', 0, "backup-mode" , NULL, SOURCE_ENV },
{ 'b', 0, "with-serverlog" , NULL, SOURCE_ENV },
{ 'b', 0, "compress-data" , NULL, SOURCE_ENV },
{ 'u', 0, "timelineid" , NULL, SOURCE_ENV },
{ 's', 0, "start-lsn" , NULL, SOURCE_ENV },
{ 's', 0, "stop-lsn" , NULL, SOURCE_ENV },
{ 't', 0, "start-time" , NULL, SOURCE_ENV },
{ 't', 0, "end-time" , NULL, SOURCE_ENV },
{ 'I', 0, "total-data-bytes" , NULL, SOURCE_ENV },
{ 'I', 0, "read-data-bytes" , NULL, SOURCE_ENV },
{ 'I', 0, "read-arclog-bytes" , NULL, SOURCE_ENV },
{ 'I', 0, "read-srvlog-bytes" , NULL, SOURCE_ENV },
{ 'I', 0, "write-bytes" , NULL, SOURCE_ENV },
{ 'u', 0, "block-size" , NULL, SOURCE_ENV },
{ 'u', 0, "xlog-block-size" , NULL, SOURCE_ENV },
{ 's', 0, "status" , NULL, SOURCE_ENV },
{ 0 }
};
backup = (pgBackup *) pgut_malloc(sizeof(*backup));
catalog_init_config(backup);
i = 0;
options[i++].var = &backup_mode;
options[i++].var = &backup->with_serverlog;
options[i++].var = &backup->compress_data;
options[i++].var = &backup->tli;
options[i++].var = &start_lsn;
options[i++].var = &stop_lsn;
options[i++].var = &backup->start_time;
options[i++].var = &backup->end_time;
options[i++].var = &backup->total_data_bytes;
options[i++].var = &backup->read_data_bytes;
options[i++].var = &backup->read_arclog_bytes;
options[i++].var = &backup->read_srvlog_bytes;
options[i++].var = &backup->write_bytes;
options[i++].var = &backup->block_size;
options[i++].var = &backup->wal_block_size;
options[i++].var = &status;
Assert(i == lengthof(options) - 1);
pgut_readopt(path, options, ERROR_CORRUPTED);
if (backup_mode)
{
backup->backup_mode = parse_backup_mode(backup_mode, WARNING);
free(backup_mode);
}
if (start_lsn)
{
XLogRecPtr lsn;
if (sscanf(start_lsn, "%X/%X", &lsn.xlogid, &lsn.xrecoff) == 2)
backup->start_lsn = lsn;
else
elog(WARNING, _("invalid START_LSN \"%s\""), start_lsn);
free(start_lsn);
}
if (stop_lsn)
{
XLogRecPtr lsn;
if (sscanf(stop_lsn, "%X/%X", &lsn.xlogid, &lsn.xrecoff) == 2)
backup->stop_lsn = lsn;
else
elog(WARNING, _("invalid STOP_LSN \"%s\""), stop_lsn);
free(stop_lsn);
}
if (status)
{
if (strcmp(status, "OK") == 0)
backup->status = BACKUP_STATUS_OK;
else if (strcmp(status, "RUNNING") == 0)
backup->status = BACKUP_STATUS_RUNNING;
else if (strcmp(status, "ERROR") == 0)
backup->status = BACKUP_STATUS_ERROR;
else if (strcmp(status, "DELETING") == 0)
backup->status = BACKUP_STATUS_DELETING;
else if (strcmp(status, "DELETED") == 0)
backup->status = BACKUP_STATUS_DELETED;
else if (strcmp(status, "DONE") == 0)
backup->status = BACKUP_STATUS_DONE;
else if (strcmp(status, "CORRUPT") == 0)
backup->status = BACKUP_STATUS_CORRUPT;
else
elog(WARNING, _("invalid STATUS \"%s\""), status);
free(status);
}
return backup;
}
BackupMode
parse_backup_mode(const char *value, int elevel)
{
const char *v = value;
size_t len;
while (IsSpace(*v)) { v++; }
len = strlen(v);
if (len > 0 && pg_strncasecmp("full", v, len) == 0)
return BACKUP_MODE_FULL;
else if (len > 0 && pg_strncasecmp("incremental", v, len) == 0)
return BACKUP_MODE_INCREMENTAL;
else if (len > 0 && pg_strncasecmp("archive", v, len) == 0)
return BACKUP_MODE_ARCHIVE;
elog(elevel, _("invalid backup-mode \"%s\""), value);
return BACKUP_MODE_INVALID;
}
/* free pgBackup object */
void
pgBackupFree(void *backup)
{
free(backup);
}
/* Compare two pgBackup with their ID (start time) in ascending order */
int
pgBackupCompareId(const void *l, const void *r)
{
pgBackup *lp = *(pgBackup **)l;
pgBackup *rp = *(pgBackup **)r;
if (lp->start_time > rp->start_time)
return 1;
else if (lp->start_time < rp->start_time)
return -1;
else
return 0;
}
/* Compare two pgBackup with their ID in descending order */
int
pgBackupCompareIdDesc(const void *l, const void *r)
{
return -pgBackupCompareId(l, r);
}
/*
* Construct absolute path of the backup directory.
* If subdir is not NULL, it will be appended after the path.
*/
void
pgBackupGetPath(const pgBackup *backup, char *path, size_t len, const char *subdir)
{
char datetime[20];
struct tm *tm;
/* generate $BACKUP_PATH/date/time path */
tm = localtime(&backup->start_time);
strftime(datetime, lengthof(datetime), "%Y%m%d/%H%M%S", tm);
if (subdir)
snprintf(path, len, "%s/%s/%s", backup_path, datetime, subdir);
else
snprintf(path, len, "%s/%s", backup_path, datetime);
}
void
catalog_init_config(pgBackup *backup)
{
backup->backup_mode = BACKUP_MODE_INVALID;
backup->with_serverlog = false;
backup->compress_data = false;
backup->status = BACKUP_STATUS_INVALID;
backup->tli = 0;
backup->start_lsn.xlogid = 0;
backup->start_lsn.xrecoff = 0;
backup->stop_lsn.xlogid = 0;
backup->stop_lsn.xrecoff = 0;
backup->start_time = (time_t) 0;
backup->end_time = (time_t) 0;
backup->total_data_bytes = BYTES_INVALID;
backup->read_data_bytes = BYTES_INVALID;
backup->read_arclog_bytes = BYTES_INVALID;
backup->read_srvlog_bytes = BYTES_INVALID;
backup->write_bytes = BYTES_INVALID;
}

886
data.c Normal file
View File

@ -0,0 +1,886 @@
/*-------------------------------------------------------------------------
*
* data.c: compress / uncompress data pages
*
* Copyright (c) 2009, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "libpq/pqsignal.h"
#include "storage/block.h"
#include "storage/bufpage.h"
#if PG_VERSION_NUM < 80300
#define XLogRecPtrIsInvalid(r) ((r).xrecoff == 0)
#endif
#ifdef HAVE_LIBZ
#include <zlib.h>
#define zlibOutSize 4096
#define zlibInSize 4096
static int doDeflate(z_stream *zp, size_t in_size, size_t out_size, void *inbuf,
void *outbuf, FILE *in, FILE *out, pg_crc32 *crc, size_t *write_size,
int flash);
static int doInflate(z_stream *zp, size_t in_size, size_t out_size,void *inbuf,
void *outbuf, FILE *in, FILE *out, pg_crc32 *crc, size_t *read_size);
static int
doDeflate(z_stream *zp, size_t in_size, size_t out_size, void *inbuf,
void *outbuf, FILE *in, FILE *out, pg_crc32 *crc, size_t *write_size,
int flash)
{
int status;
zp->next_in = inbuf;
zp->avail_in = in_size;
/* compresses until an input buffer becomes empty. */
do
{
if (interrupted)
elog(ERROR_INTERRUPTED, _("interrupted during deflate"));
status = deflate(zp, flash);
if (status == Z_STREAM_ERROR)
{
fclose(in);
fclose(out);
elog(ERROR_SYSTEM, _("can't compress data: %s"), zp->msg);
}
if (fwrite(outbuf, 1, out_size - zp->avail_out, out) !=
out_size - zp->avail_out)
{
fclose(in);
fclose(out);
elog(ERROR_SYSTEM, _("can't write file: %s"), strerror(errno));
}
/* update CRC */
COMP_CRC32(*crc, outbuf, out_size - zp->avail_out);
*write_size += out_size - zp->avail_out;
zp->next_out = outbuf;
zp->avail_out = out_size;
} while (zp->avail_in != 0);
return status;
}
static int
doInflate(z_stream *zp, size_t in_size, size_t out_size,void *inbuf,
void *outbuf, FILE *in, FILE *out, pg_crc32 *crc, size_t *read_size)
{
int status = Z_OK;
zp->next_out = outbuf;
zp->avail_out = out_size;
/* decompresses until an output buffer becomes full. */
for (;;)
{
if (interrupted)
elog(ERROR_INTERRUPTED, _("interrupted during inflate"));
/* input buffer becomes empty, read it from a file. */
if (zp->avail_in == 0)
{
size_t read_len;
read_len = fread(inbuf, 1, in_size, in);
if (read_len != in_size)
{
int errno_tmp = errno;
if (!feof(in))
{
fclose(in);
fclose(out);
elog(ERROR_CORRUPTED,
_("can't read compress file: %s"), strerror(errno_tmp));
}
if (read_len == 0 && *read_size == 0)
return Z_STREAM_END;
}
zp->next_in = inbuf;
zp->avail_in = read_len;
*read_size += read_len;
}
/* decompresses input file data */
status = inflate(zp, Z_NO_FLUSH);
if (status == Z_STREAM_END)
{
if (feof(in))
break;
/* not reached to EOF, read again */
}
else if (status == Z_OK)
{
if (zp->avail_out == 0)
break;
/* more input needed to fill out_buf */
}
else if (status != Z_OK)
{
fclose(in);
fclose(out);
elog(ERROR_SYSTEM, _("can't uncompress data: %s"), strerror(errno));
}
}
/* update CRC */
COMP_CRC32(*crc, outbuf, out_size - zp->avail_out);
return status;
}
#endif
typedef union DataPage
{
PageHeaderData header;
char data[BLCKSZ];
} DataPage;
typedef struct BackupPageHeader
{
BlockNumber block; /* block number */
uint16 hole_offset; /* number of bytes before "hole" */
uint16 hole_length; /* number of bytes in "hole" */
} BackupPageHeader;
static bool
is_valid_header(const PageHeader page)
{
const char *pagebytes;
int i;
/* Check normal case */
if (PageGetPageSize(page) == BLCKSZ &&
PageGetPageLayoutVersion(page) == PG_PAGE_LAYOUT_VERSION &&
#if PG_VERSION_NUM >= 80300
(page->pd_flags & ~PD_VALID_FLAG_BITS) == 0 &&
#endif
page->pd_lower >= SizeOfPageHeaderData &&
page->pd_lower <= page->pd_upper &&
page->pd_upper <= page->pd_special &&
page->pd_special <= BLCKSZ &&
page->pd_special == MAXALIGN(page->pd_special))
return true;
/* Check all-zeroes case */
pagebytes = (char *) page;
for (i = 0; i < BLCKSZ; i++)
{
if (pagebytes[i] != 0)
return false;
}
return true;
}
/*
* Backup data file in the from_root directory to the to_root directory with
* same relative path.
* If lsn is not NULL, pages only which are modified after the lsn will be
* copied.
*/
void
backup_data_file(const char *from_root, const char *to_root,
pgFile *file, const XLogRecPtr *lsn, bool compress_data)
{
char to_path[MAXPGPATH];
FILE *in;
FILE *out;
BackupPageHeader header;
DataPage page; /* used as read buffer */
BlockNumber blknum;
size_t read_len;
int errno_tmp;
pg_crc32 crc;
#ifdef HAVE_LIBZ
z_stream z;
char outbuf[zlibOutSize];
#endif
/* open backup mode file for read */
in = fopen(file->path, "r");
if (in == NULL)
{
/* meybe vanished, it's not error */
if (errno == ENOENT)
return;
elog(ERROR_SYSTEM, _("can't open backup mode file \"%s\": %s"),
file->path, strerror(errno));
}
/* open backup file for write */
if (check)
snprintf(to_path, lengthof(to_path), "%s/tmp", backup_path);
else
snprintf(to_path, lengthof(to_path), "%s/%s",
to_root, file->path + strlen(from_root) + 1);
out = fopen(to_path, "w");
if (out == NULL)
{
int errno_tmp = errno;
fclose(in);
elog(ERROR_SYSTEM, _("can't open backup file \"%s\": %s"),
to_path, strerror(errno_tmp));
}
INIT_CRC32(crc);
/* reset size summary */
file->read_size = 0;
file->write_size = 0;
#ifdef HAVE_LIBZ
if (compress_data)
{
z.zalloc = Z_NULL;
z.zfree = Z_NULL;
z.opaque = Z_NULL;
if (deflateInit(&z, Z_DEFAULT_COMPRESSION) != Z_OK)
elog(ERROR_SYSTEM, _("can't initialize compression library: %s"),
z.msg);
z.avail_in = 0;
z.next_out = (void *) outbuf;
z.avail_out = zlibOutSize;
}
#endif
/* read each page and write the page excluding hole */
for (blknum = 0;
(read_len = fread(&page, 1, sizeof(page), in)) == sizeof(page);
++blknum)
{
int upper_offset;
int upper_length;
header.block = blknum;
/*
* If a invalid data page was found, fallback to simple copy to ensure
* all pages in the file don't have BackupPageHeader.
*/
if (!is_valid_header(&page.header) ||
!XLogRecPtrIsInvalid(PageGetLSN(&page.header)))
{
elog(LOG, "%s fall back to simple copy", file->path);
fclose(in);
fclose(out);
file->is_datafile = false;
copy_file(from_root, to_root, file,
compress_data ? COMPRESSION : NO_COMPRESSION);
return;
}
file->read_size += read_len;
/* if the page has not been modified since last backup, skip it */
if (lsn && !XLogRecPtrIsInvalid(PageGetLSN(&page.header)) &&
XLByteLT(PageGetLSN(&page.header), *lsn))
continue;
header.hole_offset = page.header.pd_lower;
header.hole_length = page.header.pd_upper - page.header.pd_lower;
upper_offset = header.hole_offset + header.hole_length;
upper_length = BLCKSZ - upper_offset;
#ifdef HAVE_LIBZ
if (compress_data)
{
doDeflate(&z, sizeof(header), sizeof(outbuf), &header, outbuf, in,
out, &crc, &file->write_size, Z_NO_FLUSH);
doDeflate(&z, header.hole_offset, sizeof(outbuf), page.data, outbuf,
in, out, &crc, &file->write_size, Z_NO_FLUSH);
doDeflate(&z, upper_length, sizeof(outbuf),
page.data + upper_offset, outbuf, in, out, &crc,
&file->write_size, Z_NO_FLUSH);
}
else
#endif
{
/* write data page excluding hole */
if (fwrite(&header, 1, sizeof(header), out) != sizeof(header) ||
fwrite(page.data, 1, header.hole_offset, out) != header.hole_offset ||
fwrite(page.data + upper_offset, 1, upper_length, out) != upper_length)
{
int errno_tmp = errno;
/* oops */
fclose(in);
fclose(out);
elog(ERROR_SYSTEM, _("can't write at block %u of \"%s\": %s"),
blknum, to_path, strerror(errno_tmp));
}
/* update CRC */
COMP_CRC32(crc, &header, sizeof(header));
COMP_CRC32(crc, page.data, header.hole_offset);
COMP_CRC32(crc, page.data + upper_offset, upper_length);
file->write_size += sizeof(header) + read_len - header.hole_length;
}
}
errno_tmp = errno;
if (!feof(in))
{
fclose(in);
fclose(out);
elog(ERROR_SYSTEM, _("can't read backup mode file \"%s\": %s"),
file->path, strerror(errno_tmp));
}
/*
* The odd size page at the tail is probably a page exactly written now, so
* write whole of it.
*/
if (read_len > 0)
{
/*
* If the odd size page is the 1st page, fallback to simple copy because
* the file is not a datafile.
* Otherwise treat the page as a datapage with no hole.
*/
if (blknum == 0)
file->is_datafile = false;
else
{
header.block = blknum;
header.hole_offset = 0;
header.hole_length = 0;
#ifdef HAVE_LIBZ
if (compress_data)
{
doDeflate(&z, sizeof(header), sizeof(outbuf), &header, outbuf,
in, out, &crc, &file->write_size, Z_NO_FLUSH);
}
else
#endif
{
if (fwrite(&header, 1, sizeof(header), out) != sizeof(header))
{
int errno_tmp = errno;
/* oops */
fclose(in);
fclose(out);
elog(ERROR_SYSTEM,
_("can't write at block %u of \"%s\": %s"),
blknum, to_path, strerror(errno_tmp));
}
COMP_CRC32(crc, &header, sizeof(header));
file->write_size += sizeof(header);
}
}
/* write odd size page image */
#ifdef HAVE_LIBZ
if (compress_data)
{
doDeflate(&z, read_len, sizeof(outbuf), page.data, outbuf, in, out,
&crc, &file->write_size, Z_NO_FLUSH);
}
else
#endif
{
if (fwrite(page.data, 1, read_len, out) != read_len)
{
int errno_tmp = errno;
/* oops */
fclose(in);
fclose(out);
elog(ERROR_SYSTEM, _("can't write at block %u of \"%s\": %s"),
blknum, to_path, strerror(errno_tmp));
}
COMP_CRC32(crc, page.data, read_len);
file->write_size += read_len;
}
file->read_size += read_len;
}
#ifdef HAVE_LIBZ
if (compress_data)
{
if (file->read_size > 0)
{
while (doDeflate(&z, 0, sizeof(outbuf), NULL, outbuf, in, out, &crc,
&file->write_size, Z_FINISH) != Z_STREAM_END)
{
}
}
if (deflateEnd(&z) != Z_OK)
{
fclose(in);
fclose(out);
elog(ERROR_SYSTEM, _("can't close compression stream: %s"), z.msg);
}
}
#endif
/* update file permission */
if (!check && chmod(to_path, FILE_PERMISSION) == -1)
{
int errno_tmp = errno;
fclose(in);
fclose(out);
elog(ERROR_SYSTEM, _("can't change mode of \"%s\": %s"), file->path,
strerror(errno_tmp));
}
fclose(in);
fclose(out);
/* finish CRC calculation and store into pgFile */
FIN_CRC32(crc);
file->crc = crc;
/* Treat empty file as not-datafile */
if (file->read_size == 0)
file->is_datafile = false;
/* We do not backup if all pages skipped. */
if (file->write_size == 0 && file->read_size > 0)
if (remove(to_path) == -1)
elog(ERROR_SYSTEM, _("can't remove file \"%s\": %s"), to_path,
strerror(errno));
/* remove $BACKUP_PATH/tmp created during check */
if (check)
remove(to_path);
}
/*
* Restore files in the from_root directory to the to_root directory with
* same relative path.
*/
void
restore_data_file(const char *from_root, const char *to_root, pgFile *file, bool compress_data)
{
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;
#endif
/* If the file is not a datafile, copy it. */
if (!file->is_datafile)
{
copy_file(from_root, to_root, file,
compress_data ? DECOMPRESSION : NO_COMPRESSION);
return;
}
/* open backup mode file for read */
in = fopen(file->path, "r");
if (in == NULL)
{
elog(ERROR_SYSTEM, _("can't open backup file \"%s\": %s"), file->path,
strerror(errno));
}
/* open backup file for write */
snprintf(to_path, lengthof(to_path), "%s/%s", to_root,
file->path + strlen(from_root) + 1);
out = fopen(to_path, "w");
if (out == NULL)
{
int errno_tmp = errno;
fclose(in);
elog(ERROR_SYSTEM, _("can't open restore target file \"%s\": %s"),
to_path, strerror(errno_tmp));
}
#ifdef HAVE_LIBZ
if (compress_data)
{
z.zalloc = Z_NULL;
z.zfree = Z_NULL;
z.opaque = Z_NULL;
z.next_in = Z_NULL;
z.avail_in = 0;
if (inflateInit(&z) != Z_OK)
elog(ERROR_SYSTEM, _("can't initialize compression library: %s"),
z.msg);
INIT_CRC32(crc);
read_size = 0;
}
#endif
for (blknum = 0; ;blknum++)
{
size_t read_len;
DataPage page; /* used as read buffer */
int upper_offset;
int upper_length;
/* read BackupPageHeader */
#ifdef HAVE_LIBZ
if (compress_data)
{
status = doInflate(&z, sizeof(inbuf), sizeof(header), inbuf,
&header, in, out, &crc, &read_size);
if (status == Z_STREAM_END)
{
if (z.avail_out != sizeof(header))
elog(ERROR_CORRUPTED, _("backup is broken header"));
break;
}
if (z.avail_out != 0)
elog(ERROR_SYSTEM, _("can't read block %u of \"%s\""),
blknum, file->path);
}
else
#endif
{
read_len = fread(&header, 1, sizeof(header), in);
if (read_len != sizeof(header))
{
int errno_tmp = errno;
if (read_len == 0 && feof(in))
break; /* EOF found */
else if (read_len != 0 && feof(in))
{
elog(ERROR_CORRUPTED,
_("odd size page found at block %u of \"%s\""),
blknum, file->path);
}
else
{
elog(ERROR_SYSTEM, _("can't read block %u of \"%s\": %s"),
blknum, file->path, strerror(errno_tmp));
}
}
}
if (header.block < blknum || header.hole_offset > BLCKSZ ||
(int) header.hole_offset + (int) header.hole_length > BLCKSZ)
{
elog(ERROR_CORRUPTED, _("backup is broken at block %u"),
blknum);
}
upper_offset = header.hole_offset + header.hole_length;
upper_length = BLCKSZ - upper_offset;
/* 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)
{
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);
if (z.avail_out != 0)
elog(ERROR_SYSTEM, _("can't read block %u of \"%s\""),
blknum, file->path);
}
if (upper_length > 0)
{
doInflate(&z, sizeof(inbuf), upper_length, inbuf,
page.data + upper_offset, in, out, &crc, &read_size);
if (z.avail_out != 0)
elog(ERROR_SYSTEM, _("can't read block %u of \"%s\""),
blknum, file->path);
}
}
else
#endif
{
if (fread(page.data, 1, header.hole_offset, in) != header.hole_offset ||
fread(page.data + upper_offset, 1, upper_length, in) != upper_length)
{
elog(ERROR_SYSTEM, _("can't read block %u of \"%s\": %s"),
blknum, file->path, strerror(errno));
}
}
/* 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;
}
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)
elog(ERROR_SYSTEM, _("can't close compression stream: %s"), z.msg);
#endif
/* update file permission */
if (chmod(to_path, file->mode) == -1)
{
int errno_tmp = errno;
fclose(in);
fclose(out);
elog(ERROR_SYSTEM, _("can't change mode of \"%s\": %s"), to_path,
strerror(errno_tmp));
}
fclose(in);
fclose(out);
}
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;
#ifdef HAVE_LIBZ
z_stream z;
int status;
char outbuf[zlibOutSize];
char inbuf[zlibInSize];
#endif
/* open backup mode file for read */
in = fopen(file->path, "r");
if (in == NULL)
{
/* meybe deleted, it's not error */
if (errno == ENOENT)
return;
elog(ERROR_SYSTEM, _("can't open source file \"%s\": %s"), file->path,
strerror(errno));
}
/* open backup file for write */
if (check)
snprintf(to_path, lengthof(to_path), "%s/tmp", backup_path);
else
snprintf(to_path, lengthof(to_path), "%s/%s", to_root,
file->path + strlen(from_root) + 1);
out = fopen(to_path, "w");
if (out == NULL)
{
int errno_tmp = errno;
fclose(in);
elog(ERROR_SYSTEM, _("can't open destination file \"%s\": %s"),
to_path, strerror(errno_tmp));
}
/* stat source file to change mode of destination file */
if (fstat(fileno(in), &st) == -1)
{
elog(ERROR_SYSTEM, _("can't stat \"%s\": %s"), file->path,
strerror(errno));
}
INIT_CRC32(crc);
/* reset size summary */
file->read_size = 0;
file->write_size = 0;
#ifdef HAVE_LIBZ
z.zalloc = Z_NULL;
z.zfree = Z_NULL;
z.opaque = Z_NULL;
if (mode == COMPRESSION)
{
if (deflateInit(&z, Z_DEFAULT_COMPRESSION) != Z_OK)
elog(ERROR_SYSTEM, _("can't initialize compression library: %s"),
z.msg);
z.avail_in = 0;
z.next_out = (void *) outbuf;
z.avail_out = zlibOutSize;
}
else if (mode == DECOMPRESSION)
{
z.next_in = Z_NULL;
z.avail_in = 0;
if (inflateInit(&z) != Z_OK)
elog(ERROR_SYSTEM, _("can't initialize compression library: %s"),
z.msg);
}
#endif
/* copy content and calc CRC */
for (;;)
{
#ifdef HAVE_LIBZ
if (mode == COMPRESSION)
{
if ((read_len = fread(buf, 1, sizeof(buf), in)) != sizeof(buf))
break;
doDeflate(&z, read_len, sizeof(outbuf), buf, outbuf, in, out, &crc,
&file->write_size, Z_NO_FLUSH);
file->read_size += sizeof(buf);
}
else if (mode == DECOMPRESSION)
{
status = doInflate(&z, sizeof(inbuf), sizeof(outbuf), inbuf, outbuf,
in, out, &crc, &file->read_size);
if (fwrite(outbuf, 1, sizeof(outbuf) - z.avail_out, out) !=
sizeof(outbuf) - z.avail_out)
{
errno_tmp = errno;
/* oops */
fclose(in);
fclose(out);
elog(ERROR_SYSTEM, _("can't write to \"%s\": %s"), to_path,
strerror(errno_tmp));
}
file->write_size += sizeof(outbuf) - z.avail_out;
if (status == Z_STREAM_END)
break;
}
else
#endif
{
if ((read_len = fread(buf, 1, sizeof(buf), in)) != sizeof(buf))
break;
if (fwrite(buf, 1, read_len, out) != read_len)
{
errno_tmp = errno;
/* oops */
fclose(in);
fclose(out);
elog(ERROR_SYSTEM, _("can't write to \"%s\": %s"), to_path,
strerror(errno_tmp));
}
/* update CRC */
COMP_CRC32(crc, buf, read_len);
file->write_size += sizeof(buf);
file->read_size += sizeof(buf);
}
}
errno_tmp = errno;
if (!feof(in))
{
fclose(in);
fclose(out);
elog(ERROR_SYSTEM, _("can't read backup mode file \"%s\": %s"),
file->path, strerror(errno_tmp));
}
/* copy odd part. */
if (read_len > 0)
{
#ifdef HAVE_LIBZ
if (mode == COMPRESSION)
{
doDeflate(&z, read_len, sizeof(outbuf), buf, outbuf, in, out, &crc,
&file->write_size, Z_NO_FLUSH);
}
else
#endif
{
if (fwrite(buf, 1, read_len, out) != read_len)
{
errno_tmp = errno;
/* oops */
fclose(in);
fclose(out);
elog(ERROR_SYSTEM, _("can't write to \"%s\": %s"), to_path,
strerror(errno_tmp));
}
/* update CRC */
COMP_CRC32(crc, buf, read_len);
file->write_size += read_len;
}
file->read_size += read_len;
}
#ifdef HAVE_LIBZ
if (mode == COMPRESSION)
{
if (file->read_size > 0)
{
while (doDeflate(&z, 0, sizeof(outbuf), NULL, outbuf, in, out, &crc,
&file->write_size, Z_FINISH) != Z_STREAM_END)
{
}
}
if (deflateEnd(&z) != Z_OK)
elog(ERROR_SYSTEM, _("can't close compression stream: %s"), z.msg);
}
else if (mode == DECOMPRESSION)
{
if (inflateEnd(&z) != Z_OK)
elog(ERROR_SYSTEM, _("can't close compression stream: %s"), z.msg);
}
#endif
/* finish CRC calculation and store into pgFile */
FIN_CRC32(crc);
file->crc = crc;
/* update file permission */
if (chmod(to_path, st.st_mode) == -1)
{
errno_tmp = errno;
fclose(in);
fclose(out);
elog(ERROR_SYSTEM, _("can't change mode of \"%s\": %s"), to_path,
strerror(errno_tmp));
}
fclose(in);
fclose(out);
if (check)
remove(to_path);
}

View File

@ -0,0 +1,18 @@
# configuration
BACKUP_MODE=FULL
WITH_SERVERLOG=NO
COMPRESS_DATA=NO
# result
TIMELINEID=1
START_LSN=0/0b40c800
STOP_LSN=0/0b4c8020
START_TIME='2009-05-31 17:05:53'
END_TIME='2009-05-31 17:09:13'
TOTAL_DATA_BYTES=1242102558
READ_DATA_BYTES=1024
READ_ARCLOG_BYTES=9223372036854775807
READ_SRVLOG_BYTES=-1
WRITE_BYTES=242102558
BLOCK_SIZE=8192
XLOG_BLOCK_SIZE=8192
STATUS=DONE

View File

@ -0,0 +1 @@
8.4

View File

@ -0,0 +1 @@
PG_VERSION f 4 4277607361 0600 2009-08-06 18:40:18

View File

@ -0,0 +1,18 @@
# configuration
BACKUP_MODE=INCREMENTAL
WITH_SERVERLOG=NO
COMPRESS_DATA=NO
# result
TIMELINEID=1
START_LSN=0/0b40c800
STOP_LSN=0/0b4c8020
START_TIME='2009-06-01 17:05:53'
END_TIME='2009-06-01 17:09:13'
TOTAL_DATA_BYTES=1242102558
READ_DATA_BYTES=9223372036854775807
READ_ARCLOG_BYTES=16777216
READ_SRVLOG_BYTES=-1
WRITE_BYTES=162372983
BLOCK_SIZE=8192
XLOG_BLOCK_SIZE=8192
STATUS=DONE

View File

@ -0,0 +1 @@
8.4

View File

@ -0,0 +1 @@
PG_VERSION f 4 0 0600 2009-08-06 18:40:18

View File

@ -0,0 +1,18 @@
# configuration
BACKUP_MODE=ARCHIVE
WITH_SERVERLOG=YES
COMPRESS_DATA=NO
# result
TIMELINEID=1
START_LSN=0/0b40c800
STOP_LSN=0/0b4c8020
START_TIME='2009-06-02 17:05:03'
END_TIME='2009-06-02 17:05:03'
TOTAL_DATA_BYTES=-1
READ_DATA_BYTES=-1
READ_ARCLOG_BYTES=-1
READ_SRVLOG_BYTES=4335423
WRITE_BYTES=162372983
BLOCK_SIZE=8192
XLOG_BLOCK_SIZE=8192
STATUS=DELETED

View File

@ -0,0 +1,18 @@
# configuration
BACKUP_MODE=FULL
WITH_SERVERLOG=YES
COMPRESS_DATA=NO
# result
TIMELINEID=1
START_LSN=0/0b40c800
STOP_LSN=0/0b4c8020
START_TIME='2009-06-03 17:05:53'
END_TIME='****-**-** **:**:**'
TOTAL_DATA_BYTES=-1
READ_DATA_BYTES=-1
READ_ARCLOG_BYTES=-1
READ_SRVLOG_BYTES=-1
WRITE_BYTES=-1
BLOCK_SIZE=8192
XLOG_BLOCK_SIZE=8192
STATUS=RUNNING

View File

235
delete.c Normal file
View File

@ -0,0 +1,235 @@
/*-------------------------------------------------------------------------
*
* delete.c: delete backup files.
*
* Copyright (c) 2009, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
static int pgBackupDeleteFiles(pgBackup *backup);
int
do_delete(pgBackupRange *range)
{
int i;
int ret;
parray *backup_list;
bool do_delete;
/* DATE are always required */
if (!pgBackupRangeIsValid(range))
elog(ERROR_ARGS, _("required delete range option not specified: delete DATE"));
/* get exclusive lock of backup catalog */
ret = catalog_lock();
if (ret == -1)
elog(ERROR_SYSTEM, _("can't lock backup catalog."));
else if (ret == 1)
elog(ERROR_ALREADY_RUNNING,
_("another pg_rman is running, stop restore."));
/* get list of backups. */
backup_list = catalog_get_backup_list(NULL);
do_delete = false;
/* find delete target backup. */
for (i = 0; i < parray_num(backup_list); i++)
{
pgBackup *backup = (pgBackup *)parray_get(backup_list, i);
/* delete backup and update status to DELETED */
if (do_delete)
{
pgBackupDeleteFiles(backup);
continue;
}
/* find latest full backup. */
if (backup->backup_mode >= BACKUP_MODE_FULL &&
backup->status == BACKUP_STATUS_OK &&
backup->start_time <= range->begin)
do_delete = true;
}
/* release catalog lock */
catalog_unlock();
/* cleanup */
parray_walk(backup_list, pgBackupFree);
parray_free(backup_list);
return 0;
}
/*
* Delete backups that are older than KEEP_xxx_DAYS and have more generations
* than KEEP_xxx_FILES.
*/
void
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);
if (verbose)
{
char generations_str[100];
char days_str[100];
if (keep_generations == KEEP_INFINITE)
strncpy(generations_str, "INFINITE",
lengthof(generations_str));
else
snprintf(generations_str, lengthof(generations_str),
"%d", keep_generations);
if (keep_days == KEEP_INFINITE)
strncpy(days_str, "INFINITE", lengthof(days_str));
else
snprintf(days_str, lengthof(days_str), "%d", keep_days);
printf(_("delete old backups (generations=%s, days=%s)\n"),
generations_str, days_str);
}
/* delete files which satisfy both condition */
if (keep_generations == KEEP_INFINITE || keep_days == KEEP_INFINITE)
{
elog(LOG, "%s() infinite", __FUNCTION__);
return;
}
/* get list of backups. */
backup_list = catalog_get_backup_list(NULL);
backup_num = 0;
/* find delete target backup. */
for (i = 0; i < parray_num(backup_list); i++)
{
pgBackup *backup = (pgBackup *)parray_get(backup_list, i);
elog(LOG, "%s() %lu", __FUNCTION__, backup->start_time);
/*
* when validate full backup was found, we can delete the backup
* that is older than it
*/
if (backup->backup_mode >= BACKUP_MODE_FULL &&
backup->status == BACKUP_STATUS_OK)
backup_num++;
/* do not include the latest full backup in a count. */
if (backup_num - 1 <= keep_generations)
{
elog(LOG, "%s() backup are only %d", __FUNCTION__, backup_num);
continue;
}
/*
* If the start time of the backup is older than the threashold and
* there are enough generations of full backups, delete the backup.
*/
if (backup->start_time >= days_threashold)
{
elog(LOG, "%s() %lu is not older than %lu", __FUNCTION__,
backup->start_time, days_threashold);
continue;
}
elog(LOG, "%s() %lu is older than %lu", __FUNCTION__,
backup->start_time, days_threashold);
/* delete backup and update status to DELETED */
pgBackupDeleteFiles(backup);
}
/* cleanup */
parray_walk(backup_list, pgBackupFree);
parray_free(backup_list);
}
/*
* Delete backup files of the backup and update the status of the backup to
* BACKUP_STATUS_DELETED.
*/
static int
pgBackupDeleteFiles(pgBackup *backup)
{
int i;
char path[MAXPGPATH];
char timestamp[20];
parray *files;
/*
* If the backup was deleted already, nothing to do and such situation
* is not error.
*/
if (backup->status == BACKUP_STATUS_DELETED)
return 0;
time2iso(timestamp, lengthof(timestamp), backup->start_time);
elog(INFO, _("delete: %s"), timestamp);
/*
* update STATUS to BACKUP_STATUS_DELETING in preparation for the case which
* the error occurs before deleting all backup files.
*/
if (!check)
{
backup->status = BACKUP_STATUS_DELETING;
pgBackupWriteIni(backup);
}
/* list files to be deleted */
files = parray_new();
pgBackupGetPath(backup, path, lengthof(path), DATABASE_DIR);
dir_list_file(files, path, NULL, true, true);
pgBackupGetPath(backup, path, lengthof(path), ARCLOG_DIR);
dir_list_file(files, path, NULL, true, true);
pgBackupGetPath(backup, path, lengthof(path), SRVLOG_DIR);
dir_list_file(files, path, NULL, true, true);
/* delete leaf node first */
parray_qsort(files, pgFileComparePathDesc);
for (i = 0; i < parray_num(files); i++)
{
pgFile *file = (pgFile *) parray_get(files, i);
/* print progress */
elog(LOG, _("delete file(%d/%lu) \"%s\"\n"), i + 1,
(unsigned long) parray_num(files), file->path);
/* skip actual deletion in check mode */
if (!check)
{
if (remove(file->path))
{
elog(WARNING, _("can't remove \"%s\": %s"), file->path,
strerror(errno));
parray_walk(files, pgFileFree);
parray_free(files);
return 1;
}
}
}
/*
* After deleting all of the backup files, update STATUS to
* BACKUP_STATUS_DELETED.
*/
if (!check)
{
backup->status = BACKUP_STATUS_DELETED;
pgBackupWriteIni(backup);
}
parray_walk(files, pgFileFree);
parray_free(files);
return 0;
}

567
dir.c Normal file
View File

@ -0,0 +1,567 @@
/*-------------------------------------------------------------------------
*
* dir.c: directory operation utility.
*
* Copyright (c) 2009, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include <libgen.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <time.h>
#include "pgut/pgut-port.h"
/* directory exclusion list for backup mode listing */
const char *pgdata_exclude[] =
{
"pg_xlog",
"pg_stat_tmp",
"pgsql_tmp",
NULL, /* arclog_path will be set later */
NULL, /* srvlog_path will be set later */
NULL, /* centinel */
};
static pgFile *pgFileNew(const char *path, bool omit_symlink);
/* create directory, also create parent directories if necessary */
int
dir_create_dir(const char *dir, mode_t mode)
{
char copy[MAXPGPATH];
char *parent;
strncpy(copy, dir, MAXPGPATH);
parent= dirname(copy);
if (access(parent, F_OK) == -1)
dir_create_dir(parent, mode);
if (mkdir(dir, mode) == -1)
{
if (errno == EEXIST) /* already exist */
return 0;
elog(ERROR_SYSTEM, _("can't create directory \"%s\": %s"), dir,
strerror(errno));
}
return 0;
}
static pgFile *
pgFileNew(const char *path, bool omit_symlink)
{
struct stat st;
pgFile *file;
/* stat the file */
if ((omit_symlink ? stat(path, &st) : lstat(path, &st)) == -1)
{
/* file not found is not an error case */
if (errno == ENOENT)
return NULL;
elog(ERROR_SYSTEM, _("can't stat file \"%s\": %s"), path,
strerror(errno));
}
file = (pgFile *) pgut_malloc(offsetof(pgFile, path) + strlen(path) + 1);
file->mtime = st.st_mtime;
file->size = st.st_size;
file->read_size = 0;
file->write_size = 0;
file->mode = st.st_mode;
file->crc = 0;
file->is_datafile = false;
file->linked = NULL;
strcpy(file->path, path); /* enough buffer size guaranteed */
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.
*/
void
pgFileDelete(pgFile *file)
{
if (S_ISDIR(file->mode))
{
if (rmdir(file->path) == -1)
{
if (errno == ENOENT)
return;
else if (errno == ENOTDIR) /* could be symbolic link */
goto delete_file;
elog(ERROR_SYSTEM, _("can't remove directory \"%s\": %s"),
file->path, strerror(errno));
}
return;
}
delete_file:
if (remove(file->path) == -1)
{
if (errno == ENOENT)
return;
elog(ERROR_SYSTEM, _("can't remove file \"%s\": %s"), file->path,
strerror(errno));
}
}
pg_crc32
pgFileGetCRC(pgFile *file)
{
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");
if (fp == NULL)
elog(ERROR_SYSTEM, _("can't open file \"%s\": %s"),
file->path, strerror(errno));
/* calc CRC of backup file */
INIT_CRC32(crc);
while ((len = fread(buf, 1, sizeof(buf), fp)) == sizeof(buf))
{
if (interrupted)
elog(ERROR_INTERRUPTED, _("interrupted during CRC calculation"));
COMP_CRC32(crc, buf, len);
}
errno_tmp = errno;
if (!feof(fp))
elog(WARNING, _("can't read \"%s\": %s"), file->path,
strerror(errno_tmp));
if (len > 0)
COMP_CRC32(crc, buf, len);
FIN_CRC32(crc);
fclose(fp);
return crc;
}
void
pgFileFree(void *file)
{
if (file == NULL)
return;
free(((pgFile *)file)->linked);
free(file);
}
/* Compare two pgFile with their path in ascending order of ASCII code. */
int
pgFileComparePath(const void *f1, const void *f2)
{
pgFile *f1p = *(pgFile **)f1;
pgFile *f2p = *(pgFile **)f2;
return strcmp(f1p->path, f2p->path);
}
/* Compare two pgFile with their path in descending order of ASCII code. */
int
pgFileComparePathDesc(const void *f1, const void *f2)
{
return -pgFileComparePath(f1, f2);
}
/* Compare two pgFile with their modify timestamp. */
int
pgFileCompareMtime(const void *f1, const void *f2)
{
pgFile *f1p = *(pgFile **)f1;
pgFile *f2p = *(pgFile **)f2;
if (f1p->mtime > f2p->mtime)
return 1;
else if (f1p->mtime < f2p->mtime)
return -1;
else return 0;
}
/* Compare two pgFile with their modify timestamp in descending order. */
int
pgFileCompareMtimeDesc(const void *f1, const void *f2)
{
return -pgFileCompareMtime(f1, f2);
}
/*
* List files, symbolic links and directories in the directory "root" and add
* pgFile objects to "files". We add "root" to "files" if add_root is true.
*
* If the sub-directory name is in "exclude" list, the sub-directory itself is
* listed but the contents of the sub-directory is ignored.
*
* When omit_symlink is true, symbolic link is ignored and only file or
* directory llnked to will be listed.
*/
void
dir_list_file(parray *files, const char *root, const char *exclude[], bool omit_symlink, bool add_root)
{
pgFile *file;
file = pgFileNew(root, omit_symlink);
if (file == NULL)
return;
if (add_root)
parray_append(files, file);
/* chase symbolic link chain and find regular file or directory */
while (S_ISLNK(file->mode))
{
ssize_t len;
char linked[MAXPGPATH];
len = readlink(file->path, linked, sizeof(linked));
if (len == -1)
{
elog(ERROR_SYSTEM, _("can't read link \"%s\": %s"), file->path,
strerror(errno));
}
linked[len] = '\0';
file->linked = pgut_strdup(linked);
/* make absolute path to read linked file */
if (linked[0] != '/')
{
char dname[MAXPGPATH];
char *dnamep;
char absolute[MAXPGPATH];
strncpy(dname, file->path, lengthof(dname));
dnamep = dirname(dname);
snprintf(absolute, lengthof(absolute), "%s/%s", dname, linked);
file = pgFileNew(absolute, omit_symlink);
}
else
file = pgFileNew(file->linked, omit_symlink);
/* linked file is not found, stop chasing link chain */
if (file == NULL)
return;
parray_append(files, file);
}
/*
* If the entry was a directory, add it to the list and add call this
* function recursivelly.
* If the directory name is in the exclude list, do not list the contents.
*/
while (S_ISDIR(file->mode))
{
int i;
bool skip = false;
DIR *dir;
struct dirent *dent;
char *dirname;
/* skip entry which matches exclude list */
dirname = strrchr(file->path, '/');
if (dirname == NULL)
dirname = file->path;
else
dirname++;
/*
* If the item in the exclude list starts with '/', compare to th
* absolute path of the directory. Otherwise compare to the directory
* name portion.
*/
for (i = 0; exclude && exclude[i]; i++)
{
if (exclude[i][0] == '/')
{
if (strcmp(file->path, exclude[i]) == 0)
{
skip = true;
break;
}
}
else
{
if (strcmp(dirname, exclude[i]) == 0)
{
skip = true;
break;
}
}
}
if (skip)
break;
/* open directory and list contents */
dir = opendir(file->path);
if (dir == NULL)
{
if (errno == ENOENT)
{
/* maybe the direcotry was removed */
return;
}
elog(ERROR_SYSTEM, _("can't open directory \"%s\": %s"),
file->path, strerror(errno));
}
errno = 0;
while ((dent = readdir(dir)))
{
char child[MAXPGPATH];
/* skip entries point current dir or parent dir */
if (strcmp(dent->d_name, ".") == 0 ||
strcmp(dent->d_name, "..") == 0)
continue;
snprintf(child, lengthof(child), "%s/%s", file->path, dent->d_name);
dir_list_file(files, child, exclude, omit_symlink, true);
}
if (errno && errno != ENOENT)
{
int errno_tmp = errno;
closedir(dir);
elog(ERROR_SYSTEM, _("can't read directory \"%s\": %s"),
file->path, strerror(errno_tmp));
}
closedir(dir);
break; /* psuedo loop */
}
parray_qsort(files, pgFileComparePath);
}
/* print mkdirs.sh */
void
dir_print_mkdirs_sh(FILE *out, const parray *files, const char *root)
{
int i;
for (i = 0; i < parray_num(files); i++)
{
pgFile *file = (pgFile *) parray_get(files, i);
if (S_ISDIR(file->mode))
{
if (strstr(file->path, root) == file->path)
fprintf(out, "mkdir -m 700 -p %s\n", file->path + strlen(root)
+ 1);
else
fprintf(out, "mkdir -m 700 -p %s\n", file->path);
}
}
fprintf(out, "\n");
for (i = 0; i < parray_num(files); i++)
{
pgFile *file = (pgFile *) parray_get(files, i);
if (S_ISLNK(file->mode))
{
fprintf(out, "rm -f %s\n", file->path + strlen(root) + 1);
fprintf(out, "ln -s %s %s\n", file->linked, file->path + strlen(root) + 1);
}
}
}
/* print file list */
void
dir_print_file_list(FILE *out, const parray *files, const char *root)
{
int i;
int root_len = 0;
/* calculate length of root directory portion */
if (root)
{
root_len = strlen(root);
if (root[root_len - 1] != '/')
root_len++;
}
/* print each file in the list */
for (i = 0; i < parray_num(files); i++) {
pgFile *file = (pgFile *)parray_get(files, i);
char *path = file->path;
char type;
/* omit root directory portion */
if (root && strstr(path, root) == path)
path = path + root_len;
if (S_ISREG(file->mode) && file->is_datafile)
type = 'F';
else if (S_ISREG(file->mode) && !file->is_datafile)
type = 'f';
else if (S_ISDIR(file->mode))
type = 'd';
else if (S_ISLNK(file->mode))
type = 'l';
else
type = '?';
fprintf(out, "%s %c %lu %u 0%o", path, type,
(unsigned long) file->write_size,
file->crc, file->mode & (S_IRWXU | S_IRWXG | S_IRWXO));
if (S_ISLNK(file->mode))
fprintf(out, " %s\n", file->linked);
else
{
char timestamp[20];
time2iso(timestamp, 20, file->mtime);
fprintf(out, " %s\n", timestamp);
}
}
}
/*
* Construct parray of pgFile from the file list.
* If root is not NULL, path will be absolute path.
*/
parray *
dir_read_file_list(const char *root, const char *file_txt)
{
FILE *fp;
parray *files;
char buf[MAXPGPATH * 2];
fp = fopen(file_txt, "rt");
if (fp == NULL)
elog(errno == ENOENT ? ERROR_CORRUPTED : ERROR_SYSTEM,
_("can't open \"%s\": %s"), file_txt, strerror(errno));
files = parray_new();
while (fgets(buf, lengthof(buf), fp))
{
char path[MAXPGPATH];
char type;
unsigned long write_size;
pg_crc32 crc;
unsigned int mode; /* bit length of mode_t depends on platforms */
struct tm tm;
pgFile *file;
if (sscanf(buf, "%s %c %lu %u %o %d-%d-%d %d:%d:%d",
path, &type, &write_size, &crc, &mode,
&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
&tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 11)
{
elog(ERROR_CORRUPTED, _("invalid format found in \"%s\""),
file_txt);
}
if (type != 'f' && type != 'F' && type != 'd' && type != 'l')
{
elog(ERROR_CORRUPTED, _("invalid type '%c' found in \"%s\""),
type, file_txt);
}
file = (pgFile *) pgut_malloc(offsetof(pgFile, path) +
(root ? strlen(root) + 1 : 0) + strlen(path) + 1);
tm.tm_year -= 1900;
tm.tm_mon -= 1;
file->mtime = mktime(&tm);
file->mode = mode |
((type == 'f' || type == 'F') ? S_IFREG :
type == 'd' ? S_IFDIR : type == 'l' ? S_IFLNK : 0);
file->size = 0;
file->read_size = 0;
file->write_size = write_size;
file->crc = crc;
file->is_datafile = (type == 'F' ? true : false);
file->linked = NULL;
if (root)
sprintf(file->path, "%s/%s", root, path);
else
strcpy(file->path, path);
parray_append(files, file);
}
fclose(fp);
/* file.txt is sorted, so this qsort is redundant */
parray_qsort(files, pgFileComparePath);
return files;
}
/* copy contents of directory from_root into to_root */
void
dir_copy_files(const char *from_root, const char *to_root)
{
int i;
parray *files;
files = parray_new();
/* don't copy root directory */
dir_list_file(files, from_root, NULL, true, false);
for (i = 0; i < parray_num(files); i++)
{
pgFile *file = (pgFile *) parray_get(files, i);
if (S_ISDIR(file->mode))
{
char to_path[MAXPGPATH];
snprintf(to_path, lengthof(to_path), "%s/%s", to_root,
file->path + strlen(from_root) + 1);
if (verbose && !check)
printf(_("create directory \"%s\"\n"),
file->path + strlen(from_root) + 1);
if (!check)
dir_create_dir(to_path, DIR_PERMISSION);
continue;
}
else if(S_ISREG(file->mode))
{
if (verbose && !check)
printf(_("copy \"%s\"\n"),
file->path + strlen(from_root) + 1);
if (!check)
copy_file(from_root, to_root, file, NO_COMPRESSION);
}
}
/* cleanup */
parray_walk(files, pgFileFree);
parray_free(files);
}

View File

@ -0,0 +1,40 @@
\! sh sql/backup_restore.sh
Line style is ascii.
CREATE TABLESPACE
CREATE DATABASE
# of deleted backups
0
full database backup
CHECKPOINT
incremental database backup
CHECKPOINT
CHECKPOINT
archived WAL and serverlog backup
stop DB during running pgbench
diff files in BACKUP_PATH/backup/pg_xlog
# of files in BACKUP_PATH/backup/srvlog
1
diff files in BACKUP_PATH/backup/pg_xlog
# of files in BACKUP_PATH/backup/srvlog
2
full database backup after recovery
CHECKPOINT
# of files in BACKUP_PATH/backup/pg_xlog
0
# of files in BACKUP_PATH/backup/srvlog
2
# of symbolic links in ARCLOG_PATH
0
# of files in BACKUP_PATH/timeline_history
2
# of recovery target option in recovery.conf
3
# of deleted backups (show all)
4
# of deleted backups
0
delete backup
# of deleted backups
4
# of deleted backups
9

14
expected/init.out Normal file
View File

@ -0,0 +1,14 @@
\! rm -rf results/init_test
\! pg_rman init -B results/init_test --quiet;echo $?
WARNING: ARCLOG_PATH is not set because archive_command is empty
0
\! find results/init_test | xargs ls -Fd | sort
results/init_test/
results/init_test/backup/
results/init_test/backup/pg_xlog/
results/init_test/backup/srvlog/
results/init_test/pg_rman.ini
results/init_test/timeline_history/
\! pg_rman init -B results/init_test --quiet;echo $?
ERROR: backup catalog already exist.
2

80
expected/option.out Normal file
View File

@ -0,0 +1,80 @@
\! sh sql/option.sh
pg_rman manage backup/recovery of PostgreSQL database.
Usage:
pg_rman OPTION init
pg_rman OPTION backup
pg_rman OPTION restore
pg_rman OPTION show [DATE]
pg_rman OPTION show timeline [DATE]
pg_rman OPTION validate [DATE]
pg_rman OPTION delete DATE
Common Options:
-D, --pgdata=PATH location of the database storage area
-A, --arclog-path=PATH location of archive WAL storage area
-S, --srvlog-path=PATH location of server log storage area
-B, --backup-path=PATH location of the backup storage area
-c, --check show what would have been done
Backup options:
-b, --backup-mode=MODE full, incremental, or archive
-s, --with-serverlog also backup server log files
-Z, --compress-data compress data backup with zlib
-C, --smooth-checkpoint do smooth checkpoint before backup
--keep-data-generations=N keep GENERATION of full data backup
--keep-data-days=DAY keep enough data backup to recover to DAY days age
--keep-arclog-files=NUM keep NUM of archived WAL
--keep-arclog-days=DAY keep archived WAL modified in DAY days
--keep-srvlog-files=NUM keep NUM of serverlogs
--keep-srvlog-days=DAY keep serverlog modified in DAY days
Restore options:
--recovery-target-time time stamp up to which recovery will proceed
--recovery-target-xid transaction ID up to which recovery will proceed
--recovery-target-inclusive whether we stop just after the recovery target
--recovery-target-timeline recovering into a particular timeline
Catalog options:
-a, --show-all show deleted backup too
Connection options:
-d, --dbname=DBNAME database to connect
-h, --host=HOSTNAME database server host or socket directory
-p, --port=PORT database server port
-U, --username=USERNAME user name to connect as
-w, --no-password never prompt for password
-W, --password force password prompt
Generic options:
-q, --quiet don't write any messages
--debug debug mode
--help show this help, then exit
--version output version information, then exit
Read the website for details. <http://rman.projects.postgresql.org/>
Report bugs to <rman-general@lists.pgfoundry.org>.
pg_rman 1.1.0
ERROR: required parameter not specified: BACKUP_PATH (-B, --backup-path)
ERROR: required parameter not specified: BACKUP_MODE (-b, --backup-mode)
ERROR: required parameter not specified: ARCLOG_PATH (-A, --arclog-path)
ERROR: required parameter not specified: ARCLOG_PATH (-A, --arclog-path)
ERROR: required parameter not specified: ARCLOG_PATH (-A, --arclog-path)
ERROR: invalid backup-mode "bad"
ERROR: required delete range option not specified: delete DATE
INFO: validate: 2009-05-31 17:05:53
INFO: validate: 2009-06-01 17:05:53
WARNING: CRC of backup file "PG_VERSION" must be 0 but FEF71BC1
WARNING: backup 2009-06-01 17:05:53 is corrupted
WARNING: syntax error in " = INFINITE".
ERROR: required parameter not specified: BACKUP_MODE (-b, --backup-mode)
ERROR: invalid backup-mode ""
ERROR: required parameter not specified: ARCLOG_PATH (-A, --arclog-path)
ERROR: required parameter not specified: ARCLOG_PATH (-A, --arclog-path)
ERROR: invalid backup-mode "B"
ERROR: option -Z, --compress-data should be a boolean: 'FOO'
ERROR: option --keep-arclog-files should be a 32bit signed integer: 'YES'
ERROR: invalid option "TIMELINEID"
ERROR: invalid option "BACKUP_TARGETS"
ERROR: invalid backup-mode "F''\F"
ERROR: invalid backup-mode "ENV_PATH"

View File

@ -0,0 +1,50 @@
-- test show command
\! rm -rf results/sample_backup
\! cp -rp data/sample_backup results/sample_backup
\! pg_rman show -B results/sample_backup
============================================================================
Start Time Total Data WAL Log Backup Status
============================================================================
2009-06-03 17:05:53 ---- ---- ---- ---- ---- ---- RUNNING
2009-06-01 17:05:53 3m ---- 9223PB 16MB ---- 162MB DONE
2009-05-31 17:05:53 3m 1242MB ---- 9223PB ---- 242MB DONE
\! pg_rman validate -B results/sample_backup 2009-05-31 17:05:53 --debug
INFO: validate: 2009-05-31 17:05:53
LOG: database files...
LOG: (1/1) PG_VERSION
LOG: archive WAL files...
LOG: backup 2009-05-31 17:05:53 is valid
\! pg_rman validate -B results/sample_backup 2009-06-01 17:05:53 --debug
INFO: validate: 2009-06-01 17:05:53
LOG: database files...
LOG: (1/1) PG_VERSION
WARNING: CRC of backup file "PG_VERSION" must be 0 but FEF71BC1
LOG: archive WAL files...
WARNING: backup 2009-06-01 17:05:53 is corrupted
\! pg_rman show -a -B results/sample_backup
============================================================================
Start Time Total Data WAL Log Backup Status
============================================================================
2009-06-03 17:05:53 ---- ---- ---- ---- ---- ---- RUNNING
2009-06-02 17:05:03 0m ---- ---- ---- 4335kB 162MB DELETED
2009-06-01 17:05:53 3m ---- 9223PB 16MB ---- 162MB CORRUPT
2009-05-31 17:05:53 3m 1242MB ---- 9223PB ---- 242MB OK
\! pg_rman show 2009-06-01 17:05:53 -B results/sample_backup
# configuration
BACKUP_MODE=INCREMENTAL
WITH_SERVERLOG=false
COMPRESS_DATA=false
# result
TIMELINEID=1
START_LSN=0/0b40c800
STOP_LSN=0/0b4c8020
START_TIME='2009-06-01 17:05:53'
END_TIME='2009-06-01 17:09:13'
TOTAL_DATA_BYTES=1242102558
READ_DATA_BYTES=9223372036854775807
READ_ARCLOG_BYTES=16777216
READ_SRVLOG_BYTES=-1
WRITE_BYTES=162372983
BLOCK_SIZE=8192
XLOG_BLOCK_SIZE=8192
STATUS=CORRUPT

156
init.c Normal file
View File

@ -0,0 +1,156 @@
/*-------------------------------------------------------------------------
*
* init.c: manage backup catalog.
*
* Copyright (c) 2009, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include <unistd.h>
static void parse_postgresql_conf(const char *path, char **log_directory,
char **archive_command);
/*
* Initialize backup catalog.
*/
int
do_init(void)
{
char path[MAXPGPATH];
char *log_directory = NULL;
char *archive_command = NULL;
FILE *fp;
if (access(backup_path, F_OK) == 0)
elog(ERROR, _("backup catalog already exist."));
/* create backup catalog root directory */
dir_create_dir(backup_path, DIR_PERMISSION);
/* create directories for backup of online files */
snprintf(path, lengthof(path), "%s/%s", backup_path, RESTORE_WORK_DIR);
dir_create_dir(path, DIR_PERMISSION);
snprintf(path, lengthof(path), "%s/%s/%s", backup_path, RESTORE_WORK_DIR,
PG_XLOG_DIR);
dir_create_dir(path, DIR_PERMISSION);
snprintf(path, lengthof(path), "%s/%s/%s", backup_path, RESTORE_WORK_DIR,
SRVLOG_DIR);
dir_create_dir(path, DIR_PERMISSION);
/* create directory for timeline history files */
snprintf(path, lengthof(path), "%s/%s", backup_path, TIMELINE_HISTORY_DIR);
dir_create_dir(path, DIR_PERMISSION);
/* read postgresql.conf */
if (pgdata)
{
snprintf(path, lengthof(path), "%s/%s", pgdata, "postgresql.conf");
parse_postgresql_conf(path, &log_directory, &archive_command);
}
/* create pg_rman.ini */
snprintf(path, lengthof(path), "%s/%s", backup_path, PG_RMAN_INI_FILE);
fp = fopen(path, "wt");
if (fp == NULL)
elog(ERROR_SYSTEM, _("can't create pg_rman.ini: %s"), strerror(errno));
/* set ARCLOG_PATH refered with log_directory */
if (arclog_path == NULL && archive_command && archive_command[0])
{
char *command = pgut_strdup(archive_command);
char *begin;
char *end;
char *fname;
/* example: 'cp "%p" /path/to/arclog/"%f"' */
for (begin = command; *begin;)
{
begin = begin + strspn(begin, " \n\r\t\v");
end = begin + strcspn(begin, " \n\r\t\v");
*end = '\0';
if ((fname = strstr(begin, "%f")) != NULL)
{
while (strchr(" \n\r\t\v""'", *begin))
begin++;
fname--;
while (fname > begin && strchr(" \n\r\t\v\"'/", fname[-1]))
fname--;
*fname = '\0';
if (is_absolute_path(begin))
arclog_path = pgut_strdup(begin);
break;
}
begin = end + 1;
}
free(command);
}
if (arclog_path)
{
fprintf(fp, "ARCLOG_PATH='%s'\n", arclog_path);
elog(INFO, "ARCLOG_PATH is set to '%s'", arclog_path);
}
else if (archive_command && archive_command[0])
elog(WARNING, "ARCLOG_PATH is not set because failed to parse archive_command '%s'", archive_command);
else
elog(WARNING, "ARCLOG_PATH is not set because archive_command is empty");
/* set SRVLOG_PATH refered with log_directory */
if (srvlog_path == NULL)
{
if (log_directory)
{
if (is_absolute_path(log_directory))
srvlog_path = pgut_strdup(log_directory);
else
{
srvlog_path = pgut_malloc(MAXPGPATH);
snprintf(srvlog_path, MAXPGPATH, "%s/%s", pgdata, log_directory);
}
}
else if (pgdata)
{
/* default: log_directory = 'pg_log' */
srvlog_path = pgut_malloc(MAXPGPATH);
snprintf(srvlog_path, MAXPGPATH, "%s/%s", pgdata, "pg_log");
}
}
if (srvlog_path)
{
fprintf(fp, "SRVLOG_PATH='%s'\n", srvlog_path);
elog(INFO, "SRVLOG_PATH is set to '%s'", srvlog_path);
}
fprintf(fp, "\n");
fclose(fp);
free(archive_command);
free(log_directory);
return 0;
}
static void
parse_postgresql_conf(const char *path,
char **log_directory,
char **archive_command)
{
pgut_option options[] =
{
{ 's', 0, "log_directory" , NULL, SOURCE_ENV },
{ 's', 0, "archive_command" , NULL, SOURCE_ENV },
{ 0 }
};
options[0].var = log_directory;
options[1].var = archive_command;
pgut_readopt(path, options, LOG); /* ignore unknown options */
}

184
parray.c Normal file
View File

@ -0,0 +1,184 @@
/*-------------------------------------------------------------------------
*
* parray.c: pointer array collection.
*
* Copyright (c) 2009, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
/* members of struct parray are hidden from client. */
struct parray
{
void **data; /* poiter array, expanded if necessary */
size_t alloced; /* number of elements allocated */
size_t used; /* number of elements in use */
};
/*
* Create new parray object.
* Never returns NULL.
*/
parray *
parray_new(void)
{
parray *a = pgut_malloc(sizeof(parray));
a->data = NULL;
a->used = 0;
a->alloced = 0;
parray_expand(a, 1024);
return a;
}
/*
* Expand array pointed by data to newsize.
* Elements in expanded area are initialized to NULL.
* Note: never returns NULL.
*/
void
parray_expand(parray *array, size_t newsize)
{
void **p;
/* already allocated */
if (newsize <= array->alloced)
return;
p = pgut_realloc(array->data, sizeof(void *) * newsize);
/* initialize expanded area to NULL */
memset(p + array->alloced, 0, (newsize - array->alloced) * sizeof(void *));
array->alloced = newsize;
array->data = p;
return;
}
void
parray_free(parray *array)
{
if (array == NULL)
return;
free(array->data);
free(array);
}
void
parray_append(parray *array, void *elem)
{
if (array->used + 1 > array->alloced)
parray_expand(array, array->alloced * 2);
array->data[array->used++] = elem;
}
void
parray_insert(parray *array, size_t index, void *elem)
{
if (array->used + 1 > array->alloced)
parray_expand(array, array->alloced * 2);
memmove(array->data + index + 1, array->data + index,
(array->alloced - index - 1) * sizeof(void *));
array->data[index] = elem;
/* adjust used count */
if (array->used < index + 1)
array->used = index + 1;
else
array->used++;
}
/*
* Concatinate two parray.
* parray_concat() appends the copy of the content of src to the end of dest.
*/
parray *
parray_concat(parray *dest, const parray *src)
{
/* expand head array */
parray_expand(dest, dest->used + src->used);
/* copy content of src after content of dest */
memcpy(dest->data + dest->used * sizeof(void *), src->data,
src->used * sizeof(void *));
dest->used += parray_num(src);
return dest;
}
void
parray_set(parray *array, size_t index, void *elem)
{
if (index > array->alloced - 1)
parray_expand(array, index + 1);
array->data[index] = elem;
/* adjust used count */
if (array->used < index + 1)
array->used = index + 1;
}
void *
parray_get(const parray *array, size_t index)
{
if (index > array->alloced - 1)
return NULL;
return array->data[index];
}
void *
parray_remove(parray *array, size_t index)
{
void *val;
/* removing unused element */
if (index > array->used)
return NULL;
val = array->data[index];
/* Do not move if the last element was removed. */
if (index < array->alloced - 1)
memmove(array->data + index, array->data + index + 1,
(array->alloced - index - 1) * sizeof(void *));
/* adjust used count */
array->used--;
return val;
}
size_t
parray_num(const parray *array)
{
return array->used;
}
void
parray_qsort(parray *array, int(*compare)(const void *, const void *))
{
qsort(array->data, array->used, sizeof(void *), compare);
}
void
parray_walk(parray *array, void (*action)(void *))
{
int i;
for (i = 0; i < array->used; i++)
action(array->data[i]);
}
void *
parray_bsearch(parray *array, const void *key, int(*compare)(const void *, const void *))
{
return bsearch(&key, array->data, array->used, sizeof(void *), compare);
}

34
parray.h Normal file
View File

@ -0,0 +1,34 @@
/*-------------------------------------------------------------------------
*
* parray.h: pointer array collection.
*
* Copyright (c) 2009, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#ifndef PARRAY_H
#define PARRAY_H
/*
* "parray" hold pointers to objects in a linear memory area.
* Client use "parray *" to access parray object.
*/
typedef struct parray parray;
extern parray *parray_new(void);
extern void parray_expand(parray *array, size_t newnum);
extern void parray_free(parray *array);
extern void parray_append(parray *array, void *val);
extern void parray_insert(parray *array, size_t index, void *val);
extern parray *parray_concat(parray *head, const parray *tail);
extern void parray_set(parray *array, size_t index, void *val);
extern void *parray_get(const parray *array, size_t index);
extern void *parray_remove(parray *array, size_t index);
extern size_t parray_num(const parray *array);
extern void parray_qsort(parray *array, int(*compare)(const void *, const void *));
extern void *parray_bsearch(parray *array, const void *key, int(*compare)(const void *, const void *));
extern void parray_walk(parray *array, void (*action)(void *));
#endif /* PARRAY_H */

301
pg_rman.c Normal file
View File

@ -0,0 +1,301 @@
/*-------------------------------------------------------------------------
*
* pg_rman.c: Backup/Recovery manager for PostgreSQL.
*
* Copyright (c) 2009, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
const char *PROGRAM_VERSION = "1.1.1";
const char *PROGRAM_URL = "https://code.google.com/p/pg-rman/";
const char *PROGRAM_EMAIL = "https://code.google.com/p/pg-rman/issues/list";
/* path configuration */
char *backup_path;
char *pgdata;
char *arclog_path;
char *srvlog_path;
/* common configuration */
bool verbose = false;
bool check = false;
/* directory configuration */
pgBackup current;
/* backup configuration */
static bool smooth_checkpoint;
static int keep_arclog_files = KEEP_INFINITE;
static int keep_arclog_days = KEEP_INFINITE;
static int keep_srvlog_files = KEEP_INFINITE;
static int keep_srvlog_days = KEEP_INFINITE;
static int keep_data_generations = KEEP_INFINITE;
static int keep_data_days = KEEP_INFINITE;
/* restore configuration */
static char *target_time;
static char *target_xid;
static char *target_inclusive;
static TimeLineID target_tli;
/* show configuration */
static bool show_all = false;
static void opt_backup_mode(pgut_option *opt, const char *arg);
static void parse_range(pgBackupRange *range, const char *arg1, const char *arg2);
static pgut_option options[] =
{
/* directory options */
{ 's', 'D', "pgdata" , &pgdata , SOURCE_ENV },
{ 's', 'A', "arclog-path" , &arclog_path , SOURCE_ENV },
{ 's', 'B', "backup-path" , &backup_path , SOURCE_ENV },
{ 's', 'S', "srvlog-path" , &srvlog_path , SOURCE_ENV },
/* common options */
{ 'b', 'v', "verbose" , &verbose },
{ 'b', 'c', "check" , &check },
/* backup options */
{ 'f', 'b', "backup-mode" , opt_backup_mode , SOURCE_ENV },
{ 'b', 's', "with-serverlog" , &current.with_serverlog , SOURCE_ENV },
{ 'b', 'Z', "compress-data" , &current.compress_data , SOURCE_ENV },
{ 'b', 'C', "smooth-checkpoint" , &smooth_checkpoint , SOURCE_ENV },
/* options with only long name (keep-xxx) */
{ 'i', 1, "keep-data-generations" , &keep_data_generations, SOURCE_ENV },
{ 'i', 2, "keep-data-days" , &keep_data_days , SOURCE_ENV },
{ 'i', 3, "keep-arclog-files" , &keep_arclog_files , SOURCE_ENV },
{ 'i', 4, "keep-arclog-days" , &keep_arclog_days , SOURCE_ENV },
{ 'i', 5, "keep-srvlog-files" , &keep_srvlog_files , SOURCE_ENV },
{ 'i', 6, "keep-srvlog-days" , &keep_srvlog_days , SOURCE_ENV },
/* restore options */
{ 's', 7, "recovery-target-time" , &target_time , SOURCE_ENV },
{ 's', 8, "recovery-target-xid" , &target_xid , SOURCE_ENV },
{ 's', 9, "recovery-target-inclusive" , &target_inclusive , SOURCE_ENV },
{ 'u', 10, "recovery-target-timeline" , &target_tli , SOURCE_ENV },
/* catalog options */
{ 'b', 'a', "show-all" , &show_all },
{ 0 }
};
/*
* Entry point of pg_rman command.
*/
int
main(int argc, char *argv[])
{
const char *cmd = NULL;
const char *range1 = NULL;
const char *range2 = NULL;
bool show_timeline = false;
pgBackupRange range;
int i;
/* do not buffer progress messages */
setvbuf(stdout, 0, _IONBF, 0); /* TODO: remove this */
/* initialize configuration */
catalog_init_config(&current);
/* overwrite configuration with command line arguments */
i = pgut_getopt(argc, argv, options);
for (; i < argc; i++)
{
if (cmd == NULL)
cmd = argv[i];
else if (pg_strcasecmp(argv[i], "timeline") == 0 &&
pg_strcasecmp(cmd, "show") == 0)
show_timeline = true;
else if (range1 == NULL)
range1 = argv[i];
else if (range2 == NULL)
range2 = argv[i];
else
elog(ERROR_ARGS, "too many arguments");
}
/* command argument (backup/restore/show/...) is required. */
if (cmd == NULL)
{
help(false);
return HELP;
}
/* get object range argument if any */
if (range1 && range2)
parse_range(&range, range1, range2);
else if (range1)
parse_range(&range, range1, "");
else
range.begin = range.end = 0;
/* Read default configuration from file. */
if (backup_path)
{
char path[MAXPGPATH];
snprintf(path, lengthof(path), "%s/%s", backup_path, PG_RMAN_INI_FILE);
pgut_readopt(path, options, ERROR_ARGS);
}
/* BACKUP_PATH is always required */
if (backup_path == NULL)
elog(ERROR_ARGS, "required parameter not specified: BACKUP_PATH (-B, --backup-path)");
/* path must be absolute */
if (pgdata != NULL && !is_absolute_path(pgdata))
elog(ERROR_ARGS, "-D, --pgdata must be an absolute path");
if (arclog_path != NULL && !is_absolute_path(arclog_path))
elog(ERROR_ARGS, "-A, --arclog-path must be an absolute path");
if (srvlog_path != NULL && !is_absolute_path(srvlog_path))
elog(ERROR_ARGS, "-S, --srvlog-path must be an absolute path");
/* setup exclusion list for file search */
for (i = 0; pgdata_exclude[i]; i++) /* find first empty slot */
;
if (arclog_path)
pgdata_exclude[i++] = arclog_path;
if (srvlog_path)
pgdata_exclude[i++] = srvlog_path;
/* do actual operation */
if (pg_strcasecmp(cmd, "init") == 0)
return do_init();
else if (pg_strcasecmp(cmd, "backup") == 0)
return do_backup(smooth_checkpoint,
keep_arclog_files, keep_arclog_days,
keep_srvlog_files, keep_srvlog_days,
keep_data_generations, keep_data_days);
else if (pg_strcasecmp(cmd, "restore") == 0)
return do_restore(target_time, target_xid, target_inclusive, target_tli);
else if (pg_strcasecmp(cmd, "show") == 0)
return do_show(&range, show_timeline, show_all);
else if (pg_strcasecmp(cmd, "validate") == 0)
return do_validate(&range);
else if (pg_strcasecmp(cmd, "delete") == 0)
return do_delete(&range);
else
elog(ERROR_ARGS, "invalid command \"%s\"", cmd);
return 0;
}
void
pgut_help(bool details)
{
printf(_("%s manage backup/recovery of PostgreSQL database.\n\n"), PROGRAM_NAME);
printf(_("Usage:\n"));
printf(_(" %s OPTION init\n"), PROGRAM_NAME);
printf(_(" %s OPTION backup\n"), PROGRAM_NAME);
printf(_(" %s OPTION restore\n"), PROGRAM_NAME);
printf(_(" %s OPTION show [DATE]\n"), PROGRAM_NAME);
printf(_(" %s OPTION show timeline [DATE]\n"), PROGRAM_NAME);
printf(_(" %s OPTION validate [DATE]\n"), PROGRAM_NAME);
printf(_(" %s OPTION delete DATE\n"), PROGRAM_NAME);
if (!details)
return;
printf(_("\nCommon Options:\n"));
printf(_(" -D, --pgdata=PATH location of the database storage area\n"));
printf(_(" -A, --arclog-path=PATH location of archive WAL storage area\n"));
printf(_(" -S, --srvlog-path=PATH location of server log storage area\n"));
printf(_(" -B, --backup-path=PATH location of the backup storage area\n"));
printf(_(" -c, --check show what would have been done\n"));
printf(_("\nBackup options:\n"));
printf(_(" -b, --backup-mode=MODE full, incremental, or archive\n"));
printf(_(" -s, --with-serverlog also backup server log files\n"));
printf(_(" -Z, --compress-data compress data backup with zlib\n"));
printf(_(" -C, --smooth-checkpoint do smooth checkpoint before backup\n"));
printf(_(" --keep-data-generations=N keep GENERATION of full data backup\n"));
printf(_(" --keep-data-days=DAY keep enough data backup to recover to DAY days age\n"));
printf(_(" --keep-arclog-files=NUM keep NUM of archived WAL\n"));
printf(_(" --keep-arclog-days=DAY keep archived WAL modified in DAY days\n"));
printf(_(" --keep-srvlog-files=NUM keep NUM of serverlogs\n"));
printf(_(" --keep-srvlog-days=DAY keep serverlog modified in DAY days\n"));
printf(_("\nRestore options:\n"));
printf(_(" --recovery-target-time time stamp up to which recovery will proceed\n"));
printf(_(" --recovery-target-xid transaction ID up to which recovery will proceed\n"));
printf(_(" --recovery-target-inclusive whether we stop just after the recovery target\n"));
printf(_(" --recovery-target-timeline recovering into a particular timeline\n"));
printf(_("\nCatalog options:\n"));
printf(_(" -a, --show-all show deleted backup too\n"));
}
/*
* Create range object from one or two arguments.
* All not-digit characters in the argument(s) are igonred.
* Both arg1 and arg2 must be valid pointer.
*/
static void
parse_range(pgBackupRange *range, const char *arg1, const char *arg2)
{
size_t len = strlen(arg1) + strlen(arg2) + 1;
char *tmp;
int num;
struct tm tm;
tmp = pgut_malloc(len);
tmp[0] = '\0';
if (arg1 != NULL)
remove_not_digit(tmp, len, arg1);
if (arg2 != NULL)
remove_not_digit(tmp + strlen(tmp), len - strlen(tmp), arg2);
tm.tm_year = 0; /* tm_year is year - 1900 */
tm.tm_mon = 0; /* tm_mon is 0 - 11 */
tm.tm_mday = 1; /* tm_mday is 1 - 31 */
tm.tm_hour = 0;
tm.tm_min = 0;
tm.tm_sec = 0;
num = sscanf(tmp, "%04d %02d %02d %02d %02d %02d",
&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
&tm.tm_hour, &tm.tm_min, &tm.tm_sec);
if (num < 1)
elog(ERROR_ARGS, _("supplied id(%s) is invalid."), tmp);
free(tmp);
/* adjust year and month to convert to time_t */
tm.tm_year -= 1900;
if (num > 1)
tm.tm_mon -= 1;
range->begin = mktime(&tm);
switch (num)
{
case 1:
tm.tm_year++;
break;
case 2:
tm.tm_mon++;
break;
case 3:
tm.tm_mday++;
break;
case 4:
tm.tm_hour++;
break;
case 5:
tm.tm_min++;
break;
case 6:
tm.tm_sec++;
break;
}
range->end = mktime(&tm);
range->end--;
}
static void
opt_backup_mode(pgut_option *opt, const char *arg)
{
current.backup_mode = parse_backup_mode(arg, ERROR_ARGS);
}

283
pg_rman.h Normal file
View File

@ -0,0 +1,283 @@
/*-------------------------------------------------------------------------
*
* pg_rman.h: Backup/Recovery manager for PostgreSQL.
*
* Copyright (c) 2009, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#ifndef PG_RMAN_H
#define PG_RMAN_H
#include "postgres_fe.h"
#include <limits.h>
#include "libpq-fe.h"
#include "pgut/pgut.h"
#include "access/xlogdefs.h"
#include "utils/pg_crc.h"
#include "parray.h"
/* Directory/File names */
#define DATABASE_DIR "database"
#define ARCLOG_DIR "arclog"
#define SRVLOG_DIR "srvlog"
#define RESTORE_WORK_DIR "backup"
#define PG_XLOG_DIR "pg_xlog"
#define TIMELINE_HISTORY_DIR "timeline_history"
#define BACKUP_INI_FILE "backup.ini"
#define PG_RMAN_INI_FILE "pg_rman.ini"
#define MKDIRS_SH_FILE "mkdirs.sh"
#define DATABASE_FILE_LIST "file_database.txt"
#define ARCLOG_FILE_LIST "file_arclog.txt"
#define SRVLOG_FILE_LIST "file_srvlog.txt"
/* Direcotry/File permission */
#define DIR_PERMISSION (0700)
#define FILE_PERMISSION (0600)
/* Exit code */
#define ERROR_ARCHIVE_FAILED 20 /* cannot archive xlog file */
#define ERROR_NO_BACKUP 21 /* backup was not found in the catalog */
#define ERROR_CORRUPTED 22 /* backup catalog is corrupted */
#define ERROR_ALREADY_RUNNING 23 /* another pg_rman is running */
#define ERROR_PG_INCOMPATIBLE 24 /* block size is not compatible */
#define ERROR_PG_RUNNING 25 /* PostgreSQL server is running */
#define ERROR_PID_BROKEN 26 /* postmaster.pid file is broken */
/* backup mode file */
typedef struct pgFile
{
time_t mtime; /* time of last modification */
mode_t mode; /* protection (file type and permission) */
size_t size; /* size of the file */
size_t read_size; /* size of the portion read (if only some pages are
backed up partially, it's different from size) */
size_t write_size; /* size of the backed-up file. BYTES_INVALID means
that the file existed but was not backed up
because not modified since last backup. */
pg_crc32 crc; /* CRC value of the file, regular file only */
char *linked; /* path of the linked file */
bool is_datafile; /* true if the file is PostgreSQL data file */
char path[1]; /* path of the file */
} pgFile;
typedef struct pgBackupRange
{
time_t begin;
time_t end; /* begin +1 when one backup is target */
} pgBackupRange;
#define pgBackupRangeIsValid(range) \
(((range)->begin != (time_t) 0) || ((range)->end != (time_t) 0))
#define pgBackupRangeIsSingle(range) \
(pgBackupRangeIsValid(range) && (range)->begin == ((range)->end))
/* Backup status */
/* XXX re-order ? */
typedef enum BackupStatus
{
BACKUP_STATUS_INVALID, /* the pgBackup is invalid */
BACKUP_STATUS_OK, /* completed backup */
BACKUP_STATUS_RUNNING, /* running backup */
BACKUP_STATUS_ERROR, /* aborted because of unexpected error */
BACKUP_STATUS_DELETING, /* data files are being deleted */
BACKUP_STATUS_DELETED, /* data files have been deleted */
BACKUP_STATUS_DONE, /* completed but not validated yet */
BACKUP_STATUS_CORRUPT /* files are corrupted, not available */
} BackupStatus;
typedef enum BackupMode
{
BACKUP_MODE_INVALID,
BACKUP_MODE_ARCHIVE, /* arhicve only */
BACKUP_MODE_INCREMENTAL, /* incremental backup */
BACKUP_MODE_FULL /* full backup */
} BackupMode;
/*
* pg_rman takes backup into the directroy $BACKUP_PATH/<date>/<time>.
*
* status == -1 indicates the pgBackup is invalid.
*/
typedef struct pgBackup
{
/* Backup Level */
BackupMode backup_mode;
bool with_serverlog;
bool compress_data;
/* Status - one of BACKUP_STATUS_xxx */
BackupStatus status;
/* Timestamp, etc. */
TimeLineID tli;
XLogRecPtr start_lsn;
XLogRecPtr stop_lsn;
time_t start_time;
time_t end_time;
/* Size (-1 means not-backup'ed) */
int64 total_data_bytes;
int64 read_data_bytes;
int64 read_arclog_bytes;
int64 read_srvlog_bytes;
int64 write_bytes;
/* data/wal block size for compatibility check */
uint32 block_size;
uint32 wal_block_size;
} pgBackup;
/* special values of pgBackup */
#define KEEP_INFINITE (INT_MAX)
#define BYTES_INVALID (-1)
#define HAVE_DATABASE(backup) ((backup)->backup_mode >= BACKUP_MODE_INCREMENTAL)
#define HAVE_ARCLOG(backup) ((backup)->backup_mode >= BACKUP_MODE_ARCHIVE)
#define TOTAL_READ_SIZE(backup) \
((HAVE_DATABASE((backup)) ? (backup)->read_data_bytes : 0) + \
(HAVE_ARCLOG((backup)) ? (backup)->read_arclog_bytes : 0) + \
((backup)->with_serverlog ? (backup)->read_srvlog_bytes : 0))
typedef struct pgTimeLine
{
TimeLineID tli;
XLogRecPtr end;
} pgTimeLine;
typedef enum CompressionMode
{
NO_COMPRESSION,
COMPRESSION,
DECOMPRESSION,
} CompressionMode;
/* path configuration */
extern char *backup_path;
extern char *pgdata;
extern char *arclog_path;
extern char *srvlog_path;
/* common configuration */
extern bool verbose;
extern bool check;
/* current settings */
extern pgBackup current;
/* exclude directory list for $PGDATA file listing */
extern const char *pgdata_exclude[];
/* in backup.c */
extern int do_backup(bool smooth_checkpoint,
int keep_arclog_files,
int keep_arclog_days,
int keep_srvlog_files,
int keep_srvlog_days,
int keep_data_generations,
int keep_data_days);
extern BackupMode parse_backup_mode(const char *value, int elevel);
/* in restore.c */
extern int do_restore(const char *target_time,
const char *target_xid,
const char *target_inclusive,
TimeLineID target_tli);
/* in init.c */
extern int do_init(void);
/* in show.c */
extern int do_show(pgBackupRange *range, bool show_timeline, bool show_all);
/* in delete.c */
extern int do_delete(pgBackupRange *range);
extern void pgBackupDelete(int keep_generations, int keep_days);
/* in validate.c */
extern int do_validate(pgBackupRange *range);
extern void pgBackupValidate(pgBackup *backup, bool size_only);
/* in catalog.c */
extern pgBackup *catalog_get_backup(time_t timestamp);
extern parray *catalog_get_backup_list(const pgBackupRange *range);
extern pgBackup *catalog_get_last_data_backup(parray *backup_list);
extern pgBackup *catalog_get_last_arclog_backup(parray *backup_list);
extern pgBackup *catalog_get_last_srvlog_backup(parray *backup_list);
extern int catalog_lock(void);
extern void catalog_unlock(void);
extern void catalog_init_config(pgBackup *backup);
extern void pgBackupWriteConfigSection(FILE *out, pgBackup *backup);
extern void pgBackupWriteResultSection(FILE *out, pgBackup *backup);
extern void pgBackupWriteIni(pgBackup *backup);
extern void pgBackupGetPath(const pgBackup *backup, char *path, size_t len, const char *subdir);
extern int pgBackupCreateDir(pgBackup *backup);
extern void pgBackupFree(void *backup);
extern int pgBackupCompareId(const void *f1, const void *f2);
extern int pgBackupCompareIdDesc(const void *f1, const void *f2);
/* in dir.c */
extern void dir_list_file(parray *files, const char *root, const char *exclude[], bool omit_symlink, bool add_root);
extern void dir_print_mkdirs_sh(FILE *out, const parray *files, const char *root);
extern void dir_print_file_list(FILE *out, const parray *files, const char *root);
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);
extern int pgFileComparePath(const void *f1, const void *f2);
extern int pgFileComparePathDesc(const void *f1, const void *f2);
extern int pgFileCompareMtime(const void *f1, const void *f2);
extern int pgFileCompareMtimeDesc(const void *f1, const void *f2);
/* in xlog.c */
extern bool xlog_is_complete_wal(const pgFile *file);
extern bool xlog_logfname2lsn(const char *logfname, XLogRecPtr *lsn);
extern void xlog_fname(char *fname, size_t len, TimeLineID tli, XLogRecPtr *lsn);
/* in data.c */
extern void backup_data_file(const char *from_root, const char *to_root,
pgFile *file, const XLogRecPtr *lsn, bool compress);
extern void restore_data_file(const char *from_root, const char *to_root,
pgFile *file, bool compress);
extern void copy_file(const char *from_root, const char *to_root,
pgFile *file, CompressionMode compress);
/* in util.c */
extern void time2iso(char *buf, size_t len, time_t time);
extern const char *status2str(BackupStatus status);
extern void remove_trailing_space(char *buf, int comment_mark);
extern void remove_not_digit(char *buf, size_t len, const char *str);
/* in pgsql_src/pg_ctl.c */
extern bool is_pg_running(void);
/* access/xlog_internal.h */
#define XLogSegSize ((uint32) XLOG_SEG_SIZE)
#define XLogSegsPerFile (((uint32) 0xffffffff) / XLogSegSize)
#define XLogFileSize (XLogSegsPerFile * XLogSegSize)
#define NextLogSeg(logId, logSeg) \
do { \
if ((logSeg) >= XLogSegsPerFile-1) \
{ \
(logId)++; \
(logSeg) = 0; \
} \
else \
(logSeg)++; \
} while (0)
#define MAXFNAMELEN 64
#define XLogFileName(fname, tli, log, seg) \
snprintf(fname, MAXFNAMELEN, "%08X%08X%08X", tli, log, seg)
#endif /* PG_RMAN_H */

View File

@ -0,0 +1,24 @@
Files in this directory are parts of PostgreSQL Database Management System.
Copyright holders of those files are following organizations:
Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
Portions Copyright (c) 1994, The Regents of the University of California
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose, without fee, and without a written agreement
is hereby granted, provided that the above copyright notice and this
paragraph and the following two paragraphs appear in all copies.
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING
LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO
PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

515
pgsql_src/pg_crc.c Normal file
View File

@ -0,0 +1,515 @@
/*-------------------------------------------------------------------------
*
* pg_crc.c
* PostgreSQL CRC support
*
* See Ross Williams' excellent introduction
* A PAINLESS GUIDE TO CRC ERROR DETECTION ALGORITHMS, available from
* http://www.ross.net/crc/download/crc_v3.txt or several other net sites.
*
* We use a normal (not "reflected", in Williams' terms) CRC, using initial
* all-ones register contents and a final bit inversion.
*
* The 64-bit variant is not used as of PostgreSQL 8.1, but we retain the
* code for possible future use.
*
*
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/hash/pg_crc.c,v 1.21 2009/01/01 17:23:51 momjian Exp $
*
*-------------------------------------------------------------------------
*/
/* Use c.h so that this file can be built in either frontend or backend */
#include "c.h"
/*
* This table is based on the polynomial
* x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1.
* (This is the same polynomial used in Ethernet checksums, for instance.)
*/
const uint32 pg_crc32_table[256] = {
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
};
#ifdef PROVIDE_64BIT_CRC
/*
* This table is based on the polynomial
*
* x^64 + x^62 + x^57 + x^55 + x^54 + x^53 + x^52 + x^47 + x^46 + x^45 +
* x^40 + x^39 + x^38 + x^37 + x^35 + x^33 + x^32 + x^31 + x^29 + x^27 +
* x^24 + x^23 + x^22 + x^21 + x^19 + x^17 + x^13 + x^12 + x^10 + x^9 +
* x^7 + x^4 + x + 1
*
* which is borrowed from the DLT1 spec
* (ECMA-182, available from http://www.ecma.ch/ecma1/STAND/ECMA-182.HTM)
*/
#ifdef INT64_IS_BUSTED
const uint32 pg_crc64_table0[256] = {
0x00000000, 0xA9EA3693,
0x53D46D26, 0xFA3E5BB5,
0x0E42ECDF, 0xA7A8DA4C,
0x5D9681F9, 0xF47CB76A,
0x1C85D9BE, 0xB56FEF2D,
0x4F51B498, 0xE6BB820B,
0x12C73561, 0xBB2D03F2,
0x41135847, 0xE8F96ED4,
0x90E185EF, 0x390BB37C,
0xC335E8C9, 0x6ADFDE5A,
0x9EA36930, 0x37495FA3,
0xCD770416, 0x649D3285,
0x8C645C51, 0x258E6AC2,
0xDFB03177, 0x765A07E4,
0x8226B08E, 0x2BCC861D,
0xD1F2DDA8, 0x7818EB3B,
0x21C30BDE, 0x88293D4D,
0x721766F8, 0xDBFD506B,
0x2F81E701, 0x866BD192,
0x7C558A27, 0xD5BFBCB4,
0x3D46D260, 0x94ACE4F3,
0x6E92BF46, 0xC77889D5,
0x33043EBF, 0x9AEE082C,
0x60D05399, 0xC93A650A,
0xB1228E31, 0x18C8B8A2,
0xE2F6E317, 0x4B1CD584,
0xBF6062EE, 0x168A547D,
0xECB40FC8, 0x455E395B,
0xADA7578F, 0x044D611C,
0xFE733AA9, 0x57990C3A,
0xA3E5BB50, 0x0A0F8DC3,
0xF031D676, 0x59DBE0E5,
0xEA6C212F, 0x438617BC,
0xB9B84C09, 0x10527A9A,
0xE42ECDF0, 0x4DC4FB63,
0xB7FAA0D6, 0x1E109645,
0xF6E9F891, 0x5F03CE02,
0xA53D95B7, 0x0CD7A324,
0xF8AB144E, 0x514122DD,
0xAB7F7968, 0x02954FFB,
0x7A8DA4C0, 0xD3679253,
0x2959C9E6, 0x80B3FF75,
0x74CF481F, 0xDD257E8C,
0x271B2539, 0x8EF113AA,
0x66087D7E, 0xCFE24BED,
0x35DC1058, 0x9C3626CB,
0x684A91A1, 0xC1A0A732,
0x3B9EFC87, 0x9274CA14,
0xCBAF2AF1, 0x62451C62,
0x987B47D7, 0x31917144,
0xC5EDC62E, 0x6C07F0BD,
0x9639AB08, 0x3FD39D9B,
0xD72AF34F, 0x7EC0C5DC,
0x84FE9E69, 0x2D14A8FA,
0xD9681F90, 0x70822903,
0x8ABC72B6, 0x23564425,
0x5B4EAF1E, 0xF2A4998D,
0x089AC238, 0xA170F4AB,
0x550C43C1, 0xFCE67552,
0x06D82EE7, 0xAF321874,
0x47CB76A0, 0xEE214033,
0x141F1B86, 0xBDF52D15,
0x49899A7F, 0xE063ACEC,
0x1A5DF759, 0xB3B7C1CA,
0x7D3274CD, 0xD4D8425E,
0x2EE619EB, 0x870C2F78,
0x73709812, 0xDA9AAE81,
0x20A4F534, 0x894EC3A7,
0x61B7AD73, 0xC85D9BE0,
0x3263C055, 0x9B89F6C6,
0x6FF541AC, 0xC61F773F,
0x3C212C8A, 0x95CB1A19,
0xEDD3F122, 0x4439C7B1,
0xBE079C04, 0x17EDAA97,
0xE3911DFD, 0x4A7B2B6E,
0xB04570DB, 0x19AF4648,
0xF156289C, 0x58BC1E0F,
0xA28245BA, 0x0B687329,
0xFF14C443, 0x56FEF2D0,
0xACC0A965, 0x052A9FF6,
0x5CF17F13, 0xF51B4980,
0x0F251235, 0xA6CF24A6,
0x52B393CC, 0xFB59A55F,
0x0167FEEA, 0xA88DC879,
0x4074A6AD, 0xE99E903E,
0x13A0CB8B, 0xBA4AFD18,
0x4E364A72, 0xE7DC7CE1,
0x1DE22754, 0xB40811C7,
0xCC10FAFC, 0x65FACC6F,
0x9FC497DA, 0x362EA149,
0xC2521623, 0x6BB820B0,
0x91867B05, 0x386C4D96,
0xD0952342, 0x797F15D1,
0x83414E64, 0x2AAB78F7,
0xDED7CF9D, 0x773DF90E,
0x8D03A2BB, 0x24E99428,
0x975E55E2, 0x3EB46371,
0xC48A38C4, 0x6D600E57,
0x991CB93D, 0x30F68FAE,
0xCAC8D41B, 0x6322E288,
0x8BDB8C5C, 0x2231BACF,
0xD80FE17A, 0x71E5D7E9,
0x85996083, 0x2C735610,
0xD64D0DA5, 0x7FA73B36,
0x07BFD00D, 0xAE55E69E,
0x546BBD2B, 0xFD818BB8,
0x09FD3CD2, 0xA0170A41,
0x5A2951F4, 0xF3C36767,
0x1B3A09B3, 0xB2D03F20,
0x48EE6495, 0xE1045206,
0x1578E56C, 0xBC92D3FF,
0x46AC884A, 0xEF46BED9,
0xB69D5E3C, 0x1F7768AF,
0xE549331A, 0x4CA30589,
0xB8DFB2E3, 0x11358470,
0xEB0BDFC5, 0x42E1E956,
0xAA188782, 0x03F2B111,
0xF9CCEAA4, 0x5026DC37,
0xA45A6B5D, 0x0DB05DCE,
0xF78E067B, 0x5E6430E8,
0x267CDBD3, 0x8F96ED40,
0x75A8B6F5, 0xDC428066,
0x283E370C, 0x81D4019F,
0x7BEA5A2A, 0xD2006CB9,
0x3AF9026D, 0x931334FE,
0x692D6F4B, 0xC0C759D8,
0x34BBEEB2, 0x9D51D821,
0x676F8394, 0xCE85B507
};
const uint32 pg_crc64_table1[256] = {
0x00000000, 0x42F0E1EB,
0x85E1C3D7, 0xC711223C,
0x49336645, 0x0BC387AE,
0xCCD2A592, 0x8E224479,
0x9266CC8A, 0xD0962D61,
0x17870F5D, 0x5577EEB6,
0xDB55AACF, 0x99A54B24,
0x5EB46918, 0x1C4488F3,
0x663D78FF, 0x24CD9914,
0xE3DCBB28, 0xA12C5AC3,
0x2F0E1EBA, 0x6DFEFF51,
0xAAEFDD6D, 0xE81F3C86,
0xF45BB475, 0xB6AB559E,
0x71BA77A2, 0x334A9649,
0xBD68D230, 0xFF9833DB,
0x388911E7, 0x7A79F00C,
0xCC7AF1FF, 0x8E8A1014,
0x499B3228, 0x0B6BD3C3,
0x854997BA, 0xC7B97651,
0x00A8546D, 0x4258B586,
0x5E1C3D75, 0x1CECDC9E,
0xDBFDFEA2, 0x990D1F49,
0x172F5B30, 0x55DFBADB,
0x92CE98E7, 0xD03E790C,
0xAA478900, 0xE8B768EB,
0x2FA64AD7, 0x6D56AB3C,
0xE374EF45, 0xA1840EAE,
0x66952C92, 0x2465CD79,
0x3821458A, 0x7AD1A461,
0xBDC0865D, 0xFF3067B6,
0x711223CF, 0x33E2C224,
0xF4F3E018, 0xB60301F3,
0xDA050215, 0x98F5E3FE,
0x5FE4C1C2, 0x1D142029,
0x93366450, 0xD1C685BB,
0x16D7A787, 0x5427466C,
0x4863CE9F, 0x0A932F74,
0xCD820D48, 0x8F72ECA3,
0x0150A8DA, 0x43A04931,
0x84B16B0D, 0xC6418AE6,
0xBC387AEA, 0xFEC89B01,
0x39D9B93D, 0x7B2958D6,
0xF50B1CAF, 0xB7FBFD44,
0x70EADF78, 0x321A3E93,
0x2E5EB660, 0x6CAE578B,
0xABBF75B7, 0xE94F945C,
0x676DD025, 0x259D31CE,
0xE28C13F2, 0xA07CF219,
0x167FF3EA, 0x548F1201,
0x939E303D, 0xD16ED1D6,
0x5F4C95AF, 0x1DBC7444,
0xDAAD5678, 0x985DB793,
0x84193F60, 0xC6E9DE8B,
0x01F8FCB7, 0x43081D5C,
0xCD2A5925, 0x8FDAB8CE,
0x48CB9AF2, 0x0A3B7B19,
0x70428B15, 0x32B26AFE,
0xF5A348C2, 0xB753A929,
0x3971ED50, 0x7B810CBB,
0xBC902E87, 0xFE60CF6C,
0xE224479F, 0xA0D4A674,
0x67C58448, 0x253565A3,
0xAB1721DA, 0xE9E7C031,
0x2EF6E20D, 0x6C0603E6,
0xF6FAE5C0, 0xB40A042B,
0x731B2617, 0x31EBC7FC,
0xBFC98385, 0xFD39626E,
0x3A284052, 0x78D8A1B9,
0x649C294A, 0x266CC8A1,
0xE17DEA9D, 0xA38D0B76,
0x2DAF4F0F, 0x6F5FAEE4,
0xA84E8CD8, 0xEABE6D33,
0x90C79D3F, 0xD2377CD4,
0x15265EE8, 0x57D6BF03,
0xD9F4FB7A, 0x9B041A91,
0x5C1538AD, 0x1EE5D946,
0x02A151B5, 0x4051B05E,
0x87409262, 0xC5B07389,
0x4B9237F0, 0x0962D61B,
0xCE73F427, 0x8C8315CC,
0x3A80143F, 0x7870F5D4,
0xBF61D7E8, 0xFD913603,
0x73B3727A, 0x31439391,
0xF652B1AD, 0xB4A25046,
0xA8E6D8B5, 0xEA16395E,
0x2D071B62, 0x6FF7FA89,
0xE1D5BEF0, 0xA3255F1B,
0x64347D27, 0x26C49CCC,
0x5CBD6CC0, 0x1E4D8D2B,
0xD95CAF17, 0x9BAC4EFC,
0x158E0A85, 0x577EEB6E,
0x906FC952, 0xD29F28B9,
0xCEDBA04A, 0x8C2B41A1,
0x4B3A639D, 0x09CA8276,
0x87E8C60F, 0xC51827E4,
0x020905D8, 0x40F9E433,
0x2CFFE7D5, 0x6E0F063E,
0xA91E2402, 0xEBEEC5E9,
0x65CC8190, 0x273C607B,
0xE02D4247, 0xA2DDA3AC,
0xBE992B5F, 0xFC69CAB4,
0x3B78E888, 0x79880963,
0xF7AA4D1A, 0xB55AACF1,
0x724B8ECD, 0x30BB6F26,
0x4AC29F2A, 0x08327EC1,
0xCF235CFD, 0x8DD3BD16,
0x03F1F96F, 0x41011884,
0x86103AB8, 0xC4E0DB53,
0xD8A453A0, 0x9A54B24B,
0x5D459077, 0x1FB5719C,
0x919735E5, 0xD367D40E,
0x1476F632, 0x568617D9,
0xE085162A, 0xA275F7C1,
0x6564D5FD, 0x27943416,
0xA9B6706F, 0xEB469184,
0x2C57B3B8, 0x6EA75253,
0x72E3DAA0, 0x30133B4B,
0xF7021977, 0xB5F2F89C,
0x3BD0BCE5, 0x79205D0E,
0xBE317F32, 0xFCC19ED9,
0x86B86ED5, 0xC4488F3E,
0x0359AD02, 0x41A94CE9,
0xCF8B0890, 0x8D7BE97B,
0x4A6ACB47, 0x089A2AAC,
0x14DEA25F, 0x562E43B4,
0x913F6188, 0xD3CF8063,
0x5DEDC41A, 0x1F1D25F1,
0xD80C07CD, 0x9AFCE626
};
#else /* int64 works */
const uint64 pg_crc64_table[256] = {
UINT64CONST(0x0000000000000000), UINT64CONST(0x42F0E1EBA9EA3693),
UINT64CONST(0x85E1C3D753D46D26), UINT64CONST(0xC711223CFA3E5BB5),
UINT64CONST(0x493366450E42ECDF), UINT64CONST(0x0BC387AEA7A8DA4C),
UINT64CONST(0xCCD2A5925D9681F9), UINT64CONST(0x8E224479F47CB76A),
UINT64CONST(0x9266CC8A1C85D9BE), UINT64CONST(0xD0962D61B56FEF2D),
UINT64CONST(0x17870F5D4F51B498), UINT64CONST(0x5577EEB6E6BB820B),
UINT64CONST(0xDB55AACF12C73561), UINT64CONST(0x99A54B24BB2D03F2),
UINT64CONST(0x5EB4691841135847), UINT64CONST(0x1C4488F3E8F96ED4),
UINT64CONST(0x663D78FF90E185EF), UINT64CONST(0x24CD9914390BB37C),
UINT64CONST(0xE3DCBB28C335E8C9), UINT64CONST(0xA12C5AC36ADFDE5A),
UINT64CONST(0x2F0E1EBA9EA36930), UINT64CONST(0x6DFEFF5137495FA3),
UINT64CONST(0xAAEFDD6DCD770416), UINT64CONST(0xE81F3C86649D3285),
UINT64CONST(0xF45BB4758C645C51), UINT64CONST(0xB6AB559E258E6AC2),
UINT64CONST(0x71BA77A2DFB03177), UINT64CONST(0x334A9649765A07E4),
UINT64CONST(0xBD68D2308226B08E), UINT64CONST(0xFF9833DB2BCC861D),
UINT64CONST(0x388911E7D1F2DDA8), UINT64CONST(0x7A79F00C7818EB3B),
UINT64CONST(0xCC7AF1FF21C30BDE), UINT64CONST(0x8E8A101488293D4D),
UINT64CONST(0x499B3228721766F8), UINT64CONST(0x0B6BD3C3DBFD506B),
UINT64CONST(0x854997BA2F81E701), UINT64CONST(0xC7B97651866BD192),
UINT64CONST(0x00A8546D7C558A27), UINT64CONST(0x4258B586D5BFBCB4),
UINT64CONST(0x5E1C3D753D46D260), UINT64CONST(0x1CECDC9E94ACE4F3),
UINT64CONST(0xDBFDFEA26E92BF46), UINT64CONST(0x990D1F49C77889D5),
UINT64CONST(0x172F5B3033043EBF), UINT64CONST(0x55DFBADB9AEE082C),
UINT64CONST(0x92CE98E760D05399), UINT64CONST(0xD03E790CC93A650A),
UINT64CONST(0xAA478900B1228E31), UINT64CONST(0xE8B768EB18C8B8A2),
UINT64CONST(0x2FA64AD7E2F6E317), UINT64CONST(0x6D56AB3C4B1CD584),
UINT64CONST(0xE374EF45BF6062EE), UINT64CONST(0xA1840EAE168A547D),
UINT64CONST(0x66952C92ECB40FC8), UINT64CONST(0x2465CD79455E395B),
UINT64CONST(0x3821458AADA7578F), UINT64CONST(0x7AD1A461044D611C),
UINT64CONST(0xBDC0865DFE733AA9), UINT64CONST(0xFF3067B657990C3A),
UINT64CONST(0x711223CFA3E5BB50), UINT64CONST(0x33E2C2240A0F8DC3),
UINT64CONST(0xF4F3E018F031D676), UINT64CONST(0xB60301F359DBE0E5),
UINT64CONST(0xDA050215EA6C212F), UINT64CONST(0x98F5E3FE438617BC),
UINT64CONST(0x5FE4C1C2B9B84C09), UINT64CONST(0x1D14202910527A9A),
UINT64CONST(0x93366450E42ECDF0), UINT64CONST(0xD1C685BB4DC4FB63),
UINT64CONST(0x16D7A787B7FAA0D6), UINT64CONST(0x5427466C1E109645),
UINT64CONST(0x4863CE9FF6E9F891), UINT64CONST(0x0A932F745F03CE02),
UINT64CONST(0xCD820D48A53D95B7), UINT64CONST(0x8F72ECA30CD7A324),
UINT64CONST(0x0150A8DAF8AB144E), UINT64CONST(0x43A04931514122DD),
UINT64CONST(0x84B16B0DAB7F7968), UINT64CONST(0xC6418AE602954FFB),
UINT64CONST(0xBC387AEA7A8DA4C0), UINT64CONST(0xFEC89B01D3679253),
UINT64CONST(0x39D9B93D2959C9E6), UINT64CONST(0x7B2958D680B3FF75),
UINT64CONST(0xF50B1CAF74CF481F), UINT64CONST(0xB7FBFD44DD257E8C),
UINT64CONST(0x70EADF78271B2539), UINT64CONST(0x321A3E938EF113AA),
UINT64CONST(0x2E5EB66066087D7E), UINT64CONST(0x6CAE578BCFE24BED),
UINT64CONST(0xABBF75B735DC1058), UINT64CONST(0xE94F945C9C3626CB),
UINT64CONST(0x676DD025684A91A1), UINT64CONST(0x259D31CEC1A0A732),
UINT64CONST(0xE28C13F23B9EFC87), UINT64CONST(0xA07CF2199274CA14),
UINT64CONST(0x167FF3EACBAF2AF1), UINT64CONST(0x548F120162451C62),
UINT64CONST(0x939E303D987B47D7), UINT64CONST(0xD16ED1D631917144),
UINT64CONST(0x5F4C95AFC5EDC62E), UINT64CONST(0x1DBC74446C07F0BD),
UINT64CONST(0xDAAD56789639AB08), UINT64CONST(0x985DB7933FD39D9B),
UINT64CONST(0x84193F60D72AF34F), UINT64CONST(0xC6E9DE8B7EC0C5DC),
UINT64CONST(0x01F8FCB784FE9E69), UINT64CONST(0x43081D5C2D14A8FA),
UINT64CONST(0xCD2A5925D9681F90), UINT64CONST(0x8FDAB8CE70822903),
UINT64CONST(0x48CB9AF28ABC72B6), UINT64CONST(0x0A3B7B1923564425),
UINT64CONST(0x70428B155B4EAF1E), UINT64CONST(0x32B26AFEF2A4998D),
UINT64CONST(0xF5A348C2089AC238), UINT64CONST(0xB753A929A170F4AB),
UINT64CONST(0x3971ED50550C43C1), UINT64CONST(0x7B810CBBFCE67552),
UINT64CONST(0xBC902E8706D82EE7), UINT64CONST(0xFE60CF6CAF321874),
UINT64CONST(0xE224479F47CB76A0), UINT64CONST(0xA0D4A674EE214033),
UINT64CONST(0x67C58448141F1B86), UINT64CONST(0x253565A3BDF52D15),
UINT64CONST(0xAB1721DA49899A7F), UINT64CONST(0xE9E7C031E063ACEC),
UINT64CONST(0x2EF6E20D1A5DF759), UINT64CONST(0x6C0603E6B3B7C1CA),
UINT64CONST(0xF6FAE5C07D3274CD), UINT64CONST(0xB40A042BD4D8425E),
UINT64CONST(0x731B26172EE619EB), UINT64CONST(0x31EBC7FC870C2F78),
UINT64CONST(0xBFC9838573709812), UINT64CONST(0xFD39626EDA9AAE81),
UINT64CONST(0x3A28405220A4F534), UINT64CONST(0x78D8A1B9894EC3A7),
UINT64CONST(0x649C294A61B7AD73), UINT64CONST(0x266CC8A1C85D9BE0),
UINT64CONST(0xE17DEA9D3263C055), UINT64CONST(0xA38D0B769B89F6C6),
UINT64CONST(0x2DAF4F0F6FF541AC), UINT64CONST(0x6F5FAEE4C61F773F),
UINT64CONST(0xA84E8CD83C212C8A), UINT64CONST(0xEABE6D3395CB1A19),
UINT64CONST(0x90C79D3FEDD3F122), UINT64CONST(0xD2377CD44439C7B1),
UINT64CONST(0x15265EE8BE079C04), UINT64CONST(0x57D6BF0317EDAA97),
UINT64CONST(0xD9F4FB7AE3911DFD), UINT64CONST(0x9B041A914A7B2B6E),
UINT64CONST(0x5C1538ADB04570DB), UINT64CONST(0x1EE5D94619AF4648),
UINT64CONST(0x02A151B5F156289C), UINT64CONST(0x4051B05E58BC1E0F),
UINT64CONST(0x87409262A28245BA), UINT64CONST(0xC5B073890B687329),
UINT64CONST(0x4B9237F0FF14C443), UINT64CONST(0x0962D61B56FEF2D0),
UINT64CONST(0xCE73F427ACC0A965), UINT64CONST(0x8C8315CC052A9FF6),
UINT64CONST(0x3A80143F5CF17F13), UINT64CONST(0x7870F5D4F51B4980),
UINT64CONST(0xBF61D7E80F251235), UINT64CONST(0xFD913603A6CF24A6),
UINT64CONST(0x73B3727A52B393CC), UINT64CONST(0x31439391FB59A55F),
UINT64CONST(0xF652B1AD0167FEEA), UINT64CONST(0xB4A25046A88DC879),
UINT64CONST(0xA8E6D8B54074A6AD), UINT64CONST(0xEA16395EE99E903E),
UINT64CONST(0x2D071B6213A0CB8B), UINT64CONST(0x6FF7FA89BA4AFD18),
UINT64CONST(0xE1D5BEF04E364A72), UINT64CONST(0xA3255F1BE7DC7CE1),
UINT64CONST(0x64347D271DE22754), UINT64CONST(0x26C49CCCB40811C7),
UINT64CONST(0x5CBD6CC0CC10FAFC), UINT64CONST(0x1E4D8D2B65FACC6F),
UINT64CONST(0xD95CAF179FC497DA), UINT64CONST(0x9BAC4EFC362EA149),
UINT64CONST(0x158E0A85C2521623), UINT64CONST(0x577EEB6E6BB820B0),
UINT64CONST(0x906FC95291867B05), UINT64CONST(0xD29F28B9386C4D96),
UINT64CONST(0xCEDBA04AD0952342), UINT64CONST(0x8C2B41A1797F15D1),
UINT64CONST(0x4B3A639D83414E64), UINT64CONST(0x09CA82762AAB78F7),
UINT64CONST(0x87E8C60FDED7CF9D), UINT64CONST(0xC51827E4773DF90E),
UINT64CONST(0x020905D88D03A2BB), UINT64CONST(0x40F9E43324E99428),
UINT64CONST(0x2CFFE7D5975E55E2), UINT64CONST(0x6E0F063E3EB46371),
UINT64CONST(0xA91E2402C48A38C4), UINT64CONST(0xEBEEC5E96D600E57),
UINT64CONST(0x65CC8190991CB93D), UINT64CONST(0x273C607B30F68FAE),
UINT64CONST(0xE02D4247CAC8D41B), UINT64CONST(0xA2DDA3AC6322E288),
UINT64CONST(0xBE992B5F8BDB8C5C), UINT64CONST(0xFC69CAB42231BACF),
UINT64CONST(0x3B78E888D80FE17A), UINT64CONST(0x7988096371E5D7E9),
UINT64CONST(0xF7AA4D1A85996083), UINT64CONST(0xB55AACF12C735610),
UINT64CONST(0x724B8ECDD64D0DA5), UINT64CONST(0x30BB6F267FA73B36),
UINT64CONST(0x4AC29F2A07BFD00D), UINT64CONST(0x08327EC1AE55E69E),
UINT64CONST(0xCF235CFD546BBD2B), UINT64CONST(0x8DD3BD16FD818BB8),
UINT64CONST(0x03F1F96F09FD3CD2), UINT64CONST(0x41011884A0170A41),
UINT64CONST(0x86103AB85A2951F4), UINT64CONST(0xC4E0DB53F3C36767),
UINT64CONST(0xD8A453A01B3A09B3), UINT64CONST(0x9A54B24BB2D03F20),
UINT64CONST(0x5D45907748EE6495), UINT64CONST(0x1FB5719CE1045206),
UINT64CONST(0x919735E51578E56C), UINT64CONST(0xD367D40EBC92D3FF),
UINT64CONST(0x1476F63246AC884A), UINT64CONST(0x568617D9EF46BED9),
UINT64CONST(0xE085162AB69D5E3C), UINT64CONST(0xA275F7C11F7768AF),
UINT64CONST(0x6564D5FDE549331A), UINT64CONST(0x279434164CA30589),
UINT64CONST(0xA9B6706FB8DFB2E3), UINT64CONST(0xEB46918411358470),
UINT64CONST(0x2C57B3B8EB0BDFC5), UINT64CONST(0x6EA7525342E1E956),
UINT64CONST(0x72E3DAA0AA188782), UINT64CONST(0x30133B4B03F2B111),
UINT64CONST(0xF7021977F9CCEAA4), UINT64CONST(0xB5F2F89C5026DC37),
UINT64CONST(0x3BD0BCE5A45A6B5D), UINT64CONST(0x79205D0E0DB05DCE),
UINT64CONST(0xBE317F32F78E067B), UINT64CONST(0xFCC19ED95E6430E8),
UINT64CONST(0x86B86ED5267CDBD3), UINT64CONST(0xC4488F3E8F96ED40),
UINT64CONST(0x0359AD0275A8B6F5), UINT64CONST(0x41A94CE9DC428066),
UINT64CONST(0xCF8B0890283E370C), UINT64CONST(0x8D7BE97B81D4019F),
UINT64CONST(0x4A6ACB477BEA5A2A), UINT64CONST(0x089A2AACD2006CB9),
UINT64CONST(0x14DEA25F3AF9026D), UINT64CONST(0x562E43B4931334FE),
UINT64CONST(0x913F6188692D6F4B), UINT64CONST(0xD3CF8063C0C759D8),
UINT64CONST(0x5DEDC41A34BBEEB2), UINT64CONST(0x1F1D25F19D51D821),
UINT64CONST(0xD80C07CD676F8394), UINT64CONST(0x9AFCE626CE85B507)
};
#endif /* INT64_IS_BUSTED */
#endif /* PROVIDE_64BIT_CRC */

105
pgsql_src/pg_ctl.c Normal file
View File

@ -0,0 +1,105 @@
/*-------------------------------------------------------------------------
*
* pg_ctl --- start/stops/restarts the PostgreSQL server
*
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/bin/pg_ctl/pg_ctl.c,v 1.111 2009/06/11 14:49:07 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres_fe.h"
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include "pg_rman.h"
/* PID can be negative for standalone backend */
typedef long pgpid_t;
static pgpid_t get_pgpid(void);
static bool postmaster_is_alive(pid_t pid);
static char pid_file[MAXPGPATH];
static pgpid_t
get_pgpid(void)
{
FILE *pidf;
long pid;
snprintf(pid_file, lengthof(pid_file), "%s/postmaster.pid", pgdata);
pidf = fopen(pid_file, "r");
if (pidf == NULL)
{
/* No pid file, not an error on startup */
if (errno == ENOENT)
return 0;
else
elog(ERROR_SYSTEM, _("could not open PID file \"%s\": %s\n"),
pid_file, strerror(errno));
}
if (fscanf(pidf, "%ld", &pid) != 1)
elog(ERROR_PID_BROKEN, _("invalid data in PID file \"%s\"\n"), pid_file);
fclose(pidf);
return (pgpid_t) pid;
}
/*
* utility routines
*/
static bool
postmaster_is_alive(pid_t pid)
{
/*
* Test to see if the process is still there. Note that we do not
* consider an EPERM failure to mean that the process is still there;
* EPERM must mean that the given PID belongs to some other userid, and
* considering the permissions on $PGDATA, that means it's not the
* postmaster we are after.
*
* Don't believe that our own PID or parent shell's PID is the postmaster,
* either. (Windows hasn't got getppid(), though.)
*/
if (pid == getpid())
return false;
#ifndef WIN32
if (pid == getppid())
return false;
#endif
if (kill(pid, 0) == 0)
return true;
return false;
}
/*
* original is do_status() in src/bin/pg_ctl/pg_ctl.c
* changes are:
* renamed from do_status() from do_status().
* return true if PG server is running.
* don't print any message.
* don't print postopts file.
* log with elog() in pgut library.
*/
bool
is_pg_running(void)
{
pgpid_t pid;
pid = get_pgpid();
if (pid == 0) /* 0 means no pid file */
return false;
if (pid < 0) /* standalone backend */
pid = -pid;
return postmaster_is_alive((pid_t) pid);
}

240
pgut/pgut-port.c Normal file
View File

@ -0,0 +1,240 @@
/*-------------------------------------------------------------------------
*
* pgut-port.c
*
* Copyright (c) 2009, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "c.h"
#include "pgut-port.h"
#ifdef WIN32
#include <winioctl.h>
int
uname(struct utsname *buf)
{
OSVERSIONINFO os = { sizeof(OSVERSIONINFO) };
SYSTEM_INFO sys = { { sizeof(SYSTEM_INFO) } };
DWORD bufsize;
GetVersionEx(&os);
GetSystemInfo(&sys);
/* sysname */
strcpy(buf->sysname, "Windows");
/* nodename */
bufsize = lengthof(buf->nodename);
GetComputerName(buf->nodename, &bufsize);
/* release: major.minor */
snprintf(buf->release, lengthof(buf->release), "%ld.%ld",
os.dwMajorVersion, os.dwMinorVersion);
/* version */
strcpy(buf->sysname, os.szCSDVersion);
/* machine */
switch (sys.wProcessorArchitecture)
{
case PROCESSOR_ARCHITECTURE_INTEL:
strcpy(buf->machine, "x86");
break;
case PROCESSOR_ARCHITECTURE_IA64:
strcpy(buf->machine, "IA64");
break;
case PROCESSOR_ARCHITECTURE_AMD64:
strcpy(buf->machine, "x86_64");
break;
case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64:
strcpy(buf->machine, "x86_on_win64");
break;
default:
strcpy(buf->machine, "unknown");
break;
}
return 0;
}
#define NTFS_BLOCK_SIZE 512
int
statfs(const char *path, struct statfs *buf)
{
ULARGE_INTEGER availBytes;
ULARGE_INTEGER totalBytes;
ULARGE_INTEGER freeBytes;
if (!GetDiskFreeSpaceEx(path, &availBytes, &totalBytes, &freeBytes))
{
_dosmaperr(GetLastError());
return -1;
}
memset(buf, 0, sizeof(struct statfs));
buf->f_type = NTFS_SB_MAGIC;
buf->f_bsize = NTFS_BLOCK_SIZE;
buf->f_blocks = (long) (totalBytes.QuadPart / NTFS_BLOCK_SIZE);
buf->f_bfree = (long) (freeBytes.QuadPart / NTFS_BLOCK_SIZE);
buf->f_bavail = (long) (availBytes.QuadPart / NTFS_BLOCK_SIZE);
buf->f_namelen = MAX_PATH;
return 0;
}
#define REPARSE_DATA_SIZE 1024
/* same layout as REPARSE_DATA_BUFFER, which is defined only in old winnt.h */
typedef struct REPARSE_DATA
{
ULONG ReparseTag;
WORD ReparseDataLength;
WORD Reserved;
union
{
struct
{
WORD SubstituteNameOffset;
WORD SubstituteNameLength;
WORD PrintNameOffset;
WORD PrintNameLength;
ULONG Flags;
WCHAR PathBuffer[1];
} Symlink;
struct
{
WORD SubstituteNameOffset;
WORD SubstituteNameLength;
WORD PrintNameOffset;
WORD PrintNameLength;
WCHAR PathBuffer[1];
} Mount;
struct
{
BYTE DataBuffer[REPARSE_DATA_SIZE];
} Generic;
};
} REPARSE_DATA;
ssize_t
readlink(const char *path, char *target, size_t size)
{
HANDLE handle;
DWORD attr;
REPARSE_DATA data;
DWORD datasize;
PCWSTR wpath;
int wlen;
int r;
attr = GetFileAttributes(path);
if (attr == INVALID_FILE_ATTRIBUTES)
{
_dosmaperr(GetLastError());
return -1;
}
if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
{
errno = EINVAL; /* not a symlink */
return -1;
}
handle = CreateFileA(path, 0,
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
if (handle == INVALID_HANDLE_VALUE)
{
_dosmaperr(GetLastError());
return -1;
}
wpath = NULL;
if (DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0,
&data, sizeof(data), &datasize, NULL))
{
switch (data.ReparseTag)
{
case IO_REPARSE_TAG_MOUNT_POINT:
{
wpath = data.Mount.PathBuffer + data.Mount.SubstituteNameOffset;
wlen = data.Mount.SubstituteNameLength;
break;
}
case IO_REPARSE_TAG_SYMLINK:
{
wpath = data.Symlink.PathBuffer + data.Symlink.SubstituteNameOffset;
wlen = data.Symlink.SubstituteNameLength;
break;
}
}
}
if (wpath == NULL)
r = -1;
else
{
if (wcsncmp(wpath, L"\\??\\", 4) == 0 ||
wcsncmp(wpath, L"\\\\?\\", 4) == 0)
{
wpath += 4;
wlen -= 4;
}
r = WideCharToMultiByte(CP_ACP, 0, wpath, wlen, target, size, NULL, NULL);
}
CloseHandle(handle);
return r;
}
char *
blkid_devno_to_devname(dev_t devno)
{
static char devname[4];
char letter = 'A' + devno;
if ('A' <= letter && letter <= 'Z')
{
snprintf(devname, lengthof(devname), "%c:\\", 'A' + devno);
return devname;
}
else
return NULL;
}
int
flock(int fd, int operation)
{
BOOL ret;
HANDLE handle = (HANDLE) _get_osfhandle(fd);
DWORD lo = 0;
DWORD hi = 1;
if (operation & LOCK_UN)
{
ret = UnlockFileEx(handle, 0, lo, hi, NULL);
}
else
{
DWORD flags = 0;
if (operation & LOCK_EX)
flags |= LOCKFILE_EXCLUSIVE_LOCK;
if (operation & LOCK_NB)
flags |= LOCKFILE_FAIL_IMMEDIATELY;
ret = LockFileEx(handle, flags, 0, lo, hi, NULL);
}
if (!ret)
{
_dosmaperr(GetLastError());
return -1;
}
return 0;
}
#endif

71
pgut/pgut-port.h Normal file
View File

@ -0,0 +1,71 @@
/*-------------------------------------------------------------------------
*
* pgut-port.h
*
* Copyright (c) 2009, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#ifndef PGUT_PORT_H
#define PGUT_PORT_H
#include <sys/stat.h>
#ifndef WIN32
#include <blkid/blkid.h>
#include <sys/utsname.h>
#include <sys/statfs.h>
#include <unistd.h>
#else
#include <time.h>
struct utsname
{
char sysname[32];
char nodename[256];
char release[128];
char version[128];
char machine[32];
};
#define NTFS_SB_MAGIC 0x5346544e
typedef struct { int val[2]; } fsid_t;
struct statfs
{
long f_type;
long f_bsize;
long f_blocks;
long f_bfree;
long f_bavail;
long f_files;
long f_ffree;
fsid_t f_fsid;
long f_namelen;
};
extern int uname(struct utsname *buf);
extern int statfs(const char *path, struct statfs *buf);
extern ssize_t readlink(const char *path, char *target, size_t size);
extern char *blkid_devno_to_devname(dev_t devno);
#define LOCK_SH 1 /* Shared lock. */
#define LOCK_EX 2 /* Exclusive lock. */
#define LOCK_UN 8 /* Unlock. */
#define LOCK_NB 4 /* Don't block when locking. */
extern int flock(int fd, int operation);
#define S_IFLNK (0)
#define S_IRWXG (0)
#define S_IRWXO (0)
#define S_ISLNK(mode) (0)
#endif
#endif /* PGUT_PORT_H */

1686
pgut/pgut.c Normal file

File diff suppressed because it is too large Load Diff

245
pgut/pgut.h Normal file
View File

@ -0,0 +1,245 @@
/*-------------------------------------------------------------------------
*
* pgut.h
*
* Copyright (c) 2009, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#ifndef PGUT_H
#define PGUT_H
#include "libpq-fe.h"
#include "pqexpbuffer.h"
#include <assert.h>
#include <sys/time.h>
#if !defined(C_H) && !defined(__cplusplus)
#ifndef bool
typedef char bool;
#endif
#ifndef true
#define true ((bool) 1)
#endif
#ifndef false
#define false ((bool) 0)
#endif
#endif
#define INFINITE_STR "INFINITE"
typedef enum YesNo
{
DEFAULT,
NO,
YES
} YesNo;
typedef enum pgut_optsrc
{
SOURCE_DEFAULT,
SOURCE_ENV,
SOURCE_FILE,
SOURCE_CMDLINE,
SOURCE_CONST
} pgut_optsrc;
/*
* type:
* b: bool (true)
* B: bool (false)
* f: pgut_optfn
* i: 32bit singed integer
* u: 32bit unsigned integer
* I: 64bit singed integer
* U: 64bit unsigned integer
* s: string
* t: time_t
* y: YesNo (YES)
* Y: YesNo (NO)
*/
typedef struct pgut_option
{
char type;
char sname; /* short name */
const char *lname; /* long name */
void *var; /* pointer to variable */
pgut_optsrc allowed; /* allowed source */
pgut_optsrc source; /* actual source */
} pgut_option;
typedef void (*pgut_optfn) (pgut_option *opt, const char *arg);
typedef void (*pgut_atexit_callback)(bool fatal, void *userdata);
/*
* pgut client variables and functions
*/
extern const char *PROGRAM_NAME;
extern const char *PROGRAM_VERSION;
extern const char *PROGRAM_URL;
extern const char *PROGRAM_EMAIL;
extern void pgut_help(bool details);
/*
* pgut framework variables and functions
*/
extern const char *dbname;
extern const char *host;
extern const char *port;
extern const char *username;
extern char *password;
extern bool debug;
extern bool quiet;
#ifndef PGUT_NO_PROMPT
extern YesNo prompt_password;
#endif
extern PGconn *connection;
extern bool interrupted;
extern void help(bool details);
extern int pgut_getopt(int argc, char **argv, pgut_option options[]);
extern void pgut_readopt(const char *path, pgut_option options[], int elevel);
extern void pgut_atexit_push(pgut_atexit_callback callback, void *userdata);
extern void pgut_atexit_pop(pgut_atexit_callback callback, void *userdata);
/*
* Database connections
*/
extern PGconn *pgut_connect(int elevel);
extern void pgut_disconnect(PGconn *conn);
extern PGresult *pgut_execute(PGconn* conn, const char *query, int nParams, const char **params, int elevel);
extern void pgut_command(PGconn* conn, const char *query, int nParams, const char **params, int elevel);
extern bool pgut_send(PGconn* conn, const char *query, int nParams, const char **params, int elevel);
extern int pgut_wait(int num, PGconn *connections[], struct timeval *timeout);
extern PGconn *reconnect_elevel(int elevel);
extern void reconnect(void);
extern void disconnect(void);
extern PGresult *execute_elevel(const char *query, int nParams, const char **params, int elevel);
extern PGresult *execute(const char *query, int nParams, const char **params);
extern void command(const char *query, int nParams, const char **params);
/*
* memory allocators
*/
extern void *pgut_malloc(size_t size);
extern void *pgut_realloc(void *p, size_t size);
extern char *pgut_strdup(const char *str);
extern char *strdup_with_len(const char *str, size_t len);
extern char *strdup_trim(const char *str);
/*
* elog
*/
#define LOG (-4)
#define INFO (-3)
#define NOTICE (-2)
#define WARNING (-1)
#define HELP 1
#define ERROR 2
#define FATAL 3
#define PANIC 4
#define ERROR_SYSTEM 10 /* I/O or system error */
#define ERROR_NOMEM 11 /* memory exhausted */
#define ERROR_ARGS 12 /* some configurations are invalid */
#define ERROR_INTERRUPTED 13 /* interrupted by signal */
#define ERROR_PG_COMMAND 14 /* PostgreSQL query or command error */
#define ERROR_PG_CONNECT 15 /* PostgreSQL connection error */
#undef elog
extern void
elog(int elevel, const char *fmt, ...)
__attribute__((format(printf, 2, 3)));
/*
* Assert
*/
#undef Assert
#undef AssertArg
#undef AssertMacro
#ifdef USE_ASSERT_CHECKING
#define Assert(x) assert(x)
#define AssertArg(x) assert(x)
#define AssertMacro(x) assert(x)
#else
#define Assert(x) ((void) 0)
#define AssertArg(x) ((void) 0)
#define AssertMacro(x) ((void) 0)
#endif
/*
* StringInfo and string operations
*/
#define STRINGINFO_H
#define StringInfoData PQExpBufferData
#define StringInfo PQExpBuffer
#define makeStringInfo createPQExpBuffer
#define initStringInfo initPQExpBuffer
#define freeStringInfo destroyPQExpBuffer
#define termStringInfo termPQExpBuffer
#define resetStringInfo resetPQExpBuffer
#define enlargeStringInfo enlargePQExpBuffer
#define printfStringInfo printfPQExpBuffer /* reset + append */
#define appendStringInfo appendPQExpBuffer
#define appendStringInfoString appendPQExpBufferStr
#define appendStringInfoChar appendPQExpBufferChar
#define appendBinaryStringInfo appendBinaryPQExpBuffer
extern int appendStringInfoFile(StringInfo str, FILE *fp);
extern int appendStringInfoFd(StringInfo str, int fd);
extern bool parse_bool(const char *value, bool *result);
extern bool parse_bool_with_len(const char *value, size_t len, bool *result);
extern bool parse_int32(const char *value, int32 *result);
extern bool parse_uint32(const char *value, uint32 *result);
extern bool parse_int64(const char *value, int64 *result);
extern bool parse_uint64(const char *value, uint64 *result);
extern bool parse_time(const char *value, time_t *time);
#define IsSpace(c) (isspace((unsigned char)(c)))
#define IsAlpha(c) (isalpha((unsigned char)(c)))
#define IsAlnum(c) (isalnum((unsigned char)(c)))
#define IsIdentHead(c) (IsAlpha(c) || (c) == '_')
#define IsIdentBody(c) (IsAlnum(c) || (c) == '_')
#define ToLower(c) (tolower((unsigned char)(c)))
#define ToUpper(c) (toupper((unsigned char)(c)))
/*
* socket operations
*/
extern int wait_for_socket(int sock, struct timeval *timeout);
extern int wait_for_sockets(int nfds, fd_set *fds, struct timeval *timeout);
/*
* import from postgres.h and catalog/genbki.h in 8.4
*/
#if PG_VERSION_NUM < 80400
typedef unsigned long Datum;
typedef struct MemoryContextData *MemoryContext;
#define CATALOG(name,oid) typedef struct CppConcat(FormData_,name)
#define BKI_BOOTSTRAP
#define BKI_SHARED_RELATION
#define BKI_WITHOUT_OIDS
#define DATA(x) extern int no_such_variable
#define DESCR(x) extern int no_such_variable
#define SHDESCR(x) extern int no_such_variable
typedef int aclitem;
#endif
#ifdef WIN32
extern int sleep(unsigned int seconds);
extern int usleep(unsigned int usec);
#endif
#endif /* PGUT_H */

978
restore.c Normal file
View File

@ -0,0 +1,978 @@
/*-------------------------------------------------------------------------
*
* restore.c: restore DB cluster and archived WAL.
*
* Copyright (c) 2009, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include "catalog/pg_control.h"
#if PG_VERSION_NUM < 80200
#define XLOG_BLCKSZ BLCKSZ
#endif
static void backup_online_files(bool re_recovery);
static void restore_online_files(void);
static void restore_database(pgBackup *backup);
static void restore_archive_logs(pgBackup *backup);
static void create_recovery_conf(const char *target_time,
const char *target_xid,
const char *target_inclusive,
TimeLineID target_tli);
static parray * readTimeLineHistory(TimeLineID targetTLI);
static bool satisfy_timeline(const parray *timelines, const pgBackup *backup);
static TimeLineID get_current_timeline(void);
static TimeLineID get_fullbackup_timeline(parray *backups);
static void print_backup_id(const pgBackup *backup);
static void search_next_wal(const char *path, uint32 *needId, uint32 *needSeg, parray *timelines);
int
do_restore(const char *target_time,
const char *target_xid,
const char *target_inclusive,
TimeLineID target_tli)
{
int i;
int base_index; /* index of base (full) backup */
int last_restored_index; /* index of last restored database backup */
int ret;
TimeLineID cur_tli;
TimeLineID backup_tli;
parray *backups;
pgBackup *base_backup = NULL;
parray *files;
parray *timelines;
char timeline_dir[MAXPGPATH];
uint32 needId = 0;
uint32 needSeg = 0;
/* PGDATA and ARCLOG_PATH are always required */
if (pgdata == NULL)
elog(ERROR_ARGS,
_("required parameter not specified: PGDATA (-D, --pgdata)"));
if (arclog_path == NULL)
elog(ERROR_ARGS,
_("required parameter not specified: ARCLOG_PATH (-A, --arclog-path)"));
if (srvlog_path == NULL)
elog(ERROR_ARGS,
_("required parameter not specified: SRVLOG_PATH (-S, --srvlog-path)"));
if (verbose)
{
printf(_("========================================\n"));
printf(_("restore start\n"));
}
/* get exclusive lock of backup catalog */
ret = catalog_lock();
if (ret == -1)
elog(ERROR_SYSTEM, _("can't lock backup catalog."));
else if (ret == 1)
elog(ERROR_ALREADY_RUNNING,
_("another pg_rman is running, stop restore."));
/* confirm the PostgreSQL server is not running */
if (is_pg_running())
elog(ERROR_PG_RUNNING, _("PostgreSQL server is running"));
/* get list of backups. (index == 0) is the last backup */
backups = catalog_get_backup_list(NULL);
cur_tli = get_current_timeline();
backup_tli = get_fullbackup_timeline(backups);
/* determine target timeline */
if (target_tli == 0)
target_tli = cur_tli != 0 ? cur_tli : backup_tli;
if (verbose)
{
printf(_("current timeline ID = %u\n"), cur_tli);
printf(_("latest full backup timeline ID = %u\n"), backup_tli);
printf(_("target timeline ID = %u\n"), target_tli);
}
/* backup online WAL and serverlog */
backup_online_files(cur_tli != 0 && cur_tli != backup_tli);
/*
* Clear restore destination, but don't remove $PGDATA.
* To remove symbolic link, get file list with "omit_symlink = false".
*/
if (!check)
{
if (verbose)
{
printf(_("----------------------------------------\n"));
printf(_("clearing restore destination\n"));
}
files = parray_new();
dir_list_file(files, pgdata, NULL, false, false);
parray_qsort(files, pgFileComparePathDesc); /* delete from leaf */
for (i = 0; i < parray_num(files); i++)
{
pgFile *file = (pgFile *) parray_get(files, i);
pgFileDelete(file);
}
parray_walk(files, pgFileFree);
parray_free(files);
}
/*
* restore timeline history files and get timeline branches can reach
* recovery target point.
*/
snprintf(timeline_dir, lengthof(timeline_dir), "%s/%s", backup_path,
TIMELINE_HISTORY_DIR);
if (verbose && !check)
printf(_("restoring timeline history files\n"));
dir_copy_files(timeline_dir, arclog_path);
timelines = readTimeLineHistory(target_tli);
/* find last full backup which can be used as base backup. */
if (verbose)
printf(_("searching recent full backup\n"));
for (i = 0; i < parray_num(backups); i++)
{
base_backup = (pgBackup *) parray_get(backups, i);
if (base_backup->backup_mode < BACKUP_MODE_FULL ||
base_backup->status != BACKUP_STATUS_OK)
continue;
#ifndef HAVE_LIBZ
/* Make sure we won't need decompression we haven't got */
if (base_backup->compress_data &&
(HAVE_DATABASE(base_backup) || HAVE_ARCLOG(base_backup)))
{
elog(EXIT_NOT_SUPPORTED,
_("can't restore from compressed backup (compression not supported in this installation)"));
}
#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."));
base_backup_found:
base_index = i;
if (verbose)
print_backup_id(base_backup);
/* restore base backup */
restore_database(base_backup);
last_restored_index = base_index;
/* restore following incremental backup */
if (verbose)
printf(_("searching incremental backup...\n"));
for (i = base_index - 1; i >= 0; i--)
{
pgBackup *backup = (pgBackup *) parray_get(backups, i);
/* don't use incomplete nor different timeline backup */
if (backup->status != BACKUP_STATUS_OK ||
backup->tli != base_backup->tli)
continue;
/* use database backup only */
if (backup->backup_mode < BACKUP_MODE_INCREMENTAL)
continue;
/* is the backup is necessary for restore to target timeline ? */
if (!satisfy_timeline(timelines, backup))
continue;
if (verbose)
print_backup_id(backup);
restore_database(backup);
last_restored_index = i;
}
/*
* Restore archived WAL which backed up with or after last restored backup.
* We don't check the backup->tli because a backup of arhived WAL
* can contain WALs which were archived in multiple timeline.
*/
if (verbose)
printf(_("searching backed-up WAL...\n"));
if (check)
{
pgBackup *backup = (pgBackup *) parray_get(backups, last_restored_index);
/* XLByteToSeg(xlrp, logId, logSeg) */
needId = backup->start_lsn.xlogid;
needSeg = backup->start_lsn.xrecoff / XLogSegSize;
}
for (i = last_restored_index; i >= 0; i--)
{
pgBackup *backup = (pgBackup *) parray_get(backups, i);
/* don't use incomplete backup */
if (backup->status != BACKUP_STATUS_OK)
continue;
if (!HAVE_ARCLOG(backup))
continue;
/* care timeline junction */
if (!satisfy_timeline(timelines, backup))
continue;
restore_archive_logs(backup);
if (check)
{
char xlogpath[MAXPGPATH];
pgBackupGetPath(backup, xlogpath, lengthof(xlogpath), ARCLOG_DIR);
search_next_wal(xlogpath, &needId, &needSeg, timelines);
}
}
/* copy online WAL backup to $PGDATA/pg_xlog */
restore_online_files();
if (check)
{
char xlogpath[MAXPGPATH];
if (verbose)
printf(_("searching archived WAL...\n"));
search_next_wal(arclog_path, &needId, &needSeg, timelines);
if (verbose)
printf(_("searching online WAL...\n"));
snprintf(xlogpath, lengthof(xlogpath), "%s/%s", pgdata, PG_XLOG_DIR);
search_next_wal(xlogpath, &needId, &needSeg, timelines);
if (verbose)
printf(_("all necessary files are found.\n"));
}
/* create recovery.conf */
create_recovery_conf(target_time, target_xid, target_inclusive, target_tli);
/* release catalog lock */
catalog_unlock();
/* cleanup */
parray_walk(backups, pgBackupFree);
parray_free(backups);
/* print restore complete message */
if (verbose && !check)
{
printf(_("all restore completed\n"));
printf(_("========================================\n"));
}
if (!check)
elog(INFO, _("restore complete. Recovery starts automatically when the PostgreSQL server is started."));
return 0;
}
/*
* Validate and restore backup.
*/
void
restore_database(pgBackup *backup)
{
char timestamp[100];
char path[MAXPGPATH];
char list_path[MAXPGPATH];
int ret;
parray *files;
int i;
/* confirm block size compatibility */
if (backup->block_size != BLCKSZ)
elog(ERROR_PG_INCOMPATIBLE,
_("BLCKSZ(%d) is not compatible(%d expected)"),
backup->block_size, BLCKSZ);
if (backup->wal_block_size != XLOG_BLCKSZ)
elog(ERROR_PG_INCOMPATIBLE,
_("XLOG_BLCKSZ(%d) is not compatible(%d expected)"),
backup->wal_block_size, XLOG_BLCKSZ);
time2iso(timestamp, lengthof(timestamp), backup->start_time);
if (verbose && !check)
{
printf(_("----------------------------------------\n"));
printf(_("restoring database from backup %s.\n"), timestamp);
}
/*
* Validate backup files with its size, because load of CRC calculation is
* not light.
*/
pgBackupValidate(backup, true);
/* make direcotries and symbolic links */
pgBackupGetPath(backup, path, lengthof(path), MKDIRS_SH_FILE);
if (!check)
{
char pwd[MAXPGPATH];
/* keep orginal directory */
if (getcwd(pwd, sizeof(pwd)) == NULL)
elog(ERROR_SYSTEM, _("can't get current working directoryh: %s"),
strerror(errno));
/* create pgdata directory */
dir_create_dir(pgdata, DIR_PERMISSION);
/* change directory to pgdata */
if (chdir(pgdata))
elog(ERROR_SYSTEM, _("can't change directoryh: %s"),
strerror(errno));
/* Execute mkdirs.sh */
ret = system(path);
if (ret != 0)
elog(ERROR_SYSTEM, _("can't execute mkdirs.sh: %s"),
strerror(errno));
/* go back to original directory */
if (chdir(pwd))
elog(ERROR_SYSTEM, _("can't change directoryh: %s"),
strerror(errno));
}
/*
* get list of files which need to be restored.
*/
pgBackupGetPath(backup, path, lengthof(path), DATABASE_DIR);
pgBackupGetPath(backup, list_path, lengthof(list_path), DATABASE_FILE_LIST);
files = dir_read_file_list(path, list_path);
for (i = parray_num(files) - 1; i >= 0; i--)
{
pgFile *file = (pgFile *) parray_get(files, i);
/* remove files which are not backed up */
if (file->write_size == BYTES_INVALID)
pgFileFree(parray_remove(files, i));
}
/* restore files into $PGDATA */
for (i = 0; i < parray_num(files); i++)
{
char from_root[MAXPGPATH];
pgFile *file = (pgFile *) parray_get(files, i);
pgBackupGetPath(backup, from_root, lengthof(from_root), DATABASE_DIR);
/* check for interrupt */
if (interrupted)
elog(ERROR_INTERRUPTED, _("interrupted during restore database"));
/* print progress */
if (verbose && !check)
printf(_("(%d/%lu) %s "), i + 1, (unsigned long) parray_num(files),
file->path + strlen(from_root) + 1);
/* directories are created with mkdirs.sh */
if (S_ISDIR(file->mode))
{
if (verbose && !check)
printf(_("directory, skip\n"));
continue;
}
/* not backed up */
if (file->write_size == BYTES_INVALID)
{
if (verbose && !check)
printf(_("not backed up, skip\n"));
continue;
}
/* restore file */
if (!check)
restore_data_file(from_root, pgdata, file, backup->compress_data);
/* print size of restored file */
if (verbose && !check)
printf(_("restored %lu\n"), (unsigned long) file->write_size);
}
/* Delete files which are not in file list. */
if (!check)
{
parray *files_now;
parray_walk(files, pgFileFree);
parray_free(files);
/* re-read file list to change base path to $PGDATA */
files = dir_read_file_list(pgdata, list_path);
parray_qsort(files, pgFileComparePathDesc);
/* get list of files restored to pgdata */
files_now = parray_new();
dir_list_file(files_now, pgdata, pgdata_exclude, true, false);
/* to delete from leaf, sort in reversed order */
parray_qsort(files_now, pgFileComparePathDesc);
for (i = 0; i < parray_num(files_now); i++)
{
pgFile *file = (pgFile *) parray_get(files_now, i);
/* If the file is not in the file list, delete it */
if (parray_bsearch(files, file, pgFileComparePathDesc) == NULL)
{
if (verbose)
printf(_(" delete %s\n"), file->path + strlen(pgdata) + 1);
pgFileDelete(file);
}
}
parray_walk(files_now, pgFileFree);
parray_free(files_now);
}
/* remove postmaster.pid */
snprintf(path, lengthof(path), "%s/postmaster.pid", pgdata);
if (remove(path) == -1 && errno != ENOENT)
elog(ERROR_SYSTEM, _("can't remove postmaster.pid: %s"),
strerror(errno));
/* cleanup */
parray_walk(files, pgFileFree);
parray_free(files);
if (verbose && !check)
printf(_("resotre backup completed\n"));
}
/*
* Restore archived WAL by creating symbolic link which linked to backup WAL in
* archive directory.
*/
void
restore_archive_logs(pgBackup *backup)
{
int i;
char timestamp[100];
parray *files;
char path[MAXPGPATH];
char list_path[MAXPGPATH];
char base_path[MAXPGPATH];
time2iso(timestamp, lengthof(timestamp), backup->start_time);
if (verbose && !check)
{
printf(_("----------------------------------------\n"));
printf(_("restoring WAL from backup %s.\n"), timestamp);
}
pgBackupGetPath(backup, list_path, lengthof(list_path), ARCLOG_FILE_LIST);
pgBackupGetPath(backup, base_path, lengthof(list_path), ARCLOG_DIR);
files = dir_read_file_list(base_path, list_path);
for (i = 0; i < parray_num(files); i++)
{
pgFile *file = (pgFile *) parray_get(files, i);
/* check for interrupt */
if (interrupted)
elog(ERROR_INTERRUPTED, _("interrupted during restore WAL"));
/* print progress */
snprintf(path, lengthof(path), "%s/%s", arclog_path,
file->path + strlen(base_path) + 1);
if (verbose && !check)
printf(_("(%d/%lu) %s "), i + 1, (unsigned long) parray_num(files),
file->path + strlen(base_path) + 1);
/* skip files which are not in backup */
if (file->write_size == BYTES_INVALID)
{
if (verbose && !check)
printf(_("skip(not backed up)\n"));
continue;
}
/*
* skip timeline history files because timeline history files will be
* restored from $BACKUP_PATH/timeline_history.
*/
if (strstr(file->path, ".history") ==
file->path + strlen(file->path) - strlen(".history"))
{
if (verbose && !check)
printf(_("skip(timeline history)\n"));
continue;
}
if (!check)
{
if (backup->compress_data)
{
copy_file(base_path, arclog_path, file, DECOMPRESSION);
if (verbose)
printf(_("decompressed\n"));
continue;
}
/* even same file exist, use backup file */
if ((remove(path) == -1) && errno != ENOENT)
elog(ERROR_SYSTEM, _("can't remove file \"%s\": %s"), path,
strerror(errno));
if ((symlink(file->path, path) == -1))
elog(ERROR_SYSTEM, _("can't create link to \"%s\": %s"),
file->path, strerror(errno));
if (verbose)
printf(_("linked\n"));
}
}
parray_walk(files, pgFileFree);
parray_free(files);
}
static void
create_recovery_conf(const char *target_time,
const char *target_xid,
const char *target_inclusive,
TimeLineID target_tli)
{
char path[MAXPGPATH];
FILE *fp;
if (verbose && !check)
{
printf(_("----------------------------------------\n"));
printf(_("creating recovery.conf\n"));
}
if (!check)
{
snprintf(path, lengthof(path), "%s/recovery.conf", pgdata);
fp = fopen(path, "wt");
if (fp == NULL)
elog(ERROR_SYSTEM, _("can't open recovery.conf \"%s\": %s"), path,
strerror(errno));
fprintf(fp, "# recovery.conf generated by pg_rman %s\n",
PROGRAM_VERSION);
fprintf(fp, "restore_command = 'cp %s/%%f %%p'\n", arclog_path);
if (target_time)
fprintf(fp, "recovery_target_time = '%s'\n", target_time);
if (target_xid)
fprintf(fp, "recovery_target_xid = '%s'\n", target_xid);
if (target_inclusive)
fprintf(fp, "recovery_target_inclusive = '%s'\n", target_inclusive);
fprintf(fp, "recovery_target_timeline = '%u'\n", target_tli);
fclose(fp);
}
}
static void
backup_online_files(bool re_recovery)
{
char work_path[MAXPGPATH];
char pg_xlog_path[MAXPGPATH];
bool files_exist;
parray *files;
if (verbose && !check)
{
printf(_("----------------------------------------\n"));
printf(_("backup online WAL and serverlog start\n"));
}
/* get list of files in $BACKUP_PATH/backup/pg_xlog */
files = parray_new();
snprintf(work_path, lengthof(work_path), "%s/%s/%s", backup_path,
RESTORE_WORK_DIR, PG_XLOG_DIR);
dir_list_file(files, work_path, NULL, true, false);
files_exist = parray_num(files) > 0;
parray_walk(files, pgFileFree);
parray_free(files);
/* If files exist in RESTORE_WORK_DIR and not re-recovery, use them. */
if (files_exist && !re_recovery)
{
if (verbose)
printf(_("online WALs have been already backed up, use them.\n"));
return;
}
/* backup online WAL */
snprintf(pg_xlog_path, lengthof(pg_xlog_path), "%s/pg_xlog", pgdata);
snprintf(work_path, lengthof(work_path), "%s/%s/%s", backup_path,
RESTORE_WORK_DIR, PG_XLOG_DIR);
dir_create_dir(work_path, DIR_PERMISSION);
dir_copy_files(pg_xlog_path, work_path);
/* backup serverlog */
snprintf(work_path, lengthof(work_path), "%s/%s/%s", backup_path,
RESTORE_WORK_DIR, SRVLOG_DIR);
dir_create_dir(work_path, DIR_PERMISSION);
dir_copy_files(srvlog_path, work_path);
}
static void
restore_online_files(void)
{
int i;
char root_backup[MAXPGPATH];
parray *files_backup;
/* get list of files in $BACKUP_PATH/backup/pg_xlog */
files_backup = parray_new();
snprintf(root_backup, lengthof(root_backup), "%s/%s/%s", backup_path,
RESTORE_WORK_DIR, PG_XLOG_DIR);
dir_list_file(files_backup, root_backup, NULL, true, false);
if (verbose && !check)
{
printf(_("----------------------------------------\n"));
printf(_("restoring online WAL\n"));
}
/* restore online WAL */
for (i = 0; i < parray_num(files_backup); i++)
{
pgFile *file = (pgFile *) parray_get(files_backup, i);
if (S_ISDIR(file->mode))
{
char to_path[MAXPGPATH];
snprintf(to_path, lengthof(to_path), "%s/%s/%s", pgdata,
PG_XLOG_DIR, file->path + strlen(root_backup) + 1);
if (verbose && !check)
printf(_("create directory \"%s\"\n"),
file->path + strlen(root_backup) + 1);
if (!check)
dir_create_dir(to_path, DIR_PERMISSION);
continue;
}
else if(S_ISREG(file->mode))
{
char to_root[MAXPGPATH];
snprintf(to_root, lengthof(to_root), "%s/%s", pgdata, PG_XLOG_DIR);
if (verbose && !check)
printf(_("restore \"%s\"\n"),
file->path + strlen(root_backup) + 1);
if (!check)
copy_file(root_backup, to_root, file, NO_COMPRESSION);
}
}
/* cleanup */
parray_walk(files_backup, pgFileFree);
parray_free(files_backup);
}
/*
* Try to read a timeline's history file.
*
* If successful, return the list of component pgTimeLine (the ancestor
* timelines followed by target timeline). If we can't find the history file,
* assume that the timeline has no parents, and return a list of just the
* specified timeline ID.
* based on readTimeLineHistory() in xlog.c
*/
static parray *
readTimeLineHistory(TimeLineID targetTLI)
{
parray *result;
char path[MAXPGPATH];
char fline[MAXPGPATH];
FILE *fd;
pgTimeLine *timeline;
pgTimeLine *last_timeline = NULL;
result = parray_new();
/* search from arclog_path first */
snprintf(path, lengthof(path), "%s/%08X.history", arclog_path,
targetTLI);
fd = fopen(path, "rt");
if (fd == NULL)
{
if (errno != ENOENT)
elog(ERROR_SYSTEM, _("could not open file \"%s\": %s"), path,
strerror(errno));
/* search from restore work directory next */
snprintf(path, lengthof(path), "%s/%s/%s/%08X.history", backup_path,
RESTORE_WORK_DIR, PG_XLOG_DIR, targetTLI);
fd = fopen(path, "rt");
if (fd == NULL)
{
if (errno != ENOENT)
elog(ERROR_SYSTEM, _("could not open file \"%s\": %s"), path,
strerror(errno));
}
}
/*
* Parse the file...
*/
while (fd && fgets(fline, sizeof(fline), fd) != NULL)
{
/* skip leading whitespace and check for # comment */
char *ptr;
char *endptr;
for (ptr = fline; *ptr; ptr++)
{
if (!IsSpace(*ptr))
break;
}
if (*ptr == '\0' || *ptr == '#')
continue;
timeline = pgut_malloc(sizeof(*timeline));
timeline->tli = 0;
timeline->end.xlogid = 0;
timeline->end.xrecoff = 0;
/* expect a numeric timeline ID as first field of line */
timeline->tli = (TimeLineID) strtoul(ptr, &endptr, 0);
if (endptr == ptr)
elog(ERROR_CORRUPTED,
_("syntax error(timeline ID) in history file: %s"),
fline);
if (last_timeline && timeline->tli <= last_timeline->tli)
elog(ERROR_CORRUPTED,
_("Timeline IDs must be in increasing sequence."));
/* Build list with newest item first */
parray_insert(result, 0, timeline);
last_timeline = timeline;
/* parse end point(logfname, xid) in the timeline */
for (ptr = endptr; *ptr; ptr++)
{
if (!IsSpace(*ptr))
break;
}
if (*ptr == '\0' || *ptr == '#')
elog(ERROR_CORRUPTED,
_("End logfile must follow Timeline ID."));
if (!xlog_logfname2lsn(ptr, &timeline->end))
elog(ERROR_CORRUPTED,
_("syntax error(endfname) in history file: %s"), fline);
/* we ignore the remainder of each line */
}
if (fd)
fclose(fd);
if (last_timeline && targetTLI <= last_timeline->tli)
elog(ERROR_CORRUPTED,
_("Timeline IDs must be less than child timeline's ID."));
/* append target timeline */
timeline = pgut_malloc(sizeof(*timeline));
timeline->tli = targetTLI;
timeline->end.xlogid = (uint32) -1; /* lsn in target timelie is valid */
timeline->end.xrecoff = (uint32) -1; /* lsn target timelie is valid */
parray_insert(result, 0, timeline);
/* dump timeline branches for debug */
if (debug)
{
int i;
for (i = 0; i < parray_num(result); i++)
{
pgTimeLine *timeline = parray_get(result, i);
elog(LOG, "%s() result[%d]: %08X/%08X/%08X", __FUNCTION__, i,
timeline->tli, timeline->end.xlogid, timeline->end.xrecoff);
}
}
return result;
}
static bool
satisfy_timeline(const parray *timelines, const pgBackup *backup)
{
int i;
for (i = 0; i < parray_num(timelines); i++)
{
pgTimeLine *timeline = (pgTimeLine *) parray_get(timelines, i);
if (backup->tli == timeline->tli &&
XLByteLT(backup->stop_lsn, timeline->end))
return true;
}
return false;
}
/* get TLI of the current database */
static TimeLineID
get_current_timeline(void)
{
ControlFileData ControlFile;
int fd;
char ControlFilePath[MAXPGPATH];
pg_crc32 crc;
TimeLineID ret;
snprintf(ControlFilePath, MAXPGPATH, "%s/global/pg_control", pgdata);
if ((fd = open(ControlFilePath, O_RDONLY | PG_BINARY, 0)) == -1)
{
elog(WARNING, _("can't open pg_controldata file \"%s\": %s"),
ControlFilePath, strerror(errno));
return 0;
}
if (read(fd, &ControlFile, sizeof(ControlFileData)) != sizeof(ControlFileData))
{
elog(WARNING, _("can't read pg_controldata file \"%s\": %s"),
ControlFilePath, strerror(errno));
return 0;
}
close(fd);
/* Check the CRC. */
INIT_CRC32(crc);
COMP_CRC32(crc,
(char *) &ControlFile,
offsetof(ControlFileData, crc));
FIN_CRC32(crc);
if (!EQ_CRC32(crc, ControlFile.crc))
{
elog(WARNING, _("Calculated CRC checksum does not match value stored in file.\n"
"Either the file is corrupt, or it has a different layout than this program\n"
"is expecting. The results below are untrustworthy.\n"));
return 0;
}
if (ControlFile.pg_control_version % 65536 == 0 && ControlFile.pg_control_version / 65536 != 0)
{
elog(WARNING, _("possible byte ordering mismatch\n"
"The byte ordering used to store the pg_control file might not match the one\n"
"used by this program. In that case the results below would be incorrect, and\n"
"the PostgreSQL installation would be incompatible with this data directory.\n"));
return 0;
}
ret = ControlFile.checkPointCopy.ThisTimeLineID;
return ret;
}
/* get TLI of the latest full backup */
static TimeLineID
get_fullbackup_timeline(parray *backups)
{
int i;
pgBackup *base_backup = NULL;
TimeLineID ret;
for (i = 0; i < parray_num(backups); i++)
{
base_backup = (pgBackup *) parray_get(backups, i);
if (base_backup->backup_mode >= BACKUP_MODE_FULL)
{
/*
* Validate backup files with its size, because load of CRC
* calculation is not light.
*/
if (base_backup->status == BACKUP_STATUS_DONE)
pgBackupValidate(base_backup, true);
if (base_backup->status == BACKUP_STATUS_OK)
break;
}
}
/* no full backup found, can't restore */
if (i == parray_num(backups))
elog(ERROR_NO_BACKUP, _("no full backup found, can't restore."));
ret = base_backup->tli;
return ret;
}
static void
print_backup_id(const pgBackup *backup)
{
char timestamp[100];
time2iso(timestamp, lengthof(timestamp), backup->start_time);
printf(_(" %s (%X/%08X)\n"), timestamp, backup->stop_lsn.xlogid,
backup->stop_lsn.xrecoff);
}
static void
search_next_wal(const char *path, uint32 *needId, uint32 *needSeg, parray *timelines)
{
int i;
int j;
int count;
char xlogfname[MAXFNAMELEN];
char pre_xlogfname[MAXFNAMELEN];
char xlogpath[MAXPGPATH];
struct stat st;
count = 0;
for (;;)
{
for (i = 0; i < parray_num(timelines); i++)
{
pgTimeLine *timeline = (pgTimeLine *) parray_get(timelines, i);
XLogFileName(xlogfname, timeline->tli, *needId, *needSeg);
snprintf(xlogpath, lengthof(xlogpath), "%s/%s", path, xlogfname);
if (stat(xlogpath, &st) == 0)
break;
}
/* not found */
if (i == parray_num(timelines))
{
if (count == 1)
printf(_("\n"));
else if (count > 1)
printf(_(" - %s\n"), pre_xlogfname);
return;
}
count++;
if (count == 1)
printf(_("%s"), xlogfname);
strcpy(pre_xlogfname, xlogfname);
/* delete old TLI */
for (j = i + 1; j < parray_num(timelines); j++)
parray_remove(timelines, i + 1);
/* XXX: should we add a linebreak when we find a timeline? */
NextLogSeg(*needId, *needSeg);
}
}

255
show.c Normal file
View File

@ -0,0 +1,255 @@
/*-------------------------------------------------------------------------
*
* show.c: show backup catalog.
*
* Copyright (c) 2009, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
static void show_backup_list(FILE *out, parray *backup_list, bool show_all);
static void show_timeline_backup_list(FILE *out, parray *backup_list, bool show_all);
static void show_backup_detail(FILE *out, pgBackup *backup);
/*
* Show backup catalog information.
* If range is { 0, 0 }, show list of all backup, otherwise show detail of the
* backup indicated by id.
*/
int
do_show(pgBackupRange *range, bool show_timeline, bool show_all)
{
if (pgBackupRangeIsSingle(range))
{
pgBackup *backup;
backup = catalog_get_backup(range->begin);
if (backup == NULL)
{
char timestamp[100];
time2iso(timestamp, lengthof(timestamp), range->begin);
elog(INFO, _("backup taken at \"%s\" doesn not exist."),
timestamp);
/* This is not error case */
return 0;
}
show_backup_detail(stdout, backup);
/* cleanup */
pgBackupFree(backup);
}
else
{
parray *backup_list;
backup_list = catalog_get_backup_list(range);
if (backup_list == NULL)
return 1;
if (!show_timeline)
show_backup_list(stdout, backup_list, show_all);
else
show_timeline_backup_list(stdout, backup_list, show_all);
/* cleanup */
parray_walk(backup_list, pgBackupFree);
parray_free(backup_list);
}
return 0;
}
static void
pretty_size(int64 size, char *buf, size_t len)
{
int exp = 0;
/* minus means the size is invalid */
if (size < 0)
{
strncpy(buf, "----", len);
return;
}
/* determine postfix */
while (size > 9999)
{
++exp;
size /= 1000;
}
switch (exp)
{
case 0:
snprintf(buf, len, INT64_FORMAT "B", size);
break;
case 1:
snprintf(buf, len, INT64_FORMAT "kB", size);
break;
case 2:
snprintf(buf, len, INT64_FORMAT "MB", size);
break;
case 3:
snprintf(buf, len, INT64_FORMAT "GB", size);
break;
case 4:
snprintf(buf, len, INT64_FORMAT "TB", size);
break;
case 5:
snprintf(buf, len, INT64_FORMAT "PB", size);
break;
default:
strncpy(buf, "***", len);
break;
}
}
static TimeLineID
get_parent_tli(TimeLineID child_tli)
{
TimeLineID result = 0;
char path[MAXPGPATH];
char fline[MAXPGPATH];
FILE *fd;
/* search from timeline history dir */
snprintf(path, lengthof(path), "%s/%s/%08X.history", backup_path,
TIMELINE_HISTORY_DIR, child_tli);
fd = fopen(path, "rt");
if (fd == NULL)
{
if (errno != ENOENT)
elog(ERROR_SYSTEM, _("could not open file \"%s\": %s"), path,
strerror(errno));
return 0;
}
/*
* Parse the file...
*/
while (fgets(fline, sizeof(fline), fd) != NULL)
{
/* skip leading whitespace and check for # comment */
char *ptr;
char *endptr;
for (ptr = fline; *ptr; ptr++)
{
if (!IsSpace(*ptr))
break;
}
if (*ptr == '\0' || *ptr == '#')
continue;
/* expect a numeric timeline ID as first field of line */
result = (TimeLineID) strtoul(ptr, &endptr, 0);
if (endptr == ptr)
elog(ERROR_CORRUPTED,
_("syntax error(timeline ID) in history file: %s"),
fline);
}
fclose(fd);
/* TLI of the last line is parent TLI */
return result;
}
static void
show_backup_list(FILE *out, parray *backup_list, bool show_all)
{
int i;
/* show header */
fputs("============================================================================\n", out);
fputs("Start Time Total Data WAL Log Backup Status \n", out);
fputs("============================================================================\n", out);
for (i = 0; i < parray_num(backup_list); i++)
{
pgBackup *backup;
char timestamp[20];
char duration[20] = "----";
char total_data_bytes_str[10] = "----";
char read_data_bytes_str[10] = "----";
char read_arclog_bytes_str[10] = "----";
char read_srvlog_bytes_str[10] = "----";
char write_bytes_str[10];
backup = parray_get(backup_list, i);
/* skip deleted backup */
if (backup->status == BACKUP_STATUS_DELETED && !show_all)
continue;
time2iso(timestamp, lengthof(timestamp), backup->start_time);
if (backup->end_time != (time_t) 0)
snprintf(duration, lengthof(duration), "%lum",
(backup->end_time - backup->start_time) / 60);
/* "Full" is only for full backup */
if (backup->backup_mode >= BACKUP_MODE_FULL)
pretty_size(backup->total_data_bytes, total_data_bytes_str,
lengthof(total_data_bytes_str));
else if (backup->backup_mode >= BACKUP_MODE_INCREMENTAL)
pretty_size(backup->read_data_bytes, read_data_bytes_str,
lengthof(read_data_bytes_str));
if (HAVE_ARCLOG(backup))
pretty_size(backup->read_arclog_bytes, read_arclog_bytes_str,
lengthof(read_arclog_bytes_str));
if (backup->with_serverlog)
pretty_size(backup->read_srvlog_bytes, read_srvlog_bytes_str,
lengthof(read_srvlog_bytes_str));
pretty_size(backup->write_bytes, write_bytes_str,
lengthof(write_bytes_str));
fprintf(out, "%-19s %5s %6s %6s %6s %6s %6s %s\n",
timestamp, duration,
total_data_bytes_str, read_data_bytes_str, read_arclog_bytes_str,
read_srvlog_bytes_str, write_bytes_str, status2str(backup->status));
}
}
static void
show_timeline_backup_list(FILE *out, parray *backup_list, bool show_all)
{
int i;
/* show header */
fputs("============================================================\n", out);
fputs("Start Mode Current TLI Parent TLI Status \n", out);
fputs("============================================================\n", out);
for (i = 0; i < parray_num(backup_list); i++)
{
static const char *modes[] = { "", "ARCH", "INCR", "FULL"};
pgBackup *backup;
char timestamp[20];
TimeLineID parent_tli;
backup = parray_get(backup_list, i);
/* skip deleted backup and serverlog backup */
if ((backup->status == BACKUP_STATUS_DELETED || !HAVE_ARCLOG(backup)) &&
!show_all)
continue;
time2iso(timestamp, lengthof(timestamp), backup->start_time);
parent_tli = get_parent_tli(backup->tli);
fprintf(out, "%-19s %-4s %10d %10d %s\n",
timestamp, modes[backup->backup_mode], backup->tli, parent_tli,
status2str(backup->status));
}
}
static void
show_backup_detail(FILE *out, pgBackup *backup)
{
pgBackupWriteConfigSection(out, backup);
pgBackupWriteResultSection(out, backup);
}

3
sql/backup.sql Normal file
View File

@ -0,0 +1,3 @@
\! pg_rman backup --verbose
\! pg_rman backup -B data/sample_backup --verbose

274
sql/backup_restore.sh Normal file
View File

@ -0,0 +1,274 @@
#!/bin/sh
BASE_PATH=`pwd`
export PGDATA=$BASE_PATH/results/sample_database
export BACKUP_PATH=$BASE_PATH/results/sample_backup2
export ARCLOG_PATH=$BASE_PATH/results/arclog
export SRVLOG_PATH=$PGDATA/pg_log
export COMPRESS_DATA=YES
XLOG_PATH=$PGDATA/pg_xlog
TBLSPC_PATH=$BASE_PATH/results/tblspc
# Port used for test database cluster
TEST_PGPORT=54321
# configuration
SCALE=1
DURATION=10
ISOLATE_SRVLOG=0
ISOLATE_WAL=0
while [ $# -gt 0 ]; do
case $1 in
"-s")
ISOLATE_SRVLOG=1
shift
;;
"-w")
ISOLATE_WAL=1
shift
;;
"-d")
DURATION=`expr $2 + 0`
if [ $? -ne 0 ]; then
echo "invalid duration"
exit 1
fi
shift 2
;;
"-s")
SCALE=`expr $2 + 0`
if [ $? -ne 0 ]; then
echo "invalid scale"
exit 1
fi
shift 2
;;
*)
shift
;;
esac
done
# delete old database cluster
pg_ctl stop -m immediate > /dev/null 2>&1
rm -rf $PGDATA
rm -rf $BASE_PATH/results/pg_xlog
rm -rf $BASE_PATH/results/srvlog
rm -rf $ARCLOG_PATH
rm -rf $SRVLOG_PATH
rm -rf $TBLSPC_PATH
# create new backup catalog
rm -rf $BACKUP_PATH
pg_rman init -B $BACKUP_PATH --quiet
# create default configuration file
cat << EOF > $BACKUP_PATH/pg_rman.ini
# comment
BACKUP_MODE = F # comment
EOF
# create new database cluster
initdb --no-locale > $BASE_PATH/results/initdb.log 2>&1
cat << EOF >> $PGDATA/postgresql.conf
port = $TEST_PGPORT
logging_collector = on
archive_mode = on
archive_command = 'cp "%p" "$ARCLOG_PATH/%f"'
EOF
mkdir -p $ARCLOG_PATH
mkdir -p $TBLSPC_PATH
# determine serverlog directory
if [ "$ISOLATE_SRVLOG" -ne 0 ]; then
export SRVLOG_PATH=$BASE_PATH/results/srvlog
echo "log_directory = '$SRVLOG_PATH'" >> $PGDATA/postgresql.conf
mkdir -p $SRVLOG_PATH
else
export SRVLOG_PATH=$PGDATA/pg_log
echo "log_directory = 'pg_log'" >> $PGDATA/postgresql.conf
fi
# isolate online WAL
if [ "$ISOLATE_WAL" -ne 0 ]; then
XLOG_PATH=$BASE_PATH/results/pg_xlog
mv $PGDATA/pg_xlog $XLOG_PATH
ln -s $XLOG_PATH $PGDATA/pg_xlog
fi
# start PostgreSQL
pg_ctl start -w -t 3600 > /dev/null 2>&1
# create tablespace and database for pgbench
mkdir -p $TBLSPC_PATH/pgbench
psql -p $TEST_PGPORT postgres <<EOF
CREATE TABLESPACE pgbench LOCATION '$TBLSPC_PATH/pgbench';
CREATE DATABASE pgbench TABLESPACE = pgbench;
EOF
# data_delete
export KEEP_DATA_GENERATIONS=2
export KEEP_DATA_DAYS=0
for i in `seq 1 5`; do
pg_rman -p $TEST_PGPORT backup --verbose -d postgres > $BASE_PATH/results/log_full_0_$i 2>&1
done
pg_rman -p $TEST_PGPORT show `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show_d_1 2>&1
echo "# of deleted backups"
grep -c DELETED $BASE_PATH/results/log_show_d_1
pgbench -p $TEST_PGPORT -i -s $SCALE pgbench > $BASE_PATH/results/pgbench.log 2>&1
echo "full database backup"
psql -p $TEST_PGPORT postgres -c "checkpoint"
pg_rman -p $TEST_PGPORT backup --verbose -d postgres > $BASE_PATH/results/log_full_1 2>&1
pgbench -p $TEST_PGPORT -T $DURATION -c 10 pgbench >> $BASE_PATH/results/pgbench.log 2>&1
echo "incremental database backup"
psql -p $TEST_PGPORT postgres -c "checkpoint"
pg_rman -p $TEST_PGPORT backup -b i --verbose -d postgres > $BASE_PATH/results/log_incr1 2>&1
# validate all backup
pg_rman validate `date +%Y` --verbose > $BASE_PATH/results/log_validate1 2>&1
pg_rman -p $TEST_PGPORT show `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show0 2>&1
pg_dumpall > $BASE_PATH/results/dump_before_rtx.sql
target_xid=`psql -p $TEST_PGPORT pgbench -tAq -c "INSERT INTO pgbench_history VALUES (1) RETURNING(xmin);"`
psql -p $TEST_PGPORT postgres -c "checkpoint"
pg_rman -p $TEST_PGPORT backup -b i --verbose -d postgres > $BASE_PATH/results/log_incr2 2>&1
pgbench -p $TEST_PGPORT -T $DURATION -c 10 pgbench >> $BASE_PATH/results/pgbench.log 2>&1
echo "archived WAL and serverlog backup"
pg_rman -p $TEST_PGPORT backup -b a --verbose -d postgres > $BASE_PATH/results/log_arclog 2>&1
# stop PG during transaction and get commited info for verifing
echo "stop DB during running pgbench"
pgbench -p $TEST_PGPORT -T $DURATION -c 10 pgbench >> $BASE_PATH/results/pgbench.log 2>&1 &
sleep `expr $DURATION / 2`
pg_ctl stop -m immediate > /dev/null 2>&1
cp -rp $PGDATA $PGDATA.bak
pg_ctl start -w -t 3600 > /dev/null 2>&1
pg_dumpall > $BASE_PATH/results/dump_before.sql
# revert to crushed cluster
pg_ctl stop > /dev/null 2>&1
rm -rf $PGDATA
mv $PGDATA.bak $PGDATA
# validate all backup
pg_rman validate `date +%Y` --verbose > $BASE_PATH/results/log_validate2 2>&1
# restore check with pg_rman
pg_rman restore -! --verbose --check > $BASE_PATH/results/log_restore_check_1 2>&1
# restore with pg_rman
CUR_TLI=`pg_controldata | grep TimeLineID | awk '{print $4}'`
pg_rman restore -! --verbose > $BASE_PATH/results/log_restore1_1 2>&1
CUR_TLI_R=`grep "current timeline ID = " $BASE_PATH/results/log_restore1_1 | awk '{print $5}'`
TARGET_TLI=`grep "target timeline ID = " $BASE_PATH/results/log_restore1_1 | awk '{print $5}'`
if [ "$CUR_TLI" != "$CUR_TLI_R" -o "$CUR_TLI" != "$CUR_TLI_R" ]; then
echo "failed: bad timeline ID" CUR_TLI=$CUR_TLI CUR_TLI_R=$CUR_TLI_R
fi
# Backup of online-WAL and serverlog.
echo "diff files in BACKUP_PATH/backup/pg_xlog"
diff -r $PGDATA/pg_xlog $BACKUP_PATH/backup/pg_xlog
echo "# of files in BACKUP_PATH/backup/srvlog"
find $BACKUP_PATH/backup/srvlog -type f | wc -l
# recovery database
pg_ctl start -w -t 3600 > /dev/null 2>&1
# re-restore with pg_rman
pg_ctl stop -m immediate > /dev/null 2>&1
# restore check with pg_rman
pg_rman restore -! --verbose --check > $BASE_PATH/results/log_restore_check_2 2>&1
CUR_TLI=`pg_controldata | grep TimeLineID | awk '{print $4}'`
pg_rman restore -! --verbose > $BASE_PATH/results/log_restore1_2 2>&1
CUR_TLI_R=`grep "current timeline ID = " $BASE_PATH/results/log_restore1_2 | awk '{print $5}'`
TARGET_TLI=`grep "target timeline ID = " $BASE_PATH/results/log_restore1_2 | awk '{print $5}'`
if [ "$CUR_TLI" != "$CUR_TLI_R" -o "$CUR_TLI" != "$CUR_TLI_R" ]; then
echo "failed: bad timeline ID" CUR_TLI=$CUR_TLI CUR_TLI_R=$CUR_TLI_R
fi
# Backup of online-WAL and serverlog.
echo "diff files in BACKUP_PATH/backup/pg_xlog"
diff -r $PGDATA/pg_xlog $BACKUP_PATH/backup/pg_xlog
echo "# of files in BACKUP_PATH/backup/srvlog"
find $BACKUP_PATH/backup/srvlog -type f | wc -l
# re-recovery database
pg_ctl start -w -t 3600 > /dev/null 2>&1
# compare recovery results
pg_dumpall > $BASE_PATH/results/dump_after.sql
diff $BASE_PATH/results/dump_before.sql $BASE_PATH/results/dump_after.sql
# take a backup and delete backed up online files
# incrementa backup can't find last full backup because new timeline started.
echo "full database backup after recovery"
psql -p $TEST_PGPORT postgres -c "checkpoint"
pg_rman -p $TEST_PGPORT backup -b f --verbose -d postgres > $BASE_PATH/results/log_full2 2>&1
# Backup of online-WAL should been deleted, but serverlog remain.
echo "# of files in BACKUP_PATH/backup/pg_xlog"
find $BACKUP_PATH/backup/pg_xlog -type f | wc -l
echo "# of files in BACKUP_PATH/backup/srvlog"
find $BACKUP_PATH/backup/srvlog -type f | wc -l
# Symbolic links in $ARCLOG_PATH should be deleted.
echo "# of symbolic links in ARCLOG_PATH"
find $ARCLOG_PATH -type l | wc -l
# timeline history files are backed up.
echo "# of files in BACKUP_PATH/timeline_history"
find $BACKUP_PATH/timeline_history -type f | wc -l
# restore with pg_rman
pg_ctl stop -m immediate > /dev/null 2>&1
# restore check with pg_rman
pg_rman restore -! --verbose --check > $BASE_PATH/results/log_restore_check_3 2>&1
CUR_TLI=`pg_controldata | grep TimeLineID | awk '{print $4}'`
pg_rman restore -! --recovery-target-xid $target_xid --recovery-target-inclusive false --verbose > $BASE_PATH/results/log_restore2 2>&1
CUR_TLI_R=`grep "current timeline ID = " $BASE_PATH/results/log_restore2 | awk '{print $5}'`
TARGET_TLI=`grep "target timeline ID = " $BASE_PATH/results/log_restore2 | awk '{print $5}'`
if [ "$CUR_TLI" != "$CUR_TLI_R" -o "$CUR_TLI" != "$CUR_TLI_R" ]; then
echo "failed: bad timeline ID" CUR_TLI=$CUR_TLI CUR_TLI_R=$CUR_TLI_R
fi
echo "# of recovery target option in recovery.conf"
grep -c "recovery_target_" $PGDATA/recovery.conf
# recovery database
pg_ctl start -w -t 3600 > /dev/null 2>&1
pg_dumpall > $BASE_PATH/results/dump_after_rtx.sql
diff $BASE_PATH/results/dump_before_rtx.sql $BASE_PATH/results/dump_after_rtx.sql
# show timeline
pg_rman -p $TEST_PGPORT show timeline --verbose -a -d postgres > $BASE_PATH/results/log_show_timeline_1 2>&1
pg_rman -p $TEST_PGPORT show timeline `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show_timeline_2 2>&1
pg_rman -p $TEST_PGPORT show timeline `date +%Y` --verbose -d postgres > $BASE_PATH/results/log_show_timeline_3 2>&1
echo "# of deleted backups (show all)"
grep -c DELETED $BASE_PATH/results/log_show_timeline_2
echo "# of deleted backups"
grep -c DELETED $BASE_PATH/results/log_show_timeline_3
echo "delete backup"
pg_rman -p $TEST_PGPORT delete --debug -d postgres > $BASE_PATH/results/log_delete1 2>&1
pg_rman -p $TEST_PGPORT show `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show1 2>&1
echo "# of deleted backups"
grep -c DELETED $BASE_PATH/results/log_show1
pg_rman -p $TEST_PGPORT delete `date "+%Y-%m-%d %T"` --debug -d postgres > $BASE_PATH/results/log_delete2 2>&1
pg_rman -p $TEST_PGPORT show `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show2 2>&1
echo "# of deleted backups"
grep -c DELETED $BASE_PATH/results/log_show2
pg_rman -p $TEST_PGPORT show timeline `date +%Y` -a --verbose -d postgres > $BASE_PATH/results/log_show_timeline_4 2>&1
# cleanup
pg_ctl stop -m immediate > /dev/null 2>&1

2
sql/backup_restore.sql Normal file
View File

@ -0,0 +1,2 @@
\! sh sql/backup_restore.sh

4
sql/init.sql Normal file
View File

@ -0,0 +1,4 @@
\! rm -rf results/init_test
\! pg_rman init -B results/init_test --quiet;echo $?
\! find results/init_test | xargs ls -Fd | sort
\! pg_rman init -B results/init_test --quiet;echo $?

80
sql/option.sh Normal file
View File

@ -0,0 +1,80 @@
#!/bin/sh
#============================================================================
# This is a test script for option test of pg_rman.
#============================================================================
BASE_PATH=`pwd`
# Clear environment variables used by pg_rman except $PGDATA.
# List of environment variables is defined in catalog.c.
unset BACKUP_PATH
unset ARCLOG_PATH
unset SRVLOG_PATH
unset BACKUP_MODE
unset COMPRESS_DATA
unset KEEP_ARCLOG_DAYS
unset KEEP_DATA_GENERATIONS
unset KEEP_DATA_DAYS
unset KEEP_SRVLOG_FILES
unset KEEP_SRVLOG_DAYS
export PGDATA=$BASE_PATH/results/sample_database
# Note: not exported
BACKUP_PATH=$BASE_PATH/results/sample_backup2
# Setup backup catalog for backup test.
rm -rf $BACKUP_PATH
cp -rp data/sample_backup $BACKUP_PATH
# general option
pg_rman --help
pg_rman --version
# backup option
# required arguments check
pg_rman backup --verbose
pg_rman backup --verbose -B $BACKUP_PATH
pg_rman backup --verbose -B $BACKUP_PATH -b f
pg_rman backup --verbose -B $BACKUP_PATH -b i
pg_rman backup --verbose -B $BACKUP_PATH -b a
# bad arguments check
pg_rman backup --verbose -B $BACKUP_PATH -b bad
# delete or validate requires DATE
pg_rman delete -B $BACKUP_PATH
pg_rman validate -B $BACKUP_PATH
# invalid configuration file check
echo " = INFINITE" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_MODE= " > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_MODE = F#S" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_MODE = F #comment A" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_MODE=B" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "COMPRESS_DATA=FOO" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "KEEP_ARCLOG_FILES=YES" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "TIMELINEID=-1" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_TARGETS=F" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
echo "BACKUP_MODE='F''\'\\\F'" > $BACKUP_PATH/pg_rman.ini
pg_rman backup --verbose -B $BACKUP_PATH
# configuration priorityfile check
echo "BACKUP_MODE=ENV_PATH" > $BACKUP_PATH/pg_rman.ini
mkdir $BACKUP_PATH/conf_path
echo "BACKUP_PATH=$BACKUP_PATH/conf_path" > $BACKUP_PATH/pg_rman.conf
echo "BACKUP_MODE=CONF_PATH" > $BACKUP_PATH/conf_path/pg_rman.ini
mkdir $BACKUP_PATH/comm_path
echo "BACKUP_MODE=COMM_PATH" > $BACKUP_PATH/comm_path/pg_rman.ini
export BACKUP_PATH=$BACKUP_PATH
pg_rman backup --verbose

1
sql/option.sql Normal file
View File

@ -0,0 +1 @@
\! sh sql/option.sh

8
sql/show_validate.sql Normal file
View File

@ -0,0 +1,8 @@
-- test show command
\! rm -rf results/sample_backup
\! cp -rp data/sample_backup results/sample_backup
\! pg_rman show -B results/sample_backup
\! pg_rman validate -B results/sample_backup 2009-05-31 17:05:53 --debug
\! pg_rman validate -B results/sample_backup 2009-06-01 17:05:53 --debug
\! pg_rman show -a -B results/sample_backup
\! pg_rman show 2009-06-01 17:05:53 -B results/sample_backup

85
util.c Normal file
View File

@ -0,0 +1,85 @@
/*-------------------------------------------------------------------------
*
* util.c: log messages to log file or stderr, and misc code.
*
* Copyright (c) 2009, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include <time.h>
/*
* Convert time_t value to ISO-8601 format string
*/
void
time2iso(char *buf, size_t len, time_t time)
{
struct tm *tm = localtime(&time);
if (time == (time_t) 0)
strncpy(buf,"****-**-** **:**:**", len);
else
strftime(buf, len, "%Y-%m-%d %H:%M:%S", tm);
}
const char *
status2str(BackupStatus status)
{
static const char *statusName[] =
{
"UNKNOWN",
"OK",
"RUNNING",
"ERROR",
"DELETING",
"DELETED",
"DONE",
"CORRUPT"
};
if (status < BACKUP_STATUS_INVALID || BACKUP_STATUS_CORRUPT < status)
return "UNKNOWN";
return statusName[status];
}
void
remove_trailing_space(char *buf, int comment_mark)
{
int i;
char *last_char = NULL;
for (i = 0; buf[i]; i++)
{
if (buf[i] == comment_mark || buf[i] == '\n' || buf[i] == '\r')
{
buf[i] = '\0';
break;
}
}
for (i = 0; buf[i]; i++)
{
if (!isspace(buf[i]))
last_char = buf + i;
}
if (last_char != NULL)
*(last_char + 1) = '\0';
}
void
remove_not_digit(char *buf, size_t len, const char *str)
{
int i, j;
for (i = 0, j = 0; str[i] && j < len; i++)
{
if (!isdigit(str[i]))
continue;
buf[j++] = str[i];
}
buf[j] = '\0';
}

183
validate.c Normal file
View File

@ -0,0 +1,183 @@
/*-------------------------------------------------------------------------
*
* validate.c: validate backup files.
*
* Copyright (c) 2009, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include <sys/stat.h>
static bool pgBackupValidateFiles(parray *files, const char *root, bool size_only);
/*
* Validate files in the backup and update its status to OK.
* If any of files are corrupted, update its stutus to CORRUPT.
*/
int
do_validate(pgBackupRange *range)
{
int i;
parray *backup_list;
catalog_lock();
/* get backup list matches given range */
backup_list = catalog_get_backup_list(range);
parray_qsort(backup_list, pgBackupCompareId);
for (i = 0; i < parray_num(backup_list); i++)
{
pgBackup *backup = (pgBackup *)parray_get(backup_list, i);
/* Validate completed backups only. */
if (backup->status != BACKUP_STATUS_DONE)
continue;
/* validate with CRC value and update status to OK */
pgBackupValidate(backup, false);
}
/* cleanup */
parray_walk(backup_list, pgBackupFree);
parray_free(backup_list);
catalog_unlock();
return 0;
}
/*
* Validate each files in the backup with its size.
*/
void
pgBackupValidate(pgBackup *backup, bool size_only)
{
char timestamp[100];
char base_path[MAXPGPATH];
char path[MAXPGPATH];
parray *files;
bool corrupted = false;
time2iso(timestamp, lengthof(timestamp), backup->start_time);
elog(INFO, "validate: %s", timestamp);
if (HAVE_DATABASE(backup))
{
elog(LOG, "database files...");
pgBackupGetPath(backup, base_path, lengthof(base_path), DATABASE_DIR);
pgBackupGetPath(backup, path, lengthof(path),
DATABASE_FILE_LIST);
files = dir_read_file_list(base_path, path);
if (!pgBackupValidateFiles(files, base_path, size_only))
corrupted = true;
parray_walk(files, pgFileFree);
parray_free(files);
}
if (HAVE_ARCLOG(backup))
{
elog(LOG, "archive WAL files...");
pgBackupGetPath(backup, base_path, lengthof(base_path), ARCLOG_DIR);
pgBackupGetPath(backup, path, lengthof(path), ARCLOG_FILE_LIST);
files = dir_read_file_list(base_path, path);
if (!pgBackupValidateFiles(files, base_path, size_only))
corrupted = true;
parray_walk(files, pgFileFree);
parray_free(files);
}
if (backup->with_serverlog)
{
elog(LOG, "server log files...");
pgBackupGetPath(backup, base_path, lengthof(base_path), SRVLOG_DIR);
pgBackupGetPath(backup, path, lengthof(path), SRVLOG_FILE_LIST);
files = dir_read_file_list(base_path, path);
if (!pgBackupValidateFiles(files, base_path, size_only))
corrupted = true;
parray_walk(files, pgFileFree);
parray_free(files);
}
/* update status to OK */
if (corrupted)
backup->status = BACKUP_STATUS_CORRUPT;
else
backup->status = BACKUP_STATUS_OK;
pgBackupWriteIni(backup);
if (corrupted)
elog(WARNING, "backup %s is corrupted", timestamp);
else
elog(LOG, "backup %s is valid", timestamp);
}
static const char *
get_relative_path(const char *path, const char *root)
{
size_t rootlen = strlen(root);
if (strncmp(path, root, rootlen) == 0 && path[rootlen] == '/')
return path + rootlen + 1;
else
return path;
}
/*
* Validate files in the backup with size or CRC.
*/
static bool
pgBackupValidateFiles(parray *files, const char *root, bool size_only)
{
int i;
for (i = 0; i < parray_num(files); i++)
{
struct stat st;
pgFile *file = (pgFile *) parray_get(files, i);
if (interrupted)
elog(ERROR_INTERRUPTED, _("interrupted during validate"));
/* skipped backup while incremental backup */
if (file->write_size == BYTES_INVALID || !S_ISREG(file->mode))
continue;
/* print progress */
elog(LOG, _("(%d/%lu) %s"), i + 1, (unsigned long) parray_num(files),
get_relative_path(file->path, root));
/* always validate file size */
if (stat(file->path, &st) == -1)
{
if (errno == ENOENT)
elog(WARNING, _("backup file \"%s\" vanished"), file->path);
else
elog(ERROR_SYSTEM, _("can't stat backup file \"%s\": %s"),
get_relative_path(file->path, root), strerror(errno));
return false;
}
if (file->write_size != st.st_size)
{
elog(WARNING, _("size of backup file \"%s\" must be %lu but %lu"),
get_relative_path(file->path, root),
(unsigned long) file->write_size,
(unsigned long) st.st_size);
return false;
}
/* validate CRC too */
if (!size_only)
{
pg_crc32 crc;
crc = pgFileGetCRC(file);
if (crc != file->crc)
{
elog(WARNING, _("CRC of backup file \"%s\" must be %X but %X"),
get_relative_path(file->path, root), file->crc, crc);
return false;
}
}
}
return true;
}

91
xlog.c Normal file
View File

@ -0,0 +1,91 @@
/*-------------------------------------------------------------------------
*
* xlog.c: Parse WAL files.
*
* Copyright (c) 2009, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
*
*-------------------------------------------------------------------------
*/
#include "pg_rman.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#if PG_VERSION_NUM >= 80400
typedef unsigned long Datum;
typedef struct MemoryContextData *MemoryContext;
#endif
#include "access/xlog_internal.h"
/*
* Return whether the file is a WAL segment or not.
* based on ValidXLOGHeader() in src/backend/access/transam/xlog.c.
*/
bool
xlog_is_complete_wal(const pgFile *file)
{
FILE *fp;
char page[XLOG_BLCKSZ];
XLogPageHeader header = (XLogPageHeader) page;
XLogLongPageHeader lheader = (XLogLongPageHeader) page;
fp = fopen(file->path, "r");
if (!fp)
return false;
if (fread(page, 1, sizeof(page), fp) != XLOG_BLCKSZ)
{
fclose(fp);
return false;
}
fclose(fp);
/* check header */
if (header->xlp_magic != XLOG_PAGE_MAGIC)
return false;
if ((header->xlp_info & ~XLP_ALL_FLAGS) != 0)
return false;
if (header->xlp_info & XLP_LONG_HEADER)
{
if (lheader->xlp_seg_size != XLogSegSize)
return false;
/* compressed WAL (with lesslog) has 0 in lheader->xlp_xlog_blcksz. */
if (lheader->xlp_xlog_blcksz != XLOG_BLCKSZ &&
lheader->xlp_xlog_blcksz != 0)
return false;
}
/* check size (actual file size, not backup file size) */
if (lheader->xlp_xlog_blcksz == XLOG_BLCKSZ && file->size != XLogSegSize)
return false;
return true;
}
bool
xlog_logfname2lsn(const char *logfname, XLogRecPtr *lsn)
{
uint32 tli;
if (sscanf(logfname, "%08X%08X%08X",
&tli, &lsn->xlogid, &lsn->xrecoff) != 3)
return false;
lsn->xrecoff *= XLogSegSize;
return true;
}
/*
* based on XLogFileName() in xlog_internal.h
*/
void
xlog_fname(char *fname, size_t len, TimeLineID tli, XLogRecPtr *lsn)
{
snprintf(fname, len, "%08X%08X%08X", tli,
lsn->xlogid, lsn->xrecoff / XLogSegSize);
}