rsyncfile.c revision 185094
155682Smarkm/*- 2233294Sstas * Copyright (c) 2008, Ulf Lilleengen <lulf@FreeBSD.org> 355682Smarkm * All rights reserved. 455682Smarkm * 5233294Sstas * Redistribution and use in source and binary forms, with or without 655682Smarkm * modification, are permitted provided that the following conditions 755682Smarkm * are met: 855682Smarkm * 1. Redistributions of source code must retain the above copyright 9233294Sstas * notice, this list of conditions and the following disclaimer. 1055682Smarkm * 2. Redistributions in binary form must reproduce the above copyright 1155682Smarkm * notice, this list of conditions and the following disclaimer in the 12233294Sstas * documentation and/or other materials provided with the distribution. 1355682Smarkm * 1455682Smarkm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1555682Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1755682Smarkm * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1855682Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1955682Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2155682Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2255682Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2355682Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2455682Smarkm * SUCH DAMAGE. 2555682Smarkm * 2655682Smarkm * $FreeBSD: projects/csup_cvsmode/contrib/csup/rsyncfile.c 185094 2008-11-19 14:57:00Z lulf $ 2755682Smarkm */ 2855682Smarkm 2955682Smarkm#include <errno.h> 3055682Smarkm#include <string.h> 3155682Smarkm#include <stdlib.h> 3255682Smarkm#include <stdio.h> 3355682Smarkm 3455682Smarkm#include <sys/types.h> 3555682Smarkm#include <sys/stat.h> 3655682Smarkm#include <sys/mman.h> 3755682Smarkm#include <unistd.h> 3855682Smarkm 39178825Sdfr#include "misc.h" 4055682Smarkm#include "fattr.h" 4155682Smarkm#include "rsyncfile.h" 4255682Smarkm 4355682Smarkm#define MINBLOCKSIZE 1024 4455682Smarkm#define MAXBLOCKSIZE (16 * 1024) 45233294Sstas#define RECEIVEBUFFERSIZE (15 * 1024) 4655682Smarkm#define BLOCKINFOSIZE 26 4755682Smarkm#define SEARCHREGION 10 4855682Smarkm#define MAXBLOCKS (RECEIVEBUFFERSIZE / BLOCKINFOSIZE) 4955682Smarkm 5055682Smarkm#define CHAR_OFFSET 3 5190926Snectar#define RSUM_SIZE 9 5255682Smarkm 5355682Smarkmstruct rsyncfile { 54 char *start; 55 char *buf; 56 char *end; 57 size_t blocksize; 58 size_t fsize; 59 struct fattr *fa; 60 int fd; 61 62 char *blockptr; 63 int blocknum; 64 char blockmd5[MD5_DIGEST_SIZE]; 65 char rsumstr[RSUM_SIZE]; 66 uint32_t rsum; 67}; 68 69static size_t rsync_chooseblocksize(size_t); 70static uint32_t rsync_rollsum(uint8_t *, size_t); 71 72/* Open a file and initialize variable for rsync operation. */ 73struct rsyncfile * 74rsync_open(char *path, size_t blocksize, int read) 75{ 76 struct rsyncfile *rf; 77 struct stat st; 78 int error; 79 80 rf = xmalloc(sizeof(*rf)); 81 error = stat(path, &st); 82 if (error) { 83 free(rf); 84 return (NULL); 85 } 86 rf->fsize = st.st_size; 87 rf->fa = fattr_fromstat(&st); 88 89 rf->fd = open(path, read ? O_RDONLY : O_RDWR); 90 if (rf->fd < 0) { 91 free(rf); 92 return (NULL); 93 } 94 rf->buf = mmap(0, rf->fsize, PROT_READ, MAP_SHARED, rf->fd, 0); 95 if (rf->buf == MAP_FAILED) { 96 free(rf); 97 return (NULL); 98 } 99 rf->start = rf->buf; 100 rf->end = rf->buf + rf->fsize; 101 rf->blocksize = (blocksize == 0 ? rsync_chooseblocksize(rf->fsize) : 102 blocksize); 103 rf->blockptr = rf->buf; 104 rf->blocknum = 0; 105 return (rf); 106} 107 108/* Close and free all resources related to an rsync file transfer. */ 109int 110rsync_close(struct rsyncfile *rf) 111{ 112 int error; 113 114 error = munmap(rf->buf, rf->fsize); 115 if (error) 116 return (error); 117 close(rf->fd); 118 free(rf); 119} 120 121/* 122 * Choose the most appropriate block size for an rsync transfer. Modeled 123 * algorithm after cvsup. 124 */ 125static size_t 126rsync_chooseblocksize(size_t fsize) 127{ 128 size_t bestrem, blocksize, bs, hisearch, losearch, rem; 129 130 blocksize = fsize / MAXBLOCKS; 131 losearch = blocksize - SEARCHREGION; 132 hisearch = blocksize + SEARCHREGION; 133 134 if (losearch < MINBLOCKSIZE) { 135 losearch = MINBLOCKSIZE; 136 hisearch = losearch + (2 * SEARCHREGION); 137 } else if (hisearch > MAXBLOCKSIZE) { 138 hisearch = MAXBLOCKSIZE; 139 losearch = hisearch - (2 * SEARCHREGION); 140 } 141 142 bestrem = MAXBLOCKSIZE; 143 for (bs = losearch; bs <= hisearch;) { 144 rem = fsize % bs; 145 if (rem < bestrem) { 146 bestrem = rem; 147 blocksize = bs; 148 } 149 } 150 return (bestrem); 151} 152 153/* Get the next rsync block of a file. */ 154int 155rsync_nextblock(struct rsyncfile *rf) 156{ 157 uint32_t rolling; 158 char *ptr; 159 MD5_CTX ctx; 160 size_t blocksize, i; 161 162 if (rf->blockptr >= rf->end) 163 return (0); 164 blocksize = min((rf->end - rf->blockptr), rf->blocksize); 165 /* Calculate MD5 of the block. */ 166 MD5_Init(&ctx); 167 MD5_Update(&ctx, rf->blockptr, blocksize); 168 MD5_End(rf->blockmd5, &ctx); 169 170 rf->rsum = rsync_rollsum(rf->blockptr, blocksize); 171 snprintf(rf->rsumstr, RSUM_SIZE, "%x", rf->rsum); 172 rf->blocknum++; 173 rf->blockptr += blocksize; 174 return (1); 175} 176 177/* Get the rolling checksum of a file. */ 178static uint32_t 179rsync_rollsum(uint8_t *buf, size_t len) 180{ 181 uint32_t a, b; 182 uint8_t *ptr, *limit; 183 184 a = b = 0; 185 ptr = buf; 186 limit = buf + len; 187 188 while (ptr < limit) { 189 a += *ptr + CHAR_OFFSET; 190 b += a; 191 ptr++; 192 } 193 return ((b << 16) | a); 194} 195 196/* Get running sum so far. */ 197char * 198rsync_rsum(struct rsyncfile *rf) 199{ 200 201 return (rf->rsumstr); 202} 203 204/* Get MD5 of current block. */ 205char * 206rsync_blockmd5(struct rsyncfile *rf) 207{ 208 209 return (rf->blockmd5); 210} 211 212/* Accessor for blocksize. */ 213size_t 214rsync_blocksize(struct rsyncfile *rf) 215{ 216 217 return (rf->blocksize); 218} 219 220/* Accessor for filesize. */ 221size_t 222rsync_filesize(struct rsyncfile *rf) 223{ 224 225 return (rf->fsize); 226} 227