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