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_object.h"
43 #include "got_lib_object_cache.h"
44 #include "got_lib_object_idset.h"
45 #include "got_lib_ratelimit.h"
46 #include "got_lib_pack.h"
47 #include "got_lib_pack_create.h"
48 #include "got_lib_repository.h"
50 static const struct got_error
*
51 get_base_object_id(struct got_object_id
*base_id
, struct got_packidx
*packidx
,
54 const struct got_error
*err
;
57 err
= got_packidx_get_offset_idx(&idx
, packidx
, base_offset
);
61 return got_error(GOT_ERR_BAD_PACKIDX
);
63 return got_packidx_get_object_id(base_id
, packidx
, idx
);
66 struct search_deltas_arg
{
67 struct got_pack_metavec
*v
;
68 struct got_packidx
*packidx
;
69 struct got_pack
*pack
;
70 struct got_object_idset
*idset
;
71 int ncolored
, nfound
, ntrees
, ncommits
;
72 got_pack_progress_cb progress_cb
;
74 struct got_ratelimit
*rl
;
75 got_cancel_cb cancel_cb
;
79 static const struct got_error
*
80 search_delta_for_object(struct got_object_id
*id
, void *data
, void *arg
)
82 const struct got_error
*err
;
83 struct search_deltas_arg
*a
= arg
;
85 uint8_t *delta_buf
= NULL
;
86 uint64_t base_size
, result_size
;
87 size_t delta_size
, delta_compressed_size
;
88 off_t delta_offset
, delta_data_offset
, base_offset
;
89 struct got_object_id base_id
;
92 err
= a
->cancel_cb(a
->cancel_arg
);
97 obj_idx
= got_packidx_get_object_idx(a
->packidx
, id
);
99 return NULL
; /* object not present in our pack file */
101 err
= got_packfile_extract_raw_delta(&delta_buf
, &delta_size
,
102 &delta_compressed_size
, &delta_offset
, &delta_data_offset
,
103 &base_offset
, &base_id
, &base_size
, &result_size
,
104 a
->pack
, a
->packidx
, obj_idx
);
106 if (err
->code
== GOT_ERR_OBJ_TYPE
)
107 return NULL
; /* object not stored as a delta */
112 * If this is an offset delta we must determine the base
113 * object ID ourselves.
115 if (base_offset
!= 0) {
116 err
= get_base_object_id(&base_id
, a
->packidx
, base_offset
);
121 if (got_object_idset_contains(a
->idset
, &base_id
)) {
122 struct got_pack_meta
*m
, *base
;
124 m
= got_object_idset_get(a
->idset
, id
);
126 err
= got_error_msg(GOT_ERR_NO_OBJ
,
127 "delta object not found");
131 base
= got_object_idset_get(a
->idset
, &base_id
);
133 err
= got_error_msg(GOT_ERR_NO_OBJ
,
134 "delta base object not found");
138 m
->base_obj_id
= got_object_id_dup(&base_id
);
139 if (m
->base_obj_id
== NULL
) {
140 err
= got_error_from_errno("got_object_id_dup");
145 m
->size
= result_size
;
146 m
->delta_len
= delta_size
;
147 m
->delta_compressed_len
= delta_compressed_size
;
148 m
->reused_delta_offset
= delta_data_offset
;
151 err
= got_pack_add_meta(m
, a
->v
);
155 err
= got_pack_report_progress(a
->progress_cb
, a
->progress_arg
,
156 a
->rl
, a
->ncolored
, a
->nfound
, a
->ntrees
, 0L, a
->ncommits
,
157 got_object_idset_num_elements(a
->idset
), a
->v
->nmeta
, 0);
166 const struct got_error
*
167 got_pack_search_deltas(struct got_packidx
**packidx
, struct got_pack
**pack
,
168 struct got_pack_metavec
*v
, struct got_object_idset
*idset
,
169 int ncolored
, int nfound
, int ntrees
, int ncommits
,
170 struct got_repository
*repo
,
171 got_pack_progress_cb progress_cb
, void *progress_arg
,
172 struct got_ratelimit
*rl
, got_cancel_cb cancel_cb
, void *cancel_arg
)
174 const struct got_error
*err
= NULL
;
175 struct search_deltas_arg sda
;
180 err
= got_pack_find_pack_for_reuse(packidx
, repo
);
184 if (*packidx
== NULL
)
187 err
= got_pack_cache_pack_for_packidx(pack
, *packidx
, repo
);
191 memset(&sda
, 0, sizeof(sda
));
195 sda
.packidx
= *packidx
;
196 sda
.ncolored
= ncolored
;
199 sda
.ncommits
= ncommits
;
200 sda
.progress_cb
= progress_cb
;
201 sda
.progress_arg
= progress_arg
;
203 sda
.cancel_cb
= cancel_cb
;
204 sda
.cancel_arg
= cancel_arg
;
205 return got_object_idset_for_each(idset
, search_delta_for_object
, &sda
);
208 const struct got_error
*
209 got_pack_load_packed_object_ids(int *found_all_objects
,
210 struct got_object_id
**ours
, int nours
,
211 struct got_object_id
**theirs
, int ntheirs
,
212 int want_meta
, uint32_t seed
, struct got_object_idset
*idset
,
213 struct got_object_idset
*idset_exclude
, int loose_obj_only
,
214 struct got_repository
*repo
, struct got_packidx
*packidx
,
215 int *ncolored
, int *nfound
, int *ntrees
,
216 got_pack_progress_cb progress_cb
, void *progress_arg
,
217 struct got_ratelimit
*rl
, got_cancel_cb cancel_cb
, void *cancel_arg
)
219 /* We do not need this optimized traversal while using direct I/O. */
220 *found_all_objects
= 0;
224 const struct got_error
*
225 got_pack_paint_commits(int *ncolored
, struct got_object_id_queue
*ids
, int nids
,
226 struct got_object_idset
*keep
, struct got_object_idset
*drop
,
227 struct got_object_idset
*skip
, struct got_repository
*repo
,
228 got_pack_progress_cb progress_cb
, void *progress_arg
,
229 struct got_ratelimit
*rl
, got_cancel_cb cancel_cb
, void *cancel_arg
)
231 const struct got_error
*err
= NULL
;
232 struct got_commit_object
*commit
= NULL
;
233 struct got_packidx
*packidx
= NULL
;
234 struct got_pack
*pack
= NULL
;
235 const struct got_object_id_queue
*parents
;
236 struct got_object_qid
*qid
= NULL
;
237 int nqueued
= nids
, nskip
= 0;
239 while (!STAILQ_EMPTY(ids
) && nskip
!= nqueued
) {
243 err
= cancel_cb(cancel_arg
);
248 qid
= STAILQ_FIRST(ids
);
249 STAILQ_REMOVE_HEAD(ids
, entry
);
251 color
= (intptr_t)qid
->data
;
252 if (color
== COLOR_SKIP
)
255 if (got_object_idset_contains(skip
, &qid
->id
)) {
256 got_object_qid_free(qid
);
260 if (color
== COLOR_KEEP
&&
261 got_object_idset_contains(keep
, &qid
->id
)) {
262 got_object_qid_free(qid
);
266 if (color
== COLOR_DROP
&&
267 got_object_idset_contains(drop
, &qid
->id
)) {
268 got_object_qid_free(qid
);
275 if (got_object_idset_contains(drop
, &qid
->id
)) {
276 err
= got_pack_paint_commit(qid
, COLOR_SKIP
);
281 err
= got_object_idset_add(keep
, &qid
->id
, NULL
);
286 if (got_object_idset_contains(keep
, &qid
->id
)) {
287 err
= got_pack_paint_commit(qid
, COLOR_SKIP
);
292 err
= got_object_idset_add(drop
, &qid
->id
, NULL
);
297 if (!got_object_idset_contains(skip
, &qid
->id
)) {
298 err
= got_object_idset_add(skip
, &qid
->id
,
305 /* should not happen */
306 err
= got_error_fmt(GOT_ERR_NOT_IMPL
,
307 "%s invalid commit color %"PRIdPTR
, __func__
,
312 err
= got_pack_report_progress(progress_cb
, progress_arg
, rl
,
313 *ncolored
, 0, 0, 0L, 0, 0, 0, 0);
317 err
= got_object_open_as_commit(&commit
, repo
, &qid
->id
);
321 parents
= got_object_commit_get_parent_ids(commit
);
323 struct got_object_qid
*pid
;
324 color
= (intptr_t)qid
->data
;
325 STAILQ_FOREACH(pid
, parents
, entry
) {
326 err
= got_pack_queue_commit_id(ids
, &pid
->id
,
331 if (color
== COLOR_SKIP
)
336 if (pack
== NULL
&& (commit
->flags
& GOT_COMMIT_FLAG_PACKED
)) {
338 * We now know that at least one pack file exists.
339 * Pin a suitable pack to ensure it remains cached
340 * while we are churning through commit history.
342 if (packidx
== NULL
) {
343 err
= got_pack_find_pack_for_commit_painting(
344 &packidx
, ids
, nqueued
, repo
);
348 if (packidx
!= NULL
) {
349 err
= got_pack_cache_pack_for_packidx(&pack
,
353 err
= got_repo_pin_pack(repo
, packidx
, pack
);
359 got_object_commit_close(commit
);
362 got_object_qid_free(qid
);
367 got_object_commit_close(commit
);
368 got_object_qid_free(qid
);
369 got_repo_unpin_pack(repo
);