author | kergoth <kergoth> | 2002-01-25 22:14:26 (UTC) |
---|---|---|
committer | kergoth <kergoth> | 2002-01-25 22:14:26 (UTC) |
commit | 15318cad33835e4e2dc620d033e43cd930676cdd (patch) (unidiff) | |
tree | c2fa0399a2c47fda8e2cd0092c73a809d17f68eb /rsync/patch.c | |
download | opie-15318cad33835e4e2dc620d033e43cd930676cdd.zip opie-15318cad33835e4e2dc620d033e43cd930676cdd.tar.gz opie-15318cad33835e4e2dc620d033e43cd930676cdd.tar.bz2 |
Initial revision
-rw-r--r-- | rsync/patch.c | 317 |
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 | |||
48 | static rs_result rs_patch_s_cmdbyte(rs_job_t *); | ||
49 | static rs_result rs_patch_s_params(rs_job_t *); | ||
50 | static rs_result rs_patch_s_run(rs_job_t *); | ||
51 | static rs_result rs_patch_s_literal(rs_job_t *); | ||
52 | static rs_result rs_patch_s_copy(rs_job_t *); | ||
53 | static 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 | */ | ||
61 | static 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 | */ | ||
89 | static 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 | */ | ||
121 | static 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 | */ | ||
148 | static 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 | |||
166 | static 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 | */ | ||
204 | static 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 | */ | ||
256 | static 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 | */ | ||
306 | rs_job_t * | ||
307 | rs_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 | } | ||