Skip to content

Commit 8e9faf2

Browse files
peffgitster
authored andcommitted
pkt-line: allow writing of LARGE_PACKET_MAX buffers
When we send out pkt-lines with refnames, we use a static 1000-byte buffer. This means that the maximum size of a ref over the git protocol is around 950 bytes (the exact size depends on the protocol line being written, but figure on a sha1 plus some boilerplate). This is enough for any sane workflow, but occasionally odd things happen (e.g., a bug may create a ref "foo/foo/foo/..." accidentally). With the current code, you cannot even use "push" to delete such a ref from a remote. Let's switch to using a strbuf, with a hard-limit of LARGE_PACKET_MAX (which is specified by the protocol). This matches the size of the readers, as of 74543a0 (pkt-line: provide a LARGE_PACKET_MAX static buffer, 2013-02-20). Versions of git older than that will complain about our large packets, but it's really no worse than the current behavior. Right now the sender barfs with "impossibly long line" trying to send the packet, and afterwards the reader will barf with "protocol error: bad line length %d", which is arguably better anyway. Note that we're not really _solving_ the problem here, but just bumping the limits. In theory, the length of a ref is unbounded, and pkt-line can only represent sizes up to 65531 bytes. So we are just bumping the limit, not removing it. But hopefully 64K should be enough for anyone. As a bonus, by using a strbuf for the formatting we can eliminate an unnecessary copy in format_buf_write. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent c18b867 commit 8e9faf2

File tree

2 files changed

+52
-18
lines changed

2 files changed

+52
-18
lines changed

pkt-line.c

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -64,44 +64,45 @@ void packet_buf_flush(struct strbuf *buf)
6464
}
6565

6666
#define hex(a) (hexchar[(a) & 15])
67-
static char buffer[1000];
68-
static unsigned format_packet(const char *fmt, va_list args)
67+
static void format_packet(struct strbuf *out, const char *fmt, va_list args)
6968
{
7069
static char hexchar[] = "0123456789abcdef";
71-
unsigned n;
70+
size_t orig_len, n;
7271

73-
n = vsnprintf(buffer + 4, sizeof(buffer) - 4, fmt, args);
74-
if (n >= sizeof(buffer)-4)
72+
orig_len = out->len;
73+
strbuf_addstr(out, "0000");
74+
strbuf_vaddf(out, fmt, args);
75+
n = out->len - orig_len;
76+
77+
if (n > LARGE_PACKET_MAX)
7578
die("protocol error: impossibly long line");
76-
n += 4;
77-
buffer[0] = hex(n >> 12);
78-
buffer[1] = hex(n >> 8);
79-
buffer[2] = hex(n >> 4);
80-
buffer[3] = hex(n);
81-
packet_trace(buffer+4, n-4, 1);
82-
return n;
79+
80+
out->buf[orig_len + 0] = hex(n >> 12);
81+
out->buf[orig_len + 1] = hex(n >> 8);
82+
out->buf[orig_len + 2] = hex(n >> 4);
83+
out->buf[orig_len + 3] = hex(n);
84+
packet_trace(out->buf + orig_len + 4, n - 4, 1);
8385
}
8486

8587
void packet_write(int fd, const char *fmt, ...)
8688
{
89+
static struct strbuf buf = STRBUF_INIT;
8790
va_list args;
88-
unsigned n;
8991

92+
strbuf_reset(&buf);
9093
va_start(args, fmt);
91-
n = format_packet(fmt, args);
94+
format_packet(&buf, fmt, args);
9295
va_end(args);
93-
write_or_die(fd, buffer, n);
96+
write_or_die(fd, buf.buf, buf.len);
9497
}
9598

9699
void packet_buf_write(struct strbuf *buf, const char *fmt, ...)
97100
{
98101
va_list args;
99-
unsigned n;
100102

101103
va_start(args, fmt);
102-
n = format_packet(fmt, args);
104+
format_packet(buf, fmt, args);
103105
va_end(args);
104-
strbuf_add(buf, buffer, n);
105106
}
106107

107108
static int get_packet_data(int fd, char **src_buf, size_t *src_size,

t/t5527-fetch-odd-refs.sh

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,37 @@ test_expect_success 'suffix ref is ignored during fetch' '
2626
test_cmp expect actual
2727
'
2828

29+
test_expect_success 'try to create repo with absurdly long refname' '
30+
ref240=$_z40/$_z40/$_z40/$_z40/$_z40/$_z40 &&
31+
ref1440=$ref240/$ref240/$ref240/$ref240/$ref240/$ref240 &&
32+
git init long &&
33+
(
34+
cd long &&
35+
test_commit long &&
36+
test_commit master
37+
) &&
38+
if git -C long update-ref refs/heads/$ref1440 long; then
39+
test_set_prereq LONG_REF
40+
else
41+
echo >&2 "long refs not supported"
42+
fi
43+
'
44+
45+
test_expect_success LONG_REF 'fetch handles extremely long refname' '
46+
git fetch long refs/heads/*:refs/remotes/long/* &&
47+
cat >expect <<-\EOF &&
48+
long
49+
master
50+
EOF
51+
git for-each-ref --format="%(subject)" refs/remotes/long >actual &&
52+
test_cmp expect actual
53+
'
54+
55+
test_expect_success LONG_REF 'push handles extremely long refname' '
56+
git push long :refs/heads/$ref1440 &&
57+
git -C long for-each-ref --format="%(subject)" refs/heads >actual &&
58+
echo master >expect &&
59+
test_cmp expect actual
60+
'
61+
2962
test_done

0 commit comments

Comments
 (0)