rsyncfile.c revision 185128
1184257Slulf/*- 2184257Slulf * Copyright (c) 2008, 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: projects/csup_cvsmode/contrib/csup/rsyncfile.c 185128 2008-11-20 12:23:44Z lulf $ 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> 37184257Slulf#include <unistd.h> 38184257Slulf 39184257Slulf#include "misc.h" 40184257Slulf#include "fattr.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 struct fattr *fa; 60184257Slulf int fd; 61184257Slulf 62184257Slulf char *blockptr; 63184257Slulf int blocknum; 64184257Slulf char blockmd5[MD5_DIGEST_SIZE]; 65184257Slulf char rsumstr[RSUM_SIZE]; 66184257Slulf uint32_t rsum; 67184257Slulf}; 68184257Slulf 69184257Slulfstatic size_t rsync_chooseblocksize(size_t); 70184257Slulfstatic uint32_t rsync_rollsum(uint8_t *, size_t); 71184257Slulf 72184257Slulf/* Open a file and initialize variable for rsync operation. */ 73184257Slulfstruct rsyncfile * 74184257Slulfrsync_open(char *path, size_t blocksize, int read) 75184257Slulf{ 76184257Slulf struct rsyncfile *rf; 77184257Slulf struct stat st; 78184257Slulf int error; 79184257Slulf 80185094Slulf rf = xmalloc(sizeof(*rf)); 81184257Slulf error = stat(path, &st); 82184257Slulf if (error) { 83184257Slulf free(rf); 84184257Slulf return (NULL); 85184257Slulf } 86184257Slulf rf->fsize = st.st_size; 87184257Slulf rf->fa = fattr_fromstat(&st); 88184257Slulf 89184257Slulf rf->fd = open(path, read ? O_RDONLY : O_RDWR); 90184257Slulf if (rf->fd < 0) { 91184257Slulf free(rf); 92184257Slulf return (NULL); 93184257Slulf } 94184257Slulf rf->buf = mmap(0, rf->fsize, PROT_READ, MAP_SHARED, rf->fd, 0); 95184257Slulf if (rf->buf == MAP_FAILED) { 96184257Slulf free(rf); 97184257Slulf return (NULL); 98184257Slulf } 99184257Slulf rf->start = rf->buf; 100184257Slulf rf->end = rf->buf + rf->fsize; 101184257Slulf rf->blocksize = (blocksize == 0 ? rsync_chooseblocksize(rf->fsize) : 102184257Slulf blocksize); 103184257Slulf rf->blockptr = rf->buf; 104184257Slulf rf->blocknum = 0; 105184257Slulf return (rf); 106184257Slulf} 107184257Slulf 108184257Slulf/* Close and free all resources related to an rsync file transfer. */ 109184257Slulfint 110184257Slulfrsync_close(struct rsyncfile *rf) 111184257Slulf{ 112184257Slulf int error; 113184257Slulf 114184257Slulf error = munmap(rf->buf, rf->fsize); 115184257Slulf if (error) 116184257Slulf return (error); 117184257Slulf close(rf->fd); 118184257Slulf free(rf); 119184257Slulf} 120184257Slulf 121184257Slulf/* 122184257Slulf * Choose the most appropriate block size for an rsync transfer. Modeled 123184257Slulf * algorithm after cvsup. 124184257Slulf */ 125184257Slulfstatic size_t 126184257Slulfrsync_chooseblocksize(size_t fsize) 127184257Slulf{ 128184257Slulf size_t bestrem, blocksize, bs, hisearch, losearch, rem; 129184257Slulf 130184257Slulf blocksize = fsize / MAXBLOCKS; 131184257Slulf losearch = blocksize - SEARCHREGION; 132184257Slulf hisearch = blocksize + SEARCHREGION; 133184257Slulf 134184257Slulf if (losearch < MINBLOCKSIZE) { 135184257Slulf losearch = MINBLOCKSIZE; 136184257Slulf hisearch = losearch + (2 * SEARCHREGION); 137184257Slulf } else if (hisearch > MAXBLOCKSIZE) { 138184257Slulf hisearch = MAXBLOCKSIZE; 139184257Slulf losearch = hisearch - (2 * SEARCHREGION); 140184257Slulf } 141184257Slulf 142184257Slulf bestrem = MAXBLOCKSIZE; 143185128Slulf for (bs = losearch; bs <= hisearch; bs++) { 144184257Slulf rem = fsize % bs; 145184257Slulf if (rem < bestrem) { 146184257Slulf bestrem = rem; 147184257Slulf blocksize = bs; 148184257Slulf } 149184257Slulf } 150184257Slulf return (bestrem); 151184257Slulf} 152184257Slulf 153184257Slulf/* Get the next rsync block of a file. */ 154184257Slulfint 155184257Slulfrsync_nextblock(struct rsyncfile *rf) 156184257Slulf{ 157184257Slulf uint32_t rolling; 158184257Slulf char *ptr; 159184257Slulf MD5_CTX ctx; 160184257Slulf size_t blocksize, i; 161184257Slulf 162184257Slulf if (rf->blockptr >= rf->end) 163184257Slulf return (0); 164184257Slulf blocksize = min((rf->end - rf->blockptr), rf->blocksize); 165184257Slulf /* Calculate MD5 of the block. */ 166184257Slulf MD5_Init(&ctx); 167184257Slulf MD5_Update(&ctx, rf->blockptr, blocksize); 168184257Slulf MD5_End(rf->blockmd5, &ctx); 169184257Slulf 170184257Slulf rf->rsum = rsync_rollsum(rf->blockptr, blocksize); 171184257Slulf snprintf(rf->rsumstr, RSUM_SIZE, "%x", rf->rsum); 172184257Slulf rf->blocknum++; 173184257Slulf rf->blockptr += blocksize; 174184257Slulf return (1); 175184257Slulf} 176184257Slulf 177184257Slulf/* Get the rolling checksum of a file. */ 178184257Slulfstatic uint32_t 179184257Slulfrsync_rollsum(uint8_t *buf, size_t len) 180184257Slulf{ 181184257Slulf uint32_t a, b; 182184257Slulf uint8_t *ptr, *limit; 183184257Slulf 184184257Slulf a = b = 0; 185184257Slulf ptr = buf; 186184257Slulf limit = buf + len; 187184257Slulf 188184257Slulf while (ptr < limit) { 189184257Slulf a += *ptr + CHAR_OFFSET; 190184257Slulf b += a; 191184257Slulf ptr++; 192184257Slulf } 193184257Slulf return ((b << 16) | a); 194184257Slulf} 195184257Slulf 196184257Slulf/* Get running sum so far. */ 197184257Slulfchar * 198184257Slulfrsync_rsum(struct rsyncfile *rf) 199184257Slulf{ 200184257Slulf 201184257Slulf return (rf->rsumstr); 202184257Slulf} 203184257Slulf 204184257Slulf/* Get MD5 of current block. */ 205184257Slulfchar * 206184257Slulfrsync_blockmd5(struct rsyncfile *rf) 207184257Slulf{ 208184257Slulf 209184257Slulf return (rf->blockmd5); 210184257Slulf} 211184257Slulf 212184257Slulf/* Accessor for blocksize. */ 213184257Slulfsize_t 214184257Slulfrsync_blocksize(struct rsyncfile *rf) 215184257Slulf{ 216184257Slulf 217184257Slulf return (rf->blocksize); 218184257Slulf} 219184257Slulf 220184257Slulf/* Accessor for filesize. */ 221184257Slulfsize_t 222184257Slulfrsync_filesize(struct rsyncfile *rf) 223184257Slulf{ 224184257Slulf 225184257Slulf return (rf->fsize); 226184257Slulf} 227