]> git.ipfire.org Git - thirdparty/git.git/blame - reftable/stack.c
Merge branch 'vd/fsck-submodule-url-test'
[thirdparty/git.git] / reftable / stack.c
CommitLineData
e48d4272
HWN
1/*
2Copyright 2020 Google LLC
3
4Use of this source code is governed by a BSD-style
5license that can be found in the LICENSE file or at
6https://developers.google.com/open-source/licenses/bsd
7*/
8
9#include "stack.h"
10
11#include "system.h"
12#include "merged.h"
13#include "reader.h"
14#include "refname.h"
15#include "reftable-error.h"
16#include "reftable-record.h"
17#include "reftable-merged.h"
18#include "writer.h"
19
3054fbd9
PS
20#include "tempfile.h"
21
e48d4272
HWN
22static int stack_try_add(struct reftable_stack *st,
23 int (*write_table)(struct reftable_writer *wr,
24 void *arg),
25 void *arg);
26static int stack_write_compact(struct reftable_stack *st,
27 struct reftable_writer *wr, int first, int last,
28 struct reftable_log_expiry_config *config);
29static int stack_check_addition(struct reftable_stack *st,
30 const char *new_tab_name);
31static void reftable_addition_close(struct reftable_addition *add);
32static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
33 int reuse_open);
34
35static void stack_filename(struct strbuf *dest, struct reftable_stack *st,
36 const char *name)
37{
38 strbuf_reset(dest);
39 strbuf_addstr(dest, st->reftable_dir);
40 strbuf_addstr(dest, "/");
41 strbuf_addstr(dest, name);
42}
43
44static ssize_t reftable_fd_write(void *arg, const void *data, size_t sz)
45{
46 int *fdp = (int *)arg;
85a8c899 47 return write_in_full(*fdp, data, sz);
e48d4272
HWN
48}
49
50int reftable_new_stack(struct reftable_stack **dest, const char *dir,
51 struct reftable_write_options config)
52{
53 struct reftable_stack *p =
54 reftable_calloc(sizeof(struct reftable_stack));
55 struct strbuf list_file_name = STRBUF_INIT;
56 int err = 0;
57
58 if (config.hash_id == 0) {
59 config.hash_id = GIT_SHA1_FORMAT_ID;
60 }
61
62 *dest = NULL;
63
64 strbuf_reset(&list_file_name);
65 strbuf_addstr(&list_file_name, dir);
66 strbuf_addstr(&list_file_name, "/tables.list");
67
68 p->list_file = strbuf_detach(&list_file_name, NULL);
69 p->reftable_dir = xstrdup(dir);
70 p->config = config;
71
72 err = reftable_stack_reload_maybe_reuse(p, 1);
73 if (err < 0) {
74 reftable_stack_destroy(p);
75 } else {
76 *dest = p;
77 }
78 return err;
79}
80
81static int fd_read_lines(int fd, char ***namesp)
82{
83 off_t size = lseek(fd, 0, SEEK_END);
84 char *buf = NULL;
85 int err = 0;
86 if (size < 0) {
87 err = REFTABLE_IO_ERROR;
88 goto done;
89 }
90 err = lseek(fd, 0, SEEK_SET);
91 if (err < 0) {
92 err = REFTABLE_IO_ERROR;
93 goto done;
94 }
95
96 buf = reftable_malloc(size + 1);
917a2b3c 97 if (read_in_full(fd, buf, size) != size) {
e48d4272
HWN
98 err = REFTABLE_IO_ERROR;
99 goto done;
100 }
101 buf[size] = 0;
102
103 parse_names(buf, size, namesp);
104
105done:
106 reftable_free(buf);
107 return err;
108}
109
110int read_lines(const char *filename, char ***namesp)
111{
112 int fd = open(filename, O_RDONLY);
113 int err = 0;
114 if (fd < 0) {
115 if (errno == ENOENT) {
116 *namesp = reftable_calloc(sizeof(char *));
117 return 0;
118 }
119
120 return REFTABLE_IO_ERROR;
121 }
122 err = fd_read_lines(fd, namesp);
123 close(fd);
124 return err;
125}
126
127struct reftable_merged_table *
128reftable_stack_merged_table(struct reftable_stack *st)
129{
130 return st->merged;
131}
132
133static int has_name(char **names, const char *name)
134{
135 while (*names) {
136 if (!strcmp(*names, name))
137 return 1;
138 names++;
139 }
140 return 0;
141}
142
143/* Close and free the stack */
144void reftable_stack_destroy(struct reftable_stack *st)
145{
146 char **names = NULL;
147 int err = 0;
148 if (st->merged) {
149 reftable_merged_table_free(st->merged);
150 st->merged = NULL;
151 }
152
153 err = read_lines(st->list_file, &names);
154 if (err < 0) {
155 FREE_AND_NULL(names);
156 }
157
158 if (st->readers) {
159 int i = 0;
160 struct strbuf filename = STRBUF_INIT;
161 for (i = 0; i < st->readers_len; i++) {
162 const char *name = reader_name(st->readers[i]);
163 strbuf_reset(&filename);
164 if (names && !has_name(names, name)) {
165 stack_filename(&filename, st, name);
166 }
167 reftable_reader_free(st->readers[i]);
168
169 if (filename.len) {
170 /* On Windows, can only unlink after closing. */
171 unlink(filename.buf);
172 }
173 }
174 strbuf_release(&filename);
175 st->readers_len = 0;
176 FREE_AND_NULL(st->readers);
177 }
178 FREE_AND_NULL(st->list_file);
179 FREE_AND_NULL(st->reftable_dir);
180 reftable_free(st);
181 free_names(names);
182}
183
184static struct reftable_reader **stack_copy_readers(struct reftable_stack *st,
185 int cur_len)
186{
187 struct reftable_reader **cur =
188 reftable_calloc(sizeof(struct reftable_reader *) * cur_len);
189 int i = 0;
190 for (i = 0; i < cur_len; i++) {
191 cur[i] = st->readers[i];
192 }
193 return cur;
194}
195
196static int reftable_stack_reload_once(struct reftable_stack *st, char **names,
197 int reuse_open)
198{
199 int cur_len = !st->merged ? 0 : st->merged->stack_len;
200 struct reftable_reader **cur = stack_copy_readers(st, cur_len);
201 int err = 0;
202 int names_len = names_length(names);
203 struct reftable_reader **new_readers =
204 reftable_calloc(sizeof(struct reftable_reader *) * names_len);
205 struct reftable_table *new_tables =
206 reftable_calloc(sizeof(struct reftable_table) * names_len);
207 int new_readers_len = 0;
208 struct reftable_merged_table *new_merged = NULL;
d779996a 209 struct strbuf table_path = STRBUF_INIT;
e48d4272
HWN
210 int i;
211
212 while (*names) {
213 struct reftable_reader *rd = NULL;
214 char *name = *names++;
215
216 /* this is linear; we assume compaction keeps the number of
217 tables under control so this is not quadratic. */
218 int j = 0;
219 for (j = 0; reuse_open && j < cur_len; j++) {
220 if (cur[j] && 0 == strcmp(cur[j]->name, name)) {
221 rd = cur[j];
222 cur[j] = NULL;
223 break;
224 }
225 }
226
227 if (!rd) {
228 struct reftable_block_source src = { NULL };
e48d4272
HWN
229 stack_filename(&table_path, st, name);
230
231 err = reftable_block_source_from_file(&src,
232 table_path.buf);
e48d4272
HWN
233 if (err < 0)
234 goto done;
235
236 err = reftable_new_reader(&rd, &src, name);
237 if (err < 0)
238 goto done;
239 }
240
241 new_readers[new_readers_len] = rd;
242 reftable_table_from_reader(&new_tables[new_readers_len], rd);
243 new_readers_len++;
244 }
245
246 /* success! */
247 err = reftable_new_merged_table(&new_merged, new_tables,
248 new_readers_len, st->config.hash_id);
249 if (err < 0)
250 goto done;
251
252 new_tables = NULL;
253 st->readers_len = new_readers_len;
254 if (st->merged) {
255 merged_table_release(st->merged);
256 reftable_merged_table_free(st->merged);
257 }
258 if (st->readers) {
259 reftable_free(st->readers);
260 }
261 st->readers = new_readers;
262 new_readers = NULL;
263 new_readers_len = 0;
264
265 new_merged->suppress_deletions = 1;
266 st->merged = new_merged;
267 for (i = 0; i < cur_len; i++) {
268 if (cur[i]) {
269 const char *name = reader_name(cur[i]);
d779996a 270 stack_filename(&table_path, st, name);
e48d4272
HWN
271
272 reader_close(cur[i]);
273 reftable_reader_free(cur[i]);
274
275 /* On Windows, can only unlink after closing. */
d779996a 276 unlink(table_path.buf);
e48d4272
HWN
277 }
278 }
279
280done:
281 for (i = 0; i < new_readers_len; i++) {
282 reader_close(new_readers[i]);
283 reftable_reader_free(new_readers[i]);
284 }
285 reftable_free(new_readers);
286 reftable_free(new_tables);
287 reftable_free(cur);
d779996a 288 strbuf_release(&table_path);
e48d4272
HWN
289 return err;
290}
291
292/* return negative if a before b. */
293static int tv_cmp(struct timeval *a, struct timeval *b)
294{
295 time_t diff = a->tv_sec - b->tv_sec;
296 int udiff = a->tv_usec - b->tv_usec;
297
298 if (diff != 0)
299 return diff;
300
301 return udiff;
302}
303
304static int reftable_stack_reload_maybe_reuse(struct reftable_stack *st,
305 int reuse_open)
306{
307 struct timeval deadline = { 0 };
308 int err = gettimeofday(&deadline, NULL);
309 int64_t delay = 0;
310 int tries = 0;
311 if (err < 0)
312 return err;
313
314 deadline.tv_sec += 3;
315 while (1) {
316 char **names = NULL;
317 char **names_after = NULL;
318 struct timeval now = { 0 };
319 int err = gettimeofday(&now, NULL);
320 int err2 = 0;
321 if (err < 0) {
322 return err;
323 }
324
325 /* Only look at deadlines after the first few times. This
326 simplifies debugging in GDB */
327 tries++;
328 if (tries > 3 && tv_cmp(&now, &deadline) >= 0) {
329 break;
330 }
331
332 err = read_lines(st->list_file, &names);
333 if (err < 0) {
334 free_names(names);
335 return err;
336 }
337 err = reftable_stack_reload_once(st, names, reuse_open);
338 if (err == 0) {
339 free_names(names);
340 break;
341 }
342 if (err != REFTABLE_NOT_EXIST_ERROR) {
343 free_names(names);
344 return err;
345 }
346
347 /* err == REFTABLE_NOT_EXIST_ERROR can be caused by a concurrent
348 writer. Check if there was one by checking if the name list
349 changed.
350 */
351 err2 = read_lines(st->list_file, &names_after);
352 if (err2 < 0) {
353 free_names(names);
354 return err2;
355 }
356
357 if (names_equal(names_after, names)) {
358 free_names(names);
359 free_names(names_after);
360 return err;
361 }
362 free_names(names);
363 free_names(names_after);
364
365 delay = delay + (delay * rand()) / RAND_MAX + 1;
366 sleep_millisec(delay);
367 }
368
369 return 0;
370}
371
372/* -1 = error
373 0 = up to date
374 1 = changed. */
375static int stack_uptodate(struct reftable_stack *st)
376{
377 char **names = NULL;
378 int err = read_lines(st->list_file, &names);
379 int i = 0;
380 if (err < 0)
381 return err;
382
383 for (i = 0; i < st->readers_len; i++) {
384 if (!names[i]) {
385 err = 1;
386 goto done;
387 }
388
389 if (strcmp(st->readers[i]->name, names[i])) {
390 err = 1;
391 goto done;
392 }
393 }
394
395 if (names[st->merged->stack_len]) {
396 err = 1;
397 goto done;
398 }
399
400done:
401 free_names(names);
402 return err;
403}
404
405int reftable_stack_reload(struct reftable_stack *st)
406{
407 int err = stack_uptodate(st);
408 if (err > 0)
409 return reftable_stack_reload_maybe_reuse(st, 1);
410 return err;
411}
412
413int reftable_stack_add(struct reftable_stack *st,
414 int (*write)(struct reftable_writer *wr, void *arg),
415 void *arg)
416{
417 int err = stack_try_add(st, write, arg);
418 if (err < 0) {
419 if (err == REFTABLE_LOCK_ERROR) {
420 /* Ignore error return, we want to propagate
421 REFTABLE_LOCK_ERROR.
422 */
423 reftable_stack_reload(st);
424 }
425 return err;
426 }
427
e48d4272
HWN
428 return 0;
429}
430
431static void format_name(struct strbuf *dest, uint64_t min, uint64_t max)
432{
433 char buf[100];
9abda981 434 uint32_t rnd = (uint32_t)git_rand();
e48d4272
HWN
435 snprintf(buf, sizeof(buf), "0x%012" PRIx64 "-0x%012" PRIx64 "-%08x",
436 min, max, rnd);
437 strbuf_reset(dest);
438 strbuf_addstr(dest, buf);
439}
440
441struct reftable_addition {
3054fbd9 442 struct tempfile *lock_file;
e48d4272
HWN
443 struct reftable_stack *stack;
444
445 char **new_tables;
446 int new_tables_len;
447 uint64_t next_update_index;
448};
449
3054fbd9 450#define REFTABLE_ADDITION_INIT {0}
e48d4272
HWN
451
452static int reftable_stack_init_addition(struct reftable_addition *add,
453 struct reftable_stack *st)
454{
3054fbd9 455 struct strbuf lock_file_name = STRBUF_INIT;
e48d4272
HWN
456 int err = 0;
457 add->stack = st;
458
3054fbd9 459 strbuf_addf(&lock_file_name, "%s.lock", st->list_file);
e48d4272 460
3054fbd9
PS
461 add->lock_file = create_tempfile(lock_file_name.buf);
462 if (!add->lock_file) {
e48d4272
HWN
463 if (errno == EEXIST) {
464 err = REFTABLE_LOCK_ERROR;
465 } else {
466 err = REFTABLE_IO_ERROR;
467 }
468 goto done;
469 }
cd1799de 470 if (st->config.default_permissions) {
3054fbd9 471 if (chmod(add->lock_file->filename.buf, st->config.default_permissions) < 0) {
cd1799de
HWN
472 err = REFTABLE_IO_ERROR;
473 goto done;
474 }
475 }
476
e48d4272
HWN
477 err = stack_uptodate(st);
478 if (err < 0)
479 goto done;
480
481 if (err > 1) {
482 err = REFTABLE_LOCK_ERROR;
483 goto done;
484 }
485
486 add->next_update_index = reftable_stack_next_update_index(st);
487done:
488 if (err) {
489 reftable_addition_close(add);
490 }
3054fbd9 491 strbuf_release(&lock_file_name);
e48d4272
HWN
492 return err;
493}
494
495static void reftable_addition_close(struct reftable_addition *add)
496{
497 int i = 0;
498 struct strbuf nm = STRBUF_INIT;
499 for (i = 0; i < add->new_tables_len; i++) {
500 stack_filename(&nm, add->stack, add->new_tables[i]);
501 unlink(nm.buf);
502 reftable_free(add->new_tables[i]);
503 add->new_tables[i] = NULL;
504 }
505 reftable_free(add->new_tables);
506 add->new_tables = NULL;
507 add->new_tables_len = 0;
508
3054fbd9 509 delete_tempfile(&add->lock_file);
e48d4272
HWN
510 strbuf_release(&nm);
511}
512
513void reftable_addition_destroy(struct reftable_addition *add)
514{
515 if (!add) {
516 return;
517 }
518 reftable_addition_close(add);
519 reftable_free(add);
520}
521
522int reftable_addition_commit(struct reftable_addition *add)
523{
524 struct strbuf table_list = STRBUF_INIT;
3054fbd9 525 int lock_file_fd = get_tempfile_fd(add->lock_file);
e48d4272
HWN
526 int i = 0;
527 int err = 0;
3054fbd9 528
e48d4272
HWN
529 if (add->new_tables_len == 0)
530 goto done;
531
532 for (i = 0; i < add->stack->merged->stack_len; i++) {
533 strbuf_addstr(&table_list, add->stack->readers[i]->name);
534 strbuf_addstr(&table_list, "\n");
535 }
536 for (i = 0; i < add->new_tables_len; i++) {
537 strbuf_addstr(&table_list, add->new_tables[i]);
538 strbuf_addstr(&table_list, "\n");
539 }
540
3054fbd9 541 err = write_in_full(lock_file_fd, table_list.buf, table_list.len);
e48d4272
HWN
542 strbuf_release(&table_list);
543 if (err < 0) {
544 err = REFTABLE_IO_ERROR;
545 goto done;
546 }
547
3054fbd9 548 err = rename_tempfile(&add->lock_file, add->stack->list_file);
e48d4272
HWN
549 if (err < 0) {
550 err = REFTABLE_IO_ERROR;
551 goto done;
552 }
553
554 /* success, no more state to clean up. */
e48d4272
HWN
555 for (i = 0; i < add->new_tables_len; i++) {
556 reftable_free(add->new_tables[i]);
557 }
558 reftable_free(add->new_tables);
559 add->new_tables = NULL;
560 add->new_tables_len = 0;
561
562 err = reftable_stack_reload(add->stack);
5c086453
PS
563 if (err)
564 goto done;
565
566 if (!add->stack->disable_auto_compact)
567 err = reftable_stack_auto_compact(add->stack);
568
e48d4272
HWN
569done:
570 reftable_addition_close(add);
571 return err;
572}
573
574int reftable_stack_new_addition(struct reftable_addition **dest,
575 struct reftable_stack *st)
576{
577 int err = 0;
578 struct reftable_addition empty = REFTABLE_ADDITION_INIT;
579 *dest = reftable_calloc(sizeof(**dest));
580 **dest = empty;
581 err = reftable_stack_init_addition(*dest, st);
582 if (err) {
583 reftable_free(*dest);
584 *dest = NULL;
585 }
586 return err;
587}
588
589static int stack_try_add(struct reftable_stack *st,
590 int (*write_table)(struct reftable_writer *wr,
591 void *arg),
592 void *arg)
593{
594 struct reftable_addition add = REFTABLE_ADDITION_INIT;
595 int err = reftable_stack_init_addition(&add, st);
596 if (err < 0)
597 goto done;
598 if (err > 0) {
599 err = REFTABLE_LOCK_ERROR;
600 goto done;
601 }
602
603 err = reftable_addition_add(&add, write_table, arg);
604 if (err < 0)
605 goto done;
606
607 err = reftable_addition_commit(&add);
608done:
609 reftable_addition_close(&add);
610 return err;
611}
612
613int reftable_addition_add(struct reftable_addition *add,
614 int (*write_table)(struct reftable_writer *wr,
615 void *arg),
616 void *arg)
617{
618 struct strbuf temp_tab_file_name = STRBUF_INIT;
619 struct strbuf tab_file_name = STRBUF_INIT;
620 struct strbuf next_name = STRBUF_INIT;
621 struct reftable_writer *wr = NULL;
622 int err = 0;
623 int tab_fd = 0;
624
625 strbuf_reset(&next_name);
626 format_name(&next_name, add->next_update_index, add->next_update_index);
627
628 stack_filename(&temp_tab_file_name, add->stack, next_name.buf);
629 strbuf_addstr(&temp_tab_file_name, ".temp.XXXXXX");
630
631 tab_fd = mkstemp(temp_tab_file_name.buf);
632 if (tab_fd < 0) {
633 err = REFTABLE_IO_ERROR;
634 goto done;
635 }
cd1799de
HWN
636 if (add->stack->config.default_permissions) {
637 if (chmod(temp_tab_file_name.buf, add->stack->config.default_permissions)) {
638 err = REFTABLE_IO_ERROR;
639 goto done;
640 }
641 }
e48d4272
HWN
642 wr = reftable_new_writer(reftable_fd_write, &tab_fd,
643 &add->stack->config);
644 err = write_table(wr, arg);
645 if (err < 0)
646 goto done;
647
648 err = reftable_writer_close(wr);
649 if (err == REFTABLE_EMPTY_TABLE_ERROR) {
650 err = 0;
651 goto done;
652 }
653 if (err < 0)
654 goto done;
655
656 err = close(tab_fd);
657 tab_fd = 0;
658 if (err < 0) {
659 err = REFTABLE_IO_ERROR;
660 goto done;
661 }
662
663 err = stack_check_addition(add->stack, temp_tab_file_name.buf);
664 if (err < 0)
665 goto done;
666
667 if (wr->min_update_index < add->next_update_index) {
668 err = REFTABLE_API_ERROR;
669 goto done;
670 }
671
672 format_name(&next_name, wr->min_update_index, wr->max_update_index);
673 strbuf_addstr(&next_name, ".ref");
674
675 stack_filename(&tab_file_name, add->stack, next_name.buf);
676
677 /*
678 On windows, this relies on rand() picking a unique destination name.
679 Maybe we should do retry loop as well?
680 */
681 err = rename(temp_tab_file_name.buf, tab_file_name.buf);
682 if (err < 0) {
683 err = REFTABLE_IO_ERROR;
684 goto done;
685 }
686
687 add->new_tables = reftable_realloc(add->new_tables,
688 sizeof(*add->new_tables) *
689 (add->new_tables_len + 1));
690 add->new_tables[add->new_tables_len] = strbuf_detach(&next_name, NULL);
691 add->new_tables_len++;
692done:
693 if (tab_fd > 0) {
694 close(tab_fd);
695 tab_fd = 0;
696 }
697 if (temp_tab_file_name.len > 0) {
698 unlink(temp_tab_file_name.buf);
699 }
700
701 strbuf_release(&temp_tab_file_name);
702 strbuf_release(&tab_file_name);
703 strbuf_release(&next_name);
704 reftable_writer_free(wr);
705 return err;
706}
707
708uint64_t reftable_stack_next_update_index(struct reftable_stack *st)
709{
710 int sz = st->merged->stack_len;
711 if (sz > 0)
712 return reftable_reader_max_update_index(st->readers[sz - 1]) +
713 1;
714 return 1;
715}
716
717static int stack_compact_locked(struct reftable_stack *st, int first, int last,
718 struct strbuf *temp_tab,
719 struct reftable_log_expiry_config *config)
720{
721 struct strbuf next_name = STRBUF_INIT;
722 int tab_fd = -1;
723 struct reftable_writer *wr = NULL;
724 int err = 0;
725
726 format_name(&next_name,
727 reftable_reader_min_update_index(st->readers[first]),
728 reftable_reader_max_update_index(st->readers[last]));
729
730 stack_filename(temp_tab, st, next_name.buf);
731 strbuf_addstr(temp_tab, ".temp.XXXXXX");
732
733 tab_fd = mkstemp(temp_tab->buf);
734 wr = reftable_new_writer(reftable_fd_write, &tab_fd, &st->config);
735
736 err = stack_write_compact(st, wr, first, last, config);
737 if (err < 0)
738 goto done;
739 err = reftable_writer_close(wr);
740 if (err < 0)
741 goto done;
742
743 err = close(tab_fd);
744 tab_fd = 0;
745
746done:
747 reftable_writer_free(wr);
748 if (tab_fd > 0) {
749 close(tab_fd);
750 tab_fd = 0;
751 }
752 if (err != 0 && temp_tab->len > 0) {
753 unlink(temp_tab->buf);
754 strbuf_release(temp_tab);
755 }
756 strbuf_release(&next_name);
757 return err;
758}
759
760static int stack_write_compact(struct reftable_stack *st,
761 struct reftable_writer *wr, int first, int last,
762 struct reftable_log_expiry_config *config)
763{
764 int subtabs_len = last - first + 1;
765 struct reftable_table *subtabs = reftable_calloc(
766 sizeof(struct reftable_table) * (last - first + 1));
767 struct reftable_merged_table *mt = NULL;
768 int err = 0;
769 struct reftable_iterator it = { NULL };
770 struct reftable_ref_record ref = { NULL };
771 struct reftable_log_record log = { NULL };
772
773 uint64_t entries = 0;
774
775 int i = 0, j = 0;
776 for (i = first, j = 0; i <= last; i++) {
777 struct reftable_reader *t = st->readers[i];
778 reftable_table_from_reader(&subtabs[j++], t);
779 st->stats.bytes += t->size;
780 }
781 reftable_writer_set_limits(wr, st->readers[first]->min_update_index,
782 st->readers[last]->max_update_index);
783
784 err = reftable_new_merged_table(&mt, subtabs, subtabs_len,
785 st->config.hash_id);
786 if (err < 0) {
787 reftable_free(subtabs);
788 goto done;
789 }
790
791 err = reftable_merged_table_seek_ref(mt, &it, "");
792 if (err < 0)
793 goto done;
794
795 while (1) {
796 err = reftable_iterator_next_ref(&it, &ref);
797 if (err > 0) {
798 err = 0;
799 break;
800 }
d26c2148
PS
801 if (err < 0)
802 goto done;
e48d4272
HWN
803
804 if (first == 0 && reftable_ref_record_is_deletion(&ref)) {
805 continue;
806 }
807
808 err = reftable_writer_add_ref(wr, &ref);
d26c2148
PS
809 if (err < 0)
810 goto done;
e48d4272
HWN
811 entries++;
812 }
813 reftable_iterator_destroy(&it);
814
815 err = reftable_merged_table_seek_log(mt, &it, "");
816 if (err < 0)
817 goto done;
818
819 while (1) {
820 err = reftable_iterator_next_log(&it, &log);
821 if (err > 0) {
822 err = 0;
823 break;
824 }
d26c2148
PS
825 if (err < 0)
826 goto done;
e48d4272
HWN
827 if (first == 0 && reftable_log_record_is_deletion(&log)) {
828 continue;
829 }
830
831 if (config && config->min_update_index > 0 &&
832 log.update_index < config->min_update_index) {
833 continue;
834 }
835
836 if (config && config->time > 0 &&
837 log.value.update.time < config->time) {
838 continue;
839 }
840
841 err = reftable_writer_add_log(wr, &log);
d26c2148
PS
842 if (err < 0)
843 goto done;
e48d4272
HWN
844 entries++;
845 }
846
847done:
848 reftable_iterator_destroy(&it);
849 if (mt) {
850 merged_table_release(mt);
851 reftable_merged_table_free(mt);
852 }
853 reftable_ref_record_release(&ref);
854 reftable_log_record_release(&log);
855 st->stats.entries_written += entries;
856 return err;
857}
858
859/* < 0: error. 0 == OK, > 0 attempt failed; could retry. */
860static int stack_compact_range(struct reftable_stack *st, int first, int last,
861 struct reftable_log_expiry_config *expiry)
862{
863 struct strbuf temp_tab_file_name = STRBUF_INIT;
864 struct strbuf new_table_name = STRBUF_INIT;
865 struct strbuf lock_file_name = STRBUF_INIT;
866 struct strbuf ref_list_contents = STRBUF_INIT;
867 struct strbuf new_table_path = STRBUF_INIT;
868 int err = 0;
869 int have_lock = 0;
b20aab50 870 int lock_file_fd = -1;
e48d4272
HWN
871 int compact_count = last - first + 1;
872 char **listp = NULL;
873 char **delete_on_success =
874 reftable_calloc(sizeof(char *) * (compact_count + 1));
875 char **subtable_locks =
876 reftable_calloc(sizeof(char *) * (compact_count + 1));
877 int i = 0;
878 int j = 0;
879 int is_empty_table = 0;
880
881 if (first > last || (!expiry && first == last)) {
882 err = 0;
883 goto done;
884 }
885
886 st->stats.attempts++;
887
888 strbuf_reset(&lock_file_name);
889 strbuf_addstr(&lock_file_name, st->list_file);
890 strbuf_addstr(&lock_file_name, ".lock");
891
892 lock_file_fd =
cd1799de 893 open(lock_file_name.buf, O_EXCL | O_CREAT | O_WRONLY, 0666);
e48d4272
HWN
894 if (lock_file_fd < 0) {
895 if (errno == EEXIST) {
896 err = 1;
897 } else {
898 err = REFTABLE_IO_ERROR;
899 }
900 goto done;
901 }
902 /* Don't want to write to the lock for now. */
903 close(lock_file_fd);
b20aab50 904 lock_file_fd = -1;
e48d4272
HWN
905
906 have_lock = 1;
907 err = stack_uptodate(st);
908 if (err != 0)
909 goto done;
910
911 for (i = first, j = 0; i <= last; i++) {
912 struct strbuf subtab_file_name = STRBUF_INIT;
913 struct strbuf subtab_lock = STRBUF_INIT;
914 int sublock_file_fd = -1;
915
916 stack_filename(&subtab_file_name, st,
917 reader_name(st->readers[i]));
918
919 strbuf_reset(&subtab_lock);
920 strbuf_addbuf(&subtab_lock, &subtab_file_name);
921 strbuf_addstr(&subtab_lock, ".lock");
922
923 sublock_file_fd = open(subtab_lock.buf,
cd1799de
HWN
924 O_EXCL | O_CREAT | O_WRONLY, 0666);
925 if (sublock_file_fd >= 0) {
e48d4272
HWN
926 close(sublock_file_fd);
927 } else if (sublock_file_fd < 0) {
928 if (errno == EEXIST) {
929 err = 1;
930 } else {
931 err = REFTABLE_IO_ERROR;
932 }
933 }
934
935 subtable_locks[j] = subtab_lock.buf;
936 delete_on_success[j] = subtab_file_name.buf;
937 j++;
938
939 if (err != 0)
940 goto done;
941 }
942
943 err = unlink(lock_file_name.buf);
944 if (err < 0)
945 goto done;
946 have_lock = 0;
947
948 err = stack_compact_locked(st, first, last, &temp_tab_file_name,
949 expiry);
950 /* Compaction + tombstones can create an empty table out of non-empty
951 * tables. */
952 is_empty_table = (err == REFTABLE_EMPTY_TABLE_ERROR);
953 if (is_empty_table) {
954 err = 0;
955 }
956 if (err < 0)
957 goto done;
958
959 lock_file_fd =
cd1799de 960 open(lock_file_name.buf, O_EXCL | O_CREAT | O_WRONLY, 0666);
e48d4272
HWN
961 if (lock_file_fd < 0) {
962 if (errno == EEXIST) {
963 err = 1;
964 } else {
965 err = REFTABLE_IO_ERROR;
966 }
967 goto done;
968 }
969 have_lock = 1;
cd1799de
HWN
970 if (st->config.default_permissions) {
971 if (chmod(lock_file_name.buf, st->config.default_permissions) < 0) {
972 err = REFTABLE_IO_ERROR;
973 goto done;
974 }
975 }
e48d4272
HWN
976
977 format_name(&new_table_name, st->readers[first]->min_update_index,
978 st->readers[last]->max_update_index);
979 strbuf_addstr(&new_table_name, ".ref");
980
981 stack_filename(&new_table_path, st, new_table_name.buf);
982
983 if (!is_empty_table) {
984 /* retry? */
985 err = rename(temp_tab_file_name.buf, new_table_path.buf);
986 if (err < 0) {
987 err = REFTABLE_IO_ERROR;
988 goto done;
989 }
990 }
991
992 for (i = 0; i < first; i++) {
993 strbuf_addstr(&ref_list_contents, st->readers[i]->name);
994 strbuf_addstr(&ref_list_contents, "\n");
995 }
996 if (!is_empty_table) {
997 strbuf_addbuf(&ref_list_contents, &new_table_name);
998 strbuf_addstr(&ref_list_contents, "\n");
999 }
1000 for (i = last + 1; i < st->merged->stack_len; i++) {
1001 strbuf_addstr(&ref_list_contents, st->readers[i]->name);
1002 strbuf_addstr(&ref_list_contents, "\n");
1003 }
1004
85a8c899 1005 err = write_in_full(lock_file_fd, ref_list_contents.buf, ref_list_contents.len);
e48d4272
HWN
1006 if (err < 0) {
1007 err = REFTABLE_IO_ERROR;
1008 unlink(new_table_path.buf);
1009 goto done;
1010 }
1011 err = close(lock_file_fd);
b20aab50 1012 lock_file_fd = -1;
e48d4272
HWN
1013 if (err < 0) {
1014 err = REFTABLE_IO_ERROR;
1015 unlink(new_table_path.buf);
1016 goto done;
1017 }
1018
1019 err = rename(lock_file_name.buf, st->list_file);
1020 if (err < 0) {
1021 err = REFTABLE_IO_ERROR;
1022 unlink(new_table_path.buf);
1023 goto done;
1024 }
1025 have_lock = 0;
1026
1027 /* Reload the stack before deleting. On windows, we can only delete the
1028 files after we closed them.
1029 */
1030 err = reftable_stack_reload_maybe_reuse(st, first < last);
1031
1032 listp = delete_on_success;
1033 while (*listp) {
1034 if (strcmp(*listp, new_table_path.buf)) {
1035 unlink(*listp);
1036 }
1037 listp++;
1038 }
1039
1040done:
1041 free_names(delete_on_success);
1042
1043 listp = subtable_locks;
1044 while (*listp) {
1045 unlink(*listp);
1046 listp++;
1047 }
1048 free_names(subtable_locks);
b20aab50 1049 if (lock_file_fd >= 0) {
e48d4272 1050 close(lock_file_fd);
b20aab50 1051 lock_file_fd = -1;
e48d4272
HWN
1052 }
1053 if (have_lock) {
1054 unlink(lock_file_name.buf);
1055 }
1056 strbuf_release(&new_table_name);
1057 strbuf_release(&new_table_path);
1058 strbuf_release(&ref_list_contents);
1059 strbuf_release(&temp_tab_file_name);
1060 strbuf_release(&lock_file_name);
1061 return err;
1062}
1063
1064int reftable_stack_compact_all(struct reftable_stack *st,
1065 struct reftable_log_expiry_config *config)
1066{
1067 return stack_compact_range(st, 0, st->merged->stack_len - 1, config);
1068}
1069
1070static int stack_compact_range_stats(struct reftable_stack *st, int first,
1071 int last,
1072 struct reftable_log_expiry_config *config)
1073{
1074 int err = stack_compact_range(st, first, last, config);
1075 if (err > 0) {
1076 st->stats.failures++;
1077 }
1078 return err;
1079}
1080
1081static int segment_size(struct segment *s)
1082{
1083 return s->end - s->start;
1084}
1085
1086int fastlog2(uint64_t sz)
1087{
1088 int l = 0;
1089 if (sz == 0)
1090 return 0;
1091 for (; sz; sz /= 2) {
1092 l++;
1093 }
1094 return l - 1;
1095}
1096
1097struct segment *sizes_to_segments(int *seglen, uint64_t *sizes, int n)
1098{
1099 struct segment *segs = reftable_calloc(sizeof(struct segment) * n);
1100 int next = 0;
1101 struct segment cur = { 0 };
1102 int i = 0;
1103
1104 if (n == 0) {
1105 *seglen = 0;
1106 return segs;
1107 }
1108 for (i = 0; i < n; i++) {
1109 int log = fastlog2(sizes[i]);
1110 if (cur.log != log && cur.bytes > 0) {
1111 struct segment fresh = {
1112 .start = i,
1113 };
1114
1115 segs[next++] = cur;
1116 cur = fresh;
1117 }
1118
1119 cur.log = log;
1120 cur.end = i + 1;
1121 cur.bytes += sizes[i];
1122 }
1123 segs[next++] = cur;
1124 *seglen = next;
1125 return segs;
1126}
1127
1128struct segment suggest_compaction_segment(uint64_t *sizes, int n)
1129{
1130 int seglen = 0;
1131 struct segment *segs = sizes_to_segments(&seglen, sizes, n);
1132 struct segment min_seg = {
1133 .log = 64,
1134 };
1135 int i = 0;
1136 for (i = 0; i < seglen; i++) {
1137 if (segment_size(&segs[i]) == 1) {
1138 continue;
1139 }
1140
1141 if (segs[i].log < min_seg.log) {
1142 min_seg = segs[i];
1143 }
1144 }
1145
1146 while (min_seg.start > 0) {
1147 int prev = min_seg.start - 1;
1148 if (fastlog2(min_seg.bytes) < fastlog2(sizes[prev])) {
1149 break;
1150 }
1151
1152 min_seg.start = prev;
1153 min_seg.bytes += sizes[prev];
1154 }
1155
1156 reftable_free(segs);
1157 return min_seg;
1158}
1159
1160static uint64_t *stack_table_sizes_for_compaction(struct reftable_stack *st)
1161{
1162 uint64_t *sizes =
1163 reftable_calloc(sizeof(uint64_t) * st->merged->stack_len);
1164 int version = (st->config.hash_id == GIT_SHA1_FORMAT_ID) ? 1 : 2;
1165 int overhead = header_size(version) - 1;
1166 int i = 0;
1167 for (i = 0; i < st->merged->stack_len; i++) {
1168 sizes[i] = st->readers[i]->size - overhead;
1169 }
1170 return sizes;
1171}
1172
1173int reftable_stack_auto_compact(struct reftable_stack *st)
1174{
1175 uint64_t *sizes = stack_table_sizes_for_compaction(st);
1176 struct segment seg =
1177 suggest_compaction_segment(sizes, st->merged->stack_len);
1178 reftable_free(sizes);
1179 if (segment_size(&seg) > 0)
1180 return stack_compact_range_stats(st, seg.start, seg.end - 1,
1181 NULL);
1182
1183 return 0;
1184}
1185
1186struct reftable_compaction_stats *
1187reftable_stack_compaction_stats(struct reftable_stack *st)
1188{
1189 return &st->stats;
1190}
1191
1192int reftable_stack_read_ref(struct reftable_stack *st, const char *refname,
1193 struct reftable_ref_record *ref)
1194{
1195 struct reftable_table tab = { NULL };
1196 reftable_table_from_merged_table(&tab, reftable_stack_merged_table(st));
1197 return reftable_table_read_ref(&tab, refname, ref);
1198}
1199
1200int reftable_stack_read_log(struct reftable_stack *st, const char *refname,
1201 struct reftable_log_record *log)
1202{
1203 struct reftable_iterator it = { NULL };
1204 struct reftable_merged_table *mt = reftable_stack_merged_table(st);
1205 int err = reftable_merged_table_seek_log(mt, &it, refname);
1206 if (err)
1207 goto done;
1208
1209 err = reftable_iterator_next_log(&it, log);
1210 if (err)
1211 goto done;
1212
1213 if (strcmp(log->refname, refname) ||
1214 reftable_log_record_is_deletion(log)) {
1215 err = 1;
1216 goto done;
1217 }
1218
1219done:
1220 if (err) {
1221 reftable_log_record_release(log);
1222 }
1223 reftable_iterator_destroy(&it);
1224 return err;
1225}
1226
1227static int stack_check_addition(struct reftable_stack *st,
1228 const char *new_tab_name)
1229{
1230 int err = 0;
1231 struct reftable_block_source src = { NULL };
1232 struct reftable_reader *rd = NULL;
1233 struct reftable_table tab = { NULL };
1234 struct reftable_ref_record *refs = NULL;
1235 struct reftable_iterator it = { NULL };
1236 int cap = 0;
1237 int len = 0;
1238 int i = 0;
1239
1240 if (st->config.skip_name_check)
1241 return 0;
1242
1243 err = reftable_block_source_from_file(&src, new_tab_name);
1244 if (err < 0)
1245 goto done;
1246
1247 err = reftable_new_reader(&rd, &src, new_tab_name);
1248 if (err < 0)
1249 goto done;
1250
1251 err = reftable_reader_seek_ref(rd, &it, "");
1252 if (err > 0) {
1253 err = 0;
1254 goto done;
1255 }
1256 if (err < 0)
1257 goto done;
1258
1259 while (1) {
1260 struct reftable_ref_record ref = { NULL };
1261 err = reftable_iterator_next_ref(&it, &ref);
1262 if (err > 0) {
1263 break;
1264 }
1265 if (err < 0)
1266 goto done;
1267
1268 if (len >= cap) {
1269 cap = 2 * cap + 1;
1270 refs = reftable_realloc(refs, cap * sizeof(refs[0]));
1271 }
1272
1273 refs[len++] = ref;
1274 }
1275
1276 reftable_table_from_merged_table(&tab, reftable_stack_merged_table(st));
1277
1278 err = validate_ref_record_addition(tab, refs, len);
1279
1280done:
1281 for (i = 0; i < len; i++) {
1282 reftable_ref_record_release(&refs[i]);
1283 }
1284
1285 free(refs);
1286 reftable_iterator_destroy(&it);
1287 reftable_reader_free(rd);
1288 return err;
1289}
1290
1291static int is_table_name(const char *s)
1292{
1293 const char *dot = strrchr(s, '.');
1294 return dot && !strcmp(dot, ".ref");
1295}
1296
1297static void remove_maybe_stale_table(struct reftable_stack *st, uint64_t max,
1298 const char *name)
1299{
1300 int err = 0;
1301 uint64_t update_idx = 0;
1302 struct reftable_block_source src = { NULL };
1303 struct reftable_reader *rd = NULL;
1304 struct strbuf table_path = STRBUF_INIT;
1305 stack_filename(&table_path, st, name);
1306
1307 err = reftable_block_source_from_file(&src, table_path.buf);
1308 if (err < 0)
1309 goto done;
1310
1311 err = reftable_new_reader(&rd, &src, name);
1312 if (err < 0)
1313 goto done;
1314
1315 update_idx = reftable_reader_max_update_index(rd);
1316 reftable_reader_free(rd);
1317
1318 if (update_idx <= max) {
1319 unlink(table_path.buf);
1320 }
1321done:
1322 strbuf_release(&table_path);
1323}
1324
1325static int reftable_stack_clean_locked(struct reftable_stack *st)
1326{
1327 uint64_t max = reftable_merged_table_max_update_index(
1328 reftable_stack_merged_table(st));
1329 DIR *dir = opendir(st->reftable_dir);
1330 struct dirent *d = NULL;
1331 if (!dir) {
1332 return REFTABLE_IO_ERROR;
1333 }
1334
1335 while ((d = readdir(dir))) {
1336 int i = 0;
1337 int found = 0;
1338 if (!is_table_name(d->d_name))
1339 continue;
1340
1341 for (i = 0; !found && i < st->readers_len; i++) {
1342 found = !strcmp(reader_name(st->readers[i]), d->d_name);
1343 }
1344 if (found)
1345 continue;
1346
1347 remove_maybe_stale_table(st, max, d->d_name);
1348 }
1349
1350 closedir(dir);
1351 return 0;
1352}
1353
1354int reftable_stack_clean(struct reftable_stack *st)
1355{
1356 struct reftable_addition *add = NULL;
1357 int err = reftable_stack_new_addition(&add, st);
1358 if (err < 0) {
1359 goto done;
1360 }
1361
1362 err = reftable_stack_reload(st);
1363 if (err < 0) {
1364 goto done;
1365 }
1366
1367 err = reftable_stack_clean_locked(st);
1368
1369done:
1370 reftable_addition_destroy(add);
1371 return err;
1372}
1373
1374int reftable_stack_print_directory(const char *stackdir, uint32_t hash_id)
1375{
1376 struct reftable_stack *stack = NULL;
1377 struct reftable_write_options cfg = { .hash_id = hash_id };
1378 struct reftable_merged_table *merged = NULL;
1379 struct reftable_table table = { NULL };
1380
1381 int err = reftable_new_stack(&stack, stackdir, cfg);
1382 if (err < 0)
1383 goto done;
1384
1385 merged = reftable_stack_merged_table(stack);
1386 reftable_table_from_merged_table(&table, merged);
1387 err = reftable_table_print(&table);
1388done:
1389 if (stack)
1390 reftable_stack_destroy(stack);
1391 return err;
1392}