xref: /freebsd/stand/libsa/gzipfs.c (revision 02e9120893770924227138ba49df1edb3896112a)
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 <sys/cdefs.h>
28 #include "stand.h"
29 
30 #include <sys/stat.h>
31 #include <string.h>
32 #include <zlib.h>
33 
34 #define Z_BUFSIZE 2048	/* XXX larger? */
35 
36 struct z_file
37 {
38     int			zf_rawfd;
39     off_t		zf_dataoffset;
40     z_stream		zf_zstream;
41     unsigned char	zf_buf[Z_BUFSIZE];
42     int			zf_endseen;
43 };
44 
45 static int	zf_fill(struct z_file *z);
46 static int	zf_open(const char *path, struct open_file *f);
47 static int	zf_close(struct open_file *f);
48 static int	zf_read(struct open_file *f, void *buf, size_t size, size_t *resid);
49 static off_t	zf_seek(struct open_file *f, off_t offset, int where);
50 static int	zf_stat(struct open_file *f, struct stat *sb);
51 
52 struct fs_ops gzipfs_fsops = {
53 	.fs_name = "zip",
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