Imported Upstream version 2.5.4
[platform/upstream/git.git] / transport.c
index 325f03e..164a716 100644 (file)
@@ -117,7 +117,7 @@ static void insert_packed_refs(const char *packed_refs, struct ref **list)
                        return;
                }
 
-               if (hexval(buffer[0]) > 0xf)
+               if (!isxdigit(buffer[0]))
                        continue;
                len = strlen(buffer);
                if (len && buffer[len - 1] == '\n')
@@ -168,7 +168,8 @@ static void set_upstreams(struct transport *transport, struct ref *refs,
                /* Follow symbolic refs (mainly for HEAD). */
                localname = ref->peer_ref->name;
                remotename = ref->name;
-               tmp = resolve_ref_unsafe(localname, sha, 1, &flag);
+               tmp = resolve_ref_unsafe(localname, RESOLVE_REF_READING,
+                                        sha, &flag);
                if (tmp && flag & REF_ISSYMREF &&
                        starts_with(tmp, "refs/heads/"))
                        localname = tmp;
@@ -192,14 +193,16 @@ static void set_upstreams(struct transport *transport, struct ref *refs,
 
 static const char *rsync_url(const char *url)
 {
-       return !starts_with(url, "rsync://") ? skip_prefix(url, "rsync:") : url;
+       if (!starts_with(url, "rsync://"))
+               skip_prefix(url, "rsync:", &url);
+       return url;
 }
 
 static struct ref *get_refs_via_rsync(struct transport *transport, int for_push)
 {
        struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT;
        struct ref dummy = {NULL}, *tail = &dummy;
-       struct child_process rsync;
+       struct child_process rsync = CHILD_PROCESS_INIT;
        const char *args[5];
        int temp_dir_len;
 
@@ -216,7 +219,6 @@ static struct ref *get_refs_via_rsync(struct transport *transport, int for_push)
        strbuf_addstr(&buf, rsync_url(transport->url));
        strbuf_addstr(&buf, "/refs");
 
-       memset(&rsync, 0, sizeof(rsync));
        rsync.argv = args;
        rsync.stdout_to_stderr = 1;
        args[0] = "rsync";
@@ -261,40 +263,26 @@ static struct ref *get_refs_via_rsync(struct transport *transport, int for_push)
 static int fetch_objs_via_rsync(struct transport *transport,
                                int nr_objs, struct ref **to_fetch)
 {
-       struct strbuf buf = STRBUF_INIT;
-       struct child_process rsync;
-       const char *args[8];
-       int result;
+       struct child_process rsync = CHILD_PROCESS_INIT;
 
-       strbuf_addstr(&buf, rsync_url(transport->url));
-       strbuf_addstr(&buf, "/objects/");
-
-       memset(&rsync, 0, sizeof(rsync));
-       rsync.argv = args;
        rsync.stdout_to_stderr = 1;
-       args[0] = "rsync";
-       args[1] = (transport->verbose > 1) ? "-rv" : "-r";
-       args[2] = "--ignore-existing";
-       args[3] = "--exclude";
-       args[4] = "info";
-       args[5] = buf.buf;
-       args[6] = get_object_directory();
-       args[7] = NULL;
+       argv_array_push(&rsync.args, "rsync");
+       argv_array_push(&rsync.args, (transport->verbose > 1) ? "-rv" : "-r");
+       argv_array_push(&rsync.args, "--ignore-existing");
+       argv_array_push(&rsync.args, "--exclude");
+       argv_array_push(&rsync.args, "info");
+       argv_array_pushf(&rsync.args, "%s/objects/", rsync_url(transport->url));
+       argv_array_push(&rsync.args, get_object_directory());
 
        /* NEEDSWORK: handle one level of alternates */
-       result = run_command(&rsync);
-
-       strbuf_release(&buf);
-
-       return result;
+       return run_command(&rsync);
 }
 
-static int write_one_ref(const char *name, const unsigned char *sha1,
-               int flags, void *data)
+static int write_one_ref(const char *name, const struct object_id *oid,
+                        int flags, void *data)
 {
        struct strbuf *buf = data;
        int len = buf->len;
-       FILE *f;
 
        /* when called via for_each_ref(), flags is non-zero */
        if (flags && !starts_with(name, "refs/heads/") &&
@@ -303,27 +291,26 @@ static int write_one_ref(const char *name, const unsigned char *sha1,
 
        strbuf_addstr(buf, name);
        if (safe_create_leading_directories(buf->buf) ||
-                       !(f = fopen(buf->buf, "w")) ||
-                       fprintf(f, "%s\n", sha1_to_hex(sha1)) < 0 ||
-                       fclose(f))
-               return error("problems writing temporary file %s", buf->buf);
+           write_file(buf->buf, 0, "%s\n", oid_to_hex(oid)))
+               return error("problems writing temporary file %s: %s",
+                            buf->buf, strerror(errno));
        strbuf_setlen(buf, len);
        return 0;
 }
 
 static int write_refs_to_temp_dir(struct strbuf *temp_dir,
-               int refspec_nr, const char **refspec)
+                                 int refspec_nr, const char **refspec)
 {
        int i;
 
        for (i = 0; i < refspec_nr; i++) {
-               unsigned char sha1[20];
+               struct object_id oid;
                char *ref;
 
-               if (dwim_ref(refspec[i], strlen(refspec[i]), sha1, &ref) != 1)
+               if (dwim_ref(refspec[i], strlen(refspec[i]), oid.hash, &ref) != 1)
                        return error("Could not get ref %s", refspec[i]);
 
-               if (write_one_ref(ref, sha1, 0, temp_dir)) {
+               if (write_one_ref(ref, &oid, 0, temp_dir)) {
                        free(ref);
                        return -1;
                }
@@ -337,7 +324,7 @@ static int rsync_transport_push(struct transport *transport,
 {
        struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT;
        int result = 0, i;
-       struct child_process rsync;
+       struct child_process rsync = CHILD_PROCESS_INIT;
        const char *args[10];
 
        if (flags & TRANSPORT_PUSH_MIRROR)
@@ -348,7 +335,6 @@ static int rsync_transport_push(struct transport *transport,
        strbuf_addstr(&buf, rsync_url(transport->url));
        strbuf_addch(&buf, '/');
 
-       memset(&rsync, 0, sizeof(rsync));
        rsync.argv = args;
        rsync.stdout_to_stderr = 1;
        i = 0;
@@ -490,6 +476,9 @@ static int set_git_option(struct git_transport_options *opts,
                                die("transport: invalid depth option '%s'", value);
                }
                return 0;
+       } else if (!strcmp(name, TRANS_OPT_PUSH_CERT)) {
+               opts->push_cert = !!value;
+               return 0;
        }
        return 1;
 }
@@ -528,7 +517,7 @@ static int fetch_refs_via_pack(struct transport *transport,
                               int nr_heads, struct ref **to_fetch)
 {
        struct git_transport_data *data = transport->data;
-       const struct ref *refs;
+       struct ref *refs;
        char *dest = xstrdup(transport->url);
        struct fetch_pack_args args;
        struct ref *refs_tmp = NULL;
@@ -561,15 +550,17 @@ static int fetch_refs_via_pack(struct transport *transport,
                          &transport->pack_lockfile);
        close(data->fd[0]);
        close(data->fd[1]);
-       if (finish_connect(data->conn))
+       if (finish_connect(data->conn)) {
+               free_refs(refs);
                refs = NULL;
+       }
        data->conn = NULL;
        data->got_remote_heads = 0;
        data->options.self_contained_and_connected =
                args.self_contained_and_connected;
 
        free_refs(refs_tmp);
-
+       free_refs(refs);
        free(dest);
        return (refs ? 0 : -1);
 }
@@ -737,6 +728,10 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i
                                                 ref->deletion ? NULL : ref->peer_ref,
                                                 "remote failed to report status", porcelain);
                break;
+       case REF_STATUS_ATOMIC_PUSH_FAILED:
+               print_ref_status('!', "[rejected]", ref, ref->peer_ref,
+                                                "atomic push failed", porcelain);
+               break;
        case REF_STATUS_OK:
                print_ok_ref_status(ref, porcelain);
                break;
@@ -753,7 +748,7 @@ void transport_print_push_status(const char *dest, struct ref *refs,
        unsigned char head_sha1[20];
        char *head;
 
-       head = resolve_refdup("HEAD", head_sha1, 1, NULL);
+       head = resolve_refdup("HEAD", RESOLVE_REF_READING, head_sha1, NULL);
 
        if (verbose) {
                for (ref = refs; ref; ref = ref->next)
@@ -784,6 +779,7 @@ void transport_print_push_status(const char *dest, struct ref *refs,
                        *reject_reasons |= REJECT_NEEDS_FORCE;
                }
        }
+       free(head);
 }
 
 void transport_verify_remote_names(int nr_heads, const char **heads)
@@ -833,6 +829,9 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
        args.progress = transport->progress;
        args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
        args.porcelain = !!(flags & TRANSPORT_PUSH_PORCELAIN);
+       args.push_cert = !!(flags & TRANSPORT_PUSH_CERT);
+       args.atomic = !!(flags & TRANSPORT_PUSH_ATOMIC);
+       args.url = transport->url;
 
        ret = send_pack(&args, data->fd, data->conn, remote_refs,
                        &data->extra_have);
@@ -913,6 +912,42 @@ static int external_specification_len(const char *url)
        return strchr(url, ':') - url;
 }
 
+static const struct string_list *protocol_whitelist(void)
+{
+       static int enabled = -1;
+       static struct string_list allowed = STRING_LIST_INIT_DUP;
+
+       if (enabled < 0) {
+               const char *v = getenv("GIT_ALLOW_PROTOCOL");
+               if (v) {
+                       string_list_split(&allowed, v, ':', -1);
+                       string_list_sort(&allowed);
+                       enabled = 1;
+               } else {
+                       enabled = 0;
+               }
+       }
+
+       return enabled ? &allowed : NULL;
+}
+
+int is_transport_allowed(const char *type)
+{
+       const struct string_list *allowed = protocol_whitelist();
+       return !allowed || string_list_has_string(allowed, type);
+}
+
+void transport_check_allowed(const char *type)
+{
+       if (!is_transport_allowed(type))
+               die("transport '%s' not allowed", type);
+}
+
+int transport_restrict_protocols(void)
+{
+       return !!protocol_whitelist();
+}
+
 struct transport *transport_get(struct remote *remote, const char *url)
 {
        const char *helper;
@@ -944,12 +979,14 @@ struct transport *transport_get(struct remote *remote, const char *url)
        if (helper) {
                transport_helper_init(ret, helper);
        } else if (starts_with(url, "rsync:")) {
+               transport_check_allowed("rsync");
                ret->get_refs_list = get_refs_via_rsync;
                ret->fetch = fetch_objs_via_rsync;
                ret->push = rsync_transport_push;
                ret->smart_options = NULL;
        } else if (url_is_local_not_ssh(url) && is_file(url) && is_bundle(url, 1)) {
                struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
+               transport_check_allowed("file");
                ret->data = data;
                ret->get_refs_list = get_refs_from_bundle;
                ret->fetch = fetch_refs_from_bundle;
@@ -961,7 +998,10 @@ struct transport *transport_get(struct remote *remote, const char *url)
                || starts_with(url, "ssh://")
                || starts_with(url, "git+ssh://")
                || starts_with(url, "ssh+git://")) {
-               /* These are builtin smart transports. */
+               /*
+                * These are builtin smart transports; "allowed" transports
+                * will be checked individually in git_connect.
+                */
                struct git_transport_data *data = xcalloc(1, sizeof(*data));
                ret->data = data;
                ret->set_option = NULL;
@@ -977,9 +1017,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
        } else {
                /* Unknown protocol in URL. Pass to external handler. */
                int len = external_specification_len(url);
-               char *handler = xmalloc(len + 1);
-               handler[len] = 0;
-               strncpy(handler, url, len);
+               char *handler = xmemdupz(url, len);
                transport_helper_init(ret, handler);
        }
 
@@ -1066,7 +1104,7 @@ static int run_pre_push_hook(struct transport *transport,
 {
        int ret = 0, x;
        struct ref *r;
-       struct child_process proc;
+       struct child_process proc = CHILD_PROCESS_INIT;
        struct strbuf buf;
        const char *argv[4];
 
@@ -1077,7 +1115,6 @@ static int run_pre_push_hook(struct transport *transport,
        argv[2] = transport->url;
        argv[3] = NULL;
 
-       memset(&proc, 0, sizeof(proc));
        proc.argv = argv;
        proc.in = -1;
 
@@ -1186,10 +1223,8 @@ int transport_push(struct transport *transport,
                if ((flags & (TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND |
                              TRANSPORT_RECURSE_SUBMODULES_CHECK)) && !is_bare_repository()) {
                        struct ref *ref = remote_refs;
-                       struct string_list needs_pushing;
+                       struct string_list needs_pushing = STRING_LIST_INIT_DUP;
 
-                       memset(&needs_pushing, 0, sizeof(struct string_list));
-                       needs_pushing.strdup_strings = 1;
                        for (; ref; ref = ref->next)
                                if (!is_null_sha1(ref->new_sha1) &&
                                    find_unpushed_submodules(ref->new_sha1,
@@ -1369,11 +1404,11 @@ static int refs_from_alternate_cb(struct alternate_object_database *e,
        while (other[len-1] == '/')
                other[--len] = '\0';
        if (len < 8 || memcmp(other + len - 8, "/objects", 8))
-               return 0;
+               goto out;
        /* Is this a git repository with refs? */
        memcpy(other + len - 8, "/refs", 6);
        if (!is_directory(other))
-               return 0;
+               goto out;
        other[len - 8] = '\0';
        remote = remote_get(other);
        transport = transport_get(remote, other);
@@ -1382,6 +1417,7 @@ static int refs_from_alternate_cb(struct alternate_object_database *e,
             extra = extra->next)
                cb->fn(extra, cb->data);
        transport_disconnect(transport);
+out:
        free(other);
        return 0;
 }