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 | } |