diff --git a/builtin/archive.c b/builtin/archive.c index 9a1cfd3dac0123cb65d3db7597221a29e9010bd9..49178f159e246c9a99805d806fa5f096337e8d2f 100644 --- a/builtin/archive.c +++ b/builtin/archive.c @@ -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 */ diff --git a/builtin/fetch-pack.c b/builtin/fetch-pack.c index 670e81fd995953bff9c29cc6fab99d03f1fee8b7..aba44655524ff722d90de09760945f5f30088752 100644 --- a/builtin/fetch-pack.c +++ b/builtin/fetch-pack.c @@ -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); diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index 62ba6e7a3d014be01fe01ed497c083f7e44e9d34..ccebd74f166f5d02e8e6a0bb6033d741f8580f7b 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -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); } diff --git a/builtin/send-pack.c b/builtin/send-pack.c index 57a46b2654aa82b154d32eb77addd6a4f89c8e8d..152c4ea092a44ff9e70e293ddeecb4ad7105e25d 100644 --- a/builtin/send-pack.c +++ b/builtin/send-pack.c @@ -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); diff --git a/builtin/upload-archive.c b/builtin/upload-archive.c index b928beb8ed51c9af9aa4f640e80443a5e059d505..af2da35e7d05f4de2d7a77a708ee3cf6f490266e 100644 --- a/builtin/upload-archive.c +++ b/builtin/upload-archive.c @@ -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 "; @@ -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))) diff --git a/cache.h b/cache.h index 59e5b5317910c2cd41a00d694f0de50b3d6f40f7..bcdb3edbc4c10a8d87c075f831a9177e60b13251 100644 --- a/cache.h +++ b/cache.h @@ -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); diff --git a/connect.c b/connect.c index 49e56ba35a645359b0d7c1f7bbc9e2108b3424d9..f57efd06c1d7ab01076b67d37ed24e34e17c4ebb 100644 --- a/connect.c +++ b/connect.c @@ -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); diff --git a/daemon.c b/daemon.c index df8c0ab0588e70ad6e6f56195535030055782d85..6aeddcb98d2694a705d34e80293fc81a20bc39a8 100644 --- a/daemon.c +++ b/daemon.c @@ -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); diff --git a/fetch-pack.c b/fetch-pack.c index cef8fde61bf4516f95ae13b05f73cb26a268f90c..f156dd4fac30cda4e09c508b7091cdb8d96917e2 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -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); diff --git a/http-backend.c b/http-backend.c index f50e77fb2890207bd7385b59bdd561133991c910..8144f3ad5eb68c789fd031921e6e702170310d68 100644 --- a/http-backend.c +++ b/http-backend.c @@ -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); diff --git a/http.c b/http.c index d9d1aad3be468394e39966c0b45e7e101b387b0c..8803c70825ac8aefb619b6c56d6015573eb5d227 100644 --- a/http.c +++ b/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; diff --git a/pkt-line.c b/pkt-line.c index eaba15f124b5d8247d39bd0844967cb5a9101ba8..70f19501d0e0c0dd625976519fd039d5c7a8b314 100644 --- a/pkt-line.c +++ b/pkt-line.c @@ -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); } diff --git a/pkt-line.h b/pkt-line.h index 8cfeb0c31c8661bf11ce78d3679a21cd030ffb75..0a838d1656171019c07fe802d615ecaf78412003 100644 --- a/pkt-line.h +++ b/pkt-line.h @@ -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 diff --git a/remote-curl.c b/remote-curl.c index 933c69ac2627c1903276b92b457843e12649f39e..93a09a64c3b1689b0f29a26c6cdd601ef298b975 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -76,21 +76,82 @@ struct discovery { char *buf_alloc; char *buf; size_t len; + struct ref *refs; unsigned proto_git : 1; }; static struct discovery *last_discovery; +static struct ref *parse_git_refs(struct discovery *heads, int for_push) +{ + struct ref *list = NULL; + get_remote_heads(-1, heads->buf, heads->len, &list, + for_push ? REF_NORMAL : 0, NULL); + return list; +} + +static struct ref *parse_info_refs(struct discovery *heads) +{ + char *data, *start, *mid; + char *ref_name; + int i = 0; + + struct ref *refs = NULL; + struct ref *ref = NULL; + struct ref *last_ref = NULL; + + data = heads->buf; + start = NULL; + mid = data; + while (i < heads->len) { + if (!start) { + start = &data[i]; + } + if (data[i] == '\t') + mid = &data[i]; + if (data[i] == '\n') { + if (mid - start != 40) + die("%sinfo/refs not valid: is this a git repository?", url); + data[i] = 0; + ref_name = mid + 1; + ref = xmalloc(sizeof(struct ref) + + strlen(ref_name) + 1); + memset(ref, 0, sizeof(struct ref)); + strcpy(ref->name, ref_name); + get_sha1_hex(start, ref->old_sha1); + if (!refs) + refs = ref; + if (last_ref) + last_ref->next = ref; + last_ref = ref; + start = NULL; + } + i++; + } + + ref = alloc_ref("HEAD"); + if (!http_fetch_ref(url, ref) && + !resolve_remote_symref(ref, refs)) { + ref->next = refs; + refs = ref; + } else { + free(ref); + } + + 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) +static struct discovery* discover_refs(const char *service, int for_push) { struct strbuf exp = STRBUF_INIT; struct strbuf type = STRBUF_INIT; @@ -138,32 +199,35 @@ static struct discovery* discover_refs(const char *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. */ - 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); + line = packet_read_line_buf(&last->buf, &last->len, NULL); strbuf_reset(&exp); strbuf_addf(&exp, "# service=%s", service); - if (strbuf_cmp(&exp, &buffer)) - die("invalid server response; got '%s'", buffer.buf); + 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. */ - strbuf_reset(&buffer); - while (packet_get_line(&buffer, &last->buf, &last->len) > 0) - strbuf_reset(&buffer); + 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); @@ -172,99 +236,16 @@ static struct discovery* discover_refs(const char *service) 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"); - return list; -} - -static struct ref *parse_info_refs(struct discovery *heads) -{ - char *data, *start, *mid; - char *ref_name; - int i = 0; - - struct ref *refs = NULL; - struct ref *ref = NULL; - struct ref *last_ref = NULL; - - data = heads->buf; - start = NULL; - mid = data; - while (i < heads->len) { - if (!start) { - start = &data[i]; - } - if (data[i] == '\t') - mid = &data[i]; - if (data[i] == '\n') { - if (mid - start != 40) - die("%sinfo/refs not valid: is this a git repository?", url); - data[i] = 0; - ref_name = mid + 1; - ref = xmalloc(sizeof(struct ref) + - strlen(ref_name) + 1); - memset(ref, 0, sizeof(struct ref)); - strcpy(ref->name, ref_name); - get_sha1_hex(start, ref->old_sha1); - if (!refs) - refs = ref; - if (last_ref) - last_ref->next = ref; - last_ref = ref; - start = NULL; - } - i++; - } - - ref = alloc_ref("HEAD"); - if (!http_fetch_ref(url, ref) && - !resolve_remote_symref(ref, refs)) { - ref->next = refs; - refs = ref; - } else { - free(ref); - } - - return refs; -} - 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) diff --git a/send-pack.c b/send-pack.c index 97ab336097bfb45c63aec739a6eef2df26cc4790..7d172ef37f7b185f9586ce9015a08481d0bd259c 100644 --- a/send-pack.c +++ b/send-pack.c @@ -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); diff --git a/sideband.c b/sideband.c index d5ffa1c8919a6db750606c78a1b44d8618fa35a5..d1125f5c528b680f915bb2fbf54231cc8c3b2ec0 100644 --- a/sideband.c +++ b/sideband.c @@ -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; } diff --git a/sideband.h b/sideband.h index d72db35d1e0dc109f75b292762013c11b86426aa..e46bed0b0158c0253bacb2a3db028770ad221666 100644 --- a/sideband.h +++ b/sideband.h @@ -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); diff --git a/t/t5503-tagfollow.sh b/t/t5503-tagfollow.sh index 60de2d6ede958e713aebe85d73ee65ddbc10201d..f30c03885cda050f0bc704428ffa8a66932ff3fd 100755 --- a/t/t5503-tagfollow.sh +++ b/t/t5503-tagfollow.sh @@ -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 - <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 - <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 - <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 - <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 ' diff --git a/t/t5700-clone-reference.sh b/t/t5700-clone-reference.sh index c47d450cc3731cb471aa8485178f517bb0d6cbf5..60f1552adef417b8d37ca4215be1e66627c37258 100755 --- a/t/t5700-clone-reference.sh +++ b/t/t5700-clone-reference.sh @@ -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 diff --git a/transport.c b/transport.c index eb9eb33e0f0f151156ca646bb998c538f2471d7f..ba5d8afb1b04ba9d331c721fd9f730184fff2f23 100644 --- a/transport.c +++ b/transport.c @@ -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; } diff --git a/upload-pack.c b/upload-pack.c index f5673ee4c2d65787040658c5e3ee06ecd129636d..bfa6279cc418278079bd95854adfe8d8301b6788 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -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 @@ static void check_non_tip(void) 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; } diff --git a/write_or_die.c b/write_or_die.c index 960f448cffd9ffd4e53763dfed3669bc374b8620..b50f99a9361926d2116c85e3e90a132fb9dab742 100644 --- a/write_or_die.c +++ b/write_or_die.c @@ -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;