mirror of
https://github.com/git/git.git
synced 2024-11-19 08:14:00 +01:00
8e9faf27c1
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 <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
217 lines
4.7 KiB
C
217 lines
4.7 KiB
C
#include "cache.h"
|
|
#include "pkt-line.h"
|
|
|
|
char packet_buffer[LARGE_PACKET_MAX];
|
|
static const char *packet_trace_prefix = "git";
|
|
static struct trace_key trace_packet = TRACE_KEY_INIT(PACKET);
|
|
|
|
void packet_trace_identity(const char *prog)
|
|
{
|
|
packet_trace_prefix = xstrdup(prog);
|
|
}
|
|
|
|
static void packet_trace(const char *buf, unsigned int len, int write)
|
|
{
|
|
int i;
|
|
struct strbuf out;
|
|
|
|
if (!trace_want(&trace_packet))
|
|
return;
|
|
|
|
/* +32 is just a guess for header + quoting */
|
|
strbuf_init(&out, len+32);
|
|
|
|
strbuf_addf(&out, "packet: %12s%c ",
|
|
packet_trace_prefix, write ? '>' : '<');
|
|
|
|
if ((len >= 4 && starts_with(buf, "PACK")) ||
|
|
(len >= 5 && starts_with(buf+1, "PACK"))) {
|
|
strbuf_addstr(&out, "PACK ...");
|
|
trace_disable(&trace_packet);
|
|
}
|
|
else {
|
|
/* XXX we should really handle printable utf8 */
|
|
for (i = 0; i < len; i++) {
|
|
/* suppress newlines */
|
|
if (buf[i] == '\n')
|
|
continue;
|
|
if (buf[i] >= 0x20 && buf[i] <= 0x7e)
|
|
strbuf_addch(&out, buf[i]);
|
|
else
|
|
strbuf_addf(&out, "\\%o", buf[i]);
|
|
}
|
|
}
|
|
|
|
strbuf_addch(&out, '\n');
|
|
trace_strbuf(&trace_packet, &out);
|
|
strbuf_release(&out);
|
|
}
|
|
|
|
/*
|
|
* If we buffered things up above (we don't, but we should),
|
|
* we'd flush it here
|
|
*/
|
|
void packet_flush(int fd)
|
|
{
|
|
packet_trace("0000", 4, 1);
|
|
write_or_die(fd, "0000", 4);
|
|
}
|
|
|
|
void packet_buf_flush(struct strbuf *buf)
|
|
{
|
|
packet_trace("0000", 4, 1);
|
|
strbuf_add(buf, "0000", 4);
|
|
}
|
|
|
|
#define hex(a) (hexchar[(a) & 15])
|
|
static void format_packet(struct strbuf *out, const char *fmt, va_list args)
|
|
{
|
|
static char hexchar[] = "0123456789abcdef";
|
|
size_t orig_len, n;
|
|
|
|
orig_len = out->len;
|
|
strbuf_addstr(out, "0000");
|
|
strbuf_vaddf(out, fmt, args);
|
|
n = out->len - orig_len;
|
|
|
|
if (n > LARGE_PACKET_MAX)
|
|
die("protocol error: impossibly long line");
|
|
|
|
out->buf[orig_len + 0] = hex(n >> 12);
|
|
out->buf[orig_len + 1] = hex(n >> 8);
|
|
out->buf[orig_len + 2] = hex(n >> 4);
|
|
out->buf[orig_len + 3] = hex(n);
|
|
packet_trace(out->buf + orig_len + 4, n - 4, 1);
|
|
}
|
|
|
|
void packet_write(int fd, const char *fmt, ...)
|
|
{
|
|
static struct strbuf buf = STRBUF_INIT;
|
|
va_list args;
|
|
|
|
strbuf_reset(&buf);
|
|
va_start(args, fmt);
|
|
format_packet(&buf, fmt, args);
|
|
va_end(args);
|
|
write_or_die(fd, buf.buf, buf.len);
|
|
}
|
|
|
|
void packet_buf_write(struct strbuf *buf, const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
format_packet(buf, fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
static int get_packet_data(int fd, char **src_buf, size_t *src_size,
|
|
void *dst, unsigned size, int options)
|
|
{
|
|
ssize_t ret;
|
|
|
|
if (fd >= 0 && src_buf && *src_buf)
|
|
die("BUG: multiple sources given to packet_read");
|
|
|
|
/* Read up to "size" bytes from our source, whatever it is. */
|
|
if (src_buf && *src_buf) {
|
|
ret = size < *src_size ? size : *src_size;
|
|
memcpy(dst, *src_buf, ret);
|
|
*src_buf += ret;
|
|
*src_size -= ret;
|
|
} else {
|
|
ret = read_in_full(fd, dst, size);
|
|
if (ret < 0)
|
|
die_errno("read error");
|
|
}
|
|
|
|
/* And complain if we didn't get enough bytes to satisfy the read. */
|
|
if (ret < size) {
|
|
if (options & PACKET_READ_GENTLE_ON_EOF)
|
|
return -1;
|
|
|
|
die("The remote end hung up unexpectedly");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int packet_length(const char *linelen)
|
|
{
|
|
int n;
|
|
int len = 0;
|
|
|
|
for (n = 0; n < 4; n++) {
|
|
unsigned char c = linelen[n];
|
|
len <<= 4;
|
|
if (c >= '0' && c <= '9') {
|
|
len += c - '0';
|
|
continue;
|
|
}
|
|
if (c >= 'a' && c <= 'f') {
|
|
len += c - 'a' + 10;
|
|
continue;
|
|
}
|
|
if (c >= 'A' && c <= 'F') {
|
|
len += c - 'A' + 10;
|
|
continue;
|
|
}
|
|
return -1;
|
|
}
|
|
return len;
|
|
}
|
|
|
|
int packet_read(int fd, char **src_buf, size_t *src_len,
|
|
char *buffer, unsigned size, int options)
|
|
{
|
|
int len, ret;
|
|
char linelen[4];
|
|
|
|
ret = get_packet_data(fd, src_buf, src_len, linelen, 4, options);
|
|
if (ret < 0)
|
|
return ret;
|
|
len = packet_length(linelen);
|
|
if (len < 0)
|
|
die("protocol error: bad line length character: %.4s", linelen);
|
|
if (!len) {
|
|
packet_trace("0000", 4, 0);
|
|
return 0;
|
|
}
|
|
len -= 4;
|
|
if (len >= size)
|
|
die("protocol error: bad line length %d", len);
|
|
ret = get_packet_data(fd, src_buf, src_len, buffer, len, options);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if ((options & PACKET_READ_CHOMP_NEWLINE) &&
|
|
len && buffer[len-1] == '\n')
|
|
len--;
|
|
|
|
buffer[len] = 0;
|
|
packet_trace(buffer, len, 0);
|
|
return len;
|
|
}
|
|
|
|
static char *packet_read_line_generic(int fd,
|
|
char **src, size_t *src_len,
|
|
int *dst_len)
|
|
{
|
|
int len = packet_read(fd, src, src_len,
|
|
packet_buffer, sizeof(packet_buffer),
|
|
PACKET_READ_CHOMP_NEWLINE);
|
|
if (dst_len)
|
|
*dst_len = len;
|
|
return len ? packet_buffer : NULL;
|
|
}
|
|
|
|
char *packet_read_line(int fd, int *len_p)
|
|
{
|
|
return packet_read_line_generic(fd, NULL, NULL, len_p);
|
|
}
|
|
|
|
char *packet_read_line_buf(char **src, size_t *src_len, int *dst_len)
|
|
{
|
|
return packet_read_line_generic(-1, src, src_len, dst_len);
|
|
}
|