rsyncfile.c revision 185130
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 185130 2008-11-20 12:52:07Z 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); 70185130Slulfstatic uint32_t rsync_rollsum(char *, size_t); 71184257Slulf 72184257Slulf/* Open a file and initialize variable for rsync operation. */ 73184257Slulfstruct rsyncfile * 74185130Slulfrsync_open(char *path, size_t blocksize, int rdonly) 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 89185130Slulf rf->fd = open(path, rdonly ? 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); 119185130Slulf return (0); 120184257Slulf} 121184257Slulf 122184257Slulf/* 123184257Slulf * Choose the most appropriate block size for an rsync transfer. Modeled 124184257Slulf * algorithm after cvsup. 125184257Slulf */ 126184257Slulfstatic size_t 127184257Slulfrsync_chooseblocksize(size_t fsize) 128184257Slulf{ 129184257Slulf size_t bestrem, blocksize, bs, hisearch, losearch, rem; 130184257Slulf 131184257Slulf blocksize = fsize / MAXBLOCKS; 132184257Slulf losearch = blocksize - SEARCHREGION; 133184257Slulf hisearch = blocksize + SEARCHREGION; 134184257Slulf 135184257Slulf if (losearch < MINBLOCKSIZE) { 136184257Slulf losearch = MINBLOCKSIZE; 137184257Slulf hisearch = losearch + (2 * SEARCHREGION); 138184257Slulf } else if (hisearch > MAXBLOCKSIZE) { 139184257Slulf hisearch = MAXBLOCKSIZE; 140184257Slulf losearch = hisearch - (2 * SEARCHREGION); 141184257Slulf } 142184257Slulf 143184257Slulf bestrem = MAXBLOCKSIZE; 144185128Slulf for (bs = losearch; bs <= hisearch; bs++) { 145184257Slulf rem = fsize % bs; 146184257Slulf if (rem < bestrem) { 147184257Slulf bestrem = rem; 148184257Slulf blocksize = bs; 149184257Slulf } 150184257Slulf } 151184257Slulf return (bestrem); 152184257Slulf} 153184257Slulf 154184257Slulf/* Get the next rsync block of a file. */ 155184257Slulfint 156184257Slulfrsync_nextblock(struct rsyncfile *rf) 157184257Slulf{ 158184257Slulf MD5_CTX ctx; 159185130Slulf size_t blocksize; 160184257Slulf 161184257Slulf if (rf->blockptr >= rf->end) 162184257Slulf return (0); 163185130Slulf blocksize = min((size_t)(rf->end - rf->blockptr), rf->blocksize); 164184257Slulf /* Calculate MD5 of the block. */ 165184257Slulf MD5_Init(&ctx); 166184257Slulf MD5_Update(&ctx, rf->blockptr, blocksize); 167184257Slulf MD5_End(rf->blockmd5, &ctx); 168184257Slulf 169184257Slulf rf->rsum = rsync_rollsum(rf->blockptr, blocksize); 170184257Slulf snprintf(rf->rsumstr, RSUM_SIZE, "%x", rf->rsum); 171184257Slulf rf->blocknum++; 172184257Slulf rf->blockptr += blocksize; 173184257Slulf return (1); 174184257Slulf} 175184257Slulf 176184257Slulf/* Get the rolling checksum of a file. */ 177184257Slulfstatic uint32_t 178185130Slulfrsync_rollsum(char *buf, size_t len) 179184257Slulf{ 180184257Slulf uint32_t a, b; 181185130Slulf char *ptr, *limit; 182184257Slulf 183184257Slulf a = b = 0; 184184257Slulf ptr = buf; 185184257Slulf limit = buf + len; 186184257Slulf 187184257Slulf while (ptr < limit) { 188184257Slulf a += *ptr + CHAR_OFFSET; 189184257Slulf b += a; 190184257Slulf ptr++; 191184257Slulf } 192184257Slulf return ((b << 16) | a); 193184257Slulf} 194184257Slulf 195184257Slulf/* Get running sum so far. */ 196184257Slulfchar * 197184257Slulfrsync_rsum(struct rsyncfile *rf) 198184257Slulf{ 199184257Slulf 200184257Slulf return (rf->rsumstr); 201184257Slulf} 202184257Slulf 203184257Slulf/* Get MD5 of current block. */ 204184257Slulfchar * 205184257Slulfrsync_blockmd5(struct rsyncfile *rf) 206184257Slulf{ 207184257Slulf 208184257Slulf return (rf->blockmd5); 209184257Slulf} 210184257Slulf 211184257Slulf/* Accessor for blocksize. */ 212184257Slulfsize_t 213184257Slulfrsync_blocksize(struct rsyncfile *rf) 214184257Slulf{ 215184257Slulf 216184257Slulf return (rf->blocksize); 217184257Slulf} 218184257Slulf 219184257Slulf/* Accessor for filesize. */ 220184257Slulfsize_t 221184257Slulfrsync_filesize(struct rsyncfile *rf) 222184257Slulf{ 223184257Slulf 224184257Slulf return (rf->fsize); 225184257Slulf} 226