git
/
chunk-format.c
214 строк · 4.8 Кб
1#define USE_THE_REPOSITORY_VARIABLE2
3#include "git-compat-util.h"4#include "chunk-format.h"5#include "csum-file.h"6#include "gettext.h"7#include "hash.h"8#include "trace2.h"9
10/*
11* When writing a chunk-based file format, collect the chunks in
12* an array of chunk_info structs. The size stores the _expected_
13* amount of data that will be written by write_fn.
14*/
15struct chunk_info {16uint32_t id;17uint64_t size;18chunk_write_fn write_fn;19
20const void *start;21};22
23struct chunkfile {24struct hashfile *f;25
26struct chunk_info *chunks;27size_t chunks_nr;28size_t chunks_alloc;29};30
31struct chunkfile *init_chunkfile(struct hashfile *f)32{
33struct chunkfile *cf = xcalloc(1, sizeof(*cf));34cf->f = f;35return cf;36}
37
38void free_chunkfile(struct chunkfile *cf)39{
40if (!cf)41return;42free(cf->chunks);43free(cf);44}
45
46int get_num_chunks(struct chunkfile *cf)47{
48return cf->chunks_nr;49}
50
51void add_chunk(struct chunkfile *cf,52uint32_t id,53size_t size,54chunk_write_fn fn)55{
56ALLOC_GROW(cf->chunks, cf->chunks_nr + 1, cf->chunks_alloc);57
58cf->chunks[cf->chunks_nr].id = id;59cf->chunks[cf->chunks_nr].write_fn = fn;60cf->chunks[cf->chunks_nr].size = size;61cf->chunks_nr++;62}
63
64int write_chunkfile(struct chunkfile *cf, void *data)65{
66int i, result = 0;67uint64_t cur_offset = hashfile_total(cf->f);68
69trace2_region_enter("chunkfile", "write", the_repository);70
71/* Add the table of contents to the current offset */72cur_offset += (cf->chunks_nr + 1) * CHUNK_TOC_ENTRY_SIZE;73
74for (i = 0; i < cf->chunks_nr; i++) {75hashwrite_be32(cf->f, cf->chunks[i].id);76hashwrite_be64(cf->f, cur_offset);77
78cur_offset += cf->chunks[i].size;79}80
81/* Trailing entry marks the end of the chunks */82hashwrite_be32(cf->f, 0);83hashwrite_be64(cf->f, cur_offset);84
85for (i = 0; i < cf->chunks_nr; i++) {86off_t start_offset = hashfile_total(cf->f);87result = cf->chunks[i].write_fn(cf->f, data);88
89if (result)90goto cleanup;91
92if (hashfile_total(cf->f) - start_offset != cf->chunks[i].size)93BUG("expected to write %"PRId64" bytes to chunk %"PRIx32", but wrote %"PRId64" instead",94cf->chunks[i].size, cf->chunks[i].id,95hashfile_total(cf->f) - start_offset);96}97
98cleanup:99trace2_region_leave("chunkfile", "write", the_repository);100return result;101}
102
103int read_table_of_contents(struct chunkfile *cf,104const unsigned char *mfile,105size_t mfile_size,106uint64_t toc_offset,107int toc_length,108unsigned expected_alignment)109{
110int i;111uint32_t chunk_id;112const unsigned char *table_of_contents = mfile + toc_offset;113
114ALLOC_GROW(cf->chunks, toc_length, cf->chunks_alloc);115
116while (toc_length--) {117uint64_t chunk_offset, next_chunk_offset;118
119chunk_id = get_be32(table_of_contents);120chunk_offset = get_be64(table_of_contents + 4);121
122if (!chunk_id) {123error(_("terminating chunk id appears earlier than expected"));124return 1;125}126if (chunk_offset % expected_alignment != 0) {127error(_("chunk id %"PRIx32" not %d-byte aligned"),128chunk_id, expected_alignment);129return 1;130}131
132table_of_contents += CHUNK_TOC_ENTRY_SIZE;133next_chunk_offset = get_be64(table_of_contents + 4);134
135if (next_chunk_offset < chunk_offset ||136next_chunk_offset > mfile_size - the_hash_algo->rawsz) {137error(_("improper chunk offset(s) %"PRIx64" and %"PRIx64""),138chunk_offset, next_chunk_offset);139return -1;140}141
142for (i = 0; i < cf->chunks_nr; i++) {143if (cf->chunks[i].id == chunk_id) {144error(_("duplicate chunk ID %"PRIx32" found"),145chunk_id);146return -1;147}148}149
150cf->chunks[cf->chunks_nr].id = chunk_id;151cf->chunks[cf->chunks_nr].start = mfile + chunk_offset;152cf->chunks[cf->chunks_nr].size = next_chunk_offset - chunk_offset;153cf->chunks_nr++;154}155
156chunk_id = get_be32(table_of_contents);157if (chunk_id) {158error(_("final chunk has non-zero id %"PRIx32""), chunk_id);159return -1;160}161
162return 0;163}
164
165struct pair_chunk_data {166const unsigned char **p;167size_t *size;168};169
170static int pair_chunk_fn(const unsigned char *chunk_start,171size_t chunk_size,172void *data)173{
174struct pair_chunk_data *pcd = data;175*pcd->p = chunk_start;176*pcd->size = chunk_size;177return 0;178}
179
180int pair_chunk(struct chunkfile *cf,181uint32_t chunk_id,182const unsigned char **p,183size_t *size)184{
185struct pair_chunk_data pcd = { .p = p, .size = size };186return read_chunk(cf, chunk_id, pair_chunk_fn, &pcd);187}
188
189int read_chunk(struct chunkfile *cf,190uint32_t chunk_id,191chunk_read_fn fn,192void *data)193{
194int i;195
196for (i = 0; i < cf->chunks_nr; i++) {197if (cf->chunks[i].id == chunk_id)198return fn(cf->chunks[i].start, cf->chunks[i].size, data);199}200
201return CHUNK_NOT_FOUND;202}
203
204uint8_t oid_version(const struct git_hash_algo *algop)205{
206switch (hash_algo_by_ptr(algop)) {207case GIT_HASH_SHA1:208return 1;209case GIT_HASH_SHA256:210return 2;211default:212die(_("invalid hash version"));213}214}
215