+
+ return iter.err;
+}
+
+/*
+ * Open compressed log file and decompress the stored chunks and
+ * write them to stdout. The chunks are stored sequentially in the
+ * file, so we iterate over them and do them one-by-one.
+ */
+int iolog_file_inflate(const char *file)
+{
+ struct inflate_chunk_iter iter = { .chunk_sz = 64 * 1024 * 1024, };
+ struct iolog_compress ic;
+ z_stream stream;
+ struct stat sb;
+ ssize_t ret;
+ size_t total;
+ void *buf;
+ FILE *f;
+
+ f = fopen(file, "r");
+ if (!f) {
+ perror("fopen");
+ return 1;
+ }
+
+ if (stat(file, &sb) < 0) {
+ fclose(f);
+ perror("stat");
+ return 1;
+ }
+
+ ic.buf = buf = malloc(sb.st_size);
+ ic.len = sb.st_size;
+ ic.seq = 1;
+
+ ret = fread(ic.buf, ic.len, 1, f);
+ if (ret < 0) {
+ perror("fread");
+ fclose(f);
+ free(buf);
+ return 1;
+ } else if (ret != 1) {
+ log_err("fio: short read on reading log\n");
+ fclose(f);
+ free(buf);
+ return 1;
+ }
+
+ fclose(f);
+
+ /*
+ * Each chunk will return Z_STREAM_END. We don't know how many
+ * chunks are in the file, so we just keep looping and incrementing
+ * the sequence number until we have consumed the whole compressed
+ * file.
+ */
+ total = ic.len;
+ do {
+ size_t iret;
+
+ iret = inflate_chunk(&ic, 1, stdout, &stream, &iter);
+ total -= iret;
+ if (!total)
+ break;
+ if (iter.err)
+ break;
+
+ ic.seq++;
+ ic.len -= iret;
+ ic.buf += iret;
+ } while (1);
+
+ if (iter.seq) {
+ finish_chunk(&stream, stdout, &iter);
+ free(iter.buf);
+ }
+
+ free(buf);
+ return iter.err;