PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
copy_file.c
Go to the documentation of this file.
1/*
2 * Copy entire files.
3 *
4 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
5 * Portions Copyright (c) 1994, Regents of the University of California
6 *
7 * src/bin/pg_combinebackup/copy_file.h
8 *
9 *-------------------------------------------------------------------------
10 */
11#include "postgres_fe.h"
12
13#ifdef HAVE_COPYFILE_H
14#include <copyfile.h>
15#endif
16#ifdef __linux__
17#include <sys/ioctl.h>
18#include <linux/fs.h>
19#endif
20#include <fcntl.h>
21#include <limits.h>
22#include <sys/stat.h>
23#include <unistd.h>
24
25#include "common/file_perm.h"
26#include "common/logging.h"
27#include "copy_file.h"
28
29static void copy_file_blocks(const char *src, const char *dst,
30 pg_checksum_context *checksum_ctx);
31
32static void copy_file_clone(const char *src, const char *dest,
33 pg_checksum_context *checksum_ctx);
34
35static void copy_file_by_range(const char *src, const char *dest,
36 pg_checksum_context *checksum_ctx);
37
38#ifdef WIN32
39static void copy_file_copyfile(const char *src, const char *dst,
40 pg_checksum_context *checksum_ctx);
41#endif
42
43/*
44 * Copy a regular file, optionally computing a checksum, and emitting
45 * appropriate debug messages. But if we're in dry-run mode, then just emit
46 * the messages and don't copy anything.
47 */
48void
49copy_file(const char *src, const char *dst,
50 pg_checksum_context *checksum_ctx,
51 CopyMethod copy_method, bool dry_run)
52{
53 char *strategy_name = NULL;
54 void (*strategy_implementation) (const char *, const char *,
55 pg_checksum_context *checksum_ctx) = NULL;
56
57 /*
58 * In dry-run mode, we don't actually copy anything, nor do we read any
59 * data from the source file, but we do verify that we can open it.
60 */
61 if (dry_run)
62 {
63 int fd;
64
65 if ((fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
66 pg_fatal("could not open file \"%s\": %m", src);
67 if (close(fd) < 0)
68 pg_fatal("could not close file \"%s\": %m", src);
69 }
70
71#ifdef WIN32
72 copy_method = COPY_METHOD_COPYFILE;
73#endif
74
75 /* Determine the name of the copy strategy for use in log messages. */
76 switch (copy_method)
77 {
79 strategy_name = "clone";
80 strategy_implementation = copy_file_clone;
81 break;
83 /* leave NULL for simple block-by-block copy */
84 strategy_implementation = copy_file_blocks;
85 break;
87 strategy_name = "copy_file_range";
88 strategy_implementation = copy_file_by_range;
89 break;
90#ifdef WIN32
91 case COPY_METHOD_COPYFILE:
92 strategy_name = "CopyFile";
93 strategy_implementation = copy_file_copyfile;
94 break;
95#endif
96 }
97
98 if (dry_run)
99 {
100 if (strategy_name)
101 pg_log_debug("would copy \"%s\" to \"%s\" using strategy %s",
102 src, dst, strategy_name);
103 else
104 pg_log_debug("would copy \"%s\" to \"%s\"",
105 src, dst);
106 }
107 else
108 {
109 if (strategy_name)
110 pg_log_debug("copying \"%s\" to \"%s\" using strategy %s",
111 src, dst, strategy_name);
112 else if (checksum_ctx->type == CHECKSUM_TYPE_NONE)
113 pg_log_debug("copying \"%s\" to \"%s\"",
114 src, dst);
115 else
116 pg_log_debug("copying \"%s\" to \"%s\" and checksumming with %s",
117 src, dst, pg_checksum_type_name(checksum_ctx->type));
118
119 strategy_implementation(src, dst, checksum_ctx);
120 }
121}
122
123/*
124 * Calculate checksum for the src file.
125 */
126static void
127checksum_file(const char *src, pg_checksum_context *checksum_ctx)
128{
129 int src_fd;
130 uint8 *buffer;
131 const int buffer_size = 50 * BLCKSZ;
132 ssize_t rb;
133
134 /* bail out if no checksum needed */
135 if (checksum_ctx->type == CHECKSUM_TYPE_NONE)
136 return;
137
138 if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
139 pg_fatal("could not open file \"%s\": %m", src);
140
141 buffer = pg_malloc(buffer_size);
142
143 while ((rb = read(src_fd, buffer, buffer_size)) > 0)
144 {
145 if (pg_checksum_update(checksum_ctx, buffer, rb) < 0)
146 pg_fatal("could not update checksum of file \"%s\"", src);
147 }
148
149 if (rb < 0)
150 pg_fatal("could not read file \"%s\": %m", src);
151
152 pg_free(buffer);
153 close(src_fd);
154}
155
156/*
157 * Copy a file block by block, and optionally compute a checksum as we go.
158 */
159static void
160copy_file_blocks(const char *src, const char *dst,
161 pg_checksum_context *checksum_ctx)
162{
163 int src_fd;
164 int dest_fd;
165 uint8 *buffer;
166 const int buffer_size = 50 * BLCKSZ;
167 ssize_t rb;
168 unsigned offset = 0;
169
170 if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
171 pg_fatal("could not open file \"%s\": %m", src);
172
173 if ((dest_fd = open(dst, O_WRONLY | O_CREAT | O_EXCL | PG_BINARY,
175 pg_fatal("could not open file \"%s\": %m", dst);
176
177 buffer = pg_malloc(buffer_size);
178
179 while ((rb = read(src_fd, buffer, buffer_size)) > 0)
180 {
181 ssize_t wb;
182
183 if ((wb = write(dest_fd, buffer, rb)) != rb)
184 {
185 if (wb < 0)
186 pg_fatal("could not write to file \"%s\": %m", dst);
187 else
188 pg_fatal("could not write to file \"%s\", offset %u: wrote %d of %d",
189 dst, offset, (int) wb, (int) rb);
190 }
191
192 if (pg_checksum_update(checksum_ctx, buffer, rb) < 0)
193 pg_fatal("could not update checksum of file \"%s\"", dst);
194
195 offset += rb;
196 }
197
198 if (rb < 0)
199 pg_fatal("could not read from file \"%s\": %m", dst);
200
201 pg_free(buffer);
202 close(src_fd);
203 close(dest_fd);
204}
205
206/*
207 * copy_file_clone
208 * Clones/reflinks a file from src to dest.
209 *
210 * If needed, also reads the file and calculates the checksum.
211 */
212static void
213copy_file_clone(const char *src, const char *dest,
214 pg_checksum_context *checksum_ctx)
215{
216#if defined(HAVE_COPYFILE) && defined(COPYFILE_CLONE_FORCE)
217 if (copyfile(src, dest, NULL, COPYFILE_CLONE_FORCE) < 0)
218 pg_fatal("error while cloning file \"%s\" to \"%s\": %m", src, dest);
219#elif defined(__linux__) && defined(FICLONE)
220 {
221 int src_fd;
222 int dest_fd;
223
224 if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
225 pg_fatal("could not open file \"%s\": %m", src);
226
227 if ((dest_fd = open(dest, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
229 pg_fatal("could not create file \"%s\": %m", dest);
230
231 if (ioctl(dest_fd, FICLONE, src_fd) < 0)
232 {
233 int save_errno = errno;
234
235 unlink(dest);
236
237 pg_fatal("error while cloning file \"%s\" to \"%s\": %s",
238 src, dest, strerror(save_errno));
239 }
240
241 close(src_fd);
242 close(dest_fd);
243 }
244#else
245 pg_fatal("file cloning not supported on this platform");
246#endif
247
248 /* if needed, calculate checksum of the file */
249 checksum_file(src, checksum_ctx);
250}
251
252/*
253 * copy_file_by_range
254 * Copies a file from src to dest using copy_file_range system call.
255 *
256 * If needed, also reads the file and calculates the checksum.
257 */
258static void
259copy_file_by_range(const char *src, const char *dest,
260 pg_checksum_context *checksum_ctx)
261{
262#if defined(HAVE_COPY_FILE_RANGE)
263 int src_fd;
264 int dest_fd;
265 ssize_t nbytes;
266
267 if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
268 pg_fatal("could not open file \"%s\": %m", src);
269
270 if ((dest_fd = open(dest, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
272 pg_fatal("could not create file \"%s\": %m", dest);
273
274 do
275 {
276 nbytes = copy_file_range(src_fd, NULL, dest_fd, NULL, SSIZE_MAX, 0);
277 if (nbytes < 0)
278 pg_fatal("error while copying file range from \"%s\" to \"%s\": %m",
279 src, dest);
280 } while (nbytes > 0);
281
282 close(src_fd);
283 close(dest_fd);
284#else
285 pg_fatal("copy_file_range not supported on this platform");
286#endif
287
288 /* if needed, calculate checksum of the file */
289 checksum_file(src, checksum_ctx);
290}
291
292#ifdef WIN32
293static void
294copy_file_copyfile(const char *src, const char *dst,
295 pg_checksum_context *checksum_ctx)
296{
297 if (CopyFile(src, dst, true) == 0)
298 {
299 _dosmaperr(GetLastError());
300 pg_fatal("could not copy file \"%s\" to \"%s\": %m", src, dst);
301 }
302
303 /* if needed, calculate checksum of the file */
304 checksum_file(src, checksum_ctx);
305}
306#endif /* WIN32 */
uint8_t uint8
Definition: c.h:486
#define PG_BINARY
Definition: c.h:1230
char * pg_checksum_type_name(pg_checksum_type type)
int pg_checksum_update(pg_checksum_context *context, const uint8 *input, size_t len)
@ CHECKSUM_TYPE_NONE
static void copy_file_blocks(const char *src, const char *dst, pg_checksum_context *checksum_ctx)
Definition: copy_file.c:160
static void copy_file_clone(const char *src, const char *dest, pg_checksum_context *checksum_ctx)
Definition: copy_file.c:213
void copy_file(const char *src, const char *dst, pg_checksum_context *checksum_ctx, CopyMethod copy_method, bool dry_run)
Definition: copy_file.c:49
static void checksum_file(const char *src, pg_checksum_context *checksum_ctx)
Definition: copy_file.c:127
static void copy_file_by_range(const char *src, const char *dest, pg_checksum_context *checksum_ctx)
Definition: copy_file.c:259
CopyMethod
Definition: copy_file.h:21
@ COPY_METHOD_CLONE
Definition: copy_file.h:22
@ COPY_METHOD_COPY
Definition: copy_file.h:23
@ COPY_METHOD_COPY_FILE_RANGE
Definition: copy_file.h:24
void * pg_malloc(size_t size)
Definition: fe_memutils.c:47
void pg_free(void *ptr)
Definition: fe_memutils.c:105
int pg_file_create_mode
Definition: file_perm.c:19
#define close(a)
Definition: win32.h:12
#define write(a, b, c)
Definition: win32.h:14
#define read(a, b, c)
Definition: win32.h:13
#define pg_log_debug(...)
Definition: logging.h:133
#define pg_fatal(...)
static bool dry_run
#define strerror
Definition: port.h:251
static int fd(const char *x, int i)
Definition: preproc-init.c:105
pg_checksum_type type
void _dosmaperr(unsigned long)
Definition: win32error.c:177