Age Owner Branch data TLA 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
116 rhaas@postgresql.org 45 :GNC 10261 : copy_file(const char *src, const char *dst,
46 : : pg_checksum_context *checksum_ctx,
47 : : CopyMethod copy_method, bool dry_run)
48 : : {
9 tomas.vondra@postgre 49 : 10261 : char *strategy_name = NULL;
50 : 10261 : 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 : : */
116 rhaas@postgresql.org 57 [ - + ]: 10261 : if (dry_run)
58 : : {
59 : : int fd;
60 : :
116 rhaas@postgresql.org 61 [ # # ]:UNC 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. */
9 tomas.vondra@postgre 72 [ - + - - ]:GNC 10261 : switch (copy_method)
73 : : {
9 tomas.vondra@postgre 74 :UNC 0 : case COPY_METHOD_CLONE:
75 : 0 : strategy_name = "clone";
76 : 0 : strategy_implementation = copy_file_clone;
77 : 0 : break;
9 tomas.vondra@postgre 78 :GNC 10261 : case COPY_METHOD_COPY:
79 : : /* leave NULL for simple block-by-block copy */
80 : 10261 : strategy_implementation = copy_file_blocks;
81 : 10261 : break;
9 tomas.vondra@postgre 82 :UNC 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 : :
116 rhaas@postgresql.org 94 [ - + ]:GNC 10261 : if (dry_run)
95 : : {
9 tomas.vondra@postgre 96 [ # # ]:UNC 0 : if (strategy_name)
97 [ # # ]: 0 : pg_log_debug("would copy \"%s\" to \"%s\" using strategy %s",
98 : : src, dst, strategy_name);
99 : : else
116 rhaas@postgresql.org 100 [ # # ]: 0 : pg_log_debug("would copy \"%s\" to \"%s\"",
101 : : src, dst);
102 : : }
103 : : else
104 : : {
9 tomas.vondra@postgre 105 [ - + ]:GNC 10261 : if (strategy_name)
9 tomas.vondra@postgre 106 [ # # ]:UNC 0 : pg_log_debug("copying \"%s\" to \"%s\" using strategy %s",
107 : : src, dst, strategy_name);
9 tomas.vondra@postgre 108 [ + + ]:GNC 10261 : else if (checksum_ctx->type == CHECKSUM_TYPE_NONE)
116 rhaas@postgresql.org 109 [ + + ]: 9296 : pg_log_debug("copying \"%s\" to \"%s\"",
110 : : src, dst);
111 : : else
112 [ - + ]: 965 : pg_log_debug("copying \"%s\" to \"%s\" and checksumming with %s",
113 : : src, dst, pg_checksum_type_name(checksum_ctx->type));
114 : :
9 tomas.vondra@postgre 115 : 10261 : strategy_implementation(src, dst, checksum_ctx);
116 : : }
116 rhaas@postgresql.org 117 : 10261 : }
118 : :
119 : : /*
120 : : * Calculate checksum for the src file.
121 : : */
122 : : static void
9 tomas.vondra@postgre 123 :UNC 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
116 rhaas@postgresql.org 156 :GNC 10261 : 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 : 10261 : const int buffer_size = 50 * BLCKSZ;
163 : : ssize_t rb;
164 : 10261 : unsigned offset = 0;
165 : :
166 [ - + ]: 10261 : if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
116 rhaas@postgresql.org 167 :UNC 0 : pg_fatal("could not open file \"%s\": %m", src);
168 : :
116 rhaas@postgresql.org 169 [ - + ]:GNC 10261 : if ((dest_fd = open(dst, O_WRONLY | O_CREAT | O_EXCL | PG_BINARY,
170 : : pg_file_create_mode)) < 0)
116 rhaas@postgresql.org 171 :UNC 0 : pg_fatal("could not open file \"%s\": %m", dst);
172 : :
116 rhaas@postgresql.org 173 :GNC 10261 : buffer = pg_malloc(buffer_size);
174 : :
175 [ + + ]: 19225 : while ((rb = read(src_fd, buffer, buffer_size)) > 0)
176 : : {
177 : : ssize_t wb;
178 : :
179 [ - + ]: 8964 : if ((wb = write(dest_fd, buffer, rb)) != rb)
180 : : {
116 rhaas@postgresql.org 181 [ # # ]:UNC 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 : :
116 rhaas@postgresql.org 188 [ - + ]:GNC 8964 : if (pg_checksum_update(checksum_ctx, buffer, rb) < 0)
116 rhaas@postgresql.org 189 :UNC 0 : pg_fatal("could not update checksum of file \"%s\"", dst);
190 : :
116 rhaas@postgresql.org 191 :GNC 8964 : offset += rb;
192 : : }
193 : :
194 [ - + ]: 10261 : if (rb < 0)
116 rhaas@postgresql.org 195 :UNC 0 : pg_fatal("could not read file \"%s\": %m", dst);
196 : :
116 rhaas@postgresql.org 197 :GNC 10261 : pg_free(buffer);
198 : 10261 : close(src_fd);
199 : 10261 : close(dest_fd);
200 : 10261 : }
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
9 tomas.vondra@postgre 209 :UNC 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 */
|