PostgreSQL Source Code git master
basic_archive.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * basic_archive.c
4 *
5 * This file demonstrates a basic archive library implementation that is
6 * roughly equivalent to the following shell command:
7 *
8 * test ! -f /path/to/dest && cp /path/to/src /path/to/dest
9 *
10 * One notable difference between this module and the shell command above
11 * is that this module first copies the file to a temporary destination,
12 * syncs it to disk, and then durably moves it to the final destination.
13 *
14 * Another notable difference is that if /path/to/dest already exists
15 * but has contents identical to /path/to/src, archiving will succeed,
16 * whereas the command shown above would fail. This prevents problems if
17 * a file is successfully archived and then the system crashes before
18 * a durable record of the success has been made.
19 *
20 * Copyright (c) 2022-2025, PostgreSQL Global Development Group
21 *
22 * IDENTIFICATION
23 * contrib/basic_archive/basic_archive.c
24 *
25 *-------------------------------------------------------------------------
26 */
27#include "postgres.h"
28
29#include <sys/stat.h>
30#include <sys/time.h>
31#include <unistd.h>
32
34#include "common/int.h"
35#include "miscadmin.h"
36#include "storage/copydir.h"
37#include "storage/fd.h"
38#include "utils/guc.h"
39
41
42static char *archive_directory = NULL;
43
45static bool basic_archive_file(ArchiveModuleState *state, const char *file, const char *path);
46static bool check_archive_directory(char **newval, void **extra, GucSource source);
47static bool compare_files(const char *file1, const char *file2);
48
50 .startup_cb = NULL,
51 .check_configured_cb = basic_archive_configured,
52 .archive_file_cb = basic_archive_file,
53 .shutdown_cb = NULL
54};
55
56/*
57 * _PG_init
58 *
59 * Defines the module's GUC.
60 */
61void
63{
64 DefineCustomStringVariable("basic_archive.archive_directory",
65 gettext_noop("Archive file destination directory."),
66 NULL,
68 "",
70 0,
71 check_archive_directory, NULL, NULL);
72
73 MarkGUCPrefixReserved("basic_archive");
74}
75
76/*
77 * _PG_archive_module_init
78 *
79 * Returns the module's archiving callbacks.
80 */
83{
85}
86
87/*
88 * check_archive_directory
89 *
90 * Checks that the provided archive directory exists.
91 */
92static bool
94{
95 struct stat st;
96
97 /*
98 * The default value is an empty string, so we have to accept that value.
99 * Our check_configured callback also checks for this and prevents
100 * archiving from proceeding if it is still empty.
101 */
102 if (*newval == NULL || *newval[0] == '\0')
103 return true;
104
105 /*
106 * Make sure the file paths won't be too long. The docs indicate that the
107 * file names to be archived can be up to 64 characters long.
108 */
109 if (strlen(*newval) + 64 + 2 >= MAXPGPATH)
110 {
111 GUC_check_errdetail("Archive directory too long.");
112 return false;
113 }
114
115 /*
116 * Do a basic sanity check that the specified archive directory exists. It
117 * could be removed at some point in the future, so we still need to be
118 * prepared for it not to exist in the actual archiving logic.
119 */
120 if (stat(*newval, &st) != 0 || !S_ISDIR(st.st_mode))
121 {
122 GUC_check_errdetail("Specified archive directory does not exist.");
123 return false;
124 }
125
126 return true;
127}
128
129/*
130 * basic_archive_configured
131 *
132 * Checks that archive_directory is not blank.
133 */
134static bool
136{
137 if (archive_directory != NULL && archive_directory[0] != '\0')
138 return true;
139
140 arch_module_check_errdetail("%s is not set.",
141 "basic_archive.archive_directory");
142 return false;
143}
144
145/*
146 * basic_archive_file
147 *
148 * Archives one file.
149 */
150static bool
151basic_archive_file(ArchiveModuleState *state, const char *file, const char *path)
152{
153 char destination[MAXPGPATH];
154 char temp[MAXPGPATH + 256];
155 struct stat st;
156 struct timeval tv;
157 uint64 epoch; /* milliseconds */
158
160 (errmsg("archiving \"%s\" via basic_archive", file)));
161
162 snprintf(destination, MAXPGPATH, "%s/%s", archive_directory, file);
163
164 /*
165 * First, check if the file has already been archived. If it already
166 * exists and has the same contents as the file we're trying to archive,
167 * we can return success (after ensuring the file is persisted to disk).
168 * This scenario is possible if the server crashed after archiving the
169 * file but before renaming its .ready file to .done.
170 *
171 * If the archive file already exists but has different contents,
172 * something might be wrong, so we just fail.
173 */
174 if (stat(destination, &st) == 0)
175 {
176 if (compare_files(path, destination))
177 {
179 (errmsg("archive file \"%s\" already exists with identical contents",
180 destination)));
181
182 fsync_fname(destination, false);
184
185 return true;
186 }
187
189 (errmsg("archive file \"%s\" already exists", destination)));
190 }
191 else if (errno != ENOENT)
194 errmsg("could not stat file \"%s\": %m", destination)));
195
196 /*
197 * Pick a sufficiently unique name for the temporary file so that a
198 * collision is unlikely. This helps avoid problems in case a temporary
199 * file was left around after a crash or another server happens to be
200 * archiving to the same directory.
201 */
202 gettimeofday(&tv, NULL);
203 if (pg_mul_u64_overflow((uint64) 1000, (uint64) tv.tv_sec, &epoch) ||
204 pg_add_u64_overflow(epoch, (uint64) (tv.tv_usec / 1000), &epoch))
205 elog(ERROR, "could not generate temporary file name for archiving");
206
207 snprintf(temp, sizeof(temp), "%s/%s.%s.%d." UINT64_FORMAT,
208 archive_directory, "archtemp", file, MyProcPid, epoch);
209
210 /*
211 * Copy the file to its temporary destination. Note that this will fail
212 * if temp already exists.
213 */
214 copy_file(path, temp);
215
216 /*
217 * Sync the temporary file to disk and move it to its final destination.
218 * Note that this will overwrite any existing file, but this is only
219 * possible if someone else created the file since the stat() above.
220 */
221 (void) durable_rename(temp, destination, ERROR);
222
224 (errmsg("archived \"%s\" via basic_archive", file)));
225
226 return true;
227}
228
229/*
230 * compare_files
231 *
232 * Returns whether the contents of the files are the same.
233 */
234static bool
235compare_files(const char *file1, const char *file2)
236{
237#define CMP_BUF_SIZE (4096)
238 char buf1[CMP_BUF_SIZE];
239 char buf2[CMP_BUF_SIZE];
240 int fd1;
241 int fd2;
242 bool ret = true;
243
244 fd1 = OpenTransientFile(file1, O_RDONLY | PG_BINARY);
245 if (fd1 < 0)
248 errmsg("could not open file \"%s\": %m", file1)));
249
250 fd2 = OpenTransientFile(file2, O_RDONLY | PG_BINARY);
251 if (fd2 < 0)
254 errmsg("could not open file \"%s\": %m", file2)));
255
256 for (;;)
257 {
258 int nbytes = 0;
259 int buf1_len = 0;
260 int buf2_len = 0;
261
262 while (buf1_len < CMP_BUF_SIZE)
263 {
264 nbytes = read(fd1, buf1 + buf1_len, CMP_BUF_SIZE - buf1_len);
265 if (nbytes < 0)
268 errmsg("could not read file \"%s\": %m", file1)));
269 else if (nbytes == 0)
270 break;
271
272 buf1_len += nbytes;
273 }
274
275 while (buf2_len < CMP_BUF_SIZE)
276 {
277 nbytes = read(fd2, buf2 + buf2_len, CMP_BUF_SIZE - buf2_len);
278 if (nbytes < 0)
281 errmsg("could not read file \"%s\": %m", file2)));
282 else if (nbytes == 0)
283 break;
284
285 buf2_len += nbytes;
286 }
287
288 if (buf1_len != buf2_len || memcmp(buf1, buf2, buf1_len) != 0)
289 {
290 ret = false;
291 break;
292 }
293 else if (buf1_len == 0)
294 break;
295 }
296
297 if (CloseTransientFile(fd1) != 0)
300 errmsg("could not close file \"%s\": %m", file1)));
301
302 if (CloseTransientFile(fd2) != 0)
305 errmsg("could not close file \"%s\": %m", file2)));
306
307 return ret;
308}
#define arch_module_check_errdetail
static char * archive_directory
Definition: basic_archive.c:42
void _PG_init(void)
Definition: basic_archive.c:62
PG_MODULE_MAGIC
Definition: basic_archive.c:40
static bool basic_archive_file(ArchiveModuleState *state, const char *file, const char *path)
#define CMP_BUF_SIZE
static const ArchiveModuleCallbacks basic_archive_callbacks
Definition: basic_archive.c:49
static bool compare_files(const char *file1, const char *file2)
static bool basic_archive_configured(ArchiveModuleState *state)
static bool check_archive_directory(char **newval, void **extra, GucSource source)
Definition: basic_archive.c:93
const ArchiveModuleCallbacks * _PG_archive_module_init(void)
Definition: basic_archive.c:82
#define gettext_noop(x)
Definition: c.h:1153
#define PG_BINARY
Definition: c.h:1230
#define UINT64_FORMAT
Definition: c.h:507
uint64_t uint64
Definition: c.h:489
void copy_file(const char *fromfile, const char *tofile)
Definition: copydir.c:117
int errcode_for_file_access(void)
Definition: elog.c:876
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#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:781
int CloseTransientFile(int fd)
Definition: fd.c:2831
void fsync_fname(const char *fname, bool isdir)
Definition: fd.c:755
int OpenTransientFile(const char *fileName, int fileFlags)
Definition: fd.c:2655
int MyProcPid
Definition: globals.c:46
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:5218
#define newval
void MarkGUCPrefixReserved(const char *className)
Definition: guc.c:5279
#define GUC_check_errdetail
Definition: guc.h:476
GucSource
Definition: guc.h:108
@ PGC_SIGHUP
Definition: guc.h:71
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 read(a, b, c)
Definition: win32.h:13
#define MAXPGPATH
static rewind_source * source
Definition: pg_rewind.c:89
#define snprintf
Definition: port.h:238
ArchiveStartupCB startup_cb
unsigned short st_mode
Definition: win32_port.h:258
Definition: regguts.h:323
#define stat
Definition: win32_port.h:274
#define S_ISDIR(m)
Definition: win32_port.h:315
static const unsigned __int64 epoch
int gettimeofday(struct timeval *tp, void *tzp)