reftable/stack: fix stale lock when dying

When starting a transaction via `reftable_stack_init_addition()`, we create a lockfile for the reftable stack itself which we'll write the new list of tables to. But if we terminate abnormally e.g. via a call to `die()`, then we do not remove the lockfile. Subsequent executions of Git which try to modify references will thus fail with an out-of-date error. Fix this bug by registering the lock as a `struct tempfile`, which ensures automatic cleanup for us. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Dec 11, 2023 at 10:07 UTC 3054fbd93edb5f12c1a320dfb6abec139bdf9628
1 file changed +15 -32
reftable/stack.c
+15 -32
@@ -17,6 +17,8 @@ https://developers.google.com/open-source/licenses/bsd
17 #include "reftable-merged.h"
18 #include "writer.h"
19
20 +#include "tempfile.h"
21 +
22 static int stack_try_add(struct reftable_stack *st,
23 int (*write_table)(struct reftable_writer *wr,
24 void *arg),
@@ -440,8 +442,7 @@ static void format_name(struct strbuf *dest, uint64_t min, uint64_t max)
442 }
443
444 struct reftable_addition {
443 - int lock_file_fd;
444 - struct strbuf lock_file_name;
445 + struct tempfile *lock_file;
446 struct reftable_stack *stack;
447
448 char **new_tables;
@@ -449,24 +450,19 @@ struct reftable_addition {
450 uint64_t next_update_index;
451 };
452
452 -#define REFTABLE_ADDITION_INIT \
453 - { \
454 - .lock_file_name = STRBUF_INIT \
455 - }
453 +#define REFTABLE_ADDITION_INIT {0}
454
455 static int reftable_stack_init_addition(struct reftable_addition *add,
456 struct reftable_stack *st)
457 {
458 + struct strbuf lock_file_name = STRBUF_INIT;
459 int err = 0;
460 add->stack = st;
461
463 - strbuf_reset(&add->lock_file_name);
464 - strbuf_addstr(&add->lock_file_name, st->list_file);
465 - strbuf_addstr(&add->lock_file_name, ".lock");
462 + strbuf_addf(&lock_file_name, "%s.lock", st->list_file);
463
467 - add->lock_file_fd = open(add->lock_file_name.buf,
468 - O_EXCL | O_CREAT | O_WRONLY, 0666);
469 - if (add->lock_file_fd < 0) {
464 + add->lock_file = create_tempfile(lock_file_name.buf);
465 + if (!add->lock_file) {
466 if (errno == EEXIST) {
467 err = REFTABLE_LOCK_ERROR;
468 } else {
@@ -475,7 +471,7 @@ static int reftable_stack_init_addition(struct reftable_addition *add,
471 goto done;
472 }
473 if (st->config.default_permissions) {
478 - if (chmod(add->lock_file_name.buf, st->config.default_permissions) < 0) {
474 + if (chmod(add->lock_file->filename.buf, st->config.default_permissions) < 0) {
475 err = REFTABLE_IO_ERROR;
476 goto done;
477 }
@@ -495,6 +491,7 @@ done:
491 if (err) {
492 reftable_addition_close(add);
493 }
494 + strbuf_release(&lock_file_name);
495 return err;
496 }
497
@@ -512,15 +509,7 @@ static void reftable_addition_close(struct reftable_addition *add)
509 add->new_tables = NULL;
510 add->new_tables_len = 0;
511
515 - if (add->lock_file_fd > 0) {
516 - close(add->lock_file_fd);
517 - add->lock_file_fd = 0;
518 - }
519 - if (add->lock_file_name.len > 0) {
520 - unlink(add->lock_file_name.buf);
521 - strbuf_release(&add->lock_file_name);
522 - }
523 -
512 + delete_tempfile(&add->lock_file);
513 strbuf_release(&nm);
514 }
515
@@ -536,8 +525,10 @@ void reftable_addition_destroy(struct reftable_addition *add)
525 int reftable_addition_commit(struct reftable_addition *add)
526 {
527 struct strbuf table_list = STRBUF_INIT;
528 + int lock_file_fd = get_tempfile_fd(add->lock_file);
529 int i = 0;
530 int err = 0;
531 +
532 if (add->new_tables_len == 0)
533 goto done;
534
@@ -550,28 +541,20 @@ int reftable_addition_commit(struct reftable_addition *add)
541 strbuf_addstr(&table_list, "\n");
542 }
543
553 - err = write_in_full(add->lock_file_fd, table_list.buf, table_list.len);
544 + err = write_in_full(lock_file_fd, table_list.buf, table_list.len);
545 strbuf_release(&table_list);
546 if (err < 0) {
547 err = REFTABLE_IO_ERROR;
548 goto done;
549 }
550
560 - err = close(add->lock_file_fd);
561 - add->lock_file_fd = 0;
562 - if (err < 0) {
563 - err = REFTABLE_IO_ERROR;
564 - goto done;
565 - }
566 -
567 - err = rename(add->lock_file_name.buf, add->stack->list_file);
551 + err = rename_tempfile(&add->lock_file, add->stack->list_file);
552 if (err < 0) {
553 err = REFTABLE_IO_ERROR;
554 goto done;
555 }
556
557 /* success, no more state to clean up. */
574 - strbuf_release(&add->lock_file_name);
558 for (i = 0; i < add->new_tables_len; i++) {
559 reftable_free(add->new_tables[i]);
560 }