2 * svndiff.c -- Encoding and decoding svndiff-format deltas.
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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
21 * ====================================================================
27 #include "svn_delta.h"
30 #include "svn_pools.h"
31 #include "svn_private_config.h"
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"
39 /* ----- Text delta to svndiff ----- */
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 {
46 svn_boolean_t header_done;
48 int compression_level;
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)
60 /* Append an encoded integer to a string. */
62 append_encoded_int(svn_stringbuf_t *header, svn_filesize_t val)
64 unsigned char buf[SVN__MAX_ENCODED_UINT_LEN], *p;
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);
72 send_simple_insertion_window(svn_txdelta_window_t *window,
73 struct encoder_baton *eb)
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;
81 apr_size_t len = window->new_data->len;
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);
88 /* write stream header if necessary */
91 eb->header_done = TRUE;
95 headers[3] = (unsigned char)eb->version;
96 header_current = headers + 4;
100 header_current = headers;
103 /* Encode the action code and length. */
104 if (window->tview_len >> 6 == 0)
106 ibuf[0] = (unsigned char)(window->tview_len + (0x2 << 6));
111 ibuf[0] = (0x2 << 6);
112 ip_len = svn__encode_uint(ibuf + 1, window->tview_len) - ibuf;
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);
124 /* append instructions (1 to a handful of bytes) */
125 for (i = 0; i < ip_len; ++i)
126 header_current[i] = ibuf[i];
128 header_len = header_current - headers + ip_len;
130 /* Write out the window. */
131 SVN_ERR(svn_stream_write(eb->output, (const char *)headers, &header_len));
133 SVN_ERR(svn_stream_write(eb->output, window->new_data->data, &len));
139 window_handler(svn_txdelta_window_t *window, void *baton)
141 struct encoder_baton *eb = baton;
143 svn_stringbuf_t *instructions;
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;
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));
155 /* Make sure we write the header. */
156 if (!eb->header_done)
158 char svnver[4] = {'S','V','N','\0'};
160 svnver[3] = (char)eb->version;
161 SVN_ERR(svn_stream_write(eb->output, svnver, &len));
162 eb->header_done = TRUE;
167 svn_stream_t *output = eb->output;
169 /* We're done; clean up.
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.
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.
181 svn_pool_destroy(eb->pool);
183 return svn_stream_close(output);
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);
192 /* Encode the instructions. */
193 for (op = window->ops; op < window->ops + window->num_ops; op++)
195 /* Encode the action code and length. */
197 switch (op->action_code)
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;
203 if (op->length >> 6 == 0)
204 *ip++ |= (unsigned char)op->length;
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);
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)
218 SVN_ERR(svn__compress(instructions, i1, eb->compression_level));
221 append_encoded_int(header, instructions->len);
222 if (eb->version == 1)
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;
230 SVN_ERR(svn__compress(original, compressed, eb->compression_level));
231 newdata = svn_stringbuf__morph_into_string(compressed);
234 newdata = window->new_data;
236 append_encoded_int(header, newdata->len);
238 /* Write out the window. */
240 SVN_ERR(svn_stream_write(eb->output, header->data, &len));
241 if (instructions->len > 0)
243 len = instructions->len;
244 SVN_ERR(svn_stream_write(eb->output, instructions->data, &len));
246 if (newdata->len > 0)
249 SVN_ERR(svn_stream_write(eb->output, newdata->data, &len));
252 svn_pool_destroy(pool);
257 svn_txdelta_to_svndiff3(svn_txdelta_window_handler_t *handler,
258 void **handler_baton,
259 svn_stream_t *output,
261 int compression_level,
264 apr_pool_t *subpool = svn_pool_create(pool);
265 struct encoder_baton *eb;
267 eb = apr_palloc(subpool, sizeof(*eb));
269 eb->header_done = FALSE;
271 eb->version = svndiff_version;
272 eb->compression_level = compression_level;
274 *handler = window_handler;
279 svn_txdelta_to_svndiff2(svn_txdelta_window_handler_t *handler,
280 void **handler_baton,
281 svn_stream_t *output,
285 svn_txdelta_to_svndiff3(handler, handler_baton, output, svndiff_version,
286 SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool);
290 svn_txdelta_to_svndiff(svn_stream_t *output,
292 svn_txdelta_window_handler_t *handler,
293 void **handler_baton)
295 svn_txdelta_to_svndiff3(handler, handler_baton, output, 0,
296 SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool);
300 /* ----- svndiff to text delta ----- */
302 /* An svndiff parser object. */
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;
310 /* Pool to create subpools from; each developing window will be a
314 /* The current subpool which contains our current window-buffer. */
317 /* The actual svndiff data buffer, living within subpool. */
318 svn_stringbuf_t *buffer;
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;
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;
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
333 svn_boolean_t error_on_early_close;
335 /* svndiff version in use by delta. */
336 unsigned char version;
340 /* Wrapper aroung svn__deencode_uint taking a file size as *VAL. */
341 static const unsigned char *
342 decode_file_offset(svn_filesize_t *val,
343 const unsigned char *p,
344 const unsigned char *end)
346 apr_uint64_t temp = 0;
347 const unsigned char *result = svn__decode_uint(&temp, p, end);
348 *val = (svn_filesize_t)temp;
353 /* Same as above, only decode into a size variable. */
354 static const unsigned char *
355 decode_size(apr_size_t *val,
356 const unsigned char *p,
357 const unsigned char *end)
359 apr_uint64_t temp = 0;
360 const unsigned char *result = svn__decode_uint(&temp, p, end);
361 if (temp > APR_SIZE_MAX)
364 *val = (apr_size_t)temp;
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 *
372 decode_instruction(svn_txdelta_op_t *op,
373 const unsigned char *p,
374 const unsigned char *end)
382 /* We need this more than once */
385 /* Decode the instruction selector. */
386 action = (c >> 6) & 0x3;
390 /* This relies on enum svn_delta_action values to match and never to be
392 op->action_code = (enum svn_delta_action)(action);
394 /* Decode the length and offset. */
395 op->length = c & 0x3f;
398 p = decode_size(&op->length, p, end);
402 if (action != svn_txdelta_new)
404 p = decode_size(&op->offset, p, end);
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
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,
426 apr_size_t tpos = 0, npos = 0;
430 p = decode_instruction(&op, p, end);
432 /* Detect any malformed operations from the instruction stream. */
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);
446 switch (op.action_code)
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);
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);
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);
475 if (tpos != tview_len)
476 return svn_error_create(SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
477 _("Delta does not fill the target window"));
479 return svn_error_create(SVN_ERR_SVNDIFF_INVALID_OPS, NULL,
480 _("Delta does not contain enough new data"));
487 zlib_decode(const unsigned char *in, apr_size_t inLen, svn_stringbuf_t *out,
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;
498 return svn__decompress(&compressed, out, limit);
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
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)
512 const unsigned char *insend;
515 svn_txdelta_op_t *ops, *op;
516 svn_string_t *new_data = apr_palloc(pool, sizeof(*new_data));
518 window->sview_offset = sview_offset;
519 window->sview_len = sview_len;
520 window->tview_len = tview_len;
522 insend = data + inslen;
526 svn_stringbuf_t *instout = svn_stringbuf_create_empty(pool);
527 svn_stringbuf_t *ndout = svn_stringbuf_create_empty(pool);
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));
535 data = (unsigned char *)instout->data;
536 insend = (unsigned char *)instout->data + instout->len;
538 new_data->data = (const char *) ndout->data;
539 new_data->len = newlen;
543 /* Copy the data because an svn_string_t must have the invariant
545 char *buf = apr_palloc(pool, newlen + 1);
547 memcpy(buf, insend, newlen);
549 new_data->data = buf;
550 new_data->len = newlen;
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));
557 /* Allocate a buffer for the instructions and decode them. */
558 ops = apr_palloc(pool, ninst * sizeof(*ops));
561 for (op = ops; op < ops + ninst; op++)
563 data = decode_instruction(op, data, insend);
564 if (op->action_code == svn_txdelta_source)
566 else if (op->action_code == svn_txdelta_new)
572 SVN_ERR_ASSERT(data == insend);
575 window->num_ops = ninst;
576 window->new_data = new_data;
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))
586 write_handler(void *baton,
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;
596 /* Chew up four bytes at the beginning for the header. */
597 if (db->header_bytes < SVNDIFF_HEADER_SIZE)
599 apr_size_t nheader = SVNDIFF_HEADER_SIZE - db->header_bytes;
600 if (nheader > buflen)
602 if (memcmp(buffer, SVNDIFF_V0 + db->header_bytes, nheader) == 0)
604 else if (memcmp(buffer, SVNDIFF_V1 + db->header_bytes, nheader) == 0)
607 return svn_error_create(SVN_ERR_SVNDIFF_INVALID_HEADER, NULL,
608 _("Svndiff has invalid header"));
611 db->header_bytes += nheader;
614 /* Concatenate the old with the new. */
615 svn_stringbuf_appendbytes(db->buffer, buffer, buflen);
617 /* We have a buffer of svndiff data that might be good for:
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.
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.
632 svn_txdelta_window_t window;
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;
638 p = decode_file_offset(&sview_offset, p, end);
642 p = decode_size(&sview_len, p, end);
646 p = decode_size(&tview_len, p, end);
650 p = decode_size(&inslen, p, end);
654 p = decode_size(&newlen, p, end);
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"));
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"));
673 /* Check for source windows which slide backwards. */
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"));
682 /* Wait for more data if we don't have enough bytes for the
684 if ((apr_size_t) (end - p) < inslen + newlen)
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,
691 SVN_ERR(db->consumer_func(&window, db->consumer_baton));
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;
699 svn_stringbuf_ncreate((const char *) p, remaining, newpool);
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;
705 /* We've copied stuff out of the old pool. Toss that pool and use
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...
710 svn_pool_destroy(db->subpool);
711 db->subpool = newpool;
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"));
725 /* Minimal svn_stream_t write handler, doing nothing */
727 noop_write_handler(void *baton,
735 close_handler(void *baton)
737 struct decode_baton *db = (struct decode_baton *) baton;
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"));
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);
755 svn_txdelta_parse_svndiff(svn_txdelta_window_handler_t handler,
757 svn_boolean_t error_on_early_close,
760 apr_pool_t *subpool = svn_pool_create(pool);
761 struct decode_baton *db = apr_palloc(pool, sizeof(*db));
762 svn_stream_t *stream;
764 db->consumer_func = handler;
765 db->consumer_baton = handler_baton;
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);
775 if (handler != svn_delta_noop_window_handler)
777 svn_stream_set_write(stream, write_handler);
778 svn_stream_set_close(stream, close_handler);
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);
790 /* Routines for reading one svndiff window at a time. */
792 /* Read one byte from STREAM into *BYTE. */
794 read_one_byte(unsigned char *byte, svn_stream_t *stream)
799 SVN_ERR(svn_stream_read_full(stream, &c, &len));
801 return svn_error_create(SVN_ERR_SVNDIFF_UNEXPECTED_END, NULL,
802 _("Unexpected end of svndiff input"));
803 *byte = (unsigned char) c;
807 /* Read and decode one integer from STREAM into *SIZE.
808 Increment *BYTE_COUNTER by the number of chars we have read. */
810 read_one_size(apr_size_t *size,
811 apr_size_t *byte_counter,
812 svn_stream_t *stream)
819 SVN_ERR(read_one_byte(&c, stream));
821 *size = (*size << 7) | (c & 0x7f);
828 /* Read a window header from STREAM and check it for integer overflow. */
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)
837 /* Read the source view offset by hand, since it's not an apr_size_t. */
842 SVN_ERR(read_one_byte(&c, stream));
844 *sview_offset = (*sview_offset << 7) | (c & 0x7f);
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));
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"));
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"));
874 svn_txdelta_read_svndiff_window(svn_txdelta_window_t **window,
875 svn_stream_t *stream,
879 svn_filesize_t sview_offset;
880 apr_size_t sview_len, tview_len, inslen, newlen, len, header_len;
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);
898 svn_txdelta_skip_svndiff_window(apr_file_t *file,
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;
907 SVN_ERR(read_window_header(stream, &sview_offset, &sview_len, &tview_len,
908 &inslen, &newlen, &header_len));
910 offset = inslen + newlen;
911 return svn_io_file_seek(file, APR_CUR, &offset, pool);
915 svn_txdelta__read_raw_window_len(apr_size_t *window_len,
916 svn_stream_t *stream,
919 svn_filesize_t sview_offset;
920 apr_size_t sview_len, tview_len, inslen, newlen, header_len;
922 SVN_ERR(read_window_header(stream, &sview_offset, &sview_len, &tview_len,
923 &inslen, &newlen, &header_len));
925 *window_len = inslen + newlen + header_len;