2 * Copyright (c) 2020 Ori Bernstein
3 * Copyright (c) 2021 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>
31 #include "got_error.h"
32 #include "got_cancel.h"
33 #include "got_object.h"
35 #include "got_reference.h"
36 #include "got_repository_admin.h"
37 #include "got_opentemp.h"
39 #include "got_lib_deltify.h"
40 #include "got_lib_delta.h"
41 #include "got_lib_object.h"
42 #include "got_lib_object_idset.h"
43 #include "got_lib_object_cache.h"
44 #include "got_lib_deflate.h"
45 #include "got_lib_pack.h"
46 #include "got_lib_privsep.h"
47 #include "got_lib_repository.h"
50 #define MAX(_a,_b) ((_a) > (_b) ? (_a) : (_b))
53 struct got_pack_meta
{
54 struct got_object_id id
;
59 /* The best delta we picked */
60 struct got_pack_meta
*head
;
61 struct got_pack_meta
*prev
;
62 struct got_delta_instruction
*deltas
;
66 /* Only used for delta window */
67 struct got_delta_table
*dtab
;
69 /* Only used for writing offset deltas */
73 struct got_pack_metavec
{
74 struct got_pack_meta
**meta
;
79 static const struct got_error
*
80 alloc_meta(struct got_pack_meta
**new, struct got_object_id
*id
,
81 const char *path
, int obj_type
, time_t mtime
)
83 const struct got_error
*err
= NULL
;
84 struct got_pack_meta
*m
;
88 m
= calloc(1, sizeof(*m
));
90 return got_error_from_errno("calloc");
92 memcpy(&m
->id
, id
, sizeof(m
->id
));
94 m
->path
= strdup(path
);
95 if (m
->path
== NULL
) {
96 err
= got_error_from_errno("strdup");
101 m
->obj_type
= obj_type
;
108 clear_meta(struct got_pack_meta
*meta
)
119 free_nmeta(struct got_pack_meta
**meta
, int nmeta
)
123 for (i
= 0; i
< nmeta
; i
++)
129 delta_order_cmp(const void *pa
, const void *pb
)
131 struct got_pack_meta
*a
, *b
;
134 a
= *(struct got_pack_meta
**)pa
;
135 b
= *(struct got_pack_meta
**)pb
;
137 if (a
->obj_type
!= b
->obj_type
)
138 return a
->obj_type
- b
->obj_type
;
139 cmp
= strcmp(a
->path
, b
->path
);
142 if (a
->mtime
!= b
->mtime
)
143 return a
->mtime
- b
->mtime
;
144 return got_object_id_cmp(&a
->id
, &b
->id
);
148 delta_size(struct got_delta_instruction
*deltas
, int ndeltas
)
151 for (i
= 0; i
< ndeltas
; i
++) {
153 size
+= GOT_DELTA_SIZE_SHIFT
;
155 size
+= deltas
[i
].len
+ 1;
161 static const struct got_error
*
162 pick_deltas(struct got_pack_meta
**meta
, int nmeta
, int nours
,
163 struct got_repository
*repo
,
164 got_pack_progress_cb progress_cb
, void *progress_arg
,
165 got_cancel_cb cancel_cb
, void *cancel_arg
)
167 const struct got_error
*err
= NULL
;
168 struct got_pack_meta
*m
= NULL
, *base
= NULL
;
169 struct got_raw_object
*raw
= NULL
, *base_raw
= NULL
;
170 struct got_delta_instruction
*deltas
;
171 int i
, j
, size
, ndeltas
, best
;
172 const int max_base_candidates
= 10;
174 qsort(meta
, nmeta
, sizeof(struct got_pack_meta
*), delta_order_cmp
);
175 for (i
= 0; i
< nmeta
; i
++) {
177 err
= (*cancel_cb
)(cancel_arg
);
182 err
= progress_cb(progress_arg
, 0L, nours
, nmeta
, i
, 0);
190 if (m
->obj_type
== GOT_OBJ_TYPE_COMMIT
||
191 m
->obj_type
== GOT_OBJ_TYPE_TAG
)
194 err
= got_object_raw_open(&raw
, repo
, &m
->id
, 8192);
198 err
= got_deltify_init(&m
->dtab
, raw
->f
, raw
->hdrlen
,
199 raw
->size
+ raw
->hdrlen
);
203 if (i
> max_base_candidates
) {
204 struct got_pack_meta
*n
= NULL
;
205 n
= meta
[i
- (max_base_candidates
+ 1)];
206 got_deltify_free(n
->dtab
);
211 for (j
= MAX(0, i
- max_base_candidates
); j
< i
; j
++) {
213 err
= (*cancel_cb
)(cancel_arg
);
218 /* long chains make unpacking slow, avoid such bases */
219 if (base
->nchain
>= 128 ||
220 base
->obj_type
!= m
->obj_type
)
223 err
= got_object_raw_open(&base_raw
, repo
, &base
->id
,
227 err
= got_deltify(&deltas
, &ndeltas
,
228 raw
->f
, raw
->hdrlen
, raw
->size
+ raw
->hdrlen
,
229 base
->dtab
, base_raw
->f
, base_raw
->hdrlen
,
230 base_raw
->size
+ base_raw
->hdrlen
);
231 got_object_raw_close(base_raw
);
236 size
= delta_size(deltas
, ndeltas
);
237 if (size
+ 32 < best
){
239 * if we already picked a best delta,
245 m
->ndeltas
= ndeltas
;
246 m
->nchain
= base
->nchain
+ 1;
248 m
->head
= base
->head
;
258 got_object_raw_close(raw
);
262 for (i
= MAX(0, nmeta
- max_base_candidates
); i
< nmeta
; i
++) {
263 got_deltify_free(meta
[i
]->dtab
);
264 meta
[i
]->dtab
= NULL
;
267 got_object_raw_close(raw
);
269 got_object_raw_close(base_raw
);
273 static const struct got_error
*
274 search_packidx(int *found
, struct got_object_id
*id
,
275 struct got_repository
*repo
)
277 const struct got_error
*err
= NULL
;
278 struct got_packidx
*packidx
= NULL
;
283 err
= got_repo_search_packidx(&packidx
, &idx
, repo
, id
);
285 *found
= 1; /* object is already packed */
286 else if (err
->code
== GOT_ERR_NO_OBJ
)
291 static const int obj_types
[] = {
297 GOT_OBJ_TYPE_OFFSET_DELTA
,
298 GOT_OBJ_TYPE_REF_DELTA
301 static const struct got_error
*
302 add_meta(struct got_pack_metavec
*v
, struct got_object_idset
*idset
,
303 struct got_object_id
*id
, const char *path
, int obj_type
,
304 time_t mtime
, int loose_obj_only
, struct got_repository
*repo
)
306 const struct got_error
*err
;
307 struct got_pack_meta
*m
;
309 if (loose_obj_only
) {
311 err
= search_packidx(&is_packed
, id
, repo
);
318 err
= got_object_idset_add(idset
, id
, (void *)&obj_types
[obj_type
]);
325 err
= alloc_meta(&m
, id
, path
, obj_type
, mtime
);
329 if (v
->nmeta
== v
->metasz
){
330 size_t newsize
= 2 * v
->metasz
;
331 struct got_pack_meta
**new;
332 new = reallocarray(v
->meta
, newsize
, sizeof(*new));
334 err
= got_error_from_errno("reallocarray");
345 v
->meta
[v
->nmeta
++] = m
;
350 static const struct got_error
*
351 load_tree_entries(struct got_object_id_queue
*ids
, struct got_pack_metavec
*v
,
352 struct got_object_idset
*idset
, struct got_object_id
*tree_id
,
353 const char *dpath
, time_t mtime
, struct got_repository
*repo
,
354 int loose_obj_only
, got_cancel_cb cancel_cb
, void *cancel_arg
)
356 const struct got_error
*err
;
357 struct got_tree_object
*tree
;
361 err
= got_object_open_as_tree(&tree
, repo
, tree_id
);
365 for (i
= 0; i
< got_object_tree_get_nentries(tree
); i
++) {
366 struct got_tree_entry
*e
= got_object_tree_get_entry(tree
, i
);
367 struct got_object_id
*id
= got_tree_entry_get_id(e
);
368 mode_t mode
= got_tree_entry_get_mode(e
);
371 err
= (*cancel_cb
)(cancel_arg
);
376 if (got_object_tree_entry_is_submodule(e
) ||
377 got_object_idset_contains(idset
, id
))
380 if (asprintf(&p
, "%s%s%s", dpath
, dpath
[0] != '\0' ? "/" : "",
381 got_tree_entry_get_name(e
)) == -1) {
382 err
= got_error_from_errno("asprintf");
387 struct got_object_qid
*qid
;
388 err
= got_object_qid_alloc(&qid
, id
);
391 STAILQ_INSERT_TAIL(ids
, qid
, entry
);
392 } else if (S_ISREG(mode
) || S_ISLNK(mode
)) {
393 err
= add_meta(v
, idset
, id
, p
, GOT_OBJ_TYPE_BLOB
,
394 mtime
, loose_obj_only
, repo
);
402 got_object_tree_close(tree
);
407 static const struct got_error
*
408 load_tree(struct got_pack_metavec
*v
, struct got_object_idset
*idset
,
409 struct got_object_id
*tree_id
, const char *dpath
, time_t mtime
,
410 int loose_obj_only
, struct got_repository
*repo
,
411 got_cancel_cb cancel_cb
, void *cancel_arg
)
413 const struct got_error
*err
= NULL
;
414 struct got_object_id_queue tree_ids
;
415 struct got_object_qid
*qid
;
417 if (got_object_idset_contains(idset
, tree_id
))
420 err
= got_object_qid_alloc(&qid
, tree_id
);
424 STAILQ_INIT(&tree_ids
);
425 STAILQ_INSERT_TAIL(&tree_ids
, qid
, entry
);
427 while (!STAILQ_EMPTY(&tree_ids
)) {
429 err
= (*cancel_cb
)(cancel_arg
);
434 qid
= STAILQ_FIRST(&tree_ids
);
435 STAILQ_REMOVE_HEAD(&tree_ids
, entry
);
437 if (got_object_idset_contains(idset
, qid
->id
)) {
438 got_object_qid_free(qid
);
442 err
= add_meta(v
, idset
, qid
->id
, dpath
, GOT_OBJ_TYPE_TREE
,
443 mtime
, loose_obj_only
, repo
);
445 got_object_qid_free(qid
);
449 err
= load_tree_entries(&tree_ids
, v
, idset
, qid
->id
, dpath
,
450 mtime
, repo
, loose_obj_only
, cancel_cb
, cancel_arg
);
451 got_object_qid_free(qid
);
456 got_object_id_queue_free(&tree_ids
);
460 static const struct got_error
*
461 load_commit(struct got_pack_metavec
*v
, struct got_object_idset
*idset
,
462 struct got_object_id
*id
, struct got_repository
*repo
, int loose_obj_only
,
463 got_cancel_cb cancel_cb
, void *cancel_arg
)
465 const struct got_error
*err
;
466 struct got_commit_object
*commit
;
468 if (got_object_idset_contains(idset
, id
))
471 if (loose_obj_only
) {
473 err
= search_packidx(&is_packed
, id
, repo
);
480 err
= got_object_open_as_commit(&commit
, repo
, id
);
484 err
= add_meta(v
, idset
, id
, "", GOT_OBJ_TYPE_COMMIT
,
485 got_object_commit_get_committer_time(commit
),
486 loose_obj_only
, repo
);
490 err
= load_tree(v
, idset
, got_object_commit_get_tree_id(commit
),
491 "", got_object_commit_get_committer_time(commit
),
492 loose_obj_only
, repo
, cancel_cb
, cancel_arg
);
494 got_object_commit_close(commit
);
498 static const struct got_error
*
499 load_tag(struct got_pack_metavec
*v
, struct got_object_idset
*idset
,
500 struct got_object_id
*id
, struct got_repository
*repo
, int loose_obj_only
,
501 got_cancel_cb cancel_cb
, void *cancel_arg
)
503 const struct got_error
*err
;
504 struct got_tag_object
*tag
= NULL
;
506 if (got_object_idset_contains(idset
, id
))
509 if (loose_obj_only
) {
511 err
= search_packidx(&is_packed
, id
, repo
);
518 err
= got_object_open_as_tag(&tag
, repo
, id
);
522 err
= add_meta(v
, idset
, id
, "", GOT_OBJ_TYPE_TAG
,
523 got_object_tag_get_tagger_time(tag
),
524 loose_obj_only
, repo
);
528 switch (got_object_tag_get_object_type(tag
)) {
529 case GOT_OBJ_TYPE_COMMIT
:
530 err
= load_commit(v
, idset
,
531 got_object_tag_get_object_id(tag
), repo
,
532 loose_obj_only
, cancel_cb
, cancel_arg
);
534 case GOT_OBJ_TYPE_TREE
:
535 err
= load_tree(v
, idset
, got_object_tag_get_object_id(tag
),
536 "", got_object_tag_get_tagger_time(tag
),
537 loose_obj_only
, repo
, cancel_cb
, cancel_arg
);
544 got_object_tag_close(tag
);
548 enum findtwixt_color
{
553 static const int findtwixt_colors
[] = {
559 static const struct got_error
*
560 queue_commit_id(struct got_object_id_queue
*ids
, struct got_object_id
*id
,
561 int color
, struct got_repository
*repo
)
563 const struct got_error
*err
;
564 struct got_object_qid
*qid
;
566 err
= got_object_qid_alloc(&qid
, id
);
570 STAILQ_INSERT_TAIL(ids
, qid
, entry
);
571 qid
->data
= (void *)&findtwixt_colors
[color
];
575 static const struct got_error
*
576 drop_commit(struct got_object_idset
*keep
, struct got_object_idset
*drop
,
577 struct got_object_id
*id
, struct got_repository
*repo
,
578 got_cancel_cb cancel_cb
, void *cancel_arg
)
580 const struct got_error
*err
= NULL
;
581 struct got_commit_object
*commit
;
582 const struct got_object_id_queue
*parents
;
583 struct got_object_id_queue ids
;
584 struct got_object_qid
*qid
;
588 err
= got_object_qid_alloc(&qid
, id
);
591 STAILQ_INSERT_HEAD(&ids
, qid
, entry
);
593 while (!STAILQ_EMPTY(&ids
)) {
595 err
= (*cancel_cb
)(cancel_arg
);
600 qid
= STAILQ_FIRST(&ids
);
601 STAILQ_REMOVE_HEAD(&ids
, entry
);
603 if (got_object_idset_contains(drop
, qid
->id
)) {
604 got_object_qid_free(qid
);
608 err
= got_object_idset_add(drop
, qid
->id
,
609 (void *)&obj_types
[GOT_OBJ_TYPE_COMMIT
]);
611 got_object_qid_free(qid
);
615 if (!got_object_idset_contains(keep
, qid
->id
)) {
616 got_object_qid_free(qid
);
620 err
= got_object_open_as_commit(&commit
, repo
, qid
->id
);
621 got_object_qid_free(qid
);
625 parents
= got_object_commit_get_parent_ids(commit
);
627 err
= got_object_id_queue_copy(parents
, &ids
);
629 got_object_commit_close(commit
);
633 got_object_commit_close(commit
);
636 got_object_id_queue_free(&ids
);
640 struct append_id_arg
{
641 struct got_object_id
**array
;
645 static const struct got_error
*
646 append_id(struct got_object_id
*id
, void *data
, void *arg
)
648 struct append_id_arg
*a
= arg
;
650 a
->array
[a
->idx
] = got_object_id_dup(id
);
651 if (a
->array
[a
->idx
] == NULL
)
652 return got_error_from_errno("got_object_id_dup");
658 static const struct got_error
*
659 findtwixt(struct got_object_id
***res
, int *nres
,
660 struct got_object_id
**head
, int nhead
,
661 struct got_object_id
**tail
, int ntail
,
662 struct got_repository
*repo
,
663 got_cancel_cb cancel_cb
, void *cancel_arg
)
665 const struct got_error
*err
= NULL
;
666 struct got_object_id_queue ids
;
667 struct got_object_idset
*keep
, *drop
;
668 struct got_object_qid
*qid
;
669 int i
, ncolor
, nkeep
, obj_type
;
675 keep
= got_object_idset_alloc();
677 return got_error_from_errno("got_object_idset_alloc");
679 drop
= got_object_idset_alloc();
681 err
= got_error_from_errno("got_object_idset_alloc");
685 for (i
= 0; i
< nhead
; i
++) {
686 struct got_object_id
*id
= head
[i
];
689 err
= got_object_get_type(&obj_type
, repo
, id
);
692 if (obj_type
!= GOT_OBJ_TYPE_COMMIT
)
694 err
= queue_commit_id(&ids
, id
, COLOR_KEEP
, repo
);
698 for (i
= 0; i
< ntail
; i
++) {
699 struct got_object_id
*id
= tail
[i
];
702 err
= got_object_get_type(&obj_type
, repo
, id
);
705 if (obj_type
!= GOT_OBJ_TYPE_COMMIT
)
707 err
= queue_commit_id(&ids
, id
, COLOR_DROP
, repo
);
712 while (!STAILQ_EMPTY(&ids
)) {
714 qid
= STAILQ_FIRST(&ids
);
715 qcolor
= *((int *)qid
->data
);
717 if (got_object_idset_contains(drop
, qid
->id
))
719 else if (got_object_idset_contains(keep
, qid
->id
))
722 ncolor
= COLOR_BLANK
;
724 if (ncolor
== COLOR_DROP
|| (ncolor
== COLOR_KEEP
&&
725 qcolor
== COLOR_KEEP
)) {
726 STAILQ_REMOVE_HEAD(&ids
, entry
);
727 got_object_qid_free(qid
);
731 if (ncolor
== COLOR_KEEP
&& qcolor
== COLOR_DROP
) {
732 err
= drop_commit(keep
, drop
, qid
->id
, repo
,
733 cancel_cb
, cancel_arg
);
736 } else if (ncolor
== COLOR_BLANK
) {
737 struct got_commit_object
*commit
;
738 struct got_object_id
*id
;
739 const struct got_object_id_queue
*parents
;
740 struct got_object_qid
*pid
;
742 id
= got_object_id_dup(qid
->id
);
744 err
= got_error_from_errno("got_object_id_dup");
747 if (qcolor
== COLOR_KEEP
)
748 err
= got_object_idset_add(keep
, id
,
749 (void *)&obj_types
[GOT_OBJ_TYPE_COMMIT
]);
751 err
= got_object_idset_add(drop
, id
,
752 (void *)&obj_types
[GOT_OBJ_TYPE_COMMIT
]);
758 err
= got_object_open_as_commit(&commit
, repo
, id
);
763 parents
= got_object_commit_get_parent_ids(commit
);
765 STAILQ_FOREACH(pid
, parents
, entry
) {
766 err
= queue_commit_id(&ids
, pid
->id
,
774 got_object_commit_close(commit
);
777 /* should not happen */
778 err
= got_error_fmt(GOT_ERR_NOT_IMPL
,
779 "%s ncolor=%d qcolor=%d", __func__
, ncolor
, qcolor
);
783 STAILQ_REMOVE_HEAD(&ids
, entry
);
784 got_object_qid_free(qid
);
787 nkeep
= got_object_idset_num_elements(keep
);
789 struct append_id_arg arg
;
790 arg
.array
= calloc(nkeep
, sizeof(struct got_object_id
*));
791 if (arg
.array
== NULL
) {
792 err
= got_error_from_errno("calloc");
796 err
= got_object_idset_for_each(keep
, append_id
, &arg
);
805 got_object_idset_free(keep
);
806 got_object_idset_free(drop
);
807 got_object_id_queue_free(&ids
);
811 static const struct got_error
*
812 read_meta(struct got_pack_meta
***meta
, int *nmeta
,
813 struct got_object_id
**theirs
, int ntheirs
,
814 struct got_object_id
**ours
, int nours
, struct got_repository
*repo
,
815 int loose_obj_only
, got_pack_progress_cb progress_cb
, void *progress_arg
,
816 got_cancel_cb cancel_cb
, void *cancel_arg
)
818 const struct got_error
*err
= NULL
;
819 struct got_object_id
**ids
= NULL
;
820 struct got_object_idset
*idset
;
821 int i
, nobj
= 0, obj_type
;
822 struct got_pack_metavec v
;
827 idset
= got_object_idset_alloc();
829 return got_error_from_errno("got_object_idset_alloc");
833 v
.meta
= calloc(v
.metasz
, sizeof(struct got_pack_meta
*));
834 if (v
.meta
== NULL
) {
835 err
= got_error_from_errno("calloc");
839 err
= findtwixt(&ids
, &nobj
, ours
, nours
, theirs
, ntheirs
, repo
,
840 cancel_cb
, cancel_arg
);
841 if (err
|| nobj
== 0)
844 for (i
= 0; i
< ntheirs
; i
++) {
845 struct got_object_id
*id
= theirs
[i
];
848 err
= got_object_get_type(&obj_type
, repo
, id
);
851 if (obj_type
!= GOT_OBJ_TYPE_COMMIT
)
853 err
= load_commit(NULL
, idset
, id
, repo
,
854 loose_obj_only
, cancel_cb
, cancel_arg
);
858 err
= progress_cb(progress_arg
, 0L, nours
,
865 for (i
= 0; i
< ntheirs
; i
++) {
866 struct got_object_id
*id
= theirs
[i
];
870 cached_type
= got_object_idset_get(idset
, id
);
871 if (cached_type
== NULL
) {
872 err
= got_object_get_type(&obj_type
, repo
, id
);
876 obj_type
= *cached_type
;
877 if (obj_type
!= GOT_OBJ_TYPE_TAG
)
879 err
= load_tag(NULL
, idset
, id
, repo
,
880 loose_obj_only
, cancel_cb
, cancel_arg
);
884 err
= progress_cb(progress_arg
, 0L, nours
,
891 for (i
= 0; i
< nobj
; i
++) {
892 err
= load_commit(&v
, idset
, ids
[i
], repo
,
893 loose_obj_only
, cancel_cb
, cancel_arg
);
897 err
= progress_cb(progress_arg
, 0L, nours
,
904 for (i
= 0; i
< nours
; i
++) {
905 struct got_object_id
*id
= ours
[i
];
909 cached_type
= got_object_idset_get(idset
, id
);
910 if (cached_type
== NULL
) {
911 err
= got_object_get_type(&obj_type
, repo
, id
);
915 obj_type
= *cached_type
;
916 if (obj_type
!= GOT_OBJ_TYPE_TAG
)
918 err
= load_tag(&v
, idset
, id
, repo
,
919 loose_obj_only
, cancel_cb
, cancel_arg
);
923 err
= progress_cb(progress_arg
, 0L, nours
,
931 for (i
= 0; i
< nobj
; i
++) {
935 got_object_idset_free(idset
);
945 const struct got_error
*
946 hwrite(FILE *f
, void *buf
, int len
, SHA1_CTX
*ctx
)
950 SHA1Update(ctx
, buf
, len
);
951 n
= fwrite(buf
, 1, len
, f
);
953 return got_ferror(f
, GOT_ERR_IO
);
958 putbe32(char *b
, uint32_t n
)
967 write_order_cmp(const void *pa
, const void *pb
)
969 struct got_pack_meta
*a
, *b
, *ahd
, *bhd
;
971 a
= *(struct got_pack_meta
**)pa
;
972 b
= *(struct got_pack_meta
**)pb
;
973 ahd
= (a
->head
== NULL
) ? a
: a
->head
;
974 bhd
= (b
->head
== NULL
) ? b
: b
->head
;
975 if (ahd
->mtime
!= bhd
->mtime
)
976 return bhd
->mtime
- ahd
->mtime
;
978 return (uintptr_t)bhd
- (uintptr_t)ahd
;
979 if (a
->nchain
!= b
->nchain
)
980 return a
->nchain
- b
->nchain
;
981 return a
->mtime
- b
->mtime
;
984 static const struct got_error
*
985 packhdr(int *hdrlen
, char *hdr
, size_t bufsize
, int obj_type
, size_t len
)
991 hdr
[0] = obj_type
<< 4;
994 for (i
= 1; len
!= 0; i
++){
996 return got_error(GOT_ERR_NO_SPACE
);
997 hdr
[i
- 1] |= GOT_DELTA_SIZE_MORE
;
998 hdr
[i
] = len
& GOT_DELTA_SIZE_VAL_MASK
;
999 len
>>= GOT_DELTA_SIZE_SHIFT
;
1006 static const struct got_error
*
1007 encodedelta(struct got_pack_meta
*m
, struct got_raw_object
*o
,
1008 off_t base_size
, FILE *f
)
1010 unsigned char buf
[16], *bp
;
1014 struct got_delta_instruction
*d
;
1016 /* base object size */
1017 buf
[0] = base_size
& GOT_DELTA_SIZE_VAL_MASK
;
1018 n
= base_size
>> GOT_DELTA_SIZE_SHIFT
;
1019 for (i
= 1; n
> 0; i
++) {
1020 buf
[i
- 1] |= GOT_DELTA_SIZE_MORE
;
1021 buf
[i
] = n
& GOT_DELTA_SIZE_VAL_MASK
;
1022 n
>>= GOT_DELTA_SIZE_SHIFT
;
1024 w
= fwrite(buf
, 1, i
, f
);
1026 return got_ferror(f
, GOT_ERR_IO
);
1028 /* target object size */
1029 buf
[0] = o
->size
& GOT_DELTA_SIZE_VAL_MASK
;
1030 n
= o
->size
>> GOT_DELTA_SIZE_SHIFT
;
1031 for (i
= 1; n
> 0; i
++) {
1032 buf
[i
- 1] |= GOT_DELTA_SIZE_MORE
;
1033 buf
[i
] = n
& GOT_DELTA_SIZE_VAL_MASK
;
1034 n
>>= GOT_DELTA_SIZE_SHIFT
;
1036 w
= fwrite(buf
, 1, i
, f
);
1038 return got_ferror(f
, GOT_ERR_IO
);
1040 for (j
= 0; j
< m
->ndeltas
; j
++) {
1045 buf
[0] = GOT_DELTA_BASE_COPY
;
1046 for (i
= 0; i
< 4; i
++) {
1047 /* DELTA_COPY_OFF1 ... DELTA_COPY_OFF4 */
1056 if (n
!= GOT_DELTA_COPY_DEFAULT_LEN
) {
1057 /* DELTA_COPY_LEN1 ... DELTA_COPY_LEN3 */
1058 for (i
= 0; i
< 3 && n
> 0; i
++) {
1059 buf
[0] |= 1 << (i
+ 4);
1064 w
= fwrite(buf
, 1, bp
- buf
, f
);
1066 return got_ferror(f
, GOT_ERR_IO
);
1070 if (fseeko(o
->f
, o
->hdrlen
+ d
->offset
, SEEK_SET
) == -1)
1071 return got_error_from_errno("fseeko");
1073 while (n
!= d
->len
) {
1074 buf
[0] = (d
->len
- n
< 127) ? d
->len
- n
: 127;
1075 w
= fwrite(buf
, 1, 1, f
);
1077 return got_ferror(f
, GOT_ERR_IO
);
1078 r
= fread(content
, 1, buf
[0], o
->f
);
1080 return got_ferror(o
->f
, GOT_ERR_IO
);
1081 w
= fwrite(content
, 1, buf
[0], f
);
1083 return got_ferror(f
, GOT_ERR_IO
);
1093 packoff(char *hdr
, off_t off
)
1098 rbuf
[0] = off
& GOT_DELTA_SIZE_VAL_MASK
;
1099 for (i
= 1; (off
>>= GOT_DELTA_SIZE_SHIFT
) != 0; i
++) {
1100 rbuf
[i
] = (--off
& GOT_DELTA_SIZE_VAL_MASK
) |
1101 GOT_DELTA_SIZE_MORE
;
1106 hdr
[j
++] = rbuf
[--i
];
1110 static const struct got_error
*
1111 genpack(uint8_t *pack_sha1
, FILE *packfile
,
1112 struct got_pack_meta
**meta
, int nmeta
, int nours
,
1113 int use_offset_deltas
, struct got_repository
*repo
,
1114 got_pack_progress_cb progress_cb
, void *progress_arg
,
1115 got_cancel_cb cancel_cb
, void *cancel_arg
)
1117 const struct got_error
*err
= NULL
;
1121 struct got_pack_meta
*m
;
1122 struct got_raw_object
*raw
= NULL
, *base_raw
= NULL
;
1123 FILE *delta_file
= NULL
;
1126 struct got_deflate_checksum csum
;
1127 off_t packfile_size
= 0;
1130 csum
.output_sha1
= &ctx
;
1131 csum
.output_crc
= NULL
;
1133 err
= hwrite(packfile
, "PACK", 4, &ctx
);
1136 putbe32(buf
, GOT_PACKFILE_VERSION
);
1137 err
= hwrite(packfile
, buf
, 4, &ctx
);
1140 putbe32(buf
, nmeta
);
1141 err
= hwrite(packfile
, buf
, 4, &ctx
);
1144 qsort(meta
, nmeta
, sizeof(struct got_pack_meta
*), write_order_cmp
);
1145 for (i
= 0; i
< nmeta
; i
++) {
1147 err
= progress_cb(progress_arg
, packfile_size
, nours
,
1153 m
->off
= ftello(packfile
);
1154 err
= got_object_raw_open(&raw
, repo
, &m
->id
, 8192);
1157 if (m
->deltas
== NULL
) {
1158 err
= packhdr(&nh
, buf
, sizeof(buf
),
1159 m
->obj_type
, raw
->size
);
1162 err
= hwrite(packfile
, buf
, nh
, &ctx
);
1165 packfile_size
+= nh
;
1166 if (fseeko(raw
->f
, raw
->hdrlen
, SEEK_SET
) == -1) {
1167 err
= got_error_from_errno("fseeko");
1170 err
= got_deflate_to_file(&outlen
, raw
->f
, packfile
,
1174 packfile_size
+= outlen
;
1176 if (delta_file
== NULL
) {
1177 delta_file
= got_opentemp();
1178 if (delta_file
== NULL
) {
1179 err
= got_error_from_errno(
1184 if (ftruncate(fileno(delta_file
), 0L) == -1) {
1185 err
= got_error_from_errno("ftruncate");
1188 if (fseeko(delta_file
, 0L, SEEK_SET
) == -1) {
1189 err
= got_error_from_errno("fseeko");
1192 err
= got_object_raw_open(&base_raw
, repo
,
1193 &m
->prev
->id
, 8192);
1196 err
= encodedelta(m
, raw
, base_raw
->size
, delta_file
);
1199 nd
= ftello(delta_file
);
1200 if (fseeko(delta_file
, 0L, SEEK_SET
) == -1) {
1201 err
= got_error_from_errno("fseeko");
1204 got_object_raw_close(base_raw
);
1206 if (use_offset_deltas
&& m
->prev
->off
!= 0) {
1207 err
= packhdr(&nh
, buf
, sizeof(buf
),
1208 GOT_OBJ_TYPE_OFFSET_DELTA
, nd
);
1211 nh
+= packoff(buf
+ nh
,
1212 m
->off
- m
->prev
->off
);
1213 err
= hwrite(packfile
, buf
, nh
, &ctx
);
1216 packfile_size
+= nh
;
1218 err
= packhdr(&nh
, buf
, sizeof(buf
),
1219 GOT_OBJ_TYPE_REF_DELTA
, nd
);
1220 err
= hwrite(packfile
, buf
, nh
, &ctx
);
1223 packfile_size
+= nh
;
1224 err
= hwrite(packfile
, m
->prev
->id
.sha1
,
1225 sizeof(m
->prev
->id
.sha1
), &ctx
);
1226 packfile_size
+= sizeof(m
->prev
->id
.sha1
);
1230 err
= got_deflate_to_file(&outlen
, delta_file
,
1234 packfile_size
+= outlen
;
1236 got_object_raw_close(raw
);
1239 SHA1Final(pack_sha1
, &ctx
);
1240 n
= fwrite(pack_sha1
, 1, SHA1_DIGEST_LENGTH
, packfile
);
1241 if (n
!= SHA1_DIGEST_LENGTH
)
1242 err
= got_ferror(packfile
, GOT_ERR_IO
);
1243 packfile_size
+= SHA1_DIGEST_LENGTH
;
1244 packfile_size
+= sizeof(struct got_packfile_hdr
);
1245 err
= progress_cb(progress_arg
, packfile_size
, nours
,
1246 nmeta
, nmeta
, nmeta
);
1250 if (delta_file
&& fclose(delta_file
) == EOF
&& err
== NULL
)
1251 err
= got_error_from_errno("fclose");
1253 got_object_raw_close(raw
);
1255 got_object_raw_close(base_raw
);
1259 const struct got_error
*
1260 got_pack_create(uint8_t *packsha1
, FILE *packfile
,
1261 struct got_object_id
**theirs
, int ntheirs
,
1262 struct got_object_id
**ours
, int nours
,
1263 struct got_repository
*repo
, int loose_obj_only
, int allow_empty
,
1264 got_pack_progress_cb progress_cb
, void *progress_arg
,
1265 got_cancel_cb cancel_cb
, void *cancel_arg
)
1267 const struct got_error
*err
;
1268 struct got_pack_meta
**meta
;
1271 err
= read_meta(&meta
, &nmeta
, theirs
, ntheirs
, ours
, nours
, repo
,
1272 loose_obj_only
, progress_cb
, progress_arg
, cancel_cb
, cancel_arg
);
1276 if (nmeta
== 0 && !allow_empty
) {
1277 err
= got_error(GOT_ERR_CANNOT_PACK
);
1281 err
= pick_deltas(meta
, nmeta
, nours
, repo
,
1282 progress_cb
, progress_arg
, cancel_cb
, cancel_arg
);
1287 err
= genpack(packsha1
, packfile
, meta
, nmeta
, nours
, 1, repo
,
1288 progress_cb
, progress_arg
, cancel_cb
, cancel_arg
);
1292 free_nmeta(meta
, nmeta
);