1/*- 2 * Copyright (c) 2008-2009, Ulf Lilleengen <lulf@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29#include <errno.h> 30#include <string.h> 31#include <stdlib.h> 32#include <stdio.h> 33 34#include <sys/types.h> 35#include <sys/stat.h> 36#include <sys/mman.h> 37#include <fcntl.h> 38#include <unistd.h> 39 40#include "misc.h" 41#include "rsyncfile.h" 42 43#define MINBLOCKSIZE 1024 44#define MAXBLOCKSIZE (16 * 1024) 45#define RECEIVEBUFFERSIZE (15 * 1024) 46#define BLOCKINFOSIZE 26 47#define SEARCHREGION 10 48#define MAXBLOCKS (RECEIVEBUFFERSIZE / BLOCKINFOSIZE) 49 50#define CHAR_OFFSET 3 51#define RSUM_SIZE 9 52 53struct rsyncfile { 54 char *start; 55 char *buf; 56 char *end; 57 size_t blocksize; 58 size_t fsize; 59 int fd; 60 61 char *blockptr; 62 int blocknum; 63 char blockmd5[MD5_DIGEST_SIZE]; 64 char rsumstr[RSUM_SIZE]; 65 uint32_t rsum; 66}; 67 68static size_t rsync_chooseblocksize(size_t); 69static uint32_t rsync_rollsum(char *, size_t); 70 71/* Open a file and initialize variable for rsync operation. */ 72struct rsyncfile * 73rsync_open(char *path, size_t blocksize, int rdonly) 74{ 75 struct rsyncfile *rf; 76 struct stat st; 77 int error; 78 79 rf = xmalloc(sizeof(*rf)); 80 error = stat(path, &st); 81 if (error) { 82 free(rf); 83 return (NULL); 84 } 85 rf->fsize = st.st_size; 86 87 rf->fd = open(path, rdonly ? O_RDONLY : O_RDWR); 88 if (rf->fd < 0) { 89 free(rf); 90 return (NULL); 91 } 92 rf->buf = mmap(0, rf->fsize, PROT_READ, MAP_SHARED, rf->fd, 0); 93 if (rf->buf == MAP_FAILED) { 94 free(rf); 95 return (NULL); 96 } 97 rf->start = rf->buf; 98 rf->end = rf->buf + rf->fsize; 99 rf->blocksize = (blocksize == 0 ? rsync_chooseblocksize(rf->fsize) : 100 blocksize); 101 rf->blockptr = rf->buf; 102 rf->blocknum = 0; 103 return (rf); 104} 105 106/* Close and free all resources related to an rsync file transfer. */ 107int 108rsync_close(struct rsyncfile *rf) 109{ 110 int error; 111 112 error = munmap(rf->buf, rf->fsize); 113 if (error) 114 return (error); 115 close(rf->fd); 116 free(rf); 117 return (0); 118} 119 120/* 121 * Choose the most appropriate block size for an rsync transfer. Modeled 122 * algorithm after cvsup. 123 */ 124static size_t 125rsync_chooseblocksize(size_t fsize) 126{ 127 size_t bestrem, blocksize, bs, hisearch, losearch, rem; 128 129 blocksize = fsize / MAXBLOCKS; 130 losearch = blocksize - SEARCHREGION; 131 hisearch = blocksize + SEARCHREGION; 132 133 if (losearch < MINBLOCKSIZE) { 134 losearch = MINBLOCKSIZE; 135 hisearch = losearch + (2 * SEARCHREGION); 136 } else if (hisearch > MAXBLOCKSIZE) { 137 hisearch = MAXBLOCKSIZE; 138 losearch = hisearch - (2 * SEARCHREGION); 139 } 140 141 bestrem = MAXBLOCKSIZE; 142 for (bs = losearch; bs <= hisearch; bs++) { 143 rem = fsize % bs; 144 if (rem < bestrem) { 145 bestrem = rem; 146 blocksize = bs; 147 } 148 } 149 return (bestrem); 150} 151 152/* Get the next rsync block of a file. */ 153int 154rsync_nextblock(struct rsyncfile *rf) 155{ 156 MD5_CTX ctx; 157 size_t blocksize; 158 159 if (rf->blockptr >= rf->end) 160 return (0); 161 blocksize = min((size_t)(rf->end - rf->blockptr), rf->blocksize); 162 /* Calculate MD5 of the block. */ 163 MD5_Init(&ctx); 164 MD5_Update(&ctx, rf->blockptr, blocksize); 165 MD5_End(rf->blockmd5, &ctx); 166 167 rf->rsum = rsync_rollsum(rf->blockptr, blocksize); 168 snprintf(rf->rsumstr, RSUM_SIZE, "%x", rf->rsum); 169 rf->blocknum++; 170 rf->blockptr += blocksize; 171 return (1); 172} 173 174/* Get the rolling checksum of a file. */ 175static uint32_t 176rsync_rollsum(char *buf, size_t len) 177{ 178 uint32_t a, b; 179 char *ptr, *limit; 180 181 a = b = 0; 182 ptr = buf; 183 limit = buf + len; 184 185 while (ptr < limit) { 186 a += *ptr + CHAR_OFFSET; 187 b += a; 188 ptr++; 189 } 190 return ((b << 16) | a); 191} 192 193/* Get running sum so far. */ 194char * 195rsync_rsum(struct rsyncfile *rf) 196{ 197 198 return (rf->rsumstr); 199} 200 201/* Get MD5 of current block. */ 202char * 203rsync_blockmd5(struct rsyncfile *rf) 204{ 205 206 return (rf->blockmd5); 207} 208 209/* Accessor for blocksize. */ 210size_t 211rsync_blocksize(struct rsyncfile *rf) 212{ 213 214 return (rf->blocksize); 215} 216 217/* Accessor for filesize. */ 218size_t 219rsync_filesize(struct rsyncfile *rf) 220{ 221 222 return (rf->fsize); 223} 224