1414#include <copyfile.h>
1515#endif
1616#include <fcntl.h>
17+ #include <limits.h>
1718#include <sys/stat.h>
1819#include <unistd.h>
1920
2425static void copy_file_blocks (const char * src , const char * dst ,
2526 pg_checksum_context * checksum_ctx );
2627
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+
2734#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 );
2937#endif
3038
3139/*
@@ -35,8 +43,13 @@ static void copy_file_copyfile(const char *src, const char *dst);
3543 */
3644void
3745copy_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 )
3948{
49+ char * strategy_name = NULL ;
50+ void (* strategy_implementation ) (const char * , const char * ,
51+ pg_checksum_context * checksum_ctx ) = NULL ;
52+
4053 /*
4154 * In dry-run mode, we don't actually copy anything, nor do we read any
4255 * 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,
5164 pg_fatal ("could not close \"%s\": %m" , src );
5265 }
5366
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-
6467#ifdef WIN32
65- strategy_name = "CopyFile" ;
66- strategy_implementation = copy_file_copyfile ;
68+ copy_method = COPY_METHOD_COPYFILE ;
6769#endif
6870
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
8292 }
8393
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- */
8894 if (dry_run )
8995 {
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
91100 pg_log_debug ("would copy \"%s\" to \"%s\"" ,
92101 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 ));
96102 }
97103 else
98104 {
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 )
100109 pg_log_debug ("copying \"%s\" to \"%s\"" ,
101110 src , dst );
102111 else
103112 pg_log_debug ("copying \"%s\" to \"%s\" and checksumming with %s" ,
104113 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 );
106116 }
107117}
108118
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+
109155/*
110156 * Copy a file block by block, and optionally compute a checksum as we go.
111157 */
@@ -156,14 +202,98 @@ copy_file_blocks(const char *src, const char *dst,
156202 close (dest_fd );
157203}
158204
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+
159285#ifdef WIN32
160286static 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 )
162289{
163290 if (CopyFile (src , dst , true) == 0 )
164291 {
165292 _dosmaperr (GetLastError ());
166293 pg_fatal ("could not copy \"%s\" to \"%s\": %m" , src , dst );
167294 }
295+
296+ /* if needed, calculate checksum of the file */
297+ checksum_file (src , checksum_ctx );
168298}
169299#endif /* WIN32 */
0 commit comments