Blob


1 /*
2 * Copyright (c) 2020 Ori Bernstein
3 * Copyright (c) 2021, 2022 Stefan Sperling <stsp@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
18 #include <sys/types.h>
19 #include <sys/queue.h>
20 #include <sys/uio.h>
22 #include <limits.h>
23 #include <stdio.h>
24 #include <stdint.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <time.h>
28 #include <imsg.h>
29 #include <inttypes.h>
30 #include <unistd.h>
32 #include "got_error.h"
33 #include "got_cancel.h"
34 #include "got_object.h"
35 #include "got_reference.h"
36 #include "got_repository_admin.h"
37 #include "got_path.h"
39 #include "got_lib_delta.h"
40 #include "got_lib_object.h"
41 #include "got_lib_object_cache.h"
42 #include "got_lib_object_idset.h"
43 #include "got_lib_privsep.h"
44 #include "got_lib_ratelimit.h"
45 #include "got_lib_pack.h"
46 #include "got_lib_pack_create.h"
47 #include "got_lib_repository.h"
49 struct send_id_arg {
50 struct imsgbuf *ibuf;
51 struct got_object_id *ids[GOT_IMSG_OBJ_ID_LIST_MAX_NIDS];
52 size_t nids;
53 };
55 static const struct got_error *
56 send_id(struct got_object_id *id, void *data, void *arg)
57 {
58 const struct got_error *err = NULL;
59 struct send_id_arg *a = arg;
61 a->ids[a->nids++] = id;
63 if (a->nids >= GOT_IMSG_OBJ_ID_LIST_MAX_NIDS) {
64 err = got_privsep_send_object_idlist(a->ibuf, a->ids, a->nids);
65 if (err)
66 return err;
67 a->nids = 0;
68 }
70 return NULL;
71 }
73 static const struct got_error *
74 send_idset(struct imsgbuf *ibuf, struct got_object_idset *idset)
75 {
76 const struct got_error *err;
77 struct send_id_arg sia;
79 memset(&sia, 0, sizeof(sia));
80 sia.ibuf = ibuf;
81 err = got_object_idset_for_each(idset, send_id, &sia);
82 if (err)
83 return err;
85 if (sia.nids > 0) {
86 err = got_privsep_send_object_idlist(ibuf, sia.ids, sia.nids);
87 if (err)
88 return err;
89 }
91 return got_privsep_send_object_idlist_done(ibuf);
92 }
94 static const struct got_error *
95 recv_reused_delta(struct got_imsg_reused_delta *delta,
96 struct got_object_idset *idset, struct got_pack_metavec *v)
97 {
98 struct got_pack_meta *m, *base;
100 if (delta->delta_offset + delta->delta_size < delta->delta_offset ||
101 delta->delta_offset +
102 delta->delta_compressed_size < delta->delta_offset)
103 return got_error(GOT_ERR_BAD_PACKFILE);
105 m = got_object_idset_get(idset, &delta->id);
106 if (m == NULL)
107 return got_error(GOT_ERR_NO_OBJ);
109 base = got_object_idset_get(idset, &delta->base_id);
110 if (base == NULL)
111 return got_error(GOT_ERR_NO_OBJ);
113 m->delta_len = delta->delta_size;
114 m->delta_compressed_len = delta->delta_compressed_size;
115 m->delta_offset = 0;
116 m->prev = base;
117 m->size = delta->result_size;
118 m->reused_delta_offset = delta->delta_offset;
119 m->base_obj_id = got_object_id_dup(&delta->base_id);
120 if (m->base_obj_id == NULL)
121 return got_error_from_errno("got_object_id_dup");
123 return got_pack_add_meta(m, v);
126 const struct got_error *
127 got_pack_search_deltas(struct got_packidx **packidx, struct got_pack **pack,
128 struct got_pack_metavec *v, struct got_object_idset *idset,
129 int ncolored, int nfound, int ntrees, int ncommits,
130 struct got_repository *repo,
131 got_pack_progress_cb progress_cb, void *progress_arg,
132 struct got_ratelimit *rl, got_cancel_cb cancel_cb, void *cancel_arg)
134 const struct got_error *err = NULL;
135 struct got_imsg_reused_delta deltas[GOT_IMSG_REUSED_DELTAS_MAX_NDELTAS];
136 size_t ndeltas, i;
138 *packidx = NULL;
139 *pack = NULL;
141 err = got_pack_find_pack_for_reuse(packidx, repo);
142 if (err)
143 return err;
145 if (*packidx == NULL)
146 return NULL;
148 err = got_pack_cache_pack_for_packidx(pack, *packidx, repo);
149 if (err)
150 goto done;
152 if ((*pack)->privsep_child == NULL) {
153 err = got_pack_start_privsep_child(*pack, *packidx);
154 if (err)
155 goto done;
158 err = got_privsep_send_delta_reuse_req((*pack)->privsep_child->ibuf);
159 if (err)
160 goto done;
162 err = send_idset((*pack)->privsep_child->ibuf, idset);
163 if (err)
164 goto done;
166 for (;;) {
167 int done = 0;
169 if (cancel_cb) {
170 err = (*cancel_cb)(cancel_arg);
171 if (err)
172 break;
175 err = got_privsep_recv_reused_deltas(&done, deltas, &ndeltas,
176 (*pack)->privsep_child->ibuf);
177 if (err || done)
178 break;
180 for (i = 0; i < ndeltas; i++) {
181 struct got_imsg_reused_delta *delta = &deltas[i];
182 err = recv_reused_delta(delta, idset, v);
183 if (err)
184 goto done;
187 err = got_pack_report_progress(progress_cb, progress_arg, rl,
188 ncolored, nfound, ntrees, 0L, ncommits,
189 got_object_idset_num_elements(idset), v->nmeta, 0);
190 if (err)
191 break;
193 done:
194 return err;
197 struct recv_painted_commit_arg {
198 int *ncolored;
199 int *nqueued;
200 int *nskip;
201 struct got_object_id_queue *ids;
202 struct got_object_idset *keep;
203 struct got_object_idset *drop;
204 struct got_object_idset *skip;
205 got_pack_progress_cb progress_cb;
206 void *progress_arg;
207 struct got_ratelimit *rl;
208 got_cancel_cb cancel_cb;
209 void *cancel_arg;
210 };
212 static const struct got_error *
213 recv_painted_commit(void *arg, struct got_object_id *id, intptr_t color)
215 const struct got_error *err = NULL;
216 struct recv_painted_commit_arg *a = arg;
217 struct got_object_qid *qid, *tmp;
219 if (a->cancel_cb) {
220 err = a->cancel_cb(a->cancel_arg);
221 if (err)
222 return err;
225 switch (color) {
226 case COLOR_KEEP:
227 err = got_object_idset_add(a->keep, id, NULL);
228 if (err)
229 return err;
230 (*a->ncolored)++;
231 break;
232 case COLOR_DROP:
233 err = got_object_idset_add(a->drop, id, NULL);
234 if (err)
235 return err;
236 (*a->ncolored)++;
237 break;
238 case COLOR_SKIP:
239 err = got_object_idset_add(a->skip, id, NULL);
240 if (err)
241 return err;
242 break;
243 default:
244 /* should not happen */
245 return got_error_fmt(GOT_ERR_NOT_IMPL,
246 "%s invalid commit color %"PRIdPTR, __func__, color);
249 STAILQ_FOREACH_SAFE(qid, a->ids, entry, tmp) {
250 if (got_object_id_cmp(&qid->id, id) != 0)
251 continue;
252 STAILQ_REMOVE(a->ids, qid, got_object_qid, entry);
253 color = (intptr_t)qid->data;
254 got_object_qid_free(qid);
255 (*a->nqueued)--;
256 if (color == COLOR_SKIP)
257 (*a->nskip)--;
258 break;
261 return got_pack_report_progress(a->progress_cb, a->progress_arg, a->rl,
262 *a->ncolored, 0, 0, 0L, 0, 0, 0, 0);
265 static const struct got_error *
266 paint_packed_commits(struct got_pack *pack, struct got_object_id *id,
267 int idx, intptr_t color, int *ncolored, int *nqueued, int *nskip,
268 struct got_object_id_queue *ids,
269 struct got_object_idset *keep, struct got_object_idset *drop,
270 struct got_object_idset *skip, struct got_repository *repo,
271 got_pack_progress_cb progress_cb, void *progress_arg,
272 struct got_ratelimit *rl, got_cancel_cb cancel_cb, void *cancel_arg)
274 const struct got_error *err = NULL;
275 struct got_object_id_queue next_ids;
276 struct got_object_qid *qid, *tmp;
277 struct recv_painted_commit_arg arg;
279 STAILQ_INIT(&next_ids);
281 err = got_privsep_send_painting_request(pack->privsep_child->ibuf,
282 idx, id, color);
283 if (err)
284 return err;
286 arg.ncolored = ncolored;
287 arg.nqueued = nqueued;
288 arg.nskip = nskip;
289 arg.ids = ids;
290 arg.keep = keep;
291 arg.drop = drop;
292 arg.skip = skip;
293 arg.progress_cb = progress_cb;
294 arg.progress_arg = progress_arg;
295 arg.rl = rl;
296 arg.cancel_cb = cancel_cb;
297 arg.cancel_arg = cancel_arg;
298 err = got_privsep_recv_painted_commits(&next_ids,
299 recv_painted_commit, &arg, pack->privsep_child->ibuf);
300 if (err)
301 return err;
303 STAILQ_FOREACH_SAFE(qid, &next_ids, entry, tmp) {
304 struct got_object_qid *old_id;
305 intptr_t qcolor, ocolor;
306 STAILQ_FOREACH(old_id, ids, entry) {
307 if (got_object_id_cmp(&qid->id, &old_id->id))
308 continue;
309 qcolor = (intptr_t)qid->data;
310 ocolor = (intptr_t)old_id->data;
311 STAILQ_REMOVE(&next_ids, qid, got_object_qid, entry);
312 got_object_qid_free(qid);
313 qid = NULL;
314 if (qcolor != ocolor) {
315 got_pack_paint_commit(old_id, qcolor);
316 if (ocolor == COLOR_SKIP)
317 (*nskip)--;
318 else if (qcolor == COLOR_SKIP)
319 (*nskip)++;
321 break;
324 while (!STAILQ_EMPTY(&next_ids)) {
325 qid = STAILQ_FIRST(&next_ids);
326 STAILQ_REMOVE_HEAD(&next_ids, entry);
327 got_pack_paint_commit(qid, color);
328 STAILQ_INSERT_TAIL(ids, qid, entry);
329 (*nqueued)++;
330 if (color == COLOR_SKIP)
331 (*nskip)++;
334 return err;
337 const struct got_error *
338 got_pack_paint_commits(int *ncolored, struct got_object_id_queue *ids, int nids,
339 struct got_object_idset *keep, struct got_object_idset *drop,
340 struct got_object_idset *skip, struct got_repository *repo,
341 got_pack_progress_cb progress_cb, void *progress_arg,
342 struct got_ratelimit *rl, got_cancel_cb cancel_cb, void *cancel_arg)
344 const struct got_error *err = NULL;
345 struct got_commit_object *commit = NULL;
346 struct got_packidx *packidx = NULL;
347 struct got_pack *pack = NULL;
348 const struct got_object_id_queue *parents;
349 struct got_object_qid *qid = NULL;
350 int nqueued = nids, nskip = 0;
351 int idx;
353 while (!STAILQ_EMPTY(ids) && nskip != nqueued) {
354 intptr_t color;
356 if (cancel_cb) {
357 err = cancel_cb(cancel_arg);
358 if (err)
359 break;
362 qid = STAILQ_FIRST(ids);
363 STAILQ_REMOVE_HEAD(ids, entry);
364 nqueued--;
365 color = (intptr_t)qid->data;
366 if (color == COLOR_SKIP)
367 nskip--;
369 if (got_object_idset_contains(skip, &qid->id)) {
370 got_object_qid_free(qid);
371 qid = NULL;
372 continue;
374 if (color == COLOR_KEEP &&
375 got_object_idset_contains(keep, &qid->id)) {
376 got_object_qid_free(qid);
377 qid = NULL;
378 continue;
380 if (color == COLOR_DROP &&
381 got_object_idset_contains(drop, &qid->id)) {
382 got_object_qid_free(qid);
383 qid = NULL;
384 continue;
387 /* Pinned pack may have moved to different cache slot. */
388 pack = got_repo_get_pinned_pack(repo);
390 if (packidx && pack) {
391 idx = got_packidx_get_object_idx(packidx, &qid->id);
392 if (idx != -1) {
393 err = paint_packed_commits(pack, &qid->id,
394 idx, color, ncolored, &nqueued, &nskip,
395 ids, keep, drop, skip, repo,
396 progress_cb, progress_arg, rl,
397 cancel_cb, cancel_arg);
398 if (err)
399 break;
400 got_object_qid_free(qid);
401 qid = NULL;
402 continue;
406 switch (color) {
407 case COLOR_KEEP:
408 if (got_object_idset_contains(drop, &qid->id)) {
409 err = got_pack_paint_commit(qid, COLOR_SKIP);
410 if (err)
411 goto done;
412 } else
413 (*ncolored)++;
414 err = got_object_idset_add(keep, &qid->id, NULL);
415 if (err)
416 goto done;
417 break;
418 case COLOR_DROP:
419 if (got_object_idset_contains(keep, &qid->id)) {
420 err = got_pack_paint_commit(qid, COLOR_SKIP);
421 if (err)
422 goto done;
423 } else
424 (*ncolored)++;
425 err = got_object_idset_add(drop, &qid->id, NULL);
426 if (err)
427 goto done;
428 break;
429 case COLOR_SKIP:
430 if (!got_object_idset_contains(skip, &qid->id)) {
431 err = got_object_idset_add(skip, &qid->id,
432 NULL);
433 if (err)
434 goto done;
436 break;
437 default:
438 /* should not happen */
439 err = got_error_fmt(GOT_ERR_NOT_IMPL,
440 "%s invalid commit color %"PRIdPTR, __func__,
441 color);
442 goto done;
445 err = got_pack_report_progress(progress_cb, progress_arg, rl,
446 *ncolored, 0, 0, 0L, 0, 0, 0, 0);
447 if (err)
448 break;
450 err = got_object_open_as_commit(&commit, repo, &qid->id);
451 if (err)
452 break;
454 parents = got_object_commit_get_parent_ids(commit);
455 if (parents) {
456 struct got_object_qid *pid;
457 color = (intptr_t)qid->data;
458 STAILQ_FOREACH(pid, parents, entry) {
459 err = got_pack_queue_commit_id(ids, &pid->id,
460 color, repo);
461 if (err)
462 break;
463 nqueued++;
464 if (color == COLOR_SKIP)
465 nskip++;
469 if (pack == NULL && (commit->flags & GOT_COMMIT_FLAG_PACKED)) {
470 if (packidx == NULL) {
471 err = got_pack_find_pack_for_commit_painting(
472 &packidx, ids, nqueued, repo);
473 if (err)
474 goto done;
476 if (packidx != NULL) {
477 err = got_pack_cache_pack_for_packidx(&pack,
478 packidx, repo);
479 if (err)
480 goto done;
481 if (pack->privsep_child == NULL) {
482 err = got_pack_start_privsep_child(
483 pack, packidx);
484 if (err)
485 goto done;
487 err = got_privsep_init_commit_painting(
488 pack->privsep_child->ibuf);
489 if (err)
490 goto done;
491 err = send_idset(pack->privsep_child->ibuf,
492 keep);
493 if (err)
494 goto done;
495 err = send_idset(pack->privsep_child->ibuf, drop);
496 if (err)
497 goto done;
498 err = send_idset(pack->privsep_child->ibuf, skip);
499 if (err)
500 goto done;
501 err = got_repo_pin_pack(repo, packidx, pack);
502 if (err)
503 goto done;
507 got_object_commit_close(commit);
508 commit = NULL;
510 got_object_qid_free(qid);
511 qid = NULL;
513 done:
514 if (pack) {
515 const struct got_error *pack_err;
516 pack_err = got_privsep_send_painting_commits_done(
517 pack->privsep_child->ibuf);
518 if (err == NULL)
519 err = pack_err;
521 if (commit)
522 got_object_commit_close(commit);
523 got_object_qid_free(qid);
524 got_repo_unpin_pack(repo);
525 return err;
528 struct load_packed_obj_arg {
529 /* output parameters: */
530 struct got_object_id *id;
531 char *dpath;
532 time_t mtime;
534 /* input parameters: */
535 uint32_t seed;
536 int want_meta;
537 struct got_object_idset *idset;
538 struct got_object_idset *idset_exclude;
539 int loose_obj_only;
540 int *ncolored;
541 int *nfound;
542 int *ntrees;
543 got_pack_progress_cb progress_cb;
544 void *progress_arg;
545 struct got_ratelimit *rl;
546 got_cancel_cb cancel_cb;
547 void *cancel_arg;
548 };
550 static const struct got_error *
551 load_packed_commit_id(void *arg, time_t mtime, struct got_object_id *id,
552 struct got_repository *repo)
554 struct load_packed_obj_arg *a = arg;
556 if (got_object_idset_contains(a->idset, id) ||
557 got_object_idset_contains(a->idset_exclude, id))
558 return NULL;
560 return got_pack_add_object(a->want_meta,
561 a->want_meta ? a->idset : a->idset_exclude,
562 id, "", GOT_OBJ_TYPE_COMMIT, mtime, a->seed, a->loose_obj_only,
563 repo, a->ncolored, a->nfound, a->ntrees,
564 a->progress_cb, a->progress_arg, a->rl);
567 static const struct got_error *
568 load_packed_tree_ids(void *arg, struct got_tree_object *tree, time_t mtime,
569 struct got_object_id *id, const char *dpath, struct got_repository *repo)
571 const struct got_error *err;
572 struct load_packed_obj_arg *a = arg;
573 const char *relpath;
575 /*
576 * When we receive a tree's ID and path but not the tree itself,
577 * this tree object was not found in the pack file. This is the
578 * last time we are being called for this optimized traversal.
579 * Return from here and switch to loading objects the slow way.
580 */
581 if (tree == NULL) {
582 free(a->id);
583 a->id = got_object_id_dup(id);
584 if (a->id == NULL) {
585 err = got_error_from_errno("got_object_id_dup");
586 free(a->dpath);
587 a->dpath = NULL;
588 return err;
591 free(a->dpath);
592 a->dpath = strdup(dpath);
593 if (a->dpath == NULL) {
594 err = got_error_from_errno("strdup");
595 free(a->id);
596 a->id = NULL;
597 return err;
600 a->mtime = mtime;
601 return NULL;
604 if (got_object_idset_contains(a->idset, id) ||
605 got_object_idset_contains(a->idset_exclude, id))
606 return NULL;
608 relpath = dpath;
609 while (relpath[0] == '/')
610 relpath++;
612 err = got_pack_add_object(a->want_meta,
613 a->want_meta ? a->idset : a->idset_exclude,
614 id, relpath, GOT_OBJ_TYPE_TREE, mtime, a->seed,
615 a->loose_obj_only, repo, a->ncolored, a->nfound, a->ntrees,
616 a->progress_cb, a->progress_arg, a->rl);
617 if (err)
618 return err;
620 return got_pack_load_tree_entries(NULL, a->want_meta, a->idset,
621 a->idset_exclude, tree, dpath, mtime, a->seed, repo,
622 a->loose_obj_only, a->ncolored, a->nfound, a->ntrees,
623 a->progress_cb, a->progress_arg, a->rl,
624 a->cancel_cb, a->cancel_arg);
627 const struct got_error *
628 got_pack_load_packed_object_ids(int *found_all_objects,
629 struct got_object_id **ours, int nours,
630 struct got_object_id **theirs, int ntheirs,
631 int want_meta, uint32_t seed, struct got_object_idset *idset,
632 struct got_object_idset *idset_exclude, int loose_obj_only,
633 struct got_repository *repo, struct got_packidx *packidx,
634 int *ncolored, int *nfound, int *ntrees,
635 got_pack_progress_cb progress_cb, void *progress_arg,
636 struct got_ratelimit *rl, got_cancel_cb cancel_cb, void *cancel_arg)
638 const struct got_error *err = NULL;
639 struct load_packed_obj_arg lpa;
641 memset(&lpa, 0, sizeof(lpa));
642 lpa.seed = seed;
643 lpa.want_meta = want_meta;
644 lpa.idset = idset;
645 lpa.idset_exclude = idset_exclude;
646 lpa.loose_obj_only = loose_obj_only;
647 lpa.ncolored = ncolored;
648 lpa.nfound = nfound;
649 lpa.ntrees = ntrees;
650 lpa.progress_cb = progress_cb;
651 lpa.progress_arg = progress_arg;
652 lpa.rl = rl;
653 lpa.cancel_cb = cancel_cb;
654 lpa.cancel_arg = cancel_arg;
656 /* Attempt to load objects via got-read-pack, as far as possible. */
657 err = got_object_enumerate(found_all_objects, load_packed_commit_id,
658 load_packed_tree_ids, &lpa, ours, nours, theirs, ntheirs,
659 packidx, repo);
660 if (err)
661 return err;
663 if (lpa.id == NULL)
664 return NULL;
666 /*
667 * An incomplete tree hierarchy was present in the pack file
668 * and caused loading to be aborted.
669 * Continue loading trees the slow way.
670 */
671 err = got_pack_load_tree(want_meta, idset, idset_exclude,
672 lpa.id, lpa.dpath, lpa.mtime, seed, repo, loose_obj_only,
673 ncolored, nfound, ntrees, progress_cb, progress_arg, rl,
674 cancel_cb, cancel_arg);
675 free(lpa.id);
676 free(lpa.dpath);
677 return err;