#include "GbCopyFile.h" #include "Log.h" #include "Sanity.h" #include <errno.h> #include <stdio.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <sys/sendfile.h> #include <sys/types.h> #include <sys/stat.h> //note: keep copy functionality in-sync with moveFile() static const size_t io_buffer_size = 1024*1024; int copyFile(const char *src, const char *dst) { //Allocate IO buffer char *buffer = new char[io_buffer_size]; //Use O_DIRECT because the source file will soon be gone and shouldn't //pollute the kernels file system cache int fd_src = open(src,O_RDONLY|O_DIRECT); if(fd_src<0) { int saved_errno = errno; if(errno==EINVAL) { //open(...O_DIRECT) can fail due to filesystem (eg. on development machines /tmp can be a //tmpfs which curiously doesn't support O_DIRECT. Other file systems, eg. NFS have the same issue. fd_src = open(src,O_RDONLY); if(fd_src<0) { saved_errno = errno; log(LOG_ERROR,"copyFile:open(%s) failed with errno=%d (%s)", src, errno, strerror(errno)); delete[] buffer; errno = saved_errno; return -1; } } else { log(LOG_ERROR,"copyFile:open(%s) failed with errno=%d (%s)", src, errno, strerror(errno)); delete[] buffer; errno = saved_errno; return -1; } } //Tell the OS that we are going to read the source file sequentially (void)posix_fadvise(fd_src,0,0,POSIX_FADV_SEQUENTIAL); //Open destination. We use O_TRUNC instead of O_EXCL because if we crashed //during a copy the destination file will be left over (it is a bit risky). //We don't use O_DIRECT nor posix_fadvise(fd_dst,0,0,POSIX_FADV_SEQUENTIAL) //because the destination is likely to be used immediately when finished int fd_dst = open(dst,O_CREAT|O_TRUNC|O_WRONLY,0666); if(fd_dst<0) { int saved_errno = errno; log(LOG_ERROR,"move_file:open(%s) failed with errno=%d (%s)", dst,errno,strerror(errno)); (void)close(fd_src); delete[] buffer; errno = saved_errno; return -1; } struct stat st_src; (void)fstat(fd_src, &st_src); //sendfile() should be the most efficient way, but it may fail on some (older) kernels long rc = sendfile(fd_dst, fd_src, NULL, st_src.st_size); if(rc<0) { //fallback: //if sendfile() fails, then do a traditional roll-your-own copy loop for(;;) { long bytes_read = read(fd_src, buffer, io_buffer_size); if(bytes_read==0) break; if(bytes_read<0) { int saved_errno = errno; log(LOG_ERROR,"moveFile:open(%s) failed with errno=%d (%s)", src,errno,strerror(errno)); close(fd_src); close(fd_dst); unlink(dst); delete[] buffer; errno = saved_errno; return -1; } long bytes_written = write(fd_dst, buffer, (size_t)bytes_read); if(bytes_written!=bytes_read) { int saved_errno = errno; log(LOG_ERROR,"moveFile:write(%s) failed with errno=%d (%s)", dst,errno,strerror(errno)); close(fd_src); close(fd_dst); unlink(dst); delete[] buffer; errno = saved_errno; return -1; } } } struct stat st_dst; (void)fstat(fd_dst, &st_dst); if (st_dst.st_size != st_src.st_size) { logError("copying file from src=%s to dst=%s ends up with different file sizes. src=%ld bytes dst=%ld bytes", src, dst, st_src.st_size, st_dst.st_size); gbshutdownCorrupted(); } close(fd_src); close(fd_dst); delete[] buffer; return 0; }