* Gunzip a given file and remove the .gz if successful. * @param ci container with filename * @return true if the gunzip completed */
| 386 | * @return true if the gunzip completed |
| 387 | */ |
| 388 | static bool GunzipFile(const ContentInfo &ci) |
| 389 | { |
| 390 | #if defined(WITH_ZLIB) |
| 391 | bool ret = true; |
| 392 | |
| 393 | /* Need to open the file with fopen() to support non-ASCII on Windows. */ |
| 394 | auto ftmp = FileHandle::Open(GetFullFilename(ci, true), "rb"); |
| 395 | if (!ftmp.has_value()) return false; |
| 396 | /* Duplicate the handle, and close the FILE*, to avoid double-closing the handle later. */ |
| 397 | int fdup = dup(fileno(*ftmp)); |
| 398 | gzFile fin = gzdopen(fdup, "rb"); |
| 399 | ftmp.reset(); |
| 400 | |
| 401 | auto fout = FileHandle::Open(GetFullFilename(ci, false), "wb"); |
| 402 | |
| 403 | if (fin == nullptr || !fout.has_value()) { |
| 404 | ret = false; |
| 405 | } else { |
| 406 | uint8_t buff[8192]; |
| 407 | for (;;) { |
| 408 | int read = gzread(fin, buff, sizeof(buff)); |
| 409 | if (read == 0) { |
| 410 | /* If gzread() returns 0, either the end-of-file has been |
| 411 | * reached or an underlying read error has occurred. |
| 412 | * |
| 413 | * gzeof() can't be used, because: |
| 414 | * 1.2.5 - it is safe, 1 means 'everything was OK' |
| 415 | * 1.2.3.5, 1.2.4 - 0 or 1 is returned 'randomly' |
| 416 | * 1.2.3.3 - 1 is returned for truncated archive |
| 417 | * |
| 418 | * So we use gzerror(). When proper end of archive |
| 419 | * has been reached, then: |
| 420 | * errnum == Z_STREAM_END in 1.2.3.3, |
| 421 | * errnum == 0 in 1.2.4 and 1.2.5 */ |
| 422 | int errnum; |
| 423 | gzerror(fin, &errnum); |
| 424 | if (errnum != 0 && errnum != Z_STREAM_END) ret = false; |
| 425 | break; |
| 426 | } |
| 427 | if (read < 0 || static_cast<size_t>(read) != fwrite(buff, 1, read, *fout)) { |
| 428 | /* If gzread() returns -1, there was an error in archive */ |
| 429 | ret = false; |
| 430 | break; |
| 431 | } |
| 432 | /* DO NOT DO THIS! It will fail to detect broken archive with 1.2.3.3! |
| 433 | * if (read < sizeof(buff)) break; */ |
| 434 | } |
| 435 | } |
| 436 | |
| 437 | if (fin != nullptr) { |
| 438 | gzclose(fin); |
| 439 | } else if (fdup != -1) { |
| 440 | /* Failing gzdopen does not close the passed file descriptor. */ |
| 441 | close(fdup); |
| 442 | } |
| 443 | |
| 444 | return ret; |
| 445 | #else |
no test coverage detected