1184257Slulf/*- 2186743Slulf * Copyright (c) 2008-2009, Ulf Lilleengen <lulf@FreeBSD.org> 3184257Slulf * All rights reserved. 4184257Slulf * 5184257Slulf * Redistribution and use in source and binary forms, with or without 6184257Slulf * modification, are permitted provided that the following conditions 7184257Slulf * are met: 8184257Slulf * 1. Redistributions of source code must retain the above copyright 9184257Slulf * notice, this list of conditions and the following disclaimer. 10184257Slulf * 2. Redistributions in binary form must reproduce the above copyright 11184257Slulf * notice, this list of conditions and the following disclaimer in the 12184257Slulf * documentation and/or other materials provided with the distribution. 13184257Slulf * 14184257Slulf * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15184257Slulf * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16184257Slulf * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17184257Slulf * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18184257Slulf * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19184257Slulf * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20184257Slulf * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21184257Slulf * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22184257Slulf * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23184257Slulf * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24184257Slulf * SUCH DAMAGE. 25184257Slulf * 26184257Slulf * $FreeBSD$ 27184257Slulf */ 28184257Slulf 29184257Slulf#include <errno.h> 30184257Slulf#include <string.h> 31184257Slulf#include <stdlib.h> 32184257Slulf#include <stdio.h> 33184257Slulf 34184257Slulf#include <sys/types.h> 35184257Slulf#include <sys/stat.h> 36184257Slulf#include <sys/mman.h> 37186698Slulf#include <fcntl.h> 38184257Slulf#include <unistd.h> 39184257Slulf 40184257Slulf#include "misc.h" 41184257Slulf#include "rsyncfile.h" 42184257Slulf 43184257Slulf#define MINBLOCKSIZE 1024 44184257Slulf#define MAXBLOCKSIZE (16 * 1024) 45184257Slulf#define RECEIVEBUFFERSIZE (15 * 1024) 46184257Slulf#define BLOCKINFOSIZE 26 47184257Slulf#define SEARCHREGION 10 48184257Slulf#define MAXBLOCKS (RECEIVEBUFFERSIZE / BLOCKINFOSIZE) 49184257Slulf 50184257Slulf#define CHAR_OFFSET 3 51184257Slulf#define RSUM_SIZE 9 52184257Slulf 53184257Slulfstruct rsyncfile { 54184257Slulf char *start; 55184257Slulf char *buf; 56184257Slulf char *end; 57184257Slulf size_t blocksize; 58184257Slulf size_t fsize; 59184257Slulf int fd; 60184257Slulf 61184257Slulf char *blockptr; 62184257Slulf int blocknum; 63184257Slulf char blockmd5[MD5_DIGEST_SIZE]; 64184257Slulf char rsumstr[RSUM_SIZE]; 65184257Slulf uint32_t rsum; 66184257Slulf}; 67184257Slulf 68185134Slulfstatic size_t rsync_chooseblocksize(size_t); 69185134Slulfstatic uint32_t rsync_rollsum(char *, size_t); 70184257Slulf 71184257Slulf/* Open a file and initialize variable for rsync operation. */ 72184257Slulfstruct rsyncfile * 73185130Slulfrsync_open(char *path, size_t blocksize, int rdonly) 74184257Slulf{ 75184257Slulf struct rsyncfile *rf; 76184257Slulf struct stat st; 77184257Slulf int error; 78184257Slulf 79185094Slulf rf = xmalloc(sizeof(*rf)); 80184257Slulf error = stat(path, &st); 81184257Slulf if (error) { 82184257Slulf free(rf); 83184257Slulf return (NULL); 84184257Slulf } 85184257Slulf rf->fsize = st.st_size; 86184257Slulf 87185130Slulf rf->fd = open(path, rdonly ? O_RDONLY : O_RDWR); 88184257Slulf if (rf->fd < 0) { 89184257Slulf free(rf); 90184257Slulf return (NULL); 91184257Slulf } 92184257Slulf rf->buf = mmap(0, rf->fsize, PROT_READ, MAP_SHARED, rf->fd, 0); 93184257Slulf if (rf->buf == MAP_FAILED) { 94184257Slulf free(rf); 95184257Slulf return (NULL); 96184257Slulf } 97184257Slulf rf->start = rf->buf; 98184257Slulf rf->end = rf->buf + rf->fsize; 99184257Slulf rf->blocksize = (blocksize == 0 ? rsync_chooseblocksize(rf->fsize) : 100184257Slulf blocksize); 101184257Slulf rf->blockptr = rf->buf; 102184257Slulf rf->blocknum = 0; 103184257Slulf return (rf); 104184257Slulf} 105184257Slulf 106184257Slulf/* Close and free all resources related to an rsync file transfer. */ 107184257Slulfint 108184257Slulfrsync_close(struct rsyncfile *rf) 109184257Slulf{ 110184257Slulf int error; 111184257Slulf 112184257Slulf error = munmap(rf->buf, rf->fsize); 113184257Slulf if (error) 114184257Slulf return (error); 115184257Slulf close(rf->fd); 116184257Slulf free(rf); 117185130Slulf return (0); 118184257Slulf} 119184257Slulf 120184257Slulf/* 121184257Slulf * Choose the most appropriate block size for an rsync transfer. Modeled 122184257Slulf * algorithm after cvsup. 123184257Slulf */ 124184257Slulfstatic size_t 125184257Slulfrsync_chooseblocksize(size_t fsize) 126184257Slulf{ 127184257Slulf size_t bestrem, blocksize, bs, hisearch, losearch, rem; 128184257Slulf 129184257Slulf blocksize = fsize / MAXBLOCKS; 130184257Slulf losearch = blocksize - SEARCHREGION; 131184257Slulf hisearch = blocksize + SEARCHREGION; 132184257Slulf 133184257Slulf if (losearch < MINBLOCKSIZE) { 134184257Slulf losearch = MINBLOCKSIZE; 135184257Slulf hisearch = losearch + (2 * SEARCHREGION); 136184257Slulf } else if (hisearch > MAXBLOCKSIZE) { 137184257Slulf hisearch = MAXBLOCKSIZE; 138184257Slulf losearch = hisearch - (2 * SEARCHREGION); 139184257Slulf } 140184257Slulf 141184257Slulf bestrem = MAXBLOCKSIZE; 142185128Slulf for (bs = losearch; bs <= hisearch; bs++) { 143184257Slulf rem = fsize % bs; 144184257Slulf if (rem < bestrem) { 145184257Slulf bestrem = rem; 146184257Slulf blocksize = bs; 147184257Slulf } 148184257Slulf } 149184257Slulf return (bestrem); 150184257Slulf} 151184257Slulf 152184257Slulf/* Get the next rsync block of a file. */ 153184257Slulfint 154184257Slulfrsync_nextblock(struct rsyncfile *rf) 155184257Slulf{ 156184257Slulf MD5_CTX ctx; 157185130Slulf size_t blocksize; 158184257Slulf 159184257Slulf if (rf->blockptr >= rf->end) 160184257Slulf return (0); 161185130Slulf blocksize = min((size_t)(rf->end - rf->blockptr), rf->blocksize); 162184257Slulf /* Calculate MD5 of the block. */ 163184257Slulf MD5_Init(&ctx); 164184257Slulf MD5_Update(&ctx, rf->blockptr, blocksize); 165184257Slulf MD5_End(rf->blockmd5, &ctx); 166184257Slulf 167184257Slulf rf->rsum = rsync_rollsum(rf->blockptr, blocksize); 168184257Slulf snprintf(rf->rsumstr, RSUM_SIZE, "%x", rf->rsum); 169184257Slulf rf->blocknum++; 170184257Slulf rf->blockptr += blocksize; 171184257Slulf return (1); 172184257Slulf} 173184257Slulf 174184257Slulf/* Get the rolling checksum of a file. */ 175184257Slulfstatic uint32_t 176185130Slulfrsync_rollsum(char *buf, size_t len) 177184257Slulf{ 178184257Slulf uint32_t a, b; 179185130Slulf char *ptr, *limit; 180184257Slulf 181184257Slulf a = b = 0; 182184257Slulf ptr = buf; 183184257Slulf limit = buf + len; 184184257Slulf 185184257Slulf while (ptr < limit) { 186184257Slulf a += *ptr + CHAR_OFFSET; 187184257Slulf b += a; 188184257Slulf ptr++; 189184257Slulf } 190184257Slulf return ((b << 16) | a); 191184257Slulf} 192184257Slulf 193184257Slulf/* Get running sum so far. */ 194184257Slulfchar * 195184257Slulfrsync_rsum(struct rsyncfile *rf) 196184257Slulf{ 197184257Slulf 198184257Slulf return (rf->rsumstr); 199184257Slulf} 200184257Slulf 201184257Slulf/* Get MD5 of current block. */ 202184257Slulfchar * 203184257Slulfrsync_blockmd5(struct rsyncfile *rf) 204184257Slulf{ 205184257Slulf 206184257Slulf return (rf->blockmd5); 207184257Slulf} 208184257Slulf 209184257Slulf/* Accessor for blocksize. */ 210184257Slulfsize_t 211184257Slulfrsync_blocksize(struct rsyncfile *rf) 212184257Slulf{ 213184257Slulf 214184257Slulf return (rf->blocksize); 215184257Slulf} 216184257Slulf 217184257Slulf/* Accessor for filesize. */ 218184257Slulfsize_t 219184257Slulfrsync_filesize(struct rsyncfile *rf) 220184257Slulf{ 221184257Slulf 222184257Slulf return (rf->fsize); 223184257Slulf} 224