:: limine / common / compress / gzip.c 5.9 KB raw

1
/* Limine glue around pdgzip: transparent gzip decompression layer over a
2
 * file_handle.  The underlying decoder lives in common/compress/pdgzip.c
3
 * (imported by ./bootstrap from the upstream iczelia/pdgzip repo); this
4
 * file only wires pdgzip's streaming read-callback API into Limine's
5
 * file_handle abstraction and adds support for random-access reads via
6
 * rewind-and-skip.
7
 *
8
 * Copyright (C) 2019-2026 Mintsuki and contributors.
9
 *
10
 * Redistribution and use in source and binary forms, with or without
11
 * modification, are permitted provided that the following conditions are met:
12
 *
13
 * 1. Redistributions of source code must retain the above copyright notice, this
14
 *    list of conditions and the following disclaimer.
15
 *
16
 * 2. Redistributions in binary form must reproduce the above copyright notice,
17
 *    this list of conditions and the following disclaimer in the documentation
18
 *    and/or other materials provided with the distribution.
19
 *
20
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
 */
31
32
#include <stdint.h>
33
#include <stddef.h>
34
#include <stdbool.h>
35
#include <lib/libc.h>
36
#include <lib/misc.h>
37
#include <lib/print.h>
38
#include <mm/pmm.h>
39
#include <compress/gzip.h>
40
#include <compress/pdgzip.h>
41
42
struct gzip_handle {
43
  struct file_handle * source;     /*  compressed file (owned)  */
44
  pdgzip_t           * gz;         /*  decoder backed by `scratch`  */
45
  void               * scratch;    /*  pdgzip scratch buffer  */
46
  size_t               scratch_sz;
47
  uint64_t             src_pos;    /*  next byte to pull from `source`  */
48
  uint64_t             dec_pos;    /*  current decompressed stream offset  */
49
};
50
51
/*  pdgzip read callback: pull up to `len` bytes from the compressed source
52
    starting at gh->src_pos.  A short read (including zero) signals EOF to
53
    the decoder, which is correct at the end of the file.  */
54
static size_t gz_source_read(void * user, void * buf, size_t len) {
55
  struct gzip_handle * gh = user;
56
  uint64_t avail = gh->source->size - gh->src_pos;
57
  if ((uint64_t)len > avail) len = (size_t)avail;
58
  if (len == 0) return 0;
59
  size_t got = fread(gh->source, buf, gh->src_pos, len);
60
  gh->src_pos += got;
61
  return got;
62
}
63
64
/*  (Re)initialize the decoder for a fresh pass over the compressed stream.
65
    pdgzip_init zeroes its own scratch, so we only need to reset our own
66
    bookkeeping.  */
67
static void gz_reset(struct gzip_handle * gh) {
68
  pdgzip_cfg_t cfg = { .read = gz_source_read, .user = gh, .concat = 0 };
69
  gh->src_pos = 0;
70
  gh->dec_pos = 0;
71
  gh->gz = pdgzip_init(gh->scratch, &cfg);
72
}
73
74
static uint64_t gzip_read(struct file_handle * file, void * buf, uint64_t loc, uint64_t count) {
75
  struct gzip_handle * gh = file->fd;
76
  /*  Rewind on backward seeks.  */
77
  if (loc < gh->dec_pos) gz_reset(gh);
78
  /*  Skip forward to reach the requested offset.  EOS during seek means
79
      the requested location is past end-of-stream - return 0 bytes.  */
80
  while (gh->dec_pos < loc) {
81
    uint8_t discard[4096];
82
    uint64_t gap = loc - gh->dec_pos;
83
    size_t chunk = gap > sizeof(discard) ? sizeof(discard) : (size_t)gap;
84
    int64_t n = pdgzip_read(gh->gz, discard, chunk);
85
    if (n < 0) panic(false, "gzip: decompression error during seek");
86
    if (n == 0) return 0;
87
    gh->dec_pos += (uint64_t)n;
88
  }
89
  /*  Decompress the requested data.  */
90
  uint8_t * dst = buf;
91
  uint64_t remaining = count;
92
  while (remaining > 0) {
93
    size_t chunk = remaining > 65536 ? 65536 : (size_t)remaining;
94
    int64_t n = pdgzip_read(gh->gz, dst, chunk);
95
    if (n < 0) panic(false, "gzip: decompression error");
96
    if (n == 0) break;
97
    dst += n;
98
    remaining -= (uint64_t)n;
99
    gh->dec_pos += (uint64_t)n;
100
  }
101
  return count - remaining;
102
}
103
104
static void gzip_close(struct file_handle * file) {
105
  struct gzip_handle * gh = file->fd;
106
  fclose(gh->source);
107
  pmm_free(gh->scratch, gh->scratch_sz);
108
  pmm_free(gh, sizeof(struct gzip_handle));
109
}
110
111
bool gzip_check(struct file_handle * fd) {
112
  if (fd->size < 18) return false;
113
  uint8_t magic[2];  fread(fd, magic, 0, 2);
114
  return magic[0] == 0x1F && magic[1] == 0x8B;
115
}
116
117
struct file_handle * gzip_open(struct file_handle * compressed) {
118
  /*  The decompressed size is not known up front.  The 4-byte ISIZE trailer
119
      is unreliable (modulo 2^32, spec defect) and callers must instead
120
      drain until gzip_read returns 0 bytes (EOS).  Advertise an unknown
121
      size via UINT64_MAX.  */
122
  struct gzip_handle * gh = ext_mem_alloc(sizeof(struct gzip_handle));
123
  gh->source     = compressed;
124
  gh->scratch_sz = pdgzip_state_size();
125
  gh->scratch    = ext_mem_alloc(gh->scratch_sz);
126
  gz_reset(gh);
127
  /*  Depends on ext_mem_alloc returning zeroed memory.  */
128
  struct file_handle * ret = ext_mem_alloc(sizeof(struct file_handle));
129
  ret->fd = gh;
130
  ret->read = (void *) gzip_read;
131
  ret->close = (void *) gzip_close;
132
  ret->size = UINT64_MAX;
133
  ret->vol = compressed->vol;
134
  if (compressed->path != NULL && compressed->path_len > 0) {
135
    ret->path = ext_mem_alloc(compressed->path_len);
136
    memcpy(ret->path, compressed->path, compressed->path_len);
137
    ret->path_len = compressed->path_len;
138
  }
139
#if defined (UEFI)
140
  ret->efi_part_handle = compressed->efi_part_handle;
141
#endif
142
  ret->pxe = compressed->pxe;
143
  ret->pxe_ip = compressed->pxe_ip;
144
  ret->pxe_port = compressed->pxe_port;
145
  return ret;
146
}
tab: 248 wrap: offon