static void show_ref(const char *path, const unsigned char *sha1) { if (ref_is_hidden(path)) return; if (sent_capabilities) { packet_write(1, "%s %s\n", sha1_to_hex(sha1), path); } else { struct strbuf cap = STRBUF_INIT; strbuf_addstr(&cap, "report-status delete-refs side-band-64k quiet"); if (advertise_atomic_push) strbuf_addstr(&cap, " atomic"); if (prefer_ofs_delta) strbuf_addstr(&cap, " ofs-delta"); if (push_cert_nonce) strbuf_addf(&cap, " push-cert=%s", push_cert_nonce); strbuf_addf(&cap, " agent=%s", git_user_agent_sanitized()); packet_write(1, "%s %s%c%s\n", sha1_to_hex(sha1), path, 0, cap.buf); strbuf_release(&cap); sent_capabilities = 1; } }
static int send_ref(const char *refname, const struct object_id *oid, int flag, void *cb_data) { static const char *capabilities = "multi_ack thin-pack side-band" " side-band-64k ofs-delta shallow no-progress" " include-tag multi_ack_detailed"; const char *refname_nons = strip_namespace(refname); struct object_id peeled; if (mark_our_ref(refname_nons, refname, oid)) return 0; if (capabilities) { struct strbuf symref_info = STRBUF_INIT; format_symref_info(&symref_info, cb_data); packet_write(1, "%s %s%c%s%s%s%s%s agent=%s\n", oid_to_hex(oid), refname_nons, 0, capabilities, (allow_unadvertised_object_request & ALLOW_TIP_SHA1) ? " allow-tip-sha1-in-want" : "", (allow_unadvertised_object_request & ALLOW_REACHABLE_SHA1) ? " allow-reachable-sha1-in-want" : "", stateless_rpc ? " no-done" : "", symref_info.buf, git_user_agent_sanitized()); strbuf_release(&symref_info); } else { packet_write(1, "%s %s\n", oid_to_hex(oid), refname_nons); } capabilities = NULL; if (!peel_ref(refname, peeled.hash)) packet_write(1, "%s %s^{}\n", oid_to_hex(&peeled), refname_nons); return 0; }
static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { static const char *capabilities = "multi_ack thin-pack side-band" " side-band-64k ofs-delta shallow no-progress" " include-tag multi_ack_detailed"; const char *refname_nons = strip_namespace(refname); unsigned char peeled[20]; if (mark_our_ref(refname, sha1, flag, NULL)) return 0; if (capabilities) { struct strbuf symref_info = STRBUF_INIT; format_symref_info(&symref_info, cb_data); packet_write(1, "%s %s%c%s%s%s%s agent=%s\n", sha1_to_hex(sha1), refname_nons, 0, capabilities, allow_tip_sha1_in_want ? " allow-tip-sha1-in-want" : "", stateless_rpc ? " no-done" : "", symref_info.buf, git_user_agent_sanitized()); strbuf_release(&symref_info); } else { packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname_nons); } capabilities = NULL; if (!peel_ref(refname, peeled)) packet_write(1, "%s %s^{}\n", sha1_to_hex(peeled), refname_nons); return 0; }
static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { static const char *capabilities = "multi_ack thin-pack side-band" " side-band-64k ofs-delta shallow no-progress" " include-tag multi_ack_detailed"; struct object *o = parse_object(sha1); if (!o) die("git upload-pack: cannot find object %s:", sha1_to_hex(sha1)); if (capabilities) packet_write(1, "%s %s%c%s%s\n", sha1_to_hex(sha1), refname, 0, capabilities, stateless_rpc ? " no-done" : ""); else packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname); capabilities = NULL; if (!(o->flags & OUR_REF)) { o->flags |= OUR_REF; nr_our_refs++; } if (o->type == OBJ_TAG) { o = deref_tag(o, refname, 0); if (o) packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname); } return 0; }
int cmd_upload_archive(int argc, const char **argv, const char *prefix) { const char *sent_argv[MAX_ARGS]; struct child_process cld = { sent_argv }; cld.out = cld.err = -1; cld.git_cmd = 1; if (argc != 2) usage(upload_archive_usage); if (!enter_repo(argv[1], 0)) die("'%s' does not appear to be a git repository", argv[1]); prepare_argv(sent_argv, argv); if (start_command(&cld)) { int err = errno; packet_write(1, "NACK fork failed on the remote side\n"); die("upload-archive: %s", strerror(err)); } /* parent - read from child, multiplex and send out to fd#1 */ packet_write(1, "ACK\n"); packet_flush(1); while (1) { struct pollfd pfd[2]; int status; pfd[0].fd = cld.out; pfd[0].events = POLLIN; pfd[1].fd = cld.err; pfd[1].events = POLLIN; if (poll(pfd, 2, -1) < 0) { if (errno != EINTR) { error("poll failed resuming: %s", strerror(errno)); sleep(1); } continue; } if (pfd[1].revents & POLLIN) /* Status stream ready */ if (process_input(pfd[1].fd, 2)) continue; if (pfd[0].revents & POLLIN) /* Data stream ready */ if (process_input(pfd[0].fd, 1)) continue; if (waitpid(cld.pid, &status, 0) < 0) error_clnt("%s", lostchild); else if (!WIFEXITED(status) || WEXITSTATUS(status) > 0) error_clnt("%s", deadchild); packet_flush(1); break; } return 0; }
static int show_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data) { if (!capabilities_to_send) packet_write(1, "%s %s\n", sha1_to_hex(sha1), path); else packet_write(1, "%s %s%c%s\n", sha1_to_hex(sha1), path, 0, capabilities_to_send); capabilities_to_send = NULL; return 0; }
static void show_ref(const char *path, const unsigned char *sha1) { if (sent_capabilities) packet_write(1, "%s %s\n", sha1_to_hex(sha1), path); else packet_write(1, "%s %s%c%s%s\n", sha1_to_hex(sha1), path, 0, " report-status delete-refs side-band-64k", prefer_ofs_delta ? " ofs-delta" : ""); sent_capabilities = 1; }
int cmd_upload_archive(int argc, const char **argv, const char *prefix) { struct child_process writer = { argv }; /* * Set up sideband subprocess. * * We (parent) monitor and read from child, sending its fd#1 and fd#2 * multiplexed out to our fd#1. If the child dies, we tell the other * end over channel #3. */ argv[0] = "upload-archive--writer"; writer.out = writer.err = -1; writer.git_cmd = 1; if (start_command(&writer)) { int err = errno; packet_write(1, "NACK unable to spawn subprocess\n"); die("upload-archive: %s", strerror(err)); } packet_write(1, "ACK\n"); packet_flush(1); while (1) { struct pollfd pfd[2]; pfd[0].fd = writer.out; pfd[0].events = POLLIN; pfd[1].fd = writer.err; pfd[1].events = POLLIN; if (poll(pfd, 2, -1) < 0) { if (errno != EINTR) { error("poll failed resuming: %s", strerror(errno)); sleep(1); } continue; } if (pfd[1].revents & POLLIN) /* Status stream ready */ if (process_input(pfd[1].fd, 2)) continue; if (pfd[0].revents & POLLIN) /* Data stream ready */ if (process_input(pfd[0].fd, 1)) continue; if (finish_command(&writer)) error_clnt("%s", deadchild); packet_flush(1); break; } return 0; }
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; struct transport *transport; struct remote *_remote; _remote = remote_get(remote); if (!_remote->url[0]) die(_("git archive: Remote with no URL")); transport = transport_get(_remote, _remote->url[0]); transport_connect(transport, "git-upload-archive", exec, fd); /* * Inject a fake --format field at the beginning of the * arguments, with the format inferred from our output * filename. This way explicit --format options can override * it. */ if (name_hint) { const char *format = archive_format_from_filename(name_hint); if (format) packet_write(fd[1], "argument --format=%s\n", format); } for (i = 1; i < argc; i++) packet_write(fd[1], "argument %s\n", argv[i]); packet_flush(fd[1]); len = packet_read_line(fd[0], buf, sizeof(buf)); if (!len) 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 ")) die(_("git archive: NACK %s"), buf + 5); die(_("git archive: protocol error")); } len = packet_read_line(fd[0], buf, sizeof(buf)); if (len) die(_("git archive: expected a flush")); /* Now, start reading from fd[0] and spit it out to stdout */ rv = recv_sideband("archive", fd[0], 1); rv |= transport_disconnect(transport); return !!rv; }
static void report(const char *unpack_status) { struct command *cmd; packet_write(1, "unpack %s\n", unpack_status ? unpack_status : "ok"); for (cmd = commands; cmd; cmd = cmd->next) { if (!cmd->error_string) packet_write(1, "ok %s\n", cmd->ref_name); else packet_write(1, "ng %s %s\n", cmd->ref_name, cmd->error_string); } packet_flush(1); }
static void show_ref(const char *path, const unsigned char *sha1) { if (ref_is_hidden(path)) return; if (sent_capabilities) packet_write(1, "%s %s\n", sha1_to_hex(sha1), path); else packet_write(1, "%s %s%c%s%s agent=%s\n", sha1_to_hex(sha1), path, 0, " report-status delete-refs side-band-64k quiet", prefer_ofs_delta ? " ofs-delta" : "", git_user_agent_sanitized()); sent_capabilities = 1; }
static int daemon_error(const char *dir, const char *msg) { if (!informative_errors) msg = "access denied or repository not exported"; packet_write(1, "ERR %s: %s", msg, dir); return -1; }
static void get_info_refs(char *arg) { const char *service_name = get_parameter("service"); struct strbuf buf = STRBUF_INIT; hdr_nocache(); if (service_name) { const char *argv[] = {NULL /* service name */, "--stateless-rpc", "--advertise-refs", ".", NULL}; struct rpc_service *svc = select_service(service_name); strbuf_addf(&buf, "application/x-git-%s-advertisement", svc->name); hdr_str(content_type, buf.buf); end_headers(); packet_write(1, "# service=git-%s\n", svc->name); packet_flush(1); argv[0] = svc->name; run_service(argv); } else { select_getanyfile(); for_each_namespaced_ref(show_text_ref, &buf); send_strbuf("text/plain", &buf); } strbuf_release(&buf); }
static int get_common_commits(void) { static char line[1000]; unsigned char sha1[20]; char last_hex[41]; save_commit_buffer = 0; for (;;) { int len = packet_read_line(0, line, sizeof(line)); reset_timeout(); if (!len) { if (have_obj.nr == 0 || multi_ack) packet_write(1, "NAK\n"); if (stateless_rpc) exit(0); continue; } strip(line, len); if (!prefixcmp(line, "have ")) { switch (got_sha1(line+5, sha1)) { case -1: /* they have what we do not */ if (multi_ack && ok_to_give_up()) { const char *hex = sha1_to_hex(sha1); if (multi_ack == 2) packet_write(1, "ACK %s ready\n", hex); else packet_write(1, "ACK %s continue\n", hex); } break; default: memcpy(last_hex, sha1_to_hex(sha1), 41); if (multi_ack == 2) packet_write(1, "ACK %s common\n", last_hex); else if (multi_ack) packet_write(1, "ACK %s continue\n", last_hex); else if (have_obj.nr == 1) packet_write(1, "ACK %s\n", last_hex); break; } continue; } if (!strcmp(line, "done")) { if (have_obj.nr > 0) { if (multi_ack) packet_write(1, "ACK %s\n", last_hex); return 0; } packet_write(1, "NAK\n"); return -1; } die("git upload-pack: expected SHA1 list, got '%s'", line); } }
int cmd_send_cache_hash (SESSION * session) { int ret; struct buf* buf = buf_new(); buf_append_data(buf, session->cache_hash, sizeof (session->cache_hash)); ret = packet_write (session, 0x0f, buf->ptr, buf->len); DSFYDEBUG ("packet_write() returned %d\n", ret); buf_free(buf); return ret; }
/* * Open a connection using Git's native protocol. * * The caller is responsible for freeing hostandport, but this function may * modify it (for example, to truncate it to remove the port part). */ static struct child_process *git_connect_git(int fd[2], char *hostandport, const char *path, const char *prog, enum protocol_version version, int flags) { struct child_process *conn; struct strbuf request = STRBUF_INIT; /* * Set up virtual host information based on where we will * connect, unless the user has overridden us in * the environment. */ char *target_host = getenv("GIT_OVERRIDE_VIRTUAL_HOST"); if (target_host) target_host = xstrdup(target_host); else target_host = xstrdup(hostandport); transport_check_allowed("git"); /* * These underlying connection commands die() if they * cannot connect. */ if (git_use_proxy(hostandport)) conn = git_proxy_connect(fd, hostandport); else conn = git_tcp_connect(fd, hostandport, flags); /* * Separate original protocol components prog and path * from extended host header with a NUL byte. * * Note: Do not add any other headers here! Doing so * will cause older git-daemon servers to crash. */ strbuf_addf(&request, "%s %s%chost=%s%c", prog, path, 0, target_host, 0); /* If using a new version put that stuff here after a second null byte */ if (version > 0) { strbuf_addch(&request, '\0'); strbuf_addf(&request, "version=%d%c", version, '\0'); } packet_write(fd[1], request.buf, request.len); free(target_host); strbuf_release(&request); return conn; }
/** Returns -1 if there was an error writing. * Otherwise the number of bytes written is returned */ int connection_write( struct connection * c, const struct packet * p ) { uint16_t buff_size = p->size + 4; unsigned char * buffer = malloc( buff_size ); init_packet( buffer, p->size + 4 ); packet_write( buffer, p ); int16_t nb_bytes = write( c->fd_out, buffer, buff_size ); if ( nb_bytes == -1 ) { #ifndef EMBEDDED perror("write"); #endif return -1; } free( buffer ); return nb_bytes; }
static void udp_write(struct dechunkiser_ctx *o, int id, uint8_t *data, int size) { int i = 0; while (i < size) { int stream, psize; udp_payload_header_parse(data + i, &psize, &stream); if (stream > o->ports) { fprintf(stderr, "Bad stream %d > %d\n", stream, o->ports); return; } packet_write(o->outfd, o->ip, o->port[stream], data + i + UDP_PAYLOAD_HEADER_SIZE, psize); i += UDP_PAYLOAD_HEADER_SIZE + psize; } }
int main(int argc, char *argv[]) { int fd, opt; char pktbuf[1500], *iface_name = NULL; ssize_t len; while((opt = getopt(argc, argv, "hi:")) != -1) { switch (opt) { case 'i': iface_name = optarg; break; case 'h': default: usage(); return EXIT_FAILURE; } } if (iface_name == NULL) { fprintf(stderr, "No interface set\n"); return EXIT_FAILURE; } fd = sock_open_raw(iface_name); if (fd < 0) { perror("sock_open_raw"); return EXIT_FAILURE; } len = packet_read(STDIN_FILENO, pktbuf, sizeof(pktbuf)); if (len < 0) { perror("packet_read"); return EXIT_FAILURE; } close(STDIN_FILENO); if (packet_write(fd, pktbuf, (size_t)len) != len) { perror("packet_write"); return EXIT_FAILURE; } return EXIT_SUCCESS; }
static error_t stream_write (struct pq *pq, void *source, char *data, size_t data_len, size_t *amount) { struct packet *packet = pq_tail (pq, PACKET_TYPE_DATA, source); if (packet_readable (packet) > 0 && data_len > PACKET_SIZE_LARGE && (! page_aligned (data - packet->buf_end) || ! packet_ensure_efficiently (packet, data_len))) /* Put a large page-aligned transfer in its own packet, if it's page-aligned `differently' than the end of the current packet, or if the current packet can't be extended in place. */ packet = pq_queue (pq, PACKET_TYPE_DATA, source); if (!packet) return ENOBUFS; else return packet_write (packet, data, data_len, amount); }
static void rtp_write(struct dechunkiser_ctx *ctx, int id, uint8_t *data, int size) { uint8_t* data_end = data + size; printf_log(ctx, 2, "Got chunk of size %i", size); while (data < data_end) { // NOTE: `stream` here is the index in the ports array. int stream, psize; rtp_payload_per_pkt_header_parse(data, &psize, &stream); data += RTP_PAYLOAD_PER_PKT_HEADER_SIZE; if (stream > ctx->ports_len) { printf_log(ctx, 1, "Received Chunk with bad stream %d > %d", stream, ctx->ports_len); return; } printf_log(ctx, 2, "sending packet of size %i from port id #%i to port %i", psize, stream, ctx->ports[stream]); packet_write(ctx->outfd, ctx->ip, ctx->ports[stream], data, psize); data += psize; } }
static int run_remote_archiver(int argc, const char **argv, const char *remote, const char *exec) { char buf[LARGE_PACKET_MAX]; int fd[2], i, len, rv; struct transport *transport; struct remote *_remote; _remote = remote_get(remote); if (!_remote->url[0]) die(_("git archive: Remote with no URL")); transport = transport_get(_remote, _remote->url[0]); transport_connect(transport, "git-upload-archive", exec, fd); for (i = 1; i < argc; i++) packet_write(fd[1], "argument %s\n", argv[i]); packet_flush(fd[1]); len = packet_read_line(fd[0], buf, sizeof(buf)); if (!len) 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 ")) die(_("git archive: NACK %s"), buf + 5); die(_("git archive: protocol error")); } len = packet_read_line(fd[0], buf, sizeof(buf)); if (len) die(_("git archive: expected a flush")); /* Now, start reading from fd[0] and spit it out to stdout */ rv = recv_sideband("archive", fd[0], 1); rv |= transport_disconnect(transport); return !!rv; }
static int run_remote_archiver(int argc, const char **argv, const char *remote, const char *exec) { char *url, buf[LARGE_PACKET_MAX]; int fd[2], i, len, rv; struct child_process *conn; url = xstrdup(remote); conn = git_connect(fd, url, exec, 0); for (i = 1; i < argc; i++) packet_write(fd[1], "argument %s\n", argv[i]); packet_flush(fd[1]); len = packet_read_line(fd[0], buf, sizeof(buf)); if (!len) 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 ")) die("git archive: NACK %s", buf + 5); die("git archive: protocol error"); } len = packet_read_line(fd[0], buf, sizeof(buf)); if (len) die("git archive: expected a flush"); /* Now, start reading from fd[0] and spit it out to stdout */ rv = recv_sideband("archive", fd[0], 1); close(fd[0]); close(fd[1]); rv |= finish_connect(conn); return !!rv; }
/* * Request ads * The response is plain XML * */ int cmd_requestad (SESSION * session, unsigned char ad_type) { CHANNEL *ch; int ret; char buf[100]; struct buf* b = buf_new(); _snprintf(buf, sizeof(buf), "RequestAd-with-type-%d", ad_type); ch = channel_register (buf, dump_generic, NULL); DSFYDEBUG ("allocated channel %d, retrieving ads with type id %d\n", ch->channel_id, ad_type); buf_append_u16(b, ch->channel_id); buf_append_u8(b, ad_type); ret = packet_write (session, CMD_REQUESTAD, b->ptr, b->len); DSFYDEBUG ("packet_write() returned %d\n", ret); buf_free(b); return ret; }
int send_pack(struct send_pack_args *args, int fd[], struct child_process *conn, struct ref *remote_refs, struct extra_have_objects *extra_have) { int in = fd[0]; int out = fd[1]; struct ref *ref; int new_refs; int ask_for_status_report = 0; int allow_deleting_refs = 0; int expect_status_report = 0; int ret; /* Does the other end support the reporting? */ if (server_supports("report-status")) ask_for_status_report = 1; if (server_supports("delete-refs")) allow_deleting_refs = 1; if (server_supports("ofs-delta")) args->use_ofs_delta = 1; if (!remote_refs) { fprintf(stderr, "No refs in common and none specified; doing nothing.\n" "Perhaps you should specify a branch such as 'master'.\n"); return 0; } /* * Finally, tell the other end! */ new_refs = 0; for (ref = remote_refs; ref; ref = ref->next) { if (ref->peer_ref) hashcpy(ref->new_sha1, ref->peer_ref->new_sha1); else if (!args->send_mirror) continue; ref->deletion = is_null_sha1(ref->new_sha1); if (ref->deletion && !allow_deleting_refs) { ref->status = REF_STATUS_REJECT_NODELETE; continue; } if (!ref->deletion && !hashcmp(ref->old_sha1, ref->new_sha1)) { ref->status = REF_STATUS_UPTODATE; continue; } /* This part determines what can overwrite what. * The rules are: * * (0) you can always use --force or +A:B notation to * selectively force individual ref pairs. * * (1) if the old thing does not exist, it is OK. * * (2) if you do not have the old thing, you are not allowed * to overwrite it; you would not know what you are losing * otherwise. * * (3) if both new and old are commit-ish, and new is a * descendant of old, it is OK. * * (4) regardless of all of the above, removing :B is * always allowed. */ ref->nonfastforward = !ref->deletion && !is_null_sha1(ref->old_sha1) && (!has_sha1_file(ref->old_sha1) || !ref_newer(ref->new_sha1, ref->old_sha1)); if (ref->nonfastforward && !ref->force && !args->force_update) { ref->status = REF_STATUS_REJECT_NONFASTFORWARD; continue; } if (!ref->deletion) new_refs++; if (!args->dry_run) { char *old_hex = sha1_to_hex(ref->old_sha1); char *new_hex = sha1_to_hex(ref->new_sha1); if (ask_for_status_report) { packet_write(out, "%s %s %s%c%s", old_hex, new_hex, ref->name, 0, "report-status"); ask_for_status_report = 0; expect_status_report = 1; } else packet_write(out, "%s %s %s", old_hex, new_hex, ref->name); } ref->status = expect_status_report ? REF_STATUS_EXPECTING_REPORT : REF_STATUS_OK; } packet_flush(out); if (new_refs && !args->dry_run) { if (pack_objects(out, remote_refs, extra_have, args) < 0) { for (ref = remote_refs; ref; ref = ref->next) ref->status = REF_STATUS_NONE; return -1; } } if (expect_status_report) ret = receive_status(in, remote_refs); else ret = 0; if (ret < 0) return ret; for (ref = remote_refs; ref; ref = ref->next) { switch (ref->status) { case REF_STATUS_NONE: case REF_STATUS_UPTODATE: case REF_STATUS_OK: break; default: return -1; } } return 0; }
static void receive_needs(void) { struct object_array shallows = OBJECT_ARRAY_INIT; int depth = 0; int has_non_tip = 0; shallow_nr = 0; for (;;) { struct object *o; const char *features; unsigned char sha1_buf[20]; char *line = packet_read_line(0, NULL); reset_timeout(); if (!line) break; if (starts_with(line, "shallow ")) { unsigned char sha1[20]; struct object *object; if (get_sha1_hex(line + 8, sha1)) die("invalid shallow line: %s", line); object = parse_object(sha1); if (!object) continue; if (object->type != OBJ_COMMIT) die("invalid shallow object %s", sha1_to_hex(sha1)); if (!(object->flags & CLIENT_SHALLOW)) { object->flags |= CLIENT_SHALLOW; add_object_array(object, NULL, &shallows); } continue; } if (starts_with(line, "deepen ")) { char *end; depth = strtol(line + 7, &end, 0); if (end == line + 7 || depth <= 0) die("Invalid deepen: %s", line); continue; } if (!starts_with(line, "want ") || get_sha1_hex(line+5, sha1_buf)) die("git upload-pack: protocol error, " "expected to get sha, not '%s'", line); features = line + 45; if (parse_feature_request(features, "multi_ack_detailed")) multi_ack = 2; else if (parse_feature_request(features, "multi_ack")) multi_ack = 1; if (parse_feature_request(features, "no-done")) no_done = 1; if (parse_feature_request(features, "thin-pack")) use_thin_pack = 1; if (parse_feature_request(features, "ofs-delta")) use_ofs_delta = 1; if (parse_feature_request(features, "side-band-64k")) use_sideband = LARGE_PACKET_MAX; else if (parse_feature_request(features, "side-band")) use_sideband = DEFAULT_PACKET_MAX; if (parse_feature_request(features, "no-progress")) no_progress = 1; if (parse_feature_request(features, "include-tag")) use_include_tag = 1; o = parse_object(sha1_buf); if (!o) die("git upload-pack: not our ref %s", sha1_to_hex(sha1_buf)); if (!(o->flags & WANTED)) { o->flags |= WANTED; if (!is_our_ref(o)) has_non_tip = 1; add_object_array(o, NULL, &want_obj); } } /* * We have sent all our refs already, and the other end * should have chosen out of them. When we are operating * in the stateless RPC mode, however, their choice may * have been based on the set of older refs advertised * by another process that handled the initial request. */ if (has_non_tip) check_non_tip(); if (!use_sideband && daemon_mode) no_progress = 1; if (depth == 0 && shallows.nr == 0) return; if (depth > 0) { struct commit_list *result = NULL, *backup = NULL; int i; if (depth == INFINITE_DEPTH && !is_repository_shallow()) for (i = 0; i < shallows.nr; i++) { struct object *object = shallows.objects[i].item; object->flags |= NOT_SHALLOW; } else backup = result = get_shallow_commits(&want_obj, depth, SHALLOW, NOT_SHALLOW); while (result) { struct object *object = &result->item->object; if (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) { packet_write(1, "shallow %s", sha1_to_hex(object->sha1)); register_shallow(object->sha1); shallow_nr++; } result = result->next; } free_commit_list(backup); for (i = 0; i < shallows.nr; i++) { struct object *object = shallows.objects[i].item; if (object->flags & NOT_SHALLOW) { struct commit_list *parents; packet_write(1, "unshallow %s", sha1_to_hex(object->sha1)); object->flags &= ~CLIENT_SHALLOW; /* make sure the real parents are parsed */ unregister_shallow(object->sha1); object->parsed = 0; parse_commit_or_die((struct commit *)object); parents = ((struct commit *)object)->parents; while (parents) { add_object_array(&parents->item->object, NULL, &want_obj); parents = parents->next; } add_object_array(object, NULL, &extra_edge_obj); } /* make sure commit traversal conforms to client */ register_shallow(object->sha1); } packet_flush(1); } else if (shallows.nr > 0) { int i; for (i = 0; i < shallows.nr; i++) register_shallow(shallows.objects[i].item->sha1); } shallow_nr += shallows.nr; free(shallows.objects); }
static int get_common_commits(void) { unsigned char sha1[20]; char last_hex[41]; int got_common = 0; int got_other = 0; int sent_ready = 0; save_commit_buffer = 0; for (;;) { char *line = packet_read_line(0, NULL); reset_timeout(); if (!line) { if (multi_ack == 2 && got_common && !got_other && ok_to_give_up()) { sent_ready = 1; packet_write(1, "ACK %s ready\n", last_hex); } if (have_obj.nr == 0 || multi_ack) packet_write(1, "NAK\n"); if (no_done && sent_ready) { packet_write(1, "ACK %s\n", last_hex); return 0; } if (stateless_rpc) exit(0); got_common = 0; got_other = 0; continue; } if (starts_with(line, "have ")) { switch (got_sha1(line+5, sha1)) { case -1: /* they have what we do not */ got_other = 1; if (multi_ack && ok_to_give_up()) { const char *hex = sha1_to_hex(sha1); if (multi_ack == 2) { sent_ready = 1; packet_write(1, "ACK %s ready\n", hex); } else packet_write(1, "ACK %s continue\n", hex); } break; default: got_common = 1; memcpy(last_hex, sha1_to_hex(sha1), 41); if (multi_ack == 2) packet_write(1, "ACK %s common\n", last_hex); else if (multi_ack) packet_write(1, "ACK %s continue\n", last_hex); else if (have_obj.nr == 1) packet_write(1, "ACK %s\n", last_hex); break; } continue; } if (!strcmp(line, "done")) { if (have_obj.nr > 0) { if (multi_ack) packet_write(1, "ACK %s\n", last_hex); return 0; } packet_write(1, "NAK\n"); return -1; } die("git upload-pack: expected SHA1 list, got '%s'", line); } }
/* * This returns a dummy child_process if the transport protocol does not * need fork(2), or a struct child_process object if it does. Once done, * finish the connection with finish_connect() with the value returned from * this function (it is safe to call finish_connect() with NULL to support * the former case). * * If it returns, the connect is successful; it just dies on errors (this * will hopefully be changed in a libification effort, to return NULL when * the connection failed). */ struct child_process *git_connect(int fd[2], const char *url_orig, const char *prog, int flags) { char *url = xstrdup(url_orig); char *host, *path; char *end; int c; struct child_process *conn; enum protocol protocol = PROTO_LOCAL; int free_path = 0; char *port = NULL; const char **arg; struct strbuf cmd; /* Without this we cannot rely on waitpid() to tell * what happened to our children. */ signal(SIGCHLD, SIG_DFL); host = strstr(url, "://"); if (host) { *host = '\0'; protocol = get_protocol(url); host += 3; c = '/'; } else { host = url; c = ':'; } /* * Don't do destructive transforms with git:// as that * protocol code does '[]' unwrapping of its own. */ if (host[0] == '[') { end = strchr(host + 1, ']'); if (end) { if (protocol != PROTO_GIT) { *end = 0; host++; } end++; } else end = host; } else end = host; path = strchr(end, c); if (path && !has_dos_drive_prefix(end)) { if (c == ':') { protocol = PROTO_SSH; *path++ = '\0'; } } else path = end; if (!path || !*path) die("No path specified. See 'man git-pull' for valid url syntax"); /* * null-terminate hostname and point path to ~ for URL's like this: * ssh://host.xz/~user/repo */ if (protocol != PROTO_LOCAL && host != url) { char *ptr = path; if (path[1] == '~') path++; else { path = xstrdup(ptr); free_path = 1; } *ptr = '\0'; } /* * Add support for ssh port: ssh://host.xy:<port>/... */ if (protocol == PROTO_SSH && host != url) port = get_port(host); if (protocol == PROTO_GIT) { /* These underlying connection commands die() if they * cannot connect. */ char *target_host = xstrdup(host); if (git_use_proxy(host)) git_proxy_connect(fd, host); else git_tcp_connect(fd, host, flags); /* * Separate original protocol components prog and path * from extended host header with a NUL byte. * * Note: Do not add any other headers here! Doing so * will cause older git-daemon servers to crash. */ packet_write(fd[1], "%s %s%chost=%s%c", prog, path, 0, target_host, 0); free(target_host); free(url); if (free_path) free(path); return &no_fork; } conn = xcalloc(1, sizeof(*conn)); strbuf_init(&cmd, MAX_CMD_LEN); strbuf_addstr(&cmd, prog); strbuf_addch(&cmd, ' '); sq_quote_buf(&cmd, path); if (cmd.len >= MAX_CMD_LEN) die("command line too long"); conn->in = conn->out = -1; conn->argv = arg = xcalloc(7, sizeof(*arg)); if (protocol == PROTO_SSH) { const char *ssh = getenv("GIT_SSH"); int putty = ssh && strcasestr(ssh, "plink"); if (!ssh) ssh = "ssh"; *arg++ = ssh; if (putty && !strcasestr(ssh, "tortoiseplink")) *arg++ = "-batch"; if (port) { /* P is for PuTTY, p is for OpenSSH */ *arg++ = putty ? "-P" : "-p"; *arg++ = port; } *arg++ = host; } else { /* remove these from the environment */ const char *env[] = { ALTERNATE_DB_ENVIRONMENT, DB_ENVIRONMENT, GIT_DIR_ENVIRONMENT, GIT_WORK_TREE_ENVIRONMENT, GRAFT_ENVIRONMENT, INDEX_ENVIRONMENT, NO_REPLACE_OBJECTS_ENVIRONMENT, NULL }; conn->env = env; conn->use_shell = 1; } *arg++ = cmd.buf; *arg = NULL; if (start_command(conn)) die("unable to fork"); fd[0] = conn->out; /* read from child's stdout */ fd[1] = conn->in; /* write to child's stdin */ strbuf_release(&cmd); free(url); if (free_path) free(path); return conn; }
// Go back to the previous sync point, then read everything from there to // the current sync point, not including the NAND blocks. static int st_backtrack(struct state *st) { struct pkt pkt; int ret; int before_nand = 1; ret = st->fdh->seek(st->last_run_offset); if (-1 == ret) { perror("Unable to backtrack"); } // Read every packet from the last while ((ret = packet_get_next_raw(st, &pkt)) == 0) { // Eventually we'll hit the sync point that brought us here. // Return to searching. if (is_sync_point(st, &pkt)) { if (pkt.header.type == PACKET_HELLO) { pkt.header.sec = 0; pkt.header.nsec = 0; packet_write(st, &pkt); } jstate_set(st, ST_SEARCHING); st->last_run_offset = st->fdh->pos(); break; } else if (is_nand(st, &pkt)) before_nand = 0; else { int nsec_dif, sec_dif; if (before_nand) { sec_dif = st->last_sec_dif; nsec_dif = st->last_nsec_dif; } else { sec_dif = st->sec_dif; nsec_dif = st->nsec_dif; } if (nsec_dif > 0) { pkt.header.nsec += nsec_dif; if (pkt.header.nsec > 1000000000L) { pkt.header.nsec -= 1000000000L; pkt.header.sec++; } pkt.header.sec += sec_dif; } else { pkt.header.nsec -= nsec_dif; if (pkt.header.nsec <= 0) { pkt.header.nsec += 1000000000L; pkt.header.sec--; } pkt.header.sec -= sec_dif; } // Fudge the time for the "reset card" command // (due to timing weirdness, it can vary widely.) if (pkt.header.type == PACKET_COMMAND && pkt.data.command.cmd[0] == 'r' && pkt.data.command.cmd[1] == 'c') { pkt.header.sec = 0; pkt.header.nsec = 8; } st->last_sec = pkt.header.sec; st->last_nsec = pkt.header.nsec; packet_write(st, &pkt); } } return ret; }
static int st_joining(struct state *st) { struct pkt pkt; int ret; // Actually attempt to join the data if (st->buffer_offset >= 0) { struct pkt pkts[REQUIRED_MATCHES]; struct pkt old_pkts[REQUIRED_MATCHES]; int synced = 0; int disk_offset = 0; int buffer_start = st->buffer_offset; for (disk_offset=disk_offset; (disk_offset+REQUIRED_MATCHES) < SKIP_AMOUNT && !synced; disk_offset++) { fill_buffer(st, pkts, REQUIRED_MATCHES, packet_get_next_raw); for (st->search_limit = 0; (st->search_limit + REQUIRED_MATCHES) < SKIP_AMOUNT && !synced; st->search_limit++) { int i; int matches_found = 0; fill_buffer(st, old_pkts, REQUIRED_MATCHES, buffer_get_packet); // Check to see if our run matches up for (i=0; i<REQUIRED_MATCHES; i++) { int dat = pkts[i].data.nand_cycle.data; int old_dat = old_pkts[i].data.nand_cycle.data; int ctrl = pkts[i].data.nand_cycle.control; int old_ctrl = old_pkts[i].data.nand_cycle.control; if (dat == old_dat && ctrl == old_ctrl) matches_found++; } // If enough packets match, we're synced if (matches_found >= REQUIRED_MATCHES-1) { st->last_sec_dif = st->sec_dif; st->last_nsec_dif = st->nsec_dif; st->sec_dif = -(pkts[REQUIRED_MATCHES/2].header.sec-old_pkts[REQUIRED_MATCHES/2].header.sec); st->nsec_dif = -(pkts[REQUIRED_MATCHES/2].header.nsec-old_pkts[REQUIRED_MATCHES/2].header.nsec); if (st->nsec_dif > 1000000000L) { st->nsec_dif -= 1000000000L; st->sec_dif++; } else if (st->nsec_dif < 0) { st->nsec_dif += 1000000000L; st->sec_dif--; } synced=1; buffer_unget_packet(st, old_pkts); } else { empty_buffer(st, old_pkts, REQUIRED_MATCHES, buffer_unget_packet); buffer_get_packet(st, old_pkts); } } if (!synced) { empty_buffer(st, pkts, REQUIRED_MATCHES, packet_unget); empty_buffer(st, old_pkts, REQUIRED_MATCHES, buffer_unget_packet); packet_get_next_raw(st, pkts); } } if (!synced) printf("Couldn't join\n"); // Now we're synced, just ignore the rest of the matched-buffer // packets. This is because if they're in the buffer, they've // already been written out. int tries = 0; while ((st->buffer_offset+st->search_limit)%SKIP_AMOUNT != buffer_start-1) { struct pkt old_pkt; int dat, old_dat, ctrl, old_ctrl; buffer_get_packet(st, &old_pkt); packet_get_next_raw(st, &pkt); dat = pkt.data.nand_cycle.data; old_dat = old_pkt.data.nand_cycle.data; ctrl = pkt.data.nand_cycle.control; old_ctrl = old_pkt.data.nand_cycle.control; tries++; if ((dat != old_dat) || (ctrl != old_ctrl)) { printf("Join anomaly after %d tries: %d/%d and %d/%d\n", tries, old_dat, dat, old_ctrl, ctrl); } } st->buffer_offset = -1; st->search_limit = 0; } // Done now, copy data while ((ret = packet_get_next_raw(st, &pkt)) == 0) { if (!is_nand(st, &pkt)) { packet_unget(st, &pkt); jstate_set(st, ST_SEARCHING); break; } if (st->nsec_dif > 0) { pkt.header.nsec += st->nsec_dif; if (pkt.header.nsec > 1000000000L) { pkt.header.nsec -= 1000000000L; pkt.header.sec++; } pkt.header.sec += st->sec_dif; } else { pkt.header.nsec -= st->nsec_dif; if (pkt.header.nsec <= 0) { pkt.header.nsec += 1000000000L; pkt.header.sec--; } pkt.header.sec -= st->sec_dif; } packet_write(st, &pkt); buffer_put_packet(st, &pkt); } return ret; }