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