1 /*
2 * svndiff.c -- Encoding and decoding svndiff-format deltas.
3 *
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 * ====================================================================
22 */
23
24
25 #include <assert.h>
26 #include <string.h>
27 #include "svn_delta.h"
28 #include "svn_io.h"
29 #include "delta.h"
30 #include "svn_pools.h"
31 #include "svn_private_config.h"
32
33 #include "private/svn_error_private.h"
34 #include "private/svn_delta_private.h"
35 #include "private/svn_subr_private.h"
36 #include "private/svn_string_private.h"
37 #include "private/svn_dep_compat.h"
38
39 /* ----- Text delta to svndiff ----- */
40
41 /* We make one of these and get it passed back to us in calls to the
42 window handler. We only use it to record the write function and
43 baton passed to svn_txdelta_to_svndiff3(). */
44 struct encoder_baton {
45 svn_stream_t *output;
46 svn_boolean_t header_done;
47 int version;
48 int compression_level;
49 apr_pool_t *pool;
50 };
51
52 /* This is at least as big as the largest size for a single instruction. */
53 #define MAX_INSTRUCTION_LEN (2*SVN__MAX_ENCODED_UINT_LEN+1)
54 /* This is at least as big as the largest possible instructions
55 section: in theory, the instructions could be SVN_DELTA_WINDOW_SIZE
56 1-byte copy-from-source instructions (though this is very unlikely). */
57 #define MAX_INSTRUCTION_SECTION_LEN (SVN_DELTA_WINDOW_SIZE*MAX_INSTRUCTION_LEN)
58
59
60 /* Append an encoded integer to a string. */
61 static void
append_encoded_int(svn_stringbuf_t * header,svn_filesize_t val)62 append_encoded_int(svn_stringbuf_t *header, svn_filesize_t val)
63 {
64 unsigned char buf[SVN__MAX_ENCODED_UINT_LEN], *p;
65
66 SVN_ERR_ASSERT_NO_RETURN(val >= 0);
67 p = svn__encode_uint(buf, (apr_uint64_t)val);
68 svn_stringbuf_appendbytes(header, (const char *)buf, p - buf);
69 }
70
71 static svn_error_t *
send_simple_insertion_window(svn_txdelta_window_t * window,struct encoder_baton * eb)72 send_simple_insertion_window(svn_txdelta_window_t *window,
73 struct encoder_baton *eb)
74 {
75 unsigned char headers[4 + 5 * SVN__MAX_ENCODED_UINT_LEN
76 + MAX_INSTRUCTION_LEN];
77 unsigned char ibuf[MAX_INSTRUCTION_LEN];
78 unsigned char *header_current;
79 apr_size_t header_len;
80 apr_size_t ip_len, i;
81 apr_size_t len = window->new_data->len;
82
83 /* there is only one target copy op. It must span the whole window */
84 assert(window->ops[0].action_code == svn_txdelta_new);
85 assert(window->ops[0].length == window->tview_len);
86 assert(window->ops[0].offset == 0);
87
88 /* write stream header if necessary */
89 if (!eb->header_done)
90 {
91 eb->header_done = TRUE;
92 headers[0] = 'S';
93 headers[1] = 'V';
94 headers[2] = 'N';
95 headers[3] = (unsigned char)eb->version;
96 header_current = headers + 4;
97 }
98 else
99 {
100 header_current = headers;
101 }
102
103 /* Encode the action code and length. */
104 if (window->tview_len >> 6 == 0)
105 {
106 ibuf[0] = (unsigned char)(window->tview_len + (0x2 << 6));
107 ip_len = 1;
108 }
109 else
110 {
111 ibuf[0] = (0x2 << 6);
112 ip_len = svn__encode_uint(ibuf + 1, window->tview_len) - ibuf;
113 }
114
115 /* encode the window header. Please note that the source window may
116 * have content despite not being used for deltification. */
117 header_current = svn__encode_uint(header_current,
118 (apr_uint64_t)window->sview_offset);
119 header_current = svn__encode_uint(header_current, window->sview_len);
120 header_current = svn__encode_uint(header_current, window->tview_len);
121 header_current[0] = (unsigned char)ip_len; /* 1 instruction */
122 header_current = svn__encode_uint(&header_current[1], len);
123
124 /* append instructions (1 to a handful of bytes) */
125 for (i = 0; i < ip_len; ++i)
126 header_current[i] = ibuf[i];
127
128 header_len = header_current - headers + ip_len;
129
130 /* Write out the window. */
131 SVN_ERR(svn_stream_write(eb->output, (const char *)headers, &header_len));
132 if (len)
133 SVN_ERR(svn_stream_write(eb->output, window->new_data->data, &len));
134
135 return SVN_NO_ERROR;
136 }
137
138 static svn_error_t *
window_handler(svn_txdelta_window_t * window,void * baton)139 window_handler(svn_txdelta_window_t *window, void *baton)
140 {
141 struct encoder_baton *eb = baton;
142 apr_pool_t *pool;
143 svn_stringbuf_t *instructions;
144 svn_stringbuf_t *i1;
145 svn_stringbuf_t *header;
146 const svn_string_t *newdata;
147 unsigned char ibuf[MAX_INSTRUCTION_LEN], *ip;
148 const svn_txdelta_op_t *op;
149 apr_size_t len;
150
151 /* use specialized code if there is no source */
152 if (window && !window->src_ops && window->num_ops == 1 && !eb->version)
153 return svn_error_trace(send_simple_insertion_window(window, eb));
154
155 /* Make sure we write the header. */
156 if (!eb->header_done)
157 {
158 char svnver[4] = {'S','V','N','\0'};
159 len = 4;
160 svnver[3] = (char)eb->version;
161 SVN_ERR(svn_stream_write(eb->output, svnver, &len));
162 eb->header_done = TRUE;
163 }
164
165 if (window == NULL)
166 {
167 svn_stream_t *output = eb->output;
168
169 /* We're done; clean up.
170
171 We clean our pool first. Given that the output stream was passed
172 TO us, we'll assume it has a longer lifetime, and that it will not
173 be affected by our pool destruction.
174
175 The contrary point of view (close the stream first): that could
176 tell our user that everything related to the output stream is done,
177 and a cleanup of the user pool should occur. However, that user
178 pool could include the subpool we created for our work (eb->pool),
179 which would then make our call to svn_pool_destroy() puke.
180 */
181 svn_pool_destroy(eb->pool);
182
183 return svn_stream_close(output);
184 }
185
186 /* create the necessary data buffers */
187 pool = svn_pool_create(eb->pool);
188 instructions = svn_stringbuf_create_empty(pool);
189 i1 = svn_stringbuf_create_empty(pool);
190 header = svn_stringbuf_create_empty(pool);
191
192 /* Encode the instructions. */
193 for (op = window->ops; op < window->ops + window->num_ops; op++)
194 {
195 /* Encode the action code and length. */
196 ip = ibuf;
197 switch (op->action_code)
198 {
199 case svn_txdelta_source: *ip = 0; break;
200 case svn_txdelta_target: *ip = (0x1 << 6); break;
201 case svn_txdelta_new: *ip = (0x2 << 6); break;
202 }
203 if (op->length >> 6 == 0)
204 *ip++ |= (unsigned char)op->length;
205 else
206 ip = svn__encode_uint(ip + 1, op->length);
207 if (op->action_code != svn_txdelta_new)
208 ip = svn__encode_uint(ip, op->offset);
209 svn_stringbuf_appendbytes(instructions, (const char *)ibuf, ip - ibuf);
210 }
211
212 /* Encode the header. */
213 append_encoded_int(header, window->sview_offset);
214 append_encoded_int(header, window->sview_len);
215 append_encoded_int(header, window->tview_len);
216 if (eb->version == 1)
217 {
218 SVN_ERR(svn__compress(instructions, i1, eb->compression_level));
219 instructions = i1;
220 }
221 append_encoded_int(header, instructions->len);
222 if (eb->version == 1)
223 {
224 svn_stringbuf_t *compressed = svn_stringbuf_create_empty(pool);
225 svn_stringbuf_t *original = svn_stringbuf_create_empty(pool);
226 original->data = (char *)window->new_data->data; /* won't be modified */
227 original->len = window->new_data->len;
228 original->blocksize = window->new_data->len + 1;
229
230 SVN_ERR(svn__compress(original, compressed, eb->compression_level));
231 newdata = svn_stringbuf__morph_into_string(compressed);
232 }
233 else
234 newdata = window->new_data;
235
236 append_encoded_int(header, newdata->len);
237
238 /* Write out the window. */
239 len = header->len;
240 SVN_ERR(svn_stream_write(eb->output, header->data, &len));
241 if (instructions->len > 0)
242 {
243 len = instructions->len;
244 SVN_ERR(svn_stream_write(eb->output, instructions->data, &len));
245 }
246 if (newdata->len > 0)
247 {
248 len = newdata->len;
249 SVN_ERR(svn_stream_write(eb->output, newdata->data, &len));
250 }
251
252 svn_pool_destroy(pool);
253 return SVN_NO_ERROR;
254 }
255
256 void
svn_txdelta_to_svndiff3(svn_txdelta_window_handler_t * handler,void ** handler_baton,svn_stream_t * output,int svndiff_version,int compression_level,apr_pool_t * pool)257 svn_txdelta_to_svndiff3(svn_txdelta_window_handler_t *handler,
258 void **handler_baton,
259 svn_stream_t *output,
260 int svndiff_version,
261 int compression_level,
262 apr_pool_t *pool)
263 {
264 apr_pool_t *subpool = svn_pool_create(pool);
265 struct encoder_baton *eb;
266
267 eb = apr_palloc(subpool, sizeof(*eb));
268 eb->output = output;
269 eb->header_done = FALSE;
270 eb->pool = subpool;
271 eb->version = svndiff_version;
272 eb->compression_level = compression_level;
273
274 *handler = window_handler;
275 *handler_baton = eb;
276 }
277
278 void
svn_txdelta_to_svndiff2(svn_txdelta_window_handler_t * handler,void ** handler_baton,svn_stream_t * output,int svndiff_version,apr_pool_t * pool)279 svn_txdelta_to_svndiff2(svn_txdelta_window_handler_t *handler,
280 void **handler_baton,
281 svn_stream_t *output,
282 int svndiff_version,
283 apr_pool_t *pool)
284 {
285 svn_txdelta_to_svndiff3(handler, handler_baton, output, svndiff_version,
286 SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool);
287 }
288
289 void
svn_txdelta_to_svndiff(svn_stream_t * output,apr_pool_t * pool,svn_txdelta_window_handler_t * handler,void ** handler_baton)290 svn_txdelta_to_svndiff(svn_stream_t *output,
291 apr_pool_t *pool,
292 svn_txdelta_window_handler_t *handler,
293 void **handler_baton)
294 {
295 svn_txdelta_to_svndiff3(handler, handler_baton, output, 0,
296 SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool);
297 }
298
299
300 /* ----- svndiff to text delta ----- */
301
302 /* An svndiff parser object. */
303 struct decode_baton
304 {
305 /* Once the svndiff parser has enough data buffered to create a
306 "window", it passes this window to the caller's consumer routine. */
307 svn_txdelta_window_handler_t consumer_func;
308 void *consumer_baton;
309
310 /* Pool to create subpools from; each developing window will be a
311 subpool. */
312 apr_pool_t *pool;
313
314 /* The current subpool which contains our current window-buffer. */
315 apr_pool_t *subpool;
316
317 /* The actual svndiff data buffer, living within subpool. */
318 svn_stringbuf_t *buffer;
319
320 /* The offset and size of the last source view, so that we can check
321 to make sure the next one isn't sliding backwards. */
322 svn_filesize_t last_sview_offset;
323 apr_size_t last_sview_len;
324
325 /* We have to discard four bytes at the beginning for the header.
326 This field keeps track of how many of those bytes we have read. */
327 apr_size_t header_bytes;
328
329 /* Do we want an error to occur when we close the stream that
330 indicates we didn't send the whole svndiff data? If you plan to
331 not transmit the whole svndiff data stream, you will want this to
332 be FALSE. */
333 svn_boolean_t error_on_early_close;
334
335 /* svndiff version in use by delta. */
336 unsigned char version;
337 };
338
339
340 /* Wrapper aroung svn__deencode_uint taking a file size as *VAL. */
341 static const unsigned char *
decode_file_offset(svn_filesize_t * val,const unsigned char * p,const unsigned char * end)342 decode_file_offset(svn_filesize_t *val,
343 const unsigned char *p,
344 const unsigned char *end)
345 {
346 apr_uint64_t temp = 0;
347 const unsigned char *result = svn__decode_uint(&temp, p, end);
348 *val = (svn_filesize_t)temp;
349
350 return result;
351 }
352
353 /* Same as above, only decode into a size variable. */
354 static const unsigned char *
decode_size(apr_size_t * val,const unsigned char * p,const unsigned char * end)355 decode_size(apr_size_t *val,
356 const unsigned char *p,
357 const unsigned char *end)
358 {
359 apr_uint64_t temp = 0;
360 const unsigned char *result = svn__decode_uint(&temp, p, end);
361 if (temp > APR_SIZE_MAX)
362 return NULL;
363
364 *val = (apr_size_t)temp;
365 return result;
366 }
367
368 /* Decode an instruction into OP, returning a pointer to the text
369 after the instruction. Note that if the action code is
370 svn_txdelta_new, the offset field of *OP will not be set. */
371 static const unsigned char *
decode_instruction(svn_txdelta_op_t * op,const unsigned char * p,const unsigned char * end)372 decode_instruction(svn_txdelta_op_t *op,
373 const unsigned char *p,
374 const unsigned char *end)
375 {
376 apr_size_t c;
377 apr_size_t action;
378
379 if (p == end)
380 return NULL;
381
382 /* We need this more than once */
383 c = *p++;
384
385 /* Decode the instruction selector. */
386 action = (c >> 6) & 0x3;
387 if (action >= 0x3)
388 return NULL;
389
390 /* This relies on enum svn_delta_action values to match and never to be
391 redefined. */
392 op->action_code = (enum svn_delta_action)(action);
393
394 /* Decode the length and offset. */
395 op->length = c & 0x3f;
396 if (op->length == 0)
397 {
398 p = decode_size(&op->length, p, end);
399 if (p == NULL)
400 return NULL;
401 }
402 if (action != svn_txdelta_new)
403 {
404 p = decode_size(&op->offset, p, end);
405 if (p == NULL)
406 return NULL;
407 }
408
409 return p;
410 }
411
412 /* Count the instructions in the range [P..END-1] and make sure they
413 are valid for the given window lengths. Return an error if the
414 instructions are invalid; otherwise set *NINST to the number of
415 instructions. */
416 static svn_error_t *
count_and_verify_instructions(int * ninst,const unsigned char * p,const unsigned char * end,apr_size_t sview_len,apr_size_t tview_len,apr_size_t new_len)417 count_and_verify_instructions(int *ninst,
418 const unsigned char *p,
419 const unsigned char *end,
420 apr_size_t sview_len,
421 apr_size_t tview_len,
422 apr_size_t new_len)
423 {
424 int n = 0;
425 svn_txdelta_op_t op;
426 apr_size_t tpos = 0, npos = 0;
427
428 while (p < end)
429 {
430 p = decode_instruction(&op, p, end);
431
432 /* Detect any malformed operations from the instruction stream. */
433 if (p == NULL)
434 return svn_error_createf
435 (SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
436 _("Invalid diff stream: insn %d cannot be decoded"), n);
437 else if (op.length == 0)
438 return svn_error_createf
439 (SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
440 _("Invalid diff stream: insn %d has length zero"), n);
441 else if (op.length > tview_len - tpos)
442 return svn_error_createf
443 (SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
444 _("Invalid diff stream: insn %d overflows the target view"), n);
445
446 switch (op.action_code)
447 {
448 case svn_txdelta_source:
449 if (op.length > sview_len - op.offset ||
450 op.offset > sview_len)
451 return svn_error_createf
452 (SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
453 _("Invalid diff stream: "
454 "[src] insn %d overflows the source view"), n);
455 break;
456 case svn_txdelta_target:
457 if (op.offset >= tpos)
458 return svn_error_createf
459 (SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
460 _("Invalid diff stream: "
461 "[tgt] insn %d starts beyond the target view position"), n);
462 break;
463 case svn_txdelta_new:
464 if (op.length > new_len - npos)
465 return svn_error_createf
466 (SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
467 _("Invalid diff stream: "
468 "[new] insn %d overflows the new data section"), n);
469 npos += op.length;
470 break;
471 }
472 tpos += op.length;
473 n++;
474 }
475 if (tpos != tview_len)
476 return svn_error_create(SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
477 _("Delta does not fill the target window"));
478 if (npos != new_len)
479 return svn_error_create(SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
480 _("Delta does not contain enough new data"));
481
482 *ninst = n;
483 return SVN_NO_ERROR;
484 }
485
486 static svn_error_t *
zlib_decode(const unsigned char * in,apr_size_t inLen,svn_stringbuf_t * out,apr_size_t limit)487 zlib_decode(const unsigned char *in, apr_size_t inLen, svn_stringbuf_t *out,
488 apr_size_t limit)
489 {
490 /* construct a fake string buffer as parameter to svn__decompress.
491 This is fine as that function never writes to it. */
492 svn_stringbuf_t compressed;
493 compressed.pool = NULL;
494 compressed.data = (char *)in;
495 compressed.len = inLen;
496 compressed.blocksize = inLen + 1;
497
498 return svn__decompress(&compressed, out, limit);
499 }
500
501 /* Given the five integer fields of a window header and a pointer to
502 the remainder of the window contents, fill in a delta window
503 structure *WINDOW. New allocations will be performed in POOL;
504 the new_data field of *WINDOW will refer directly to memory pointed
505 to by DATA. */
506 static svn_error_t *
decode_window(svn_txdelta_window_t * window,svn_filesize_t sview_offset,apr_size_t sview_len,apr_size_t tview_len,apr_size_t inslen,apr_size_t newlen,const unsigned char * data,apr_pool_t * pool,unsigned int version)507 decode_window(svn_txdelta_window_t *window, svn_filesize_t sview_offset,
508 apr_size_t sview_len, apr_size_t tview_len, apr_size_t inslen,
509 apr_size_t newlen, const unsigned char *data, apr_pool_t *pool,
510 unsigned int version)
511 {
512 const unsigned char *insend;
513 int ninst;
514 apr_size_t npos;
515 svn_txdelta_op_t *ops, *op;
516 svn_string_t *new_data = apr_palloc(pool, sizeof(*new_data));
517
518 window->sview_offset = sview_offset;
519 window->sview_len = sview_len;
520 window->tview_len = tview_len;
521
522 insend = data + inslen;
523
524 if (version == 1)
525 {
526 svn_stringbuf_t *instout = svn_stringbuf_create_empty(pool);
527 svn_stringbuf_t *ndout = svn_stringbuf_create_empty(pool);
528
529 SVN_ERR(zlib_decode(insend, newlen, ndout,
530 SVN_DELTA_WINDOW_SIZE));
531 SVN_ERR(zlib_decode(data, insend - data, instout,
532 MAX_INSTRUCTION_SECTION_LEN));
533
534 newlen = ndout->len;
535 data = (unsigned char *)instout->data;
536 insend = (unsigned char *)instout->data + instout->len;
537
538 new_data->data = (const char *) ndout->data;
539 new_data->len = newlen;
540 }
541 else
542 {
543 /* Copy the data because an svn_string_t must have the invariant
544 data[len]=='\0'. */
545 char *buf = apr_palloc(pool, newlen + 1);
546
547 memcpy(buf, insend, newlen);
548 buf[newlen] = '\0';
549 new_data->data = buf;
550 new_data->len = newlen;
551 }
552
553 /* Count the instructions and make sure they are all valid. */
554 SVN_ERR(count_and_verify_instructions(&ninst, data, insend,
555 sview_len, tview_len, newlen));
556
557 /* Allocate a buffer for the instructions and decode them. */
558 ops = apr_palloc(pool, ninst * sizeof(*ops));
559 npos = 0;
560 window->src_ops = 0;
561 for (op = ops; op < ops + ninst; op++)
562 {
563 data = decode_instruction(op, data, insend);
564 if (op->action_code == svn_txdelta_source)
565 ++window->src_ops;
566 else if (op->action_code == svn_txdelta_new)
567 {
568 op->offset = npos;
569 npos += op->length;
570 }
571 }
572 SVN_ERR_ASSERT(data == insend);
573
574 window->ops = ops;
575 window->num_ops = ninst;
576 window->new_data = new_data;
577
578 return SVN_NO_ERROR;
579 }
580
581 static const char SVNDIFF_V0[] = { 'S', 'V', 'N', 0 };
582 static const char SVNDIFF_V1[] = { 'S', 'V', 'N', 1 };
583 #define SVNDIFF_HEADER_SIZE (sizeof(SVNDIFF_V0))
584
585 static svn_error_t *
write_handler(void * baton,const char * buffer,apr_size_t * len)586 write_handler(void *baton,
587 const char *buffer,
588 apr_size_t *len)
589 {
590 struct decode_baton *db = (struct decode_baton *) baton;
591 const unsigned char *p, *end;
592 svn_filesize_t sview_offset;
593 apr_size_t sview_len, tview_len, inslen, newlen, remaining;
594 apr_size_t buflen = *len;
595
596 /* Chew up four bytes at the beginning for the header. */
597 if (db->header_bytes < SVNDIFF_HEADER_SIZE)
598 {
599 apr_size_t nheader = SVNDIFF_HEADER_SIZE - db->header_bytes;
600 if (nheader > buflen)
601 nheader = buflen;
602 if (memcmp(buffer, SVNDIFF_V0 + db->header_bytes, nheader) == 0)
603 db->version = 0;
604 else if (memcmp(buffer, SVNDIFF_V1 + db->header_bytes, nheader) == 0)
605 db->version = 1;
606 else
607 return svn_error_create(SVN_ERR_SVNDIFF_INVALID_HEADER, NULL,
608 _("Svndiff has invalid header"));
609 buflen -= nheader;
610 buffer += nheader;
611 db->header_bytes += nheader;
612 }
613
614 /* Concatenate the old with the new. */
615 svn_stringbuf_appendbytes(db->buffer, buffer, buflen);
616
617 /* We have a buffer of svndiff data that might be good for:
618
619 a) an integral number of windows' worth of data - this is a
620 trivial case. Make windows from our data and ship them off.
621
622 b) a non-integral number of windows' worth of data - we shall
623 consume the integral portion of the window data, and then
624 somewhere in the following loop the decoding of the svndiff
625 data will run out of stuff to decode, and will simply return
626 SVN_NO_ERROR, anxiously awaiting more data.
627 */
628
629 while (1)
630 {
631 apr_pool_t *newpool;
632 svn_txdelta_window_t window;
633
634 /* Read the header, if we have enough bytes for that. */
635 p = (const unsigned char *) db->buffer->data;
636 end = (const unsigned char *) db->buffer->data + db->buffer->len;
637
638 p = decode_file_offset(&sview_offset, p, end);
639 if (p == NULL)
640 break;
641
642 p = decode_size(&sview_len, p, end);
643 if (p == NULL)
644 break;
645
646 p = decode_size(&tview_len, p, end);
647 if (p == NULL)
648 break;
649
650 p = decode_size(&inslen, p, end);
651 if (p == NULL)
652 break;
653
654 p = decode_size(&newlen, p, end);
655 if (p == NULL)
656 break;
657
658 if (tview_len > SVN_DELTA_WINDOW_SIZE ||
659 sview_len > SVN_DELTA_WINDOW_SIZE ||
660 /* for svndiff1, newlen includes the original length */
661 newlen > SVN_DELTA_WINDOW_SIZE + SVN__MAX_ENCODED_UINT_LEN ||
662 inslen > MAX_INSTRUCTION_SECTION_LEN)
663 return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL,
664 _("Svndiff contains a too-large window"));
665
666 /* Check for integer overflow. */
667 if (sview_offset < 0 || inslen + newlen < inslen
668 || sview_len + tview_len < sview_len
669 || (apr_size_t)sview_offset + sview_len < (apr_size_t)sview_offset)
670 return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL,
671 _("Svndiff contains corrupt window header"));
672
673 /* Check for source windows which slide backwards. */
674 if (sview_len > 0
675 && (sview_offset < db->last_sview_offset
676 || (sview_offset + sview_len
677 < db->last_sview_offset + db->last_sview_len)))
678 return svn_error_create
679 (SVN_ERR_SVNDIFF_BACKWARD_VIEW, NULL,
680 _("Svndiff has backwards-sliding source views"));
681
682 /* Wait for more data if we don't have enough bytes for the
683 whole window. */
684 if ((apr_size_t) (end - p) < inslen + newlen)
685 return SVN_NO_ERROR;
686
687 /* Decode the window and send it off. */
688 SVN_ERR(decode_window(&window, sview_offset, sview_len, tview_len,
689 inslen, newlen, p, db->subpool,
690 db->version));
691 SVN_ERR(db->consumer_func(&window, db->consumer_baton));
692
693 /* Make a new subpool and buffer, saving aside the remaining
694 data in the old buffer. */
695 newpool = svn_pool_create(db->pool);
696 p += inslen + newlen;
697 remaining = db->buffer->data + db->buffer->len - (const char *) p;
698 db->buffer =
699 svn_stringbuf_ncreate((const char *) p, remaining, newpool);
700
701 /* Remember the offset and length of the source view for next time. */
702 db->last_sview_offset = sview_offset;
703 db->last_sview_len = sview_len;
704
705 /* We've copied stuff out of the old pool. Toss that pool and use
706 our new pool.
707 ### might be nice to avoid the copy and just use svn_pool_clear
708 ### to get rid of whatever the "other stuff" is. future project...
709 */
710 svn_pool_destroy(db->subpool);
711 db->subpool = newpool;
712 }
713
714 /* At this point we processed all integral windows and DB->BUFFER is empty
715 or contains partially read window header.
716 Check that unprocessed data is not larger that theoretical maximum
717 window header size. */
718 if (db->buffer->len > 5 * SVN__MAX_ENCODED_UINT_LEN)
719 return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL,
720 _("Svndiff contains a too-large window header"));
721
722 return SVN_NO_ERROR;
723 }
724
725 /* Minimal svn_stream_t write handler, doing nothing */
726 static svn_error_t *
noop_write_handler(void * baton,const char * buffer,apr_size_t * len)727 noop_write_handler(void *baton,
728 const char *buffer,
729 apr_size_t *len)
730 {
731 return SVN_NO_ERROR;
732 }
733
734 static svn_error_t *
close_handler(void * baton)735 close_handler(void *baton)
736 {
737 struct decode_baton *db = (struct decode_baton *) baton;
738 svn_error_t *err;
739
740 /* Make sure that we're at a plausible end of stream, returning an
741 error if we are expected to do so. */
742 if ((db->error_on_early_close)
743 && (db->header_bytes < 4 || db->buffer->len != 0))
744 return svn_error_create(SVN_ERR_SVNDIFF_UNEXPECTED_END, NULL,
745 _("Unexpected end of svndiff input"));
746
747 /* Tell the window consumer that we're done, and clean up. */
748 err = db->consumer_func(NULL, db->consumer_baton);
749 svn_pool_destroy(db->pool);
750 return err;
751 }
752
753
754 svn_stream_t *
svn_txdelta_parse_svndiff(svn_txdelta_window_handler_t handler,void * handler_baton,svn_boolean_t error_on_early_close,apr_pool_t * pool)755 svn_txdelta_parse_svndiff(svn_txdelta_window_handler_t handler,
756 void *handler_baton,
757 svn_boolean_t error_on_early_close,
758 apr_pool_t *pool)
759 {
760 apr_pool_t *subpool = svn_pool_create(pool);
761 struct decode_baton *db = apr_palloc(pool, sizeof(*db));
762 svn_stream_t *stream;
763
764 db->consumer_func = handler;
765 db->consumer_baton = handler_baton;
766 db->pool = subpool;
767 db->subpool = svn_pool_create(subpool);
768 db->buffer = svn_stringbuf_create_empty(db->subpool);
769 db->last_sview_offset = 0;
770 db->last_sview_len = 0;
771 db->header_bytes = 0;
772 db->error_on_early_close = error_on_early_close;
773 stream = svn_stream_create(db, pool);
774
775 if (handler != svn_delta_noop_window_handler)
776 {
777 svn_stream_set_write(stream, write_handler);
778 svn_stream_set_close(stream, close_handler);
779 }
780 else
781 {
782 /* And else we just ignore everything as efficiently as we can.
783 by only hooking a no-op handler */
784 svn_stream_set_write(stream, noop_write_handler);
785 }
786 return stream;
787 }
788
789
790 /* Routines for reading one svndiff window at a time. */
791
792 /* Read one byte from STREAM into *BYTE. */
793 static svn_error_t *
read_one_byte(unsigned char * byte,svn_stream_t * stream)794 read_one_byte(unsigned char *byte, svn_stream_t *stream)
795 {
796 char c;
797 apr_size_t len = 1;
798
799 SVN_ERR(svn_stream_read_full(stream, &c, &len));
800 if (len == 0)
801 return svn_error_create(SVN_ERR_SVNDIFF_UNEXPECTED_END, NULL,
802 _("Unexpected end of svndiff input"));
803 *byte = (unsigned char) c;
804 return SVN_NO_ERROR;
805 }
806
807 /* Read and decode one integer from STREAM into *SIZE.
808 Increment *BYTE_COUNTER by the number of chars we have read. */
809 static svn_error_t *
read_one_size(apr_size_t * size,apr_size_t * byte_counter,svn_stream_t * stream)810 read_one_size(apr_size_t *size,
811 apr_size_t *byte_counter,
812 svn_stream_t *stream)
813 {
814 unsigned char c;
815
816 *size = 0;
817 while (1)
818 {
819 SVN_ERR(read_one_byte(&c, stream));
820 ++*byte_counter;
821 *size = (*size << 7) | (c & 0x7f);
822 if (!(c & 0x80))
823 break;
824 }
825 return SVN_NO_ERROR;
826 }
827
828 /* Read a window header from STREAM and check it for integer overflow. */
829 static svn_error_t *
read_window_header(svn_stream_t * stream,svn_filesize_t * sview_offset,apr_size_t * sview_len,apr_size_t * tview_len,apr_size_t * inslen,apr_size_t * newlen,apr_size_t * header_len)830 read_window_header(svn_stream_t *stream, svn_filesize_t *sview_offset,
831 apr_size_t *sview_len, apr_size_t *tview_len,
832 apr_size_t *inslen, apr_size_t *newlen,
833 apr_size_t *header_len)
834 {
835 unsigned char c;
836
837 /* Read the source view offset by hand, since it's not an apr_size_t. */
838 *header_len = 0;
839 *sview_offset = 0;
840 while (1)
841 {
842 SVN_ERR(read_one_byte(&c, stream));
843 ++*header_len;
844 *sview_offset = (*sview_offset << 7) | (c & 0x7f);
845 if (!(c & 0x80))
846 break;
847 }
848
849 /* Read the four size fields. */
850 SVN_ERR(read_one_size(sview_len, header_len, stream));
851 SVN_ERR(read_one_size(tview_len, header_len, stream));
852 SVN_ERR(read_one_size(inslen, header_len, stream));
853 SVN_ERR(read_one_size(newlen, header_len, stream));
854
855 if (*tview_len > SVN_DELTA_WINDOW_SIZE ||
856 *sview_len > SVN_DELTA_WINDOW_SIZE ||
857 /* for svndiff1, newlen includes the original length */
858 *newlen > SVN_DELTA_WINDOW_SIZE + SVN__MAX_ENCODED_UINT_LEN ||
859 *inslen > MAX_INSTRUCTION_SECTION_LEN)
860 return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL,
861 _("Svndiff contains a too-large window"));
862
863 /* Check for integer overflow. */
864 if (*sview_offset < 0 || *inslen + *newlen < *inslen
865 || *sview_len + *tview_len < *sview_len
866 || (apr_size_t)*sview_offset + *sview_len < (apr_size_t)*sview_offset)
867 return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL,
868 _("Svndiff contains corrupt window header"));
869
870 return SVN_NO_ERROR;
871 }
872
873 svn_error_t *
svn_txdelta_read_svndiff_window(svn_txdelta_window_t ** window,svn_stream_t * stream,int svndiff_version,apr_pool_t * pool)874 svn_txdelta_read_svndiff_window(svn_txdelta_window_t **window,
875 svn_stream_t *stream,
876 int svndiff_version,
877 apr_pool_t *pool)
878 {
879 svn_filesize_t sview_offset;
880 apr_size_t sview_len, tview_len, inslen, newlen, len, header_len;
881 unsigned char *buf;
882
883 SVN_ERR(read_window_header(stream, &sview_offset, &sview_len, &tview_len,
884 &inslen, &newlen, &header_len));
885 len = inslen + newlen;
886 buf = apr_palloc(pool, len);
887 SVN_ERR(svn_stream_read_full(stream, (char*)buf, &len));
888 if (len < inslen + newlen)
889 return svn_error_create(SVN_ERR_SVNDIFF_UNEXPECTED_END, NULL,
890 _("Unexpected end of svndiff input"));
891 *window = apr_palloc(pool, sizeof(**window));
892 return decode_window(*window, sview_offset, sview_len, tview_len, inslen,
893 newlen, buf, pool, svndiff_version);
894 }
895
896
897 svn_error_t *
svn_txdelta_skip_svndiff_window(apr_file_t * file,int svndiff_version,apr_pool_t * pool)898 svn_txdelta_skip_svndiff_window(apr_file_t *file,
899 int svndiff_version,
900 apr_pool_t *pool)
901 {
902 svn_stream_t *stream = svn_stream_from_aprfile2(file, TRUE, pool);
903 svn_filesize_t sview_offset;
904 apr_size_t sview_len, tview_len, inslen, newlen, header_len;
905 apr_off_t offset;
906
907 SVN_ERR(read_window_header(stream, &sview_offset, &sview_len, &tview_len,
908 &inslen, &newlen, &header_len));
909
910 offset = inslen + newlen;
911 return svn_io_file_seek(file, APR_CUR, &offset, pool);
912 }
913
914 svn_error_t *
svn_txdelta__read_raw_window_len(apr_size_t * window_len,svn_stream_t * stream,apr_pool_t * pool)915 svn_txdelta__read_raw_window_len(apr_size_t *window_len,
916 svn_stream_t *stream,
917 apr_pool_t *pool)
918 {
919 svn_filesize_t sview_offset;
920 apr_size_t sview_len, tview_len, inslen, newlen, header_len;
921
922 SVN_ERR(read_window_header(stream, &sview_offset, &sview_len, &tview_len,
923 &inslen, &newlen, &header_len));
924
925 *window_len = inslen + newlen + header_len;
926 return SVN_NO_ERROR;
927 }
928
929