misc/copy_file.c

Go to the documentation of this file.
00001 // Copyright (C) 2007 Benedikt Böhm <hollow@gentoo.org>
00002 //
00003 // This program is free software; you can redistribute it and/or
00004 // modify it under the terms of the GNU General Public License
00005 // as published by the Free Software Foundation; either version 2
00006 // of the License, or (at your option) any later version.
00007 //
00008 // This program is distributed in the hope that it will be useful,
00009 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00010 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011 // GNU General Public License for more details.
00012 //
00013 // You should have received a copy of the GNU General Public License
00014 // along with this program; if not, write to the Free Software
00015 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
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         /* although sparse memcpy is not as efficient as pure read/write
00040          * we decrease instructions by about 50-80% using long instead of
00041          * char for most of the copy operation */
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) /* 16M */
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         /* install SIGBUS handler for mmap */
00073         void (*oldhandler)(int) = signal(SIGBUS, __copy_file_sigbus_handler);
00074 
00075         /* get file length */
00076         struct stat sb;
00077 
00078         if (fstat(srcfd, &sb) == -1)
00079                 goto out;
00080 
00081         /* create sparse file */
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         /* save environment for non-local jump */
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                 /* map source file */
00101                 srcbuf = mmap(0, bufsize, PROT_READ, MAP_SHARED, srcfd, offset);
00102 
00103                 if (srcbuf == MAP_FAILED)
00104                         goto out;
00105 
00106                 /* map destination file */
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                 /* advise to sequential order (more aggressive read ahead) */
00115                 madvise(srcbuf, bufsize, MADV_SEQUENTIAL);
00116                 madvise(dstbuf, bufsize, MADV_SEQUENTIAL);
00117 
00118                 /* copy memory area with sparse support */
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 }

Generated on Tue Jun 19 20:38:26 2007 for lucid by  doxygen 1.5.2