git bundle create would leave an invalid, partially written bundle if an error occured during creation. Fix that using lock_file. Signed-off-by: Mark Levedahl <mdl123@xxxxxxxxxxx> --- struct lock_file is now static. *caller* closes the lock file before commiting / rolling back. builtin-bundle.c | 57 +++++++++++++++++++++++++++++++++++------------------ 1 files changed, 37 insertions(+), 20 deletions(-) diff --git a/builtin-bundle.c b/builtin-bundle.c index f4b4f03..e5a2859 100644 --- a/builtin-bundle.c +++ b/builtin-bundle.c @@ -186,10 +186,21 @@ static int list_heads(struct bundle_header *header, int argc, const char **argv) return list_refs(&header->references, argc, argv); } +/* create_bundle uses lock_file, delete if write fails */ +static inline void lwrite_or_die(int fd, const void *buf, size_t count, struct lock_file *lock) +{ + if (write_in_full(fd, buf, count) != count) { + close(fd); + rollback_lock_file(lock); + die("Unable to write bundle"); + } +} + static int create_bundle(struct bundle_header *header, const char *path, int argc, const char **argv) { int bundle_fd = -1; + static struct lock_file lock; const char **argv_boundary = xmalloc((argc + 4) * sizeof(const char *)); const char **argv_pack = xmalloc(5 * sizeof(const char *)); int i, ref_count = 0; @@ -198,17 +209,9 @@ static int create_bundle(struct bundle_header *header, const char *path, struct child_process rls; FILE *rls_fout; - /* - * NEEDSWORK: this should use something like lock-file - * to create temporary that is cleaned up upon error. - */ - bundle_fd = (!strcmp(path, "-") ? 1 : - open(path, O_CREAT | O_EXCL | O_WRONLY, 0666)); - if (bundle_fd < 0) - return error("Could not create '%s': %s", path, strerror(errno)); - /* write signature */ - write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature)); + bundle_fd = hold_lock_file_for_update(&lock, path, 1); + lwrite_or_die(bundle_fd, bundle_signature, strlen(bundle_signature), &lock); /* init revs to list objects for pack-objects later */ save_commit_buffer = 0; @@ -230,7 +233,7 @@ static int create_bundle(struct bundle_header *header, const char *path, while (fgets(buffer, sizeof(buffer), rls_fout)) { unsigned char sha1[20]; if (buffer[0] == '-') { - write_or_die(bundle_fd, buffer, strlen(buffer)); + lwrite_or_die(bundle_fd, buffer, strlen(buffer), &lock); if (!get_sha1_hex(buffer + 1, sha1)) { struct object *object = parse_object(sha1); object->flags |= UNINTERESTING; @@ -242,13 +245,19 @@ static int create_bundle(struct bundle_header *header, const char *path, } } fclose(rls_fout); - if (finish_command(&rls)) + if (finish_command(&rls)) { + close(bundle_fd); + rollback_lock_file(&lock); return error("rev-list died"); + } /* write references */ argc = setup_revisions(argc, argv, &revs, NULL); - if (argc > 1) + if (argc > 1) { + close(bundle_fd); + rollback_lock_file(&lock); return error("unrecognized argument: %s'", argv[1]); + } for (i = 0; i < revs.pending.nr; i++) { struct object_array_entry *e = revs.pending.objects + i; @@ -307,17 +316,20 @@ static int create_bundle(struct bundle_header *header, const char *path, } ref_count++; - write_or_die(bundle_fd, sha1_to_hex(e->item->sha1), 40); - write_or_die(bundle_fd, " ", 1); - write_or_die(bundle_fd, ref, strlen(ref)); - write_or_die(bundle_fd, "\n", 1); + lwrite_or_die(bundle_fd, sha1_to_hex(e->item->sha1), 40, &lock); + lwrite_or_die(bundle_fd, " ", 1, &lock); + lwrite_or_die(bundle_fd, ref, strlen(ref), &lock); + lwrite_or_die(bundle_fd, "\n", 1, &lock); free(ref); } - if (!ref_count) + if (!ref_count) { + close(bundle_fd); + rollback_lock_file(&lock); die ("Refusing to create empty bundle."); + } /* end header */ - write_or_die(bundle_fd, "\n", 1); + lwrite_or_die(bundle_fd, "\n", 1, &lock); /* write pack */ argv_pack[0] = "pack-objects"; @@ -339,8 +351,13 @@ static int create_bundle(struct bundle_header *header, const char *path, write(rls.in, sha1_to_hex(object->sha1), 40); write(rls.in, "\n", 1); } - if (finish_command(&rls)) + if (finish_command(&rls)) { + close(bundle_fd); + rollback_lock_file(&lock); return error ("pack-objects died"); + } + close(bundle_fd); + commit_lock_file(&lock); return 0; } -- 1.5.3.rc4.79.gb5c7e-dirty - 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