PostgreSQL Source Code  git master
basic_archive.c File Reference
#include "postgres.h"
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include "archive/archive_module.h"
#include "common/int.h"
#include "miscadmin.h"
#include "storage/copydir.h"
#include "storage/fd.h"
#include "utils/guc.h"
#include "utils/memutils.h"
Include dependency graph for basic_archive.c:

Go to the source code of this file.

Data Structures

struct  BasicArchiveData
 

Macros

#define CMP_BUF_SIZE   (4096)
 

Typedefs

typedef struct BasicArchiveData BasicArchiveData
 

Functions

static void basic_archive_startup (ArchiveModuleState *state)
 
static bool basic_archive_configured (ArchiveModuleState *state)
 
static bool basic_archive_file (ArchiveModuleState *state, const char *file, const char *path)
 
static void basic_archive_file_internal (const char *file, const char *path)
 
static bool check_archive_directory (char **newval, void **extra, GucSource source)
 
static bool compare_files (const char *file1, const char *file2)
 
static void basic_archive_shutdown (ArchiveModuleState *state)
 
void _PG_init (void)
 
const ArchiveModuleCallbacks_PG_archive_module_init (void)
 

Variables

 PG_MODULE_MAGIC
 
static char * archive_directory = NULL
 
static const ArchiveModuleCallbacks basic_archive_callbacks
 

Macro Definition Documentation

◆ CMP_BUF_SIZE

#define CMP_BUF_SIZE   (4096)

Typedef Documentation

◆ BasicArchiveData

Function Documentation

◆ _PG_archive_module_init()

const ArchiveModuleCallbacks* _PG_archive_module_init ( void  )

Definition at line 91 of file basic_archive.c.

92 {
94 }
static const ArchiveModuleCallbacks basic_archive_callbacks
Definition: basic_archive.c:58

References basic_archive_callbacks.

◆ _PG_init()

void _PG_init ( void  )

Definition at line 71 of file basic_archive.c.

72 {
73  DefineCustomStringVariable("basic_archive.archive_directory",
74  gettext_noop("Archive file destination directory."),
75  NULL,
77  "",
78  PGC_SIGHUP,
79  0,
80  check_archive_directory, NULL, NULL);
81 
82  MarkGUCPrefixReserved("basic_archive");
83 }
static char * archive_directory
Definition: basic_archive.c:48
static bool check_archive_directory(char **newval, void **extra, GucSource source)
#define gettext_noop(x)
Definition: c.h:1204
void DefineCustomStringVariable(const char *name, const char *short_desc, const char *long_desc, char **valueAddr, const char *bootValue, GucContext context, int flags, GucStringCheckHook check_hook, GucStringAssignHook assign_hook, GucShowHook show_hook)
Definition: guc.c:5044
void MarkGUCPrefixReserved(const char *className)
Definition: guc.c:5105
@ PGC_SIGHUP
Definition: guc.h:71

References archive_directory, check_archive_directory(), DefineCustomStringVariable(), gettext_noop, MarkGUCPrefixReserved(), and PGC_SIGHUP.

◆ basic_archive_configured()

static bool basic_archive_configured ( ArchiveModuleState state)
static

Definition at line 162 of file basic_archive.c.

163 {
164  return archive_directory != NULL && archive_directory[0] != '\0';
165 }

References archive_directory.

◆ basic_archive_file()

static bool basic_archive_file ( ArchiveModuleState state,
const char *  file,
const char *  path 
)
static

Definition at line 173 of file basic_archive.c.

174 {
175  sigjmp_buf local_sigjmp_buf;
176  MemoryContext oldcontext;
177  BasicArchiveData *data = (BasicArchiveData *) state->private_data;
178  MemoryContext basic_archive_context = data->context;
179 
180  /*
181  * We run basic_archive_file_internal() in our own memory context so that
182  * we can easily reset it during error recovery (thus avoiding memory
183  * leaks).
184  */
185  oldcontext = MemoryContextSwitchTo(basic_archive_context);
186 
187  /*
188  * Since the archiver operates at the bottom of the exception stack,
189  * ERRORs turn into FATALs and cause the archiver process to restart.
190  * However, using ereport(ERROR, ...) when there are problems is easy to
191  * code and maintain. Therefore, we create our own exception handler to
192  * catch ERRORs and return false instead of restarting the archiver
193  * whenever there is a failure.
194  */
195  if (sigsetjmp(local_sigjmp_buf, 1) != 0)
196  {
197  /* Since not using PG_TRY, must reset error stack by hand */
198  error_context_stack = NULL;
199 
200  /* Prevent interrupts while cleaning up */
201  HOLD_INTERRUPTS();
202 
203  /* Report the error and clear ErrorContext for next time */
204  EmitErrorReport();
205  FlushErrorState();
206 
207  /* Close any files left open by copy_file() or compare_files() */
209 
210  /* Reset our memory context and switch back to the original one */
211  MemoryContextSwitchTo(oldcontext);
212  MemoryContextReset(basic_archive_context);
213 
214  /* Remove our exception handler */
215  PG_exception_stack = NULL;
216 
217  /* Now we can allow interrupts again */
219 
220  /* Report failure so that the archiver retries this file */
221  return false;
222  }
223 
224  /* Enable our exception handler */
225  PG_exception_stack = &local_sigjmp_buf;
226 
227  /* Archive the file! */
228  basic_archive_file_internal(file, path);
229 
230  /* Remove our exception handler */
231  PG_exception_stack = NULL;
232 
233  /* Reset our memory context and switch back to the original one */
234  MemoryContextSwitchTo(oldcontext);
235  MemoryContextReset(basic_archive_context);
236 
237  return true;
238 }
static void basic_archive_file_internal(const char *file, const char *path)
#define InvalidSubTransactionId
Definition: c.h:642
void EmitErrorReport(void)
Definition: elog.c:1669
ErrorContextCallback * error_context_stack
Definition: elog.c:95
void FlushErrorState(void)
Definition: elog.c:1825
sigjmp_buf * PG_exception_stack
Definition: elog.c:97
void AtEOSubXact_Files(bool isCommit, SubTransactionId mySubid, SubTransactionId parentSubid)
Definition: fd.c:3029
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:330
#define RESUME_INTERRUPTS()
Definition: miscadmin.h:134
#define HOLD_INTERRUPTS()
Definition: miscadmin.h:132
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:138
const void * data
Definition: regguts.h:323

References AtEOSubXact_Files(), basic_archive_file_internal(), data, EmitErrorReport(), error_context_stack, FlushErrorState(), HOLD_INTERRUPTS, InvalidSubTransactionId, MemoryContextReset(), MemoryContextSwitchTo(), PG_exception_stack, and RESUME_INTERRUPTS.

◆ basic_archive_file_internal()

static void basic_archive_file_internal ( const char *  file,
const char *  path 
)
static

Definition at line 241 of file basic_archive.c.

242 {
243  char destination[MAXPGPATH];
244  char temp[MAXPGPATH + 256];
245  struct stat st;
246  struct timeval tv;
247  uint64 epoch; /* milliseconds */
248 
249  ereport(DEBUG3,
250  (errmsg("archiving \"%s\" via basic_archive", file)));
251 
252  snprintf(destination, MAXPGPATH, "%s/%s", archive_directory, file);
253 
254  /*
255  * First, check if the file has already been archived. If it already
256  * exists and has the same contents as the file we're trying to archive,
257  * we can return success (after ensuring the file is persisted to disk).
258  * This scenario is possible if the server crashed after archiving the
259  * file but before renaming its .ready file to .done.
260  *
261  * If the archive file already exists but has different contents,
262  * something might be wrong, so we just fail.
263  */
264  if (stat(destination, &st) == 0)
265  {
266  if (compare_files(path, destination))
267  {
268  ereport(DEBUG3,
269  (errmsg("archive file \"%s\" already exists with identical contents",
270  destination)));
271 
272  fsync_fname(destination, false);
274 
275  return;
276  }
277 
278  ereport(ERROR,
279  (errmsg("archive file \"%s\" already exists", destination)));
280  }
281  else if (errno != ENOENT)
282  ereport(ERROR,
284  errmsg("could not stat file \"%s\": %m", destination)));
285 
286  /*
287  * Pick a sufficiently unique name for the temporary file so that a
288  * collision is unlikely. This helps avoid problems in case a temporary
289  * file was left around after a crash or another server happens to be
290  * archiving to the same directory.
291  */
292  gettimeofday(&tv, NULL);
293  if (pg_mul_u64_overflow((uint64) 1000, (uint64) tv.tv_sec, &epoch) ||
294  pg_add_u64_overflow(epoch, (uint64) (tv.tv_usec / 1000), &epoch))
295  elog(ERROR, "could not generate temporary file name for archiving");
296 
297  snprintf(temp, sizeof(temp), "%s/%s.%s.%d." UINT64_FORMAT,
298  archive_directory, "archtemp", file, MyProcPid, epoch);
299 
300  /*
301  * Copy the file to its temporary destination. Note that this will fail
302  * if temp already exists.
303  */
304  copy_file(path, temp);
305 
306  /*
307  * Sync the temporary file to disk and move it to its final destination.
308  * Note that this will overwrite any existing file, but this is only
309  * possible if someone else created the file since the stat() above.
310  */
311  (void) durable_rename(temp, destination, ERROR);
312 
313  ereport(DEBUG1,
314  (errmsg("archived \"%s\" via basic_archive", file)));
315 }
static bool compare_files(const char *file1, const char *file2)
#define UINT64_FORMAT
Definition: c.h:533
void copy_file(const char *fromfile, const char *tofile)
Definition: copydir.c:117
int errcode_for_file_access(void)
Definition: elog.c:881
int errmsg(const char *fmt,...)
Definition: elog.c:1069
#define DEBUG3
Definition: elog.h:28
#define DEBUG1
Definition: elog.h:30
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
int durable_rename(const char *oldfile, const char *newfile, int elevel)
Definition: fd.c:693
void fsync_fname(const char *fname, bool isdir)
Definition: fd.c:667
int MyProcPid
Definition: globals.c:44
static bool pg_add_u64_overflow(uint64 a, uint64 b, uint64 *result)
Definition: int.h:376
static bool pg_mul_u64_overflow(uint64 a, uint64 b, uint64 *result)
Definition: int.h:410
#define MAXPGPATH
#define snprintf
Definition: port.h:238
#define stat
Definition: win32_port.h:292
static const unsigned __int64 epoch
int gettimeofday(struct timeval *tp, void *tzp)

References archive_directory, compare_files(), copy_file(), DEBUG1, DEBUG3, durable_rename(), elog(), epoch, ereport, errcode_for_file_access(), errmsg(), ERROR, fsync_fname(), gettimeofday(), MAXPGPATH, MyProcPid, pg_add_u64_overflow(), pg_mul_u64_overflow(), snprintf, stat, and UINT64_FORMAT.

Referenced by basic_archive_file().

◆ basic_archive_shutdown()

static void basic_archive_shutdown ( ArchiveModuleState state)
static

Definition at line 404 of file basic_archive.c.

405 {
406  BasicArchiveData *data = (BasicArchiveData *) state->private_data;
407  MemoryContext basic_archive_context;
408 
409  /*
410  * If we didn't get to storing the pointer to our allocated state, we
411  * don't have anything to clean up.
412  */
413  if (data == NULL)
414  return;
415 
416  basic_archive_context = data->context;
417  Assert(CurrentMemoryContext != basic_archive_context);
418 
419  if (MemoryContextIsValid(basic_archive_context))
420  MemoryContextDelete(basic_archive_context);
421  data->context = NULL;
422 
423  /*
424  * Finally, free the state.
425  */
426  pfree(data);
427  state->private_data = NULL;
428 }
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
Assert(fmt[strlen(fmt) - 1] !='\n')
void pfree(void *pointer)
Definition: mcxt.c:1456
MemoryContext CurrentMemoryContext
Definition: mcxt.c:135
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:403
#define MemoryContextIsValid(context)
Definition: memnodes.h:107

References Assert(), CurrentMemoryContext, data, if(), MemoryContextDelete(), MemoryContextIsValid, and pfree().

◆ basic_archive_startup()

void basic_archive_startup ( ArchiveModuleState state)
static

Definition at line 102 of file basic_archive.c.

103 {
105 
107  sizeof(BasicArchiveData));
109  "basic_archive",
111  state->private_data = (void *) data;
112 }
MemoryContext TopMemoryContext
Definition: mcxt.c:141
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:1064
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:153

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, data, MemoryContextAllocZero(), and TopMemoryContext.

◆ check_archive_directory()

static bool check_archive_directory ( char **  newval,
void **  extra,
GucSource  source 
)
static

Definition at line 120 of file basic_archive.c.

121 {
122  struct stat st;
123 
124  /*
125  * The default value is an empty string, so we have to accept that value.
126  * Our check_configured callback also checks for this and prevents
127  * archiving from proceeding if it is still empty.
128  */
129  if (*newval == NULL || *newval[0] == '\0')
130  return true;
131 
132  /*
133  * Make sure the file paths won't be too long. The docs indicate that the
134  * file names to be archived can be up to 64 characters long.
135  */
136  if (strlen(*newval) + 64 + 2 >= MAXPGPATH)
137  {
138  GUC_check_errdetail("Archive directory too long.");
139  return false;
140  }
141 
142  /*
143  * Do a basic sanity check that the specified archive directory exists. It
144  * could be removed at some point in the future, so we still need to be
145  * prepared for it not to exist in the actual archiving logic.
146  */
147  if (stat(*newval, &st) != 0 || !S_ISDIR(st.st_mode))
148  {
149  GUC_check_errdetail("Specified archive directory does not exist.");
150  return false;
151  }
152 
153  return true;
154 }
#define newval
#define GUC_check_errdetail
Definition: guc.h:434
#define S_ISDIR(m)
Definition: win32_port.h:333

References GUC_check_errdetail, MAXPGPATH, newval, S_ISDIR, stat::st_mode, and stat.

Referenced by _PG_init().

◆ compare_files()

static bool compare_files ( const char *  file1,
const char *  file2 
)
static

Definition at line 323 of file basic_archive.c.

324 {
325 #define CMP_BUF_SIZE (4096)
326  char buf1[CMP_BUF_SIZE];
327  char buf2[CMP_BUF_SIZE];
328  int fd1;
329  int fd2;
330  bool ret = true;
331 
332  fd1 = OpenTransientFile(file1, O_RDONLY | PG_BINARY);
333  if (fd1 < 0)
334  ereport(ERROR,
336  errmsg("could not open file \"%s\": %m", file1)));
337 
338  fd2 = OpenTransientFile(file2, O_RDONLY | PG_BINARY);
339  if (fd2 < 0)
340  ereport(ERROR,
342  errmsg("could not open file \"%s\": %m", file2)));
343 
344  for (;;)
345  {
346  int nbytes = 0;
347  int buf1_len = 0;
348  int buf2_len = 0;
349 
350  while (buf1_len < CMP_BUF_SIZE)
351  {
352  nbytes = read(fd1, buf1 + buf1_len, CMP_BUF_SIZE - buf1_len);
353  if (nbytes < 0)
354  ereport(ERROR,
356  errmsg("could not read file \"%s\": %m", file1)));
357  else if (nbytes == 0)
358  break;
359 
360  buf1_len += nbytes;
361  }
362 
363  while (buf2_len < CMP_BUF_SIZE)
364  {
365  nbytes = read(fd2, buf2 + buf2_len, CMP_BUF_SIZE - buf2_len);
366  if (nbytes < 0)
367  ereport(ERROR,
369  errmsg("could not read file \"%s\": %m", file2)));
370  else if (nbytes == 0)
371  break;
372 
373  buf2_len += nbytes;
374  }
375 
376  if (buf1_len != buf2_len || memcmp(buf1, buf2, buf1_len) != 0)
377  {
378  ret = false;
379  break;
380  }
381  else if (buf1_len == 0)
382  break;
383  }
384 
385  if (CloseTransientFile(fd1) != 0)
386  ereport(ERROR,
388  errmsg("could not close file \"%s\": %m", file1)));
389 
390  if (CloseTransientFile(fd2) != 0)
391  ereport(ERROR,
393  errmsg("could not close file \"%s\": %m", file2)));
394 
395  return ret;
396 }
#define CMP_BUF_SIZE
#define PG_BINARY
Definition: c.h:1278
int CloseTransientFile(int fd)
Definition: fd.c:2706
int OpenTransientFile(const char *fileName, int fileFlags)
Definition: fd.c:2530
#define read(a, b, c)
Definition: win32.h:13

References CloseTransientFile(), CMP_BUF_SIZE, ereport, errcode_for_file_access(), errmsg(), ERROR, OpenTransientFile(), PG_BINARY, and read.

Referenced by basic_archive_file_internal().

Variable Documentation

◆ archive_directory

char* archive_directory = NULL
static

Definition at line 48 of file basic_archive.c.

Referenced by _PG_init(), basic_archive_configured(), and basic_archive_file_internal().

◆ basic_archive_callbacks

const ArchiveModuleCallbacks basic_archive_callbacks
static
Initial value:
= {
.startup_cb = basic_archive_startup,
.check_configured_cb = basic_archive_configured,
.archive_file_cb = basic_archive_file,
.shutdown_cb = basic_archive_shutdown
}
static void basic_archive_startup(ArchiveModuleState *state)
static bool basic_archive_file(ArchiveModuleState *state, const char *file, const char *path)
static bool basic_archive_configured(ArchiveModuleState *state)
static void basic_archive_shutdown(ArchiveModuleState *state)

Definition at line 58 of file basic_archive.c.

Referenced by _PG_archive_module_init().

◆ PG_MODULE_MAGIC

PG_MODULE_MAGIC

Definition at line 41 of file basic_archive.c.