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