00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 #include <unistd.h>
00018 #include <errno.h>
00019 #include <fcntl.h>
00020 #include <signal.h>
00021 #include <setjmp.h>
00022 #include <sys/mman.h>
00023 #include <sys/stat.h>
00024
00025 #include "misc.h"
00026 #include "open.h"
00027
00028 static sigjmp_buf __copy_file_sigjmp_env;
00029
00030 static
00031 void __copy_file_sigbus_handler(int sig)
00032 {
00033 siglongjmp(__copy_file_sigjmp_env, 1);
00034 }
00035
00036 static
00037 void __copy_file_sparse_memcpy(void *dst, const void *src, int len)
00038 {
00039
00040
00041
00042 long *dsti = dst;
00043 const long *srci = src;
00044
00045 int leni = len / sizeof(long);
00046 int rest = len - sizeof(long) * leni;
00047
00048 int i;
00049
00050 for (i = 0; i < leni; srci++, dsti++, i++) {
00051 if (*srci != 0)
00052 *dsti = *srci;
00053 }
00054
00055 char *dstc = (void *) dsti;
00056 const char *srcc = (const void *) srci;
00057
00058 for (i = 0; i < rest; srcc++, dstc++, i++) {
00059 if (*srcc != 0)
00060 *dstc = *srcc;
00061 }
00062 }
00063
00064 #define CHUNKSIZE (16*1024*1024)
00065
00066 int copy_file(int srcfd, int dstfd)
00067 {
00068 int errno_orig;
00069 int rc = -1, bufsize = 0;
00070 void *srcbuf = MAP_FAILED, *dstbuf = MAP_FAILED;
00071
00072
00073 void (*oldhandler)(int) = signal(SIGBUS, __copy_file_sigbus_handler);
00074
00075
00076 struct stat sb;
00077
00078 if (fstat(srcfd, &sb) == -1)
00079 goto out;
00080
00081
00082 if (ftruncate(dstfd, sb.st_size) == -1)
00083 goto out;
00084
00085 if (sb.st_size < 1) {
00086 rc = 0;
00087 goto out;
00088 }
00089
00090
00091 if (sigsetjmp(__copy_file_sigjmp_env, 1) != 0)
00092 goto out;
00093
00094 int offset = 0;
00095
00096 while (offset < sb.st_size) {
00097 bufsize = sb.st_size - offset;
00098 bufsize = bufsize > CHUNKSIZE ? CHUNKSIZE : bufsize;
00099
00100
00101 srcbuf = mmap(0, bufsize, PROT_READ, MAP_SHARED, srcfd, offset);
00102
00103 if (srcbuf == MAP_FAILED)
00104 goto out;
00105
00106
00107 dstbuf = mmap(0, bufsize, PROT_WRITE, MAP_SHARED, dstfd, offset);
00108
00109 if (dstbuf == MAP_FAILED)
00110 goto out;
00111
00112 offset += bufsize;
00113
00114
00115 madvise(srcbuf, bufsize, MADV_SEQUENTIAL);
00116 madvise(dstbuf, bufsize, MADV_SEQUENTIAL);
00117
00118
00119 __copy_file_sparse_memcpy(dstbuf, srcbuf, bufsize);
00120
00121 munmap(srcbuf, bufsize);
00122 srcbuf = MAP_FAILED;
00123
00124 munmap(dstbuf, bufsize);
00125 dstbuf = MAP_FAILED;
00126 }
00127
00128 rc = 0;
00129
00130 out:
00131 if (srcbuf && srcbuf != MAP_FAILED)
00132 munmap(srcbuf, bufsize);
00133
00134 if (dstbuf && dstbuf != MAP_FAILED)
00135 munmap(dstbuf, bufsize);
00136
00137 errno_orig = errno;
00138 signal(SIGBUS, oldhandler);
00139 errno = errno_orig;
00140 return rc;
00141 }