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