Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/stand/libsa/gzipfs.c
34677 views
1
/*
2
* Copyright (c) 1998 Michael Smith.
3
* All rights reserved.
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
*
14
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
* SUCH DAMAGE.
25
*/
26
27
#include "stand.h"
28
29
#include <sys/stat.h>
30
#include <string.h>
31
#include <zlib.h>
32
33
#define Z_BUFSIZE 2048 /* XXX larger? */
34
35
struct z_file
36
{
37
int zf_rawfd;
38
off_t zf_dataoffset;
39
z_stream zf_zstream;
40
unsigned char zf_buf[Z_BUFSIZE];
41
int zf_endseen;
42
};
43
44
static int zf_fill(struct z_file *z);
45
static int zf_open(const char *path, struct open_file *f);
46
static int zf_close(struct open_file *f);
47
static int zf_read(struct open_file *f, void *buf, size_t size, size_t *resid);
48
static off_t zf_seek(struct open_file *f, off_t offset, int where);
49
static int zf_stat(struct open_file *f, struct stat *sb);
50
51
struct fs_ops gzipfs_fsops = {
52
.fs_name = "zip",
53
.fs_flags = 0,
54
.fo_open = zf_open,
55
.fo_close = zf_close,
56
.fo_read = zf_read,
57
.fo_write = null_write,
58
.fo_seek = zf_seek,
59
.fo_stat = zf_stat,
60
.fo_readdir = null_readdir,
61
};
62
63
static int
64
zf_fill(struct z_file *zf)
65
{
66
int result;
67
int req;
68
69
req = Z_BUFSIZE - zf->zf_zstream.avail_in;
70
result = 0;
71
72
/* If we need more */
73
if (req > 0) {
74
/* move old data to bottom of buffer */
75
if (req < Z_BUFSIZE)
76
bcopy(zf->zf_buf + req, zf->zf_buf, Z_BUFSIZE - req);
77
78
/* read to fill buffer and update availibility data */
79
result = read(zf->zf_rawfd, zf->zf_buf + zf->zf_zstream.avail_in, req);
80
zf->zf_zstream.next_in = zf->zf_buf;
81
if (result >= 0)
82
zf->zf_zstream.avail_in += result;
83
}
84
return(result);
85
}
86
87
/*
88
* Adapted from get_byte/check_header in libz
89
*
90
* Returns 0 if the header is OK, nonzero if not.
91
*/
92
static int
93
get_byte(struct z_file *zf, off_t *curoffp)
94
{
95
if ((zf->zf_zstream.avail_in == 0) && (zf_fill(zf) == -1))
96
return(-1);
97
zf->zf_zstream.avail_in--;
98
++*curoffp;
99
return(*(zf->zf_zstream.next_in)++);
100
}
101
102
static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
103
104
/* gzip flag byte */
105
#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
106
#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
107
#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
108
#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
109
#define COMMENT 0x10 /* bit 4 set: file comment present */
110
#define RESERVED 0xE0 /* bits 5..7: reserved */
111
112
static int
113
check_header(struct z_file *zf)
114
{
115
int method; /* method byte */
116
int flags; /* flags byte */
117
uInt len;
118
int c;
119
120
zf->zf_dataoffset = 0;
121
/* Check the gzip magic header */
122
for (len = 0; len < 2; len++) {
123
c = get_byte(zf, &zf->zf_dataoffset);
124
if (c != gz_magic[len]) {
125
return(1);
126
}
127
}
128
method = get_byte(zf, &zf->zf_dataoffset);
129
flags = get_byte(zf, &zf->zf_dataoffset);
130
if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
131
return(1);
132
}
133
134
/* Discard time, xflags and OS code: */
135
for (len = 0; len < 6; len++) (void)get_byte(zf, &zf->zf_dataoffset);
136
137
if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
138
len = (uInt)get_byte(zf, &zf->zf_dataoffset);
139
len += ((uInt)get_byte(zf, &zf->zf_dataoffset))<<8;
140
/* len is garbage if EOF but the loop below will quit anyway */
141
while (len-- != 0 && get_byte(zf, &zf->zf_dataoffset) != -1) ;
142
}
143
if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
144
while ((c = get_byte(zf, &zf->zf_dataoffset)) != 0 && c != -1) ;
145
}
146
if ((flags & COMMENT) != 0) { /* skip the .gz file comment */
147
while ((c = get_byte(zf, &zf->zf_dataoffset)) != 0 && c != -1) ;
148
}
149
if ((flags & HEAD_CRC) != 0) { /* skip the header crc */
150
for (len = 0; len < 2; len++) c = get_byte(zf, &zf->zf_dataoffset);
151
}
152
/* if there's data left, we're in business */
153
return((c == -1) ? 1 : 0);
154
}
155
156
static int
157
zf_open(const char *fname, struct open_file *f)
158
{
159
static char *zfname;
160
int rawfd;
161
struct z_file *zf;
162
char *cp;
163
int error;
164
struct stat sb;
165
166
/* Have to be in "just read it" mode */
167
if (f->f_flags != F_READ)
168
return(EPERM);
169
170
/* If the name already ends in .gz or .bz2, ignore it */
171
if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".gz")
172
|| !strcmp(cp, ".bz2") || !strcmp(cp, ".split")))
173
return(ENOENT);
174
175
/* Construct new name */
176
zfname = malloc(strlen(fname) + 4);
177
if (zfname == NULL)
178
return(ENOMEM);
179
sprintf(zfname, "%s.gz", fname);
180
181
/* Try to open the compressed datafile */
182
rawfd = open(zfname, O_RDONLY);
183
free(zfname);
184
if (rawfd == -1)
185
return(ENOENT);
186
187
if (fstat(rawfd, &sb) < 0) {
188
printf("zf_open: stat failed\n");
189
close(rawfd);
190
return(ENOENT);
191
}
192
if (!S_ISREG(sb.st_mode)) {
193
printf("zf_open: not a file\n");
194
close(rawfd);
195
return(EISDIR); /* best guess */
196
}
197
198
/* Allocate a z_file structure, populate it */
199
zf = malloc(sizeof(struct z_file));
200
if (zf == NULL)
201
return(ENOMEM);
202
bzero(zf, sizeof(struct z_file));
203
zf->zf_rawfd = rawfd;
204
205
/* Verify that the file is gzipped */
206
if (check_header(zf)) {
207
close(zf->zf_rawfd);
208
free(zf);
209
return(EFTYPE);
210
}
211
212
/* Initialise the inflation engine */
213
if ((error = inflateInit2(&(zf->zf_zstream), -15)) != Z_OK) {
214
printf("zf_open: inflateInit returned %d : %s\n", error, zf->zf_zstream.msg);
215
close(zf->zf_rawfd);
216
free(zf);
217
return(EIO);
218
}
219
220
/* Looks OK, we'll take it */
221
f->f_fsdata = zf;
222
return(0);
223
}
224
225
static int
226
zf_close(struct open_file *f)
227
{
228
struct z_file *zf = (struct z_file *)f->f_fsdata;
229
230
inflateEnd(&(zf->zf_zstream));
231
close(zf->zf_rawfd);
232
free(zf);
233
return(0);
234
}
235
236
static int
237
zf_read(struct open_file *f, void *buf, size_t size, size_t *resid)
238
{
239
struct z_file *zf = (struct z_file *)f->f_fsdata;
240
int error;
241
242
zf->zf_zstream.next_out = buf; /* where and how much */
243
zf->zf_zstream.avail_out = size;
244
245
while (zf->zf_zstream.avail_out && zf->zf_endseen == 0) {
246
if ((zf->zf_zstream.avail_in == 0) && (zf_fill(zf) == -1)) {
247
printf("zf_read: fill error\n");
248
return(EIO);
249
}
250
if (zf->zf_zstream.avail_in == 0) { /* oops, unexpected EOF */
251
printf("zf_read: unexpected EOF\n");
252
if (zf->zf_zstream.avail_out == size)
253
return(EIO);
254
break;
255
}
256
257
error = inflate(&zf->zf_zstream, Z_SYNC_FLUSH); /* decompression pass */
258
if (error == Z_STREAM_END) { /* EOF, all done */
259
zf->zf_endseen = 1;
260
break;
261
}
262
if (error != Z_OK) { /* argh, decompression error */
263
printf("inflate: %s\n", zf->zf_zstream.msg);
264
return(EIO);
265
}
266
}
267
if (resid != NULL)
268
*resid = zf->zf_zstream.avail_out;
269
return(0);
270
}
271
272
static int
273
zf_rewind(struct open_file *f)
274
{
275
struct z_file *zf = (struct z_file *)f->f_fsdata;
276
277
if (lseek(zf->zf_rawfd, zf->zf_dataoffset, SEEK_SET) == -1)
278
return(-1);
279
zf->zf_zstream.avail_in = 0;
280
zf->zf_zstream.next_in = NULL;
281
zf->zf_endseen = 0;
282
(void)inflateReset(&zf->zf_zstream);
283
284
return(0);
285
}
286
287
static off_t
288
zf_seek(struct open_file *f, off_t offset, int where)
289
{
290
struct z_file *zf = (struct z_file *)f->f_fsdata;
291
off_t target;
292
char discard[16];
293
294
switch (where) {
295
case SEEK_SET:
296
target = offset;
297
break;
298
case SEEK_CUR:
299
target = offset + zf->zf_zstream.total_out;
300
break;
301
default:
302
errno = EINVAL;
303
return(-1);
304
}
305
306
/* rewind if required */
307
if (target < zf->zf_zstream.total_out && zf_rewind(f) != 0)
308
return(-1);
309
310
/* skip forwards if required */
311
while (target > zf->zf_zstream.total_out) {
312
errno = zf_read(f, discard, min(sizeof(discard),
313
target - zf->zf_zstream.total_out), NULL);
314
if (errno)
315
return(-1);
316
/* Break out of loop if end of file has been reached. */
317
if (zf->zf_endseen)
318
break;
319
}
320
/* This is where we are (be honest if we overshot) */
321
return(zf->zf_zstream.total_out);
322
}
323
324
325
static int
326
zf_stat(struct open_file *f, struct stat *sb)
327
{
328
struct z_file *zf = (struct z_file *)f->f_fsdata;
329
int result;
330
331
/* stat as normal, but indicate that size is unknown */
332
if ((result = fstat(zf->zf_rawfd, sb)) == 0)
333
sb->st_size = -1;
334
return(result);
335
}
336
337
338
339
340