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
}