2 * Copyright (c) 2020 Ori Bernstein
3 * Copyright (c) 2021, 2022 Stefan Sperling <stsp@openbsd.org>
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.
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.
18 #include "got_compat.h"
20 #include <sys/types.h>
21 #include <sys/queue.h>
34 #include "got_error.h"
35 #include "got_cancel.h"
36 #include "got_object.h"
37 #include "got_reference.h"
38 #include "got_repository_admin.h"
41 #include "got_lib_delta.h"
42 #include "got_lib_hash.h"
43 #include "got_lib_object.h"
44 #include "got_lib_object_cache.h"
45 #include "got_lib_object_idset.h"
46 #include "got_lib_ratelimit.h"
47 #include "got_lib_pack.h"
48 #include "got_lib_pack_create.h"
49 #include "got_lib_repository.h"
51 static const struct got_error
*
52 get_base_object_id(struct got_object_id
*base_id
, struct got_packidx
*packidx
,
55 const struct got_error
*err
;
58 err
= got_packidx_get_offset_idx(&idx
, packidx
, base_offset
);
62 return got_error(GOT_ERR_BAD_PACKIDX
);
64 return got_packidx_get_object_id(base_id
, packidx
, idx
);
67 struct search_deltas_arg
{
68 struct got_pack_metavec
*v
;
69 struct got_packidx
*packidx
;
70 struct got_pack
*pack
;
71 struct got_object_idset
*idset
;
72 int ncolored
, nfound
, ntrees
, ncommits
;
73 got_pack_progress_cb progress_cb
;
75 struct got_ratelimit
*rl
;
76 got_cancel_cb cancel_cb
;
80 static const struct got_error
*
81 search_delta_for_object(struct got_object_id
*id
, void *data
, void *arg
)
83 const struct got_error
*err
;
84 struct search_deltas_arg
*a
= arg
;
86 uint8_t *delta_buf
= NULL
;
87 uint64_t base_size
, result_size
;
88 size_t delta_size
, delta_compressed_size
;
89 off_t delta_offset
, delta_data_offset
, base_offset
;
90 struct got_object_id base_id
;
93 err
= a
->cancel_cb(a
->cancel_arg
);
98 obj_idx
= got_packidx_get_object_idx(a
->packidx
, id
);
100 return NULL
; /* object not present in our pack file */
102 err
= got_packfile_extract_raw_delta(&delta_buf
, &delta_size
,
103 &delta_compressed_size
, &delta_offset
, &delta_data_offset
,
104 &base_offset
, &base_id
, &base_size
, &result_size
,
105 a
->pack
, a
->packidx
, obj_idx
);
107 if (err
->code
== GOT_ERR_OBJ_TYPE
)
108 return NULL
; /* object not stored as a delta */
113 * If this is an offset delta we must determine the base
114 * object ID ourselves.
116 if (base_offset
!= 0) {
117 err
= get_base_object_id(&base_id
, a
->packidx
, base_offset
);
122 if (got_object_idset_contains(a
->idset
, &base_id
)) {
123 struct got_pack_meta
*m
, *base
;
125 m
= got_object_idset_get(a
->idset
, id
);
127 err
= got_error_msg(GOT_ERR_NO_OBJ
,
128 "delta object not found");
132 base
= got_object_idset_get(a
->idset
, &base_id
);
134 err
= got_error_msg(GOT_ERR_NO_OBJ
,
135 "delta base object not found");
139 m
->base_obj_id
= got_object_id_dup(&base_id
);
140 if (m
->base_obj_id
== NULL
) {
141 err
= got_error_from_errno("got_object_id_dup");
146 m
->size
= result_size
;
147 m
->delta_len
= delta_size
;
148 m
->delta_compressed_len
= delta_compressed_size
;
149 m
->reused_delta_offset
= delta_data_offset
;
152 err
= got_pack_add_meta(m
, a
->v
);
156 err
= got_pack_report_progress(a
->progress_cb
, a
->progress_arg
,
157 a
->rl
, a
->ncolored
, a
->nfound
, a
->ntrees
, 0L, a
->ncommits
,
158 got_object_idset_num_elements(a
->idset
), a
->v
->nmeta
, 0, 0);
167 const struct got_error
*
168 got_pack_search_deltas(struct got_packidx
**packidx
, struct got_pack
**pack
,
169 struct got_pack_metavec
*v
, struct got_object_idset
*idset
,
170 int ncolored
, int nfound
, int ntrees
, int ncommits
,
171 struct got_repository
*repo
,
172 got_pack_progress_cb progress_cb
, void *progress_arg
,
173 struct got_ratelimit
*rl
, got_cancel_cb cancel_cb
, void *cancel_arg
)
175 const struct got_error
*err
= NULL
;
176 struct search_deltas_arg sda
;
181 err
= got_pack_find_pack_for_reuse(packidx
, repo
);
185 if (*packidx
== NULL
)
188 err
= got_pack_cache_pack_for_packidx(pack
, *packidx
, repo
);
192 memset(&sda
, 0, sizeof(sda
));
196 sda
.packidx
= *packidx
;
197 sda
.ncolored
= ncolored
;
200 sda
.ncommits
= ncommits
;
201 sda
.progress_cb
= progress_cb
;
202 sda
.progress_arg
= progress_arg
;
204 sda
.cancel_cb
= cancel_cb
;
205 sda
.cancel_arg
= cancel_arg
;
206 return got_object_idset_for_each(idset
, search_delta_for_object
, &sda
);
209 const struct got_error
*
210 got_pack_load_packed_object_ids(int *found_all_objects
,
211 struct got_object_id
**ours
, int nours
,
212 struct got_object_id
**theirs
, int ntheirs
,
213 int want_meta
, uint32_t seed
, struct got_object_idset
*idset
,
214 struct got_object_idset
*idset_exclude
, int loose_obj_only
,
215 struct got_repository
*repo
, struct got_packidx
*packidx
,
216 int *ncolored
, int *nfound
, int *ntrees
,
217 got_pack_progress_cb progress_cb
, void *progress_arg
,
218 struct got_ratelimit
*rl
, got_cancel_cb cancel_cb
, void *cancel_arg
)
220 /* We do not need this optimized traversal while using direct I/O. */
221 *found_all_objects
= 0;
225 const struct got_error
*
226 got_pack_paint_commits(int *ncolored
, struct got_object_id_queue
*ids
, int nids
,
227 struct got_object_idset
*keep
, struct got_object_idset
*drop
,
228 struct got_object_idset
*skip
, struct got_repository
*repo
,
229 got_pack_progress_cb progress_cb
, void *progress_arg
,
230 struct got_ratelimit
*rl
, got_cancel_cb cancel_cb
, void *cancel_arg
)
232 const struct got_error
*err
= NULL
;
233 struct got_commit_object
*commit
= NULL
;
234 struct got_packidx
*packidx
= NULL
;
235 struct got_pack
*pack
= NULL
;
236 const struct got_object_id_queue
*parents
;
237 struct got_object_qid
*qid
= NULL
;
238 int nqueued
= nids
, nskip
= 0;
240 while (!STAILQ_EMPTY(ids
) && nskip
!= nqueued
) {
244 err
= cancel_cb(cancel_arg
);
249 qid
= STAILQ_FIRST(ids
);
250 STAILQ_REMOVE_HEAD(ids
, entry
);
252 color
= (intptr_t)qid
->data
;
253 if (color
== COLOR_SKIP
)
256 if (got_object_idset_contains(skip
, &qid
->id
)) {
257 got_object_qid_free(qid
);
261 if (color
== COLOR_KEEP
&&
262 got_object_idset_contains(keep
, &qid
->id
)) {
263 got_object_qid_free(qid
);
267 if (color
== COLOR_DROP
&&
268 got_object_idset_contains(drop
, &qid
->id
)) {
269 got_object_qid_free(qid
);
276 if (got_object_idset_contains(drop
, &qid
->id
)) {
277 err
= got_pack_paint_commit(qid
, COLOR_SKIP
);
280 err
= got_object_idset_add(skip
, &qid
->id
,
284 err
= got_pack_repaint_parent_commits(&qid
->id
,
285 COLOR_SKIP
, skip
, skip
, repo
);
290 err
= got_object_idset_add(keep
, &qid
->id
, NULL
);
295 if (got_object_idset_contains(keep
, &qid
->id
)) {
296 err
= got_pack_paint_commit(qid
, COLOR_SKIP
);
299 err
= got_object_idset_add(skip
, &qid
->id
,
303 err
= got_pack_repaint_parent_commits(&qid
->id
,
304 COLOR_SKIP
, skip
, skip
, repo
);
309 err
= got_object_idset_add(drop
, &qid
->id
, NULL
);
314 if (!got_object_idset_contains(skip
, &qid
->id
)) {
315 err
= got_object_idset_add(skip
, &qid
->id
,
322 /* should not happen */
323 err
= got_error_fmt(GOT_ERR_NOT_IMPL
,
324 "%s invalid commit color %"PRIdPTR
, __func__
,
329 err
= got_pack_report_progress(progress_cb
, progress_arg
, rl
,
330 *ncolored
, 0, 0, 0L, 0, 0, 0, 0, 0);
334 err
= got_object_open_as_commit(&commit
, repo
, &qid
->id
);
338 parents
= got_object_commit_get_parent_ids(commit
);
340 struct got_object_qid
*pid
;
341 color
= (intptr_t)qid
->data
;
342 STAILQ_FOREACH(pid
, parents
, entry
) {
343 err
= got_pack_queue_commit_id(ids
, &pid
->id
,
348 if (color
== COLOR_SKIP
)
353 if (pack
== NULL
&& (commit
->flags
& GOT_COMMIT_FLAG_PACKED
)) {
355 * We now know that at least one pack file exists.
356 * Pin a suitable pack to ensure it remains cached
357 * while we are churning through commit history.
359 if (packidx
== NULL
) {
360 err
= got_pack_find_pack_for_commit_painting(
361 &packidx
, ids
, nqueued
, repo
);
365 if (packidx
!= NULL
) {
366 err
= got_pack_cache_pack_for_packidx(&pack
,
370 err
= got_repo_pin_pack(repo
, packidx
, pack
);
376 got_object_commit_close(commit
);
379 got_object_qid_free(qid
);
384 got_object_commit_close(commit
);
385 got_object_qid_free(qid
);
386 got_repo_unpin_pack(repo
);