Line data Source code
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 :
29 : static void copy_file_blocks(const char *src, const char *dst,
30 : pg_checksum_context *checksum_ctx);
31 :
32 : static void copy_file_clone(const char *src, const char *dest,
33 : pg_checksum_context *checksum_ctx);
34 :
35 : static void copy_file_by_range(const char *src, const char *dest,
36 : pg_checksum_context *checksum_ctx);
37 :
38 : #ifdef WIN32
39 : static 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 : */
48 : void
49 23926 : copy_file(const char *src, const char *dst,
50 : pg_checksum_context *checksum_ctx,
51 : CopyMethod copy_method, bool dry_run)
52 : {
53 23926 : char *strategy_name = NULL;
54 23926 : 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 23926 : if (dry_run)
62 : {
63 : int fd;
64 :
65 0 : if ((fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
66 0 : pg_fatal("could not open file \"%s\": %m", src);
67 0 : if (close(fd) < 0)
68 0 : 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 23926 : switch (copy_method)
77 : {
78 0 : case COPY_METHOD_CLONE:
79 0 : strategy_name = "clone";
80 0 : strategy_implementation = copy_file_clone;
81 0 : break;
82 23926 : case COPY_METHOD_COPY:
83 : /* leave NULL for simple block-by-block copy */
84 23926 : strategy_implementation = copy_file_blocks;
85 23926 : break;
86 0 : case COPY_METHOD_COPY_FILE_RANGE:
87 0 : strategy_name = "copy_file_range";
88 0 : strategy_implementation = copy_file_by_range;
89 0 : 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 23926 : if (dry_run)
99 : {
100 0 : if (strategy_name)
101 0 : pg_log_debug("would copy \"%s\" to \"%s\" using strategy %s",
102 : src, dst, strategy_name);
103 : else
104 0 : pg_log_debug("would copy \"%s\" to \"%s\"",
105 : src, dst);
106 : }
107 : else
108 : {
109 23926 : if (strategy_name)
110 0 : pg_log_debug("copying \"%s\" to \"%s\" using strategy %s",
111 : src, dst, strategy_name);
112 23926 : else if (checksum_ctx->type == CHECKSUM_TYPE_NONE)
113 21988 : pg_log_debug("copying \"%s\" to \"%s\"",
114 : src, dst);
115 : else
116 1938 : pg_log_debug("copying \"%s\" to \"%s\" and checksumming with %s",
117 : src, dst, pg_checksum_type_name(checksum_ctx->type));
118 :
119 23926 : strategy_implementation(src, dst, checksum_ctx);
120 : }
121 23926 : }
122 :
123 : /*
124 : * Calculate checksum for the src file.
125 : */
126 : static void
127 0 : checksum_file(const char *src, pg_checksum_context *checksum_ctx)
128 : {
129 : int src_fd;
130 : uint8 *buffer;
131 0 : const int buffer_size = 50 * BLCKSZ;
132 : ssize_t rb;
133 :
134 : /* bail out if no checksum needed */
135 0 : if (checksum_ctx->type == CHECKSUM_TYPE_NONE)
136 0 : return;
137 :
138 0 : if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
139 0 : pg_fatal("could not open file \"%s\": %m", src);
140 :
141 0 : buffer = pg_malloc(buffer_size);
142 :
143 0 : while ((rb = read(src_fd, buffer, buffer_size)) > 0)
144 : {
145 0 : if (pg_checksum_update(checksum_ctx, buffer, rb) < 0)
146 0 : pg_fatal("could not update checksum of file \"%s\"", src);
147 : }
148 :
149 0 : if (rb < 0)
150 0 : pg_fatal("could not read file \"%s\": %m", src);
151 :
152 0 : pg_free(buffer);
153 0 : close(src_fd);
154 : }
155 :
156 : /*
157 : * Copy a file block by block, and optionally compute a checksum as we go.
158 : */
159 : static void
160 23926 : copy_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 23926 : const int buffer_size = 50 * BLCKSZ;
167 : ssize_t rb;
168 23926 : unsigned offset = 0;
169 :
170 23926 : if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
171 0 : pg_fatal("could not open file \"%s\": %m", src);
172 :
173 23926 : if ((dest_fd = open(dst, O_WRONLY | O_CREAT | O_EXCL | PG_BINARY,
174 : pg_file_create_mode)) < 0)
175 0 : pg_fatal("could not open file \"%s\": %m", dst);
176 :
177 23926 : buffer = pg_malloc(buffer_size);
178 :
179 44884 : while ((rb = read(src_fd, buffer, buffer_size)) > 0)
180 : {
181 : ssize_t wb;
182 :
183 20958 : if ((wb = write(dest_fd, buffer, rb)) != rb)
184 : {
185 0 : if (wb < 0)
186 0 : pg_fatal("could not write to file \"%s\": %m", dst);
187 : else
188 0 : pg_fatal("could not write to file \"%s\", offset %u: wrote %d of %d",
189 : dst, offset, (int) wb, (int) rb);
190 : }
191 :
192 20958 : if (pg_checksum_update(checksum_ctx, buffer, rb) < 0)
193 0 : pg_fatal("could not update checksum of file \"%s\"", dst);
194 :
195 20958 : offset += rb;
196 : }
197 :
198 23926 : if (rb < 0)
199 0 : pg_fatal("could not read from file \"%s\": %m", dst);
200 :
201 23926 : pg_free(buffer);
202 23926 : close(src_fd);
203 23926 : close(dest_fd);
204 23926 : }
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 : */
212 : static void
213 0 : copy_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 0 : if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
225 0 : pg_fatal("could not open file \"%s\": %m", src);
226 :
227 0 : if ((dest_fd = open(dest, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
228 : pg_file_create_mode)) < 0)
229 0 : pg_fatal("could not create file \"%s\": %m", dest);
230 :
231 0 : if (ioctl(dest_fd, FICLONE, src_fd) < 0)
232 : {
233 0 : int save_errno = errno;
234 :
235 0 : unlink(dest);
236 :
237 0 : pg_fatal("error while cloning file \"%s\" to \"%s\": %s",
238 : src, dest, strerror(save_errno));
239 : }
240 :
241 0 : close(src_fd);
242 0 : 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 0 : checksum_file(src, checksum_ctx);
250 0 : }
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 : */
258 : static void
259 0 : copy_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 0 : if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
268 0 : pg_fatal("could not open file \"%s\": %m", src);
269 :
270 0 : if ((dest_fd = open(dest, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
271 : pg_file_create_mode)) < 0)
272 0 : pg_fatal("could not create file \"%s\": %m", dest);
273 :
274 : do
275 : {
276 0 : nbytes = copy_file_range(src_fd, NULL, dest_fd, NULL, SSIZE_MAX, 0);
277 0 : if (nbytes < 0)
278 0 : pg_fatal("error while copying file range from \"%s\" to \"%s\": %m",
279 : src, dest);
280 0 : } while (nbytes > 0);
281 :
282 0 : close(src_fd);
283 0 : 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 0 : checksum_file(src, checksum_ctx);
290 0 : }
291 :
292 : #ifdef WIN32
293 : static void
294 : copy_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 */
|