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