commit_packed_refs(): use a staging file separate from the lockfile

We will want to be able to hold the lockfile for `packed-refs` even after we have activated the new values. So use a separate tempfile, `packed-refs.new`, as a place to stage the new contents of the `packed-refs` file. For now this is all done within `commit_packed_refs()`, but that will change shortly. Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Michael Haggerty committed Jun 23, 2017 at 09:01 UTC 42dfa7ecef22191b004862fb56074b408c94fc97
1 file changed +32 -8
refs/packed-backend.c
+32 -8
@@ -68,6 +68,13 @@ struct packed_ref_store {
68 * thus the enclosing `packed_ref_store`) must not be freed.
69 */
70 struct lock_file lock;
71 +
72 + /*
73 + * Temporary file used when rewriting new contents to the
74 + * "packed-refs" file. Note that this (and thus the enclosing
75 + * `packed_ref_store`) must not be freed.
76 + */
77 + struct tempfile tempfile;
78 };
79
80 struct ref_store *packed_ref_store_create(const char *path,
@@ -522,10 +529,16 @@ int lock_packed_refs(struct ref_store *ref_store, int flags)
529 timeout_configured = 1;
530 }
531
532 + /*
533 + * Note that we close the lockfile immediately because we
534 + * don't write new content to it, but rather to a separate
535 + * tempfile.
536 + */
537 if (hold_lock_file_for_update_timeout(
538 &refs->lock,
539 refs->path,
528 - flags, timeout_value) < 0)
540 + flags, timeout_value) < 0 ||
541 + close_lock_file(&refs->lock))
542 return -1;
543
544 /*
@@ -567,13 +580,23 @@ int commit_packed_refs(struct ref_store *ref_store, struct strbuf *err)
580 get_packed_ref_cache(refs);
581 int ok;
582 int ret = -1;
583 + struct strbuf sb = STRBUF_INIT;
584 FILE *out;
585 struct ref_iterator *iter;
586
587 if (!is_lock_file_locked(&refs->lock))
588 die("BUG: commit_packed_refs() called when unlocked");
589
576 - out = fdopen_lock_file(&refs->lock, "w");
590 + strbuf_addf(&sb, "%s.new", refs->path);
591 + if (create_tempfile(&refs->tempfile, sb.buf) < 0) {
592 + strbuf_addf(err, "unable to create file %s: %s",
593 + sb.buf, strerror(errno));
594 + strbuf_release(&sb);
595 + goto out;
596 + }
597 + strbuf_release(&sb);
598 +
599 + out = fdopen_tempfile(&refs->tempfile, "w");
600 if (!out) {
601 strbuf_addf(err, "unable to fdopen packed-refs tempfile: %s",
602 strerror(errno));
@@ -582,7 +605,7 @@ int commit_packed_refs(struct ref_store *ref_store, struct strbuf *err)
605
606 if (fprintf(out, "%s", PACKED_REFS_HEADER) < 0) {
607 strbuf_addf(err, "error writing to %s: %s",
585 - get_lock_file_path(&refs->lock), strerror(errno));
608 + get_tempfile_path(&refs->tempfile), strerror(errno));
609 goto error;
610 }
611
@@ -594,7 +617,7 @@ int commit_packed_refs(struct ref_store *ref_store, struct strbuf *err)
617 if (write_packed_entry(out, iter->refname, iter->oid->hash,
618 peel_error ? NULL : peeled.hash)) {
619 strbuf_addf(err, "error writing to %s: %s",
597 - get_lock_file_path(&refs->lock),
620 + get_tempfile_path(&refs->tempfile),
621 strerror(errno));
622 ref_iterator_abort(iter);
623 goto error;
@@ -602,13 +625,13 @@ int commit_packed_refs(struct ref_store *ref_store, struct strbuf *err)
625 }
626
627 if (ok != ITER_DONE) {
605 - strbuf_addf(err, "unable to write packed-refs file: "
628 + strbuf_addf(err, "unable to rewrite packed-refs file: "
629 "error iterating over old contents");
630 goto error;
631 }
632
610 - if (commit_lock_file(&refs->lock)) {
611 - strbuf_addf(err, "error overwriting %s: %s",
633 + if (rename_tempfile(&refs->tempfile, refs->path)) {
634 + strbuf_addf(err, "error replacing %s: %s",
635 refs->path, strerror(errno));
636 goto out;
637 }
@@ -617,9 +640,10 @@ int commit_packed_refs(struct ref_store *ref_store, struct strbuf *err)
640 goto out;
641
642 error:
620 - rollback_lock_file(&refs->lock);
643 + delete_tempfile(&refs->tempfile);
644
645 out:
646 + rollback_lock_file(&refs->lock);
647 release_packed_ref_cache(packed_ref_cache);
648 return ret;
649 }