diff options
Diffstat (limited to 'src/util.c')
-rw-r--r-- | src/util.c | 151 |
1 files changed, 151 insertions, 0 deletions
@@ -6,6 +6,9 @@ #include <errno.h> #include <limits.h> #include <unistd.h> +#include <sys/mman.h> +#include <fcntl.h> + #include "util.h" void statistics(DB *db) { @@ -104,3 +107,151 @@ int isdirectory(const char *path) { return !!S_ISDIR(st.st_mode); } + + +#define MMAPSIZE (100*1024*1024) +#define BUFSIZE (1024*1024) + +int copy_fd(int sfd, int dfd, uint32_t l) { + off_t src_o, dst_o; + void *src_p, *dst_p; + void *buf = NULL; + int r = -1; + struct stat st; + + if ((src_o = lseek(sfd, 0, SEEK_CUR)) == (off_t) -1) { + fprintf(stderr, "lseek(): %s\n", strerror(errno)); + goto finish; + } + + if (fstat(sfd, &st) < 0) { + fprintf(stderr, "fstat(): %s\n", strerror(errno)); + goto finish; + } + + if (l == (uint32_t) -1 || l >= st.st_size - src_o) + l = st.st_size - src_o; + + if ((dst_o = lseek(dfd, 0, SEEK_CUR)) == (off_t) -1) { + fprintf(stderr, "lseek(): %s\n", strerror(errno)); + goto finish; + } + + if (fstat(dfd, &st) < 0) { + fprintf(stderr, "fstat(): %s\n", strerror(errno)); + goto finish; + } + + if (dst_o+l > st.st_size) + if (ftruncate(dfd, dst_o+l) < 0) { + fprintf(stderr, "ftruncate(): %s\n", strerror(errno)); + goto finish; + } + + if (l > BUFSIZE) { + uint32_t m = l < MMAPSIZE ? l : MMAPSIZE; + + while (l > 0) { + if ((src_p = mmap(NULL, m, PROT_READ, MAP_SHARED, sfd, src_o)) == MAP_FAILED) + break; + + if ((dst_p = mmap(NULL, m, PROT_READ|PROT_WRITE, MAP_SHARED, dfd, dst_o)) == MAP_FAILED) { + munmap(src_p, m); + break; + } + + memcpy(dst_p, src_p, m); + + munmap(src_p, m); + munmap(dst_p, m); + + src_o += m; + dst_o += m; + l -= m; + + m = l < MMAPSIZE ? l : MMAPSIZE; + } + + + if (lseek(sfd, src_o, SEEK_SET) == (off_t) -1) { + fprintf(stderr, "lseek(): %s\n", strerror(errno)); + goto finish; + } + + if (lseek(dfd, dst_o, SEEK_SET) == (off_t) -1) { + fprintf(stderr, "lseek(): %s\n", strerror(errno)); + goto finish; + } + } + + if (l > 0) { + if (!(buf = malloc(BUFSIZE))) + goto finish; + + for (;;) { + ssize_t m; + + if ((m = read(sfd, buf, BUFSIZE)) < 0) { + fprintf(stderr, "read(): %s\n", strerror(errno)); + goto finish; + } + + if (!m) + break; + + if (write(dfd, buf, m) < 0) { + fprintf(stderr, "write(): %s\n", strerror(errno)); + goto finish; + } + } + } + + r = 0; + +finish: + + return r; +} + +int copy_file(const char *src, const char *dst) { + int sfd = -1, dfd = -1, r = -1; + + if ((sfd = open(src, O_RDONLY)) < 0) { + fprintf(stderr, "open(%s, O_RDONLY): %s\n", src, strerror(errno)); + goto finish; + } + + if ((dfd = open(dst, O_RDWR|O_TRUNC|O_CREAT, 0666)) < 0) { + fprintf(stderr, "open(%s, O_RDWR|O_TRUNC|O_CREAT): %s\n", dst, strerror(errno)); + goto finish; + } + + if (copy_fd(sfd, dfd, (uint32_t) -1) < 0) + goto finish; + + r = 0; + +finish: + + if (sfd >= 0) + close(sfd); + + if (dfd >= 0) + close(dfd); + + return r; +} + +int copy_or_link_file(const char *src, const char *dst) { + + if (link(src, dst) < 0) { + + if (errno == EXDEV || errno == EPERM) + return copy_file(src, dst); + + fprintf(stderr, "link(%s, %s): %s\n", src, dst, strerror(errno)); + return -1; + } + + return 0; +} |