@matheusbsbs / matheus-bagliano / commits / 39c68542fc
Fork 0

Wrap inflate and other zlib routines for better error reporting

R. Tyler Ballance reported a mysterious transient repository corruption; after much digging, it turns out that we were not catching and reporting memory allocation errors from some calls we make to zlib. This one _just_ wraps things; it doesn't do the "retry on low memory error" part, at least not yet. It is an independent issue from the reporting. Some of the errors are expected and passed back to the caller, but we die when zlib reports it failed to allocate memory for now. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Linus Torvalds committed Jan 7, 2009 at 19:54 UTC 39c68542fc8d8477f2080c99efedb9dce975abc6
9 files changed +99 -34
builtin-apply.c
+3 -2
@@ -1258,8 +1258,9 @@ static char *inflate_it(const void *data, unsigned long size,
1258 stream.avail_in = size;
1259 stream.next_out = out = xmalloc(inflated_size);
1260 stream.avail_out = inflated_size;
1261 - inflateInit(&stream);
1262 - st = inflate(&stream, Z_FINISH);
1261 + git_inflate_init(&stream);
1262 + st = git_inflate(&stream, Z_FINISH);
1263 + git_inflate_end(&stream);
1264 if ((st != Z_STREAM_END) || stream.total_out != inflated_size) {
1265 free(out);
1266 return NULL;
builtin-pack-objects.c
+3 -3
@@ -195,16 +195,16 @@ static int check_pack_inflate(struct packed_git *p,
195 int st;
196
197 memset(&stream, 0, sizeof(stream));
198 - inflateInit(&stream);
198 + git_inflate_init(&stream);
199 do {
200 in = use_pack(p, w_curs, offset, &stream.avail_in);
201 stream.next_in = in;
202 stream.next_out = fakebuf;
203 stream.avail_out = sizeof(fakebuf);
204 - st = inflate(&stream, Z_FINISH);
204 + st = git_inflate(&stream, Z_FINISH);
205 offset += stream.next_in - in;
206 } while (st == Z_OK || st == Z_BUF_ERROR);
207 - inflateEnd(&stream);
207 + git_inflate_end(&stream);
208 return (st == Z_STREAM_END &&
209 stream.total_out == expect &&
210 stream.total_in == len) ? 0 : -1;
builtin-unpack-objects.c
+3 -3
@@ -99,10 +99,10 @@ static void *get_data(unsigned long size)
99 stream.avail_out = size;
100 stream.next_in = fill(1);
101 stream.avail_in = len;
102 - inflateInit(&stream);
102 + git_inflate_init(&stream);
103
104 for (;;) {
105 - int ret = inflate(&stream, 0);
105 + int ret = git_inflate(&stream, 0);
106 use(len - stream.avail_in);
107 if (stream.total_out == size && ret == Z_STREAM_END)
108 break;
@@ -118,7 +118,7 @@ static void *get_data(unsigned long size)
118 stream.next_in = fill(1);
119 stream.avail_in = len;
120 }
121 - inflateEnd(&stream);
121 + git_inflate_end(&stream);
122 return buf;
123 }
124
cache.h
+4
@@ -12,6 +12,10 @@
12 #define deflateBound(c,s) ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11)
13 #endif
14
15 +void git_inflate_init(z_streamp strm);
16 +void git_inflate_end(z_streamp strm);
17 +int git_inflate(z_streamp strm, int flush);
18 +
19 #if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
20 #define DTYPE(de) ((de)->d_type)
21 #else
http-push.c
+4 -4
@@ -208,7 +208,7 @@ static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
208 do {
209 request->stream.next_out = expn;
210 request->stream.avail_out = sizeof(expn);
211 - request->zret = inflate(&request->stream, Z_SYNC_FLUSH);
211 + request->zret = git_inflate(&request->stream, Z_SYNC_FLUSH);
212 SHA1_Update(&request->c, expn,
213 sizeof(expn) - request->stream.avail_out);
214 } while (request->stream.avail_in && request->zret == Z_OK);
@@ -268,7 +268,7 @@ static void start_fetch_loose(struct transfer_request *request)
268
269 memset(&request->stream, 0, sizeof(request->stream));
270
271 - inflateInit(&request->stream);
271 + git_inflate_init(&request->stream);
272
273 SHA1_Init(&request->c);
274
@@ -309,7 +309,7 @@ static void start_fetch_loose(struct transfer_request *request)
309 file; also rewind to the beginning of the local file. */
310 if (prev_read == -1) {
311 memset(&request->stream, 0, sizeof(request->stream));
312 - inflateInit(&request->stream);
312 + git_inflate_init(&request->stream);
313 SHA1_Init(&request->c);
314 if (prev_posn>0) {
315 prev_posn = 0;
@@ -741,7 +741,7 @@ static void finish_request(struct transfer_request *request)
741 if (request->http_code == 416)
742 fprintf(stderr, "Warning: requested range invalid; we may already have all the data.\n");
743
744 - inflateEnd(&request->stream);
744 + git_inflate_end(&request->stream);
745 SHA1_Final(request->real_sha1, &request->c);
746 if (request->zret != Z_STREAM_END) {
747 unlink(request->tmpfile);
http-walker.c
+4 -4
@@ -82,7 +82,7 @@ static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
82 do {
83 obj_req->stream.next_out = expn;
84 obj_req->stream.avail_out = sizeof(expn);
85 - obj_req->zret = inflate(&obj_req->stream, Z_SYNC_FLUSH);
85 + obj_req->zret = git_inflate(&obj_req->stream, Z_SYNC_FLUSH);
86 SHA1_Update(&obj_req->c, expn,
87 sizeof(expn) - obj_req->stream.avail_out);
88 } while (obj_req->stream.avail_in && obj_req->zret == Z_OK);
@@ -142,7 +142,7 @@ static void start_object_request(struct walker *walker,
142
143 memset(&obj_req->stream, 0, sizeof(obj_req->stream));
144
145 - inflateInit(&obj_req->stream);
145 + git_inflate_init(&obj_req->stream);
146
147 SHA1_Init(&obj_req->c);
148
@@ -183,7 +183,7 @@ static void start_object_request(struct walker *walker,
183 file; also rewind to the beginning of the local file. */
184 if (prev_read == -1) {
185 memset(&obj_req->stream, 0, sizeof(obj_req->stream));
186 - inflateInit(&obj_req->stream);
186 + git_inflate_init(&obj_req->stream);
187 SHA1_Init(&obj_req->c);
188 if (prev_posn>0) {
189 prev_posn = 0;
@@ -243,7 +243,7 @@ static void finish_object_request(struct object_request *obj_req)
243 return;
244 }
245
246 - inflateEnd(&obj_req->stream);
246 + git_inflate_end(&obj_req->stream);
247 SHA1_Final(obj_req->real_sha1, &obj_req->c);
248 if (obj_req->zret != Z_STREAM_END) {
249 unlink(obj_req->tmpfile);
index-pack.c
+6 -6
@@ -271,10 +271,10 @@ static void *unpack_entry_data(unsigned long offset, unsigned long size)
271 stream.avail_out = size;
272 stream.next_in = fill(1);
273 stream.avail_in = input_len;
274 - inflateInit(&stream);
274 + git_inflate_init(&stream);
275
276 for (;;) {
277 - int ret = inflate(&stream, 0);
277 + int ret = git_inflate(&stream, 0);
278 use(input_len - stream.avail_in);
279 if (stream.total_out == size && ret == Z_STREAM_END)
280 break;
@@ -283,7 +283,7 @@ static void *unpack_entry_data(unsigned long offset, unsigned long size)
283 stream.next_in = fill(1);
284 stream.avail_in = input_len;
285 }
286 - inflateEnd(&stream);
286 + git_inflate_end(&stream);
287 return buf;
288 }
289
@@ -378,9 +378,9 @@ static void *get_data_from_pack(struct object_entry *obj)
378 stream.avail_out = obj->size;
379 stream.next_in = src;
380 stream.avail_in = len;
381 - inflateInit(&stream);
382 - while ((st = inflate(&stream, Z_FINISH)) == Z_OK);
383 - inflateEnd(&stream);
381 + git_inflate_init(&stream);
382 + while ((st = git_inflate(&stream, Z_FINISH)) == Z_OK);
383 + git_inflate_end(&stream);
384 if (st != Z_STREAM_END || stream.total_out != obj->size)
385 die("serious inflate inconsistency");
386 free(src);
sha1_file.c
+12 -12
@@ -1182,8 +1182,8 @@ static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned lon
1182 stream->avail_out = bufsiz;
1183
1184 if (legacy_loose_object(map)) {
1185 - inflateInit(stream);
1186 - return inflate(stream, 0);
1185 + git_inflate_init(stream);
1186 + return git_inflate(stream, 0);
1187 }
1188
1189
@@ -1203,7 +1203,7 @@ static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned lon
1203 /* Set up the stream for the rest.. */
1204 stream->next_in = map;
1205 stream->avail_in = mapsize;
1206 - inflateInit(stream);
1206 + git_inflate_init(stream);
1207
1208 /* And generate the fake traditional header */
1209 stream->total_out = 1 + snprintf(buffer, bufsiz, "%s %lu",
@@ -1240,11 +1240,11 @@ static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size
1240 stream->next_out = buf + bytes;
1241 stream->avail_out = size - bytes;
1242 while (status == Z_OK)
1243 - status = inflate(stream, Z_FINISH);
1243 + status = git_inflate(stream, Z_FINISH);
1244 }
1245 buf[size] = 0;
1246 if (status == Z_STREAM_END && !stream->avail_in) {
1247 - inflateEnd(stream);
1247 + git_inflate_end(stream);
1248 return buf;
1249 }
1250
@@ -1334,15 +1334,15 @@ unsigned long get_size_from_delta(struct packed_git *p,
1334 stream.next_out = delta_head;
1335 stream.avail_out = sizeof(delta_head);
1336
1337 - inflateInit(&stream);
1337 + git_inflate_init(&stream);
1338 do {
1339 in = use_pack(p, w_curs, curpos, &stream.avail_in);
1340 stream.next_in = in;
1341 - st = inflate(&stream, Z_FINISH);
1341 + st = git_inflate(&stream, Z_FINISH);
1342 curpos += stream.next_in - in;
1343 } while ((st == Z_OK || st == Z_BUF_ERROR) &&
1344 stream.total_out < sizeof(delta_head));
1345 - inflateEnd(&stream);
1345 + git_inflate_end(&stream);
1346 if ((st != Z_STREAM_END) && stream.total_out != sizeof(delta_head))
1347 die("delta data unpack-initial failed");
1348
@@ -1550,14 +1550,14 @@ static void *unpack_compressed_entry(struct packed_git *p,
1550 stream.next_out = buffer;
1551 stream.avail_out = size;
1552
1553 - inflateInit(&stream);
1553 + git_inflate_init(&stream);
1554 do {
1555 in = use_pack(p, w_curs, curpos, &stream.avail_in);
1556 stream.next_in = in;
1557 - st = inflate(&stream, Z_FINISH);
1557 + st = git_inflate(&stream, Z_FINISH);
1558 curpos += stream.next_in - in;
1559 } while (st == Z_OK || st == Z_BUF_ERROR);
1560 - inflateEnd(&stream);
1560 + git_inflate_end(&stream);
1561 if ((st != Z_STREAM_END) || stream.total_out != size) {
1562 free(buffer);
1563 return NULL;
@@ -1965,7 +1965,7 @@ static int sha1_loose_object_info(const unsigned char *sha1, unsigned long *size
1965 status = error("unable to parse %s header", sha1_to_hex(sha1));
1966 else if (sizep)
1967 *sizep = size;
1968 - inflateEnd(&stream);
1968 + git_inflate_end(&stream);
1969 munmap(map, mapsize);
1970 return status;
1971 }
wrapper.c
+60
@@ -196,3 +196,63 @@ int xmkstemp(char *template)
196 die("Unable to create temporary file: %s", strerror(errno));
197 return fd;
198 }
199 +
200 +/*
201 + * zlib wrappers to make sure we don't silently miss errors
202 + * at init time.
203 + */
204 +void git_inflate_init(z_streamp strm)
205 +{
206 + const char *err;
207 +
208 + switch (inflateInit(strm)) {
209 + case Z_OK:
210 + return;
211 +
212 + case Z_MEM_ERROR:
213 + err = "out of memory";
214 + break;
215 + case Z_VERSION_ERROR:
216 + err = "wrong version";
217 + break;
218 + default:
219 + err = "error";
220 + }
221 + die("inflateInit: %s (%s)", err, strm->msg ? strm->msg : "no message");
222 +}
223 +
224 +void git_inflate_end(z_streamp strm)
225 +{
226 + if (inflateEnd(strm) != Z_OK)
227 + error("inflateEnd: %s", strm->msg ? strm->msg : "failed");
228 +}
229 +
230 +int git_inflate(z_streamp strm, int flush)
231 +{
232 + int ret = inflate(strm, flush);
233 + const char *err;
234 +
235 + switch (ret) {
236 + /* Out of memory is fatal. */
237 + case Z_MEM_ERROR:
238 + die("inflate: out of memory");
239 +
240 + /* Data corruption errors: we may want to recover from them (fsck) */
241 + case Z_NEED_DICT:
242 + err = "needs dictionary"; break;
243 + case Z_DATA_ERROR:
244 + err = "data stream error"; break;
245 + case Z_STREAM_ERROR:
246 + err = "stream consistency error"; break;
247 + default:
248 + err = "unknown error"; break;
249 +
250 + /* Z_BUF_ERROR: normal, needs more space in the output buffer */
251 + case Z_BUF_ERROR:
252 + case Z_OK:
253 + case Z_STREAM_END:
254 + return ret;
255 + }
256 + error("inflate: %s (%s)", err, strm->msg ? strm->msg : "no message");
257 + return ret;
258 +}