X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=connect.c;h=66daa11a5737efd6ee2dbd6ff2ad0e1475fcba20;hb=e27e609bbf81271318d99f2643f378f3fde6c6c6;hp=66e78a29054a121c92beca3d997105f0137c170d;hpb=1ef9e05dbf36a80bb65fb150dd7bdd60852db777;p=git.git diff --git a/connect.c b/connect.c index 66e78a290..66daa11a5 100644 --- a/connect.c +++ b/connect.c @@ -3,20 +3,43 @@ #include "pkt-line.h" #include "quote.h" #include "refs.h" -#include -#include -#include -#include -#include -#include -static char *server_capabilities = NULL; +static char *server_capabilities; + +static int check_ref(const char *name, int len, unsigned int flags) +{ + if (!flags) + return 1; + + if (len < 5 || memcmp(name, "refs/", 5)) + return 0; + + /* Skip the "refs/" part */ + name += 5; + len -= 5; + + /* REF_NORMAL means that we don't want the magic fake tag refs */ + if ((flags & REF_NORMAL) && check_ref_format(name) < 0) + return 0; + + /* REF_HEADS means that we want regular branch heads */ + if ((flags & REF_HEADS) && !memcmp(name, "heads/", 6)) + return 1; + + /* REF_TAGS means that we want tags */ + if ((flags & REF_TAGS) && !memcmp(name, "tags/", 5)) + return 1; + + /* All type bits clear means that we are ok with anything */ + return !(flags & ~REF_NORMAL); +} /* * Read all the refs from the other end */ struct ref **get_remote_heads(int in, struct ref **list, - int nr_match, char **match, int ignore_funny) + int nr_match, char **match, + unsigned int flags) { *list = NULL; for (;;) { @@ -40,17 +63,15 @@ struct ref **get_remote_heads(int in, struct ref **list, if (len != name_len + 41) { if (server_capabilities) free(server_capabilities); - server_capabilities = strdup(name + name_len + 1); + server_capabilities = xstrdup(name + name_len + 1); } - if (ignore_funny && 45 < len && !memcmp(name, "refs/", 5) && - check_ref_format(name + 5)) + if (!check_ref(name, name_len, flags)) continue; - if (nr_match && !path_match(name, nr_match, match)) continue; ref = xcalloc(1, sizeof(*ref) + len - 40); - memcpy(ref->old_sha1, old_sha1, 20); + hashcpy(ref->old_sha1, old_sha1); memcpy(ref->name, buffer + 41, len - 40); *list = ref; list = &ref->next; @@ -117,6 +138,7 @@ struct refspec { * +A:B means overwrite remote B with local A. * +A is a shorthand for +A:A. * A is a shorthand for A:A. + * :B means delete remote B. */ static struct refspec *parse_ref_spec(int nr_refspec, char **refspec) { @@ -147,21 +169,58 @@ static int count_refspec_match(const char *pattern, struct ref *refs, struct ref **matched_ref) { - int match; int patlen = strlen(pattern); + struct ref *matched_weak = NULL; + struct ref *matched = NULL; + int weak_match = 0; + int match = 0; - for (match = 0; refs; refs = refs->next) { + for (weak_match = match = 0; refs; refs = refs->next) { char *name = refs->name; int namelen = strlen(name); + int weak_match; + if (namelen < patlen || memcmp(name + namelen - patlen, pattern, patlen)) continue; if (namelen != patlen && name[namelen - patlen - 1] != '/') continue; - match++; - *matched_ref = refs; + + /* A match is "weak" if it is with refs outside + * heads or tags, and did not specify the pattern + * in full (e.g. "refs/remotes/origin/master") or at + * least from the toplevel (e.g. "remotes/origin/master"); + * otherwise "git push $URL master" would result in + * ambiguity between remotes/origin/master and heads/master + * at the remote site. + */ + if (namelen != patlen && + patlen != namelen - 5 && + strncmp(name, "refs/heads/", 11) && + strncmp(name, "refs/tags/", 10)) { + /* We want to catch the case where only weak + * matches are found and there are multiple + * matches, and where more than one strong + * matches are found, as ambiguous. One + * strong match with zero or more weak matches + * are acceptable as a unique match. + */ + matched_weak = refs; + weak_match++; + } + else { + matched = refs; + match++; + } + } + if (!matched) { + *matched_ref = matched_weak; + return weak_match; + } + else { + *matched_ref = matched; + return match; } - return match; } static void link_dst_tail(struct ref *ref, struct ref ***tail) @@ -176,12 +235,19 @@ static struct ref *try_explicit_object_name(const char *name) unsigned char sha1[20]; struct ref *ref; int len; + + if (!*name) { + ref = xcalloc(1, sizeof(*ref) + 20); + strcpy(ref->name, "(delete)"); + hashclr(ref->new_sha1); + return ref; + } if (get_sha1(name, sha1)) return NULL; len = strlen(name) + 1; ref = xcalloc(1, sizeof(*ref) + len); memcpy(ref->name, name, len); - memcpy(ref->new_sha1, sha1, 20); + hashcpy(ref->new_sha1, sha1); return ref; } @@ -198,7 +264,8 @@ static int match_explicit_refs(struct ref *src, struct ref *dst, break; case 0: /* The source could be in the get_sha1() format - * not a reference name. + * not a reference name. :refs/other is a + * way to delete 'other' ref at the remote end. */ matched_src = try_explicit_object_name(rs[i].src); if (matched_src) @@ -291,7 +358,7 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, int len = strlen(src->name) + 1; dst_peer = xcalloc(1, sizeof(*dst_peer) + len); memcpy(dst_peer->name, src->name, len); - memcpy(dst_peer->new_sha1, src->new_sha1, 20); + hashcpy(dst_peer->new_sha1, src->new_sha1); link_dst_tail(dst_peer, dst_tail); } dst_peer->peer_ref = src; @@ -328,9 +395,9 @@ static enum protocol get_protocol(const char *name) */ static int git_tcp_connect_sock(char *host) { - int sockfd = -1; + int sockfd = -1, saved_errno = 0; char *colon, *end; - char *port = STR(DEFAULT_GIT_PORT); + const char *port = STR(DEFAULT_GIT_PORT); struct addrinfo hints, *ai0, *ai; int gai; @@ -362,9 +429,12 @@ static int git_tcp_connect_sock(char *host) for (ai0 = ai; ai; ai = ai->ai_next) { sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (sockfd < 0) + if (sockfd < 0) { + saved_errno = errno; continue; + } if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) { + saved_errno = errno; close(sockfd); sockfd = -1; continue; @@ -375,7 +445,7 @@ static int git_tcp_connect_sock(char *host) freeaddrinfo(ai0); if (sockfd < 0) - die("unable to connect a socket (%s)", strerror(errno)); + die("unable to connect a socket (%s)", strerror(saved_errno)); return sockfd; } @@ -387,7 +457,7 @@ static int git_tcp_connect_sock(char *host) */ static int git_tcp_connect_sock(char *host) { - int sockfd = -1; + int sockfd = -1, saved_errno = 0; char *colon, *end; char *port = STR(DEFAULT_GIT_PORT), *ep; struct hostent *he; @@ -426,8 +496,10 @@ static int git_tcp_connect_sock(char *host) for (ap = he->h_addr_list; *ap; ap++) { sockfd = socket(he->h_addrtype, SOCK_STREAM, 0); - if (sockfd < 0) + if (sockfd < 0) { + saved_errno = errno; continue; + } memset(&sa, 0, sizeof sa); sa.sin_family = he->h_addrtype; @@ -435,6 +507,7 @@ static int git_tcp_connect_sock(char *host) memcpy(&sa.sin_addr, *ap, he->h_length); if (connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) { + saved_errno = errno; close(sockfd); sockfd = -1; continue; @@ -443,7 +516,7 @@ static int git_tcp_connect_sock(char *host) } if (sockfd < 0) - die("unable to connect a socket (%s)", strerror(errno)); + die("unable to connect a socket (%s)", strerror(saved_errno)); return sockfd; } @@ -451,8 +524,7 @@ static int git_tcp_connect_sock(char *host) #endif /* NO_IPV6 */ -static void git_tcp_connect(int fd[2], - const char *prog, char *host, char *path) +static void git_tcp_connect(int fd[2], char *host) { int sockfd = git_tcp_connect_sock(host); @@ -461,8 +533,8 @@ static void git_tcp_connect(int fd[2], } -static char *git_proxy_command = NULL; -static const char *rhost_name = NULL; +static char *git_proxy_command; +static const char *rhost_name; static int rhost_len; static int git_proxy_command_options(const char *var, const char *value) @@ -522,10 +594,9 @@ static int git_use_proxy(const char *host) return (git_proxy_command && *git_proxy_command); } -static void git_proxy_connect(int fd[2], - const char *prog, char *host, char *path) +static void git_proxy_connect(int fd[2], char *host) { - char *port = STR(DEFAULT_GIT_PORT); + const char *port = STR(DEFAULT_GIT_PORT); char *colon, *end; int pipefd[2][2]; pid_t pid; @@ -568,12 +639,19 @@ static void git_proxy_connect(int fd[2], close(pipefd[1][0]); } +#define MAX_CMD_LEN 1024 + /* - * Yeah, yeah, fixme. Need to pass in the heads etc. + * This returns 0 if the transport protocol does not need fork(2), + * or a process id 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 0 to support the former + * case). + * + * Does not return a negative value on error; it just dies. */ -int git_connect(int fd[2], char *url, const char *prog) +pid_t git_connect(int fd[2], char *url, const char *prog) { - char command[1024]; char *host, *path = url; char *end; int c; @@ -630,7 +708,7 @@ int git_connect(int fd[2], char *url, const char *prog) if (path[1] == '~') path++; else { - path = strdup(ptr); + path = xstrdup(ptr); free_path = 1; } @@ -641,11 +719,11 @@ int git_connect(int fd[2], char *url, const char *prog) /* These underlying connection commands die() if they * cannot connect. */ - char *target_host = strdup(host); + char *target_host = xstrdup(host); if (git_use_proxy(host)) - git_proxy_connect(fd, prog, host, path); + git_proxy_connect(fd, host); else - git_tcp_connect(fd, prog, host, path); + git_tcp_connect(fd, host); /* * Separate original protocol components prog and path * from extended components with a NUL byte. @@ -666,8 +744,18 @@ int git_connect(int fd[2], char *url, const char *prog) if (pid < 0) die("unable to fork"); if (!pid) { - snprintf(command, sizeof(command), "%s %s", prog, - sq_quote(path)); + char command[MAX_CMD_LEN]; + char *posn = command; + int size = MAX_CMD_LEN; + int of = 0; + + of |= add_to_string(&posn, &size, prog, 0); + of |= add_to_string(&posn, &size, " ", 0); + of |= add_to_string(&posn, &size, path, 1); + + if (of) + die("command line too long"); + dup2(pipefd[1][0], 0); dup2(pipefd[0][1], 1); close(pipefd[0][0]); @@ -706,14 +794,12 @@ int git_connect(int fd[2], char *url, const char *prog) int finish_connect(pid_t pid) { - int ret; + if (pid == 0) + return 0; - for (;;) { - ret = waitpid(pid, NULL, 0); - if (!ret) - break; + while (waitpid(pid, NULL, 0) < 0) { if (errno != EINTR) - break; + return -1; } - return ret; + return 0; }