summaryrefslogtreecommitdiff
path: root/rsync/patch.c
Unidiff
Diffstat (limited to 'rsync/patch.c') (more/less context) (ignore whitespace changes)
-rw-r--r--rsync/patch.c317
1 files changed, 317 insertions, 0 deletions
diff --git a/rsync/patch.c b/rsync/patch.c
new file mode 100644
index 0000000..d2daa85
--- a/dev/null
+++ b/rsync/patch.c
@@ -0,0 +1,317 @@
1/*= -*- c-basic-offset: 4; indent-tabs-mode: nil; -*-
2 *
3 * librsync -- the library for network deltas
4 * $Id$
5 *
6 * Copyright (C) 2000, 2001 by Martin Pool <mbp@samba.org>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 2.1 of
11 * the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23
24 /*
25 | This is Tranquility Base.
26 */
27
28
29#include <config_rsync.h>
30
31#include <assert.h>
32#include <stdlib.h>
33#include <stdio.h>
34
35#include "rsync.h"
36#include "util.h"
37#include "trace.h"
38#include "protocol.h"
39#include "netint.h"
40#include "command.h"
41#include "sumset.h"
42#include "prototab.h"
43#include "stream.h"
44#include "job.h"
45
46
47
48static rs_result rs_patch_s_cmdbyte(rs_job_t *);
49static rs_result rs_patch_s_params(rs_job_t *);
50static rs_result rs_patch_s_run(rs_job_t *);
51static rs_result rs_patch_s_literal(rs_job_t *);
52static rs_result rs_patch_s_copy(rs_job_t *);
53static rs_result rs_patch_s_copying(rs_job_t *);
54
55
56/**
57 * State of trying to read the first byte of a command. Once we've
58 * taken that in, we can know how much data to read to get the
59 * arguments.
60 */
61static rs_result rs_patch_s_cmdbyte(rs_job_t *job)
62{
63 rs_result result;
64
65 if ((result = rs_suck_byte(job, &job->op)) != RS_DONE)
66 return result;
67
68 job->cmd = &rs_prototab[job->op];
69
70 rs_trace("got command byte 0x%02x (%s), len_1=%.0f", job->op,
71 rs_op_kind_name(job->cmd->kind),
72 (double) job->cmd->len_1);
73
74 if (job->cmd->len_1)
75 job->statefn = rs_patch_s_params;
76 else {
77 job->param1 = job->cmd->immediate;
78 job->statefn = rs_patch_s_run;
79 }
80
81 return RS_RUNNING;
82}
83
84
85/**
86 * Called after reading a command byte to pull in its parameters and
87 * then setup to execute the command.
88 */
89static rs_result rs_patch_s_params(rs_job_t *job)
90{
91 rs_result result;
92 int len = job->cmd->len_1 + job->cmd->len_2;
93 void *p;
94
95 assert(len);
96
97 result = rs_scoop_readahead(job, len, &p);
98 if (result != RS_DONE)
99 return result;
100
101 /* we now must have LEN bytes buffered */
102 result = rs_suck_netint(job, &job->param1, job->cmd->len_1);
103 /* shouldn't fail, since we already checked */
104 assert(result == RS_DONE);
105
106 if (job->cmd->len_2) {
107 result = rs_suck_netint(job, &job->param2, job->cmd->len_2);
108 assert(result == RS_DONE);
109 }
110
111 job->statefn = rs_patch_s_run;
112
113 return RS_RUNNING;
114}
115
116
117
118/**
119 * Called when we've read in the whole command and we need to execute it.
120 */
121static rs_result rs_patch_s_run(rs_job_t *job)
122{
123 rs_trace("running command 0x%x, kind %d", job->op, job->cmd->kind);
124
125 switch (job->cmd->kind) {
126 case RS_KIND_LITERAL:
127 job->statefn = rs_patch_s_literal;
128 return RS_RUNNING;
129
130 case RS_KIND_END:
131 return RS_DONE;
132 /* so we exit here; trying to continue causes an error */
133
134 case RS_KIND_COPY:
135 job->statefn = rs_patch_s_copy;
136 return RS_RUNNING;
137
138 default:
139 rs_error("bogus command 0x%02x", job->op);
140 return RS_CORRUPT;
141 }
142}
143
144
145/**
146 * Called when trying to copy through literal data.
147 */
148static rs_result rs_patch_s_literal(rs_job_t *job)
149{
150 rs_long_t len = job->param1;
151
152 rs_trace("LITERAL(len=%.0f)", (double) len);
153
154 job->stats.lit_cmds++;
155 job->stats.lit_bytes += len;
156 job->stats.lit_cmdbytes += 1 + job->cmd->len_1;
157
158 rs_tube_copy(job, len);
159
160 job->statefn = rs_patch_s_cmdbyte;
161 return RS_RUNNING;
162}
163
164
165
166static rs_result rs_patch_s_copy(rs_job_t *job)
167{
168 rs_long_t where, len;
169 rs_stats_t *stats;
170
171 where = job->param1;
172 len = job->param2;
173
174 rs_trace("COPY(where=%.0f, len=%.0f)", (double) where, (double) len);
175
176 if (len < 0) {
177 rs_log(RS_LOG_ERR, "invalid length=%.0f on COPY command", (double) len);
178 return RS_CORRUPT;
179 }
180
181 if (where < 0) {
182 rs_log(RS_LOG_ERR, "invalid where=%.0f on COPY command", (double) where);
183 return RS_CORRUPT;
184 }
185
186 job->basis_pos = where;
187 job->basis_len = len;
188
189 stats = &job->stats;
190
191 stats->copy_cmds++;
192 stats->copy_bytes += len;
193 stats->copy_cmdbytes += 1 + job->cmd->len_1 + job->cmd->len_2;
194
195 job->statefn = rs_patch_s_copying;
196 return RS_RUNNING;
197}
198
199
200/**
201 * Called when we're executing a COPY command and waiting for all the
202 * data to be retrieved from the callback.
203 */
204static rs_result rs_patch_s_copying(rs_job_t *job)
205{
206 rs_result result;
207 size_t len;
208 void *buf, *ptr;
209 rs_buffers_t *buffs = job->stream;
210
211 len = job->basis_len;
212
213 /* copy only as much as will fit in the output buffer, so that we
214 * don't have to block or store the input. */
215 if (len > buffs->avail_out)
216 len = buffs->avail_out;
217
218 if (!len)
219 return RS_BLOCKED;
220
221 rs_trace("copy %.0f bytes from basis at offset %.0f",
222 (double) len, (double) job->basis_pos);
223
224 ptr = buf = rs_alloc(len, "basis buffer");
225
226 result = (job->copy_cb)(job->copy_arg, job->basis_pos, &len, &ptr);
227 if (result != RS_DONE)
228 return result;
229 else
230 rs_trace("copy callback returned %s", rs_strerror(result));
231
232 rs_trace("got %.0f bytes back from basis callback", (double) len);
233
234 memcpy(buffs->next_out, ptr, len);
235
236 buffs->next_out += len;
237 buffs->avail_out -= len;
238
239 job->basis_pos += len;
240 job->basis_len -= len;
241
242 free(buf);
243
244 if (!job->basis_len) {
245 /* Done! */
246 job->statefn = rs_patch_s_cmdbyte;
247 }
248
249 return RS_RUNNING;
250}
251
252
253/**
254 * Called while we're trying to read the header of the patch.
255 */
256static rs_result rs_patch_s_header(rs_job_t *job)
257{
258 int v;
259 rs_result result;
260
261
262 if ((result = rs_suck_n4(job, &v)) != RS_DONE)
263 return result;
264
265 if (v != RS_DELTA_MAGIC) {
266 rs_log(RS_LOG_ERR,
267 "got magic number %#x rather than expected value %#x",
268 v, RS_DELTA_MAGIC);
269 return RS_BAD_MAGIC;
270 } else
271 rs_trace("got patch magic %#x", v);
272
273
274 job->statefn = rs_patch_s_cmdbyte;
275
276 return RS_RUNNING;
277}
278
279
280
281/**
282 * \brief Apply a \ref gloss_delta to a \ref gloss_basis to recreate
283 * the new file.
284 *
285 * This gives you back a ::rs_job_t object, which can be cranked by
286 * calling rs_job_iter() and updating the stream pointers. When
287 * finished, call rs_job_finish() to dispose of it.
288 *
289 * \param stream Contains pointers to input and output buffers, to be
290 * adjusted by caller on each iteration.
291 *
292 * \param copy_cb Callback used to retrieve content from the basis
293 * file.
294 *
295 * \param copy_arg Opaque environment pointer passed through to the
296 * callback.
297 *
298 * \todo As output is produced, accumulate the MD4 checksum of the
299 * output. Then if we find a CHECKSUM command we can check it's
300 * contents against the output.
301 *
302 * \todo Implement COPY commands.
303 *
304 * \sa rs_patch_file()
305 */
306rs_job_t *
307rs_patch_begin(rs_copy_cb *copy_cb, void *copy_arg)
308{
309 rs_job_t *job = rs_job_new("patch", rs_patch_s_header);
310
311 job->copy_cb = copy_cb;
312 job->copy_arg = copy_arg;
313
314 rs_mdfour_begin(&job->output_md4);
315
316 return job;
317}