249efb6eeda1b6fe7eff488cdeedbaf74fddff73
   1/*
   2 * Licensed under a two-clause BSD-style license.
   3 * See LICENSE for details.
   4 */
   5
   6#include "git-compat-util.h"
   7#include "line_buffer.h"
   8#include "svndiff.h"
   9
  10/*
  11 * svndiff0 applier
  12 *
  13 * See http://svn.apache.org/repos/asf/subversion/trunk/notes/svndiff.
  14 *
  15 * svndiff0 ::= 'SVN\0' window*
  16 * window ::= int int int int int instructions inline_data;
  17 * int ::= highdigit* lowdigit;
  18 * highdigit ::= # binary 1000 0000 OR-ed with 7 bit value;
  19 * lowdigit ::= # 7 bit value;
  20 */
  21
  22#define VLI_CONTINUE    0x80
  23#define VLI_DIGIT_MASK  0x7f
  24#define VLI_BITS_PER_DIGIT 7
  25
  26static int error_short_read(struct line_buffer *input)
  27{
  28        if (buffer_ferror(input))
  29                return error("error reading delta: %s", strerror(errno));
  30        return error("invalid delta: unexpected end of file");
  31}
  32
  33static int read_magic(struct line_buffer *in, off_t *len)
  34{
  35        static const char magic[] = {'S', 'V', 'N', '\0'};
  36        struct strbuf sb = STRBUF_INIT;
  37
  38        if (*len < sizeof(magic) ||
  39            buffer_read_binary(in, &sb, sizeof(magic)) != sizeof(magic)) {
  40                error_short_read(in);
  41                strbuf_release(&sb);
  42                return -1;
  43        }
  44
  45        if (memcmp(sb.buf, magic, sizeof(magic))) {
  46                strbuf_release(&sb);
  47                return error("invalid delta: unrecognized file type");
  48        }
  49
  50        *len -= sizeof(magic);
  51        strbuf_release(&sb);
  52        return 0;
  53}
  54
  55static int read_int(struct line_buffer *in, uintmax_t *result, off_t *len)
  56{
  57        uintmax_t rv = 0;
  58        off_t sz;
  59        for (sz = *len; sz; sz--) {
  60                const int ch = buffer_read_char(in);
  61                if (ch == EOF)
  62                        break;
  63
  64                rv <<= VLI_BITS_PER_DIGIT;
  65                rv += (ch & VLI_DIGIT_MASK);
  66                if (ch & VLI_CONTINUE)
  67                        continue;
  68
  69                *result = rv;
  70                *len = sz - 1;
  71                return 0;
  72        }
  73        return error_short_read(in);
  74}
  75
  76static int read_offset(struct line_buffer *in, off_t *result, off_t *len)
  77{
  78        uintmax_t val;
  79        if (read_int(in, &val, len))
  80                return -1;
  81        if (val > maximum_signed_value_of_type(off_t))
  82                return error("unrepresentable offset in delta: %"PRIuMAX"", val);
  83        *result = val;
  84        return 0;
  85}
  86
  87static int read_length(struct line_buffer *in, size_t *result, off_t *len)
  88{
  89        uintmax_t val;
  90        if (read_int(in, &val, len))
  91                return -1;
  92        if (val > SIZE_MAX)
  93                return error("unrepresentable length in delta: %"PRIuMAX"", val);
  94        *result = val;
  95        return 0;
  96}
  97
  98static int apply_one_window(struct line_buffer *delta, off_t *delta_len)
  99{
 100        size_t out_len;
 101        size_t instructions_len;
 102        size_t data_len;
 103        assert(delta_len);
 104
 105        /* "source view" offset and length already handled; */
 106        if (read_length(delta, &out_len, delta_len) ||
 107            read_length(delta, &instructions_len, delta_len) ||
 108            read_length(delta, &data_len, delta_len))
 109                return -1;
 110        if (instructions_len)
 111                return error("What do you think I am?  A delta applier?");
 112        if (data_len)
 113                return error("No support for inline data yet");
 114        return 0;
 115}
 116
 117int svndiff0_apply(struct line_buffer *delta, off_t delta_len,
 118                        struct sliding_view *preimage, FILE *postimage)
 119{
 120        assert(delta && preimage && postimage);
 121
 122        if (read_magic(delta, &delta_len))
 123                return -1;
 124        while (delta_len) {     /* For each window: */
 125                off_t pre_off;
 126                size_t pre_len;
 127
 128                if (read_offset(delta, &pre_off, &delta_len) ||
 129                    read_length(delta, &pre_len, &delta_len) ||
 130                    apply_one_window(delta, &delta_len))
 131                        return -1;
 132        }
 133        return 0;
 134}