Git fork
at reftables-rust 276 lines 6.5 kB view raw
1/* 2 * zlib wrappers to make sure we don't silently miss errors 3 * at init time. 4 */ 5#include "git-compat-util.h" 6#include "git-zlib.h" 7 8static const char *zerr_to_string(int status) 9{ 10 switch (status) { 11 case Z_MEM_ERROR: 12 return "out of memory"; 13 case Z_VERSION_ERROR: 14 return "wrong version"; 15 case Z_NEED_DICT: 16 return "needs dictionary"; 17 case Z_DATA_ERROR: 18 return "data stream error"; 19 case Z_STREAM_ERROR: 20 return "stream consistency error"; 21 default: 22 return "unknown error"; 23 } 24} 25 26/* 27 * avail_in and avail_out in zlib are counted in uInt, which typically 28 * limits the size of the buffer we can use to 4GB when interacting 29 * with zlib in a single call to inflate/deflate. 30 */ 31/* #define ZLIB_BUF_MAX ((uInt)-1) */ 32#define ZLIB_BUF_MAX ((uInt) 1024 * 1024 * 1024) /* 1GB */ 33static inline uInt zlib_buf_cap(unsigned long len) 34{ 35 return (ZLIB_BUF_MAX < len) ? ZLIB_BUF_MAX : len; 36} 37 38static void zlib_pre_call(git_zstream *s) 39{ 40 s->z.next_in = s->next_in; 41 s->z.next_out = s->next_out; 42 s->z.total_in = s->total_in; 43 s->z.total_out = s->total_out; 44 s->z.avail_in = zlib_buf_cap(s->avail_in); 45 s->z.avail_out = zlib_buf_cap(s->avail_out); 46} 47 48static void zlib_post_call(git_zstream *s, int status) 49{ 50 unsigned long bytes_consumed; 51 unsigned long bytes_produced; 52 53 bytes_consumed = s->z.next_in - s->next_in; 54 bytes_produced = s->z.next_out - s->next_out; 55 if (s->z.total_out != s->total_out + bytes_produced) 56 BUG("total_out mismatch"); 57 /* 58 * zlib does not update total_in when it returns Z_NEED_DICT, 59 * causing a mismatch here. Skip the sanity check in that case. 60 */ 61 if (status != Z_NEED_DICT && 62 s->z.total_in != s->total_in + bytes_consumed) 63 BUG("total_in mismatch"); 64 65 s->total_out = s->z.total_out; 66 s->total_in = s->z.total_in; 67 /* zlib-ng marks `next_in` as `const`, so we have to cast it away. */ 68 s->next_in = (unsigned char *) s->z.next_in; 69 s->next_out = s->z.next_out; 70 s->avail_in -= bytes_consumed; 71 s->avail_out -= bytes_produced; 72} 73 74void git_inflate_init(git_zstream *strm) 75{ 76 int status; 77 78 zlib_pre_call(strm); 79 status = inflateInit(&strm->z); 80 zlib_post_call(strm, status); 81 if (status == Z_OK) 82 return; 83 die("inflateInit: %s (%s)", zerr_to_string(status), 84 strm->z.msg ? strm->z.msg : "no message"); 85} 86 87void git_inflate_init_gzip_only(git_zstream *strm) 88{ 89 /* 90 * Use default 15 bits, +16 is to accept only gzip and to 91 * yield Z_DATA_ERROR when fed zlib format. 92 */ 93 const int windowBits = 15 + 16; 94 int status; 95 96 zlib_pre_call(strm); 97 status = inflateInit2(&strm->z, windowBits); 98 zlib_post_call(strm, status); 99 if (status == Z_OK) 100 return; 101 die("inflateInit2: %s (%s)", zerr_to_string(status), 102 strm->z.msg ? strm->z.msg : "no message"); 103} 104 105void git_inflate_end(git_zstream *strm) 106{ 107 int status; 108 109 zlib_pre_call(strm); 110 status = inflateEnd(&strm->z); 111 zlib_post_call(strm, status); 112 if (status == Z_OK) 113 return; 114 error("inflateEnd: %s (%s)", zerr_to_string(status), 115 strm->z.msg ? strm->z.msg : "no message"); 116} 117 118int git_inflate(git_zstream *strm, int flush) 119{ 120 int status; 121 122 for (;;) { 123 zlib_pre_call(strm); 124 /* Never say Z_FINISH unless we are feeding everything */ 125 status = inflate(&strm->z, 126 (strm->z.avail_in != strm->avail_in) 127 ? 0 : flush); 128 if (status == Z_MEM_ERROR) 129 die("inflate: out of memory"); 130 zlib_post_call(strm, status); 131 132 /* 133 * Let zlib work another round, while we can still 134 * make progress. 135 */ 136 if ((strm->avail_out && !strm->z.avail_out) && 137 (status == Z_OK || status == Z_BUF_ERROR)) 138 continue; 139 break; 140 } 141 142 switch (status) { 143 /* Z_BUF_ERROR: normal, needs more space in the output buffer */ 144 case Z_BUF_ERROR: 145 case Z_OK: 146 case Z_STREAM_END: 147 return status; 148 default: 149 break; 150 } 151 error("inflate: %s (%s)", zerr_to_string(status), 152 strm->z.msg ? strm->z.msg : "no message"); 153 return status; 154} 155 156unsigned long git_deflate_bound(git_zstream *strm, unsigned long size) 157{ 158 return deflateBound(&strm->z, size); 159} 160 161void git_deflate_init(git_zstream *strm, int level) 162{ 163 int status; 164 165 memset(strm, 0, sizeof(*strm)); 166 zlib_pre_call(strm); 167 status = deflateInit(&strm->z, level); 168 zlib_post_call(strm, status); 169 if (status == Z_OK) 170 return; 171 die("deflateInit: %s (%s)", zerr_to_string(status), 172 strm->z.msg ? strm->z.msg : "no message"); 173} 174 175static void do_git_deflate_init(git_zstream *strm, int level, int windowBits) 176{ 177 int status; 178 179 memset(strm, 0, sizeof(*strm)); 180 zlib_pre_call(strm); 181 status = deflateInit2(&strm->z, level, 182 Z_DEFLATED, windowBits, 183 8, Z_DEFAULT_STRATEGY); 184 zlib_post_call(strm, status); 185 if (status == Z_OK) 186 return; 187 die("deflateInit2: %s (%s)", zerr_to_string(status), 188 strm->z.msg ? strm->z.msg : "no message"); 189} 190 191void git_deflate_init_gzip(git_zstream *strm, int level) 192{ 193 /* 194 * Use default 15 bits, +16 is to generate gzip header/trailer 195 * instead of the zlib wrapper. 196 */ 197 do_git_deflate_init(strm, level, 15 + 16); 198} 199 200void git_deflate_init_raw(git_zstream *strm, int level) 201{ 202 /* 203 * Use default 15 bits, negate the value to get raw compressed 204 * data without zlib header and trailer. 205 */ 206 do_git_deflate_init(strm, level, -15); 207} 208 209int git_deflate_abort(git_zstream *strm) 210{ 211 int status; 212 213 zlib_pre_call(strm); 214 status = deflateEnd(&strm->z); 215 zlib_post_call(strm, status); 216 return status; 217} 218 219void git_deflate_end(git_zstream *strm) 220{ 221 int status = git_deflate_abort(strm); 222 223 if (status == Z_OK) 224 return; 225 error("deflateEnd: %s (%s)", zerr_to_string(status), 226 strm->z.msg ? strm->z.msg : "no message"); 227} 228 229int git_deflate_end_gently(git_zstream *strm) 230{ 231 int status; 232 233 zlib_pre_call(strm); 234 status = deflateEnd(&strm->z); 235 zlib_post_call(strm, status); 236 return status; 237} 238 239int git_deflate(git_zstream *strm, int flush) 240{ 241 int status; 242 243 for (;;) { 244 zlib_pre_call(strm); 245 246 /* Never say Z_FINISH unless we are feeding everything */ 247 status = deflate(&strm->z, 248 (strm->z.avail_in != strm->avail_in) 249 ? 0 : flush); 250 if (status == Z_MEM_ERROR) 251 die("deflate: out of memory"); 252 zlib_post_call(strm, status); 253 254 /* 255 * Let zlib work another round, while we can still 256 * make progress. 257 */ 258 if ((strm->avail_out && !strm->z.avail_out) && 259 (status == Z_OK || status == Z_BUF_ERROR)) 260 continue; 261 break; 262 } 263 264 switch (status) { 265 /* Z_BUF_ERROR: normal, needs more space in the output buffer */ 266 case Z_BUF_ERROR: 267 case Z_OK: 268 case Z_STREAM_END: 269 return status; 270 default: 271 break; 272 } 273 error("deflate: %s (%s)", zerr_to_string(status), 274 strm->z.msg ? strm->z.msg : "no message"); 275 return status; 276}