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 #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 copy_file(const char *src, const char *dst,
46  pg_checksum_context *checksum_ctx,
47  CopyMethod copy_method, bool dry_run)
48 {
49  char *strategy_name = NULL;
50  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  if (dry_run)
58  {
59  int fd;
60 
61  if ((fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
62  pg_fatal("could not open \"%s\": %m", src);
63  if (close(fd) < 0)
64  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  switch (copy_method)
73  {
74  case COPY_METHOD_CLONE:
75  strategy_name = "clone";
76  strategy_implementation = copy_file_clone;
77  break;
78  case COPY_METHOD_COPY:
79  /* leave NULL for simple block-by-block copy */
80  strategy_implementation = copy_file_blocks;
81  break;
83  strategy_name = "copy_file_range";
84  strategy_implementation = copy_file_by_range;
85  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  if (dry_run)
95  {
96  if (strategy_name)
97  pg_log_debug("would copy \"%s\" to \"%s\" using strategy %s",
98  src, dst, strategy_name);
99  else
100  pg_log_debug("would copy \"%s\" to \"%s\"",
101  src, dst);
102  }
103  else
104  {
105  if (strategy_name)
106  pg_log_debug("copying \"%s\" to \"%s\" using strategy %s",
107  src, dst, strategy_name);
108  else if (checksum_ctx->type == CHECKSUM_TYPE_NONE)
109  pg_log_debug("copying \"%s\" to \"%s\"",
110  src, dst);
111  else
112  pg_log_debug("copying \"%s\" to \"%s\" and checksumming with %s",
113  src, dst, pg_checksum_type_name(checksum_ctx->type));
114 
115  strategy_implementation(src, dst, checksum_ctx);
116  }
117 }
118 
119 /*
120  * Calculate checksum for the src file.
121  */
122 static void
123 checksum_file(const char *src, pg_checksum_context *checksum_ctx)
124 {
125  int src_fd;
126  uint8 *buffer;
127  const int buffer_size = 50 * BLCKSZ;
128  ssize_t rb;
129 
130  /* bail out if no checksum needed */
131  if (checksum_ctx->type == CHECKSUM_TYPE_NONE)
132  return;
133 
134  if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
135  pg_fatal("could not open file \"%s\": %m", src);
136 
137  buffer = pg_malloc(buffer_size);
138 
139  while ((rb = read(src_fd, buffer, buffer_size)) > 0)
140  {
141  if (pg_checksum_update(checksum_ctx, buffer, rb) < 0)
142  pg_fatal("could not update checksum of file \"%s\"", src);
143  }
144 
145  if (rb < 0)
146  pg_fatal("could not read file \"%s\": %m", src);
147 
148  pg_free(buffer);
149  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 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  const int buffer_size = 50 * BLCKSZ;
163  ssize_t rb;
164  unsigned offset = 0;
165 
166  if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
167  pg_fatal("could not open file \"%s\": %m", src);
168 
169  if ((dest_fd = open(dst, O_WRONLY | O_CREAT | O_EXCL | PG_BINARY,
170  pg_file_create_mode)) < 0)
171  pg_fatal("could not open file \"%s\": %m", dst);
172 
173  buffer = pg_malloc(buffer_size);
174 
175  while ((rb = read(src_fd, buffer, buffer_size)) > 0)
176  {
177  ssize_t wb;
178 
179  if ((wb = write(dest_fd, buffer, rb)) != rb)
180  {
181  if (wb < 0)
182  pg_fatal("could not write file \"%s\": %m", dst);
183  else
184  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  if (pg_checksum_update(checksum_ctx, buffer, rb) < 0)
189  pg_fatal("could not update checksum of file \"%s\"", dst);
190 
191  offset += rb;
192  }
193 
194  if (rb < 0)
195  pg_fatal("could not read file \"%s\": %m", dst);
196 
197  pg_free(buffer);
198  close(src_fd);
199  close(dest_fd);
200 }
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 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  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 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  if ((src_fd = open(src, O_RDONLY | PG_BINARY, 0)) < 0)
258  pg_fatal("could not open file \"%s\": %m", src);
259 
260  if ((dest_fd = open(dest, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
261  pg_file_create_mode)) < 0)
262  pg_fatal("could not create file \"%s\": %m", dest);
263 
264  do
265  {
266  nbytes = copy_file_range(src_fd, NULL, dest_fd, NULL, SSIZE_MAX, 0);
267  if (nbytes < 0)
268  pg_fatal("error while copying file range from \"%s\" to \"%s\": %m",
269  src, dest);
270  } while (nbytes > 0);
271 
272  close(src_fd);
273  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  checksum_file(src, checksum_ctx);
280 }
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 */
#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:156
void copy_file(const char *src, const char *dst, pg_checksum_context *checksum_ctx, CopyMethod copy_method, bool dry_run)
Definition: copy_file.c:45
static void copy_file_by_range(const char *src, const char *dst, pg_checksum_context *checksum_ctx)
Definition: copy_file.c:249
static void checksum_file(const char *src, pg_checksum_context *checksum_ctx)
Definition: copy_file.c:123
static void copy_file_clone(const char *src, const char *dst, pg_checksum_context *checksum_ctx)
Definition: copy_file.c:209
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
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