PostgreSQL Source Code git master
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
43static void copy_file_link(const char *src, const char *dest,
44 pg_checksum_context *checksum_ctx);
45
46/*
47 * Copy a regular file, optionally computing a checksum, and emitting
48 * appropriate debug messages. But if we're in dry-run mode, then just emit
49 * the messages and don't copy anything.
50 */
51void
52copy_file(const char *src, const char *dst,
53 pg_checksum_context *checksum_ctx,
54 CopyMethod copy_method, bool dry_run)
55{
56 char *strategy_name = NULL;
57 void (*strategy_implementation) (const char *, const char *,
58 pg_checksum_context *checksum_ctx) = NULL;
59
60 /*
61 * In dry-run mode, we don't actually copy anything, nor do we read any
62 * data from the source file, but we do verify that we can open it.
63 */
64 if (dry_run)
65 {
66 int fd;
67
68 if ((fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
69 pg_fatal("could not open file \"%s\": %m", src);
70 if (close(fd) < 0)
71 pg_fatal("could not close file \"%s\": %m", src);
72 }
73
74#ifdef WIN32
75
76 /*
77 * We have no specific switch to enable CopyFile on Windows, because it's
78 * supported (as far as we know) on all Windows machines. So,
79 * automatically enable it unless some other strategy was selected.
80 */
81 if (copy_method == COPY_METHOD_COPY)
82 copy_method = COPY_METHOD_COPYFILE;
83#endif
84
85 /* Determine the name of the copy strategy for use in log messages. */
86 switch (copy_method)
87 {
89 strategy_name = "clone";
90 strategy_implementation = copy_file_clone;
91 break;
93 /* leave NULL for simple block-by-block copy */
94 strategy_implementation = copy_file_blocks;
95 break;
97 strategy_name = "copy_file_range";
98 strategy_implementation = copy_file_by_range;
99 break;
100#ifdef WIN32
101 case COPY_METHOD_COPYFILE:
102 strategy_name = "CopyFile";
103 strategy_implementation = copy_file_copyfile;
104 break;
105#endif
106 case COPY_METHOD_LINK:
107 strategy_name = "link";
108 strategy_implementation = copy_file_link;
109 break;
110 }
111
112 if (dry_run)
113 {
114 if (strategy_name)
115 pg_log_debug("would copy \"%s\" to \"%s\" using strategy %s",
116 src, dst, strategy_name);
117 else
118 pg_log_debug("would copy \"%s\" to \"%s\"",
119 src, dst);
120 }
121 else
122 {
123 if (strategy_name)
124 pg_log_debug("copying \"%s\" to \"%s\" using strategy %s",
125 src, dst, strategy_name);
126 else if (checksum_ctx->type == CHECKSUM_TYPE_NONE)
127 pg_log_debug("copying \"%s\" to \"%s\"",
128 src, dst);
129 else
130 pg_log_debug("copying \"%s\" to \"%s\" and checksumming with %s",
131 src, dst, pg_checksum_type_name(checksum_ctx->type));
132
133 strategy_implementation(src, dst, checksum_ctx);
134 }
135}
136
137/*
138 * Calculate checksum for the src file.
139 */
140static void
141checksum_file(const char *src, pg_checksum_context *checksum_ctx)
142{
143 int src_fd;
144 uint8 *buffer;
145 const int buffer_size = 50 * BLCKSZ;
146 ssize_t rb;
147
148 /* bail out if no checksum needed */
149 if (checksum_ctx->type == CHECKSUM_TYPE_NONE)
150 return;
151
152 if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
153 pg_fatal("could not open file \"%s\": %m", src);
154
155 buffer = pg_malloc(buffer_size);
156
157 while ((rb = read(src_fd, buffer, buffer_size)) > 0)
158 {
159 if (pg_checksum_update(checksum_ctx, buffer, rb) < 0)
160 pg_fatal("could not update checksum of file \"%s\"", src);
161 }
162
163 if (rb < 0)
164 pg_fatal("could not read file \"%s\": %m", src);
165
166 pg_free(buffer);
167 close(src_fd);
168}
169
170/*
171 * Copy a file block by block, and optionally compute a checksum as we go.
172 */
173static void
174copy_file_blocks(const char *src, const char *dst,
175 pg_checksum_context *checksum_ctx)
176{
177 int src_fd;
178 int dest_fd;
179 uint8 *buffer;
180 const int buffer_size = 50 * BLCKSZ;
181 ssize_t rb;
182 unsigned offset = 0;
183
184 if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
185 pg_fatal("could not open file \"%s\": %m", src);
186
187 if ((dest_fd = open(dst, O_WRONLY | O_CREAT | O_EXCL | PG_BINARY,
189 pg_fatal("could not open file \"%s\": %m", dst);
190
191 buffer = pg_malloc(buffer_size);
192
193 while ((rb = read(src_fd, buffer, buffer_size)) > 0)
194 {
195 ssize_t wb;
196
197 if ((wb = write(dest_fd, buffer, rb)) != rb)
198 {
199 if (wb < 0)
200 pg_fatal("could not write to file \"%s\": %m", dst);
201 else
202 pg_fatal("could not write to file \"%s\", offset %u: wrote %d of %d",
203 dst, offset, (int) wb, (int) rb);
204 }
205
206 if (pg_checksum_update(checksum_ctx, buffer, rb) < 0)
207 pg_fatal("could not update checksum of file \"%s\"", dst);
208
209 offset += rb;
210 }
211
212 if (rb < 0)
213 pg_fatal("could not read from file \"%s\": %m", dst);
214
215 pg_free(buffer);
216 close(src_fd);
217 close(dest_fd);
218}
219
220/*
221 * copy_file_clone
222 * Clones/reflinks a file from src to dest.
223 *
224 * If needed, also reads the file and calculates the checksum.
225 */
226static void
227copy_file_clone(const char *src, const char *dest,
228 pg_checksum_context *checksum_ctx)
229{
230#if defined(HAVE_COPYFILE) && defined(COPYFILE_CLONE_FORCE)
231 if (copyfile(src, dest, NULL, COPYFILE_CLONE_FORCE) < 0)
232 pg_fatal("error while cloning file \"%s\" to \"%s\": %m", src, dest);
233#elif defined(__linux__) && defined(FICLONE)
234 {
235 int src_fd;
236 int dest_fd;
237
238 if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
239 pg_fatal("could not open file \"%s\": %m", src);
240
241 if ((dest_fd = open(dest, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
243 pg_fatal("could not create file \"%s\": %m", dest);
244
245 if (ioctl(dest_fd, FICLONE, src_fd) < 0)
246 {
247 int save_errno = errno;
248
249 unlink(dest);
250
251 pg_fatal("error while cloning file \"%s\" to \"%s\": %s",
252 src, dest, strerror(save_errno));
253 }
254
255 close(src_fd);
256 close(dest_fd);
257 }
258#else
259 pg_fatal("file cloning not supported on this platform");
260#endif
261
262 /* if needed, calculate checksum of the file */
263 checksum_file(src, checksum_ctx);
264}
265
266/*
267 * copy_file_by_range
268 * Copies a file from src to dest using copy_file_range system call.
269 *
270 * If needed, also reads the file and calculates the checksum.
271 */
272static void
273copy_file_by_range(const char *src, const char *dest,
274 pg_checksum_context *checksum_ctx)
275{
276#if defined(HAVE_COPY_FILE_RANGE)
277 int src_fd;
278 int dest_fd;
279 ssize_t nbytes;
280
281 if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
282 pg_fatal("could not open file \"%s\": %m", src);
283
284 if ((dest_fd = open(dest, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
286 pg_fatal("could not create file \"%s\": %m", dest);
287
288 do
289 {
290 nbytes = copy_file_range(src_fd, NULL, dest_fd, NULL, SSIZE_MAX, 0);
291 if (nbytes < 0)
292 pg_fatal("error while copying file range from \"%s\" to \"%s\": %m",
293 src, dest);
294 } while (nbytes > 0);
295
296 close(src_fd);
297 close(dest_fd);
298#else
299 pg_fatal("copy_file_range not supported on this platform");
300#endif
301
302 /* if needed, calculate checksum of the file */
303 checksum_file(src, checksum_ctx);
304}
305
306#ifdef WIN32
307static void
308copy_file_copyfile(const char *src, const char *dst,
309 pg_checksum_context *checksum_ctx)
310{
311 if (CopyFile(src, dst, true) == 0)
312 {
313 _dosmaperr(GetLastError());
314 pg_fatal("could not copy file \"%s\" to \"%s\": %m", src, dst);
315 }
316
317 /* if needed, calculate checksum of the file */
318 checksum_file(src, checksum_ctx);
319}
320#endif /* WIN32 */
321
322/*
323 * copy_file_link
324 * Hard-links a file from src to dest.
325 *
326 * If needed, also reads the file and calculates the checksum.
327 */
328static void
329copy_file_link(const char *src, const char *dest,
330 pg_checksum_context *checksum_ctx)
331{
332 if (link(src, dest) < 0)
333 pg_fatal("could not create link from \"%s\" to \"%s\": %m",
334 src, dest);
335
336 /* if needed, calculate checksum of the file */
337 checksum_file(src, checksum_ctx);
338}
uint8_t uint8
Definition: c.h:500
#define PG_BINARY
Definition: c.h:1244
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:174
static void copy_file_clone(const char *src, const char *dest, pg_checksum_context *checksum_ctx)
Definition: copy_file.c:227
void copy_file(const char *src, const char *dst, pg_checksum_context *checksum_ctx, CopyMethod copy_method, bool dry_run)
Definition: copy_file.c:52
static void checksum_file(const char *src, pg_checksum_context *checksum_ctx)
Definition: copy_file.c:141
static void copy_file_link(const char *src, const char *dest, pg_checksum_context *checksum_ctx)
Definition: copy_file.c:329
static void copy_file_by_range(const char *src, const char *dest, pg_checksum_context *checksum_ctx)
Definition: copy_file.c:273
CopyMethod
Definition: copy_file.h:20
@ COPY_METHOD_CLONE
Definition: copy_file.h:21
@ COPY_METHOD_LINK
Definition: copy_file.h:27
@ COPY_METHOD_COPY
Definition: copy_file.h:22
@ COPY_METHOD_COPY_FILE_RANGE
Definition: copy_file.h:23
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:252
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