PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
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 dependency graph for basic_archive.c:

Go to the source code of this file.

Macros

#define CMP_BUF_SIZE   (4096)
 

Functions

 PG_MODULE_MAGIC_EXT (.name="basic_archive",.version=PG_VERSION)
 
static bool basic_archive_configured (ArchiveModuleState *state)
 
static bool basic_archive_file (ArchiveModuleState *state, 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)
 
void _PG_init (void)
 
const ArchiveModuleCallbacks_PG_archive_module_init (void)
 

Variables

static char * archive_directory = NULL
 
static const ArchiveModuleCallbacks basic_archive_callbacks
 

Macro Definition Documentation

◆ CMP_BUF_SIZE

#define CMP_BUF_SIZE   (4096)

Function Documentation

◆ _PG_archive_module_init()

const ArchiveModuleCallbacks * _PG_archive_module_init ( void  )

Definition at line 85 of file basic_archive.c.

86{
88}
static const ArchiveModuleCallbacks basic_archive_callbacks
Definition: basic_archive.c:52

References basic_archive_callbacks.

◆ _PG_init()

void _PG_init ( void  )

Definition at line 65 of file basic_archive.c.

66{
67 DefineCustomStringVariable("basic_archive.archive_directory",
68 gettext_noop("Archive file destination directory."),
69 NULL,
71 "",
73 0,
74 check_archive_directory, NULL, NULL);
75
76 MarkGUCPrefixReserved("basic_archive");
77}
static char * archive_directory
Definition: basic_archive.c:45
static bool check_archive_directory(char **newval, void **extra, GucSource source)
Definition: basic_archive.c:96
#define gettext_noop(x)
Definition: c.h:1167
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:5219
void MarkGUCPrefixReserved(const char *className)
Definition: guc.c:5280
@ PGC_SIGHUP
Definition: guc.h:75

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 138 of file basic_archive.c.

139{
140 if (archive_directory != NULL && archive_directory[0] != '\0')
141 return true;
142
143 arch_module_check_errdetail("%s is not set.",
144 "basic_archive.archive_directory");
145 return false;
146}
#define arch_module_check_errdetail

References arch_module_check_errdetail, and archive_directory.

◆ basic_archive_file()

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

Definition at line 154 of file basic_archive.c.

155{
156 char destination[MAXPGPATH];
157 char temp[MAXPGPATH + 256];
158 struct stat st;
159 struct timeval tv;
160 uint64 epoch; /* milliseconds */
161
163 (errmsg("archiving \"%s\" via basic_archive", file)));
164
165 snprintf(destination, MAXPGPATH, "%s/%s", archive_directory, file);
166
167 /*
168 * First, check if the file has already been archived. If it already
169 * exists and has the same contents as the file we're trying to archive,
170 * we can return success (after ensuring the file is persisted to disk).
171 * This scenario is possible if the server crashed after archiving the
172 * file but before renaming its .ready file to .done.
173 *
174 * If the archive file already exists but has different contents,
175 * something might be wrong, so we just fail.
176 */
177 if (stat(destination, &st) == 0)
178 {
179 if (compare_files(path, destination))
180 {
182 (errmsg("archive file \"%s\" already exists with identical contents",
183 destination)));
184
185 fsync_fname(destination, false);
187
188 return true;
189 }
190
192 (errmsg("archive file \"%s\" already exists", destination)));
193 }
194 else if (errno != ENOENT)
197 errmsg("could not stat file \"%s\": %m", destination)));
198
199 /*
200 * Pick a sufficiently unique name for the temporary file so that a
201 * collision is unlikely. This helps avoid problems in case a temporary
202 * file was left around after a crash or another server happens to be
203 * archiving to the same directory.
204 */
205 gettimeofday(&tv, NULL);
206 if (pg_mul_u64_overflow((uint64) 1000, (uint64) tv.tv_sec, &epoch) ||
207 pg_add_u64_overflow(epoch, (uint64) (tv.tv_usec / 1000), &epoch))
208 elog(ERROR, "could not generate temporary file name for archiving");
209
210 snprintf(temp, sizeof(temp), "%s/%s.%s.%d." UINT64_FORMAT,
211 archive_directory, "archtemp", file, MyProcPid, epoch);
212
213 /*
214 * Copy the file to its temporary destination. Note that this will fail
215 * if temp already exists.
216 */
217 copy_file(path, temp);
218
219 /*
220 * Sync the temporary file to disk and move it to its final destination.
221 * Note that this will overwrite any existing file, but this is only
222 * possible if someone else created the file since the stat() above.
223 */
224 (void) durable_rename(temp, destination, ERROR);
225
227 (errmsg("archived \"%s\" via basic_archive", file)));
228
229 return true;
230}
static bool compare_files(const char *file1, const char *file2)
#define UINT64_FORMAT
Definition: c.h:521
uint64_t uint64
Definition: c.h:503
void copy_file(const char *fromfile, const char *tofile)
Definition: copydir.c:133
int errcode_for_file_access(void)
Definition: elog.c:877
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define DEBUG3
Definition: elog.h:28
#define DEBUG1
Definition: elog.h:30
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define ereport(elevel,...)
Definition: elog.h:149
int durable_rename(const char *oldfile, const char *newfile, int elevel)
Definition: fd.c:782
void fsync_fname(const char *fname, bool isdir)
Definition: fd.c:756
int MyProcPid
Definition: globals.c:48
static bool pg_add_u64_overflow(uint64 a, uint64 b, uint64 *result)
Definition: int.h:514
static bool pg_mul_u64_overflow(uint64 a, uint64 b, uint64 *result)
Definition: int.h:548
#define MAXPGPATH
#define snprintf
Definition: port.h:239
#define stat
Definition: win32_port.h:274
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.

◆ check_archive_directory()

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

Definition at line 96 of file basic_archive.c.

97{
98 struct stat st;
99
100 /*
101 * The default value is an empty string, so we have to accept that value.
102 * Our check_configured callback also checks for this and prevents
103 * archiving from proceeding if it is still empty.
104 */
105 if (*newval == NULL || *newval[0] == '\0')
106 return true;
107
108 /*
109 * Make sure the file paths won't be too long. The docs indicate that the
110 * file names to be archived can be up to 64 characters long.
111 */
112 if (strlen(*newval) + 64 + 2 >= MAXPGPATH)
113 {
114 GUC_check_errdetail("Archive directory too long.");
115 return false;
116 }
117
118 /*
119 * Do a basic sanity check that the specified archive directory exists. It
120 * could be removed at some point in the future, so we still need to be
121 * prepared for it not to exist in the actual archiving logic.
122 */
123 if (stat(*newval, &st) != 0 || !S_ISDIR(st.st_mode))
124 {
125 GUC_check_errdetail("Specified archive directory does not exist.");
126 return false;
127 }
128
129 return true;
130}
#define newval
#define GUC_check_errdetail
Definition: guc.h:481
#define S_ISDIR(m)
Definition: win32_port.h:315

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 238 of file basic_archive.c.

239{
240#define CMP_BUF_SIZE (4096)
241 char buf1[CMP_BUF_SIZE];
242 char buf2[CMP_BUF_SIZE];
243 int fd1;
244 int fd2;
245 bool ret = true;
246
247 fd1 = OpenTransientFile(file1, O_RDONLY | PG_BINARY);
248 if (fd1 < 0)
251 errmsg("could not open file \"%s\": %m", file1)));
252
253 fd2 = OpenTransientFile(file2, O_RDONLY | PG_BINARY);
254 if (fd2 < 0)
257 errmsg("could not open file \"%s\": %m", file2)));
258
259 for (;;)
260 {
261 int nbytes = 0;
262 int buf1_len = 0;
263 int buf2_len = 0;
264
265 while (buf1_len < CMP_BUF_SIZE)
266 {
267 nbytes = read(fd1, buf1 + buf1_len, CMP_BUF_SIZE - buf1_len);
268 if (nbytes < 0)
271 errmsg("could not read file \"%s\": %m", file1)));
272 else if (nbytes == 0)
273 break;
274
275 buf1_len += nbytes;
276 }
277
278 while (buf2_len < CMP_BUF_SIZE)
279 {
280 nbytes = read(fd2, buf2 + buf2_len, CMP_BUF_SIZE - buf2_len);
281 if (nbytes < 0)
284 errmsg("could not read file \"%s\": %m", file2)));
285 else if (nbytes == 0)
286 break;
287
288 buf2_len += nbytes;
289 }
290
291 if (buf1_len != buf2_len || memcmp(buf1, buf2, buf1_len) != 0)
292 {
293 ret = false;
294 break;
295 }
296 else if (buf1_len == 0)
297 break;
298 }
299
300 if (CloseTransientFile(fd1) != 0)
303 errmsg("could not close file \"%s\": %m", file1)));
304
305 if (CloseTransientFile(fd2) != 0)
308 errmsg("could not close file \"%s\": %m", file2)));
309
310 return ret;
311}
#define CMP_BUF_SIZE
#define PG_BINARY
Definition: c.h:1244
int CloseTransientFile(int fd)
Definition: fd.c:2871
int OpenTransientFile(const char *fileName, int fileFlags)
Definition: fd.c:2694
#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().

◆ PG_MODULE_MAGIC_EXT()

PG_MODULE_MAGIC_EXT ( name = "basic_archive",
version = PG_VERSION 
)

Variable Documentation

◆ archive_directory

char* archive_directory = NULL
static

Definition at line 45 of file basic_archive.c.

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

◆ basic_archive_callbacks

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

Definition at line 52 of file basic_archive.c.

Referenced by _PG_archive_module_init().