Raw
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 "writer.h"
10
11 #include "system.h"
12
13 #include "block.h"
14 #include "constants.h"
15 #include "record.h"
16 #include "tree.h"
17 #include "reftable-error.h"
18
19 /* finishes a block, and writes it to storage */
20 static int writer_flush_block(struct reftable_writer *w);
21
22 /* deallocates memory related to the index */
23 static void writer_clear_index(struct reftable_writer *w);
24
25 /* finishes writing a 'r' (refs) or 'g' (reflogs) section */
26 static int writer_finish_public_section(struct reftable_writer *w);
27
28 static struct reftable_block_stats *
29 writer_reftable_block_stats(struct reftable_writer *w, uint8_t typ)
30 {
31 switch (typ) {
32 case 'r':
33 return &w->stats.ref_stats;
34 case 'o':
35 return &w->stats.obj_stats;
36 case 'i':
37 return &w->stats.idx_stats;
38 case 'g':
39 return &w->stats.log_stats;
40 }
41 abort();
42 return NULL;
43 }
44
45 /* write data, queuing the padding for the next write. Returns negative for
46 * error. */
47 static int padded_write(struct reftable_writer *w, uint8_t *data, size_t len,
48 int padding)
49 {
50 int n = 0;
51 if (w->pending_padding > 0) {
52 uint8_t *zeroed;
53 int n;
54
55 zeroed = reftable_calloc(w->pending_padding, sizeof(*zeroed));
56 if (!zeroed)
57 return -1;
58
59 n = w->write(w->write_arg, zeroed, w->pending_padding);
60 if (n < 0) {
61 reftable_free(zeroed);
62 return n;
63 }
64
65 w->pending_padding = 0;
66 reftable_free(zeroed);
67 }
68
69 w->pending_padding = padding;
70 n = w->write(w->write_arg, data, len);
71 if (n < 0)
72 return n;
73 n += padding;
74 return 0;
75 }
76
77 static void options_set_defaults(struct reftable_write_options *opts)
78 {
79 if (opts->restart_interval == 0) {
80 opts->restart_interval = 16;
81 }
82
83 if (opts->block_size == 0) {
84 opts->block_size = DEFAULT_BLOCK_SIZE;
85 }
86 }
87
88 static int writer_version(struct reftable_writer *w)
89 {
90 return (w->hash_id == 0 || w->hash_id == REFTABLE_HASH_SHA1) ?
91 1 :
92 2;
93 }
94
95 static int writer_write_header(struct reftable_writer *w, uint8_t *dest)
96 {
97 memcpy(dest, "REFT", 4);
98
99 dest[4] = writer_version(w);
100
101 reftable_put_be24(dest + 5, w->opts.block_size);
102 reftable_put_be64(dest + 8, w->min_update_index);
103 reftable_put_be64(dest + 16, w->max_update_index);
104 if (writer_version(w) == 2) {
105 uint32_t hash_id;
106
107 switch (w->hash_id) {
108 case REFTABLE_HASH_SHA1:
109 hash_id = REFTABLE_FORMAT_ID_SHA1;
110 break;
111 case REFTABLE_HASH_SHA256:
112 hash_id = REFTABLE_FORMAT_ID_SHA256;
113 break;
114 default:
115 return -1;
116 }
117
118 reftable_put_be32(dest + 24, hash_id);
119 }
120
121 return header_size(writer_version(w));
122 }
123
124 static int writer_reinit_block_writer(struct reftable_writer *w, uint8_t typ)
125 {
126 int block_start = 0, ret;
127
128 if (w->next == 0)
129 block_start = header_size(writer_version(w));
130
131 reftable_buf_reset(&w->last_key);
132 ret = block_writer_init(&w->block_writer_data, typ, w->block,
133 w->opts.block_size, block_start,
134 hash_size(w->hash_id));
135 if (ret < 0)
136 return ret;
137
138 w->block_writer = &w->block_writer_data;
139 w->block_writer->restart_interval = w->opts.restart_interval;
140
141 return 0;
142 }
143
144 int reftable_writer_new(struct reftable_writer **out,
145 ssize_t (*writer_func)(void *, const void *, size_t),
146 int (*flush_func)(void *),
147 void *writer_arg,
148 enum reftable_hash hash_id,
149 const struct reftable_write_options *_opts)
150 {
151 struct reftable_write_options opts = {0};
152 struct reftable_writer *wp;
153
154 if (_opts)
155 opts = *_opts;
156 options_set_defaults(&opts);
157 if (opts.block_size >= (1 << 24))
158 return REFTABLE_API_ERROR;
159
160 if (!hash_id)
161 hash_id = REFTABLE_HASH_SHA1;
162
163 wp = reftable_calloc(1, sizeof(*wp));
164 if (!wp)
165 return REFTABLE_OUT_OF_MEMORY_ERROR;
166
167 reftable_buf_init(&wp->block_writer_data.last_key);
168 reftable_buf_init(&wp->last_key);
169 reftable_buf_init(&wp->scratch);
170 REFTABLE_CALLOC_ARRAY(wp->block, opts.block_size);
171 if (!wp->block) {
172 reftable_free(wp);
173 return REFTABLE_OUT_OF_MEMORY_ERROR;
174 }
175 wp->write = writer_func;
176 wp->write_arg = writer_arg;
177 wp->opts = opts;
178 wp->hash_id = hash_id;
179 wp->flush = flush_func;
180 writer_reinit_block_writer(wp, REFTABLE_BLOCK_TYPE_REF);
181
182 *out = wp;
183
184 return 0;
185 }
186
187 int reftable_writer_set_limits(struct reftable_writer *w, uint64_t min,
188 uint64_t max)
189 {
190 /*
191 * Set the min/max update index limits for the reftable writer.
192 * This must be called before adding any records, since:
193 * - The 'next' field gets set after writing the first block.
194 * - The 'last_key' field updates with each new record (but resets
195 * after sections).
196 * Returns REFTABLE_API_ERROR if called after writing has begun.
197 */
198 if (w->next || w->last_key.len)
199 return REFTABLE_API_ERROR;
200
201 w->min_update_index = min;
202 w->max_update_index = max;
203
204 return 0;
205 }
206
207 static void writer_release(struct reftable_writer *w)
208 {
209 if (w) {
210 reftable_free(w->block);
211 w->block = NULL;
212 block_writer_release(&w->block_writer_data);
213 w->block_writer = NULL;
214 writer_clear_index(w);
215 reftable_buf_release(&w->last_key);
216 reftable_buf_release(&w->scratch);
217 }
218 }
219
220 void reftable_writer_free(struct reftable_writer *w)
221 {
222 writer_release(w);
223 reftable_free(w);
224 }
225
226 struct obj_index_tree_node {
227 struct reftable_buf hash;
228 uint64_t *offsets;
229 size_t offset_len;
230 size_t offset_cap;
231 };
232
233 #define OBJ_INDEX_TREE_NODE_INIT \
234 { \
235 .hash = REFTABLE_BUF_INIT \
236 }
237
238 static int obj_index_tree_node_compare(const void *a, const void *b)
239 {
240 return reftable_buf_cmp(&((const struct obj_index_tree_node *)a)->hash,
241 &((const struct obj_index_tree_node *)b)->hash);
242 }
243
244 static int writer_index_hash(struct reftable_writer *w, struct reftable_buf *hash)
245 {
246 uint64_t off = w->next;
247 struct obj_index_tree_node want = { .hash = *hash };
248 struct obj_index_tree_node *key;
249 struct tree_node *node;
250
251 node = tree_search(w->obj_index_tree, &want, &obj_index_tree_node_compare);
252 if (!node) {
253 struct obj_index_tree_node empty = OBJ_INDEX_TREE_NODE_INIT;
254 int err;
255
256 key = reftable_malloc(sizeof(*key));
257 if (!key)
258 return REFTABLE_OUT_OF_MEMORY_ERROR;
259
260 *key = empty;
261
262 reftable_buf_reset(&key->hash);
263 err = reftable_buf_add(&key->hash, hash->buf, hash->len);
264 if (err < 0) {
265 reftable_free(key);
266 return err;
267 }
268 tree_insert(&w->obj_index_tree, key,
269 &obj_index_tree_node_compare);
270 } else {
271 key = node->key;
272 }
273
274 if (key->offset_len > 0 && key->offsets[key->offset_len - 1] == off)
275 return 0;
276
277 REFTABLE_ALLOC_GROW_OR_NULL(key->offsets, key->offset_len + 1,
278 key->offset_cap);
279 if (!key->offsets)
280 return REFTABLE_OUT_OF_MEMORY_ERROR;
281 key->offsets[key->offset_len++] = off;
282
283 return 0;
284 }
285
286 static int writer_add_record(struct reftable_writer *w,
287 struct reftable_record *rec)
288 {
289 int err;
290
291 err = reftable_record_key(rec, &w->scratch);
292 if (err < 0)
293 goto done;
294
295 if (reftable_buf_cmp(&w->last_key, &w->scratch) >= 0) {
296 err = REFTABLE_API_ERROR;
297 goto done;
298 }
299
300 reftable_buf_reset(&w->last_key);
301 err = reftable_buf_add(&w->last_key, w->scratch.buf, w->scratch.len);
302 if (err < 0)
303 goto done;
304
305 if (!w->block_writer) {
306 err = writer_reinit_block_writer(w, reftable_record_type(rec));
307 if (err < 0)
308 goto done;
309 }
310
311 if (block_writer_type(w->block_writer) != reftable_record_type(rec))
312 return REFTABLE_API_ERROR;
313
314 /*
315 * Try to add the record to the writer. If this succeeds then we're
316 * done. Otherwise the block writer may have hit the block size limit
317 * and needs to be flushed.
318 */
319 err = block_writer_add(w->block_writer, rec);
320 if (err == 0)
321 goto done;
322
323 if (err != REFTABLE_ENTRY_TOO_BIG_ERROR)
324 goto done;
325 /*
326 * The current block is full, so we need to flush and reinitialize the
327 * writer to start writing the next block.
328 */
329 err = writer_flush_block(w);
330 if (err < 0)
331 goto done;
332 err = writer_reinit_block_writer(w, reftable_record_type(rec));
333 if (err < 0)
334 goto done;
335
336 /*
337 * Try to add the record to the writer again. If this still fails then
338 * the record does not fit into the block size.
339 */
340 err = block_writer_add(w->block_writer, rec);
341 if (err)
342 goto done;
343
344 done:
345 return err;
346 }
347
348 int reftable_writer_add_ref(struct reftable_writer *w,
349 struct reftable_ref_record *ref)
350 {
351 struct reftable_record rec = {
352 .type = REFTABLE_BLOCK_TYPE_REF,
353 .u = {
354 .ref = *ref
355 },
356 };
357 int err;
358
359 if (!ref->refname ||
360 ref->update_index < w->min_update_index ||
361 ref->update_index > w->max_update_index)
362 return REFTABLE_API_ERROR;
363
364 rec.u.ref.update_index -= w->min_update_index;
365
366 err = writer_add_record(w, &rec);
367 if (err < 0)
368 goto out;
369
370 if (!w->opts.skip_index_objects && reftable_ref_record_val1(ref)) {
371 reftable_buf_reset(&w->scratch);
372 err = reftable_buf_add(&w->scratch, (char *)reftable_ref_record_val1(ref),
373 hash_size(w->hash_id));
374 if (err < 0)
375 goto out;
376
377 err = writer_index_hash(w, &w->scratch);
378 if (err < 0)
379 goto out;
380 }
381
382 if (!w->opts.skip_index_objects && reftable_ref_record_val2(ref)) {
383 reftable_buf_reset(&w->scratch);
384 err = reftable_buf_add(&w->scratch, reftable_ref_record_val2(ref),
385 hash_size(w->hash_id));
386 if (err < 0)
387 goto out;
388
389 err = writer_index_hash(w, &w->scratch);
390 if (err < 0)
391 goto out;
392 }
393
394 err = 0;
395
396 out:
397 return err;
398 }
399
400 int reftable_writer_add_refs(struct reftable_writer *w,
401 struct reftable_ref_record *refs, size_t n)
402 {
403 int err = 0;
404
405 if (n)
406 qsort(refs, n, sizeof(*refs), reftable_ref_record_compare_name);
407
408 for (size_t i = 0; err == 0 && i < n; i++)
409 err = reftable_writer_add_ref(w, &refs[i]);
410
411 return err;
412 }
413
414 static int reftable_writer_add_log_verbatim(struct reftable_writer *w,
415 struct reftable_log_record *log)
416 {
417 struct reftable_record rec = {
418 .type = REFTABLE_BLOCK_TYPE_LOG,
419 .u = {
420 .log = *log,
421 },
422 };
423 if (w->block_writer &&
424 block_writer_type(w->block_writer) == REFTABLE_BLOCK_TYPE_REF) {
425 int err = writer_finish_public_section(w);
426 if (err < 0)
427 return err;
428 }
429
430 w->next -= w->pending_padding;
431 w->pending_padding = 0;
432 return writer_add_record(w, &rec);
433 }
434
435 int reftable_writer_add_log(struct reftable_writer *w,
436 struct reftable_log_record *log)
437 {
438 char *input_log_message = NULL;
439 struct reftable_buf cleaned_message = REFTABLE_BUF_INIT;
440 int err = 0;
441
442 if (log->value_type == REFTABLE_LOG_DELETION)
443 return reftable_writer_add_log_verbatim(w, log);
444
445 /*
446 * Verify only the upper limit of the update_index. Each reflog entry
447 * is tied to a specific update_index. Entries in the reflog can be
448 * replaced by adding a new entry with the same update_index,
449 * effectively canceling the old one.
450 *
451 * Consequently, reflog updates may include update_index values lower
452 * than the writer's min_update_index.
453 */
454 if (log->update_index > w->max_update_index)
455 return REFTABLE_API_ERROR;
456
457 if (!log->refname)
458 return REFTABLE_API_ERROR;
459
460 input_log_message = log->value.update.message;
461 if (!w->opts.exact_log_message && log->value.update.message) {
462 err = reftable_buf_addstr(&cleaned_message, log->value.update.message);
463 if (err < 0)
464 goto done;
465
466 while (cleaned_message.len &&
467 cleaned_message.buf[cleaned_message.len - 1] == '\n') {
468 err = reftable_buf_setlen(&cleaned_message,
469 cleaned_message.len - 1);
470 if (err < 0)
471 goto done;
472 }
473 if (strchr(cleaned_message.buf, '\n')) {
474 /* multiple lines not allowed. */
475 err = REFTABLE_API_ERROR;
476 goto done;
477 }
478
479 err = reftable_buf_addstr(&cleaned_message, "\n");
480 if (err < 0)
481 goto done;
482
483 log->value.update.message = cleaned_message.buf;
484 }
485
486 err = reftable_writer_add_log_verbatim(w, log);
487 log->value.update.message = input_log_message;
488 done:
489 reftable_buf_release(&cleaned_message);
490 return err;
491 }
492
493 int reftable_writer_add_logs(struct reftable_writer *w,
494 struct reftable_log_record *logs, size_t n)
495 {
496 int err = 0;
497
498 if (n)
499 qsort(logs, n, sizeof(*logs), reftable_log_record_compare_key);
500
501 for (size_t i = 0; err == 0 && i < n; i++)
502 err = reftable_writer_add_log(w, &logs[i]);
503
504 return err;
505 }
506
507 static int writer_finish_section(struct reftable_writer *w)
508 {
509 struct reftable_block_stats *bstats = NULL;
510 uint8_t typ = block_writer_type(w->block_writer);
511 uint64_t index_start = 0;
512 int max_level = 0;
513 size_t threshold = w->opts.unpadded ? 1 : 3;
514 int before_blocks = w->stats.idx_stats.blocks;
515 int err;
516
517 err = writer_flush_block(w);
518 if (err < 0)
519 return err;
520
521 /*
522 * When the section we are about to index has a lot of blocks then the
523 * index itself may span across multiple blocks, as well. This would
524 * require a linear scan over index blocks only to find the desired
525 * indexed block, which is inefficient. Instead, we write a multi-level
526 * index where index records of level N+1 will refer to index blocks of
527 * level N. This isn't constant time, either, but at least logarithmic.
528 *
529 * This loop handles writing this multi-level index. Note that we write
530 * the lowest-level index pointing to the indexed blocks first. We then
531 * continue writing additional index levels until the current level has
532 * less blocks than the threshold so that the highest level will be at
533 * the end of the index section.
534 *
535 * Readers are thus required to start reading the index section from
536 * its end, which is why we set `index_start` to the beginning of the
537 * last index section.
538 */
539 while (w->index_len > threshold) {
540 struct reftable_index_record *idx = NULL;
541 size_t i, idx_len;
542
543 max_level++;
544 index_start = w->next;
545 err = writer_reinit_block_writer(w, REFTABLE_BLOCK_TYPE_INDEX);
546 if (err < 0)
547 return err;
548
549 idx = w->index;
550 idx_len = w->index_len;
551
552 w->index = NULL;
553 w->index_len = 0;
554 w->index_cap = 0;
555 for (i = 0; i < idx_len; i++) {
556 struct reftable_record rec = {
557 .type = REFTABLE_BLOCK_TYPE_INDEX,
558 .u = {
559 .idx = idx[i],
560 },
561 };
562
563 err = writer_add_record(w, &rec);
564 if (err < 0)
565 return err;
566 }
567
568 err = writer_flush_block(w);
569 if (err < 0)
570 return err;
571
572 for (i = 0; i < idx_len; i++)
573 reftable_buf_release(&idx[i].last_key);
574 reftable_free(idx);
575 }
576
577 /*
578 * The index may still contain a number of index blocks lower than the
579 * threshold. Clear it so that these entries don't leak into the next
580 * index section.
581 */
582 writer_clear_index(w);
583
584 bstats = writer_reftable_block_stats(w, typ);
585 bstats->index_blocks = w->stats.idx_stats.blocks - before_blocks;
586 bstats->index_offset = index_start;
587 bstats->max_index_level = max_level;
588
589 /* Reinit lastKey, as the next section can start with any key. */
590 reftable_buf_reset(&w->last_key);
591
592 return 0;
593 }
594
595 struct common_prefix_arg {
596 struct reftable_buf *last;
597 size_t max;
598 };
599
600 static void update_common(void *void_arg, void *key)
601 {
602 struct common_prefix_arg *arg = void_arg;
603 struct obj_index_tree_node *entry = key;
604 if (arg->last) {
605 size_t n = common_prefix_size(&entry->hash, arg->last);
606 if (n > arg->max)
607 arg->max = n;
608 }
609 arg->last = &entry->hash;
610 }
611
612 struct write_record_arg {
613 struct reftable_writer *w;
614 int err;
615 };
616
617 static void write_object_record(void *void_arg, void *key)
618 {
619 struct write_record_arg *arg = void_arg;
620 struct obj_index_tree_node *entry = key;
621 struct reftable_record
622 rec = { .type = REFTABLE_BLOCK_TYPE_OBJ,
623 .u.obj = {
624 .hash_prefix = (uint8_t *)entry->hash.buf,
625 .hash_prefix_len = arg->w->stats.object_id_len,
626 .offsets = entry->offsets,
627 .offset_len = entry->offset_len,
628 } };
629 if (arg->err < 0)
630 goto done;
631
632 /*
633 * Try to add the record to the writer. If this succeeds then we're
634 * done. Otherwise the block writer may have hit the block size limit
635 * and needs to be flushed.
636 */
637 arg->err = block_writer_add(arg->w->block_writer, &rec);
638 if (arg->err == 0)
639 goto done;
640
641 if (arg->err != REFTABLE_ENTRY_TOO_BIG_ERROR)
642 goto done;
643
644 /*
645 * The current block is full, so we need to flush and reinitialize the
646 * writer to start writing the next block.
647 */
648 arg->err = writer_flush_block(arg->w);
649 if (arg->err < 0)
650 goto done;
651
652 arg->err = writer_reinit_block_writer(arg->w, REFTABLE_BLOCK_TYPE_OBJ);
653 if (arg->err < 0)
654 goto done;
655
656 /*
657 * If this still fails then we may need to reset record's offset
658 * length to reduce the data size to be written.
659 */
660 arg->err = block_writer_add(arg->w->block_writer, &rec);
661 if (arg->err == 0)
662 goto done;
663
664 if (arg->err != REFTABLE_ENTRY_TOO_BIG_ERROR)
665 goto done;
666
667 rec.u.obj.offset_len = 0;
668 arg->err = block_writer_add(arg->w->block_writer, &rec);
669
670 /* Should be able to write into a fresh block. */
671 assert(arg->err == 0);
672
673 done:;
674 }
675
676 static void object_record_free(void *void_arg REFTABLE_UNUSED, void *key)
677 {
678 struct obj_index_tree_node *entry = key;
679
680 REFTABLE_FREE_AND_NULL(entry->offsets);
681 reftable_buf_release(&entry->hash);
682 reftable_free(entry);
683 }
684
685 static int writer_dump_object_index(struct reftable_writer *w)
686 {
687 struct write_record_arg closure = { .w = w };
688 struct common_prefix_arg common = {
689 .max = 1, /* obj_id_len should be >= 2. */
690 };
691 int err;
692
693 if (w->obj_index_tree)
694 infix_walk(w->obj_index_tree, &update_common, &common);
695 w->stats.object_id_len = common.max + 1;
696
697 err = writer_reinit_block_writer(w, REFTABLE_BLOCK_TYPE_OBJ);
698 if (err < 0)
699 return err;
700
701 if (w->obj_index_tree)
702 infix_walk(w->obj_index_tree, &write_object_record, &closure);
703
704 if (closure.err < 0)
705 return closure.err;
706 return writer_finish_section(w);
707 }
708
709 static int writer_finish_public_section(struct reftable_writer *w)
710 {
711 uint8_t typ = 0;
712 int err = 0;
713
714 if (!w->block_writer)
715 return 0;
716
717 typ = block_writer_type(w->block_writer);
718 err = writer_finish_section(w);
719 if (err < 0)
720 return err;
721 if (typ == REFTABLE_BLOCK_TYPE_REF && !w->opts.skip_index_objects &&
722 w->stats.ref_stats.index_blocks > 0) {
723 err = writer_dump_object_index(w);
724 if (err < 0)
725 return err;
726 }
727
728 if (w->obj_index_tree) {
729 infix_walk(w->obj_index_tree, &object_record_free, NULL);
730 tree_free(w->obj_index_tree);
731 w->obj_index_tree = NULL;
732 }
733
734 w->block_writer = NULL;
735 return 0;
736 }
737
738 int reftable_writer_close(struct reftable_writer *w)
739 {
740 uint8_t footer[72];
741 uint8_t *p = footer;
742 int err = writer_finish_public_section(w);
743 int empty_table = w->next == 0;
744 if (err != 0)
745 goto done;
746 w->pending_padding = 0;
747 if (empty_table) {
748 /* Empty tables need a header anyway. */
749 uint8_t header[28];
750 int n = writer_write_header(w, header);
751 err = padded_write(w, header, n, 0);
752 if (err < 0)
753 goto done;
754 }
755
756 p += writer_write_header(w, footer);
757 reftable_put_be64(p, w->stats.ref_stats.index_offset);
758 p += 8;
759 reftable_put_be64(p, (w->stats.obj_stats.offset) << 5 | w->stats.object_id_len);
760 p += 8;
761 reftable_put_be64(p, w->stats.obj_stats.index_offset);
762 p += 8;
763
764 reftable_put_be64(p, w->stats.log_stats.offset);
765 p += 8;
766 reftable_put_be64(p, w->stats.log_stats.index_offset);
767 p += 8;
768
769 reftable_put_be32(p, crc32(0, footer, p - footer));
770 p += 4;
771
772 err = w->flush(w->write_arg);
773 if (err < 0) {
774 err = REFTABLE_IO_ERROR;
775 goto done;
776 }
777
778 err = padded_write(w, footer, footer_size(writer_version(w)), 0);
779 if (err < 0)
780 goto done;
781
782 if (empty_table) {
783 err = REFTABLE_EMPTY_TABLE_ERROR;
784 goto done;
785 }
786
787 done:
788 writer_release(w);
789 return err;
790 }
791
792 static void writer_clear_index(struct reftable_writer *w)
793 {
794 for (size_t i = 0; w->index && i < w->index_len; i++)
795 reftable_buf_release(&w->index[i].last_key);
796 REFTABLE_FREE_AND_NULL(w->index);
797 w->index_len = 0;
798 w->index_cap = 0;
799 }
800
801 static int writer_flush_nonempty_block(struct reftable_writer *w)
802 {
803 struct reftable_index_record index_record = {
804 .last_key = REFTABLE_BUF_INIT,
805 };
806 uint8_t typ = block_writer_type(w->block_writer);
807 struct reftable_block_stats *bstats;
808 int raw_bytes, padding = 0, err;
809 uint64_t block_typ_off;
810
811 /*
812 * Finish the current block. This will cause the block writer to emit
813 * restart points and potentially compress records in case we are
814 * writing a log block.
815 *
816 * Note that this is still happening in memory.
817 */
818 raw_bytes = block_writer_finish(w->block_writer);
819 if (raw_bytes < 0)
820 return raw_bytes;
821
822 /*
823 * By default, all records except for log records are padded to the
824 * block size.
825 */
826 if (!w->opts.unpadded && typ != REFTABLE_BLOCK_TYPE_LOG)
827 padding = w->opts.block_size - raw_bytes;
828
829 bstats = writer_reftable_block_stats(w, typ);
830 block_typ_off = (bstats->blocks == 0) ? w->next : 0;
831 if (block_typ_off > 0)
832 bstats->offset = block_typ_off;
833 bstats->entries += w->block_writer->entries;
834 bstats->restarts += w->block_writer->restart_len;
835 bstats->blocks++;
836 w->stats.blocks++;
837
838 /*
839 * If this is the first block we're writing to the table then we need
840 * to also write the reftable header.
841 */
842 if (!w->next)
843 writer_write_header(w, w->block);
844
845 err = padded_write(w, w->block, raw_bytes, padding);
846 if (err < 0)
847 return err;
848
849 /*
850 * Add an index record for every block that we're writing. If we end up
851 * having more than a threshold of index records we will end up writing
852 * an index section in `writer_finish_section()`. Each index record
853 * contains the last record key of the block it is indexing as well as
854 * the offset of that block.
855 *
856 * Note that this also applies when flushing index blocks, in which
857 * case we will end up with a multi-level index.
858 */
859 REFTABLE_ALLOC_GROW_OR_NULL(w->index, w->index_len + 1, w->index_cap);
860 if (!w->index)
861 return REFTABLE_OUT_OF_MEMORY_ERROR;
862
863 index_record.offset = w->next;
864 reftable_buf_reset(&index_record.last_key);
865 err = reftable_buf_add(&index_record.last_key, w->block_writer->last_key.buf,
866 w->block_writer->last_key.len);
867 if (err < 0)
868 return err;
869 w->index[w->index_len] = index_record;
870 w->index_len++;
871
872 w->next += padding + raw_bytes;
873 w->block_writer = NULL;
874
875 return 0;
876 }
877
878 static int writer_flush_block(struct reftable_writer *w)
879 {
880 if (!w->block_writer)
881 return 0;
882 if (w->block_writer->entries == 0)
883 return 0;
884 return writer_flush_nonempty_block(w);
885 }
886
887 const struct reftable_stats *reftable_writer_stats(struct reftable_writer *w)
888 {
889 return &w->stats;
890 }