14
14
#include <copyfile.h>
15
15
#endif
16
16
#include <fcntl.h>
17
+ #include <limits.h>
17
18
#include <sys/stat.h>
18
19
#include <unistd.h>
19
20
24
25
static void copy_file_blocks (const char * src , const char * dst ,
25
26
pg_checksum_context * checksum_ctx );
26
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
+
27
34
#ifdef WIN32
28
- static void copy_file_copyfile (const char * src , const char * dst );
35
+ static void copy_file_copyfile (const char * src , const char * dst ,
36
+ pg_checksum_context * checksum_ctx );
29
37
#endif
30
38
31
39
/*
@@ -35,8 +43,13 @@ static void copy_file_copyfile(const char *src, const char *dst);
35
43
*/
36
44
void
37
45
copy_file (const char * src , const char * dst ,
38
- pg_checksum_context * checksum_ctx , bool dry_run )
46
+ pg_checksum_context * checksum_ctx ,
47
+ CopyMethod copy_method , bool dry_run )
39
48
{
49
+ char * strategy_name = NULL ;
50
+ void (* strategy_implementation ) (const char * , const char * ,
51
+ pg_checksum_context * checksum_ctx ) = NULL ;
52
+
40
53
/*
41
54
* In dry-run mode, we don't actually copy anything, nor do we read any
42
55
* data from the source file, but we do verify that we can open it.
@@ -51,61 +64,94 @@ copy_file(const char *src, const char *dst,
51
64
pg_fatal ("could not close \"%s\": %m" , src );
52
65
}
53
66
54
- /*
55
- * If we don't need to compute a checksum, then we can use any special
56
- * operating system primitives that we know about to copy the file; this
57
- * may be quicker than a naive block copy.
58
- */
59
- if (checksum_ctx -> type == CHECKSUM_TYPE_NONE )
60
- {
61
- char * strategy_name = NULL ;
62
- void (* strategy_implementation ) (const char * , const char * ) = NULL ;
63
-
64
67
#ifdef WIN32
65
- strategy_name = "CopyFile" ;
66
- strategy_implementation = copy_file_copyfile ;
68
+ copy_method = COPY_METHOD_COPYFILE ;
67
69
#endif
68
70
69
- if (strategy_name != NULL )
70
- {
71
- if (dry_run )
72
- pg_log_debug ("would copy \"%s\" to \"%s\" using strategy %s" ,
73
- src , dst , strategy_name );
74
- else
75
- {
76
- pg_log_debug ("copying \"%s\" to \"%s\" using strategy %s" ,
77
- src , dst , strategy_name );
78
- (* strategy_implementation ) (src , dst );
79
- }
80
- return ;
81
- }
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 ;
82
+ case COPY_METHOD_COPY_FILE_RANGE :
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
82
92
}
83
93
84
- /*
85
- * Fall back to the simple approach of reading and writing all the blocks,
86
- * feeding them into the checksum context as we go.
87
- */
88
94
if (dry_run )
89
95
{
90
- if (checksum_ctx -> type == CHECKSUM_TYPE_NONE )
96
+ if (strategy_name )
97
+ pg_log_debug ("would copy \"%s\" to \"%s\" using strategy %s" ,
98
+ src , dst , strategy_name );
99
+ else
91
100
pg_log_debug ("would copy \"%s\" to \"%s\"" ,
92
101
src , dst );
93
- else
94
- pg_log_debug ("would copy \"%s\" to \"%s\" and checksum with %s" ,
95
- src , dst , pg_checksum_type_name (checksum_ctx -> type ));
96
102
}
97
103
else
98
104
{
99
- if (checksum_ctx -> type == CHECKSUM_TYPE_NONE )
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 )
100
109
pg_log_debug ("copying \"%s\" to \"%s\"" ,
101
110
src , dst );
102
111
else
103
112
pg_log_debug ("copying \"%s\" to \"%s\" and checksumming with %s" ,
104
113
src , dst , pg_checksum_type_name (checksum_ctx -> type ));
105
- copy_file_blocks (src , dst , checksum_ctx );
114
+
115
+ strategy_implementation (src , dst , checksum_ctx );
106
116
}
107
117
}
108
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
+ unsigned offset = 0 ;
130
+
131
+ /* bail out if no checksum needed */
132
+ if (checksum_ctx -> type == CHECKSUM_TYPE_NONE )
133
+ return ;
134
+
135
+ if ((src_fd = open (src , O_RDONLY | PG_BINARY , 0 )) < 0 )
136
+ pg_fatal ("could not open file \"%s\": %m" , src );
137
+
138
+ buffer = pg_malloc (buffer_size );
139
+
140
+ while ((rb = read (src_fd , buffer , buffer_size )) > 0 )
141
+ {
142
+ if (pg_checksum_update (checksum_ctx , buffer , rb ) < 0 )
143
+ pg_fatal ("could not update checksum of file \"%s\"" , src );
144
+
145
+ offset += rb ;
146
+ }
147
+
148
+ if (rb < 0 )
149
+ pg_fatal ("could not read file \"%s\": %m" , src );
150
+
151
+ pg_free (buffer );
152
+ close (src_fd );
153
+ }
154
+
109
155
/*
110
156
* Copy a file block by block, and optionally compute a checksum as we go.
111
157
*/
@@ -156,14 +202,98 @@ copy_file_blocks(const char *src, const char *dst,
156
202
close (dest_fd );
157
203
}
158
204
205
+ /*
206
+ * copy_file_clone
207
+ * Clones/reflinks a file from src to dest.
208
+ *
209
+ * If needed, also reads the file and calculates the checksum.
210
+ */
211
+ static void
212
+ copy_file_clone (const char * src , const char * dest ,
213
+ pg_checksum_context * checksum_ctx )
214
+ {
215
+ #if defined(HAVE_COPYFILE ) && defined(COPYFILE_CLONE_FORCE )
216
+ if (copyfile (src , dest , NULL , COPYFILE_CLONE_FORCE ) < 0 )
217
+ pg_fatal ("error while cloning file \"%s\" to \"%s\": %m" , src , dest );
218
+ #elif defined(__linux__ ) && defined(FICLONE )
219
+ {
220
+ if ((src_fd = open (src , O_RDONLY | PG_BINARY , 0 )) < 0 )
221
+ pg_fatal ("could not open file \"%s\": %m" , src );
222
+
223
+ if ((dest_fd = open (dest , O_RDWR | O_CREAT | O_EXCL | PG_BINARY ,
224
+ pg_file_create_mode )) < 0 )
225
+ pg_fatal ("could not create file \"%s\": %m" , dest );
226
+
227
+ if (ioctl (dest_fd , FICLONE , src_fd ) < 0 )
228
+ {
229
+ int save_errno = errno ;
230
+
231
+ unlink (dest );
232
+
233
+ pg_fatal ("error while cloning file \"%s\" to \"%s\": %s" ,
234
+ src , dest );
235
+ }
236
+ }
237
+ #else
238
+ pg_fatal ("file cloning not supported on this platform" );
239
+ #endif
240
+
241
+ /* if needed, calculate checksum of the file */
242
+ checksum_file (src , checksum_ctx );
243
+ }
244
+
245
+ /*
246
+ * copy_file_by_range
247
+ * Copies a file from src to dest using copy_file_range system call.
248
+ *
249
+ * If needed, also reads the file and calculates the checksum.
250
+ */
251
+ static void
252
+ copy_file_by_range (const char * src , const char * dest ,
253
+ pg_checksum_context * checksum_ctx )
254
+ {
255
+ #if defined(HAVE_COPY_FILE_RANGE )
256
+ int src_fd ;
257
+ int dest_fd ;
258
+ ssize_t nbytes ;
259
+
260
+ if ((src_fd = open (src , O_RDONLY | PG_BINARY , 0 )) < 0 )
261
+ pg_fatal ("could not open file \"%s\": %m" , src );
262
+
263
+ if ((dest_fd = open (dest , O_RDWR | O_CREAT | O_EXCL | PG_BINARY ,
264
+ pg_file_create_mode )) < 0 )
265
+ pg_fatal ("could not create file \"%s\": %m" , dest );
266
+
267
+ do
268
+ {
269
+ nbytes = copy_file_range (src_fd , NULL , dest_fd , NULL , SSIZE_MAX , 0 );
270
+ if (nbytes < 0 )
271
+ pg_fatal ("error while copying file range from \"%s\" to \"%s\": %m" ,
272
+ src , dest );
273
+ } while (nbytes > 0 );
274
+
275
+ close (src_fd );
276
+ close (dest_fd );
277
+ #else
278
+ pg_fatal ("copy_file_range not supported on this platform" );
279
+ #endif
280
+
281
+ /* if needed, calculate checksum of the file */
282
+ checksum_file (src , checksum_ctx );
283
+ }
284
+
159
285
#ifdef WIN32
160
286
static void
161
- copy_file_copyfile (const char * src , const char * dst )
287
+ copy_file_copyfile (const char * src , const char * dst ,
288
+ pg_checksum_context * checksum_ctx )
162
289
{
163
290
if (CopyFile (src , dst , true) == 0 )
164
291
{
165
292
_dosmaperr (GetLastError ());
166
293
pg_fatal ("could not copy \"%s\" to \"%s\": %m" , src , dst );
167
294
}
295
+
296
+ /* if needed, calculate checksum of the file */
297
+ checksum_file (src , checksum_ctx );
168
298
}
169
299
#endif /* WIN32 */
0 commit comments