Wrap all the ref updates inside a transaction. Free commands when we are finished with them instead of leaking. Signed-off-by: Ronnie Sahlberg <sahlberg@xxxxxxxxxx> --- builtin/receive-pack.c | 74 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 22 deletions(-) diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index c323081..2724a86 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -194,7 +194,7 @@ static void write_head_info(void) struct command { struct command *next; - const char *error_string; + char *error_string; unsigned int skip_update:1, did_not_exist:1; int index; @@ -468,14 +468,13 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si) return 0; } -static const char *update(struct command *cmd, struct shallow_info *si) +static char *update(struct command *cmd, struct shallow_info *si) { const char *name = cmd->ref_name; struct strbuf namespaced_name_buf = STRBUF_INIT; const char *namespaced_name; unsigned char *old_sha1 = cmd->old_sha1; unsigned char *new_sha1 = cmd->new_sha1; - struct ref_lock *lock; /* only refs/... are allowed */ if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) { @@ -576,19 +575,27 @@ static const char *update(struct command *cmd, struct shallow_info *si) return NULL; /* good */ } else { + struct strbuf err = STRBUF_INIT; + struct ref_transaction *transaction; + if (shallow_update && si->shallow_ref[cmd->index] && update_shallow_ref(cmd, si)) return "shallow error"; - lock = lock_any_ref_for_update(namespaced_name, old_sha1, - 0, NULL); - if (!lock) { - rp_error("failed to lock %s", name); - return "failed to lock"; - } - if (write_ref_sha1(lock, new_sha1, "push")) { - return "failed to write"; /* error() already called */ + transaction = ref_transaction_begin(&err); + if (!transaction || + ref_transaction_update(transaction, namespaced_name, + new_sha1, old_sha1, 0, 1, &err) || + ref_transaction_commit(transaction, "push", &err)) { + char *str = strbuf_detach(&err, NULL); + ref_transaction_free(transaction); + + rp_error("%s", str); + return str; } + + ref_transaction_free(transaction); + strbuf_release(&err); return NULL; /* good */ } } @@ -647,6 +654,9 @@ static void check_aliased_update(struct command *cmd, struct string_list *list) char cmd_oldh[41], cmd_newh[41], dst_oldh[41], dst_newh[41]; int flag; + if (cmd->error_string) + die("BUG: check_alised_update called with failed cmd"); + strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name); dst_name = resolve_ref_unsafe(buf.buf, sha1, 0, &flag); strbuf_release(&buf); @@ -658,7 +668,7 @@ static void check_aliased_update(struct command *cmd, struct string_list *list) if (!dst_name) { rp_error("refusing update to broken symref '%s'", cmd->ref_name); cmd->skip_update = 1; - cmd->error_string = "broken symref"; + cmd->error_string = xstrdup("broken symref"); return; } @@ -684,8 +694,9 @@ static void check_aliased_update(struct command *cmd, struct string_list *list) cmd->ref_name, cmd_oldh, cmd_newh, dst_cmd->ref_name, dst_oldh, dst_newh); - cmd->error_string = dst_cmd->error_string = - "inconsistent aliased update"; + cmd->error_string = xstrdup("inconsistent aliased update"); + free(dst_cmd->error_string); + dst_cmd->error_string = xstrdup("inconsistent aliased update"); } static void check_aliased_updates(struct command *commands) @@ -733,7 +744,9 @@ static void set_connectivity_errors(struct command *commands, if (!check_everything_connected(command_singleton_iterator, 0, &singleton)) continue; - cmd->error_string = "missing necessary objects"; + if (cmd->error_string) /* can't happen */ + continue; + cmd->error_string = xstrdup("missing necessary objects"); } } @@ -770,9 +783,9 @@ static void reject_updates_to_hidden(struct command *commands) if (cmd->error_string || !ref_is_hidden(cmd->ref_name)) continue; if (is_null_sha1(cmd->new_sha1)) - cmd->error_string = "deny deleting a hidden ref"; + cmd->error_string = xstrdup("deny deleting a hidden ref"); else - cmd->error_string = "deny updating a hidden ref"; + cmd->error_string = xstrdup("deny updating a hidden ref"); } } @@ -786,8 +799,11 @@ static void execute_commands(struct command *commands, struct iterate_data data; if (unpacker_error) { - for (cmd = commands; cmd; cmd = cmd->next) - cmd->error_string = "unpacker error"; + for (cmd = commands; cmd; cmd = cmd->next) { + if (cmd->error_string) /* can't happen */ + continue; + cmd->error_string = xstrdup("unpacker error"); + } return; } @@ -800,8 +816,9 @@ static void execute_commands(struct command *commands, if (run_receive_hook(commands, "pre-receive", 0)) { for (cmd = commands; cmd; cmd = cmd->next) { - if (!cmd->error_string) - cmd->error_string = "pre-receive hook declined"; + if (cmd->error_string) + continue; + cmd->error_string = xstrdup("pre-receive hook declined"); } return; } @@ -1079,7 +1096,8 @@ static void update_shallow_info(struct command *commands, if (is_null_sha1(cmd->new_sha1)) continue; if (ref_status[cmd->index]) { - cmd->error_string = "shallow update not allowed"; + free(cmd->error_string); + cmd->error_string = xstrdup("shallow update not allowed"); cmd->skip_update = 1; } } @@ -1120,6 +1138,17 @@ static int delete_only(struct command *commands) return 1; } +static void free_commands(struct command *commands) +{ + while (commands) { + struct command *next = commands->next; + + free(commands->error_string); + free(commands); + commands = next; + } +} + int cmd_receive_pack(int argc, const char **argv, const char *prefix) { int advertise_refs = 0; @@ -1210,6 +1239,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) if (auto_update_server_info) update_server_info(0); clear_shallow_info(&si); + free_commands(commands); } if (use_sideband) packet_flush(1); -- 2.0.0.599.g83ced0e -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html