Merge branch 'jk/pkt-line-cleanup'

Clean up pkt-line API, implementation and its callers to make them
more robust.

* jk/pkt-line-cleanup:
  do not use GIT_TRACE_PACKET=3 in tests
  remote-curl: always parse incoming refs
  remote-curl: move ref-parsing code up in file
  remote-curl: pass buffer straight to get_remote_heads
  teach get_remote_heads to read from a memory buffer
  pkt-line: share buffer/descriptor reading implementation
  pkt-line: provide a LARGE_PACKET_MAX static buffer
  pkt-line: move LARGE_PACKET_MAX definition from sideband
  pkt-line: teach packet_read_line to chomp newlines
  pkt-line: provide a generic reading function with options
  pkt-line: drop safe_write function
  pkt-line: move a misplaced comment
  write_or_die: raise SIGPIPE when we get EPIPE
  upload-archive: use argv_array to store client arguments
  upload-archive: do not copy repo name
  send-pack: prefer prefixcmp over memcmp in receive_status
  fetch-pack: fix out-of-bounds buffer offset in get_ack
  upload-pack: remove packet debugging harness
  upload-pack: do not add duplicate objects to shallow list
  upload-pack: use get_sha1_hex to parse "shallow" lines
This commit is contained in:
Junio C Hamano 2013-04-01 08:59:37 -07:00
Родитель 900c8ecb5c 2ad23273e7
Коммит e013bdab0f
22 изменённых файлов: 363 добавлений и 390 удалений

Просмотреть файл

@ -27,8 +27,8 @@ static int run_remote_archiver(int argc, const char **argv,
const char *remote, const char *exec,
const char *name_hint)
{
char buf[LARGE_PACKET_MAX];
int fd[2], i, len, rv;
char *buf;
int fd[2], i, rv;
struct transport *transport;
struct remote *_remote;
@ -53,21 +53,18 @@ static int run_remote_archiver(int argc, const char **argv,
packet_write(fd[1], "argument %s\n", argv[i]);
packet_flush(fd[1]);
len = packet_read_line(fd[0], buf, sizeof(buf));
if (!len)
buf = packet_read_line(fd[0], NULL);
if (!buf)
die(_("git archive: expected ACK/NAK, got EOF"));
if (buf[len-1] == '\n')
buf[--len] = 0;
if (strcmp(buf, "ACK")) {
if (len > 5 && !prefixcmp(buf, "NACK "))
if (!prefixcmp(buf, "NACK "))
die(_("git archive: NACK %s"), buf + 5);
if (len > 4 && !prefixcmp(buf, "ERR "))
if (!prefixcmp(buf, "ERR "))
die(_("remote error: %s"), buf + 4);
die(_("git archive: protocol error"));
}
len = packet_read_line(fd[0], buf, sizeof(buf));
if (len)
if (packet_read_line(fd[0], NULL))
die(_("git archive: expected a flush"));
/* Now, start reading from fd[0] and spit it out to stdout */

Просмотреть файл

@ -119,14 +119,11 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
/* in stateless RPC mode we use pkt-line to read
* from stdin, until we get a flush packet
*/
static char line[1000];
for (;;) {
int n = packet_read_line(0, line, sizeof(line));
if (!n)
char *line = packet_read_line(0, NULL);
if (!line)
break;
if (line[n-1] == '\n')
n--;
add_sought_entry_mem(&sought, &nr_sought, &alloc_sought, line, n);
add_sought_entry(&sought, &nr_sought, &alloc_sought, line);
}
}
else {
@ -147,7 +144,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
args.verbose ? CONNECT_VERBOSE : 0);
}
get_remote_heads(fd[0], &ref, 0, NULL);
get_remote_heads(fd[0], NULL, 0, &ref, 0, NULL);
ref = fetch_pack(&args, fd, conn, ref, dest,
sought, nr_sought, pack_lockfile_ptr);

Просмотреть файл

@ -754,17 +754,15 @@ static struct command *read_head_info(void)
struct command *commands = NULL;
struct command **p = &commands;
for (;;) {
static char line[1000];
char *line;
unsigned char old_sha1[20], new_sha1[20];
struct command *cmd;
char *refname;
int len, reflen;
len = packet_read_line(0, line, sizeof(line));
if (!len)
line = packet_read_line(0, &len);
if (!line)
break;
if (line[len-1] == '\n')
line[--len] = 0;
if (len < 83 ||
line[40] != ' ' ||
line[81] != ' ' ||
@ -932,7 +930,7 @@ static void report(struct command *commands, const char *unpack_status)
if (use_sideband)
send_sideband(1, 1, buf.buf, buf.len, use_sideband);
else
safe_write(1, buf.buf, buf.len);
write_or_die(1, buf.buf, buf.len);
strbuf_release(&buf);
}

Просмотреть файл

@ -79,7 +79,7 @@ static void print_helper_status(struct ref *ref)
}
strbuf_addch(&buf, '\n');
safe_write(1, buf.buf, buf.len);
write_or_die(1, buf.buf, buf.len);
}
strbuf_release(&buf);
}
@ -207,7 +207,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
memset(&extra_have, 0, sizeof(extra_have));
get_remote_heads(fd[0], &remote_refs, REF_NORMAL, &extra_have);
get_remote_heads(fd[0], NULL, 0, &remote_refs, REF_NORMAL, &extra_have);
transport_verify_remote_names(nr_refspecs, refspecs);

Просмотреть файл

@ -7,6 +7,7 @@
#include "pkt-line.h"
#include "sideband.h"
#include "run-command.h"
#include "argv-array.h"
static const char upload_archive_usage[] =
"git upload-archive <repo>";
@ -18,51 +19,31 @@ static const char deadchild[] =
int cmd_upload_archive_writer(int argc, const char **argv, const char *prefix)
{
const char *sent_argv[MAX_ARGS];
struct argv_array sent_argv = ARGV_ARRAY_INIT;
const char *arg_cmd = "argument ";
char *p, buf[4096];
int sent_argc;
int len;
if (argc != 2)
usage(upload_archive_usage);
if (strlen(argv[1]) + 1 > sizeof(buf))
die("insanely long repository name");
strcpy(buf, argv[1]); /* enter-repo smudges its argument */
if (!enter_repo(buf, 0))
die("'%s' does not appear to be a git repository", buf);
if (!enter_repo(argv[1], 0))
die("'%s' does not appear to be a git repository", argv[1]);
/* put received options in sent_argv[] */
sent_argc = 1;
sent_argv[0] = "git-upload-archive";
for (p = buf;;) {
/* This will die if not enough free space in buf */
len = packet_read_line(0, p, (buf + sizeof buf) - p);
if (len == 0)
argv_array_push(&sent_argv, "git-upload-archive");
for (;;) {
char *buf = packet_read_line(0, NULL);
if (!buf)
break; /* got a flush */
if (sent_argc > MAX_ARGS - 2)
die("Too many options (>%d)", MAX_ARGS - 2);
if (sent_argv.argc > MAX_ARGS)
die("Too many options (>%d)", MAX_ARGS - 1);
if (p[len-1] == '\n') {
p[--len] = 0;
}
if (len < strlen(arg_cmd) ||
strncmp(arg_cmd, p, strlen(arg_cmd)))
if (prefixcmp(buf, arg_cmd))
die("'argument' token or flush expected");
len -= strlen(arg_cmd);
memmove(p, p + strlen(arg_cmd), len);
sent_argv[sent_argc++] = p;
p += len;
*p++ = 0;
argv_array_push(&sent_argv, buf + strlen(arg_cmd));
}
sent_argv[sent_argc] = NULL;
/* parse all options sent by the client */
return write_archive(sent_argc, sent_argv, prefix, 0, NULL, 1);
return write_archive(sent_argv.argc, sent_argv.argv, prefix, 0, NULL, 1);
}
__attribute__((format (printf, 1, 2)))

Просмотреть файл

@ -1064,7 +1064,9 @@ struct extra_have_objects {
int nr, alloc;
unsigned char (*array)[20];
};
extern struct ref **get_remote_heads(int in, struct ref **list, unsigned int flags, struct extra_have_objects *);
extern struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
struct ref **list, unsigned int flags,
struct extra_have_objects *);
extern int server_supports(const char *feature);
extern int parse_feature_request(const char *features, const char *feature);
extern const char *server_feature_value(const char *feature, int *len_ret);

Просмотреть файл

@ -62,8 +62,8 @@ static void die_initial_contact(int got_at_least_one_head)
/*
* Read all the refs from the other end
*/
struct ref **get_remote_heads(int in, struct ref **list,
unsigned int flags,
struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
struct ref **list, unsigned int flags,
struct extra_have_objects *extra_have)
{
int got_at_least_one_head = 0;
@ -72,18 +72,19 @@ struct ref **get_remote_heads(int in, struct ref **list,
for (;;) {
struct ref *ref;
unsigned char old_sha1[20];
static char buffer[1000];
char *name;
int len, name_len;
char *buffer = packet_buffer;
len = packet_read(in, buffer, sizeof(buffer));
len = packet_read(in, &src_buf, &src_len,
packet_buffer, sizeof(packet_buffer),
PACKET_READ_GENTLE_ON_EOF |
PACKET_READ_CHOMP_NEWLINE);
if (len < 0)
die_initial_contact(got_at_least_one_head);
if (!len)
break;
if (buffer[len-1] == '\n')
buffer[--len] = 0;
if (len > 4 && !prefixcmp(buffer, "ERR "))
die("remote error: %s", buffer + 4);

Просмотреть файл

@ -600,7 +600,7 @@ static void parse_host_arg(char *extra_args, int buflen)
static int execute(void)
{
static char line[1000];
char *line = packet_buffer;
int pktlen, len, i;
char *addr = getenv("REMOTE_ADDR"), *port = getenv("REMOTE_PORT");
@ -608,7 +608,7 @@ static int execute(void)
loginfo("Connection from %s:%s", addr, port);
alarm(init_timeout ? init_timeout : timeout);
pktlen = packet_read_line(0, line, sizeof(line));
pktlen = packet_read(0, NULL, NULL, packet_buffer, sizeof(packet_buffer), 0);
alarm(0);
len = strlen(line);

Просмотреть файл

@ -172,8 +172,8 @@ static void consume_shallow_list(struct fetch_pack_args *args, int fd)
* shallow and unshallow commands every time there
* is a block of have lines exchanged.
*/
char line[1000];
while (packet_read_line(fd, line, sizeof(line))) {
char *line;
while ((line = packet_read_line(fd, NULL))) {
if (!prefixcmp(line, "shallow "))
continue;
if (!prefixcmp(line, "unshallow "))
@ -215,17 +215,17 @@ static int write_shallow_commits(struct strbuf *out, int use_pack_protocol)
static enum ack_type get_ack(int fd, unsigned char *result_sha1)
{
static char line[1000];
int len = packet_read_line(fd, line, sizeof(line));
int len;
char *line = packet_read_line(fd, &len);
if (!len)
die("git fetch-pack: expected ACK/NAK, got EOF");
if (line[len-1] == '\n')
line[--len] = 0;
if (!strcmp(line, "NAK"))
return NAK;
if (!prefixcmp(line, "ACK ")) {
if (!get_sha1_hex(line+4, result_sha1)) {
if (len < 45)
return ACK;
if (strstr(line+45, "continue"))
return ACK_continue;
if (strstr(line+45, "common"))
@ -245,7 +245,7 @@ static void send_request(struct fetch_pack_args *args,
send_sideband(fd, -1, buf->buf, buf->len, LARGE_PACKET_MAX);
packet_flush(fd);
} else
safe_write(fd, buf->buf, buf->len);
write_or_die(fd, buf->buf, buf->len);
}
static void insert_one_alternate_ref(const struct ref *ref, void *unused)
@ -346,11 +346,11 @@ static int find_common(struct fetch_pack_args *args,
state_len = req_buf.len;
if (args->depth > 0) {
char line[1024];
char *line;
unsigned char sha1[20];
send_request(args, fd[1], &req_buf);
while (packet_read_line(fd[0], line, sizeof(line))) {
while ((line = packet_read_line(fd[0], NULL))) {
if (!prefixcmp(line, "shallow ")) {
if (get_sha1_hex(line + 8, sha1))
die("invalid shallow line: %s", line);

Просмотреть файл

@ -70,7 +70,7 @@ static void format_write(int fd, const char *fmt, ...)
if (n >= sizeof(buffer))
die("protocol error: impossibly long line");
safe_write(fd, buffer, n);
write_or_die(fd, buffer, n);
}
static void http_status(unsigned code, const char *msg)
@ -111,7 +111,7 @@ static void hdr_cache_forever(void)
static void end_headers(void)
{
safe_write(1, "\r\n", 2);
write_or_die(1, "\r\n", 2);
}
__attribute__((format (printf, 1, 2)))
@ -157,7 +157,7 @@ static void send_strbuf(const char *type, struct strbuf *buf)
hdr_int(content_length, buf->len);
hdr_str(content_type, type);
end_headers();
safe_write(1, buf->buf, buf->len);
write_or_die(1, buf->buf, buf->len);
}
static void send_local_file(const char *the_type, const char *name)
@ -185,7 +185,7 @@ static void send_local_file(const char *the_type, const char *name)
die_errno("Cannot read '%s'", p);
if (!n)
break;
safe_write(1, buf, n);
write_or_die(1, buf, n);
}
close(fd);
free(buf);

1
http.c
Просмотреть файл

@ -5,6 +5,7 @@
#include "url.h"
#include "credential.h"
#include "version.h"
#include "pkt-line.h"
int active_requests;
int http_is_verbose;

Просмотреть файл

@ -1,6 +1,7 @@
#include "cache.h"
#include "pkt-line.h"
char packet_buffer[LARGE_PACKET_MAX];
static const char *packet_trace_prefix = "git";
static const char trace_key[] = "GIT_TRACE_PACKET";
@ -46,38 +47,6 @@ static void packet_trace(const char *buf, unsigned int len, int write)
strbuf_release(&out);
}
/*
* Write a packetized stream, where each line is preceded by
* its length (including the header) as a 4-byte hex number.
* A length of 'zero' means end of stream (and a length of 1-3
* would be an error).
*
* This is all pretty stupid, but we use this packetized line
* format to make a streaming format possible without ever
* over-running the read buffers. That way we'll never read
* into what might be the pack data (which should go to another
* process entirely).
*
* The writing side could use stdio, but since the reading
* side can't, we stay with pure read/write interfaces.
*/
ssize_t safe_write(int fd, const void *buf, ssize_t n)
{
ssize_t nn = n;
while (n) {
int ret = xwrite(fd, buf, n);
if (ret > 0) {
buf = (char *) buf + ret;
n -= ret;
continue;
}
if (!ret)
die("write error (disk full?)");
die_errno("write error");
}
return nn;
}
/*
* If we buffered things up above (we don't, but we should),
* we'd flush it here
@ -85,7 +54,7 @@ ssize_t safe_write(int fd, const void *buf, ssize_t n)
void packet_flush(int fd)
{
packet_trace("0000", 4, 1);
safe_write(fd, "0000", 4);
write_or_die(fd, "0000", 4);
}
void packet_buf_flush(struct strbuf *buf)
@ -121,7 +90,7 @@ void packet_write(int fd, const char *fmt, ...)
va_start(args, fmt);
n = format_packet(fmt, args);
va_end(args);
safe_write(fd, buffer, n);
write_or_die(fd, buffer, n);
}
void packet_buf_write(struct strbuf *buf, const char *fmt, ...)
@ -135,13 +104,29 @@ void packet_buf_write(struct strbuf *buf, const char *fmt, ...)
strbuf_add(buf, buffer, n);
}
static int safe_read(int fd, void *buffer, unsigned size, int return_line_fail)
static int get_packet_data(int fd, char **src_buf, size_t *src_size,
void *dst, unsigned size, int options)
{
ssize_t ret = read_in_full(fd, buffer, size);
if (ret < 0)
die_errno("read error");
else if (ret < size) {
if (return_line_fail)
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");
@ -175,13 +160,14 @@ static int packet_length(const char *linelen)
return len;
}
static int packet_read_internal(int fd, char *buffer, unsigned size, int return_line_fail)
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 = safe_read(fd, linelen, 4, return_line_fail);
if (return_line_fail && ret < 0)
ret = get_packet_data(fd, src_buf, src_len, linelen, 4, options);
if (ret < 0)
return ret;
len = packet_length(linelen);
if (len < 0)
@ -193,50 +179,37 @@ static int packet_read_internal(int fd, char *buffer, unsigned size, int return_
len -= 4;
if (len >= size)
die("protocol error: bad line length %d", len);
ret = safe_read(fd, buffer, len, return_line_fail);
if (return_line_fail && ret < 0)
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;
}
int packet_read(int fd, char *buffer, unsigned size)
static char *packet_read_line_generic(int fd,
char **src, size_t *src_len,
int *dst_len)
{
return packet_read_internal(fd, buffer, size, 1);
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;
}
int packet_read_line(int fd, char *buffer, unsigned size)
char *packet_read_line(int fd, int *len_p)
{
return packet_read_internal(fd, buffer, size, 0);
return packet_read_line_generic(fd, NULL, NULL, len_p);
}
int packet_get_line(struct strbuf *out,
char **src_buf, size_t *src_len)
char *packet_read_line_buf(char **src, size_t *src_len, int *dst_len)
{
int len;
if (*src_len < 4)
return -1;
len = packet_length(*src_buf);
if (len < 0)
return -1;
if (!len) {
*src_buf += 4;
*src_len -= 4;
packet_trace("0000", 4, 0);
return 0;
}
if (*src_len < len)
return -2;
*src_buf += 4;
*src_len -= 4;
len -= 4;
strbuf_add(out, *src_buf, len);
*src_buf += len;
*src_len -= len;
packet_trace(out->buf, out->len, 0);
return len;
return packet_read_line_generic(-1, src, src_len, dst_len);
}

Просмотреть файл

@ -5,16 +5,78 @@
#include "strbuf.h"
/*
* Silly packetized line writing interface
* Write a packetized stream, where each line is preceded by
* its length (including the header) as a 4-byte hex number.
* A length of 'zero' means end of stream (and a length of 1-3
* would be an error).
*
* This is all pretty stupid, but we use this packetized line
* format to make a streaming format possible without ever
* over-running the read buffers. That way we'll never read
* into what might be the pack data (which should go to another
* process entirely).
*
* The writing side could use stdio, but since the reading
* side can't, we stay with pure read/write interfaces.
*/
void packet_flush(int fd);
void packet_write(int fd, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
void packet_buf_flush(struct strbuf *buf);
void packet_buf_write(struct strbuf *buf, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
int packet_read_line(int fd, char *buffer, unsigned size);
int packet_read(int fd, char *buffer, unsigned size);
int packet_get_line(struct strbuf *out, char **src_buf, size_t *src_len);
ssize_t safe_write(int, const void *, ssize_t);
/*
* Read a packetized line into the buffer, which must be at least size bytes
* long. The return value specifies the number of bytes read into the buffer.
*
* If src_buffer is not NULL (and nor is *src_buffer), it should point to a
* buffer containing the packet data to parse, of at least *src_len bytes.
* After the function returns, src_buf will be incremented and src_len
* decremented by the number of bytes consumed.
*
* If src_buffer (or *src_buffer) is NULL, then data is read from the
* descriptor "fd".
*
* If options does not contain PACKET_READ_GENTLE_ON_EOF, we will die under any
* of the following conditions:
*
* 1. Read error from descriptor.
*
* 2. Protocol error from the remote (e.g., bogus length characters).
*
* 3. Receiving a packet larger than "size" bytes.
*
* 4. Truncated output from the remote (e.g., we expected a packet but got
* EOF, or we got a partial packet followed by EOF).
*
* If options does contain PACKET_READ_GENTLE_ON_EOF, we will not die on
* condition 4 (truncated input), but instead return -1. However, we will still
* die for the other 3 conditions.
*
* If options contains PACKET_READ_CHOMP_NEWLINE, a trailing newline (if
* present) is removed from the buffer before returning.
*/
#define PACKET_READ_GENTLE_ON_EOF (1u<<0)
#define PACKET_READ_CHOMP_NEWLINE (1u<<1)
int packet_read(int fd, char **src_buffer, size_t *src_len, char
*buffer, unsigned size, int options);
/*
* Convenience wrapper for packet_read that is not gentle, and sets the
* CHOMP_NEWLINE option. The return value is NULL for a flush packet,
* and otherwise points to a static buffer (that may be overwritten by
* subsequent calls). If the size parameter is not NULL, the length of the
* packet is written to it.
*/
char *packet_read_line(int fd, int *size);
/*
* Same as packet_read_line, but read from a buf rather than a descriptor;
* see packet_read for details on how src_* is used.
*/
char *packet_read_line_buf(char **src_buf, size_t *src_len, int *size);
#define DEFAULT_PACKET_MAX 1000
#define LARGE_PACKET_MAX 65520
extern char packet_buffer[LARGE_PACKET_MAX];
#endif

Просмотреть файл

@ -76,129 +76,16 @@ struct discovery {
char *buf_alloc;
char *buf;
size_t len;
struct ref *refs;
unsigned proto_git : 1;
};
static struct discovery *last_discovery;
static void free_discovery(struct discovery *d)
{
if (d) {
if (d == last_discovery)
last_discovery = NULL;
free(d->buf_alloc);
free(d);
}
}
static struct discovery* discover_refs(const char *service)
{
struct strbuf exp = STRBUF_INIT;
struct strbuf type = STRBUF_INIT;
struct strbuf buffer = STRBUF_INIT;
struct discovery *last = last_discovery;
char *refs_url;
int http_ret, maybe_smart = 0;
if (last && !strcmp(service, last->service))
return last;
free_discovery(last);
strbuf_addf(&buffer, "%sinfo/refs", url);
if ((!prefixcmp(url, "http://") || !prefixcmp(url, "https://")) &&
git_env_bool("GIT_SMART_HTTP", 1)) {
maybe_smart = 1;
if (!strchr(url, '?'))
strbuf_addch(&buffer, '?');
else
strbuf_addch(&buffer, '&');
strbuf_addf(&buffer, "service=%s", service);
}
refs_url = strbuf_detach(&buffer, NULL);
http_ret = http_get_strbuf(refs_url, &type, &buffer, HTTP_NO_CACHE);
switch (http_ret) {
case HTTP_OK:
break;
case HTTP_MISSING_TARGET:
die("%s not found: did you run git update-server-info on the"
" server?", refs_url);
case HTTP_NOAUTH:
die("Authentication failed");
default:
http_error(refs_url, http_ret);
die("HTTP request failed");
}
last= xcalloc(1, sizeof(*last_discovery));
last->service = service;
last->buf_alloc = strbuf_detach(&buffer, &last->len);
last->buf = last->buf_alloc;
strbuf_addf(&exp, "application/x-%s-advertisement", service);
if (maybe_smart &&
(5 <= last->len && last->buf[4] == '#') &&
!strbuf_cmp(&exp, &type)) {
/*
* smart HTTP response; validate that the service
* pkt-line matches our request.
*/
if (packet_get_line(&buffer, &last->buf, &last->len) <= 0)
die("%s has invalid packet header", refs_url);
if (buffer.len && buffer.buf[buffer.len - 1] == '\n')
strbuf_setlen(&buffer, buffer.len - 1);
strbuf_reset(&exp);
strbuf_addf(&exp, "# service=%s", service);
if (strbuf_cmp(&exp, &buffer))
die("invalid server response; got '%s'", buffer.buf);
strbuf_release(&exp);
/* The header can include additional metadata lines, up
* until a packet flush marker. Ignore these now, but
* in the future we might start to scan them.
*/
strbuf_reset(&buffer);
while (packet_get_line(&buffer, &last->buf, &last->len) > 0)
strbuf_reset(&buffer);
last->proto_git = 1;
}
free(refs_url);
strbuf_release(&exp);
strbuf_release(&type);
strbuf_release(&buffer);
last_discovery = last;
return last;
}
static int write_discovery(int in, int out, void *data)
{
struct discovery *heads = data;
int err = 0;
if (write_in_full(out, heads->buf, heads->len) != heads->len)
err = 1;
close(out);
return err;
}
static struct ref *parse_git_refs(struct discovery *heads, int for_push)
{
struct ref *list = NULL;
struct async async;
memset(&async, 0, sizeof(async));
async.proc = write_discovery;
async.data = heads;
async.out = -1;
if (start_async(&async))
die("cannot start thread to parse advertised refs");
get_remote_heads(async.out, &list,
for_push ? REF_NORMAL : 0, NULL);
close(async.out);
if (finish_async(&async))
die("ref parsing thread failed");
get_remote_heads(-1, heads->buf, heads->len, &list,
for_push ? REF_NORMAL : 0, NULL);
return list;
}
@ -253,18 +140,112 @@ static struct ref *parse_info_refs(struct discovery *heads)
return refs;
}
static void free_discovery(struct discovery *d)
{
if (d) {
if (d == last_discovery)
last_discovery = NULL;
free(d->buf_alloc);
free_refs(d->refs);
free(d);
}
}
static struct discovery* discover_refs(const char *service, int for_push)
{
struct strbuf exp = STRBUF_INIT;
struct strbuf type = STRBUF_INIT;
struct strbuf buffer = STRBUF_INIT;
struct discovery *last = last_discovery;
char *refs_url;
int http_ret, maybe_smart = 0;
if (last && !strcmp(service, last->service))
return last;
free_discovery(last);
strbuf_addf(&buffer, "%sinfo/refs", url);
if ((!prefixcmp(url, "http://") || !prefixcmp(url, "https://")) &&
git_env_bool("GIT_SMART_HTTP", 1)) {
maybe_smart = 1;
if (!strchr(url, '?'))
strbuf_addch(&buffer, '?');
else
strbuf_addch(&buffer, '&');
strbuf_addf(&buffer, "service=%s", service);
}
refs_url = strbuf_detach(&buffer, NULL);
http_ret = http_get_strbuf(refs_url, &type, &buffer, HTTP_NO_CACHE);
switch (http_ret) {
case HTTP_OK:
break;
case HTTP_MISSING_TARGET:
die("%s not found: did you run git update-server-info on the"
" server?", refs_url);
case HTTP_NOAUTH:
die("Authentication failed");
default:
http_error(refs_url, http_ret);
die("HTTP request failed");
}
last= xcalloc(1, sizeof(*last_discovery));
last->service = service;
last->buf_alloc = strbuf_detach(&buffer, &last->len);
last->buf = last->buf_alloc;
strbuf_addf(&exp, "application/x-%s-advertisement", service);
if (maybe_smart &&
(5 <= last->len && last->buf[4] == '#') &&
!strbuf_cmp(&exp, &type)) {
char *line;
/*
* smart HTTP response; validate that the service
* pkt-line matches our request.
*/
line = packet_read_line_buf(&last->buf, &last->len, NULL);
strbuf_reset(&exp);
strbuf_addf(&exp, "# service=%s", service);
if (strcmp(line, exp.buf))
die("invalid server response; got '%s'", line);
strbuf_release(&exp);
/* The header can include additional metadata lines, up
* until a packet flush marker. Ignore these now, but
* in the future we might start to scan them.
*/
while (packet_read_line_buf(&last->buf, &last->len, NULL))
;
last->proto_git = 1;
}
if (last->proto_git)
last->refs = parse_git_refs(last, for_push);
else
last->refs = parse_info_refs(last);
free(refs_url);
strbuf_release(&exp);
strbuf_release(&type);
strbuf_release(&buffer);
last_discovery = last;
return last;
}
static struct ref *get_refs(int for_push)
{
struct discovery *heads;
if (for_push)
heads = discover_refs("git-receive-pack");
heads = discover_refs("git-receive-pack", for_push);
else
heads = discover_refs("git-upload-pack");
heads = discover_refs("git-upload-pack", for_push);
if (heads->proto_git)
return parse_git_refs(heads, for_push);
return parse_info_refs(heads);
return heads->refs;
}
static void output_refs(struct ref *refs)
@ -278,7 +259,6 @@ static void output_refs(struct ref *refs)
}
printf("\n");
fflush(stdout);
free_refs(refs);
}
struct rpc_state {
@ -308,7 +288,7 @@ static size_t rpc_out(void *ptr, size_t eltsize,
if (!avail) {
rpc->initial_buffer = 0;
avail = packet_read_line(rpc->out, rpc->buf, rpc->alloc);
avail = packet_read(rpc->out, NULL, NULL, rpc->buf, rpc->alloc, 0);
if (!avail)
return 0;
rpc->pos = 0;
@ -425,7 +405,7 @@ static int post_rpc(struct rpc_state *rpc)
break;
}
n = packet_read_line(rpc->out, buf, left);
n = packet_read(rpc->out, NULL, NULL, buf, left, 0);
if (!n)
break;
rpc->len += n;
@ -579,7 +559,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads)
rpc->hdr_accept = strbuf_detach(&buf, NULL);
while (!err) {
int n = packet_read_line(rpc->out, rpc->buf, rpc->alloc);
int n = packet_read(rpc->out, NULL, NULL, rpc->buf, rpc->alloc, 0);
if (!n)
break;
rpc->pos = 0;
@ -685,7 +665,7 @@ static int fetch_git(struct discovery *heads,
err = rpc_service(&rpc, heads);
if (rpc.result.len)
safe_write(1, rpc.result.buf, rpc.result.len);
write_or_die(1, rpc.result.buf, rpc.result.len);
strbuf_release(&rpc.result);
strbuf_release(&preamble);
free(depth_arg);
@ -694,7 +674,7 @@ static int fetch_git(struct discovery *heads,
static int fetch(int nr_heads, struct ref **to_fetch)
{
struct discovery *d = discover_refs("git-upload-pack");
struct discovery *d = discover_refs("git-upload-pack", 0);
if (d->proto_git)
return fetch_git(d, nr_heads, to_fetch);
else
@ -805,7 +785,7 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs)
err = rpc_service(&rpc, heads);
if (rpc.result.len)
safe_write(1, rpc.result.buf, rpc.result.len);
write_or_die(1, rpc.result.buf, rpc.result.len);
strbuf_release(&rpc.result);
free(argv);
return err;
@ -813,7 +793,7 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs)
static int push(int nr_spec, char **specs)
{
struct discovery *heads = discover_refs("git-receive-pack");
struct discovery *heads = discover_refs("git-receive-pack", 1);
int ret;
if (heads->proto_git)

Просмотреть файл

@ -106,15 +106,11 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext
static int receive_status(int in, struct ref *refs)
{
struct ref *hint;
char line[1000];
int ret = 0;
int len = packet_read_line(in, line, sizeof(line));
if (len < 10 || memcmp(line, "unpack ", 7))
char *line = packet_read_line(in, NULL);
if (prefixcmp(line, "unpack "))
return error("did not receive remote status");
if (memcmp(line, "unpack ok\n", 10)) {
char *p = line + strlen(line) - 1;
if (*p == '\n')
*p = '\0';
if (strcmp(line, "unpack ok")) {
error("unpack failed: %s", line + 7);
ret = -1;
}
@ -122,17 +118,15 @@ static int receive_status(int in, struct ref *refs)
while (1) {
char *refname;
char *msg;
len = packet_read_line(in, line, sizeof(line));
if (!len)
line = packet_read_line(in, NULL);
if (!line)
break;
if (len < 3 ||
(memcmp(line, "ok ", 3) && memcmp(line, "ng ", 3))) {
fprintf(stderr, "protocol error: %s\n", line);
if (prefixcmp(line, "ok ") && prefixcmp(line, "ng ")) {
error("invalid ref status from remote: %s", line);
ret = -1;
break;
}
line[strlen(line)-1] = '\0';
refname = line + 3;
msg = strchr(refname, ' ');
if (msg)
@ -281,7 +275,7 @@ int send_pack(struct send_pack_args *args,
send_sideband(out, -1, req_buf.buf, req_buf.len, LARGE_PACKET_MAX);
}
} else {
safe_write(out, req_buf.buf, req_buf.len);
write_or_die(out, req_buf.buf, req_buf.len);
packet_flush(out);
}
strbuf_release(&req_buf);

Просмотреть файл

@ -1,3 +1,4 @@
#include "cache.h"
#include "pkt-line.h"
#include "sideband.h"
@ -37,7 +38,7 @@ int recv_sideband(const char *me, int in_stream, int out)
while (1) {
int band, len;
len = packet_read_line(in_stream, buf + pf, LARGE_PACKET_MAX);
len = packet_read(in_stream, NULL, NULL, buf + pf, LARGE_PACKET_MAX, 0);
if (len == 0)
break;
if (len < 1) {
@ -108,7 +109,7 @@ int recv_sideband(const char *me, int in_stream, int out)
} while (len);
continue;
case 1:
safe_write(out, buf + pf+1, len);
write_or_die(out, buf + pf+1, len);
continue;
default:
fprintf(stderr, "%s: protocol error: bad band #%d\n",
@ -138,12 +139,12 @@ ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet
if (0 <= band) {
sprintf(hdr, "%04x", n + 5);
hdr[4] = band;
safe_write(fd, hdr, 5);
write_or_die(fd, hdr, 5);
} else {
sprintf(hdr, "%04x", n + 4);
safe_write(fd, hdr, 4);
write_or_die(fd, hdr, 4);
}
safe_write(fd, p, n);
write_or_die(fd, p, n);
p += n;
sz -= n;
}

Просмотреть файл

@ -4,9 +4,6 @@
#define SIDEBAND_PROTOCOL_ERROR -2
#define SIDEBAND_REMOTE_ERROR -1
#define DEFAULT_PACKET_MAX 1000
#define LARGE_PACKET_MAX 65520
int recv_sideband(const char *me, int in_stream, int out);
ssize_t send_sideband(int fd, int band, const char *data, ssize_t sz, int packet_max);

Просмотреть файл

@ -4,10 +4,6 @@ test_description='test automatic tag following'
. ./test-lib.sh
if ! test_have_prereq NOT_MINGW; then
say "GIT_DEBUG_SEND_PACK not supported - skipping tests"
fi
# End state of the repository:
#
# T - tag1 S - tag2
@ -17,7 +13,7 @@ fi
# \ C - origin/cat \
# origin/master master
test_expect_success NOT_MINGW setup '
test_expect_success setup '
test_tick &&
echo ichi >file &&
git add file &&
@ -39,28 +35,35 @@ test_expect_success NOT_MINGW setup '
'
U=UPLOAD_LOG
UPATH="$(pwd)/$U"
test_expect_success NOT_MINGW 'setup expect' '
test_expect_success 'setup expect' '
cat - <<EOF >expect
#S
want $A
#E
EOF
'
test_expect_success NOT_MINGW 'fetch A (new commit : 1 connection)' '
get_needs () {
test -s "$1" &&
perl -alne '
next unless $F[1] eq "upload-pack<";
last if $F[2] eq "0000";
print $F[2], " ", $F[3];
' "$1"
}
test_expect_success 'fetch A (new commit : 1 connection)' '
rm -f $U &&
(
cd cloned &&
GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
GIT_TRACE_PACKET=$UPATH git fetch &&
test $A = $(git rev-parse --verify origin/master)
) &&
test -s $U &&
cut -d" " -f1,2 $U >actual &&
get_needs $U >actual &&
test_cmp expect actual
'
test_expect_success NOT_MINGW "create tag T on A, create C on branch cat" '
test_expect_success "create tag T on A, create C on branch cat" '
git tag -a -m tag1 tag1 $A &&
T=$(git rev-parse --verify tag1) &&
@ -72,30 +75,27 @@ test_expect_success NOT_MINGW "create tag T on A, create C on branch cat" '
git checkout master
'
test_expect_success NOT_MINGW 'setup expect' '
test_expect_success 'setup expect' '
cat - <<EOF >expect
#S
want $C
want $T
#E
EOF
'
test_expect_success NOT_MINGW 'fetch C, T (new branch, tag : 1 connection)' '
test_expect_success 'fetch C, T (new branch, tag : 1 connection)' '
rm -f $U &&
(
cd cloned &&
GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
GIT_TRACE_PACKET=$UPATH git fetch &&
test $C = $(git rev-parse --verify origin/cat) &&
test $T = $(git rev-parse --verify tag1) &&
test $A = $(git rev-parse --verify tag1^0)
) &&
test -s $U &&
cut -d" " -f1,2 $U >actual &&
get_needs $U >actual &&
test_cmp expect actual
'
test_expect_success NOT_MINGW "create commits O, B, tag S on B" '
test_expect_success "create commits O, B, tag S on B" '
test_tick &&
echo O >file &&
git add file &&
@ -111,39 +111,34 @@ test_expect_success NOT_MINGW "create commits O, B, tag S on B" '
S=$(git rev-parse --verify tag2)
'
test_expect_success NOT_MINGW 'setup expect' '
test_expect_success 'setup expect' '
cat - <<EOF >expect
#S
want $B
want $S
#E
EOF
'
test_expect_success NOT_MINGW 'fetch B, S (commit and tag : 1 connection)' '
test_expect_success 'fetch B, S (commit and tag : 1 connection)' '
rm -f $U &&
(
cd cloned &&
GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
GIT_TRACE_PACKET=$UPATH git fetch &&
test $B = $(git rev-parse --verify origin/master) &&
test $B = $(git rev-parse --verify tag2^0) &&
test $S = $(git rev-parse --verify tag2)
) &&
test -s $U &&
cut -d" " -f1,2 $U >actual &&
get_needs $U >actual &&
test_cmp expect actual
'
test_expect_success NOT_MINGW 'setup expect' '
test_expect_success 'setup expect' '
cat - <<EOF >expect
#S
want $B
want $S
#E
EOF
'
test_expect_success NOT_MINGW 'new clone fetch master and tags' '
test_expect_success 'new clone fetch master and tags' '
git branch -D cat
rm -f $U
(
@ -151,15 +146,14 @@ test_expect_success NOT_MINGW 'new clone fetch master and tags' '
cd clone2 &&
git init &&
git remote add origin .. &&
GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
GIT_TRACE_PACKET=$UPATH git fetch &&
test $B = $(git rev-parse --verify origin/master) &&
test $S = $(git rev-parse --verify tag2) &&
test $B = $(git rev-parse --verify tag2^0) &&
test $T = $(git rev-parse --verify tag1) &&
test $A = $(git rev-parse --verify tag1^0)
) &&
test -s $U &&
cut -d" " -f1,2 $U >actual &&
get_needs $U >actual &&
test_cmp expect actual
'

Просмотреть файл

@ -54,11 +54,14 @@ cd "$base_dir"
rm -f "$U.D"
test_expect_success 'cloning with reference (no -l -s)' \
'GIT_DEBUG_SEND_PACK=3 git clone --reference B "file://$(pwd)/A" D 3>"$U.D"'
test_expect_success 'cloning with reference (no -l -s)' '
GIT_TRACE_PACKET=$U.D git clone --reference B "file://$(pwd)/A" D
'
test_expect_success 'fetched no objects' \
'! grep "^want" "$U.D"'
test_expect_success 'fetched no objects' '
test -s "$U.D" &&
! grep " want" "$U.D"
'
cd "$base_dir"
@ -173,12 +176,13 @@ test_expect_success 'fetch with incomplete alternates' '
(
cd K &&
git remote add J "file://$base_dir/J" &&
GIT_DEBUG_SEND_PACK=3 git fetch J 3>"$U.K"
GIT_TRACE_PACKET=$U.K git fetch J
) &&
master_object=$(cd A && git for-each-ref --format="%(objectname)" refs/heads/master) &&
! grep "^want $master_object" "$U.K" &&
test -s "$U.K" &&
! grep " want $master_object" "$U.K" &&
tag_object=$(cd A && git for-each-ref --format="%(objectname)" refs/tags/HEAD) &&
! grep "^want $tag_object" "$U.K"
! grep " want $tag_object" "$U.K"
'
test_done

Просмотреть файл

@ -508,7 +508,7 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus
struct ref *refs;
connect_setup(transport, for_push, 0);
get_remote_heads(data->fd[0], &refs,
get_remote_heads(data->fd[0], NULL, 0, &refs,
for_push ? REF_NORMAL : 0, &data->extra_have);
data->got_remote_heads = 1;
@ -537,7 +537,7 @@ static int fetch_refs_via_pack(struct transport *transport,
if (!data->got_remote_heads) {
connect_setup(transport, 0, 0);
get_remote_heads(data->fd[0], &refs_tmp, 0, NULL);
get_remote_heads(data->fd[0], NULL, 0, &refs_tmp, 0, NULL);
data->got_remote_heads = 1;
}
@ -795,7 +795,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
struct ref *tmp_refs;
connect_setup(transport, 1, 0);
get_remote_heads(data->fd[0], &tmp_refs, REF_NORMAL, NULL);
get_remote_heads(data->fd[0], NULL, 0, &tmp_refs, REF_NORMAL, NULL);
data->got_remote_heads = 1;
}

Просмотреть файл

@ -44,7 +44,6 @@ static unsigned int timeout;
* otherwise maximum packet size (up to 65520 bytes).
*/
static int use_sideband;
static int debug_fd;
static int advertise_refs;
static int stateless_rpc;
@ -53,13 +52,6 @@ static void reset_timeout(void)
alarm(timeout);
}
static int strip(char *line, int len)
{
if (len && line[len-1] == '\n')
line[--len] = 0;
return len;
}
static ssize_t send_client_data(int fd, const char *data, ssize_t sz)
{
if (use_sideband)
@ -72,7 +64,8 @@ static ssize_t send_client_data(int fd, const char *data, ssize_t sz)
xwrite(fd, data, sz);
return sz;
}
return safe_write(fd, data, sz);
write_or_die(fd, data, sz);
return sz;
}
static FILE *pack_pipe = NULL;
@ -415,7 +408,6 @@ static int ok_to_give_up(void)
static int get_common_commits(void)
{
static char line[1000];
unsigned char sha1[20];
char last_hex[41];
int got_common = 0;
@ -425,10 +417,10 @@ static int get_common_commits(void)
save_commit_buffer = 0;
for (;;) {
int len = packet_read_line(0, line, sizeof(line));
char *line = packet_read_line(0, NULL);
reset_timeout();
if (!len) {
if (!line) {
if (multi_ack == 2 && got_common
&& !got_other && ok_to_give_up()) {
sent_ready = 1;
@ -447,7 +439,6 @@ static int get_common_commits(void)
got_other = 0;
continue;
}
strip(line, len);
if (!prefixcmp(line, "have ")) {
switch (got_sha1(line+5, sha1)) {
case -1: /* they have what we do not */
@ -581,36 +572,33 @@ error:
static void receive_needs(void)
{
struct object_array shallows = OBJECT_ARRAY_INIT;
static char line[1000];
int len, depth = 0;
int depth = 0;
int has_non_tip = 0;
shallow_nr = 0;
if (debug_fd)
write_str_in_full(debug_fd, "#S\n");
for (;;) {
struct object *o;
const char *features;
unsigned char sha1_buf[20];
len = packet_read_line(0, line, sizeof(line));
char *line = packet_read_line(0, NULL);
reset_timeout();
if (!len)
if (!line)
break;
if (debug_fd)
write_in_full(debug_fd, line, len);
if (!prefixcmp(line, "shallow ")) {
unsigned char sha1[20];
struct object *object;
if (get_sha1(line + 8, sha1))
if (get_sha1_hex(line + 8, sha1))
die("invalid shallow line: %s", line);
object = parse_object(sha1);
if (!object)
die("did not find object for %s", line);
if (object->type != OBJ_COMMIT)
die("invalid shallow object %s", sha1_to_hex(sha1));
object->flags |= CLIENT_SHALLOW;
add_object_array(object, NULL, &shallows);
if (!(object->flags & CLIENT_SHALLOW)) {
object->flags |= CLIENT_SHALLOW;
add_object_array(object, NULL, &shallows);
}
continue;
}
if (!prefixcmp(line, "deepen ")) {
@ -657,8 +645,6 @@ static void receive_needs(void)
add_object_array(o, NULL, &want_obj);
}
}
if (debug_fd)
write_str_in_full(debug_fd, "#E\n");
/*
* We have sent all our refs already, and the other end
@ -854,8 +840,6 @@ int main(int argc, char **argv)
if (is_repository_shallow())
die("attempt to fetch/clone from a shallow repository");
git_config(upload_pack_config, NULL);
if (getenv("GIT_DEBUG_SEND_PACK"))
debug_fd = atoi(getenv("GIT_DEBUG_SEND_PACK"));
upload_pack();
return 0;
}

Просмотреть файл

@ -1,5 +1,15 @@
#include "cache.h"
static void check_pipe(int err)
{
if (err == EPIPE) {
signal(SIGPIPE, SIG_DFL);
raise(SIGPIPE);
/* Should never happen, but just in case... */
exit(141);
}
}
/*
* Some cases use stdio, but want to flush after the write
* to get error handling (and to get better interactive
@ -34,8 +44,7 @@ void maybe_flush_or_die(FILE *f, const char *desc)
return;
}
if (fflush(f)) {
if (errno == EPIPE)
exit(0);
check_pipe(errno);
die_errno("write failure on '%s'", desc);
}
}
@ -50,8 +59,7 @@ void fsync_or_die(int fd, const char *msg)
void write_or_die(int fd, const void *buf, size_t count)
{
if (write_in_full(fd, buf, count) < 0) {
if (errno == EPIPE)
exit(0);
check_pipe(errno);
die_errno("write error");
}
}
@ -59,8 +67,7 @@ void write_or_die(int fd, const void *buf, size_t count)
int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg)
{
if (write_in_full(fd, buf, count) < 0) {
if (errno == EPIPE)
exit(0);
check_pipe(errno);
fprintf(stderr, "%s: write error (%s)\n",
msg, strerror(errno));
return 0;