Coverage Report

Created: 2020-05-07 18:36

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