1251881Speter/* 2251881Speter * svndiff.c -- Encoding and decoding svndiff-format deltas. 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter 25251881Speter#include <assert.h> 26251881Speter#include <string.h> 27251881Speter#include "svn_delta.h" 28251881Speter#include "svn_io.h" 29251881Speter#include "delta.h" 30251881Speter#include "svn_pools.h" 31251881Speter#include "svn_private_config.h" 32251881Speter 33251881Speter#include "private/svn_error_private.h" 34251881Speter#include "private/svn_delta_private.h" 35299742Sdim#include "private/svn_subr_private.h" 36299742Sdim#include "private/svn_string_private.h" 37299742Sdim#include "private/svn_dep_compat.h" 38251881Speter 39251881Speter/* ----- Text delta to svndiff ----- */ 40251881Speter 41251881Speter/* We make one of these and get it passed back to us in calls to the 42251881Speter window handler. We only use it to record the write function and 43251881Speter baton passed to svn_txdelta_to_svndiff3(). */ 44251881Speterstruct encoder_baton { 45251881Speter svn_stream_t *output; 46251881Speter svn_boolean_t header_done; 47251881Speter int version; 48251881Speter int compression_level; 49251881Speter apr_pool_t *pool; 50251881Speter}; 51251881Speter 52251881Speter/* This is at least as big as the largest size for a single instruction. */ 53299742Sdim#define MAX_INSTRUCTION_LEN (2*SVN__MAX_ENCODED_UINT_LEN+1) 54251881Speter/* This is at least as big as the largest possible instructions 55251881Speter section: in theory, the instructions could be SVN_DELTA_WINDOW_SIZE 56251881Speter 1-byte copy-from-source instructions (though this is very unlikely). */ 57251881Speter#define MAX_INSTRUCTION_SECTION_LEN (SVN_DELTA_WINDOW_SIZE*MAX_INSTRUCTION_LEN) 58251881Speter 59251881Speter 60251881Speter/* Append an encoded integer to a string. */ 61251881Speterstatic void 62251881Speterappend_encoded_int(svn_stringbuf_t *header, svn_filesize_t val) 63251881Speter{ 64299742Sdim unsigned char buf[SVN__MAX_ENCODED_UINT_LEN], *p; 65251881Speter 66299742Sdim SVN_ERR_ASSERT_NO_RETURN(val >= 0); 67299742Sdim p = svn__encode_uint(buf, (apr_uint64_t)val); 68251881Speter svn_stringbuf_appendbytes(header, (const char *)buf, p - buf); 69251881Speter} 70251881Speter 71251881Speterstatic svn_error_t * 72251881Spetersend_simple_insertion_window(svn_txdelta_window_t *window, 73251881Speter struct encoder_baton *eb) 74251881Speter{ 75299742Sdim unsigned char headers[4 + 5 * SVN__MAX_ENCODED_UINT_LEN 76299742Sdim + MAX_INSTRUCTION_LEN]; 77251881Speter unsigned char ibuf[MAX_INSTRUCTION_LEN]; 78251881Speter unsigned char *header_current; 79251881Speter apr_size_t header_len; 80251881Speter apr_size_t ip_len, i; 81251881Speter apr_size_t len = window->new_data->len; 82251881Speter 83251881Speter /* there is only one target copy op. It must span the whole window */ 84251881Speter assert(window->ops[0].action_code == svn_txdelta_new); 85251881Speter assert(window->ops[0].length == window->tview_len); 86251881Speter assert(window->ops[0].offset == 0); 87251881Speter 88251881Speter /* write stream header if necessary */ 89251881Speter if (!eb->header_done) 90251881Speter { 91251881Speter eb->header_done = TRUE; 92251881Speter headers[0] = 'S'; 93251881Speter headers[1] = 'V'; 94251881Speter headers[2] = 'N'; 95251881Speter headers[3] = (unsigned char)eb->version; 96251881Speter header_current = headers + 4; 97251881Speter } 98251881Speter else 99251881Speter { 100251881Speter header_current = headers; 101251881Speter } 102251881Speter 103251881Speter /* Encode the action code and length. */ 104251881Speter if (window->tview_len >> 6 == 0) 105251881Speter { 106251881Speter ibuf[0] = (unsigned char)(window->tview_len + (0x2 << 6)); 107251881Speter ip_len = 1; 108251881Speter } 109251881Speter else 110251881Speter { 111251881Speter ibuf[0] = (0x2 << 6); 112299742Sdim ip_len = svn__encode_uint(ibuf + 1, window->tview_len) - ibuf; 113251881Speter } 114251881Speter 115251881Speter /* encode the window header. Please note that the source window may 116251881Speter * have content despite not being used for deltification. */ 117299742Sdim header_current = svn__encode_uint(header_current, 118299742Sdim (apr_uint64_t)window->sview_offset); 119299742Sdim header_current = svn__encode_uint(header_current, window->sview_len); 120299742Sdim header_current = svn__encode_uint(header_current, window->tview_len); 121251881Speter header_current[0] = (unsigned char)ip_len; /* 1 instruction */ 122299742Sdim header_current = svn__encode_uint(&header_current[1], len); 123251881Speter 124251881Speter /* append instructions (1 to a handful of bytes) */ 125251881Speter for (i = 0; i < ip_len; ++i) 126251881Speter header_current[i] = ibuf[i]; 127251881Speter 128251881Speter header_len = header_current - headers + ip_len; 129251881Speter 130251881Speter /* Write out the window. */ 131251881Speter SVN_ERR(svn_stream_write(eb->output, (const char *)headers, &header_len)); 132251881Speter if (len) 133251881Speter SVN_ERR(svn_stream_write(eb->output, window->new_data->data, &len)); 134251881Speter 135251881Speter return SVN_NO_ERROR; 136251881Speter} 137251881Speter 138251881Speterstatic svn_error_t * 139251881Speterwindow_handler(svn_txdelta_window_t *window, void *baton) 140251881Speter{ 141251881Speter struct encoder_baton *eb = baton; 142251881Speter apr_pool_t *pool; 143251881Speter svn_stringbuf_t *instructions; 144251881Speter svn_stringbuf_t *i1; 145251881Speter svn_stringbuf_t *header; 146251881Speter const svn_string_t *newdata; 147251881Speter unsigned char ibuf[MAX_INSTRUCTION_LEN], *ip; 148251881Speter const svn_txdelta_op_t *op; 149251881Speter apr_size_t len; 150251881Speter 151251881Speter /* use specialized code if there is no source */ 152251881Speter if (window && !window->src_ops && window->num_ops == 1 && !eb->version) 153251881Speter return svn_error_trace(send_simple_insertion_window(window, eb)); 154251881Speter 155251881Speter /* Make sure we write the header. */ 156251881Speter if (!eb->header_done) 157251881Speter { 158251881Speter char svnver[4] = {'S','V','N','\0'}; 159251881Speter len = 4; 160251881Speter svnver[3] = (char)eb->version; 161251881Speter SVN_ERR(svn_stream_write(eb->output, svnver, &len)); 162251881Speter eb->header_done = TRUE; 163251881Speter } 164251881Speter 165251881Speter if (window == NULL) 166251881Speter { 167251881Speter svn_stream_t *output = eb->output; 168251881Speter 169251881Speter /* We're done; clean up. 170251881Speter 171251881Speter We clean our pool first. Given that the output stream was passed 172251881Speter TO us, we'll assume it has a longer lifetime, and that it will not 173251881Speter be affected by our pool destruction. 174251881Speter 175251881Speter The contrary point of view (close the stream first): that could 176251881Speter tell our user that everything related to the output stream is done, 177251881Speter and a cleanup of the user pool should occur. However, that user 178251881Speter pool could include the subpool we created for our work (eb->pool), 179251881Speter which would then make our call to svn_pool_destroy() puke. 180251881Speter */ 181251881Speter svn_pool_destroy(eb->pool); 182251881Speter 183251881Speter return svn_stream_close(output); 184251881Speter } 185251881Speter 186251881Speter /* create the necessary data buffers */ 187251881Speter pool = svn_pool_create(eb->pool); 188251881Speter instructions = svn_stringbuf_create_empty(pool); 189251881Speter i1 = svn_stringbuf_create_empty(pool); 190251881Speter header = svn_stringbuf_create_empty(pool); 191251881Speter 192251881Speter /* Encode the instructions. */ 193251881Speter for (op = window->ops; op < window->ops + window->num_ops; op++) 194251881Speter { 195251881Speter /* Encode the action code and length. */ 196251881Speter ip = ibuf; 197251881Speter switch (op->action_code) 198251881Speter { 199251881Speter case svn_txdelta_source: *ip = 0; break; 200251881Speter case svn_txdelta_target: *ip = (0x1 << 6); break; 201251881Speter case svn_txdelta_new: *ip = (0x2 << 6); break; 202251881Speter } 203251881Speter if (op->length >> 6 == 0) 204251881Speter *ip++ |= (unsigned char)op->length; 205251881Speter else 206299742Sdim ip = svn__encode_uint(ip + 1, op->length); 207251881Speter if (op->action_code != svn_txdelta_new) 208299742Sdim ip = svn__encode_uint(ip, op->offset); 209251881Speter svn_stringbuf_appendbytes(instructions, (const char *)ibuf, ip - ibuf); 210251881Speter } 211251881Speter 212251881Speter /* Encode the header. */ 213251881Speter append_encoded_int(header, window->sview_offset); 214251881Speter append_encoded_int(header, window->sview_len); 215251881Speter append_encoded_int(header, window->tview_len); 216251881Speter if (eb->version == 1) 217251881Speter { 218299742Sdim SVN_ERR(svn__compress(instructions, i1, eb->compression_level)); 219251881Speter instructions = i1; 220251881Speter } 221251881Speter append_encoded_int(header, instructions->len); 222251881Speter if (eb->version == 1) 223251881Speter { 224299742Sdim svn_stringbuf_t *compressed = svn_stringbuf_create_empty(pool); 225299742Sdim svn_stringbuf_t *original = svn_stringbuf_create_empty(pool); 226299742Sdim original->data = (char *)window->new_data->data; /* won't be modified */ 227299742Sdim original->len = window->new_data->len; 228299742Sdim original->blocksize = window->new_data->len + 1; 229299742Sdim 230299742Sdim SVN_ERR(svn__compress(original, compressed, eb->compression_level)); 231299742Sdim newdata = svn_stringbuf__morph_into_string(compressed); 232251881Speter } 233251881Speter else 234251881Speter newdata = window->new_data; 235251881Speter 236251881Speter append_encoded_int(header, newdata->len); 237251881Speter 238251881Speter /* Write out the window. */ 239251881Speter len = header->len; 240251881Speter SVN_ERR(svn_stream_write(eb->output, header->data, &len)); 241251881Speter if (instructions->len > 0) 242251881Speter { 243251881Speter len = instructions->len; 244251881Speter SVN_ERR(svn_stream_write(eb->output, instructions->data, &len)); 245251881Speter } 246251881Speter if (newdata->len > 0) 247251881Speter { 248251881Speter len = newdata->len; 249251881Speter SVN_ERR(svn_stream_write(eb->output, newdata->data, &len)); 250251881Speter } 251251881Speter 252251881Speter svn_pool_destroy(pool); 253251881Speter return SVN_NO_ERROR; 254251881Speter} 255251881Speter 256251881Spetervoid 257251881Spetersvn_txdelta_to_svndiff3(svn_txdelta_window_handler_t *handler, 258251881Speter void **handler_baton, 259251881Speter svn_stream_t *output, 260251881Speter int svndiff_version, 261251881Speter int compression_level, 262251881Speter apr_pool_t *pool) 263251881Speter{ 264251881Speter apr_pool_t *subpool = svn_pool_create(pool); 265251881Speter struct encoder_baton *eb; 266251881Speter 267251881Speter eb = apr_palloc(subpool, sizeof(*eb)); 268251881Speter eb->output = output; 269251881Speter eb->header_done = FALSE; 270251881Speter eb->pool = subpool; 271251881Speter eb->version = svndiff_version; 272251881Speter eb->compression_level = compression_level; 273251881Speter 274251881Speter *handler = window_handler; 275251881Speter *handler_baton = eb; 276251881Speter} 277251881Speter 278251881Spetervoid 279251881Spetersvn_txdelta_to_svndiff2(svn_txdelta_window_handler_t *handler, 280251881Speter void **handler_baton, 281251881Speter svn_stream_t *output, 282251881Speter int svndiff_version, 283251881Speter apr_pool_t *pool) 284251881Speter{ 285251881Speter svn_txdelta_to_svndiff3(handler, handler_baton, output, svndiff_version, 286251881Speter SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool); 287251881Speter} 288251881Speter 289251881Spetervoid 290251881Spetersvn_txdelta_to_svndiff(svn_stream_t *output, 291251881Speter apr_pool_t *pool, 292251881Speter svn_txdelta_window_handler_t *handler, 293251881Speter void **handler_baton) 294251881Speter{ 295251881Speter svn_txdelta_to_svndiff3(handler, handler_baton, output, 0, 296251881Speter SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, pool); 297251881Speter} 298251881Speter 299251881Speter 300251881Speter/* ----- svndiff to text delta ----- */ 301251881Speter 302251881Speter/* An svndiff parser object. */ 303251881Speterstruct decode_baton 304251881Speter{ 305251881Speter /* Once the svndiff parser has enough data buffered to create a 306251881Speter "window", it passes this window to the caller's consumer routine. */ 307251881Speter svn_txdelta_window_handler_t consumer_func; 308251881Speter void *consumer_baton; 309251881Speter 310251881Speter /* Pool to create subpools from; each developing window will be a 311251881Speter subpool. */ 312251881Speter apr_pool_t *pool; 313251881Speter 314251881Speter /* The current subpool which contains our current window-buffer. */ 315251881Speter apr_pool_t *subpool; 316251881Speter 317251881Speter /* The actual svndiff data buffer, living within subpool. */ 318251881Speter svn_stringbuf_t *buffer; 319251881Speter 320251881Speter /* The offset and size of the last source view, so that we can check 321251881Speter to make sure the next one isn't sliding backwards. */ 322251881Speter svn_filesize_t last_sview_offset; 323251881Speter apr_size_t last_sview_len; 324251881Speter 325251881Speter /* We have to discard four bytes at the beginning for the header. 326251881Speter This field keeps track of how many of those bytes we have read. */ 327251881Speter apr_size_t header_bytes; 328251881Speter 329251881Speter /* Do we want an error to occur when we close the stream that 330251881Speter indicates we didn't send the whole svndiff data? If you plan to 331251881Speter not transmit the whole svndiff data stream, you will want this to 332251881Speter be FALSE. */ 333251881Speter svn_boolean_t error_on_early_close; 334251881Speter 335251881Speter /* svndiff version in use by delta. */ 336251881Speter unsigned char version; 337251881Speter}; 338251881Speter 339251881Speter 340299742Sdim/* Wrapper aroung svn__deencode_uint taking a file size as *VAL. */ 341251881Speterstatic const unsigned char * 342251881Speterdecode_file_offset(svn_filesize_t *val, 343251881Speter const unsigned char *p, 344251881Speter const unsigned char *end) 345251881Speter{ 346299742Sdim apr_uint64_t temp = 0; 347299742Sdim const unsigned char *result = svn__decode_uint(&temp, p, end); 348299742Sdim *val = (svn_filesize_t)temp; 349251881Speter 350299742Sdim return result; 351251881Speter} 352251881Speter 353251881Speter/* Same as above, only decode into a size variable. */ 354251881Speterstatic const unsigned char * 355251881Speterdecode_size(apr_size_t *val, 356251881Speter const unsigned char *p, 357251881Speter const unsigned char *end) 358251881Speter{ 359299742Sdim apr_uint64_t temp = 0; 360299742Sdim const unsigned char *result = svn__decode_uint(&temp, p, end); 361299742Sdim if (temp > APR_SIZE_MAX) 362299742Sdim return NULL; 363251881Speter 364299742Sdim *val = (apr_size_t)temp; 365299742Sdim return result; 366251881Speter} 367251881Speter 368251881Speter/* Decode an instruction into OP, returning a pointer to the text 369251881Speter after the instruction. Note that if the action code is 370251881Speter svn_txdelta_new, the offset field of *OP will not be set. */ 371251881Speterstatic const unsigned char * 372251881Speterdecode_instruction(svn_txdelta_op_t *op, 373251881Speter const unsigned char *p, 374251881Speter const unsigned char *end) 375251881Speter{ 376251881Speter apr_size_t c; 377251881Speter apr_size_t action; 378251881Speter 379251881Speter if (p == end) 380251881Speter return NULL; 381251881Speter 382251881Speter /* We need this more than once */ 383251881Speter c = *p++; 384251881Speter 385251881Speter /* Decode the instruction selector. */ 386251881Speter action = (c >> 6) & 0x3; 387251881Speter if (action >= 0x3) 388251881Speter return NULL; 389251881Speter 390251881Speter /* This relies on enum svn_delta_action values to match and never to be 391251881Speter redefined. */ 392251881Speter op->action_code = (enum svn_delta_action)(action); 393251881Speter 394251881Speter /* Decode the length and offset. */ 395251881Speter op->length = c & 0x3f; 396251881Speter if (op->length == 0) 397251881Speter { 398251881Speter p = decode_size(&op->length, p, end); 399251881Speter if (p == NULL) 400251881Speter return NULL; 401251881Speter } 402251881Speter if (action != svn_txdelta_new) 403251881Speter { 404251881Speter p = decode_size(&op->offset, p, end); 405251881Speter if (p == NULL) 406251881Speter return NULL; 407251881Speter } 408251881Speter 409251881Speter return p; 410251881Speter} 411251881Speter 412251881Speter/* Count the instructions in the range [P..END-1] and make sure they 413251881Speter are valid for the given window lengths. Return an error if the 414251881Speter instructions are invalid; otherwise set *NINST to the number of 415251881Speter instructions. */ 416251881Speterstatic svn_error_t * 417251881Spetercount_and_verify_instructions(int *ninst, 418251881Speter const unsigned char *p, 419251881Speter const unsigned char *end, 420251881Speter apr_size_t sview_len, 421251881Speter apr_size_t tview_len, 422251881Speter apr_size_t new_len) 423251881Speter{ 424251881Speter int n = 0; 425251881Speter svn_txdelta_op_t op; 426251881Speter apr_size_t tpos = 0, npos = 0; 427251881Speter 428251881Speter while (p < end) 429251881Speter { 430251881Speter p = decode_instruction(&op, p, end); 431251881Speter 432251881Speter /* Detect any malformed operations from the instruction stream. */ 433251881Speter if (p == NULL) 434251881Speter return svn_error_createf 435251881Speter (SVN_ERR_SVNDIFF_INVALID_OPS, NULL, 436251881Speter _("Invalid diff stream: insn %d cannot be decoded"), n); 437251881Speter else if (op.length == 0) 438251881Speter return svn_error_createf 439251881Speter (SVN_ERR_SVNDIFF_INVALID_OPS, NULL, 440251881Speter _("Invalid diff stream: insn %d has length zero"), n); 441251881Speter else if (op.length > tview_len - tpos) 442251881Speter return svn_error_createf 443251881Speter (SVN_ERR_SVNDIFF_INVALID_OPS, NULL, 444251881Speter _("Invalid diff stream: insn %d overflows the target view"), n); 445251881Speter 446251881Speter switch (op.action_code) 447251881Speter { 448251881Speter case svn_txdelta_source: 449251881Speter if (op.length > sview_len - op.offset || 450251881Speter op.offset > sview_len) 451251881Speter return svn_error_createf 452251881Speter (SVN_ERR_SVNDIFF_INVALID_OPS, NULL, 453251881Speter _("Invalid diff stream: " 454251881Speter "[src] insn %d overflows the source view"), n); 455251881Speter break; 456251881Speter case svn_txdelta_target: 457251881Speter if (op.offset >= tpos) 458251881Speter return svn_error_createf 459251881Speter (SVN_ERR_SVNDIFF_INVALID_OPS, NULL, 460251881Speter _("Invalid diff stream: " 461251881Speter "[tgt] insn %d starts beyond the target view position"), n); 462251881Speter break; 463251881Speter case svn_txdelta_new: 464251881Speter if (op.length > new_len - npos) 465251881Speter return svn_error_createf 466251881Speter (SVN_ERR_SVNDIFF_INVALID_OPS, NULL, 467251881Speter _("Invalid diff stream: " 468251881Speter "[new] insn %d overflows the new data section"), n); 469251881Speter npos += op.length; 470251881Speter break; 471251881Speter } 472251881Speter tpos += op.length; 473251881Speter n++; 474251881Speter } 475251881Speter if (tpos != tview_len) 476251881Speter return svn_error_create(SVN_ERR_SVNDIFF_INVALID_OPS, NULL, 477251881Speter _("Delta does not fill the target window")); 478251881Speter if (npos != new_len) 479251881Speter return svn_error_create(SVN_ERR_SVNDIFF_INVALID_OPS, NULL, 480251881Speter _("Delta does not contain enough new data")); 481251881Speter 482251881Speter *ninst = n; 483251881Speter return SVN_NO_ERROR; 484251881Speter} 485251881Speter 486299742Sdimstatic svn_error_t * 487299742Sdimzlib_decode(const unsigned char *in, apr_size_t inLen, svn_stringbuf_t *out, 488299742Sdim apr_size_t limit) 489299742Sdim{ 490299742Sdim /* construct a fake string buffer as parameter to svn__decompress. 491299742Sdim This is fine as that function never writes to it. */ 492299742Sdim svn_stringbuf_t compressed; 493299742Sdim compressed.pool = NULL; 494299742Sdim compressed.data = (char *)in; 495299742Sdim compressed.len = inLen; 496299742Sdim compressed.blocksize = inLen + 1; 497299742Sdim 498299742Sdim return svn__decompress(&compressed, out, limit); 499299742Sdim} 500299742Sdim 501251881Speter/* Given the five integer fields of a window header and a pointer to 502251881Speter the remainder of the window contents, fill in a delta window 503251881Speter structure *WINDOW. New allocations will be performed in POOL; 504251881Speter the new_data field of *WINDOW will refer directly to memory pointed 505251881Speter to by DATA. */ 506251881Speterstatic svn_error_t * 507251881Speterdecode_window(svn_txdelta_window_t *window, svn_filesize_t sview_offset, 508251881Speter apr_size_t sview_len, apr_size_t tview_len, apr_size_t inslen, 509251881Speter apr_size_t newlen, const unsigned char *data, apr_pool_t *pool, 510251881Speter unsigned int version) 511251881Speter{ 512251881Speter const unsigned char *insend; 513251881Speter int ninst; 514251881Speter apr_size_t npos; 515251881Speter svn_txdelta_op_t *ops, *op; 516251881Speter svn_string_t *new_data = apr_palloc(pool, sizeof(*new_data)); 517251881Speter 518251881Speter window->sview_offset = sview_offset; 519251881Speter window->sview_len = sview_len; 520251881Speter window->tview_len = tview_len; 521251881Speter 522251881Speter insend = data + inslen; 523251881Speter 524251881Speter if (version == 1) 525251881Speter { 526251881Speter svn_stringbuf_t *instout = svn_stringbuf_create_empty(pool); 527251881Speter svn_stringbuf_t *ndout = svn_stringbuf_create_empty(pool); 528251881Speter 529251881Speter SVN_ERR(zlib_decode(insend, newlen, ndout, 530251881Speter SVN_DELTA_WINDOW_SIZE)); 531251881Speter SVN_ERR(zlib_decode(data, insend - data, instout, 532251881Speter MAX_INSTRUCTION_SECTION_LEN)); 533251881Speter 534251881Speter newlen = ndout->len; 535251881Speter data = (unsigned char *)instout->data; 536251881Speter insend = (unsigned char *)instout->data + instout->len; 537251881Speter 538251881Speter new_data->data = (const char *) ndout->data; 539251881Speter new_data->len = newlen; 540251881Speter } 541251881Speter else 542251881Speter { 543269847Speter /* Copy the data because an svn_string_t must have the invariant 544269847Speter data[len]=='\0'. */ 545269847Speter char *buf = apr_palloc(pool, newlen + 1); 546269847Speter 547269847Speter memcpy(buf, insend, newlen); 548269847Speter buf[newlen] = '\0'; 549269847Speter new_data->data = buf; 550251881Speter new_data->len = newlen; 551251881Speter } 552251881Speter 553251881Speter /* Count the instructions and make sure they are all valid. */ 554251881Speter SVN_ERR(count_and_verify_instructions(&ninst, data, insend, 555251881Speter sview_len, tview_len, newlen)); 556251881Speter 557251881Speter /* Allocate a buffer for the instructions and decode them. */ 558251881Speter ops = apr_palloc(pool, ninst * sizeof(*ops)); 559251881Speter npos = 0; 560251881Speter window->src_ops = 0; 561251881Speter for (op = ops; op < ops + ninst; op++) 562251881Speter { 563251881Speter data = decode_instruction(op, data, insend); 564251881Speter if (op->action_code == svn_txdelta_source) 565251881Speter ++window->src_ops; 566251881Speter else if (op->action_code == svn_txdelta_new) 567251881Speter { 568251881Speter op->offset = npos; 569251881Speter npos += op->length; 570251881Speter } 571251881Speter } 572251881Speter SVN_ERR_ASSERT(data == insend); 573251881Speter 574251881Speter window->ops = ops; 575251881Speter window->num_ops = ninst; 576251881Speter window->new_data = new_data; 577251881Speter 578251881Speter return SVN_NO_ERROR; 579251881Speter} 580251881Speter 581299742Sdimstatic const char SVNDIFF_V0[] = { 'S', 'V', 'N', 0 }; 582299742Sdimstatic const char SVNDIFF_V1[] = { 'S', 'V', 'N', 1 }; 583299742Sdim#define SVNDIFF_HEADER_SIZE (sizeof(SVNDIFF_V0)) 584299742Sdim 585251881Speterstatic svn_error_t * 586251881Speterwrite_handler(void *baton, 587251881Speter const char *buffer, 588251881Speter apr_size_t *len) 589251881Speter{ 590251881Speter struct decode_baton *db = (struct decode_baton *) baton; 591251881Speter const unsigned char *p, *end; 592251881Speter svn_filesize_t sview_offset; 593251881Speter apr_size_t sview_len, tview_len, inslen, newlen, remaining; 594251881Speter apr_size_t buflen = *len; 595251881Speter 596251881Speter /* Chew up four bytes at the beginning for the header. */ 597299742Sdim if (db->header_bytes < SVNDIFF_HEADER_SIZE) 598251881Speter { 599299742Sdim apr_size_t nheader = SVNDIFF_HEADER_SIZE - db->header_bytes; 600251881Speter if (nheader > buflen) 601251881Speter nheader = buflen; 602299742Sdim if (memcmp(buffer, SVNDIFF_V0 + db->header_bytes, nheader) == 0) 603251881Speter db->version = 0; 604299742Sdim else if (memcmp(buffer, SVNDIFF_V1 + db->header_bytes, nheader) == 0) 605251881Speter db->version = 1; 606251881Speter else 607251881Speter return svn_error_create(SVN_ERR_SVNDIFF_INVALID_HEADER, NULL, 608251881Speter _("Svndiff has invalid header")); 609251881Speter buflen -= nheader; 610251881Speter buffer += nheader; 611251881Speter db->header_bytes += nheader; 612251881Speter } 613251881Speter 614251881Speter /* Concatenate the old with the new. */ 615251881Speter svn_stringbuf_appendbytes(db->buffer, buffer, buflen); 616251881Speter 617251881Speter /* We have a buffer of svndiff data that might be good for: 618251881Speter 619251881Speter a) an integral number of windows' worth of data - this is a 620251881Speter trivial case. Make windows from our data and ship them off. 621251881Speter 622251881Speter b) a non-integral number of windows' worth of data - we shall 623251881Speter consume the integral portion of the window data, and then 624251881Speter somewhere in the following loop the decoding of the svndiff 625251881Speter data will run out of stuff to decode, and will simply return 626251881Speter SVN_NO_ERROR, anxiously awaiting more data. 627251881Speter */ 628251881Speter 629251881Speter while (1) 630251881Speter { 631251881Speter apr_pool_t *newpool; 632251881Speter svn_txdelta_window_t window; 633251881Speter 634251881Speter /* Read the header, if we have enough bytes for that. */ 635251881Speter p = (const unsigned char *) db->buffer->data; 636251881Speter end = (const unsigned char *) db->buffer->data + db->buffer->len; 637251881Speter 638251881Speter p = decode_file_offset(&sview_offset, p, end); 639251881Speter if (p == NULL) 640289166Speter break; 641251881Speter 642251881Speter p = decode_size(&sview_len, p, end); 643251881Speter if (p == NULL) 644289166Speter break; 645251881Speter 646251881Speter p = decode_size(&tview_len, p, end); 647251881Speter if (p == NULL) 648289166Speter break; 649251881Speter 650251881Speter p = decode_size(&inslen, p, end); 651251881Speter if (p == NULL) 652289166Speter break; 653251881Speter 654251881Speter p = decode_size(&newlen, p, end); 655251881Speter if (p == NULL) 656289166Speter break; 657251881Speter 658251881Speter if (tview_len > SVN_DELTA_WINDOW_SIZE || 659251881Speter sview_len > SVN_DELTA_WINDOW_SIZE || 660251881Speter /* for svndiff1, newlen includes the original length */ 661299742Sdim newlen > SVN_DELTA_WINDOW_SIZE + SVN__MAX_ENCODED_UINT_LEN || 662251881Speter inslen > MAX_INSTRUCTION_SECTION_LEN) 663251881Speter return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL, 664251881Speter _("Svndiff contains a too-large window")); 665251881Speter 666251881Speter /* Check for integer overflow. */ 667251881Speter if (sview_offset < 0 || inslen + newlen < inslen 668251881Speter || sview_len + tview_len < sview_len 669251881Speter || (apr_size_t)sview_offset + sview_len < (apr_size_t)sview_offset) 670251881Speter return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL, 671251881Speter _("Svndiff contains corrupt window header")); 672251881Speter 673251881Speter /* Check for source windows which slide backwards. */ 674251881Speter if (sview_len > 0 675251881Speter && (sview_offset < db->last_sview_offset 676251881Speter || (sview_offset + sview_len 677251881Speter < db->last_sview_offset + db->last_sview_len))) 678251881Speter return svn_error_create 679251881Speter (SVN_ERR_SVNDIFF_BACKWARD_VIEW, NULL, 680251881Speter _("Svndiff has backwards-sliding source views")); 681251881Speter 682251881Speter /* Wait for more data if we don't have enough bytes for the 683251881Speter whole window. */ 684251881Speter if ((apr_size_t) (end - p) < inslen + newlen) 685251881Speter return SVN_NO_ERROR; 686251881Speter 687251881Speter /* Decode the window and send it off. */ 688251881Speter SVN_ERR(decode_window(&window, sview_offset, sview_len, tview_len, 689251881Speter inslen, newlen, p, db->subpool, 690251881Speter db->version)); 691251881Speter SVN_ERR(db->consumer_func(&window, db->consumer_baton)); 692251881Speter 693251881Speter /* Make a new subpool and buffer, saving aside the remaining 694251881Speter data in the old buffer. */ 695251881Speter newpool = svn_pool_create(db->pool); 696251881Speter p += inslen + newlen; 697251881Speter remaining = db->buffer->data + db->buffer->len - (const char *) p; 698251881Speter db->buffer = 699251881Speter svn_stringbuf_ncreate((const char *) p, remaining, newpool); 700251881Speter 701251881Speter /* Remember the offset and length of the source view for next time. */ 702251881Speter db->last_sview_offset = sview_offset; 703251881Speter db->last_sview_len = sview_len; 704251881Speter 705251881Speter /* We've copied stuff out of the old pool. Toss that pool and use 706251881Speter our new pool. 707251881Speter ### might be nice to avoid the copy and just use svn_pool_clear 708251881Speter ### to get rid of whatever the "other stuff" is. future project... 709251881Speter */ 710251881Speter svn_pool_destroy(db->subpool); 711251881Speter db->subpool = newpool; 712251881Speter } 713251881Speter 714289166Speter /* At this point we processed all integral windows and DB->BUFFER is empty 715289166Speter or contains partially read window header. 716289166Speter Check that unprocessed data is not larger that theoretical maximum 717289166Speter window header size. */ 718299742Sdim if (db->buffer->len > 5 * SVN__MAX_ENCODED_UINT_LEN) 719289166Speter return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL, 720289166Speter _("Svndiff contains a too-large window header")); 721289166Speter 722289166Speter return SVN_NO_ERROR; 723251881Speter} 724251881Speter 725251881Speter/* Minimal svn_stream_t write handler, doing nothing */ 726251881Speterstatic svn_error_t * 727251881Speternoop_write_handler(void *baton, 728251881Speter const char *buffer, 729251881Speter apr_size_t *len) 730251881Speter{ 731251881Speter return SVN_NO_ERROR; 732251881Speter} 733251881Speter 734251881Speterstatic svn_error_t * 735251881Speterclose_handler(void *baton) 736251881Speter{ 737251881Speter struct decode_baton *db = (struct decode_baton *) baton; 738251881Speter svn_error_t *err; 739251881Speter 740251881Speter /* Make sure that we're at a plausible end of stream, returning an 741251881Speter error if we are expected to do so. */ 742251881Speter if ((db->error_on_early_close) 743251881Speter && (db->header_bytes < 4 || db->buffer->len != 0)) 744251881Speter return svn_error_create(SVN_ERR_SVNDIFF_UNEXPECTED_END, NULL, 745251881Speter _("Unexpected end of svndiff input")); 746251881Speter 747251881Speter /* Tell the window consumer that we're done, and clean up. */ 748251881Speter err = db->consumer_func(NULL, db->consumer_baton); 749251881Speter svn_pool_destroy(db->pool); 750251881Speter return err; 751251881Speter} 752251881Speter 753251881Speter 754251881Spetersvn_stream_t * 755251881Spetersvn_txdelta_parse_svndiff(svn_txdelta_window_handler_t handler, 756251881Speter void *handler_baton, 757251881Speter svn_boolean_t error_on_early_close, 758251881Speter apr_pool_t *pool) 759251881Speter{ 760251881Speter apr_pool_t *subpool = svn_pool_create(pool); 761251881Speter struct decode_baton *db = apr_palloc(pool, sizeof(*db)); 762251881Speter svn_stream_t *stream; 763251881Speter 764251881Speter db->consumer_func = handler; 765251881Speter db->consumer_baton = handler_baton; 766251881Speter db->pool = subpool; 767251881Speter db->subpool = svn_pool_create(subpool); 768251881Speter db->buffer = svn_stringbuf_create_empty(db->subpool); 769251881Speter db->last_sview_offset = 0; 770251881Speter db->last_sview_len = 0; 771251881Speter db->header_bytes = 0; 772251881Speter db->error_on_early_close = error_on_early_close; 773251881Speter stream = svn_stream_create(db, pool); 774251881Speter 775251881Speter if (handler != svn_delta_noop_window_handler) 776251881Speter { 777251881Speter svn_stream_set_write(stream, write_handler); 778251881Speter svn_stream_set_close(stream, close_handler); 779251881Speter } 780251881Speter else 781251881Speter { 782251881Speter /* And else we just ignore everything as efficiently as we can. 783251881Speter by only hooking a no-op handler */ 784251881Speter svn_stream_set_write(stream, noop_write_handler); 785251881Speter } 786251881Speter return stream; 787251881Speter} 788251881Speter 789251881Speter 790251881Speter/* Routines for reading one svndiff window at a time. */ 791251881Speter 792251881Speter/* Read one byte from STREAM into *BYTE. */ 793251881Speterstatic svn_error_t * 794251881Speterread_one_byte(unsigned char *byte, svn_stream_t *stream) 795251881Speter{ 796251881Speter char c; 797251881Speter apr_size_t len = 1; 798251881Speter 799299742Sdim SVN_ERR(svn_stream_read_full(stream, &c, &len)); 800251881Speter if (len == 0) 801251881Speter return svn_error_create(SVN_ERR_SVNDIFF_UNEXPECTED_END, NULL, 802251881Speter _("Unexpected end of svndiff input")); 803251881Speter *byte = (unsigned char) c; 804251881Speter return SVN_NO_ERROR; 805251881Speter} 806251881Speter 807299742Sdim/* Read and decode one integer from STREAM into *SIZE. 808299742Sdim Increment *BYTE_COUNTER by the number of chars we have read. */ 809251881Speterstatic svn_error_t * 810299742Sdimread_one_size(apr_size_t *size, 811299742Sdim apr_size_t *byte_counter, 812299742Sdim svn_stream_t *stream) 813251881Speter{ 814251881Speter unsigned char c; 815251881Speter 816251881Speter *size = 0; 817251881Speter while (1) 818251881Speter { 819251881Speter SVN_ERR(read_one_byte(&c, stream)); 820299742Sdim ++*byte_counter; 821251881Speter *size = (*size << 7) | (c & 0x7f); 822251881Speter if (!(c & 0x80)) 823251881Speter break; 824251881Speter } 825251881Speter return SVN_NO_ERROR; 826251881Speter} 827251881Speter 828251881Speter/* Read a window header from STREAM and check it for integer overflow. */ 829251881Speterstatic svn_error_t * 830251881Speterread_window_header(svn_stream_t *stream, svn_filesize_t *sview_offset, 831251881Speter apr_size_t *sview_len, apr_size_t *tview_len, 832299742Sdim apr_size_t *inslen, apr_size_t *newlen, 833299742Sdim apr_size_t *header_len) 834251881Speter{ 835251881Speter unsigned char c; 836251881Speter 837251881Speter /* Read the source view offset by hand, since it's not an apr_size_t. */ 838299742Sdim *header_len = 0; 839251881Speter *sview_offset = 0; 840251881Speter while (1) 841251881Speter { 842251881Speter SVN_ERR(read_one_byte(&c, stream)); 843299742Sdim ++*header_len; 844251881Speter *sview_offset = (*sview_offset << 7) | (c & 0x7f); 845251881Speter if (!(c & 0x80)) 846251881Speter break; 847251881Speter } 848251881Speter 849251881Speter /* Read the four size fields. */ 850299742Sdim SVN_ERR(read_one_size(sview_len, header_len, stream)); 851299742Sdim SVN_ERR(read_one_size(tview_len, header_len, stream)); 852299742Sdim SVN_ERR(read_one_size(inslen, header_len, stream)); 853299742Sdim SVN_ERR(read_one_size(newlen, header_len, stream)); 854251881Speter 855251881Speter if (*tview_len > SVN_DELTA_WINDOW_SIZE || 856251881Speter *sview_len > SVN_DELTA_WINDOW_SIZE || 857251881Speter /* for svndiff1, newlen includes the original length */ 858299742Sdim *newlen > SVN_DELTA_WINDOW_SIZE + SVN__MAX_ENCODED_UINT_LEN || 859251881Speter *inslen > MAX_INSTRUCTION_SECTION_LEN) 860251881Speter return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL, 861251881Speter _("Svndiff contains a too-large window")); 862251881Speter 863251881Speter /* Check for integer overflow. */ 864251881Speter if (*sview_offset < 0 || *inslen + *newlen < *inslen 865251881Speter || *sview_len + *tview_len < *sview_len 866251881Speter || (apr_size_t)*sview_offset + *sview_len < (apr_size_t)*sview_offset) 867251881Speter return svn_error_create(SVN_ERR_SVNDIFF_CORRUPT_WINDOW, NULL, 868251881Speter _("Svndiff contains corrupt window header")); 869251881Speter 870251881Speter return SVN_NO_ERROR; 871251881Speter} 872251881Speter 873251881Spetersvn_error_t * 874251881Spetersvn_txdelta_read_svndiff_window(svn_txdelta_window_t **window, 875251881Speter svn_stream_t *stream, 876251881Speter int svndiff_version, 877251881Speter apr_pool_t *pool) 878251881Speter{ 879251881Speter svn_filesize_t sview_offset; 880299742Sdim apr_size_t sview_len, tview_len, inslen, newlen, len, header_len; 881251881Speter unsigned char *buf; 882251881Speter 883251881Speter SVN_ERR(read_window_header(stream, &sview_offset, &sview_len, &tview_len, 884299742Sdim &inslen, &newlen, &header_len)); 885251881Speter len = inslen + newlen; 886251881Speter buf = apr_palloc(pool, len); 887299742Sdim SVN_ERR(svn_stream_read_full(stream, (char*)buf, &len)); 888251881Speter if (len < inslen + newlen) 889251881Speter return svn_error_create(SVN_ERR_SVNDIFF_UNEXPECTED_END, NULL, 890251881Speter _("Unexpected end of svndiff input")); 891251881Speter *window = apr_palloc(pool, sizeof(**window)); 892251881Speter return decode_window(*window, sview_offset, sview_len, tview_len, inslen, 893251881Speter newlen, buf, pool, svndiff_version); 894251881Speter} 895251881Speter 896251881Speter 897251881Spetersvn_error_t * 898251881Spetersvn_txdelta_skip_svndiff_window(apr_file_t *file, 899251881Speter int svndiff_version, 900251881Speter apr_pool_t *pool) 901251881Speter{ 902251881Speter svn_stream_t *stream = svn_stream_from_aprfile2(file, TRUE, pool); 903251881Speter svn_filesize_t sview_offset; 904299742Sdim apr_size_t sview_len, tview_len, inslen, newlen, header_len; 905251881Speter apr_off_t offset; 906251881Speter 907251881Speter SVN_ERR(read_window_header(stream, &sview_offset, &sview_len, &tview_len, 908299742Sdim &inslen, &newlen, &header_len)); 909251881Speter 910251881Speter offset = inslen + newlen; 911251881Speter return svn_io_file_seek(file, APR_CUR, &offset, pool); 912251881Speter} 913251881Speter 914251881Spetersvn_error_t * 915299742Sdimsvn_txdelta__read_raw_window_len(apr_size_t *window_len, 916299742Sdim svn_stream_t *stream, 917299742Sdim apr_pool_t *pool) 918251881Speter{ 919299742Sdim svn_filesize_t sview_offset; 920299742Sdim apr_size_t sview_len, tview_len, inslen, newlen, header_len; 921299742Sdim 922299742Sdim SVN_ERR(read_window_header(stream, &sview_offset, &sview_len, &tview_len, 923299742Sdim &inslen, &newlen, &header_len)); 924299742Sdim 925299742Sdim *window_len = inslen + newlen + header_len; 926299742Sdim return SVN_NO_ERROR; 927251881Speter} 928251881Speter 929