/* * Copyright (c) 2020 AIIT XUOS Lab * XiUOS is licensed under Mulan PSL v2. * You can use this software according to the terms and conditions of the Mulan PSL v2. * You may obtain a copy of Mulan PSL v2 at: * http://license.coscl.org.cn/MulanPSL2 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. * See the Mulan PSL v2 for more details. */ #include #if defined(FS_VFS) && defined(TOOL_SHELL) #include #include #include #include #include #include #include "utility.h" #define USE_HEAP_MEM struct globals { uint32_t crc32; int in_fd; int out_fd; size_t outcnt; size_t outbuf_cnt; unsigned char window[GUNZIP_WSIZE]; unsigned char bytebuffer[GUNZIP_BYTEBUFFER_MAX]; uint32_t bytebuffer_offset; uint32_t bytebuffer_size; uint32_t to_read; uint32_t bi_buf; uint32_t bi_valid; uint32_t inflate_codes_ml; uint32_t inflate_codes_md; uint32_t inflate_codes_bb; uint32_t inflate_codes_k; uint32_t inflate_codes_w; uint32_t inflate_codes_bl; uint32_t inflate_codes_bd; uint32_t inflate_codes_nn; uint32_t inflate_codes_dd; huft_t *inflate_codes_tl; huft_t *inflate_codes_td; int resume_copy; int method; int need_another_block; int eof; uint32_t inflate_stored_n; uint32_t inflate_stored_b; uint32_t inflate_stored_k; uint32_t inflate_stored_w; }; #ifdef USE_HEAP_MEM static struct globals *glbp; #define GLB (*glbp) #else struct globals GLB; #endif static const uint16_t mask_bits[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff }; static const uint16_t cplens[] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 }; static const uint8_t cplext[] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99 }; static const uint16_t cpdist[] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 }; static const uint8_t cpdext[] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; static const uint8_t border[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; static void InitInflateCodes(uint32_t bl, uint32_t bd) { GLB.inflate_codes_bl = bl; GLB.inflate_codes_bd = bd; GLB.inflate_codes_bb = GLB.bi_buf; GLB.inflate_codes_k = GLB.bi_valid; GLB.inflate_codes_w = GLB.outbuf_cnt; GLB.inflate_codes_ml = mask_bits[bl]; GLB.inflate_codes_md = mask_bits[bd]; } static int InflateCodes(); static void HuftFree(huft_t *p) { huft_t *q; while (p) { q = (--p)->t; free(p); p = q; } } static int HuftBuild(const uint32_t *b, const uint32_t n, const uint32_t s, const uint16_t *d, const unsigned char *e, huft_t **t, uint32_t *m) { uint32_t a; uint32_t c[BMAX + 1]; uint32_t eob_len; uint32_t f; int g; int htl; uint32_t i; uint32_t j; int k; const uint32_t *p; huft_t *q; huft_t r; huft_t *u[BMAX]; uint32_t v[NMAX + 1]; int ws[BMAX + 1]; int w; uint32_t x[BMAX + 1]; uint32_t *xp; int y; uint32_t z; eob_len = n > 256 ? b[256] : BMAX; *t = NULL; memset(c, 0, sizeof(c)); p = b; i = n; do { c[*p]++; p++; } while (--i); if (c[0] == n) { q = malloc(3 * sizeof(*q)); memset(q, 0, 3 * sizeof(*q)); q[1].e = 99; q[1].b = 1; q[2].e = 99; q[2].b = 1; *t = q + 1; *m = 1; return 0; } for (j = 1; (j <= BMAX) && (c[j] == 0); j++) continue; k = j; for (i = BMAX; (c[i] == 0) && i; i--) continue; g = i; *m = (*m < j) ? j : ((*m > i) ? i : *m); for (y = 1 << j; j < i; j++, y <<= 1) { y -= c[j]; if (y < 0) return -1; } y -= c[i]; if (y < 0) return -1; c[i] += y; x[1] = j = 0; p = c + 1; xp = x + 2; while (--i) { j += *p++; *xp++ = j; } memset(v, 0xff, sizeof(v)); p = b; i = 0; do { j = *p++; if (j != 0) { v[x[j]++] = i; } } while (++i < n); x[0] = i = 0; p = v; htl = -1; w = ws[0] = 0; u[0] = NULL; q = NULL; z = 0; for (; k <= g; k++) { a = c[k]; while (a--) { while (k > ws[htl + 1]) { w = ws[++htl]; z = g - w; z = z > *m ? *m : z; j = k - w; f = 1 << j; if (f > a + 1) { f -= a + 1; xp = c + k; while (++j < z) { f <<= 1; if (f <= *++xp) { break; } f -= *xp; } } j = (w + j > eob_len && w < eob_len) ? eob_len - w : j; z = 1 << j; ws[htl+1] = w + j; q = malloc((z + 1) * sizeof(huft_t)); memset(q, 0, (z + 1) * sizeof(huft_t)); *t = q + 1; t = &(q->t); u[htl] = ++q; if (htl) { x[htl] = i; r.b = (unsigned char)(w - ws[htl - 1]); r.e = (unsigned char)(16 + j); r.t = q; j = (i & ((1 << w) - 1)) >> ws[htl - 1]; u[htl - 1][j] = r; } } r.b = (unsigned char)(k - w); if (*p == 0xffffffff) { r.e = 99; } else if (*p < s) { r.e = (unsigned char)(*p < 256 ? 16 : 15); r.n = (unsigned short)(*p++); } else { r.e = (unsigned char)e[*p - s]; r.n = d[*p++ - s]; } f = 1 << (k - w); for (j = i >> w; j < z; j += f) { q[j] = r; } for (j = 1 << (k - 1); i & j; j >>= 1) { i ^= j; } i ^= j; while ((i & ((1 << w) - 1)) != x[htl]) { w = ws[--htl]; } } } *m = ws[1]; return y != 0 && g != 1 ? -1 : 0; } int FillBitBuffer(uint32_t *bitbuffer, uint32_t *current_len, uint32_t required_len) { while (*current_len < required_len) { if (GLB.bytebuffer_offset >= GLB.bytebuffer_size) { uint32_t size = GUNZIP_BYTEBUFFER_MAX - 4; if (GLB.to_read >= 0 && GLB.to_read < size) size = GLB.to_read; GLB.bytebuffer_size = read(GLB.in_fd, &GLB.bytebuffer[4], size); if (GLB.bytebuffer_size == 0) { return -1; } if (GLB.to_read >= 0) GLB.to_read -= GLB.bytebuffer_size; GLB.bytebuffer_size += 4; GLB.bytebuffer_offset = 4; } *bitbuffer |= (uint32_t)GLB.bytebuffer[GLB.bytebuffer_offset] << *current_len; GLB.bytebuffer_offset++; *current_len += 8; } return 0; } static int InflateBlock() { uint32_t ll[286 + 30]; uint32_t t; uint32_t b; uint32_t k; b = GLB.bi_buf; k = GLB.bi_valid; if (FillBitBuffer(&b, &k, 1) < 0) return -1; GLB.eof = b & 1; b >>= 1; k -= 1; if (FillBitBuffer(&b, &k, 2) < 0) return -1; t = b & 0x3; b >>= 2; k -= 2; GLB.bi_buf = b; GLB.bi_valid = k; switch (t) { case 0: { uint32_t n; uint32_t b_stored; uint32_t k_stored; b_stored = GLB.bi_buf; k_stored = GLB.bi_valid; n = k_stored & 0x7; b_stored >>= n; k_stored -= n; if (FillBitBuffer(&b_stored, &k_stored, 16) < 0) return -1; n = b_stored & 0xffff; b_stored >>= 16; k_stored -= 16; if (FillBitBuffer(&b_stored, &k_stored, 16) < 0) return -1; if (n != ((~b_stored) & 0xffff)) return -1; b_stored >>= 16; k_stored -= 16; GLB.inflate_stored_n = n; GLB.inflate_stored_b = b_stored; GLB.inflate_stored_k = k_stored; return STORED; } case 1: { int i; uint32_t bl; uint32_t bd; for (i = 0; i < 144; i++) ll[i] = 8; for (; i < 256; i++) ll[i] = 9; for (; i < 280; i++) ll[i] = 7; for (; i < 288; i++) ll[i] = 8; bl = 7; HuftBuild(ll, 288, 257, cplens, cplext, &GLB.inflate_codes_tl, &bl); for (i = 0; i < 30; i++) ll[i] = 5; bd = 5; HuftBuild(ll, 30, 0, cpdist, cpdext, &GLB.inflate_codes_td, &bd); InitInflateCodes(bl, bd); return CODES; } case 2: { int dbits = 6; int lbits = 9; huft_t *td; uint32_t i; uint32_t j; uint32_t l; uint32_t m; uint32_t n; uint32_t bl; uint32_t bd; uint32_t nb; uint32_t nl; uint32_t nd; uint32_t b_dynamic; uint32_t k_dynamic; b_dynamic = GLB.bi_buf; k_dynamic = GLB.bi_valid; if (FillBitBuffer(&b_dynamic, &k_dynamic, 5) < 0) return -1; nl = 257 + (b_dynamic & 0x1f); b_dynamic >>= 5; k_dynamic -= 5; if (FillBitBuffer(&b_dynamic, &k_dynamic, 5) < 0) return -1; nd = 1 + (b_dynamic & 0x1f); b_dynamic >>= 5; k_dynamic -= 5; if (FillBitBuffer(&b_dynamic, &k_dynamic, 4) < 0) return -1; nb = 4 + (b_dynamic & 0xf); b_dynamic >>= 4; k_dynamic -= 4; if (nl > 286 || nd > 30) return -1; for (j = 0; j < nb; j++) { if (FillBitBuffer(&b_dynamic, &k_dynamic, 3) < 0) return -1; ll[border[j]] = b_dynamic & 0x7; b_dynamic >>= 3; k_dynamic -= 3; } for (; j < 19; j++) ll[border[j]] = 0; bl = 7; i = HuftBuild(ll, 19, 19, NULL, NULL, &GLB.inflate_codes_tl, &bl); if (i < 0) return -1; n = nl + nd; m = mask_bits[bl]; i = l = 0; while (i < n) { if (FillBitBuffer(&b_dynamic, &k_dynamic, bl) < 0) return -1; td = GLB.inflate_codes_tl + (b_dynamic & m); j = td->b; b_dynamic >>= j; k_dynamic -= j; j = td->n; if (j < 16) { ll[i++] = l = j; } else if (j == 16) { if (FillBitBuffer(&b_dynamic, &k_dynamic, 2) < 0) return -1; j = 3 + (b_dynamic & 3); b_dynamic >>= 2; k_dynamic -= 2; if (i + j > n) return -1; while (j--) { ll[i++] = l; } } else if (j == 17) { if (FillBitBuffer(&b_dynamic, &k_dynamic, 3) < 0) return -1; j = 3 + (b_dynamic & 7); b_dynamic >>= 3; k_dynamic -= 3; if (i + j > n) return -1; while (j--) { ll[i++] = 0; } l = 0; } else { if (FillBitBuffer(&b_dynamic, &k_dynamic, 7) < 0) return -1; j = 11 + ((unsigned) b_dynamic & 0x7f); b_dynamic >>= 7; k_dynamic -= 7; if ((unsigned) i + j > n) return -1; while (j--) ll[i++] = 0; l = 0; } } HuftFree(GLB.inflate_codes_tl); GLB.bi_buf = b_dynamic; GLB.bi_valid = k_dynamic; bl = lbits; i = HuftBuild(ll, nl, 257, cplens, cplext, &GLB.inflate_codes_tl, &bl); if (i < 0) return -1; bd = dbits; i = HuftBuild(ll + nl, nd, 0, cpdist, cpdext, &GLB.inflate_codes_td, &bd); if (i < 0) return -1; InitInflateCodes(bl, bd); return CODES; } default: return -1; } } static int InflateStored() { while (GLB.inflate_stored_n--) { if (FillBitBuffer(&GLB.inflate_stored_b, &GLB.inflate_stored_k, 8) < 0) return -1; GLB.window[GLB.inflate_stored_w++] = (unsigned char)GLB.inflate_stored_b; GLB.inflate_stored_b >>= 8; GLB.inflate_stored_k -= 8; if (GLB.inflate_stored_w == GUNZIP_WSIZE) { GLB.outbuf_cnt = GLB.inflate_stored_w; GLB.inflate_stored_w = 0; return 1; } } GLB.outbuf_cnt = GLB.inflate_stored_w; GLB.bi_buf = GLB.inflate_stored_b; GLB.bi_valid = GLB.inflate_stored_k; return 0; } static int InflateCodes() { unsigned e; huft_t *t; if (GLB.resume_copy) goto do_copy; while (1) { if (FillBitBuffer(&GLB.inflate_codes_bb, &GLB.inflate_codes_k,GLB.inflate_codes_bl) < 0) return -1; t = GLB.inflate_codes_tl + (GLB.inflate_codes_bb & GLB.inflate_codes_ml); e = t->e; if (e > 16) do { if (e == 99) return -1; GLB.inflate_codes_bb >>= t->b; GLB.inflate_codes_k -= t->b; e -= 16; if (FillBitBuffer(&GLB.inflate_codes_bb, &GLB.inflate_codes_k, e) < 0) return -1; t = t->t + (GLB.inflate_codes_bb & mask_bits[e]); e = t->e; } while (e > 16); GLB.inflate_codes_bb >>= t->b; GLB.inflate_codes_k -= t->b; if (e == 16) { GLB.window[GLB.inflate_codes_w++] = (unsigned char)t->n; if (GLB.inflate_codes_w == GUNZIP_WSIZE) { GLB.outbuf_cnt = GLB.inflate_codes_w; GLB.inflate_codes_w = 0; return 1; } } else { if (e == 15) break; if (FillBitBuffer(&GLB.inflate_codes_bb, &GLB.inflate_codes_k, e) < 0) return -1; GLB.inflate_codes_nn = t->n + (GLB.inflate_codes_bb & mask_bits[e]); GLB.inflate_codes_bb >>= e; GLB.inflate_codes_k -= e; if (FillBitBuffer(&GLB.inflate_codes_bb, &GLB.inflate_codes_k, GLB.inflate_codes_bd) < 0) return -1; t = GLB.inflate_codes_td + (GLB.inflate_codes_bb & GLB.inflate_codes_md); e = t->e; if (e > 16) do { if (e == 99) return -1; GLB.inflate_codes_bb >>= t->b; GLB.inflate_codes_k -= t->b; e -= 16; if (FillBitBuffer(&GLB.inflate_codes_bb, &GLB.inflate_codes_k, e) < 0) return -1; t = t->t + (GLB.inflate_codes_bb & mask_bits[e]); e = t->e; } while (e > 16); GLB.inflate_codes_bb >>= t->b; GLB.inflate_codes_k -= t->b; if (FillBitBuffer(&GLB.inflate_codes_bb, &GLB.inflate_codes_k, e) < 0) return -1; GLB.inflate_codes_dd = GLB.inflate_codes_w - t->n - (GLB.inflate_codes_bb & mask_bits[e]); GLB.inflate_codes_bb >>= e; GLB.inflate_codes_k -= e; do_copy: do { uint32_t delta; GLB.inflate_codes_dd &= GUNZIP_WSIZE - 1; e = GUNZIP_WSIZE - (GLB.inflate_codes_dd > GLB.inflate_codes_w ? GLB.inflate_codes_dd : GLB.inflate_codes_w); delta = GLB.inflate_codes_w > GLB.inflate_codes_dd ? GLB.inflate_codes_w - GLB.inflate_codes_dd : GLB.inflate_codes_dd - GLB.inflate_codes_w; if (e > GLB.inflate_codes_nn) e = GLB.inflate_codes_nn; GLB.inflate_codes_nn -= e; if (delta >= e) { memcpy(GLB.window + GLB.inflate_codes_w, GLB.window + GLB.inflate_codes_dd, e); GLB.inflate_codes_w += e; GLB.inflate_codes_dd += e; } else { do { GLB.window[GLB.inflate_codes_w++] = GLB.window[GLB.inflate_codes_dd++]; } while (--e); } if (GLB.inflate_codes_w == GUNZIP_WSIZE) { GLB.outbuf_cnt = GLB.inflate_codes_w; GLB.resume_copy = (GLB.inflate_codes_nn != 0); GLB.inflate_codes_w = 0; return 1; } } while (GLB.inflate_codes_nn); GLB.resume_copy = 0; } } GLB.outbuf_cnt = GLB.inflate_codes_w; GLB.bi_buf = GLB.inflate_codes_bb; GLB.bi_valid = GLB.inflate_codes_k; HuftFree(GLB.inflate_codes_tl); HuftFree(GLB.inflate_codes_td); GLB.inflate_codes_tl = NULL; GLB.inflate_codes_td = NULL; return 0; } static void UpdateCrc32(unsigned char *buf, size_t len) { for (int i = 0; i < len; i++) { GLB.crc32 = GLB.crc32 ^ buf[i]; for (int j = 0; j < 8; j++) GLB.crc32 = (GLB.crc32 >> 1) ^ (0xedb88320 & -(GLB.crc32 & 1)); } } static int InflateGetNextWindow() { while (1) { int ret; if (GLB.need_another_block) { if (GLB.eof) { UpdateCrc32(GLB.window, GLB.outbuf_cnt); GLB.eof = 0; return 0; } GLB.method = InflateBlock(); GLB.need_another_block = 0; } switch (GLB.method) { case STORED: ret = InflateStored(); break; case CODES: ret = InflateCodes(); break; default: return -1; } if (ret < 0) return -1; if (ret == 1) { UpdateCrc32(GLB.window, GLB.outbuf_cnt); return 1; } GLB.need_another_block = 1; } } static inline int FlushOutbuf() { int written_size = write(GLB.out_fd, GLB.window, GLB.outbuf_cnt); int ret = written_size == GLB.outbuf_cnt ? 0 : -1; GLB.outcnt += written_size; GLB.outbuf_cnt = 0; return ret; } static int inflate() { GLB.crc32 = ~0; GLB.outcnt = 0; GLB.outbuf_cnt = 0; GLB.method = -1; GLB.need_another_block = 1; GLB.resume_copy = 0; GLB.bi_buf = 0; GLB.bi_valid = 0; while (1) { int ret = InflateGetNextWindow(); if (ret < 0) return -1; if (FlushOutbuf() < 0) return -1; if (ret == 0) break; } if (GLB.bi_valid >= 8) { GLB.bytebuffer_offset--; GLB.bytebuffer[GLB.bytebuffer_offset] = GLB.bi_buf & 0xff; GLB.bi_buf >>= 8; GLB.bi_valid -= 8; } return 0; } static int FillByteBuffer(int n) { int count = GLB.bytebuffer_size - GLB.bytebuffer_offset; if (count < n) { memmove(GLB.bytebuffer, &GLB.bytebuffer[GLB.bytebuffer_offset], count); GLB.bytebuffer_offset = 0; GLB.bytebuffer_size = read(GLB.in_fd, &GLB.bytebuffer[count], GUNZIP_BYTEBUFFER_MAX - count); if ((int)GLB.bytebuffer_size < 0) return -1; GLB.bytebuffer_size += count; if (GLB.bytebuffer_size < n) return -1; } return 0; } static uint16_t BufferRead16() { uint16_t res; res = GLB.bytebuffer[GLB.bytebuffer_offset]; res |= GLB.bytebuffer[GLB.bytebuffer_offset + 1] << 8; GLB.bytebuffer_offset += 2; return res; } static uint32_t BufferRead32() { uint32_t res; res = GLB.bytebuffer[GLB.bytebuffer_offset]; res |= (uint32_t)GLB.bytebuffer[GLB.bytebuffer_offset + 1] << 8; res |= (uint32_t)GLB.bytebuffer[GLB.bytebuffer_offset + 2] << 16; res |= (uint32_t)GLB.bytebuffer[GLB.bytebuffer_offset + 3] << 24; GLB.bytebuffer_offset += 4; return res; } int InflateUnzip(int zip_fd, int dst_fd, uint32_t cmpsize, uint32_t ucmpsize, uint32_t crc32) { int ret = 0; #ifdef USE_HEAP_MEM glbp = malloc(sizeof(struct globals)); if (glbp == NULL) { KPrintf("Memory too small\n"); return -1; } #endif memset(&GLB, 0, sizeof(struct globals)); GLB.to_read = cmpsize; GLB.bytebuffer_offset = 4; GLB.in_fd = zip_fd; GLB.out_fd = dst_fd; if ((ret = inflate()) < 0) goto free_glbp; ret = ucmpsize == GLB.outcnt && ~crc32 == GLB.crc32; free_glbp: #ifdef USE_HEAP_MEM free(glbp); #endif return ret; } static int GunzipCheckFooter() { int crc32, length; if (FillByteBuffer(8) < 0) return -1; crc32 = BufferRead32(); length = BufferRead32(); if (~GLB.crc32 != crc32 || length != GLB.outcnt) return -1; return 0; } static int GunzipCheckHeader() { GzipHdr_t hdr; if (FillByteBuffer(8) < 0) return -1; memcpy(&hdr, &GLB.bytebuffer[GLB.bytebuffer_offset], 8); GLB.bytebuffer_offset += 8; if (hdr.gz_method != 8) return -1; if (hdr.flags & 0x04) { uint16_t extra_len; extra_len = BufferRead16(); if (FillByteBuffer(extra_len) < 0) return -1; GLB.bytebuffer_offset += extra_len; } if (hdr.flags & 0x18) { while (1) { do { if (FillByteBuffer(1) < 0) return -1; } while (GLB.bytebuffer[GLB.bytebuffer_offset++] != 0); if ((hdr.flags & 0x18) != 0x18) break; hdr.flags &= ~0x18; } } if (hdr.flags & 0x02) { if (FillByteBuffer(2) < 0) return -1; GLB.bytebuffer_offset += 2; } return 0; } static int GunzipCheckMagic() { uint16_t magic; if (FillByteBuffer(2) < 0) return -1; magic = BufferRead16(); if (magic != 0x8b1f) return -1; return 0; } int GzipDecompress(char *file_name) { int ret = 0; #ifdef USE_HEAP_MEM glbp = malloc(sizeof(struct globals)); if (glbp == NULL) { KPrintf("Memory too small\n"); return -1; } #endif memset(&GLB, 0, sizeof(struct globals)); GLB.to_read = -1; if ((GLB.in_fd = open(file_name, O_RDONLY)) < 0) { ret = -1; goto free_glbp; } if (TruncateExtension(file_name, ".gz") < 0) { KPrintf("Invalid extension: %s\n", file_name); ret = -1; goto close_in_fd; } if ((GLB.out_fd = open(file_name, O_WRONLY | O_CREAT | O_TRUNC)) < 0) { ret = -1; goto close_in_fd; } strcat(file_name, ".gz"); if (GunzipCheckMagic() < 0) { KPrintf("Invalid magic number: %s\n", file_name); ret = -1; goto close_out_fd; } again: if (GunzipCheckHeader() < 0) { KPrintf("Invalid header: %s\n", file_name); ret = -1; goto close_out_fd; } if ((inflate()) < 0) { ret = -1; goto close_out_fd; } if (GunzipCheckFooter() < 0) { KPrintf("Invalid footer (checksum or length): %s\n", file_name); ret = -1; } if (GunzipCheckMagic() == 0) goto again; close_out_fd: close(GLB.out_fd); close_in_fd: close(GLB.in_fd); free_glbp: #ifdef USE_HEAP_MEM free(glbp); #endif return ret; } static void GunzipPrintUsage() { KPrintf("Usage: gunzip [FILEs]...\n"); KPrintf("File names MUST end with the .gz extension\n"); } int gunzip(int argc, char **argv) { int ret = 0; if (argc < 2) { GunzipPrintUsage(); return 0; } for (int i = 1; i < argc; i++) if (GzipDecompress(argv[i]) < 0) ret = -1; if (ret < 0) KPrintf("Failed to extract (some) file(s)\n"); return 0; } #endif