vcs-svn: parse svndiff0 window header
Each window in a subversion delta (svndiff0-format file) starts with a window header, consisting of five integers with variable-length representation: source view offset source view length output length instructions length auxiliary data length Parse it. The result is not usable for deltas with nonempty postimage yet; in fact, this only adds support for deltas without any instructions or auxiliary data. This is a good place to stop, though, since that little support lets us add some simple passing tests concerning error handling to the test suite. Improved-by: Ramkumar Ramachandra <artagnon@gmail.com> Improved-by: David Barr <david.barr@cordelta.com> Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Jonathan Nieder committed
Oct 13, 2010 at 04:21 UTC
252712111fad127db365e3dd764309fe5658679a
2 files changed
+142
-6
t/t9011-svn-da.sh
+55
-1
@@ -26,10 +26,64 @@ test_expect_success 'reject svndiff2' '
26
test_must_fail test-svn-fe -d preimage bad.filetype 4
27
'
28
29
-test_expect_failure 'one-window empty delta' '
29
+test_expect_success 'one-window empty delta' '
30
printf "SVNQ%s" "QQQQQ" | q_to_nul >clear.onewindow &&
31
test-svn-fe -d preimage clear.onewindow 9 >actual &&
32
test_cmp empty actual
33
'
34
35
+test_expect_success 'reject incomplete window header' '
36
+ printf "SVNQ%s" "QQQQQ" | q_to_nul >clear.onewindow &&
37
+ printf "SVNQ%s" "QQ" | q_to_nul >clear.partialwindow &&
38
+ test_must_fail test-svn-fe -d preimage clear.onewindow 6 &&
39
+ test_must_fail test-svn-fe -d preimage clear.partialwindow 6
40
+'
41
+
42
+test_expect_success 'reject declared delta longer than actual delta' '
43
+ printf "SVNQ%s" "QQQQQ" | q_to_nul >clear.onewindow &&
44
+ printf "SVNQ%s" "QQ" | q_to_nul >clear.partialwindow &&
45
+ test_must_fail test-svn-fe -d preimage clear.onewindow 14 &&
46
+ test_must_fail test-svn-fe -d preimage clear.partialwindow 9
47
+'
48
+
49
+test_expect_success 'two-window empty delta' '
50
+ printf "SVNQ%s%s" "QQQQQ" "QQQQQ" | q_to_nul >clear.twowindow &&
51
+ test-svn-fe -d preimage clear.twowindow 14 >actual &&
52
+ test_must_fail test-svn-fe -d preimage clear.twowindow 13 &&
53
+ test_cmp empty actual
54
+'
55
+
56
+test_expect_success 'noisy zeroes' '
57
+ printf "SVNQ%s" \
58
+ "RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQ" |
59
+ tr R "\200" |
60
+ q_to_nul >clear.noisy &&
61
+ len=$(wc -c <clear.noisy) &&
62
+ test-svn-fe -d preimage clear.noisy $len &&
63
+ test_cmp empty actual
64
+'
65
+
66
+test_expect_success 'reject variable-length int in magic' '
67
+ printf "SVNRQ" | tr R "\200" | q_to_nul >clear.badmagic &&
68
+ test_must_fail test-svn-fe -d preimage clear.badmagic 5
69
+'
70
+
71
+test_expect_success 'reject truncated integer' '
72
+ printf "SVNQ%s%s" "QQQQQ" "QQQQRRQ" |
73
+ tr R "\200" |
74
+ q_to_nul >clear.fullint &&
75
+ printf "SVNQ%s%s" "QQQQQ" "QQQQRR" |
76
+ tr RT "\201" |
77
+ q_to_nul >clear.partialint &&
78
+ test_must_fail test-svn-fe -d preimage clear.fullint 15 &&
79
+ test-svn-fe -d preimage clear.fullint 16 &&
80
+ test_must_fail test-svn-fe -d preimage clear.partialint 15
81
+'
82
+
83
+test_expect_success 'nonempty (but unused) preimage view' '
84
+ printf "SVNQ%b" "Q\003QQQ" | q_to_nul >clear.readpreimage &&
85
+ test-svn-fe -d preimage clear.readpreimage 9 >actual &&
86
+ test_cmp empty actual
87
+'
88
+
89
test_done
vcs-svn/svndiff.c
+87
-5
@@ -13,8 +13,16 @@
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
+
26
static int error_short_read(struct line_buffer *input)
27
{
28
if (buffer_ferror(input))
@@ -28,17 +36,84 @@ static int read_magic(struct line_buffer *in, off_t *len)
36
struct strbuf sb = STRBUF_INIT;
37
38
if (*len < sizeof(magic) ||
31
- buffer_read_binary(in, &sb, sizeof(magic)) != sizeof(magic))
32
- return error_short_read(in);
39
+ buffer_read_binary(in, &sb, sizeof(magic)) != sizeof(magic)) {
40
+ error_short_read(in);
41
+ strbuf_release(&sb);
42
+ return -1;
43
+ }
44
34
- if (memcmp(sb.buf, magic, sizeof(magic)))
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
55
+static 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
+
76
+static 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
+
87
+static 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
+
98
+static 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
+
117
int svndiff0_apply(struct line_buffer *delta, off_t delta_len,
118
struct sliding_view *preimage, FILE *postimage)
119
{
@@ -46,7 +121,14 @@ int svndiff0_apply(struct line_buffer *delta, off_t delta_len,
121
122
if (read_magic(delta, &delta_len))
123
return -1;
49
- if (delta_len)
50
- return error("What do you think I am? A delta applier?");
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
}