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