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 <sys/types.h>
19 #include <sys/queue.h>
33 #include "got_error.h"
34 #include "got_cancel.h"
35 #include "got_object.h"
36 #include "got_reference.h"
37 #include "got_repository_admin.h"
40 #include "got_lib_delta.h"
41 #include "got_lib_object.h"
42 #include "got_lib_object_cache.h"
43 #include "got_lib_object_idset.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 static const struct got_error
*
50 get_base_object_id(struct got_object_id
*base_id
, struct got_packidx
*packidx
,
53 const struct got_error
*err
;
56 err
= got_packidx_get_offset_idx(&idx
, packidx
, base_offset
);
60 return got_error(GOT_ERR_BAD_PACKIDX
);
62 return got_packidx_get_object_id(base_id
, packidx
, idx
);
65 struct search_deltas_arg
{
66 struct got_pack_metavec
*v
;
67 struct got_packidx
*packidx
;
68 struct got_pack
*pack
;
69 struct got_object_idset
*idset
;
70 int ncolored
, nfound
, ntrees
, ncommits
;
71 got_pack_progress_cb progress_cb
;
73 struct got_ratelimit
*rl
;
74 got_cancel_cb cancel_cb
;
78 static const struct got_error
*
79 search_delta_for_object(struct got_object_id
*id
, void *data
, void *arg
)
81 const struct got_error
*err
;
82 struct search_deltas_arg
*a
= arg
;
84 uint8_t *delta_buf
= NULL
;
85 uint64_t base_size
, result_size
;
86 size_t delta_size
, delta_compressed_size
;
87 off_t delta_offset
, delta_data_offset
, base_offset
;
88 struct got_object_id base_id
;
91 err
= a
->cancel_cb(a
->cancel_arg
);
96 obj_idx
= got_packidx_get_object_idx(a
->packidx
, id
);
98 return NULL
; /* object not present in our pack file */
100 err
= got_packfile_extract_raw_delta(&delta_buf
, &delta_size
,
101 &delta_compressed_size
, &delta_offset
, &delta_data_offset
,
102 &base_offset
, &base_id
, &base_size
, &result_size
,
103 a
->pack
, a
->packidx
, obj_idx
);
105 if (err
->code
== GOT_ERR_OBJ_TYPE
)
106 return NULL
; /* object not stored as a delta */
111 * If this is an offset delta we must determine the base
112 * object ID ourselves.
114 if (base_offset
!= 0) {
115 err
= get_base_object_id(&base_id
, a
->packidx
, base_offset
);
120 if (got_object_idset_contains(a
->idset
, &base_id
)) {
121 struct got_pack_meta
*m
, *base
;
123 m
= got_object_idset_get(a
->idset
, id
);
125 err
= got_error_msg(GOT_ERR_NO_OBJ
,
126 "delta object not found");
130 base
= got_object_idset_get(a
->idset
, &base_id
);
132 err
= got_error_msg(GOT_ERR_NO_OBJ
,
133 "delta base object not found");
137 m
->base_obj_id
= got_object_id_dup(&base_id
);
138 if (m
->base_obj_id
== NULL
) {
139 err
= got_error_from_errno("got_object_id_dup");
144 m
->size
= result_size
;
145 m
->delta_len
= delta_size
;
146 m
->delta_compressed_len
= delta_compressed_size
;
147 m
->reused_delta_offset
= delta_data_offset
;
150 err
= got_pack_add_meta(m
, a
->v
);
154 err
= got_pack_report_progress(a
->progress_cb
, a
->progress_arg
,
155 a
->rl
, a
->ncolored
, a
->nfound
, a
->ntrees
, 0L, a
->ncommits
,
156 got_object_idset_num_elements(a
->idset
), a
->v
->nmeta
, 0);
165 const struct got_error
*
166 got_pack_search_deltas(struct got_packidx
**packidx
, struct got_pack
**pack
,
167 struct got_pack_metavec
*v
, struct got_object_idset
*idset
,
168 int ncolored
, int nfound
, int ntrees
, int ncommits
,
169 struct got_repository
*repo
,
170 got_pack_progress_cb progress_cb
, void *progress_arg
,
171 struct got_ratelimit
*rl
, got_cancel_cb cancel_cb
, void *cancel_arg
)
173 const struct got_error
*err
= NULL
;
174 struct search_deltas_arg sda
;
179 err
= got_pack_find_pack_for_reuse(packidx
, repo
);
183 if (*packidx
== NULL
)
186 err
= got_pack_cache_pack_for_packidx(pack
, *packidx
, repo
);
190 memset(&sda
, 0, sizeof(sda
));
194 sda
.packidx
= *packidx
;
195 sda
.ncolored
= ncolored
;
198 sda
.ncommits
= ncommits
;
199 sda
.progress_cb
= progress_cb
;
200 sda
.progress_arg
= progress_arg
;
202 sda
.cancel_cb
= cancel_cb
;
203 sda
.cancel_arg
= cancel_arg
;
204 return got_object_idset_for_each(idset
, search_delta_for_object
, &sda
);
207 const struct got_error
*
208 got_pack_load_packed_object_ids(int *found_all_objects
,
209 struct got_object_id
**ours
, int nours
,
210 struct got_object_id
**theirs
, int ntheirs
,
211 int want_meta
, uint32_t seed
, struct got_object_idset
*idset
,
212 struct got_object_idset
*idset_exclude
, int loose_obj_only
,
213 struct got_repository
*repo
, struct got_packidx
*packidx
,
214 int *ncolored
, int *nfound
, int *ntrees
,
215 got_pack_progress_cb progress_cb
, void *progress_arg
,
216 struct got_ratelimit
*rl
, got_cancel_cb cancel_cb
, void *cancel_arg
)
218 /* We do not need this optimized traversal while using direct I/O. */
219 *found_all_objects
= 0;
223 const struct got_error
*
224 got_pack_paint_commits(int *ncolored
, struct got_object_id_queue
*ids
, int nids
,
225 struct got_object_idset
*keep
, struct got_object_idset
*drop
,
226 struct got_object_idset
*skip
, struct got_repository
*repo
,
227 got_pack_progress_cb progress_cb
, void *progress_arg
,
228 struct got_ratelimit
*rl
, got_cancel_cb cancel_cb
, void *cancel_arg
)
230 const struct got_error
*err
= NULL
;
231 struct got_commit_object
*commit
= NULL
;
232 struct got_packidx
*packidx
= NULL
;
233 struct got_pack
*pack
= NULL
;
234 const struct got_object_id_queue
*parents
;
235 struct got_object_qid
*qid
= NULL
;
236 int nqueued
= nids
, nskip
= 0;
238 while (!STAILQ_EMPTY(ids
) && nskip
!= nqueued
) {
242 err
= cancel_cb(cancel_arg
);
247 qid
= STAILQ_FIRST(ids
);
248 STAILQ_REMOVE_HEAD(ids
, entry
);
250 color
= (intptr_t)qid
->data
;
251 if (color
== COLOR_SKIP
)
254 if (got_object_idset_contains(skip
, &qid
->id
)) {
255 got_object_qid_free(qid
);
259 if (color
== COLOR_KEEP
&&
260 got_object_idset_contains(keep
, &qid
->id
)) {
261 got_object_qid_free(qid
);
265 if (color
== COLOR_DROP
&&
266 got_object_idset_contains(drop
, &qid
->id
)) {
267 got_object_qid_free(qid
);
274 if (got_object_idset_contains(drop
, &qid
->id
)) {
275 err
= got_pack_paint_commit(qid
, COLOR_SKIP
);
280 err
= got_object_idset_add(keep
, &qid
->id
, NULL
);
285 if (got_object_idset_contains(keep
, &qid
->id
)) {
286 err
= got_pack_paint_commit(qid
, COLOR_SKIP
);
291 err
= got_object_idset_add(drop
, &qid
->id
, NULL
);
296 if (!got_object_idset_contains(skip
, &qid
->id
)) {
297 err
= got_object_idset_add(skip
, &qid
->id
,
304 /* should not happen */
305 err
= got_error_fmt(GOT_ERR_NOT_IMPL
,
306 "%s invalid commit color %"PRIdPTR
, __func__
,
311 err
= got_pack_report_progress(progress_cb
, progress_arg
, rl
,
312 *ncolored
, 0, 0, 0L, 0, 0, 0, 0);
316 err
= got_object_open_as_commit(&commit
, repo
, &qid
->id
);
320 parents
= got_object_commit_get_parent_ids(commit
);
322 struct got_object_qid
*pid
;
323 color
= (intptr_t)qid
->data
;
324 STAILQ_FOREACH(pid
, parents
, entry
) {
325 err
= got_pack_queue_commit_id(ids
, &pid
->id
,
330 if (color
== COLOR_SKIP
)
335 if (pack
== NULL
&& (commit
->flags
& GOT_COMMIT_FLAG_PACKED
)) {
337 * We now know that at least one pack file exists.
338 * Pin a suitable pack to ensure it remains cached
339 * while we are churning through commit history.
341 if (packidx
== NULL
) {
342 err
= got_pack_find_pack_for_commit_painting(
343 &packidx
, ids
, nqueued
, repo
);
347 if (packidx
!= NULL
) {
348 err
= got_pack_cache_pack_for_packidx(&pack
,
352 err
= got_repo_pin_pack(repo
, packidx
, pack
);
358 got_object_commit_close(commit
);
361 got_object_qid_free(qid
);
366 got_object_commit_close(commit
);
367 got_object_qid_free(qid
);
368 got_repo_unpin_pack(repo
);