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 : static 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 : */
51 : void
52 25880 : copy_file(const char *src, const char *dst,
53 : pg_checksum_context *checksum_ctx,
54 : CopyMethod copy_method, bool dry_run)
55 : {
56 25880 : char *strategy_name = NULL;
57 25880 : 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 25880 : if (dry_run)
65 : {
66 : int fd;
67 :
68 0 : if ((fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
69 0 : pg_fatal("could not open file \"%s\": %m", src);
70 0 : if (close(fd) < 0)
71 0 : 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 25880 : switch (copy_method)
87 : {
88 0 : case COPY_METHOD_CLONE:
89 0 : strategy_name = "clone";
90 0 : strategy_implementation = copy_file_clone;
91 0 : break;
92 23932 : case COPY_METHOD_COPY:
93 : /* leave NULL for simple block-by-block copy */
94 23932 : strategy_implementation = copy_file_blocks;
95 23932 : break;
96 0 : case COPY_METHOD_COPY_FILE_RANGE:
97 0 : strategy_name = "copy_file_range";
98 0 : strategy_implementation = copy_file_by_range;
99 0 : break;
100 : #ifdef WIN32
101 : case COPY_METHOD_COPYFILE:
102 : strategy_name = "CopyFile";
103 : strategy_implementation = copy_file_copyfile;
104 : break;
105 : #endif
106 1948 : case COPY_METHOD_LINK:
107 1948 : strategy_name = "link";
108 1948 : strategy_implementation = copy_file_link;
109 1948 : break;
110 : }
111 :
112 25880 : if (dry_run)
113 : {
114 0 : if (strategy_name)
115 0 : pg_log_debug("would copy \"%s\" to \"%s\" using strategy %s",
116 : src, dst, strategy_name);
117 : else
118 0 : pg_log_debug("would copy \"%s\" to \"%s\"",
119 : src, dst);
120 : }
121 : else
122 : {
123 25880 : if (strategy_name)
124 1948 : pg_log_debug("copying \"%s\" to \"%s\" using strategy %s",
125 : src, dst, strategy_name);
126 23932 : else if (checksum_ctx->type == CHECKSUM_TYPE_NONE)
127 21994 : pg_log_debug("copying \"%s\" to \"%s\"",
128 : src, dst);
129 : else
130 1938 : pg_log_debug("copying \"%s\" to \"%s\" and checksumming with %s",
131 : src, dst, pg_checksum_type_name(checksum_ctx->type));
132 :
133 25880 : strategy_implementation(src, dst, checksum_ctx);
134 : }
135 25880 : }
136 :
137 : /*
138 : * Calculate checksum for the src file.
139 : */
140 : static void
141 1948 : checksum_file(const char *src, pg_checksum_context *checksum_ctx)
142 : {
143 : int src_fd;
144 : uint8 *buffer;
145 1948 : const int buffer_size = 50 * BLCKSZ;
146 : ssize_t rb;
147 :
148 : /* bail out if no checksum needed */
149 1948 : if (checksum_ctx->type == CHECKSUM_TYPE_NONE)
150 1948 : return;
151 :
152 0 : if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
153 0 : pg_fatal("could not open file \"%s\": %m", src);
154 :
155 0 : buffer = pg_malloc(buffer_size);
156 :
157 0 : while ((rb = read(src_fd, buffer, buffer_size)) > 0)
158 : {
159 0 : if (pg_checksum_update(checksum_ctx, buffer, rb) < 0)
160 0 : pg_fatal("could not update checksum of file \"%s\"", src);
161 : }
162 :
163 0 : if (rb < 0)
164 0 : pg_fatal("could not read file \"%s\": %m", src);
165 :
166 0 : pg_free(buffer);
167 0 : close(src_fd);
168 : }
169 :
170 : /*
171 : * Copy a file block by block, and optionally compute a checksum as we go.
172 : */
173 : static void
174 23932 : copy_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 23932 : const int buffer_size = 50 * BLCKSZ;
181 : ssize_t rb;
182 23932 : unsigned offset = 0;
183 :
184 23932 : if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
185 0 : pg_fatal("could not open file \"%s\": %m", src);
186 :
187 23932 : if ((dest_fd = open(dst, O_WRONLY | O_CREAT | O_EXCL | PG_BINARY,
188 : pg_file_create_mode)) < 0)
189 0 : pg_fatal("could not open file \"%s\": %m", dst);
190 :
191 23932 : buffer = pg_malloc(buffer_size);
192 :
193 44890 : while ((rb = read(src_fd, buffer, buffer_size)) > 0)
194 : {
195 : ssize_t wb;
196 :
197 20958 : if ((wb = write(dest_fd, buffer, rb)) != rb)
198 : {
199 0 : if (wb < 0)
200 0 : pg_fatal("could not write to file \"%s\": %m", dst);
201 : else
202 0 : pg_fatal("could not write to file \"%s\", offset %u: wrote %d of %d",
203 : dst, offset, (int) wb, (int) rb);
204 : }
205 :
206 20958 : if (pg_checksum_update(checksum_ctx, buffer, rb) < 0)
207 0 : pg_fatal("could not update checksum of file \"%s\"", dst);
208 :
209 20958 : offset += rb;
210 : }
211 :
212 23932 : if (rb < 0)
213 0 : pg_fatal("could not read from file \"%s\": %m", dst);
214 :
215 23932 : pg_free(buffer);
216 23932 : close(src_fd);
217 23932 : close(dest_fd);
218 23932 : }
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 : */
226 : static void
227 0 : copy_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 0 : if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
239 0 : pg_fatal("could not open file \"%s\": %m", src);
240 :
241 0 : if ((dest_fd = open(dest, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
242 : pg_file_create_mode)) < 0)
243 0 : pg_fatal("could not create file \"%s\": %m", dest);
244 :
245 0 : if (ioctl(dest_fd, FICLONE, src_fd) < 0)
246 : {
247 0 : int save_errno = errno;
248 :
249 0 : unlink(dest);
250 :
251 0 : pg_fatal("error while cloning file \"%s\" to \"%s\": %s",
252 : src, dest, strerror(save_errno));
253 : }
254 :
255 0 : close(src_fd);
256 0 : 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 0 : checksum_file(src, checksum_ctx);
264 0 : }
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 : */
272 : static void
273 0 : copy_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 0 : if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
282 0 : pg_fatal("could not open file \"%s\": %m", src);
283 :
284 0 : if ((dest_fd = open(dest, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
285 : pg_file_create_mode)) < 0)
286 0 : pg_fatal("could not create file \"%s\": %m", dest);
287 :
288 : do
289 : {
290 0 : nbytes = copy_file_range(src_fd, NULL, dest_fd, NULL, SSIZE_MAX, 0);
291 0 : if (nbytes < 0)
292 0 : pg_fatal("error while copying file range from \"%s\" to \"%s\": %m",
293 : src, dest);
294 0 : } while (nbytes > 0);
295 :
296 0 : close(src_fd);
297 0 : 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 0 : checksum_file(src, checksum_ctx);
304 0 : }
305 :
306 : #ifdef WIN32
307 : static void
308 : copy_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 : */
328 : static void
329 1948 : copy_file_link(const char *src, const char *dest,
330 : pg_checksum_context *checksum_ctx)
331 : {
332 1948 : if (link(src, dest) < 0)
333 0 : pg_fatal("error while linking file from \"%s\" to \"%s\": %m",
334 : src, dest);
335 :
336 : /* if needed, calculate checksum of the file */
337 1948 : checksum_file(src, checksum_ctx);
338 1948 : }
|