summaryrefslogtreecommitdiff
path: root/transport.c
diff options
context:
space:
mode:
Diffstat (limited to 'transport.c')
-rw-r--r--transport.c110
1 files changed, 83 insertions, 27 deletions
diff --git a/transport.c b/transport.c
index 055d2a27d9..37e4f5e42c 100644
--- a/transport.c
+++ b/transport.c
@@ -15,6 +15,7 @@
#include "submodule.h"
#include "string-list.h"
#include "sha1-array.h"
+#include "sigchain.h"
/* rsync support */
@@ -117,7 +118,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 +169,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;
@@ -277,12 +279,11 @@ static int fetch_objs_via_rsync(struct transport *transport,
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/") &&
@@ -291,27 +292,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_gently(buf->buf, "%s", 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;
}
@@ -477,9 +477,6 @@ 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;
}
@@ -518,7 +515,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;
@@ -551,15 +548,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);
}
@@ -727,6 +726,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;
@@ -743,7 +746,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)
@@ -774,6 +777,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)
@@ -823,9 +827,16 @@ 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;
+ if (flags & TRANSPORT_PUSH_CERT_ALWAYS)
+ args.push_cert = SEND_PACK_PUSH_CERT_ALWAYS;
+ else if (flags & TRANSPORT_PUSH_CERT_IF_ASKED)
+ args.push_cert = SEND_PACK_PUSH_CERT_IF_ASKED;
+ else
+ args.push_cert = SEND_PACK_PUSH_CERT_NEVER;
+
ret = send_pack(&args, data->fd, data->conn, remote_refs,
&data->extra_have);
@@ -905,6 +916,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;
@@ -936,12 +983,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;
@@ -953,7 +1002,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;
@@ -969,9 +1021,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);
}
@@ -1077,6 +1127,8 @@ static int run_pre_push_hook(struct transport *transport,
return -1;
}
+ sigchain_push(SIGPIPE, SIG_IGN);
+
strbuf_init(&buf, 256);
for (r = remote_refs; r; r = r->next) {
@@ -1090,8 +1142,10 @@ static int run_pre_push_hook(struct transport *transport,
r->peer_ref->name, sha1_to_hex(r->new_sha1),
r->name, sha1_to_hex(r->old_sha1));
- if (write_in_full(proc.in, buf.buf, buf.len) != buf.len) {
- ret = -1;
+ if (write_in_full(proc.in, buf.buf, buf.len) < 0) {
+ /* We do not mind if a hook does not read all refs. */
+ if (errno != EPIPE)
+ ret = -1;
break;
}
}
@@ -1102,6 +1156,8 @@ static int run_pre_push_hook(struct transport *transport,
if (!ret)
ret = x;
+ sigchain_pop(SIGPIPE);
+
x = finish_command(&proc);
if (!ret)
ret = x;