|
19 | 19 | #include "postgres.h" |
20 | 20 |
|
21 | 21 | #include <fcntl.h> |
| 22 | +#include <sys/stat.h> |
22 | 23 | #include <unistd.h> |
23 | 24 |
|
24 | 25 | #include "common/file_utils.h" |
25 | 26 | #include "miscadmin.h" |
26 | 27 | #include "pgstat.h" |
27 | 28 | #include "storage/copydir.h" |
| 29 | + |
| 30 | +#include <limits.h> |
| 31 | +#include <common/logging.h> |
| 32 | + |
28 | 33 | #include "storage/fd.h" |
| 34 | +#include "pg_config.h" |
29 | 35 |
|
30 | 36 | /* |
31 | 37 | * copydir: copy a directory |
@@ -71,7 +77,11 @@ copydir(const char *fromdir, const char *todir, bool recurse) |
71 | 77 | copydir(fromfile, tofile, true); |
72 | 78 | } |
73 | 79 | else if (xlde_type == PGFILETYPE_REG) |
| 80 | +#if defined(HAVE_COPY_FILE_RANGE) |
| 81 | + copy_file_reflink(fromfile, tofile); |
| 82 | +#else |
74 | 83 | copy_file(fromfile, tofile); |
| 84 | +#endif |
75 | 85 | } |
76 | 86 | FreeDir(xldir); |
77 | 87 |
|
@@ -214,3 +224,63 @@ copy_file(const char *fromfile, const char *tofile) |
214 | 224 |
|
215 | 225 | pfree(buffer); |
216 | 226 | } |
| 227 | + |
| 228 | +/* |
| 229 | + * copy one file using copy_file_range to give filesystem a chance to do COW optimization |
| 230 | + */ |
| 231 | +void |
| 232 | +copy_file_reflink(const char *fromfile, const char *tofile) |
| 233 | +{ |
| 234 | +#if defined(HAVE_COPY_FILE_RANGE) |
| 235 | + int srcfd; |
| 236 | + int dstfd; |
| 237 | + ssize_t nbytes; |
| 238 | + |
| 239 | + /* |
| 240 | + * Open the files |
| 241 | + */ |
| 242 | + srcfd = OpenTransientFile(fromfile, O_RDONLY | PG_BINARY); |
| 243 | + |
| 244 | + if (srcfd < 0) |
| 245 | + ereport(ERROR, |
| 246 | + errcode_for_file_access(), |
| 247 | + errmsg("could not open file \"%s\": %m", fromfile)); |
| 248 | + |
| 249 | + dstfd = OpenTransientFile(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY); |
| 250 | + |
| 251 | + if (dstfd < 0) |
| 252 | + ereport(ERROR, |
| 253 | + errcode_for_file_access(), |
| 254 | + errmsg("could not create file \"%s\": %m", tofile)); |
| 255 | + |
| 256 | + /* |
| 257 | + * Do the data copying. |
| 258 | + */ |
| 259 | + do |
| 260 | + { |
| 261 | + /* If we got a cancel signal during the copy of the file, quit */ |
| 262 | + CHECK_FOR_INTERRUPTS(); |
| 263 | + |
| 264 | + pgstat_report_wait_start(WAIT_EVENT_COPY_FILE_READ); |
| 265 | + nbytes = copy_file_range(srcfd, NULL, dstfd, NULL, SSIZE_MAX, 0); |
| 266 | + pgstat_report_wait_end(); |
| 267 | + if (nbytes < 0) |
| 268 | + ereport(ERROR, |
| 269 | + errcode_for_file_access(), |
| 270 | + errmsg("could not copy file \"%s\": %m", fromfile)); |
| 271 | + } |
| 272 | + while (nbytes > 0); |
| 273 | + |
| 274 | + if (CloseTransientFile(dstfd) != 0) |
| 275 | + ereport(ERROR, |
| 276 | + errcode_for_file_access(), |
| 277 | + errmsg("could not close file \"%s\": %m", tofile)); |
| 278 | + |
| 279 | + if (CloseTransientFile(srcfd) != 0) |
| 280 | + ereport(ERROR, |
| 281 | + errcode_for_file_access(), |
| 282 | + errmsg("could not close file \"%s\": %m", fromfile)); |
| 283 | +#else |
| 284 | + pg_fatal("copy_file_range not available on this platform"); |
| 285 | +#endif |
| 286 | +} |
0 commit comments