rsyncfile.c revision 185130
1/*- 2 * Copyright (c) 2008, 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: projects/csup_cvsmode/contrib/csup/rsyncfile.c 185130 2008-11-20 12:52:07Z lulf $ 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 <unistd.h> 38 39#include "misc.h" 40#include "fattr.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 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(char *, size_t); 71 72/* Open a file and initialize variable for rsync operation. */ 73struct rsyncfile * 74rsync_open(char *path, size_t blocksize, int rdonly) 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, rdonly ? 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 return (0); 120} 121 122/* 123 * Choose the most appropriate block size for an rsync transfer. Modeled 124 * algorithm after cvsup. 125 */ 126static size_t 127rsync_chooseblocksize(size_t fsize) 128{ 129 size_t bestrem, blocksize, bs, hisearch, losearch, rem; 130 131 blocksize = fsize / MAXBLOCKS; 132 losearch = blocksize - SEARCHREGION; 133 hisearch = blocksize + SEARCHREGION; 134 135 if (losearch < MINBLOCKSIZE) { 136 losearch = MINBLOCKSIZE; 137 hisearch = losearch + (2 * SEARCHREGION); 138 } else if (hisearch > MAXBLOCKSIZE) { 139 hisearch = MAXBLOCKSIZE; 140 losearch = hisearch - (2 * SEARCHREGION); 141 } 142 143 bestrem = MAXBLOCKSIZE; 144 for (bs = losearch; bs <= hisearch; bs++) { 145 rem = fsize % bs; 146 if (rem < bestrem) { 147 bestrem = rem; 148 blocksize = bs; 149 } 150 } 151 return (bestrem); 152} 153 154/* Get the next rsync block of a file. */ 155int 156rsync_nextblock(struct rsyncfile *rf) 157{ 158 MD5_CTX ctx; 159 size_t blocksize; 160 161 if (rf->blockptr >= rf->end) 162 return (0); 163 blocksize = min((size_t)(rf->end - rf->blockptr), rf->blocksize); 164 /* Calculate MD5 of the block. */ 165 MD5_Init(&ctx); 166 MD5_Update(&ctx, rf->blockptr, blocksize); 167 MD5_End(rf->blockmd5, &ctx); 168 169 rf->rsum = rsync_rollsum(rf->blockptr, blocksize); 170 snprintf(rf->rsumstr, RSUM_SIZE, "%x", rf->rsum); 171 rf->blocknum++; 172 rf->blockptr += blocksize; 173 return (1); 174} 175 176/* Get the rolling checksum of a file. */ 177static uint32_t 178rsync_rollsum(char *buf, size_t len) 179{ 180 uint32_t a, b; 181 char *ptr, *limit; 182 183 a = b = 0; 184 ptr = buf; 185 limit = buf + len; 186 187 while (ptr < limit) { 188 a += *ptr + CHAR_OFFSET; 189 b += a; 190 ptr++; 191 } 192 return ((b << 16) | a); 193} 194 195/* Get running sum so far. */ 196char * 197rsync_rsum(struct rsyncfile *rf) 198{ 199 200 return (rf->rsumstr); 201} 202 203/* Get MD5 of current block. */ 204char * 205rsync_blockmd5(struct rsyncfile *rf) 206{ 207 208 return (rf->blockmd5); 209} 210 211/* Accessor for blocksize. */ 212size_t 213rsync_blocksize(struct rsyncfile *rf) 214{ 215 216 return (rf->blocksize); 217} 218 219/* Accessor for filesize. */ 220size_t 221rsync_filesize(struct rsyncfile *rf) 222{ 223 224 return (rf->fsize); 225} 226